1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "bus-locator.h"
10 #include "bus-map-properties.h"
13 #include "format-table.h"
15 #include "locale-setup.h"
16 #include "main-func.h"
17 #include "memory-util.h"
19 #include "pretty-print.h"
20 #include "proc-cmdline.h"
22 #include "spawn-polkit-agent.h"
24 #include "terminal-util.h"
28 /* Enough time for locale-gen to finish server-side (in case it is in use) */
29 #define LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
31 static PagerFlags arg_pager_flags
= 0;
32 static bool arg_ask_password
= true;
33 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
34 static const char *arg_host
= NULL
;
35 static bool arg_convert
= true;
37 typedef struct StatusInfo
{
39 const char *vconsole_keymap
;
40 const char *vconsole_keymap_toggle
;
41 const char *x11_layout
;
42 const char *x11_model
;
43 const char *x11_variant
;
44 const char *x11_options
;
47 static void status_info_clear(StatusInfo
*info
) {
49 strv_free(info
->locale
);
54 static int print_status_info(StatusInfo
*i
) {
55 _cleanup_strv_free_
char **kernel_locale
= NULL
;
56 _cleanup_(table_unrefp
) Table
*table
= NULL
;
62 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
63 _cleanup_(locale_context_clear
) LocaleContext c
= { .mtime
= USEC_INFINITY
};
65 r
= locale_context_load(&c
, LOCALE_LOAD_PROC_CMDLINE
);
67 return log_error_errno(r
, "Failed to read /proc/cmdline: %m");
69 r
= locale_context_build_env(&c
, &kernel_locale
, NULL
);
71 return log_error_errno(r
, "Failed to build locale settings from kernel command line: %m");
74 table
= table_new("key", "value");
78 assert_se(cell
= table_get_cell(table
, 0, 0));
79 (void) table_set_ellipsize_percent(table
, cell
, 100);
80 (void) table_set_align_percent(table
, cell
, 100);
82 table_set_header(table
, false);
84 r
= table_set_empty_string(table
, "n/a");
88 if (!strv_isempty(kernel_locale
)) {
89 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.");
90 r
= table_add_many(table
,
91 TABLE_STRING
, "Command Line:",
92 TABLE_SET_COLOR
, ansi_highlight_yellow(),
93 TABLE_STRV
, kernel_locale
,
94 TABLE_SET_COLOR
, ansi_highlight_yellow());
96 return table_log_add_error(r
);
99 r
= table_add_many(table
,
100 TABLE_STRING
, "System Locale:",
101 TABLE_STRV
, i
->locale
,
102 TABLE_STRING
, "VC Keymap:",
103 TABLE_STRING
, i
->vconsole_keymap
);
105 return table_log_add_error(r
);
107 if (!isempty(i
->vconsole_keymap_toggle
)) {
108 r
= table_add_many(table
,
109 TABLE_STRING
, "VC Toggle Keymap:",
110 TABLE_STRING
, i
->vconsole_keymap_toggle
);
112 return table_log_add_error(r
);
115 r
= table_add_many(table
,
116 TABLE_STRING
, "X11 Layout:",
117 TABLE_STRING
, i
->x11_layout
);
119 return table_log_add_error(r
);
121 if (!isempty(i
->x11_model
)) {
122 r
= table_add_many(table
,
123 TABLE_STRING
, "X11 Model:",
124 TABLE_STRING
, i
->x11_model
);
126 return table_log_add_error(r
);
129 if (!isempty(i
->x11_variant
)) {
130 r
= table_add_many(table
,
131 TABLE_STRING
, "X11 Variant:",
132 TABLE_STRING
, i
->x11_variant
);
134 return table_log_add_error(r
);
137 if (!isempty(i
->x11_options
)) {
138 r
= table_add_many(table
,
139 TABLE_STRING
, "X11 Options:",
140 TABLE_STRING
, i
->x11_options
);
142 return table_log_add_error(r
);
145 r
= table_print(table
, NULL
);
147 return table_log_print_error(r
);
152 static int show_status(int argc
, char **argv
, void *userdata
) {
153 _cleanup_(status_info_clear
) StatusInfo info
= {};
154 static const struct bus_properties_map map
[] = {
155 { "VConsoleKeymap", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap
) },
156 { "VConsoleKeymapToggle", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap_toggle
) },
157 { "X11Layout", "s", NULL
, offsetof(StatusInfo
, x11_layout
) },
158 { "X11Model", "s", NULL
, offsetof(StatusInfo
, x11_model
) },
159 { "X11Variant", "s", NULL
, offsetof(StatusInfo
, x11_variant
) },
160 { "X11Options", "s", NULL
, offsetof(StatusInfo
, x11_options
) },
161 { "Locale", "as", NULL
, offsetof(StatusInfo
, locale
) },
165 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
166 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
167 sd_bus
*bus
= userdata
;
172 r
= bus_map_all_properties(bus
,
173 "org.freedesktop.locale1",
174 "/org/freedesktop/locale1",
181 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
183 return print_status_info(&info
);
186 static int set_locale(int argc
, char **argv
, void *userdata
) {
187 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
188 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
189 sd_bus
*bus
= userdata
;
194 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
196 r
= bus_message_new_method_call(bus
, &m
, bus_locale
, "SetLocale");
198 return bus_log_create_error(r
);
200 r
= sd_bus_message_append_strv(m
, argv
+ 1);
202 return bus_log_create_error(r
);
204 r
= sd_bus_message_append(m
, "b", arg_ask_password
);
206 return bus_log_create_error(r
);
208 /* We use a longer timeout for the method call in case localed is running locale-gen */
209 r
= sd_bus_call(bus
, m
, LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
211 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
216 static int list_locales(int argc
, char **argv
, void *userdata
) {
217 _cleanup_strv_free_
char **l
= NULL
;
222 return log_error_errno(r
, "Failed to read list of locales: %m");
224 pager_open(arg_pager_flags
);
230 static int set_vconsole_keymap(int argc
, char **argv
, void *userdata
) {
231 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
232 const char *map
, *toggle_map
;
233 sd_bus
*bus
= userdata
;
238 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
241 toggle_map
= argc
> 2 ? argv
[2] : "";
246 "SetVConsoleKeyboard",
249 "ssbb", map
, toggle_map
, arg_convert
, arg_ask_password
);
251 return log_error_errno(r
, "Failed to set keymap: %s", bus_error_message(&error
, r
));
256 static int list_vconsole_keymaps(int argc
, char **argv
, void *userdata
) {
257 _cleanup_strv_free_
char **l
= NULL
;
262 return log_error_errno(r
, "Failed to read list of keymaps: %m");
264 pager_open(arg_pager_flags
);
271 static int set_x11_keymap(int argc
, char **argv
, void *userdata
) {
272 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
273 const char *layout
, *model
, *variant
, *options
;
274 sd_bus
*bus
= userdata
;
277 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
280 model
= argc
> 2 ? argv
[2] : "";
281 variant
= argc
> 3 ? argv
[3] : "";
282 options
= argc
> 4 ? argv
[4] : "";
290 "ssssbb", layout
, model
, variant
, options
,
291 arg_convert
, arg_ask_password
);
293 return log_error_errno(r
, "Failed to set keymap: %s", bus_error_message(&error
, r
));
298 static int list_x11_keymaps(int argc
, char **argv
, void *userdata
) {
299 _cleanup_fclose_
FILE *f
= NULL
;
300 _cleanup_strv_free_
char **list
= NULL
;
307 } state
= NONE
, look_for
;
310 f
= fopen("/usr/share/X11/xkb/rules/base.lst", "re");
312 return log_error_errno(errno
, "Failed to open keyboard mapping list. %m");
314 if (streq(argv
[0], "list-x11-keymap-models"))
316 else if (streq(argv
[0], "list-x11-keymap-layouts"))
318 else if (streq(argv
[0], "list-x11-keymap-variants"))
320 else if (streq(argv
[0], "list-x11-keymap-options"))
323 assert_not_reached();
326 _cleanup_free_
char *line
= NULL
;
329 r
= read_line(f
, LONG_LINE_MAX
, &line
);
331 return log_error_errno(r
, "Failed to read keyboard mapping list: %m");
341 if (startswith(l
, "! model"))
343 else if (startswith(l
, "! layout"))
345 else if (startswith(l
, "! variant"))
347 else if (startswith(l
, "! option"))
355 if (state
!= look_for
)
358 w
= l
+ strcspn(l
, WHITESPACE
);
368 w
+= strspn(w
, WHITESPACE
);
376 if (!streq(w
, argv
[1]))
381 r
= strv_extend(&list
, l
);
386 if (strv_isempty(list
))
387 return log_error_errno(SYNTHETIC_ERRNO(ENOENT
),
388 "Couldn't find any entries.");
393 pager_open(arg_pager_flags
);
399 static int help(void) {
400 _cleanup_free_
char *link
= NULL
;
403 r
= terminal_urlify_man("localectl", "1", &link
);
407 printf("%s [OPTIONS...] COMMAND ...\n\n"
408 "%sQuery or change system locale and keyboard settings.%s\n"
410 " status Show current locale settings\n"
411 " set-locale LOCALE... Set system locale\n"
412 " list-locales Show known locales\n"
413 " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
414 " list-keymaps Show known virtual console keyboard mappings\n"
415 " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
416 " Set X11 and console keyboard mappings\n"
417 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
418 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
419 " list-x11-keymap-variants [LAYOUT]\n"
420 " Show known X11 keyboard mapping variants\n"
421 " list-x11-keymap-options Show known X11 keyboard mapping options\n"
423 " -h --help Show this help\n"
424 " --version Show package version\n"
425 " --no-pager Do not pipe output into a pager\n"
426 " --no-ask-password Do not prompt for password\n"
427 " -H --host=[USER@]HOST Operate on remote host\n"
428 " -M --machine=CONTAINER Operate on local container\n"
429 " --no-convert Don't convert keyboard mappings\n"
430 "\nSee the %s for details.\n",
431 program_invocation_short_name
,
439 static int verb_help(int argc
, char **argv
, void *userdata
) {
443 static int parse_argv(int argc
, char *argv
[]) {
452 static const struct option options
[] = {
453 { "help", no_argument
, NULL
, 'h' },
454 { "version", no_argument
, NULL
, ARG_VERSION
},
455 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
456 { "host", required_argument
, NULL
, 'H' },
457 { "machine", required_argument
, NULL
, 'M' },
458 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
459 { "no-convert", no_argument
, NULL
, ARG_NO_CONVERT
},
468 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
483 arg_pager_flags
|= PAGER_DISABLE
;
486 case ARG_NO_ASK_PASSWORD
:
487 arg_ask_password
= false;
491 arg_transport
= BUS_TRANSPORT_REMOTE
;
496 arg_transport
= BUS_TRANSPORT_MACHINE
;
504 assert_not_reached();
510 static int localectl_main(sd_bus
*bus
, int argc
, char *argv
[]) {
512 static const Verb verbs
[] = {
513 { "status", VERB_ANY
, 1, VERB_DEFAULT
, show_status
},
514 { "set-locale", 2, VERB_ANY
, 0, set_locale
},
515 { "list-locales", VERB_ANY
, 1, 0, list_locales
},
516 { "set-keymap", 2, 3, 0, set_vconsole_keymap
},
517 { "list-keymaps", VERB_ANY
, 1, 0, list_vconsole_keymaps
},
518 { "set-x11-keymap", 2, 5, 0, set_x11_keymap
},
519 { "list-x11-keymap-models", VERB_ANY
, 1, 0, list_x11_keymaps
},
520 { "list-x11-keymap-layouts", VERB_ANY
, 1, 0, list_x11_keymaps
},
521 { "list-x11-keymap-variants", VERB_ANY
, 2, 0, list_x11_keymaps
},
522 { "list-x11-keymap-options", VERB_ANY
, 1, 0, list_x11_keymaps
},
523 { "help", VERB_ANY
, VERB_ANY
, 0, verb_help
}, /* Not documented, but supported since it is created. */
527 return dispatch_verb(argc
, argv
, verbs
, bus
);
530 static int run(int argc
, char *argv
[]) {
531 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
534 setlocale(LC_ALL
, "");
537 r
= parse_argv(argc
, argv
);
541 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
543 return bus_log_connect_error(r
, arg_transport
);
545 return localectl_main(bus
, argc
, argv
);
548 DEFINE_MAIN_FUNCTION(run
);