diff options
Diffstat (limited to 'src/hostname')
-rw-r--r-- | src/hostname/.gitignore | 1 | ||||
l--------- | src/hostname/Makefile | 1 | ||||
-rw-r--r-- | src/hostname/hostnamectl.c | 546 | ||||
-rw-r--r-- | src/hostname/hostnamed.c | 636 | ||||
-rw-r--r-- | src/hostname/org.freedesktop.hostname1.conf | 27 | ||||
-rw-r--r-- | src/hostname/org.freedesktop.hostname1.policy.in | 49 | ||||
-rw-r--r-- | src/hostname/org.freedesktop.hostname1.service | 12 |
7 files changed, 1272 insertions, 0 deletions
diff --git a/src/hostname/.gitignore b/src/hostname/.gitignore new file mode 100644 index 0000000000..1ff281b231 --- /dev/null +++ b/src/hostname/.gitignore @@ -0,0 +1 @@ +org.freedesktop.hostname1.policy diff --git a/src/hostname/Makefile b/src/hostname/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/hostname/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c new file mode 100644 index 0000000000..b7ae5ccc6c --- /dev/null +++ b/src/hostname/hostnamectl.c @@ -0,0 +1,546 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <getopt.h> +#include <locale.h> +#include <string.h> +#include <sys/timex.h> +#include <sys/utsname.h> + +#include "dbus-common.h" +#include "util.h" +#include "spawn-polkit-agent.h" +#include "build.h" +#include "hwclock.h" +#include "strv.h" +#include "sd-id128.h" +#include "virt.h" + +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static const char *arg_host = NULL; +static bool arg_set_transient = false; +static bool arg_set_pretty = false; +static bool arg_set_static = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + const char *hostname; + const char *static_hostname; + const char *pretty_hostname; + const char *icon_name; +} StatusInfo; + +static void print_status_info(StatusInfo *i) { + sd_id128_t mid, bid; + int r; + const char *id = NULL; + _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL; + struct utsname u; + + assert(i); + + printf(" Static hostname: %s\n", + strna(i->static_hostname)); + + if (!streq_ptr(i->hostname, i->static_hostname)) + printf("Transient hostname: %s\n", + strna(i->hostname)); + + printf(" Pretty hostname: %s\n" + " Icon name: %s\n", + strna(i->pretty_hostname), + strna(i->icon_name)); + + r = sd_id128_get_machine(&mid); + if (r >= 0) + printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid)); + + r = sd_id128_get_boot(&bid); + if (r >= 0) + printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid)); + + if (detect_virtualization(&id) > 0) + printf(" Virtualization: %s\n", id); + + r = parse_env_file("/etc/os-release", NEWLINE, + "PRETTY_NAME", &pretty_name, + "CPE_NAME", &cpe_name, + NULL); + + if (!isempty(pretty_name)) + printf(" Operating System: %s\n", pretty_name); + + if (!isempty(cpe_name)) + printf(" CPE OS Name: %s\n", cpe_name); + + assert_se(uname(&u) >= 0); + printf(" Kernel: %s %s\n" + " Architecture: %s\n", u.sysname, u.release, u.machine); + +} + +static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + if (!isempty(s)) { + if (streq(name, "Hostname")) + i->hostname = s; + if (streq(name, "StaticHostname")) + i->static_hostname = s; + if (streq(name, "PrettyHostname")) + i->pretty_hostname = s; + if (streq(name, "IconName")) + i->icon_name = s; + } + break; + } + } + + return 0; +} + +static int show_status(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + StatusInfo info; + + assert(args); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + zero(info); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + r = status_property(name, &sub3, &info); + if (r < 0) { + log_error("Failed to parse reply."); + return r; + } + + dbus_message_iter_next(&sub); + } + + print_status_info(&info); + return 0; +} + +static char* hostname_simplify(char *s) { + char *p, *d; + + for (p = s, d = s; *p; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + *(d++) = *p; + else if (*p >= 'A' && *p <= 'Z') + *(d++) = *p - 'A' + 'a'; + else if (*p == ' ') + *(d++) = '-'; + } + + *d = 0; + + strshorten(s, HOST_NAME_MAX); + return s; +} + +static int set_hostname(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + _cleanup_free_ char *h = NULL; + const char *hostname = args[1]; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + if (arg_set_pretty) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetPrettyHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + h = strdup(hostname); + if (!h) + return log_oom(); + + hostname = hostname_simplify(h); + } + + if (arg_set_static) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetStaticHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + if (arg_set_transient) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + return 0; +} + +static int set_icon_name(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + return bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetIconName", + &reply, + NULL, + DBUS_TYPE_STRING, &args[1], + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int help(void) { + + printf("%s [OPTIONS...] COMMAND ...\n\n" + "Query or change system hostname.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --transient Only set transient hostname\n" + " --static Only set static hostname\n" + " --pretty Only set pretty hostname\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n\n" + "Commands:\n" + " status Show current hostname settings\n" + " set-hostname NAME Set system hostname\n" + " set-icon-name NAME Set icon name for host\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, + ARG_SET_TRANSIENT, + ARG_SET_STATIC, + ARG_SET_PRETTY + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "transient", no_argument, NULL, ARG_SET_TRANSIENT }, + { "static", no_argument, NULL, ARG_SET_STATIC }, + { "pretty", no_argument, NULL, ARG_SET_PRETTY }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case ARG_SET_TRANSIENT: + arg_set_transient = true; + break; + + case ARG_SET_PRETTY: + arg_set_pretty = true; + break; + + case ARG_SET_STATIC: + arg_set_static = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (!arg_set_transient && !arg_set_pretty && !arg_set_static) + arg_set_transient = arg_set_pretty = arg_set_static = true; + + return 1; +} + +static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-hostname", EQUAL, 2, set_hostname }, + { "set-icon-name", EQUAL, 2, set_icon_name }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = hostnamectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + return retval; +} diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c new file mode 100644 index 0000000000..cd3ef491ac --- /dev/null +++ b/src/hostname/hostnamed.c @@ -0,0 +1,636 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dbus/dbus.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <dlfcn.h> + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" +#include "polkit.h" +#include "def.h" +#include "virt.h" + +#define INTERFACE \ + " <interface name=\"org.freedesktop.hostname1\">\n" \ + " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \ + " <method name=\"SetHostname\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetStaticHostname\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetPrettyHostname\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetIconName\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ + " </method>\n" \ + " </interface>\n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "<node>\n" \ + INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "</node>\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.hostname1\0" + +const char hostname_interface[] _introspect_("hostname1") = INTERFACE; + +enum { + PROP_HOSTNAME, + PROP_STATIC_HOSTNAME, + PROP_PRETTY_HOSTNAME, + PROP_ICON_NAME, + _PROP_MAX +}; + +static char *data[_PROP_MAX] = { + NULL, + NULL, + NULL, + NULL +}; + +static usec_t remain_until = 0; + +static void free_data(void) { + int p; + + for (p = 0; p < _PROP_MAX; p++) { + free(data[p]); + data[p] = NULL; + } +} + +static int read_data(void) { + int r; + + free_data(); + + data[PROP_HOSTNAME] = gethostname_malloc(); + if (!data[PROP_HOSTNAME]) + return -ENOMEM; + + r = read_one_line_file("/etc/hostname", &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], + 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))) { + dlclose(dl); + return true; + } + + return false; +} + +static const char* fallback_icon_name(void) { + +#if defined(__i386__) || defined(__x86_64__) + int r; + char *type; + unsigned t; +#endif + + if (detect_virtualization(NULL) > 0) + return "computer-vm"; + +#if defined(__i386__) || defined(__x86_64__) + 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; + + /* We only list the really obvious cases here. The DMI data is + unreliable enough, so let's not do any additional guesswork + on top of that. + + See the SMBIOS Specification 2.7.1 section 7.4.1 for + details about the values listed here: + + http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf + */ + + switch (t) { + + case 0x3: + case 0x4: + case 0x6: + case 0x7: + return "computer-desktop"; + + case 0x9: + case 0xA: + case 0xE: + return "computer-laptop"; + + case 0x11: + case 0x1C: + return "computer-server"; + } + +#endif + return NULL; +} + +static int write_data_hostname(void) { + const char *hn; + + if (isempty(data[PROP_HOSTNAME])) + hn = "localhost"; + else + hn = data[PROP_HOSTNAME]; + + if (sethostname(hn, strlen(hn)) < 0) + return -errno; + + return 0; +} + +static int write_data_static_hostname(void) { + + if (isempty(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]); +} + +static int write_data_other(void) { + + static const char * const name[_PROP_MAX] = { + [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME", + [PROP_ICON_NAME] = "ICON_NAME" + }; + + char **l = NULL; + int r, p; + + r = load_env_file("/etc/machine-info", &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = 2; p < _PROP_MAX; p++) { + char *t, **u; + + assert(name[p]); + + if (isempty(data[p])) { + strv_env_unset(l, name[p]); + continue; + } + + if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, t); + free(t); + strv_free(l); + + if (!u) + return -ENOMEM; + l = u; + } + + if (strv_isempty(l)) { + + if (unlink("/etc/machine-info") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file("/etc/machine-info", l); + strv_free(l); + + return r; +} + +static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) { + const char *name; + + assert(i); + assert(property); + + if (isempty(data[PROP_ICON_NAME])) + name = fallback_icon_name(); + else + name = data[PROP_ICON_NAME]; + + return bus_property_append_string(i, property, (void*) 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 const BusBoundProperties bps[] = { + { "org.freedesktop.hostname1", bus_hostname_properties, data }, + { NULL, } +}; + +static DBusHandlerResult hostname_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) { + 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 = data[PROP_STATIC_HOSTNAME]; + + if (isempty(name)) + name = "localhost"; + + if (!hostname_is_valid(name)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!streq_ptr(name, data[PROP_HOSTNAME])) { + char *h; + + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + h = strdup(name); + if (!h) + goto oom; + + free(data[PROP_HOSTNAME]); + data[PROP_HOSTNAME] = h; + + 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 host name to '%s'", strempty(data[PROP_HOSTNAME])); + + changed = bus_properties_changed_new( + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "Hostname\0"); + if (!changed) + goto oom; + } + + } 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, NULL, &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; + } + + } 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, NULL, &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; + + /* The icon name might ultimately be + * used as file name, so better be + * safe than sorry */ + if (k == PROP_ICON_NAME && !filename_is_safe(name)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + 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; + } + + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int connect_bus(DBusConnection **_bus) { + static const DBusObjectPathVTable hostname_vtable = { + .message_function = hostname_message_handler + }; + DBusError error; + DBusConnection *bus = NULL; + int r; + + 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; + } + + 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)) { + r = log_oom(); + goto fail; + } + + 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; + } + + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + log_error("Failed to acquire name."); + r = -EEXIST; + goto fail; + } + + if (_bus) + *_bus = bus; + + return 0; + +fail: + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_error_free(&error); + + return r; +} + +int main(int argc, char *argv[]) { + 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 + "<node>\n", stdout); + fputs(hostname_interface, stdout); + fputs("</node>\n", stdout); + return 0; + } + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + 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)); + goto finish; + } + + r = connect_bus(&bus); + if (r < 0) + goto finish; + + remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + for (;;) { + + if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) + break; + + if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { + exiting = true; + bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); + } + } + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/hostname/org.freedesktop.hostname1.conf b/src/hostname/org.freedesktop.hostname1.conf new file mode 100644 index 0000000000..46b4aadc83 --- /dev/null +++ b/src/hostname/org.freedesktop.hostname1.conf @@ -0,0 +1,27 @@ +<?xml version="1.0"?> <!--*-nxml-*--> +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> + +<!-- + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + 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. +--> + +<busconfig> + + <policy user="root"> + <allow own="org.freedesktop.hostname1"/> + <allow send_destination="org.freedesktop.hostname1"/> + <allow receive_sender="org.freedesktop.hostname1"/> + </policy> + + <policy context="default"> + <allow send_destination="org.freedesktop.hostname1"/> + <allow receive_sender="org.freedesktop.hostname1"/> + </policy> + +</busconfig> diff --git a/src/hostname/org.freedesktop.hostname1.policy.in b/src/hostname/org.freedesktop.hostname1.policy.in new file mode 100644 index 0000000000..df082d8e6f --- /dev/null +++ b/src/hostname/org.freedesktop.hostname1.policy.in @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*--> +<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> + +<!-- + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + 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. +--> + +<policyconfig> + + <vendor>The systemd Project</vendor> + <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> + + <action id="org.freedesktop.hostname1.set-hostname"> + <_description>Set host name</_description> + <_message>Authentication is required to set the local host name.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.hostname1.set-static-hostname"> + <_description>Set static host name</_description> + <_message>Authentication is required to set the statically configured local host name, as well as the pretty host name.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.hostname1.set-machine-info"> + <_description>Set machine information</_description> + <_message>Authentication is required to set local machine information.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + +</policyconfig> diff --git a/src/hostname/org.freedesktop.hostname1.service b/src/hostname/org.freedesktop.hostname1.service new file mode 100644 index 0000000000..6041ed60ca --- /dev/null +++ b/src/hostname/org.freedesktop.hostname1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# 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. + +[D-BUS Service] +Name=org.freedesktop.hostname1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.hostname1.service |