#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"
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;
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 */
}
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,
Context *c = userdata;
const char *name;
int interactive, r;
- struct utsname u;
assert(m);
assert(c);
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,
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);
}
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(
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);
}
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),
}
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;
#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"
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;
return read_etc_hostname_stream(f, ret);
}
-static bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
+void hostname_update_source_hint(const char *hostname, HostnameSource source) {
+ int r;
- if (isempty(u.nodename))
- return false;
+ /* 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).
+ */
- /* 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;
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);
}
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);
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);