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