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) {
195 static const char * const name[_PROP_MAX] = {
196 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
197 [PROP_ICON_NAME] = "ICON_NAME"
203 r = load_env_file("/etc/machine-info", &l);
204 if (r < 0 && r != -ENOENT)
207 for (p = 2; p < _PROP_MAX; p++) {
212 if (isempty(data[p])) {
213 l = strv_env_unset(l, name[p]);
217 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
222 u = strv_env_set(l, t);
231 if (strv_isempty(l)) {
233 if (unlink("/etc/machine-info") < 0)
234 return errno == ENOENT ? 0 : -errno;
239 r = write_env_file("/etc/machine-info", l);
245 /* This mimics dbus_bus_get_unix_user() */
246 static pid_t get_unix_process_id(
247 DBusConnection *connection,
251 DBusMessage *m = NULL, *reply = NULL;
254 m = dbus_message_new_method_call(
258 "GetConnectionUnixProcessID");
260 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
264 if (!dbus_message_append_args(
266 DBUS_TYPE_STRING, &name,
267 DBUS_TYPE_INVALID)) {
268 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
272 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
276 if (dbus_set_error_from_message(error, reply))
279 if (!dbus_message_get_args(
281 DBUS_TYPE_UINT32, &pid,
287 dbus_message_unref(m);
290 dbus_message_unref(reply);
295 static int verify_polkit(
297 DBusMessage *request,
302 DBusMessage *m = NULL, *reply = NULL;
303 const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
305 uint32_t flags = interactive ? 1 : 0;
308 unsigned long long starttime_raw;
309 uint64_t starttime_u64;
310 DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
312 dbus_bool_t authorized = FALSE;
317 sender = dbus_message_get_sender(request);
321 pid_raw = get_unix_process_id(c, sender, error);
325 r = get_starttime_of_pid(pid_raw, &starttime_raw);
329 m = dbus_message_new_method_call(
330 "org.freedesktop.PolicyKit1",
331 "/org/freedesktop/PolicyKit1/Authority",
332 "org.freedesktop.PolicyKit1.Authority",
333 "CheckAuthorization");
337 dbus_message_iter_init_append(m, &iter_msg);
339 pid_u32 = (uint32_t) pid_raw;
340 starttime_u64 = (uint64_t) starttime_raw;
342 if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
343 !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
344 !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
345 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
346 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
347 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
348 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
349 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
350 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
351 !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
352 !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
353 !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
354 !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
355 !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
356 !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
357 !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
358 !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
359 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
360 !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
361 !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
362 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
363 !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
368 reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
374 if (dbus_set_error_from_message(error, reply)) {
379 if (!dbus_message_iter_init(reply, &iter_msg) ||
380 dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
385 dbus_message_iter_recurse(&iter_msg, &iter_struct);
387 if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
392 dbus_message_iter_get_basic(&iter_struct, &authorized);
394 r = authorized ? 0 : -EPERM;
399 dbus_message_unref(m);
402 dbus_message_unref(reply);
407 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
413 if (isempty(data[PROP_ICON_NAME]))
414 name = fallback_icon_name();
416 name = data[PROP_ICON_NAME];
418 return bus_property_append_string(i, property, (void*) name);
421 static DBusHandlerResult hostname_message_handler(
422 DBusConnection *connection,
423 DBusMessage *message,
426 const BusProperty properties[] = {
427 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
428 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
429 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
430 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
431 { NULL, NULL, NULL, NULL, NULL }
434 DBusMessage *reply = NULL, *changed = NULL;
441 dbus_error_init(&error);
443 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
445 dbus_bool_t interactive;
447 if (!dbus_message_get_args(
450 DBUS_TYPE_STRING, &name,
451 DBUS_TYPE_BOOLEAN, &interactive,
453 return bus_send_error_reply(connection, message, &error, -EINVAL);
456 name = data[PROP_STATIC_HOSTNAME];
461 if (!hostname_is_valid(name))
462 return bus_send_error_reply(connection, message, NULL, -EINVAL);
464 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
467 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
469 return bus_send_error_reply(connection, message, &error, r);
475 free(data[PROP_HOSTNAME]);
476 data[PROP_HOSTNAME] = h;
478 r = write_data_hostname();
480 return bus_send_error_reply(connection, message, NULL, r);
482 log_info("Changed host name to '%s'", data[PROP_HOSTNAME]);
484 changed = bus_properties_changed_new(
485 "/org/freedesktop/hostname1",
486 "org.freedesktop.hostname1",
492 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
494 dbus_bool_t interactive;
496 if (!dbus_message_get_args(
499 DBUS_TYPE_STRING, &name,
500 DBUS_TYPE_BOOLEAN, &interactive,
502 return bus_send_error_reply(connection, message, &error, -EINVAL);
507 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
509 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
511 return bus_send_error_reply(connection, message, &error, r);
514 free(data[PROP_STATIC_HOSTNAME]);
515 data[PROP_STATIC_HOSTNAME] = NULL;
519 if (!hostname_is_valid(name))
520 return bus_send_error_reply(connection, message, NULL, -EINVAL);
526 free(data[PROP_STATIC_HOSTNAME]);
527 data[PROP_STATIC_HOSTNAME] = h;
530 r = write_data_static_hostname();
532 return bus_send_error_reply(connection, message, NULL, r);
534 log_info("Changed static host name to '%s'", data[PROP_HOSTNAME]);
536 changed = bus_properties_changed_new(
537 "/org/freedesktop/hostname1",
538 "org.freedesktop.hostname1",
544 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
545 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
548 dbus_bool_t interactive;
551 if (!dbus_message_get_args(
554 DBUS_TYPE_STRING, &name,
555 DBUS_TYPE_BOOLEAN, &interactive,
557 return bus_send_error_reply(connection, message, &error, -EINVAL);
562 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
564 if (!streq_ptr(name, data[k])) {
566 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-machine-info", interactive, &error);
568 return bus_send_error_reply(connection, message, &error, r);
584 r = write_data_other();
586 return bus_send_error_reply(connection, message, NULL, r);
588 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", data[k]);
590 changed = bus_properties_changed_new(
591 "/org/freedesktop/hostname1",
592 "org.freedesktop.hostname1",
593 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
599 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
601 if (!(reply = dbus_message_new_method_return(message)))
604 if (!dbus_connection_send(connection, reply, NULL))
607 dbus_message_unref(reply);
612 if (!dbus_connection_send(connection, changed, NULL))
615 dbus_message_unref(changed);
618 return DBUS_HANDLER_RESULT_HANDLED;
622 dbus_message_unref(reply);
625 dbus_message_unref(changed);
627 dbus_error_free(&error);
629 return DBUS_HANDLER_RESULT_NEED_MEMORY;
632 int main(int argc, char *argv[]) {
633 const DBusObjectPathVTable hostname_vtable = {
634 .message_function = hostname_message_handler
637 DBusConnection *bus = NULL;
641 dbus_error_init(&error);
643 log_set_target(LOG_TARGET_AUTO);
644 log_parse_environment();
648 log_error("This program takes no arguments.");
657 log_error("Failed to read hostname data: %s", strerror(-r));
661 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
663 log_error("Failed to get system D-Bus connection: %s", error.message);
668 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
669 log_error("Not enough memory");
674 if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
675 log_error("Failed to register name on bus: %s", error.message);
679 while (dbus_connection_read_write_dispatch(bus, -1))
688 dbus_connection_flush(bus);
689 dbus_connection_close(bus);
690 dbus_connection_unref(bus);
693 dbus_error_free(&error);
695 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;