chiark / gitweb /
core: when the user hits Ctrl-Alt-Del more than 7x per 2s, reboot immediately
[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 "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 = 0;
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                                 r = 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                         r = asprintf(&printed, UID_FMT, uid);
235         }
236
237         if (r < 0 || !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 }