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