]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/specifier.c
Merge pull request #13365 from keszybz/fix-commits-from-pr-13246
[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>
9e2f7c11 7#include <string.h>
6aaa8c2f 8#include <sys/utsname.h>
9e2f7c11 9
a8fbdf54
TA
10#include "sd-id128.h"
11
b5efdb8a 12#include "alloc-util.h"
ca78ad1d 13#include "format-util.h"
b294e594 14#include "fs-util.h"
07630cea 15#include "hostname-util.h"
9e2f7c11 16#include "macro.h"
b5efdb8a 17#include "specifier.h"
07630cea 18#include "string-util.h"
e82f30d1 19#include "strv.h"
36444d22 20#include "user-util.h"
9e2f7c11
LP
21
22/*
23 * Generic infrastructure for replacing %x style specifiers in
24 * strings. Will call a callback for each replacement.
9e2f7c11
LP
25 */
26
751223fe
ZJS
27/* Any ASCII character or digit: our pool of potential specifiers,
28 * and "%" used for escaping. */
29#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
30
303ee601 31int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) {
e2093454 32 size_t l, allocated = 0;
8208c8f2
ZJS
33 _cleanup_free_ char *ret = NULL;
34 char *t;
9e2f7c11
LP
35 const char *f;
36 bool percent = false;
19f6d710 37 int r;
9e2f7c11
LP
38
39 assert(text);
40 assert(table);
41
42 l = strlen(text);
e2093454 43 if (!GREEDY_REALLOC(ret, allocated, l + 1))
19f6d710 44 return -ENOMEM;
19f6d710 45 t = ret;
9e2f7c11 46
e2093454 47 for (f = text; *f; f++, l--)
9e2f7c11
LP
48 if (percent) {
49 if (*f == '%')
50 *(t++) = '%';
51 else {
52 const Specifier *i;
53
54 for (i = table; i->specifier; i++)
55 if (i->specifier == *f)
56 break;
57
58 if (i->lookup) {
19f6d710 59 _cleanup_free_ char *w = NULL;
9e2f7c11
LP
60 size_t k, j;
61
19f6d710 62 r = i->lookup(i->specifier, i->data, userdata, &w);
8208c8f2 63 if (r < 0)
19f6d710 64 return r;
9e2f7c11 65
19f6d710 66 j = t - ret;
9e2f7c11
LP
67 k = strlen(w);
68
e2093454 69 if (!GREEDY_REALLOC(ret, allocated, j + k + l + 1))
19f6d710 70 return -ENOMEM;
e2093454 71 memcpy(ret + j, w, k);
8208c8f2
ZJS
72 t = ret + j + k;
73 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
751223fe
ZJS
74 /* Oops, an unknown specifier. */
75 return -EBADSLT;
8208c8f2 76 else {
9e2f7c11
LP
77 *(t++) = '%';
78 *(t++) = *f;
79 }
80 }
81
82 percent = false;
83 } else if (*f == '%')
84 percent = true;
85 else
86 *(t++) = *f;
9e2f7c11 87
bec8a68c 88 /* If string ended with a stray %, also end with % */
038492ae
FS
89 if (percent)
90 *(t++) = '%';
bec8a68c
ZJS
91 *(t++) = 0;
92
93 /* Try to deallocate unused bytes, but don't sweat it too much */
94 if ((size_t)(t - ret) < allocated) {
95 t = realloc(ret, t - ret);
96 if (t)
97 ret = t;
98 }
038492ae 99
ae2a15bc 100 *_ret = TAKE_PTR(ret);
19f6d710 101 return 0;
9e2f7c11
LP
102}
103
104/* Generic handler for simple string replacements */
105
303ee601 106int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
19f6d710
LP
107 char *n;
108
109 n = strdup(strempty(data));
110 if (!n)
111 return -ENOMEM;
112
113 *ret = n;
114 return 0;
9e2f7c11 115}
d848b9cb 116
303ee601 117int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) {
d848b9cb 118 sd_id128_t id;
19f6d710 119 char *n;
d848b9cb
ZJS
120 int r;
121
122 r = sd_id128_get_machine(&id);
123 if (r < 0)
19f6d710 124 return r;
d848b9cb 125
19f6d710
LP
126 n = new(char, 33);
127 if (!n)
128 return -ENOMEM;
d848b9cb 129
19f6d710
LP
130 *ret = sd_id128_to_string(id, n);
131 return 0;
d848b9cb
ZJS
132}
133
303ee601 134int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) {
d848b9cb 135 sd_id128_t id;
19f6d710 136 char *n;
d848b9cb
ZJS
137 int r;
138
139 r = sd_id128_get_boot(&id);
140 if (r < 0)
19f6d710 141 return r;
d848b9cb 142
19f6d710
LP
143 n = new(char, 33);
144 if (!n)
145 return -ENOMEM;
d848b9cb 146
19f6d710
LP
147 *ret = sd_id128_to_string(id, n);
148 return 0;
d848b9cb
ZJS
149}
150
303ee601 151int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
19f6d710
LP
152 char *n;
153
154 n = gethostname_malloc();
155 if (!n)
156 return -ENOMEM;
157
158 *ret = n;
159 return 0;
d848b9cb 160}
6aaa8c2f 161
303ee601 162int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
6aaa8c2f 163 struct utsname uts;
19f6d710 164 char *n;
6aaa8c2f
ZJS
165 int r;
166
167 r = uname(&uts);
168 if (r < 0)
19f6d710
LP
169 return -errno;
170
171 n = strdup(uts.release);
172 if (!n)
173 return -ENOMEM;
6aaa8c2f 174
19f6d710
LP
175 *ret = n;
176 return 0;
6aaa8c2f 177}
e82f30d1 178
303ee601 179int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
b75f0c69
DC
180 char *t;
181
182 t = gid_to_name(getgid());
183 if (!t)
184 return -ENOMEM;
185
186 *ret = t;
187 return 0;
188}
189
303ee601 190int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
b75f0c69
DC
191 if (asprintf(ret, UID_FMT, getgid()) < 0)
192 return -ENOMEM;
193
194 return 0;
195}
196
303ee601 197int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
198 char *t;
199
200 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
201 * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
202
c987fefc 203 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
36444d22
LP
204 * specifer_user_id() below.
205 */
206
207 t = uid_to_name(getuid());
208 if (!t)
209 return -ENOMEM;
210
211 *ret = t;
212 return 0;
213}
214
303ee601 215int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
216
217 if (asprintf(ret, UID_FMT, getuid()) < 0)
218 return -ENOMEM;
219
220 return 0;
221}
222
303ee601 223int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
224
225 /* On PID 1 (which runs as root) this will not result in NSS,
226 * which is good. See above */
227
228 return get_home_dir(ret);
229}
230
303ee601 231int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) {
36444d22
LP
232
233 /* On PID 1 (which runs as root) this will not result in NSS,
234 * which is good. See above */
235
236 return get_shell(ret);
237}
238
303ee601 239int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
b294e594
LP
240 const char *p;
241 char *copy;
242 int r;
243
244 r = tmp_dir(&p);
245 if (r < 0)
246 return r;
247
248 copy = strdup(p);
249 if (!copy)
250 return -ENOMEM;
251
252 *ret = copy;
253 return 0;
254}
255
303ee601 256int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
b294e594
LP
257 const char *p;
258 char *copy;
259 int r;
260
261 r = var_tmp_dir(&p);
262 if (r < 0)
263 return r;
264
265 copy = strdup(p);
266 if (!copy)
267 return -ENOMEM;
268
269 *ret = copy;
270 return 0;
271}
272
e82f30d1
LP
273int specifier_escape_strv(char **l, char ***ret) {
274 char **z, **p, **q;
275
276 assert(ret);
277
278 if (strv_isempty(l)) {
279 *ret = NULL;
280 return 0;
281 }
282
283 z = new(char*, strv_length(l)+1);
284 if (!z)
285 return -ENOMEM;
286
287 for (p = l, q = z; *p; p++, q++) {
288
289 *q = specifier_escape(*p);
290 if (!*q) {
291 strv_free(z);
292 return -ENOMEM;
293 }
294 }
295
296 *q = NULL;
297 *ret = z;
298
299 return 0;
300}