1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "env-file-label.h"
9 #include "errno-util.h"
11 #include "locale-setup.h"
12 #include "proc-cmdline.h"
13 #include "stat-util.h"
16 void locale_context_clear(LocaleContext
*c
) {
19 c
->st
= (struct stat
) {};
21 for (LocaleVariable i
= 0; i
< _VARIABLE_LC_MAX
; i
++)
22 c
->locale
[i
] = mfree(c
->locale
[i
]);
25 static int locale_context_load_proc(LocaleContext
*c
, LocaleLoadFlag flag
) {
30 if (!FLAGS_SET(flag
, LOCALE_LOAD_PROC_CMDLINE
))
33 locale_context_clear(c
);
35 r
= proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX
,
36 "locale.LANG", &c
->locale
[VARIABLE_LANG
],
37 "locale.LANGUAGE", &c
->locale
[VARIABLE_LANGUAGE
],
38 "locale.LC_CTYPE", &c
->locale
[VARIABLE_LC_CTYPE
],
39 "locale.LC_NUMERIC", &c
->locale
[VARIABLE_LC_NUMERIC
],
40 "locale.LC_TIME", &c
->locale
[VARIABLE_LC_TIME
],
41 "locale.LC_COLLATE", &c
->locale
[VARIABLE_LC_COLLATE
],
42 "locale.LC_MONETARY", &c
->locale
[VARIABLE_LC_MONETARY
],
43 "locale.LC_MESSAGES", &c
->locale
[VARIABLE_LC_MESSAGES
],
44 "locale.LC_PAPER", &c
->locale
[VARIABLE_LC_PAPER
],
45 "locale.LC_NAME", &c
->locale
[VARIABLE_LC_NAME
],
46 "locale.LC_ADDRESS", &c
->locale
[VARIABLE_LC_ADDRESS
],
47 "locale.LC_TELEPHONE", &c
->locale
[VARIABLE_LC_TELEPHONE
],
48 "locale.LC_MEASUREMENT", &c
->locale
[VARIABLE_LC_MEASUREMENT
],
49 "locale.LC_IDENTIFICATION", &c
->locale
[VARIABLE_LC_IDENTIFICATION
]);
53 return log_debug_errno(r
, "Failed to read /proc/cmdline: %m");
57 static int locale_context_load_conf(LocaleContext
*c
, LocaleLoadFlag flag
) {
58 _cleanup_close_
int fd
= -EBADF
;
64 if (!FLAGS_SET(flag
, LOCALE_LOAD_LOCALE_CONF
))
67 fd
= RET_NERRNO(open("/etc/locale.conf", O_CLOEXEC
| O_PATH
));
71 return log_debug_errno(errno
, "Failed to open /etc/locale.conf: %m");
73 if (fstat(fd
, &st
) < 0)
74 return log_debug_errno(errno
, "Failed to stat /etc/locale.conf: %m");
76 /* If the file is not changed, then we do not need to re-read the file. */
77 if (stat_inode_unmodified(&c
->st
, &st
))
81 locale_context_clear(c
);
83 r
= parse_env_file_fd(fd
, "/etc/locale.conf",
84 "LANG", &c
->locale
[VARIABLE_LANG
],
85 "LANGUAGE", &c
->locale
[VARIABLE_LANGUAGE
],
86 "LC_CTYPE", &c
->locale
[VARIABLE_LC_CTYPE
],
87 "LC_NUMERIC", &c
->locale
[VARIABLE_LC_NUMERIC
],
88 "LC_TIME", &c
->locale
[VARIABLE_LC_TIME
],
89 "LC_COLLATE", &c
->locale
[VARIABLE_LC_COLLATE
],
90 "LC_MONETARY", &c
->locale
[VARIABLE_LC_MONETARY
],
91 "LC_MESSAGES", &c
->locale
[VARIABLE_LC_MESSAGES
],
92 "LC_PAPER", &c
->locale
[VARIABLE_LC_PAPER
],
93 "LC_NAME", &c
->locale
[VARIABLE_LC_NAME
],
94 "LC_ADDRESS", &c
->locale
[VARIABLE_LC_ADDRESS
],
95 "LC_TELEPHONE", &c
->locale
[VARIABLE_LC_TELEPHONE
],
96 "LC_MEASUREMENT", &c
->locale
[VARIABLE_LC_MEASUREMENT
],
97 "LC_IDENTIFICATION", &c
->locale
[VARIABLE_LC_IDENTIFICATION
]);
99 return log_debug_errno(r
, "Failed to read /etc/locale.conf: %m");
101 return 1; /* loaded */
104 static int locale_context_load_env(LocaleContext
*c
, LocaleLoadFlag flag
) {
109 if (!FLAGS_SET(flag
, LOCALE_LOAD_ENVIRONMENT
))
112 locale_context_clear(c
);
114 /* Fill in what we got passed from systemd. */
115 for (LocaleVariable p
= 0; p
< _VARIABLE_LC_MAX
; p
++) {
116 const char *name
= ASSERT_PTR(locale_variable_to_string(p
));
118 r
= free_and_strdup(&c
->locale
[p
], empty_to_null(getenv(name
)));
120 return log_oom_debug();
123 return 1; /* loaded */
126 int locale_context_load(LocaleContext
*c
, LocaleLoadFlag flag
) {
131 r
= locale_context_load_proc(c
, flag
);
135 r
= locale_context_load_conf(c
, flag
);
139 r
= locale_context_load_env(c
, flag
);
143 /* Nothing loaded, or error. */
144 locale_context_clear(c
);
148 if (FLAGS_SET(flag
, LOCALE_LOAD_SIMPLIFY
))
149 locale_variables_simplify(c
->locale
);
154 int locale_context_build_env(const LocaleContext
*c
, char ***ret_set
, char ***ret_unset
) {
155 _cleanup_strv_free_
char **set
= NULL
, **unset
= NULL
;
160 if (!ret_set
&& !ret_unset
)
163 for (LocaleVariable p
= 0; p
< _VARIABLE_LC_MAX
; p
++) {
164 const char *name
= ASSERT_PTR(locale_variable_to_string(p
));
166 if (isempty(c
->locale
[p
])) {
169 r
= strv_extend(&unset
, name
);
173 r
= strv_env_assign(&set
, name
, c
->locale
[p
]);
180 *ret_set
= TAKE_PTR(set
);
182 *ret_unset
= TAKE_PTR(unset
);
186 int locale_context_save(LocaleContext
*c
, char ***ret_set
, char ***ret_unset
) {
187 _cleanup_strv_free_
char **set
= NULL
, **unset
= NULL
;
192 /* Set values will be returned as strv in *ret on success. */
194 r
= locale_context_build_env(c
, &set
, ret_unset
? &unset
: NULL
);
198 if (strv_isempty(set
)) {
199 if (unlink("/etc/locale.conf") < 0)
200 return errno
== ENOENT
? 0 : -errno
;
202 c
->st
= (struct stat
) {};
211 r
= write_env_file_label(AT_FDCWD
, "/etc/locale.conf", NULL
, set
);
215 if (stat("/etc/locale.conf", &c
->st
) < 0)
219 *ret_set
= TAKE_PTR(set
);
221 *ret_unset
= TAKE_PTR(unset
);
225 int locale_context_merge(const LocaleContext
*c
, char *l
[_VARIABLE_LC_MAX
]) {
229 for (LocaleVariable p
= 0; p
< _VARIABLE_LC_MAX
; p
++)
230 if (!isempty(c
->locale
[p
]) && isempty(l
[p
])) {
231 l
[p
] = strdup(c
->locale
[p
]);
239 void locale_context_take(LocaleContext
*c
, char *l
[_VARIABLE_LC_MAX
]) {
243 for (LocaleVariable p
= 0; p
< _VARIABLE_LC_MAX
; p
++)
244 free_and_replace(c
->locale
[p
], l
[p
]);
247 bool locale_context_equal(const LocaleContext
*c
, char *l
[_VARIABLE_LC_MAX
]) {
251 for (LocaleVariable p
= 0; p
< _VARIABLE_LC_MAX
; p
++)
252 if (!streq_ptr(c
->locale
[p
], l
[p
]))
258 int locale_setup(char ***environment
) {
259 _cleanup_(locale_context_clear
) LocaleContext c
= {};
260 _cleanup_strv_free_
char **add
= NULL
;
265 r
= locale_context_load(&c
, LOCALE_LOAD_PROC_CMDLINE
| LOCALE_LOAD_LOCALE_CONF
);
269 r
= locale_context_build_env(&c
, &add
, NULL
);
273 if (strv_isempty(add
)) {
274 /* If no locale is configured then default to compile-time default. */
276 add
= strv_new("LANG=" SYSTEMD_DEFAULT_LOCALE
);
281 if (strv_isempty(*environment
))
282 strv_free_and_replace(*environment
, add
);
286 merged
= strv_env_merge(*environment
, add
);
290 strv_free_and_replace(*environment
, merged
);