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"
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 DBusHandlerResult hostname_message_handler(
284 DBusConnection *connection,
285 DBusMessage *message,
288 const BusProperty properties[] = {
289 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
290 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
291 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
292 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
293 { NULL, NULL, NULL, NULL, NULL }
296 DBusMessage *reply = NULL, *changed = NULL;
303 dbus_error_init(&error);
305 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
307 dbus_bool_t interactive;
309 if (!dbus_message_get_args(
312 DBUS_TYPE_STRING, &name,
313 DBUS_TYPE_BOOLEAN, &interactive,
315 return bus_send_error_reply(connection, message, &error, -EINVAL);
318 name = data[PROP_STATIC_HOSTNAME];
323 if (!hostname_is_valid(name))
324 return bus_send_error_reply(connection, message, NULL, -EINVAL);
326 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
329 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
331 return bus_send_error_reply(connection, message, &error, r);
337 free(data[PROP_HOSTNAME]);
338 data[PROP_HOSTNAME] = h;
340 r = write_data_hostname();
342 log_error("Failed to set host name: %s", strerror(-r));
343 return bus_send_error_reply(connection, message, NULL, r);
346 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
348 changed = bus_properties_changed_new(
349 "/org/freedesktop/hostname1",
350 "org.freedesktop.hostname1",
356 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
358 dbus_bool_t interactive;
360 if (!dbus_message_get_args(
363 DBUS_TYPE_STRING, &name,
364 DBUS_TYPE_BOOLEAN, &interactive,
366 return bus_send_error_reply(connection, message, &error, -EINVAL);
371 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
373 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
375 return bus_send_error_reply(connection, message, &error, r);
378 free(data[PROP_STATIC_HOSTNAME]);
379 data[PROP_STATIC_HOSTNAME] = NULL;
383 if (!hostname_is_valid(name))
384 return bus_send_error_reply(connection, message, NULL, -EINVAL);
390 free(data[PROP_STATIC_HOSTNAME]);
391 data[PROP_STATIC_HOSTNAME] = h;
394 r = write_data_static_hostname();
396 log_error("Failed to write static host name: %s", strerror(-r));
397 return bus_send_error_reply(connection, message, NULL, r);
400 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
402 changed = bus_properties_changed_new(
403 "/org/freedesktop/hostname1",
404 "org.freedesktop.hostname1",
410 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
411 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
414 dbus_bool_t interactive;
417 if (!dbus_message_get_args(
420 DBUS_TYPE_STRING, &name,
421 DBUS_TYPE_BOOLEAN, &interactive,
423 return bus_send_error_reply(connection, message, &error, -EINVAL);
428 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
430 if (!streq_ptr(name, data[k])) {
432 /* Since the pretty hostname should always be
433 * changed at the same time as the static one,
434 * use the same policy action for both... */
436 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
437 "org.freedesktop.hostname1.set-static-hostname" :
438 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
440 return bus_send_error_reply(connection, message, &error, r);
456 r = write_data_other();
458 log_error("Failed to write machine info: %s", strerror(-r));
459 return bus_send_error_reply(connection, message, NULL, r);
462 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
464 changed = bus_properties_changed_new(
465 "/org/freedesktop/hostname1",
466 "org.freedesktop.hostname1",
467 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
473 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
475 if (!(reply = dbus_message_new_method_return(message)))
478 if (!dbus_connection_send(connection, reply, NULL))
481 dbus_message_unref(reply);
486 if (!dbus_connection_send(connection, changed, NULL))
489 dbus_message_unref(changed);
492 return DBUS_HANDLER_RESULT_HANDLED;
496 dbus_message_unref(reply);
499 dbus_message_unref(changed);
501 dbus_error_free(&error);
503 return DBUS_HANDLER_RESULT_NEED_MEMORY;
506 static int connect_bus(DBusConnection **_bus) {
507 static const DBusObjectPathVTable hostname_vtable = {
508 .message_function = hostname_message_handler
511 DBusConnection *bus = NULL;
516 dbus_error_init(&error);
518 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
520 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
525 dbus_connection_set_exit_on_disconnect(bus, FALSE);
527 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
528 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
529 log_error("Not enough memory");
534 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
535 if (dbus_error_is_set(&error)) {
536 log_error("Failed to register name on bus: %s", bus_error_message(&error));
541 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
542 log_error("Failed to acquire name.");
553 dbus_connection_close(bus);
554 dbus_connection_unref(bus);
556 dbus_error_free(&error);
561 int main(int argc, char *argv[]) {
563 DBusConnection *bus = NULL;
564 bool exiting = false;
566 log_set_target(LOG_TARGET_AUTO);
567 log_parse_environment();
572 if (argc == 2 && streq(argv[1], "--introspect")) {
573 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
575 fputs(hostname_interface, stdout);
576 fputs("</node>\n", stdout);
581 log_error("This program takes no arguments.");
587 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
591 log_error("Failed to read hostname data: %s", strerror(-r));
595 r = connect_bus(&bus);
599 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
602 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
605 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
607 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
617 dbus_connection_flush(bus);
618 dbus_connection_close(bus);
619 dbus_connection_unref(bus);
622 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;