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"
38 " <interface name=\"org.freedesktop.hostname1\">\n" \
39 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
41 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"Chassis\" type=\"s\" access=\"read\"/>\n" \
44 " <method name=\"SetHostname\">\n" \
45 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
46 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
48 " <method name=\"SetStaticHostname\">\n" \
49 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
50 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
52 " <method name=\"SetPrettyHostname\">\n" \
53 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
54 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
56 " <method name=\"SetIconName\">\n" \
57 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
58 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
60 " <method name=\"SetChassis\">\n" \
61 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
62 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
66 #define INTROSPECTION \
67 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
70 BUS_PROPERTIES_INTERFACE \
71 BUS_INTROSPECTABLE_INTERFACE \
75 #define INTERFACES_LIST \
76 BUS_GENERIC_INTERFACES_LIST \
77 "org.freedesktop.hostname1\0"
79 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
90 static char *data[_PROP_MAX] = {
98 static usec_t remain_until = 0;
100 static void free_data(void) {
103 for (p = 0; p < _PROP_MAX; p++) {
109 static int read_data(void) {
114 data[PROP_HOSTNAME] = gethostname_malloc();
115 if (!data[PROP_HOSTNAME])
118 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
119 if (r < 0 && r != -ENOENT)
122 r = parse_env_file("/etc/machine-info", NEWLINE,
123 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
124 "ICON_NAME", &data[PROP_ICON_NAME],
125 "CHASSIS", &data[PROP_CHASSIS],
127 if (r < 0 && r != -ENOENT)
133 static bool check_nss(void) {
136 dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY);
145 static bool valid_chassis(const char *chassis) {
149 return nulstr_contains(
160 static const char* fallback_chassis(void) {
166 v = detect_virtualization(NULL);
168 if (v == VIRTUALIZATION_VM)
170 if (v == VIRTUALIZATION_CONTAINER)
173 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
177 r = safe_atou(type, &t);
182 /* We only list the really obvious cases here as the ACPI data
183 * is not really super reliable.
185 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
187 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
210 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
214 r = safe_atou(type, &t);
219 /* We only list the really obvious cases here. The DMI data is
220 unreliable enough, so let's not do any additional guesswork
223 See the SMBIOS Specification 2.7.1 section 7.4.1 for
224 details about the values listed here:
226 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
254 static char* fallback_icon_name(void) {
257 if (!isempty(data[PROP_CHASSIS]))
258 return strappend("computer-", data[PROP_CHASSIS]);
260 chassis = fallback_chassis();
262 return strappend("computer-", chassis);
264 return strdup("computer");
267 static int write_data_hostname(void) {
270 if (isempty(data[PROP_HOSTNAME]))
273 hn = data[PROP_HOSTNAME];
275 if (sethostname(hn, strlen(hn)) < 0)
281 static int write_data_static_hostname(void) {
283 if (isempty(data[PROP_STATIC_HOSTNAME])) {
285 if (unlink("/etc/hostname") < 0)
286 return errno == ENOENT ? 0 : -errno;
291 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
294 static int write_data_other(void) {
296 static const char * const name[_PROP_MAX] = {
297 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
298 [PROP_ICON_NAME] = "ICON_NAME",
299 [PROP_CHASSIS] = "CHASSIS"
305 r = load_env_file("/etc/machine-info", &l);
306 if (r < 0 && r != -ENOENT)
309 for (p = 2; p < _PROP_MAX; p++) {
314 if (isempty(data[p])) {
315 strv_env_unset(l, name[p]);
319 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
324 u = strv_env_set(l, t);
333 if (strv_isempty(l)) {
335 if (unlink("/etc/machine-info") < 0)
336 return errno == ENOENT ? 0 : -errno;
341 r = write_env_file("/etc/machine-info", l);
347 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
349 _cleanup_free_ char *n = NULL;
354 if (isempty(data[PROP_ICON_NAME]))
355 name = n = fallback_icon_name();
357 name = data[PROP_ICON_NAME];
359 return bus_property_append_string(i, property, (void*) name);
362 static int bus_hostname_append_chassis(DBusMessageIter *i, const char *property, void *userdata) {
368 if (isempty(data[PROP_CHASSIS]))
369 name = fallback_chassis();
371 name = data[PROP_CHASSIS];
373 return bus_property_append_string(i, property, (void*) name);
376 static const BusProperty bus_hostname_properties[] = {
377 { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true },
378 { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true },
379 { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true },
380 { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true },
381 { "Chassis", bus_hostname_append_chassis, "s", sizeof(data[0])*PROP_CHASSIS, true },
385 static const BusBoundProperties bps[] = {
386 { "org.freedesktop.hostname1", bus_hostname_properties, data },
390 static DBusHandlerResult hostname_message_handler(
391 DBusConnection *connection,
392 DBusMessage *message,
396 DBusMessage *reply = NULL, *changed = NULL;
403 dbus_error_init(&error);
405 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
407 dbus_bool_t interactive;
409 if (!dbus_message_get_args(
412 DBUS_TYPE_STRING, &name,
413 DBUS_TYPE_BOOLEAN, &interactive,
415 return bus_send_error_reply(connection, message, &error, -EINVAL);
418 name = data[PROP_STATIC_HOSTNAME];
423 if (!hostname_is_valid(name))
424 return bus_send_error_reply(connection, message, NULL, -EINVAL);
426 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
429 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
431 return bus_send_error_reply(connection, message, &error, r);
437 free(data[PROP_HOSTNAME]);
438 data[PROP_HOSTNAME] = h;
440 r = write_data_hostname();
442 log_error("Failed to set host name: %s", strerror(-r));
443 return bus_send_error_reply(connection, message, NULL, r);
446 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
448 changed = bus_properties_changed_new(
449 "/org/freedesktop/hostname1",
450 "org.freedesktop.hostname1",
456 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
458 dbus_bool_t interactive;
460 if (!dbus_message_get_args(
463 DBUS_TYPE_STRING, &name,
464 DBUS_TYPE_BOOLEAN, &interactive,
466 return bus_send_error_reply(connection, message, &error, -EINVAL);
471 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
473 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
475 return bus_send_error_reply(connection, message, &error, r);
478 free(data[PROP_STATIC_HOSTNAME]);
479 data[PROP_STATIC_HOSTNAME] = NULL;
483 if (!hostname_is_valid(name))
484 return bus_send_error_reply(connection, message, NULL, -EINVAL);
490 free(data[PROP_STATIC_HOSTNAME]);
491 data[PROP_STATIC_HOSTNAME] = h;
494 r = write_data_static_hostname();
496 log_error("Failed to write static host name: %s", strerror(-r));
497 return bus_send_error_reply(connection, message, NULL, r);
500 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
502 changed = bus_properties_changed_new(
503 "/org/freedesktop/hostname1",
504 "org.freedesktop.hostname1",
510 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
511 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName") ||
512 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetChassis")) {
515 dbus_bool_t interactive;
518 if (!dbus_message_get_args(
521 DBUS_TYPE_STRING, &name,
522 DBUS_TYPE_BOOLEAN, &interactive,
524 return bus_send_error_reply(connection, message, &error, -EINVAL);
529 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME :
530 streq(dbus_message_get_member(message), "SetChassis") ? PROP_CHASSIS : PROP_ICON_NAME;
532 if (!streq_ptr(name, data[k])) {
534 /* Since the pretty hostname should always be
535 * changed at the same time as the static one,
536 * use the same policy action for both... */
538 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
539 "org.freedesktop.hostname1.set-static-hostname" :
540 "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error);
542 return bus_send_error_reply(connection, message, &error, r);
550 /* The icon name might ultimately be
551 * used as file name, so better be
553 if (k == PROP_ICON_NAME && !filename_is_safe(name))
554 return bus_send_error_reply(connection, message, NULL, -EINVAL);
555 if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name))
556 return bus_send_error_reply(connection, message, NULL, -EINVAL);
557 if (k == PROP_CHASSIS && !valid_chassis(name))
558 return bus_send_error_reply(connection, message, NULL, -EINVAL);
568 r = write_data_other();
570 log_error("Failed to write machine info: %s", strerror(-r));
571 return bus_send_error_reply(connection, message, NULL, r);
574 log_info("Changed %s to '%s'",
575 k == PROP_PRETTY_HOSTNAME ? "pretty host name" :
576 k == PROP_CHASSIS ? "chassis" : "icon name", strempty(data[k]));
578 changed = bus_properties_changed_new(
579 "/org/freedesktop/hostname1",
580 "org.freedesktop.hostname1",
581 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" :
582 k == PROP_CHASSIS ? "Chassis\0" : "IconName\0");
588 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
590 reply = dbus_message_new_method_return(message);
594 if (!dbus_connection_send(connection, reply, NULL))
597 dbus_message_unref(reply);
602 if (!dbus_connection_send(connection, changed, NULL))
605 dbus_message_unref(changed);
608 return DBUS_HANDLER_RESULT_HANDLED;
612 dbus_message_unref(reply);
615 dbus_message_unref(changed);
617 dbus_error_free(&error);
619 return DBUS_HANDLER_RESULT_NEED_MEMORY;
622 static int connect_bus(DBusConnection **_bus) {
623 static const DBusObjectPathVTable hostname_vtable = {
624 .message_function = hostname_message_handler
627 DBusConnection *bus = NULL;
632 dbus_error_init(&error);
634 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
636 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
641 dbus_connection_set_exit_on_disconnect(bus, FALSE);
643 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
644 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
649 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
650 if (dbus_error_is_set(&error)) {
651 log_error("Failed to register name on bus: %s", bus_error_message(&error));
656 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
657 log_error("Failed to acquire name.");
668 dbus_connection_close(bus);
669 dbus_connection_unref(bus);
671 dbus_error_free(&error);
676 int main(int argc, char *argv[]) {
678 DBusConnection *bus = NULL;
679 bool exiting = false;
681 log_set_target(LOG_TARGET_AUTO);
682 log_parse_environment();
687 if (argc == 2 && streq(argv[1], "--introspect")) {
688 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
690 fputs(hostname_interface, stdout);
691 fputs("</node>\n", stdout);
696 log_error("This program takes no arguments.");
702 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
706 log_error("Failed to read hostname data: %s", strerror(-r));
710 r = connect_bus(&bus);
714 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
717 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
720 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
722 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
732 dbus_connection_flush(bus);
733 dbus_connection_close(bus);
734 dbus_connection_unref(bus);
737 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;