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