diff --git a/0001-systemd-firstboot-add-vconsole-keymap-support.patch b/0001-systemd-firstboot-add-vconsole-keymap-support.patch new file mode 100644 index 00000000..415d86be --- /dev/null +++ b/0001-systemd-firstboot-add-vconsole-keymap-support.patch @@ -0,0 +1,525 @@ +From 349fd7b2fe35d2fd6c7ba6493d2e44ae93013804 Mon Sep 17 00:00:00 2001 +From: Thomas Blume +Date: Wed, 18 Oct 2017 12:30:03 +0200 +Subject: [PATCH 1/1] systemd-firstboot: add vconsole keymap support + +yast installer needs systemd-firstboot support for setting the keymap + +This is a temporary patch to be superseded when upstream commit is available, +see https://github.com/systemd/systemd/pull/7035 for the relevant PR. + +[tblume: fixes bsc#1046436] +--- + src/basic/locale-util.c | 95 +++++++++++++++++++++++++++++++++++ + src/basic/locale-util.h | 3 ++ + src/firstboot/firstboot.c | 117 ++++++++++++++++++++++++++++++++++++++++++-- + src/locale/localectl.c | 65 +++--------------------- + src/test/test-locale-util.c | 29 +++++++++++ + 5 files changed, 247 insertions(+), 62 deletions(-) + +diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c +index ada0a28cd..f3fa918b8 100644 +--- a/src/basic/locale-util.c ++++ b/src/basic/locale-util.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -30,6 +31,7 @@ + #include + #include + ++#include "def.h" + #include "dirent-util.h" + #include "fd-util.h" + #include "hashmap.h" +@@ -270,6 +272,99 @@ out: + return (bool) cached_answer; + } + ++static thread_local Set *keymaps = NULL; ++ ++static int nftw_cb( ++ const char *fpath, ++ const struct stat *sb, ++ int tflag, ++ struct FTW *ftwbuf) { ++ ++ char *p, *e; ++ int r; ++ ++ if (tflag != FTW_F) ++ return 0; ++ ++ if (!endswith(fpath, ".map") && ++ !endswith(fpath, ".map.gz")) ++ return 0; ++ ++ p = strdup(basename(fpath)); ++ if (!p) ++ return FTW_STOP; ++ ++ 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; ++} ++ ++int get_keymaps(char ***ret) { ++ _cleanup_strv_free_ char **l = NULL; ++ const char *dir; ++ int r; ++ ++ keymaps = set_new(&string_hash_ops); ++ if (!keymaps) ++ return -ENOMEM; ++ ++ NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { ++ r = nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); ++ ++ if (r == FTW_STOP) ++ log_debug("Directory not found %s", dir); ++ else if (r < 0) ++ log_debug_errno(r, "Can't add keymap: %m"); ++ } ++ ++ l = set_get_strv(keymaps); ++ if (!l) { ++ set_free_free(keymaps); ++ return -ENOMEM; ++ } ++ ++ set_free(keymaps); ++ ++ if (strv_isempty(l)) ++ return -ENOENT; ++ ++ strv_sort(l); ++ ++ *ret = l; ++ l = NULL; ++ ++ return 0; ++} ++ ++bool keymap_is_valid(const char *name) { ++ ++ if (isempty(name)) ++ return false; ++ ++ if (strlen(name) >= 128) ++ return false; ++ ++ if (!utf8_is_valid(name)) ++ return false; ++ ++ if (!filename_is_valid(name)) ++ return false; ++ ++ if (!string_is_safe(name)) ++ return false; ++ ++ return true; ++} + + const char *special_glyph(SpecialGlyph code) { + +diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h +index 0630a034a..104864501 100644 +--- a/src/basic/locale-util.h ++++ b/src/basic/locale-util.h +@@ -71,3 +71,6 @@ const char *special_glyph(SpecialGlyph code) _const_; + + const char* locale_variable_to_string(LocaleVariable i) _const_; + LocaleVariable locale_variable_from_string(const char *s) _pure_; ++ ++int get_keymaps(char ***l); ++bool keymap_is_valid(const char *name); +diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c +index b3578d3e1..fd60ee518 100644 +--- a/src/firstboot/firstboot.c ++++ b/src/firstboot/firstboot.c +@@ -44,16 +44,19 @@ + + static char *arg_root = NULL; + static char *arg_locale = NULL; /* $LANG */ ++static char *arg_keymap = NULL; + static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ + static char *arg_timezone = NULL; + static char *arg_hostname = NULL; + static sd_id128_t arg_machine_id = {}; + static char *arg_root_password = NULL; + static bool arg_prompt_locale = false; ++static bool arg_prompt_keymap = false; + static bool arg_prompt_timezone = false; + static bool arg_prompt_hostname = false; + static bool arg_prompt_root_password = false; + static bool arg_copy_locale = false; ++static bool arg_copy_keymap = false; + static bool arg_copy_timezone = false; + static bool arg_copy_root_password = false; + +@@ -285,6 +288,80 @@ static int process_locale(void) { + return 0; + } + ++static int prompt_keymap(void) { ++ _cleanup_strv_free_ char **kmaps = NULL; ++ int r; ++ ++ if (arg_keymap) ++ return 0; ++ ++ if (!arg_prompt_keymap) ++ return 0; ++ ++ r = get_keymaps(&kmaps); ++ if (r < 0) ++ return log_error_errno(r, "Cannot query keymap list: %m"); ++ ++ print_welcome(); ++ ++ printf("\nAvailable keymaps:\n\n"); ++ r = show_menu(kmaps, 3, 22, 60); ++ if (r < 0) ++ return r; ++ ++ putchar('\n'); ++ ++ r = prompt_loop("Please enter system keymap name or number", kmaps, keymap_is_valid, &arg_keymap); ++ if (r < 0) ++ return r; ++ ++ if (isempty(arg_keymap)) ++ return 0; ++ ++ return 0; ++} ++ ++static int process_keymap(void) { ++ const char *etc_vconsoleconf; ++ char **keymap; ++ int r; ++ ++ etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf"); ++ if (laccess(etc_vconsoleconf, F_OK) >= 0) ++ return 0; ++ ++ if (arg_copy_keymap && arg_root) { ++ ++ mkdir_parents(etc_vconsoleconf, 0755); ++ r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, COPY_REFLINK); ++ if (r != -ENOENT) { ++ if (r < 0) ++ return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf); ++ ++ log_info("%s copied.", etc_vconsoleconf); ++ return 0; ++ } ++ } ++ ++ r = prompt_keymap(); ++ if (r < 0) ++ return r; ++ ++ if (!isempty(arg_keymap)) ++ keymap = STRV_MAKE(strjoina("KEYMAP=", arg_keymap)); ++ ++ if (!keymap) ++ return 0; ++ ++ mkdir_parents(etc_vconsoleconf, 0755); ++ r = write_env_file(etc_vconsoleconf, keymap); ++ if (r < 0) ++ return log_error_errno(r, "Failed to write %s: %m", etc_vconsoleconf); ++ ++ log_info("%s written.", etc_vconsoleconf); ++ return 0; ++} ++ + static int prompt_timezone(void) { + _cleanup_strv_free_ char **zones = NULL; + int r; +@@ -611,20 +688,23 @@ static void help(void) { + " --root=PATH Operate on an alternate filesystem root\n" + " --locale=LOCALE Set primary locale (LANG=)\n" + " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" ++ " --keymap=KEYMAP Set keymap\n" + " --timezone=TIMEZONE Set timezone\n" + " --hostname=NAME Set host name\n" + " --machine-ID=ID Set machine ID\n" + " --root-password=PASSWORD Set root password\n" + " --root-password-file=FILE Set root password from file\n" + " --prompt-locale Prompt the user for locale settings\n" ++ " --prompt-keymap Prompt the user for keymap settings\n" + " --prompt-timezone Prompt the user for timezone\n" + " --prompt-hostname Prompt the user for hostname\n" + " --prompt-root-password Prompt the user for root password\n" + " --prompt Prompt for all of the above\n" + " --copy-locale Copy locale from host\n" ++ " --copy-keymap Copy keymap from host\n" + " --copy-timezone Copy timezone from host\n" + " --copy-root-password Copy root password from host\n" +- " --copy Copy locale, timezone, root password\n" ++ " --copy Copy locale, keymap, timezone, root password\n" + " --setup-machine-id Generate a new random machine ID\n" + , program_invocation_short_name); + } +@@ -636,6 +716,7 @@ static int parse_argv(int argc, char *argv[]) { + ARG_ROOT, + ARG_LOCALE, + ARG_LOCALE_MESSAGES, ++ ARG_KEYMAP, + ARG_TIMEZONE, + ARG_HOSTNAME, + ARG_MACHINE_ID, +@@ -643,11 +724,13 @@ static int parse_argv(int argc, char *argv[]) { + ARG_ROOT_PASSWORD_FILE, + ARG_PROMPT, + ARG_PROMPT_LOCALE, ++ ARG_PROMPT_KEYMAP, + ARG_PROMPT_TIMEZONE, + ARG_PROMPT_HOSTNAME, + ARG_PROMPT_ROOT_PASSWORD, + ARG_COPY, + ARG_COPY_LOCALE, ++ ARG_COPY_KEYMAP, + ARG_COPY_TIMEZONE, + ARG_COPY_ROOT_PASSWORD, + ARG_SETUP_MACHINE_ID, +@@ -659,6 +742,7 @@ static int parse_argv(int argc, char *argv[]) { + { "root", required_argument, NULL, ARG_ROOT }, + { "locale", required_argument, NULL, ARG_LOCALE }, + { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, ++ { "keymap", required_argument, NULL, ARG_KEYMAP }, + { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, +@@ -666,11 +750,13 @@ static int parse_argv(int argc, char *argv[]) { + { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, + { "prompt", no_argument, NULL, ARG_PROMPT }, + { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, ++ { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, + { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, + { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, + { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, + { "copy", no_argument, NULL, ARG_COPY }, + { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, ++ { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, + { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, + { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, + { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, +@@ -723,6 +809,18 @@ static int parse_argv(int argc, char *argv[]) { + + break; + ++ case ARG_KEYMAP: ++ if (!keymap_is_valid(optarg)) { ++ log_error("Keymap %s is not valid.", optarg); ++ return -EINVAL; ++ } ++ ++ r = free_and_strdup(&arg_keymap, optarg); ++ if (r < 0) ++ return log_oom(); ++ ++ break; ++ + case ARG_TIMEZONE: + if (!timezone_is_valid(optarg)) { + log_error("Timezone %s is not valid.", optarg); +@@ -772,13 +870,17 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_PROMPT: +- arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; ++ arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; + break; + + case ARG_PROMPT_LOCALE: + arg_prompt_locale = true; + break; + ++ case ARG_PROMPT_KEYMAP: ++ arg_prompt_keymap = true; ++ break; ++ + case ARG_PROMPT_TIMEZONE: + arg_prompt_timezone = true; + break; +@@ -792,13 +894,17 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_COPY: +- arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; ++ arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = true; + break; + + case ARG_COPY_LOCALE: + arg_copy_locale = true; + break; + ++ case ARG_COPY_KEYMAP: ++ arg_copy_keymap = true; ++ break; ++ + case ARG_COPY_TIMEZONE: + arg_copy_timezone = true; + break; +@@ -853,6 +959,10 @@ int main(int argc, char *argv[]) { + if (r < 0) + goto finish; + ++ r = process_keymap(); ++ if (r < 0) ++ goto finish; ++ + r = process_timezone(); + if (r < 0) + goto finish; +@@ -873,6 +983,7 @@ finish: + free(arg_root); + free(arg_locale); + free(arg_locale_messages); ++ free(arg_keymap); + free(arg_timezone); + free(arg_hostname); + string_erase(arg_root_password); +diff --git a/src/locale/localectl.c b/src/locale/localectl.c +index 0bd18a5c0..efdd73a8f 100644 +--- a/src/locale/localectl.c ++++ b/src/locale/localectl.c +@@ -273,68 +273,15 @@ static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) { + return r; + } + +-static Set *keymaps = NULL; +- +-static int nftw_cb( +- const char *fpath, +- const struct stat *sb, +- int tflag, +- struct FTW *ftwbuf) { +- +- char *p, *e; +- int r; +- +- if (tflag != FTW_F) +- return 0; +- +- if (!endswith(fpath, ".map") && +- !endswith(fpath, ".map.gz")) +- return 0; +- +- p = strdup(basename(fpath)); +- 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; +-} +- + static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) { +- _cleanup_strv_free_ char **l = NULL; +- const char *dir; +- +- keymaps = set_new(&string_hash_ops); +- if (!keymaps) +- return log_oom(); +- +- NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) +- nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS); +- +- l = set_get_strv(keymaps); +- if (!l) { +- set_free_free(keymaps); +- return log_oom(); +- } +- +- set_free(keymaps); ++ _cleanup_strv_free_ char **l = NULL; ++ int r; + +- if (strv_isempty(l)) { +- log_error("Couldn't find any console keymaps."); +- return -ENOENT; +- } ++ assert(args); + +- strv_sort(l); ++ r = get_keymaps(&l); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read list of keymaps: %m"); + + pager_open(arg_no_pager, false); + +diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c +index 427c698d1..e6876c82e 100644 +--- a/src/test/test-locale-util.c ++++ b/src/test/test-locale-util.c +@@ -50,9 +50,38 @@ static void test_locale_is_valid(void) { + assert_se(!locale_is_valid("\x01gar\x02 bage\x03")); + } + ++static void test_get_keymaps(void) { ++ _cleanup_strv_free_ char **kmaps = NULL; ++ char **p; ++ int r; ++ ++ r = get_keymaps(&kmaps); ++ assert_se(r >= 0); ++ assert_se(kmaps); ++ ++ STRV_FOREACH(p, kmaps) { ++ puts(*p); ++ assert_se(keymap_is_valid(*p)); ++ } ++} ++ ++static void test_keymap_is_valid(void) { ++ assert_se(keymap_is_valid("uk")); ++ assert_se(keymap_is_valid("de-nodeadkeys")); ++ assert_se(keymap_is_valid("ANSI-dvorak")); ++ assert_se(keymap_is_valid("unicode")); ++ ++ assert_se(!keymap_is_valid("")); ++ assert_se(!keymap_is_valid("/usr/bin/foo")); ++ assert_se(!keymap_is_valid("\x01gar\x02 bage\x03")); ++} ++ + int main(int argc, char *argv[]) { + test_get_locales(); + test_locale_is_valid(); + ++ test_get_keymaps(); ++ test_keymap_is_valid(); ++ + return 0; + } +-- +2.14.2 + diff --git a/systemd-mini.changes b/systemd-mini.changes index 1515ef89..1672a1a1 100644 --- a/systemd-mini.changes +++ b/systemd-mini.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Thu Oct 19 07:47:16 UTC 2017 - fbui@suse.com + +- Add 0001-systemd-firstboot-add-vconsole-keymap-support.patch (bsc#1046436) + + Temporary patch until it's been merged by upstream, see + https://github.com/systemd/systemd/pull/7035 + ------------------------------------------------------------------- Fri Oct 6 07:44:38 UTC 2017 - fbui@suse.com diff --git a/systemd-mini.spec b/systemd-mini.spec index 10c9941c..59abc06e 100644 --- a/systemd-mini.spec +++ b/systemd-mini.spec @@ -163,6 +163,7 @@ Source1065: udev-remount-tmpfs # patches are temporary and should be removed as soon as a fix is # merged by upstream. Patch1: 0001-core-disable-session-keyring-per-system-sevice-entir.patch +Patch2: 0001-systemd-firstboot-add-vconsole-keymap-support.patch %description Systemd is a system and service manager, compatible with SysV and LSB diff --git a/systemd.changes b/systemd.changes index 1515ef89..1672a1a1 100644 --- a/systemd.changes +++ b/systemd.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Thu Oct 19 07:47:16 UTC 2017 - fbui@suse.com + +- Add 0001-systemd-firstboot-add-vconsole-keymap-support.patch (bsc#1046436) + + Temporary patch until it's been merged by upstream, see + https://github.com/systemd/systemd/pull/7035 + ------------------------------------------------------------------- Fri Oct 6 07:44:38 UTC 2017 - fbui@suse.com diff --git a/systemd.spec b/systemd.spec index d1e982e5..493a9cc8 100644 --- a/systemd.spec +++ b/systemd.spec @@ -161,6 +161,7 @@ Source1065: udev-remount-tmpfs # patches are temporary and should be removed as soon as a fix is # merged by upstream. Patch1: 0001-core-disable-session-keyring-per-system-sevice-entir.patch +Patch2: 0001-systemd-firstboot-add-vconsole-keymap-support.patch %description Systemd is a system and service manager, compatible with SysV and LSB