]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamectl.c
systemctl: we show headers for list-units even when piped these days, hence do the...
[thirdparty/systemd.git] / src / hostname / hostnamectl.c
CommitLineData
dbc4fbae
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdlib.h>
23#include <stdbool.h>
24#include <unistd.h>
25#include <getopt.h>
a9cdc94f 26#include <locale.h>
dbc4fbae
LP
27#include <string.h>
28#include <sys/timex.h>
fe29f9d2 29#include <sys/utsname.h>
dbc4fbae 30
b028f3e4
SP
31#include "sd-bus.h"
32
33#include "bus-util.h"
34#include "bus-error.h"
dbc4fbae
LP
35#include "util.h"
36#include "spawn-polkit-agent.h"
37#include "build.h"
38#include "hwclock.h"
39#include "strv.h"
40#include "sd-id128.h"
41#include "virt.h"
a5c32cff 42#include "fileio.h"
dbc4fbae 43
dbc4fbae 44static bool arg_ask_password = true;
b028f3e4 45static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 46static char *arg_host = NULL;
960787ae
ZJS
47static bool arg_transient = false;
48static bool arg_pretty = false;
49static bool arg_static = false;
dbc4fbae 50
dbc4fbae 51typedef struct StatusInfo {
b028f3e4
SP
52 char *hostname;
53 char *static_hostname;
54 char *pretty_hostname;
55 char *icon_name;
56 char *chassis;
dbc4fbae
LP
57} StatusInfo;
58
59static void print_status_info(StatusInfo *i) {
60 sd_id128_t mid, bid;
61 int r;
fe29f9d2
LP
62 const char *id = NULL;
63 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
64 struct utsname u;
dbc4fbae
LP
65
66 assert(i);
67
68 printf(" Static hostname: %s\n",
69 strna(i->static_hostname));
70
c0b21b96
LP
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));
75
76 if (!isempty(i->hostname) &&
77 !streq_ptr(i->hostname, i->static_hostname))
dbc4fbae
LP
78 printf("Transient hostname: %s\n",
79 strna(i->hostname));
80
c0b21b96 81 printf(" Icon name: %s\n"
7871c8e9 82 " Chassis: %s\n",
7871c8e9
LP
83 strna(i->icon_name),
84 strna(i->chassis));
dbc4fbae
LP
85
86 r = sd_id128_get_machine(&mid);
87 if (r >= 0)
88 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
89
90 r = sd_id128_get_boot(&bid);
91 if (r >= 0)
92 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
93
fe29f9d2 94 if (detect_virtualization(&id) > 0)
dbc4fbae 95 printf(" Virtualization: %s\n", id);
fe29f9d2
LP
96
97 r = parse_env_file("/etc/os-release", NEWLINE,
98 "PRETTY_NAME", &pretty_name,
99 "CPE_NAME", &cpe_name,
100 NULL);
872c8faa
ZJS
101 if (r < 0)
102 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
fe29f9d2
LP
103
104 if (!isempty(pretty_name))
105 printf(" Operating System: %s\n", pretty_name);
106
107 if (!isempty(cpe_name))
108 printf(" CPE OS Name: %s\n", cpe_name);
109
110 assert_se(uname(&u) >= 0);
111 printf(" Kernel: %s %s\n"
112 " Architecture: %s\n", u.sysname, u.release, u.machine);
113
dbc4fbae
LP
114}
115
b028f3e4
SP
116static 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;
119 const char *s;
960787ae
ZJS
120 int r;
121
b028f3e4 122 r = sd_bus_get_property(
960787ae
ZJS
123 bus,
124 "org.freedesktop.hostname1",
125 "/org/freedesktop/hostname1",
b028f3e4
SP
126 "org.freedesktop.hostname1",
127 attr,
128 &error, &reply, "s");
129 if (r < 0) {
130 log_error("Could not get property: %s", bus_error_message(&error, -r));
960787ae 131 return r;
960787ae
ZJS
132 }
133
b028f3e4
SP
134 r = sd_bus_message_read(reply, "s", &s);
135 if (r < 0)
136 return r;
960787ae 137
960787ae
ZJS
138 printf("%s\n", s);
139
140 return 0;
141}
142
b028f3e4 143static int show_all_names(sd_bus *bus) {
b92bea5d 144 StatusInfo info = {};
9f6eb1cd
KS
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) },
b028f3e4
SP
151 {}
152 };
153 int r;
dbc4fbae 154
b028f3e4
SP
155 r = bus_map_all_properties(bus,
156 "org.freedesktop.hostname1",
157 "/org/freedesktop/hostname1",
9f6eb1cd
KS
158 map,
159 &info);
dbc4fbae 160 if (r < 0)
b028f3e4 161 goto fail;
dbc4fbae
LP
162
163 print_status_info(&info);
b028f3e4
SP
164
165fail:
166 free(info.hostname);
167 free(info.static_hostname);
168 free(info.pretty_hostname);
169 free(info.icon_name);
170 free(info.chassis);
dbc4fbae
LP
171 return 0;
172}
173
b028f3e4 174static int show_status(sd_bus *bus, char **args, unsigned n) {
960787ae
ZJS
175 assert(args);
176
177 if (arg_pretty || arg_static || arg_transient) {
178 const char *attr;
179
180 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
181 log_error("Cannot query more than one name type at a time");
182 return -EINVAL;
183 }
184
185 attr = arg_pretty ? "PrettyHostname" :
186 arg_static ? "StaticHostname" : "Hostname";
187
188 return show_one_name(bus, attr);
189 } else
190 return show_all_names(bus);
191}
192
b028f3e4
SP
193static 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;
196 int r = 0;
197
198 if (first && arg_ask_password)
199 polkit_agent_open();
200 first = false;
201
202 r = sd_bus_call_method(
203 bus,
204 "org.freedesktop.hostname1",
205 "/org/freedesktop/hostname1",
206 "org.freedesktop.hostname1",
207 method,
208 &error, NULL,
209 "sb", value, arg_ask_password);
210 if (r < 0)
211 log_error("Could not set property: %s", bus_error_message(&error, -r));
212 return r;
213}
214
215static int set_hostname(sd_bus *bus, char **args, unsigned n) {
dbc4fbae
LP
216 _cleanup_free_ char *h = NULL;
217 const char *hostname = args[1];
218 int r;
219
220 assert(args);
221 assert(n == 2);
222
960787ae
ZJS
223 if (!arg_pretty && !arg_static && !arg_transient)
224 arg_pretty = arg_static = arg_transient = true;
225
226 if (arg_pretty) {
fda2c5d2
LP
227 const char *p;
228
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
233 * hostname. */
234
e724b063
LP
235 h = strdup(hostname);
236 if (!h)
237 return log_oom();
238
239 hostname_cleanup(h, true);
240
960787ae 241 if (arg_static && streq(h, hostname))
fda2c5d2
LP
242 p = "";
243 else {
244 p = hostname;
e724b063 245 hostname = h;
fda2c5d2
LP
246 }
247
b028f3e4 248 r = set_simple_string(bus, "SetPrettyHostname", p);
dbc4fbae
LP
249 if (r < 0)
250 return r;
dbc4fbae
LP
251 }
252
960787ae 253 if (arg_static) {
b028f3e4 254 r = set_simple_string(bus, "SetStaticHostname", hostname);
dbc4fbae
LP
255 if (r < 0)
256 return r;
257 }
258
960787ae 259 if (arg_transient) {
b028f3e4 260 r = set_simple_string(bus, "SetHostname", hostname);
dbc4fbae
LP
261 if (r < 0)
262 return r;
263 }
264
265 return 0;
266}
267
b028f3e4 268static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
dbc4fbae
LP
269 assert(args);
270 assert(n == 2);
271
b028f3e4 272 return set_simple_string(bus, "SetIconName", args[1]);
dbc4fbae
LP
273}
274
b028f3e4 275static int set_chassis(sd_bus *bus, char **args, unsigned n) {
7871c8e9
LP
276 assert(args);
277 assert(n == 2);
278
b028f3e4 279 return set_simple_string(bus, "SetChasis", args[1]);
7871c8e9
LP
280}
281
dbc4fbae
LP
282static int help(void) {
283
7591abd4
LP
284 printf("%s [OPTIONS...] COMMAND ...\n\n"
285 "Query or change system hostname.\n\n"
dbc4fbae
LP
286 " -h --help Show this help\n"
287 " --version Show package version\n"
dbc4fbae
LP
288 " --transient Only set transient hostname\n"
289 " --static Only set static hostname\n"
290 " --pretty Only set pretty hostname\n"
7591abd4 291 " --no-ask-password Do not prompt for password\n"
b028f3e4
SP
292 " -H --host=[USER@]HOST Operate on remote host\n"
293 " -M --machine=CONTAINER Operate on local container\n\n"
dbc4fbae 294 "Commands:\n"
7591abd4
LP
295 " status Show current hostname settings\n"
296 " set-hostname NAME Set system hostname\n"
7871c8e9
LP
297 " set-icon-name NAME Set icon name for host\n"
298 " set-chassis NAME Set chassis type for host\n",
dbc4fbae
LP
299 program_invocation_short_name);
300
301 return 0;
302}
303
304static int parse_argv(int argc, char *argv[]) {
305
306 enum {
307 ARG_VERSION = 0x100,
308 ARG_NO_ASK_PASSWORD,
960787ae
ZJS
309 ARG_TRANSIENT,
310 ARG_STATIC,
311 ARG_PRETTY
dbc4fbae
LP
312 };
313
314 static const struct option options[] = {
315 { "help", no_argument, NULL, 'h' },
316 { "version", no_argument, NULL, ARG_VERSION },
eb9da376
LP
317 { "transient", no_argument, NULL, ARG_TRANSIENT },
318 { "static", no_argument, NULL, ARG_STATIC },
319 { "pretty", no_argument, NULL, ARG_PRETTY },
dbc4fbae 320 { "host", required_argument, NULL, 'H' },
b028f3e4 321 { "machine", required_argument, NULL, 'M' },
dbc4fbae 322 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 323 {}
dbc4fbae
LP
324 };
325
326 int c;
327
328 assert(argc >= 0);
329 assert(argv);
330
fc7689bc 331 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
dbc4fbae
LP
332
333 switch (c) {
334
335 case 'h':
eb9da376 336 return help();
dbc4fbae
LP
337
338 case ARG_VERSION:
339 puts(PACKAGE_STRING);
dbc4fbae
LP
340 puts(SYSTEMD_FEATURES);
341 return 0;
342
b028f3e4
SP
343 case 'H':
344 arg_transport = BUS_TRANSPORT_REMOTE;
345 arg_host = optarg;
dbc4fbae
LP
346 break;
347
b028f3e4
SP
348 case 'M':
349 arg_transport = BUS_TRANSPORT_CONTAINER;
350 arg_host = optarg;
dbc4fbae
LP
351 break;
352
960787ae
ZJS
353 case ARG_TRANSIENT:
354 arg_transient = true;
dbc4fbae
LP
355 break;
356
960787ae
ZJS
357 case ARG_PRETTY:
358 arg_pretty = true;
dbc4fbae
LP
359 break;
360
960787ae
ZJS
361 case ARG_STATIC:
362 arg_static = true;
dbc4fbae
LP
363 break;
364
59f432ea
LP
365 case ARG_NO_ASK_PASSWORD:
366 arg_ask_password = false;
367 break;
368
dbc4fbae
LP
369 case '?':
370 return -EINVAL;
371
372 default:
eb9da376 373 assert_not_reached("Unhandled option");
dbc4fbae
LP
374 }
375 }
376
dbc4fbae
LP
377 return 1;
378}
379
b028f3e4 380static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
dbc4fbae
LP
381
382 static const struct {
383 const char* verb;
384 const enum {
385 MORE,
386 LESS,
387 EQUAL
388 } argc_cmp;
389 const int argc;
b028f3e4 390 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
dbc4fbae 391 } verbs[] = {
7871c8e9
LP
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 },
dbc4fbae
LP
396 };
397
398 int left;
399 unsigned i;
400
401 assert(argc >= 0);
402 assert(argv);
dbc4fbae
LP
403
404 left = argc - optind;
405
406 if (left <= 0)
407 /* Special rule: no arguments means "status" */
408 i = 0;
409 else {
410 if (streq(argv[optind], "help")) {
411 help();
412 return 0;
413 }
414
415 for (i = 0; i < ELEMENTSOF(verbs); i++)
416 if (streq(argv[optind], verbs[i].verb))
417 break;
418
419 if (i >= ELEMENTSOF(verbs)) {
420 log_error("Unknown operation %s", argv[optind]);
421 return -EINVAL;
422 }
423 }
424
425 switch (verbs[i].argc_cmp) {
426
427 case EQUAL:
428 if (left != verbs[i].argc) {
429 log_error("Invalid number of arguments.");
430 return -EINVAL;
431 }
432
433 break;
434
435 case MORE:
436 if (left < verbs[i].argc) {
437 log_error("Too few arguments.");
438 return -EINVAL;
439 }
440
441 break;
442
443 case LESS:
444 if (left > verbs[i].argc) {
445 log_error("Too many arguments.");
446 return -EINVAL;
447 }
448
449 break;
450
451 default:
452 assert_not_reached("Unknown comparison operator.");
453 }
454
dbc4fbae
LP
455 return verbs[i].dispatch(bus, argv + optind, left);
456}
457
458int main(int argc, char *argv[]) {
b028f3e4 459 _cleanup_bus_unref_ sd_bus *bus = NULL;
84f6181c 460 int r;
dbc4fbae 461
a9cdc94f 462 setlocale(LC_ALL, "");
dbc4fbae
LP
463 log_parse_environment();
464 log_open();
465
466 r = parse_argv(argc, argv);
b028f3e4 467 if (r <= 0)
dbc4fbae 468 goto finish;
b028f3e4
SP
469
470 r = bus_open_transport(arg_transport, arg_host, false, &bus);
471 if (r < 0) {
472 log_error("Failed to create bus connection: %s", strerror(-r));
dbc4fbae
LP
473 goto finish;
474 }
475
b028f3e4 476 r = hostnamectl_main(bus, argc, argv);
dbc4fbae
LP
477
478finish:
b028f3e4 479 return r < 0 ? EXIT_FAILURE : r;
dbc4fbae 480}