Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2014 Lennart Poettering
7 :
8 : systemd is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 of the License, or
11 : (at your option) any later version.
12 :
13 : systemd is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include <sys/mman.h>
23 :
24 : #include "set.h"
25 : #include "util.h"
26 : #include "utf8.h"
27 : #include "strv.h"
28 :
29 : #include "locale-util.h"
30 :
31 1 : static int add_locales_from_archive(Set *locales) {
32 : /* Stolen from glibc... */
33 :
34 : struct locarhead {
35 : uint32_t magic;
36 : /* Serial number. */
37 : uint32_t serial;
38 : /* Name hash table. */
39 : uint32_t namehash_offset;
40 : uint32_t namehash_used;
41 : uint32_t namehash_size;
42 : /* String table. */
43 : uint32_t string_offset;
44 : uint32_t string_used;
45 : uint32_t string_size;
46 : /* Table with locale records. */
47 : uint32_t locrectab_offset;
48 : uint32_t locrectab_used;
49 : uint32_t locrectab_size;
50 : /* MD5 sum hash table. */
51 : uint32_t sumhash_offset;
52 : uint32_t sumhash_used;
53 : uint32_t sumhash_size;
54 : };
55 :
56 : struct namehashent {
57 : /* Hash value of the name. */
58 : uint32_t hashval;
59 : /* Offset of the name in the string table. */
60 : uint32_t name_offset;
61 : /* Offset of the locale record. */
62 : uint32_t locrec_offset;
63 : };
64 :
65 : const struct locarhead *h;
66 : const struct namehashent *e;
67 1 : const void *p = MAP_FAILED;
68 2 : _cleanup_close_ int fd = -1;
69 1 : size_t sz = 0;
70 : struct stat st;
71 : unsigned i;
72 : int r;
73 :
74 1 : fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
75 1 : if (fd < 0)
76 0 : return errno == ENOENT ? 0 : -errno;
77 :
78 1 : if (fstat(fd, &st) < 0)
79 0 : return -errno;
80 :
81 1 : if (!S_ISREG(st.st_mode))
82 0 : return -EBADMSG;
83 :
84 1 : if (st.st_size < (off_t) sizeof(struct locarhead))
85 0 : return -EBADMSG;
86 :
87 1 : p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
88 1 : if (p == MAP_FAILED)
89 0 : return -errno;
90 :
91 1 : h = (const struct locarhead *) p;
92 2 : if (h->magic != 0xde020109 ||
93 2 : h->namehash_offset + h->namehash_size > st.st_size ||
94 2 : h->string_offset + h->string_size > st.st_size ||
95 2 : h->locrectab_offset + h->locrectab_size > st.st_size ||
96 1 : h->sumhash_offset + h->sumhash_size > st.st_size) {
97 0 : r = -EBADMSG;
98 0 : goto finish;
99 : }
100 :
101 1 : e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
102 908 : for (i = 0; i < h->namehash_size; i++) {
103 : char *z;
104 :
105 907 : if (e[i].locrec_offset == 0)
106 905 : continue;
107 :
108 2 : if (!utf8_is_valid((char*) p + e[i].name_offset))
109 0 : continue;
110 :
111 2 : z = strdup((char*) p + e[i].name_offset);
112 2 : if (!z) {
113 0 : r = -ENOMEM;
114 0 : goto finish;
115 : }
116 :
117 2 : r = set_consume(locales, z);
118 2 : if (r < 0)
119 0 : goto finish;
120 : }
121 :
122 1 : r = 0;
123 :
124 : finish:
125 1 : if (p != MAP_FAILED)
126 1 : munmap((void*) p, sz);
127 :
128 1 : return r;
129 : }
130 :
131 1 : static int add_locales_from_libdir (Set *locales) {
132 2 : _cleanup_closedir_ DIR *dir = NULL;
133 : struct dirent *entry;
134 : int r;
135 :
136 1 : dir = opendir("/usr/lib/locale");
137 1 : if (!dir)
138 0 : return errno == ENOENT ? 0 : -errno;
139 :
140 4 : FOREACH_DIRENT(entry, dir, return -errno) {
141 : char *z;
142 :
143 1 : if (entry->d_type != DT_DIR)
144 1 : continue;
145 :
146 0 : z = strdup(entry->d_name);
147 0 : if (!z)
148 0 : return -ENOMEM;
149 :
150 0 : r = set_consume(locales, z);
151 0 : if (r < 0 && r != -EEXIST)
152 0 : return r;
153 3 : }
154 :
155 1 : return 0;
156 : }
157 :
158 1 : int get_locales(char ***ret) {
159 2 : _cleanup_set_free_ Set *locales = NULL;
160 2 : _cleanup_strv_free_ char **l = NULL;
161 : int r;
162 :
163 1 : locales = set_new(&string_hash_ops);
164 1 : if (!locales)
165 0 : return -ENOMEM;
166 :
167 1 : r = add_locales_from_archive(locales);
168 1 : if (r < 0 && r != -ENOENT)
169 0 : return r;
170 :
171 1 : r = add_locales_from_libdir(locales);
172 1 : if (r < 0)
173 0 : return r;
174 :
175 1 : l = set_get_strv(locales);
176 1 : if (!l)
177 0 : return -ENOMEM;
178 :
179 1 : strv_sort(l);
180 :
181 1 : *ret = l;
182 1 : l = NULL;
183 :
184 1 : return 0;
185 : }
186 :
187 11 : bool locale_is_valid(const char *name) {
188 :
189 11 : if (isempty(name))
190 1 : return false;
191 :
192 10 : if (strlen(name) >= 128)
193 0 : return false;
194 :
195 10 : if (!utf8_is_valid(name))
196 0 : return false;
197 :
198 10 : if (!filename_is_valid(name))
199 1 : return false;
200 :
201 9 : if (!string_is_safe(name))
202 1 : return false;
203 :
204 8 : return true;
205 : }
206 :
207 : static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
208 : [VARIABLE_LANG] = "LANG",
209 : [VARIABLE_LANGUAGE] = "LANGUAGE",
210 : [VARIABLE_LC_CTYPE] = "LC_CTYPE",
211 : [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
212 : [VARIABLE_LC_TIME] = "LC_TIME",
213 : [VARIABLE_LC_COLLATE] = "LC_COLLATE",
214 : [VARIABLE_LC_MONETARY] = "LC_MONETARY",
215 : [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
216 : [VARIABLE_LC_PAPER] = "LC_PAPER",
217 : [VARIABLE_LC_NAME] = "LC_NAME",
218 : [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
219 : [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
220 : [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
221 : [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
222 : };
223 :
224 32 : DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);
|