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