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;
69 static void print_status_info(StatusInfo *i) {
72 const char *id = NULL;
73 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
78 printf(" Static hostname: %s\n",
79 strna(i->static_hostname));
81 if (!streq_ptr(i->hostname, i->static_hostname))
82 printf("Transient hostname: %s\n",
85 printf(" Pretty hostname: %s\n"
88 strna(i->pretty_hostname),
92 r = sd_id128_get_machine(&mid);
94 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
96 r = sd_id128_get_boot(&bid);
98 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
100 if (detect_virtualization(&id) > 0)
101 printf(" Virtualization: %s\n", id);
103 r = parse_env_file("/etc/os-release", NEWLINE,
104 "PRETTY_NAME", &pretty_name,
105 "CPE_NAME", &cpe_name,
108 if (!isempty(pretty_name))
109 printf(" Operating System: %s\n", pretty_name);
111 if (!isempty(cpe_name))
112 printf(" CPE OS Name: %s\n", cpe_name);
114 assert_se(uname(&u) >= 0);
115 printf(" Kernel: %s %s\n"
116 " Architecture: %s\n", u.sysname, u.release, u.machine);
120 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
124 switch (dbus_message_iter_get_arg_type(iter)) {
126 case DBUS_TYPE_STRING: {
129 dbus_message_iter_get_basic(iter, &s);
131 if (streq(name, "Hostname"))
133 if (streq(name, "StaticHostname"))
134 i->static_hostname = s;
135 if (streq(name, "PrettyHostname"))
136 i->pretty_hostname = s;
137 if (streq(name, "IconName"))
139 if (streq(name, "Chassis"))
149 static int show_status(DBusConnection *bus, char **args, unsigned n) {
150 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
151 const char *interface = "";
153 DBusMessageIter iter, sub, sub2, sub3;
158 r = bus_method_call_with_reply(
160 "org.freedesktop.hostname1",
161 "/org/freedesktop/hostname1",
162 "org.freedesktop.DBus.Properties",
166 DBUS_TYPE_STRING, &interface,
171 if (!dbus_message_iter_init(reply, &iter) ||
172 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
173 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
174 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) {
249 r = bus_method_call_with_reply(
251 "org.freedesktop.hostname1",
252 "/org/freedesktop/hostname1",
253 "org.freedesktop.hostname1",
257 DBUS_TYPE_STRING, &hostname,
258 DBUS_TYPE_BOOLEAN, &interactive,
263 h = strdup(hostname);
267 hostname = hostname_simplify(h);
270 if (arg_set_static) {
271 r = bus_method_call_with_reply(
273 "org.freedesktop.hostname1",
274 "/org/freedesktop/hostname1",
275 "org.freedesktop.hostname1",
279 DBUS_TYPE_STRING, &hostname,
280 DBUS_TYPE_BOOLEAN, &interactive,
287 if (arg_set_transient) {
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,
307 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
308 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
309 dbus_bool_t interactive = true;
314 polkit_agent_open_if_enabled();
316 return bus_method_call_with_reply(
318 "org.freedesktop.hostname1",
319 "/org/freedesktop/hostname1",
320 "org.freedesktop.hostname1",
324 DBUS_TYPE_STRING, &args[1],
325 DBUS_TYPE_BOOLEAN, &interactive,
329 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
330 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
331 dbus_bool_t interactive = true;
336 polkit_agent_open_if_enabled();
338 return bus_method_call_with_reply(
340 "org.freedesktop.hostname1",
341 "/org/freedesktop/hostname1",
342 "org.freedesktop.hostname1",
346 DBUS_TYPE_STRING, &args[1],
347 DBUS_TYPE_BOOLEAN, &interactive,
351 static int help(void) {
353 printf("%s [OPTIONS...] COMMAND ...\n\n"
354 "Query or change system hostname.\n\n"
355 " -h --help Show this help\n"
356 " --version Show package version\n"
357 " --transient Only set transient hostname\n"
358 " --static Only set static hostname\n"
359 " --pretty Only set pretty hostname\n"
360 " --no-ask-password Do not prompt for password\n"
361 " -H --host=[USER@]HOST Operate on remote host\n\n"
363 " status Show current hostname settings\n"
364 " set-hostname NAME Set system hostname\n"
365 " set-icon-name NAME Set icon name for host\n"
366 " set-chassis NAME Set chassis type for host\n",
367 program_invocation_short_name);
372 static int parse_argv(int argc, char *argv[]) {
382 static const struct option options[] = {
383 { "help", no_argument, NULL, 'h' },
384 { "version", no_argument, NULL, ARG_VERSION },
385 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
386 { "static", no_argument, NULL, ARG_SET_STATIC },
387 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
388 { "host", required_argument, NULL, 'H' },
389 { "privileged", no_argument, NULL, 'P' },
390 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
399 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
408 puts(PACKAGE_STRING);
409 puts(SYSTEMD_FEATURES);
413 arg_transport = TRANSPORT_POLKIT;
417 arg_transport = TRANSPORT_SSH;
421 case ARG_SET_TRANSIENT:
422 arg_set_transient = true;
426 arg_set_pretty = true;
430 arg_set_static = true;
433 case ARG_NO_ASK_PASSWORD:
434 arg_ask_password = false;
441 log_error("Unknown option code %c", c);
446 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
447 arg_set_transient = arg_set_pretty = arg_set_static = true;
452 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
454 static const struct {
462 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
464 { "status", LESS, 1, show_status },
465 { "set-hostname", EQUAL, 2, set_hostname },
466 { "set-icon-name", EQUAL, 2, set_icon_name },
467 { "set-chassis", EQUAL, 2, set_chassis },
477 left = argc - optind;
480 /* Special rule: no arguments means "status" */
483 if (streq(argv[optind], "help")) {
488 for (i = 0; i < ELEMENTSOF(verbs); i++)
489 if (streq(argv[optind], verbs[i].verb))
492 if (i >= ELEMENTSOF(verbs)) {
493 log_error("Unknown operation %s", argv[optind]);
498 switch (verbs[i].argc_cmp) {
501 if (left != verbs[i].argc) {
502 log_error("Invalid number of arguments.");
509 if (left < verbs[i].argc) {
510 log_error("Too few arguments.");
517 if (left > verbs[i].argc) {
518 log_error("Too many arguments.");
525 assert_not_reached("Unknown comparison operator.");
529 log_error("Failed to get D-Bus connection: %s", error->message);
533 return verbs[i].dispatch(bus, argv + optind, left);
536 int main(int argc, char *argv[]) {
537 int r, retval = EXIT_FAILURE;
538 DBusConnection *bus = NULL;
541 dbus_error_init(&error);
543 setlocale(LC_ALL, "");
544 log_parse_environment();
547 r = parse_argv(argc, argv);
551 retval = EXIT_SUCCESS;
555 if (arg_transport == TRANSPORT_NORMAL)
556 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
557 else if (arg_transport == TRANSPORT_POLKIT)
558 bus_connect_system_polkit(&bus, &error);
559 else if (arg_transport == TRANSPORT_SSH)
560 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
562 assert_not_reached("Uh, invalid transport...");
564 r = hostnamectl_main(bus, argc, argv, &error);
565 retval = r < 0 ? EXIT_FAILURE : r;
569 dbus_connection_flush(bus);
570 dbus_connection_close(bus);
571 dbus_connection_unref(bus);
574 dbus_error_free(&error);