1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/timex.h>
29 #include <sys/utsname.h>
31 #include "dbus-common.h"
33 #include "spawn-polkit-agent.h"
41 static enum transport {
45 } arg_transport = TRANSPORT_NORMAL;
46 static bool arg_ask_password = true;
47 static char *arg_host = NULL;
48 static char *arg_user = NULL;
49 static bool arg_transient = false;
50 static bool arg_pretty = false;
51 static bool arg_static = false;
53 static void polkit_agent_open_if_enabled(void) {
55 /* Open the polkit agent as a child process if necessary */
57 if (!arg_ask_password)
63 typedef struct StatusInfo {
65 const char *static_hostname;
66 const char *pretty_hostname;
67 const char *icon_name;
71 static void print_status_info(StatusInfo *i) {
74 const char *id = NULL;
75 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
80 printf(" Static hostname: %s\n",
81 strna(i->static_hostname));
83 if (!isempty(i->pretty_hostname) &&
84 !streq_ptr(i->pretty_hostname, i->static_hostname))
85 printf(" Pretty hostname: %s\n",
86 strna(i->pretty_hostname));
88 if (!isempty(i->hostname) &&
89 !streq_ptr(i->hostname, i->static_hostname))
90 printf("Transient hostname: %s\n",
93 printf(" Icon name: %s\n"
98 r = sd_id128_get_machine(&mid);
100 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
102 r = sd_id128_get_boot(&bid);
104 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
106 if (detect_virtualization(&id) > 0)
107 printf(" Virtualization: %s\n", id);
109 r = parse_env_file("/etc/os-release", NEWLINE,
110 "PRETTY_NAME", &pretty_name,
111 "CPE_NAME", &cpe_name,
114 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
116 if (!isempty(pretty_name))
117 printf(" Operating System: %s\n", pretty_name);
119 if (!isempty(cpe_name))
120 printf(" CPE OS Name: %s\n", cpe_name);
122 assert_se(uname(&u) >= 0);
123 printf(" Kernel: %s %s\n"
124 " Architecture: %s\n", u.sysname, u.release, u.machine);
128 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
132 switch (dbus_message_iter_get_arg_type(iter)) {
134 case DBUS_TYPE_STRING: {
137 dbus_message_iter_get_basic(iter, &s);
139 if (streq(name, "Hostname"))
141 if (streq(name, "StaticHostname"))
142 i->static_hostname = s;
143 if (streq(name, "PrettyHostname"))
144 i->pretty_hostname = s;
145 if (streq(name, "IconName"))
147 if (streq(name, "Chassis"))
157 static int show_one_name(DBusConnection *bus, const char* attr) {
158 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
159 const char *interface = "org.freedesktop.hostname1", *s;
160 DBusMessageIter iter, sub;
163 r = bus_method_call_with_reply(
165 "org.freedesktop.hostname1",
166 "/org/freedesktop/hostname1",
167 "org.freedesktop.DBus.Properties",
171 DBUS_TYPE_STRING, &interface,
172 DBUS_TYPE_STRING, &attr,
177 if (!dbus_message_iter_init(reply, &iter) ||
178 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
179 log_error("Failed to parse reply.");
183 dbus_message_iter_recurse(&iter, &sub);
185 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
186 log_error("Failed to parse reply.");
190 dbus_message_iter_get_basic(&sub, &s);
196 static int show_all_names(DBusConnection *bus) {
197 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
198 const char *interface = "";
200 DBusMessageIter iter, sub, sub2, sub3;
201 StatusInfo info = {};
203 r = bus_method_call_with_reply(
205 "org.freedesktop.hostname1",
206 "/org/freedesktop/hostname1",
207 "org.freedesktop.DBus.Properties",
211 DBUS_TYPE_STRING, &interface,
216 if (!dbus_message_iter_init(reply, &iter) ||
217 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
218 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
219 log_error("Failed to parse reply.");
223 dbus_message_iter_recurse(&iter, &sub);
225 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
228 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
229 log_error("Failed to parse reply.");
233 dbus_message_iter_recurse(&sub, &sub2);
235 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
236 log_error("Failed to parse reply.");
240 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
241 log_error("Failed to parse reply.");
245 dbus_message_iter_recurse(&sub2, &sub3);
247 r = status_property(name, &sub3, &info);
249 log_error("Failed to parse reply.");
253 dbus_message_iter_next(&sub);
256 print_status_info(&info);
260 static int show_status(DBusConnection *bus, char **args, unsigned n) {
263 if (arg_pretty || arg_static || arg_transient) {
266 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
267 log_error("Cannot query more than one name type at a time");
271 attr = arg_pretty ? "PrettyHostname" :
272 arg_static ? "StaticHostname" : "Hostname";
274 return show_one_name(bus, attr);
276 return show_all_names(bus);
279 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
280 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
281 dbus_bool_t interactive = arg_ask_password;
282 _cleanup_free_ char *h = NULL;
283 const char *hostname = args[1];
289 polkit_agent_open_if_enabled();
291 if (!arg_pretty && !arg_static && !arg_transient)
292 arg_pretty = arg_static = arg_transient = true;
297 /* If the passed hostname is already valid, then
298 * assume the user doesn't know anything about pretty
299 * hostnames, so let's unset the pretty hostname, and
300 * just set the passed hostname as static/dynamic
303 h = strdup(hostname);
307 hostname_cleanup(h, true);
309 if (arg_static && streq(h, hostname))
316 r = bus_method_call_with_reply(
318 "org.freedesktop.hostname1",
319 "/org/freedesktop/hostname1",
320 "org.freedesktop.hostname1",
324 DBUS_TYPE_STRING, &p,
325 DBUS_TYPE_BOOLEAN, &interactive,
330 dbus_message_unref(reply);
335 r = bus_method_call_with_reply(
337 "org.freedesktop.hostname1",
338 "/org/freedesktop/hostname1",
339 "org.freedesktop.hostname1",
343 DBUS_TYPE_STRING, &hostname,
344 DBUS_TYPE_BOOLEAN, &interactive,
350 dbus_message_unref(reply);
355 r = bus_method_call_with_reply(
357 "org.freedesktop.hostname1",
358 "/org/freedesktop/hostname1",
359 "org.freedesktop.hostname1",
363 DBUS_TYPE_STRING, &hostname,
364 DBUS_TYPE_BOOLEAN, &interactive,
374 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
375 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
376 dbus_bool_t interactive = arg_ask_password;
381 polkit_agent_open_if_enabled();
383 return bus_method_call_with_reply(
385 "org.freedesktop.hostname1",
386 "/org/freedesktop/hostname1",
387 "org.freedesktop.hostname1",
391 DBUS_TYPE_STRING, &args[1],
392 DBUS_TYPE_BOOLEAN, &interactive,
396 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
397 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
398 dbus_bool_t interactive = arg_ask_password;
403 polkit_agent_open_if_enabled();
405 return bus_method_call_with_reply(
407 "org.freedesktop.hostname1",
408 "/org/freedesktop/hostname1",
409 "org.freedesktop.hostname1",
413 DBUS_TYPE_STRING, &args[1],
414 DBUS_TYPE_BOOLEAN, &interactive,
418 static int help(void) {
420 printf("%s [OPTIONS...] COMMAND ...\n\n"
421 "Query or change system hostname.\n\n"
422 " -h --help Show this help\n"
423 " --version Show package version\n"
424 " --transient Only set transient hostname\n"
425 " --static Only set static hostname\n"
426 " --pretty Only set pretty hostname\n"
427 " -P --privileged Acquire privileges before execution\n"
428 " --no-ask-password Do not prompt for password\n"
429 " -H --host=[USER@]HOST Operate on remote host\n\n"
431 " status Show current hostname settings\n"
432 " set-hostname NAME Set system hostname\n"
433 " set-icon-name NAME Set icon name for host\n"
434 " set-chassis NAME Set chassis type for host\n",
435 program_invocation_short_name);
440 static int parse_argv(int argc, char *argv[]) {
450 static const struct option options[] = {
451 { "help", no_argument, NULL, 'h' },
452 { "version", no_argument, NULL, ARG_VERSION },
453 { "transient", no_argument, NULL, ARG_TRANSIENT },
454 { "static", no_argument, NULL, ARG_STATIC },
455 { "pretty", no_argument, NULL, ARG_PRETTY },
456 { "host", required_argument, NULL, 'H' },
457 { "privileged", no_argument, NULL, 'P' },
458 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
467 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
476 puts(PACKAGE_STRING);
477 puts(SYSTEMD_FEATURES);
481 arg_transport = TRANSPORT_POLKIT;
485 arg_transport = TRANSPORT_SSH;
486 parse_user_at_host(optarg, &arg_user, &arg_host);
490 arg_transient = true;
501 case ARG_NO_ASK_PASSWORD:
502 arg_ask_password = false;
509 log_error("Unknown option code %c", c);
517 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
519 static const struct {
527 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
529 { "status", LESS, 1, show_status },
530 { "set-hostname", EQUAL, 2, set_hostname },
531 { "set-icon-name", EQUAL, 2, set_icon_name },
532 { "set-chassis", EQUAL, 2, set_chassis },
542 left = argc - optind;
545 /* Special rule: no arguments means "status" */
548 if (streq(argv[optind], "help")) {
553 for (i = 0; i < ELEMENTSOF(verbs); i++)
554 if (streq(argv[optind], verbs[i].verb))
557 if (i >= ELEMENTSOF(verbs)) {
558 log_error("Unknown operation %s", argv[optind]);
563 switch (verbs[i].argc_cmp) {
566 if (left != verbs[i].argc) {
567 log_error("Invalid number of arguments.");
574 if (left < verbs[i].argc) {
575 log_error("Too few arguments.");
582 if (left > verbs[i].argc) {
583 log_error("Too many arguments.");
590 assert_not_reached("Unknown comparison operator.");
594 log_error("Failed to get D-Bus connection: %s", error->message);
598 return verbs[i].dispatch(bus, argv + optind, left);
601 int main(int argc, char *argv[]) {
602 int r, retval = EXIT_FAILURE;
603 DBusConnection *bus = NULL;
606 dbus_error_init(&error);
608 setlocale(LC_ALL, "");
609 log_parse_environment();
612 r = parse_argv(argc, argv);
616 retval = EXIT_SUCCESS;
620 if (arg_transport == TRANSPORT_NORMAL)
621 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
622 else if (arg_transport == TRANSPORT_POLKIT)
623 bus_connect_system_polkit(&bus, &error);
624 else if (arg_transport == TRANSPORT_SSH)
625 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
627 assert_not_reached("Uh, invalid transport...");
629 r = hostnamectl_main(bus, argc, argv, &error);
630 retval = r < 0 ? EXIT_FAILURE : r;
634 dbus_connection_flush(bus);
635 dbus_connection_close(bus);
636 dbus_connection_unref(bus);
639 dbus_error_free(&error);