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;
155 StatusInfo info = {};
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.");
179 dbus_message_iter_recurse(&iter, &sub);
181 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
184 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
185 log_error("Failed to parse reply.");
189 dbus_message_iter_recurse(&sub, &sub2);
191 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
192 log_error("Failed to parse reply.");
196 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
197 log_error("Failed to parse reply.");
201 dbus_message_iter_recurse(&sub2, &sub3);
203 r = status_property(name, &sub3, &info);
205 log_error("Failed to parse reply.");
209 dbus_message_iter_next(&sub);
212 print_status_info(&info);
216 static char* hostname_simplify(char *s) {
219 for (p = s, d = s; *p; p++) {
220 if ((*p >= 'a' && *p <= 'z') ||
221 (*p >= '0' && *p <= '9') ||
222 *p == '-' || *p == '_')
224 else if (*p >= 'A' && *p <= 'Z')
225 *(d++) = *p - 'A' + 'a';
232 strshorten(s, HOST_NAME_MAX);
236 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
237 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
238 dbus_bool_t interactive = true;
239 _cleanup_free_ char *h = NULL;
240 const char *hostname = args[1];
246 polkit_agent_open_if_enabled();
248 if (arg_set_pretty) {
251 /* If the passed hostname is already valid, then
252 * assume the user doesn't know anything about pretty
253 * hostnames, so let's unset the pretty hostname, and
254 * just set the passed hostname as static/dynamic
257 if (hostname_is_valid(hostname))
262 h = strdup(hostname);
266 hostname = hostname_simplify(h);
269 r = bus_method_call_with_reply(
271 "org.freedesktop.hostname1",
272 "/org/freedesktop/hostname1",
273 "org.freedesktop.hostname1",
277 DBUS_TYPE_STRING, &p,
278 DBUS_TYPE_BOOLEAN, &interactive,
283 dbus_message_unref(reply);
287 if (arg_set_static) {
288 r = bus_method_call_with_reply(
290 "org.freedesktop.hostname1",
291 "/org/freedesktop/hostname1",
292 "org.freedesktop.hostname1",
296 DBUS_TYPE_STRING, &hostname,
297 DBUS_TYPE_BOOLEAN, &interactive,
303 dbus_message_unref(reply);
307 if (arg_set_transient) {
308 r = bus_method_call_with_reply(
310 "org.freedesktop.hostname1",
311 "/org/freedesktop/hostname1",
312 "org.freedesktop.hostname1",
316 DBUS_TYPE_STRING, &hostname,
317 DBUS_TYPE_BOOLEAN, &interactive,
327 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
328 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
329 dbus_bool_t interactive = true;
334 polkit_agent_open_if_enabled();
336 return bus_method_call_with_reply(
338 "org.freedesktop.hostname1",
339 "/org/freedesktop/hostname1",
340 "org.freedesktop.hostname1",
344 DBUS_TYPE_STRING, &args[1],
345 DBUS_TYPE_BOOLEAN, &interactive,
349 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
350 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
351 dbus_bool_t interactive = true;
356 polkit_agent_open_if_enabled();
358 return bus_method_call_with_reply(
360 "org.freedesktop.hostname1",
361 "/org/freedesktop/hostname1",
362 "org.freedesktop.hostname1",
366 DBUS_TYPE_STRING, &args[1],
367 DBUS_TYPE_BOOLEAN, &interactive,
371 static int help(void) {
373 printf("%s [OPTIONS...] COMMAND ...\n\n"
374 "Query or change system hostname.\n\n"
375 " -h --help Show this help\n"
376 " --version Show package version\n"
377 " --transient Only set transient hostname\n"
378 " --static Only set static hostname\n"
379 " --pretty Only set pretty hostname\n"
380 " --no-ask-password Do not prompt for password\n"
381 " -H --host=[USER@]HOST Operate on remote host\n\n"
383 " status Show current hostname settings\n"
384 " set-hostname NAME Set system hostname\n"
385 " set-icon-name NAME Set icon name for host\n"
386 " set-chassis NAME Set chassis type for host\n",
387 program_invocation_short_name);
392 static int parse_argv(int argc, char *argv[]) {
402 static const struct option options[] = {
403 { "help", no_argument, NULL, 'h' },
404 { "version", no_argument, NULL, ARG_VERSION },
405 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
406 { "static", no_argument, NULL, ARG_SET_STATIC },
407 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
408 { "host", required_argument, NULL, 'H' },
409 { "privileged", no_argument, NULL, 'P' },
410 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
419 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
428 puts(PACKAGE_STRING);
429 puts(SYSTEMD_FEATURES);
433 arg_transport = TRANSPORT_POLKIT;
437 arg_transport = TRANSPORT_SSH;
441 case ARG_SET_TRANSIENT:
442 arg_set_transient = true;
446 arg_set_pretty = true;
450 arg_set_static = true;
453 case ARG_NO_ASK_PASSWORD:
454 arg_ask_password = false;
461 log_error("Unknown option code %c", c);
466 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
467 arg_set_transient = arg_set_pretty = arg_set_static = true;
472 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
474 static const struct {
482 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
484 { "status", LESS, 1, show_status },
485 { "set-hostname", EQUAL, 2, set_hostname },
486 { "set-icon-name", EQUAL, 2, set_icon_name },
487 { "set-chassis", EQUAL, 2, set_chassis },
497 left = argc - optind;
500 /* Special rule: no arguments means "status" */
503 if (streq(argv[optind], "help")) {
508 for (i = 0; i < ELEMENTSOF(verbs); i++)
509 if (streq(argv[optind], verbs[i].verb))
512 if (i >= ELEMENTSOF(verbs)) {
513 log_error("Unknown operation %s", argv[optind]);
518 switch (verbs[i].argc_cmp) {
521 if (left != verbs[i].argc) {
522 log_error("Invalid number of arguments.");
529 if (left < verbs[i].argc) {
530 log_error("Too few arguments.");
537 if (left > verbs[i].argc) {
538 log_error("Too many arguments.");
545 assert_not_reached("Unknown comparison operator.");
549 log_error("Failed to get D-Bus connection: %s", error->message);
553 return verbs[i].dispatch(bus, argv + optind, left);
556 int main(int argc, char *argv[]) {
557 int r, retval = EXIT_FAILURE;
558 DBusConnection *bus = NULL;
561 dbus_error_init(&error);
563 setlocale(LC_ALL, "");
564 log_parse_environment();
567 r = parse_argv(argc, argv);
571 retval = EXIT_SUCCESS;
575 if (arg_transport == TRANSPORT_NORMAL)
576 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
577 else if (arg_transport == TRANSPORT_POLKIT)
578 bus_connect_system_polkit(&bus, &error);
579 else if (arg_transport == TRANSPORT_SSH)
580 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
582 assert_not_reached("Uh, invalid transport...");
584 r = hostnamectl_main(bus, argc, argv, &error);
585 retval = r < 0 ? EXIT_FAILURE : r;
589 dbus_connection_flush(bus);
590 dbus_connection_close(bus);
591 dbus_connection_unref(bus);
594 dbus_error_free(&error);