X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flocale%2Flocalectl.c;h=cd7356a1e13f8d8612ccaba1e270c0441999b70f;hp=7d3ac0ad2fe52dd437b518d367ef1a0375b56a93;hb=7085053a437456ab87d726f3697002dd811fdf7a;hpb=7ca7021a9e0c443d40d0af5e9a7e1962d8032229 diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 7d3ac0ad2..cd7356a1e 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -19,6 +19,7 @@ along with systemd; If not, see . ***/ +#include #include #include #include @@ -36,6 +37,7 @@ #include "pager.h" #include "set.h" #include "path-util.h" +#include "utf8.h" static bool arg_no_pager = false; static enum transport { @@ -44,7 +46,8 @@ static enum transport { TRANSPORT_POLKIT } arg_transport = TRANSPORT_NORMAL; static bool arg_ask_password = true; -static const char *arg_host = NULL; +static char *arg_host = NULL; +static char *arg_user = NULL; static bool arg_convert = true; static void pager_open_if_enabled(void) { @@ -52,7 +55,7 @@ static void pager_open_if_enabled(void) { if (arg_no_pager) return; - pager_open(); + pager_open(false); } static void polkit_agent_open_if_enabled(void) { @@ -157,7 +160,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) { const char *interface = ""; int r; DBusMessageIter iter, sub, sub2, sub3; - StatusInfo info; + StatusInfo info = {}; assert(args); @@ -181,7 +184,6 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) { return -EIO; } - zero(info); dbus_message_iter_recurse(&iter, &sub); while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { @@ -222,7 +224,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) { static int set_locale(DBusConnection *bus, char **args, unsigned n) { _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; - dbus_bool_t interactive = true; + dbus_bool_t interactive = arg_ask_password; DBusError error; DBusMessageIter iter; int r; @@ -265,7 +267,7 @@ finish: return r; } -static int list_locales(DBusConnection *bus, char **args, unsigned n) { +static int add_locales_from_archive(Set *locales) { /* Stolen from glibc... */ struct locarhead { @@ -303,21 +305,15 @@ static int list_locales(DBusConnection *bus, char **args, unsigned n) { const struct namehashent *e; const void *p = MAP_FAILED; _cleanup_close_ int fd = -1; - _cleanup_strv_free_ char **l = NULL; - char **j; - Set *locales; size_t sz = 0; struct stat st; unsigned i; int r; - locales = set_new(string_hash_func, string_compare_func); - if (!locales) - return log_oom(); - fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) { - log_error("Failed to open locale archive: %m"); + if (errno != ENOENT) + log_error("Failed to open locale archive: %m"); r = -errno; goto finish; } @@ -365,50 +361,106 @@ static int list_locales(DBusConnection *bus, char **args, unsigned n) { 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_put(locales, z); + r = set_consume(locales, z); if (r < 0) { - free(z); log_error("Failed to add locale: %s", strerror(-r)); goto finish; } } - l = set_get_strv(locales); - if (!l) { - r = log_oom(); - goto finish; + r = 0; + + finish: + if (p != MAP_FAILED) + munmap((void*) p, sz); + + return r; +} + +static int add_locales_from_libdir (Set *locales) { + _cleanup_closedir_ DIR *dir; + struct dirent *entry; + int r; + + dir = opendir("/usr/lib/locale"); + if (!dir) { + log_error("Failed to open locale directory: %m"); + return -errno; } - set_free(locales); - locales = NULL; + errno = 0; + while ((entry = readdir(dir))) { + char *z; - strv_sort(l); + if (entry->d_type != DT_DIR) + continue; - pager_open_if_enabled(); + if (ignore_file(entry->d_name)) + continue; - STRV_FOREACH(j, l) - puts(*j); + z = strdup(entry->d_name); + if (!z) + return log_oom(); - r = 0; + r = set_consume(locales, z); + if (r < 0 && r != -EEXIST) { + log_error("Failed to add locale: %s", strerror(-r)); + return r; + } -finish: - if (p != MAP_FAILED) - munmap((void*) p, sz); + errno = 0; + } - set_free_free(locales); + if (errno > 0) { + log_error("Failed to read locale directory: %m"); + return -errno; + } - return r; + return 0; +} + +static int list_locales(DBusConnection *bus, char **args, unsigned n) { + _cleanup_set_free_ Set *locales; + _cleanup_strv_free_ char **l = NULL; + int r; + + locales = set_new(string_hash_func, string_compare_func); + if (!locales) + return log_oom(); + + r = add_locales_from_archive(locales); + if (r < 0 && r != -ENOENT) + return r; + + r = add_locales_from_libdir(locales); + if (r < 0) + return r; + + l = set_get_strv(locales); + if (!l) + return log_oom(); + + strv_sort(l); + + pager_open_if_enabled(); + + strv_print(l); + + return 0; } static int set_vconsole_keymap(DBusConnection *bus, char **args, unsigned n) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - dbus_bool_t interactive = true, b; + dbus_bool_t interactive = arg_ask_password, b; const char *map, *toggle_map; assert(bus); @@ -470,12 +522,9 @@ static int nftw_cb( if (e) *e = 0; - r = set_put(keymaps, p); - if (r == -EEXIST) - free(p); - else if (r < 0) { + r = set_consume(keymaps, p); + if (r < 0 && r != -EEXIST) { log_error("Can't add keymap: %s", strerror(-r)); - free(p); return r; } @@ -483,8 +532,7 @@ static int nftw_cb( } static int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) { - char _cleanup_strv_free_ **l = NULL; - char **i; + _cleanup_strv_free_ char **l = NULL; keymaps = set_new(string_hash_func, string_compare_func); if (!keymaps) @@ -511,16 +559,14 @@ static int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) { pager_open_if_enabled(); - STRV_FOREACH(i, l) - puts(*i); - + strv_print(l); return 0; } static int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - dbus_bool_t interactive = true, b; + dbus_bool_t interactive = arg_ask_password, b; const char *layout, *model, *variant, *options; assert(bus); @@ -536,7 +582,7 @@ static int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) { layout = args[1]; model = n > 2 ? args[2] : ""; variant = n > 3 ? args[3] : ""; - options = n > 3 ? args[4] : ""; + options = n > 4 ? args[4] : ""; b = arg_convert; return bus_method_call_with_reply( @@ -556,24 +602,133 @@ static int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) { DBUS_TYPE_INVALID); } +static int list_x11_keymaps(DBusConnection *bus, char **args, unsigned n) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **list = NULL; + char line[LINE_MAX]; + enum { + NONE, + MODELS, + LAYOUTS, + VARIANTS, + OPTIONS + } state = NONE, look_for; + int r; + + if (n > 2) { + log_error("Too many arguments."); + return -EINVAL; + } + + f = fopen("/usr/share/X11/xkb/rules/base.lst", "re"); + if (!f) { + log_error("Failed to open keyboard mapping list. %m"); + return -errno; + } + + if (streq(args[0], "list-x11-keymap-models")) + look_for = MODELS; + else if (streq(args[0], "list-x11-keymap-layouts")) + look_for = LAYOUTS; + else if (streq(args[0], "list-x11-keymap-variants")) + look_for = VARIANTS; + else if (streq(args[0], "list-x11-keymap-options")) + look_for = OPTIONS; + else + assert_not_reached("Wrong parameter"); + + FOREACH_LINE(line, f, break) { + char *l, *w; + + l = strstrip(line); + + if (isempty(l)) + continue; + + if (l[0] == '!') { + if (startswith(l, "! model")) + state = MODELS; + else if (startswith(l, "! layout")) + state = LAYOUTS; + else if (startswith(l, "! variant")) + state = VARIANTS; + else if (startswith(l, "! option")) + state = OPTIONS; + else + state = NONE; + + continue; + } + + if (state != look_for) + 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; + + if (!streq(w, args[1])) + 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; + } + + strv_sort(list); + strv_uniq(list); + + pager_open_if_enabled(); + + strv_print(list); + return 0; +} + static int help(void) { printf("%s [OPTIONS...] COMMAND ...\n\n" - "Query or change system time and date 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\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" + " -P --privileged Acquire privileges before execution\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\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" + " 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", + " 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", program_invocation_short_name); return 0; @@ -604,7 +759,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "has:H:P", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) { switch (c) { @@ -614,7 +769,6 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: puts(PACKAGE_STRING); - puts(DISTRIBUTION); puts(SYSTEMD_FEATURES); return 0; @@ -624,7 +778,7 @@ static int parse_argv(int argc, char *argv[]) { case 'H': arg_transport = TRANSPORT_SSH; - arg_host = optarg; + parse_user_at_host(optarg, &arg_user, &arg_host); break; case ARG_NO_CONVERT: @@ -635,6 +789,10 @@ static int parse_argv(int argc, char *argv[]) { arg_no_pager = true; break; + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + case '?': return -EINVAL; @@ -659,12 +817,16 @@ static int localectl_main(DBusConnection *bus, int argc, char *argv[], DBusError const int argc; int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); } verbs[] = { - { "status", LESS, 1, show_status }, - { "set-locale", MORE, 2, set_locale }, - { "list-locales", EQUAL, 1, list_locales }, - { "set-keymap", MORE, 2, set_vconsole_keymap }, - { "list-keymaps", EQUAL, 1, list_vconsole_keymaps }, - { "set-x11-keymap", MORE, 2, set_x11_keymap }, + { "status", LESS, 1, show_status }, + { "set-locale", MORE, 2, set_locale }, + { "list-locales", EQUAL, 1, list_locales }, + { "set-keymap", MORE, 2, set_vconsole_keymap }, + { "list-keymaps", EQUAL, 1, list_vconsole_keymaps }, + { "set-x11-keymap", MORE, 2, set_x11_keymap }, + { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps }, + { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps }, + { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps }, + { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps }, }; int left; @@ -740,6 +902,7 @@ int main(int argc, char *argv[]) { dbus_error_init(&error); + setlocale(LC_ALL, ""); log_parse_environment(); log_open();