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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU 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 " <interface name=\"org.freedesktop.hostname1\">\n" \
37 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
38 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
39 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
41 " <method name=\"SetHostname\">\n" \
42 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
43 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
45 " <method name=\"SetStaticHostname\">\n" \
46 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
47 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
49 " <method name=\"SetPrettyHostname\">\n" \
50 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
51 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
53 " <method name=\"SetIconName\">\n" \
54 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
55 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
59 #define INTROSPECTION \
60 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
63 BUS_PROPERTIES_INTERFACE \
64 BUS_INTROSPECTABLE_INTERFACE \
68 #define INTERFACES_LIST \
69 BUS_GENERIC_INTERFACES_LIST \
70 "org.freedesktop.hostname1\0"
72 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
82 static char *data[_PROP_MAX] = {
89 static usec_t remain_until = 0;
91 static void free_data(void) {
94 for (p = 0; p < _PROP_MAX; p++) {
100 static int read_data(void) {
105 data[PROP_HOSTNAME] = gethostname_malloc();
106 if (!data[PROP_HOSTNAME])
109 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
110 if (r < 0 && r != -ENOENT)
113 r = parse_env_file("/etc/machine-info", NEWLINE,
114 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
115 "ICON_NAME", &data[PROP_ICON_NAME],
117 if (r < 0 && r != -ENOENT)
123 static bool check_nss(void) {
127 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
135 static const char* fallback_icon_name(void) {
137 #if defined(__i386__) || defined(__x86_64__)
143 if (detect_virtualization(NULL) > 0)
144 return "computer-vm";
146 #if defined(__i386__) || defined(__x86_64__)
147 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
151 r = safe_atou(type, &t);
157 /* We only list the really obvious cases here. The DMI data is
158 unreliable enough, so let's not do any additional guesswork
161 See the SMBIOS Specification 2.7.1 section 7.4.1 for
162 details about the values listed here:
164 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
173 return "computer-desktop";
178 return "computer-laptop";
182 return "computer-server";
189 static int write_data_hostname(void) {
192 if (isempty(data[PROP_HOSTNAME]))
195 hn = data[PROP_HOSTNAME];
197 if (sethostname(hn, strlen(hn)) < 0)
203 static int write_data_static_hostname(void) {
205 if (isempty(data[PROP_STATIC_HOSTNAME])) {
207 if (unlink("/etc/hostname") < 0)
208 return errno == ENOENT ? 0 : -errno;
213 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
216 static int write_data_other(void) {
218 static const char * const name[_PROP_MAX] = {
219 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
220 [PROP_ICON_NAME] = "ICON_NAME"
226 r = load_env_file("/etc/machine-info", &l);
227 if (r < 0 && r != -ENOENT)
230 for (p = 2; p < _PROP_MAX; p++) {
235 if (isempty(data[p])) {
236 l = strv_env_unset(l, name[p]);
240 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
245 u = strv_env_set(l, t);
254 if (strv_isempty(l)) {
256 if (unlink("/etc/machine-info") < 0)
257 return errno == ENOENT ? 0 : -errno;
262 r = write_env_file("/etc/machine-info", l);
268 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
274 if (isempty(data[PROP_ICON_NAME]))
275 name = fallback_icon_name();
277 name = data[PROP_ICON_NAME];
279 return bus_property_append_string(i, property, (void*) name);
282 static DBusHandlerResult hostname_message_handler(
283 DBusConnection *connection,
284 DBusMessage *message,
287 const BusProperty properties[] = {
288 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
289 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
290 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
291 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
292 { NULL, NULL, NULL, NULL, NULL }
295 DBusMessage *reply = NULL, *changed = NULL;
302 dbus_error_init(&error);
304 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
306 dbus_bool_t interactive;
308 if (!dbus_message_get_args(
311 DBUS_TYPE_STRING, &name,
312 DBUS_TYPE_BOOLEAN, &interactive,
314 return bus_send_error_reply(connection, message, &error, -EINVAL);
317 name = data[PROP_STATIC_HOSTNAME];
322 if (!hostname_is_valid(name))
323 return bus_send_error_reply(connection, message, NULL, -EINVAL);
325 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
328 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
330 return bus_send_error_reply(connection, message, &error, r);
336 free(data[PROP_HOSTNAME]);
337 data[PROP_HOSTNAME] = h;
339 r = write_data_hostname();
341 log_error("Failed to set host name: %s", strerror(-r));
342 return bus_send_error_reply(connection, message, NULL, r);
345 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
347 changed = bus_properties_changed_new(
348 "/org/freedesktop/hostname1",
349 "org.freedesktop.hostname1",
355 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
357 dbus_bool_t interactive;
359 if (!dbus_message_get_args(
362 DBUS_TYPE_STRING, &name,
363 DBUS_TYPE_BOOLEAN, &interactive,
365 return bus_send_error_reply(connection, message, &error, -EINVAL);
370 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
372 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
374 return bus_send_error_reply(connection, message, &error, r);
377 free(data[PROP_STATIC_HOSTNAME]);
378 data[PROP_STATIC_HOSTNAME] = NULL;
382 if (!hostname_is_valid(name))
383 return bus_send_error_reply(connection, message, NULL, -EINVAL);
389 free(data[PROP_STATIC_HOSTNAME]);
390 data[PROP_STATIC_HOSTNAME] = h;
393 r = write_data_static_hostname();
395 log_error("Failed to write static host name: %s", strerror(-r));
396 return bus_send_error_reply(connection, message, NULL, r);
399 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
401 changed = bus_properties_changed_new(
402 "/org/freedesktop/hostname1",
403 "org.freedesktop.hostname1",
409 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
410 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
413 dbus_bool_t interactive;
416 if (!dbus_message_get_args(
419 DBUS_TYPE_STRING, &name,
420 DBUS_TYPE_BOOLEAN, &interactive,
422 return bus_send_error_reply(connection, message, &error, -EINVAL);
427 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
429 if (!streq_ptr(name, data[k])) {
431 /* Since the pretty hostname should always be
432 * changed at the same time as the static one,
433 * use the same policy action for both... */
435 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
436 "org.freedesktop.hostname1.set-static-hostname" :
437 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
439 return bus_send_error_reply(connection, message, &error, r);
455 r = write_data_other();
457 log_error("Failed to write machine info: %s", strerror(-r));
458 return bus_send_error_reply(connection, message, NULL, r);
461 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
463 changed = bus_properties_changed_new(
464 "/org/freedesktop/hostname1",
465 "org.freedesktop.hostname1",
466 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
472 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
474 if (!(reply = dbus_message_new_method_return(message)))
477 if (!dbus_connection_send(connection, reply, NULL))
480 dbus_message_unref(reply);
485 if (!dbus_connection_send(connection, changed, NULL))
488 dbus_message_unref(changed);
491 return DBUS_HANDLER_RESULT_HANDLED;
495 dbus_message_unref(reply);
498 dbus_message_unref(changed);
500 dbus_error_free(&error);
502 return DBUS_HANDLER_RESULT_NEED_MEMORY;
505 static int connect_bus(DBusConnection **_bus) {
506 static const DBusObjectPathVTable hostname_vtable = {
507 .message_function = hostname_message_handler
510 DBusConnection *bus = NULL;
515 dbus_error_init(&error);
517 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
519 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
524 dbus_connection_set_exit_on_disconnect(bus, FALSE);
526 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
527 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
528 log_error("Not enough memory");
533 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
534 if (dbus_error_is_set(&error)) {
535 log_error("Failed to register name on bus: %s", bus_error_message(&error));
540 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
541 log_error("Failed to acquire name.");
552 dbus_connection_close(bus);
553 dbus_connection_unref(bus);
555 dbus_error_free(&error);
560 int main(int argc, char *argv[]) {
562 DBusConnection *bus = NULL;
563 bool exiting = false;
565 log_set_target(LOG_TARGET_AUTO);
566 log_parse_environment();
571 if (argc == 2 && streq(argv[1], "--introspect")) {
572 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
574 fputs(hostname_interface, stdout);
575 fputs("</node>\n", stdout);
580 log_error("This program takes no arguments.");
586 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
590 log_error("Failed to read hostname data: %s", strerror(-r));
594 r = connect_bus(&bus);
598 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
601 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
604 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
606 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
616 dbus_connection_flush(bus);
617 dbus_connection_close(bus);
618 dbus_connection_unref(bus);
621 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;