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"
37 " <interface name=\"org.freedesktop.hostname1\">\n" \
38 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
39 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
41 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"Chassis\" type=\"s\" access=\"read\"/>\n" \
43 " <method name=\"SetHostname\">\n" \
44 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
45 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
47 " <method name=\"SetStaticHostname\">\n" \
48 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
49 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
51 " <method name=\"SetPrettyHostname\">\n" \
52 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
53 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
55 " <method name=\"SetIconName\">\n" \
56 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
57 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
59 " <method name=\"SetChassis\">\n" \
60 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
61 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
65 #define INTROSPECTION \
66 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
69 BUS_PROPERTIES_INTERFACE \
70 BUS_INTROSPECTABLE_INTERFACE \
74 #define INTERFACES_LIST \
75 BUS_GENERIC_INTERFACES_LIST \
76 "org.freedesktop.hostname1\0"
78 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
89 static char *data[_PROP_MAX] = {
97 static usec_t remain_until = 0;
99 static void free_data(void) {
102 for (p = 0; p < _PROP_MAX; p++) {
108 static int read_data(void) {
113 data[PROP_HOSTNAME] = gethostname_malloc();
114 if (!data[PROP_HOSTNAME])
117 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
118 if (r < 0 && r != -ENOENT)
121 r = parse_env_file("/etc/machine-info", NEWLINE,
122 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
123 "ICON_NAME", &data[PROP_ICON_NAME],
124 "CHASSIS", &data[PROP_CHASSIS],
126 if (r < 0 && r != -ENOENT)
132 static bool check_nss(void) {
135 dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY);
144 static bool valid_chassis(const char *chassis) {
148 return nulstr_contains(
159 static const char* fallback_chassis(void) {
165 v = detect_virtualization(NULL);
167 if (v == VIRTUALIZATION_VM)
169 if (v == VIRTUALIZATION_CONTAINER)
172 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
176 r = safe_atou(type, &t);
181 /* We only list the really obvious cases here as the ACPI data
182 * is not really super reliable.
184 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
186 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
209 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
213 r = safe_atou(type, &t);
218 /* We only list the really obvious cases here. The DMI data is
219 unreliable enough, so let's not do any additional guesswork
222 See the SMBIOS Specification 2.7.1 section 7.4.1 for
223 details about the values listed here:
225 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
253 static char* fallback_icon_name(void) {
256 if (!isempty(data[PROP_CHASSIS]))
257 return strappend("computer-", data[PROP_CHASSIS]);
259 chassis = fallback_chassis();
261 return strappend("computer-", chassis);
263 return strdup("computer");
266 static int write_data_hostname(void) {
269 if (isempty(data[PROP_HOSTNAME]))
272 hn = data[PROP_HOSTNAME];
274 if (sethostname(hn, strlen(hn)) < 0)
280 static int write_data_static_hostname(void) {
282 if (isempty(data[PROP_STATIC_HOSTNAME])) {
284 if (unlink("/etc/hostname") < 0)
285 return errno == ENOENT ? 0 : -errno;
290 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
293 static int write_data_other(void) {
295 static const char * const name[_PROP_MAX] = {
296 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
297 [PROP_ICON_NAME] = "ICON_NAME",
298 [PROP_CHASSIS] = "CHASSIS"
304 r = load_env_file("/etc/machine-info", &l);
305 if (r < 0 && r != -ENOENT)
308 for (p = 2; p < _PROP_MAX; p++) {
313 if (isempty(data[p])) {
314 strv_env_unset(l, name[p]);
318 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
323 u = strv_env_set(l, t);
332 if (strv_isempty(l)) {
334 if (unlink("/etc/machine-info") < 0)
335 return errno == ENOENT ? 0 : -errno;
340 r = write_env_file("/etc/machine-info", l);
346 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
348 _cleanup_free_ char *n = NULL;
353 if (isempty(data[PROP_ICON_NAME]))
354 name = n = fallback_icon_name();
356 name = data[PROP_ICON_NAME];
358 return bus_property_append_string(i, property, (void*) name);
361 static int bus_hostname_append_chassis(DBusMessageIter *i, const char *property, void *userdata) {
367 if (isempty(data[PROP_CHASSIS]))
368 name = fallback_chassis();
370 name = data[PROP_CHASSIS];
372 return bus_property_append_string(i, property, (void*) name);
375 static const BusProperty bus_hostname_properties[] = {
376 { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true },
377 { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true },
378 { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true },
379 { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true },
380 { "Chassis", bus_hostname_append_chassis, "s", sizeof(data[0])*PROP_CHASSIS, true },
384 static const BusBoundProperties bps[] = {
385 { "org.freedesktop.hostname1", bus_hostname_properties, data },
389 static DBusHandlerResult hostname_message_handler(
390 DBusConnection *connection,
391 DBusMessage *message,
395 DBusMessage *reply = NULL, *changed = NULL;
402 dbus_error_init(&error);
404 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
406 dbus_bool_t interactive;
408 if (!dbus_message_get_args(
411 DBUS_TYPE_STRING, &name,
412 DBUS_TYPE_BOOLEAN, &interactive,
414 return bus_send_error_reply(connection, message, &error, -EINVAL);
417 name = data[PROP_STATIC_HOSTNAME];
422 if (!hostname_is_valid(name))
423 return bus_send_error_reply(connection, message, NULL, -EINVAL);
425 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
428 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
430 return bus_send_error_reply(connection, message, &error, r);
436 free(data[PROP_HOSTNAME]);
437 data[PROP_HOSTNAME] = h;
439 r = write_data_hostname();
441 log_error("Failed to set host name: %s", strerror(-r));
442 return bus_send_error_reply(connection, message, NULL, r);
445 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
447 changed = bus_properties_changed_new(
448 "/org/freedesktop/hostname1",
449 "org.freedesktop.hostname1",
455 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
457 dbus_bool_t interactive;
459 if (!dbus_message_get_args(
462 DBUS_TYPE_STRING, &name,
463 DBUS_TYPE_BOOLEAN, &interactive,
465 return bus_send_error_reply(connection, message, &error, -EINVAL);
470 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
472 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
474 return bus_send_error_reply(connection, message, &error, r);
477 free(data[PROP_STATIC_HOSTNAME]);
478 data[PROP_STATIC_HOSTNAME] = NULL;
482 if (!hostname_is_valid(name))
483 return bus_send_error_reply(connection, message, NULL, -EINVAL);
489 free(data[PROP_STATIC_HOSTNAME]);
490 data[PROP_STATIC_HOSTNAME] = h;
493 r = write_data_static_hostname();
495 log_error("Failed to write static host name: %s", strerror(-r));
496 return bus_send_error_reply(connection, message, NULL, r);
499 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
501 changed = bus_properties_changed_new(
502 "/org/freedesktop/hostname1",
503 "org.freedesktop.hostname1",
509 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
510 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName") ||
511 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetChassis")) {
514 dbus_bool_t interactive;
517 if (!dbus_message_get_args(
520 DBUS_TYPE_STRING, &name,
521 DBUS_TYPE_BOOLEAN, &interactive,
523 return bus_send_error_reply(connection, message, &error, -EINVAL);
528 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME :
529 streq(dbus_message_get_member(message), "SetChassis") ? PROP_CHASSIS : PROP_ICON_NAME;
531 if (!streq_ptr(name, data[k])) {
533 /* Since the pretty hostname should always be
534 * changed at the same time as the static one,
535 * use the same policy action for both... */
537 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
538 "org.freedesktop.hostname1.set-static-hostname" :
539 "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error);
541 return bus_send_error_reply(connection, message, &error, r);
549 /* The icon name might ultimately be
550 * used as file name, so better be
552 if (k == PROP_ICON_NAME && !filename_is_safe(name))
553 return bus_send_error_reply(connection, message, NULL, -EINVAL);
554 if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name))
555 return bus_send_error_reply(connection, message, NULL, -EINVAL);
556 if (k == PROP_CHASSIS && !valid_chassis(name))
557 return bus_send_error_reply(connection, message, NULL, -EINVAL);
567 r = write_data_other();
569 log_error("Failed to write machine info: %s", strerror(-r));
570 return bus_send_error_reply(connection, message, NULL, r);
573 log_info("Changed %s to '%s'",
574 k == PROP_PRETTY_HOSTNAME ? "pretty host name" :
575 k == PROP_CHASSIS ? "chassis" : "icon name", strempty(data[k]));
577 changed = bus_properties_changed_new(
578 "/org/freedesktop/hostname1",
579 "org.freedesktop.hostname1",
580 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" :
581 k == PROP_CHASSIS ? "Chassis\0" : "IconName\0");
587 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
589 reply = dbus_message_new_method_return(message);
593 if (!dbus_connection_send(connection, reply, NULL))
596 dbus_message_unref(reply);
601 if (!dbus_connection_send(connection, changed, NULL))
604 dbus_message_unref(changed);
607 return DBUS_HANDLER_RESULT_HANDLED;
611 dbus_message_unref(reply);
614 dbus_message_unref(changed);
616 dbus_error_free(&error);
618 return DBUS_HANDLER_RESULT_NEED_MEMORY;
621 static int connect_bus(DBusConnection **_bus) {
622 static const DBusObjectPathVTable hostname_vtable = {
623 .message_function = hostname_message_handler
626 DBusConnection *bus = NULL;
631 dbus_error_init(&error);
633 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
635 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
640 dbus_connection_set_exit_on_disconnect(bus, FALSE);
642 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
643 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
648 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
649 if (dbus_error_is_set(&error)) {
650 log_error("Failed to register name on bus: %s", bus_error_message(&error));
655 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
656 log_error("Failed to acquire name.");
667 dbus_connection_close(bus);
668 dbus_connection_unref(bus);
670 dbus_error_free(&error);
675 int main(int argc, char *argv[]) {
677 DBusConnection *bus = NULL;
678 bool exiting = false;
680 log_set_target(LOG_TARGET_AUTO);
681 log_parse_environment();
686 if (argc == 2 && streq(argv[1], "--introspect")) {
687 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
689 fputs(hostname_interface, stdout);
690 fputs("</node>\n", stdout);
695 log_error("This program takes no arguments.");
701 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
705 log_error("Failed to read hostname data: %s", strerror(-r));
709 r = connect_bus(&bus);
713 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
716 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
719 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
721 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
731 dbus_connection_flush(bus);
732 dbus_connection_close(bus);
733 dbus_connection_unref(bus);
736 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;