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/>.
27 #include <sys/timex.h>
29 #include "dbus-common.h"
31 #include "spawn-polkit-agent.h"
38 static enum transport {
42 } arg_transport = TRANSPORT_NORMAL;
43 static bool arg_ask_password = true;
44 static const char *arg_host = NULL;
45 static bool arg_set_transient = false;
46 static bool arg_set_pretty = false;
47 static bool arg_set_static = false;
49 static void polkit_agent_open_if_enabled(void) {
51 /* Open the polkit agent as a child process if necessary */
53 if (!arg_ask_password)
59 typedef struct StatusInfo {
61 const char *static_hostname;
62 const char *pretty_hostname;
63 const char *icon_name;
66 static void print_status_info(StatusInfo *i) {
73 printf(" Static hostname: %s\n",
74 strna(i->static_hostname));
76 if (!streq_ptr(i->hostname, i->static_hostname))
77 printf("Transient hostname: %s\n",
80 printf(" Pretty hostname: %s\n"
82 strna(i->pretty_hostname),
85 r = sd_id128_get_machine(&mid);
87 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
89 r = sd_id128_get_boot(&bid);
91 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
93 if (detect_virtualization(&id) >= 0)
94 printf(" Virtualization: %s\n", id);
97 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
101 switch (dbus_message_iter_get_arg_type(iter)) {
103 case DBUS_TYPE_STRING: {
106 dbus_message_iter_get_basic(iter, &s);
108 if (streq(name, "Hostname"))
110 if (streq(name, "StaticHostname"))
111 i->static_hostname = s;
112 if (streq(name, "PrettyHostname"))
113 i->pretty_hostname = s;
114 if (streq(name, "IconName"))
124 static int show_status(DBusConnection *bus, char **args, unsigned n) {
125 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
126 const char *interface = "";
128 DBusMessageIter iter, sub, sub2, sub3;
133 r = bus_method_call_with_reply(
135 "org.freedesktop.hostname1",
136 "/org/freedesktop/hostname1",
137 "org.freedesktop.DBus.Properties",
141 DBUS_TYPE_STRING, &interface,
146 if (!dbus_message_iter_init(reply, &iter) ||
147 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
148 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
149 log_error("Failed to parse reply.");
154 dbus_message_iter_recurse(&iter, &sub);
156 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
159 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
160 log_error("Failed to parse reply.");
164 dbus_message_iter_recurse(&sub, &sub2);
166 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
167 log_error("Failed to parse reply.");
171 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
172 log_error("Failed to parse reply.");
176 dbus_message_iter_recurse(&sub2, &sub3);
178 r = status_property(name, &sub3, &info);
180 log_error("Failed to parse reply.");
184 dbus_message_iter_next(&sub);
187 print_status_info(&info);
191 static char* hostname_simplify(char *s) {
194 for (p = s, d = s; *p; p++) {
195 if ((*p >= 'a' && *p <= 'z') ||
196 (*p >= '0' && *p <= '9') ||
197 *p == '-' || *p == '_')
199 else if (*p >= 'A' && *p <= 'Z')
200 *(d++) = *p - 'A' + 'a';
207 strshorten(s, HOST_NAME_MAX);
211 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
212 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
213 dbus_bool_t interactive = true;
214 _cleanup_free_ char *h = NULL;
215 const char *hostname = args[1];
221 polkit_agent_open_if_enabled();
223 if (arg_set_pretty) {
224 r = bus_method_call_with_reply(
226 "org.freedesktop.hostname1",
227 "/org/freedesktop/hostname1",
228 "org.freedesktop.hostname1",
232 DBUS_TYPE_STRING, &hostname,
233 DBUS_TYPE_BOOLEAN, &interactive,
238 h = strdup(hostname);
242 hostname = hostname_simplify(h);
245 if (arg_set_static) {
246 r = bus_method_call_with_reply(
248 "org.freedesktop.hostname1",
249 "/org/freedesktop/hostname1",
250 "org.freedesktop.hostname1",
254 DBUS_TYPE_STRING, &hostname,
255 DBUS_TYPE_BOOLEAN, &interactive,
262 if (arg_set_transient) {
263 r = bus_method_call_with_reply(
265 "org.freedesktop.hostname1",
266 "/org/freedesktop/hostname1",
267 "org.freedesktop.hostname1",
271 DBUS_TYPE_STRING, &hostname,
272 DBUS_TYPE_BOOLEAN, &interactive,
282 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
283 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
284 dbus_bool_t interactive = true;
289 polkit_agent_open_if_enabled();
291 return bus_method_call_with_reply(
293 "org.freedesktop.hostname1",
294 "/org/freedesktop/hostname1",
295 "org.freedesktop.hostname1",
299 DBUS_TYPE_STRING, &args[1],
300 DBUS_TYPE_BOOLEAN, &interactive,
304 static int help(void) {
306 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
307 "Query or set system hostname.\n\n"
308 " -h --help Show this help\n"
309 " --version Show package version\n"
310 " --no-ask-password Do not prompt for password\n"
311 " --transient Only set transient hostname\n"
312 " --static Only set static hostname\n"
313 " --pretty Only set pretty hostname\n"
314 " -H --host=[USER@]HOST Operate on remote host\n\n"
316 " status Show current hostname settings\n"
317 " set-hostname [NAME] Set system hostname\n"
318 " set-icon-name [NAME] Set icon name for host\n",
319 program_invocation_short_name);
324 static int parse_argv(int argc, char *argv[]) {
334 static const struct option options[] = {
335 { "help", no_argument, NULL, 'h' },
336 { "version", no_argument, NULL, ARG_VERSION },
337 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
338 { "static", no_argument, NULL, ARG_SET_STATIC },
339 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
340 { "host", required_argument, NULL, 'H' },
341 { "privileged", no_argument, NULL, 'P' },
342 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
351 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
360 puts(PACKAGE_STRING);
362 puts(SYSTEMD_FEATURES);
366 arg_transport = TRANSPORT_POLKIT;
370 arg_transport = TRANSPORT_SSH;
374 case ARG_SET_TRANSIENT:
375 arg_set_transient = true;
379 arg_set_pretty = true;
383 arg_set_static = true;
390 log_error("Unknown option code %c", c);
395 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
396 arg_set_transient = arg_set_pretty = arg_set_static = true;
401 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
403 static const struct {
411 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
413 { "status", LESS, 1, show_status },
414 { "set-hostname", LESS, 2, set_hostname },
415 { "set-icon-name", EQUAL, 2, set_icon_name },
425 left = argc - optind;
428 /* Special rule: no arguments means "status" */
431 if (streq(argv[optind], "help")) {
436 for (i = 0; i < ELEMENTSOF(verbs); i++)
437 if (streq(argv[optind], verbs[i].verb))
440 if (i >= ELEMENTSOF(verbs)) {
441 log_error("Unknown operation %s", argv[optind]);
446 switch (verbs[i].argc_cmp) {
449 if (left != verbs[i].argc) {
450 log_error("Invalid number of arguments.");
457 if (left < verbs[i].argc) {
458 log_error("Too few arguments.");
465 if (left > verbs[i].argc) {
466 log_error("Too many arguments.");
473 assert_not_reached("Unknown comparison operator.");
477 log_error("Failed to get D-Bus connection: %s", error->message);
481 return verbs[i].dispatch(bus, argv + optind, left);
484 int main(int argc, char *argv[]) {
485 int r, retval = EXIT_FAILURE;
486 DBusConnection *bus = NULL;
489 dbus_error_init(&error);
491 log_parse_environment();
494 r = parse_argv(argc, argv);
498 retval = EXIT_SUCCESS;
502 if (arg_transport == TRANSPORT_NORMAL)
503 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
504 else if (arg_transport == TRANSPORT_POLKIT)
505 bus_connect_system_polkit(&bus, &error);
506 else if (arg_transport == TRANSPORT_SSH)
507 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
509 assert_not_reached("Uh, invalid transport...");
511 r = hostnamectl_main(bus, argc, argv, &error);
512 retval = r < 0 ? EXIT_FAILURE : r;
516 dbus_connection_flush(bus);
517 dbus_connection_close(bus);
518 dbus_connection_unref(bus);
521 dbus_error_free(&error);