1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "bus-error.h"
11 #include "bus-locator.h"
12 #include "bus-map-properties.h"
16 #include "locale-util.h"
17 #include "main-func.h"
18 #include "memory-util.h"
20 #include "pretty-print.h"
21 #include "proc-cmdline.h"
23 #include "spawn-polkit-agent.h"
25 #include "terminal-util.h"
29 /* Enough time for locale-gen to finish server-side (in case it is in use) */
30 #define LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
32 static PagerFlags arg_pager_flags
= 0;
33 static bool arg_ask_password
= true;
34 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
35 static const char *arg_host
= NULL
;
36 static bool arg_convert
= true;
38 typedef struct StatusInfo
{
40 const char *vconsole_keymap
;
41 const char *vconsole_keymap_toggle
;
42 const char *x11_layout
;
43 const char *x11_model
;
44 const char *x11_variant
;
45 const char *x11_options
;
48 static void status_info_clear(StatusInfo
*info
) {
50 strv_free(info
->locale
);
55 static void print_overridden_variables(void) {
56 _cleanup_(locale_variables_freep
) char *variables
[_VARIABLE_LC_MAX
] = {};
57 bool print_warning
= true;
60 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
63 r
= proc_cmdline_get_key_many(
64 PROC_CMDLINE_STRIP_RD_PREFIX
,
65 "locale.LANG", &variables
[VARIABLE_LANG
],
66 "locale.LANGUAGE", &variables
[VARIABLE_LANGUAGE
],
67 "locale.LC_CTYPE", &variables
[VARIABLE_LC_CTYPE
],
68 "locale.LC_NUMERIC", &variables
[VARIABLE_LC_NUMERIC
],
69 "locale.LC_TIME", &variables
[VARIABLE_LC_TIME
],
70 "locale.LC_COLLATE", &variables
[VARIABLE_LC_COLLATE
],
71 "locale.LC_MONETARY", &variables
[VARIABLE_LC_MONETARY
],
72 "locale.LC_MESSAGES", &variables
[VARIABLE_LC_MESSAGES
],
73 "locale.LC_PAPER", &variables
[VARIABLE_LC_PAPER
],
74 "locale.LC_NAME", &variables
[VARIABLE_LC_NAME
],
75 "locale.LC_ADDRESS", &variables
[VARIABLE_LC_ADDRESS
],
76 "locale.LC_TELEPHONE", &variables
[VARIABLE_LC_TELEPHONE
],
77 "locale.LC_MEASUREMENT", &variables
[VARIABLE_LC_MEASUREMENT
],
78 "locale.LC_IDENTIFICATION", &variables
[VARIABLE_LC_IDENTIFICATION
]);
79 if (r
< 0 && r
!= -ENOENT
) {
80 log_warning_errno(r
, "Failed to read /proc/cmdline: %m");
84 for (LocaleVariable j
= 0; j
< _VARIABLE_LC_MAX
; j
++)
87 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"
88 " Command Line: %s=%s", locale_variable_to_string(j
), variables
[j
]);
90 print_warning
= false;
92 log_warning(" %s=%s", locale_variable_to_string(j
), variables
[j
]);
96 static void print_status_info(StatusInfo
*i
) {
99 if (strv_isempty(i
->locale
))
100 puts(" System Locale: n/a");
104 printf(" System Locale: %s\n", i
->locale
[0]);
105 STRV_FOREACH(j
, i
->locale
+ 1)
109 printf(" VC Keymap: %s\n", strna(i
->vconsole_keymap
));
110 if (!isempty(i
->vconsole_keymap_toggle
))
111 printf("VC Toggle Keymap: %s\n", i
->vconsole_keymap_toggle
);
113 printf(" X11 Layout: %s\n", strna(i
->x11_layout
));
114 if (!isempty(i
->x11_model
))
115 printf(" X11 Model: %s\n", i
->x11_model
);
116 if (!isempty(i
->x11_variant
))
117 printf(" X11 Variant: %s\n", i
->x11_variant
);
118 if (!isempty(i
->x11_options
))
119 printf(" X11 Options: %s\n", i
->x11_options
);
122 static int show_status(int argc
, char **argv
, void *userdata
) {
123 _cleanup_(status_info_clear
) StatusInfo info
= {};
124 static const struct bus_properties_map map
[] = {
125 { "VConsoleKeymap", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap
) },
126 { "VConsoleKeymapToggle", "s", NULL
, offsetof(StatusInfo
, vconsole_keymap_toggle
) },
127 { "X11Layout", "s", NULL
, offsetof(StatusInfo
, x11_layout
) },
128 { "X11Model", "s", NULL
, offsetof(StatusInfo
, x11_model
) },
129 { "X11Variant", "s", NULL
, offsetof(StatusInfo
, x11_variant
) },
130 { "X11Options", "s", NULL
, offsetof(StatusInfo
, x11_options
) },
131 { "Locale", "as", NULL
, offsetof(StatusInfo
, locale
) },
135 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
136 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
137 sd_bus
*bus
= userdata
;
142 r
= bus_map_all_properties(bus
,
143 "org.freedesktop.locale1",
144 "/org/freedesktop/locale1",
151 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
153 print_overridden_variables();
154 print_status_info(&info
);
159 static int set_locale(int argc
, char **argv
, void *userdata
) {
160 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
161 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
162 sd_bus
*bus
= userdata
;
167 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
169 r
= bus_message_new_method_call(bus
, &m
, bus_locale
, "SetLocale");
171 return bus_log_create_error(r
);
173 r
= sd_bus_message_append_strv(m
, argv
+ 1);
175 return bus_log_create_error(r
);
177 r
= sd_bus_message_append(m
, "b", arg_ask_password
);
179 return bus_log_create_error(r
);
181 /* We use a longer timeout for the method call in case localed is running locale-gen */
182 r
= sd_bus_call(bus
, m
, LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
184 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
189 static int list_locales(int argc
, char **argv
, void *userdata
) {
190 _cleanup_strv_free_
char **l
= NULL
;
195 return log_error_errno(r
, "Failed to read list of locales: %m");
197 (void) pager_open(arg_pager_flags
);
203 static int set_vconsole_keymap(int argc
, char **argv
, void *userdata
) {
204 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
205 const char *map
, *toggle_map
;
206 sd_bus
*bus
= userdata
;
211 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
214 toggle_map
= argc
> 2 ? argv
[2] : "";
219 "SetVConsoleKeyboard",
222 "ssbb", map
, toggle_map
, arg_convert
, arg_ask_password
);
224 return log_error_errno(r
, "Failed to set keymap: %s", bus_error_message(&error
, r
));
229 static int list_vconsole_keymaps(int argc
, char **argv
, void *userdata
) {
230 _cleanup_strv_free_
char **l
= NULL
;
235 return log_error_errno(r
, "Failed to read list of keymaps: %m");
237 (void) pager_open(arg_pager_flags
);
244 static int set_x11_keymap(int argc
, char **argv
, void *userdata
) {
245 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
246 const char *layout
, *model
, *variant
, *options
;
247 sd_bus
*bus
= userdata
;
250 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
253 model
= argc
> 2 ? argv
[2] : "";
254 variant
= argc
> 3 ? argv
[3] : "";
255 options
= argc
> 4 ? argv
[4] : "";
263 "ssssbb", layout
, model
, variant
, options
,
264 arg_convert
, arg_ask_password
);
266 return log_error_errno(r
, "Failed to set keymap: %s", bus_error_message(&error
, r
));
271 static int list_x11_keymaps(int argc
, char **argv
, void *userdata
) {
272 _cleanup_fclose_
FILE *f
= NULL
;
273 _cleanup_strv_free_
char **list
= NULL
;
280 } state
= NONE
, look_for
;
283 f
= fopen("/usr/share/X11/xkb/rules/base.lst", "re");
285 return log_error_errno(errno
, "Failed to open keyboard mapping list. %m");
287 if (streq(argv
[0], "list-x11-keymap-models"))
289 else if (streq(argv
[0], "list-x11-keymap-layouts"))
291 else if (streq(argv
[0], "list-x11-keymap-variants"))
293 else if (streq(argv
[0], "list-x11-keymap-options"))
296 assert_not_reached("Wrong parameter");
299 _cleanup_free_
char *line
= NULL
;
302 r
= read_line(f
, LONG_LINE_MAX
, &line
);
304 return log_error_errno(r
, "Failed to read keyboard mapping list: %m");
314 if (startswith(l
, "! model"))
316 else if (startswith(l
, "! layout"))
318 else if (startswith(l
, "! variant"))
320 else if (startswith(l
, "! option"))
328 if (state
!= look_for
)
331 w
= l
+ strcspn(l
, WHITESPACE
);
341 w
+= strspn(w
, WHITESPACE
);
349 if (!streq(w
, argv
[1]))
354 r
= strv_extend(&list
, l
);
359 if (strv_isempty(list
))
360 return log_error_errno(SYNTHETIC_ERRNO(ENOENT
),
361 "Couldn't find any entries.");
366 (void) pager_open(arg_pager_flags
);
372 static int help(void) {
373 _cleanup_free_
char *link
= NULL
;
376 r
= terminal_urlify_man("localectl", "1", &link
);
380 printf("%s [OPTIONS...] COMMAND ...\n\n"
381 "%sQuery or change system locale and keyboard settings.%s\n"
383 " status Show current locale settings\n"
384 " set-locale LOCALE... Set system locale\n"
385 " list-locales Show known locales\n"
386 " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
387 " list-keymaps Show known virtual console keyboard mappings\n"
388 " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
389 " Set X11 and console keyboard mappings\n"
390 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
391 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
392 " list-x11-keymap-variants [LAYOUT]\n"
393 " Show known X11 keyboard mapping variants\n"
394 " list-x11-keymap-options Show known X11 keyboard mapping options\n"
396 " -h --help Show this help\n"
397 " --version Show package version\n"
398 " --no-pager Do not pipe output into a pager\n"
399 " --no-ask-password Do not prompt for password\n"
400 " -H --host=[USER@]HOST Operate on remote host\n"
401 " -M --machine=CONTAINER Operate on local container\n"
402 " --no-convert Don't convert keyboard mappings\n"
403 "\nSee the %s for details.\n",
404 program_invocation_short_name
,
412 static int verb_help(int argc
, char **argv
, void *userdata
) {
416 static int parse_argv(int argc
, char *argv
[]) {
425 static const struct option options
[] = {
426 { "help", no_argument
, NULL
, 'h' },
427 { "version", no_argument
, NULL
, ARG_VERSION
},
428 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
429 { "host", required_argument
, NULL
, 'H' },
430 { "machine", required_argument
, NULL
, 'M' },
431 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
432 { "no-convert", no_argument
, NULL
, ARG_NO_CONVERT
},
441 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
456 arg_pager_flags
|= PAGER_DISABLE
;
459 case ARG_NO_ASK_PASSWORD
:
460 arg_ask_password
= false;
464 arg_transport
= BUS_TRANSPORT_REMOTE
;
469 arg_transport
= BUS_TRANSPORT_MACHINE
;
477 assert_not_reached("Unhandled option");
483 static int localectl_main(sd_bus
*bus
, int argc
, char *argv
[]) {
485 static const Verb verbs
[] = {
486 { "status", VERB_ANY
, 1, VERB_DEFAULT
, show_status
},
487 { "set-locale", 2, VERB_ANY
, 0, set_locale
},
488 { "list-locales", VERB_ANY
, 1, 0, list_locales
},
489 { "set-keymap", 2, 3, 0, set_vconsole_keymap
},
490 { "list-keymaps", VERB_ANY
, 1, 0, list_vconsole_keymaps
},
491 { "set-x11-keymap", 2, 5, 0, set_x11_keymap
},
492 { "list-x11-keymap-models", VERB_ANY
, 1, 0, list_x11_keymaps
},
493 { "list-x11-keymap-layouts", VERB_ANY
, 1, 0, list_x11_keymaps
},
494 { "list-x11-keymap-variants", VERB_ANY
, 2, 0, list_x11_keymaps
},
495 { "list-x11-keymap-options", VERB_ANY
, 1, 0, list_x11_keymaps
},
496 { "help", VERB_ANY
, VERB_ANY
, 0, verb_help
}, /* Not documented, but supported since it is created. */
500 return dispatch_verb(argc
, argv
, verbs
, bus
);
503 static int run(int argc
, char *argv
[]) {
504 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
507 setlocale(LC_ALL
, "");
510 r
= parse_argv(argc
, argv
);
514 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
516 return bus_log_connect_error(r
);
518 return localectl_main(bus
, argc
, argv
);
521 DEFINE_MAIN_FUNCTION(run
);