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