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