]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/locale-util.c
Merge pull request #144 from teg/udev-spawn-log-less-2
[thirdparty/systemd.git] / src / basic / locale-util.c
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 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 const void *p = MAP_FAILED;
68 _cleanup_close_ int fd = -1;
69 size_t sz = 0;
70 struct stat st;
71 unsigned i;
72 int r;
73
74 fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
75 if (fd < 0)
76 return errno == ENOENT ? 0 : -errno;
77
78 if (fstat(fd, &st) < 0)
79 return -errno;
80
81 if (!S_ISREG(st.st_mode))
82 return -EBADMSG;
83
84 if (st.st_size < (off_t) sizeof(struct locarhead))
85 return -EBADMSG;
86
87 p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
88 if (p == MAP_FAILED)
89 return -errno;
90
91 h = (const struct locarhead *) p;
92 if (h->magic != 0xde020109 ||
93 h->namehash_offset + h->namehash_size > st.st_size ||
94 h->string_offset + h->string_size > st.st_size ||
95 h->locrectab_offset + h->locrectab_size > st.st_size ||
96 h->sumhash_offset + h->sumhash_size > st.st_size) {
97 r = -EBADMSG;
98 goto finish;
99 }
100
101 e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
102 for (i = 0; i < h->namehash_size; i++) {
103 char *z;
104
105 if (e[i].locrec_offset == 0)
106 continue;
107
108 if (!utf8_is_valid((char*) p + e[i].name_offset))
109 continue;
110
111 z = strdup((char*) p + e[i].name_offset);
112 if (!z) {
113 r = -ENOMEM;
114 goto finish;
115 }
116
117 r = set_consume(locales, z);
118 if (r < 0)
119 goto finish;
120 }
121
122 r = 0;
123
124 finish:
125 if (p != MAP_FAILED)
126 munmap((void*) p, sz);
127
128 return r;
129 }
130
131 static int add_locales_from_libdir (Set *locales) {
132 _cleanup_closedir_ DIR *dir = NULL;
133 struct dirent *entry;
134 int r;
135
136 dir = opendir("/usr/lib/locale");
137 if (!dir)
138 return errno == ENOENT ? 0 : -errno;
139
140 FOREACH_DIRENT(entry, dir, return -errno) {
141 char *z;
142
143 if (entry->d_type != DT_DIR)
144 continue;
145
146 z = strdup(entry->d_name);
147 if (!z)
148 return -ENOMEM;
149
150 r = set_consume(locales, z);
151 if (r < 0 && r != -EEXIST)
152 return r;
153 }
154
155 return 0;
156 }
157
158 int get_locales(char ***ret) {
159 _cleanup_set_free_ Set *locales = NULL;
160 _cleanup_strv_free_ char **l = NULL;
161 int r;
162
163 locales = set_new(&string_hash_ops);
164 if (!locales)
165 return -ENOMEM;
166
167 r = add_locales_from_archive(locales);
168 if (r < 0 && r != -ENOENT)
169 return r;
170
171 r = add_locales_from_libdir(locales);
172 if (r < 0)
173 return r;
174
175 l = set_get_strv(locales);
176 if (!l)
177 return -ENOMEM;
178
179 strv_sort(l);
180
181 *ret = l;
182 l = NULL;
183
184 return 0;
185 }
186
187 bool locale_is_valid(const char *name) {
188
189 if (isempty(name))
190 return false;
191
192 if (strlen(name) >= 128)
193 return false;
194
195 if (!utf8_is_valid(name))
196 return false;
197
198 if (!filename_is_valid(name))
199 return false;
200
201 if (!string_is_safe(name))
202 return false;
203
204 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 DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);