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