XBPS Library API 20250531
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
55vpkg_map_add(xbps_dictionary_t d, const char *pkgname, const char *vpkgver, const char *provider)
56{
57 xbps_dictionary_t providers;
58 bool alloc;
59
60 providers = xbps_dictionary_get(d, pkgname);
61 if (!providers) {
62 providers = xbps_dictionary_create();
63 if (!providers)
64 return -ENOMEM;
65
66 if (!xbps_dictionary_set(d, pkgname, providers)) {
67 xbps_object_release(providers);
68 return -ENOMEM;
69 }
70 alloc = true;
71 }
72
73 if (!xbps_dictionary_set_cstring(providers, vpkgver, provider)) {
74 if (alloc)
75 xbps_object_release(providers);
76 return -ENOMEM;
77 }
78
79 if (alloc)
80 xbps_object_release(providers);
81
82 return 0;
83}
84
85static int
86store_virtualpkg(struct xbps_handle *xhp, const char *path, size_t line, char *val)
87{
88 char namebuf[XBPS_NAME_SIZE];
89 char pkgverbuf[XBPS_NAME_SIZE + sizeof("-99999_1")];
90 const char *vpkgname, *vpkgver, *provider;
91 char *p;
92 int r;
93
94
95 /*
96 * Parse strings delimited by ':' i.e
97 * <left>:<right>
98 */
99 p = strchr(val, ':');
100 if (p == NULL || p[1] == '\0') {
101 xbps_dbg_printf("%s: ignoring invalid "
102 "virtualpkg option at line %zu\n", path, line);
103 return 0;
104 }
105 *p++ = '\0';
106 provider = p;
107
108 if (xbps_pkg_name(namebuf, sizeof(namebuf), val)) {
109 vpkgname = namebuf;
110 vpkgver = val;
111 } else {
112 vpkgname = val;
113 snprintf(pkgverbuf, sizeof(pkgverbuf), "%s-99999_1", vpkgname);
114 vpkgver = pkgverbuf;
115 }
116
117 r = vpkg_map_add(xhp->vpkgd, vpkgname, vpkgver, provider);
118 if (r < 0)
119 return r;
120 r = vpkg_map_add(xhp->vpkgd_conf, vpkgname, vpkgver, provider);
121 if (r < 0)
122 return r;
123 xbps_dbg_printf("%s: added virtualpkg %s for %s\n", path, val, p);
124 return 0;
125}
126
127static void
128store_preserved_file(struct xbps_handle *xhp, const char *file)
129{
130 glob_t globbuf;
131 char *p = NULL, *rfile = NULL;
132 size_t len;
133 int rv = 0;
134
135 if (xhp->preserved_files == NULL) {
136 xhp->preserved_files = xbps_array_create();
137 assert(xhp->preserved_files);
138 }
139
140 rfile = xbps_xasprintf("%s%s", xhp->rootdir, file);
141
142 rv = glob(rfile, 0, NULL, &globbuf);
143 if (rv == GLOB_NOMATCH) {
144 if (xbps_match_string_in_array(xhp->preserved_files, file))
145 goto out;
146 xbps_array_add_cstring(xhp->preserved_files, file);
147 xbps_dbg_printf("Added preserved file: %s\n", file);
148 goto out;
149 } else if (rv != 0) {
150 goto out;
151 }
152 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
153 if (xbps_match_string_in_array(xhp->preserved_files, globbuf.gl_pathv[i]))
154 continue;
155
156 len = strlen(globbuf.gl_pathv[i]) - strlen(xhp->rootdir) + 1;
157 p = malloc(len);
158 assert(p);
159 xbps_strlcpy(p, globbuf.gl_pathv[i] + strlen(xhp->rootdir), len);
160 xbps_array_add_cstring(xhp->preserved_files, p);
161 xbps_dbg_printf("Added preserved file: %s (expanded from %s)\n", p, file);
162 free(p);
163 }
164out:
165 globfree(&globbuf);
166 free(rfile);
167}
168
169static bool
170store_repo(struct xbps_handle *xhp, const char *repo)
171{
172 if (xhp->flags & XBPS_FLAG_IGNORE_CONF_REPOS)
173 return false;
174
175 return xbps_repo_store(xhp, repo);
176}
177
178static void
179store_ignored_pkg(struct xbps_handle *xhp, const char *pkgname)
180{
181 if (xhp->ignored_pkgs == NULL) {
182 xhp->ignored_pkgs = xbps_array_create();
183 assert(xhp->ignored_pkgs);
184 }
185 xbps_array_add_cstring(xhp->ignored_pkgs, pkgname);
186 xbps_dbg_printf("Added ignored package: %s\n", pkgname);
187}
188
189static void
190store_noextract(struct xbps_handle *xhp, const char *value)
191{
192 if (*value == '\0')
193 return;
194 if (xhp->noextract == NULL) {
195 xhp->noextract = xbps_array_create();
196 assert(xhp->noextract);
197 }
198 xbps_array_add_cstring(xhp->noextract, value);
199 xbps_dbg_printf("Added noextract pattern: %s\n", value);
200}
201
202enum {
203 KEY_ERROR = 0,
204 KEY_ARCHITECTURE,
205 KEY_BESTMATCHING,
206 KEY_CACHEDIR,
207 KEY_IGNOREPKG,
208 KEY_INCLUDE,
209 KEY_NOEXTRACT,
210 KEY_PRESERVE,
211 KEY_REPOSITORY,
212 KEY_ROOTDIR,
213 KEY_STAGING,
214 KEY_SYSLOG,
215 KEY_VIRTUALPKG,
216 KEY_KEEPCONF,
217};
218
219static const struct key {
220 const char *str;
221 size_t len;
222 int key;
223} keys[] = {
224 { "architecture", 12, KEY_ARCHITECTURE },
225 { "bestmatching", 12, KEY_BESTMATCHING },
226 { "cachedir", 8, KEY_CACHEDIR },
227 { "ignorepkg", 9, KEY_IGNOREPKG },
228 { "include", 7, KEY_INCLUDE },
229 { "keepconf", 8, KEY_KEEPCONF },
230 { "noextract", 9, KEY_NOEXTRACT },
231 { "preserve", 8, KEY_PRESERVE },
232 { "repository", 10, KEY_REPOSITORY },
233 { "rootdir", 7, KEY_ROOTDIR },
234 { "staging", 7, KEY_STAGING },
235 { "syslog", 6, KEY_SYSLOG },
236 { "virtualpkg", 10, KEY_VIRTUALPKG },
237};
238
239static int
240cmpkey(const void *a, const void *b)
241{
242 const struct key *ka = a;
243 const struct key *kb = b;
244 return strncmp(ka->str, kb->str, ka->len);
245}
246
247static int
248parse_option(char *line, size_t linelen, char **valp, size_t *vallen)
249{
250 size_t len;
251 char *p;
252 struct key needle, *result;
253
254 p = strpbrk(line, " \t=");
255 if (p == NULL)
256 return KEY_ERROR;
257 needle.str = line;
258 needle.len = p-line;
259
260 while (*p && isblank((unsigned char)*p))
261 p++;
262 if (*p != '=')
263 return KEY_ERROR;
264
265 result = bsearch(&needle, keys, __arraycount(keys), sizeof(struct key), cmpkey);
266 if (result == NULL)
267 return KEY_ERROR;
268
269 p++;
270 while (isblank((unsigned char)*p))
271 p++;
272
273 len = linelen-(p-line);
274 /* eat trailing spaces, len - 1 here because \0 should be set -after- the first non-space
275 * if len points at the actual current character, we can never make it an empty string
276 * because than end needs to be set to -1, but len is a unsigned type thus would result in underflow */
277 while (len > 0 && isblank((unsigned char)p[len-1]))
278 len--;
279
280 p[len] = '\0';
281 *valp = p;
282 *vallen = len;
283
284 return result->key;
285}
286
287static int parse_file(struct xbps_handle *, const char *, bool);
288
289static int
290parse_files_glob(struct xbps_handle *xhp, xbps_dictionary_t seen,
291 const char *cwd, const char *pat, bool nested)
292{
293 char tmppath[PATH_MAX];
294 glob_t globbuf;
295 int rs, rv = 0, rv2;
296
297 rs = snprintf(tmppath, PATH_MAX, "%s/%s",
298 pat[0] == '/' ? xhp->rootdir : cwd, pat);
299 if (rs < 0 || rs >= PATH_MAX)
300 return ENOMEM;
301
302 switch (glob(tmppath, 0, NULL, &globbuf)) {
303 case 0: break;
304 case GLOB_NOSPACE: return ENOMEM;
305 case GLOB_NOMATCH: return 0;
306 default: return 0;
307 }
308 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
309 if (seen != NULL) {
310 const char *fname;
311 bool mask = false;
312 fname = basename(globbuf.gl_pathv[i]);
313 if (xbps_dictionary_get_bool(seen, fname, &mask) && mask)
314 continue;
315 xbps_dictionary_set_bool(seen, fname, true);
316 }
317 if ((rv2 = parse_file(xhp, globbuf.gl_pathv[i], nested)) != 0)
318 rv = rv2;
319 }
320 globfree(&globbuf);
321
322 return rv;
323}
324
325static int
326parse_file(struct xbps_handle *xhp, const char *path, bool nested)
327{
328 FILE *fp;
329 size_t len, nlines = 0;
330 ssize_t rd;
331 char *line = NULL;
332 int rv = 0;
333 int size, rs;
334 char *dir;
335
336 if ((fp = fopen(path, "r")) == NULL) {
337 rv = errno;
338 xbps_error_printf("cannot read configuration file %s: %s\n", path, strerror(rv));
339 return rv;
340 }
341
342 xbps_dbg_printf("Parsing configuration file: %s\n", path);
343
344 while ((rd = getline(&line, &len, fp)) != -1) {
345 char *val = NULL;
346 size_t vallen;
347
348 if (line[rd-1] == '\n') {
349 line[rd-1] = '\0';
350 rd--;
351 }
352
353 nlines++;
354
355 /* eat blanks */
356 while (isblank((unsigned char)*line))
357 line++;
358 /* ignore comments or empty lines */
359 if (line[0] == '#' || line[0] == '\0')
360 continue;
361
362 switch (parse_option(line, rd, &val, &vallen)) {
363 case KEY_ERROR:
364 xbps_dbg_printf("%s: ignoring invalid option at "
365 "line %zu\n", path, nlines);
366 continue;
367 case KEY_ROOTDIR:
368 size = sizeof xhp->rootdir;
369 rs = snprintf(xhp->rootdir, size, "%s", val);
370 if (rs < 0 || rs >= size) {
371 rv = ENOMEM;
372 break;
373 }
374 xbps_dbg_printf("%s: rootdir set to %s\n", path, val);
375 break;
376 case KEY_CACHEDIR:
377 size = sizeof xhp->cachedir;
378 rs = snprintf(xhp->cachedir, size, "%s", val);
379 if (rs < 0 || rs >= size) {
380 rv = ENOMEM;
381 break;
382 }
383 xbps_dbg_printf("%s: cachedir set to %s\n", path, val);
384 break;
385 case KEY_ARCHITECTURE:
386 size = sizeof xhp->native_arch;
387 rs = snprintf(xhp->native_arch, size, "%s", val);
388 if (rs < 0 || rs >= size) {
389 rv = ENOMEM;
390 break;
391 }
392 xbps_dbg_printf("%s: native architecture set to %s\n", path,
393 val);
394 break;
395 case KEY_STAGING:
396 if (strcasecmp(val, "true") == 0) {
397 xhp->flags |= XBPS_FLAG_USE_STAGE;
398 xbps_dbg_printf("%s: repository stage enabled\n", path);
399 } else {
400 xhp->flags &= ~XBPS_FLAG_USE_STAGE;
401 xbps_dbg_printf("%s: repository stage disabled\n", path);
402 }
403 break;
404 case KEY_SYSLOG:
405 if (strcasecmp(val, "true") == 0) {
406 xhp->flags &= ~XBPS_FLAG_DISABLE_SYSLOG;
407 xbps_dbg_printf("%s: syslog enabled\n", path);
408 } else {
409 xhp->flags |= XBPS_FLAG_DISABLE_SYSLOG;
410 xbps_dbg_printf("%s: syslog disabled\n", path);
411 }
412 break;
413 case KEY_REPOSITORY:
414 if (store_repo(xhp, val))
415 xbps_dbg_printf("%s: added repository %s\n", path, val);
416 break;
417 case KEY_VIRTUALPKG:
418 rv = store_virtualpkg(xhp, path, nlines, val);
419 if (rv < 0) {
420 rv = -rv;
421 break;
422 }
423 rv = 0;
424 break;
425 case KEY_PRESERVE:
426 store_preserved_file(xhp, val);
427 break;
428 case KEY_KEEPCONF:
429 if (strcasecmp(val, "true") == 0) {
430 xhp->flags |= XBPS_FLAG_KEEP_CONFIG;
431 xbps_dbg_printf("%s: config preservation enabled\n", path);
432 } else {
433 xhp->flags &= ~XBPS_FLAG_KEEP_CONFIG;
434 xbps_dbg_printf("%s: config preservation disabled\n", path);
435 }
436 break;
437 case KEY_BESTMATCHING:
438 if (strcasecmp(val, "true") == 0) {
439 xhp->flags |= XBPS_FLAG_BESTMATCH;
440 xbps_dbg_printf("%s: pkg best matching enabled\n", path);
441 } else {
442 xhp->flags &= ~XBPS_FLAG_BESTMATCH;
443 xbps_dbg_printf("%s: pkg best matching disabled\n", path);
444 }
445 break;
446 case KEY_IGNOREPKG:
447 store_ignored_pkg(xhp, val);
448 break;
449 case KEY_NOEXTRACT:
450 store_noextract(xhp, val);
451 break;
452 case KEY_INCLUDE:
453 /* Avoid double-nested parsing, only allow it once */
454 if (nested) {
455 xbps_dbg_printf("%s: ignoring nested include\n", path);
456 continue;
457 }
458 dir = strdup(path);
459 rv = parse_files_glob(xhp, NULL, dirname(dir), val, true);
460 free(dir);
461 break;
462 }
463 }
464 free(line);
465 fclose(fp);
466
467 return rv;
468}
469
470int HIDDEN
471xbps_conf_init(struct xbps_handle *xhp)
472{
473 xbps_dictionary_t seen;
474 int rv = 0;
475
476 assert(xhp);
477 seen = xbps_dictionary_create();
478 assert(seen);
479
480 if (*xhp->confdir) {
481 xbps_dbg_printf("Processing configuration directory: %s\n", xhp->confdir);
482 if ((rv = parse_files_glob(xhp, seen, xhp->confdir, "*.conf", false)))
483 goto out;
484 }
485 if (*xhp->sysconfdir) {
486 xbps_dbg_printf("Processing system configuration directory: %s\n", xhp->sysconfdir);
487 if ((rv = parse_files_glob(xhp, seen, xhp->sysconfdir, "*.conf", false)))
488 goto out;
489 }
490
491out:
492 xbps_object_release(seen);
493 return rv;
494}
char confdir[XBPS_MAXPATH]
Definition xbps.h:646
char rootdir[XBPS_MAXPATH]
Definition xbps.h:659
char native_arch[64]
Definition xbps.h:680
int flags
Definition xbps.h:688
char cachedir[XBPS_MAXPATH]
Definition xbps.h:666
Generic XBPS structure handler for initialization.
Definition xbps.h:559
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:388
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf
bool xbps_pkg_name(char *dst, size_t len, const char *pkg)
Definition util.c:253
size_t xbps_strlcpy(char *dst, const char *src, size_t dstsize)
Definition util.c:575