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