]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/specifier.c
Add %l as specifier for the hostname without any domain component
[thirdparty/systemd.git] / src / shared / specifier.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
ca78ad1d 13#include "format-util.h"
b294e594 14#include "fs-util.h"
07630cea 15#include "hostname-util.h"
9e2f7c11 16#include "macro.h"
268f5a54 17#include "os-util.h"
b5efdb8a 18#include "specifier.h"
07630cea 19#include "string-util.h"
e82f30d1 20#include "strv.h"
36444d22 21#include "user-util.h"
9e2f7c11
LP
22
23/*
24 * Generic infrastructure for replacing %x style specifiers in
25 * strings. Will call a callback for each replacement.
9e2f7c11
LP
26 */
27
751223fe
ZJS
28/* Any ASCII character or digit: our pool of potential specifiers,
29 * and "%" used for escaping. */
30#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
31
303ee601 32int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) {
e2093454 33 size_t l, allocated = 0;
8208c8f2
ZJS
34 _cleanup_free_ char *ret = NULL;
35 char *t;
9e2f7c11
LP
36 const char *f;
37 bool percent = false;
19f6d710 38 int r;
9e2f7c11
LP
39
40 assert(text);
41 assert(table);
42
43 l = strlen(text);
e2093454 44 if (!GREEDY_REALLOC(ret, allocated, l + 1))
19f6d710 45 return -ENOMEM;
19f6d710 46 t = ret;
9e2f7c11 47
e2093454 48 for (f = text; *f; f++, l--)
9e2f7c11
LP
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) {
19f6d710 60 _cleanup_free_ char *w = NULL;
9e2f7c11
LP
61 size_t k, j;
62
19f6d710 63 r = i->lookup(i->specifier, i->data, userdata, &w);
8208c8f2 64 if (r < 0)
19f6d710 65 return r;
9e2f7c11 66
19f6d710 67 j = t - ret;
9e2f7c11
LP
68 k = strlen(w);
69
e2093454 70 if (!GREEDY_REALLOC(ret, allocated, j + k + l + 1))
19f6d710 71 return -ENOMEM;
e2093454 72 memcpy(ret + j, w, k);
8208c8f2
ZJS
73 t = ret + j + k;
74 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
751223fe
ZJS
75 /* Oops, an unknown specifier. */
76 return -EBADSLT;
8208c8f2 77 else {
9e2f7c11
LP
78 *(t++) = '%';
79 *(t++) = *f;
80 }
81 }
82
83 percent = false;
84 } else if (*f == '%')
85 percent = true;
86 else
87 *(t++) = *f;
9e2f7c11 88
bec8a68c 89 /* If string ended with a stray %, also end with % */
038492ae
FS
90 if (percent)
91 *(t++) = '%';
bec8a68c
ZJS
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 }
038492ae 100
ae2a15bc 101 *_ret = TAKE_PTR(ret);
19f6d710 102 return 0;
9e2f7c11
LP
103}
104
105/* Generic handler for simple string replacements */
106
303ee601 107int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
19f6d710
LP
108 char *n;
109
110 n = strdup(strempty(data));
111 if (!n)
112 return -ENOMEM;
113
114 *ret = n;
115 return 0;
9e2f7c11 116}
d848b9cb 117
303ee601 118int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) {
d848b9cb 119 sd_id128_t id;
19f6d710 120 char *n;
d848b9cb
ZJS
121 int r;
122
123 r = sd_id128_get_machine(&id);
124 if (r < 0)
19f6d710 125 return r;
d848b9cb 126
19f6d710
LP
127 n = new(char, 33);
128 if (!n)
129 return -ENOMEM;
d848b9cb 130
19f6d710
LP
131 *ret = sd_id128_to_string(id, n);
132 return 0;
d848b9cb
ZJS
133}
134
303ee601 135int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) {
d848b9cb 136 sd_id128_t id;
19f6d710 137 char *n;
d848b9cb
ZJS
138 int r;
139
140 r = sd_id128_get_boot(&id);
141 if (r < 0)
19f6d710 142 return r;
d848b9cb 143
19f6d710
LP
144 n = new(char, 33);
145 if (!n)
146 return -ENOMEM;
d848b9cb 147
19f6d710
LP
148 *ret = sd_id128_to_string(id, n);
149 return 0;
d848b9cb
ZJS
150}
151
303ee601 152int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
19f6d710
LP
153 char *n;
154
155 n = gethostname_malloc();
156 if (!n)
157 return -ENOMEM;
158
159 *ret = n;
160 return 0;
d848b9cb 161}
6aaa8c2f 162
e97708fa
ZJS
163int 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
303ee601 174int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
6aaa8c2f 175 struct utsname uts;
19f6d710 176 char *n;
6aaa8c2f
ZJS
177 int r;
178
179 r = uname(&uts);
180 if (r < 0)
19f6d710
LP
181 return -errno;
182
183 n = strdup(uts.release);
184 if (!n)
185 return -ENOMEM;
6aaa8c2f 186
19f6d710
LP
187 *ret = n;
188 return 0;
6aaa8c2f 189}
e82f30d1 190
268f5a54
LP
191int 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
202static 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
221int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret) {
222 return specifier_os_release_common("ID", ret);
223}
224
225int 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
229int 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
233int 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
303ee601 237int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
b75f0c69
DC
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
303ee601 248int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
b75f0c69
DC
249 if (asprintf(ret, UID_FMT, getgid()) < 0)
250 return -ENOMEM;
251
252 return 0;
253}
254
303ee601 255int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
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
c987fefc 261 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
36444d22
LP
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
303ee601 273int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
274
275 if (asprintf(ret, UID_FMT, getuid()) < 0)
276 return -ENOMEM;
277
278 return 0;
279}
280
303ee601 281int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
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
303ee601 289int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
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
303ee601 297int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
b294e594
LP
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
303ee601 314int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
b294e594
LP
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
e82f30d1
LP
331int 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}