]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamectl.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / hostname / hostnamectl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
dbc4fbae
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
dbc4fbae 21#include <getopt.h>
a9cdc94f 22#include <locale.h>
3f6fd1ba
LP
23#include <stdbool.h>
24#include <stdlib.h>
dbc4fbae 25#include <string.h>
dbc4fbae 26
b028f3e4 27#include "sd-bus.h"
958b66ea 28#include "sd-id128.h"
3f6fd1ba 29
b5efdb8a 30#include "alloc-util.h"
3f6fd1ba 31#include "architecture.h"
b028f3e4 32#include "bus-error.h"
3f6fd1ba
LP
33#include "bus-util.h"
34#include "hostname-util.h"
dbc4fbae 35#include "spawn-polkit-agent.h"
3f6fd1ba 36#include "util.h"
dbc4fbae 37
dbc4fbae 38static bool arg_ask_password = true;
b028f3e4 39static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 40static char *arg_host = NULL;
960787ae
ZJS
41static bool arg_transient = false;
42static bool arg_pretty = false;
43static bool arg_static = false;
dbc4fbae 44
dbc4fbae 45typedef struct StatusInfo {
b028f3e4
SP
46 char *hostname;
47 char *static_hostname;
48 char *pretty_hostname;
49 char *icon_name;
50 char *chassis;
799298d6 51 char *deployment;
41414fed 52 char *location;
fa4f8f9b
DH
53 char *kernel_name;
54 char *kernel_release;
3448456b
DH
55 char *os_pretty_name;
56 char *os_cpe_name;
e9a2e453
LP
57 char *virtualization;
58 char *architecture;
dbc4fbae
LP
59} StatusInfo;
60
61static void print_status_info(StatusInfo *i) {
39883f62 62 sd_id128_t mid = {}, bid = {};
dbc4fbae 63 int r;
dbc4fbae
LP
64
65 assert(i);
66
e9a2e453 67 printf(" Static hostname: %s\n", strna(i->static_hostname));
dbc4fbae 68
c0b21b96
LP
69 if (!isempty(i->pretty_hostname) &&
70 !streq_ptr(i->pretty_hostname, i->static_hostname))
e9a2e453 71 printf(" Pretty hostname: %s\n", i->pretty_hostname);
c0b21b96
LP
72
73 if (!isempty(i->hostname) &&
74 !streq_ptr(i->hostname, i->static_hostname))
e9a2e453 75 printf("Transient hostname: %s\n", i->hostname);
dbc4fbae 76
41414fed
LP
77 if (!isempty(i->icon_name))
78 printf(" Icon name: %s\n",
79 strna(i->icon_name));
80
81 if (!isempty(i->chassis))
82 printf(" Chassis: %s\n",
83 strna(i->chassis));
84
85 if (!isempty(i->deployment))
86 printf(" Deployment: %s\n", i->deployment);
87
88 if (!isempty(i->location))
89 printf(" Location: %s\n", i->location);
dbc4fbae
LP
90
91 r = sd_id128_get_machine(&mid);
92 if (r >= 0)
93 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
94
95 r = sd_id128_get_boot(&bid);
96 if (r >= 0)
97 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
98
e9a2e453
LP
99 if (!isempty(i->virtualization))
100 printf(" Virtualization: %s\n", i->virtualization);
fe29f9d2 101
3448456b
DH
102 if (!isempty(i->os_pretty_name))
103 printf(" Operating System: %s\n", i->os_pretty_name);
fe29f9d2 104
3448456b
DH
105 if (!isempty(i->os_cpe_name))
106 printf(" CPE OS Name: %s\n", i->os_cpe_name);
fe29f9d2 107
fa4f8f9b
DH
108 if (!isempty(i->kernel_name) && !isempty(i->kernel_release))
109 printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release);
e9a2e453
LP
110
111 if (!isempty(i->architecture))
112 printf(" Architecture: %s\n", i->architecture);
fe29f9d2 113
dbc4fbae
LP
114}
115
b028f3e4 116static int show_one_name(sd_bus *bus, const char* attr) {
4afd3348
LP
117 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
118 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b028f3e4 119 const char *s;
960787ae
ZJS
120 int r;
121
b028f3e4 122 r = sd_bus_get_property(
960787ae
ZJS
123 bus,
124 "org.freedesktop.hostname1",
125 "/org/freedesktop/hostname1",
b028f3e4
SP
126 "org.freedesktop.hostname1",
127 attr,
128 &error, &reply, "s");
f9e0eefc
LP
129 if (r < 0)
130 return log_error_errno(r, "Could not get property: %s", bus_error_message(&error, r));
960787ae 131
b028f3e4
SP
132 r = sd_bus_message_read(reply, "s", &s);
133 if (r < 0)
5b30bef8 134 return bus_log_parse_error(r);
960787ae 135
960787ae
ZJS
136 printf("%s\n", s);
137
138 return 0;
139}
140
f9e0eefc 141static int show_all_names(sd_bus *bus, sd_bus_error *error) {
b92bea5d 142 StatusInfo info = {};
e9a2e453
LP
143
144 static const struct bus_properties_map hostname_map[] = {
41414fed
LP
145 { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) },
146 { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) },
147 { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) },
148 { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) },
149 { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) },
150 { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) },
151 { "Location", "s", NULL, offsetof(StatusInfo, location) },
152 { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) },
153 { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) },
154 { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) },
155 { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) },
b028f3e4
SP
156 {}
157 };
e9a2e453
LP
158
159 static const struct bus_properties_map manager_map[] = {
41414fed
LP
160 { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) },
161 { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) },
e9a2e453
LP
162 {}
163 };
164
b028f3e4 165 int r;
dbc4fbae 166
b028f3e4
SP
167 r = bus_map_all_properties(bus,
168 "org.freedesktop.hostname1",
169 "/org/freedesktop/hostname1",
e9a2e453 170 hostname_map,
f9e0eefc 171 error,
9f6eb1cd 172 &info);
dbc4fbae 173 if (r < 0)
b028f3e4 174 goto fail;
dbc4fbae 175
e9a2e453
LP
176 bus_map_all_properties(bus,
177 "org.freedesktop.systemd1",
178 "/org/freedesktop/systemd1",
179 manager_map,
f9e0eefc 180 error,
e9a2e453
LP
181 &info);
182
dbc4fbae 183 print_status_info(&info);
b028f3e4
SP
184
185fail:
186 free(info.hostname);
187 free(info.static_hostname);
188 free(info.pretty_hostname);
189 free(info.icon_name);
190 free(info.chassis);
799298d6 191 free(info.deployment);
41414fed 192 free(info.location);
fa4f8f9b
DH
193 free(info.kernel_name);
194 free(info.kernel_release);
3448456b
DH
195 free(info.os_pretty_name);
196 free(info.os_cpe_name);
e9a2e453
LP
197 free(info.virtualization);
198 free(info.architecture);
199
200 return r;
dbc4fbae
LP
201}
202
b028f3e4 203static int show_status(sd_bus *bus, char **args, unsigned n) {
f9e0eefc
LP
204 int r;
205
960787ae
ZJS
206 assert(args);
207
208 if (arg_pretty || arg_static || arg_transient) {
209 const char *attr;
210
211 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
212 log_error("Cannot query more than one name type at a time");
213 return -EINVAL;
214 }
215
216 attr = arg_pretty ? "PrettyHostname" :
217 arg_static ? "StaticHostname" : "Hostname";
218
219 return show_one_name(bus, attr);
f9e0eefc
LP
220 } else {
221 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
222
223 r = show_all_names(bus, &error);
224 if (r < 0)
225 return log_error_errno(r, "Failed to query system properties: %s", bus_error_message(&error, r));
226
227 return 0;
228 }
960787ae
ZJS
229}
230
b028f3e4 231static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
4afd3348 232 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b028f3e4
SP
233 int r = 0;
234
8a4b13c5 235 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
b028f3e4
SP
236
237 r = sd_bus_call_method(
238 bus,
239 "org.freedesktop.hostname1",
240 "/org/freedesktop/hostname1",
241 "org.freedesktop.hostname1",
242 method,
243 &error, NULL,
244 "sb", value, arg_ask_password);
245 if (r < 0)
246 log_error("Could not set property: %s", bus_error_message(&error, -r));
247 return r;
248}
249
250static int set_hostname(sd_bus *bus, char **args, unsigned n) {
dbc4fbae 251 _cleanup_free_ char *h = NULL;
2ae0858e 252 const char *hostname = args[1];
dbc4fbae
LP
253 int r;
254
255 assert(args);
256 assert(n == 2);
257
960787ae
ZJS
258 if (!arg_pretty && !arg_static && !arg_transient)
259 arg_pretty = arg_static = arg_transient = true;
260
261 if (arg_pretty) {
fda2c5d2
LP
262 const char *p;
263
2ae0858e
LP
264 /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
265 * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
fda2c5d2 266 * hostname. */
2ae0858e
LP
267 if (arg_static && hostname_is_valid(hostname, true))
268 p = ""; /* No pretty hostname (as it is redundant), just a static one */
269 else
270 p = hostname; /* Use the passed name as pretty hostname */
fda2c5d2 271
b028f3e4 272 r = set_simple_string(bus, "SetPrettyHostname", p);
dbc4fbae
LP
273 if (r < 0)
274 return r;
2ae0858e
LP
275
276 /* Now that we set the pretty hostname, let's clean up the parameter and use that as static
277 * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
278 * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
629ff674 279 * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
2ae0858e
LP
280 * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
281 * he does and pass the name as-is. */
282 h = strdup(hostname);
283 if (!h)
284 return log_oom();
285
286 hostname = hostname_cleanup(h); /* Use the cleaned up name as static hostname */
dbc4fbae
LP
287 }
288
960787ae 289 if (arg_static) {
b028f3e4 290 r = set_simple_string(bus, "SetStaticHostname", hostname);
dbc4fbae
LP
291 if (r < 0)
292 return r;
293 }
294
960787ae 295 if (arg_transient) {
b028f3e4 296 r = set_simple_string(bus, "SetHostname", hostname);
dbc4fbae
LP
297 if (r < 0)
298 return r;
299 }
300
301 return 0;
302}
303
b028f3e4 304static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
dbc4fbae
LP
305 assert(args);
306 assert(n == 2);
307
b028f3e4 308 return set_simple_string(bus, "SetIconName", args[1]);
dbc4fbae
LP
309}
310
b028f3e4 311static int set_chassis(sd_bus *bus, char **args, unsigned n) {
7871c8e9
LP
312 assert(args);
313 assert(n == 2);
314
f20c84c1 315 return set_simple_string(bus, "SetChassis", args[1]);
7871c8e9
LP
316}
317
799298d6
JG
318static int set_deployment(sd_bus *bus, char **args, unsigned n) {
319 assert(args);
320 assert(n == 2);
321
322 return set_simple_string(bus, "SetDeployment", args[1]);
323}
324
41414fed
LP
325static int set_location(sd_bus *bus, char **args, unsigned n) {
326 assert(args);
327 assert(n == 2);
328
329 return set_simple_string(bus, "SetLocation", args[1]);
330}
331
601185b4 332static void help(void) {
7591abd4
LP
333 printf("%s [OPTIONS...] COMMAND ...\n\n"
334 "Query or change system hostname.\n\n"
dbc4fbae
LP
335 " -h --help Show this help\n"
336 " --version Show package version\n"
7591abd4 337 " --no-ask-password Do not prompt for password\n"
b028f3e4 338 " -H --host=[USER@]HOST Operate on remote host\n"
a86a47ce
LP
339 " -M --machine=CONTAINER Operate on local container\n"
340 " --transient Only set transient hostname\n"
341 " --static Only set static hostname\n"
342 " --pretty Only set pretty hostname\n\n"
dbc4fbae 343 "Commands:\n"
7591abd4
LP
344 " status Show current hostname settings\n"
345 " set-hostname NAME Set system hostname\n"
7871c8e9 346 " set-icon-name NAME Set icon name for host\n"
799298d6 347 " set-chassis NAME Set chassis type for host\n"
601185b4 348 " set-deployment NAME Set deployment environment for host\n"
41414fed 349 " set-location NAME Set location for host\n"
601185b4 350 , program_invocation_short_name);
dbc4fbae
LP
351}
352
353static int parse_argv(int argc, char *argv[]) {
354
355 enum {
356 ARG_VERSION = 0x100,
357 ARG_NO_ASK_PASSWORD,
960787ae
ZJS
358 ARG_TRANSIENT,
359 ARG_STATIC,
360 ARG_PRETTY
dbc4fbae
LP
361 };
362
363 static const struct option options[] = {
364 { "help", no_argument, NULL, 'h' },
365 { "version", no_argument, NULL, ARG_VERSION },
eb9da376
LP
366 { "transient", no_argument, NULL, ARG_TRANSIENT },
367 { "static", no_argument, NULL, ARG_STATIC },
368 { "pretty", no_argument, NULL, ARG_PRETTY },
dbc4fbae 369 { "host", required_argument, NULL, 'H' },
b028f3e4 370 { "machine", required_argument, NULL, 'M' },
dbc4fbae 371 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 372 {}
dbc4fbae
LP
373 };
374
375 int c;
376
377 assert(argc >= 0);
378 assert(argv);
379
601185b4 380 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
dbc4fbae
LP
381
382 switch (c) {
383
384 case 'h':
601185b4
ZJS
385 help();
386 return 0;
dbc4fbae
LP
387
388 case ARG_VERSION:
3f6fd1ba 389 return version();
dbc4fbae 390
b028f3e4
SP
391 case 'H':
392 arg_transport = BUS_TRANSPORT_REMOTE;
393 arg_host = optarg;
dbc4fbae
LP
394 break;
395
b028f3e4 396 case 'M':
de33fc62 397 arg_transport = BUS_TRANSPORT_MACHINE;
b028f3e4 398 arg_host = optarg;
dbc4fbae
LP
399 break;
400
960787ae
ZJS
401 case ARG_TRANSIENT:
402 arg_transient = true;
dbc4fbae
LP
403 break;
404
960787ae
ZJS
405 case ARG_PRETTY:
406 arg_pretty = true;
dbc4fbae
LP
407 break;
408
960787ae
ZJS
409 case ARG_STATIC:
410 arg_static = true;
dbc4fbae
LP
411 break;
412
59f432ea
LP
413 case ARG_NO_ASK_PASSWORD:
414 arg_ask_password = false;
415 break;
416
dbc4fbae
LP
417 case '?':
418 return -EINVAL;
419
420 default:
eb9da376 421 assert_not_reached("Unhandled option");
dbc4fbae 422 }
dbc4fbae 423
dbc4fbae
LP
424 return 1;
425}
426
b028f3e4 427static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
dbc4fbae
LP
428
429 static const struct {
430 const char* verb;
431 const enum {
432 MORE,
433 LESS,
434 EQUAL
435 } argc_cmp;
436 const int argc;
b028f3e4 437 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
dbc4fbae 438 } verbs[] = {
41414fed
LP
439 { "status", LESS, 1, show_status },
440 { "set-hostname", EQUAL, 2, set_hostname },
441 { "set-icon-name", EQUAL, 2, set_icon_name },
442 { "set-chassis", EQUAL, 2, set_chassis },
443 { "set-deployment", EQUAL, 2, set_deployment },
444 { "set-location", EQUAL, 2, set_location },
dbc4fbae
LP
445 };
446
447 int left;
448 unsigned i;
449
450 assert(argc >= 0);
451 assert(argv);
dbc4fbae
LP
452
453 left = argc - optind;
454
455 if (left <= 0)
456 /* Special rule: no arguments means "status" */
457 i = 0;
458 else {
459 if (streq(argv[optind], "help")) {
460 help();
461 return 0;
462 }
463
464 for (i = 0; i < ELEMENTSOF(verbs); i++)
465 if (streq(argv[optind], verbs[i].verb))
466 break;
467
468 if (i >= ELEMENTSOF(verbs)) {
469 log_error("Unknown operation %s", argv[optind]);
470 return -EINVAL;
471 }
472 }
473
474 switch (verbs[i].argc_cmp) {
475
476 case EQUAL:
477 if (left != verbs[i].argc) {
478 log_error("Invalid number of arguments.");
479 return -EINVAL;
480 }
481
482 break;
483
484 case MORE:
485 if (left < verbs[i].argc) {
486 log_error("Too few arguments.");
487 return -EINVAL;
488 }
489
490 break;
491
492 case LESS:
493 if (left > verbs[i].argc) {
494 log_error("Too many arguments.");
495 return -EINVAL;
496 }
497
498 break;
499
500 default:
501 assert_not_reached("Unknown comparison operator.");
502 }
503
dbc4fbae
LP
504 return verbs[i].dispatch(bus, argv + optind, left);
505}
506
507int main(int argc, char *argv[]) {
4afd3348 508 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
84f6181c 509 int r;
dbc4fbae 510
a9cdc94f 511 setlocale(LC_ALL, "");
dbc4fbae
LP
512 log_parse_environment();
513 log_open();
514
515 r = parse_argv(argc, argv);
b028f3e4 516 if (r <= 0)
dbc4fbae 517 goto finish;
b028f3e4 518
266f3e26 519 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
b028f3e4 520 if (r < 0) {
da927ba9 521 log_error_errno(r, "Failed to create bus connection: %m");
dbc4fbae
LP
522 goto finish;
523 }
524
b028f3e4 525 r = hostnamectl_main(bus, argc, argv);
dbc4fbae
LP
526
527finish:
5567fafb 528 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
dbc4fbae 529}