]>
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" |
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 | 31 | int 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 | 106 | int 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 | 117 | int 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 | 134 | int 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 | 151 | int 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 | 162 | int 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 | 179 | int 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 | 190 | int 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 | 197 | int 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 | 215 | int 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 | 223 | int 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 | 231 | int 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 | 239 | int 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 | 256 | int 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 |
273 | int 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 | } |