]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamectl.c
enable localization for common *ctl commands
[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 '?':
407 return -EINVAL;
408
409 default:
410 log_error("Unknown option code %c", c);
411 return -EINVAL;
412 }
413 }
414
415 if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
416 arg_set_transient = arg_set_pretty = arg_set_static = true;
417
418 return 1;
419 }
420
421 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
422
423 static const struct {
424 const char* verb;
425 const enum {
426 MORE,
427 LESS,
428 EQUAL
429 } argc_cmp;
430 const int argc;
431 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
432 } verbs[] = {
433 { "status", LESS, 1, show_status },
434 { "set-hostname", EQUAL, 2, set_hostname },
435 { "set-icon-name", EQUAL, 2, set_icon_name },
436 };
437
438 int left;
439 unsigned i;
440
441 assert(argc >= 0);
442 assert(argv);
443 assert(error);
444
445 left = argc - optind;
446
447 if (left <= 0)
448 /* Special rule: no arguments means "status" */
449 i = 0;
450 else {
451 if (streq(argv[optind], "help")) {
452 help();
453 return 0;
454 }
455
456 for (i = 0; i < ELEMENTSOF(verbs); i++)
457 if (streq(argv[optind], verbs[i].verb))
458 break;
459
460 if (i >= ELEMENTSOF(verbs)) {
461 log_error("Unknown operation %s", argv[optind]);
462 return -EINVAL;
463 }
464 }
465
466 switch (verbs[i].argc_cmp) {
467
468 case EQUAL:
469 if (left != verbs[i].argc) {
470 log_error("Invalid number of arguments.");
471 return -EINVAL;
472 }
473
474 break;
475
476 case MORE:
477 if (left < verbs[i].argc) {
478 log_error("Too few arguments.");
479 return -EINVAL;
480 }
481
482 break;
483
484 case LESS:
485 if (left > verbs[i].argc) {
486 log_error("Too many arguments.");
487 return -EINVAL;
488 }
489
490 break;
491
492 default:
493 assert_not_reached("Unknown comparison operator.");
494 }
495
496 if (!bus) {
497 log_error("Failed to get D-Bus connection: %s", error->message);
498 return -EIO;
499 }
500
501 return verbs[i].dispatch(bus, argv + optind, left);
502 }
503
504 int main(int argc, char *argv[]) {
505 int r, retval = EXIT_FAILURE;
506 DBusConnection *bus = NULL;
507 DBusError error;
508
509 dbus_error_init(&error);
510
511 setlocale(LC_ALL, "");
512 log_parse_environment();
513 log_open();
514
515 r = parse_argv(argc, argv);
516 if (r < 0)
517 goto finish;
518 else if (r == 0) {
519 retval = EXIT_SUCCESS;
520 goto finish;
521 }
522
523 if (arg_transport == TRANSPORT_NORMAL)
524 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
525 else if (arg_transport == TRANSPORT_POLKIT)
526 bus_connect_system_polkit(&bus, &error);
527 else if (arg_transport == TRANSPORT_SSH)
528 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
529 else
530 assert_not_reached("Uh, invalid transport...");
531
532 r = hostnamectl_main(bus, argc, argv, &error);
533 retval = r < 0 ? EXIT_FAILURE : r;
534
535 finish:
536 if (bus) {
537 dbus_connection_flush(bus);
538 dbus_connection_close(bus);
539 dbus_connection_unref(bus);
540 }
541
542 dbus_error_free(&error);
543 dbus_shutdown();
544
545 return retval;
546 }