]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamectl.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / hostname / hostnamectl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
21 #include <getopt.h>
22 #include <locale.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "sd-bus.h"
28 #include "sd-id128.h"
29
30 #include "alloc-util.h"
31 #include "architecture.h"
32 #include "bus-error.h"
33 #include "bus-util.h"
34 #include "hostname-util.h"
35 #include "spawn-polkit-agent.h"
36 #include "util.h"
37
38 static bool arg_ask_password = true;
39 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
40 static char *arg_host = NULL;
41 static bool arg_transient = false;
42 static bool arg_pretty = false;
43 static bool arg_static = false;
44
45 typedef struct StatusInfo {
46 char *hostname;
47 char *static_hostname;
48 char *pretty_hostname;
49 char *icon_name;
50 char *chassis;
51 char *deployment;
52 char *location;
53 char *kernel_name;
54 char *kernel_release;
55 char *os_pretty_name;
56 char *os_cpe_name;
57 char *virtualization;
58 char *architecture;
59 } StatusInfo;
60
61 static void print_status_info(StatusInfo *i) {
62 sd_id128_t mid = {}, bid = {};
63 int r;
64
65 assert(i);
66
67 printf(" Static hostname: %s\n", strna(i->static_hostname));
68
69 if (!isempty(i->pretty_hostname) &&
70 !streq_ptr(i->pretty_hostname, i->static_hostname))
71 printf(" Pretty hostname: %s\n", i->pretty_hostname);
72
73 if (!isempty(i->hostname) &&
74 !streq_ptr(i->hostname, i->static_hostname))
75 printf("Transient hostname: %s\n", i->hostname);
76
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);
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
99 if (!isempty(i->virtualization))
100 printf(" Virtualization: %s\n", i->virtualization);
101
102 if (!isempty(i->os_pretty_name))
103 printf(" Operating System: %s\n", i->os_pretty_name);
104
105 if (!isempty(i->os_cpe_name))
106 printf(" CPE OS Name: %s\n", i->os_cpe_name);
107
108 if (!isempty(i->kernel_name) && !isempty(i->kernel_release))
109 printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release);
110
111 if (!isempty(i->architecture))
112 printf(" Architecture: %s\n", i->architecture);
113
114 }
115
116 static int show_one_name(sd_bus *bus, const char* attr) {
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;
119 const char *s;
120 int r;
121
122 r = sd_bus_get_property(
123 bus,
124 "org.freedesktop.hostname1",
125 "/org/freedesktop/hostname1",
126 "org.freedesktop.hostname1",
127 attr,
128 &error, &reply, "s");
129 if (r < 0)
130 return log_error_errno(r, "Could not get property: %s", bus_error_message(&error, r));
131
132 r = sd_bus_message_read(reply, "s", &s);
133 if (r < 0)
134 return bus_log_parse_error(r);
135
136 printf("%s\n", s);
137
138 return 0;
139 }
140
141 static int show_all_names(sd_bus *bus, sd_bus_error *error) {
142 StatusInfo info = {};
143
144 static const struct bus_properties_map hostname_map[] = {
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) },
156 {}
157 };
158
159 static const struct bus_properties_map manager_map[] = {
160 { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) },
161 { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) },
162 {}
163 };
164
165 int r;
166
167 r = bus_map_all_properties(bus,
168 "org.freedesktop.hostname1",
169 "/org/freedesktop/hostname1",
170 hostname_map,
171 error,
172 &info);
173 if (r < 0)
174 goto fail;
175
176 bus_map_all_properties(bus,
177 "org.freedesktop.systemd1",
178 "/org/freedesktop/systemd1",
179 manager_map,
180 error,
181 &info);
182
183 print_status_info(&info);
184
185 fail:
186 free(info.hostname);
187 free(info.static_hostname);
188 free(info.pretty_hostname);
189 free(info.icon_name);
190 free(info.chassis);
191 free(info.deployment);
192 free(info.location);
193 free(info.kernel_name);
194 free(info.kernel_release);
195 free(info.os_pretty_name);
196 free(info.os_cpe_name);
197 free(info.virtualization);
198 free(info.architecture);
199
200 return r;
201 }
202
203 static int show_status(sd_bus *bus, char **args, unsigned n) {
204 int r;
205
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);
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 }
229 }
230
231 static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
232 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
233 int r = 0;
234
235 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
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
250 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
251 _cleanup_free_ char *h = NULL;
252 const char *hostname = args[1];
253 int r;
254
255 assert(args);
256 assert(n == 2);
257
258 if (!arg_pretty && !arg_static && !arg_transient)
259 arg_pretty = arg_static = arg_transient = true;
260
261 if (arg_pretty) {
262 const char *p;
263
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
266 * hostname. */
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 */
271
272 r = set_simple_string(bus, "SetPrettyHostname", p);
273 if (r < 0)
274 return r;
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
279 * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
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 */
287 }
288
289 if (arg_static) {
290 r = set_simple_string(bus, "SetStaticHostname", hostname);
291 if (r < 0)
292 return r;
293 }
294
295 if (arg_transient) {
296 r = set_simple_string(bus, "SetHostname", hostname);
297 if (r < 0)
298 return r;
299 }
300
301 return 0;
302 }
303
304 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
305 assert(args);
306 assert(n == 2);
307
308 return set_simple_string(bus, "SetIconName", args[1]);
309 }
310
311 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
312 assert(args);
313 assert(n == 2);
314
315 return set_simple_string(bus, "SetChassis", args[1]);
316 }
317
318 static 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
325 static 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
332 static void help(void) {
333 printf("%s [OPTIONS...] COMMAND ...\n\n"
334 "Query or change system hostname.\n\n"
335 " -h --help Show this help\n"
336 " --version Show package version\n"
337 " --no-ask-password Do not prompt for password\n"
338 " -H --host=[USER@]HOST Operate on remote host\n"
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"
343 "Commands:\n"
344 " status Show current hostname settings\n"
345 " set-hostname NAME Set system hostname\n"
346 " set-icon-name NAME Set icon name for host\n"
347 " set-chassis NAME Set chassis type for host\n"
348 " set-deployment NAME Set deployment environment for host\n"
349 " set-location NAME Set location for host\n"
350 , program_invocation_short_name);
351 }
352
353 static int parse_argv(int argc, char *argv[]) {
354
355 enum {
356 ARG_VERSION = 0x100,
357 ARG_NO_ASK_PASSWORD,
358 ARG_TRANSIENT,
359 ARG_STATIC,
360 ARG_PRETTY
361 };
362
363 static const struct option options[] = {
364 { "help", no_argument, NULL, 'h' },
365 { "version", no_argument, NULL, ARG_VERSION },
366 { "transient", no_argument, NULL, ARG_TRANSIENT },
367 { "static", no_argument, NULL, ARG_STATIC },
368 { "pretty", no_argument, NULL, ARG_PRETTY },
369 { "host", required_argument, NULL, 'H' },
370 { "machine", required_argument, NULL, 'M' },
371 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
372 {}
373 };
374
375 int c;
376
377 assert(argc >= 0);
378 assert(argv);
379
380 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
381
382 switch (c) {
383
384 case 'h':
385 help();
386 return 0;
387
388 case ARG_VERSION:
389 return version();
390
391 case 'H':
392 arg_transport = BUS_TRANSPORT_REMOTE;
393 arg_host = optarg;
394 break;
395
396 case 'M':
397 arg_transport = BUS_TRANSPORT_MACHINE;
398 arg_host = optarg;
399 break;
400
401 case ARG_TRANSIENT:
402 arg_transient = true;
403 break;
404
405 case ARG_PRETTY:
406 arg_pretty = true;
407 break;
408
409 case ARG_STATIC:
410 arg_static = true;
411 break;
412
413 case ARG_NO_ASK_PASSWORD:
414 arg_ask_password = false;
415 break;
416
417 case '?':
418 return -EINVAL;
419
420 default:
421 assert_not_reached("Unhandled option");
422 }
423
424 return 1;
425 }
426
427 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
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;
437 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
438 } verbs[] = {
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 },
445 };
446
447 int left;
448 unsigned i;
449
450 assert(argc >= 0);
451 assert(argv);
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
504 return verbs[i].dispatch(bus, argv + optind, left);
505 }
506
507 int main(int argc, char *argv[]) {
508 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
509 int r;
510
511 setlocale(LC_ALL, "");
512 log_parse_environment();
513 log_open();
514
515 r = parse_argv(argc, argv);
516 if (r <= 0)
517 goto finish;
518
519 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
520 if (r < 0) {
521 log_error_errno(r, "Failed to create bus connection: %m");
522 goto finish;
523 }
524
525 r = hostnamectl_main(bus, argc, argv);
526
527 finish:
528 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
529 }