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