X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fhostname%2Fhostnamed.c;h=a4849b3673a3ffd44e680983b548527af6f7b294;hp=1340c29dd5d05fdd5fddab104d85c5ff7cee9fe8;hb=799298d6513e7d40125c19d46578bb870d047db4;hpb=d200735e13c52dcfe36c0e066f9f6c2fbfb85a9c diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 1340c29dd..a4849b367 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -6,126 +6,130 @@ Copyright 2011 Lennart Poettering systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ -#include - #include #include #include #include +#include #include "util.h" #include "strv.h" -#include "dbus-common.h" -#include "polkit.h" #include "def.h" #include "virt.h" +#include "env-util.h" +#include "fileio-label.h" +#include "label.h" +#include "bus-util.h" +#include "event-util.h" -#define INTERFACE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "\n" \ - INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - BUS_PEER_INTERFACE \ - "\n" - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.hostname1\0" - -const char hostname_interface[] _introspect_("hostname1") = INTERFACE; +#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:") enum { PROP_HOSTNAME, PROP_STATIC_HOSTNAME, PROP_PRETTY_HOSTNAME, PROP_ICON_NAME, + PROP_CHASSIS, + PROP_DEPLOYMENT, + PROP_KERNEL_NAME, + PROP_KERNEL_RELEASE, + PROP_KERNEL_VERSION, + PROP_OS_PRETTY_NAME, + PROP_OS_CPE_NAME, _PROP_MAX }; -static char *data[_PROP_MAX] = { - NULL, - NULL, - NULL, - NULL -}; - -static usec_t remain_until = 0; +typedef struct Context { + char *data[_PROP_MAX]; + Hashmap *polkit_registry; +} Context; -static void free_data(void) { +static void context_reset(Context *c) { int p; + assert(c); + for (p = 0; p < _PROP_MAX; p++) { - free(data[p]); - data[p] = NULL; + free(c->data[p]); + c->data[p] = NULL; } } -static int read_data(void) { +static void context_free(Context *c, sd_bus *bus) { + assert(c); + + context_reset(c); + bus_verify_polkit_async_registry_free(bus, c->polkit_registry); +} + +static int context_read_data(Context *c) { int r; + struct utsname u; + + assert(c); - free_data(); + context_reset(c); - data[PROP_HOSTNAME] = gethostname_malloc(); - if (!data[PROP_HOSTNAME]) + assert_se(uname(&u) >= 0); + c->data[PROP_KERNEL_NAME] = strdup(u.sysname); + c->data[PROP_KERNEL_RELEASE] = strdup(u.release); + c->data[PROP_KERNEL_VERSION] = strdup(u.version); + if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] || + !c->data[PROP_KERNEL_VERSION]) return -ENOMEM; - r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]); + c->data[PROP_HOSTNAME] = gethostname_malloc(); + if (!c->data[PROP_HOSTNAME]) + return -ENOMEM; + + r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]); if (r < 0 && r != -ENOENT) return r; r = parse_env_file("/etc/machine-info", NEWLINE, - "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME], - "ICON_NAME", &data[PROP_ICON_NAME], + "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], + "ICON_NAME", &c->data[PROP_ICON_NAME], + "CHASSIS", &c->data[PROP_CHASSIS], + "DEPLOYMENT", &c->data[PROP_DEPLOYMENT], NULL); if (r < 0 && r != -ENOENT) return r; + r = parse_env_file("/etc/os-release", NEWLINE, + "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], + "CPE_NAME", &c->data[PROP_OS_CPE_NAME], + NULL); + if (r == -ENOENT) { + r = parse_env_file("/usr/lib/os-release", NEWLINE, + "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], + "CPE_NAME", &c->data[PROP_OS_CPE_NAME], + NULL); + } + + if (r < 0 && r != -ENOENT) + return r; + return 0; } static bool check_nss(void) { - void *dl; - if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) { + dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY); + if (dl) { dlclose(dl); return true; } @@ -133,25 +137,83 @@ static bool check_nss(void) { return false; } -static const char* fallback_icon_name(void) { +static bool valid_chassis(const char *chassis) { + assert(chassis); + + return nulstr_contains( + "vm\0" + "container\0" + "desktop\0" + "laptop\0" + "server\0" + "tablet\0" + "handset\0" + "watch\0", + chassis); +} + +static bool valid_deployment(const char *deployment) { + assert(deployment); + + return strspn(deployment, VALID_DEPLOYMENT_CHARS) == strlen(deployment); +} -#if defined(__i386__) || defined(__x86_64__) +static const char* fallback_chassis(void) { int r; char *type; unsigned t; -#endif + int v; - if (detect_virtualization(NULL) > 0) - return "computer-vm"; + v = detect_virtualization(NULL); -#if defined(__i386__) || defined(__x86_64__) + if (v == VIRTUALIZATION_VM) + return "vm"; + if (v == VIRTUALIZATION_CONTAINER) + return "container"; + + r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type); + if (r < 0) + goto try_dmi; + + r = safe_atou(type, &t); + free(type); + if (r < 0) + goto try_dmi; + + /* We only list the really obvious cases here as the ACPI data + * is not really super reliable. + * + * See the ACPI 5.0 Spec Section 5.2.9.1 for details: + * + * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf + */ + + switch(t) { + + case 1: + case 3: + case 6: + return "desktop"; + + case 2: + return "laptop"; + + case 4: + case 5: + case 7: + return "server"; + + case 8: + return "tablet"; + } + +try_dmi: r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type); if (r < 0) return NULL; r = safe_atou(type, &t); free(type); - if (r < 0) return NULL; @@ -171,29 +233,69 @@ static const char* fallback_icon_name(void) { case 0x4: case 0x6: case 0x7: - return "computer-desktop"; + return "desktop"; + case 0x8: case 0x9: case 0xA: case 0xE: - return "computer-laptop"; + return "laptop"; + + case 0xB: + return "handset"; case 0x11: case 0x1C: - return "computer-server"; + return "server"; } -#endif return NULL; } -static int write_data_hostname(void) { +static char* context_fallback_icon_name(Context *c) { + const char *chassis; + + assert(c); + + if (!isempty(c->data[PROP_CHASSIS])) + return strappend("computer-", c->data[PROP_CHASSIS]); + + chassis = fallback_chassis(); + if (chassis) + return strappend("computer-", chassis); + + 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 *static_hn; const char *hn; - if (isempty(data[PROP_HOSTNAME])) - hn = "localhost"; + assert(c); + + 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; + + /* ... the transient host name, (ie: DHCP) comes next ...*/ + else if (!isempty(c->data[PROP_HOSTNAME])) + hn = c->data[PROP_HOSTNAME]; + + /* ... fallback to static "localhost.*" ignored above ... */ + else if (!isempty(static_hn)) + hn = static_hn; + + /* ... and the ultimate fallback */ else - hn = data[PROP_HOSTNAME]; + hn = "localhost"; if (sethostname(hn, strlen(hn)) < 0) return -errno; @@ -201,54 +303,58 @@ static int write_data_hostname(void) { return 0; } -static int write_data_static_hostname(void) { +static int context_write_data_static_hostname(Context *c) { + + assert(c); - if (isempty(data[PROP_STATIC_HOSTNAME])) { + if (isempty(c->data[PROP_STATIC_HOSTNAME])) { if (unlink("/etc/hostname") < 0) return errno == ENOENT ? 0 : -errno; return 0; } - - return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]); + return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]); } -static int write_data_other(void) { +static int context_write_data_machine_info(Context *c) { static const char * const name[_PROP_MAX] = { [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME", - [PROP_ICON_NAME] = "ICON_NAME" + [PROP_ICON_NAME] = "ICON_NAME", + [PROP_CHASSIS] = "CHASSIS", + [PROP_DEPLOYMENT] = "DEPLOYMENT", }; - char **l = NULL; + _cleanup_strv_free_ char **l = NULL; int r, p; - r = load_env_file("/etc/machine-info", &l); + assert(c); + + r = load_env_file(NULL, "/etc/machine-info", NULL, &l); if (r < 0 && r != -ENOENT) return r; - for (p = 2; p < _PROP_MAX; p++) { + for (p = PROP_PRETTY_HOSTNAME; p <= PROP_DEPLOYMENT; p++) { char *t, **u; assert(name[p]); - if (isempty(data[p])) { + if (isempty(c->data[p])) { strv_env_unset(l, name[p]); continue; } - if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) { - strv_free(l); + if (asprintf(&t, "%s=%s", name[p], strempty(c->data[p])) < 0) return -ENOMEM; - } u = strv_env_set(l, t); free(t); - strv_free(l); if (!u) return -ENOMEM; + + strv_free(l); l = u; } @@ -260,328 +366,342 @@ static int write_data_other(void) { return 0; } - r = write_env_file("/etc/machine-info", l); - strv_free(l); - - return r; + return write_env_file_label("/etc/machine-info", l); } -static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) { +static int property_get_icon_name( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *n = NULL; + Context *c = userdata; const char *name; - assert(i); - assert(property); + if (isempty(c->data[PROP_ICON_NAME])) + name = n = context_fallback_icon_name(c); + else + name = c->data[PROP_ICON_NAME]; + + if (!name) + return -ENOMEM; + + return sd_bus_message_append(reply, "s", name); +} - if (isempty(data[PROP_ICON_NAME])) - name = fallback_icon_name(); +static int property_get_chassis( + 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; + const char *name; + + if (isempty(c->data[PROP_CHASSIS])) + name = fallback_chassis(); else - name = data[PROP_ICON_NAME]; + name = c->data[PROP_CHASSIS]; - return bus_property_append_string(i, property, (void*) name); + return sd_bus_message_append(reply, "s", name); } -static const BusProperty bus_hostname_properties[] = { - { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true }, - { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true }, - { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true }, - { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true }, - { NULL, } -}; +static int property_get_deployment( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { -static const BusBoundProperties bps[] = { - { "org.freedesktop.hostname1", bus_hostname_properties, data }, - { NULL, } -}; + Context *c = userdata; + const char *name; -static DBusHandlerResult hostname_message_handler( - DBusConnection *connection, - DBusMessage *message, - void *userdata) { + name = c->data[PROP_DEPLOYMENT]; + return sd_bus_message_append(reply, "s", name); +} - DBusMessage *reply = NULL, *changed = NULL; - DBusError error; +static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *name; + int interactive; + char *h; int r; - assert(connection); - assert(message); + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; + + if (isempty(name)) + name = c->data[PROP_STATIC_HOSTNAME]; + + if (isempty(name)) + name = "localhost"; + + if (!hostname_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); + + if (streq_ptr(name, c->data[PROP_HOSTNAME])) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-hostname", interactive, error, method_set_hostname, c); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - dbus_error_init(&error); + h = strdup(name); + if (!h) + return -ENOMEM; - if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) { - const char *name; - dbus_bool_t interactive; + free(c->data[PROP_HOSTNAME]); + c->data[PROP_HOSTNAME] = h; - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); + r = context_update_kernel_hostname(c); + if (r < 0) { + log_error("Failed to set host name: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + } - if (isempty(name)) - name = data[PROP_STATIC_HOSTNAME]; + log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME])); - if (isempty(name)) - name = "localhost"; + sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *name; + int interactive; + int r; + + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; + + if (isempty(name)) + name = NULL; + + if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-static-hostname", interactive, error, method_set_static_hostname, c); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (isempty(name)) { + free(c->data[PROP_STATIC_HOSTNAME]); + c->data[PROP_STATIC_HOSTNAME] = NULL; + } else { + char *h; if (!hostname_is_valid(name)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); - if (!streq_ptr(name, data[PROP_HOSTNAME])) { - char *h; + h = strdup(name); + if (!h) + return -ENOMEM; - r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); + free(c->data[PROP_STATIC_HOSTNAME]); + c->data[PROP_STATIC_HOSTNAME] = h; + } - h = strdup(name); - if (!h) - goto oom; + r = context_update_kernel_hostname(c); + if (r < 0) { + log_error("Failed to set host name: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + } - free(data[PROP_HOSTNAME]); - data[PROP_HOSTNAME] = h; + r = context_write_data_static_hostname(c); + if (r < 0) { + log_error("Failed to write static host name: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r)); + } - r = write_data_hostname(); - if (r < 0) { - log_error("Failed to set host name: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } + log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); - log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME])); + sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL); - changed = bus_properties_changed_new( - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - "Hostname\0"); - if (!changed) - goto oom; - } + return sd_bus_reply_method_return(m, NULL); +} - } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) { - const char *name; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (isempty(name)) - name = NULL; - - if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) { - - r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (isempty(name)) { - free(data[PROP_STATIC_HOSTNAME]); - data[PROP_STATIC_HOSTNAME] = NULL; - } else { - char *h; - - if (!hostname_is_valid(name)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - h = strdup(name); - if (!h) - goto oom; - - free(data[PROP_STATIC_HOSTNAME]); - data[PROP_STATIC_HOSTNAME] = h; - } - - r = write_data_static_hostname(); - if (r < 0) { - log_error("Failed to write static host name: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } - - log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME])); - - changed = bus_properties_changed_new( - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - "StaticHostname\0"); - if (!changed) - goto oom; - } +static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) { + int interactive; + const char *name; + int r; - } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") || - dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) { - - const char *name; - dbus_bool_t interactive; - int k; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (isempty(name)) - name = NULL; - - k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME; - - if (!streq_ptr(name, data[k])) { - - /* Since the pretty hostname should always be - * changed at the same time as the static one, - * use the same policy action for both... */ - - r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ? - "org.freedesktop.hostname1.set-static-hostname" : - "org.freedesktop.hostname1.set-machine-info", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (isempty(name)) { - free(data[k]); - data[k] = NULL; - } else { - char *h; - - h = strdup(name); - if (!h) - goto oom; - - free(data[k]); - data[k] = h; - } - - r = write_data_other(); - if (r < 0) { - log_error("Failed to write machine info: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } - - log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k])); - - changed = bus_properties_changed_new( - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0"); - if (!changed) - goto oom; - } + assert(c); + assert(bus); + assert(m); - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; - if (!(reply = dbus_message_new_method_return(message))) - goto oom; + if (isempty(name)) + name = NULL; - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; + if (streq_ptr(name, c->data[prop])) + return sd_bus_reply_method_return(m, NULL); - dbus_message_unref(reply); - reply = NULL; + /* Since the pretty hostname should always be changed at the + * same time as the static one, use the same policy action for + * both... */ - if (changed) { + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, prop == PROP_PRETTY_HOSTNAME ? + "org.freedesktop.hostname1.set-static-hostname" : + "org.freedesktop.hostname1.set-machine-info", interactive, error, cb, c); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (isempty(name)) { + free(c->data[prop]); + c->data[prop] = NULL; + } else { + char *h; + + /* The icon name might ultimately be used as file + * name, so better be safe than sorry */ + + if (prop == PROP_ICON_NAME && !filename_is_safe(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name); + if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name); + if (prop == PROP_CHASSIS && !valid_chassis(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name); + if (prop == PROP_DEPLOYMENT && !valid_deployment(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name); + + h = strdup(name); + if (!h) + return -ENOMEM; - if (!dbus_connection_send(connection, changed, NULL)) - goto oom; + free(c->data[prop]); + c->data[prop] = h; + } - dbus_message_unref(changed); + r = context_write_data_machine_info(c); + if (r < 0) { + log_error("Failed to write machine info: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r)); } - return DBUS_HANDLER_RESULT_HANDLED; + log_info("Changed %s to '%s'", + prop == PROP_PRETTY_HOSTNAME ? "pretty host name" : + prop == PROP_DEPLOYMENT ? "deployment" : + prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop])); -oom: - if (reply) - dbus_message_unref(reply); + sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", + prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" : + prop == PROP_DEPLOYMENT ? "Deployment" : + prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL); - if (changed) - dbus_message_unref(changed); + return sd_bus_reply_method_return(m, NULL); +} - dbus_error_free(&error); +static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error); +} - return DBUS_HANDLER_RESULT_NEED_MEMORY; +static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error); } -static int connect_bus(DBusConnection **_bus) { - static const DBusObjectPathVTable hostname_vtable = { - .message_function = hostname_message_handler - }; - DBusError error; - DBusConnection *bus = NULL; +static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error); +} + +static int method_set_deployment(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, bus, m, PROP_DEPLOYMENT, method_set_deployment, error); +} + +static const sd_bus_vtable hostname_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, 0), + SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, 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_deployment, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { + _cleanup_bus_unref_ sd_bus *bus = NULL; int r; + assert(c); + assert(event); assert(_bus); - dbus_error_init(&error); - - bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (!bus) { - log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); - r = -ECONNREFUSED; - goto fail; + r = sd_bus_default_system(&bus); + if (r < 0) { + log_error("Failed to get system bus connection: %s", strerror(-r)); + return r; } - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) || - !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { - log_error("Not enough memory"); - r = -ENOMEM; - goto fail; + r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c); + if (r < 0) { + log_error("Failed to register object: %s", strerror(-r)); + return r; } - r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to register name on bus: %s", bus_error_message(&error)); - r = -EEXIST; - goto fail; + r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); + if (r < 0) { + log_error("Failed to register name: %s", strerror(-r)); + return r; } - if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - log_error("Failed to acquire name."); - r = -EEXIST; - goto fail; + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) { + log_error("Failed to attach bus to event loop: %s", strerror(-r)); + return r; } - if (_bus) - *_bus = bus; + *_bus = bus; + bus = NULL; return 0; - -fail: - dbus_connection_close(bus); - dbus_connection_unref(bus); - - dbus_error_free(&error); - - return r; } int main(int argc, char *argv[]) { + Context context = {}; + + _cleanup_event_unref_ sd_event *event = NULL; + _cleanup_bus_unref_ sd_bus *bus = NULL; int r; - DBusConnection *bus = NULL; - bool exiting = false; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); umask(0022); - - if (argc == 2 && streq(argv[1], "--introspect")) { - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "\n", stdout); - fputs(hostname_interface, stdout); - fputs("\n", stdout); - return 0; - } + label_init("/etc"); if (argc != 1) { log_error("This program takes no arguments."); @@ -592,38 +712,38 @@ int main(int argc, char *argv[]) { if (!check_nss()) log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!"); - r = read_data(); - if (r < 0) { - log_error("Failed to read hostname data: %s", strerror(-r)); + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; goto finish; } - r = connect_bus(&bus); - if (r < 0) + r = sd_event_default(&event); + if (r < 0) { + log_error("Failed to allocate event loop: %s", strerror(-r)); goto finish; + } - remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; - for (;;) { + sd_event_set_watchdog(event, true); - if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) - break; + r = connect_bus(&context, event, &bus); + if (r < 0) + goto finish; - if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { - exiting = true; - bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); - } + r = context_read_data(&context); + if (r < 0) { + log_error("Failed to read hostname and machine information: %s", strerror(-r)); + goto finish; } - r = 0; + r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); + if (r < 0) { + log_error("Failed to run event loop: %s", strerror(-r)); + goto finish; + } finish: - free_data(); - - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } + context_free(&context, bus); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }