]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamectl.c
treewide: no need to negate errno for log_*_errno()
[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"
24efb112 38#include "clock-util.h"
dbc4fbae
LP
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;
799298d6 70 char *deployment;
41414fed 71 char *location;
fa4f8f9b
DH
72 char *kernel_name;
73 char *kernel_release;
3448456b
DH
74 char *os_pretty_name;
75 char *os_cpe_name;
e9a2e453
LP
76 char *virtualization;
77 char *architecture;
dbc4fbae
LP
78} StatusInfo;
79
80static void print_status_info(StatusInfo *i) {
39883f62 81 sd_id128_t mid = {}, bid = {};
dbc4fbae 82 int r;
dbc4fbae
LP
83
84 assert(i);
85
e9a2e453 86 printf(" Static hostname: %s\n", strna(i->static_hostname));
dbc4fbae 87
c0b21b96
LP
88 if (!isempty(i->pretty_hostname) &&
89 !streq_ptr(i->pretty_hostname, i->static_hostname))
e9a2e453 90 printf(" Pretty hostname: %s\n", i->pretty_hostname);
c0b21b96
LP
91
92 if (!isempty(i->hostname) &&
93 !streq_ptr(i->hostname, i->static_hostname))
e9a2e453 94 printf("Transient hostname: %s\n", i->hostname);
dbc4fbae 95
41414fed
LP
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);
dbc4fbae
LP
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
e9a2e453
LP
118 if (!isempty(i->virtualization))
119 printf(" Virtualization: %s\n", i->virtualization);
fe29f9d2 120
3448456b
DH
121 if (!isempty(i->os_pretty_name))
122 printf(" Operating System: %s\n", i->os_pretty_name);
fe29f9d2 123
3448456b
DH
124 if (!isempty(i->os_cpe_name))
125 printf(" CPE OS Name: %s\n", i->os_cpe_name);
fe29f9d2 126
fa4f8f9b
DH
127 if (!isempty(i->kernel_name) && !isempty(i->kernel_release))
128 printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release);
e9a2e453
LP
129
130 if (!isempty(i->architecture))
131 printf(" Architecture: %s\n", i->architecture);
fe29f9d2 132
dbc4fbae
LP
133}
134
b028f3e4
SP
135static 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;
960787ae
ZJS
139 int r;
140
b028f3e4 141 r = sd_bus_get_property(
960787ae
ZJS
142 bus,
143 "org.freedesktop.hostname1",
144 "/org/freedesktop/hostname1",
b028f3e4
SP
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));
960787ae 150 return r;
960787ae
ZJS
151 }
152
b028f3e4
SP
153 r = sd_bus_message_read(reply, "s", &s);
154 if (r < 0)
5b30bef8 155 return bus_log_parse_error(r);
960787ae 156
960787ae
ZJS
157 printf("%s\n", s);
158
159 return 0;
160}
161
b028f3e4 162static int show_all_names(sd_bus *bus) {
b92bea5d 163 StatusInfo info = {};
e9a2e453
LP
164
165 static const struct bus_properties_map hostname_map[] = {
41414fed
LP
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) },
b028f3e4
SP
177 {}
178 };
e9a2e453
LP
179
180 static const struct bus_properties_map manager_map[] = {
41414fed
LP
181 { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) },
182 { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) },
e9a2e453
LP
183 {}
184 };
185
b028f3e4 186 int r;
dbc4fbae 187
b028f3e4
SP
188 r = bus_map_all_properties(bus,
189 "org.freedesktop.hostname1",
190 "/org/freedesktop/hostname1",
e9a2e453 191 hostname_map,
9f6eb1cd 192 &info);
dbc4fbae 193 if (r < 0)
b028f3e4 194 goto fail;
dbc4fbae 195
e9a2e453
LP
196 bus_map_all_properties(bus,
197 "org.freedesktop.systemd1",
198 "/org/freedesktop/systemd1",
199 manager_map,
200 &info);
201
dbc4fbae 202 print_status_info(&info);
b028f3e4
SP
203
204fail:
205 free(info.hostname);
206 free(info.static_hostname);
207 free(info.pretty_hostname);
208 free(info.icon_name);
209 free(info.chassis);
799298d6 210 free(info.deployment);
41414fed 211 free(info.location);
fa4f8f9b
DH
212 free(info.kernel_name);
213 free(info.kernel_release);
3448456b
DH
214 free(info.os_pretty_name);
215 free(info.os_cpe_name);
e9a2e453
LP
216 free(info.virtualization);
217 free(info.architecture);
218
219 return r;
dbc4fbae
LP
220}
221
b028f3e4 222static int show_status(sd_bus *bus, char **args, unsigned n) {
960787ae
ZJS
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
b028f3e4
SP
241static 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;
b028f3e4
SP
243 int r = 0;
244
46e65dcc 245 polkit_agent_open_if_enabled();
b028f3e4
SP
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
260static int set_hostname(sd_bus *bus, char **args, unsigned n) {
dbc4fbae
LP
261 _cleanup_free_ char *h = NULL;
262 const char *hostname = args[1];
263 int r;
264
265 assert(args);
266 assert(n == 2);
267
960787ae
ZJS
268 if (!arg_pretty && !arg_static && !arg_transient)
269 arg_pretty = arg_static = arg_transient = true;
270
271 if (arg_pretty) {
fda2c5d2
LP
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
e724b063
LP
280 h = strdup(hostname);
281 if (!h)
282 return log_oom();
283
284 hostname_cleanup(h, true);
285
960787ae 286 if (arg_static && streq(h, hostname))
fda2c5d2
LP
287 p = "";
288 else {
289 p = hostname;
e724b063 290 hostname = h;
fda2c5d2
LP
291 }
292
b028f3e4 293 r = set_simple_string(bus, "SetPrettyHostname", p);
dbc4fbae
LP
294 if (r < 0)
295 return r;
dbc4fbae
LP
296 }
297
960787ae 298 if (arg_static) {
b028f3e4 299 r = set_simple_string(bus, "SetStaticHostname", hostname);
dbc4fbae
LP
300 if (r < 0)
301 return r;
302 }
303
960787ae 304 if (arg_transient) {
b028f3e4 305 r = set_simple_string(bus, "SetHostname", hostname);
dbc4fbae
LP
306 if (r < 0)
307 return r;
308 }
309
310 return 0;
311}
312
b028f3e4 313static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
dbc4fbae
LP
314 assert(args);
315 assert(n == 2);
316
b028f3e4 317 return set_simple_string(bus, "SetIconName", args[1]);
dbc4fbae
LP
318}
319
b028f3e4 320static int set_chassis(sd_bus *bus, char **args, unsigned n) {
7871c8e9
LP
321 assert(args);
322 assert(n == 2);
323
f20c84c1 324 return set_simple_string(bus, "SetChassis", args[1]);
7871c8e9
LP
325}
326
799298d6
JG
327static 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
41414fed
LP
334static 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
601185b4 341static void help(void) {
7591abd4
LP
342 printf("%s [OPTIONS...] COMMAND ...\n\n"
343 "Query or change system hostname.\n\n"
dbc4fbae
LP
344 " -h --help Show this help\n"
345 " --version Show package version\n"
7591abd4 346 " --no-ask-password Do not prompt for password\n"
b028f3e4 347 " -H --host=[USER@]HOST Operate on remote host\n"
a86a47ce
LP
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"
dbc4fbae 352 "Commands:\n"
7591abd4
LP
353 " status Show current hostname settings\n"
354 " set-hostname NAME Set system hostname\n"
7871c8e9 355 " set-icon-name NAME Set icon name for host\n"
799298d6 356 " set-chassis NAME Set chassis type for host\n"
601185b4 357 " set-deployment NAME Set deployment environment for host\n"
41414fed 358 " set-location NAME Set location for host\n"
601185b4 359 , program_invocation_short_name);
dbc4fbae
LP
360}
361
362static int parse_argv(int argc, char *argv[]) {
363
364 enum {
365 ARG_VERSION = 0x100,
366 ARG_NO_ASK_PASSWORD,
960787ae
ZJS
367 ARG_TRANSIENT,
368 ARG_STATIC,
369 ARG_PRETTY
dbc4fbae
LP
370 };
371
372 static const struct option options[] = {
373 { "help", no_argument, NULL, 'h' },
374 { "version", no_argument, NULL, ARG_VERSION },
eb9da376
LP
375 { "transient", no_argument, NULL, ARG_TRANSIENT },
376 { "static", no_argument, NULL, ARG_STATIC },
377 { "pretty", no_argument, NULL, ARG_PRETTY },
dbc4fbae 378 { "host", required_argument, NULL, 'H' },
b028f3e4 379 { "machine", required_argument, NULL, 'M' },
dbc4fbae 380 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 381 {}
dbc4fbae
LP
382 };
383
384 int c;
385
386 assert(argc >= 0);
387 assert(argv);
388
601185b4 389 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
dbc4fbae
LP
390
391 switch (c) {
392
393 case 'h':
601185b4
ZJS
394 help();
395 return 0;
dbc4fbae
LP
396
397 case ARG_VERSION:
398 puts(PACKAGE_STRING);
dbc4fbae
LP
399 puts(SYSTEMD_FEATURES);
400 return 0;
401
b028f3e4
SP
402 case 'H':
403 arg_transport = BUS_TRANSPORT_REMOTE;
404 arg_host = optarg;
dbc4fbae
LP
405 break;
406
b028f3e4
SP
407 case 'M':
408 arg_transport = BUS_TRANSPORT_CONTAINER;
409 arg_host = optarg;
dbc4fbae
LP
410 break;
411
960787ae
ZJS
412 case ARG_TRANSIENT:
413 arg_transient = true;
dbc4fbae
LP
414 break;
415
960787ae
ZJS
416 case ARG_PRETTY:
417 arg_pretty = true;
dbc4fbae
LP
418 break;
419
960787ae
ZJS
420 case ARG_STATIC:
421 arg_static = true;
dbc4fbae
LP
422 break;
423
59f432ea
LP
424 case ARG_NO_ASK_PASSWORD:
425 arg_ask_password = false;
426 break;
427
dbc4fbae
LP
428 case '?':
429 return -EINVAL;
430
431 default:
eb9da376 432 assert_not_reached("Unhandled option");
dbc4fbae 433 }
dbc4fbae 434
dbc4fbae
LP
435 return 1;
436}
437
b028f3e4 438static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
dbc4fbae
LP
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;
b028f3e4 448 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
dbc4fbae 449 } verbs[] = {
41414fed
LP
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 },
dbc4fbae
LP
456 };
457
458 int left;
459 unsigned i;
460
461 assert(argc >= 0);
462 assert(argv);
dbc4fbae
LP
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
dbc4fbae
LP
515 return verbs[i].dispatch(bus, argv + optind, left);
516}
517
518int main(int argc, char *argv[]) {
24996861 519 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
84f6181c 520 int r;
dbc4fbae 521
a9cdc94f 522 setlocale(LC_ALL, "");
dbc4fbae
LP
523 log_parse_environment();
524 log_open();
525
526 r = parse_argv(argc, argv);
b028f3e4 527 if (r <= 0)
dbc4fbae 528 goto finish;
b028f3e4
SP
529
530 r = bus_open_transport(arg_transport, arg_host, false, &bus);
531 if (r < 0) {
da927ba9 532 log_error_errno(r, "Failed to create bus connection: %m");
dbc4fbae
LP
533 goto finish;
534 }
535
b028f3e4 536 r = hostnamectl_main(bus, argc, argv);
dbc4fbae
LP
537
538finish:
b028f3e4 539 return r < 0 ? EXIT_FAILURE : r;
dbc4fbae 540}