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