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