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 static void polkit_agent_open_if_enabled(void) {
53 /* Open the polkit agent as a child process if necessary */
54 if (!arg_ask_password)
57 if (arg_transport != BUS_TRANSPORT_LOCAL)
63 typedef struct StatusInfo {
65 char *static_hostname;
66 char *pretty_hostname;
71 static void print_status_info(StatusInfo *i) {
74 const char *id = NULL;
75 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
80 printf(" Static hostname: %s\n",
81 strna(i->static_hostname));
83 if (!isempty(i->pretty_hostname) &&
84 !streq_ptr(i->pretty_hostname, i->static_hostname))
85 printf(" Pretty hostname: %s\n",
86 strna(i->pretty_hostname));
88 if (!isempty(i->hostname) &&
89 !streq_ptr(i->hostname, i->static_hostname))
90 printf("Transient hostname: %s\n",
93 printf(" Icon name: %s\n"
98 r = sd_id128_get_machine(&mid);
100 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
102 r = sd_id128_get_boot(&bid);
104 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
106 if (detect_virtualization(&id) > 0)
107 printf(" Virtualization: %s\n", id);
109 r = parse_env_file("/etc/os-release", NEWLINE,
110 "PRETTY_NAME", &pretty_name,
111 "CPE_NAME", &cpe_name,
114 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
116 if (!isempty(pretty_name))
117 printf(" Operating System: %s\n", pretty_name);
119 if (!isempty(cpe_name))
120 printf(" CPE OS Name: %s\n", cpe_name);
122 assert_se(uname(&u) >= 0);
123 printf(" Kernel: %s %s\n"
124 " Architecture: %s\n", u.sysname, u.release, u.machine);
128 static int show_one_name(sd_bus *bus, const char* attr) {
129 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
130 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
134 r = sd_bus_get_property(
136 "org.freedesktop.hostname1",
137 "/org/freedesktop/hostname1",
138 "org.freedesktop.hostname1",
140 &error, &reply, "s");
142 log_error("Could not get property: %s", bus_error_message(&error, -r));
146 r = sd_bus_message_read(reply, "s", &s);
148 return bus_log_parse_error(r);
155 static int show_all_names(sd_bus *bus) {
156 StatusInfo info = {};
157 static const struct bus_properties_map map[] = {
158 { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) },
159 { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) },
160 { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) },
161 { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) },
162 { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) },
167 r = bus_map_all_properties(bus,
168 "org.freedesktop.hostname1",
169 "/org/freedesktop/hostname1",
175 print_status_info(&info);
179 free(info.static_hostname);
180 free(info.pretty_hostname);
181 free(info.icon_name);
186 static int show_status(sd_bus *bus, char **args, unsigned n) {
189 if (arg_pretty || arg_static || arg_transient) {
192 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
193 log_error("Cannot query more than one name type at a time");
197 attr = arg_pretty ? "PrettyHostname" :
198 arg_static ? "StaticHostname" : "Hostname";
200 return show_one_name(bus, attr);
202 return show_all_names(bus);
205 static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
206 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
209 polkit_agent_open_if_enabled();
211 r = sd_bus_call_method(
213 "org.freedesktop.hostname1",
214 "/org/freedesktop/hostname1",
215 "org.freedesktop.hostname1",
218 "sb", value, arg_ask_password);
220 log_error("Could not set property: %s", bus_error_message(&error, -r));
224 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
225 _cleanup_free_ char *h = NULL;
226 const char *hostname = args[1];
232 if (!arg_pretty && !arg_static && !arg_transient)
233 arg_pretty = arg_static = arg_transient = true;
238 /* If the passed hostname is already valid, then
239 * assume the user doesn't know anything about pretty
240 * hostnames, so let's unset the pretty hostname, and
241 * just set the passed hostname as static/dynamic
244 h = strdup(hostname);
248 hostname_cleanup(h, true);
250 if (arg_static && streq(h, hostname))
257 r = set_simple_string(bus, "SetPrettyHostname", p);
263 r = set_simple_string(bus, "SetStaticHostname", hostname);
269 r = set_simple_string(bus, "SetHostname", hostname);
277 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
281 return set_simple_string(bus, "SetIconName", args[1]);
284 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
288 return set_simple_string(bus, "SetChassis", args[1]);
291 static int help(void) {
293 printf("%s [OPTIONS...] COMMAND ...\n\n"
294 "Query or change system hostname.\n\n"
295 " -h --help Show this help\n"
296 " --version Show package version\n"
297 " --no-ask-password Do not prompt for password\n"
298 " -H --host=[USER@]HOST Operate on remote host\n"
299 " -M --machine=CONTAINER Operate on local container\n"
300 " --transient Only set transient hostname\n"
301 " --static Only set static hostname\n"
302 " --pretty Only set pretty hostname\n\n"
304 " status Show current hostname settings\n"
305 " set-hostname NAME Set system hostname\n"
306 " set-icon-name NAME Set icon name for host\n"
307 " set-chassis NAME Set chassis type for host\n",
308 program_invocation_short_name);
313 static int parse_argv(int argc, char *argv[]) {
323 static const struct option options[] = {
324 { "help", no_argument, NULL, 'h' },
325 { "version", no_argument, NULL, ARG_VERSION },
326 { "transient", no_argument, NULL, ARG_TRANSIENT },
327 { "static", no_argument, NULL, ARG_STATIC },
328 { "pretty", no_argument, NULL, ARG_PRETTY },
329 { "host", required_argument, NULL, 'H' },
330 { "machine", required_argument, NULL, 'M' },
331 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
340 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
348 puts(PACKAGE_STRING);
349 puts(SYSTEMD_FEATURES);
353 arg_transport = BUS_TRANSPORT_REMOTE;
358 arg_transport = BUS_TRANSPORT_CONTAINER;
363 arg_transient = true;
374 case ARG_NO_ASK_PASSWORD:
375 arg_ask_password = false;
382 assert_not_reached("Unhandled option");
389 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
391 static const struct {
399 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
401 { "status", LESS, 1, show_status },
402 { "set-hostname", EQUAL, 2, set_hostname },
403 { "set-icon-name", EQUAL, 2, set_icon_name },
404 { "set-chassis", EQUAL, 2, set_chassis },
413 left = argc - optind;
416 /* Special rule: no arguments means "status" */
419 if (streq(argv[optind], "help")) {
424 for (i = 0; i < ELEMENTSOF(verbs); i++)
425 if (streq(argv[optind], verbs[i].verb))
428 if (i >= ELEMENTSOF(verbs)) {
429 log_error("Unknown operation %s", argv[optind]);
434 switch (verbs[i].argc_cmp) {
437 if (left != verbs[i].argc) {
438 log_error("Invalid number of arguments.");
445 if (left < verbs[i].argc) {
446 log_error("Too few arguments.");
453 if (left > verbs[i].argc) {
454 log_error("Too many arguments.");
461 assert_not_reached("Unknown comparison operator.");
464 return verbs[i].dispatch(bus, argv + optind, left);
467 int main(int argc, char *argv[]) {
468 _cleanup_bus_unref_ sd_bus *bus = NULL;
471 setlocale(LC_ALL, "");
472 log_parse_environment();
475 r = parse_argv(argc, argv);
479 r = bus_open_transport(arg_transport, arg_host, false, &bus);
481 log_error("Failed to create bus connection: %s", strerror(-r));
485 r = hostnamectl_main(bus, argc, argv);
488 return r < 0 ? EXIT_FAILURE : r;