]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamectl.c
hostnamectl: fix parsing of --no-ask-password
[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 "dbus-common.h"
32 #include "util.h"
33 #include "spawn-polkit-agent.h"
34 #include "build.h"
35 #include "hwclock.h"
36 #include "strv.h"
37 #include "sd-id128.h"
38 #include "virt.h"
39
40 static enum transport {
41 TRANSPORT_NORMAL,
42 TRANSPORT_SSH,
43 TRANSPORT_POLKIT
44 } arg_transport = TRANSPORT_NORMAL;
45 static bool arg_ask_password = true;
46 static const char *arg_host = NULL;
47 static bool arg_set_transient = false;
48 static bool arg_set_pretty = false;
49 static bool arg_set_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
55 if (!arg_ask_password)
56 return;
57
58 polkit_agent_open();
59 }
60
61 typedef struct StatusInfo {
62 const char *hostname;
63 const char *static_hostname;
64 const char *pretty_hostname;
65 const char *icon_name;
66 } StatusInfo;
67
68 static void print_status_info(StatusInfo *i) {
69 sd_id128_t mid, bid;
70 int r;
71 const char *id = NULL;
72 _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
73 struct utsname u;
74
75 assert(i);
76
77 printf(" Static hostname: %s\n",
78 strna(i->static_hostname));
79
80 if (!streq_ptr(i->hostname, i->static_hostname))
81 printf("Transient hostname: %s\n",
82 strna(i->hostname));
83
84 printf(" Pretty hostname: %s\n"
85 " Icon name: %s\n",
86 strna(i->pretty_hostname),
87 strna(i->icon_name));
88
89 r = sd_id128_get_machine(&mid);
90 if (r >= 0)
91 printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
92
93 r = sd_id128_get_boot(&bid);
94 if (r >= 0)
95 printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
96
97 if (detect_virtualization(&id) > 0)
98 printf(" Virtualization: %s\n", id);
99
100 r = parse_env_file("/etc/os-release", NEWLINE,
101 "PRETTY_NAME", &pretty_name,
102 "CPE_NAME", &cpe_name,
103 NULL);
104
105 if (!isempty(pretty_name))
106 printf(" Operating System: %s\n", pretty_name);
107
108 if (!isempty(cpe_name))
109 printf(" CPE OS Name: %s\n", cpe_name);
110
111 assert_se(uname(&u) >= 0);
112 printf(" Kernel: %s %s\n"
113 " Architecture: %s\n", u.sysname, u.release, u.machine);
114
115 }
116
117 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
118 assert(name);
119 assert(iter);
120
121 switch (dbus_message_iter_get_arg_type(iter)) {
122
123 case DBUS_TYPE_STRING: {
124 const char *s;
125
126 dbus_message_iter_get_basic(iter, &s);
127 if (!isempty(s)) {
128 if (streq(name, "Hostname"))
129 i->hostname = s;
130 if (streq(name, "StaticHostname"))
131 i->static_hostname = s;
132 if (streq(name, "PrettyHostname"))
133 i->pretty_hostname = s;
134 if (streq(name, "IconName"))
135 i->icon_name = s;
136 }
137 break;
138 }
139 }
140
141 return 0;
142 }
143
144 static int show_status(DBusConnection *bus, char **args, unsigned n) {
145 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
146 const char *interface = "";
147 int r;
148 DBusMessageIter iter, sub, sub2, sub3;
149 StatusInfo info;
150
151 assert(args);
152
153 r = bus_method_call_with_reply(
154 bus,
155 "org.freedesktop.hostname1",
156 "/org/freedesktop/hostname1",
157 "org.freedesktop.DBus.Properties",
158 "GetAll",
159 &reply,
160 NULL,
161 DBUS_TYPE_STRING, &interface,
162 DBUS_TYPE_INVALID);
163 if (r < 0)
164 return r;
165
166 if (!dbus_message_iter_init(reply, &iter) ||
167 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
168 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
169 log_error("Failed to parse reply.");
170 return -EIO;
171 }
172
173 zero(info);
174 dbus_message_iter_recurse(&iter, &sub);
175
176 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
177 const char *name;
178
179 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
180 log_error("Failed to parse reply.");
181 return -EIO;
182 }
183
184 dbus_message_iter_recurse(&sub, &sub2);
185
186 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
187 log_error("Failed to parse reply.");
188 return -EIO;
189 }
190
191 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
192 log_error("Failed to parse reply.");
193 return -EIO;
194 }
195
196 dbus_message_iter_recurse(&sub2, &sub3);
197
198 r = status_property(name, &sub3, &info);
199 if (r < 0) {
200 log_error("Failed to parse reply.");
201 return r;
202 }
203
204 dbus_message_iter_next(&sub);
205 }
206
207 print_status_info(&info);
208 return 0;
209 }
210
211 static char* hostname_simplify(char *s) {
212 char *p, *d;
213
214 for (p = s, d = s; *p; p++) {
215 if ((*p >= 'a' && *p <= 'z') ||
216 (*p >= '0' && *p <= '9') ||
217 *p == '-' || *p == '_')
218 *(d++) = *p;
219 else if (*p >= 'A' && *p <= 'Z')
220 *(d++) = *p - 'A' + 'a';
221 else if (*p == ' ')
222 *(d++) = '-';
223 }
224
225 *d = 0;
226
227 strshorten(s, HOST_NAME_MAX);
228 return s;
229 }
230
231 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
232 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
233 dbus_bool_t interactive = true;
234 _cleanup_free_ char *h = NULL;
235 const char *hostname = args[1];
236 int r;
237
238 assert(args);
239 assert(n == 2);
240
241 polkit_agent_open_if_enabled();
242
243 if (arg_set_pretty) {
244 r = bus_method_call_with_reply(
245 bus,
246 "org.freedesktop.hostname1",
247 "/org/freedesktop/hostname1",
248 "org.freedesktop.hostname1",
249 "SetPrettyHostname",
250 &reply,
251 NULL,
252 DBUS_TYPE_STRING, &hostname,
253 DBUS_TYPE_BOOLEAN, &interactive,
254 DBUS_TYPE_INVALID);
255 if (r < 0)
256 return r;
257
258 h = strdup(hostname);
259 if (!h)
260 return log_oom();
261
262 hostname = hostname_simplify(h);
263 }
264
265 if (arg_set_static) {
266 r = bus_method_call_with_reply(
267 bus,
268 "org.freedesktop.hostname1",
269 "/org/freedesktop/hostname1",
270 "org.freedesktop.hostname1",
271 "SetStaticHostname",
272 &reply,
273 NULL,
274 DBUS_TYPE_STRING, &hostname,
275 DBUS_TYPE_BOOLEAN, &interactive,
276 DBUS_TYPE_INVALID);
277
278 if (r < 0)
279 return r;
280 }
281
282 if (arg_set_transient) {
283 r = bus_method_call_with_reply(
284 bus,
285 "org.freedesktop.hostname1",
286 "/org/freedesktop/hostname1",
287 "org.freedesktop.hostname1",
288 "SetHostname",
289 &reply,
290 NULL,
291 DBUS_TYPE_STRING, &hostname,
292 DBUS_TYPE_BOOLEAN, &interactive,
293 DBUS_TYPE_INVALID);
294
295 if (r < 0)
296 return r;
297 }
298
299 return 0;
300 }
301
302 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
303 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
304 dbus_bool_t interactive = true;
305
306 assert(args);
307 assert(n == 2);
308
309 polkit_agent_open_if_enabled();
310
311 return bus_method_call_with_reply(
312 bus,
313 "org.freedesktop.hostname1",
314 "/org/freedesktop/hostname1",
315 "org.freedesktop.hostname1",
316 "SetIconName",
317 &reply,
318 NULL,
319 DBUS_TYPE_STRING, &args[1],
320 DBUS_TYPE_BOOLEAN, &interactive,
321 DBUS_TYPE_INVALID);
322 }
323
324 static int help(void) {
325
326 printf("%s [OPTIONS...] COMMAND ...\n\n"
327 "Query or change system hostname.\n\n"
328 " -h --help Show this help\n"
329 " --version Show package version\n"
330 " --transient Only set transient hostname\n"
331 " --static Only set static hostname\n"
332 " --pretty Only set pretty hostname\n"
333 " --no-ask-password Do not prompt for password\n"
334 " -H --host=[USER@]HOST Operate on remote host\n\n"
335 "Commands:\n"
336 " status Show current hostname settings\n"
337 " set-hostname NAME Set system hostname\n"
338 " set-icon-name NAME Set icon name for host\n",
339 program_invocation_short_name);
340
341 return 0;
342 }
343
344 static int parse_argv(int argc, char *argv[]) {
345
346 enum {
347 ARG_VERSION = 0x100,
348 ARG_NO_ASK_PASSWORD,
349 ARG_SET_TRANSIENT,
350 ARG_SET_STATIC,
351 ARG_SET_PRETTY
352 };
353
354 static const struct option options[] = {
355 { "help", no_argument, NULL, 'h' },
356 { "version", no_argument, NULL, ARG_VERSION },
357 { "transient", no_argument, NULL, ARG_SET_TRANSIENT },
358 { "static", no_argument, NULL, ARG_SET_STATIC },
359 { "pretty", no_argument, NULL, ARG_SET_PRETTY },
360 { "host", required_argument, NULL, 'H' },
361 { "privileged", no_argument, NULL, 'P' },
362 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
363 { NULL, 0, NULL, 0 }
364 };
365
366 int c;
367
368 assert(argc >= 0);
369 assert(argv);
370
371 while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
372
373 switch (c) {
374
375 case 'h':
376 help();
377 return 0;
378
379 case ARG_VERSION:
380 puts(PACKAGE_STRING);
381 puts(DISTRIBUTION);
382 puts(SYSTEMD_FEATURES);
383 return 0;
384
385 case 'P':
386 arg_transport = TRANSPORT_POLKIT;
387 break;
388
389 case 'H':
390 arg_transport = TRANSPORT_SSH;
391 arg_host = optarg;
392 break;
393
394 case ARG_SET_TRANSIENT:
395 arg_set_transient = true;
396 break;
397
398 case ARG_SET_PRETTY:
399 arg_set_pretty = true;
400 break;
401
402 case ARG_SET_STATIC:
403 arg_set_static = true;
404 break;
405
406 case ARG_NO_ASK_PASSWORD:
407 arg_ask_password = false;
408 break;
409
410 case '?':
411 return -EINVAL;
412
413 default:
414 log_error("Unknown option code %c", c);
415 return -EINVAL;
416 }
417 }
418
419 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
420 arg_set_transient = arg_set_pretty = arg_set_static = true;
421
422 return 1;
423 }
424
425 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
426
427 static const struct {
428 const char* verb;
429 const enum {
430 MORE,
431 LESS,
432 EQUAL
433 } argc_cmp;
434 const int argc;
435 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
436 } verbs[] = {
437 { "status", LESS, 1, show_status },
438 { "set-hostname", EQUAL, 2, set_hostname },
439 { "set-icon-name", EQUAL, 2, set_icon_name },
440 };
441
442 int left;
443 unsigned i;
444
445 assert(argc >= 0);
446 assert(argv);
447 assert(error);
448
449 left = argc - optind;
450
451 if (left <= 0)
452 /* Special rule: no arguments means "status" */
453 i = 0;
454 else {
455 if (streq(argv[optind], "help")) {
456 help();
457 return 0;
458 }
459
460 for (i = 0; i < ELEMENTSOF(verbs); i++)
461 if (streq(argv[optind], verbs[i].verb))
462 break;
463
464 if (i >= ELEMENTSOF(verbs)) {
465 log_error("Unknown operation %s", argv[optind]);
466 return -EINVAL;
467 }
468 }
469
470 switch (verbs[i].argc_cmp) {
471
472 case EQUAL:
473 if (left != verbs[i].argc) {
474 log_error("Invalid number of arguments.");
475 return -EINVAL;
476 }
477
478 break;
479
480 case MORE:
481 if (left < verbs[i].argc) {
482 log_error("Too few arguments.");
483 return -EINVAL;
484 }
485
486 break;
487
488 case LESS:
489 if (left > verbs[i].argc) {
490 log_error("Too many arguments.");
491 return -EINVAL;
492 }
493
494 break;
495
496 default:
497 assert_not_reached("Unknown comparison operator.");
498 }
499
500 if (!bus) {
501 log_error("Failed to get D-Bus connection: %s", error->message);
502 return -EIO;
503 }
504
505 return verbs[i].dispatch(bus, argv + optind, left);
506 }
507
508 int main(int argc, char *argv[]) {
509 int r, retval = EXIT_FAILURE;
510 DBusConnection *bus = NULL;
511 DBusError error;
512
513 dbus_error_init(&error);
514
515 setlocale(LC_ALL, "");
516 log_parse_environment();
517 log_open();
518
519 r = parse_argv(argc, argv);
520 if (r < 0)
521 goto finish;
522 else if (r == 0) {
523 retval = EXIT_SUCCESS;
524 goto finish;
525 }
526
527 if (arg_transport == TRANSPORT_NORMAL)
528 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
529 else if (arg_transport == TRANSPORT_POLKIT)
530 bus_connect_system_polkit(&bus, &error);
531 else if (arg_transport == TRANSPORT_SSH)
532 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
533 else
534 assert_not_reached("Uh, invalid transport...");
535
536 r = hostnamectl_main(bus, argc, argv, &error);
537 retval = r < 0 ? EXIT_FAILURE : r;
538
539 finish:
540 if (bus) {
541 dbus_connection_flush(bus);
542 dbus_connection_close(bus);
543 dbus_connection_unref(bus);
544 }
545
546 dbus_error_free(&error);
547 dbus_shutdown();
548
549 return retval;
550 }