]>
Commit | Line | Data |
---|---|---|
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 | 32 | int 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 | 107 | int 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 | 118 | int 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 | 135 | int 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 | 152 | int 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 |
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 | ||
303ee601 | 174 | int 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 |
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 | ||
303ee601 | 237 | int 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 | 248 | int 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 | 255 | int 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 | 273 | int 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 | 281 | int 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 | 289 | int 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 | 297 | int 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 | 314 | int 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 |
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 | } |