]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/locale-util.c
util-lib: split out fd-related operations into fd-util.[ch]
[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 "fd-util.h"
25 #include "locale-util.h"
26 #include "set.h"
27 #include "string-util.h"
28 #include "strv.h"
29 #include "utf8.h"
30 #include "util.h"
31
32 static int add_locales_from_archive(Set *locales) {
33 /* Stolen from glibc... */
34
35 struct locarhead {
36 uint32_t magic;
37 /* Serial number. */
38 uint32_t serial;
39 /* Name hash table. */
40 uint32_t namehash_offset;
41 uint32_t namehash_used;
42 uint32_t namehash_size;
43 /* String table. */
44 uint32_t string_offset;
45 uint32_t string_used;
46 uint32_t string_size;
47 /* Table with locale records. */
48 uint32_t locrectab_offset;
49 uint32_t locrectab_used;
50 uint32_t locrectab_size;
51 /* MD5 sum hash table. */
52 uint32_t sumhash_offset;
53 uint32_t sumhash_used;
54 uint32_t sumhash_size;
55 };
56
57 struct namehashent {
58 /* Hash value of the name. */
59 uint32_t hashval;
60 /* Offset of the name in the string table. */
61 uint32_t name_offset;
62 /* Offset of the locale record. */
63 uint32_t locrec_offset;
64 };
65
66 const struct locarhead *h;
67 const struct namehashent *e;
68 const void *p = MAP_FAILED;
69 _cleanup_close_ int fd = -1;
70 size_t sz = 0;
71 struct stat st;
72 unsigned i;
73 int r;
74
75 fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
76 if (fd < 0)
77 return errno == ENOENT ? 0 : -errno;
78
79 if (fstat(fd, &st) < 0)
80 return -errno;
81
82 if (!S_ISREG(st.st_mode))
83 return -EBADMSG;
84
85 if (st.st_size < (off_t) sizeof(struct locarhead))
86 return -EBADMSG;
87
88 p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
89 if (p == MAP_FAILED)
90 return -errno;
91
92 h = (const struct locarhead *) p;
93 if (h->magic != 0xde020109 ||
94 h->namehash_offset + h->namehash_size > st.st_size ||
95 h->string_offset + h->string_size > st.st_size ||
96 h->locrectab_offset + h->locrectab_size > st.st_size ||
97 h->sumhash_offset + h->sumhash_size > st.st_size) {
98 r = -EBADMSG;
99 goto finish;
100 }
101
102 e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
103 for (i = 0; i < h->namehash_size; i++) {
104 char *z;
105
106 if (e[i].locrec_offset == 0)
107 continue;
108
109 if (!utf8_is_valid((char*) p + e[i].name_offset))
110 continue;
111
112 z = strdup((char*) p + e[i].name_offset);
113 if (!z) {
114 r = -ENOMEM;
115 goto finish;
116 }
117
118 r = set_consume(locales, z);
119 if (r < 0)
120 goto finish;
121 }
122
123 r = 0;
124
125 finish:
126 if (p != MAP_FAILED)
127 munmap((void*) p, sz);
128
129 return r;
130 }
131
132 static int add_locales_from_libdir (Set *locales) {
133 _cleanup_closedir_ DIR *dir = NULL;
134 struct dirent *entry;
135 int r;
136
137 dir = opendir("/usr/lib/locale");
138 if (!dir)
139 return errno == ENOENT ? 0 : -errno;
140
141 FOREACH_DIRENT(entry, dir, return -errno) {
142 char *z;
143
144 if (entry->d_type != DT_DIR)
145 continue;
146
147 z = strdup(entry->d_name);
148 if (!z)
149 return -ENOMEM;
150
151 r = set_consume(locales, z);
152 if (r < 0 && r != -EEXIST)
153 return r;
154 }
155
156 return 0;
157 }
158
159 int get_locales(char ***ret) {
160 _cleanup_set_free_ Set *locales = NULL;
161 _cleanup_strv_free_ char **l = NULL;
162 int r;
163
164 locales = set_new(&string_hash_ops);
165 if (!locales)
166 return -ENOMEM;
167
168 r = add_locales_from_archive(locales);
169 if (r < 0 && r != -ENOENT)
170 return r;
171
172 r = add_locales_from_libdir(locales);
173 if (r < 0)
174 return r;
175
176 l = set_get_strv(locales);
177 if (!l)
178 return -ENOMEM;
179
180 strv_sort(l);
181
182 *ret = l;
183 l = NULL;
184
185 return 0;
186 }
187
188 bool locale_is_valid(const char *name) {
189
190 if (isempty(name))
191 return false;
192
193 if (strlen(name) >= 128)
194 return false;
195
196 if (!utf8_is_valid(name))
197 return false;
198
199 if (!filename_is_valid(name))
200 return false;
201
202 if (!string_is_safe(name))
203 return false;
204
205 return true;
206 }
207
208 static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
209 [VARIABLE_LANG] = "LANG",
210 [VARIABLE_LANGUAGE] = "LANGUAGE",
211 [VARIABLE_LC_CTYPE] = "LC_CTYPE",
212 [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
213 [VARIABLE_LC_TIME] = "LC_TIME",
214 [VARIABLE_LC_COLLATE] = "LC_COLLATE",
215 [VARIABLE_LC_MONETARY] = "LC_MONETARY",
216 [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
217 [VARIABLE_LC_PAPER] = "LC_PAPER",
218 [VARIABLE_LC_NAME] = "LC_NAME",
219 [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
220 [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
221 [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
222 [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
223 };
224
225 DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);