diff --git a/man/org.freedesktop.hostname1.xml b/man/org.freedesktop.hostname1.xml
index 8e5c37345d..a17339ad07 100644
--- a/man/org.freedesktop.hostname1.xml
+++ b/man/org.freedesktop.hostname1.xml
@@ -64,6 +64,7 @@ node /org/freedesktop/hostname1 {
readonly s PrettyHostname = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s FallbackHostname = '...';
+ readonly s HostnameSource = '...';
readonly s IconName = '...';
readonly s Chassis = '...';
readonly s Deployment = '...';
@@ -117,6 +118,8 @@ node /org/freedesktop/hostname1 {
+
+
@@ -171,6 +174,11 @@ node /org/freedesktop/hostname1 {
The FallbackHostname property exposes the fallback hostname (configured at
compilation time).
+ The HostnameSource property exposes the origin of the currently configured
+ hostname. One of static (set from /etc/hostname),
+ transient (a non-permanent hostname from an external source),
+ fallback (the compiled-in fallback value).
+
The IconName property exposes the icon name following the
XDG icon naming spec. If not set, information such as the chassis type (see below) is used to find a
suitable fallback icon name (i.e. computer-laptop
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/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 9e8a4dd8a4..f0bf29028f 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -31,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"
@@ -60,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;
@@ -317,35 +320,46 @@ static int context_update_kernel_hostname(
const char *transient_hn) {
const char *hn;
- struct utsname u;
+ 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;
- }
-
/* /etc/hostname has the highest preference ... */
- if (c->data[PROP_STATIC_HOSTNAME])
+ 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;
+ hns = HOSTNAME_TRANSIENT;
/* ... and the ultimate fallback */
- else
+ } else {
hn = FALLBACK_HOSTNAME;
+ hns = HOSTNAME_FALLBACK;
+ }
r = sethostname_idempotent(hn);
if (r < 0)
- return r;
+ 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"));
+
+ 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 */
}
@@ -452,6 +466,50 @@ static int property_get_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,
@@ -570,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);
@@ -579,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);
+ name = empty_to_null(name);
- if (isempty(name))
- name = c->data[PROP_STATIC_HOSTNAME];
-
- 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,
@@ -609,18 +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");
- } else if (r == 0)
- log_debug("Hostname was already set to <%s>.", name);
- else {
- log_info("Hostname set to <%s>", name);
-
+ 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);
}
@@ -645,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(
@@ -666,25 +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");
}
- if (c->data[PROP_STATIC_HOSTNAME])
- log_info("Changed static hostname to <%s>", c->data[PROP_STATIC_HOSTNAME]);
- else
- log_info("Unset 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);
+ "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
+ "StaticHostname", "Hostname", "HostnameSource", NULL);
return sd_bus_reply_method_return(m, NULL);
}
@@ -850,6 +892,7 @@ static const sd_bus_vtable hostname_vtable[] = {
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/shared/hostname-setup.c b/src/shared/hostname-setup.c
index 7eccc86c3b..c0465d3dcd 100644
--- a/src/shared/hostname-setup.c
+++ b/src/shared/hostname-setup.c
@@ -9,11 +9,13 @@
#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"
@@ -39,6 +41,26 @@ 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;
@@ -123,24 +145,26 @@ int read_etc_hostname(const char *path, char **ret) {
return read_etc_hostname_stream(f, ret);
}
-static bool hostname_is_set(void) {
- struct utsname u;
+void hostname_update_source_hint(const char *hostname, HostnameSource source) {
+ int r;
- assert_se(uname(&u) >= 0);
+ /* 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 (isempty(u.nodename))
- return false;
-
- /* This is the built-in kernel default hostname */
- if (streq(u.nodename, "(none)"))
- return false;
-
- return true;
+ 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;
@@ -148,9 +172,10 @@ int hostname_setup(bool really) {
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))
+ if (hostname_is_valid(b, true)) {
hn = b;
- else {
+ source = HOSTNAME_TRANSIENT;
+ } else {
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
b = mfree(b);
}
@@ -163,19 +188,27 @@ int hostname_setup(bool really) {
enoent = true;
else
log_warning_errno(r, "Failed to read configured hostname: %m");
- } else
+ } else {
hn = b;
+ source = HOSTNAME_STATIC;
+ }
}
if (isempty(hn)) {
/* Don't override the hostname if it is already set and not explicitly configured */
- if (hostname_is_set())
+
+ 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.");
+ log_info("No hostname configured, using fallback hostname.");
hn = FALLBACK_HOSTNAME;
+ source = HOSTNAME_FALLBACK;
+
}
r = sethostname_idempotent_full(hn, really);
@@ -188,5 +221,16 @@ int hostname_setup(bool really) {
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
index 90637c4b49..022f0eb835 100644
--- a/src/shared/hostname-setup.h
+++ b/src/shared/hostname-setup.h
@@ -4,6 +4,14 @@
#include
#include
+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);
@@ -11,4 +19,6 @@ 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/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in
index d3d0efebd0..222700564e 100644
--- a/units/systemd-hostnamed.service.in
+++ b/units/systemd-hostnamed.service.in
@@ -32,7 +32,7 @@ ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=strict
-ReadWritePaths=/etc
+ReadWritePaths=/etc /run/systemd
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes