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>
34 #include "bus-error.h"
36 #include "spawn-polkit-agent.h"
44 static bool arg_ask_password = true;
45 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
46 static char *arg_host = NULL;
47 static bool arg_transient = false;
48 static bool arg_pretty = false;
49 static bool arg_static = false;
51 typedef struct StatusInfo {
53 char *static_hostname;
54 char *pretty_hostname;
59 static void print_status_info(StatusInfo *i) {
62 const char *id = NULL;
63 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
68 printf(" Static hostname: %s\n",
69 strna(i->static_hostname));
71 if (!isempty(i->pretty_hostname) &&
72 !streq_ptr(i->pretty_hostname, i->static_hostname))
73 printf(" Pretty hostname: %s\n",
74 strna(i->pretty_hostname));
76 if (!isempty(i->hostname) &&
77 !streq_ptr(i->hostname, i->static_hostname))
78 printf("Transient hostname: %s\n",
81 printf(" Icon name: %s\n"
86 r = sd_id128_get_machine(&mid);
88 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
90 r = sd_id128_get_boot(&bid);
92 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
94 if (detect_virtualization(&id) > 0)
95 printf(" Virtualization: %s\n", id);
97 r = parse_env_file("/etc/os-release", NEWLINE,
98 "PRETTY_NAME", &pretty_name,
99 "CPE_NAME", &cpe_name,
102 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
104 if (!isempty(pretty_name))
105 printf(" Operating System: %s\n", pretty_name);
107 if (!isempty(cpe_name))
108 printf(" CPE OS Name: %s\n", cpe_name);
110 assert_se(uname(&u) >= 0);
111 printf(" Kernel: %s %s\n"
112 " Architecture: %s\n", u.sysname, u.release, u.machine);
116 static int show_one_name(sd_bus *bus, const char* attr) {
117 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
118 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
122 r = sd_bus_get_property(
124 "org.freedesktop.hostname1",
125 "/org/freedesktop/hostname1",
126 "org.freedesktop.hostname1",
128 &error, &reply, "s");
130 log_error("Could not get property: %s", bus_error_message(&error, -r));
134 r = sd_bus_message_read(reply, "s", &s);
143 static int show_all_names(sd_bus *bus) {
144 StatusInfo info = {};
145 static const struct bus_properties_map map[] = {
146 { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) },
147 { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) },
148 { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) },
149 { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) },
150 { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) },
155 r = bus_map_all_properties(bus,
156 "org.freedesktop.hostname1",
157 "/org/freedesktop/hostname1",
163 print_status_info(&info);
167 free(info.static_hostname);
168 free(info.pretty_hostname);
169 free(info.icon_name);
174 static int show_status(sd_bus *bus, char **args, unsigned n) {
177 if (arg_pretty || arg_static || arg_transient) {
180 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
181 log_error("Cannot query more than one name type at a time");
185 attr = arg_pretty ? "PrettyHostname" :
186 arg_static ? "StaticHostname" : "Hostname";
188 return show_one_name(bus, attr);
190 return show_all_names(bus);
193 static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
194 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
195 static bool first=true;
198 if (first && arg_ask_password)
202 r = sd_bus_call_method(
204 "org.freedesktop.hostname1",
205 "/org/freedesktop/hostname1",
206 "org.freedesktop.hostname1",
209 "sb", value, arg_ask_password);
211 log_error("Could not set property: %s", bus_error_message(&error, -r));
215 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
216 _cleanup_free_ char *h = NULL;
217 const char *hostname = args[1];
223 if (!arg_pretty && !arg_static && !arg_transient)
224 arg_pretty = arg_static = arg_transient = true;
229 /* If the passed hostname is already valid, then
230 * assume the user doesn't know anything about pretty
231 * hostnames, so let's unset the pretty hostname, and
232 * just set the passed hostname as static/dynamic
235 h = strdup(hostname);
239 hostname_cleanup(h, true);
241 if (arg_static && streq(h, hostname))
248 r = set_simple_string(bus, "SetPrettyHostname", p);
254 r = set_simple_string(bus, "SetStaticHostname", hostname);
260 r = set_simple_string(bus, "SetHostname", hostname);
268 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
272 return set_simple_string(bus, "SetIconName", args[1]);
275 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
279 return set_simple_string(bus, "SetChasis", args[1]);
282 static int help(void) {
284 printf("%s [OPTIONS...] COMMAND ...\n\n"
285 "Query or change system hostname.\n\n"
286 " -h --help Show this help\n"
287 " --version Show package version\n"
288 " --transient Only set transient hostname\n"
289 " --static Only set static hostname\n"
290 " --pretty Only set pretty hostname\n"
291 " --no-ask-password Do not prompt for password\n"
292 " -H --host=[USER@]HOST Operate on remote host\n"
293 " -M --machine=CONTAINER Operate on local container\n\n"
295 " status Show current hostname settings\n"
296 " set-hostname NAME Set system hostname\n"
297 " set-icon-name NAME Set icon name for host\n"
298 " set-chassis NAME Set chassis type for host\n",
299 program_invocation_short_name);
304 static int parse_argv(int argc, char *argv[]) {
314 static const struct option options[] = {
315 { "help", no_argument, NULL, 'h' },
316 { "version", no_argument, NULL, ARG_VERSION },
317 { "transient", no_argument, NULL, ARG_TRANSIENT },
318 { "static", no_argument, NULL, ARG_STATIC },
319 { "pretty", no_argument, NULL, ARG_PRETTY },
320 { "host", required_argument, NULL, 'H' },
321 { "machine", required_argument, NULL, 'M' },
322 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
331 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
339 puts(PACKAGE_STRING);
340 puts(SYSTEMD_FEATURES);
344 arg_transport = BUS_TRANSPORT_REMOTE;
349 arg_transport = BUS_TRANSPORT_CONTAINER;
354 arg_transient = true;
365 case ARG_NO_ASK_PASSWORD:
366 arg_ask_password = false;
373 assert_not_reached("Unhandled option");
380 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
382 static const struct {
390 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
392 { "status", LESS, 1, show_status },
393 { "set-hostname", EQUAL, 2, set_hostname },
394 { "set-icon-name", EQUAL, 2, set_icon_name },
395 { "set-chassis", EQUAL, 2, set_chassis },
404 left = argc - optind;
407 /* Special rule: no arguments means "status" */
410 if (streq(argv[optind], "help")) {
415 for (i = 0; i < ELEMENTSOF(verbs); i++)
416 if (streq(argv[optind], verbs[i].verb))
419 if (i >= ELEMENTSOF(verbs)) {
420 log_error("Unknown operation %s", argv[optind]);
425 switch (verbs[i].argc_cmp) {
428 if (left != verbs[i].argc) {
429 log_error("Invalid number of arguments.");
436 if (left < verbs[i].argc) {
437 log_error("Too few arguments.");
444 if (left > verbs[i].argc) {
445 log_error("Too many arguments.");
452 assert_not_reached("Unknown comparison operator.");
455 return verbs[i].dispatch(bus, argv + optind, left);
458 int main(int argc, char *argv[]) {
459 _cleanup_bus_unref_ sd_bus *bus = NULL;
462 setlocale(LC_ALL, "");
463 log_parse_environment();
466 r = parse_argv(argc, argv);
470 r = bus_open_transport(arg_transport, arg_host, false, &bus);
472 log_error("Failed to create bus connection: %s", strerror(-r));
476 r = hostnamectl_main(bus, argc, argv);
479 return r < 0 ? EXIT_FAILURE : r;