localectl.c revision 2ebcf936702e8e511098711b4add885372360018
/*-*- 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) {
log_warning_errno(r, "Failed to read /proc/cmdline: %m");
goto finish;
}
for (j = 0; j < _VARIABLE_LC_MAX; j++)
if (variables[j]) {
if (print_warning) {
log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n"
print_warning = false;
} else
}
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) {
log_error_errno(r, "Could not get properties: %m");
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 log_error_errno(r, "Failed to read list of locales: %m");
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 log_error_errno(r, "Can't add keymap: %m");
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)
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 console and X11 keyboard mappings\n"
" list-keymaps Show known virtual console keyboard mappings\n"
" set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
" Set X11 and console keyboard mappings\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) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
}
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}