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 const char *arg_host = NULL;
48 static bool arg_set_transient = false;
49 static bool arg_set_pretty = false;
50 static bool arg_set_static = false;
52 static void polkit_agent_open_if_enabled(void) {
54 /* Open the polkit agent as a child process if necessary */
56 if (!arg_ask_password)
62 typedef struct StatusInfo {
64 const char *static_hostname;
65 const char *pretty_hostname;
66 const char *icon_name;
70 static void print_status_info(StatusInfo *i) {
73 const char *id = NULL;
74 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
79 printf(" Static hostname: %s\n",
80 strna(i->static_hostname));
82 if (!isempty(i->pretty_hostname) &&
83 !streq_ptr(i->pretty_hostname, i->static_hostname))
84 printf(" Pretty hostname: %s\n",
85 strna(i->pretty_hostname));
87 if (!isempty(i->hostname) &&
88 !streq_ptr(i->hostname, i->static_hostname))
89 printf("Transient hostname: %s\n",
92 printf(" Icon name: %s\n"
97 r = sd_id128_get_machine(&mid);
99 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
101 r = sd_id128_get_boot(&bid);
103 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
105 if (detect_virtualization(&id) > 0)
106 printf(" Virtualization: %s\n", id);
108 r = parse_env_file("/etc/os-release", NEWLINE,
109 "PRETTY_NAME", &pretty_name,
110 "CPE_NAME", &cpe_name,
113 if (!isempty(pretty_name))
114 printf(" Operating System: %s\n", pretty_name);
116 if (!isempty(cpe_name))
117 printf(" CPE OS Name: %s\n", cpe_name);
119 assert_se(uname(&u) >= 0);
120 printf(" Kernel: %s %s\n"
121 " Architecture: %s\n", u.sysname, u.release, u.machine);
125 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
129 switch (dbus_message_iter_get_arg_type(iter)) {
131 case DBUS_TYPE_STRING: {
134 dbus_message_iter_get_basic(iter, &s);
136 if (streq(name, "Hostname"))
138 if (streq(name, "StaticHostname"))
139 i->static_hostname = s;
140 if (streq(name, "PrettyHostname"))
141 i->pretty_hostname = s;
142 if (streq(name, "IconName"))
144 if (streq(name, "Chassis"))
154 static int show_status(DBusConnection *bus, char **args, unsigned n) {
155 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
156 const char *interface = "";
158 DBusMessageIter iter, sub, sub2, sub3;
159 StatusInfo info = {};
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,
176 if (!dbus_message_iter_init(reply, &iter) ||
177 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
178 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
179 log_error("Failed to parse reply.");
183 dbus_message_iter_recurse(&iter, &sub);
185 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
188 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
189 log_error("Failed to parse reply.");
193 dbus_message_iter_recurse(&sub, &sub2);
195 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
196 log_error("Failed to parse reply.");
200 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
201 log_error("Failed to parse reply.");
205 dbus_message_iter_recurse(&sub2, &sub3);
207 r = status_property(name, &sub3, &info);
209 log_error("Failed to parse reply.");
213 dbus_message_iter_next(&sub);
216 print_status_info(&info);
220 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
221 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
222 dbus_bool_t interactive = true;
223 _cleanup_free_ char *h = NULL;
224 const char *hostname = args[1];
230 polkit_agent_open_if_enabled();
232 if (arg_set_pretty) {
235 /* If the passed hostname is already valid, then
236 * assume the user doesn't know anything about pretty
237 * hostnames, so let's unset the pretty hostname, and
238 * just set the passed hostname as static/dynamic
241 h = strdup(hostname);
245 hostname_cleanup(h, true);
247 if (arg_set_static && streq(h, hostname))
254 r = bus_method_call_with_reply(
256 "org.freedesktop.hostname1",
257 "/org/freedesktop/hostname1",
258 "org.freedesktop.hostname1",
262 DBUS_TYPE_STRING, &p,
263 DBUS_TYPE_BOOLEAN, &interactive,
268 dbus_message_unref(reply);
272 if (arg_set_static) {
273 r = bus_method_call_with_reply(
275 "org.freedesktop.hostname1",
276 "/org/freedesktop/hostname1",
277 "org.freedesktop.hostname1",
281 DBUS_TYPE_STRING, &hostname,
282 DBUS_TYPE_BOOLEAN, &interactive,
288 dbus_message_unref(reply);
292 if (arg_set_transient) {
293 r = bus_method_call_with_reply(
295 "org.freedesktop.hostname1",
296 "/org/freedesktop/hostname1",
297 "org.freedesktop.hostname1",
301 DBUS_TYPE_STRING, &hostname,
302 DBUS_TYPE_BOOLEAN, &interactive,
312 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
313 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
314 dbus_bool_t interactive = true;
319 polkit_agent_open_if_enabled();
321 return bus_method_call_with_reply(
323 "org.freedesktop.hostname1",
324 "/org/freedesktop/hostname1",
325 "org.freedesktop.hostname1",
329 DBUS_TYPE_STRING, &args[1],
330 DBUS_TYPE_BOOLEAN, &interactive,
334 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
335 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
336 dbus_bool_t interactive = true;
341 polkit_agent_open_if_enabled();
343 return bus_method_call_with_reply(
345 "org.freedesktop.hostname1",
346 "/org/freedesktop/hostname1",
347 "org.freedesktop.hostname1",
351 DBUS_TYPE_STRING, &args[1],
352 DBUS_TYPE_BOOLEAN, &interactive,
356 static int help(void) {
358 printf("%s [OPTIONS...] COMMAND ...\n\n"
359 "Query or change system hostname.\n\n"
360 " -h --help Show this help\n"
361 " --version Show package version\n"
362 " --transient Only set transient hostname\n"
363 " --static Only set static hostname\n"
364 " --pretty Only set pretty hostname\n"
365 " --no-ask-password Do not prompt for password\n"
366 " -H --host=[USER@]HOST Operate on remote host\n\n"
368 " status Show current hostname settings\n"
369 " set-hostname NAME Set system hostname\n"
370 " set-icon-name NAME Set icon name for host\n"
371 " set-chassis NAME Set chassis type for host\n",
372 program_invocation_short_name);
377 static int parse_argv(int argc, char *argv[]) {
387 static const struct option options[] = {
388 { "help", no_argument, NULL, 'h' },
389 { "version", no_argument, NULL, ARG_VERSION },
390 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
391 { "static", no_argument, NULL, ARG_SET_STATIC },
392 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
393 { "host", required_argument, NULL, 'H' },
394 { "privileged", no_argument, NULL, 'P' },
395 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
404 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
413 puts(PACKAGE_STRING);
414 puts(SYSTEMD_FEATURES);
418 arg_transport = TRANSPORT_POLKIT;
422 arg_transport = TRANSPORT_SSH;
426 case ARG_SET_TRANSIENT:
427 arg_set_transient = true;
431 arg_set_pretty = true;
435 arg_set_static = true;
438 case ARG_NO_ASK_PASSWORD:
439 arg_ask_password = false;
446 log_error("Unknown option code %c", c);
451 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
452 arg_set_transient = arg_set_pretty = arg_set_static = true;
457 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
459 static const struct {
467 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
469 { "status", LESS, 1, show_status },
470 { "set-hostname", EQUAL, 2, set_hostname },
471 { "set-icon-name", EQUAL, 2, set_icon_name },
472 { "set-chassis", EQUAL, 2, set_chassis },
482 left = argc - optind;
485 /* Special rule: no arguments means "status" */
488 if (streq(argv[optind], "help")) {
493 for (i = 0; i < ELEMENTSOF(verbs); i++)
494 if (streq(argv[optind], verbs[i].verb))
497 if (i >= ELEMENTSOF(verbs)) {
498 log_error("Unknown operation %s", argv[optind]);
503 switch (verbs[i].argc_cmp) {
506 if (left != verbs[i].argc) {
507 log_error("Invalid number of arguments.");
514 if (left < verbs[i].argc) {
515 log_error("Too few arguments.");
522 if (left > verbs[i].argc) {
523 log_error("Too many arguments.");
530 assert_not_reached("Unknown comparison operator.");
534 log_error("Failed to get D-Bus connection: %s", error->message);
538 return verbs[i].dispatch(bus, argv + optind, left);
541 int main(int argc, char *argv[]) {
542 int r, retval = EXIT_FAILURE;
543 DBusConnection *bus = NULL;
546 dbus_error_init(&error);
548 setlocale(LC_ALL, "");
549 log_parse_environment();
552 r = parse_argv(argc, argv);
556 retval = EXIT_SUCCESS;
560 if (arg_transport == TRANSPORT_NORMAL)
561 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
562 else if (arg_transport == TRANSPORT_POLKIT)
563 bus_connect_system_polkit(&bus, &error);
564 else if (arg_transport == TRANSPORT_SSH)
565 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
567 assert_not_reached("Uh, invalid transport...");
569 r = hostnamectl_main(bus, argc, argv, &error);
570 retval = r < 0 ? EXIT_FAILURE : r;
574 dbus_connection_flush(bus);
575 dbus_connection_close(bus);
576 dbus_connection_unref(bus);
579 dbus_error_free(&error);