chiark / gitweb /
7736899a35ae9f0b8e49c232233ea98534d6dd2d
[elogind.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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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 -ENOTSUP;
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 }