]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/unit-printf.c
core: add %v specifier
[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 char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
34 Unit *u = userdata;
35 assert(u);
36
37 return unit_name_to_prefix_and_instance(u->id);
38 }
39
40 static char *specifier_prefix(char specifier, void *data, void *userdata) {
41 Unit *u = userdata;
42 assert(u);
43
44 return unit_name_to_prefix(u->id);
45 }
46
47 static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
48 Unit *u = userdata;
49 char *p, *r;
50
51 assert(u);
52
53 p = unit_name_to_prefix(u->id);
54 if (!p)
55 return NULL;
56
57 r = unit_name_unescape(p);
58 free(p);
59
60 return r;
61 }
62
63 static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
64 Unit *u = userdata;
65 assert(u);
66
67 if (u->instance)
68 return unit_name_unescape(u->instance);
69
70 return strdup("");
71 }
72
73 static char *specifier_filename(char specifier, void *data, void *userdata) {
74 Unit *u = userdata;
75 assert(u);
76
77 if (u->instance)
78 return unit_name_path_unescape(u->instance);
79
80 return unit_name_to_path(u->id);
81 }
82
83 static char *specifier_cgroup(char specifier, void *data, void *userdata) {
84 Unit *u = userdata;
85 assert(u);
86
87 return unit_default_cgroup_path(u);
88 }
89
90 static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
91 _cleanup_free_ char *p = NULL;
92 Unit *u = userdata;
93 const char *slice;
94 int r;
95
96 assert(u);
97
98 slice = unit_slice_name(u);
99 if (specifier == 'R' || !slice)
100 return strdup(u->manager->cgroup_root);
101
102 r = cg_slice_to_path(slice, &p);
103 if (r < 0)
104 return NULL;
105
106 return strjoin(u->manager->cgroup_root, "/", p, NULL);
107 }
108
109 static char *specifier_runtime(char specifier, void *data, void *userdata) {
110 Unit *u = userdata;
111 assert(u);
112
113 if (u->manager->running_as == SYSTEMD_USER) {
114 const char *e;
115
116 e = getenv("XDG_RUNTIME_DIR");
117 if (e)
118 return strdup(e);
119 }
120
121 return strdup("/run");
122 }
123
124 static char *specifier_user_name(char specifier, void *data, void *userdata) {
125 Unit *u = userdata;
126 ExecContext *c;
127 int r;
128 const char *username;
129 _cleanup_free_ char *tmp = NULL;
130 uid_t uid;
131 char *printed = NULL;
132
133 assert(u);
134
135 c = unit_get_exec_context(u);
136
137 if (c && c->user)
138 username = c->user;
139 else
140 /* get USER env from env or our own uid */
141 username = tmp = getusername_malloc();
142
143 /* fish username from passwd */
144 r = get_user_creds(&username, &uid, NULL, NULL, NULL);
145 if (r < 0)
146 return NULL;
147
148 switch (specifier) {
149 case 'U':
150 if (asprintf(&printed, "%d", uid) < 0)
151 return NULL;
152 break;
153 case 'u':
154 printed = strdup(username);
155 break;
156 }
157
158 return printed;
159 }
160
161 static char *specifier_user_home(char specifier, void *data, void *userdata) {
162 Unit *u = userdata;
163 ExecContext *c;
164 int r;
165 const char *username, *home;
166
167 assert(u);
168
169 c = unit_get_exec_context(u);
170
171 /* return HOME if set, otherwise from passwd */
172 if (!c || !c->user) {
173 char *h;
174
175 r = get_home_dir(&h);
176 if (r < 0)
177 return NULL;
178
179 return h;
180 }
181
182 username = c->user;
183 r = get_user_creds(&username, NULL, NULL, &home, NULL);
184 if (r < 0)
185 return NULL;
186
187 return strdup(home);
188 }
189
190 static char *specifier_user_shell(char specifier, void *data, void *userdata) {
191 Unit *u = userdata;
192 ExecContext *c;
193 int r;
194 const char *username, *shell;
195 char *ret;
196
197 assert(u);
198
199 c = unit_get_exec_context(u);
200
201 if (c && c->user)
202 username = c->user;
203 else
204 username = "root";
205
206 /* return /bin/sh for root, otherwise the value from passwd */
207 r = get_user_creds(&username, NULL, NULL, NULL, &shell);
208 if (r < 0) {
209 log_warning_unit(u->id,
210 "Failed to determine shell: %s",
211 strerror(-r));
212 return NULL;
213 }
214
215 if (!path_is_absolute(shell)) {
216 log_warning_unit(u->id,
217 "Shell %s is not absolute, ignoring.",
218 shell);
219 }
220
221 ret = strdup(shell);
222 if (!ret)
223 log_oom();
224
225 return ret;
226 }
227
228 char *unit_name_printf(Unit *u, const char* format) {
229
230 /*
231 * This will use the passed string as format string and
232 * replace the following specifiers:
233 *
234 * %n: the full id of the unit (foo@bar.waldo)
235 * %N: the id of the unit without the suffix (foo@bar)
236 * %p: the prefix (foo)
237 * %i: the instance (bar)
238 */
239
240 const Specifier table[] = {
241 { 'n', specifier_string, u->id },
242 { 'N', specifier_prefix_and_instance, NULL },
243 { 'p', specifier_prefix, NULL },
244 { 'i', specifier_string, u->instance },
245 { 0, NULL, NULL }
246 };
247
248 assert(u);
249 assert(format);
250
251 return specifier_printf(format, table, u);
252 }
253
254 char *unit_full_printf(Unit *u, const char *format) {
255
256 /* This is similar to unit_name_printf() but also supports
257 * unescaping. Also, adds a couple of additional codes:
258 *
259 * %f the the instance if set, otherwise the id
260 * %c cgroup path of unit
261 * %r where units in this slice are place in the cgroup tree
262 * %R the root of this systemd's instance tree
263 * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
264 * %U the UID of the configured user or running user
265 * %u the username of the configured user or running user
266 * %h the homedir of the configured user or running user
267 * %s the shell of the configured user or running user
268 * %m the machine ID of the running system
269 * %H the host name of the running system
270 * %b the boot ID of the running system
271 * %v `uname -r` of the running system
272 */
273
274 const Specifier table[] = {
275 { 'n', specifier_string, u->id },
276 { 'N', specifier_prefix_and_instance, NULL },
277 { 'p', specifier_prefix, NULL },
278 { 'P', specifier_prefix_unescaped, NULL },
279 { 'i', specifier_string, u->instance },
280 { 'I', specifier_instance_unescaped, NULL },
281
282 { 'f', specifier_filename, NULL },
283 { 'c', specifier_cgroup, NULL },
284 { 'r', specifier_cgroup_root, NULL },
285 { 'R', specifier_cgroup_root, NULL },
286 { 't', specifier_runtime, NULL },
287 { 'U', specifier_user_name, NULL },
288 { 'u', specifier_user_name, NULL },
289 { 'h', specifier_user_home, NULL },
290 { 's', specifier_user_shell, NULL },
291
292 { 'm', specifier_machine_id, NULL },
293 { 'H', specifier_host_name, NULL },
294 { 'b', specifier_boot_id, NULL },
295 { 'v', specifier_kernel_release, NULL },
296 {}
297 };
298
299 assert(format);
300
301 return specifier_printf(format, table, u);
302 }
303
304 char **unit_full_printf_strv(Unit *u, char **l) {
305 size_t n;
306 char **r, **i, **j;
307
308 /* Applies unit_full_printf to every entry in l */
309
310 assert(u);
311
312 n = strv_length(l);
313 r = new(char*, n+1);
314 if (!r)
315 return NULL;
316
317 for (i = l, j = r; *i; i++, j++) {
318 *j = unit_full_printf(u, *i);
319 if (!*j)
320 goto fail;
321 }
322
323 *j = NULL;
324 return r;
325
326 fail:
327 for (j--; j >= r; j--)
328 free(*j);
329
330 free(r);
331
332 return NULL;
333 }