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