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 " <method name=\"SetHostname\">\n" \
43 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
44 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
46 " <method name=\"SetStaticHostname\">\n" \
47 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
50 " <method name=\"SetPrettyHostname\">\n" \
51 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
52 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
54 " <method name=\"SetIconName\">\n" \
55 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
56 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
60 #define INTROSPECTION \
61 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
64 BUS_PROPERTIES_INTERFACE \
65 BUS_INTROSPECTABLE_INTERFACE \
69 #define INTERFACES_LIST \
70 BUS_GENERIC_INTERFACES_LIST \
71 "org.freedesktop.hostname1\0"
73 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
83 static char *data[_PROP_MAX] = {
90 static usec_t remain_until = 0;
92 static void free_data(void) {
95 for (p = 0; p < _PROP_MAX; p++) {
101 static int read_data(void) {
106 data[PROP_HOSTNAME] = gethostname_malloc();
107 if (!data[PROP_HOSTNAME])
110 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
111 if (r < 0 && r != -ENOENT)
114 r = parse_env_file("/etc/machine-info", NEWLINE,
115 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
116 "ICON_NAME", &data[PROP_ICON_NAME],
118 if (r < 0 && r != -ENOENT)
124 static bool check_nss(void) {
128 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
136 static const char* fallback_icon_name(void) {
138 #if defined(__i386__) || defined(__x86_64__)
144 if (detect_virtualization(NULL) > 0)
145 return "computer-vm";
147 #if defined(__i386__) || defined(__x86_64__)
148 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
152 r = safe_atou(type, &t);
158 /* We only list the really obvious cases here. The DMI data is
159 unreliable enough, so let's not do any additional guesswork
162 See the SMBIOS Specification 2.7.1 section 7.4.1 for
163 details about the values listed here:
165 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
174 return "computer-desktop";
179 return "computer-laptop";
183 return "computer-server";
190 static int write_data_hostname(void) {
193 if (isempty(data[PROP_HOSTNAME]))
196 hn = data[PROP_HOSTNAME];
198 if (sethostname(hn, strlen(hn)) < 0)
204 static int write_data_static_hostname(void) {
206 if (isempty(data[PROP_STATIC_HOSTNAME])) {
208 if (unlink("/etc/hostname") < 0)
209 return errno == ENOENT ? 0 : -errno;
214 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
217 static int write_data_other(void) {
219 static const char * const name[_PROP_MAX] = {
220 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
221 [PROP_ICON_NAME] = "ICON_NAME"
227 r = load_env_file("/etc/machine-info", &l);
228 if (r < 0 && r != -ENOENT)
231 for (p = 2; p < _PROP_MAX; p++) {
236 if (isempty(data[p])) {
237 strv_env_unset(l, name[p]);
241 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
246 u = strv_env_set(l, t);
255 if (strv_isempty(l)) {
257 if (unlink("/etc/machine-info") < 0)
258 return errno == ENOENT ? 0 : -errno;
263 r = write_env_file("/etc/machine-info", l);
269 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
275 if (isempty(data[PROP_ICON_NAME]))
276 name = fallback_icon_name();
278 name = data[PROP_ICON_NAME];
280 return bus_property_append_string(i, property, (void*) name);
283 static const BusProperty bus_hostname_properties[] = {
284 { "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true },
285 { "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true },
286 { "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true },
287 { "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true },
291 static const BusBoundProperties bps[] = {
292 { "org.freedesktop.hostname1", bus_hostname_properties, data },
296 static DBusHandlerResult hostname_message_handler(
297 DBusConnection *connection,
298 DBusMessage *message,
302 DBusMessage *reply = NULL, *changed = NULL;
309 dbus_error_init(&error);
311 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
313 dbus_bool_t interactive;
315 if (!dbus_message_get_args(
318 DBUS_TYPE_STRING, &name,
319 DBUS_TYPE_BOOLEAN, &interactive,
321 return bus_send_error_reply(connection, message, &error, -EINVAL);
324 name = data[PROP_STATIC_HOSTNAME];
329 if (!hostname_is_valid(name))
330 return bus_send_error_reply(connection, message, NULL, -EINVAL);
332 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
335 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
337 return bus_send_error_reply(connection, message, &error, r);
343 free(data[PROP_HOSTNAME]);
344 data[PROP_HOSTNAME] = h;
346 r = write_data_hostname();
348 log_error("Failed to set host name: %s", strerror(-r));
349 return bus_send_error_reply(connection, message, NULL, r);
352 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
354 changed = bus_properties_changed_new(
355 "/org/freedesktop/hostname1",
356 "org.freedesktop.hostname1",
362 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
364 dbus_bool_t interactive;
366 if (!dbus_message_get_args(
369 DBUS_TYPE_STRING, &name,
370 DBUS_TYPE_BOOLEAN, &interactive,
372 return bus_send_error_reply(connection, message, &error, -EINVAL);
377 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
379 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
381 return bus_send_error_reply(connection, message, &error, r);
384 free(data[PROP_STATIC_HOSTNAME]);
385 data[PROP_STATIC_HOSTNAME] = NULL;
389 if (!hostname_is_valid(name))
390 return bus_send_error_reply(connection, message, NULL, -EINVAL);
396 free(data[PROP_STATIC_HOSTNAME]);
397 data[PROP_STATIC_HOSTNAME] = h;
400 r = write_data_static_hostname();
402 log_error("Failed to write static host name: %s", strerror(-r));
403 return bus_send_error_reply(connection, message, NULL, r);
406 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
408 changed = bus_properties_changed_new(
409 "/org/freedesktop/hostname1",
410 "org.freedesktop.hostname1",
416 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
417 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
420 dbus_bool_t interactive;
423 if (!dbus_message_get_args(
426 DBUS_TYPE_STRING, &name,
427 DBUS_TYPE_BOOLEAN, &interactive,
429 return bus_send_error_reply(connection, message, &error, -EINVAL);
434 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
436 if (!streq_ptr(name, data[k])) {
438 /* Since the pretty hostname should always be
439 * changed at the same time as the static one,
440 * use the same policy action for both... */
442 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
443 "org.freedesktop.hostname1.set-static-hostname" :
444 "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error);
446 return bus_send_error_reply(connection, message, &error, r);
454 /* The icon name might ultimately be
455 * used as file name, so better be
457 if (k == PROP_ICON_NAME && !filename_is_safe(name))
458 return bus_send_error_reply(connection, message, NULL, -EINVAL);
459 if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name))
460 return bus_send_error_reply(connection, message, NULL, -EINVAL);
470 r = write_data_other();
472 log_error("Failed to write machine info: %s", strerror(-r));
473 return bus_send_error_reply(connection, message, NULL, r);
476 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
478 changed = bus_properties_changed_new(
479 "/org/freedesktop/hostname1",
480 "org.freedesktop.hostname1",
481 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
487 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
489 if (!(reply = dbus_message_new_method_return(message)))
492 if (!dbus_connection_send(connection, reply, NULL))
495 dbus_message_unref(reply);
500 if (!dbus_connection_send(connection, changed, NULL))
503 dbus_message_unref(changed);
506 return DBUS_HANDLER_RESULT_HANDLED;
510 dbus_message_unref(reply);
513 dbus_message_unref(changed);
515 dbus_error_free(&error);
517 return DBUS_HANDLER_RESULT_NEED_MEMORY;
520 static int connect_bus(DBusConnection **_bus) {
521 static const DBusObjectPathVTable hostname_vtable = {
522 .message_function = hostname_message_handler
525 DBusConnection *bus = NULL;
530 dbus_error_init(&error);
532 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
534 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
539 dbus_connection_set_exit_on_disconnect(bus, FALSE);
541 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
542 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
547 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
548 if (dbus_error_is_set(&error)) {
549 log_error("Failed to register name on bus: %s", bus_error_message(&error));
554 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
555 log_error("Failed to acquire name.");
566 dbus_connection_close(bus);
567 dbus_connection_unref(bus);
569 dbus_error_free(&error);
574 int main(int argc, char *argv[]) {
576 DBusConnection *bus = NULL;
577 bool exiting = false;
579 log_set_target(LOG_TARGET_AUTO);
580 log_parse_environment();
585 if (argc == 2 && streq(argv[1], "--introspect")) {
586 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
588 fputs(hostname_interface, stdout);
589 fputs("</node>\n", stdout);
594 log_error("This program takes no arguments.");
600 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
604 log_error("Failed to read hostname data: %s", strerror(-r));
608 r = connect_bus(&bus);
612 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
615 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
618 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
620 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
630 dbus_connection_flush(bus);
631 dbus_connection_close(bus);
632 dbus_connection_unref(bus);
635 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;