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