]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localectl.c
tree-wide: merge pager_open_if_enabled() to the pager_open()
[thirdparty/systemd.git] / src / locale / localectl.c
CommitLineData
2087a7af
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2012 Lennart Poettering
4d7859d1 5 Copyright 2013 Kay Sievers
2087a7af
LP
6
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.
11
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.
16
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/>.
19***/
20
3f6fd1ba
LP
21#include <ftw.h>
22#include <getopt.h>
a9cdc94f 23#include <locale.h>
2087a7af 24#include <stdbool.h>
3f6fd1ba 25#include <stdlib.h>
2087a7af 26#include <string.h>
2087a7af 27
4d7859d1 28#include "sd-bus.h"
3f6fd1ba 29
4d7859d1 30#include "bus-error.h"
3f6fd1ba 31#include "bus-util.h"
0732ef7a 32#include "def.h"
3ffd4af2 33#include "fd-util.h"
a3428668 34#include "fileio.h"
75683450 35#include "locale-util.h"
3f6fd1ba
LP
36#include "pager.h"
37#include "set.h"
38#include "spawn-polkit-agent.h"
39#include "strv.h"
40#include "util.h"
41#include "virt.h"
2087a7af
LP
42
43static bool arg_no_pager = false;
2087a7af 44static bool arg_ask_password = true;
4d7859d1 45static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 46static char *arg_host = NULL;
2087a7af
LP
47static bool arg_convert = true;
48
2087a7af
LP
49static void polkit_agent_open_if_enabled(void) {
50
51 /* Open the polkit agent as a child process if necessary */
2087a7af
LP
52 if (!arg_ask_password)
53 return;
54
46e65dcc
LP
55 if (arg_transport != BUS_TRANSPORT_LOCAL)
56 return;
57
2087a7af
LP
58 polkit_agent_open();
59}
60
61typedef struct StatusInfo {
62 char **locale;
e7e55dbd
DH
63 char *vconsole_keymap;
64 char *vconsole_keymap_toggle;
65 char *x11_layout;
66 char *x11_model;
67 char *x11_variant;
68 char *x11_options;
2087a7af
LP
69} StatusInfo;
70
e7e55dbd
DH
71static void status_info_clear(StatusInfo *info) {
72 if (info) {
73 strv_free(info->locale);
74 free(info->vconsole_keymap);
75 free(info->vconsole_keymap_toggle);
76 free(info->x11_layout);
77 free(info->x11_model);
78 free(info->x11_variant);
79 free(info->x11_options);
80 zero(*info);
81 }
82}
83
ff9b60f3 84static void print_overridden_variables(void) {
a3428668
MS
85 int r;
86 char *variables[_VARIABLE_LC_MAX] = {};
87 LocaleVariable j;
88 bool print_warning = true;
89
75f86906 90 if (detect_container() > 0 || arg_host)
a3428668
MS
91 return;
92
93 r = parse_env_file("/proc/cmdline", WHITESPACE,
94 "locale.LANG", &variables[VARIABLE_LANG],
95 "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
96 "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
97 "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
98 "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
99 "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
100 "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
101 "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
102 "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
103 "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
104 "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
105 "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
106 "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
107 "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
108 NULL);
109
110 if (r < 0 && r != -ENOENT) {
da927ba9 111 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
a3428668
MS
112 goto finish;
113 }
114
63229aa1 115 for (j = 0; j < _VARIABLE_LC_MAX; j++)
a3428668
MS
116 if (variables[j]) {
117 if (print_warning) {
b344bcbb 118 log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"
ff49bc32 119 " Command Line: %s=%s", locale_variable_to_string(j), variables[j]);
a3428668
MS
120
121 print_warning = false;
b344bcbb 122 } else
ff49bc32 123 log_warning(" %s=%s", locale_variable_to_string(j), variables[j]);
a3428668
MS
124 }
125 finish:
63229aa1 126 for (j = 0; j < _VARIABLE_LC_MAX; j++)
a3428668
MS
127 free(variables[j]);
128}
129
2087a7af
LP
130static void print_status_info(StatusInfo *i) {
131 assert(i);
132
133 if (strv_isempty(i->locale))
134 puts(" System Locale: n/a\n");
135 else {
136 char **j;
137
138 printf(" System Locale: %s\n", i->locale[0]);
139 STRV_FOREACH(j, i->locale + 1)
140 printf(" %s\n", *j);
141 }
142
143 printf(" VC Keymap: %s\n", strna(i->vconsole_keymap));
144 if (!isempty(i->vconsole_keymap_toggle))
145 printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle);
146
147 printf(" X11 Layout: %s\n", strna(i->x11_layout));
148 if (!isempty(i->x11_model))
149 printf(" X11 Model: %s\n", i->x11_model);
150 if (!isempty(i->x11_variant))
151 printf(" X11 Variant: %s\n", i->x11_variant);
152 if (!isempty(i->x11_options))
153 printf(" X11 Options: %s\n", i->x11_options);
154}
155
4d7859d1 156static int show_status(sd_bus *bus, char **args, unsigned n) {
e7e55dbd 157 _cleanup_(status_info_clear) StatusInfo info = {};
9f6eb1cd
KS
158 static const struct bus_properties_map map[] = {
159 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
160 { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) },
161 { "VConsoleKeymapToggle", "s", NULL, offsetof(StatusInfo, vconsole_keymap_toggle) },
162 { "X11Layout", "s", NULL, offsetof(StatusInfo, x11_layout) },
163 { "X11Model", "s", NULL, offsetof(StatusInfo, x11_model) },
164 { "X11Variant", "s", NULL, offsetof(StatusInfo, x11_variant) },
165 { "X11Options", "s", NULL, offsetof(StatusInfo, x11_options) },
166 { "Locale", "as", NULL, offsetof(StatusInfo, locale) },
ffc06c35
KS
167 {}
168 };
169 int r;
2087a7af 170
ffc06c35 171 assert(bus);
2087a7af 172
ffc06c35
KS
173 r = bus_map_all_properties(bus,
174 "org.freedesktop.locale1",
175 "/org/freedesktop/locale1",
9f6eb1cd
KS
176 map,
177 &info);
e7e55dbd
DH
178 if (r < 0)
179 return log_error_errno(r, "Could not get properties: %m");
2087a7af 180
ff9b60f3 181 print_overridden_variables();
2087a7af 182 print_status_info(&info);
4d7859d1 183
4d7859d1 184 return r;
2087a7af
LP
185}
186
4d7859d1 187static int set_locale(sd_bus *bus, char **args, unsigned n) {
4afd3348
LP
188 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
189 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7af
LP
190 int r;
191
192 assert(bus);
193 assert(args);
194
2087a7af
LP
195 polkit_agent_open_if_enabled();
196
151b9b96
LP
197 r = sd_bus_message_new_method_call(
198 bus,
199 &m,
2087a7af
LP
200 "org.freedesktop.locale1",
201 "/org/freedesktop/locale1",
202 "org.freedesktop.locale1",
151b9b96 203 "SetLocale");
4d7859d1 204 if (r < 0)
94676f3e 205 return bus_log_create_error(r);
2087a7af 206
4d7859d1
KS
207 r = sd_bus_message_append_strv(m, args + 1);
208 if (r < 0)
94676f3e 209 return bus_log_create_error(r);
2087a7af 210
4d7859d1 211 r = sd_bus_message_append(m, "b", arg_ask_password);
2087a7af 212 if (r < 0)
94676f3e 213 return bus_log_create_error(r);
2087a7af 214
c49b30a2 215 r = sd_bus_call(bus, m, 0, &error, NULL);
4d7859d1 216 if (r < 0) {
e1636421 217 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
4d7859d1 218 return r;
2087a7af
LP
219 }
220
4d7859d1 221 return 0;
2087a7af
LP
222}
223
4d7859d1 224static int list_locales(sd_bus *bus, char **args, unsigned n) {
17d33cec 225 _cleanup_strv_free_ char **l = NULL;
17d33cec
GC
226 int r;
227
75683450 228 assert(args);
17d33cec 229
75683450 230 r = get_locales(&l);
f647962d
MS
231 if (r < 0)
232 return log_error_errno(r, "Failed to read list of locales: %m");
2087a7af 233
ea4b98e6 234 pager_open(arg_no_pager, false);
7c2d8094 235 strv_print(l);
2087a7af 236
bac3c8ee 237 return 0;
2087a7af
LP
238}
239
4d7859d1 240static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) {
4afd3348 241 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7af 242 const char *map, *toggle_map;
e1636421 243 int r;
2087a7af
LP
244
245 assert(bus);
246 assert(args);
247
248 if (n > 3) {
249 log_error("Too many arguments.");
250 return -EINVAL;
251 }
252
253 polkit_agent_open_if_enabled();
254
255 map = args[1];
256 toggle_map = n > 2 ? args[2] : "";
2087a7af 257
e1636421
LP
258 r = sd_bus_call_method(
259 bus,
2087a7af
LP
260 "org.freedesktop.locale1",
261 "/org/freedesktop/locale1",
262 "org.freedesktop.locale1",
263 "SetVConsoleKeyboard",
4d7859d1 264 &error,
2087a7af 265 NULL,
4d7859d1 266 "ssbb", map, toggle_map, arg_convert, arg_ask_password);
e1636421
LP
267 if (r < 0)
268 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
269
270 return r;
2087a7af
LP
271}
272
273static Set *keymaps = NULL;
274
275static int nftw_cb(
276 const char *fpath,
277 const struct stat *sb,
278 int tflag,
279 struct FTW *ftwbuf) {
280
281 char *p, *e;
282 int r;
283
284 if (tflag != FTW_F)
285 return 0;
286
287 if (!endswith(fpath, ".map") &&
288 !endswith(fpath, ".map.gz"))
289 return 0;
290
2b6bf07d 291 p = strdup(basename(fpath));
2087a7af
LP
292 if (!p)
293 return log_oom();
294
295 e = endswith(p, ".map");
296 if (e)
297 *e = 0;
298
299 e = endswith(p, ".map.gz");
300 if (e)
301 *e = 0;
302
ef42202a 303 r = set_consume(keymaps, p);
f647962d
MS
304 if (r < 0 && r != -EEXIST)
305 return log_error_errno(r, "Can't add keymap: %m");
2087a7af
LP
306
307 return 0;
308}
309
4d7859d1 310static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) {
7fd1b19b 311 _cleanup_strv_free_ char **l = NULL;
0732ef7a 312 const char *dir;
2087a7af 313
d5099efc 314 keymaps = set_new(&string_hash_ops);
2087a7af
LP
315 if (!keymaps)
316 return log_oom();
317
0732ef7a
ZJS
318 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS)
319 nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
2087a7af
LP
320
321 l = set_get_strv(keymaps);
322 if (!l) {
323 set_free_free(keymaps);
324 return log_oom();
325 }
326
327 set_free(keymaps);
328
329 if (strv_isempty(l)) {
330 log_error("Couldn't find any console keymaps.");
331 return -ENOENT;
332 }
333
334 strv_sort(l);
335
ea4b98e6 336 pager_open(arg_no_pager, false);
2087a7af 337
7c2d8094 338 strv_print(l);
2087a7af
LP
339
340 return 0;
341}
342
4d7859d1 343static int set_x11_keymap(sd_bus *bus, char **args, unsigned n) {
4afd3348 344 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7af 345 const char *layout, *model, *variant, *options;
e1636421 346 int r;
2087a7af
LP
347
348 assert(bus);
349 assert(args);
350
351 if (n > 5) {
352 log_error("Too many arguments.");
353 return -EINVAL;
354 }
355
356 polkit_agent_open_if_enabled();
357
358 layout = args[1];
359 model = n > 2 ? args[2] : "";
360 variant = n > 3 ? args[3] : "";
6b2b6f30 361 options = n > 4 ? args[4] : "";
2087a7af 362
e1636421
LP
363 r = sd_bus_call_method(
364 bus,
2087a7af
LP
365 "org.freedesktop.locale1",
366 "/org/freedesktop/locale1",
367 "org.freedesktop.locale1",
368 "SetX11Keyboard",
4d7859d1 369 &error,
2087a7af 370 NULL,
4d7859d1
KS
371 "ssssbb", layout, model, variant, options,
372 arg_convert, arg_ask_password);
e1636421
LP
373 if (r < 0)
374 log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
375
376 return r;
2087a7af
LP
377}
378
4d7859d1 379static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) {
50cfc579 380 _cleanup_fclose_ FILE *f = NULL;
7fd1b19b 381 _cleanup_strv_free_ char **list = NULL;
50cfc579
LP
382 char line[LINE_MAX];
383 enum {
384 NONE,
385 MODELS,
386 LAYOUTS,
387 VARIANTS,
388 OPTIONS
389 } state = NONE, look_for;
390 int r;
391
392 if (n > 2) {
393 log_error("Too many arguments.");
394 return -EINVAL;
395 }
396
c62e11ce 397 f = fopen("/usr/share/X11/xkb/rules/base.lst", "re");
4a62c710
MS
398 if (!f)
399 return log_error_errno(errno, "Failed to open keyboard mapping list. %m");
50cfc579
LP
400
401 if (streq(args[0], "list-x11-keymap-models"))
402 look_for = MODELS;
403 else if (streq(args[0], "list-x11-keymap-layouts"))
404 look_for = LAYOUTS;
405 else if (streq(args[0], "list-x11-keymap-variants"))
406 look_for = VARIANTS;
407 else if (streq(args[0], "list-x11-keymap-options"))
408 look_for = OPTIONS;
409 else
410 assert_not_reached("Wrong parameter");
411
412 FOREACH_LINE(line, f, break) {
413 char *l, *w;
414
415 l = strstrip(line);
416
417 if (isempty(l))
418 continue;
419
420 if (l[0] == '!') {
421 if (startswith(l, "! model"))
422 state = MODELS;
423 else if (startswith(l, "! layout"))
424 state = LAYOUTS;
425 else if (startswith(l, "! variant"))
426 state = VARIANTS;
427 else if (startswith(l, "! option"))
428 state = OPTIONS;
429 else
430 state = NONE;
431
432 continue;
433 }
434
435 if (state != look_for)
436 continue;
437
438 w = l + strcspn(l, WHITESPACE);
439
440 if (n > 1) {
441 char *e;
442
443 if (*w == 0)
444 continue;
445
446 *w = 0;
447 w++;
448 w += strspn(w, WHITESPACE);
449
450 e = strchr(w, ':');
451 if (!e)
452 continue;
453
454 *e = 0;
455
456 if (!streq(w, args[1]))
457 continue;
458 } else
459 *w = 0;
460
7d6884b6
TA
461 r = strv_extend(&list, l);
462 if (r < 0)
463 return log_oom();
50cfc579
LP
464 }
465
466 if (strv_isempty(list)) {
467 log_error("Couldn't find any entries.");
468 return -ENOENT;
469 }
470
471 strv_sort(list);
472 strv_uniq(list);
473
ea4b98e6 474 pager_open(arg_no_pager, false);
50cfc579
LP
475
476 strv_print(list);
477 return 0;
478}
479
601185b4 480static void help(void) {
2087a7af 481 printf("%s [OPTIONS...] COMMAND ...\n\n"
82c1d8f4 482 "Query or change system locale and keyboard settings.\n\n"
50cfc579
LP
483 " -h --help Show this help\n"
484 " --version Show package version\n"
50cfc579
LP
485 " --no-pager Do not pipe output into a pager\n"
486 " --no-ask-password Do not prompt for password\n"
4d7859d1 487 " -H --host=[USER@]HOST Operate on remote host\n"
a86a47ce
LP
488 " -M --machine=CONTAINER Operate on local container\n"
489 " --no-convert Don't convert keyboard mappings\n\n"
2087a7af 490 "Commands:\n"
50cfc579
LP
491 " status Show current locale settings\n"
492 " set-locale LOCALE... Set system locale\n"
493 " list-locales Show known locales\n"
2ebcf936 494 " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
50cfc579 495 " list-keymaps Show known virtual console keyboard mappings\n"
31cf921a 496 " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
2ebcf936 497 " Set X11 and console keyboard mappings\n"
50cfc579
LP
498 " list-x11-keymap-models Show known X11 keyboard mapping models\n"
499 " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
500 " list-x11-keymap-variants [LAYOUT]\n"
501 " Show known X11 keyboard mapping variants\n"
601185b4
ZJS
502 " list-x11-keymap-options Show known X11 keyboard mapping options\n"
503 , program_invocation_short_name);
2087a7af
LP
504}
505
506static int parse_argv(int argc, char *argv[]) {
507
508 enum {
509 ARG_VERSION = 0x100,
510 ARG_NO_PAGER,
511 ARG_NO_CONVERT,
512 ARG_NO_ASK_PASSWORD
513 };
514
515 static const struct option options[] = {
4d7859d1
KS
516 { "help", no_argument, NULL, 'h' },
517 { "version", no_argument, NULL, ARG_VERSION },
518 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
519 { "host", required_argument, NULL, 'H' },
520 { "machine", required_argument, NULL, 'M' },
521 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
522 { "no-convert", no_argument, NULL, ARG_NO_CONVERT },
eb9da376 523 {}
2087a7af
LP
524 };
525
526 int c;
527
528 assert(argc >= 0);
529 assert(argv);
530
601185b4 531 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
2087a7af
LP
532
533 switch (c) {
534
535 case 'h':
601185b4
ZJS
536 help();
537 return 0;
2087a7af
LP
538
539 case ARG_VERSION:
3f6fd1ba 540 return version();
2087a7af 541
2087a7af
LP
542 case ARG_NO_CONVERT:
543 arg_convert = false;
544 break;
545
546 case ARG_NO_PAGER:
547 arg_no_pager = true;
548 break;
549
546158bc
JJ
550 case ARG_NO_ASK_PASSWORD:
551 arg_ask_password = false;
552 break;
553
4d7859d1
KS
554 case 'H':
555 arg_transport = BUS_TRANSPORT_REMOTE;
556 arg_host = optarg;
557 break;
558
559 case 'M':
de33fc62 560 arg_transport = BUS_TRANSPORT_MACHINE;
4d7859d1
KS
561 arg_host = optarg;
562 break;
563
2087a7af
LP
564 case '?':
565 return -EINVAL;
566
567 default:
eb9da376 568 assert_not_reached("Unhandled option");
2087a7af 569 }
2087a7af
LP
570
571 return 1;
572}
573
4d7859d1 574static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
2087a7af
LP
575
576 static const struct {
577 const char* verb;
578 const enum {
579 MORE,
580 LESS,
581 EQUAL
582 } argc_cmp;
583 const int argc;
4d7859d1 584 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
2087a7af 585 } verbs[] = {
50cfc579
LP
586 { "status", LESS, 1, show_status },
587 { "set-locale", MORE, 2, set_locale },
588 { "list-locales", EQUAL, 1, list_locales },
589 { "set-keymap", MORE, 2, set_vconsole_keymap },
590 { "list-keymaps", EQUAL, 1, list_vconsole_keymaps },
591 { "set-x11-keymap", MORE, 2, set_x11_keymap },
592 { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps },
593 { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps },
594 { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps },
595 { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps },
2087a7af
LP
596 };
597
598 int left;
599 unsigned i;
600
601 assert(argc >= 0);
602 assert(argv);
2087a7af
LP
603
604 left = argc - optind;
605
606 if (left <= 0)
607 /* Special rule: no arguments means "status" */
608 i = 0;
609 else {
610 if (streq(argv[optind], "help")) {
611 help();
612 return 0;
613 }
614
615 for (i = 0; i < ELEMENTSOF(verbs); i++)
616 if (streq(argv[optind], verbs[i].verb))
617 break;
618
619 if (i >= ELEMENTSOF(verbs)) {
620 log_error("Unknown operation %s", argv[optind]);
621 return -EINVAL;
622 }
623 }
624
625 switch (verbs[i].argc_cmp) {
626
627 case EQUAL:
628 if (left != verbs[i].argc) {
629 log_error("Invalid number of arguments.");
630 return -EINVAL;
631 }
632
633 break;
634
635 case MORE:
636 if (left < verbs[i].argc) {
637 log_error("Too few arguments.");
638 return -EINVAL;
639 }
640
641 break;
642
643 case LESS:
644 if (left > verbs[i].argc) {
645 log_error("Too many arguments.");
646 return -EINVAL;
647 }
648
649 break;
650
651 default:
652 assert_not_reached("Unknown comparison operator.");
653 }
654
2087a7af
LP
655 return verbs[i].dispatch(bus, argv + optind, left);
656}
657
4d7859d1 658int main(int argc, char*argv[]) {
4afd3348 659 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
84f6181c 660 int r;
2087a7af 661
a9cdc94f 662 setlocale(LC_ALL, "");
2087a7af
LP
663 log_parse_environment();
664 log_open();
665
666 r = parse_argv(argc, argv);
84f6181c 667 if (r <= 0)
2087a7af 668 goto finish;
2087a7af 669
266f3e26 670 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
4d7859d1 671 if (r < 0) {
da927ba9 672 log_error_errno(r, "Failed to create bus connection: %m");
4d7859d1 673 goto finish;
2087a7af
LP
674 }
675
4d7859d1 676 r = localectl_main(bus, argc, argv);
2087a7af 677
4d7859d1 678finish:
2087a7af
LP
679 pager_close();
680
84f6181c 681 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2087a7af 682}