#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <sys/capability.h>
#include "sd-bus.h"
#include "bus-message.h"
#include "event-util.h"
#include "locale-util.h"
+#include "selinux-util.h"
#ifdef HAVE_XKBCOMMON
#include <xkbcommon/xkbcommon.h>
return isempty(s) || streq(s, "-") ? NULL : s;
}
-static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
+static int read_next_mapping(const char* filename,
+ unsigned min_fields, unsigned max_fields,
+ FILE *f, unsigned *n, char ***a) {
assert(f);
assert(n);
assert(a);
char line[LINE_MAX];
char *l, **b;
int r;
+ size_t length;
errno = 0;
if (!fgets(line, sizeof(line), f)) {
if (r < 0)
return r;
- if (strv_length(b) < 5) {
- log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
+ length = strv_length(b);
+ if (length < min_fields || length > max_fields) {
+ log_error("Invalid line %s:%u, ignoring.", filename, *n);
strv_free(b);
continue;
_cleanup_strv_free_ char **a = NULL;
int r;
- r = read_next_mapping(f, &n, &a);
+ r = read_next_mapping(SYSTEMD_KBD_MODEL_MAP, 5, UINT_MAX, f, &n, &a);
if (r < 0)
return r;
if (r == 0)
int r;
r = x11_write_data(c);
- if (r < 0) {
- log_error_errno(r, "Failed to set X11 keyboard layout: %m");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set X11 keyboard layout: %m");
log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
strempty(c->x11_layout),
_cleanup_strv_free_ char **a = NULL;
unsigned matching = 0;
- r = read_next_mapping(f, &n, &a);
+ r = read_next_mapping(SYSTEMD_KBD_MODEL_MAP, 5, UINT_MAX, f, &n, &a);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int find_language_fallback(const char *lang, char **language) {
+ _cleanup_fclose_ FILE *f = NULL;
+ unsigned n = 0;
+
+ assert(language);
+
+ f = fopen(SYSTEMD_LANGUAGE_FALLBACK_MAP, "re");
+ if (!f)
+ return -errno;
+
+ for (;;) {
+ _cleanup_strv_free_ char **a = NULL;
+ int r;
+
+ r = read_next_mapping(SYSTEMD_LANGUAGE_FALLBACK_MAP, 2, 2, f, &n, &a);
+ if (r <= 0)
+ return r;
+
+ if (streq(lang, a[0])) {
+ assert(strv_length(a) == 2);
+ *language = a[1];
+ a[1] = NULL;
+ return 1;
+ }
+ }
+
+ assert_not_reached("should not be here");
+}
+
static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
bool modified = false;
int r;
Context *c = userdata;
_cleanup_strv_free_ char **l = NULL;
char **i;
+ const char *lang = NULL;
int interactive;
bool modified = false;
- bool passed[_LOCALE_MAX] = {};
+ bool have[_LOCALE_MAX] = {};
int p;
int r;
(*i)[k] == '=' &&
locale_is_valid((*i) + k + 1)) {
valid = true;
- passed[p] = true;
+ have[p] = true;
+
+ if (p == LOCALE_LANG)
+ lang = (*i) + k + 1;
if (!streq_ptr(*i + k + 1, c->locale[p]))
modified = true;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
}
+ /* If LANG was specified, but not LANGUAGE, check if we should
+ * set it based on the language fallback table. */
+ if (have[LOCALE_LANG] && !have[LOCALE_LANGUAGE]) {
+ _cleanup_free_ char *language = NULL;
+
+ assert(lang);
+
+ (void) find_language_fallback(lang, &language);
+ if (language) {
+ log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language);
+ if (!streq_ptr(language, c->locale[LOCALE_LANGUAGE])) {
+ r = strv_extendf(&l, "LANGUAGE=%s", language);
+ if (r < 0)
+ return r;
+
+ have[LOCALE_LANGUAGE] = true;
+ modified = true;
+ }
+ }
+ }
+
/* Check whether a variable is unset */
if (!modified)
for (p = 0; p < _LOCALE_MAX; p++)
- if (!isempty(c->locale[p]) && !passed[p]) {
+ if (!isempty(c->locale[p]) && !have[p]) {
modified = true;
break;
}
}
for (p = 0; p < _LOCALE_MAX; p++) {
- if (passed[p])
+ if (have[p])
continue;
free_and_replace(&c->locale[p], NULL);
if (!streq_ptr(keymap, c->vc_keymap) ||
!streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
- if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
- (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
+ if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) ||
+ (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
#ifdef HAVE_XKBCOMMON
static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
- /* suppress xkb messages for now */
+ const char *fmt;
+
+ fmt = strjoina("libxkbcommon: ", format);
+ log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
}
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = verify_xkb_rmlvo(model, layout, variant, options);
- if (r < 0)
- log_warning_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
- strempty(model), strempty(layout), strempty(variant), strempty(options));
+ if (r < 0) {
+ log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
+ strempty(model), strempty(layout), strempty(variant), strempty(options));
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing");
+ }
if (free_and_strdup(&c->x11_layout, layout) < 0 ||
free_and_strdup(&c->x11_model, model) < 0 ||
assert(_bus);
r = sd_bus_default_system(&bus);
- if (r < 0) {
- log_error_errno(r, "Failed to get system bus connection: %m");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get system bus connection: %m");
r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
- if (r < 0) {
- log_error_errno(r, "Failed to register object: %m");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to register object: %m");
r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
- if (r < 0) {
- log_error_errno(r, "Failed to register name: %m");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to register name: %m");
r = sd_bus_attach_event(bus, event, 0);
- if (r < 0) {
- log_error_errno(r, "Failed to attach bus to event loop: %m");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
*_bus = bus;
bus = NULL;