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 809 : int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) {
32 809 : size_t l, allocated = 0;
33 809 : _cleanup_free_ char *ret = NULL;
34 : char *t;
35 : const char *f;
36 809 : bool percent = false;
37 : int r;
38 :
39 809 : assert(text);
40 809 : assert(table);
41 :
42 809 : l = strlen(text);
43 809 : if (!GREEDY_REALLOC(ret, allocated, l + 1))
44 0 : return -ENOMEM;
45 809 : t = ret;
46 :
47 10932 : for (f = text; *f; f++, l--)
48 10132 : if (percent) {
49 122 : if (*f == '%')
50 4 : *(t++) = '%';
51 : else {
52 : const Specifier *i;
53 :
54 1101 : for (i = table; i->specifier; i++)
55 1092 : if (i->specifier == *f)
56 109 : break;
57 :
58 118 : if (i->lookup) {
59 109 : _cleanup_free_ char *w = NULL;
60 : size_t k, j;
61 :
62 109 : r = i->lookup(i->specifier, i->data, userdata, &w);
63 109 : if (r < 0)
64 1 : return r;
65 :
66 108 : j = t - ret;
67 108 : k = strlen(w);
68 :
69 108 : if (!GREEDY_REALLOC(ret, allocated, j + k + l + 1))
70 0 : return -ENOMEM;
71 108 : memcpy(ret + j, w, k);
72 108 : t = ret + j + k;
73 9 : } else if (strchr(POSSIBLE_SPECIFIERS, *f))
74 : /* Oops, an unknown specifier. */
75 8 : return -EBADSLT;
76 : else {
77 1 : *(t++) = '%';
78 1 : *(t++) = *f;
79 : }
80 : }
81 :
82 113 : percent = false;
83 10010 : } else if (*f == '%')
84 123 : percent = true;
85 : else
86 9887 : *(t++) = *f;
87 :
88 : /* If string ended with a stray %, also end with % */
89 800 : if (percent)
90 1 : *(t++) = '%';
91 800 : *(t++) = 0;
92 :
93 : /* Try to deallocate unused bytes, but don't sweat it too much */
94 800 : if ((size_t)(t - ret) < allocated) {
95 800 : t = realloc(ret, t - ret);
96 800 : if (t)
97 800 : ret = t;
98 : }
99 :
100 800 : *_ret = TAKE_PTR(ret);
101 800 : return 0;
102 : }
103 :
104 : /* Generic handler for simple string replacements */
105 :
106 10 : int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
107 : char *n;
108 :
109 10 : n = strdup(strempty(data));
110 10 : if (!n)
111 0 : return -ENOMEM;
112 :
113 10 : *ret = n;
114 10 : return 0;
115 : }
116 :
117 9 : 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 9 : r = sd_id128_get_machine(&id);
123 9 : if (r < 0)
124 0 : return r;
125 :
126 9 : n = new(char, 33);
127 9 : if (!n)
128 0 : return -ENOMEM;
129 :
130 9 : *ret = sd_id128_to_string(id, n);
131 9 : return 0;
132 : }
133 :
134 9 : 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 9 : r = sd_id128_get_boot(&id);
140 9 : if (r < 0)
141 0 : return r;
142 :
143 9 : n = new(char, 33);
144 9 : if (!n)
145 0 : return -ENOMEM;
146 :
147 9 : *ret = sd_id128_to_string(id, n);
148 9 : return 0;
149 : }
150 :
151 7 : int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
152 : char *n;
153 :
154 7 : n = gethostname_malloc();
155 7 : if (!n)
156 0 : return -ENOMEM;
157 :
158 7 : *ret = n;
159 7 : return 0;
160 : }
161 :
162 7 : 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 7 : r = uname(&uts);
168 7 : if (r < 0)
169 0 : return -errno;
170 :
171 7 : n = strdup(uts.release);
172 7 : if (!n)
173 0 : return -ENOMEM;
174 :
175 7 : *ret = n;
176 7 : return 0;
177 : }
178 :
179 8 : int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
180 : char *t;
181 :
182 8 : t = gid_to_name(getgid());
183 8 : if (!t)
184 0 : return -ENOMEM;
185 :
186 8 : *ret = t;
187 8 : return 0;
188 : }
189 :
190 8 : int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
191 8 : if (asprintf(ret, UID_FMT, getgid()) < 0)
192 0 : return -ENOMEM;
193 :
194 8 : return 0;
195 : }
196 :
197 8 : 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 8 : t = uid_to_name(getuid());
208 8 : if (!t)
209 0 : return -ENOMEM;
210 :
211 8 : *ret = t;
212 8 : return 0;
213 : }
214 :
215 8 : int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
216 :
217 8 : if (asprintf(ret, UID_FMT, getuid()) < 0)
218 0 : return -ENOMEM;
219 :
220 8 : return 0;
221 : }
222 :
223 4 : 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 4 : 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 6 : int specifier_escape_strv(char **l, char ***ret) {
274 : char **z, **p, **q;
275 :
276 6 : assert(ret);
277 :
278 6 : if (strv_isempty(l)) {
279 2 : *ret = NULL;
280 2 : return 0;
281 : }
282 :
283 4 : z = new(char*, strv_length(l)+1);
284 4 : if (!z)
285 0 : return -ENOMEM;
286 :
287 14 : for (p = l, q = z; *p; p++, q++) {
288 :
289 10 : *q = specifier_escape(*p);
290 10 : if (!*q) {
291 0 : strv_free(z);
292 0 : return -ENOMEM;
293 : }
294 : }
295 :
296 4 : *q = NULL;
297 4 : *ret = z;
298 :
299 4 : return 0;
300 : }
|