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