XBPS Library API 20240111
The X Binary Package System
conf.c
1/*
2 * Copyright (c) 2011-2015 Juan Romero Pardines.
3 * Copyright (c) 2014 Enno Boland.
4 * Copyright (c) 2019 Duncan Overbruck <mail@duncano.de>.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/types.h>
29#ifdef __FreeBSD__
30#define _WITH_GETLINE /* getline() */
31#endif
32#include <ctype.h>
33#include <dirent.h>
34#include <errno.h>
35#include <glob.h>
36#include <libgen.h>
37#include <limits.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <strings.h>
43
44#include "xbps_api_impl.h"
45
46/**
47 * @file lib/conf.c
48 * @brief Configuration parsing
49 * @defgroup conf Configuration parsing
50 *
51 * Functions for parsing xbps configuration files.
52 */
53
54static int
55store_virtualpkg(struct xbps_handle *xhp, const char *path, size_t line, char *val)
56{
57 char *p;
58 /*
59 * Parse strings delimited by ':' i.e
60 * <left>:<right>
61 */
62 p = strchr(val, ':');
63 if (p == NULL || p[1] == '\0') {
64 xbps_dbg_printf("%s: ignoring invalid "
65 "virtualpkg option at line %zu\n", path, line);
66 return 0;
67 }
68 *p++ = '\0';
69
70 if (!xbps_dictionary_set_cstring(xhp->vpkgd, val, p))
71 return -errno;
72 if (!xbps_dictionary_set_cstring(xhp->vpkgd_conf, val, p))
73 return -errno;
74 xbps_dbg_printf("%s: added virtualpkg %s for %s\n", path, val, p);
75 return 1;
76}
77
78static void
79store_preserved_file(struct xbps_handle *xhp, const char *file)
80{
81 glob_t globbuf;
82 char *p = NULL, *rfile = NULL;
83 size_t len;
84 int rv = 0;
85
86 if (xhp->preserved_files == NULL) {
87 xhp->preserved_files = xbps_array_create();
88 assert(xhp->preserved_files);
89 }
90
91 rfile = xbps_xasprintf("%s%s", xhp->rootdir, file);
92
93 rv = glob(rfile, 0, NULL, &globbuf);
94 if (rv == GLOB_NOMATCH) {
95 if (xbps_match_string_in_array(xhp->preserved_files, file))
96 goto out;
97 xbps_array_add_cstring(xhp->preserved_files, file);
98 xbps_dbg_printf("Added preserved file: %s\n", file);
99 goto out;
100 } else if (rv != 0) {
101 goto out;
102 }
103 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
104 if (xbps_match_string_in_array(xhp->preserved_files, globbuf.gl_pathv[i]))
105 continue;
106
107 len = strlen(globbuf.gl_pathv[i]) - strlen(xhp->rootdir) + 1;
108 p = malloc(len);
109 assert(p);
110 xbps_strlcpy(p, globbuf.gl_pathv[i] + strlen(xhp->rootdir), len);
111 xbps_array_add_cstring(xhp->preserved_files, p);
112 xbps_dbg_printf("Added preserved file: %s (expanded from %s)\n", p, file);
113 free(p);
114 }
115out:
116 globfree(&globbuf);
117 free(rfile);
118}
119
120static bool
121store_repo(struct xbps_handle *xhp, const char *repo)
122{
123 if (xhp->flags & XBPS_FLAG_IGNORE_CONF_REPOS)
124 return false;
125
126 return xbps_repo_store(xhp, repo);
127}
128
129static void
130store_ignored_pkg(struct xbps_handle *xhp, const char *pkgname)
131{
132 if (xhp->ignored_pkgs == NULL) {
133 xhp->ignored_pkgs = xbps_array_create();
134 assert(xhp->ignored_pkgs);
135 }
136 xbps_array_add_cstring(xhp->ignored_pkgs, pkgname);
137 xbps_dbg_printf("Added ignored package: %s\n", pkgname);
138}
139
140static void
141store_noextract(struct xbps_handle *xhp, const char *value)
142{
143 if (*value == '\0')
144 return;
145 if (xhp->noextract == NULL) {
146 xhp->noextract = xbps_array_create();
147 assert(xhp->noextract);
148 }
149 xbps_array_add_cstring(xhp->noextract, value);
150 xbps_dbg_printf("Added noextract pattern: %s\n", value);
151}
152
153enum {
154 KEY_ERROR = 0,
155 KEY_ARCHITECTURE,
156 KEY_BESTMATCHING,
157 KEY_CACHEDIR,
158 KEY_IGNOREPKG,
159 KEY_INCLUDE,
160 KEY_NOEXTRACT,
161 KEY_PRESERVE,
162 KEY_REPOSITORY,
163 KEY_ROOTDIR,
164 KEY_SYSLOG,
165 KEY_VIRTUALPKG,
166 KEY_KEEPCONF,
167};
168
169static const struct key {
170 const char *str;
171 size_t len;
172 int key;
173} keys[] = {
174 { "architecture", 12, KEY_ARCHITECTURE },
175 { "bestmatching", 12, KEY_BESTMATCHING },
176 { "cachedir", 8, KEY_CACHEDIR },
177 { "ignorepkg", 9, KEY_IGNOREPKG },
178 { "include", 7, KEY_INCLUDE },
179 { "keepconf", 8, KEY_KEEPCONF },
180 { "noextract", 9, KEY_NOEXTRACT },
181 { "preserve", 8, KEY_PRESERVE },
182 { "repository", 10, KEY_REPOSITORY },
183 { "rootdir", 7, KEY_ROOTDIR },
184 { "syslog", 6, KEY_SYSLOG },
185 { "virtualpkg", 10, KEY_VIRTUALPKG },
186};
187
188static int
189cmpkey(const void *a, const void *b)
190{
191 const struct key *ka = a;
192 const struct key *kb = b;
193 return strncmp(ka->str, kb->str, ka->len);
194}
195
196static int
197parse_option(char *line, size_t linelen, char **valp, size_t *vallen)
198{
199 size_t len;
200 char *p;
201 struct key needle, *result;
202
203 p = strpbrk(line, " \t=");
204 if (p == NULL)
205 return KEY_ERROR;
206 needle.str = line;
207 needle.len = p-line;
208
209 while (*p && isblank((unsigned char)*p))
210 p++;
211 if (*p != '=')
212 return KEY_ERROR;
213
214 result = bsearch(&needle, keys, __arraycount(keys), sizeof(struct key), cmpkey);
215 if (result == NULL)
216 return KEY_ERROR;
217
218 p++;
219 while (isblank((unsigned char)*p))
220 p++;
221
222 len = linelen-(p-line);
223 /* eat trailing spaces, len - 1 here because \0 should be set -after- the first non-space
224 * if len points at the actual current character, we can never make it an empty string
225 * because than end needs to be set to -1, but len is a unsigned type thus would result in underflow */
226 while (len > 0 && isblank((unsigned char)p[len-1]))
227 len--;
228
229 p[len] = '\0';
230 *valp = p;
231 *vallen = len;
232
233 return result->key;
234}
235
236static int parse_file(struct xbps_handle *, const char *, bool);
237
238static int
239parse_files_glob(struct xbps_handle *xhp, xbps_dictionary_t seen,
240 const char *cwd, const char *pat, bool nested)
241{
242 char tmppath[PATH_MAX];
243 glob_t globbuf;
244 int rs, rv = 0, rv2;
245
246 rs = snprintf(tmppath, PATH_MAX, "%s/%s",
247 pat[0] == '/' ? xhp->rootdir : cwd, pat);
248 if (rs < 0 || rs >= PATH_MAX)
249 return ENOMEM;
250
251 switch (glob(tmppath, 0, NULL, &globbuf)) {
252 case 0: break;
253 case GLOB_NOSPACE: return ENOMEM;
254 case GLOB_NOMATCH: return 0;
255 default: return 0;
256 }
257 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
258 if (seen != NULL) {
259 const char *fname;
260 bool mask = false;
261 fname = basename(globbuf.gl_pathv[i]);
262 if (xbps_dictionary_get_bool(seen, fname, &mask) && mask)
263 continue;
264 xbps_dictionary_set_bool(seen, fname, true);
265 }
266 if ((rv2 = parse_file(xhp, globbuf.gl_pathv[i], nested)) != 0)
267 rv = rv2;
268 }
269 globfree(&globbuf);
270
271 return rv;
272}
273
274static int
275parse_file(struct xbps_handle *xhp, const char *path, bool nested)
276{
277 FILE *fp;
278 size_t len, nlines = 0;
279 ssize_t rd;
280 char *line = NULL;
281 int rv = 0;
282 int size, rs;
283 char *dir;
284
285 if ((fp = fopen(path, "r")) == NULL) {
286 rv = errno;
287 xbps_error_printf("cannot read configuration file %s: %s\n", path, strerror(rv));
288 return rv;
289 }
290
291 xbps_dbg_printf("Parsing configuration file: %s\n", path);
292
293 while ((rd = getline(&line, &len, fp)) != -1) {
294 char *val = NULL;
295 size_t vallen;
296
297 if (line[rd-1] == '\n') {
298 line[rd-1] = '\0';
299 rd--;
300 }
301
302 nlines++;
303
304 /* eat blanks */
305 while (isblank((unsigned char)*line))
306 line++;
307 /* ignore comments or empty lines */
308 if (line[0] == '#' || line[0] == '\0')
309 continue;
310
311 switch (parse_option(line, rd, &val, &vallen)) {
312 case KEY_ERROR:
313 xbps_dbg_printf("%s: ignoring invalid option at "
314 "line %zu\n", path, nlines);
315 continue;
316 case KEY_ROOTDIR:
317 size = sizeof xhp->rootdir;
318 rs = snprintf(xhp->rootdir, size, "%s", val);
319 if (rs < 0 || rs >= size) {
320 rv = ENOMEM;
321 break;
322 }
323 xbps_dbg_printf("%s: rootdir set to %s\n", path, val);
324 break;
325 case KEY_CACHEDIR:
326 size = sizeof xhp->cachedir;
327 rs = snprintf(xhp->cachedir, size, "%s", val);
328 if (rs < 0 || rs >= size) {
329 rv = ENOMEM;
330 break;
331 }
332 xbps_dbg_printf("%s: cachedir set to %s\n", path, val);
333 break;
334 case KEY_ARCHITECTURE:
335 size = sizeof xhp->native_arch;
336 rs = snprintf(xhp->native_arch, size, "%s", val);
337 if (rs < 0 || rs >= size) {
338 rv = ENOMEM;
339 break;
340 }
341 xbps_dbg_printf("%s: native architecture set to %s\n", path,
342 val);
343 break;
344 case KEY_SYSLOG:
345 if (strcasecmp(val, "true") == 0) {
346 xhp->flags &= ~XBPS_FLAG_DISABLE_SYSLOG;
347 xbps_dbg_printf("%s: syslog enabled\n", path);
348 } else {
349 xhp->flags |= XBPS_FLAG_DISABLE_SYSLOG;
350 xbps_dbg_printf("%s: syslog disabled\n", path);
351 }
352 break;
353 case KEY_REPOSITORY:
354 if (store_repo(xhp, val))
355 xbps_dbg_printf("%s: added repository %s\n", path, val);
356 break;
357 case KEY_VIRTUALPKG:
358 rv = store_virtualpkg(xhp, path, nlines, val);
359 if (rv < 0) {
360 rv = -rv;
361 break;
362 }
363 rv = 0;
364 break;
365 case KEY_PRESERVE:
366 store_preserved_file(xhp, val);
367 break;
368 case KEY_KEEPCONF:
369 if (strcasecmp(val, "true") == 0) {
370 xhp->flags |= XBPS_FLAG_KEEP_CONFIG;
371 xbps_dbg_printf("%s: config preservation enabled\n", path);
372 } else {
373 xhp->flags &= ~XBPS_FLAG_KEEP_CONFIG;
374 xbps_dbg_printf("%s: config preservation disabled\n", path);
375 }
376 break;
377 case KEY_BESTMATCHING:
378 if (strcasecmp(val, "true") == 0) {
379 xhp->flags |= XBPS_FLAG_BESTMATCH;
380 xbps_dbg_printf("%s: pkg best matching enabled\n", path);
381 } else {
382 xhp->flags &= ~XBPS_FLAG_BESTMATCH;
383 xbps_dbg_printf("%s: pkg best matching disabled\n", path);
384 }
385 break;
386 case KEY_IGNOREPKG:
387 store_ignored_pkg(xhp, val);
388 break;
389 case KEY_NOEXTRACT:
390 store_noextract(xhp, val);
391 break;
392 case KEY_INCLUDE:
393 /* Avoid double-nested parsing, only allow it once */
394 if (nested) {
395 xbps_dbg_printf("%s: ignoring nested include\n", path);
396 continue;
397 }
398 dir = strdup(path);
399 rv = parse_files_glob(xhp, NULL, dirname(dir), val, true);
400 free(dir);
401 break;
402 }
403 }
404 free(line);
405 fclose(fp);
406
407 return rv;
408}
409
410int HIDDEN
411xbps_conf_init(struct xbps_handle *xhp)
412{
413 xbps_dictionary_t seen;
414 int rv = 0;
415
416 assert(xhp);
417 seen = xbps_dictionary_create();
418 assert(seen);
419
420 if (*xhp->confdir) {
421 xbps_dbg_printf("Processing configuration directory: %s\n", xhp->confdir);
422 if ((rv = parse_files_glob(xhp, seen, xhp->confdir, "*.conf", false)))
423 goto out;
424 }
425 if (*xhp->sysconfdir) {
426 xbps_dbg_printf("Processing system configuration directory: %s\n", xhp->sysconfdir);
427 if ((rv = parse_files_glob(xhp, seen, xhp->sysconfdir, "*.conf", false)))
428 goto out;
429 }
430
431out:
432 xbps_object_release(seen);
433 return rv;
434}
char rootdir[XBPS_MAXPATH]
Definition xbps.h:650
char confdir[XBPS_MAXPATH+sizeof(XBPS_SYSCONF_PATH)]
Definition xbps.h:637
char native_arch[64]
Definition xbps.h:671
char cachedir[XBPS_MAXPATH+sizeof(XBPS_CACHE_PATH)]
Definition xbps.h:657
int flags
Definition xbps.h:679
Generic XBPS structure handler for initialization.
Definition xbps.h:550
bool xbps_match_string_in_array(xbps_array_t array, const char *val)
bool xbps_repo_store(struct xbps_handle *xhp, const char *url)
Definition repo.c:267
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
size_t xbps_strlcpy(char *dst, const char *src, size_t dstsize)
Definition util.c:575