1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <dbus/dbus.h>
31 #include "dbus-common.h"
36 #include "fileio-label.h"
40 " <interface name=\"org.freedesktop.hostname1\">\n" \
41 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
44 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
45 " <property name=\"Chassis\" type=\"s\" access=\"read\"/>\n" \
46 " <method name=\"SetHostname\">\n" \
47 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
50 " <method name=\"SetStaticHostname\">\n" \
51 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
52 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
54 " <method name=\"SetPrettyHostname\">\n" \
55 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
56 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
58 " <method name=\"SetIconName\">\n" \
59 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
60 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
62 " <method name=\"SetChassis\">\n" \
63 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
64 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
68 #define INTROSPECTION \
69 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
72 BUS_PROPERTIES_INTERFACE \
73 BUS_INTROSPECTABLE_INTERFACE \
77 #define INTERFACES_LIST \
78 BUS_GENERIC_INTERFACES_LIST \
79 "org.freedesktop.hostname1\0"
81 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
92 static char *data[_PROP_MAX] = {
100 static usec_t remain_until = 0;
102 static void free_data(void) {
105 for (p = 0; p < _PROP_MAX; p++) {
111 static int read_data(void) {
116 data[PROP_HOSTNAME] = gethostname_malloc();
117 if (!data[PROP_HOSTNAME])
120 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
121 if (r < 0 && r != -ENOENT)
124 r = parse_env_file("/etc/machine-info", NEWLINE,
125 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
126 "ICON_NAME", &data[PROP_ICON_NAME],
127 "CHASSIS", &data[PROP_CHASSIS],
129 if (r < 0 && r != -ENOENT)
135 static bool check_nss(void) {
138 dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY);
147 static bool valid_chassis(const char *chassis) {
151 return nulstr_contains(
162 static const char* fallback_chassis(void) {
168 v = detect_virtualization(NULL);
170 if (v == VIRTUALIZATION_VM)
172 if (v == VIRTUALIZATION_CONTAINER)
175 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
179 r = safe_atou(type, &t);
184 /* We only list the really obvious cases here as the ACPI data
185 * is not really super reliable.
187 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
189 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
212 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
216 r = safe_atou(type, &t);
221 /* We only list the really obvious cases here. The DMI data is
222 unreliable enough, so let's not do any additional guesswork
225 See the SMBIOS Specification 2.7.1 section 7.4.1 for
226 details about the values listed here:
228 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
256 static char* fallback_icon_name(void) {
259 if (!isempty(data[PROP_CHASSIS]))
260 return strappend("computer-", data[PROP_CHASSIS]);
262 chassis = fallback_chassis();
264 return strappend("computer-", chassis);
266 return strdup("computer");
269 static int write_data_hostname(void) {
272 if (isempty(data[PROP_HOSTNAME]))
275 hn = data[PROP_HOSTNAME];
277 if (sethostname(hn, strlen(hn)) < 0)
283 static int write_data_static_hostname(void) {
285 if (isempty(data[PROP_STATIC_HOSTNAME])) {
287 if (unlink("/etc/hostname") < 0)
288 return errno == ENOENT ? 0 : -errno;
292 return write_string_file_atomic_label("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
295 static int write_data_other(void) {
297 static const char * const name[_PROP_MAX] = {
298 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
299 [PROP_ICON_NAME] = "ICON_NAME",
300 [PROP_CHASSIS] = "CHASSIS"
306 r = load_env_file("/etc/machine-info", NULL, &l);
307 if (r < 0 && r != -ENOENT)
310 for (p = 2; p < _PROP_MAX; p++) {
315 if (isempty(data[p])) {
316 strv_env_unset(l, name[p]);
320 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
325 u = strv_env_set(l, t);
334 if (strv_isempty(l)) {
336 if (unlink("/etc/machine-info") < 0)
337 return errno == ENOENT ? 0 : -errno;
342 r = write_env_file_label("/etc/machine-info", l);
348 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
350 _cleanup_free_ char *n = NULL;
355 if (isempty(data[PROP_ICON_NAME]))
356 name = n = fallback_icon_name();
358 name = data[PROP_ICON_NAME];
360 return bus_property_append_string(i, property, (void*) name);
363 static int bus_hostname_append_chassis(DBusMessageIter *i, const char *property, void *userdata) {
369 if (isempty(data[PROP_CHASSIS]))
370 name = fallback_chassis();
372 name = data[PROP_CHASSIS];
374 return bus_property_append_string(i, property, (void*) name);
377 static const BusProperty bus_hostname_properties[] = {
378 { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true },
379 { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true },
380 { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true },
381 { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true },
382 { "Chassis", bus_hostname_append_chassis, "s", sizeof(data[0])*PROP_CHASSIS, true },
386 static const BusBoundProperties bps[] = {
387 { "org.freedesktop.hostname1", bus_hostname_properties, data },
391 static DBusHandlerResult hostname_message_handler(
392 DBusConnection *connection,
393 DBusMessage *message,
397 DBusMessage *reply = NULL, *changed = NULL;
404 dbus_error_init(&error);
406 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
408 dbus_bool_t interactive;
410 if (!dbus_message_get_args(
413 DBUS_TYPE_STRING, &name,
414 DBUS_TYPE_BOOLEAN, &interactive,
416 return bus_send_error_reply(connection, message, &error, -EINVAL);
419 name = data[PROP_STATIC_HOSTNAME];
424 if (!hostname_is_valid(name))
425 return bus_send_error_reply(connection, message, NULL, -EINVAL);
427 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
430 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
432 return bus_send_error_reply(connection, message, &error, r);
438 free(data[PROP_HOSTNAME]);
439 data[PROP_HOSTNAME] = h;
441 r = write_data_hostname();
443 log_error("Failed to set host name: %s", strerror(-r));
444 return bus_send_error_reply(connection, message, NULL, r);
447 log_info("Changed host name to '%s'", strna(data[PROP_HOSTNAME]));
449 changed = bus_properties_changed_new(
450 "/org/freedesktop/hostname1",
451 "org.freedesktop.hostname1",
457 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
459 dbus_bool_t interactive;
461 if (!dbus_message_get_args(
464 DBUS_TYPE_STRING, &name,
465 DBUS_TYPE_BOOLEAN, &interactive,
467 return bus_send_error_reply(connection, message, &error, -EINVAL);
472 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
474 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
476 return bus_send_error_reply(connection, message, &error, r);
479 free(data[PROP_STATIC_HOSTNAME]);
480 data[PROP_STATIC_HOSTNAME] = NULL;
484 if (!hostname_is_valid(name))
485 return bus_send_error_reply(connection, message, NULL, -EINVAL);
491 free(data[PROP_STATIC_HOSTNAME]);
492 data[PROP_STATIC_HOSTNAME] = h;
495 r = write_data_static_hostname();
497 log_error("Failed to write static host name: %s", strerror(-r));
498 return bus_send_error_reply(connection, message, NULL, r);
501 log_info("Changed static host name to '%s'", strna(data[PROP_STATIC_HOSTNAME]));
503 changed = bus_properties_changed_new(
504 "/org/freedesktop/hostname1",
505 "org.freedesktop.hostname1",
511 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
512 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName") ||
513 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetChassis")) {
516 dbus_bool_t interactive;
519 if (!dbus_message_get_args(
522 DBUS_TYPE_STRING, &name,
523 DBUS_TYPE_BOOLEAN, &interactive,
525 return bus_send_error_reply(connection, message, &error, -EINVAL);
530 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME :
531 streq(dbus_message_get_member(message), "SetChassis") ? PROP_CHASSIS : PROP_ICON_NAME;
533 if (!streq_ptr(name, data[k])) {
535 /* Since the pretty hostname should always be
536 * changed at the same time as the static one,
537 * use the same policy action for both... */
539 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
540 "org.freedesktop.hostname1.set-static-hostname" :
541 "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error);
543 return bus_send_error_reply(connection, message, &error, r);
551 /* The icon name might ultimately be
552 * used as file name, so better be
554 if (k == PROP_ICON_NAME && !filename_is_safe(name))
555 return bus_send_error_reply(connection, message, NULL, -EINVAL);
556 if (k == PROP_PRETTY_HOSTNAME && string_has_cc(name))
557 return bus_send_error_reply(connection, message, NULL, -EINVAL);
558 if (k == PROP_CHASSIS && !valid_chassis(name))
559 return bus_send_error_reply(connection, message, NULL, -EINVAL);
569 r = write_data_other();
571 log_error("Failed to write machine info: %s", strerror(-r));
572 return bus_send_error_reply(connection, message, NULL, r);
575 log_info("Changed %s to '%s'",
576 k == PROP_PRETTY_HOSTNAME ? "pretty host name" :
577 k == PROP_CHASSIS ? "chassis" : "icon name", strna(data[k]));
579 changed = bus_properties_changed_new(
580 "/org/freedesktop/hostname1",
581 "org.freedesktop.hostname1",
582 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" :
583 k == PROP_CHASSIS ? "Chassis\0" : "IconName\0");
589 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
591 reply = dbus_message_new_method_return(message);
595 if (!bus_maybe_send_reply(connection, message, reply))
598 dbus_message_unref(reply);
603 if (!dbus_connection_send(connection, changed, NULL))
606 dbus_message_unref(changed);
609 return DBUS_HANDLER_RESULT_HANDLED;
613 dbus_message_unref(reply);
616 dbus_message_unref(changed);
618 dbus_error_free(&error);
620 return DBUS_HANDLER_RESULT_NEED_MEMORY;
623 static int connect_bus(DBusConnection **_bus) {
624 static const DBusObjectPathVTable hostname_vtable = {
625 .message_function = hostname_message_handler
628 DBusConnection *bus = NULL;
633 dbus_error_init(&error);
635 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
637 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
642 dbus_connection_set_exit_on_disconnect(bus, FALSE);
644 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
645 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
650 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
651 if (dbus_error_is_set(&error)) {
652 log_error("Failed to register name on bus: %s", bus_error_message(&error));
657 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
658 log_error("Failed to acquire name.");
669 dbus_connection_close(bus);
670 dbus_connection_unref(bus);
672 dbus_error_free(&error);
677 int main(int argc, char *argv[]) {
679 DBusConnection *bus = NULL;
680 bool exiting = false;
682 log_set_target(LOG_TARGET_AUTO);
683 log_parse_environment();
689 if (argc == 2 && streq(argv[1], "--introspect")) {
690 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
692 fputs(hostname_interface, stdout);
693 fputs("</node>\n", stdout);
698 log_error("This program takes no arguments.");
704 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
708 log_error("Failed to read hostname data: %s", strerror(-r));
712 r = connect_bus(&bus);
716 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
719 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
722 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
724 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
734 dbus_connection_flush(bus);
735 dbus_connection_close(bus);
736 dbus_connection_unref(bus);
739 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;