]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
1822350d | 2 | |
1822350d | 3 | #include <errno.h> |
ca78ad1d ZJS |
4 | #include <sys/stat.h> |
5 | #include <sys/types.h> | |
1822350d KS |
6 | #include <unistd.h> |
7 | ||
8d451309 KS |
8 | #include "sd-bus.h" |
9 | ||
b5efdb8a | 10 | #include "alloc-util.h" |
8d451309 | 11 | #include "bus-error.h" |
59228d0d | 12 | #include "bus-locator.h" |
ac9f55ed | 13 | #include "bus-log-control-api.h" |
8d451309 | 14 | #include "bus-message.h" |
269e4d2d | 15 | #include "bus-polkit.h" |
a9399358 | 16 | #include "bus-unit-util.h" |
28db6fbf | 17 | #include "constants.h" |
9ef70c06 | 18 | #include "kbd-util.h" |
3e5203b3 | 19 | #include "localed-util.h" |
4897d1dc | 20 | #include "macro.h" |
c6f09e6a | 21 | #include "main-func.h" |
36dd5ffd | 22 | #include "missing_capability.h" |
bb15fafe | 23 | #include "path-util.h" |
d7b8eec7 | 24 | #include "selinux-util.h" |
fc021a5b | 25 | #include "service-util.h" |
50008ae4 | 26 | #include "signal-util.h" |
4897d1dc | 27 | #include "string-util.h" |
bb15fafe | 28 | #include "strv.h" |
ee104e11 | 29 | #include "user-util.h" |
d4f5a1f4 | 30 | |
8d451309 | 31 | static int vconsole_reload(sd_bus *bus) { |
4afd3348 | 32 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
1822350d | 33 | int r; |
1822350d KS |
34 | |
35 | assert(bus); | |
36 | ||
59228d0d | 37 | r = bus_call_method(bus, bus_systemd_mgr, "RestartUnit", &error, NULL, "ss", "systemd-vconsole-setup.service", "replace"); |
8d451309 | 38 | if (r < 0) |
26a93376 | 39 | return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); |
4ae25393 | 40 | return 0; |
1822350d KS |
41 | } |
42 | ||
ebcf1f97 LP |
43 | static int property_get_locale( |
44 | sd_bus *bus, | |
45 | const char *path, | |
46 | const char *interface, | |
47 | const char *property, | |
48 | sd_bus_message *reply, | |
49 | void *userdata, | |
50 | sd_bus_error *error) { | |
51 | ||
76400a62 | 52 | Context *c = ASSERT_PTR(userdata); |
b47d419c | 53 | _cleanup_strv_free_ char **l = NULL; |
ab4ab13c | 54 | int r; |
df4fd2c7 YW |
55 | |
56 | r = locale_read_data(c, reply); | |
57 | if (r < 0) | |
58 | return r; | |
1822350d | 59 | |
3d36b5d7 YW |
60 | r = locale_context_build_env(&c->locale_context, &l, NULL); |
61 | if (r < 0) | |
62 | return r; | |
1822350d | 63 | |
8d451309 | 64 | return sd_bus_message_append_strv(reply, l); |
1822350d KS |
65 | } |
66 | ||
df4fd2c7 YW |
67 | static int property_get_vconsole( |
68 | sd_bus *bus, | |
69 | const char *path, | |
70 | const char *interface, | |
71 | const char *property, | |
72 | sd_bus_message *reply, | |
73 | void *userdata, | |
74 | sd_bus_error *error) { | |
75 | ||
76400a62 | 76 | Context *c = ASSERT_PTR(userdata); |
df4fd2c7 YW |
77 | int r; |
78 | ||
76400a62 YW |
79 | assert(property); |
80 | ||
df4fd2c7 YW |
81 | r = vconsole_read_data(c, reply); |
82 | if (r < 0) | |
83 | return r; | |
84 | ||
85 | if (streq(property, "VConsoleKeymap")) | |
ba4a886f | 86 | return sd_bus_message_append_basic(reply, 's', c->vc.keymap); |
76400a62 | 87 | if (streq(property, "VConsoleKeymapToggle")) |
ba4a886f | 88 | return sd_bus_message_append_basic(reply, 's', c->vc.toggle); |
df4fd2c7 YW |
89 | |
90 | return -EINVAL; | |
91 | } | |
92 | ||
93 | static int property_get_xkb( | |
94 | sd_bus *bus, | |
95 | const char *path, | |
96 | const char *interface, | |
97 | const char *property, | |
98 | sd_bus_message *reply, | |
99 | void *userdata, | |
100 | sd_bus_error *error) { | |
101 | ||
76400a62 | 102 | Context *c = ASSERT_PTR(userdata); |
b41ec10a | 103 | const X11Context *xc; |
df4fd2c7 YW |
104 | int r; |
105 | ||
76400a62 YW |
106 | assert(property); |
107 | ||
189cacab YW |
108 | r = vconsole_read_data(c, reply); |
109 | if (r < 0) | |
110 | return r; | |
111 | ||
df4fd2c7 YW |
112 | r = x11_read_data(c, reply); |
113 | if (r < 0) | |
114 | return r; | |
115 | ||
f59d83af | 116 | xc = context_get_x11_context(c); |
b41ec10a | 117 | |
df4fd2c7 | 118 | if (streq(property, "X11Layout")) |
b41ec10a | 119 | return sd_bus_message_append_basic(reply, 's', xc->layout); |
76400a62 | 120 | if (streq(property, "X11Model")) |
b41ec10a | 121 | return sd_bus_message_append_basic(reply, 's', xc->model); |
76400a62 | 122 | if (streq(property, "X11Variant")) |
b41ec10a | 123 | return sd_bus_message_append_basic(reply, 's', xc->variant); |
76400a62 | 124 | if (streq(property, "X11Options")) |
b41ec10a | 125 | return sd_bus_message_append_basic(reply, 's', xc->options); |
df4fd2c7 YW |
126 | |
127 | return -EINVAL; | |
128 | } | |
129 | ||
a00a78b8 LP |
130 | static int process_locale_list_item( |
131 | const char *assignment, | |
132 | char *new_locale[static _VARIABLE_LC_MAX], | |
8f20232f | 133 | bool use_localegen, |
a00a78b8 LP |
134 | sd_bus_error *error) { |
135 | ||
136 | assert(assignment); | |
137 | assert(new_locale); | |
138 | ||
139 | for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { | |
140 | const char *name, *e; | |
141 | ||
142 | assert_se(name = locale_variable_to_string(p)); | |
143 | ||
144 | e = startswith(assignment, name); | |
145 | if (!e) | |
146 | continue; | |
147 | ||
148 | if (*e != '=') | |
149 | continue; | |
150 | ||
151 | e++; | |
152 | ||
153 | if (!locale_is_valid(e)) | |
154 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e); | |
8f20232f | 155 | if (!use_localegen && locale_is_installed(e) <= 0) |
a00a78b8 LP |
156 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e); |
157 | if (new_locale[p]) | |
158 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name); | |
159 | ||
160 | new_locale[p] = strdup(e); | |
161 | if (!new_locale[p]) | |
162 | return -ENOMEM; | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment); | |
168 | } | |
169 | ||
76400a62 | 170 | static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX], sd_bus_error *error) { |
8f20232f | 171 | int r; |
76400a62 | 172 | |
8f20232f MK |
173 | assert(new_locale); |
174 | ||
175 | for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { | |
176 | if (p == VARIABLE_LANGUAGE) | |
177 | continue; | |
178 | if (isempty(new_locale[p])) | |
179 | continue; | |
180 | if (locale_is_installed(new_locale[p])) | |
181 | continue; | |
182 | ||
183 | r = locale_gen_enable_locale(new_locale[p]); | |
184 | if (r == -ENOEXEC) { | |
185 | log_error_errno(r, "Refused to enable locale for generation: %m"); | |
186 | return sd_bus_error_setf(error, | |
187 | SD_BUS_ERROR_INVALID_ARGS, | |
188 | "Specified locale is not installed and non-UTF-8 locale will not be auto-generated: %s", | |
189 | new_locale[p]); | |
76400a62 YW |
190 | } |
191 | if (r == -EINVAL) { | |
8f20232f MK |
192 | log_error_errno(r, "Failed to enable invalid locale %s for generation.", new_locale[p]); |
193 | return sd_bus_error_setf(error, | |
194 | SD_BUS_ERROR_INVALID_ARGS, | |
cee60fc3 | 195 | "Cannot enable locale generation for invalid locale: %s", |
8f20232f | 196 | new_locale[p]); |
76400a62 YW |
197 | } |
198 | if (r < 0) { | |
8f20232f MK |
199 | log_error_errno(r, "Failed to enable locale for generation: %m"); |
200 | return sd_bus_error_set_errnof(error, r, "Failed to enable locale generation: %m"); | |
201 | } | |
202 | ||
203 | r = locale_gen_run(); | |
204 | if (r < 0) { | |
205 | log_error_errno(r, "Failed to generate locale: %m"); | |
206 | return sd_bus_error_set_errnof(error, r, "Failed to generate locale: %m"); | |
207 | } | |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
19070062 | 213 | static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
e6755a33 | 214 | _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {}; |
3d36b5d7 | 215 | _cleanup_strv_free_ char **l = NULL, **l_set = NULL, **l_unset = NULL; |
99534007 | 216 | Context *c = ASSERT_PTR(userdata); |
a00a78b8 | 217 | int interactive, r; |
8f20232f | 218 | bool use_localegen; |
1822350d | 219 | |
19070062 | 220 | assert(m); |
19070062 | 221 | |
a00a78b8 | 222 | r = sd_bus_message_read_strv(m, &l); |
8d451309 | 223 | if (r < 0) |
e0a72001 | 224 | return bus_log_parse_error(r); |
1822350d | 225 | |
8d451309 KS |
226 | r = sd_bus_message_read_basic(m, 'b', &interactive); |
227 | if (r < 0) | |
e0a72001 | 228 | return bus_log_parse_error(r); |
1822350d | 229 | |
8f20232f MK |
230 | use_localegen = locale_gen_check_available(); |
231 | ||
4156e767 | 232 | /* If single locale without variable name is provided, then we assume it is LANG=. */ |
a00a78b8 LP |
233 | if (strv_length(l) == 1 && !strchr(l[0], '=')) { |
234 | if (!locale_is_valid(l[0])) | |
235 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]); | |
8f20232f | 236 | if (!use_localegen && locale_is_installed(l[0]) <= 0) |
a00a78b8 | 237 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]); |
4156e767 | 238 | |
a00a78b8 | 239 | new_locale[VARIABLE_LANG] = strdup(l[0]); |
4156e767 | 240 | if (!new_locale[VARIABLE_LANG]) |
e0a72001 | 241 | return log_oom(); |
4156e767 YW |
242 | |
243 | l = strv_free(l); | |
244 | } | |
245 | ||
df4fd2c7 | 246 | /* Check whether a variable is valid */ |
8d451309 | 247 | STRV_FOREACH(i, l) { |
8f20232f | 248 | r = process_locale_list_item(*i, new_locale, use_localegen, error); |
a00a78b8 LP |
249 | if (r < 0) |
250 | return r; | |
8d451309 | 251 | } |
1822350d | 252 | |
4e829d21 ZJS |
253 | /* If LANG was specified, but not LANGUAGE, check if we should |
254 | * set it based on the language fallback table. */ | |
df4fd2c7 YW |
255 | if (!isempty(new_locale[VARIABLE_LANG]) && |
256 | isempty(new_locale[VARIABLE_LANGUAGE])) { | |
4e829d21 ZJS |
257 | _cleanup_free_ char *language = NULL; |
258 | ||
df4fd2c7 | 259 | (void) find_language_fallback(new_locale[VARIABLE_LANG], &language); |
4e829d21 | 260 | if (language) { |
df4fd2c7 YW |
261 | log_debug("Converted LANG=%s to LANGUAGE=%s", new_locale[VARIABLE_LANG], language); |
262 | free_and_replace(new_locale[VARIABLE_LANGUAGE], language); | |
4e829d21 ZJS |
263 | } |
264 | } | |
265 | ||
df4fd2c7 YW |
266 | r = locale_read_data(c, m); |
267 | if (r < 0) { | |
268 | log_error_errno(r, "Failed to read locale data: %m"); | |
1b09b81c | 269 | return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read locale data"); |
df4fd2c7 | 270 | } |
8d451309 | 271 | |
df4fd2c7 | 272 | /* Merge with the current settings */ |
3d36b5d7 YW |
273 | r = locale_context_merge(&c->locale_context, new_locale); |
274 | if (r < 0) | |
e0a72001 | 275 | return log_oom(); |
1822350d | 276 | |
3d36b5d7 | 277 | locale_variables_simplify(new_locale); |
1822350d | 278 | |
3d36b5d7 | 279 | if (locale_context_equal(&c->locale_context, new_locale)) { |
df4fd2c7 YW |
280 | log_debug("Locale settings were not modified."); |
281 | return sd_bus_reply_method_return(m, NULL); | |
282 | } | |
1822350d | 283 | |
7b36fb9f | 284 | r = bus_verify_polkit_async_full( |
df4fd2c7 | 285 | m, |
df4fd2c7 | 286 | "org.freedesktop.locale1.set-locale", |
7b36fb9f | 287 | /* details= */ NULL, |
df4fd2c7 | 288 | interactive, |
7b36fb9f | 289 | /* good_user= */ UID_INVALID, |
af7865c1 | 290 | &c->polkit_registry, |
df4fd2c7 YW |
291 | error); |
292 | if (r < 0) | |
293 | return r; | |
294 | if (r == 0) | |
295 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
1822350d | 296 | |
8f20232f MK |
297 | /* Generate locale in case it is missing and the system is using locale-gen */ |
298 | if (use_localegen) { | |
299 | r = locale_gen_process_locale(new_locale, error); | |
300 | if (r < 0) | |
301 | return r; | |
302 | } | |
303 | ||
3d36b5d7 | 304 | locale_context_take(&c->locale_context, new_locale); |
1822350d | 305 | |
8f20232f | 306 | /* Write locale configuration */ |
3d36b5d7 | 307 | r = locale_context_save(&c->locale_context, &l_set, &l_unset); |
df4fd2c7 YW |
308 | if (r < 0) { |
309 | log_error_errno(r, "Failed to set locale: %m"); | |
310 | return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m"); | |
311 | } | |
502f9614 | 312 | |
c8966e81 FB |
313 | /* Since we just updated the locale configuration file, ask the system manager to read it again to |
314 | * update its default locale settings. It's important to not use UnsetAndSetEnvironment or a similar | |
315 | * method because in this case unsetting variables means restoring them to PID1 default values, which | |
316 | * may be outdated, since locale.conf has just changed and PID1 hasn't read it */ | |
a9399358 | 317 | (void) bus_service_manager_reload(sd_bus_message_get_bus(m)); |
1822350d | 318 | |
3d36b5d7 | 319 | if (!strv_isempty(l_set)) { |
c2b2df60 | 320 | _cleanup_free_ char *line = NULL; |
df4fd2c7 | 321 | |
3d36b5d7 | 322 | line = strv_join(l_set, ", "); |
df4fd2c7 | 323 | log_info("Changed locale to %s.", strnull(line)); |
502f9614 | 324 | } else |
df4fd2c7 YW |
325 | log_info("Changed locale to unset."); |
326 | ||
327 | (void) sd_bus_emit_properties_changed( | |
328 | sd_bus_message_get_bus(m), | |
329 | "/org/freedesktop/locale1", | |
330 | "org.freedesktop.locale1", | |
331 | "Locale", NULL); | |
502f9614 | 332 | |
df2d202e | 333 | return sd_bus_reply_method_return(m, NULL); |
8d451309 | 334 | } |
1822350d | 335 | |
19070062 | 336 | static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
f41338da | 337 | _cleanup_(x11_context_clear) X11Context converted = {}; |
99534007 | 338 | Context *c = ASSERT_PTR(userdata); |
b47ff73b | 339 | int convert, interactive, r; |
71fa933b | 340 | bool x_needs_update; |
ba4a886f | 341 | VCContext in; |
f2cc3753 | 342 | |
19070062 | 343 | assert(m); |
19070062 | 344 | |
ba4a886f | 345 | r = sd_bus_message_read(m, "ssbb", &in.keymap, &in.toggle, &convert, &interactive); |
8d451309 | 346 | if (r < 0) |
e0a72001 | 347 | return bus_log_parse_error(r); |
1822350d | 348 | |
ba4a886f | 349 | vc_context_empty_to_null(&in); |
1822350d | 350 | |
384f22e3 YW |
351 | r = vc_context_verify_and_warn(&in, LOG_ERR, error); |
352 | if (r < 0) | |
353 | return r; | |
df4fd2c7 | 354 | |
800f65f8 YW |
355 | r = vconsole_read_data(c, m); |
356 | if (r < 0) { | |
357 | log_error_errno(r, "Failed to read virtual console keymap data: %m"); | |
358 | return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); | |
359 | } | |
360 | ||
f41338da YW |
361 | r = x11_read_data(c, m); |
362 | if (r < 0) { | |
363 | log_error_errno(r, "Failed to read X11 keyboard layout data: %m"); | |
364 | return sd_bus_error_set_errnof(error, r, "Failed to read X11 keyboard layout data: %m"); | |
365 | } | |
366 | ||
367 | if (convert) { | |
368 | r = vconsole_convert_to_x11(&in, &converted); | |
369 | if (r < 0) { | |
370 | log_error_errno(r, "Failed to convert keymap data: %m"); | |
371 | return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); | |
372 | } | |
373 | ||
8669bbb5 YW |
374 | if (x11_context_isempty(&converted)) |
375 | log_notice("No conversion found for virtual console keymap \"%s\".", strempty(in.keymap)); | |
376 | else | |
377 | log_info("The virtual console keymap '%s' is converted to X11 keyboard layout '%s' model '%s' variant '%s' options '%s'", | |
378 | in.keymap, strempty(converted.layout), strempty(converted.model), strempty(converted.variant), strempty(converted.options)); | |
379 | ||
f41338da YW |
380 | /* save the result of conversion to emit changed properties later. */ |
381 | x_needs_update = !x11_context_equal(&c->x11_from_vc, &converted) || !x11_context_equal(&c->x11_from_xorg, &converted); | |
382 | } else | |
383 | x_needs_update = !x11_context_equal(&c->x11_from_vc, &c->x11_from_xorg); | |
384 | ||
385 | if (vc_context_equal(&c->vc, &in) && !x_needs_update) | |
b47ff73b | 386 | return sd_bus_reply_method_return(m, NULL); |
1822350d | 387 | |
7b36fb9f | 388 | r = bus_verify_polkit_async_full( |
b47ff73b | 389 | m, |
b47ff73b | 390 | "org.freedesktop.locale1.set-keyboard", |
7b36fb9f | 391 | /* details= */ NULL, |
b47ff73b | 392 | interactive, |
7b36fb9f | 393 | /* good_user= */ UID_INVALID, |
af7865c1 | 394 | &c->polkit_registry, |
b47ff73b YW |
395 | error); |
396 | if (r < 0) | |
397 | return r; | |
398 | if (r == 0) | |
399 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
1822350d | 400 | |
ba4a886f YW |
401 | r = vc_context_copy(&c->vc, &in); |
402 | if (r < 0) | |
403 | return log_oom(); | |
0b507b17 | 404 | |
f41338da YW |
405 | if (x_needs_update) { |
406 | if (convert) { | |
407 | r = x11_context_copy(&c->x11_from_vc, &converted); | |
408 | if (r < 0) | |
409 | return log_oom(); | |
410 | x11_context_replace(&c->x11_from_xorg, &converted); | |
411 | } else { | |
412 | const X11Context *xc = context_get_x11_context(c); | |
413 | ||
414 | /* Even if the conversion is not requested, sync the two X11 contexts. */ | |
415 | r = x11_context_copy(&c->x11_from_vc, xc); | |
416 | if (r < 0) | |
417 | return log_oom(); | |
418 | ||
419 | r = x11_context_copy(&c->x11_from_xorg, xc); | |
420 | if (r < 0) | |
421 | return log_oom(); | |
4f34fd5d | 422 | } |
4f34fd5d YW |
423 | } |
424 | ||
b47ff73b | 425 | r = vconsole_write_data(c); |
50ec8245 YW |
426 | if (r < 0) |
427 | log_warning_errno(r, "Failed to write virtual console keymap, ignoring: %m"); | |
1822350d | 428 | |
71fa933b | 429 | if (x_needs_update) { |
4f34fd5d YW |
430 | r = x11_write_data(c); |
431 | if (r < 0) | |
432 | log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m"); | |
433 | } | |
434 | ||
b47ff73b | 435 | log_info("Changed virtual console keymap to '%s' toggle '%s'", |
ba4a886f | 436 | strempty(c->vc.keymap), strempty(c->vc.toggle)); |
1822350d | 437 | |
26a93376 | 438 | (void) vconsole_reload(sd_bus_message_get_bus(m)); |
1822350d | 439 | |
b47ff73b YW |
440 | (void) sd_bus_emit_properties_changed( |
441 | sd_bus_message_get_bus(m), | |
442 | "/org/freedesktop/locale1", | |
443 | "org.freedesktop.locale1", | |
4f34fd5d | 444 | "VConsoleKeymap", "VConsoleKeymapToggle", |
71fa933b YW |
445 | x_needs_update ? "X11Layout" : NULL, |
446 | x_needs_update ? "X11Model" : NULL, | |
447 | x_needs_update ? "X11Variant" : NULL, | |
448 | x_needs_update ? "X11Options" : NULL, | |
4f34fd5d | 449 | NULL); |
1822350d | 450 | |
df2d202e | 451 | return sd_bus_reply_method_return(m, NULL); |
8d451309 | 452 | } |
1822350d | 453 | |
19070062 | 454 | static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
f41338da | 455 | _cleanup_(vc_context_clear) VCContext converted = {}; |
99534007 | 456 | Context *c = ASSERT_PTR(userdata); |
b47ff73b | 457 | int convert, interactive, r; |
71fa933b | 458 | X11Context in; |
1822350d | 459 | |
19070062 | 460 | assert(m); |
19070062 | 461 | |
b41ec10a | 462 | r = sd_bus_message_read(m, "ssssbb", &in.layout, &in.model, &in.variant, &in.options, &convert, &interactive); |
8d451309 | 463 | if (r < 0) |
e0a72001 | 464 | return bus_log_parse_error(r); |
1822350d | 465 | |
b41ec10a | 466 | x11_context_empty_to_null(&in); |
1822350d | 467 | |
0327d5b2 YW |
468 | r = x11_context_verify_and_warn(&in, LOG_ERR, error); |
469 | if (r < 0) | |
470 | return r; | |
fe28d887 | 471 | |
189cacab YW |
472 | r = vconsole_read_data(c, m); |
473 | if (r < 0) { | |
474 | log_error_errno(r, "Failed to read virtual console keymap data: %m"); | |
475 | return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); | |
476 | } | |
477 | ||
800f65f8 YW |
478 | r = x11_read_data(c, m); |
479 | if (r < 0) { | |
480 | log_error_errno(r, "Failed to read x11 keyboard layout data: %m"); | |
481 | return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data"); | |
482 | } | |
483 | ||
f41338da YW |
484 | if (convert) { |
485 | r = x11_convert_to_vconsole(&in, &converted); | |
486 | if (r < 0) { | |
487 | log_error_errno(r, "Failed to convert keymap data: %m"); | |
488 | return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m"); | |
489 | } | |
490 | ||
8669bbb5 YW |
491 | if (vc_context_isempty(&converted)) |
492 | /* We search for layout-variant match first, but then we also look | |
493 | * for anything which matches just the layout. So it's accurate to say | |
494 | * that we couldn't find anything which matches the layout. */ | |
495 | log_notice("No conversion to virtual console map found for \"%s\".", strempty(in.layout)); | |
496 | else | |
497 | log_info("The X11 keyboard layout '%s' is converted to virtual console keymap '%s'", | |
498 | in.layout, converted.keymap); | |
499 | ||
f41338da YW |
500 | /* save the result of conversion to emit changed properties later. */ |
501 | convert = !vc_context_equal(&c->vc, &converted); | |
502 | } | |
503 | ||
504 | if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert) | |
800f65f8 YW |
505 | return sd_bus_reply_method_return(m, NULL); |
506 | ||
7b36fb9f | 507 | r = bus_verify_polkit_async_full( |
b47ff73b | 508 | m, |
b47ff73b | 509 | "org.freedesktop.locale1.set-keyboard", |
7b36fb9f | 510 | /* details= */ NULL, |
b47ff73b | 511 | interactive, |
7b36fb9f | 512 | /* good_user= */ UID_INVALID, |
af7865c1 | 513 | &c->polkit_registry, |
b47ff73b YW |
514 | error); |
515 | if (r < 0) | |
516 | return r; | |
517 | if (r == 0) | |
518 | return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ | |
1822350d | 519 | |
71fa933b YW |
520 | r = x11_context_copy(&c->x11_from_vc, &in); |
521 | if (r < 0) | |
522 | return log_oom(); | |
523 | ||
524 | r = x11_context_copy(&c->x11_from_xorg, &in); | |
b41ec10a YW |
525 | if (r < 0) |
526 | return log_oom(); | |
1822350d | 527 | |
f41338da | 528 | if (convert) |
90005a4f | 529 | vc_context_replace(&c->vc, &converted); |
4f34fd5d | 530 | |
8589823f YW |
531 | r = vconsole_write_data(c); |
532 | if (r < 0) | |
533 | log_warning_errno(r, "Failed to update vconsole.conf, ignoring: %m"); | |
4f34fd5d | 534 | |
b47ff73b | 535 | r = x11_write_data(c); |
50ec8245 YW |
536 | if (r < 0) |
537 | log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m"); | |
1822350d | 538 | |
b47ff73b | 539 | log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", |
b41ec10a YW |
540 | strempty(in.layout), |
541 | strempty(in.model), | |
542 | strempty(in.variant), | |
543 | strempty(in.options)); | |
1822350d | 544 | |
b47ff73b YW |
545 | (void) sd_bus_emit_properties_changed( |
546 | sd_bus_message_get_bus(m), | |
547 | "/org/freedesktop/locale1", | |
548 | "org.freedesktop.locale1", | |
4f34fd5d YW |
549 | "X11Layout", "X11Model", "X11Variant", "X11Options", |
550 | convert ? "VConsoleKeymap" : NULL, | |
551 | convert ? "VConsoleKeymapToggle" : NULL, | |
552 | NULL); | |
1822350d | 553 | |
4f34fd5d YW |
554 | if (convert) |
555 | (void) vconsole_reload(sd_bus_message_get_bus(m)); | |
1822350d | 556 | |
df2d202e | 557 | return sd_bus_reply_method_return(m, NULL); |
1822350d KS |
558 | } |
559 | ||
8d451309 KS |
560 | static const sd_bus_vtable locale_vtable[] = { |
561 | SD_BUS_VTABLE_START(0), | |
6d1bd3b2 | 562 | SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
df4fd2c7 YW |
563 | SD_BUS_PROPERTY("X11Layout", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
564 | SD_BUS_PROPERTY("X11Model", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
565 | SD_BUS_PROPERTY("X11Variant", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
566 | SD_BUS_PROPERTY("X11Options", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
567 | SD_BUS_PROPERTY("VConsoleKeymap", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
568 | SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
5cc34d6d | 569 | |
71243af7 OJ |
570 | SD_BUS_METHOD_WITH_ARGS("SetLocale", |
571 | SD_BUS_ARGS("as", locale, "b", interactive), | |
572 | SD_BUS_NO_RESULT, | |
573 | method_set_locale, | |
574 | SD_BUS_VTABLE_UNPRIVILEGED), | |
575 | SD_BUS_METHOD_WITH_ARGS("SetVConsoleKeyboard", | |
576 | SD_BUS_ARGS("s", keymap, "s", keymap_toggle, "b", convert, "b", interactive), | |
577 | SD_BUS_NO_RESULT, | |
578 | method_set_vc_keyboard, | |
579 | SD_BUS_VTABLE_UNPRIVILEGED), | |
580 | SD_BUS_METHOD_WITH_ARGS("SetX11Keyboard", | |
581 | SD_BUS_ARGS("s", layout, "s", model, "s", variant, "s", options, "b", convert, "b", interactive), | |
582 | SD_BUS_NO_RESULT, | |
583 | method_set_x11_keyboard, | |
584 | SD_BUS_VTABLE_UNPRIVILEGED), | |
5cc34d6d | 585 | |
8d451309 KS |
586 | SD_BUS_VTABLE_END |
587 | }; | |
588 | ||
5ceceff1 ZJS |
589 | static const BusObjectImplementation manager_object = { |
590 | "/org/freedesktop/locale1", | |
591 | "org.freedesktop.locale1", | |
592 | .vtables = BUS_VTABLES(locale_vtable), | |
593 | }; | |
594 | ||
8d451309 | 595 | static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { |
4afd3348 | 596 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; |
1822350d KS |
597 | int r; |
598 | ||
8d451309 KS |
599 | assert(c); |
600 | assert(event); | |
1822350d KS |
601 | assert(_bus); |
602 | ||
76b54375 | 603 | r = sd_bus_default_system(&bus); |
f647962d MS |
604 | if (r < 0) |
605 | return log_error_errno(r, "Failed to get system bus connection: %m"); | |
1822350d | 606 | |
5ceceff1 | 607 | r = bus_add_implementation(bus, &manager_object, c); |
f647962d | 608 | if (r < 0) |
5ceceff1 | 609 | return r; |
1822350d | 610 | |
ac9f55ed LP |
611 | r = bus_log_control_api_register(bus); |
612 | if (r < 0) | |
613 | return r; | |
614 | ||
0c0b9306 | 615 | r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL); |
f647962d | 616 | if (r < 0) |
0c0b9306 | 617 | return log_error_errno(r, "Failed to request name: %m"); |
1822350d | 618 | |
8d451309 | 619 | r = sd_bus_attach_event(bus, event, 0); |
f647962d MS |
620 | if (r < 0) |
621 | return log_error_errno(r, "Failed to attach bus to event loop: %m"); | |
1822350d | 622 | |
1cc6c93a | 623 | *_bus = TAKE_PTR(bus); |
1822350d | 624 | |
8d451309 | 625 | return 0; |
1822350d KS |
626 | } |
627 | ||
c6f09e6a | 628 | static int run(int argc, char *argv[]) { |
56b7f112 | 629 | _cleanup_(context_clear) Context context = {}; |
4afd3348 LP |
630 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; |
631 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
1822350d | 632 | int r; |
1822350d | 633 | |
d2acb93d | 634 | log_setup(); |
8d451309 | 635 | |
fc021a5b ZJS |
636 | r = service_parse_argv("systemd-localed.service", |
637 | "Manage system locale settings and key mappings.", | |
5ceceff1 ZJS |
638 | BUS_IMPLEMENTATIONS(&manager_object, |
639 | &log_control_object), | |
fc021a5b ZJS |
640 | argc, argv); |
641 | if (r <= 0) | |
642 | return r; | |
643 | ||
1822350d | 644 | umask(0022); |
a9ba0e32 | 645 | |
a452c807 | 646 | r = mac_init(); |
a9ba0e32 CG |
647 | if (r < 0) |
648 | return r; | |
1822350d | 649 | |
50008ae4 YW |
650 | assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); |
651 | ||
afc6adb5 | 652 | r = sd_event_default(&event); |
c6f09e6a YW |
653 | if (r < 0) |
654 | return log_error_errno(r, "Failed to allocate event loop: %m"); | |
1822350d | 655 | |
50008ae4 YW |
656 | (void) sd_event_set_watchdog(event, true); |
657 | ||
658 | r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); | |
c6f09e6a YW |
659 | if (r < 0) |
660 | return log_error_errno(r, "Failed to install SIGINT handler: %m"); | |
50008ae4 YW |
661 | |
662 | r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); | |
c6f09e6a YW |
663 | if (r < 0) |
664 | return log_error_errno(r, "Failed to install SIGTERM handler: %m"); | |
cde93897 | 665 | |
8d451309 | 666 | r = connect_bus(&context, event, &bus); |
1822350d | 667 | if (r < 0) |
c6f09e6a | 668 | return r; |
1822350d | 669 | |
37224a5f | 670 | r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); |
4897d1dc | 671 | if (r < 0) |
c6f09e6a | 672 | return log_error_errno(r, "Failed to run event loop: %m"); |
1822350d | 673 | |
c6f09e6a | 674 | return 0; |
1822350d | 675 | } |
c6f09e6a YW |
676 | |
677 | DEFINE_MAIN_FUNCTION(run); |