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