localectl.c revision 6b2b6f30e38d67b032d6bdc6b47ae05e143e96c5
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright 2012 Lennart Poettering
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer systemd is free software; you can redistribute it and/or modify it
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer under the terms of the GNU Lesser General Public License as published by
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer the Free Software Foundation; either version 2.1 of the License, or
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt (at your option) any later version.
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt systemd is distributed in the hope that it will be useful, but
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt WITHOUT ANY WARRANTY; without even the implied warranty of
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4be4833ece2856e0cacc09f8f8b2c02b320751faMartin Pitt Lesser General Public License for more details.
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier You should have received a copy of the GNU Lesser General Public License
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierstatic bool arg_no_pager = false;
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyerstatic bool arg_ask_password = true;
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyerstatic bool arg_convert = true;
b6f0c419e38a960873fe68bf8f89bbb0268eed02Harald Hoyerstatic void pager_open_if_enabled(void) {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierstatic void polkit_agent_open_if_enabled(void) {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier /* Open the polkit agent as a child process if necessary */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevaliertypedef struct StatusInfo {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierstatic void print_status_info(StatusInfo *i) {
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek printf(" System Locale: %s\n", i->locale[0]);
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt printf(" VC Keymap: %s\n", strna(i->vconsole_keymap));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier printf(" X11 Layout: %s\n", strna(i->x11_layout));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier printf(" X11 Variant: %s\n", i->x11_variant);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier printf(" X11 Options: %s\n", i->x11_options);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier switch (dbus_message_iter_get_arg_type(iter)) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const char *s;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier else if (streq(name, "VConsoleKeymapToggle"))
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int show_status(DBusConnection *bus, char **args, unsigned n) {
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "org.freedesktop.locale1",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "org.freedesktop.DBus.Properties",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (!dbus_message_iter_init(reply, &iter) ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
c7eda0133b6bf13a182337cbe8a61bf2faf9b32eEvgeny Vereshchagin if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int set_locale(DBusConnection *bus, char **args, unsigned n) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "org.freedesktop.locale1",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "org.freedesktop.locale1",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier "SetLocale");
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &interactive))
7d023341c765c205068e33d23d63a4000ec211dfMartin Pitt reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error("Failed to issue method call: %s", bus_error_message(&error));
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pittstatic int list_locales(DBusConnection *bus, char **args, unsigned n) {
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt /* Stolen from glibc... */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Serial number. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Name hash table. */
0fe15dc8ddddeb39a5cad1f4f4afa25fa074a5d1Evgeny Vereshchagin /* String table. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Table with locale records. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* MD5 sum hash table. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Hash value of the name. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Offset of the name in the string table. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Offset of the locale record. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const struct locarhead *h;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const struct namehashent *e;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const void *p = MAP_FAILED;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier locales = set_new(string_hash_func, string_compare_func);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error("Failed to open locale archive: %m");
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (st.st_size < (off_t) sizeof(struct locarhead)) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer h = (const struct locarhead *) p;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer h->namehash_offset + h->namehash_size > st.st_size ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer h->string_offset + h->string_size > st.st_size ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer h->locrectab_offset + h->locrectab_size > st.st_size ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer h->sumhash_offset + h->sumhash_size > st.st_size) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer for (i = 0; i < h->namehash_size; i++) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error("Failed to add locale: %s", strerror(-r));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int set_vconsole_keymap(DBusConnection *bus, char **args, unsigned n) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (n > 3) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "org.freedesktop.locale1",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "org.freedesktop.locale1",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "SetVConsoleKeyboard",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char *p, *e;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer else if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error("Can't add keymap: %s", strerror(-r));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer keymaps = set_new(string_hash_func, string_compare_func);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer nftw("/usr/share/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer nftw("/usr/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer nftw("/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error("Couldn't find any console keymaps.");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *layout, *model, *variant, *options;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (n > 5) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "org.freedesktop.locale1",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "org.freedesktop.locale1",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "SetX11Keyboard",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int help(void) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "Query or change system time and date settings.\n\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " -h --help Show this help\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " --version Show package version\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " --no-convert Don't convert keyboard mappings\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " --no-pager Do not pipe output into a pager\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " --no-ask-password Do not prompt for password\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " -H --host=[USER@]HOST Operate on remote host\n\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "Commands:\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " status Show current locale settings\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " set-locale LOCALE... Set system locale\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " list-locales Show known locales\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " set-keymap MAP [MAP] Set virtual console keyboard mapping\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " list-keymaps Show known virtual console keyboard mappings\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer " Set X11 keyboard mapping\n",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer { "no-pager", no_argument, NULL, ARG_NO_PAGER },
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer { "no-convert", no_argument, NULL, ARG_NO_CONVERT },
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer while ((c = getopt_long(argc, argv, "has:H:P", options, NULL)) >= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer switch (c) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int localectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer static const struct {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char* verb;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const enum {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer { "set-keymap", MORE, 2, set_vconsole_keymap },
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer { "list-keymaps", EQUAL, 1, list_vconsole_keymaps },
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* Special rule: no arguments means "status" */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error("Unknown operation %s", argv[optind]);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_not_reached("Unknown comparison operator.");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error("Failed to get D-Bus connection: %s", error->message);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return verbs[i].dispatch(bus, argv + optind, left);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer else if (r == 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer bus_connect_system_ssh(NULL, arg_host, &bus, &error);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert_not_reached("Uh, invalid transport...");