]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamectl.c
hostnamectl: read virtualization/architecture from remote side
[thirdparty/systemd.git] / src / hostname / hostnamectl.c
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>
26 #include <locale.h>
27 #include <string.h>
28 #include <sys/timex.h>
29 #include <sys/utsname.h>
30
31 #include "sd-bus.h"
32
33 #include "bus-util.h"
34 #include "bus-error.h"
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"
42 #include "architecture.h"
43 #include "fileio.h"
44
45 static bool arg_ask_password = true;
46 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
47 static char *arg_host = NULL;
48 static bool arg_transient = false;
49 static bool arg_pretty = false;
50 static bool arg_static = false;
51
52 static 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
64 typedef struct StatusInfo {
65 char *hostname;
66 char *static_hostname;
67 char *pretty_hostname;
68 char *icon_name;
69 char *chassis;
70 char *virtualization;
71 char *architecture;
72 } StatusInfo;
73
74 static void print_status_info(StatusInfo *i) {
75 sd_id128_t mid = {}, bid = {};
76 int r;
77 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
78 struct utsname u;
79
80 assert(i);
81
82 printf(" Static hostname: %s\n", strna(i->static_hostname));
83
84 if (!isempty(i->pretty_hostname) &&
85 !streq_ptr(i->pretty_hostname, i->static_hostname))
86 printf(" Pretty hostname: %s\n", i->pretty_hostname);
87
88 if (!isempty(i->hostname) &&
89 !streq_ptr(i->hostname, i->static_hostname))
90 printf("Transient hostname: %s\n", i->hostname);
91
92 printf(" Icon name: %s\n"
93 " Chassis: %s\n",
94 strna(i->icon_name),
95 strna(i->chassis));
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
105 if (!isempty(i->virtualization))
106 printf(" Virtualization: %s\n", i->virtualization);
107
108 r = parse_env_file("/etc/os-release", NEWLINE,
109 "PRETTY_NAME", &pretty_name,
110 "CPE_NAME", &cpe_name,
111 NULL);
112 if (r < 0)
113 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
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);
122 printf(" Kernel: %s %s\n", u.sysname, u.release);
123
124 if (!isempty(i->architecture))
125 printf(" Architecture: %s\n", i->architecture);
126
127 }
128
129 static 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;
133 int r;
134
135 r = sd_bus_get_property(
136 bus,
137 "org.freedesktop.hostname1",
138 "/org/freedesktop/hostname1",
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));
144 return r;
145 }
146
147 r = sd_bus_message_read(reply, "s", &s);
148 if (r < 0)
149 return bus_log_parse_error(r);
150
151 printf("%s\n", s);
152
153 return 0;
154 }
155
156 static int show_all_names(sd_bus *bus) {
157 StatusInfo info = {};
158
159 static const struct bus_properties_map hostname_map[] = {
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) },
165 {}
166 };
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
174 int r;
175
176 r = bus_map_all_properties(bus,
177 "org.freedesktop.hostname1",
178 "/org/freedesktop/hostname1",
179 hostname_map,
180 &info);
181 if (r < 0)
182 goto fail;
183
184 bus_map_all_properties(bus,
185 "org.freedesktop.systemd1",
186 "/org/freedesktop/systemd1",
187 manager_map,
188 &info);
189
190 print_status_info(&info);
191
192 fail:
193 free(info.hostname);
194 free(info.static_hostname);
195 free(info.pretty_hostname);
196 free(info.icon_name);
197 free(info.chassis);
198 free(info.virtualization);
199 free(info.architecture);
200
201 return r;
202 }
203
204 static int show_status(sd_bus *bus, char **args, unsigned n) {
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
223 static 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;
225 int r = 0;
226
227 polkit_agent_open_if_enabled();
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
242 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
243 _cleanup_free_ char *h = NULL;
244 const char *hostname = args[1];
245 int r;
246
247 assert(args);
248 assert(n == 2);
249
250 if (!arg_pretty && !arg_static && !arg_transient)
251 arg_pretty = arg_static = arg_transient = true;
252
253 if (arg_pretty) {
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
262 h = strdup(hostname);
263 if (!h)
264 return log_oom();
265
266 hostname_cleanup(h, true);
267
268 if (arg_static && streq(h, hostname))
269 p = "";
270 else {
271 p = hostname;
272 hostname = h;
273 }
274
275 r = set_simple_string(bus, "SetPrettyHostname", p);
276 if (r < 0)
277 return r;
278 }
279
280 if (arg_static) {
281 r = set_simple_string(bus, "SetStaticHostname", hostname);
282 if (r < 0)
283 return r;
284 }
285
286 if (arg_transient) {
287 r = set_simple_string(bus, "SetHostname", hostname);
288 if (r < 0)
289 return r;
290 }
291
292 return 0;
293 }
294
295 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
296 assert(args);
297 assert(n == 2);
298
299 return set_simple_string(bus, "SetIconName", args[1]);
300 }
301
302 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
303 assert(args);
304 assert(n == 2);
305
306 return set_simple_string(bus, "SetChassis", args[1]);
307 }
308
309 static int help(void) {
310
311 printf("%s [OPTIONS...] COMMAND ...\n\n"
312 "Query or change system hostname.\n\n"
313 " -h --help Show this help\n"
314 " --version Show package version\n"
315 " --no-ask-password Do not prompt for password\n"
316 " -H --host=[USER@]HOST Operate on remote host\n"
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"
321 "Commands:\n"
322 " status Show current hostname settings\n"
323 " set-hostname NAME Set system hostname\n"
324 " set-icon-name NAME Set icon name for host\n"
325 " set-chassis NAME Set chassis type for host\n",
326 program_invocation_short_name);
327
328 return 0;
329 }
330
331 static int parse_argv(int argc, char *argv[]) {
332
333 enum {
334 ARG_VERSION = 0x100,
335 ARG_NO_ASK_PASSWORD,
336 ARG_TRANSIENT,
337 ARG_STATIC,
338 ARG_PRETTY
339 };
340
341 static const struct option options[] = {
342 { "help", no_argument, NULL, 'h' },
343 { "version", no_argument, NULL, ARG_VERSION },
344 { "transient", no_argument, NULL, ARG_TRANSIENT },
345 { "static", no_argument, NULL, ARG_STATIC },
346 { "pretty", no_argument, NULL, ARG_PRETTY },
347 { "host", required_argument, NULL, 'H' },
348 { "machine", required_argument, NULL, 'M' },
349 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
350 {}
351 };
352
353 int c;
354
355 assert(argc >= 0);
356 assert(argv);
357
358 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
359
360 switch (c) {
361
362 case 'h':
363 return help();
364
365 case ARG_VERSION:
366 puts(PACKAGE_STRING);
367 puts(SYSTEMD_FEATURES);
368 return 0;
369
370 case 'H':
371 arg_transport = BUS_TRANSPORT_REMOTE;
372 arg_host = optarg;
373 break;
374
375 case 'M':
376 arg_transport = BUS_TRANSPORT_CONTAINER;
377 arg_host = optarg;
378 break;
379
380 case ARG_TRANSIENT:
381 arg_transient = true;
382 break;
383
384 case ARG_PRETTY:
385 arg_pretty = true;
386 break;
387
388 case ARG_STATIC:
389 arg_static = true;
390 break;
391
392 case ARG_NO_ASK_PASSWORD:
393 arg_ask_password = false;
394 break;
395
396 case '?':
397 return -EINVAL;
398
399 default:
400 assert_not_reached("Unhandled option");
401 }
402 }
403
404 return 1;
405 }
406
407 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
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;
417 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
418 } verbs[] = {
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 },
423 };
424
425 int left;
426 unsigned i;
427
428 assert(argc >= 0);
429 assert(argv);
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
482 return verbs[i].dispatch(bus, argv + optind, left);
483 }
484
485 int main(int argc, char *argv[]) {
486 _cleanup_bus_unref_ sd_bus *bus = NULL;
487 int r;
488
489 setlocale(LC_ALL, "");
490 log_parse_environment();
491 log_open();
492
493 r = parse_argv(argc, argv);
494 if (r <= 0)
495 goto finish;
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));
500 goto finish;
501 }
502
503 r = hostnamectl_main(bus, argc, argv);
504
505 finish:
506 return r < 0 ? EXIT_FAILURE : r;
507 }