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