]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
1822350d KS |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2011 Lennart Poettering | |
8d451309 | 6 | Copyright 2013 Kay Sievers |
1822350d KS |
7 | ***/ |
8 | ||
1822350d KS |
9 | #include <errno.h> |
10 | #include <string.h> | |
11 | #include <unistd.h> | |
12 | ||
349cc4a5 | 13 | #if HAVE_XKBCOMMON |
3ffd4af2 | 14 | #include <xkbcommon/xkbcommon.h> |
5de34470 | 15 | #include <dlfcn.h> |
3ffd4af2 LP |
16 | #endif |
17 | ||
8d451309 KS |
18 | #include "sd-bus.h" |
19 | ||
b5efdb8a | 20 | #include "alloc-util.h" |
8d451309 KS |
21 | #include "bus-error.h" |
22 | #include "bus-message.h" | |
bb15fafe LP |
23 | #include "bus-util.h" |
24 | #include "def.h" | |
4897d1dc | 25 | #include "keymap-util.h" |
75683450 | 26 | #include "locale-util.h" |
4897d1dc | 27 | #include "macro.h" |
bb15fafe | 28 | #include "path-util.h" |
d7b8eec7 | 29 | #include "selinux-util.h" |
4897d1dc | 30 | #include "string-util.h" |
bb15fafe | 31 | #include "strv.h" |
ee104e11 | 32 | #include "user-util.h" |
d4f5a1f4 | 33 | |
4897d1dc | 34 | static Hashmap *polkit_registry = NULL; |
1822350d | 35 | |
8d451309 KS |
36 | static int locale_update_system_manager(Context *c, sd_bus *bus) { |
37 | _cleanup_free_ char **l_unset = NULL; | |
38 | _cleanup_strv_free_ char **l_set = NULL; | |
4afd3348 | 39 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; |
8d451309 KS |
40 | sd_bus_error error = SD_BUS_ERROR_NULL; |
41 | unsigned c_set, c_unset, p; | |
42 | int r; | |
1822350d KS |
43 | |
44 | assert(bus); | |
45 | ||
78c97cbe | 46 | l_unset = new0(char*, _VARIABLE_LC_MAX); |
8d451309 KS |
47 | if (!l_unset) |
48 | return -ENOMEM; | |
1822350d | 49 | |
78c97cbe | 50 | l_set = new0(char*, _VARIABLE_LC_MAX); |
8d451309 KS |
51 | if (!l_set) |
52 | return -ENOMEM; | |
53 | ||
78c97cbe ZJS |
54 | for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) { |
55 | const char *name; | |
56 | ||
57 | name = locale_variable_to_string(p); | |
58 | assert(name); | |
1822350d | 59 | |
8d451309 | 60 | if (isempty(c->locale[p])) |
78c97cbe | 61 | l_unset[c_set++] = (char*) name; |
1822350d KS |
62 | else { |
63 | char *s; | |
64 | ||
78c97cbe | 65 | if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0) |
8d451309 | 66 | return -ENOMEM; |
1822350d KS |
67 | |
68 | l_set[c_unset++] = s; | |
69 | } | |
70 | } | |
71 | ||
78c97cbe | 72 | assert(c_set + c_unset == _VARIABLE_LC_MAX); |
151b9b96 | 73 | r = sd_bus_message_new_method_call(bus, &m, |
8d451309 KS |
74 | "org.freedesktop.systemd1", |
75 | "/org/freedesktop/systemd1", | |
76 | "org.freedesktop.systemd1.Manager", | |
151b9b96 | 77 | "UnsetAndSetEnvironment"); |
8d451309 KS |
78 | if (r < 0) |
79 | return r; | |
1822350d | 80 | |
8d451309 KS |
81 | r = sd_bus_message_append_strv(m, l_unset); |
82 | if (r < 0) | |
83 | return r; | |
1822350d | 84 | |
8d451309 KS |
85 | r = sd_bus_message_append_strv(m, l_set); |
86 | if (r < 0) | |
87 | return r; | |
1822350d | 88 | |
c49b30a2 | 89 | r = sd_bus_call(bus, m, 0, &error, NULL); |
8d451309 | 90 | if (r < 0) |
da927ba9 | 91 | log_error_errno(r, "Failed to update the manager environment: %m"); |
1822350d | 92 | |
8d451309 | 93 | return 0; |
1822350d KS |
94 | } |
95 | ||
8d451309 | 96 | static int vconsole_reload(sd_bus *bus) { |
4afd3348 | 97 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
1822350d | 98 | int r; |
1822350d KS |
99 | |
100 | assert(bus); | |
101 | ||
8d451309 | 102 | r = sd_bus_call_method(bus, |
1822350d KS |
103 | "org.freedesktop.systemd1", |
104 | "/org/freedesktop/systemd1", | |
105 | "org.freedesktop.systemd1.Manager", | |
8d451309 KS |
106 | "RestartUnit", |
107 | &error, | |
108 | NULL, | |
109 | "ss", "systemd-vconsole-setup.service", "replace"); | |
1822350d | 110 | |
8d451309 KS |
111 | if (r < 0) |
112 | log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); | |
1822350d KS |
113 | return r; |
114 | } | |
115 | ||
4897d1dc | 116 | static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus *bus) { |
78bd12a0 | 117 | int r; |
0732ef7a | 118 | |
4897d1dc | 119 | assert(bus); |
4e829d21 | 120 | |
4897d1dc ZJS |
121 | r = vconsole_convert_to_x11(c); |
122 | if (r <= 0) | |
123 | return r; | |
4e829d21 | 124 | |
4897d1dc ZJS |
125 | /* modified */ |
126 | r = x11_write_data(c); | |
127 | if (r < 0) | |
128 | return log_error_errno(r, "Failed to write X11 keyboard layout: %m"); | |
4e829d21 | 129 | |
4897d1dc ZJS |
130 | sd_bus_emit_properties_changed(bus, |
131 | "/org/freedesktop/locale1", | |
132 | "org.freedesktop.locale1", | |
133 | "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); | |
4e829d21 | 134 | |
4897d1dc | 135 | return 1; |
4e829d21 ZJS |
136 | } |
137 | ||
4897d1dc | 138 | static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus *bus) { |
0732ef7a | 139 | int r; |
1822350d | 140 | |
8d451309 | 141 | assert(bus); |
1822350d | 142 | |
4897d1dc ZJS |
143 | r = x11_convert_to_vconsole(c); |
144 | if (r <= 0) | |
145 | return r; | |
502f9614 | 146 | |
4897d1dc ZJS |
147 | /* modified */ |
148 | r = vconsole_write_data(c); | |
149 | if (r < 0) | |
150 | log_error_errno(r, "Failed to save virtual console keymap: %m"); | |
1822350d | 151 | |
4897d1dc ZJS |
152 | sd_bus_emit_properties_changed(bus, |
153 | "/org/freedesktop/locale1", | |
154 | "org.freedesktop.locale1", | |
155 | "VConsoleKeymap", "VConsoleKeymapToggle", NULL); | |
1822350d | 156 | |
4897d1dc | 157 | return vconsole_reload(bus); |
1822350d KS |
158 | } |
159 | ||
ebcf1f97 LP |
160 | static int property_get_locale( |
161 | sd_bus *bus, | |
162 | const char *path, | |
163 | const char *interface, | |
164 | const char *property, | |
165 | sd_bus_message *reply, | |
166 | void *userdata, | |
167 | sd_bus_error *error) { | |
168 | ||
8d451309 | 169 | Context *c = userdata; |
b47d419c | 170 | _cleanup_strv_free_ char **l = NULL; |
8d451309 | 171 | int p, q; |
1822350d | 172 | |
78c97cbe | 173 | l = new0(char*, _VARIABLE_LC_MAX+1); |
1822350d KS |
174 | if (!l) |
175 | return -ENOMEM; | |
176 | ||
78c97cbe | 177 | for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) { |
1822350d | 178 | char *t; |
78c97cbe ZJS |
179 | const char *name; |
180 | ||
181 | name = locale_variable_to_string(p); | |
182 | assert(name); | |
1822350d | 183 | |
8d451309 | 184 | if (isempty(c->locale[p])) |
1822350d KS |
185 | continue; |
186 | ||
78c97cbe | 187 | if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) |
1822350d | 188 | return -ENOMEM; |
1822350d | 189 | |
8d451309 | 190 | l[q++] = t; |
1822350d KS |
191 | } |
192 | ||
8d451309 | 193 | return sd_bus_message_append_strv(reply, l); |
1822350d KS |
194 | } |
195 | ||
19070062 | 196 | static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
8d451309 | 197 | Context *c = userdata; |
8d451309 KS |
198 | _cleanup_strv_free_ char **l = NULL; |
199 | char **i; | |
4e829d21 | 200 | const char *lang = NULL; |
8d451309 KS |
201 | int interactive; |
202 | bool modified = false; | |
78c97cbe | 203 | bool have[_VARIABLE_LC_MAX] = {}; |
8d451309 | 204 | int p; |
1822350d KS |
205 | int r; |
206 | ||
19070062 LP |
207 | assert(m); |
208 | assert(c); | |
209 | ||
8d451309 KS |
210 | r = bus_message_read_strv_extend(m, &l); |
211 | if (r < 0) | |
ebcf1f97 | 212 | return r; |
1822350d | 213 | |
8d451309 KS |
214 | r = sd_bus_message_read_basic(m, 'b', &interactive); |
215 | if (r < 0) | |
ebcf1f97 | 216 | return r; |
1822350d | 217 | |
502f9614 | 218 | /* Check whether a variable changed and if it is valid */ |
8d451309 KS |
219 | STRV_FOREACH(i, l) { |
220 | bool valid = false; | |
1822350d | 221 | |
78c97cbe | 222 | for (p = 0; p < _VARIABLE_LC_MAX; p++) { |
8d451309 | 223 | size_t k; |
78c97cbe | 224 | const char *name; |
1822350d | 225 | |
78c97cbe ZJS |
226 | name = locale_variable_to_string(p); |
227 | assert(name); | |
228 | ||
229 | k = strlen(name); | |
230 | if (startswith(*i, name) && | |
8d451309 | 231 | (*i)[k] == '=' && |
75683450 | 232 | locale_is_valid((*i) + k + 1)) { |
8d451309 | 233 | valid = true; |
4e829d21 ZJS |
234 | have[p] = true; |
235 | ||
78c97cbe | 236 | if (p == VARIABLE_LANG) |
4e829d21 | 237 | lang = (*i) + k + 1; |
1822350d | 238 | |
8d451309 KS |
239 | if (!streq_ptr(*i + k + 1, c->locale[p])) |
240 | modified = true; | |
1822350d | 241 | |
8d451309 KS |
242 | break; |
243 | } | |
1822350d KS |
244 | } |
245 | ||
8d451309 | 246 | if (!valid) |
ebcf1f97 | 247 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); |
8d451309 | 248 | } |
1822350d | 249 | |
4e829d21 ZJS |
250 | /* If LANG was specified, but not LANGUAGE, check if we should |
251 | * set it based on the language fallback table. */ | |
78c97cbe | 252 | if (have[VARIABLE_LANG] && !have[VARIABLE_LANGUAGE]) { |
4e829d21 ZJS |
253 | _cleanup_free_ char *language = NULL; |
254 | ||
255 | assert(lang); | |
256 | ||
257 | (void) find_language_fallback(lang, &language); | |
258 | if (language) { | |
259 | log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language); | |
78c97cbe | 260 | if (!streq_ptr(language, c->locale[VARIABLE_LANGUAGE])) { |
4e829d21 ZJS |
261 | r = strv_extendf(&l, "LANGUAGE=%s", language); |
262 | if (r < 0) | |
263 | return r; | |
264 | ||
78c97cbe | 265 | have[VARIABLE_LANGUAGE] = true; |
4e829d21 ZJS |
266 | modified = true; |
267 | } | |
268 | } | |
269 | } | |
270 | ||
8d451309 | 271 | /* Check whether a variable is unset */ |
28efac0d | 272 | if (!modified) |
78c97cbe | 273 | for (p = 0; p < _VARIABLE_LC_MAX; p++) |
4e829d21 | 274 | if (!isempty(c->locale[p]) && !have[p]) { |
8d451309 KS |
275 | modified = true; |
276 | break; | |
277 | } | |
8d451309 KS |
278 | |
279 | if (modified) { | |
502f9614 ZJS |
280 | _cleanup_strv_free_ char **settings = NULL; |
281 | ||
c529695e LP |
282 | r = bus_verify_polkit_async( |
283 | m, | |
284 | CAP_SYS_ADMIN, | |
285 | "org.freedesktop.locale1.set-locale", | |
403ed0e5 | 286 | NULL, |
c529695e LP |
287 | interactive, |
288 | UID_INVALID, | |
4897d1dc | 289 | &polkit_registry, |
c529695e | 290 | error); |
8d451309 | 291 | if (r < 0) |
ebcf1f97 | 292 | return r; |
8d451309 KS |
293 | if (r == 0) |
294 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
1822350d | 295 | |
28efac0d | 296 | STRV_FOREACH(i, l) |
78c97cbe | 297 | for (p = 0; p < _VARIABLE_LC_MAX; p++) { |
1822350d | 298 | size_t k; |
78c97cbe ZJS |
299 | const char *name; |
300 | ||
301 | name = locale_variable_to_string(p); | |
302 | assert(name); | |
1822350d | 303 | |
78c97cbe ZJS |
304 | k = strlen(name); |
305 | if (startswith(*i, name) && (*i)[k] == '=') { | |
af76d302 ZJS |
306 | r = free_and_strdup(&c->locale[p], *i + k + 1); |
307 | if (r < 0) | |
308 | return r; | |
1822350d KS |
309 | break; |
310 | } | |
311 | } | |
1822350d | 312 | |
78c97cbe | 313 | for (p = 0; p < _VARIABLE_LC_MAX; p++) { |
4e829d21 | 314 | if (have[p]) |
8d451309 | 315 | continue; |
1822350d | 316 | |
87699fe3 | 317 | c->locale[p] = mfree(c->locale[p]); |
8d451309 | 318 | } |
1822350d | 319 | |
8d451309 | 320 | locale_simplify(c); |
1822350d | 321 | |
502f9614 | 322 | r = locale_write_data(c, &settings); |
8d451309 | 323 | if (r < 0) { |
da927ba9 | 324 | log_error_errno(r, "Failed to set locale: %m"); |
048c386e | 325 | return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m"); |
8d451309 | 326 | } |
1822350d | 327 | |
19070062 | 328 | locale_update_system_manager(c, sd_bus_message_get_bus(m)); |
1822350d | 329 | |
502f9614 ZJS |
330 | if (settings) { |
331 | _cleanup_free_ char *line; | |
332 | ||
333 | line = strv_join(settings, ", "); | |
334 | log_info("Changed locale to %s.", strnull(line)); | |
335 | } else | |
336 | log_info("Changed locale to unset."); | |
1822350d | 337 | |
19070062 LP |
338 | (void) sd_bus_emit_properties_changed( |
339 | sd_bus_message_get_bus(m), | |
8d451309 KS |
340 | "/org/freedesktop/locale1", |
341 | "org.freedesktop.locale1", | |
342 | "Locale", NULL); | |
502f9614 ZJS |
343 | } else |
344 | log_debug("Locale settings were not modified."); | |
345 | ||
1822350d | 346 | |
df2d202e | 347 | return sd_bus_reply_method_return(m, NULL); |
8d451309 | 348 | } |
1822350d | 349 | |
19070062 | 350 | static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
8d451309 | 351 | Context *c = userdata; |
8d451309 | 352 | const char *keymap, *keymap_toggle; |
102d8f81 | 353 | int convert, interactive; |
8d451309 | 354 | int r; |
f2cc3753 | 355 | |
19070062 LP |
356 | assert(m); |
357 | assert(c); | |
358 | ||
8d451309 KS |
359 | r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive); |
360 | if (r < 0) | |
ebcf1f97 | 361 | return r; |
1822350d | 362 | |
3c6f7c34 LP |
363 | keymap = empty_to_null(keymap); |
364 | keymap_toggle = empty_to_null(keymap_toggle); | |
1822350d | 365 | |
8d451309 KS |
366 | if (!streq_ptr(keymap, c->vc_keymap) || |
367 | !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) { | |
1822350d | 368 | |
ae6c3cc0 LP |
369 | if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) || |
370 | (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle)))) | |
e74f76ca | 371 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keymap data"); |
1822350d | 372 | |
c529695e LP |
373 | r = bus_verify_polkit_async( |
374 | m, | |
375 | CAP_SYS_ADMIN, | |
376 | "org.freedesktop.locale1.set-keyboard", | |
403ed0e5 | 377 | NULL, |
c529695e LP |
378 | interactive, |
379 | UID_INVALID, | |
4897d1dc | 380 | &polkit_registry, |
c529695e | 381 | error); |
8d451309 | 382 | if (r < 0) |
ebcf1f97 | 383 | return r; |
8d451309 KS |
384 | if (r == 0) |
385 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
1822350d | 386 | |
af76d302 ZJS |
387 | if (free_and_strdup(&c->vc_keymap, keymap) < 0 || |
388 | free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0) | |
8d451309 | 389 | return -ENOMEM; |
0b507b17 | 390 | |
8d451309 KS |
391 | r = vconsole_write_data(c); |
392 | if (r < 0) { | |
da927ba9 | 393 | log_error_errno(r, "Failed to set virtual console keymap: %m"); |
048c386e | 394 | return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %m"); |
8d451309 | 395 | } |
1822350d | 396 | |
502f9614 ZJS |
397 | log_info("Changed virtual console keymap to '%s' toggle '%s'", |
398 | strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); | |
1822350d | 399 | |
19070062 | 400 | r = vconsole_reload(sd_bus_message_get_bus(m)); |
8d451309 | 401 | if (r < 0) |
da927ba9 | 402 | log_error_errno(r, "Failed to request keymap reload: %m"); |
1822350d | 403 | |
19070062 LP |
404 | (void) sd_bus_emit_properties_changed( |
405 | sd_bus_message_get_bus(m), | |
8d451309 KS |
406 | "/org/freedesktop/locale1", |
407 | "org.freedesktop.locale1", | |
408 | "VConsoleKeymap", "VConsoleKeymapToggle", NULL); | |
1822350d | 409 | |
8d451309 | 410 | if (convert) { |
4897d1dc | 411 | r = vconsole_convert_to_x11_and_emit(c, sd_bus_message_get_bus(m)); |
1822350d | 412 | if (r < 0) |
da927ba9 | 413 | log_error_errno(r, "Failed to convert keymap data: %m"); |
1822350d | 414 | } |
8d451309 | 415 | } |
1822350d | 416 | |
df2d202e | 417 | return sd_bus_reply_method_return(m, NULL); |
8d451309 | 418 | } |
1822350d | 419 | |
349cc4a5 | 420 | #if HAVE_XKBCOMMON |
5de34470 | 421 | |
3cb063fd | 422 | _printf_(3, 0) |
d4f5a1f4 | 423 | static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { |
8433e339 JS |
424 | const char *fmt; |
425 | ||
63c372cb | 426 | fmt = strjoina("libxkbcommon: ", format); |
032b7541 ZJS |
427 | #pragma GCC diagnostic push |
428 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
8433e339 | 429 | log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); |
032b7541 | 430 | #pragma GCC diagnostic pop |
d4f5a1f4 DH |
431 | } |
432 | ||
5de34470 LP |
433 | #define LOAD_SYMBOL(symbol, dl, name) \ |
434 | ({ \ | |
435 | (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \ | |
436 | (symbol) ? 0 : -EOPNOTSUPP; \ | |
437 | }) | |
438 | ||
d4f5a1f4 | 439 | static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { |
5de34470 LP |
440 | |
441 | /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge | |
442 | * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function | |
443 | * pointers to the shared library are below: */ | |
444 | ||
445 | struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL; | |
446 | void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL; | |
447 | void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL; | |
448 | struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL; | |
449 | void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL; | |
450 | ||
d4f5a1f4 DH |
451 | const struct xkb_rule_names rmlvo = { |
452 | .model = model, | |
453 | .layout = layout, | |
454 | .variant = variant, | |
455 | .options = options, | |
456 | }; | |
457 | struct xkb_context *ctx = NULL; | |
458 | struct xkb_keymap *km = NULL; | |
5de34470 | 459 | void *dl; |
d4f5a1f4 DH |
460 | int r; |
461 | ||
5de34470 LP |
462 | /* Compile keymap from RMLVO information to check out its validity */ |
463 | ||
464 | dl = dlopen("libxkbcommon.so.0", RTLD_LAZY); | |
465 | if (!dl) | |
466 | return -EOPNOTSUPP; | |
467 | ||
468 | r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new"); | |
469 | if (r < 0) | |
470 | goto finish; | |
471 | ||
472 | r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref"); | |
473 | if (r < 0) | |
474 | goto finish; | |
475 | ||
476 | r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn"); | |
477 | if (r < 0) | |
478 | goto finish; | |
d4f5a1f4 | 479 | |
5de34470 LP |
480 | r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names"); |
481 | if (r < 0) | |
482 | goto finish; | |
483 | ||
484 | r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref"); | |
485 | if (r < 0) | |
486 | goto finish; | |
487 | ||
488 | ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); | |
d4f5a1f4 DH |
489 | if (!ctx) { |
490 | r = -ENOMEM; | |
5de34470 | 491 | goto finish; |
d4f5a1f4 DH |
492 | } |
493 | ||
5de34470 | 494 | symbol_xkb_context_set_log_fn(ctx, log_xkb); |
d4f5a1f4 | 495 | |
5de34470 | 496 | km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); |
d4f5a1f4 DH |
497 | if (!km) { |
498 | r = -EINVAL; | |
5de34470 | 499 | goto finish; |
d4f5a1f4 DH |
500 | } |
501 | ||
502 | r = 0; | |
503 | ||
5de34470 LP |
504 | finish: |
505 | if (symbol_xkb_keymap_unref && km) | |
506 | symbol_xkb_keymap_unref(km); | |
507 | ||
508 | if (symbol_xkb_context_unref && ctx) | |
509 | symbol_xkb_context_unref(ctx); | |
510 | ||
511 | (void) dlclose(dl); | |
d4f5a1f4 DH |
512 | return r; |
513 | } | |
5de34470 | 514 | |
d4f5a1f4 | 515 | #else |
5de34470 | 516 | |
d4f5a1f4 DH |
517 | static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { |
518 | return 0; | |
519 | } | |
5de34470 | 520 | |
d4f5a1f4 DH |
521 | #endif |
522 | ||
19070062 | 523 | static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
8d451309 | 524 | Context *c = userdata; |
8d451309 | 525 | const char *layout, *model, *variant, *options; |
102d8f81 | 526 | int convert, interactive; |
8d451309 | 527 | int r; |
1822350d | 528 | |
19070062 LP |
529 | assert(m); |
530 | assert(c); | |
531 | ||
8d451309 KS |
532 | r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive); |
533 | if (r < 0) | |
ebcf1f97 | 534 | return r; |
1822350d | 535 | |
3c6f7c34 LP |
536 | layout = empty_to_null(layout); |
537 | model = empty_to_null(model); | |
538 | variant = empty_to_null(variant); | |
539 | options = empty_to_null(options); | |
1822350d | 540 | |
8d451309 KS |
541 | if (!streq_ptr(layout, c->x11_layout) || |
542 | !streq_ptr(model, c->x11_model) || | |
543 | !streq_ptr(variant, c->x11_variant) || | |
544 | !streq_ptr(options, c->x11_options)) { | |
1822350d | 545 | |
8d451309 KS |
546 | if ((layout && !string_is_safe(layout)) || |
547 | (model && !string_is_safe(model)) || | |
548 | (variant && !string_is_safe(variant)) || | |
549 | (options && !string_is_safe(options))) | |
e74f76ca | 550 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data"); |
1822350d | 551 | |
c529695e LP |
552 | r = bus_verify_polkit_async( |
553 | m, | |
554 | CAP_SYS_ADMIN, | |
555 | "org.freedesktop.locale1.set-keyboard", | |
403ed0e5 | 556 | NULL, |
c529695e LP |
557 | interactive, |
558 | UID_INVALID, | |
4897d1dc | 559 | &polkit_registry, |
c529695e | 560 | error); |
8d451309 | 561 | if (r < 0) |
ebcf1f97 | 562 | return r; |
8d451309 KS |
563 | if (r == 0) |
564 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
565 | ||
8623d3a3 | 566 | r = verify_xkb_rmlvo(model, layout, variant, options); |
8433e339 JS |
567 | if (r < 0) { |
568 | log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", | |
569 | strempty(model), strempty(layout), strempty(variant), strempty(options)); | |
5de34470 LP |
570 | |
571 | if (r == -EOPNOTSUPP) | |
572 | return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system."); | |
573 | ||
574 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); | |
8433e339 | 575 | } |
8623d3a3 | 576 | |
af76d302 ZJS |
577 | if (free_and_strdup(&c->x11_layout, layout) < 0 || |
578 | free_and_strdup(&c->x11_model, model) < 0 || | |
579 | free_and_strdup(&c->x11_variant, variant) < 0 || | |
580 | free_and_strdup(&c->x11_options, options) < 0) | |
8d451309 | 581 | return -ENOMEM; |
1822350d | 582 | |
e78af5ff | 583 | r = x11_write_data(c); |
8d451309 | 584 | if (r < 0) { |
da927ba9 | 585 | log_error_errno(r, "Failed to set X11 keyboard layout: %m"); |
048c386e | 586 | return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %m"); |
1822350d | 587 | } |
1822350d | 588 | |
502f9614 ZJS |
589 | log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", |
590 | strempty(c->x11_layout), | |
591 | strempty(c->x11_model), | |
592 | strempty(c->x11_variant), | |
593 | strempty(c->x11_options)); | |
1822350d | 594 | |
19070062 LP |
595 | (void) sd_bus_emit_properties_changed( |
596 | sd_bus_message_get_bus(m), | |
8d451309 KS |
597 | "/org/freedesktop/locale1", |
598 | "org.freedesktop.locale1", | |
c168eb67 | 599 | "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); |
1822350d | 600 | |
8d451309 | 601 | if (convert) { |
4897d1dc | 602 | r = x11_convert_to_vconsole_and_emit(c, sd_bus_message_get_bus(m)); |
8d451309 | 603 | if (r < 0) |
da927ba9 | 604 | log_error_errno(r, "Failed to convert keymap data: %m"); |
8d451309 | 605 | } |
1822350d KS |
606 | } |
607 | ||
df2d202e | 608 | return sd_bus_reply_method_return(m, NULL); |
1822350d KS |
609 | } |
610 | ||
8d451309 KS |
611 | static const sd_bus_vtable locale_vtable[] = { |
612 | SD_BUS_VTABLE_START(0), | |
6d1bd3b2 LP |
613 | SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
614 | SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
615 | SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
616 | SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
617 | SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
618 | SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
619 | SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
adacb957 LP |
620 | SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED), |
621 | SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), | |
622 | SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), | |
8d451309 KS |
623 | SD_BUS_VTABLE_END |
624 | }; | |
625 | ||
626 | static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { | |
4afd3348 | 627 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; |
1822350d KS |
628 | int r; |
629 | ||
8d451309 KS |
630 | assert(c); |
631 | assert(event); | |
1822350d KS |
632 | assert(_bus); |
633 | ||
76b54375 | 634 | r = sd_bus_default_system(&bus); |
f647962d MS |
635 | if (r < 0) |
636 | return log_error_errno(r, "Failed to get system bus connection: %m"); | |
1822350d | 637 | |
19befb2d | 638 | r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c); |
f647962d MS |
639 | if (r < 0) |
640 | return log_error_errno(r, "Failed to register object: %m"); | |
1822350d | 641 | |
0c0b9306 | 642 | r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL); |
f647962d | 643 | if (r < 0) |
0c0b9306 | 644 | return log_error_errno(r, "Failed to request name: %m"); |
1822350d | 645 | |
8d451309 | 646 | r = sd_bus_attach_event(bus, event, 0); |
f647962d MS |
647 | if (r < 0) |
648 | return log_error_errno(r, "Failed to attach bus to event loop: %m"); | |
1822350d | 649 | |
1cc6c93a | 650 | *_bus = TAKE_PTR(bus); |
1822350d | 651 | |
8d451309 | 652 | return 0; |
1822350d KS |
653 | } |
654 | ||
655 | int main(int argc, char *argv[]) { | |
28efac0d | 656 | _cleanup_(context_free) Context context = {}; |
4afd3348 LP |
657 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; |
658 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
1822350d | 659 | int r; |
1822350d KS |
660 | |
661 | log_set_target(LOG_TARGET_AUTO); | |
662 | log_parse_environment(); | |
663 | log_open(); | |
8d451309 | 664 | |
1822350d | 665 | umask(0022); |
c3dacc8b | 666 | mac_selinux_init(); |
1822350d | 667 | |
1822350d KS |
668 | if (argc != 1) { |
669 | log_error("This program takes no arguments."); | |
670 | r = -EINVAL; | |
671 | goto finish; | |
672 | } | |
673 | ||
afc6adb5 | 674 | r = sd_event_default(&event); |
1822350d | 675 | if (r < 0) { |
da927ba9 | 676 | log_error_errno(r, "Failed to allocate event loop: %m"); |
1822350d KS |
677 | goto finish; |
678 | } | |
679 | ||
cde93897 LP |
680 | sd_event_set_watchdog(event, true); |
681 | ||
8d451309 | 682 | r = connect_bus(&context, event, &bus); |
1822350d KS |
683 | if (r < 0) |
684 | goto finish; | |
685 | ||
8d451309 KS |
686 | r = context_read_data(&context); |
687 | if (r < 0) { | |
da927ba9 | 688 | log_error_errno(r, "Failed to read locale data: %m"); |
8d451309 KS |
689 | goto finish; |
690 | } | |
1822350d | 691 | |
37224a5f | 692 | r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); |
4897d1dc | 693 | if (r < 0) |
da927ba9 | 694 | log_error_errno(r, "Failed to run event loop: %m"); |
1822350d | 695 | |
1822350d | 696 | finish: |
4897d1dc ZJS |
697 | bus_verify_polkit_async_registry_free(polkit_registry); |
698 | ||
1822350d KS |
699 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
700 | } |