/
niceSOFT
/
systemd
Обзор
Документация
Войти
/
niceSOFT
/
systemd
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v261
src/shared/hostname-setup.c
414 строк
13 KB
Frantisek Sumsal
tree-wide: various fixlets suggested by Coccinelle
05 мар 2026, 12:27
05 мар 2026, 12:27
90f5d2a
Код
Авторство
О чём код?
/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include <sched.h> #include <stdio.h> #include <sys/utsname.h> #include <unistd.h> #include "sd-daemon.h" #include "alloc-util.h" #include "creds-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hexdecoct.h" #include "hostname-setup.h" #include "hostname-util.h" #include "initrd-util.h" #include "io-util.h" #include "log.h" #include "namespace-util.h" #include "pidref.h" #include "proc-cmdline.h" #include "process-util.h" #include "siphash24.h" #include "string-table.h" #include "string-util.h" static int sethostname_idempotent_full(const char *s, bool really) { struct utsname u; assert(s); if (uname(&u) < 0) return -errno; if (streq_ptr(s, u.nodename)) return 0; if (really && sethostname(s, strlen(s)) < 0) return -errno; return 1; } int sethostname_idempotent(const char *s) { return sethostname_idempotent_full(s, true); } int shorten_overlong(const char *s, char **ret) { _cleanup_free_ char *h = NULL; /* Shorten an overlong name to LINUX_HOST_NAME_MAX or to the first dot, * whatever comes earlier. */ assert(s); assert(ret); h = strdup(s); if (!h) return -ENOMEM; if (hostname_is_valid(h, 0)) { *ret = TAKE_PTR(h); return 0; } char *p = strchr(h, '.'); if (p) *p = 0; strshorten(h, LINUX_HOST_NAME_MAX); if (!hostname_is_valid(h, /* flags= */ 0)) return -EDOM; *ret = TAKE_PTR(h); return 1; } static int acquire_hostname_from_credential(char **ret) { _cleanup_free_ char *cred = NULL; int r; assert(ret); r = read_credential_with_decryption("system.hostname", (void **) &cred, /* ret_size= */ NULL); if (r < 0) return log_warning_errno(r, "Failed to read system.hostname credential, ignoring: %m"); if (r == 0) /* not found */ return -ENXIO; if (!hostname_is_valid(cred, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */ return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Hostname specified in system.hostname credential is invalid, ignoring: %s", cred); log_info("Initializing hostname from credential."); *ret = TAKE_PTR(cred); return 0; } int read_etc_hostname_stream(FILE *f, bool substitute_wildcards, char **ret) { int r; assert(f); assert(ret); for (;;) { _cleanup_free_ char *line = NULL; r = read_stripped_line(f, LONG_LINE_MAX, &line); if (r < 0) return r; if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */ return -ENOENT; /* File may have empty lines or comments, ignore them */ if (IN_SET(line[0], '\0', '#')) continue; if (substitute_wildcards) { r = hostname_substitute_wildcards(line); if (r < 0) return r; } hostname_cleanup(line); /* normalize the hostname */ /* check that the hostname we return is valid */ if (!hostname_is_valid( line, VALID_HOSTNAME_TRAILING_DOT| (substitute_wildcards ? 0 : VALID_HOSTNAME_QUESTION_MARK))) return -EBADMSG; *ret = TAKE_PTR(line); return 0; } } int read_etc_hostname(const char *path, bool substitute_wildcards, char **ret) { _cleanup_fclose_ FILE *f = NULL; assert(ret); if (!path) path = etc_hostname(); f = fopen(path, "re"); if (!f) return -errno; return read_etc_hostname_stream(f, substitute_wildcards, ret); } void hostname_update_source_hint(const char *hostname, HostnameSource source) { int r; assert(hostname); /* Why save the value and not just create a flag file? This way we will * notice if somebody sets the hostname directly (not going through hostnamed). */ if (source == HOSTNAME_DEFAULT) { r = write_string_file("/run/systemd/default-hostname", hostname, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC); if (r < 0) log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m"); } else (void) unlink_or_warn("/run/systemd/default-hostname"); } int hostname_setup(bool really) { _cleanup_free_ char *hn = NULL; HostnameSource source; bool enoent = false; int r; r = proc_cmdline_get_key("systemd.hostname", 0, &hn); if (r < 0) log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m"); else if (r > 0) { if (hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT)) source = HOSTNAME_TRANSIENT; else { log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", hn); hn = mfree(hn); } } if (!hn) { r = read_etc_hostname(/* path= */ NULL, /* substitute_wildcards= */ true, &hn); if (r == -ENOENT) enoent = true; else if (r < 0) log_warning_errno(r, "Failed to read configured hostname, ignoring: %m"); else source = HOSTNAME_STATIC; } if (!hn) { r = acquire_hostname_from_credential(&hn); if (r >= 0) source = HOSTNAME_TRANSIENT; } if (!hn) { /* Don't override the hostname if it is already set and not explicitly configured */ r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hn); if (r == -ENOMEM) return log_oom(); if (r >= 0) { log_debug("No hostname configured, leaving existing hostname <%s> in place.", hn); goto finish; } if (enoent) log_info("No hostname configured, using default hostname."); hn = get_default_hostname(); if (!hn) return log_oom(); source = HOSTNAME_DEFAULT; } r = sethostname_idempotent_full(hn, really); if (r < 0) return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn); if (r == 0) log_debug("Hostname was already set to <%s>.", hn); else log_info("Hostname %s to <%s>.", really ? "set" : "would have been set", hn); if (really) hostname_update_source_hint(hn, source); finish: if (!in_initrd()) (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_HOSTNAME=%s", hn); return 0; } static const char* const hostname_source_table[] = { [HOSTNAME_STATIC] = "static", [HOSTNAME_TRANSIENT] = "transient", [HOSTNAME_DEFAULT] = "default", }; DEFINE_STRING_TABLE_LOOKUP(hostname_source, HostnameSource); int hostname_substitute_wildcards(char *name) { static const sd_id128_t key = SD_ID128_MAKE(98,10,ad,df,8d,7d,4f,b5,89,1b,4b,56,ac,c2,26,8f); sd_id128_t mid = SD_ID128_NULL; size_t left_bits = 0, counter = 0; uint64_t h = 0; int r; assert(name); /* Replaces every occurrence of '?' in the specified string with a nibble hashed from * /etc/machine-id. This is supposed to be used on /etc/hostname files that want to automatically * configure a hostname derived from the machine ID in some form. * * Note that this does not directly use the machine ID, because that's not necessarily supposed to be * public information to be broadcast on the network, while the hostname certainly is. */ for (char *n = name; ; n++) { n = strchr(n, '?'); if (!n) return 0; if (left_bits <= 0) { if (sd_id128_is_null(mid)) { r = sd_id128_get_machine(&mid); if (r < 0) return r; } struct siphash state; siphash24_init(&state, key.bytes); siphash24_compress_typesafe(mid, &state); siphash24_compress_typesafe(counter, &state); /* counter mode */ h = siphash24_finalize(&state); left_bits = sizeof(h) * 8; counter++; } assert(left_bits >= 4); *n = hexchar(h & 0xf); h >>= 4; left_bits -= 4; } } char* get_default_hostname(void) { int r; _cleanup_free_ char *h = get_default_hostname_raw(); if (!h) return NULL; r = hostname_substitute_wildcards(h); if (r < 0) { log_debug_errno(r, "Failed to substitute wildcards in hostname, falling back to built-in name: %m"); return strdup(FALLBACK_HOSTNAME); } return TAKE_PTR(h); } int gethostname_full(GetHostnameFlags flags, char **ret) { _cleanup_free_ char *buf = NULL, *fallback = NULL; struct utsname u; const char *s; assert(ret); if (uname(&u) < 0) return -errno; s = u.nodename; if (isempty(s) || streq(s, "(none)") || (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) || (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) { if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT)) return -ENXIO; s = fallback = get_default_hostname(); if (!s) return -ENOMEM; if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.') return -ENXIO; } if (FLAGS_SET(flags, GET_HOSTNAME_SHORT)) buf = strdupcspn(s, "."); else buf = strdup(s); if (!buf) return -ENOMEM; *ret = TAKE_PTR(buf); return 0; } int pidref_gethostname_full(PidRef *pidref, GetHostnameFlags flags, char **ret) { int r; assert(pidref); assert(ret); r = pidref_in_same_namespace(pidref, NULL, NAMESPACE_UTS); if (r < 0) return r; if (r > 0) return gethostname_full(flags, ret); _cleanup_close_ int utsns_fd = r = pidref_namespace_open_by_type(pidref, NAMESPACE_UTS); if (r < 0) return r; _cleanup_close_pair_ int errno_pipe[2] = EBADF_PAIR; r = pipe2(errno_pipe, O_CLOEXEC); if (r < 0) return -errno; _cleanup_close_pair_ int result_pipe[2] = EBADF_PAIR; r = pipe2(result_pipe, O_CLOEXEC); if (r < 0) return -errno; _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL; r = pidref_safe_fork("(sd-gethostname)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &child); if (r < 0) return r; if (r == 0) { errno_pipe[0] = safe_close(errno_pipe[0]); result_pipe[0] = safe_close(result_pipe[0]); if (setns(utsns_fd, CLONE_NEWUTS) < 0) report_errno_and_exit(errno_pipe[1], -errno); char *t; r = gethostname_full(flags, &t); if (r < 0) report_errno_and_exit(errno_pipe[1], r); r = loop_write(result_pipe[1], t, strlen(t) + 1); report_errno_and_exit(errno_pipe[1], r); } errno_pipe[1] = safe_close(errno_pipe[1]); result_pipe[1] = safe_close(result_pipe[1]); r = read_errno(errno_pipe[0]); if (r < 0) return r; char buf[LINUX_HOST_NAME_MAX+1]; ssize_t n = loop_read(result_pipe[0], buf, sizeof(buf), /* do_poll= */ false); if (n < 0) return n; if (n == 0 || buf[n - 1] != '\0') return -EPROTO; return strdup_to(ret, buf); }