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