LCOV - code coverage report
Current view: top level - locale - keymap-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 192 433 44.3 %
Date: 2019-08-23 13:36:53 Functions: 13 20 65.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 125 380 32.9 %

           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                 :            : #include "bus-util.h"
      10                 :            : #include "env-file-label.h"
      11                 :            : #include "env-file.h"
      12                 :            : #include "env-util.h"
      13                 :            : #include "fd-util.h"
      14                 :            : #include "fileio-label.h"
      15                 :            : #include "fileio.h"
      16                 :            : #include "kbd-util.h"
      17                 :            : #include "keymap-util.h"
      18                 :            : #include "locale-util.h"
      19                 :            : #include "macro.h"
      20                 :            : #include "mkdir.h"
      21                 :            : #include "nulstr-util.h"
      22                 :            : #include "string-util.h"
      23                 :            : #include "strv.h"
      24                 :            : #include "tmpfile-util.h"
      25                 :            : 
      26                 :       3652 : static bool startswith_comma(const char *s, const char *prefix) {
      27                 :       3652 :         s = startswith(s, prefix);
      28         [ +  + ]:       3652 :         if (!s)
      29                 :       3628 :                 return false;
      30                 :            : 
      31         [ +  - ]:         24 :         return IN_SET(*s, ',', '\0');
      32                 :            : }
      33                 :            : 
      34                 :         56 : static const char* systemd_kbd_model_map(void) {
      35                 :            :         const char* s;
      36                 :            : 
      37                 :         56 :         s = getenv("SYSTEMD_KBD_MODEL_MAP");
      38         [ +  - ]:         56 :         if (s)
      39                 :         56 :                 return s;
      40                 :            : 
      41                 :          0 :         return SYSTEMD_KBD_MODEL_MAP;
      42                 :            : }
      43                 :            : 
      44                 :         16 : static const char* systemd_language_fallback_map(void) {
      45                 :            :         const char* s;
      46                 :            : 
      47                 :         16 :         s = getenv("SYSTEMD_LANGUAGE_FALLBACK_MAP");
      48         [ +  - ]:         16 :         if (s)
      49                 :         16 :                 return s;
      50                 :            : 
      51                 :          0 :         return SYSTEMD_LANGUAGE_FALLBACK_MAP;
      52                 :            : }
      53                 :            : 
      54                 :         16 : static void context_free_x11(Context *c) {
      55                 :         16 :         c->x11_layout = mfree(c->x11_layout);
      56                 :         16 :         c->x11_options = mfree(c->x11_options);
      57                 :         16 :         c->x11_model = mfree(c->x11_model);
      58                 :         16 :         c->x11_variant = mfree(c->x11_variant);
      59                 :         16 : }
      60                 :            : 
      61                 :         16 : static void context_free_vconsole(Context *c) {
      62                 :         16 :         c->vc_keymap = mfree(c->vc_keymap);
      63                 :         16 :         c->vc_keymap_toggle = mfree(c->vc_keymap_toggle);
      64                 :         16 : }
      65                 :            : 
      66                 :          8 : static void context_free_locale(Context *c) {
      67                 :            :         int p;
      68                 :            : 
      69         [ +  + ]:        120 :         for (p = 0; p < _VARIABLE_LC_MAX; p++)
      70                 :        112 :                 c->locale[p] = mfree(c->locale[p]);
      71                 :          8 : }
      72                 :            : 
      73                 :          8 : void context_clear(Context *c) {
      74                 :          8 :         context_free_locale(c);
      75                 :          8 :         context_free_x11(c);
      76                 :          8 :         context_free_vconsole(c);
      77                 :            : 
      78                 :          8 :         sd_bus_message_unref(c->locale_cache);
      79                 :          8 :         sd_bus_message_unref(c->x11_cache);
      80                 :          8 :         sd_bus_message_unref(c->vc_cache);
      81                 :            : 
      82                 :          8 :         bus_verify_polkit_async_registry_free(c->polkit_registry);
      83                 :          8 : };
      84                 :            : 
      85                 :          0 : void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
      86                 :            :         int p;
      87                 :            : 
      88         [ #  # ]:          0 :         for (p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++)
      89   [ #  #  #  # ]:          0 :                 if (isempty(locale[p]) || streq_ptr(locale[VARIABLE_LANG], locale[p]))
      90                 :          0 :                         locale[p] = mfree(locale[p]);
      91                 :          0 : }
      92                 :            : 
      93                 :          0 : int locale_read_data(Context *c, sd_bus_message *m) {
      94                 :            :         struct stat st;
      95                 :            :         int r;
      96                 :            : 
      97                 :            :         /* Do not try to re-read the file within single bus operation. */
      98         [ #  # ]:          0 :         if (m) {
      99         [ #  # ]:          0 :                 if (m == c->locale_cache)
     100                 :          0 :                         return 0;
     101                 :            : 
     102                 :          0 :                 sd_bus_message_unref(c->locale_cache);
     103                 :          0 :                 c->locale_cache = sd_bus_message_ref(m);
     104                 :            :         }
     105                 :            : 
     106                 :          0 :         r = stat("/etc/locale.conf", &st);
     107   [ #  #  #  # ]:          0 :         if (r < 0 && errno != ENOENT)
     108                 :          0 :                 return -errno;
     109                 :            : 
     110         [ #  # ]:          0 :         if (r >= 0) {
     111                 :            :                 usec_t t;
     112                 :            : 
     113                 :            :                 /* If mtime is not changed, then we do not need to re-read the file. */
     114                 :          0 :                 t = timespec_load(&st.st_mtim);
     115   [ #  #  #  # ]:          0 :                 if (c->locale_mtime != USEC_INFINITY && t == c->locale_mtime)
     116                 :          0 :                         return 0;
     117                 :            : 
     118                 :          0 :                 c->locale_mtime = t;
     119                 :          0 :                 context_free_locale(c);
     120                 :            : 
     121                 :          0 :                 r = parse_env_file(NULL, "/etc/locale.conf",
     122                 :            :                                    "LANG",              &c->locale[VARIABLE_LANG],
     123                 :            :                                    "LANGUAGE",          &c->locale[VARIABLE_LANGUAGE],
     124                 :            :                                    "LC_CTYPE",          &c->locale[VARIABLE_LC_CTYPE],
     125                 :            :                                    "LC_NUMERIC",        &c->locale[VARIABLE_LC_NUMERIC],
     126                 :            :                                    "LC_TIME",           &c->locale[VARIABLE_LC_TIME],
     127                 :            :                                    "LC_COLLATE",        &c->locale[VARIABLE_LC_COLLATE],
     128                 :            :                                    "LC_MONETARY",       &c->locale[VARIABLE_LC_MONETARY],
     129                 :            :                                    "LC_MESSAGES",       &c->locale[VARIABLE_LC_MESSAGES],
     130                 :            :                                    "LC_PAPER",          &c->locale[VARIABLE_LC_PAPER],
     131                 :            :                                    "LC_NAME",           &c->locale[VARIABLE_LC_NAME],
     132                 :            :                                    "LC_ADDRESS",        &c->locale[VARIABLE_LC_ADDRESS],
     133                 :            :                                    "LC_TELEPHONE",      &c->locale[VARIABLE_LC_TELEPHONE],
     134                 :            :                                    "LC_MEASUREMENT",    &c->locale[VARIABLE_LC_MEASUREMENT],
     135                 :            :                                    "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]);
     136         [ #  # ]:          0 :                 if (r < 0)
     137                 :          0 :                         return r;
     138                 :            :         } else {
     139                 :            :                 int p;
     140                 :            : 
     141                 :          0 :                 c->locale_mtime = USEC_INFINITY;
     142                 :          0 :                 context_free_locale(c);
     143                 :            : 
     144                 :            :                 /* Fill in what we got passed from systemd. */
     145         [ #  # ]:          0 :                 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
     146                 :            :                         const char *name;
     147                 :            : 
     148                 :          0 :                         name = locale_variable_to_string(p);
     149         [ #  # ]:          0 :                         assert(name);
     150                 :            : 
     151                 :          0 :                         r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name)));
     152         [ #  # ]:          0 :                         if (r < 0)
     153                 :          0 :                                 return r;
     154                 :            :                 }
     155                 :            :         }
     156                 :            : 
     157                 :          0 :         locale_simplify(c->locale);
     158                 :          0 :         return 0;
     159                 :            : }
     160                 :            : 
     161                 :          0 : int vconsole_read_data(Context *c, sd_bus_message *m) {
     162                 :            :         struct stat st;
     163                 :            :         usec_t t;
     164                 :            :         int r;
     165                 :            : 
     166                 :            :         /* Do not try to re-read the file within single bus operation. */
     167         [ #  # ]:          0 :         if (m) {
     168         [ #  # ]:          0 :                 if (m == c->vc_cache)
     169                 :          0 :                         return 0;
     170                 :            : 
     171                 :          0 :                 sd_bus_message_unref(c->vc_cache);
     172                 :          0 :                 c->vc_cache = sd_bus_message_ref(m);
     173                 :            :         }
     174                 :            : 
     175         [ #  # ]:          0 :         if (stat("/etc/vconsole.conf", &st) < 0) {
     176         [ #  # ]:          0 :                 if (errno != ENOENT)
     177                 :          0 :                         return -errno;
     178                 :            : 
     179                 :          0 :                 c->vc_mtime = USEC_INFINITY;
     180                 :          0 :                 context_free_vconsole(c);
     181                 :          0 :                 return 0;
     182                 :            :         }
     183                 :            : 
     184                 :            :         /* If mtime is not changed, then we do not need to re-read */
     185                 :          0 :         t = timespec_load(&st.st_mtim);
     186   [ #  #  #  # ]:          0 :         if (c->vc_mtime != USEC_INFINITY && t == c->vc_mtime)
     187                 :          0 :                 return 0;
     188                 :            : 
     189                 :          0 :         c->vc_mtime = t;
     190                 :          0 :         context_free_vconsole(c);
     191                 :            : 
     192                 :          0 :         r = parse_env_file(NULL, "/etc/vconsole.conf",
     193                 :            :                            "KEYMAP",        &c->vc_keymap,
     194                 :            :                            "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
     195         [ #  # ]:          0 :         if (r < 0)
     196                 :          0 :                 return r;
     197                 :            : 
     198                 :          0 :         return 0;
     199                 :            : }
     200                 :            : 
     201                 :          0 : int x11_read_data(Context *c, sd_bus_message *m) {
     202                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
     203                 :          0 :         bool in_section = false;
     204                 :            :         struct stat st;
     205                 :            :         usec_t t;
     206                 :            :         int r;
     207                 :            : 
     208                 :            :         /* Do not try to re-read the file within single bus operation. */
     209         [ #  # ]:          0 :         if (m) {
     210         [ #  # ]:          0 :                 if (m == c->x11_cache)
     211                 :          0 :                         return 0;
     212                 :            : 
     213                 :          0 :                 sd_bus_message_unref(c->x11_cache);
     214                 :          0 :                 c->x11_cache = sd_bus_message_ref(m);
     215                 :            :         }
     216                 :            : 
     217         [ #  # ]:          0 :         if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
     218         [ #  # ]:          0 :                 if (errno != ENOENT)
     219                 :          0 :                         return -errno;
     220                 :            : 
     221                 :          0 :                 c->x11_mtime = USEC_INFINITY;
     222                 :          0 :                 context_free_x11(c);
     223                 :          0 :                 return 0;
     224                 :            :         }
     225                 :            : 
     226                 :            :         /* If mtime is not changed, then we do not need to re-read */
     227                 :          0 :         t = timespec_load(&st.st_mtim);
     228   [ #  #  #  # ]:          0 :         if (c->x11_mtime != USEC_INFINITY && t == c->x11_mtime)
     229                 :          0 :                 return 0;
     230                 :            : 
     231                 :          0 :         c->x11_mtime = t;
     232                 :          0 :         context_free_x11(c);
     233                 :            : 
     234                 :          0 :         f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
     235         [ #  # ]:          0 :         if (!f)
     236                 :          0 :                 return -errno;
     237                 :            : 
     238                 :          0 :         for (;;) {
     239   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *line = NULL;
     240                 :            :                 char *l;
     241                 :            : 
     242                 :          0 :                 r = read_line(f, LONG_LINE_MAX, &line);
     243         [ #  # ]:          0 :                 if (r < 0)
     244                 :          0 :                         return r;
     245         [ #  # ]:          0 :                 if (r == 0)
     246                 :          0 :                         break;
     247                 :            : 
     248                 :          0 :                 l = strstrip(line);
     249   [ #  #  #  # ]:          0 :                 if (IN_SET(l[0], 0, '#'))
     250                 :          0 :                         continue;
     251                 :            : 
     252   [ #  #  #  # ]:          0 :                 if (in_section && first_word(l, "Option")) {
     253         [ #  # ]:          0 :                         _cleanup_strv_free_ char **a = NULL;
     254                 :            : 
     255                 :          0 :                         r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
     256         [ #  # ]:          0 :                         if (r < 0)
     257                 :          0 :                                 return r;
     258                 :            : 
     259         [ #  # ]:          0 :                         if (strv_length(a) == 3) {
     260                 :          0 :                                 char **p = NULL;
     261                 :            : 
     262         [ #  # ]:          0 :                                 if (streq(a[1], "XkbLayout"))
     263                 :          0 :                                         p = &c->x11_layout;
     264         [ #  # ]:          0 :                                 else if (streq(a[1], "XkbModel"))
     265                 :          0 :                                         p = &c->x11_model;
     266         [ #  # ]:          0 :                                 else if (streq(a[1], "XkbVariant"))
     267                 :          0 :                                         p = &c->x11_variant;
     268         [ #  # ]:          0 :                                 else if (streq(a[1], "XkbOptions"))
     269                 :          0 :                                         p = &c->x11_options;
     270                 :            : 
     271         [ #  # ]:          0 :                                 if (p) {
     272                 :          0 :                                         free_and_replace(*p, a[2]);
     273                 :            :                                 }
     274                 :            :                         }
     275                 :            : 
     276   [ #  #  #  # ]:          0 :                 } else if (!in_section && first_word(l, "Section")) {
     277         [ #  # ]:          0 :                         _cleanup_strv_free_ char **a = NULL;
     278                 :            : 
     279                 :          0 :                         r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
     280         [ #  # ]:          0 :                         if (r < 0)
     281                 :          0 :                                 return -ENOMEM;
     282                 :            : 
     283   [ #  #  #  # ]:          0 :                         if (strv_length(a) == 2 && streq(a[1], "InputClass"))
     284                 :          0 :                                 in_section = true;
     285                 :            : 
     286   [ #  #  #  # ]:          0 :                 } else if (in_section && first_word(l, "EndSection"))
     287                 :          0 :                         in_section = false;
     288                 :            :         }
     289                 :            : 
     290                 :          0 :         return 0;
     291                 :            : }
     292                 :            : 
     293                 :          0 : int locale_write_data(Context *c, char ***settings) {
     294                 :          0 :         _cleanup_strv_free_ char **l = NULL;
     295                 :            :         struct stat st;
     296                 :            :         int r, p;
     297                 :            : 
     298                 :            :         /* Set values will be returned as strv in *settings on success. */
     299                 :            : 
     300         [ #  # ]:          0 :         for (p = 0; p < _VARIABLE_LC_MAX; p++) {
     301      [ #  #  # ]:          0 :                 _cleanup_free_ char *t = NULL;
     302                 :            :                 char **u;
     303                 :            :                 const char *name;
     304                 :            : 
     305                 :          0 :                 name = locale_variable_to_string(p);
     306         [ #  # ]:          0 :                 assert(name);
     307                 :            : 
     308         [ #  # ]:          0 :                 if (isempty(c->locale[p]))
     309                 :          0 :                         continue;
     310                 :            : 
     311         [ #  # ]:          0 :                 if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
     312                 :          0 :                         return -ENOMEM;
     313                 :            : 
     314                 :          0 :                 u = strv_env_set(l, t);
     315         [ #  # ]:          0 :                 if (!u)
     316                 :          0 :                         return -ENOMEM;
     317                 :            : 
     318                 :          0 :                 strv_free_and_replace(l, u);
     319                 :            :         }
     320                 :            : 
     321         [ #  # ]:          0 :         if (strv_isempty(l)) {
     322         [ #  # ]:          0 :                 if (unlink("/etc/locale.conf") < 0)
     323         [ #  # ]:          0 :                         return errno == ENOENT ? 0 : -errno;
     324                 :            : 
     325                 :          0 :                 c->locale_mtime = USEC_INFINITY;
     326                 :          0 :                 return 0;
     327                 :            :         }
     328                 :            : 
     329                 :          0 :         r = write_env_file_label("/etc/locale.conf", l);
     330         [ #  # ]:          0 :         if (r < 0)
     331                 :          0 :                 return r;
     332                 :            : 
     333                 :          0 :         *settings = TAKE_PTR(l);
     334                 :            : 
     335         [ #  # ]:          0 :         if (stat("/etc/locale.conf", &st) >= 0)
     336                 :          0 :                 c->locale_mtime = timespec_load(&st.st_mtim);
     337                 :            : 
     338                 :          0 :         return 0;
     339                 :            : }
     340                 :            : 
     341                 :          0 : int vconsole_write_data(Context *c) {
     342                 :          0 :         _cleanup_strv_free_ char **l = NULL;
     343                 :            :         struct stat st;
     344                 :            :         int r;
     345                 :            : 
     346                 :          0 :         r = load_env_file(NULL, "/etc/vconsole.conf", &l);
     347   [ #  #  #  # ]:          0 :         if (r < 0 && r != -ENOENT)
     348                 :          0 :                 return r;
     349                 :            : 
     350         [ #  # ]:          0 :         if (isempty(c->vc_keymap))
     351                 :          0 :                 l = strv_env_unset(l, "KEYMAP");
     352                 :            :         else {
     353         [ #  # ]:          0 :                 _cleanup_free_ char *s = NULL;
     354                 :            :                 char **u;
     355                 :            : 
     356                 :          0 :                 s = strjoin("KEYMAP=", c->vc_keymap);
     357         [ #  # ]:          0 :                 if (!s)
     358                 :          0 :                         return -ENOMEM;
     359                 :            : 
     360                 :          0 :                 u = strv_env_set(l, s);
     361         [ #  # ]:          0 :                 if (!u)
     362                 :          0 :                         return -ENOMEM;
     363                 :            : 
     364                 :          0 :                 strv_free_and_replace(l, u);
     365                 :            :         }
     366                 :            : 
     367         [ #  # ]:          0 :         if (isempty(c->vc_keymap_toggle))
     368                 :          0 :                 l = strv_env_unset(l, "KEYMAP_TOGGLE");
     369                 :            :         else  {
     370         [ #  # ]:          0 :                 _cleanup_free_ char *s = NULL;
     371                 :            :                 char **u;
     372                 :            : 
     373                 :          0 :                 s = strjoin("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
     374         [ #  # ]:          0 :                 if (!s)
     375                 :          0 :                         return -ENOMEM;
     376                 :            : 
     377                 :          0 :                 u = strv_env_set(l, s);
     378         [ #  # ]:          0 :                 if (!u)
     379                 :          0 :                         return -ENOMEM;
     380                 :            : 
     381                 :          0 :                 strv_free_and_replace(l, u);
     382                 :            :         }
     383                 :            : 
     384         [ #  # ]:          0 :         if (strv_isempty(l)) {
     385         [ #  # ]:          0 :                 if (unlink("/etc/vconsole.conf") < 0)
     386         [ #  # ]:          0 :                         return errno == ENOENT ? 0 : -errno;
     387                 :            : 
     388                 :          0 :                 c->vc_mtime = USEC_INFINITY;
     389                 :          0 :                 return 0;
     390                 :            :         }
     391                 :            : 
     392                 :          0 :         r = write_env_file_label("/etc/vconsole.conf", l);
     393         [ #  # ]:          0 :         if (r < 0)
     394                 :          0 :                 return r;
     395                 :            : 
     396         [ #  # ]:          0 :         if (stat("/etc/vconsole.conf", &st) >= 0)
     397                 :          0 :                 c->vc_mtime = timespec_load(&st.st_mtim);
     398                 :            : 
     399                 :          0 :         return 0;
     400                 :            : }
     401                 :            : 
     402                 :          0 : int x11_write_data(Context *c) {
     403                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
     404                 :          0 :         _cleanup_free_ char *temp_path = NULL;
     405                 :            :         struct stat st;
     406                 :            :         int r;
     407                 :            : 
     408   [ #  #  #  # ]:          0 :         if (isempty(c->x11_layout) &&
     409         [ #  # ]:          0 :             isempty(c->x11_model) &&
     410         [ #  # ]:          0 :             isempty(c->x11_variant) &&
     411                 :          0 :             isempty(c->x11_options)) {
     412                 :            : 
     413         [ #  # ]:          0 :                 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
     414         [ #  # ]:          0 :                         return errno == ENOENT ? 0 : -errno;
     415                 :            : 
     416                 :          0 :                 c->vc_mtime = USEC_INFINITY;
     417                 :          0 :                 return 0;
     418                 :            :         }
     419                 :            : 
     420                 :          0 :         (void) mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
     421                 :          0 :         r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
     422         [ #  # ]:          0 :         if (r < 0)
     423                 :          0 :                 return r;
     424                 :            : 
     425                 :          0 :         (void) fchmod(fileno(f), 0644);
     426                 :            : 
     427                 :          0 :         fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n"
     428                 :            :               "# probably wise not to edit this file manually. Use localectl(1) to\n"
     429                 :            :               "# instruct systemd-localed to update it.\n"
     430                 :            :               "Section \"InputClass\"\n"
     431                 :            :               "        Identifier \"system-keyboard\"\n"
     432                 :            :               "        MatchIsKeyboard \"on\"\n", f);
     433                 :            : 
     434         [ #  # ]:          0 :         if (!isempty(c->x11_layout))
     435                 :          0 :                 fprintf(f, "        Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
     436                 :            : 
     437         [ #  # ]:          0 :         if (!isempty(c->x11_model))
     438                 :          0 :                 fprintf(f, "        Option \"XkbModel\" \"%s\"\n", c->x11_model);
     439                 :            : 
     440         [ #  # ]:          0 :         if (!isempty(c->x11_variant))
     441                 :          0 :                 fprintf(f, "        Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
     442                 :            : 
     443         [ #  # ]:          0 :         if (!isempty(c->x11_options))
     444                 :          0 :                 fprintf(f, "        Option \"XkbOptions\" \"%s\"\n", c->x11_options);
     445                 :            : 
     446                 :          0 :         fputs("EndSection\n", f);
     447                 :            : 
     448                 :          0 :         r = fflush_sync_and_check(f);
     449         [ #  # ]:          0 :         if (r < 0)
     450                 :          0 :                 goto fail;
     451                 :            : 
     452         [ #  # ]:          0 :         if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
     453                 :          0 :                 r = -errno;
     454                 :          0 :                 goto fail;
     455                 :            :         }
     456                 :            : 
     457         [ #  # ]:          0 :         if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
     458                 :          0 :                 c->x11_mtime = timespec_load(&st.st_mtim);
     459                 :            : 
     460                 :          0 :         return 0;
     461                 :            : 
     462                 :          0 : fail:
     463         [ #  # ]:          0 :         if (temp_path)
     464                 :          0 :                 (void) unlink(temp_path);
     465                 :            : 
     466                 :          0 :         return r;
     467                 :            : }
     468                 :            : 
     469                 :       2936 : static int read_next_mapping(const char* filename,
     470                 :            :                              unsigned min_fields, unsigned max_fields,
     471                 :            :                              FILE *f, unsigned *n, char ***a) {
     472         [ -  + ]:       2936 :         assert(f);
     473         [ -  + ]:       2936 :         assert(n);
     474         [ -  + ]:       2936 :         assert(a);
     475                 :            : 
     476                 :         96 :         for (;;) {
     477      [ +  +  + ]:       3032 :                 _cleanup_free_ char *line = NULL;
     478                 :            :                 size_t length;
     479                 :            :                 char *l, **b;
     480                 :            :                 int r;
     481                 :            : 
     482                 :       3032 :                 r = read_line(f, LONG_LINE_MAX, &line);
     483         [ -  + ]:       3032 :                 if (r < 0)
     484                 :          0 :                         return r;
     485         [ +  + ]:       3032 :                 if (r == 0)
     486                 :         40 :                         break;
     487                 :            : 
     488                 :       2992 :                 (*n)++;
     489                 :            : 
     490                 :       2992 :                 l = strstrip(line);
     491   [ +  +  +  + ]:       2992 :                 if (IN_SET(l[0], 0, '#'))
     492                 :         96 :                         continue;
     493                 :            : 
     494                 :       2896 :                 r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
     495         [ -  + ]:       2896 :                 if (r < 0)
     496                 :          0 :                         return r;
     497                 :            : 
     498                 :       2896 :                 length = strv_length(b);
     499   [ +  -  -  + ]:       2896 :                 if (length < min_fields || length > max_fields) {
     500         [ #  # ]:          0 :                         log_error("Invalid line %s:%u, ignoring.", filename, *n);
     501                 :          0 :                         strv_free(b);
     502                 :          0 :                         continue;
     503                 :            : 
     504                 :            :                 }
     505                 :            : 
     506                 :       2896 :                 *a = b;
     507                 :       2896 :                 return 1;
     508                 :            :         }
     509                 :            : 
     510                 :         40 :         return 0;
     511                 :            : }
     512                 :            : 
     513                 :         28 : int vconsole_convert_to_x11(Context *c) {
     514                 :            :         const char *map;
     515                 :         28 :         int modified = -1;
     516                 :            : 
     517                 :         28 :         map = systemd_kbd_model_map();
     518                 :            : 
     519         [ +  + ]:         28 :         if (isempty(c->vc_keymap)) {
     520                 :          8 :                 modified =
     521                 :          8 :                         !isempty(c->x11_layout) ||
     522         [ +  - ]:          4 :                         !isempty(c->x11_model) ||
     523   [ +  +  +  - ]:         16 :                         !isempty(c->x11_variant) ||
     524         [ -  + ]:          4 :                         !isempty(c->x11_options);
     525                 :            : 
     526                 :          8 :                 context_free_x11(c);
     527                 :            :         } else {
     528         [ +  - ]:         20 :                 _cleanup_fclose_ FILE *f = NULL;
     529                 :         20 :                 unsigned n = 0;
     530                 :            : 
     531                 :         20 :                 f = fopen(map, "re");
     532         [ -  + ]:         20 :                 if (!f)
     533                 :          0 :                         return -errno;
     534                 :            : 
     535                 :        872 :                 for (;;) {
     536      [ -  +  + ]:        892 :                         _cleanup_strv_free_ char **a = NULL;
     537                 :            :                         int r;
     538                 :            : 
     539                 :        892 :                         r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a);
     540         [ -  + ]:        892 :                         if (r < 0)
     541                 :          0 :                                 return r;
     542         [ +  + ]:        892 :                         if (r == 0)
     543                 :          4 :                                 break;
     544                 :            : 
     545         [ +  + ]:        888 :                         if (!streq(c->vc_keymap, a[0]))
     546                 :        872 :                                 continue;
     547                 :            : 
     548         [ -  + ]:         16 :                         if (!streq_ptr(c->x11_layout, empty_or_dash_to_null(a[1])) ||
     549         [ #  # ]:          0 :                             !streq_ptr(c->x11_model, empty_or_dash_to_null(a[2])) ||
     550         [ #  # ]:          0 :                             !streq_ptr(c->x11_variant, empty_or_dash_to_null(a[3])) ||
     551         [ #  # ]:          0 :                             !streq_ptr(c->x11_options, empty_or_dash_to_null(a[4]))) {
     552                 :            : 
     553   [ +  -  +  - ]:         32 :                                 if (free_and_strdup(&c->x11_layout, empty_or_dash_to_null(a[1])) < 0 ||
     554         [ +  - ]:         32 :                                     free_and_strdup(&c->x11_model, empty_or_dash_to_null(a[2])) < 0 ||
     555         [ -  + ]:         32 :                                     free_and_strdup(&c->x11_variant, empty_or_dash_to_null(a[3])) < 0 ||
     556                 :         16 :                                     free_and_strdup(&c->x11_options, empty_or_dash_to_null(a[4])) < 0)
     557                 :          0 :                                         return -ENOMEM;
     558                 :            : 
     559                 :         16 :                                 modified = true;
     560                 :            :                         }
     561                 :            : 
     562                 :         16 :                         break;
     563                 :            :                 }
     564                 :            :         }
     565                 :            : 
     566         [ +  + ]:         28 :         if (modified > 0)
     567         [ +  - ]:         20 :                 log_info("Changing X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
     568                 :            :                          strempty(c->x11_layout),
     569                 :            :                          strempty(c->x11_model),
     570                 :            :                          strempty(c->x11_variant),
     571                 :            :                          strempty(c->x11_options));
     572         [ +  + ]:          8 :         else if (modified < 0)
     573         [ +  - ]:          4 :                 log_notice("X11 keyboard layout was not modified: no conversion found for \"%s\".",
     574                 :            :                            c->vc_keymap);
     575                 :            :         else
     576         [ +  - ]:          4 :                 log_debug("X11 keyboard layout did not need to be modified.");
     577                 :            : 
     578                 :         28 :         return modified > 0;
     579                 :            : }
     580                 :            : 
     581                 :         56 : int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
     582                 :            :         const char *dir;
     583                 :         56 :         _cleanup_free_ char *n;
     584                 :            : 
     585         [ +  + ]:         56 :         if (x11_variant)
     586                 :         20 :                 n = strjoin(x11_layout, "-", x11_variant);
     587                 :            :         else
     588                 :         36 :                 n = strdup(x11_layout);
     589         [ -  + ]:         56 :         if (!n)
     590                 :          0 :                 return -ENOMEM;
     591                 :            : 
     592   [ +  -  +  + ]:        196 :         NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
     593   [ +  +  +  + ]:        196 :                 _cleanup_free_ char *p = NULL, *pz = NULL;
     594                 :            :                 bool uncompressed;
     595                 :            : 
     596                 :        168 :                 p = strjoin(dir, "xkb/", n, ".map");
     597                 :        168 :                 pz = strjoin(dir, "xkb/", n, ".map.gz");
     598   [ +  -  -  + ]:        168 :                 if (!p || !pz)
     599                 :          0 :                         return -ENOMEM;
     600                 :            : 
     601                 :        168 :                 uncompressed = access(p, F_OK) == 0;
     602   [ +  -  +  + ]:        168 :                 if (uncompressed || access(pz, F_OK) == 0) {
     603   [ +  -  -  + ]:         28 :                         log_debug("Found converted keymap %s at %s",
     604                 :            :                                   n, uncompressed ? p : pz);
     605                 :            : 
     606                 :         28 :                         *new_keymap = TAKE_PTR(n);
     607                 :         28 :                         return 1;
     608                 :            :                 }
     609                 :            :         }
     610                 :            : 
     611                 :         28 :         return 0;
     612                 :            : }
     613                 :            : 
     614                 :         28 : int find_legacy_keymap(Context *c, char **ret) {
     615                 :            :         const char *map;
     616                 :         28 :         _cleanup_fclose_ FILE *f = NULL;
     617                 :         28 :         _cleanup_free_ char *new_keymap = NULL;
     618                 :         28 :         unsigned n = 0;
     619                 :         28 :         unsigned best_matching = 0;
     620                 :            :         int r;
     621                 :            : 
     622         [ -  + ]:         28 :         assert(!isempty(c->x11_layout));
     623                 :            : 
     624                 :         28 :         map = systemd_kbd_model_map();
     625                 :            : 
     626                 :         28 :         f = fopen(map, "re");
     627         [ -  + ]:         28 :         if (!f)
     628                 :          0 :                 return -errno;
     629                 :            : 
     630                 :       1848 :         for (;;) {
     631      [ +  -  + ]:       1876 :                 _cleanup_strv_free_ char **a = NULL;
     632                 :       1876 :                 unsigned matching = 0;
     633                 :            : 
     634                 :       1876 :                 r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a);
     635         [ -  + ]:       1876 :                 if (r < 0)
     636                 :          0 :                         return r;
     637         [ +  + ]:       1876 :                 if (r == 0)
     638                 :         28 :                         break;
     639                 :            : 
     640                 :            :                 /* Determine how well matching this entry is */
     641         [ +  + ]:       1848 :                 if (streq(c->x11_layout, a[1]))
     642                 :            :                         /* If we got an exact match, this is best */
     643                 :         12 :                         matching = 10;
     644                 :            :                 else {
     645                 :            :                         /* We have multiple X layouts, look for an
     646                 :            :                          * entry that matches our key with everything
     647                 :            :                          * but the first layout stripped off. */
     648         [ +  + ]:       1836 :                         if (startswith_comma(c->x11_layout, a[1]))
     649                 :         20 :                                 matching = 5;
     650                 :            :                         else  {
     651                 :       1816 :                                 _cleanup_free_ char *x = NULL;
     652                 :            : 
     653                 :            :                                 /* If that didn't work, strip off the
     654                 :            :                                  * other layouts from the entry, too */
     655                 :       1816 :                                 x = strndup(a[1], strcspn(a[1], ","));
     656         [ +  + ]:       1816 :                                 if (startswith_comma(c->x11_layout, x))
     657                 :          4 :                                         matching = 1;
     658                 :            :                         }
     659                 :            :                 }
     660                 :            : 
     661         [ +  + ]:       1848 :                 if (matching > 0) {
     662   [ -  +  #  # ]:         36 :                         if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
     663                 :         36 :                                 matching++;
     664                 :            : 
     665         [ -  + ]:         36 :                                 if (streq_ptr(c->x11_variant, a[3])) {
     666                 :          0 :                                         matching++;
     667                 :            : 
     668         [ #  # ]:          0 :                                         if (streq_ptr(c->x11_options, a[4]))
     669                 :          0 :                                                 matching++;
     670                 :            :                                 }
     671                 :            :                         }
     672                 :            :                 }
     673                 :            : 
     674                 :            :                 /* The best matching entry so far, then let's save that */
     675         [ +  + ]:       1848 :                 if (matching >= MAX(best_matching, 1u)) {
     676         [ +  - ]:         36 :                         log_debug("Found legacy keymap %s with score %u",
     677                 :            :                                   a[0], matching);
     678                 :            : 
     679         [ +  + ]:         36 :                         if (matching > best_matching) {
     680                 :         24 :                                 best_matching = matching;
     681                 :            : 
     682                 :         24 :                                 r = free_and_strdup(&new_keymap, a[0]);
     683         [ -  + ]:         24 :                                 if (r < 0)
     684                 :          0 :                                         return r;
     685                 :            :                         }
     686                 :            :                 }
     687                 :            :         }
     688                 :            : 
     689   [ +  +  +  - ]:         28 :         if (best_matching < 10 && c->x11_layout) {
     690                 :            :                 /* The best match is only the first part of the X11
     691                 :            :                  * keymap. Check if we have a converted map which
     692                 :            :                  * matches just the first layout.
     693                 :            :                  */
     694                 :         16 :                 char *l, *v = NULL, *converted;
     695                 :            : 
     696                 :         16 :                 l = strndupa(c->x11_layout, strcspn(c->x11_layout, ","));
     697         [ -  + ]:         16 :                 if (c->x11_variant)
     698                 :          0 :                         v = strndupa(c->x11_variant, strcspn(c->x11_variant, ","));
     699                 :         16 :                 r = find_converted_keymap(l, v, &converted);
     700         [ -  + ]:         16 :                 if (r < 0)
     701                 :          0 :                         return r;
     702         [ +  + ]:         16 :                 if (r > 0)
     703                 :          8 :                         free_and_replace(new_keymap, converted);
     704                 :            :         }
     705                 :            : 
     706                 :         28 :         *ret = TAKE_PTR(new_keymap);
     707                 :         28 :         return (bool) *ret;
     708                 :            : }
     709                 :            : 
     710                 :         16 : int find_language_fallback(const char *lang, char **language) {
     711                 :            :         const char *map;
     712                 :         16 :         _cleanup_fclose_ FILE *f = NULL;
     713                 :         16 :         unsigned n = 0;
     714                 :            : 
     715         [ -  + ]:         16 :         assert(lang);
     716         [ -  + ]:         16 :         assert(language);
     717                 :            : 
     718                 :         16 :         map = systemd_language_fallback_map();
     719                 :            : 
     720                 :         16 :         f = fopen(map, "re");
     721         [ -  + ]:         16 :         if (!f)
     722                 :          0 :                 return -errno;
     723                 :            : 
     724                 :        152 :         for (;;) {
     725         [ +  + ]:        168 :                 _cleanup_strv_free_ char **a = NULL;
     726                 :            :                 int r;
     727                 :            : 
     728                 :        168 :                 r = read_next_mapping(map, 2, 2, f, &n, &a);
     729         [ +  + ]:        168 :                 if (r <= 0)
     730                 :          8 :                         return r;
     731                 :            : 
     732         [ +  + ]:        160 :                 if (streq(lang, a[0])) {
     733         [ -  + ]:          8 :                         assert(strv_length(a) == 2);
     734                 :          8 :                         *language = TAKE_PTR(a[1]);
     735                 :          8 :                         return 1;
     736                 :            :                 }
     737                 :            :         }
     738                 :            : 
     739                 :            :         assert_not_reached("should not be here");
     740                 :            : }
     741                 :            : 
     742                 :         36 : int x11_convert_to_vconsole(Context *c) {
     743                 :         36 :         bool modified = false;
     744                 :            : 
     745         [ +  + ]:         36 :         if (isempty(c->x11_layout)) {
     746                 :          8 :                 modified =
     747         [ +  + ]:         12 :                         !isempty(c->vc_keymap) ||
     748         [ -  + ]:          4 :                         !isempty(c->vc_keymap_toggle);
     749                 :            : 
     750                 :          8 :                 context_free_vconsole(c);
     751                 :            :         } else {
     752         [ +  - ]:         28 :                 _cleanup_free_ char *new_keymap = NULL;
     753                 :            :                 int r;
     754                 :            : 
     755                 :         28 :                 r = find_converted_keymap(c->x11_layout, c->x11_variant, &new_keymap);
     756         [ -  + ]:         28 :                 if (r < 0)
     757                 :          0 :                         return r;
     758         [ +  + ]:         28 :                 else if (r == 0) {
     759                 :         16 :                         r = find_legacy_keymap(c, &new_keymap);
     760         [ -  + ]:         16 :                         if (r < 0)
     761                 :          0 :                                 return r;
     762                 :            :                 }
     763         [ -  + ]:         28 :                 if (r == 0)
     764                 :            :                         /* We search for layout-variant match first, but then we also look
     765                 :            :                          * for anything which matches just the layout. So it's accurate to say
     766                 :            :                          * that we couldn't find anything which matches the layout. */
     767         [ #  # ]:          0 :                         log_notice("No conversion to virtual console map found for \"%s\".",
     768                 :            :                                    c->x11_layout);
     769                 :            : 
     770         [ +  + ]:         28 :                 if (!streq_ptr(c->vc_keymap, new_keymap)) {
     771                 :         20 :                         free_and_replace(c->vc_keymap, new_keymap);
     772                 :         20 :                         c->vc_keymap_toggle = mfree(c->vc_keymap_toggle);
     773                 :         20 :                         modified = true;
     774                 :            :                 }
     775                 :            :         }
     776                 :            : 
     777         [ +  + ]:         36 :         if (modified)
     778         [ +  - ]:         24 :                 log_info("Changing virtual console keymap to '%s' toggle '%s'",
     779                 :            :                          strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
     780                 :            :         else
     781         [ +  - ]:         12 :                 log_debug("Virtual console keymap was not modified.");
     782                 :            : 
     783                 :         36 :         return modified;
     784                 :            : }

Generated by: LCOV version 1.14