summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/hostname-util.c120
-rw-r--r--src/basic/hostname-util.h9
-rw-r--r--src/basic/string-table.h1
-rw-r--r--src/core/hostname-setup.c62
-rw-r--r--src/core/hostname-setup.h4
-rw-r--r--src/core/main.c2
-rw-r--r--src/core/meson.build2
-rw-r--r--src/fuzz/fuzz-hostname-setup.c (renamed from src/fuzz/fuzz-hostname-util.c)2
-rw-r--r--src/fuzz/meson.build2
-rw-r--r--src/hostname/hostnamed.c169
-rw-r--r--src/network/networkd-dhcp4.c1
-rw-r--r--src/network/test-network.c2
-rw-r--r--src/nspawn/nspawn.c1
-rw-r--r--src/shared/dissect-image.c2
-rw-r--r--src/shared/hostname-setup.c236
-rw-r--r--src/shared/hostname-setup.h24
-rw-r--r--src/shared/machine-image.c2
-rw-r--r--src/shared/meson.build2
-rw-r--r--src/test/meson.build15
-rw-r--r--src/test/test-hostname-setup.c72
-rw-r--r--src/test/test-hostname-util.c55
-rw-r--r--src/test/test-hostname.c14
22 files changed, 456 insertions, 343 deletions
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index e1e3d1b061..d7aba2c263 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -7,28 +7,10 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "fd-util.h"
-#include "fileio.h"
#include "hostname-util.h"
-#include "macro.h"
#include "string-util.h"
#include "strv.h"
-bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (isempty(u.nodename))
- return false;
-
- /* This is the built-in kernel default hostname */
- if (streq(u.nodename, "(none)"))
- return false;
-
- return true;
-}
-
char* gethostname_malloc(void) {
struct utsname u;
const char *s;
@@ -208,105 +190,3 @@ bool is_localhost(const char *hostname) {
endswith_no_case(hostname, ".localhost.localdomain") ||
endswith_no_case(hostname, ".localhost.localdomain.");
}
-
-int sethostname_idempotent(const char *s) {
- char buf[HOST_NAME_MAX + 1] = {};
-
- assert(s);
-
- if (gethostname(buf, sizeof(buf)) < 0)
- return -errno;
-
- if (streq(buf, s))
- return 0;
-
- if (sethostname(s, strlen(s)) < 0)
- return -errno;
-
- return 1;
-}
-
-int shorten_overlong(const char *s, char **ret) {
- char *h, *p;
-
- /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
- * whatever comes earlier. */
-
- assert(s);
-
- h = strdup(s);
- if (!h)
- return -ENOMEM;
-
- if (hostname_is_valid(h, 0)) {
- *ret = h;
- return 0;
- }
-
- p = strchr(h, '.');
- if (p)
- *p = 0;
-
- strshorten(h, HOST_NAME_MAX);
-
- if (!hostname_is_valid(h, 0)) {
- free(h);
- return -EDOM;
- }
-
- *ret = h;
- return 1;
-}
-
-int read_etc_hostname_stream(FILE *f, char **ret) {
- int r;
-
- assert(f);
- assert(ret);
-
- for (;;) {
- _cleanup_free_ char *line = NULL;
- char *p;
-
- r = read_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;
-
- p = strstrip(line);
-
- /* File may have empty lines or comments, ignore them */
- if (!IN_SET(*p, '\0', '#')) {
- char *copy;
-
- hostname_cleanup(p); /* normalize the hostname */
-
- if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
- return -EBADMSG;
-
- copy = strdup(p);
- if (!copy)
- return -ENOMEM;
-
- *ret = copy;
- return 0;
- }
- }
-}
-
-int read_etc_hostname(const char *path, 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, ret);
-
-}
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index fe417297ee..6cff9c1d4c 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -7,8 +7,6 @@
#include "macro.h"
#include "strv.h"
-bool hostname_is_set(void);
-
char* gethostname_malloc(void);
char* gethostname_short_malloc(void);
int gethostname_strict(char **ret);
@@ -29,10 +27,3 @@ static inline bool is_gateway_hostname(const char *hostname) {
/* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */
return STRCASE_IN_SET(hostname, "_gateway", "_gateway.");
}
-
-int sethostname_idempotent(const char *s);
-
-int shorten_overlong(const char *s, char **ret);
-
-int read_etc_hostname_stream(FILE *f, char **ret);
-int read_etc_hostname(const char *path, char **ret);
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
index 4911a455c5..ddc9b9a559 100644
--- a/src/basic/string-table.h
+++ b/src/basic/string-table.h
@@ -78,6 +78,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope)
#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
deleted file mode 100644
index 6ccaa479de..0000000000
--- a/src/core/hostname-setup.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "alloc-util.h"
-#include "fileio.h"
-#include "hostname-setup.h"
-#include "hostname-util.h"
-#include "log.h"
-#include "macro.h"
-#include "proc-cmdline.h"
-#include "string-util.h"
-#include "util.h"
-
-int hostname_setup(void) {
- _cleanup_free_ char *b = NULL;
- const char *hn = NULL;
- bool enoent = false;
- int r;
-
- r = proc_cmdline_get_key("systemd.hostname", 0, &b);
- 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(b, VALID_HOSTNAME_TRAILING_DOT))
- hn = b;
- else {
- log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
- b = mfree(b);
- }
- }
-
- if (!hn) {
- r = read_etc_hostname(NULL, &b);
- if (r == -ENOENT)
- enoent = true;
- else if (r < 0)
- log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
- else
- hn = b;
- }
-
- if (isempty(hn)) {
- /* Don't override the hostname if it is already set and not explicitly configured */
- if (hostname_is_set())
- return 0;
-
- if (enoent)
- log_info("No hostname configured.");
-
- hn = FALLBACK_HOSTNAME;
- }
-
- r = sethostname_idempotent(hn);
- if (r < 0)
- return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
-
- log_info("Set hostname to <%s>.", hn);
- return 0;
-}
diff --git a/src/core/hostname-setup.h b/src/core/hostname-setup.h
deleted file mode 100644
index 7fd0a02747..0000000000
--- a/src/core/hostname-setup.h
+++ /dev/null
@@ -1,4 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-int hostname_setup(void);
diff --git a/src/core/main.c b/src/core/main.c
index ef4d03750f..eaa56aca2a 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2064,7 +2064,7 @@ static int initialize_runtime(
}
status_welcome();
- hostname_setup();
+ (void) hostname_setup(true);
/* Force transient machine-id on first boot. */
machine_id_setup(NULL, first_boot, arg_machine_id, NULL);
(void) loopback_setup();
diff --git a/src/core/meson.build b/src/core/meson.build
index 77767eb603..662e6376f1 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -76,8 +76,6 @@ libcore_sources = '''
execute.h
generator-setup.c
generator-setup.h
- hostname-setup.c
- hostname-setup.h
ima-setup.c
ima-setup.h
ip-address-access.c
diff --git a/src/fuzz/fuzz-hostname-util.c b/src/fuzz/fuzz-hostname-setup.c
index 0a81e74424..b8d36da54a 100644
--- a/src/fuzz/fuzz-hostname-util.c
+++ b/src/fuzz/fuzz-hostname-setup.c
@@ -4,7 +4,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fuzz.h"
-#include "hostname-util.h"
+#include "hostname-setup.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_fclose_ FILE *f = NULL;
diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build
index 80fa0f6fcc..83527a68fb 100644
--- a/src/fuzz/meson.build
+++ b/src/fuzz/meson.build
@@ -123,7 +123,7 @@ fuzzers += [
[libshared],
[]],
- [['src/fuzz/fuzz-hostname-util.c'],
+ [['src/fuzz/fuzz-hostname-setup.c'],
[libshared],
[]],
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index a0dc25c834..f0bf29028f 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-get-properties.h"
#include "bus-log-control-api.h"
#include "bus-polkit.h"
#include "def.h"
@@ -16,6 +17,7 @@
#include "env-util.h"
#include "fileio-label.h"
#include "fileio.h"
+#include "hostname-setup.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "main-func.h"
@@ -29,6 +31,7 @@
#include "service-util.h"
#include "signal-util.h"
#include "stat-util.h"
+#include "string-table.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
@@ -58,6 +61,8 @@ enum {
typedef struct Context {
char *data[_PROP_MAX];
+ HostnameSource hostname_source;
+
struct stat etc_hostname_stat;
struct stat etc_os_release_stat;
struct stat etc_machine_info_stat;
@@ -66,11 +71,9 @@ typedef struct Context {
} Context;
static void context_reset(Context *c, uint64_t mask) {
- int p;
-
assert(c);
- for (p = 0; p < _PROP_MAX; p++) {
+ for (int p = 0; p < _PROP_MAX; p++) {
if (!FLAGS_SET(mask, UINT64_C(1) << p))
continue;
@@ -312,63 +315,63 @@ static char* context_fallback_icon_name(Context *c) {
return strdup("computer");
}
-static bool hostname_is_useful(const char *hn) {
- return !isempty(hn) && !is_localhost(hn);
-}
-
static int context_update_kernel_hostname(
Context *c,
const char *transient_hn) {
- const char *static_hn, *hn;
- struct utsname u;
+ const char *hn;
+ HostnameSource hns;
+ int r;
assert(c);
- if (!transient_hn) {
- /* If no transient hostname is passed in, then let's check what is currently set. */
- assert_se(uname(&u) >= 0);
- transient_hn =
- isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
- }
-
- static_hn = c->data[PROP_STATIC_HOSTNAME];
-
- /* /etc/hostname with something other than "localhost"
- * has the highest preference ... */
- if (hostname_is_useful(static_hn))
- hn = static_hn;
+ /* /etc/hostname has the highest preference ... */
+ if (c->data[PROP_STATIC_HOSTNAME]) {
+ hn = c->data[PROP_STATIC_HOSTNAME];
+ hns = HOSTNAME_STATIC;
/* ... the transient hostname, (ie: DHCP) comes next ... */
- else if (!isempty(transient_hn))
+ } else if (transient_hn) {
hn = transient_hn;
-
- /* ... fallback to static "localhost.*" ignored above ... */
- else if (!isempty(static_hn))
- hn = static_hn;
+ hns = HOSTNAME_TRANSIENT;
/* ... and the ultimate fallback */
- else
+ } else {
hn = FALLBACK_HOSTNAME;
+ hns = HOSTNAME_FALLBACK;
+ }
- if (sethostname_idempotent(hn) < 0)
- return -errno;
+ r = sethostname_idempotent(hn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set hostname: %m");
+
+ if (c->hostname_source != hns) {
+ c->hostname_source = hns;
+ r = 1;
+ }
(void) nscd_flush_cache(STRV_MAKE("hosts"));
- return 0;
+ if (r == 0)
+ log_debug("Hostname was already set to <%s>.", hn);
+ else {
+ log_info("Hostname set to <%s> (%s)", hn, hostname_source_to_string(hns));
+
+ hostname_update_source_hint(hn, hns);
+ }
+
+ return r; /* 0 if no change, 1 if something was done */
}
static int context_write_data_static_hostname(Context *c) {
assert(c);
if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
-
if (unlink("/etc/hostname") < 0)
return errno == ENOENT ? 0 : -errno;
-
return 0;
}
+
return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
}
@@ -383,7 +386,7 @@ static int context_write_data_machine_info(Context *c) {
};
_cleanup_strv_free_ char **l = NULL;
- int r, p;
+ int r;
assert(c);
@@ -391,7 +394,7 @@ static int context_write_data_machine_info(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
- for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
+ for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
_cleanup_free_ char *t = NULL;
char **u;
@@ -461,6 +464,52 @@ static int property_get_static_hostname(
return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
}
+static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_fallback_hostname, "s", FALLBACK_HOSTNAME);
+
+static int property_get_hostname_source(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Context *c = userdata;
+ int r;
+ assert(c);
+
+ context_read_etc_hostname(c);
+
+ if (c->hostname_source < 0) {
+ char hostname[HOST_NAME_MAX + 1] = {};
+ _cleanup_free_ char *fallback = NULL;
+
+ (void) get_hostname_filtered(hostname);
+
+ if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
+ c->hostname_source = HOSTNAME_STATIC;
+
+ else {
+ /* If the hostname was not set by us, try to figure out where it came from. If we set
+ * it to the fallback hostname, the file will tell us. We compare the string because
+ * it is possible that the hostname was set by an older version that had a different
+ * fallback, in the initramfs or before we reexecuted. */
+
+ r = read_one_line_file("/run/systemd/fallback-hostname", &fallback);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to read /run/systemd/fallback-hostname, ignoring: %m");
+
+ if (streq_ptr(fallback, hostname))
+ c->hostname_source = HOSTNAME_FALLBACK;
+ else
+ c->hostname_source = HOSTNAME_TRANSIENT;
+ }
+ }
+
+ return sd_bus_message_append(reply, "s", hostname_source_to_string(c->hostname_source));
+}
+
static int property_get_machine_info_field(
sd_bus *bus,
const char *path,
@@ -579,7 +628,6 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
Context *c = userdata;
const char *name;
int interactive, r;
- struct utsname u;
assert(m);
assert(c);
@@ -588,20 +636,15 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
if (r < 0)
return r;
- context_read_etc_hostname(c);
-
- if (isempty(name))
- name = c->data[PROP_STATIC_HOSTNAME];
+ name = empty_to_null(name);
- if (isempty(name))
- name = FALLBACK_HOSTNAME;
+ /* We always go through with the procedure below without comparing to the current hostname, because
+ * we might want to adjust hostname source information even if the actual hostname is unchanged. */
if (!hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
- assert_se(uname(&u) >= 0);
- if (streq_ptr(name, u.nodename))
- return sd_bus_reply_method_return(m, NULL);
+ context_read_etc_hostname(c);
r = bus_verify_polkit_async(
m,
@@ -618,14 +661,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = context_update_kernel_hostname(c, name);
- if (r < 0) {
- log_error_errno(r, "Failed to set hostname: %m");
+ if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
- }
-
- log_info("Changed hostname to '%s'", name);
-
- (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
+ else if (r > 0)
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
+ "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
+ "Hostname", "HostnameSource", NULL);
return sd_bus_reply_method_return(m, NULL);
}
@@ -650,7 +691,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
return sd_bus_reply_method_return(m, NULL);
- if (!isempty(name) && !hostname_is_valid(name, 0))
+ if (name && !hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
r = bus_verify_polkit_async(
@@ -671,21 +712,21 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
if (r < 0)
return r;
- r = context_update_kernel_hostname(c, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to set hostname: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
- }
-
r = context_write_data_static_hostname(c);
if (r < 0) {
log_error_errno(r, "Failed to write static hostname: %m");
return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
}
- log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
+ r = context_update_kernel_hostname(c, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set hostname: %m");
+ return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
+ }
- (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
+ "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
+ "StaticHostname", "Hostname", "HostnameSource", NULL);
return sd_bus_reply_method_return(m, NULL);
}
@@ -850,6 +891,8 @@ static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("FallbackHostname", "s", property_get_fallback_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("HostnameSource", "s", property_get_hostname_source, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -960,7 +1003,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
}
static int run(int argc, char *argv[]) {
- _cleanup_(context_destroy) Context context = {};
+ _cleanup_(context_destroy) Context context = {
+ .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
+ };
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 14e7a28774..080dcedab5 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -8,6 +8,7 @@
#include "escape.h"
#include "alloc-util.h"
#include "dhcp-client-internal.h"
+#include "hostname-setup.h"
#include "hostname-util.h"
#include "parse-util.h"
#include "network-internal.h"
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 03c94409fa..45cd3fa973 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -8,7 +8,7 @@
#include "alloc-util.h"
#include "dhcp-lease-internal.h"
#include "ether-addr-util.h"
-#include "hostname-util.h"
+#include "hostname-setup.h"
#include "network-internal.h"
#include "networkd-manager.h"
#include "string-util.h"
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index cfbc8f11bf..0f6411d193 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -46,6 +46,7 @@
#include "fs-util.h"
#include "gpt.h"
#include "hexdecoct.h"
+#include "hostname-setup.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "io-util.h"
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 113538bb97..b1d7d689ea 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -32,7 +32,7 @@
#include "fsck-util.h"
#include "gpt.h"
#include "hexdecoct.h"
-#include "hostname-util.h"
+#include "hostname-setup.h"
#include "id128-util.h"
#include "mkdir.h"
#include "mount-util.h"
diff --git a/src/shared/hostname-setup.c b/src/shared/hostname-setup.c
new file mode 100644
index 0000000000..c0465d3dcd
--- /dev/null
+++ b/src/shared/hostname-setup.c
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-setup.h"
+#include "hostname-util.h"
+#include "log.h"
+#include "macro.h"
+#include "proc-cmdline.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
+
+static int sethostname_idempotent_full(const char *s, bool really) {
+ char buf[HOST_NAME_MAX + 1] = {};
+
+ assert(s);
+
+ if (gethostname(buf, sizeof(buf) - 1) < 0)
+ return -errno;
+
+ if (streq(buf, s))
+ 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);
+}
+
+bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]) {
+ char buf[HOST_NAME_MAX + 1] = {};
+
+ /* Returns true if we got a good hostname, false otherwise. */
+
+ if (gethostname(buf, sizeof(buf) - 1) < 0)
+ return false; /* This can realistically only fail with ENAMETOOLONG.
+ * Let's treat that case the same as an invalid hostname. */
+
+ if (isempty(buf))
+ return false;
+
+ /* This is the built-in kernel default hostname */
+ if (streq(buf, "(none)"))
+ return false;
+
+ memcpy(ret, buf, sizeof buf);
+ return true;
+}
+
+int shorten_overlong(const char *s, char **ret) {
+ char *h, *p;
+
+ /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
+ * whatever comes earlier. */
+
+ assert(s);
+
+ h = strdup(s);
+ if (!h)
+ return -ENOMEM;
+
+ if (hostname_is_valid(h, 0)) {
+ *ret = h;
+ return 0;
+ }
+
+ p = strchr(h, '.');
+ if (p)
+ *p = 0;
+
+ strshorten(h, HOST_NAME_MAX);
+
+ if (!hostname_is_valid(h, 0)) {
+ free(h);
+ return -EDOM;
+ }
+
+ *ret = h;
+ return 1;
+}
+
+int read_etc_hostname_stream(FILE *f, char **ret) {
+ int r;
+
+ assert(f);
+ assert(ret);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ char *p;
+
+ r = read_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;
+
+ p = strstrip(line);
+
+ /* File may have empty lines or comments, ignore them */
+ if (!IN_SET(*p, '\0', '#')) {
+ char *copy;
+
+ hostname_cleanup(p); /* normalize the hostname */
+
+ if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
+ return -EBADMSG;
+
+ copy = strdup(p);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = copy;
+ return 0;
+ }
+ }
+}
+
+int read_etc_hostname(const char *path, 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, ret);
+}
+
+void hostname_update_source_hint(const char *hostname, HostnameSource source) {
+ int r;
+
+ /* 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_FALLBACK) {
+ r = write_string_file("/run/systemd/fallback-hostname", hostname,
+ WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ log_warning_errno(r, "Failed to create \"/run/systemd/fallback-hostname\": %m");
+ } else
+ unlink_or_warn("/run/systemd/fallback-hostname");
+}
+
+int hostname_setup(bool really) {
+ _cleanup_free_ char *b = NULL;
+ const char *hn = NULL;
+ HostnameSource source;
+ bool enoent = false;
+ int r;
+
+ r = proc_cmdline_get_key("systemd.hostname", 0, &b);
+ 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(b, true)) {
+ hn = b;
+ source = HOSTNAME_TRANSIENT;
+ } else {
+ log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
+ b = mfree(b);
+ }
+ }
+
+ if (!hn) {
+ r = read_etc_hostname(NULL, &b);
+ if (r < 0) {
+ if (r == -ENOENT)
+ enoent = true;
+ else
+ log_warning_errno(r, "Failed to read configured hostname: %m");
+ } else {
+ hn = b;
+ source = HOSTNAME_STATIC;
+ }
+ }
+
+ if (isempty(hn)) {
+ /* Don't override the hostname if it is already set and not explicitly configured */
+
+ char buf[HOST_NAME_MAX + 1] = {};
+ if (get_hostname_filtered(buf)) {
+ log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
+ return 0;
+ }
+
+ if (enoent)
+ log_info("No hostname configured, using fallback hostname.");
+
+ hn = FALLBACK_HOSTNAME;
+ source = HOSTNAME_FALLBACK;
+
+ }
+
+ 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);
+
+ return r;
+}
+
+static const char* const hostname_source_table[] = {
+ [HOSTNAME_STATIC] = "static",
+ [HOSTNAME_TRANSIENT] = "transient",
+ [HOSTNAME_FALLBACK] = "fallback",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(hostname_source, HostnameSource);
diff --git a/src/shared/hostname-setup.h b/src/shared/hostname-setup.h
new file mode 100644
index 0000000000..022f0eb835
--- /dev/null
+++ b/src/shared/hostname-setup.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef enum HostnameSource {
+ HOSTNAME_STATIC, /* from /etc/hostname */
+ HOSTNAME_TRANSIENT, /* a transient hostname set through systemd, hostnamed, the container manager, or otherwise */
+ HOSTNAME_FALLBACK, /* the compiled-in fallback was used */
+ _HOSTNAME_INVALID = -1,
+} HostnameSource;
+
+const char* hostname_source_to_string(HostnameSource source);
+int sethostname_idempotent(const char *s);
+
+int shorten_overlong(const char *s, char **ret);
+
+int read_etc_hostname_stream(FILE *f, char **ret);
+int read_etc_hostname(const char *path, char **ret);
+
+bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]);
+void hostname_update_source_hint(const char *hostname, HostnameSource source);
+int hostname_setup(bool really);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 671a56b9e9..0b148c067a 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -22,7 +22,7 @@
#include "fd-util.h"
#include "fs-util.h"
#include "hashmap.h"
-#include "hostname-util.h"
+#include "hostname-setup.h"
#include "id128-util.h"
#include "lockfile-util.h"
#include "log.h"
diff --git a/src/shared/meson.build b/src/shared/meson.build
index b43fe9c6d9..b4c77513e2 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -115,6 +115,8 @@ shared_sources = files('''
gpt.h
group-record.c
group-record.h
+ hostname-setup.c
+ hostname-setup.h
id128-print.c
id128-print.h
idn-util.c
diff --git a/src/test/meson.build b/src/test/meson.build
index 0cfc709f44..9254f18a23 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -105,17 +105,6 @@ tests += [
libmount,
libblkid]],
- [['src/test/test-hostname.c'],
- [libcore,
- libshared],
- [threads,
- librt,
- libseccomp,
- libselinux,
- libmount,
- libblkid],
- '', 'unsafe'],
-
[['src/test/test-dns-domain.c'],
[libcore,
libshared],
@@ -339,6 +328,10 @@ tests += [
[],
[]],
+ [['src/test/test-hostname-setup.c'],
+ [],
+ []],
+
[['src/test/test-hostname-util.c'],
[],
[]],
diff --git a/src/test/test-hostname-setup.c b/src/test/test-hostname-setup.c
new file mode 100644
index 0000000000..55996500f3
--- /dev/null
+++ b/src/test/test-hostname-setup.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "hostname-setup.h"
+#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+
+static void test_read_etc_hostname(void) {
+ char path[] = "/tmp/hostname.XXXXXX";
+ char *hostname;
+ int fd;
+
+ fd = mkostemp_safe(path);
+ assert(fd > 0);
+ close(fd);
+
+ /* simple hostname */
+ assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_etc_hostname(path, &hostname) == 0);
+ assert_se(streq(hostname, "foo"));
+ hostname = mfree(hostname);
+
+ /* with comment */
+ assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_etc_hostname(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ hostname = mfree(hostname);
+
+ /* with comment and extra whitespace */
+ assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_etc_hostname(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ hostname = mfree(hostname);
+
+ /* cleans up name */
+ assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_etc_hostname(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foobar.com"));
+ hostname = mfree(hostname);
+
+ /* no value set */
+ hostname = (char*) 0x1234;
+ assert_se(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(read_etc_hostname(path, &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ /* nonexisting file */
+ assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ unlink(path);
+}
+
+static void test_hostname_setup(void) {
+ hostname_setup(false);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ test_read_etc_hostname();
+ test_hostname_setup();
+
+ return 0;
+}
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index c7a63bd047..24c8ed9e3b 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -6,8 +6,8 @@
#include "fileio.h"
#include "hostname-util.h"
#include "string-util.h"
+#include "tests.h"
#include "tmpfile-util.h"
-#include "util.h"
static void test_hostname_is_valid(void) {
assert_se(hostname_is_valid("foobar", 0));
@@ -91,55 +91,6 @@ static void test_hostname_cleanup(void) {
assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
-static void test_read_etc_hostname(void) {
- char path[] = "/tmp/hostname.XXXXXX";
- char *hostname;
- int fd;
-
- fd = mkostemp_safe(path);
- assert(fd > 0);
- close(fd);
-
- /* simple hostname */
- assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(read_etc_hostname(path, &hostname) == 0);
- assert_se(streq(hostname, "foo"));
- hostname = mfree(hostname);
-
- /* with comment */
- assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(read_etc_hostname(path, &hostname) == 0);
- assert_se(hostname);
- assert_se(streq(hostname, "foo"));
- hostname = mfree(hostname);
-
- /* with comment and extra whitespace */
- assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(read_etc_hostname(path, &hostname) == 0);
- assert_se(hostname);
- assert_se(streq(hostname, "foo"));
- hostname = mfree(hostname);
-
- /* cleans up name */
- assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(read_etc_hostname(path, &hostname) == 0);
- assert_se(hostname);
- assert_se(streq(hostname, "foobar.com"));
- hostname = mfree(hostname);
-
- /* no value set */
- hostname = (char*) 0x1234;
- assert_se(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(read_etc_hostname(path, &hostname) == -ENOENT);
- assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
-
- /* nonexisting file */
- assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT);
- assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
-
- unlink(path);
-}
-
static void test_hostname_malloc(void) {
_cleanup_free_ char *h = NULL, *l = NULL;
@@ -158,12 +109,10 @@ static void test_fallback_hostname(void) {
}
int main(int argc, char *argv[]) {
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
test_hostname_is_valid();
test_hostname_cleanup();
- test_read_etc_hostname();
test_hostname_malloc();
test_fallback_hostname();
diff --git a/src/test/test-hostname.c b/src/test/test-hostname.c
deleted file mode 100644
index 1a925f253c..0000000000
--- a/src/test/test-hostname.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "hostname-setup.h"
-#include "util.h"
-
-int main(int argc, char* argv[]) {
- int r;
-
- r = hostname_setup();
- if (r < 0)
- log_error_errno(r, "hostname: %m");
-
- return 0;
-}