localectl.c revision e1636421f46db6d06fbd028ef20a3113fa3e11f8
/*-*- 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"
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;
}
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_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))
}
char type;
const char *contents;
int r;
if (r < 0) {
return r;
}
switch (type) {
case SD_BUS_TYPE_STRING: {
const char *s;
if (isempty(s))
break;
i->vconsole_keymap = s;
i->vconsole_keymap_toggle = s;
i->x11_layout = s;
i->x11_model = s;
i->x11_variant = s;
i->x11_options = s;
break;
}
case SD_BUS_TYPE_ARRAY: {
_cleanup_strv_free_ char **l = NULL;
break;
break;
r = bus_message_read_strv_extend(property, &l);
if (r < 0)
break;
i->locale = l;
l = NULL;
break;
}
}
return r;
}
int r;
StatusInfo info = {};
r = sd_bus_call_method( bus,
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
"org.freedesktop.DBus.Properties",
"GetAll",
&error,
&reply,
"s", "");
if (r < 0) {
return r;
}
if (r < 0)
goto fail;
const char *name;
const char *contents;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (r < 0) {
log_error("Failed to parse reply.");
return r;
}
if (r < 0)
goto fail;
if (r < 0)
goto fail;
}
fail:
return r;
}
int r;
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"SetLocale", &m);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0) {
return r;
}
return 0;
}
/* Stolen from glibc... */
struct locarhead {
/* Serial number. */
/* Name hash table. */
/* String table. */
/* Table with locale records. */
/* MD5 sum hash table. */
};
struct namehashent {
/* Hash value of the name. */
/* Offset of the name in the string table. */
/* Offset of the locale record. */
};
const struct locarhead *h;
const struct namehashent *e;
const void *p = MAP_FAILED;
unsigned i;
int r;
if (fd < 0) {
log_error("Failed to open locale archive: %m");
r = -errno;
goto finish;
}
log_error("fstat() failed: %m");
r = -errno;
goto finish;
}
log_error("Archive file is not regular");
r = -EBADMSG;
goto finish;
}
log_error("Archive has invalid size");
r = -EBADMSG;
goto finish;
}
if (p == MAP_FAILED) {
log_error("Failed to map archive: %m");
r = -errno;
goto finish;
}
h = (const struct locarhead *) p;
if (h->magic != 0xde020109 ||
log_error("Invalid archive file.");
r = -EBADMSG;
goto finish;
}
for (i = 0; i < h->namehash_size; i++) {
char *z;
if (e[i].locrec_offset == 0)
continue;
if (!utf8_is_valid((char*) p + e[i].name_offset))
continue;
z = strdup((char*) p + e[i].name_offset);
if (!z) {
r = log_oom();
goto finish;
}
r = set_consume(locales, z);
if (r < 0) {
goto finish;
}
}
r = 0;
if (p != MAP_FAILED)
return r;
}
int r;
if (!dir) {
log_error("Failed to open locale directory: %m");
return -errno;
}
errno = 0;
char *z;
continue;
continue;
if (!z)
return log_oom();
r = set_consume(locales, z);
if (r < 0 && r != -EEXIST) {
return r;
}
errno = 0;
}
if (errno > 0) {
log_error("Failed to read locale directory: %m");
return -errno;
}
return 0;
}
_cleanup_strv_free_ char **l = NULL;
int r;
if (!locales)
return log_oom();
if (r < 0 && r != -ENOENT)
return r;
if (r < 0)
return r;
l = set_get_strv(locales);
if (!l)
return log_oom();
strv_sort(l);
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;
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 int 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-convert Don't convert keyboard mappings\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\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",
return 0;
}
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:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
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, ret = EXIT_FAILURE;
log_open();
if (r < 0)
goto finish;
else if (r == 0) {
ret = EXIT_SUCCESS;
goto finish;
}
if (r < 0) {
ret = EXIT_FAILURE;
goto finish;
}
ret = r < 0 ? EXIT_FAILURE : r;
pager_close();
return ret;
}