]>
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" | |
a5c32cff | 42 | #include "fileio.h" |
dbc4fbae | 43 | |
dbc4fbae | 44 | static bool arg_ask_password = true; |
b028f3e4 | 45 | static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; |
7085053a | 46 | static char *arg_host = NULL; |
960787ae ZJS |
47 | static bool arg_transient = false; |
48 | static bool arg_pretty = false; | |
49 | static bool arg_static = false; | |
dbc4fbae | 50 | |
dbc4fbae | 51 | typedef struct StatusInfo { |
b028f3e4 SP |
52 | char *hostname; |
53 | char *static_hostname; | |
54 | char *pretty_hostname; | |
55 | char *icon_name; | |
56 | char *chassis; | |
dbc4fbae LP |
57 | } StatusInfo; |
58 | ||
59 | static void print_status_info(StatusInfo *i) { | |
60 | sd_id128_t mid, bid; | |
61 | int r; | |
fe29f9d2 LP |
62 | const char *id = NULL; |
63 | _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL; | |
64 | struct utsname u; | |
dbc4fbae LP |
65 | |
66 | assert(i); | |
67 | ||
68 | printf(" Static hostname: %s\n", | |
69 | strna(i->static_hostname)); | |
70 | ||
c0b21b96 LP |
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)) | |
dbc4fbae LP |
78 | printf("Transient hostname: %s\n", |
79 | strna(i->hostname)); | |
80 | ||
c0b21b96 | 81 | printf(" Icon name: %s\n" |
7871c8e9 | 82 | " Chassis: %s\n", |
7871c8e9 LP |
83 | strna(i->icon_name), |
84 | strna(i->chassis)); | |
dbc4fbae LP |
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 | ||
fe29f9d2 | 94 | if (detect_virtualization(&id) > 0) |
dbc4fbae | 95 | printf(" Virtualization: %s\n", id); |
fe29f9d2 LP |
96 | |
97 | r = parse_env_file("/etc/os-release", NEWLINE, | |
98 | "PRETTY_NAME", &pretty_name, | |
99 | "CPE_NAME", &cpe_name, | |
100 | NULL); | |
872c8faa ZJS |
101 | if (r < 0) |
102 | log_warning("Failed to read /etc/os-release: %s", strerror(-r)); | |
fe29f9d2 LP |
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 | ||
dbc4fbae LP |
114 | } |
115 | ||
b028f3e4 SP |
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; | |
960787ae ZJS |
120 | int r; |
121 | ||
b028f3e4 | 122 | r = sd_bus_get_property( |
960787ae ZJS |
123 | bus, |
124 | "org.freedesktop.hostname1", | |
125 | "/org/freedesktop/hostname1", | |
b028f3e4 SP |
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)); | |
960787ae | 131 | return r; |
960787ae ZJS |
132 | } |
133 | ||
b028f3e4 SP |
134 | r = sd_bus_message_read(reply, "s", &s); |
135 | if (r < 0) | |
136 | return r; | |
960787ae | 137 | |
960787ae ZJS |
138 | printf("%s\n", s); |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
b028f3e4 | 143 | static int show_all_names(sd_bus *bus) { |
b92bea5d | 144 | StatusInfo info = {}; |
9f6eb1cd KS |
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) }, | |
b028f3e4 SP |
151 | {} |
152 | }; | |
153 | int r; | |
dbc4fbae | 154 | |
b028f3e4 SP |
155 | r = bus_map_all_properties(bus, |
156 | "org.freedesktop.hostname1", | |
157 | "/org/freedesktop/hostname1", | |
9f6eb1cd KS |
158 | map, |
159 | &info); | |
dbc4fbae | 160 | if (r < 0) |
b028f3e4 | 161 | goto fail; |
dbc4fbae LP |
162 | |
163 | print_status_info(&info); | |
b028f3e4 SP |
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); | |
dbc4fbae LP |
171 | return 0; |
172 | } | |
173 | ||
b028f3e4 | 174 | static int show_status(sd_bus *bus, char **args, unsigned n) { |
960787ae ZJS |
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 | ||
b028f3e4 SP |
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) { | |
dbc4fbae LP |
216 | _cleanup_free_ char *h = NULL; |
217 | const char *hostname = args[1]; | |
218 | int r; | |
219 | ||
220 | assert(args); | |
221 | assert(n == 2); | |
222 | ||
960787ae ZJS |
223 | if (!arg_pretty && !arg_static && !arg_transient) |
224 | arg_pretty = arg_static = arg_transient = true; | |
225 | ||
226 | if (arg_pretty) { | |
fda2c5d2 LP |
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 | ||
e724b063 LP |
235 | h = strdup(hostname); |
236 | if (!h) | |
237 | return log_oom(); | |
238 | ||
239 | hostname_cleanup(h, true); | |
240 | ||
960787ae | 241 | if (arg_static && streq(h, hostname)) |
fda2c5d2 LP |
242 | p = ""; |
243 | else { | |
244 | p = hostname; | |
e724b063 | 245 | hostname = h; |
fda2c5d2 LP |
246 | } |
247 | ||
b028f3e4 | 248 | r = set_simple_string(bus, "SetPrettyHostname", p); |
dbc4fbae LP |
249 | if (r < 0) |
250 | return r; | |
dbc4fbae LP |
251 | } |
252 | ||
960787ae | 253 | if (arg_static) { |
b028f3e4 | 254 | r = set_simple_string(bus, "SetStaticHostname", hostname); |
dbc4fbae LP |
255 | if (r < 0) |
256 | return r; | |
257 | } | |
258 | ||
960787ae | 259 | if (arg_transient) { |
b028f3e4 | 260 | r = set_simple_string(bus, "SetHostname", hostname); |
dbc4fbae LP |
261 | if (r < 0) |
262 | return r; | |
263 | } | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
b028f3e4 | 268 | static int set_icon_name(sd_bus *bus, char **args, unsigned n) { |
dbc4fbae LP |
269 | assert(args); |
270 | assert(n == 2); | |
271 | ||
b028f3e4 | 272 | return set_simple_string(bus, "SetIconName", args[1]); |
dbc4fbae LP |
273 | } |
274 | ||
b028f3e4 | 275 | static int set_chassis(sd_bus *bus, char **args, unsigned n) { |
7871c8e9 LP |
276 | assert(args); |
277 | assert(n == 2); | |
278 | ||
b028f3e4 | 279 | return set_simple_string(bus, "SetChasis", args[1]); |
7871c8e9 LP |
280 | } |
281 | ||
dbc4fbae LP |
282 | static int help(void) { |
283 | ||
7591abd4 LP |
284 | printf("%s [OPTIONS...] COMMAND ...\n\n" |
285 | "Query or change system hostname.\n\n" | |
dbc4fbae LP |
286 | " -h --help Show this help\n" |
287 | " --version Show package version\n" | |
dbc4fbae LP |
288 | " --transient Only set transient hostname\n" |
289 | " --static Only set static hostname\n" | |
290 | " --pretty Only set pretty hostname\n" | |
7591abd4 | 291 | " --no-ask-password Do not prompt for password\n" |
b028f3e4 SP |
292 | " -H --host=[USER@]HOST Operate on remote host\n" |
293 | " -M --machine=CONTAINER Operate on local container\n\n" | |
dbc4fbae | 294 | "Commands:\n" |
7591abd4 LP |
295 | " status Show current hostname settings\n" |
296 | " set-hostname NAME Set system hostname\n" | |
7871c8e9 LP |
297 | " set-icon-name NAME Set icon name for host\n" |
298 | " set-chassis NAME Set chassis type for host\n", | |
dbc4fbae LP |
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, | |
960787ae ZJS |
309 | ARG_TRANSIENT, |
310 | ARG_STATIC, | |
311 | ARG_PRETTY | |
dbc4fbae LP |
312 | }; |
313 | ||
314 | static const struct option options[] = { | |
315 | { "help", no_argument, NULL, 'h' }, | |
316 | { "version", no_argument, NULL, ARG_VERSION }, | |
eb9da376 LP |
317 | { "transient", no_argument, NULL, ARG_TRANSIENT }, |
318 | { "static", no_argument, NULL, ARG_STATIC }, | |
319 | { "pretty", no_argument, NULL, ARG_PRETTY }, | |
dbc4fbae | 320 | { "host", required_argument, NULL, 'H' }, |
b028f3e4 | 321 | { "machine", required_argument, NULL, 'M' }, |
dbc4fbae | 322 | { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, |
eb9da376 | 323 | {} |
dbc4fbae LP |
324 | }; |
325 | ||
326 | int c; | |
327 | ||
328 | assert(argc >= 0); | |
329 | assert(argv); | |
330 | ||
fc7689bc | 331 | while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) { |
dbc4fbae LP |
332 | |
333 | switch (c) { | |
334 | ||
335 | case 'h': | |
eb9da376 | 336 | return help(); |
dbc4fbae LP |
337 | |
338 | case ARG_VERSION: | |
339 | puts(PACKAGE_STRING); | |
dbc4fbae LP |
340 | puts(SYSTEMD_FEATURES); |
341 | return 0; | |
342 | ||
b028f3e4 SP |
343 | case 'H': |
344 | arg_transport = BUS_TRANSPORT_REMOTE; | |
345 | arg_host = optarg; | |
dbc4fbae LP |
346 | break; |
347 | ||
b028f3e4 SP |
348 | case 'M': |
349 | arg_transport = BUS_TRANSPORT_CONTAINER; | |
350 | arg_host = optarg; | |
dbc4fbae LP |
351 | break; |
352 | ||
960787ae ZJS |
353 | case ARG_TRANSIENT: |
354 | arg_transient = true; | |
dbc4fbae LP |
355 | break; |
356 | ||
960787ae ZJS |
357 | case ARG_PRETTY: |
358 | arg_pretty = true; | |
dbc4fbae LP |
359 | break; |
360 | ||
960787ae ZJS |
361 | case ARG_STATIC: |
362 | arg_static = true; | |
dbc4fbae LP |
363 | break; |
364 | ||
59f432ea LP |
365 | case ARG_NO_ASK_PASSWORD: |
366 | arg_ask_password = false; | |
367 | break; | |
368 | ||
dbc4fbae LP |
369 | case '?': |
370 | return -EINVAL; | |
371 | ||
372 | default: | |
eb9da376 | 373 | assert_not_reached("Unhandled option"); |
dbc4fbae LP |
374 | } |
375 | } | |
376 | ||
dbc4fbae LP |
377 | return 1; |
378 | } | |
379 | ||
b028f3e4 | 380 | static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) { |
dbc4fbae LP |
381 | |
382 | static const struct { | |
383 | const char* verb; | |
384 | const enum { | |
385 | MORE, | |
386 | LESS, | |
387 | EQUAL | |
388 | } argc_cmp; | |
389 | const int argc; | |
b028f3e4 | 390 | int (* const dispatch)(sd_bus *bus, char **args, unsigned n); |
dbc4fbae | 391 | } verbs[] = { |
7871c8e9 LP |
392 | { "status", LESS, 1, show_status }, |
393 | { "set-hostname", EQUAL, 2, set_hostname }, | |
394 | { "set-icon-name", EQUAL, 2, set_icon_name }, | |
395 | { "set-chassis", EQUAL, 2, set_chassis }, | |
dbc4fbae LP |
396 | }; |
397 | ||
398 | int left; | |
399 | unsigned i; | |
400 | ||
401 | assert(argc >= 0); | |
402 | assert(argv); | |
dbc4fbae LP |
403 | |
404 | left = argc - optind; | |
405 | ||
406 | if (left <= 0) | |
407 | /* Special rule: no arguments means "status" */ | |
408 | i = 0; | |
409 | else { | |
410 | if (streq(argv[optind], "help")) { | |
411 | help(); | |
412 | return 0; | |
413 | } | |
414 | ||
415 | for (i = 0; i < ELEMENTSOF(verbs); i++) | |
416 | if (streq(argv[optind], verbs[i].verb)) | |
417 | break; | |
418 | ||
419 | if (i >= ELEMENTSOF(verbs)) { | |
420 | log_error("Unknown operation %s", argv[optind]); | |
421 | return -EINVAL; | |
422 | } | |
423 | } | |
424 | ||
425 | switch (verbs[i].argc_cmp) { | |
426 | ||
427 | case EQUAL: | |
428 | if (left != verbs[i].argc) { | |
429 | log_error("Invalid number of arguments."); | |
430 | return -EINVAL; | |
431 | } | |
432 | ||
433 | break; | |
434 | ||
435 | case MORE: | |
436 | if (left < verbs[i].argc) { | |
437 | log_error("Too few arguments."); | |
438 | return -EINVAL; | |
439 | } | |
440 | ||
441 | break; | |
442 | ||
443 | case LESS: | |
444 | if (left > verbs[i].argc) { | |
445 | log_error("Too many arguments."); | |
446 | return -EINVAL; | |
447 | } | |
448 | ||
449 | break; | |
450 | ||
451 | default: | |
452 | assert_not_reached("Unknown comparison operator."); | |
453 | } | |
454 | ||
dbc4fbae LP |
455 | return verbs[i].dispatch(bus, argv + optind, left); |
456 | } | |
457 | ||
458 | int main(int argc, char *argv[]) { | |
b028f3e4 | 459 | _cleanup_bus_unref_ sd_bus *bus = NULL; |
84f6181c | 460 | int r; |
dbc4fbae | 461 | |
a9cdc94f | 462 | setlocale(LC_ALL, ""); |
dbc4fbae LP |
463 | log_parse_environment(); |
464 | log_open(); | |
465 | ||
466 | r = parse_argv(argc, argv); | |
b028f3e4 | 467 | if (r <= 0) |
dbc4fbae | 468 | goto finish; |
b028f3e4 SP |
469 | |
470 | r = bus_open_transport(arg_transport, arg_host, false, &bus); | |
471 | if (r < 0) { | |
472 | log_error("Failed to create bus connection: %s", strerror(-r)); | |
dbc4fbae LP |
473 | goto finish; |
474 | } | |
475 | ||
b028f3e4 | 476 | r = hostnamectl_main(bus, argc, argv); |
dbc4fbae LP |
477 | |
478 | finish: | |
b028f3e4 | 479 | return r < 0 ? EXIT_FAILURE : r; |
dbc4fbae | 480 | } |