1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Kay Sievers
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
35 #include "bus-error.h"
36 #include "bus-message.h"
38 #include "spawn-polkit-agent.h"
43 #include "path-util.h"
46 #include "locale-util.h"
48 static bool arg_no_pager = false;
49 static bool arg_ask_password = true;
50 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
51 static char *arg_host = NULL;
52 static bool arg_convert = true;
54 static void pager_open_if_enabled(void) {
62 static void polkit_agent_open_if_enabled(void) {
64 /* Open the polkit agent as a child process if necessary */
65 if (!arg_ask_password)
68 if (arg_transport != BUS_TRANSPORT_LOCAL)
74 typedef struct StatusInfo {
76 const char *vconsole_keymap;
77 const char *vconsole_keymap_toggle;
78 const char *x11_layout;
79 const char *x11_model;
80 const char *x11_variant;
81 const char *x11_options;
84 static void print_status_info(StatusInfo *i) {
87 if (strv_isempty(i->locale))
88 puts(" System Locale: n/a\n");
92 printf(" System Locale: %s\n", i->locale[0]);
93 STRV_FOREACH(j, i->locale + 1)
97 printf(" VC Keymap: %s\n", strna(i->vconsole_keymap));
98 if (!isempty(i->vconsole_keymap_toggle))
99 printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle);
101 printf(" X11 Layout: %s\n", strna(i->x11_layout));
102 if (!isempty(i->x11_model))
103 printf(" X11 Model: %s\n", i->x11_model);
104 if (!isempty(i->x11_variant))
105 printf(" X11 Variant: %s\n", i->x11_variant);
106 if (!isempty(i->x11_options))
107 printf(" X11 Options: %s\n", i->x11_options);
110 static int show_status(sd_bus *bus, char **args, unsigned n) {
111 StatusInfo info = {};
112 static const struct bus_properties_map map[] = {
113 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
114 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
115 { "VConsoleKeymapToggle", "s", NULL, offsetof(StatusInfo, vconsole_keymap_toggle) },
116 { "X11Layout", "s", NULL, offsetof(StatusInfo, x11_layout) },
117 { "X11Model", "s", NULL, offsetof(StatusInfo, x11_model) },
118 { "X11Variant", "s", NULL, offsetof(StatusInfo, x11_variant) },
119 { "X11Options", "s", NULL, offsetof(StatusInfo, x11_options) },
120 { "Locale", "as", NULL, offsetof(StatusInfo, locale) },
127 r = bus_map_all_properties(bus,
128 "org.freedesktop.locale1",
129 "/org/freedesktop/locale1",
133 log_error("Could not get properties: %s", strerror(-r));
137 print_status_info(&info);
140 strv_free(info.locale);
144 static int set_locale(sd_bus *bus, char **args, unsigned n) {
145 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
146 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
152 polkit_agent_open_if_enabled();
154 r = sd_bus_message_new_method_call(
157 "org.freedesktop.locale1",
158 "/org/freedesktop/locale1",
159 "org.freedesktop.locale1",
162 return bus_log_create_error(r);
164 r = sd_bus_message_append_strv(m, args + 1);
166 return bus_log_create_error(r);
168 r = sd_bus_message_append(m, "b", arg_ask_password);
170 return bus_log_create_error(r);
172 r = sd_bus_call(bus, m, 0, &error, NULL);
174 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
181 static int list_locales(sd_bus *bus, char **args, unsigned n) {
182 _cleanup_strv_free_ char **l = NULL;
189 log_error("Failed to read list of locales: %s", strerror(-r));
193 pager_open_if_enabled();
199 static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) {
200 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
201 const char *map, *toggle_map;
208 log_error("Too many arguments.");
212 polkit_agent_open_if_enabled();
215 toggle_map = n > 2 ? args[2] : "";
217 r = sd_bus_call_method(
219 "org.freedesktop.locale1",
220 "/org/freedesktop/locale1",
221 "org.freedesktop.locale1",
222 "SetVConsoleKeyboard",
225 "ssbb", map, toggle_map, arg_convert, arg_ask_password);
227 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
232 static Set *keymaps = NULL;
236 const struct stat *sb,
238 struct FTW *ftwbuf) {
246 if (!endswith(fpath, ".map") &&
247 !endswith(fpath, ".map.gz"))
250 p = strdup(basename(fpath));
254 e = endswith(p, ".map");
258 e = endswith(p, ".map.gz");
262 r = set_consume(keymaps, p);
263 if (r < 0 && r != -EEXIST) {
264 log_error("Can't add keymap: %s", strerror(-r));
271 static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) {
272 _cleanup_strv_free_ char **l = NULL;
275 keymaps = set_new(string_hash_func, string_compare_func);
279 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS)
280 nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
282 l = set_get_strv(keymaps);
284 set_free_free(keymaps);
290 if (strv_isempty(l)) {
291 log_error("Couldn't find any console keymaps.");
297 pager_open_if_enabled();
304 static int set_x11_keymap(sd_bus *bus, char **args, unsigned n) {
305 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
306 const char *layout, *model, *variant, *options;
313 log_error("Too many arguments.");
317 polkit_agent_open_if_enabled();
320 model = n > 2 ? args[2] : "";
321 variant = n > 3 ? args[3] : "";
322 options = n > 4 ? args[4] : "";
324 r = sd_bus_call_method(
326 "org.freedesktop.locale1",
327 "/org/freedesktop/locale1",
328 "org.freedesktop.locale1",
332 "ssssbb", layout, model, variant, options,
333 arg_convert, arg_ask_password);
335 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
340 static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) {
341 _cleanup_fclose_ FILE *f = NULL;
342 _cleanup_strv_free_ char **list = NULL;
350 } state = NONE, look_for;
354 log_error("Too many arguments.");
358 f = fopen("/usr/share/X11/xkb/rules/base.lst", "re");
360 log_error("Failed to open keyboard mapping list. %m");
364 if (streq(args[0], "list-x11-keymap-models"))
366 else if (streq(args[0], "list-x11-keymap-layouts"))
368 else if (streq(args[0], "list-x11-keymap-variants"))
370 else if (streq(args[0], "list-x11-keymap-options"))
373 assert_not_reached("Wrong parameter");
375 FOREACH_LINE(line, f, break) {
384 if (startswith(l, "! model"))
386 else if (startswith(l, "! layout"))
388 else if (startswith(l, "! variant"))
390 else if (startswith(l, "! option"))
398 if (state != look_for)
401 w = l + strcspn(l, WHITESPACE);
411 w += strspn(w, WHITESPACE);
419 if (!streq(w, args[1]))
424 r = strv_extend(&list, l);
429 if (strv_isempty(list)) {
430 log_error("Couldn't find any entries.");
437 pager_open_if_enabled();
443 static int help(void) {
445 printf("%s [OPTIONS...] COMMAND ...\n\n"
446 "Query or change system locale and keyboard settings.\n\n"
447 " -h --help Show this help\n"
448 " --version Show package version\n"
449 " --no-pager Do not pipe output into a pager\n"
450 " --no-ask-password Do not prompt for password\n"
451 " -H --host=[USER@]HOST Operate on remote host\n"
452 " -M --machine=CONTAINER Operate on local container\n"
453 " --no-convert Don't convert keyboard mappings\n\n"
455 " status Show current locale settings\n"
456 " set-locale LOCALE... Set system locale\n"
457 " list-locales Show known locales\n"
458 " set-keymap MAP [MAP] Set virtual console keyboard mapping\n"
459 " list-keymaps Show known virtual console keyboard mappings\n"
460 " set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n"
461 " Set X11 keyboard mapping\n"
462 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
463 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
464 " list-x11-keymap-variants [LAYOUT]\n"
465 " Show known X11 keyboard mapping variants\n"
466 " list-x11-keymap-options Show known X11 keyboard mapping options\n",
467 program_invocation_short_name);
472 static int parse_argv(int argc, char *argv[]) {
481 static const struct option options[] = {
482 { "help", no_argument, NULL, 'h' },
483 { "version", no_argument, NULL, ARG_VERSION },
484 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
485 { "host", required_argument, NULL, 'H' },
486 { "machine", required_argument, NULL, 'M' },
487 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
488 { "no-convert", no_argument, NULL, ARG_NO_CONVERT },
497 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
505 puts(PACKAGE_STRING);
506 puts(SYSTEMD_FEATURES);
517 case ARG_NO_ASK_PASSWORD:
518 arg_ask_password = false;
522 arg_transport = BUS_TRANSPORT_REMOTE;
527 arg_transport = BUS_TRANSPORT_CONTAINER;
535 assert_not_reached("Unhandled option");
542 static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
544 static const struct {
552 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
554 { "status", LESS, 1, show_status },
555 { "set-locale", MORE, 2, set_locale },
556 { "list-locales", EQUAL, 1, list_locales },
557 { "set-keymap", MORE, 2, set_vconsole_keymap },
558 { "list-keymaps", EQUAL, 1, list_vconsole_keymaps },
559 { "set-x11-keymap", MORE, 2, set_x11_keymap },
560 { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps },
561 { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps },
562 { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps },
563 { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps },
572 left = argc - optind;
575 /* Special rule: no arguments means "status" */
578 if (streq(argv[optind], "help")) {
583 for (i = 0; i < ELEMENTSOF(verbs); i++)
584 if (streq(argv[optind], verbs[i].verb))
587 if (i >= ELEMENTSOF(verbs)) {
588 log_error("Unknown operation %s", argv[optind]);
593 switch (verbs[i].argc_cmp) {
596 if (left != verbs[i].argc) {
597 log_error("Invalid number of arguments.");
604 if (left < verbs[i].argc) {
605 log_error("Too few arguments.");
612 if (left > verbs[i].argc) {
613 log_error("Too many arguments.");
620 assert_not_reached("Unknown comparison operator.");
623 return verbs[i].dispatch(bus, argv + optind, left);
626 int main(int argc, char*argv[]) {
627 _cleanup_bus_unref_ sd_bus *bus = NULL;
630 setlocale(LC_ALL, "");
631 log_parse_environment();
634 r = parse_argv(argc, argv);
638 r = bus_open_transport(arg_transport, arg_host, false, &bus);
640 log_error("Failed to create bus connection: %s", strerror(-r));
644 r = localectl_main(bus, argc, argv);
649 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;