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