chiark / gitweb /
localectl: print warning when there are options given on kernel cmdline
[elogind.git] / src / shared / 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 #include "util.h"
29
30 #include "locale-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_safe(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);