2 This file is part of systemd.
4 Copyright 2012 Lennart Poettering
5 Copyright 2013 Kay Sievers
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include "bus-error.h"
35 #include "locale-util.h"
38 #include "spawn-polkit-agent.h"
43 static bool arg_no_pager
= false;
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_convert
= true;
49 typedef struct StatusInfo
{
51 char *vconsole_keymap
;
52 char *vconsole_keymap_toggle
;
59 static void status_info_clear(StatusInfo
*info
) {
61 strv_free(info
->locale
);
62 free(info
->vconsole_keymap
);
63 free(info
->vconsole_keymap_toggle
);
64 free(info
->x11_layout
);
65 free(info
->x11_model
);
66 free(info
->x11_variant
);
67 free(info
->x11_options
);
72 static void print_overridden_variables(void) {
74 char *variables
[_VARIABLE_LC_MAX
] = {};
76 bool print_warning
= true;
78 if (detect_container() > 0 || arg_host
)
81 r
= parse_env_file("/proc/cmdline", WHITESPACE
,
82 "locale.LANG", &variables
[VARIABLE_LANG
],
83 "locale.LANGUAGE", &variables
[VARIABLE_LANGUAGE
],
84 "locale.LC_CTYPE", &variables
[VARIABLE_LC_CTYPE
],
85 "locale.LC_NUMERIC", &variables
[VARIABLE_LC_NUMERIC
],
86 "locale.LC_TIME", &variables
[VARIABLE_LC_TIME
],
87 "locale.LC_COLLATE", &variables
[VARIABLE_LC_COLLATE
],
88 "locale.LC_MONETARY", &variables
[VARIABLE_LC_MONETARY
],
89 "locale.LC_MESSAGES", &variables
[VARIABLE_LC_MESSAGES
],
90 "locale.LC_PAPER", &variables
[VARIABLE_LC_PAPER
],
91 "locale.LC_NAME", &variables
[VARIABLE_LC_NAME
],
92 "locale.LC_ADDRESS", &variables
[VARIABLE_LC_ADDRESS
],
93 "locale.LC_TELEPHONE", &variables
[VARIABLE_LC_TELEPHONE
],
94 "locale.LC_MEASUREMENT", &variables
[VARIABLE_LC_MEASUREMENT
],
95 "locale.LC_IDENTIFICATION", &variables
[VARIABLE_LC_IDENTIFICATION
],
98 if (r
< 0 && r
!= -ENOENT
) {
99 log_warning_errno(r
, "Failed to read /proc/cmdline: %m");
103 for (j
= 0; j
< _VARIABLE_LC_MAX
; j
++)
106 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"
107 " Command Line: %s=%s", locale_variable_to_string(j
), variables
[j
]);
109 print_warning
= false;
111 log_warning(" %s=%s", locale_variable_to_string(j
), variables
[j
]);
114 for (j
= 0; j
< _VARIABLE_LC_MAX
; j
++)
118 static void print_status_info(StatusInfo
*i
) {
121 if (strv_isempty(i
->locale
))
122 puts(" System Locale: n/a");
126 printf(" System Locale: %s\n", i
->locale
[0]);
127 STRV_FOREACH(j
, i
->locale
+ 1)
131 printf(" VC Keymap: %s\n", strna(i
->vconsole_keymap
));
132 if (!isempty(i
->vconsole_keymap_toggle
))
133 printf("VC Toggle Keymap: %s\n", i
->vconsole_keymap_toggle
);
135 printf(" X11 Layout: %s\n", strna(i
->x11_layout
));
136 if (!isempty(i
->x11_model
))
137 printf(" X11 Model: %s\n", i
->x11_model
);
138 if (!isempty(i
->x11_variant
))
139 printf(" X11 Variant: %s\n", i
->x11_variant
);
140 if (!isempty(i
->x11_options
))
141 printf(" X11 Options: %s\n", i
->x11_options
);
144 static int show_status(sd_bus
*bus
, char **args
, unsigned n
) {
145 _cleanup_(status_info_clear
) StatusInfo info
= {};
146 static const struct bus_properties_map map
[] = {
147 { "VConsoleKeymap", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap
) },
148 { "VConsoleKeymap", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap
) },
149 { "VConsoleKeymapToggle", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap_toggle
) },
150 { "X11Layout", "s", NULL
, offsetof(StatusInfo
, x11_layout
) },
151 { "X11Model", "s", NULL
, offsetof(StatusInfo
, x11_model
) },
152 { "X11Variant", "s", NULL
, offsetof(StatusInfo
, x11_variant
) },
153 { "X11Options", "s", NULL
, offsetof(StatusInfo
, x11_options
) },
154 { "Locale", "as", NULL
, offsetof(StatusInfo
, locale
) },
158 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
163 r
= bus_map_all_properties(bus
,
164 "org.freedesktop.locale1",
165 "/org/freedesktop/locale1",
170 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
172 print_overridden_variables();
173 print_status_info(&info
);
178 static int set_locale(sd_bus
*bus
, char **args
, unsigned n
) {
179 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
180 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
186 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
188 r
= sd_bus_message_new_method_call(
191 "org.freedesktop.locale1",
192 "/org/freedesktop/locale1",
193 "org.freedesktop.locale1",
196 return bus_log_create_error(r
);
198 r
= sd_bus_message_append_strv(m
, args
+ 1);
200 return bus_log_create_error(r
);
202 r
= sd_bus_message_append(m
, "b", arg_ask_password
);
204 return bus_log_create_error(r
);
206 r
= sd_bus_call(bus
, m
, 0, &error
, NULL
);
208 log_error("Failed to issue method call: %s", bus_error_message(&error
, -r
));
215 static int list_locales(sd_bus
*bus
, char **args
, unsigned n
) {
216 _cleanup_strv_free_
char **l
= NULL
;
223 return log_error_errno(r
, "Failed to read list of locales: %m");
225 pager_open(arg_no_pager
, false);
231 static int set_vconsole_keymap(sd_bus
*bus
, char **args
, unsigned n
) {
232 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
233 const char *map
, *toggle_map
;
240 log_error("Too many arguments.");
244 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
247 toggle_map
= n
> 2 ? args
[2] : "";
249 r
= sd_bus_call_method(
251 "org.freedesktop.locale1",
252 "/org/freedesktop/locale1",
253 "org.freedesktop.locale1",
254 "SetVConsoleKeyboard",
257 "ssbb", map
, toggle_map
, arg_convert
, arg_ask_password
);
259 log_error("Failed to set keymap: %s", bus_error_message(&error
, -r
));
264 static int list_vconsole_keymaps(sd_bus
*bus
, char **args
, unsigned n
) {
265 _cleanup_strv_free_
char **l
= NULL
;
272 return log_error_errno(r
, "Failed to read list of keymaps: %m");
274 pager_open(arg_no_pager
, false);
281 static int set_x11_keymap(sd_bus
*bus
, char **args
, unsigned n
) {
282 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
283 const char *layout
, *model
, *variant
, *options
;
290 log_error("Too many arguments.");
294 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
297 model
= n
> 2 ? args
[2] : "";
298 variant
= n
> 3 ? args
[3] : "";
299 options
= n
> 4 ? args
[4] : "";
301 r
= sd_bus_call_method(
303 "org.freedesktop.locale1",
304 "/org/freedesktop/locale1",
305 "org.freedesktop.locale1",
309 "ssssbb", layout
, model
, variant
, options
,
310 arg_convert
, arg_ask_password
);
312 log_error("Failed to set keymap: %s", bus_error_message(&error
, -r
));
317 static int list_x11_keymaps(sd_bus
*bus
, char **args
, unsigned n
) {
318 _cleanup_fclose_
FILE *f
= NULL
;
319 _cleanup_strv_free_
char **list
= NULL
;
327 } state
= NONE
, look_for
;
331 log_error("Too many arguments.");
335 f
= fopen("/usr/share/X11/xkb/rules/base.lst", "re");
337 return log_error_errno(errno
, "Failed to open keyboard mapping list. %m");
339 if (streq(args
[0], "list-x11-keymap-models"))
341 else if (streq(args
[0], "list-x11-keymap-layouts"))
343 else if (streq(args
[0], "list-x11-keymap-variants"))
345 else if (streq(args
[0], "list-x11-keymap-options"))
348 assert_not_reached("Wrong parameter");
350 FOREACH_LINE(line
, f
, break) {
359 if (startswith(l
, "! model"))
361 else if (startswith(l
, "! layout"))
363 else if (startswith(l
, "! variant"))
365 else if (startswith(l
, "! option"))
373 if (state
!= look_for
)
376 w
= l
+ strcspn(l
, WHITESPACE
);
386 w
+= strspn(w
, WHITESPACE
);
394 if (!streq(w
, args
[1]))
399 r
= strv_extend(&list
, l
);
404 if (strv_isempty(list
)) {
405 log_error("Couldn't find any entries.");
412 pager_open(arg_no_pager
, false);
418 static void help(void) {
419 printf("%s [OPTIONS...] COMMAND ...\n\n"
420 "Query or change system locale and keyboard settings.\n\n"
421 " -h --help Show this help\n"
422 " --version Show package version\n"
423 " --no-pager Do not pipe output into a pager\n"
424 " --no-ask-password Do not prompt for password\n"
425 " -H --host=[USER@]HOST Operate on remote host\n"
426 " -M --machine=CONTAINER Operate on local container\n"
427 " --no-convert Don't convert keyboard mappings\n\n"
429 " status Show current locale settings\n"
430 " set-locale LOCALE... Set system locale\n"
431 " list-locales Show known locales\n"
432 " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
433 " list-keymaps Show known virtual console keyboard mappings\n"
434 " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
435 " Set X11 and console keyboard mappings\n"
436 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
437 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
438 " list-x11-keymap-variants [LAYOUT]\n"
439 " Show known X11 keyboard mapping variants\n"
440 " list-x11-keymap-options Show known X11 keyboard mapping options\n"
441 , program_invocation_short_name
);
444 static int parse_argv(int argc
, char *argv
[]) {
453 static const struct option options
[] = {
454 { "help", no_argument
, NULL
, 'h' },
455 { "version", no_argument
, NULL
, ARG_VERSION
},
456 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
457 { "host", required_argument
, NULL
, 'H' },
458 { "machine", required_argument
, NULL
, 'M' },
459 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
460 { "no-convert", no_argument
, NULL
, ARG_NO_CONVERT
},
469 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
488 case ARG_NO_ASK_PASSWORD
:
489 arg_ask_password
= false;
493 arg_transport
= BUS_TRANSPORT_REMOTE
;
498 arg_transport
= BUS_TRANSPORT_MACHINE
;
506 assert_not_reached("Unhandled option");
512 static int localectl_main(sd_bus
*bus
, int argc
, char *argv
[]) {
514 static const struct {
522 int (* const dispatch
)(sd_bus
*bus
, char **args
, unsigned n
);
524 { "status", LESS
, 1, show_status
},
525 { "set-locale", MORE
, 2, set_locale
},
526 { "list-locales", EQUAL
, 1, list_locales
},
527 { "set-keymap", MORE
, 2, set_vconsole_keymap
},
528 { "list-keymaps", EQUAL
, 1, list_vconsole_keymaps
},
529 { "set-x11-keymap", MORE
, 2, set_x11_keymap
},
530 { "list-x11-keymap-models", EQUAL
, 1, list_x11_keymaps
},
531 { "list-x11-keymap-layouts", EQUAL
, 1, list_x11_keymaps
},
532 { "list-x11-keymap-variants", LESS
, 2, list_x11_keymaps
},
533 { "list-x11-keymap-options", EQUAL
, 1, list_x11_keymaps
},
542 left
= argc
- optind
;
545 /* Special rule: no arguments means "status" */
548 if (streq(argv
[optind
], "help")) {
553 for (i
= 0; i
< ELEMENTSOF(verbs
); i
++)
554 if (streq(argv
[optind
], verbs
[i
].verb
))
557 if (i
>= ELEMENTSOF(verbs
)) {
558 log_error("Unknown operation %s", argv
[optind
]);
563 switch (verbs
[i
].argc_cmp
) {
566 if (left
!= verbs
[i
].argc
) {
567 log_error("Invalid number of arguments.");
574 if (left
< verbs
[i
].argc
) {
575 log_error("Too few arguments.");
582 if (left
> verbs
[i
].argc
) {
583 log_error("Too many arguments.");
590 assert_not_reached("Unknown comparison operator.");
593 return verbs
[i
].dispatch(bus
, argv
+ optind
, left
);
596 int main(int argc
, char*argv
[]) {
597 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
600 setlocale(LC_ALL
, "");
601 log_parse_environment();
604 r
= parse_argv(argc
, argv
);
608 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
610 log_error_errno(r
, "Failed to create bus connection: %m");
614 r
= localectl_main(bus
, argc
, argv
);
619 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;