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>
30 #include "dbus-common.h"
32 #define INTROSPECTION \
33 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
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" \
57 BUS_PROPERTIES_INTERFACE \
58 BUS_INTROSPECTABLE_INTERFACE \
62 #define INTERFACES_LIST \
63 BUS_GENERIC_INTERFACES_LIST \
64 "org.freedesktop.hostname1\0"
74 static char *data[_PROP_MAX] = {
81 static void free_data(void) {
84 for (p = 0; p < _PROP_MAX; p++) {
90 static int read_data(void) {
95 data[PROP_HOSTNAME] = gethostname_malloc();
96 if (!data[PROP_HOSTNAME])
99 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
100 if (r < 0 && r != -ENOENT)
103 r = parse_env_file("/etc/machine-info", NEWLINE,
104 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
105 "ICON_NAME", &data[PROP_ICON_NAME],
107 if (r < 0 && r != -ENOENT)
113 static const char* fallback_icon_name(void) {
115 #if defined(__i386__) || defined(__x86_64__)
121 if (detect_virtualization(NULL) > 0)
122 return "computer-vm";
124 #if defined(__i386__) || defined(__x86_64__)
125 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
129 r = safe_atou(type, &t);
135 /* We only list the really obvious cases here. The DMI data is
136 unreliable enough, so let's not do any additional guesswork
139 See the SMBIOS Specification 2.7.1 section 7.4.1 for
140 details about the values listed here:
142 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
151 return "computer-desktop";
156 return "computer-laptop";
160 return "computer-server";
167 static int write_data_hostname(void) {
170 if (isempty(data[PROP_HOSTNAME]))
173 hn = data[PROP_HOSTNAME];
175 if (sethostname(hn, strlen(hn)) < 0)
181 static int write_data_static_hostname(void) {
183 if (isempty(data[PROP_STATIC_HOSTNAME])) {
185 if (unlink("/etc/hostname") < 0)
186 return errno == ENOENT ? 0 : -errno;
191 return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
194 static int write_data_other(void) {
196 static const char * const name[_PROP_MAX] = {
197 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
198 [PROP_ICON_NAME] = "ICON_NAME"
204 r = load_env_file("/etc/machine-info", &l);
205 if (r < 0 && r != -ENOENT)
208 for (p = 2; p < _PROP_MAX; p++) {
213 if (isempty(data[p])) {
214 l = strv_env_unset(l, name[p]);
218 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
223 u = strv_env_set(l, t);
232 if (strv_isempty(l)) {
234 if (unlink("/etc/machine-info") < 0)
235 return errno == ENOENT ? 0 : -errno;
240 r = write_env_file("/etc/machine-info", l);
246 /* This mimics dbus_bus_get_unix_user() */
247 static pid_t get_unix_process_id(
248 DBusConnection *connection,
252 DBusMessage *m = NULL, *reply = NULL;
255 m = dbus_message_new_method_call(
259 "GetConnectionUnixProcessID");
261 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
265 if (!dbus_message_append_args(
267 DBUS_TYPE_STRING, &name,
268 DBUS_TYPE_INVALID)) {
269 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
273 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
277 if (dbus_set_error_from_message(error, reply))
280 if (!dbus_message_get_args(
282 DBUS_TYPE_UINT32, &pid,
288 dbus_message_unref(m);
291 dbus_message_unref(reply);
296 static int verify_polkit(
298 DBusMessage *request,
303 DBusMessage *m = NULL, *reply = NULL;
304 const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
306 uint32_t flags = interactive ? 1 : 0;
309 unsigned long long starttime_raw;
310 uint64_t starttime_u64;
311 DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
313 dbus_bool_t authorized = FALSE;
318 sender = dbus_message_get_sender(request);
322 pid_raw = get_unix_process_id(c, sender, error);
326 r = get_starttime_of_pid(pid_raw, &starttime_raw);
330 m = dbus_message_new_method_call(
331 "org.freedesktop.PolicyKit1",
332 "/org/freedesktop/PolicyKit1/Authority",
333 "org.freedesktop.PolicyKit1.Authority",
334 "CheckAuthorization");
338 dbus_message_iter_init_append(m, &iter_msg);
340 pid_u32 = (uint32_t) pid_raw;
341 starttime_u64 = (uint64_t) starttime_raw;
343 if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
344 !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
345 !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
346 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
347 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
348 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
349 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
350 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
351 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
352 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
353 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
354 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
355 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
356 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
357 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
358 !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
359 !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
360 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
361 !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
362 !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
363 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
364 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
369 reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
375 if (dbus_set_error_from_message(error, reply)) {
380 if (!dbus_message_iter_init(reply, &iter_msg) ||
381 dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
386 dbus_message_iter_recurse(&iter_msg, &iter_struct);
388 if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
393 dbus_message_iter_get_basic(&iter_struct, &authorized);
395 r = authorized ? 0 : -EPERM;
400 dbus_message_unref(m);
403 dbus_message_unref(reply);
408 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
414 if (isempty(data[PROP_ICON_NAME]))
415 name = fallback_icon_name();
417 name = data[PROP_ICON_NAME];
419 return bus_property_append_string(i, property, (void*) name);
422 static DBusHandlerResult hostname_message_handler(
423 DBusConnection *connection,
424 DBusMessage *message,
427 const BusProperty properties[] = {
428 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
429 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
430 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
431 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
432 { NULL, NULL, NULL, NULL, NULL }
435 DBusMessage *reply = NULL, *changed = NULL;
442 dbus_error_init(&error);
444 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
446 dbus_bool_t interactive;
448 if (!dbus_message_get_args(
451 DBUS_TYPE_STRING, &name,
452 DBUS_TYPE_BOOLEAN, &interactive,
454 return bus_send_error_reply(connection, message, &error, -EINVAL);
457 name = data[PROP_STATIC_HOSTNAME];
462 if (!hostname_is_valid(name))
463 return bus_send_error_reply(connection, message, NULL, -EINVAL);
465 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
468 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
470 return bus_send_error_reply(connection, message, &error, r);
476 free(data[PROP_HOSTNAME]);
477 data[PROP_HOSTNAME] = h;
479 r = write_data_hostname();
481 log_error("Failed to set host name: %s", strerror(-r));
482 return bus_send_error_reply(connection, message, NULL, r);
485 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
487 changed = bus_properties_changed_new(
488 "/org/freedesktop/hostname1",
489 "org.freedesktop.hostname1",
495 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
497 dbus_bool_t interactive;
499 if (!dbus_message_get_args(
502 DBUS_TYPE_STRING, &name,
503 DBUS_TYPE_BOOLEAN, &interactive,
505 return bus_send_error_reply(connection, message, &error, -EINVAL);
510 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
512 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
514 return bus_send_error_reply(connection, message, &error, r);
517 free(data[PROP_STATIC_HOSTNAME]);
518 data[PROP_STATIC_HOSTNAME] = NULL;
522 if (!hostname_is_valid(name))
523 return bus_send_error_reply(connection, message, NULL, -EINVAL);
529 free(data[PROP_STATIC_HOSTNAME]);
530 data[PROP_STATIC_HOSTNAME] = h;
533 r = write_data_static_hostname();
535 log_error("Failed to write static host name: %s", strerror(-r));
536 return bus_send_error_reply(connection, message, NULL, r);
539 log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
541 changed = bus_properties_changed_new(
542 "/org/freedesktop/hostname1",
543 "org.freedesktop.hostname1",
549 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
550 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
553 dbus_bool_t interactive;
556 if (!dbus_message_get_args(
559 DBUS_TYPE_STRING, &name,
560 DBUS_TYPE_BOOLEAN, &interactive,
562 return bus_send_error_reply(connection, message, &error, -EINVAL);
567 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
569 if (!streq_ptr(name, data[k])) {
571 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-machine-info", interactive, &error);
573 return bus_send_error_reply(connection, message, &error, r);
589 r = write_data_other();
591 log_error("Failed to write machine info: %s", strerror(-r));
592 return bus_send_error_reply(connection, message, NULL, r);
595 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
597 changed = bus_properties_changed_new(
598 "/org/freedesktop/hostname1",
599 "org.freedesktop.hostname1",
600 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
606 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
608 if (!(reply = dbus_message_new_method_return(message)))
611 if (!dbus_connection_send(connection, reply, NULL))
614 dbus_message_unref(reply);
619 if (!dbus_connection_send(connection, changed, NULL))
622 dbus_message_unref(changed);
625 return DBUS_HANDLER_RESULT_HANDLED;
629 dbus_message_unref(reply);
632 dbus_message_unref(changed);
634 dbus_error_free(&error);
636 return DBUS_HANDLER_RESULT_NEED_MEMORY;
639 int main(int argc, char *argv[]) {
640 const DBusObjectPathVTable hostname_vtable = {
641 .message_function = hostname_message_handler
644 DBusConnection *bus = NULL;
648 dbus_error_init(&error);
650 log_set_target(LOG_TARGET_AUTO);
651 log_parse_environment();
655 log_error("This program takes no arguments.");
664 log_error("Failed to read hostname data: %s", strerror(-r));
668 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
670 log_error("Failed to get system D-Bus connection: %s", error.message);
675 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
676 log_error("Not enough memory");
681 if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
682 log_error("Failed to register name on bus: %s", error.message);
687 while (dbus_connection_read_write_dispatch(bus, -1))
696 dbus_connection_flush(bus);
697 dbus_connection_close(bus);
698 dbus_connection_unref(bus);
701 dbus_error_free(&error);
703 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;