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"
33 #define INTROSPECTION \
34 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
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" \
58 BUS_PROPERTIES_INTERFACE \
59 BUS_INTROSPECTABLE_INTERFACE \
63 #define INTERFACES_LIST \
64 BUS_GENERIC_INTERFACES_LIST \
65 "org.freedesktop.hostname1\0"
75 static char *data[_PROP_MAX] = {
82 static void free_data(void) {
85 for (p = 0; p < _PROP_MAX; p++) {
91 static int read_data(void) {
96 data[PROP_HOSTNAME] = gethostname_malloc();
97 if (!data[PROP_HOSTNAME])
100 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
101 if (r < 0 && r != -ENOENT)
104 r = parse_env_file("/etc/machine-info", NEWLINE,
105 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
106 "ICON_NAME", &data[PROP_ICON_NAME],
108 if (r < 0 && r != -ENOENT)
114 static bool check_nss(void) {
118 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
126 static const char* fallback_icon_name(void) {
128 #if defined(__i386__) || defined(__x86_64__)
134 if (detect_virtualization(NULL) > 0)
135 return "computer-vm";
137 #if defined(__i386__) || defined(__x86_64__)
138 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
142 r = safe_atou(type, &t);
148 /* We only list the really obvious cases here. The DMI data is
149 unreliable enough, so let's not do any additional guesswork
152 See the SMBIOS Specification 2.7.1 section 7.4.1 for
153 details about the values listed here:
155 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
164 return "computer-desktop";
169 return "computer-laptop";
173 return "computer-server";
180 static int write_data_hostname(void) {
183 if (isempty(data[PROP_HOSTNAME]))
186 hn = data[PROP_HOSTNAME];
188 if (sethostname(hn, strlen(hn)) < 0)
194 static int write_data_static_hostname(void) {
196 if (isempty(data[PROP_STATIC_HOSTNAME])) {
198 if (unlink("/etc/hostname") < 0)
199 return errno == ENOENT ? 0 : -errno;
204 return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
207 static int write_data_other(void) {
209 static const char * const name[_PROP_MAX] = {
210 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
211 [PROP_ICON_NAME] = "ICON_NAME"
217 r = load_env_file("/etc/machine-info", &l);
218 if (r < 0 && r != -ENOENT)
221 for (p = 2; p < _PROP_MAX; p++) {
226 if (isempty(data[p])) {
227 l = strv_env_unset(l, name[p]);
231 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
236 u = strv_env_set(l, t);
245 if (strv_isempty(l)) {
247 if (unlink("/etc/machine-info") < 0)
248 return errno == ENOENT ? 0 : -errno;
253 r = write_env_file("/etc/machine-info", l);
259 /* This mimics dbus_bus_get_unix_user() */
260 static pid_t get_unix_process_id(
261 DBusConnection *connection,
265 DBusMessage *m = NULL, *reply = NULL;
268 m = dbus_message_new_method_call(
272 "GetConnectionUnixProcessID");
274 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
278 if (!dbus_message_append_args(
280 DBUS_TYPE_STRING, &name,
281 DBUS_TYPE_INVALID)) {
282 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
286 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
290 if (dbus_set_error_from_message(error, reply))
293 if (!dbus_message_get_args(
295 DBUS_TYPE_UINT32, &pid,
301 dbus_message_unref(m);
304 dbus_message_unref(reply);
309 static int verify_polkit(
311 DBusMessage *request,
316 DBusMessage *m = NULL, *reply = NULL;
317 const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
319 uint32_t flags = interactive ? 1 : 0;
322 unsigned long long starttime_raw;
323 uint64_t starttime_u64;
324 DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
326 dbus_bool_t authorized = FALSE;
331 sender = dbus_message_get_sender(request);
335 pid_raw = get_unix_process_id(c, sender, error);
339 r = get_starttime_of_pid(pid_raw, &starttime_raw);
343 m = dbus_message_new_method_call(
344 "org.freedesktop.PolicyKit1",
345 "/org/freedesktop/PolicyKit1/Authority",
346 "org.freedesktop.PolicyKit1.Authority",
347 "CheckAuthorization");
351 dbus_message_iter_init_append(m, &iter_msg);
353 pid_u32 = (uint32_t) pid_raw;
354 starttime_u64 = (uint64_t) starttime_raw;
356 if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
357 !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
358 !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
359 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
360 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
361 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
362 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
363 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
364 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
365 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
366 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
367 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
368 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
369 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
370 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
371 !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
372 !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
373 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
374 !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
375 !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
376 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
377 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
382 reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
388 if (dbus_set_error_from_message(error, reply)) {
393 if (!dbus_message_iter_init(reply, &iter_msg) ||
394 dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
399 dbus_message_iter_recurse(&iter_msg, &iter_struct);
401 if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
406 dbus_message_iter_get_basic(&iter_struct, &authorized);
408 r = authorized ? 0 : -EPERM;
413 dbus_message_unref(m);
416 dbus_message_unref(reply);
421 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
427 if (isempty(data[PROP_ICON_NAME]))
428 name = fallback_icon_name();
430 name = data[PROP_ICON_NAME];
432 return bus_property_append_string(i, property, (void*) name);
435 static DBusHandlerResult hostname_message_handler(
436 DBusConnection *connection,
437 DBusMessage *message,
440 const BusProperty properties[] = {
441 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
442 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
443 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
444 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
445 { NULL, NULL, NULL, NULL, NULL }
448 DBusMessage *reply = NULL, *changed = NULL;
455 dbus_error_init(&error);
457 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
459 dbus_bool_t interactive;
461 if (!dbus_message_get_args(
464 DBUS_TYPE_STRING, &name,
465 DBUS_TYPE_BOOLEAN, &interactive,
467 return bus_send_error_reply(connection, message, &error, -EINVAL);
470 name = data[PROP_STATIC_HOSTNAME];
475 if (!hostname_is_valid(name))
476 return bus_send_error_reply(connection, message, NULL, -EINVAL);
478 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
481 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
483 return bus_send_error_reply(connection, message, &error, r);
489 free(data[PROP_HOSTNAME]);
490 data[PROP_HOSTNAME] = h;
492 r = write_data_hostname();
494 log_error("Failed to set host name: %s", strerror(-r));
495 return bus_send_error_reply(connection, message, NULL, r);
498 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
500 changed = bus_properties_changed_new(
501 "/org/freedesktop/hostname1",
502 "org.freedesktop.hostname1",
508 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
510 dbus_bool_t interactive;
512 if (!dbus_message_get_args(
515 DBUS_TYPE_STRING, &name,
516 DBUS_TYPE_BOOLEAN, &interactive,
518 return bus_send_error_reply(connection, message, &error, -EINVAL);
523 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
525 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
527 return bus_send_error_reply(connection, message, &error, r);
530 free(data[PROP_STATIC_HOSTNAME]);
531 data[PROP_STATIC_HOSTNAME] = NULL;
535 if (!hostname_is_valid(name))
536 return bus_send_error_reply(connection, message, NULL, -EINVAL);
542 free(data[PROP_STATIC_HOSTNAME]);
543 data[PROP_STATIC_HOSTNAME] = h;
546 r = write_data_static_hostname();
548 log_error("Failed to write static host name: %s", strerror(-r));
549 return bus_send_error_reply(connection, message, NULL, r);
552 log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
554 changed = bus_properties_changed_new(
555 "/org/freedesktop/hostname1",
556 "org.freedesktop.hostname1",
562 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
563 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
566 dbus_bool_t interactive;
569 if (!dbus_message_get_args(
572 DBUS_TYPE_STRING, &name,
573 DBUS_TYPE_BOOLEAN, &interactive,
575 return bus_send_error_reply(connection, message, &error, -EINVAL);
580 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
582 if (!streq_ptr(name, data[k])) {
584 /* Since the pretty hostname should always be
585 * changed at the same time as the static one,
586 * use the same policy action for both... */
588 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
589 "org.freedesktop.hostname1.set-static-hostname" :
590 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
592 return bus_send_error_reply(connection, message, &error, r);
608 r = write_data_other();
610 log_error("Failed to write machine info: %s", strerror(-r));
611 return bus_send_error_reply(connection, message, NULL, r);
614 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
616 changed = bus_properties_changed_new(
617 "/org/freedesktop/hostname1",
618 "org.freedesktop.hostname1",
619 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
625 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
627 if (!(reply = dbus_message_new_method_return(message)))
630 if (!dbus_connection_send(connection, reply, NULL))
633 dbus_message_unref(reply);
638 if (!dbus_connection_send(connection, changed, NULL))
641 dbus_message_unref(changed);
644 return DBUS_HANDLER_RESULT_HANDLED;
648 dbus_message_unref(reply);
651 dbus_message_unref(changed);
653 dbus_error_free(&error);
655 return DBUS_HANDLER_RESULT_NEED_MEMORY;
658 static int connect_bus(DBusConnection **_bus) {
659 static const DBusObjectPathVTable hostname_vtable = {
660 .message_function = hostname_message_handler
663 DBusConnection *bus = NULL;
668 dbus_error_init(&error);
670 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
672 log_error("Failed to get system D-Bus connection: %s", error.message);
677 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
678 log_error("Not enough memory");
683 if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
684 log_error("Failed to register name on bus: %s", error.message);
695 dbus_connection_close(bus);
696 dbus_connection_unref(bus);
698 dbus_error_free(&error);
703 int main(int argc, char *argv[]) {
705 DBusConnection *bus = NULL;
707 log_set_target(LOG_TARGET_AUTO);
708 log_parse_environment();
712 log_error("This program takes no arguments.");
718 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
724 log_error("Failed to read hostname data: %s", strerror(-r));
728 r = connect_bus(&bus);
732 while (dbus_connection_read_write_dispatch(bus, -1))
741 dbus_connection_flush(bus);
742 dbus_connection_close(bus);
743 dbus_connection_unref(bus);
746 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;