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