localectl.c revision 63229aa1abdb98aa69fda9819ed2f40c8082762b
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
Copyright 2013 Kay Sievers
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <locale.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <ftw.h>
#include <fcntl.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "bus-error.h"
#include "bus-message.h"
#include "util.h"
#include "spawn-polkit-agent.h"
#include "build.h"
#include "strv.h"
#include "pager.h"
#include "set.h"
#include "path-util.h"
#include "utf8.h"
#include "def.h"
#include "virt.h"
#include "fileio.h"
#include "locale-util.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
static bool arg_convert = true;
static void pager_open_if_enabled(void) {
if (arg_no_pager)
return;
pager_open(false);
}
static void polkit_agent_open_if_enabled(void) {
/* Open the polkit agent as a child process if necessary */
if (!arg_ask_password)
return;
if (arg_transport != BUS_TRANSPORT_LOCAL)
return;
}
typedef struct StatusInfo {
char **locale;
const char *vconsole_keymap;
const char *vconsole_keymap_toggle;
const char *x11_layout;
const char *x11_model;
const char *x11_variant;
const char *x11_options;
} StatusInfo;
static void print_overriden_variables(void) {
int r;
char *variables[_VARIABLE_LC_MAX] = {};
bool print_warning = true;
return;
NULL);
if (r < 0 && r != -ENOENT) {
goto finish;
}
for (j = 0; j < _VARIABLE_LC_MAX; j++)
if (variables[j]) {
if (print_warning) {
printf("Warning: Settings on Kernel Command Line override system locale settings in /etc/locale.conf\n");
print_warning = false;
continue;
}
}
for (j = 0; j < _VARIABLE_LC_MAX; j++)
}
static void print_status_info(StatusInfo *i) {
assert(i);
if (strv_isempty(i->locale))
puts(" System Locale: n/a\n");
else {
char **j;
printf(" %s\n", *j);
}
if (!isempty(i->vconsole_keymap_toggle))
if (!isempty(i->x11_variant))
if (!isempty(i->x11_options))
}
StatusInfo info = {};
static const struct bus_properties_map map[] = {
{}
};
int r;
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
map,
&info);
if (r < 0) {
goto fail;
}
fail:
return r;
}
int r;
bus,
&m,
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"SetLocale");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
if (r < 0) {
return r;
}
return 0;
}
_cleanup_strv_free_ char **l = NULL;
int r;
r = get_locales(&l);
if (r < 0) {
return r;
}
strv_print(l);
return 0;
}
const char *map, *toggle_map;
int r;
if (n > 3) {
log_error("Too many arguments.");
return -EINVAL;
}
r = sd_bus_call_method(
bus,
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"SetVConsoleKeyboard",
&error,
NULL,
if (r < 0)
return r;
}
static int nftw_cb(
const char *fpath,
int tflag,
char *p, *e;
int r;
return 0;
return 0;
if (!p)
return log_oom();
e = endswith(p, ".map");
if (e)
*e = 0;
e = endswith(p, ".map.gz");
if (e)
*e = 0;
r = set_consume(keymaps, p);
if (r < 0 && r != -EEXIST) {
return r;
}
return 0;
}
_cleanup_strv_free_ char **l = NULL;
const char *dir;
if (!keymaps)
return log_oom();
l = set_get_strv(keymaps);
if (!l) {
return log_oom();
}
if (strv_isempty(l)) {
log_error("Couldn't find any console keymaps.");
return -ENOENT;
}
strv_sort(l);
strv_print(l);
return 0;
}
int r;
if (n > 5) {
log_error("Too many arguments.");
return -EINVAL;
}
r = sd_bus_call_method(
bus,
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"SetX11Keyboard",
&error,
NULL,
if (r < 0)
return r;
}
enum {
NONE,
int r;
if (n > 2) {
log_error("Too many arguments.");
return -EINVAL;
}
if (!f) {
log_error("Failed to open keyboard mapping list. %m");
return -errno;
}
else
assert_not_reached("Wrong parameter");
FOREACH_LINE(line, f, break) {
char *l, *w;
if (isempty(l))
continue;
if (l[0] == '!') {
if (startswith(l, "! model"))
else if (startswith(l, "! layout"))
else if (startswith(l, "! variant"))
else if (startswith(l, "! option"))
else
continue;
}
continue;
w = l + strcspn(l, WHITESPACE);
if (n > 1) {
char *e;
if (*w == 0)
continue;
*w = 0;
w++;
w += strspn(w, WHITESPACE);
e = strchr(w, ':');
if (!e)
continue;
*e = 0;
continue;
} else
*w = 0;
r = strv_extend(&list, l);
if (r < 0)
return log_oom();
}
if (strv_isempty(list)) {
log_error("Couldn't find any entries.");
return -ENOENT;
}
return 0;
}
static void help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system locale and keyboard settings.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-ask-password Do not prompt for password\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --no-convert Don't convert keyboard mappings\n\n"
"Commands:\n"
" status Show current locale settings\n"
" set-locale LOCALE... Set system locale\n"
" list-locales Show known locales\n"
" set-keymap MAP [MAP] Set virtual console keyboard mapping\n"
" list-keymaps Show known virtual console keyboard mappings\n"
" set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n"
" Set X11 keyboard mapping\n"
" list-x11-keymap-models Show known X11 keyboard mapping models\n"
" list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
" list-x11-keymap-variants [LAYOUT]\n"
" Show known X11 keyboard mapping variants\n"
" list-x11-keymap-options Show known X11 keyboard mapping options\n"
}
enum {
ARG_VERSION = 0x100,
};
{}
};
int c;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case ARG_NO_CONVERT:
arg_convert = false;
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case 'H':
break;
case 'M':
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
return 1;
}
static const struct {
const char* verb;
const enum {
MORE,
LESS,
} argc_cmp;
const int argc;
} verbs[] = {
};
int left;
unsigned i;
if (left <= 0)
/* Special rule: no arguments means "status" */
i = 0;
else {
help();
return 0;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)
break;
if (i >= ELEMENTSOF(verbs)) {
return -EINVAL;
}
}
case EQUAL:
log_error("Invalid number of arguments.");
return -EINVAL;
}
break;
case MORE:
log_error("Too few arguments.");
return -EINVAL;
}
break;
case LESS:
log_error("Too many arguments.");
return -EINVAL;
}
break;
default:
assert_not_reached("Unknown comparison operator.");
}
}
int r;
log_open();
if (r <= 0)
goto finish;
if (r < 0) {
goto finish;
}
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}