LCOV - code coverage report
Current view: top level - locale - localed.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 375 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 15 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 366 0.0 %

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

Generated by: LCOV version 1.14