Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * arch.c - architecture database functions | |
4 | * | |
5 | * Copyright © 2011 Linaro Limited | |
6 | * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> | |
7 | * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> | |
8 | * | |
9 | * This is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <compat.h> | |
25 | ||
26 | #include <assert.h> | |
27 | #include <limits.h> | |
28 | #include <string.h> | |
29 | #include <stdbool.h> | |
30 | #include <stdlib.h> | |
31 | #include <stdio.h> | |
32 | ||
33 | #include <dpkg/i18n.h> | |
34 | #include <dpkg/c-ctype.h> | |
35 | #include <dpkg/ehandle.h> | |
36 | #include <dpkg/dpkg.h> | |
37 | #include <dpkg/dpkg-db.h> | |
38 | #include <dpkg/dir.h> | |
39 | #include <dpkg/varbuf.h> | |
40 | #include <dpkg/arch.h> | |
41 | ||
42 | #define DPKG_DB_ARCH_FILE "arch" | |
43 | ||
44 | /** | |
45 | * Verify if the architecture name is valid. | |
46 | * | |
47 | * Returns NULL if the architecture name is valid. Otherwise it returns a | |
48 | * string describing why it's not valid. Currently it ensures the name | |
49 | * starts with an alphanumeric and is then composed of a combinations of | |
50 | * hyphens and alphanumerics. | |
51 | * | |
52 | * The function will abort if you pass it a NULL pointer. | |
53 | * | |
54 | * @param name The architecture name to verify. | |
55 | */ | |
56 | const char * | |
57 | dpkg_arch_name_is_illegal(const char *name) | |
58 | { | |
59 | static char buf[150]; | |
60 | const char *p = name; | |
61 | ||
62 | assert(name); | |
63 | if (!*p) | |
64 | return _("may not be empty string"); | |
65 | if (!c_isalnum(*p)) | |
66 | return _("must start with an alphanumeric"); | |
67 | while (*++p != '\0') | |
68 | if (!c_isalnum(*p) && *p != '-') | |
69 | break; | |
70 | if (*p == '\0') | |
71 | return NULL; | |
72 | ||
73 | snprintf(buf, sizeof(buf), _("character '%c' not allowed (only " | |
74 | "letters, digits and characters '%s')"), | |
75 | *p, "-"); | |
76 | return buf; | |
77 | } | |
78 | ||
79 | /* This is a special architecture used to guarantee we always have a valid | |
80 | * structure to handle. */ | |
81 | static struct dpkg_arch arch_item_none = { | |
82 | .name = "", | |
83 | .type = DPKG_ARCH_NONE, | |
84 | .next = NULL, | |
85 | }; | |
86 | static struct dpkg_arch arch_item_empty = { | |
87 | .name = "", | |
88 | .type = DPKG_ARCH_EMPTY, | |
89 | .next = NULL, | |
90 | }; | |
91 | ||
92 | static struct dpkg_arch arch_item_any = { | |
93 | .name = "any", | |
94 | .type = DPKG_ARCH_WILDCARD, | |
95 | .next = NULL, | |
96 | }; | |
97 | static struct dpkg_arch arch_item_all = { | |
98 | .name = "all", | |
99 | .type = DPKG_ARCH_ALL, | |
100 | .next = &arch_item_any, | |
101 | }; | |
102 | static struct dpkg_arch arch_item_native = { | |
103 | .name = ARCHITECTURE, | |
104 | .type = DPKG_ARCH_NATIVE, | |
105 | .next = &arch_item_all, | |
106 | }; | |
107 | static struct dpkg_arch *arch_head = &arch_item_native; | |
108 | static struct dpkg_arch *arch_builtin_tail = &arch_item_any; | |
109 | static bool arch_list_dirty; | |
110 | ||
111 | static struct dpkg_arch * | |
112 | dpkg_arch_new(const char *name, enum dpkg_arch_type type) | |
113 | { | |
114 | struct dpkg_arch *new; | |
115 | ||
116 | new = nfmalloc(sizeof(*new)); | |
117 | new->next = NULL; | |
118 | new->name = nfstrsave(name); | |
119 | new->type = type; | |
120 | ||
121 | return new; | |
122 | } | |
123 | ||
124 | /** | |
125 | * Retrieve the struct dpkg_arch for the given architecture. | |
126 | * | |
127 | * Create a new structure for the architecture if it's not yet known from | |
128 | * the system, in that case it will have arch->type == arch_unknown, if the | |
129 | * architecture is illegal it will have arch->type == arch_illegal and if | |
130 | * name is NULL or an empty string then it will have arch->type == arch_none. | |
131 | * | |
132 | * @param name The architecture name. | |
133 | */ | |
134 | struct dpkg_arch * | |
135 | dpkg_arch_find(const char *name) | |
136 | { | |
137 | struct dpkg_arch *arch, *last_arch = NULL; | |
138 | enum dpkg_arch_type type; | |
139 | ||
140 | if (name == NULL) | |
141 | return &arch_item_none; | |
142 | if (name[0] == '\0') | |
143 | return &arch_item_empty; | |
144 | ||
145 | for (arch = arch_head; arch; arch = arch->next) { | |
146 | if (strcmp(arch->name, name) == 0) | |
147 | return arch; | |
148 | last_arch = arch; | |
149 | } | |
150 | ||
151 | if (dpkg_arch_name_is_illegal(name)) | |
152 | type = DPKG_ARCH_ILLEGAL; | |
153 | else | |
154 | type = DPKG_ARCH_UNKNOWN; | |
155 | ||
156 | arch = dpkg_arch_new(name, type); | |
157 | last_arch->next = arch; | |
158 | ||
159 | return arch; | |
160 | } | |
161 | ||
162 | /** | |
163 | * Return the struct dpkg_arch corresponding to the architecture type. | |
164 | * | |
165 | * The function only returns instances for types which are unique. For | |
166 | * forward-compatibility any unknown type will return NULL. | |
167 | */ | |
168 | struct dpkg_arch * | |
169 | dpkg_arch_get(enum dpkg_arch_type type) | |
170 | { | |
171 | switch (type) { | |
172 | case DPKG_ARCH_NONE: | |
173 | return &arch_item_none; | |
174 | case DPKG_ARCH_EMPTY: | |
175 | return &arch_item_empty; | |
176 | case DPKG_ARCH_WILDCARD: | |
177 | return &arch_item_any; | |
178 | case DPKG_ARCH_ALL: | |
179 | return &arch_item_all; | |
180 | case DPKG_ARCH_NATIVE: | |
181 | return &arch_item_native; | |
182 | case DPKG_ARCH_ILLEGAL: | |
183 | case DPKG_ARCH_FOREIGN: | |
184 | case DPKG_ARCH_UNKNOWN: | |
185 | internerr("architecture type %d is not unique", type); | |
186 | default: | |
187 | /* Ignore unknown types for forward-compatibility. */ | |
188 | return NULL; | |
189 | } | |
190 | } | |
191 | ||
192 | /** | |
193 | * Return the complete list of architectures. | |
194 | * | |
195 | * In fact it returns the first item of the linked list and you can | |
196 | * traverse the list by following arch->next until it's NULL. | |
197 | */ | |
198 | struct dpkg_arch * | |
199 | dpkg_arch_get_list(void) | |
200 | { | |
201 | return arch_head; | |
202 | } | |
203 | ||
204 | /** | |
205 | * Reset the list of architectures. | |
206 | * | |
207 | * Must be called before nffreeall() to ensure we don't point to | |
208 | * unallocated memory. | |
209 | */ | |
210 | void | |
211 | dpkg_arch_reset_list(void) | |
212 | { | |
213 | arch_builtin_tail->next = NULL; | |
214 | arch_list_dirty = false; | |
215 | } | |
216 | ||
217 | void | |
218 | varbuf_add_archqual(struct varbuf *vb, const struct dpkg_arch *arch) | |
219 | { | |
220 | if (arch->type == DPKG_ARCH_NONE) | |
221 | return; | |
222 | if (arch->type == DPKG_ARCH_EMPTY) | |
223 | return; | |
224 | ||
225 | varbuf_add_char(vb, ':'); | |
226 | varbuf_add_str(vb, arch->name); | |
227 | } | |
228 | ||
229 | /** | |
230 | * Return a descriptive architecture name. | |
231 | */ | |
232 | const char * | |
233 | dpkg_arch_describe(const struct dpkg_arch *arch) | |
234 | { | |
235 | if (arch->type == DPKG_ARCH_NONE) | |
236 | return C_("architecture", "<none>"); | |
237 | if (arch->type == DPKG_ARCH_EMPTY) | |
238 | return C_("architecture", "<empty>"); | |
239 | ||
240 | return arch->name; | |
241 | } | |
242 | ||
243 | /** | |
244 | * Add a new foreign dpkg_arch architecture. | |
245 | */ | |
246 | struct dpkg_arch * | |
247 | dpkg_arch_add(const char *name) | |
248 | { | |
249 | struct dpkg_arch *arch; | |
250 | ||
251 | arch = dpkg_arch_find(name); | |
252 | if (arch->type == DPKG_ARCH_UNKNOWN) { | |
253 | arch->type = DPKG_ARCH_FOREIGN; | |
254 | arch_list_dirty = true; | |
255 | } | |
256 | ||
257 | return arch; | |
258 | } | |
259 | ||
260 | /** | |
261 | * Unmark a foreign dpkg_arch architecture. | |
262 | */ | |
263 | void | |
264 | dpkg_arch_unmark(struct dpkg_arch *arch_remove) | |
265 | { | |
266 | struct dpkg_arch *arch; | |
267 | ||
268 | for (arch = arch_builtin_tail->next; arch; arch = arch->next) { | |
269 | if (arch->type != DPKG_ARCH_FOREIGN) | |
270 | continue; | |
271 | ||
272 | if (arch == arch_remove) { | |
273 | arch->type = DPKG_ARCH_UNKNOWN; | |
274 | arch_list_dirty = true; | |
275 | return; | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | /** | |
281 | * Load the architecture database. | |
282 | */ | |
283 | void | |
284 | dpkg_arch_load_list(void) | |
285 | { | |
286 | FILE *fp; | |
287 | char *archfile; | |
288 | char archname[_POSIX2_LINE_MAX]; | |
289 | ||
290 | archfile = dpkg_db_get_path(DPKG_DB_ARCH_FILE); | |
291 | fp = fopen(archfile, "r"); | |
292 | if (fp == NULL) { | |
293 | arch_list_dirty = true; | |
294 | free(archfile); | |
295 | return; | |
296 | } | |
297 | ||
298 | while (fgets_checked(archname, sizeof(archname), fp, archfile) >= 0) | |
299 | dpkg_arch_add(archname); | |
300 | ||
301 | free(archfile); | |
302 | fclose(fp); | |
303 | } | |
304 | ||
305 | /** | |
306 | * Save the architecture database. | |
307 | */ | |
308 | void | |
309 | dpkg_arch_save_list(void) | |
310 | { | |
311 | struct atomic_file *file; | |
312 | struct dpkg_arch *arch; | |
313 | char *archfile; | |
314 | ||
315 | if (!arch_list_dirty) | |
316 | return; | |
317 | ||
318 | archfile = dpkg_db_get_path(DPKG_DB_ARCH_FILE); | |
319 | file = atomic_file_new(archfile, 0); | |
320 | atomic_file_open(file); | |
321 | ||
322 | for (arch = arch_head; arch; arch = arch->next) { | |
323 | if (arch->type != DPKG_ARCH_FOREIGN && | |
324 | arch->type != DPKG_ARCH_NATIVE) | |
325 | continue; | |
326 | ||
327 | if (fprintf(file->fp, "%s\n", arch->name) < 0) | |
328 | ohshite(_("error writing to architecture list")); | |
329 | } | |
330 | ||
331 | atomic_file_sync(file); | |
332 | atomic_file_close(file); | |
333 | atomic_file_commit(file); | |
334 | atomic_file_free(file); | |
335 | ||
336 | dir_sync_path(dpkg_db_get_dir()); | |
337 | ||
338 | arch_list_dirty = false; | |
339 | ||
340 | free(archfile); | |
341 | } |