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