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