1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/utsname.h>
8 #include "alloc-util.h"
9 #include "architecture.h"
11 #include "format-util.h"
13 #include "hostname-setup.h"
14 #include "hostname-util.h"
15 #include "id128-util.h"
17 #include "path-util.h"
18 #include "runtime-scope.h"
19 #include "specifier.h"
20 #include "string-util.h"
22 #include "user-util.h"
25 * Generic infrastructure for replacing %x style specifiers in
26 * strings. Will call a callback for each replacement.
29 /* Any ASCII character or digit: our pool of potential specifiers,
30 * and "%" used for escaping. */
31 #define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
33 int specifier_printf(const char *text
, size_t max_length
, const Specifier table
[], const char *root
, const void *userdata
, char **ret
) {
34 _cleanup_free_
char *result
= NULL
;
45 if (!GREEDY_REALLOC(result
, l
+ 1))
49 for (const char *f
= text
; *f
!= '\0'; f
++, l
--) {
58 for (i
= table
; i
->specifier
; i
++)
59 if (i
->specifier
== *f
)
63 _cleanup_free_
char *w
= NULL
;
66 r
= i
->lookup(i
->specifier
, i
->data
, root
, userdata
, &w
);
75 if (!GREEDY_REALLOC(result
, j
+ k
+ l
+ 1))
77 t
= mempcpy(result
+ j
, w
, k
);
78 } else if (strchr(POSSIBLE_SPECIFIERS
, *f
))
79 /* Oops, an unknown specifier. */
91 if ((size_t) (t
- result
) > max_length
)
95 /* If string ended with a stray %, also end with % */
98 if ((size_t) (t
- result
) > max_length
)
103 *ret
= TAKE_PTR(result
);
107 /* Generic handler for simple string replacements */
109 int specifier_string(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
110 return strdup_to(ASSERT_PTR(ret
), empty_to_null(data
));
113 int specifier_real_path(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
114 const char *path
= data
;
121 return chase(path
, root
, 0, ret
, NULL
);
124 int specifier_real_directory(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
125 _cleanup_free_
char *path
= NULL
;
130 r
= specifier_real_path(specifier
, data
, root
, userdata
, &path
);
135 return path_extract_directory(path
, ret
);
138 int specifier_id128(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
139 const sd_id128_t
*id
= ASSERT_PTR(data
);
142 n
= new(char, SD_ID128_STRING_MAX
);
146 *ret
= sd_id128_to_string(*id
, n
);
150 int specifier_uuid(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
151 const sd_id128_t
*id
= ASSERT_PTR(data
);
154 n
= new(char, SD_ID128_UUID_STRING_MAX
);
158 *ret
= sd_id128_to_uuid_string(*id
, n
);
162 int specifier_uint64(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
163 const uint64_t *n
= ASSERT_PTR(data
);
165 return asprintf(ret
, "%" PRIu64
, *n
) < 0 ? -ENOMEM
: 0;
168 int specifier_machine_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
174 r
= id128_get_machine(root
, &id
);
175 if (r
< 0) /* Translate error for missing /etc/machine-id file to EUNATCH. */
176 return r
== -ENOENT
? -EUNATCH
: r
;
178 return specifier_id128(specifier
, &id
, root
, userdata
, ret
);
181 int specifier_boot_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
187 r
= sd_id128_get_boot(&id
);
191 return specifier_id128(specifier
, &id
, root
, userdata
, ret
);
194 int specifier_hostname(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
199 n
= gethostname_malloc();
207 int specifier_short_hostname(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
212 n
= gethostname_short_malloc();
220 int specifier_pretty_hostname(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
225 if (get_pretty_hostname(&n
) < 0) {
226 n
= gethostname_short_malloc();
235 int specifier_kernel_release(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
243 return strdup_to(ret
, uts
.release
);
246 int specifier_architecture(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
247 return strdup_to(ASSERT_PTR(ret
),
248 architecture_to_string(uname_architecture()));
251 /* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid
252 * otherwise. We'll return an empty value or NULL in that case from the functions below. But if the
253 * os-release file is missing, we'll return -EUNATCH. This means that something is seriously wrong with the
256 static int parse_os_release_specifier(const char *root
, const char *id
, char **ret
) {
257 _cleanup_free_
char *v
= NULL
;
262 r
= parse_os_release(root
, id
, &v
);
264 /* parse_os_release() calls parse_env_file() which only sets the return value for
265 * entries found. Let's make sure we set the return value in all cases. */
268 /* Translate error for missing os-release file to EUNATCH. */
269 return r
== -ENOENT
? -EUNATCH
: r
;
272 int specifier_os_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
273 return parse_os_release_specifier(root
, "ID", ret
);
276 int specifier_os_version_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
277 return parse_os_release_specifier(root
, "VERSION_ID", ret
);
280 int specifier_os_build_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
281 return parse_os_release_specifier(root
, "BUILD_ID", ret
);
284 int specifier_os_variant_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
285 return parse_os_release_specifier(root
, "VARIANT_ID", ret
);
288 int specifier_os_image_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
289 return parse_os_release_specifier(root
, "IMAGE_ID", ret
);
292 int specifier_os_image_version(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
293 return parse_os_release_specifier(root
, "IMAGE_VERSION", ret
);
296 int specifier_group_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
297 RuntimeScope scope
= PTR_TO_INT(data
);
302 if (scope
== RUNTIME_SCOPE_GLOBAL
)
305 t
= gid_to_name(scope
== RUNTIME_SCOPE_USER
? getgid() : 0);
313 int specifier_group_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
314 RuntimeScope scope
= PTR_TO_INT(data
);
319 if (scope
== RUNTIME_SCOPE_GLOBAL
)
322 gid
= scope
== RUNTIME_SCOPE_USER
? getgid() : 0;
324 if (asprintf(ret
, UID_FMT
, gid
) < 0)
330 int specifier_user_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
331 RuntimeScope scope
= PTR_TO_INT(data
);
337 if (scope
== RUNTIME_SCOPE_GLOBAL
)
340 uid
= scope
== RUNTIME_SCOPE_USER
? getuid() : 0;
342 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want
343 * to be able to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
345 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain
346 * consistent with specifer_user_id() below.
349 t
= uid_to_name(uid
);
357 int specifier_user_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
358 RuntimeScope scope
= PTR_TO_INT(data
);
363 if (scope
== RUNTIME_SCOPE_GLOBAL
)
366 uid
= scope
== RUNTIME_SCOPE_USER
? getuid() : 0;
368 if (asprintf(ret
, UID_FMT
, uid
) < 0)
374 int specifier_user_home(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
377 /* On PID 1 (which runs as root) this will not result in NSS,
378 * which is good. See above */
380 return get_home_dir(ret
);
383 int specifier_user_shell(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
386 /* On PID 1 (which runs as root) this will not result in NSS,
387 * which is good. See above */
389 return get_shell(ret
);
392 int specifier_tmp_dir(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
398 if (root
) /* If root dir is set, don't honour $TMP or similar */
406 return strdup_to(ret
, p
);
409 int specifier_var_tmp_dir(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
423 return strdup_to(ret
, p
);
426 char* specifier_escape(const char *string
) {
427 return strreplace(string
, "%", "%%");
430 int specifier_escape_strv(char **l
, char ***ret
) {
431 _cleanup_strv_free_
char **z
= NULL
;
436 if (strv_isempty(l
)) {
441 z
= new(char*, strv_length(l
)+1);
445 for (p
= l
, q
= z
; *p
; p
++, q
++) {
447 *q
= specifier_escape(*p
);
458 const Specifier system_and_tmp_specifier_table
[] = {
459 COMMON_SYSTEM_SPECIFIERS
,
460 COMMON_TMP_SPECIFIERS
,