]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/specifier.c
macro: introduce TAKE_PTR() macro
[thirdparty/systemd.git] / src / shared / specifier.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9e2f7c11
LP
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
5430f7f2
LP
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
9e2f7c11
LP
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
5430f7f2 15 Lesser General Public License for more details.
9e2f7c11 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
9e2f7c11
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
a8fbdf54
TA
21#include <errno.h>
22#include <stdbool.h>
23#include <stddef.h>
24#include <stdlib.h>
9e2f7c11 25#include <string.h>
6aaa8c2f 26#include <sys/utsname.h>
9e2f7c11 27
a8fbdf54
TA
28#include "sd-id128.h"
29
b5efdb8a 30#include "alloc-util.h"
07630cea 31#include "hostname-util.h"
9e2f7c11 32#include "macro.h"
b5efdb8a 33#include "specifier.h"
07630cea 34#include "string-util.h"
e82f30d1 35#include "strv.h"
36444d22 36#include "user-util.h"
9e2f7c11
LP
37
38/*
39 * Generic infrastructure for replacing %x style specifiers in
40 * strings. Will call a callback for each replacement.
41 *
42 */
43
751223fe
ZJS
44/* Any ASCII character or digit: our pool of potential specifiers,
45 * and "%" used for escaping. */
46#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
47
19f6d710 48int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) {
8208c8f2
ZJS
49 size_t l;
50 _cleanup_free_ char *ret = NULL;
51 char *t;
9e2f7c11
LP
52 const char *f;
53 bool percent = false;
19f6d710 54 int r;
9e2f7c11
LP
55
56 assert(text);
57 assert(table);
58
59 l = strlen(text);
19f6d710
LP
60 ret = new(char, l+1);
61 if (!ret)
62 return -ENOMEM;
9e2f7c11 63
19f6d710 64 t = ret;
9e2f7c11
LP
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) {
19f6d710
LP
79 _cleanup_free_ char *w = NULL;
80 char *n;
9e2f7c11
LP
81 size_t k, j;
82
19f6d710 83 r = i->lookup(i->specifier, i->data, userdata, &w);
8208c8f2 84 if (r < 0)
19f6d710 85 return r;
9e2f7c11 86
19f6d710 87 j = t - ret;
9e2f7c11
LP
88 k = strlen(w);
89
7ae03f36 90 n = new(char, j + k + l + 1);
8208c8f2 91 if (!n)
19f6d710 92 return -ENOMEM;
9e2f7c11 93
19f6d710 94 memcpy(n, ret, j);
9e2f7c11
LP
95 memcpy(n + j, w, k);
96
8208c8f2
ZJS
97 free_and_replace(ret, n);
98 t = ret + j + k;
99 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
751223fe
ZJS
100 /* Oops, an unknown specifier. */
101 return -EBADSLT;
8208c8f2 102 else {
9e2f7c11
LP
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
038492ae
FS
115 /* if string ended with a stray %, also end with % */
116 if (percent)
117 *(t++) = '%';
118
9e2f7c11 119 *t = 0;
ae2a15bc 120 *_ret = TAKE_PTR(ret);
19f6d710 121 return 0;
9e2f7c11
LP
122}
123
124/* Generic handler for simple string replacements */
125
19f6d710
LP
126int 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;
9e2f7c11 135}
d848b9cb 136
19f6d710 137int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) {
d848b9cb 138 sd_id128_t id;
19f6d710 139 char *n;
d848b9cb
ZJS
140 int r;
141
142 r = sd_id128_get_machine(&id);
143 if (r < 0)
19f6d710 144 return r;
d848b9cb 145
19f6d710
LP
146 n = new(char, 33);
147 if (!n)
148 return -ENOMEM;
d848b9cb 149
19f6d710
LP
150 *ret = sd_id128_to_string(id, n);
151 return 0;
d848b9cb
ZJS
152}
153
19f6d710 154int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
d848b9cb 155 sd_id128_t id;
19f6d710 156 char *n;
d848b9cb
ZJS
157 int r;
158
159 r = sd_id128_get_boot(&id);
160 if (r < 0)
19f6d710 161 return r;
d848b9cb 162
19f6d710
LP
163 n = new(char, 33);
164 if (!n)
165 return -ENOMEM;
d848b9cb 166
19f6d710
LP
167 *ret = sd_id128_to_string(id, n);
168 return 0;
d848b9cb
ZJS
169}
170
19f6d710
LP
171int 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;
d848b9cb 180}
6aaa8c2f 181
19f6d710 182int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) {
6aaa8c2f 183 struct utsname uts;
19f6d710 184 char *n;
6aaa8c2f
ZJS
185 int r;
186
187 r = uname(&uts);
188 if (r < 0)
19f6d710
LP
189 return -errno;
190
191 n = strdup(uts.release);
192 if (!n)
193 return -ENOMEM;
6aaa8c2f 194
19f6d710
LP
195 *ret = n;
196 return 0;
6aaa8c2f 197}
e82f30d1 198
36444d22
LP
199int 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
c987fefc 205 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
36444d22
LP
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
217int 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
225int 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
233int 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
e82f30d1
LP
241int 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}