diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index e742a8d0b1..a64adcd9ca 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -56,7 +56,7 @@ static const char* systemd_language_fallback_map(void) { return SYSTEMD_LANGUAGE_FALLBACK_MAP; } -static void x11_context_clear(X11Context *xc) { +void x11_context_clear(X11Context *xc) { assert(xc); xc->layout = mfree(xc->layout); @@ -65,7 +65,16 @@ static void x11_context_clear(X11Context *xc) { xc->variant = mfree(xc->variant); } -static bool x11_context_isempty(const X11Context *xc) { +void x11_context_replace(X11Context *dest, X11Context *src) { + assert(dest); + assert(src); + + x11_context_clear(dest); + *dest = *src; + *src = (X11Context) {}; +} + +bool x11_context_isempty(const X11Context *xc) { assert(xc); return @@ -152,11 +161,74 @@ static void context_clear_x11(Context *c) { x11_context_clear(&c->x11_from_vc); } -static void context_clear_vconsole(Context *c) { - assert(c); +void vc_context_clear(VCContext *vc) { + assert(vc); - c->vc_keymap = mfree(c->vc_keymap); - c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); + vc->keymap = mfree(vc->keymap); + vc->toggle = mfree(vc->toggle); +} + +void vc_context_replace(VCContext *dest, VCContext *src) { + assert(dest); + assert(src); + + vc_context_clear(dest); + *dest = *src; + *src = (VCContext) {}; +} + +bool vc_context_isempty(const VCContext *vc) { + assert(vc); + + return + isempty(vc->keymap) && + isempty(vc->toggle); +} + +void vc_context_empty_to_null(VCContext *vc) { + assert(vc); + + /* Do not call vc_context_clear() for the passed object. */ + + vc->keymap = empty_to_null(vc->keymap); + vc->toggle = empty_to_null(vc->toggle); +} + +bool vc_context_equal(const VCContext *a, const VCContext *b) { + assert(a); + assert(b); + + return + streq_ptr(a->keymap, b->keymap) && + streq_ptr(a->toggle, b->toggle); +} + +int vc_context_copy(VCContext *dest, const VCContext *src) { + bool modified; + int r; + + assert(dest); + + if (dest == src) + return 0; + + if (!src) { + modified = !vc_context_isempty(dest); + vc_context_clear(dest); + return modified; + } + + r = free_and_strdup(&dest->keymap, src->keymap); + if (r < 0) + return r; + modified = r > 0; + + r = free_and_strdup(&dest->toggle, src->toggle); + if (r < 0) + return r; + modified = modified || r > 0; + + return modified; } void context_clear(Context *c) { @@ -164,7 +236,7 @@ void context_clear(Context *c) { locale_context_clear(&c->locale_context); context_clear_x11(c); - context_clear_vconsole(c); + vc_context_clear(&c->vc); c->locale_cache = sd_bus_message_unref(c->locale_cache); c->x11_cache = sd_bus_message_unref(c->x11_cache); @@ -173,7 +245,7 @@ void context_clear(Context *c) { c->polkit_registry = bus_verify_polkit_async_registry_free(c->polkit_registry); }; -static X11Context *context_get_x11_context(Context *c) { +X11Context *context_get_x11_context(Context *c) { assert(c); if (!x11_context_isempty(&c->x11_from_vc)) @@ -182,12 +254,7 @@ static X11Context *context_get_x11_context(Context *c) { if (!x11_context_isempty(&c->x11_from_xorg)) return &c->x11_from_xorg; - return NULL; -} - -X11Context *context_get_x11_context_safe(Context *c) { - assert(c); - return context_get_x11_context(c) ?: &c->x11_from_vc; + return &c->x11_from_vc; } int locale_read_data(Context *c, sd_bus_message *m) { @@ -223,7 +290,7 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { fd = RET_NERRNO(open("/etc/vconsole.conf", O_CLOEXEC | O_PATH)); if (fd == -ENOENT) { c->vc_stat = (struct stat) {}; - context_clear_vconsole(c); + vc_context_clear(&c->vc); return 0; } if (fd < 0) @@ -237,12 +304,12 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { return 0; c->vc_stat = st; - context_clear_vconsole(c); + vc_context_clear(&c->vc); x11_context_clear(&c->x11_from_vc); return parse_env_file_fd(fd, "/etc/vconsole.conf", - "KEYMAP", &c->vc_keymap, - "KEYMAP_TOGGLE", &c->vc_keymap_toggle, + "KEYMAP", &c->vc.keymap, + "KEYMAP_TOGGLE", &c->vc.toggle, "XKBLAYOUT", &c->x11_from_vc.layout, "XKBMODEL", &c->x11_from_vc.model, "XKBVARIANT", &c->x11_from_vc.variant, @@ -258,15 +325,6 @@ int x11_read_data(Context *c, sd_bus_message *m) { assert(c); - r = vconsole_read_data(c, m); - if (r < 0) - return r; - - if (!x11_context_isempty(&c->x11_from_vc)) { - log_debug("XKB settings loaded from vconsole.conf, not reading xorg.conf.d/00-keyboard.conf."); - return 0; - } - /* Do not try to re-read the file within single bus operation. */ if (m) { if (m == c->x11_cache) @@ -356,9 +414,6 @@ int x11_read_data(Context *c, sd_bus_message *m) { in_section = false; } - if (!x11_context_isempty(&c->x11_from_xorg)) - log_debug("XKB settings loaded from xorg.conf.d/00-keyboard.conf."); - return 0; } @@ -371,36 +426,31 @@ int vconsole_write_data(Context *c) { xc = context_get_x11_context(c); - /* If the X11 context is from xorg.conf, then sync one from vconsole.conf with it. */ - r = x11_context_copy(&c->x11_from_vc, xc); - if (r < 0) - return r; - r = load_env_file(NULL, "/etc/vconsole.conf", &l); if (r < 0 && r != -ENOENT) return r; - r = strv_env_assign(&l, "KEYMAP", empty_to_null(c->vc_keymap)); + r = strv_env_assign(&l, "KEYMAP", empty_to_null(c->vc.keymap)); if (r < 0) return r; - r = strv_env_assign(&l, "KEYMAP_TOGGLE", empty_to_null(c->vc_keymap_toggle)); + r = strv_env_assign(&l, "KEYMAP_TOGGLE", empty_to_null(c->vc.toggle)); if (r < 0) return r; - r = strv_env_assign(&l, "XKBLAYOUT", xc ? empty_to_null(xc->layout) : NULL); + r = strv_env_assign(&l, "XKBLAYOUT", empty_to_null(xc->layout)); if (r < 0) return r; - r = strv_env_assign(&l, "XKBMODEL", xc ? empty_to_null(xc->model) : NULL); + r = strv_env_assign(&l, "XKBMODEL", empty_to_null(xc->model)); if (r < 0) return r; - r = strv_env_assign(&l, "XKBVARIANT", xc ? empty_to_null(xc->variant) : NULL); + r = strv_env_assign(&l, "XKBVARIANT", empty_to_null(xc->variant)); if (r < 0) return r; - r = strv_env_assign(&l, "XKBOPTIONS", xc ? empty_to_null(xc->options) : NULL); + r = strv_env_assign(&l, "XKBOPTIONS", empty_to_null(xc->options)); if (r < 0) return r; @@ -431,7 +481,7 @@ int x11_write_data(Context *c) { assert(c); xc = context_get_x11_context(c); - if (!xc) { + if (x11_context_isempty(xc)) { if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) return errno == ENOENT ? 0 : -errno; @@ -517,7 +567,7 @@ static int read_next_mapping( length = strv_length(b); if (length < min_fields || length > max_fields) { - log_warning("Invalid line %s:%u, ignoring.", strna(filename), *n); + log_debug("Invalid line %s:%u, ignoring.", strna(filename), *n); continue; } @@ -530,70 +580,46 @@ static int read_next_mapping( return 0; } -int vconsole_convert_to_x11(Context *c) { - int r, modified = -1; - X11Context *xc; +int vconsole_convert_to_x11(const VCContext *vc, X11Context *ret) { + _cleanup_fclose_ FILE *f = NULL; + const char *map; + int r; - assert(c); + assert(vc); + assert(ret); - /* If Context.x11_from_vc is empty, then here we update Context.x11_from_xorg, as the caller may - * already have been called context_get_x11_context() or _safe(), and otherwise the caller's - * X11Context may be outdated. The updated context will be copied to Context.x11_from_vc in - * vconsole_write_data() if necessary. */ - xc = context_get_x11_context_safe(c); - - if (isempty(c->vc_keymap)) { - modified = !x11_context_isempty(xc); - context_clear_x11(c); - } else { - _cleanup_fclose_ FILE *f = NULL; - const char *map; - - map = systemd_kbd_model_map(); - f = fopen(map, "re"); - if (!f) - return -errno; - - for (unsigned n = 0;;) { - _cleanup_strv_free_ char **a = NULL; - - r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); - if (r < 0) - return r; - if (r == 0) - break; - - if (!streq(c->vc_keymap, a[0])) - continue; - - r = x11_context_copy(xc, - &(X11Context) { - .layout = empty_or_dash_to_null(a[1]), - .model = empty_or_dash_to_null(a[2]), - .variant = empty_or_dash_to_null(a[3]), - .options = empty_or_dash_to_null(a[4]), - }); - if (r < 0) - return r; - modified = r > 0; - - break; - } + if (isempty(vc->keymap)) { + *ret = (X11Context) {}; + return 0; } - if (modified > 0) - log_info("Changing X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", - strempty(xc->layout), - strempty(xc->model), - strempty(xc->variant), - strempty(xc->options)); - else if (modified < 0) - log_notice("X11 keyboard layout was not modified: no conversion found for \"%s\".", - c->vc_keymap); - else - log_debug("X11 keyboard layout did not need to be modified."); + map = systemd_kbd_model_map(); + f = fopen(map, "re"); + if (!f) + return -errno; - return modified > 0; + for (unsigned n = 0;;) { + _cleanup_strv_free_ char **a = NULL; + + r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); + if (r < 0) + return r; + if (r == 0) { + *ret = (X11Context) {}; + return 0; + } + + if (!streq(vc->keymap, a[0])) + continue; + + return x11_context_copy(ret, + &(X11Context) { + .layout = empty_or_dash_to_null(a[1]), + .model = empty_or_dash_to_null(a[2]), + .variant = empty_or_dash_to_null(a[3]), + .options = empty_or_dash_to_null(a[4]), + }); + } } int find_converted_keymap(const X11Context *xc, char **ret) { @@ -769,49 +795,28 @@ int find_language_fallback(const char *lang, char **ret) { } } -int x11_convert_to_vconsole(Context *c) { - bool modified = false; - const X11Context *xc; +int x11_convert_to_vconsole(const X11Context *xc, VCContext *ret) { + _cleanup_free_ char *keymap = NULL; + int r; - assert(c); - - xc = context_get_x11_context_safe(c); + assert(xc); + assert(ret); if (isempty(xc->layout)) { - modified = - !isempty(c->vc_keymap) || - !isempty(c->vc_keymap_toggle); - - context_clear_vconsole(c); - } else { - _cleanup_free_ char *new_keymap = NULL; - int r; - - r = find_converted_keymap(xc, &new_keymap); - if (r == 0) - r = find_legacy_keymap(xc, &new_keymap); - if (r < 0) - return r; - if (r == 0) - /* We search for layout-variant match first, but then we also look - * for anything which matches just the layout. So it's accurate to say - * that we couldn't find anything which matches the layout. */ - log_notice("No conversion to virtual console map found for \"%s\".", xc->layout); - - if (!streq_ptr(c->vc_keymap, new_keymap)) { - context_clear_vconsole(c); - c->vc_keymap = TAKE_PTR(new_keymap); - modified = true; - } + *ret = (VCContext) {}; + return 0; } - if (modified) - log_info("Changing virtual console keymap to '%s' toggle '%s'", - strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); - else - log_debug("Virtual console keymap was not modified."); + r = find_converted_keymap(xc, &keymap); + if (r == 0) + r = find_legacy_keymap(xc, &keymap); + if (r < 0) + return r; - return modified; + *ret = (VCContext) { + .keymap = TAKE_PTR(keymap), + }; + return 0; } bool locale_gen_check_available(void) { diff --git a/src/locale/localed-util.h b/src/locale/localed-util.h index a3e763f850..83d253c18d 100644 --- a/src/locale/localed-util.h +++ b/src/locale/localed-util.h @@ -15,6 +15,11 @@ typedef struct X11Context { char *options; } X11Context; +typedef struct VCContext { + char *keymap; + char *toggle; +} VCContext; + typedef struct Context { sd_bus_message *locale_cache; LocaleContext locale_context; @@ -26,18 +31,27 @@ typedef struct Context { sd_bus_message *vc_cache; struct stat vc_stat; - char *vc_keymap; - char *vc_keymap_toggle; + VCContext vc; Hashmap *polkit_registry; } Context; +void x11_context_clear(X11Context *xc); +void x11_context_replace(X11Context *dest, X11Context *src); +bool x11_context_isempty(const X11Context *xc); void x11_context_empty_to_null(X11Context *xc); bool x11_context_is_safe(const X11Context *xc); bool x11_context_equal(const X11Context *a, const X11Context *b); int x11_context_copy(X11Context *dest, const X11Context *src); -X11Context *context_get_x11_context_safe(Context *c); +X11Context *context_get_x11_context(Context *c); + +void vc_context_clear(VCContext *vc); +void vc_context_replace(VCContext *dest, VCContext *src); +bool vc_context_isempty(const VCContext *vc); +void vc_context_empty_to_null(VCContext *vc); +bool vc_context_equal(const VCContext *a, const VCContext *b); +int vc_context_copy(VCContext *dest, const VCContext *src); int find_converted_keymap(const X11Context *xc, char **ret); int find_legacy_keymap(const X11Context *xc, char **ret); @@ -48,9 +62,9 @@ int vconsole_read_data(Context *c, sd_bus_message *m); int x11_read_data(Context *c, sd_bus_message *m); void context_clear(Context *c); -int vconsole_convert_to_x11(Context *c); +int vconsole_convert_to_x11(const VCContext *vc, X11Context *ret); int vconsole_write_data(Context *c); -int x11_convert_to_vconsole(Context *c); +int x11_convert_to_vconsole(const X11Context *xc, VCContext *ret); int x11_write_data(Context *c); bool locale_gen_check_available(void); diff --git a/src/locale/localed.c b/src/locale/localed.c index 68c014fd47..267f27635b 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -118,9 +118,9 @@ static int property_get_vconsole( return r; if (streq(property, "VConsoleKeymap")) - return sd_bus_message_append_basic(reply, 's', c->vc_keymap); + return sd_bus_message_append_basic(reply, 's', c->vc.keymap); if (streq(property, "VConsoleKeymapToggle")) - return sd_bus_message_append_basic(reply, 's', c->vc_keymap_toggle); + return sd_bus_message_append_basic(reply, 's', c->vc.toggle); return -EINVAL; } @@ -140,11 +140,15 @@ static int property_get_xkb( assert(property); + r = vconsole_read_data(c, reply); + if (r < 0) + return r; + r = x11_read_data(c, reply); if (r < 0) return r; - xc = context_get_x11_context_safe(c); + xc = context_get_x11_context(c); if (streq(property, "X11Layout")) return sd_bus_message_append_basic(reply, 's', xc->layout); @@ -252,11 +256,11 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er r = sd_bus_message_read_strv(m, &l); if (r < 0) - return r; + return bus_log_parse_error(r); r = sd_bus_message_read_basic(m, 'b', &interactive); if (r < 0) - return r; + return bus_log_parse_error(r); use_localegen = locale_gen_check_available(); @@ -269,7 +273,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er new_locale[VARIABLE_LANG] = strdup(l[0]); if (!new_locale[VARIABLE_LANG]) - return -ENOMEM; + return log_oom(); l = strv_free(l); } @@ -303,7 +307,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er /* Merge with the current settings */ r = locale_context_merge(&c->locale_context, new_locale); if (r < 0) - return r; + return log_oom(); locale_variables_simplify(new_locale); @@ -366,26 +370,21 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er } static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { + _cleanup_(x11_context_clear) X11Context converted = {}; Context *c = ASSERT_PTR(userdata); - const char *keymap, *keymap_toggle; int convert, interactive, r; + bool x_needs_update; + VCContext in; assert(m); - r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive); + r = sd_bus_message_read(m, "ssbb", &in.keymap, &in.toggle, &convert, &interactive); if (r < 0) - return r; + return bus_log_parse_error(r); - keymap = empty_to_null(keymap); - keymap_toggle = empty_to_null(keymap_toggle); + vc_context_empty_to_null(&in); - r = vconsole_read_data(c, m); - if (r < 0) { - log_error_errno(r, "Failed to read virtual console keymap data: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); - } - - FOREACH_STRING(name, keymap ?: keymap_toggle, keymap ? keymap_toggle : NULL) { + FOREACH_STRING(name, in.keymap ?: in.toggle, in.keymap ? in.toggle : NULL) { r = keymap_exists(name); /* This also verifies that the keymap name is kosher. */ if (r < 0) { log_error_errno(r, "Failed to check keymap %s: %m", name); @@ -395,8 +394,37 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Keymap %s is not installed.", name); } - if (streq_ptr(keymap, c->vc_keymap) && - streq_ptr(keymap_toggle, c->vc_keymap_toggle)) + r = vconsole_read_data(c, m); + if (r < 0) { + log_error_errno(r, "Failed to read virtual console keymap data: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); + } + + r = x11_read_data(c, m); + if (r < 0) { + log_error_errno(r, "Failed to read X11 keyboard layout data: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to read X11 keyboard layout data: %m"); + } + + if (convert) { + r = vconsole_convert_to_x11(&in, &converted); + if (r < 0) { + log_error_errno(r, "Failed to convert keymap data: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); + } + + if (x11_context_isempty(&converted)) + log_notice("No conversion found for virtual console keymap \"%s\".", strempty(in.keymap)); + else + log_info("The virtual console keymap '%s' is converted to X11 keyboard layout '%s' model '%s' variant '%s' options '%s'", + in.keymap, strempty(converted.layout), strempty(converted.model), strempty(converted.variant), strempty(converted.options)); + + /* save the result of conversion to emit changed properties later. */ + x_needs_update = !x11_context_equal(&c->x11_from_vc, &converted) || !x11_context_equal(&c->x11_from_xorg, &converted); + } else + x_needs_update = !x11_context_equal(&c->x11_from_vc, &c->x11_from_xorg); + + if (vc_context_equal(&c->vc, &in) && !x_needs_update) return sd_bus_reply_method_return(m, NULL); r = bus_verify_polkit_async( @@ -413,39 +441,42 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - if (free_and_strdup(&c->vc_keymap, keymap) < 0 || - free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0) - return -ENOMEM; + r = vc_context_copy(&c->vc, &in); + if (r < 0) + return log_oom(); - if (convert) { - r = x11_read_data(c, m); - if (r < 0) { - log_error_errno(r, "Failed to read X11 keyboard layout data: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to read X11 keyboard layout data: %m"); + if (x_needs_update) { + if (convert) { + r = x11_context_copy(&c->x11_from_vc, &converted); + if (r < 0) + return log_oom(); + x11_context_replace(&c->x11_from_xorg, &converted); + } else { + const X11Context *xc = context_get_x11_context(c); + + /* Even if the conversion is not requested, sync the two X11 contexts. */ + r = x11_context_copy(&c->x11_from_vc, xc); + if (r < 0) + return log_oom(); + + r = x11_context_copy(&c->x11_from_xorg, xc); + if (r < 0) + return log_oom(); } - - r = vconsole_convert_to_x11(c); - if (r < 0) { - log_error_errno(r, "Failed to convert keymap data: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); - } - - /* save the result of conversion to emit changed properties later. */ - convert = r > 0; } r = vconsole_write_data(c); if (r < 0) log_warning_errno(r, "Failed to write virtual console keymap, ignoring: %m"); - if (convert) { + if (x_needs_update) { r = x11_write_data(c); if (r < 0) log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m"); } log_info("Changed virtual console keymap to '%s' toggle '%s'", - strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); + strempty(c->vc.keymap), strempty(c->vc.toggle)); (void) vconsole_reload(sd_bus_message_get_bus(m)); @@ -454,10 +485,10 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro "/org/freedesktop/locale1", "org.freedesktop.locale1", "VConsoleKeymap", "VConsoleKeymapToggle", - convert ? "X11Layout" : NULL, - convert ? "X11Model" : NULL, - convert ? "X11Variant" : NULL, - convert ? "X11Options" : NULL, + x_needs_update ? "X11Layout" : NULL, + x_needs_update ? "X11Model" : NULL, + x_needs_update ? "X11Variant" : NULL, + x_needs_update ? "X11Options" : NULL, NULL); return sd_bus_reply_method_return(m, NULL); @@ -565,29 +596,19 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v #endif static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { + _cleanup_(vc_context_clear) VCContext converted = {}; Context *c = ASSERT_PTR(userdata); int convert, interactive, r; - X11Context *xc, in; + X11Context in; assert(m); r = sd_bus_message_read(m, "ssssbb", &in.layout, &in.model, &in.variant, &in.options, &convert, &interactive); if (r < 0) - return r; + return bus_log_parse_error(r); x11_context_empty_to_null(&in); - r = x11_read_data(c, m); - if (r < 0) { - log_error_errno(r, "Failed to read x11 keyboard layout data: %m"); - return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data"); - } - - xc = context_get_x11_context_safe(c); - - if (x11_context_equal(xc, &in)) - return sd_bus_reply_method_return(m, NULL); - if (!x11_context_is_safe(&in)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data"); @@ -602,6 +623,41 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); } + r = vconsole_read_data(c, m); + if (r < 0) { + log_error_errno(r, "Failed to read virtual console keymap data: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); + } + + r = x11_read_data(c, m); + if (r < 0) { + log_error_errno(r, "Failed to read x11 keyboard layout data: %m"); + return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data"); + } + + if (convert) { + r = x11_convert_to_vconsole(&in, &converted); + if (r < 0) { + log_error_errno(r, "Failed to convert keymap data: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); + } + + if (vc_context_isempty(&converted)) + /* We search for layout-variant match first, but then we also look + * for anything which matches just the layout. So it's accurate to say + * that we couldn't find anything which matches the layout. */ + log_notice("No conversion to virtual console map found for \"%s\".", strempty(in.layout)); + else + log_info("The X11 keyboard layout '%s' is converted to virtual console keymap '%s'", + in.layout, converted.keymap); + + /* save the result of conversion to emit changed properties later. */ + convert = !vc_context_equal(&c->vc, &converted); + } + + if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert) + return sd_bus_reply_method_return(m, NULL); + r = bus_verify_polkit_async( m, CAP_SYS_ADMIN, @@ -616,20 +672,16 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = x11_context_copy(xc, &in); + r = x11_context_copy(&c->x11_from_vc, &in); if (r < 0) return log_oom(); - if (convert) { - r = x11_convert_to_vconsole(c); - if (r < 0) { - log_error_errno(r, "Failed to convert keymap data: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); - } + r = x11_context_copy(&c->x11_from_xorg, &in); + if (r < 0) + return log_oom(); - /* save the result of conversion to emit changed properties later. */ - convert = r > 0; - } + if (convert) + vc_context_replace(&c->vc, &converted); r = vconsole_write_data(c); if (r < 0) diff --git a/src/locale/test-localed-util.c b/src/locale/test-localed-util.c index 3d96b3aa85..f57c9ba1f8 100644 --- a/src/locale/test-localed-util.c +++ b/src/locale/test-localed-util.c @@ -71,125 +71,105 @@ TEST(find_legacy_keymap) { } TEST(vconsole_convert_to_x11) { - _cleanup_(context_clear) Context c = {}; - X11Context *xc = &c.x11_from_vc; + _cleanup_(x11_context_clear) X11Context xc = {}; + _cleanup_(vc_context_clear) VCContext vc = {}; - log_info("/* test emptying first (:) */"); - assert_se(free_and_strdup(&xc->layout, "foo") >= 0); - assert_se(free_and_strdup(&xc->variant, "bar") >= 0); - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(xc->layout == NULL); - assert_se(xc->variant == NULL); - - log_info("/* test emptying second (:) */"); - - assert_se(vconsole_convert_to_x11(&c) == 0); - assert_se(xc->layout == NULL); - assert_se(xc->variant == NULL); + log_info("/* test empty keymap */"); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(x11_context_isempty(&xc)); log_info("/* test without variant, new mapping (es:) */"); - assert_se(free_and_strdup(&c.vc_keymap, "es") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(xc->layout, "es")); - assert_se(xc->variant == NULL); + assert_se(free_and_strdup(&vc.keymap, "es") >= 0); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(streq(xc.layout, "es")); + assert_se(xc.variant == NULL); + x11_context_clear(&xc); log_info("/* test with known variant, new mapping (es:dvorak) */"); - assert_se(free_and_strdup(&c.vc_keymap, "es-dvorak") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(xc->layout, "es")); - assert_se(streq(xc->variant, "dvorak")); + assert_se(free_and_strdup(&vc.keymap, "es-dvorak") >= 0); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(streq(xc.layout, "es")); + assert_se(streq(xc.variant, "dvorak")); + x11_context_clear(&xc); log_info("/* test with old mapping (fr:latin9) */"); - assert_se(free_and_strdup(&c.vc_keymap, "fr-latin9") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(xc->layout, "fr")); - assert_se(streq(xc->variant, "latin9")); + assert_se(free_and_strdup(&vc.keymap, "fr-latin9") >= 0); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(streq(xc.layout, "fr")); + assert_se(streq(xc.variant, "latin9")); + x11_context_clear(&xc); log_info("/* test with a compound mapping (ru,us) */"); - assert_se(free_and_strdup(&c.vc_keymap, "ru") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(xc->layout, "ru,us")); - assert_se(xc->variant == NULL); + assert_se(free_and_strdup(&vc.keymap, "ru") >= 0); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(streq(xc.layout, "ru,us")); + assert_se(xc.variant == NULL); + x11_context_clear(&xc); log_info("/* test with a simple mapping (us) */"); - assert_se(free_and_strdup(&c.vc_keymap, "us") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(xc->layout, "us")); - assert_se(xc->variant == NULL); + assert_se(free_and_strdup(&vc.keymap, "us") >= 0); + assert_se(vconsole_convert_to_x11(&vc, &xc) >= 0); + assert_se(streq(xc.layout, "us")); + assert_se(xc.variant == NULL); } TEST(x11_convert_to_vconsole) { - _cleanup_(context_clear) Context c = {}; - X11Context *xc = &c.x11_from_xorg; - int r; + _cleanup_(x11_context_clear) X11Context xc = {}; + _cleanup_(vc_context_clear) VCContext vc = {}; - log_info("/* test emptying first (:) */"); - assert_se(free_and_strdup(&c.vc_keymap, "foobar") >= 0); - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(c.vc_keymap == NULL); - - log_info("/* test emptying second (:) */"); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(c.vc_keymap == NULL); + log_info("/* test empty layout (:) */"); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(vc_context_isempty(&vc)); log_info("/* test without variant, new mapping (es:) */"); - assert_se(free_and_strdup(&xc->layout, "es") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "es")); + assert_se(free_and_strdup(&xc.layout, "es") >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "es")); + vc_context_clear(&vc); log_info("/* test with unknown variant, new mapping (es:foobar) */"); - assert_se(free_and_strdup(&xc->variant, "foobar") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(streq(c.vc_keymap, "es")); + assert_se(free_and_strdup(&xc.variant, "foobar") >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "es")); + vc_context_clear(&vc); log_info("/* test with known variant, new mapping (es:dvorak) */"); - assert_se(free_and_strdup(&xc->variant, "dvorak") >= 0); - - r = x11_convert_to_vconsole(&c); - if (r == 0) { + assert_se(free_and_strdup(&xc.variant, "dvorak") >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + if (vc_context_isempty(&vc)) { log_info("Skipping rest of %s: keymaps are not installed", __func__); return; } - - assert_se(r == 1); - assert_se(streq(c.vc_keymap, "es-dvorak")); + assert_se(streq(vc.keymap, "es-dvorak")); + vc_context_clear(&vc); log_info("/* test with old mapping (fr:latin9) */"); - assert_se(free_and_strdup(&xc->layout, "fr") >= 0); - assert_se(free_and_strdup(&xc->variant, "latin9") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "fr-latin9")); + assert_se(free_and_strdup(&xc.layout, "fr") >= 0); + assert_se(free_and_strdup(&xc.variant, "latin9") >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "fr-latin9")); + vc_context_clear(&vc); log_info("/* test with a compound mapping (us,ru:) */"); - assert_se(free_and_strdup(&xc->layout, "us,ru") >= 0); - assert_se(free_and_strdup(&xc->variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "us")); + assert_se(free_and_strdup(&xc.layout, "us,ru") >= 0); + assert_se(free_and_strdup(&xc.variant, NULL) >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "us")); + vc_context_clear(&vc); log_info("/* test with a compound mapping (ru,us:) */"); - assert_se(free_and_strdup(&xc->layout, "ru,us") >= 0); - assert_se(free_and_strdup(&xc->variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "ru")); + assert_se(free_and_strdup(&xc.layout, "ru,us") >= 0); + assert_se(free_and_strdup(&xc.variant, NULL) >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "ru")); + vc_context_clear(&vc); /* https://bugzilla.redhat.com/show_bug.cgi?id=1333998 */ log_info("/* test with a simple new mapping (ru:) */"); - assert_se(free_and_strdup(&xc->layout, "ru") >= 0); - assert_se(free_and_strdup(&xc->variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(streq(c.vc_keymap, "ru")); + assert_se(free_and_strdup(&xc.layout, "ru") >= 0); + assert_se(free_and_strdup(&xc.variant, NULL) >= 0); + assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0); + assert_se(streq(vc.keymap, "ru")); } static int intro(void) { diff --git a/test/units/testsuite-73.sh b/test/units/testsuite-73.sh index 92576c5067..d8f668513c 100755 --- a/test/units/testsuite-73.sh +++ b/test/units/testsuite-73.sh @@ -431,12 +431,134 @@ XKBMODEL=pc105+inet" assert_not_in "X11 Options:" "$output" } +test_convert() { + if [[ -z "$(localectl list-keymaps)" ]]; then + echo "No vconsole keymap installed, skipping test." + return + fi + + if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then + echo "No x11 keymap installed, skipping test." + return + fi + + backup_keymap + trap restore_keymap RETURN + + # clear previous settings + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # set VC keymap without conversion + assert_rc 0 localectl --no-convert set-keymap us + output=$(localectl) + + # check VC keymap + vc=$(cat /etc/vconsole.conf) + assert_in "KEYMAP=us" "$vc" + assert_in "VC Keymap: us" "$output" + + # check VC -> X11 keymap conversion (nothing set) + assert_in "X11 Layout: .unset." "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + assert_not_in "XKBLAYOUT=" "$vc" + assert_not_in "XKBMODEL=" "$vc" + assert_not_in "XKBVARIANT=" "$vc" + assert_not_in "XKBOPTIONS=" "$vc" + + # set VC keymap with conversion + assert_rc 0 localectl set-keymap us + output=$(localectl) + + # check VC keymap + vc=$(cat /etc/vconsole.conf) + assert_in "KEYMAP=us" "$vc" + assert_in "VC Keymap: us" "$output" + + # check VC -> X11 keymap conversion + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_not_in "X11 Variant:" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + assert_in "XKBLAYOUT=us" "$vc" + assert_in "XKBMODEL=pc105\+inet" "$vc" + assert_not_in "XKBVARIANT" "$vc" + assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" + + # clear previous settings + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # set x11 keymap (layout) without conversion + assert_rc 0 localectl --no-convert set-x11-keymap us + + assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)" + assert_in "VC Keymap: .unset." "$(localectl)" + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_not_in 'Option "XkbModel"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_not_in 'XKBMODEL=' "$output" + assert_not_in 'XKBVARIANT=' "$output" + assert_not_in 'XKBOPTIONS=' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + # set x11 keymap (layout, model) with conversion + assert_rc 0 localectl set-x11-keymap us + + assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)" + assert_in "VC Keymap: us" "$(localectl)" + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_not_in 'Option "XkbModel"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_not_in 'XKBMODEL=' "$output" + assert_not_in 'XKBVARIANT=' "$output" + assert_not_in 'XKBOPTIONS=' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" +} + : >/failed enable_debug test_locale test_vc_keymap test_x11_keymap +test_convert touch /testok rm /failed