]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localectl.c
core: dont't remount /sys/fs/cgroup for relabel if not needed (#8595)
[thirdparty/systemd.git] / src / locale / localectl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2087a7af
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
4d7859d1 6 Copyright 2013 Kay Sievers
2087a7af
LP
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
3f6fd1ba
LP
22#include <ftw.h>
23#include <getopt.h>
a9cdc94f 24#include <locale.h>
2087a7af 25#include <stdbool.h>
3f6fd1ba 26#include <stdlib.h>
2087a7af 27#include <string.h>
2087a7af 28
4d7859d1 29#include "sd-bus.h"
3f6fd1ba 30
4d7859d1 31#include "bus-error.h"
3f6fd1ba 32#include "bus-util.h"
0732ef7a 33#include "def.h"
3ffd4af2 34#include "fd-util.h"
a3428668 35#include "fileio.h"
75683450 36#include "locale-util.h"
3f6fd1ba
LP
37#include "pager.h"
38#include "set.h"
39#include "spawn-polkit-agent.h"
40#include "strv.h"
41#include "util.h"
1d4ecb98 42#include "verbs.h"
3f6fd1ba 43#include "virt.h"
2087a7af
LP
44
45static bool arg_no_pager = false;
2087a7af 46static bool arg_ask_password = true;
4d7859d1 47static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 48static char *arg_host = NULL;
2087a7af
LP
49static bool arg_convert = true;
50
2087a7af
LP
51typedef struct StatusInfo {
52 char **locale;
f37f8a61
YW
53 const char *vconsole_keymap;
54 const char *vconsole_keymap_toggle;
55 const char *x11_layout;
56 const char *x11_model;
57 const char *x11_variant;
58 const char *x11_options;
2087a7af
LP
59} StatusInfo;
60
e7e55dbd
DH
61static void status_info_clear(StatusInfo *info) {
62 if (info) {
63 strv_free(info->locale);
e7e55dbd
DH
64 zero(*info);
65 }
66}
67
ff9b60f3 68static void print_overridden_variables(void) {
a3428668
MS
69 int r;
70 char *variables[_VARIABLE_LC_MAX] = {};
71 LocaleVariable j;
72 bool print_warning = true;
73
75f86906 74 if (detect_container() > 0 || arg_host)
a3428668
MS
75 return;
76
77 r = parse_env_file("/proc/cmdline", WHITESPACE,
78 "locale.LANG", &variables[VARIABLE_LANG],
79 "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
80 "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
81 "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
82 "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
83 "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
84 "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
85 "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
86 "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
87 "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
88 "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
89 "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
90 "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
91 "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
92 NULL);
93
94 if (r < 0 && r != -ENOENT) {
da927ba9 95 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
a3428668
MS
96 goto finish;
97 }
98
63229aa1 99 for (j = 0; j < _VARIABLE_LC_MAX; j++)
a3428668
MS
100 if (variables[j]) {
101 if (print_warning) {
b344bcbb 102 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"
c86b2d8f 103 " Command Line: %s=%s", locale_variable_to_string(j), variables[j]);
a3428668
MS
104
105 print_warning = false;
b344bcbb 106 } else
c86b2d8f 107 log_warning(" %s=%s", locale_variable_to_string(j), variables[j]);
a3428668
MS
108 }
109 finish:
63229aa1 110 for (j = 0; j < _VARIABLE_LC_MAX; j++)
a3428668
MS
111 free(variables[j]);
112}
113
2087a7af
LP
114static void print_status_info(StatusInfo *i) {
115 assert(i);
116
117 if (strv_isempty(i->locale))
a75db59c 118 puts(" System Locale: n/a");
2087a7af
LP
119 else {
120 char **j;
121
122 printf(" System Locale: %s\n", i->locale[0]);
123 STRV_FOREACH(j, i->locale + 1)
124 printf(" %s\n", *j);
125 }
126
127 printf(" VC Keymap: %s\n", strna(i->vconsole_keymap));
128 if (!isempty(i->vconsole_keymap_toggle))
129 printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle);
130
131 printf(" X11 Layout: %s\n", strna(i->x11_layout));
132 if (!isempty(i->x11_model))
133 printf(" X11 Model: %s\n", i->x11_model);
134 if (!isempty(i->x11_variant))
135 printf(" X11 Variant: %s\n", i->x11_variant);
136 if (!isempty(i->x11_options))
137 printf(" X11 Options: %s\n", i->x11_options);
138}
139
1d4ecb98 140static int show_status(int argc, char **argv, void *userdata) {
e7e55dbd 141 _cleanup_(status_info_clear) StatusInfo info = {};
9f6eb1cd
KS
142 static const struct bus_properties_map map[] = {
143 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
144 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
145 { "VConsoleKeymapToggle", "s", NULL, offsetof(StatusInfo, vconsole_keymap_toggle) },
146 { "X11Layout", "s", NULL, offsetof(StatusInfo, x11_layout) },
147 { "X11Model", "s", NULL, offsetof(StatusInfo, x11_model) },
148 { "X11Variant", "s", NULL, offsetof(StatusInfo, x11_variant) },
149 { "X11Options", "s", NULL, offsetof(StatusInfo, x11_options) },
150 { "Locale", "as", NULL, offsetof(StatusInfo, locale) },
ffc06c35
KS
151 {}
152 };
f9e0eefc
LP
153
154 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 155 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1d4ecb98 156 sd_bus *bus = userdata;
ffc06c35 157 int r;
2087a7af 158
ffc06c35 159 assert(bus);
2087a7af 160
ffc06c35
KS
161 r = bus_map_all_properties(bus,
162 "org.freedesktop.locale1",
163 "/org/freedesktop/locale1",
9f6eb1cd 164 map,
f9e0eefc 165 &error,
f37f8a61 166 &m,
9f6eb1cd 167 &info);
e7e55dbd 168 if (r < 0)
f9e0eefc 169 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
2087a7af 170
ff9b60f3 171 print_overridden_variables();
2087a7af 172 print_status_info(&info);
4d7859d1 173
4d7859d1 174 return r;
2087a7af
LP
175}
176
1d4ecb98 177static int set_locale(int argc, char **argv, void *userdata) {
4afd3348
LP
178 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
179 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1d4ecb98 180 sd_bus *bus = userdata;
2087a7af
LP
181 int r;
182
183 assert(bus);
2087a7af 184
8a4b13c5 185 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2087a7af 186
151b9b96
LP
187 r = sd_bus_message_new_method_call(
188 bus,
189 &m,
2087a7af
LP
190 "org.freedesktop.locale1",
191 "/org/freedesktop/locale1",
192 "org.freedesktop.locale1",
151b9b96 193 "SetLocale");
4d7859d1 194 if (r < 0)
94676f3e 195 return bus_log_create_error(r);
2087a7af 196
1d4ecb98 197 r = sd_bus_message_append_strv(m, argv + 1);
4d7859d1 198 if (r < 0)
94676f3e 199 return bus_log_create_error(r);
2087a7af 200
4d7859d1 201 r = sd_bus_message_append(m, "b", arg_ask_password);
2087a7af 202 if (r < 0)
94676f3e 203 return bus_log_create_error(r);
2087a7af 204
c49b30a2 205 r = sd_bus_call(bus, m, 0, &error, NULL);
4d7859d1 206 if (r < 0) {
e1636421 207 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
4d7859d1 208 return r;
2087a7af
LP
209 }
210
4d7859d1 211 return 0;
2087a7af
LP
212}
213
1d4ecb98 214static int list_locales(int argc, char **argv, void *userdata) {
17d33cec 215 _cleanup_strv_free_ char **l = NULL;
17d33cec
GC
216 int r;
217
75683450 218 r = get_locales(&l);
f647962d
MS
219 if (r < 0)
220 return log_error_errno(r, "Failed to read list of locales: %m");
2087a7af 221
ee5324aa 222 (void) pager_open(arg_no_pager, false);
7c2d8094 223 strv_print(l);
2087a7af 224
bac3c8ee 225 return 0;
2087a7af
LP
226}
227
1d4ecb98 228static int set_vconsole_keymap(int argc, char **argv, void *userdata) {
4afd3348 229 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7af 230 const char *map, *toggle_map;
1d4ecb98 231 sd_bus *bus = userdata;
e1636421 232 int r;
2087a7af
LP
233
234 assert(bus);
2087a7af 235
8a4b13c5 236 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2087a7af 237
1d4ecb98
YW
238 map = argv[1];
239 toggle_map = argc > 2 ? argv[2] : "";
2087a7af 240
e1636421
LP
241 r = sd_bus_call_method(
242 bus,
2087a7af
LP
243 "org.freedesktop.locale1",
244 "/org/freedesktop/locale1",
245 "org.freedesktop.locale1",
246 "SetVConsoleKeyboard",
4d7859d1 247 &error,
2087a7af 248 NULL,
4d7859d1 249 "ssbb", map, toggle_map, arg_convert, arg_ask_password);
e1636421
LP
250 if (r < 0)
251 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
252
253 return r;
2087a7af
LP
254}
255
1d4ecb98 256static int list_vconsole_keymaps(int argc, char **argv, void *userdata) {
a017112b 257 _cleanup_strv_free_ char **l = NULL;
ed457f13 258 int r;
2087a7af 259
ed457f13
TB
260 r = get_keymaps(&l);
261 if (r < 0)
262 return log_error_errno(r, "Failed to read list of keymaps: %m");
2087a7af 263
ee5324aa 264 (void) pager_open(arg_no_pager, false);
2087a7af 265
7c2d8094 266 strv_print(l);
2087a7af
LP
267
268 return 0;
269}
270
1d4ecb98 271static int set_x11_keymap(int argc, char **argv, void *userdata) {
4afd3348 272 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7af 273 const char *layout, *model, *variant, *options;
1d4ecb98 274 sd_bus *bus = userdata;
e1636421 275 int r;
2087a7af 276
8a4b13c5 277 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2087a7af 278
1d4ecb98
YW
279 layout = argv[1];
280 model = argc > 2 ? argv[2] : "";
281 variant = argc > 3 ? argv[3] : "";
282 options = argc > 4 ? argv[4] : "";
2087a7af 283
e1636421
LP
284 r = sd_bus_call_method(
285 bus,
2087a7af
LP
286 "org.freedesktop.locale1",
287 "/org/freedesktop/locale1",
288 "org.freedesktop.locale1",
289 "SetX11Keyboard",
4d7859d1 290 &error,
2087a7af 291 NULL,
4d7859d1
KS
292 "ssssbb", layout, model, variant, options,
293 arg_convert, arg_ask_password);
e1636421
LP
294 if (r < 0)
295 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
296
297 return r;
2087a7af
LP
298}
299
1d4ecb98 300static int list_x11_keymaps(int argc, char **argv, void *userdata) {
50cfc579 301 _cleanup_fclose_ FILE *f = NULL;
7fd1b19b 302 _cleanup_strv_free_ char **list = NULL;
50cfc579
LP
303 char line[LINE_MAX];
304 enum {
305 NONE,
306 MODELS,
307 LAYOUTS,
308 VARIANTS,
309 OPTIONS
310 } state = NONE, look_for;
311 int r;
312
c62e11ce 313 f = fopen("/usr/share/X11/xkb/rules/base.lst", "re");
4a62c710
MS
314 if (!f)
315 return log_error_errno(errno, "Failed to open keyboard mapping list. %m");
50cfc579 316
1d4ecb98 317 if (streq(argv[0], "list-x11-keymap-models"))
50cfc579 318 look_for = MODELS;
1d4ecb98 319 else if (streq(argv[0], "list-x11-keymap-layouts"))
50cfc579 320 look_for = LAYOUTS;
1d4ecb98 321 else if (streq(argv[0], "list-x11-keymap-variants"))
50cfc579 322 look_for = VARIANTS;
1d4ecb98 323 else if (streq(argv[0], "list-x11-keymap-options"))
50cfc579
LP
324 look_for = OPTIONS;
325 else
326 assert_not_reached("Wrong parameter");
327
328 FOREACH_LINE(line, f, break) {
329 char *l, *w;
330
331 l = strstrip(line);
332
333 if (isempty(l))
334 continue;
335
336 if (l[0] == '!') {
337 if (startswith(l, "! model"))
338 state = MODELS;
339 else if (startswith(l, "! layout"))
340 state = LAYOUTS;
341 else if (startswith(l, "! variant"))
342 state = VARIANTS;
343 else if (startswith(l, "! option"))
344 state = OPTIONS;
345 else
346 state = NONE;
347
348 continue;
349 }
350
351 if (state != look_for)
352 continue;
353
354 w = l + strcspn(l, WHITESPACE);
355
1d4ecb98 356 if (argc > 1) {
50cfc579
LP
357 char *e;
358
359 if (*w == 0)
360 continue;
361
362 *w = 0;
363 w++;
364 w += strspn(w, WHITESPACE);
365
366 e = strchr(w, ':');
367 if (!e)
368 continue;
369
370 *e = 0;
371
1d4ecb98 372 if (!streq(w, argv[1]))
50cfc579
LP
373 continue;
374 } else
375 *w = 0;
376
7d6884b6
TA
377 r = strv_extend(&list, l);
378 if (r < 0)
379 return log_oom();
50cfc579
LP
380 }
381
382 if (strv_isempty(list)) {
383 log_error("Couldn't find any entries.");
384 return -ENOENT;
385 }
386
387 strv_sort(list);
388 strv_uniq(list);
389
ee5324aa 390 (void) pager_open(arg_no_pager, false);
50cfc579
LP
391
392 strv_print(list);
393 return 0;
394}
395
1d4ecb98 396static int help(void) {
2087a7af 397 printf("%s [OPTIONS...] COMMAND ...\n\n"
82c1d8f4 398 "Query or change system locale and keyboard settings.\n\n"
50cfc579
LP
399 " -h --help Show this help\n"
400 " --version Show package version\n"
50cfc579
LP
401 " --no-pager Do not pipe output into a pager\n"
402 " --no-ask-password Do not prompt for password\n"
4d7859d1 403 " -H --host=[USER@]HOST Operate on remote host\n"
a86a47ce
LP
404 " -M --machine=CONTAINER Operate on local container\n"
405 " --no-convert Don't convert keyboard mappings\n\n"
2087a7af 406 "Commands:\n"
50cfc579
LP
407 " status Show current locale settings\n"
408 " set-locale LOCALE... Set system locale\n"
409 " list-locales Show known locales\n"
2ebcf936 410 " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
50cfc579 411 " list-keymaps Show known virtual console keyboard mappings\n"
31cf921a 412 " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
2ebcf936 413 " Set X11 and console keyboard mappings\n"
50cfc579
LP
414 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
415 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
416 " list-x11-keymap-variants [LAYOUT]\n"
417 " Show known X11 keyboard mapping variants\n"
601185b4
ZJS
418 " list-x11-keymap-options Show known X11 keyboard mapping options\n"
419 , program_invocation_short_name);
1d4ecb98
YW
420
421 return 0;
422}
423
424static int verb_help(int argc, char **argv, void *userdata) {
425 return help();
2087a7af
LP
426}
427
428static int parse_argv(int argc, char *argv[]) {
429
430 enum {
431 ARG_VERSION = 0x100,
432 ARG_NO_PAGER,
433 ARG_NO_CONVERT,
434 ARG_NO_ASK_PASSWORD
435 };
436
437 static const struct option options[] = {
4d7859d1
KS
438 { "help", no_argument, NULL, 'h' },
439 { "version", no_argument, NULL, ARG_VERSION },
440 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
441 { "host", required_argument, NULL, 'H' },
442 { "machine", required_argument, NULL, 'M' },
443 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
444 { "no-convert", no_argument, NULL, ARG_NO_CONVERT },
eb9da376 445 {}
2087a7af
LP
446 };
447
448 int c;
449
450 assert(argc >= 0);
451 assert(argv);
452
601185b4 453 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
2087a7af
LP
454
455 switch (c) {
456
457 case 'h':
1d4ecb98 458 return help();
2087a7af
LP
459
460 case ARG_VERSION:
3f6fd1ba 461 return version();
2087a7af 462
2087a7af
LP
463 case ARG_NO_CONVERT:
464 arg_convert = false;
465 break;
466
467 case ARG_NO_PAGER:
468 arg_no_pager = true;
469 break;
470
546158bc
JJ
471 case ARG_NO_ASK_PASSWORD:
472 arg_ask_password = false;
473 break;
474
4d7859d1
KS
475 case 'H':
476 arg_transport = BUS_TRANSPORT_REMOTE;
477 arg_host = optarg;
478 break;
479
480 case 'M':
de33fc62 481 arg_transport = BUS_TRANSPORT_MACHINE;
4d7859d1
KS
482 arg_host = optarg;
483 break;
484
2087a7af
LP
485 case '?':
486 return -EINVAL;
487
488 default:
eb9da376 489 assert_not_reached("Unhandled option");
2087a7af 490 }
2087a7af
LP
491
492 return 1;
493}
494
4d7859d1 495static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
2087a7af 496
1d4ecb98
YW
497 static const Verb verbs[] = {
498 { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
499 { "set-locale", 2, VERB_ANY, 0, set_locale },
500 { "list-locales", VERB_ANY, 1, 0, list_locales },
501 { "set-keymap", 2, 3, 0, set_vconsole_keymap },
502 { "list-keymaps", VERB_ANY, 1, 0, list_vconsole_keymaps },
503 { "set-x11-keymap", 2, 5, 0, set_x11_keymap },
504 { "list-x11-keymap-models", VERB_ANY, 1, 0, list_x11_keymaps },
505 { "list-x11-keymap-layouts", VERB_ANY, 1, 0, list_x11_keymaps },
506 { "list-x11-keymap-variants", VERB_ANY, 2, 0, list_x11_keymaps },
507 { "list-x11-keymap-options", VERB_ANY, 1, 0, list_x11_keymaps },
508 { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
509 {}
2087a7af
LP
510 };
511
1d4ecb98 512 return dispatch_verb(argc, argv, verbs, bus);
2087a7af
LP
513}
514
4d7859d1 515int main(int argc, char*argv[]) {
a3c56345 516 sd_bus *bus = NULL;
84f6181c 517 int r;
2087a7af 518
a9cdc94f 519 setlocale(LC_ALL, "");
2087a7af
LP
520 log_parse_environment();
521 log_open();
522
523 r = parse_argv(argc, argv);
84f6181c 524 if (r <= 0)
2087a7af 525 goto finish;
2087a7af 526
266f3e26 527 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
4d7859d1 528 if (r < 0) {
da927ba9 529 log_error_errno(r, "Failed to create bus connection: %m");
4d7859d1 530 goto finish;
2087a7af
LP
531 }
532
4d7859d1 533 r = localectl_main(bus, argc, argv);
2087a7af 534
4d7859d1 535finish:
0a84daa5
FB
536 /* make sure we terminate the bus connection first, and then close the
537 * pager, see issue #3543 for the details. */
a3c56345 538 sd_bus_flush_close_unref(bus);
2087a7af
LP
539 pager_close();
540
84f6181c 541 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2087a7af 542}