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"
35 " <interface name=\"org.freedesktop.hostname1\">\n" \
36 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
37 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
38 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
39 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
40 " <method name=\"SetHostname\">\n" \
41 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
42 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
44 " <method name=\"SetStaticHostname\">\n" \
45 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
46 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
48 " <method name=\"SetPrettyHostname\">\n" \
49 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
50 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
52 " <method name=\"SetIconName\">\n" \
53 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
54 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
58 #define INTROSPECTION \
59 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
62 BUS_PROPERTIES_INTERFACE \
63 BUS_INTROSPECTABLE_INTERFACE \
67 #define INTERFACES_LIST \
68 BUS_GENERIC_INTERFACES_LIST \
69 "org.freedesktop.hostname1\0"
71 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
81 static char *data[_PROP_MAX] = {
88 static void free_data(void) {
91 for (p = 0; p < _PROP_MAX; p++) {
97 static int read_data(void) {
102 data[PROP_HOSTNAME] = gethostname_malloc();
103 if (!data[PROP_HOSTNAME])
106 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
107 if (r < 0 && r != -ENOENT)
110 r = parse_env_file("/etc/machine-info", NEWLINE,
111 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
112 "ICON_NAME", &data[PROP_ICON_NAME],
114 if (r < 0 && r != -ENOENT)
120 static bool check_nss(void) {
124 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
132 static const char* fallback_icon_name(void) {
134 #if defined(__i386__) || defined(__x86_64__)
140 if (detect_virtualization(NULL) > 0)
141 return "computer-vm";
143 #if defined(__i386__) || defined(__x86_64__)
144 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
148 r = safe_atou(type, &t);
154 /* We only list the really obvious cases here. The DMI data is
155 unreliable enough, so let's not do any additional guesswork
158 See the SMBIOS Specification 2.7.1 section 7.4.1 for
159 details about the values listed here:
161 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
170 return "computer-desktop";
175 return "computer-laptop";
179 return "computer-server";
186 static int write_data_hostname(void) {
189 if (isempty(data[PROP_HOSTNAME]))
192 hn = data[PROP_HOSTNAME];
194 if (sethostname(hn, strlen(hn)) < 0)
200 static int write_data_static_hostname(void) {
202 if (isempty(data[PROP_STATIC_HOSTNAME])) {
204 if (unlink("/etc/hostname") < 0)
205 return errno == ENOENT ? 0 : -errno;
210 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
213 static int write_data_other(void) {
215 static const char * const name[_PROP_MAX] = {
216 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
217 [PROP_ICON_NAME] = "ICON_NAME"
223 r = load_env_file("/etc/machine-info", &l);
224 if (r < 0 && r != -ENOENT)
227 for (p = 2; p < _PROP_MAX; p++) {
232 if (isempty(data[p])) {
233 l = strv_env_unset(l, name[p]);
237 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
242 u = strv_env_set(l, t);
251 if (strv_isempty(l)) {
253 if (unlink("/etc/machine-info") < 0)
254 return errno == ENOENT ? 0 : -errno;
259 r = write_env_file("/etc/machine-info", l);
265 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
271 if (isempty(data[PROP_ICON_NAME]))
272 name = fallback_icon_name();
274 name = data[PROP_ICON_NAME];
276 return bus_property_append_string(i, property, (void*) name);
279 static DBusHandlerResult hostname_message_handler(
280 DBusConnection *connection,
281 DBusMessage *message,
284 const BusProperty properties[] = {
285 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
286 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
287 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
288 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
289 { NULL, NULL, NULL, NULL, NULL }
292 DBusMessage *reply = NULL, *changed = NULL;
299 dbus_error_init(&error);
301 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
303 dbus_bool_t interactive;
305 if (!dbus_message_get_args(
308 DBUS_TYPE_STRING, &name,
309 DBUS_TYPE_BOOLEAN, &interactive,
311 return bus_send_error_reply(connection, message, &error, -EINVAL);
314 name = data[PROP_STATIC_HOSTNAME];
319 if (!hostname_is_valid(name))
320 return bus_send_error_reply(connection, message, NULL, -EINVAL);
322 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
325 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
327 return bus_send_error_reply(connection, message, &error, r);
333 free(data[PROP_HOSTNAME]);
334 data[PROP_HOSTNAME] = h;
336 r = write_data_hostname();
338 log_error("Failed to set host name: %s", strerror(-r));
339 return bus_send_error_reply(connection, message, NULL, r);
342 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
344 changed = bus_properties_changed_new(
345 "/org/freedesktop/hostname1",
346 "org.freedesktop.hostname1",
352 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
354 dbus_bool_t interactive;
356 if (!dbus_message_get_args(
359 DBUS_TYPE_STRING, &name,
360 DBUS_TYPE_BOOLEAN, &interactive,
362 return bus_send_error_reply(connection, message, &error, -EINVAL);
367 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
369 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
371 return bus_send_error_reply(connection, message, &error, r);
374 free(data[PROP_STATIC_HOSTNAME]);
375 data[PROP_STATIC_HOSTNAME] = NULL;
379 if (!hostname_is_valid(name))
380 return bus_send_error_reply(connection, message, NULL, -EINVAL);
386 free(data[PROP_STATIC_HOSTNAME]);
387 data[PROP_STATIC_HOSTNAME] = h;
390 r = write_data_static_hostname();
392 log_error("Failed to write static host name: %s", strerror(-r));
393 return bus_send_error_reply(connection, message, NULL, r);
396 log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
398 changed = bus_properties_changed_new(
399 "/org/freedesktop/hostname1",
400 "org.freedesktop.hostname1",
406 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
407 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
410 dbus_bool_t interactive;
413 if (!dbus_message_get_args(
416 DBUS_TYPE_STRING, &name,
417 DBUS_TYPE_BOOLEAN, &interactive,
419 return bus_send_error_reply(connection, message, &error, -EINVAL);
424 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
426 if (!streq_ptr(name, data[k])) {
428 /* Since the pretty hostname should always be
429 * changed at the same time as the static one,
430 * use the same policy action for both... */
432 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
433 "org.freedesktop.hostname1.set-static-hostname" :
434 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
436 return bus_send_error_reply(connection, message, &error, r);
452 r = write_data_other();
454 log_error("Failed to write machine info: %s", strerror(-r));
455 return bus_send_error_reply(connection, message, NULL, r);
458 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
460 changed = bus_properties_changed_new(
461 "/org/freedesktop/hostname1",
462 "org.freedesktop.hostname1",
463 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
469 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
471 if (!(reply = dbus_message_new_method_return(message)))
474 if (!dbus_connection_send(connection, reply, NULL))
477 dbus_message_unref(reply);
482 if (!dbus_connection_send(connection, changed, NULL))
485 dbus_message_unref(changed);
488 return DBUS_HANDLER_RESULT_HANDLED;
492 dbus_message_unref(reply);
495 dbus_message_unref(changed);
497 dbus_error_free(&error);
499 return DBUS_HANDLER_RESULT_NEED_MEMORY;
502 static int connect_bus(DBusConnection **_bus) {
503 static const DBusObjectPathVTable hostname_vtable = {
504 .message_function = hostname_message_handler
507 DBusConnection *bus = NULL;
512 dbus_error_init(&error);
514 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
516 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
521 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
522 log_error("Not enough memory");
527 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
528 if (dbus_error_is_set(&error)) {
529 log_error("Failed to register name on bus: %s", bus_error_message(&error));
534 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
535 log_error("Failed to acquire name.");
546 dbus_connection_close(bus);
547 dbus_connection_unref(bus);
549 dbus_error_free(&error);
554 int main(int argc, char *argv[]) {
556 DBusConnection *bus = NULL;
558 log_set_target(LOG_TARGET_AUTO);
559 log_parse_environment();
564 if (argc == 2 && streq(argv[1], "--introspect")) {
565 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
567 fputs(hostname_interface, stdout);
568 fputs("</node>\n", stdout);
573 log_error("This program takes no arguments.");
579 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
583 log_error("Failed to read hostname data: %s", strerror(-r));
587 r = connect_bus(&bus);
591 while (dbus_connection_read_write_dispatch(bus, -1))
600 dbus_connection_flush(bus);
601 dbus_connection_close(bus);
602 dbus_connection_unref(bus);
605 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;