]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/specifier.c
shared: fix a memory leak
[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 char *ret, *t;
50 const char *f;
51 bool percent = false;
52 size_t l;
53 int r;
54
55 assert(text);
56 assert(table);
57
58 l = strlen(text);
59 ret = new(char, l+1);
60 if (!ret)
61 return -ENOMEM;
62
63 t = ret;
64
65 for (f = text; *f; f++, l--) {
66
67 if (percent) {
68 if (*f == '%')
69 *(t++) = '%';
70 else {
71 const Specifier *i;
72
73 for (i = table; i->specifier; i++)
74 if (i->specifier == *f)
75 break;
76
77 if (i->lookup) {
78 _cleanup_free_ char *w = NULL;
79 char *n;
80 size_t k, j;
81
82 r = i->lookup(i->specifier, i->data, userdata, &w);
83 if (r < 0) {
84 free(ret);
85 return r;
86 }
87
88 j = t - ret;
89 k = strlen(w);
90
91 n = new(char, j + k + l + 1);
92 if (!n) {
93 free(ret);
94 return -ENOMEM;
95 }
96
97 memcpy(n, ret, j);
98 memcpy(n + j, w, k);
99
100 free(ret);
101
102 ret = n;
103 t = n + j + k;
104 } else if (strchr(POSSIBLE_SPECIFIERS, *f)) {
105 /* Oops, an unknown specifier. */
106 free(ret);
107 return -EBADSLT;
108 } else {
109 *(t++) = '%';
110 *(t++) = *f;
111 }
112 }
113
114 percent = false;
115 } else if (*f == '%')
116 percent = true;
117 else
118 *(t++) = *f;
119 }
120
121 /* if string ended with a stray %, also end with % */
122 if (percent)
123 *(t++) = '%';
124
125 *t = 0;
126 *_ret = ret;
127 return 0;
128 }
129
130 /* Generic handler for simple string replacements */
131
132 int specifier_string(char specifier, void *data, void *userdata, char **ret) {
133 char *n;
134
135 n = strdup(strempty(data));
136 if (!n)
137 return -ENOMEM;
138
139 *ret = n;
140 return 0;
141 }
142
143 int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) {
144 sd_id128_t id;
145 char *n;
146 int r;
147
148 r = sd_id128_get_machine(&id);
149 if (r < 0)
150 return r;
151
152 n = new(char, 33);
153 if (!n)
154 return -ENOMEM;
155
156 *ret = sd_id128_to_string(id, n);
157 return 0;
158 }
159
160 int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
161 sd_id128_t id;
162 char *n;
163 int r;
164
165 r = sd_id128_get_boot(&id);
166 if (r < 0)
167 return r;
168
169 n = new(char, 33);
170 if (!n)
171 return -ENOMEM;
172
173 *ret = sd_id128_to_string(id, n);
174 return 0;
175 }
176
177 int specifier_host_name(char specifier, void *data, void *userdata, char **ret) {
178 char *n;
179
180 n = gethostname_malloc();
181 if (!n)
182 return -ENOMEM;
183
184 *ret = n;
185 return 0;
186 }
187
188 int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) {
189 struct utsname uts;
190 char *n;
191 int r;
192
193 r = uname(&uts);
194 if (r < 0)
195 return -errno;
196
197 n = strdup(uts.release);
198 if (!n)
199 return -ENOMEM;
200
201 *ret = n;
202 return 0;
203 }
204
205 int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
206 char *t;
207
208 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
209 * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
210
211 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
212 * specifer_user_id() below.
213 */
214
215 t = uid_to_name(getuid());
216 if (!t)
217 return -ENOMEM;
218
219 *ret = t;
220 return 0;
221 }
222
223 int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
224
225 if (asprintf(ret, UID_FMT, getuid()) < 0)
226 return -ENOMEM;
227
228 return 0;
229 }
230
231 int specifier_user_home(char specifier, void *data, 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 return get_home_dir(ret);
237 }
238
239 int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
240
241 /* On PID 1 (which runs as root) this will not result in NSS,
242 * which is good. See above */
243
244 return get_shell(ret);
245 }
246
247 int specifier_escape_strv(char **l, char ***ret) {
248 char **z, **p, **q;
249
250 assert(ret);
251
252 if (strv_isempty(l)) {
253 *ret = NULL;
254 return 0;
255 }
256
257 z = new(char*, strv_length(l)+1);
258 if (!z)
259 return -ENOMEM;
260
261 for (p = l, q = z; *p; p++, q++) {
262
263 *q = specifier_escape(*p);
264 if (!*q) {
265 strv_free(z);
266 return -ENOMEM;
267 }
268 }
269
270 *q = NULL;
271 *ret = z;
272
273 return 0;
274 }