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 (!streq_ptr(i->hostname, i->static_hostname))
83 printf("Transient hostname: %s\n",
86 printf(" Pretty hostname: %s\n"
89 strna(i->pretty_hostname),
93 r = sd_id128_get_machine(&mid);
95 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
97 r = sd_id128_get_boot(&bid);
99 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
101 if (detect_virtualization(&id) > 0)
102 printf(" Virtualization: %s\n", id);
104 r = parse_env_file("/etc/os-release", NEWLINE,
105 "PRETTY_NAME", &pretty_name,
106 "CPE_NAME", &cpe_name,
109 if (!isempty(pretty_name))
110 printf(" Operating System: %s\n", pretty_name);
112 if (!isempty(cpe_name))
113 printf(" CPE OS Name: %s\n", cpe_name);
115 assert_se(uname(&u) >= 0);
116 printf(" Kernel: %s %s\n"
117 " Architecture: %s\n", u.sysname, u.release, u.machine);
121 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
125 switch (dbus_message_iter_get_arg_type(iter)) {
127 case DBUS_TYPE_STRING: {
130 dbus_message_iter_get_basic(iter, &s);
132 if (streq(name, "Hostname"))
134 if (streq(name, "StaticHostname"))
135 i->static_hostname = s;
136 if (streq(name, "PrettyHostname"))
137 i->pretty_hostname = s;
138 if (streq(name, "IconName"))
140 if (streq(name, "Chassis"))
150 static int show_status(DBusConnection *bus, char **args, unsigned n) {
151 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
152 const char *interface = "";
154 DBusMessageIter iter, sub, sub2, sub3;
159 r = bus_method_call_with_reply(
161 "org.freedesktop.hostname1",
162 "/org/freedesktop/hostname1",
163 "org.freedesktop.DBus.Properties",
167 DBUS_TYPE_STRING, &interface,
172 if (!dbus_message_iter_init(reply, &iter) ||
173 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
174 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
175 log_error("Failed to parse reply.");
180 dbus_message_iter_recurse(&iter, &sub);
182 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
185 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
186 log_error("Failed to parse reply.");
190 dbus_message_iter_recurse(&sub, &sub2);
192 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
193 log_error("Failed to parse reply.");
197 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
198 log_error("Failed to parse reply.");
202 dbus_message_iter_recurse(&sub2, &sub3);
204 r = status_property(name, &sub3, &info);
206 log_error("Failed to parse reply.");
210 dbus_message_iter_next(&sub);
213 print_status_info(&info);
217 static char* hostname_simplify(char *s) {
220 for (p = s, d = s; *p; p++) {
221 if ((*p >= 'a' && *p <= 'z') ||
222 (*p >= '0' && *p <= '9') ||
223 *p == '-' || *p == '_')
225 else if (*p >= 'A' && *p <= 'Z')
226 *(d++) = *p - 'A' + 'a';
233 strshorten(s, HOST_NAME_MAX);
237 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
238 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
239 dbus_bool_t interactive = true;
240 _cleanup_free_ char *h = NULL;
241 const char *hostname = args[1];
247 polkit_agent_open_if_enabled();
249 if (arg_set_pretty) {
252 /* If the passed hostname is already valid, then
253 * assume the user doesn't know anything about pretty
254 * hostnames, so let's unset the pretty hostname, and
255 * just set the passed hostname as static/dynamic
258 if (hostname_is_valid(hostname))
263 h = strdup(hostname);
267 hostname = hostname_simplify(h);
270 r = bus_method_call_with_reply(
272 "org.freedesktop.hostname1",
273 "/org/freedesktop/hostname1",
274 "org.freedesktop.hostname1",
278 DBUS_TYPE_STRING, &p,
279 DBUS_TYPE_BOOLEAN, &interactive,
284 dbus_message_unref(reply);
288 if (arg_set_static) {
289 r = bus_method_call_with_reply(
291 "org.freedesktop.hostname1",
292 "/org/freedesktop/hostname1",
293 "org.freedesktop.hostname1",
297 DBUS_TYPE_STRING, &hostname,
298 DBUS_TYPE_BOOLEAN, &interactive,
304 dbus_message_unref(reply);
308 if (arg_set_transient) {
309 r = bus_method_call_with_reply(
311 "org.freedesktop.hostname1",
312 "/org/freedesktop/hostname1",
313 "org.freedesktop.hostname1",
317 DBUS_TYPE_STRING, &hostname,
318 DBUS_TYPE_BOOLEAN, &interactive,
328 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
329 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
330 dbus_bool_t interactive = true;
335 polkit_agent_open_if_enabled();
337 return bus_method_call_with_reply(
339 "org.freedesktop.hostname1",
340 "/org/freedesktop/hostname1",
341 "org.freedesktop.hostname1",
345 DBUS_TYPE_STRING, &args[1],
346 DBUS_TYPE_BOOLEAN, &interactive,
350 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
351 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
352 dbus_bool_t interactive = true;
357 polkit_agent_open_if_enabled();
359 return bus_method_call_with_reply(
361 "org.freedesktop.hostname1",
362 "/org/freedesktop/hostname1",
363 "org.freedesktop.hostname1",
367 DBUS_TYPE_STRING, &args[1],
368 DBUS_TYPE_BOOLEAN, &interactive,
372 static int help(void) {
374 printf("%s [OPTIONS...] COMMAND ...\n\n"
375 "Query or change system hostname.\n\n"
376 " -h --help Show this help\n"
377 " --version Show package version\n"
378 " --transient Only set transient hostname\n"
379 " --static Only set static hostname\n"
380 " --pretty Only set pretty hostname\n"
381 " --no-ask-password Do not prompt for password\n"
382 " -H --host=[USER@]HOST Operate on remote host\n\n"
384 " status Show current hostname settings\n"
385 " set-hostname NAME Set system hostname\n"
386 " set-icon-name NAME Set icon name for host\n"
387 " set-chassis NAME Set chassis type for host\n",
388 program_invocation_short_name);
393 static int parse_argv(int argc, char *argv[]) {
403 static const struct option options[] = {
404 { "help", no_argument, NULL, 'h' },
405 { "version", no_argument, NULL, ARG_VERSION },
406 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
407 { "static", no_argument, NULL, ARG_SET_STATIC },
408 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
409 { "host", required_argument, NULL, 'H' },
410 { "privileged", no_argument, NULL, 'P' },
411 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
420 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
429 puts(PACKAGE_STRING);
430 puts(SYSTEMD_FEATURES);
434 arg_transport = TRANSPORT_POLKIT;
438 arg_transport = TRANSPORT_SSH;
442 case ARG_SET_TRANSIENT:
443 arg_set_transient = true;
447 arg_set_pretty = true;
451 arg_set_static = true;
454 case ARG_NO_ASK_PASSWORD:
455 arg_ask_password = false;
462 log_error("Unknown option code %c", c);
467 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
468 arg_set_transient = arg_set_pretty = arg_set_static = true;
473 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
475 static const struct {
483 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
485 { "status", LESS, 1, show_status },
486 { "set-hostname", EQUAL, 2, set_hostname },
487 { "set-icon-name", EQUAL, 2, set_icon_name },
488 { "set-chassis", EQUAL, 2, set_chassis },
498 left = argc - optind;
501 /* Special rule: no arguments means "status" */
504 if (streq(argv[optind], "help")) {
509 for (i = 0; i < ELEMENTSOF(verbs); i++)
510 if (streq(argv[optind], verbs[i].verb))
513 if (i >= ELEMENTSOF(verbs)) {
514 log_error("Unknown operation %s", argv[optind]);
519 switch (verbs[i].argc_cmp) {
522 if (left != verbs[i].argc) {
523 log_error("Invalid number of arguments.");
530 if (left < verbs[i].argc) {
531 log_error("Too few arguments.");
538 if (left > verbs[i].argc) {
539 log_error("Too many arguments.");
546 assert_not_reached("Unknown comparison operator.");
550 log_error("Failed to get D-Bus connection: %s", error->message);
554 return verbs[i].dispatch(bus, argv + optind, left);
557 int main(int argc, char *argv[]) {
558 int r, retval = EXIT_FAILURE;
559 DBusConnection *bus = NULL;
562 dbus_error_init(&error);
564 setlocale(LC_ALL, "");
565 log_parse_environment();
568 r = parse_argv(argc, argv);
572 retval = EXIT_SUCCESS;
576 if (arg_transport == TRANSPORT_NORMAL)
577 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
578 else if (arg_transport == TRANSPORT_POLKIT)
579 bus_connect_system_polkit(&bus, &error);
580 else if (arg_transport == TRANSPORT_SSH)
581 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
583 assert_not_reached("Uh, invalid transport...");
585 r = hostnamectl_main(bus, argc, argv, &error);
586 retval = r < 0 ? EXIT_FAILURE : r;
590 dbus_connection_flush(bus);
591 dbus_connection_close(bus);
592 dbus_connection_unref(bus);
595 dbus_error_free(&error);