]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/specifier.c
tree-wide: remove Lennart's copyright lines
[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
27 /* Any ASCII character or digit: our pool of potential specifiers,
28 * and "%" used for escaping. */
29 #define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
30
31 int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) {
32 size_t l, allocated = 0;
33 _cleanup_free_ char *ret = NULL;
34 char *t;
35 const char *f;
36 bool percent = false;
37 int r;
38
39 assert(text);
40 assert(table);
41
42 l = strlen(text);
43 if (!GREEDY_REALLOC(ret, allocated, l + 1))
44 return -ENOMEM;
45 t = ret;
46
47 for (f = text; *f; f++, l--)
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) {
59 _cleanup_free_ char *w = NULL;
60 size_t k, j;
61
62 r = i->lookup(i->specifier, i->data, userdata, &w);
63 if (r < 0)
64 return r;
65
66 j = t - ret;
67 k = strlen(w);
68
69 if (!GREEDY_REALLOC(ret, allocated, j + k + l + 1))
70 return -ENOMEM;
71 memcpy(ret + j, w, k);
72 t = ret + j + k;
73 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
74 /* Oops, an unknown specifier. */
75 return -EBADSLT;
76 else {
77 *(t++) = '%';
78 *(t++) = *f;
79 }
80 }
81
82 percent = false;
83 } else if (*f == '%')
84 percent = true;
85 else
86 *(t++) = *f;
87
88 /* If string ended with a stray %, also end with % */
89 if (percent)
90 *(t++) = '%';
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 }
99
100 *_ret = TAKE_PTR(ret);
101 return 0;
102 }
103
104 /* Generic handler for simple string replacements */
105
106 int specifier_string(char specifier, void *data, void *userdata, char **ret) {
107 char *n;
108
109 n = strdup(strempty(data));
110 if (!n)
111 return -ENOMEM;
112
113 *ret = n;
114 return 0;
115 }
116
117 int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) {
118 sd_id128_t id;
119 char *n;
120 int r;
121
122 r = sd_id128_get_machine(&id);
123 if (r < 0)
124 return r;
125
126 n = new(char, 33);
127 if (!n)
128 return -ENOMEM;
129
130 *ret = sd_id128_to_string(id, n);
131 return 0;
132 }
133
134 int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
135 sd_id128_t id;
136 char *n;
137 int r;
138
139 r = sd_id128_get_boot(&id);
140 if (r < 0)
141 return r;
142
143 n = new(char, 33);
144 if (!n)
145 return -ENOMEM;
146
147 *ret = sd_id128_to_string(id, n);
148 return 0;
149 }
150
151 int specifier_host_name(char specifier, void *data, void *userdata, char **ret) {
152 char *n;
153
154 n = gethostname_malloc();
155 if (!n)
156 return -ENOMEM;
157
158 *ret = n;
159 return 0;
160 }
161
162 int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) {
163 struct utsname uts;
164 char *n;
165 int r;
166
167 r = uname(&uts);
168 if (r < 0)
169 return -errno;
170
171 n = strdup(uts.release);
172 if (!n)
173 return -ENOMEM;
174
175 *ret = n;
176 return 0;
177 }
178
179 int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
180 char *t;
181
182 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
183 * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
184
185 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
186 * specifer_user_id() below.
187 */
188
189 t = uid_to_name(getuid());
190 if (!t)
191 return -ENOMEM;
192
193 *ret = t;
194 return 0;
195 }
196
197 int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
198
199 if (asprintf(ret, UID_FMT, getuid()) < 0)
200 return -ENOMEM;
201
202 return 0;
203 }
204
205 int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
206
207 /* On PID 1 (which runs as root) this will not result in NSS,
208 * which is good. See above */
209
210 return get_home_dir(ret);
211 }
212
213 int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
214
215 /* On PID 1 (which runs as root) this will not result in NSS,
216 * which is good. See above */
217
218 return get_shell(ret);
219 }
220
221 int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
222 const char *p;
223 char *copy;
224 int r;
225
226 r = tmp_dir(&p);
227 if (r < 0)
228 return r;
229
230 copy = strdup(p);
231 if (!copy)
232 return -ENOMEM;
233
234 *ret = copy;
235 return 0;
236 }
237
238 int specifier_var_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
239 const char *p;
240 char *copy;
241 int r;
242
243 r = var_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_escape_strv(char **l, char ***ret) {
256 char **z, **p, **q;
257
258 assert(ret);
259
260 if (strv_isempty(l)) {
261 *ret = NULL;
262 return 0;
263 }
264
265 z = new(char*, strv_length(l)+1);
266 if (!z)
267 return -ENOMEM;
268
269 for (p = l, q = z; *p; p++, q++) {
270
271 *q = specifier_escape(*p);
272 if (!*q) {
273 strv_free(z);
274 return -ENOMEM;
275 }
276 }
277
278 *q = NULL;
279 *ret = z;
280
281 return 0;
282 }