]>
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> | |
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" |
b294e594 | 13 | #include "fs-util.h" |
07630cea | 14 | #include "hostname-util.h" |
9e2f7c11 | 15 | #include "macro.h" |
b5efdb8a | 16 | #include "specifier.h" |
07630cea | 17 | #include "string-util.h" |
e82f30d1 | 18 | #include "strv.h" |
36444d22 | 19 | #include "user-util.h" |
9e2f7c11 LP |
20 | |
21 | /* | |
22 | * Generic infrastructure for replacing %x style specifiers in | |
23 | * strings. Will call a callback for each replacement. | |
9e2f7c11 LP |
24 | */ |
25 | ||
751223fe ZJS |
26 | /* Any ASCII character or digit: our pool of potential specifiers, |
27 | * and "%" used for escaping. */ | |
28 | #define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%" | |
29 | ||
303ee601 | 30 | int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) { |
e2093454 | 31 | size_t l, allocated = 0; |
8208c8f2 ZJS |
32 | _cleanup_free_ char *ret = NULL; |
33 | char *t; | |
9e2f7c11 LP |
34 | const char *f; |
35 | bool percent = false; | |
19f6d710 | 36 | int r; |
9e2f7c11 LP |
37 | |
38 | assert(text); | |
39 | assert(table); | |
40 | ||
41 | l = strlen(text); | |
e2093454 | 42 | if (!GREEDY_REALLOC(ret, allocated, l + 1)) |
19f6d710 | 43 | return -ENOMEM; |
19f6d710 | 44 | t = ret; |
9e2f7c11 | 45 | |
e2093454 | 46 | for (f = text; *f; f++, l--) |
9e2f7c11 LP |
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) { | |
19f6d710 | 58 | _cleanup_free_ char *w = NULL; |
9e2f7c11 LP |
59 | size_t k, j; |
60 | ||
19f6d710 | 61 | r = i->lookup(i->specifier, i->data, userdata, &w); |
8208c8f2 | 62 | if (r < 0) |
19f6d710 | 63 | return r; |
9e2f7c11 | 64 | |
19f6d710 | 65 | j = t - ret; |
9e2f7c11 LP |
66 | k = strlen(w); |
67 | ||
e2093454 | 68 | if (!GREEDY_REALLOC(ret, allocated, j + k + l + 1)) |
19f6d710 | 69 | return -ENOMEM; |
e2093454 | 70 | memcpy(ret + j, w, k); |
8208c8f2 ZJS |
71 | t = ret + j + k; |
72 | } else if (strchr(POSSIBLE_SPECIFIERS, *f)) | |
751223fe ZJS |
73 | /* Oops, an unknown specifier. */ |
74 | return -EBADSLT; | |
8208c8f2 | 75 | else { |
9e2f7c11 LP |
76 | *(t++) = '%'; |
77 | *(t++) = *f; | |
78 | } | |
79 | } | |
80 | ||
81 | percent = false; | |
82 | } else if (*f == '%') | |
83 | percent = true; | |
84 | else | |
85 | *(t++) = *f; | |
9e2f7c11 | 86 | |
bec8a68c | 87 | /* If string ended with a stray %, also end with % */ |
038492ae FS |
88 | if (percent) |
89 | *(t++) = '%'; | |
bec8a68c ZJS |
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 | } | |
038492ae | 98 | |
ae2a15bc | 99 | *_ret = TAKE_PTR(ret); |
19f6d710 | 100 | return 0; |
9e2f7c11 LP |
101 | } |
102 | ||
103 | /* Generic handler for simple string replacements */ | |
104 | ||
303ee601 | 105 | int specifier_string(char specifier, const void *data, const void *userdata, char **ret) { |
19f6d710 LP |
106 | char *n; |
107 | ||
108 | n = strdup(strempty(data)); | |
109 | if (!n) | |
110 | return -ENOMEM; | |
111 | ||
112 | *ret = n; | |
113 | return 0; | |
9e2f7c11 | 114 | } |
d848b9cb | 115 | |
303ee601 | 116 | int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) { |
d848b9cb | 117 | sd_id128_t id; |
19f6d710 | 118 | char *n; |
d848b9cb ZJS |
119 | int r; |
120 | ||
121 | r = sd_id128_get_machine(&id); | |
122 | if (r < 0) | |
19f6d710 | 123 | return r; |
d848b9cb | 124 | |
19f6d710 LP |
125 | n = new(char, 33); |
126 | if (!n) | |
127 | return -ENOMEM; | |
d848b9cb | 128 | |
19f6d710 LP |
129 | *ret = sd_id128_to_string(id, n); |
130 | return 0; | |
d848b9cb ZJS |
131 | } |
132 | ||
303ee601 | 133 | int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) { |
d848b9cb | 134 | sd_id128_t id; |
19f6d710 | 135 | char *n; |
d848b9cb ZJS |
136 | int r; |
137 | ||
138 | r = sd_id128_get_boot(&id); | |
139 | if (r < 0) | |
19f6d710 | 140 | return r; |
d848b9cb | 141 | |
19f6d710 LP |
142 | n = new(char, 33); |
143 | if (!n) | |
144 | return -ENOMEM; | |
d848b9cb | 145 | |
19f6d710 LP |
146 | *ret = sd_id128_to_string(id, n); |
147 | return 0; | |
d848b9cb ZJS |
148 | } |
149 | ||
303ee601 | 150 | int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) { |
19f6d710 LP |
151 | char *n; |
152 | ||
153 | n = gethostname_malloc(); | |
154 | if (!n) | |
155 | return -ENOMEM; | |
156 | ||
157 | *ret = n; | |
158 | return 0; | |
d848b9cb | 159 | } |
6aaa8c2f | 160 | |
303ee601 | 161 | int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) { |
6aaa8c2f | 162 | struct utsname uts; |
19f6d710 | 163 | char *n; |
6aaa8c2f ZJS |
164 | int r; |
165 | ||
166 | r = uname(&uts); | |
167 | if (r < 0) | |
19f6d710 LP |
168 | return -errno; |
169 | ||
170 | n = strdup(uts.release); | |
171 | if (!n) | |
172 | return -ENOMEM; | |
6aaa8c2f | 173 | |
19f6d710 LP |
174 | *ret = n; |
175 | return 0; | |
6aaa8c2f | 176 | } |
e82f30d1 | 177 | |
303ee601 | 178 | int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) { |
b75f0c69 DC |
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 | ||
303ee601 | 189 | int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) { |
b75f0c69 DC |
190 | if (asprintf(ret, UID_FMT, getgid()) < 0) |
191 | return -ENOMEM; | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
303ee601 | 196 | int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) { |
36444d22 LP |
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 | ||
c987fefc | 202 | * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with |
36444d22 LP |
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 | ||
303ee601 | 214 | int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) { |
36444d22 LP |
215 | |
216 | if (asprintf(ret, UID_FMT, getuid()) < 0) | |
217 | return -ENOMEM; | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
303ee601 | 222 | int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) { |
36444d22 LP |
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 | ||
303ee601 | 230 | int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) { |
36444d22 LP |
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 | ||
303ee601 | 238 | int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) { |
b294e594 LP |
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 | ||
303ee601 | 255 | int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) { |
b294e594 LP |
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 | ||
e82f30d1 LP |
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 | } |