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_set_transient = false;
50 static bool arg_set_pretty = false;
51 static bool arg_set_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_status(DBusConnection *bus, char **args, unsigned n) {
156 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
157 const char *interface = "";
159 DBusMessageIter iter, sub, sub2, sub3;
160 StatusInfo info = {};
164 r = bus_method_call_with_reply(
166 "org.freedesktop.hostname1",
167 "/org/freedesktop/hostname1",
168 "org.freedesktop.DBus.Properties",
172 DBUS_TYPE_STRING, &interface,
177 if (!dbus_message_iter_init(reply, &iter) ||
178 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
179 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
180 log_error("Failed to parse reply.");
184 dbus_message_iter_recurse(&iter, &sub);
186 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
189 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
190 log_error("Failed to parse reply.");
194 dbus_message_iter_recurse(&sub, &sub2);
196 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
197 log_error("Failed to parse reply.");
201 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
202 log_error("Failed to parse reply.");
206 dbus_message_iter_recurse(&sub2, &sub3);
208 r = status_property(name, &sub3, &info);
210 log_error("Failed to parse reply.");
214 dbus_message_iter_next(&sub);
217 print_status_info(&info);
221 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
222 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
223 dbus_bool_t interactive = arg_ask_password;
224 _cleanup_free_ char *h = NULL;
225 const char *hostname = args[1];
231 polkit_agent_open_if_enabled();
233 if (arg_set_pretty) {
236 /* If the passed hostname is already valid, then
237 * assume the user doesn't know anything about pretty
238 * hostnames, so let's unset the pretty hostname, and
239 * just set the passed hostname as static/dynamic
242 h = strdup(hostname);
246 hostname_cleanup(h, true);
248 if (arg_set_static && streq(h, hostname))
255 r = bus_method_call_with_reply(
257 "org.freedesktop.hostname1",
258 "/org/freedesktop/hostname1",
259 "org.freedesktop.hostname1",
263 DBUS_TYPE_STRING, &p,
264 DBUS_TYPE_BOOLEAN, &interactive,
269 dbus_message_unref(reply);
273 if (arg_set_static) {
274 r = bus_method_call_with_reply(
276 "org.freedesktop.hostname1",
277 "/org/freedesktop/hostname1",
278 "org.freedesktop.hostname1",
282 DBUS_TYPE_STRING, &hostname,
283 DBUS_TYPE_BOOLEAN, &interactive,
289 dbus_message_unref(reply);
293 if (arg_set_transient) {
294 r = bus_method_call_with_reply(
296 "org.freedesktop.hostname1",
297 "/org/freedesktop/hostname1",
298 "org.freedesktop.hostname1",
302 DBUS_TYPE_STRING, &hostname,
303 DBUS_TYPE_BOOLEAN, &interactive,
313 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
314 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
315 dbus_bool_t interactive = arg_ask_password;
320 polkit_agent_open_if_enabled();
322 return bus_method_call_with_reply(
324 "org.freedesktop.hostname1",
325 "/org/freedesktop/hostname1",
326 "org.freedesktop.hostname1",
330 DBUS_TYPE_STRING, &args[1],
331 DBUS_TYPE_BOOLEAN, &interactive,
335 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
336 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
337 dbus_bool_t interactive = arg_ask_password;
342 polkit_agent_open_if_enabled();
344 return bus_method_call_with_reply(
346 "org.freedesktop.hostname1",
347 "/org/freedesktop/hostname1",
348 "org.freedesktop.hostname1",
352 DBUS_TYPE_STRING, &args[1],
353 DBUS_TYPE_BOOLEAN, &interactive,
357 static int help(void) {
359 printf("%s [OPTIONS...] COMMAND ...\n\n"
360 "Query or change system hostname.\n\n"
361 " -h --help Show this help\n"
362 " --version Show package version\n"
363 " --transient Only set transient hostname\n"
364 " --static Only set static hostname\n"
365 " --pretty Only set pretty hostname\n"
366 " -P --privileged Acquire privileges before execution\n"
367 " --no-ask-password Do not prompt for password\n"
368 " -H --host=[USER@]HOST Operate on remote host\n\n"
370 " status Show current hostname settings\n"
371 " set-hostname NAME Set system hostname\n"
372 " set-icon-name NAME Set icon name for host\n"
373 " set-chassis NAME Set chassis type for host\n",
374 program_invocation_short_name);
379 static int parse_argv(int argc, char *argv[]) {
389 static const struct option options[] = {
390 { "help", no_argument, NULL, 'h' },
391 { "version", no_argument, NULL, ARG_VERSION },
392 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
393 { "static", no_argument, NULL, ARG_SET_STATIC },
394 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
395 { "host", required_argument, NULL, 'H' },
396 { "privileged", no_argument, NULL, 'P' },
397 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
406 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
415 puts(PACKAGE_STRING);
416 puts(SYSTEMD_FEATURES);
420 arg_transport = TRANSPORT_POLKIT;
424 arg_transport = TRANSPORT_SSH;
425 parse_user_at_host(optarg, &arg_user, &arg_host);
428 case ARG_SET_TRANSIENT:
429 arg_set_transient = true;
433 arg_set_pretty = true;
437 arg_set_static = true;
440 case ARG_NO_ASK_PASSWORD:
441 arg_ask_password = false;
448 log_error("Unknown option code %c", c);
453 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
454 arg_set_transient = arg_set_pretty = arg_set_static = true;
459 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
461 static const struct {
469 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
471 { "status", LESS, 1, show_status },
472 { "set-hostname", EQUAL, 2, set_hostname },
473 { "set-icon-name", EQUAL, 2, set_icon_name },
474 { "set-chassis", EQUAL, 2, set_chassis },
484 left = argc - optind;
487 /* Special rule: no arguments means "status" */
490 if (streq(argv[optind], "help")) {
495 for (i = 0; i < ELEMENTSOF(verbs); i++)
496 if (streq(argv[optind], verbs[i].verb))
499 if (i >= ELEMENTSOF(verbs)) {
500 log_error("Unknown operation %s", argv[optind]);
505 switch (verbs[i].argc_cmp) {
508 if (left != verbs[i].argc) {
509 log_error("Invalid number of arguments.");
516 if (left < verbs[i].argc) {
517 log_error("Too few arguments.");
524 if (left > verbs[i].argc) {
525 log_error("Too many arguments.");
532 assert_not_reached("Unknown comparison operator.");
536 log_error("Failed to get D-Bus connection: %s", error->message);
540 return verbs[i].dispatch(bus, argv + optind, left);
543 int main(int argc, char *argv[]) {
544 int r, retval = EXIT_FAILURE;
545 DBusConnection *bus = NULL;
548 dbus_error_init(&error);
550 setlocale(LC_ALL, "");
551 log_parse_environment();
554 r = parse_argv(argc, argv);
558 retval = EXIT_SUCCESS;
562 if (arg_transport == TRANSPORT_NORMAL)
563 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
564 else if (arg_transport == TRANSPORT_POLKIT)
565 bus_connect_system_polkit(&bus, &error);
566 else if (arg_transport == TRANSPORT_SSH)
567 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
569 assert_not_reached("Uh, invalid transport...");
571 r = hostnamectl_main(bus, argc, argv, &error);
572 retval = r < 0 ? EXIT_FAILURE : r;
576 dbus_connection_flush(bus);
577 dbus_connection_close(bus);
578 dbus_connection_unref(bus);
581 dbus_error_free(&error);