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"
34 #define INTROSPECTION \
35 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
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" \
59 BUS_PROPERTIES_INTERFACE \
60 BUS_INTROSPECTABLE_INTERFACE \
64 #define INTERFACES_LIST \
65 BUS_GENERIC_INTERFACES_LIST \
66 "org.freedesktop.hostname1\0"
76 static char *data[_PROP_MAX] = {
83 static void free_data(void) {
86 for (p = 0; p < _PROP_MAX; p++) {
92 static int read_data(void) {
97 data[PROP_HOSTNAME] = gethostname_malloc();
98 if (!data[PROP_HOSTNAME])
101 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
102 if (r < 0 && r != -ENOENT)
105 r = parse_env_file("/etc/machine-info", NEWLINE,
106 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
107 "ICON_NAME", &data[PROP_ICON_NAME],
109 if (r < 0 && r != -ENOENT)
115 static bool check_nss(void) {
119 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
127 static const char* fallback_icon_name(void) {
129 #if defined(__i386__) || defined(__x86_64__)
135 if (detect_virtualization(NULL) > 0)
136 return "computer-vm";
138 #if defined(__i386__) || defined(__x86_64__)
139 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
143 r = safe_atou(type, &t);
149 /* We only list the really obvious cases here. The DMI data is
150 unreliable enough, so let's not do any additional guesswork
153 See the SMBIOS Specification 2.7.1 section 7.4.1 for
154 details about the values listed here:
156 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
165 return "computer-desktop";
170 return "computer-laptop";
174 return "computer-server";
181 static int write_data_hostname(void) {
184 if (isempty(data[PROP_HOSTNAME]))
187 hn = data[PROP_HOSTNAME];
189 if (sethostname(hn, strlen(hn)) < 0)
195 static int write_data_static_hostname(void) {
197 if (isempty(data[PROP_STATIC_HOSTNAME])) {
199 if (unlink("/etc/hostname") < 0)
200 return errno == ENOENT ? 0 : -errno;
205 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
208 static int write_data_other(void) {
210 static const char * const name[_PROP_MAX] = {
211 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
212 [PROP_ICON_NAME] = "ICON_NAME"
218 r = load_env_file("/etc/machine-info", &l);
219 if (r < 0 && r != -ENOENT)
222 for (p = 2; p < _PROP_MAX; p++) {
227 if (isempty(data[p])) {
228 l = strv_env_unset(l, name[p]);
232 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
237 u = strv_env_set(l, t);
246 if (strv_isempty(l)) {
248 if (unlink("/etc/machine-info") < 0)
249 return errno == ENOENT ? 0 : -errno;
254 r = write_env_file("/etc/machine-info", l);
260 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
266 if (isempty(data[PROP_ICON_NAME]))
267 name = fallback_icon_name();
269 name = data[PROP_ICON_NAME];
271 return bus_property_append_string(i, property, (void*) name);
274 static DBusHandlerResult hostname_message_handler(
275 DBusConnection *connection,
276 DBusMessage *message,
279 const BusProperty properties[] = {
280 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
281 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
282 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
283 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
284 { NULL, NULL, NULL, NULL, NULL }
287 DBusMessage *reply = NULL, *changed = NULL;
294 dbus_error_init(&error);
296 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
298 dbus_bool_t interactive;
300 if (!dbus_message_get_args(
303 DBUS_TYPE_STRING, &name,
304 DBUS_TYPE_BOOLEAN, &interactive,
306 return bus_send_error_reply(connection, message, &error, -EINVAL);
309 name = data[PROP_STATIC_HOSTNAME];
314 if (!hostname_is_valid(name))
315 return bus_send_error_reply(connection, message, NULL, -EINVAL);
317 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
320 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
322 return bus_send_error_reply(connection, message, &error, r);
328 free(data[PROP_HOSTNAME]);
329 data[PROP_HOSTNAME] = h;
331 r = write_data_hostname();
333 log_error("Failed to set host name: %s", strerror(-r));
334 return bus_send_error_reply(connection, message, NULL, r);
337 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
339 changed = bus_properties_changed_new(
340 "/org/freedesktop/hostname1",
341 "org.freedesktop.hostname1",
347 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
349 dbus_bool_t interactive;
351 if (!dbus_message_get_args(
354 DBUS_TYPE_STRING, &name,
355 DBUS_TYPE_BOOLEAN, &interactive,
357 return bus_send_error_reply(connection, message, &error, -EINVAL);
362 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
364 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
366 return bus_send_error_reply(connection, message, &error, r);
369 free(data[PROP_STATIC_HOSTNAME]);
370 data[PROP_STATIC_HOSTNAME] = NULL;
374 if (!hostname_is_valid(name))
375 return bus_send_error_reply(connection, message, NULL, -EINVAL);
381 free(data[PROP_STATIC_HOSTNAME]);
382 data[PROP_STATIC_HOSTNAME] = h;
385 r = write_data_static_hostname();
387 log_error("Failed to write static host name: %s", strerror(-r));
388 return bus_send_error_reply(connection, message, NULL, r);
391 log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
393 changed = bus_properties_changed_new(
394 "/org/freedesktop/hostname1",
395 "org.freedesktop.hostname1",
401 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
402 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
405 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);
419 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
421 if (!streq_ptr(name, data[k])) {
423 /* Since the pretty hostname should always be
424 * changed at the same time as the static one,
425 * use the same policy action for both... */
427 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
428 "org.freedesktop.hostname1.set-static-hostname" :
429 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
431 return bus_send_error_reply(connection, message, &error, r);
447 r = write_data_other();
449 log_error("Failed to write machine info: %s", strerror(-r));
450 return bus_send_error_reply(connection, message, NULL, r);
453 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
455 changed = bus_properties_changed_new(
456 "/org/freedesktop/hostname1",
457 "org.freedesktop.hostname1",
458 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
464 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
466 if (!(reply = dbus_message_new_method_return(message)))
469 if (!dbus_connection_send(connection, reply, NULL))
472 dbus_message_unref(reply);
477 if (!dbus_connection_send(connection, changed, NULL))
480 dbus_message_unref(changed);
483 return DBUS_HANDLER_RESULT_HANDLED;
487 dbus_message_unref(reply);
490 dbus_message_unref(changed);
492 dbus_error_free(&error);
494 return DBUS_HANDLER_RESULT_NEED_MEMORY;
497 static int connect_bus(DBusConnection **_bus) {
498 static const DBusObjectPathVTable hostname_vtable = {
499 .message_function = hostname_message_handler
502 DBusConnection *bus = NULL;
507 dbus_error_init(&error);
509 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
511 log_error("Failed to get system D-Bus connection: %s", error.message);
516 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
517 log_error("Not enough memory");
522 if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
523 log_error("Failed to register name on bus: %s", error.message);
534 dbus_connection_close(bus);
535 dbus_connection_unref(bus);
537 dbus_error_free(&error);
542 int main(int argc, char *argv[]) {
544 DBusConnection *bus = NULL;
546 log_set_target(LOG_TARGET_AUTO);
547 log_parse_environment();
551 log_error("This program takes no arguments.");
557 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
563 log_error("Failed to read hostname data: %s", strerror(-r));
567 r = connect_bus(&bus);
571 while (dbus_connection_read_write_dispatch(bus, -1))
580 dbus_connection_flush(bus);
581 dbus_connection_close(bus);
582 dbus_connection_unref(bus);
585 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;