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