[systemd-devel] [PATCH] [RFC]logind: set locale in user sessions

Tom Gundersen teg at jklm.no
Wed Jan 2 03:46:31 PST 2013


Changes the pam module to now set the locale for user-sessions, similarly to
what is done system-wide in PID1.

The logic is: the kernel command-line takes precedence, then
XDG_CONFIG_HOME/locale.conf, then /etc/locale.conf and finally any legacy
distro-specific files that might still be supported.

The parsing of the locale settings is split out of core/ and moved to shared/.

This is an important feature for Arch, as we traditionally set the system locale
in all user sessions, so this means we can remove a work-around [0] and close some
related bugs (e.g., [1]).

[0]:
<https://projects.archlinux.org/svntogit/packages.git/tree/trunk/locale.sh?h=packages/filesystem>
[1]: <https://bugs.archlinux.org/task/33231>
---
 Makefile.am               |   5 +-
 man/locale.conf.xml       |  17 +++-
 src/core/locale-setup.c   | 196 ++--------------------------------------------
 src/login/pam-module.c    |  57 ++++++++++++++
 src/shared/locale-parse.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/locale-parse.h |  63 +++++++++++++++
 6 files changed, 341 insertions(+), 193 deletions(-)
 create mode 100644 src/shared/locale-parse.c
 create mode 100644 src/shared/locale-parse.h

diff --git a/Makefile.am b/Makefile.am
index 94ae549..f78ce99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -847,7 +847,10 @@ libsystemd_shared_la_SOURCES = \
 	src/shared/time-dst.c \
 	src/shared/time-dst.h \
 	src/shared/calendarspec.c \
-	src/shared/calendarspec.h
+	src/shared/calendarspec.h \
+	src/shared/locale-parse.c \
+	src/shared/locale-parse.h
+
 
 libsystemd_shared_la_LIBADD = libsystemd-daemon.la
 
diff --git a/man/locale.conf.xml b/man/locale.conf.xml
index 06c0af0..5d8e99a 100644
--- a/man/locale.conf.xml
+++ b/man/locale.conf.xml
@@ -49,6 +49,7 @@
 
         <refsynopsisdiv>
                 <para><filename>/etc/locale.conf</filename></para>
+                <para><filename>$XDG_CONFIG_HOME/locale.conf</filename></para>
         </refsynopsisdiv>
 
         <refsect1>
@@ -57,7 +58,14 @@
                 <para>The <filename>/etc/locale.conf</filename> file
                 configures system-wide locale settings. It is read at
                 early-boot by
-                <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+                <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                and at session-start by
+                <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+                <para>The <filename>$XDG_CONFIG_HOME/locale.conf</filename>
+                file configures user-specific locale settings. It is read
+                at session-start by
+                <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
 
                 <para>The basic file format of
                 <filename>locale.conf</filename> is a
@@ -87,6 +95,13 @@
                 used to override the locale settings at boot.</para>
 
                 <para>The locale settings configured in
+                <filename>$XDG_CONFIG_HOME/locale.conf</filename>, are
+                user-specific and are inherited by every program in the
+                users session, unless overridden or unset by individual
+                programs.
+                </para>
+
+                <para>The locale settings configured in
                 <filename>/etc/locale.conf</filename> are system-wide
                 and are inherited by every service or user, unless
                 overridden or unset by individual programs or
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 8821fc2..f4ecb9e 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -19,208 +19,22 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 
 #include "locale-setup.h"
-#include "util.h"
+#include "locale-parse.h"
 #include "macro.h"
-#include "virt.h"
-
-enum {
-        /* We don't list LC_ALL here on purpose. People should be
-         * using LANG instead. */
-
-        VARIABLE_LANG,
-        VARIABLE_LANGUAGE,
-        VARIABLE_LC_CTYPE,
-        VARIABLE_LC_NUMERIC,
-        VARIABLE_LC_TIME,
-        VARIABLE_LC_COLLATE,
-        VARIABLE_LC_MONETARY,
-        VARIABLE_LC_MESSAGES,
-        VARIABLE_LC_PAPER,
-        VARIABLE_LC_NAME,
-        VARIABLE_LC_ADDRESS,
-        VARIABLE_LC_TELEPHONE,
-        VARIABLE_LC_MEASUREMENT,
-        VARIABLE_LC_IDENTIFICATION,
-        _VARIABLE_MAX
-};
-
-static const char * const variable_names[_VARIABLE_MAX] = {
-        [VARIABLE_LANG] = "LANG",
-        [VARIABLE_LANGUAGE] = "LANGUAGE",
-        [VARIABLE_LC_CTYPE] = "LC_CTYPE",
-        [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
-        [VARIABLE_LC_TIME] = "LC_TIME",
-        [VARIABLE_LC_COLLATE] = "LC_COLLATE",
-        [VARIABLE_LC_MONETARY] = "LC_MONETARY",
-        [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
-        [VARIABLE_LC_PAPER] = "LC_PAPER",
-        [VARIABLE_LC_NAME] = "LC_NAME",
-        [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
-        [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
-        [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
-        [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
-};
 
 int locale_setup(void) {
         char *variables[_VARIABLE_MAX];
-        int r = 0, i;
+        int r, i;
 
         zero(variables);
 
-        if (detect_container(NULL) <= 0) {
-                r = parse_env_file("/proc/cmdline", WHITESPACE,
-#if defined(TARGET_FEDORA)
-                                   "LANG",                     &variables[VARIABLE_LANG],
-#endif
-                                   "locale.LANG",              &variables[VARIABLE_LANG],
-                                   "locale.LANGUAGE",          &variables[VARIABLE_LANGUAGE],
-                                   "locale.LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
-                                   "locale.LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
-                                   "locale.LC_TIME",           &variables[VARIABLE_LC_TIME],
-                                   "locale.LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
-                                   "locale.LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
-                                   "locale.LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
-                                   "locale.LC_PAPER",          &variables[VARIABLE_LC_PAPER],
-                                   "locale.LC_NAME",           &variables[VARIABLE_LC_NAME],
-                                   "locale.LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
-                                   "locale.LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
-                                   "locale.LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
-                                   "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
-        }
-
-        /* Hmm, nothing set on the kernel cmd line? Then let's
-         * try /etc/locale.conf */
-        if (r <= 0) {
-                r = parse_env_file("/etc/locale.conf", NEWLINE,
-                                   "LANG",              &variables[VARIABLE_LANG],
-                                   "LANGUAGE",          &variables[VARIABLE_LANGUAGE],
-                                   "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
-                                   "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
-                                   "LC_TIME",           &variables[VARIABLE_LC_TIME],
-                                   "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
-                                   "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
-                                   "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
-                                   "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
-                                   "LC_NAME",           &variables[VARIABLE_LC_NAME],
-                                   "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
-                                   "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
-                                   "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
-                                   "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/locale.conf: %s", strerror(-r));
-        }
-
-#if defined(TARGET_ALTLINUX)
-        if (r <= 0) {
-                r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
-                                   "LANG", &variables[VARIABLE_LANG],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-        }
-
-#elif defined(TARGET_SUSE)
-        if (r <= 0) {
-                r = parse_env_file("/etc/sysconfig/language", NEWLINE,
-                                   "RC_LANG", &variables[VARIABLE_LANG],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r));
-        }
-
-#elif defined(TARGET_DEBIAN) || defined(TARGET_ANGSTROM)
-        if (r <= 0) {
-                r = parse_env_file("/etc/default/locale", NEWLINE,
-                                   "LANG",              &variables[VARIABLE_LANG],
-                                   "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
-                                   "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
-                                   "LC_TIME",           &variables[VARIABLE_LC_TIME],
-                                   "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
-                                   "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
-                                   "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
-                                   "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
-                                   "LC_NAME",           &variables[VARIABLE_LC_NAME],
-                                   "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
-                                   "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
-                                   "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
-                                   "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/default/locale: %s", strerror(-r));
-        }
-
-#elif defined(TARGET_GENTOO)
-        /* Gentoo's openrc expects locale variables in /etc/env.d/
-         * These files are later compiled by env-update into shell
-         * export commands at /etc/profile.env, with variables being
-         * exported by openrc's runscript (so /etc/init.d/)
-         */
-        if (r <= 0) {
-                r = parse_env_file("/etc/profile.env", NEWLINE,
-                                   "export LANG",              &variables[VARIABLE_LANG],
-                                   "export LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
-                                   "export LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
-                                   "export LC_TIME",           &variables[VARIABLE_LC_TIME],
-                                   "export LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
-                                   "export LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
-                                   "export LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
-                                   "export LC_PAPER",          &variables[VARIABLE_LC_PAPER],
-                                   "export LC_NAME",           &variables[VARIABLE_LC_NAME],
-                                   "export LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
-                                   "export LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
-                                   "export LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
-                                   "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/profile.env: %s", strerror(-r));
-        }
-
-#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-        if (r <= 0) {
-                r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
-                                   "LANG",              &variables[VARIABLE_LANG],
-                                   "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
-                                   "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
-                                   "LC_TIME",           &variables[VARIABLE_LC_TIME],
-                                   "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
-                                   "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
-                                   "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
-                                   "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
-                                   "LC_NAME",           &variables[VARIABLE_LC_NAME],
-                                   "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
-                                   "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
-                                   "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
-                                   "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
-                                   NULL);
-
-                if (r < 0 && r != -ENOENT)
-                        log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-        }
-
-#endif
-
-        if (!variables[VARIABLE_LANG]) {
-                variables[VARIABLE_LANG] = strdup("C");
-                if (!variables[VARIABLE_LANG]) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-        }
+        r = locale_parse(variables, NULL);
+        if (r < 0)
+                goto finish;
 
         for (i = 0; i < _VARIABLE_MAX; i++) {
                 if (variables[i]) {
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 88b0ef9..8971658 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -41,6 +41,7 @@
 #include "dbus-common.h"
 #include "def.h"
 #include "socket-util.h"
+#include "locale-parse.h"
 
 static int parse_argv(pam_handle_t *handle,
                       int argc, const char **argv,
@@ -321,6 +322,60 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         return 0;
 }
 
+static int locale_setup(pam_handle_t *handle, const char *home) {
+        char *variables[_VARIABLE_MAX];
+        _cleanup_free_ char *user_locale = NULL;
+        const char *path;
+        int r, i;
+
+        assert(handle);
+
+        zero(variables);
+
+        path = pam_getenv(handle, "XDG_CONFIG_HOME");
+        if (isempty(path))
+                path = getenv("XDG_CONFIG_HOME");
+
+        if (!isempty(path)) {
+                if (asprintf(&user_locale, "%s/locale.conf", path) < 0) {
+                        r = PAM_BUF_ERR;
+                        goto finish;
+                }
+        } else {
+                path = pam_getenv(handle, "HOME");
+                if (isempty(path))
+                        path = getenv("HOME");
+                if (isempty(path))
+                        path = home;
+
+                if (asprintf(&user_locale, "%s/.config/locale.conf", path) < 0) {
+                        r = PAM_BUF_ERR;
+                        goto finish;
+                }
+        }
+
+        r = locale_parse(variables, user_locale);
+        if (r < 0)
+                goto finish;
+
+        for (i = 0; i < _VARIABLE_MAX; i++) {
+                if (variables[i]) {
+                        if (pam_misc_setenv(handle, variable_names[i], variables[i], 0) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+                }
+        }
+
+        r = 0;
+
+finish:
+        for (i = 0; i < _VARIABLE_MAX; i++)
+                free(variables[i]);
+
+        return r;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -364,6 +419,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (r != PAM_SUCCESS)
                 goto finish;
 
+        locale_setup(handle, pw->pw_dir);
+
         /* Make sure we don't enter a loop by talking to
          * systemd-logind when it is actually waiting for the
          * background to finish start-up. If the service is
diff --git a/src/shared/locale-parse.c b/src/shared/locale-parse.c
new file mode 100644
index 0000000..9e04fbd
--- /dev/null
+++ b/src/shared/locale-parse.c
@@ -0,0 +1,196 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <errno.h>
+
+#include "locale-parse.h"
+#include "util.h"
+#include "virt.h"
+
+static int locale_parse_file(char *variables[_VARIABLE_MAX], const char *filename) {
+        int r;
+
+        assert(filename);
+
+        r = parse_env_file(filename, NEWLINE,
+                        "LANG",              &variables[VARIABLE_LANG],
+                        "LANGUAGE",          &variables[VARIABLE_LANGUAGE],
+                        "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
+                        "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
+                        "LC_TIME",           &variables[VARIABLE_LC_TIME],
+                        "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
+                        "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
+                        "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
+                        "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
+                        "LC_NAME",           &variables[VARIABLE_LC_NAME],
+                        "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
+                        "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
+                        "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
+                        "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+                        NULL);
+
+        if (r < 0 && r != -ENOENT)
+                log_warning("Failed to read %s: %s", filename, strerror(-r));
+
+        return r;
+}
+
+int locale_parse(char *variables[_VARIABLE_MAX], const char *user_locale) {
+        int r = 0;
+
+        if (detect_container(NULL) <= 0) {
+                r = parse_env_file("/proc/cmdline", WHITESPACE,
+#if defined(TARGET_FEDORA)
+                                   "LANG",                     &variables[VARIABLE_LANG],
+#endif
+                                   "locale.LANG",              &variables[VARIABLE_LANG],
+                                   "locale.LANGUAGE",          &variables[VARIABLE_LANGUAGE],
+                                   "locale.LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
+                                   "locale.LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
+                                   "locale.LC_TIME",           &variables[VARIABLE_LC_TIME],
+                                   "locale.LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
+                                   "locale.LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
+                                   "locale.LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
+                                   "locale.LC_PAPER",          &variables[VARIABLE_LC_PAPER],
+                                   "locale.LC_NAME",           &variables[VARIABLE_LC_NAME],
+                                   "locale.LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
+                                   "locale.LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
+                                   "locale.LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
+                                   "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
+        }
+
+        /* If nothing set on the kernel commandline, parse user-specific config file */
+        if (r <= 0 && user_locale)
+                r = locale_parse_file(variables, user_locale);
+
+        /* If the user-specific config file, could not be parsed, try the system-wide config file */
+        if (r <= 0)
+                r = locale_parse_file(variables, "/etc/locale.conf");
+
+        /* If the system-wide config file could not be parsed, try a legacy file */
+#if defined(TARGET_ALTLINUX)
+        if (r <= 0) {
+                r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+                                   "LANG", &variables[VARIABLE_LANG],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+        }
+
+#elif defined(TARGET_SUSE)
+        if (r <= 0) {
+                r = parse_env_file("/etc/sysconfig/language", NEWLINE,
+                                   "RC_LANG", &variables[VARIABLE_LANG],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r));
+        }
+
+#elif defined(TARGET_DEBIAN) || defined(TARGET_ANGSTROM)
+        if (r <= 0) {
+                r = parse_env_file("/etc/default/locale", NEWLINE,
+                                   "LANG",              &variables[VARIABLE_LANG],
+                                   "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
+                                   "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
+                                   "LC_TIME",           &variables[VARIABLE_LC_TIME],
+                                   "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
+                                   "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
+                                   "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
+                                   "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
+                                   "LC_NAME",           &variables[VARIABLE_LC_NAME],
+                                   "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
+                                   "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
+                                   "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
+                                   "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /etc/default/locale: %s", strerror(-r));
+        }
+
+#elif defined(TARGET_GENTOO)
+        /* Gentoo's openrc expects locale variables in /etc/env.d/
+         * These files are later compiled by env-update into shell
+         * export commands at /etc/profile.env, with variables being
+         * exported by openrc's runscript (so /etc/init.d/)
+         */
+        if (r <= 0) {
+                r = parse_env_file("/etc/profile.env", NEWLINE,
+                                   "export LANG",              &variables[VARIABLE_LANG],
+                                   "export LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
+                                   "export LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
+                                   "export LC_TIME",           &variables[VARIABLE_LC_TIME],
+                                   "export LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
+                                   "export LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
+                                   "export LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
+                                   "export LC_PAPER",          &variables[VARIABLE_LC_PAPER],
+                                   "export LC_NAME",           &variables[VARIABLE_LC_NAME],
+                                   "export LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
+                                   "export LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
+                                   "export LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
+                                   "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /etc/profile.env: %s", strerror(-r));
+        }
+
+#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+        if (r <= 0) {
+                r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+                                   "LANG",              &variables[VARIABLE_LANG],
+                                   "LC_CTYPE",          &variables[VARIABLE_LC_CTYPE],
+                                   "LC_NUMERIC",        &variables[VARIABLE_LC_NUMERIC],
+                                   "LC_TIME",           &variables[VARIABLE_LC_TIME],
+                                   "LC_COLLATE",        &variables[VARIABLE_LC_COLLATE],
+                                   "LC_MONETARY",       &variables[VARIABLE_LC_MONETARY],
+                                   "LC_MESSAGES",       &variables[VARIABLE_LC_MESSAGES],
+                                   "LC_PAPER",          &variables[VARIABLE_LC_PAPER],
+                                   "LC_NAME",           &variables[VARIABLE_LC_NAME],
+                                   "LC_ADDRESS",        &variables[VARIABLE_LC_ADDRESS],
+                                   "LC_TELEPHONE",      &variables[VARIABLE_LC_TELEPHONE],
+                                   "LC_MEASUREMENT",    &variables[VARIABLE_LC_MEASUREMENT],
+                                   "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+                                   NULL);
+
+                if (r < 0 && r != -ENOENT)
+                        log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+        }
+
+#endif
+
+        if (!variables[VARIABLE_LANG]) {
+                variables[VARIABLE_LANG] = strdup("C");
+                if (!variables[VARIABLE_LANG]) {
+                        return -ENOMEM;
+                }
+        }
+
+        return 0;
+}
diff --git a/src/shared/locale-parse.h b/src/shared/locale-parse.h
new file mode 100644
index 0000000..a829730
--- /dev/null
+++ b/src/shared/locale-parse.h
@@ -0,0 +1,63 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+enum {
+        /* We don't list LC_ALL here on purpose. People should be
+         * using LANG instead. */
+
+        VARIABLE_LANG,
+        VARIABLE_LANGUAGE,
+        VARIABLE_LC_CTYPE,
+        VARIABLE_LC_NUMERIC,
+        VARIABLE_LC_TIME,
+        VARIABLE_LC_COLLATE,
+        VARIABLE_LC_MONETARY,
+        VARIABLE_LC_MESSAGES,
+        VARIABLE_LC_PAPER,
+        VARIABLE_LC_NAME,
+        VARIABLE_LC_ADDRESS,
+        VARIABLE_LC_TELEPHONE,
+        VARIABLE_LC_MEASUREMENT,
+        VARIABLE_LC_IDENTIFICATION,
+        _VARIABLE_MAX
+};
+
+static const char * const variable_names[_VARIABLE_MAX] = {
+        [VARIABLE_LANG] = "LANG",
+        [VARIABLE_LANGUAGE] = "LANGUAGE",
+        [VARIABLE_LC_CTYPE] = "LC_CTYPE",
+        [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
+        [VARIABLE_LC_TIME] = "LC_TIME",
+        [VARIABLE_LC_COLLATE] = "LC_COLLATE",
+        [VARIABLE_LC_MONETARY] = "LC_MONETARY",
+        [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
+        [VARIABLE_LC_PAPER] = "LC_PAPER",
+        [VARIABLE_LC_NAME] = "LC_NAME",
+        [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
+        [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
+        [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
+        [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
+};
+
+int locale_parse(char *[], const char *);
-- 
1.8.0.3



More information about the systemd-devel mailing list