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"
40 static enum transport {
44 } arg_transport = TRANSPORT_NORMAL;
45 static bool arg_ask_password = true;
46 static const char *arg_host = NULL;
47 static bool arg_set_transient = false;
48 static bool arg_set_pretty = false;
49 static bool arg_set_static = false;
51 static void polkit_agent_open_if_enabled(void) {
53 /* Open the polkit agent as a child process if necessary */
55 if (!arg_ask_password)
61 typedef struct StatusInfo {
63 const char *static_hostname;
64 const char *pretty_hostname;
65 const char *icon_name;
68 static void print_status_info(StatusInfo *i) {
71 const char *id = NULL;
72 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
77 printf(" Static hostname: %s\n",
78 strna(i->static_hostname));
80 if (!streq_ptr(i->hostname, i->static_hostname))
81 printf("Transient hostname: %s\n",
84 printf(" Pretty hostname: %s\n"
86 strna(i->pretty_hostname),
89 r = sd_id128_get_machine(&mid);
91 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
93 r = sd_id128_get_boot(&bid);
95 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
97 if (detect_virtualization(&id) > 0)
98 printf(" Virtualization: %s\n", id);
100 r = parse_env_file("/etc/os-release", NEWLINE,
101 "PRETTY_NAME", &pretty_name,
102 "CPE_NAME", &cpe_name,
105 if (!isempty(pretty_name))
106 printf(" Operating System: %s\n", pretty_name);
108 if (!isempty(cpe_name))
109 printf(" CPE OS Name: %s\n", cpe_name);
111 assert_se(uname(&u) >= 0);
112 printf(" Kernel: %s %s\n"
113 " Architecture: %s\n", u.sysname, u.release, u.machine);
117 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
121 switch (dbus_message_iter_get_arg_type(iter)) {
123 case DBUS_TYPE_STRING: {
126 dbus_message_iter_get_basic(iter, &s);
128 if (streq(name, "Hostname"))
130 if (streq(name, "StaticHostname"))
131 i->static_hostname = s;
132 if (streq(name, "PrettyHostname"))
133 i->pretty_hostname = s;
134 if (streq(name, "IconName"))
144 static int show_status(DBusConnection *bus, char **args, unsigned n) {
145 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
146 const char *interface = "";
148 DBusMessageIter iter, sub, sub2, sub3;
153 r = bus_method_call_with_reply(
155 "org.freedesktop.hostname1",
156 "/org/freedesktop/hostname1",
157 "org.freedesktop.DBus.Properties",
161 DBUS_TYPE_STRING, &interface,
166 if (!dbus_message_iter_init(reply, &iter) ||
167 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
168 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
169 log_error("Failed to parse reply.");
174 dbus_message_iter_recurse(&iter, &sub);
176 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
179 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
180 log_error("Failed to parse reply.");
184 dbus_message_iter_recurse(&sub, &sub2);
186 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
187 log_error("Failed to parse reply.");
191 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
192 log_error("Failed to parse reply.");
196 dbus_message_iter_recurse(&sub2, &sub3);
198 r = status_property(name, &sub3, &info);
200 log_error("Failed to parse reply.");
204 dbus_message_iter_next(&sub);
207 print_status_info(&info);
211 static char* hostname_simplify(char *s) {
214 for (p = s, d = s; *p; p++) {
215 if ((*p >= 'a' && *p <= 'z') ||
216 (*p >= '0' && *p <= '9') ||
217 *p == '-' || *p == '_')
219 else if (*p >= 'A' && *p <= 'Z')
220 *(d++) = *p - 'A' + 'a';
227 strshorten(s, HOST_NAME_MAX);
231 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
232 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
233 dbus_bool_t interactive = true;
234 _cleanup_free_ char *h = NULL;
235 const char *hostname = args[1];
241 polkit_agent_open_if_enabled();
243 if (arg_set_pretty) {
244 r = bus_method_call_with_reply(
246 "org.freedesktop.hostname1",
247 "/org/freedesktop/hostname1",
248 "org.freedesktop.hostname1",
252 DBUS_TYPE_STRING, &hostname,
253 DBUS_TYPE_BOOLEAN, &interactive,
258 h = strdup(hostname);
262 hostname = hostname_simplify(h);
265 if (arg_set_static) {
266 r = bus_method_call_with_reply(
268 "org.freedesktop.hostname1",
269 "/org/freedesktop/hostname1",
270 "org.freedesktop.hostname1",
274 DBUS_TYPE_STRING, &hostname,
275 DBUS_TYPE_BOOLEAN, &interactive,
282 if (arg_set_transient) {
283 r = bus_method_call_with_reply(
285 "org.freedesktop.hostname1",
286 "/org/freedesktop/hostname1",
287 "org.freedesktop.hostname1",
291 DBUS_TYPE_STRING, &hostname,
292 DBUS_TYPE_BOOLEAN, &interactive,
302 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
303 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
304 dbus_bool_t interactive = true;
309 polkit_agent_open_if_enabled();
311 return bus_method_call_with_reply(
313 "org.freedesktop.hostname1",
314 "/org/freedesktop/hostname1",
315 "org.freedesktop.hostname1",
319 DBUS_TYPE_STRING, &args[1],
320 DBUS_TYPE_BOOLEAN, &interactive,
324 static int help(void) {
326 printf("%s [OPTIONS...] COMMAND ...\n\n"
327 "Query or change system hostname.\n\n"
328 " -h --help Show this help\n"
329 " --version Show package version\n"
330 " --transient Only set transient hostname\n"
331 " --static Only set static hostname\n"
332 " --pretty Only set pretty hostname\n"
333 " --no-ask-password Do not prompt for password\n"
334 " -H --host=[USER@]HOST Operate on remote host\n\n"
336 " status Show current hostname settings\n"
337 " set-hostname NAME Set system hostname\n"
338 " set-icon-name NAME Set icon name for host\n",
339 program_invocation_short_name);
344 static int parse_argv(int argc, char *argv[]) {
354 static const struct option options[] = {
355 { "help", no_argument, NULL, 'h' },
356 { "version", no_argument, NULL, ARG_VERSION },
357 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
358 { "static", no_argument, NULL, ARG_SET_STATIC },
359 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
360 { "host", required_argument, NULL, 'H' },
361 { "privileged", no_argument, NULL, 'P' },
362 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
371 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
380 puts(PACKAGE_STRING);
382 puts(SYSTEMD_FEATURES);
386 arg_transport = TRANSPORT_POLKIT;
390 arg_transport = TRANSPORT_SSH;
394 case ARG_SET_TRANSIENT:
395 arg_set_transient = true;
399 arg_set_pretty = true;
403 arg_set_static = true;
406 case ARG_NO_ASK_PASSWORD:
407 arg_ask_password = false;
414 log_error("Unknown option code %c", c);
419 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
420 arg_set_transient = arg_set_pretty = arg_set_static = true;
425 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
427 static const struct {
435 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
437 { "status", LESS, 1, show_status },
438 { "set-hostname", EQUAL, 2, set_hostname },
439 { "set-icon-name", EQUAL, 2, set_icon_name },
449 left = argc - optind;
452 /* Special rule: no arguments means "status" */
455 if (streq(argv[optind], "help")) {
460 for (i = 0; i < ELEMENTSOF(verbs); i++)
461 if (streq(argv[optind], verbs[i].verb))
464 if (i >= ELEMENTSOF(verbs)) {
465 log_error("Unknown operation %s", argv[optind]);
470 switch (verbs[i].argc_cmp) {
473 if (left != verbs[i].argc) {
474 log_error("Invalid number of arguments.");
481 if (left < verbs[i].argc) {
482 log_error("Too few arguments.");
489 if (left > verbs[i].argc) {
490 log_error("Too many arguments.");
497 assert_not_reached("Unknown comparison operator.");
501 log_error("Failed to get D-Bus connection: %s", error->message);
505 return verbs[i].dispatch(bus, argv + optind, left);
508 int main(int argc, char *argv[]) {
509 int r, retval = EXIT_FAILURE;
510 DBusConnection *bus = NULL;
513 dbus_error_init(&error);
515 setlocale(LC_ALL, "");
516 log_parse_environment();
519 r = parse_argv(argc, argv);
523 retval = EXIT_SUCCESS;
527 if (arg_transport == TRANSPORT_NORMAL)
528 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
529 else if (arg_transport == TRANSPORT_POLKIT)
530 bus_connect_system_polkit(&bus, &error);
531 else if (arg_transport == TRANSPORT_SSH)
532 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
534 assert_not_reached("Uh, invalid transport...");
536 r = hostnamectl_main(bus, argc, argv, &error);
537 retval = r < 0 ? EXIT_FAILURE : r;
541 dbus_connection_flush(bus);
542 dbus_connection_close(bus);
543 dbus_connection_unref(bus);
546 dbus_error_free(&error);