locale-util.c revision a34286684ebb78dd3db0d7f34feb2c121c9d00cc
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack/***
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack This file is part of systemd.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Copyright 2014 Lennart Poettering
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack systemd is free software; you can redistribute it and/or modify it
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack under the terms of the GNU Lesser General Public License as published by
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack the Free Software Foundation; either version 2.1 of the License, or
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack (at your option) any later version.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack systemd is distributed in the hope that it will be useful, but
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack WITHOUT ANY WARRANTY; without even the implied warranty of
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack Lesser General Public License for more details.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack You should have received a copy of the GNU Lesser General Public License
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack along with systemd; If not, see <http://www.gnu.org/licenses/>.
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack***/
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include <sys/mman.h>
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "set.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "util.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "utf8.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "strv.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "util.h"
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack#include "locale-util.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poetteringstatic int add_locales_from_archive(Set *locales) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Stolen from glibc... */
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering struct locarhead {
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering uint32_t magic;
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering /* Serial number. */
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering uint32_t serial;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Name hash table. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t namehash_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t namehash_used;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t namehash_size;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* String table. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t string_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t string_used;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t string_size;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Table with locale records. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t locrectab_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t locrectab_used;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t locrectab_size;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* MD5 sum hash table. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t sumhash_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t sumhash_used;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t sumhash_size;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack };
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack struct namehashent {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Hash value of the name. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t hashval;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Offset of the name in the string table. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t name_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack /* Offset of the locale record. */
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack uint32_t locrec_offset;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack };
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack const struct locarhead *h;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack const struct namehashent *e;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack const void *p = MAP_FAILED;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack _cleanup_close_ int fd = -1;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack size_t sz = 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack struct stat st;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack unsigned i;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (fd < 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return errno == ENOENT ? 0 : -errno;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (fstat(fd, &st) < 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -errno;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!S_ISREG(st.st_mode))
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -EBADMSG;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (st.st_size < (off_t) sizeof(struct locarhead))
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -EBADMSG;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (p == MAP_FAILED)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -errno;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack h = (const struct locarhead *) p;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (h->magic != 0xde020109 ||
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack h->namehash_offset + h->namehash_size > st.st_size ||
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack h->string_offset + h->string_size > st.st_size ||
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack h->locrectab_offset + h->locrectab_size > st.st_size ||
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack h->sumhash_offset + h->sumhash_size > st.st_size) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = -EBADMSG;
d02608170e599b1ffbc7c9a22062bae2579d6e36Lennart Poettering goto finish;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack for (i = 0; i < h->namehash_size; i++) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack char *z;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (e[i].locrec_offset == 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack continue;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!utf8_is_valid((char*) p + e[i].name_offset))
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack continue;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack z = strdup((char*) p + e[i].name_offset);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!z) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = -ENOMEM;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack goto finish;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = set_consume(locales, z);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack goto finish;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack finish:
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (p != MAP_FAILED)
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack munmap((void*) p, sz);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackstatic int add_locales_from_libdir (Set *locales) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack _cleanup_closedir_ DIR *dir = NULL;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack struct dirent *entry;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack dir = opendir("/usr/lib/locale");
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!dir)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return errno == ENOENT ? 0 : -errno;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack FOREACH_DIRENT(entry, dir, return -errno) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack char *z;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (entry->d_type != DT_DIR)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack continue;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack z = strdup(entry->d_name);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!z)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -ENOMEM;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = set_consume(locales, z);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0 && r != -EEXIST)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack }
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return 0;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack}
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mackint get_locales(char ***ret) {
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack _cleanup_set_free_ Set *locales = NULL;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack _cleanup_strv_free_ char **l = NULL;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack int r;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack locales = set_new(&string_hash_ops);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!locales)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -ENOMEM;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = add_locales_from_archive(locales);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0 && r != -ENOENT)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return r;
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack r = add_locales_from_libdir(locales);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (r < 0)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return r;
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack l = set_get_strv(locales);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack if (!l)
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack return -ENOMEM;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
c2a23db0b91faca3795099fd4b41587bac170ff7Daniel Mack strv_sort(l);
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack *ret = l;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack l = NULL;
e2fa5721c3ee5ea400b99a6463e8c1c257e20415Daniel Mack
return 0;
}
bool locale_is_valid(const char *name) {
if (isempty(name))
return false;
if (strlen(name) >= 128)
return false;
if (!utf8_is_valid(name))
return false;
if (!filename_is_safe(name))
return false;
if (!string_is_safe(name))
return false;
return true;
}
static const char * const locale_variable_table[_VARIABLE_LC_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"
};
DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);