1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <stdlib.h>
5 
6 #include "env-file.h"
7 #include "env-util.h"
8 #include "locale-setup.h"
9 #include "locale-util.h"
10 #include "proc-cmdline.h"
11 #include "string-util.h"
12 #include "strv.h"
13 #include "util.h"
14 #include "virt.h"
15 
locale_setup(char *** environment)16 int locale_setup(char ***environment) {
17         _cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {};
18         _cleanup_strv_free_ char **add = NULL;
19         int r;
20 
21         r = proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX,
22                                       "locale.LANG",              &variables[VARIABLE_LANG],
23                                       "locale.LANGUAGE",          &variables[VARIABLE_LANGUAGE],
24                                       "locale.LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
25                                       "locale.LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
26                                       "locale.LC_TIME",           &variables[VARIABLE_LC_TIME],
27                                       "locale.LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
28                                       "locale.LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
29                                       "locale.LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
30                                       "locale.LC_PAPER",          &variables[VARIABLE_LC_PAPER],
31                                       "locale.LC_NAME",           &variables[VARIABLE_LC_NAME],
32                                       "locale.LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
33                                       "locale.LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
34                                       "locale.LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
35                                       "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
36         if (r < 0 && r != -ENOENT)
37                 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
38 
39         /* Hmm, nothing set on the kernel cmd line? Then let's try /etc/locale.conf */
40         if (r <= 0) {
41                 r = parse_env_file(NULL, "/etc/locale.conf",
42                                    "LANG",              &variables[VARIABLE_LANG],
43                                    "LANGUAGE",          &variables[VARIABLE_LANGUAGE],
44                                    "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
45                                    "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
46                                    "LC_TIME",           &variables[VARIABLE_LC_TIME],
47                                    "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
48                                    "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
49                                    "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
50                                    "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
51                                    "LC_NAME",           &variables[VARIABLE_LC_NAME],
52                                    "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
53                                    "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
54                                    "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
55                                    "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
56                 if (r < 0 && r != -ENOENT)
57                         log_warning_errno(r, "Failed to read /etc/locale.conf: %m");
58         }
59 
60         for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++) {
61                 char *s;
62 
63                 if (!variables[i])
64                         continue;
65 
66                 s = strjoin(locale_variable_to_string(i), "=", variables[i]);
67                 if (!s)
68                         return -ENOMEM;
69 
70                 if (strv_consume(&add, s) < 0)
71                         return -ENOMEM;
72         }
73 
74         if (strv_isempty(add)) {
75                 /* If no locale is configured then default to compile-time default. */
76 
77                 add = strv_new("LANG=" SYSTEMD_DEFAULT_LOCALE);
78                 if (!add)
79                         return -ENOMEM;
80         }
81 
82         if (strv_isempty(*environment))
83                 strv_free_and_replace(*environment, add);
84         else {
85                 char **merged;
86 
87                 merged = strv_env_merge(*environment, add);
88                 if (!merged)
89                         return -ENOMEM;
90 
91                 strv_free_and_replace(*environment, merged);
92         }
93 
94         return 0;
95 }
96