]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/specifier.c
basic: spit out chase_symlinks() from fs-util.[ch] → chase-symlinks.[ch]
[thirdparty/systemd.git] / src / shared / specifier.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
9e2f7c11 2
a8fbdf54
TA
3#include <errno.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdlib.h>
6aaa8c2f 7#include <sys/utsname.h>
9e2f7c11 8
a8fbdf54
TA
9#include "sd-id128.h"
10
b5efdb8a 11#include "alloc-util.h"
268f5a54 12#include "architecture.h"
f4351959 13#include "chase-symlinks.h"
de61a04b 14#include "fd-util.h"
ca78ad1d 15#include "format-util.h"
b294e594 16#include "fs-util.h"
07630cea 17#include "hostname-util.h"
de61a04b 18#include "id128-util.h"
9e2f7c11 19#include "macro.h"
268f5a54 20#include "os-util.h"
b5efdb8a 21#include "specifier.h"
07630cea 22#include "string-util.h"
e82f30d1 23#include "strv.h"
36444d22 24#include "user-util.h"
9e2f7c11
LP
25
26/*
27 * Generic infrastructure for replacing %x style specifiers in
28 * strings. Will call a callback for each replacement.
9e2f7c11
LP
29 */
30
751223fe
ZJS
31/* Any ASCII character or digit: our pool of potential specifiers,
32 * and "%" used for escaping. */
33#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
34
de61a04b 35int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) {
695c5fee 36 _cleanup_free_ char *result = NULL;
9e2f7c11 37 bool percent = false;
319a4f4b
LP
38 const char *f;
39 size_t l;
40 char *t;
19f6d710 41 int r;
9e2f7c11
LP
42
43 assert(text);
44 assert(table);
45
46 l = strlen(text);
319a4f4b 47 if (!GREEDY_REALLOC(result, l + 1))
19f6d710 48 return -ENOMEM;
695c5fee 49 t = result;
9e2f7c11 50
06536492 51 for (f = text; *f != '\0'; f++, l--) {
9e2f7c11
LP
52 if (percent) {
53 if (*f == '%')
54 *(t++) = '%';
55 else {
56 const Specifier *i;
57
58 for (i = table; i->specifier; i++)
59 if (i->specifier == *f)
60 break;
61
62 if (i->lookup) {
19f6d710 63 _cleanup_free_ char *w = NULL;
9e2f7c11
LP
64 size_t k, j;
65
de61a04b 66 r = i->lookup(i->specifier, i->data, root, userdata, &w);
8208c8f2 67 if (r < 0)
19f6d710 68 return r;
9e2f7c11 69
695c5fee 70 j = t - result;
9e2f7c11
LP
71 k = strlen(w);
72
319a4f4b 73 if (!GREEDY_REALLOC(result, j + k + l + 1))
19f6d710 74 return -ENOMEM;
695c5fee
YW
75 memcpy(result + j, w, k);
76 t = result + j + k;
8208c8f2 77 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
751223fe
ZJS
78 /* Oops, an unknown specifier. */
79 return -EBADSLT;
8208c8f2 80 else {
9e2f7c11
LP
81 *(t++) = '%';
82 *(t++) = *f;
83 }
84 }
85
86 percent = false;
87 } else if (*f == '%')
88 percent = true;
89 else
90 *(t++) = *f;
9e2f7c11 91
06536492
YW
92 if ((size_t) (t - result) > max_length)
93 return -ENAMETOOLONG;
94 }
95
bec8a68c 96 /* If string ended with a stray %, also end with % */
06536492 97 if (percent) {
038492ae 98 *(t++) = '%';
06536492
YW
99 if ((size_t) (t - result) > max_length)
100 return -ENAMETOOLONG;
101 }
bec8a68c
ZJS
102 *(t++) = 0;
103
695c5fee 104 *ret = TAKE_PTR(result);
19f6d710 105 return 0;
9e2f7c11
LP
106}
107
108/* Generic handler for simple string replacements */
109
de61a04b 110int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
19f6d710
LP
111 char *n;
112
113 n = strdup(strempty(data));
114 if (!n)
115 return -ENOMEM;
116
117 *ret = n;
118 return 0;
9e2f7c11 119}
d848b9cb 120
de61a04b 121int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
d848b9cb 122 sd_id128_t id;
19f6d710 123 char *n;
d848b9cb
ZJS
124 int r;
125
de61a04b
LP
126 if (root) {
127 _cleanup_close_ int fd = -1;
128
129 fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
130 if (fd < 0)
131 return fd;
132
133 r = id128_read_fd(fd, ID128_PLAIN, &id);
134 } else
135 r = sd_id128_get_machine(&id);
d848b9cb 136 if (r < 0)
19f6d710 137 return r;
d848b9cb 138
56709784 139 n = new(char, SD_ID128_STRING_MAX);
19f6d710
LP
140 if (!n)
141 return -ENOMEM;
d848b9cb 142
19f6d710
LP
143 *ret = sd_id128_to_string(id, n);
144 return 0;
d848b9cb
ZJS
145}
146
de61a04b 147int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
d848b9cb 148 sd_id128_t id;
19f6d710 149 char *n;
d848b9cb
ZJS
150 int r;
151
152 r = sd_id128_get_boot(&id);
153 if (r < 0)
19f6d710 154 return r;
d848b9cb 155
56709784 156 n = new(char, SD_ID128_STRING_MAX);
19f6d710
LP
157 if (!n)
158 return -ENOMEM;
d848b9cb 159
19f6d710
LP
160 *ret = sd_id128_to_string(id, n);
161 return 0;
d848b9cb
ZJS
162}
163
de61a04b 164int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
19f6d710
LP
165 char *n;
166
167 n = gethostname_malloc();
168 if (!n)
169 return -ENOMEM;
170
171 *ret = n;
172 return 0;
d848b9cb 173}
6aaa8c2f 174
de61a04b 175int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
e97708fa
ZJS
176 char *n;
177
178 n = gethostname_short_malloc();
179 if (!n)
180 return -ENOMEM;
181
182 *ret = n;
183 return 0;
184}
185
de61a04b 186int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6aaa8c2f 187 struct utsname uts;
19f6d710 188 char *n;
6aaa8c2f
ZJS
189 int r;
190
191 r = uname(&uts);
192 if (r < 0)
19f6d710
LP
193 return -errno;
194
195 n = strdup(uts.release);
196 if (!n)
197 return -ENOMEM;
6aaa8c2f 198
19f6d710
LP
199 *ret = n;
200 return 0;
6aaa8c2f 201}
e82f30d1 202
de61a04b 203int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
268f5a54
LP
204 char *t;
205
206 t = strdup(architecture_to_string(uname_architecture()));
207 if (!t)
208 return -ENOMEM;
209
210 *ret = t;
211 return 0;
212}
213
de61a04b 214static int specifier_os_release_common(const char *field, const char *root, char **ret) {
268f5a54
LP
215 char *t = NULL;
216 int r;
217
de61a04b 218 r = parse_os_release(root, field, &t);
268f5a54
LP
219 if (r < 0)
220 return r;
221 if (!t) {
222 /* fields in /etc/os-release might quite possibly be missing, even if everything is entirely
223 * valid otherwise. Let's hence return "" in that case. */
224 t = strdup("");
225 if (!t)
226 return -ENOMEM;
227 }
228
229 *ret = t;
230 return 0;
231}
232
de61a04b
LP
233int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
234 return specifier_os_release_common("ID", root, ret);
268f5a54
LP
235}
236
de61a04b
LP
237int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
238 return specifier_os_release_common("VERSION_ID", root, ret);
268f5a54
LP
239}
240
de61a04b
LP
241int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
242 return specifier_os_release_common("BUILD_ID", root, ret);
268f5a54
LP
243}
244
de61a04b
LP
245int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
246 return specifier_os_release_common("VARIANT_ID", root, ret);
268f5a54
LP
247}
248
de61a04b
LP
249int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
250 return specifier_os_release_common("IMAGE_ID", root, ret);
9a515f0a
LP
251}
252
de61a04b
LP
253int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
254 return specifier_os_release_common("IMAGE_VERSION", root, ret);
9a515f0a
LP
255}
256
de61a04b 257int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b75f0c69
DC
258 char *t;
259
260 t = gid_to_name(getgid());
261 if (!t)
262 return -ENOMEM;
263
264 *ret = t;
265 return 0;
266}
267
de61a04b 268int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b75f0c69
DC
269 if (asprintf(ret, UID_FMT, getgid()) < 0)
270 return -ENOMEM;
271
272 return 0;
273}
274
de61a04b 275int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
36444d22
LP
276 char *t;
277
278 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
279 * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
280
c987fefc 281 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
36444d22
LP
282 * specifer_user_id() below.
283 */
284
285 t = uid_to_name(getuid());
286 if (!t)
287 return -ENOMEM;
288
289 *ret = t;
290 return 0;
291}
292
de61a04b 293int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
36444d22
LP
294
295 if (asprintf(ret, UID_FMT, getuid()) < 0)
296 return -ENOMEM;
297
298 return 0;
299}
300
de61a04b 301int specifier_user_home(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
36444d22
LP
302
303 /* On PID 1 (which runs as root) this will not result in NSS,
304 * which is good. See above */
305
306 return get_home_dir(ret);
307}
308
de61a04b 309int specifier_user_shell(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
36444d22
LP
310
311 /* On PID 1 (which runs as root) this will not result in NSS,
312 * which is good. See above */
313
314 return get_shell(ret);
315}
316
de61a04b 317int specifier_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b294e594
LP
318 const char *p;
319 char *copy;
320 int r;
321
de61a04b
LP
322 if (root) /* If root dir is set, don't honour $TMP or similar */
323 p = "/tmp";
324 else {
325 r = tmp_dir(&p);
326 if (r < 0)
327 return r;
328 }
b294e594
LP
329 copy = strdup(p);
330 if (!copy)
331 return -ENOMEM;
332
333 *ret = copy;
334 return 0;
335}
336
de61a04b 337int specifier_var_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b294e594
LP
338 const char *p;
339 char *copy;
340 int r;
341
de61a04b
LP
342 if (root)
343 p = "/var/tmp";
344 else {
345 r = var_tmp_dir(&p);
346 if (r < 0)
347 return r;
348 }
b294e594
LP
349 copy = strdup(p);
350 if (!copy)
351 return -ENOMEM;
352
353 *ret = copy;
354 return 0;
355}
356
e82f30d1
LP
357int specifier_escape_strv(char **l, char ***ret) {
358 char **z, **p, **q;
359
360 assert(ret);
361
362 if (strv_isempty(l)) {
363 *ret = NULL;
364 return 0;
365 }
366
367 z = new(char*, strv_length(l)+1);
368 if (!z)
369 return -ENOMEM;
370
371 for (p = l, q = z; *p; p++, q++) {
372
373 *q = specifier_escape(*p);
374 if (!*q) {
375 strv_free(z);
376 return -ENOMEM;
377 }
378 }
379
380 *q = NULL;
381 *ret = z;
382
383 return 0;
384}
2caed041
LP
385
386const Specifier system_and_tmp_specifier_table[] = {
387 COMMON_SYSTEM_SPECIFIERS,
388 COMMON_TMP_SPECIFIERS,
389 {}
390};