1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <sys/utsname.h>
11 #include "alloc-util.h"
12 #include "architecture.h"
13 #include "chase-symlinks.h"
15 #include "format-util.h"
17 #include "hostname-util.h"
18 #include "id128-util.h"
21 #include "path-util.h"
22 #include "specifier.h"
23 #include "string-util.h"
25 #include "user-util.h"
28 * Generic infrastructure for replacing %x style specifiers in
29 * strings. Will call a callback for each replacement.
32 /* Any ASCII character or digit: our pool of potential specifiers,
33 * and "%" used for escaping. */
34 #define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
36 int specifier_printf(const char *text
, size_t max_length
, const Specifier table
[], const char *root
, const void *userdata
, char **ret
) {
37 _cleanup_free_
char *result
= NULL
;
48 if (!GREEDY_REALLOC(result
, l
+ 1))
52 for (const char *f
= text
; *f
!= '\0'; f
++, l
--) {
61 for (i
= table
; i
->specifier
; i
++)
62 if (i
->specifier
== *f
)
66 _cleanup_free_
char *w
= NULL
;
69 r
= i
->lookup(i
->specifier
, i
->data
, root
, userdata
, &w
);
78 if (!GREEDY_REALLOC(result
, j
+ k
+ l
+ 1))
80 memcpy(result
+ j
, w
, k
);
82 } else if (strchr(POSSIBLE_SPECIFIERS
, *f
))
83 /* Oops, an unknown specifier. */
95 if ((size_t) (t
- result
) > max_length
)
99 /* If string ended with a stray %, also end with % */
102 if ((size_t) (t
- result
) > max_length
)
103 return -ENAMETOOLONG
;
107 *ret
= TAKE_PTR(result
);
111 /* Generic handler for simple string replacements */
113 int specifier_string(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
118 if (!isempty(data
)) {
128 int specifier_real_path(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
129 const char *path
= data
;
136 return chase_symlinks(path
, root
, 0, ret
, NULL
);
139 int specifier_real_directory(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
140 _cleanup_free_
char *path
= NULL
;
145 r
= specifier_real_path(specifier
, data
, root
, userdata
, &path
);
150 return path_extract_directory(path
, ret
);
153 int specifier_machine_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
161 _cleanup_close_
int fd
= -1;
163 fd
= chase_symlinks_and_open("/etc/machine-id", root
, CHASE_PREFIX_ROOT
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
, NULL
);
167 r
= id128_read_fd(fd
, ID128_PLAIN
, &id
);
169 r
= sd_id128_get_machine(&id
);
173 n
= new(char, SD_ID128_STRING_MAX
);
177 *ret
= sd_id128_to_string(id
, n
);
181 int specifier_boot_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
188 r
= sd_id128_get_boot(&id
);
192 n
= new(char, SD_ID128_STRING_MAX
);
196 *ret
= sd_id128_to_string(id
, n
);
200 int specifier_host_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
205 n
= gethostname_malloc();
213 int specifier_short_host_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
218 n
= gethostname_short_malloc();
226 int specifier_pretty_host_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
231 if (get_pretty_hostname(&n
) < 0) {
232 n
= gethostname_short_malloc();
241 int specifier_kernel_release(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
250 n
= strdup(uts
.release
);
258 int specifier_architecture(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
263 t
= strdup(architecture_to_string(uname_architecture()));
271 /* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid
272 * otherwise. We'll return an empty value or NULL in that case from the functions below. */
274 int specifier_os_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
276 return parse_os_release(root
, "ID", ret
);
279 int specifier_os_version_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
281 return parse_os_release(root
, "VERSION_ID", ret
);
284 int specifier_os_build_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
286 return parse_os_release(root
, "BUILD_ID", ret
);
289 int specifier_os_variant_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
291 return parse_os_release(root
, "VARIANT_ID", ret
);
294 int specifier_os_image_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
296 return parse_os_release(root
, "IMAGE_ID", ret
);
299 int specifier_os_image_version(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
301 return parse_os_release(root
, "IMAGE_VERSION", ret
);
304 int specifier_group_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
309 t
= gid_to_name(getgid());
317 int specifier_group_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
320 if (asprintf(ret
, UID_FMT
, getgid()) < 0)
326 int specifier_user_name(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
331 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
332 * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
334 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with
335 * specifer_user_id() below.
338 t
= uid_to_name(getuid());
346 int specifier_user_id(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
349 if (asprintf(ret
, UID_FMT
, getuid()) < 0)
355 int specifier_user_home(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
358 /* On PID 1 (which runs as root) this will not result in NSS,
359 * which is good. See above */
361 return get_home_dir(ret
);
364 int specifier_user_shell(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
367 /* On PID 1 (which runs as root) this will not result in NSS,
368 * which is good. See above */
370 return get_shell(ret
);
373 int specifier_tmp_dir(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
380 if (root
) /* If root dir is set, don't honour $TMP or similar */
395 int specifier_var_tmp_dir(char specifier
, const void *data
, const char *root
, const void *userdata
, char **ret
) {
417 int specifier_escape_strv(char **l
, char ***ret
) {
422 if (strv_isempty(l
)) {
427 z
= new(char*, strv_length(l
)+1);
431 for (p
= l
, q
= z
; *p
; p
++, q
++) {
433 *q
= specifier_escape(*p
);
446 const Specifier system_and_tmp_specifier_table
[] = {
447 COMMON_SYSTEM_SPECIFIERS
,
448 COMMON_TMP_SPECIFIERS
,