]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/unit-printf.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / core / unit-printf.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "cgroup-util.h"
23 #include "formats-util.h"
24 #include "macro.h"
25 #include "specifier.h"
26 #include "string-util.h"
27 #include "strv.h"
28 #include "unit-name.h"
29 #include "unit.h"
30 #include "unit-printf.h"
31
32 static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
33 Unit *u = userdata;
34
35 assert(u);
36
37 return unit_name_to_prefix_and_instance(u->id, ret);
38 }
39
40 static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
41 Unit *u = userdata;
42
43 assert(u);
44
45 return unit_name_to_prefix(u->id, ret);
46 }
47
48 static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) {
49 _cleanup_free_ char *p = NULL;
50 Unit *u = userdata;
51 int r;
52
53 assert(u);
54
55 r = unit_name_to_prefix(u->id, &p);
56 if (r < 0)
57 return r;
58
59 return unit_name_unescape(p, ret);
60 }
61
62 static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) {
63 Unit *u = userdata;
64
65 assert(u);
66
67 if (!u->instance)
68 return -EINVAL;
69
70 return unit_name_unescape(u->instance, ret);
71 }
72
73 static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
74 Unit *u = userdata;
75
76 assert(u);
77
78 if (u->instance)
79 return unit_name_path_unescape(u->instance, ret);
80 else
81 return unit_name_to_path(u->id, ret);
82 }
83
84 static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
85 Unit *u = userdata;
86 char *n;
87
88 assert(u);
89
90 if (u->cgroup_path)
91 n = strdup(u->cgroup_path);
92 else
93 n = unit_default_cgroup_path(u);
94 if (!n)
95 return -ENOMEM;
96
97 *ret = n;
98 return 0;
99 }
100
101 static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) {
102 Unit *u = userdata;
103 char *n;
104
105 assert(u);
106
107 n = strdup(u->manager->cgroup_root);
108 if (!n)
109 return -ENOMEM;
110
111 *ret = n;
112 return 0;
113 }
114
115 static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) {
116 Unit *u = userdata;
117 char *n;
118
119 assert(u);
120
121 if (UNIT_ISSET(u->slice)) {
122 Unit *slice;
123
124 slice = UNIT_DEREF(u->slice);
125
126 if (slice->cgroup_path)
127 n = strdup(slice->cgroup_path);
128 else
129 n = unit_default_cgroup_path(slice);
130 } else
131 n = strdup(u->manager->cgroup_root);
132
133 *ret = n;
134 return 0;
135 }
136
137 static int specifier_runtime(char specifier, void *data, void *userdata, char **ret) {
138 Unit *u = userdata;
139 const char *e;
140 char *n = NULL;
141
142 assert(u);
143
144 if (u->manager->running_as == MANAGER_SYSTEM)
145 e = "/run";
146 else {
147 e = getenv("XDG_RUNTIME_DIR");
148 if (!e)
149 return -EOPNOTSUPP;
150 }
151
152 n = strdup(e);
153 if (!n)
154 return -ENOMEM;
155
156 *ret = n;
157 return 0;
158 }
159
160 static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
161 char *printed = NULL;
162 Unit *u = userdata;
163 ExecContext *c;
164 int r = 0;
165
166 assert(u);
167
168 c = unit_get_exec_context(u);
169 if (!c)
170 return -EINVAL;
171
172 if (u->manager->running_as == MANAGER_SYSTEM) {
173
174 /* We cannot use NSS from PID 1, hence try to make the
175 * best of it in that case, and fail if we can't help
176 * it */
177
178 if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
179 printed = strdup(specifier == 'u' ? "root" : "0");
180 else {
181 if (specifier == 'u')
182 printed = strdup(c->user);
183 else {
184 uid_t uid;
185
186 r = parse_uid(c->user, &uid);
187 if (r < 0)
188 return -ENODATA;
189
190 r = asprintf(&printed, UID_FMT, uid);
191 }
192 }
193
194 } else {
195 _cleanup_free_ char *tmp = NULL;
196 const char *username = NULL;
197 uid_t uid;
198
199 if (c->user)
200 username = c->user;
201 else
202 /* get USER env from env or our own uid */
203 username = tmp = getusername_malloc();
204
205 /* fish username from passwd */
206 r = get_user_creds(&username, &uid, NULL, NULL, NULL);
207 if (r < 0)
208 return r;
209
210 if (specifier == 'u')
211 printed = strdup(username);
212 else
213 r = asprintf(&printed, UID_FMT, uid);
214 }
215
216 if (r < 0 || !printed)
217 return -ENOMEM;
218
219 *ret = printed;
220 return 0;
221 }
222
223 static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
224 Unit *u = userdata;
225 ExecContext *c;
226 char *n;
227 int r;
228
229 assert(u);
230
231 c = unit_get_exec_context(u);
232 if (!c)
233 return -EOPNOTSUPP;
234
235 if (u->manager->running_as == MANAGER_SYSTEM) {
236
237 /* We cannot use NSS from PID 1, hence try to make the
238 * best of it if we can, but fail if we can't */
239
240 if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
241 n = strdup("/root");
242 else
243 return -EOPNOTSUPP;
244
245 } else {
246
247 /* return HOME if set, otherwise from passwd */
248 if (!c || !c->user) {
249 r = get_home_dir(&n);
250 if (r < 0)
251 return r;
252 } else {
253 const char *username, *home;
254
255 username = c->user;
256 r = get_user_creds(&username, NULL, NULL, &home, NULL);
257 if (r < 0)
258 return r;
259
260 n = strdup(home);
261 }
262 }
263
264 if (!n)
265 return -ENOMEM;
266
267 *ret = n;
268 return 0;
269 }
270
271 static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
272 Unit *u = userdata;
273 ExecContext *c;
274 char *n;
275 int r;
276
277 assert(u);
278
279 c = unit_get_exec_context(u);
280 if (!c)
281 return -EOPNOTSUPP;
282
283 if (u->manager->running_as == MANAGER_SYSTEM) {
284
285 /* We cannot use NSS from PID 1, hence try to make the
286 * best of it if we can, but fail if we can't */
287
288 if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
289 n = strdup("/bin/sh");
290 else
291 return -EOPNOTSUPP;
292
293 } else {
294
295 /* return /bin/sh for root, otherwise the value from passwd */
296 if (!c->user) {
297 r = get_shell(&n);
298 if (r < 0)
299 return r;
300 } else {
301 const char *username, *shell;
302
303 username = c->user;
304 r = get_user_creds(&username, NULL, NULL, NULL, &shell);
305 if (r < 0)
306 return r;
307
308 n = strdup(shell);
309 }
310 }
311
312 if (!n)
313 return -ENOMEM;
314
315 *ret = n;
316 return 0;
317 }
318
319 int unit_name_printf(Unit *u, const char* format, char **ret) {
320
321 /*
322 * This will use the passed string as format string and
323 * replace the following specifiers:
324 *
325 * %n: the full id of the unit (foo@bar.waldo)
326 * %N: the id of the unit without the suffix (foo@bar)
327 * %p: the prefix (foo)
328 * %i: the instance (bar)
329 */
330
331 const Specifier table[] = {
332 { 'n', specifier_string, u->id },
333 { 'N', specifier_prefix_and_instance, NULL },
334 { 'p', specifier_prefix, NULL },
335 { 'i', specifier_string, u->instance },
336 { 0, NULL, NULL }
337 };
338
339 assert(u);
340 assert(format);
341 assert(ret);
342
343 return specifier_printf(format, table, u, ret);
344 }
345
346 int unit_full_printf(Unit *u, const char *format, char **ret) {
347
348 /* This is similar to unit_name_printf() but also supports
349 * unescaping. Also, adds a couple of additional codes:
350 *
351 * %f the instance if set, otherwise the id
352 * %c cgroup path of unit
353 * %r where units in this slice are placed in the cgroup tree
354 * %R the root of this systemd's instance tree
355 * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
356 * %U the UID of the configured user or running user
357 * %u the username of the configured user or running user
358 * %h the homedir of the configured user or running user
359 * %s the shell of the configured user or running user
360 * %m the machine ID of the running system
361 * %H the host name of the running system
362 * %b the boot ID of the running system
363 * %v `uname -r` of the running system
364 */
365
366 const Specifier table[] = {
367 { 'n', specifier_string, u->id },
368 { 'N', specifier_prefix_and_instance, NULL },
369 { 'p', specifier_prefix, NULL },
370 { 'P', specifier_prefix_unescaped, NULL },
371 { 'i', specifier_string, u->instance },
372 { 'I', specifier_instance_unescaped, NULL },
373
374 { 'f', specifier_filename, NULL },
375 { 'c', specifier_cgroup, NULL },
376 { 'r', specifier_cgroup_slice, NULL },
377 { 'R', specifier_cgroup_root, NULL },
378 { 't', specifier_runtime, NULL },
379 { 'U', specifier_user_name, NULL },
380 { 'u', specifier_user_name, NULL },
381 { 'h', specifier_user_home, NULL },
382 { 's', specifier_user_shell, NULL },
383
384 { 'm', specifier_machine_id, NULL },
385 { 'H', specifier_host_name, NULL },
386 { 'b', specifier_boot_id, NULL },
387 { 'v', specifier_kernel_release, NULL },
388 {}
389 };
390
391 assert(u);
392 assert(format);
393 assert(ret);
394
395 return specifier_printf(format, table, u, ret);
396 }
397
398 int unit_full_printf_strv(Unit *u, char **l, char ***ret) {
399 size_t n;
400 char **r, **i, **j;
401 int q;
402
403 /* Applies unit_full_printf to every entry in l */
404
405 assert(u);
406
407 n = strv_length(l);
408 r = new(char*, n+1);
409 if (!r)
410 return -ENOMEM;
411
412 for (i = l, j = r; *i; i++, j++) {
413 q = unit_full_printf(u, *i, j);
414 if (q < 0)
415 goto fail;
416 }
417
418 *j = NULL;
419 *ret = r;
420 return 0;
421
422 fail:
423 for (j--; j >= r; j--)
424 free(*j);
425
426 free(r);
427 return q;
428 }