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 if (!isempty(pretty_name))
115 printf(" Operating System: %s\n", pretty_name);
117 if (!isempty(cpe_name))
118 printf(" CPE OS Name: %s\n", cpe_name);
120 assert_se(uname(&u) >= 0);
121 printf(" Kernel: %s %s\n"
122 " Architecture: %s\n", u.sysname, u.release, u.machine);
126 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
130 switch (dbus_message_iter_get_arg_type(iter)) {
132 case DBUS_TYPE_STRING: {
135 dbus_message_iter_get_basic(iter, &s);
137 if (streq(name, "Hostname"))
139 if (streq(name, "StaticHostname"))
140 i->static_hostname = s;
141 if (streq(name, "PrettyHostname"))
142 i->pretty_hostname = s;
143 if (streq(name, "IconName"))
145 if (streq(name, "Chassis"))
155 static int show_one_name(DBusConnection *bus, const char* attr) {
156 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
157 const char *interface = "org.freedesktop.hostname1", *s;
158 DBusMessageIter iter, sub;
161 r = bus_method_call_with_reply(
163 "org.freedesktop.hostname1",
164 "/org/freedesktop/hostname1",
165 "org.freedesktop.DBus.Properties",
169 DBUS_TYPE_STRING, &interface,
170 DBUS_TYPE_STRING, &attr,
175 if (!dbus_message_iter_init(reply, &iter) ||
176 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
177 log_error("Failed to parse reply.");
181 dbus_message_iter_recurse(&iter, &sub);
183 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
184 log_error("Failed to parse reply.");
188 dbus_message_iter_get_basic(&sub, &s);
194 static int show_all_names(DBusConnection *bus) {
195 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
196 const char *interface = "";
198 DBusMessageIter iter, sub, sub2, sub3;
199 StatusInfo info = {};
201 r = bus_method_call_with_reply(
203 "org.freedesktop.hostname1",
204 "/org/freedesktop/hostname1",
205 "org.freedesktop.DBus.Properties",
209 DBUS_TYPE_STRING, &interface,
214 if (!dbus_message_iter_init(reply, &iter) ||
215 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
216 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
217 log_error("Failed to parse reply.");
221 dbus_message_iter_recurse(&iter, &sub);
223 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
226 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
227 log_error("Failed to parse reply.");
231 dbus_message_iter_recurse(&sub, &sub2);
233 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
234 log_error("Failed to parse reply.");
238 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
239 log_error("Failed to parse reply.");
243 dbus_message_iter_recurse(&sub2, &sub3);
245 r = status_property(name, &sub3, &info);
247 log_error("Failed to parse reply.");
251 dbus_message_iter_next(&sub);
254 print_status_info(&info);
258 static int show_status(DBusConnection *bus, char **args, unsigned n) {
261 if (arg_pretty || arg_static || arg_transient) {
264 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
265 log_error("Cannot query more than one name type at a time");
269 attr = arg_pretty ? "PrettyHostname" :
270 arg_static ? "StaticHostname" : "Hostname";
272 return show_one_name(bus, attr);
274 return show_all_names(bus);
277 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
278 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
279 dbus_bool_t interactive = arg_ask_password;
280 _cleanup_free_ char *h = NULL;
281 const char *hostname = args[1];
287 polkit_agent_open_if_enabled();
289 if (!arg_pretty && !arg_static && !arg_transient)
290 arg_pretty = arg_static = arg_transient = true;
295 /* If the passed hostname is already valid, then
296 * assume the user doesn't know anything about pretty
297 * hostnames, so let's unset the pretty hostname, and
298 * just set the passed hostname as static/dynamic
301 h = strdup(hostname);
305 hostname_cleanup(h, true);
307 if (arg_static && streq(h, hostname))
314 r = bus_method_call_with_reply(
316 "org.freedesktop.hostname1",
317 "/org/freedesktop/hostname1",
318 "org.freedesktop.hostname1",
322 DBUS_TYPE_STRING, &p,
323 DBUS_TYPE_BOOLEAN, &interactive,
328 dbus_message_unref(reply);
333 r = bus_method_call_with_reply(
335 "org.freedesktop.hostname1",
336 "/org/freedesktop/hostname1",
337 "org.freedesktop.hostname1",
341 DBUS_TYPE_STRING, &hostname,
342 DBUS_TYPE_BOOLEAN, &interactive,
348 dbus_message_unref(reply);
353 r = bus_method_call_with_reply(
355 "org.freedesktop.hostname1",
356 "/org/freedesktop/hostname1",
357 "org.freedesktop.hostname1",
361 DBUS_TYPE_STRING, &hostname,
362 DBUS_TYPE_BOOLEAN, &interactive,
372 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
373 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
374 dbus_bool_t interactive = arg_ask_password;
379 polkit_agent_open_if_enabled();
381 return bus_method_call_with_reply(
383 "org.freedesktop.hostname1",
384 "/org/freedesktop/hostname1",
385 "org.freedesktop.hostname1",
389 DBUS_TYPE_STRING, &args[1],
390 DBUS_TYPE_BOOLEAN, &interactive,
394 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
395 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
396 dbus_bool_t interactive = arg_ask_password;
401 polkit_agent_open_if_enabled();
403 return bus_method_call_with_reply(
405 "org.freedesktop.hostname1",
406 "/org/freedesktop/hostname1",
407 "org.freedesktop.hostname1",
411 DBUS_TYPE_STRING, &args[1],
412 DBUS_TYPE_BOOLEAN, &interactive,
416 static int help(void) {
418 printf("%s [OPTIONS...] COMMAND ...\n\n"
419 "Query or change system hostname.\n\n"
420 " -h --help Show this help\n"
421 " --version Show package version\n"
422 " --transient Only set transient hostname\n"
423 " --static Only set static hostname\n"
424 " --pretty Only set pretty hostname\n"
425 " -P --privileged Acquire privileges before execution\n"
426 " --no-ask-password Do not prompt for password\n"
427 " -H --host=[USER@]HOST Operate on remote host\n\n"
429 " status Show current hostname settings\n"
430 " set-hostname NAME Set system hostname\n"
431 " set-icon-name NAME Set icon name for host\n"
432 " set-chassis NAME Set chassis type for host\n",
433 program_invocation_short_name);
438 static int parse_argv(int argc, char *argv[]) {
448 static const struct option options[] = {
449 { "help", no_argument, NULL, 'h' },
450 { "version", no_argument, NULL, ARG_VERSION },
451 { "transient", no_argument, NULL, ARG_TRANSIENT },
452 { "static", no_argument, NULL, ARG_STATIC },
453 { "pretty", no_argument, NULL, ARG_PRETTY },
454 { "host", required_argument, NULL, 'H' },
455 { "privileged", no_argument, NULL, 'P' },
456 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
465 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
474 puts(PACKAGE_STRING);
475 puts(SYSTEMD_FEATURES);
479 arg_transport = TRANSPORT_POLKIT;
483 arg_transport = TRANSPORT_SSH;
484 parse_user_at_host(optarg, &arg_user, &arg_host);
488 arg_transient = true;
499 case ARG_NO_ASK_PASSWORD:
500 arg_ask_password = false;
507 log_error("Unknown option code %c", c);
515 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
517 static const struct {
525 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
527 { "status", LESS, 1, show_status },
528 { "set-hostname", EQUAL, 2, set_hostname },
529 { "set-icon-name", EQUAL, 2, set_icon_name },
530 { "set-chassis", EQUAL, 2, set_chassis },
540 left = argc - optind;
543 /* Special rule: no arguments means "status" */
546 if (streq(argv[optind], "help")) {
551 for (i = 0; i < ELEMENTSOF(verbs); i++)
552 if (streq(argv[optind], verbs[i].verb))
555 if (i >= ELEMENTSOF(verbs)) {
556 log_error("Unknown operation %s", argv[optind]);
561 switch (verbs[i].argc_cmp) {
564 if (left != verbs[i].argc) {
565 log_error("Invalid number of arguments.");
572 if (left < verbs[i].argc) {
573 log_error("Too few arguments.");
580 if (left > verbs[i].argc) {
581 log_error("Too many arguments.");
588 assert_not_reached("Unknown comparison operator.");
592 log_error("Failed to get D-Bus connection: %s", error->message);
596 return verbs[i].dispatch(bus, argv + optind, left);
599 int main(int argc, char *argv[]) {
600 int r, retval = EXIT_FAILURE;
601 DBusConnection *bus = NULL;
604 dbus_error_init(&error);
606 setlocale(LC_ALL, "");
607 log_parse_environment();
610 r = parse_argv(argc, argv);
614 retval = EXIT_SUCCESS;
618 if (arg_transport == TRANSPORT_NORMAL)
619 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
620 else if (arg_transport == TRANSPORT_POLKIT)
621 bus_connect_system_polkit(&bus, &error);
622 else if (arg_transport == TRANSPORT_SSH)
623 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
625 assert_not_reached("Uh, invalid transport...");
627 r = hostnamectl_main(bus, argc, argv, &error);
628 retval = r < 0 ? EXIT_FAILURE : r;
632 dbus_connection_flush(bus);
633 dbus_connection_close(bus);
634 dbus_connection_unref(bus);
637 dbus_error_free(&error);