2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
5 * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/utsname.h>
36 #include <sys/sysctl.h>
50 STAILQ_ENTRY(config_value) next;
58 STAILQ_HEAD(, config_value) *list;
60 bool main_only; /* Only set in pkg.conf. */
63 static struct config_entry c[] = {
67 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
91 [ASSUME_ALWAYS_YES] = {
148 pkg_get_myabi(char *dest, size_t sz)
151 char machine_arch[255];
159 len = sizeof(machine_arch);
160 error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0);
163 machine_arch[len] = '\0';
166 * Use __FreeBSD_version rather than kernel version (uts.release) for
167 * use in jails. This is equivalent to the value of uname -U.
169 snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000,
176 subst_packagesite(const char *abi)
179 const char *variable_string;
182 if (c[PACKAGESITE].value != NULL)
183 oldval = c[PACKAGESITE].value;
185 oldval = c[PACKAGESITE].val;
187 if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
190 asprintf(&newval, "%.*s%s%s",
191 (int)(variable_string - oldval), oldval, abi,
192 variable_string + strlen("${ABI}"));
194 errx(EXIT_FAILURE, "asprintf");
196 free(c[PACKAGESITE].value);
197 c[PACKAGESITE].value = newval;
201 boolstr_to_bool(const char *str)
203 if (str != NULL && (strcasecmp(str, "true") == 0 ||
204 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
212 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
217 const ucl_object_t *cur, *seq, *tmp;
218 ucl_object_iter_t it = NULL, itseq = NULL, it_obj = NULL;
219 struct config_entry *temp_config;
220 struct config_value *cv;
221 const char *key, *evkey;
225 /* Temporary config for configs that may be disabled. */
226 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
227 buffp = open_memstream(&buf, &bufsz);
229 err(EXIT_FAILURE, "open_memstream()");
231 while ((cur = ucl_iterate_object(obj, &it, true))) {
232 key = ucl_object_key(cur);
236 memset(buf, 0, bufsz);
239 if (conftype == CONFFILE_PKG) {
240 for (j = 0; j < strlen(key); ++j)
241 fputc(toupper(key[j]), buffp);
243 } else if (conftype == CONFFILE_REPO) {
244 if (strcasecmp(key, "url") == 0)
245 fputs("PACKAGESITE", buffp);
246 else if (strcasecmp(key, "mirror_type") == 0)
247 fputs("MIRROR_TYPE", buffp);
248 else if (strcasecmp(key, "signature_type") == 0)
249 fputs("SIGNATURE_TYPE", buffp);
250 else if (strcasecmp(key, "fingerprints") == 0)
251 fputs("FINGERPRINTS", buffp);
252 else if (strcasecmp(key, "pubkey") == 0)
253 fputs("PUBKEY", buffp);
254 else if (strcasecmp(key, "enabled") == 0) {
255 if ((cur->type != UCL_BOOLEAN) ||
256 !ucl_object_toboolean(cur))
263 for (i = 0; i < CONFIG_SIZE; i++) {
264 if (strcmp(buf, c[i].key) == 0)
268 /* Silently skip unknown keys to be future compatible. */
269 if (i == CONFIG_SIZE)
272 /* env has priority over config file */
276 /* Parse sequence value ["item1", "item2"] */
278 case PKG_CONFIG_LIST:
279 if (cur->type != UCL_ARRAY) {
280 warnx("Skipping invalid array "
281 "value for %s.\n", c[i].key);
284 temp_config[i].list =
285 malloc(sizeof(*temp_config[i].list));
286 STAILQ_INIT(temp_config[i].list);
288 while ((seq = ucl_iterate_object(cur, &itseq, true))) {
289 if (seq->type != UCL_STRING)
291 cv = malloc(sizeof(struct config_value));
293 strdup(ucl_object_tostring(seq));
294 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
298 case PKG_CONFIG_BOOL:
299 temp_config[i].value =
300 strdup(ucl_object_toboolean(cur) ? "yes" : "no");
302 case PKG_CONFIG_OBJECT:
303 if (strcmp(c[i].key, "PKG_ENV") == 0) {
305 ucl_iterate_object(cur, &it_obj, true))) {
306 evkey = ucl_object_key(tmp);
307 if (evkey != NULL && *evkey != '\0') {
308 setenv(evkey, ucl_object_tostring_forced(tmp), 1);
314 /* Normal string value. */
315 temp_config[i].value = strdup(ucl_object_tostring(cur));
320 /* Repo is enabled, copy over all settings from temp_config. */
321 for (i = 0; i < CONFIG_SIZE; i++) {
324 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
325 if (conftype != CONFFILE_PKG && c[i].main_only == true)
328 case PKG_CONFIG_LIST:
329 c[i].list = temp_config[i].list;
332 c[i].value = temp_config[i].value;
344 * Parse new repo style configs in style:
351 parse_repo_file(ucl_object_t *obj, const char *requested_repo)
353 ucl_object_iter_t it = NULL;
354 const ucl_object_t *cur;
357 while ((cur = ucl_iterate_object(obj, &it, true))) {
358 key = ucl_object_key(cur);
363 if (cur->type != UCL_OBJECT)
366 if (requested_repo != NULL && strcmp(requested_repo, key) != 0)
369 config_parse(cur, CONFFILE_REPO);
375 read_conf_file(const char *confpath, const char *requested_repo,
376 pkg_conf_file_t conftype)
378 struct ucl_parser *p;
379 ucl_object_t *obj = NULL;
381 p = ucl_parser_new(0);
383 if (!ucl_parser_add_file(p, confpath)) {
385 errx(EXIT_FAILURE, "Unable to parse configuration "
386 "file %s: %s", confpath, ucl_parser_get_error(p));
388 /* no configuration present */
392 obj = ucl_parser_get_object(p);
393 if (obj->type != UCL_OBJECT)
394 warnx("Invalid configuration format, ignoring the "
395 "configuration file %s", confpath);
397 if (conftype == CONFFILE_PKG)
398 config_parse(obj, conftype);
399 else if (conftype == CONFFILE_REPO)
400 parse_repo_file(obj, requested_repo);
403 ucl_object_unref(obj);
410 load_repositories(const char *repodir, const char *requested_repo)
416 char path[MAXPATHLEN];
421 if ((d = opendir(repodir)) == NULL)
424 while ((ent = readdir(d))) {
425 /* Trim out 'repos'. */
426 if ((n = strlen(ent->d_name)) <= 5)
428 p = &ent->d_name[n - 5];
429 if (strcmp(p, ".conf") == 0) {
430 snprintf(path, sizeof(path), "%s%s%s",
432 repodir[strlen(repodir) - 1] == '/' ? "" : "/",
434 if (access(path, F_OK) != 0)
436 if (read_conf_file(path, requested_repo,
451 config_init(const char *requested_repo)
455 const char *localbase;
457 char confpath[MAXPATHLEN];
458 struct config_value *cv;
461 for (i = 0; i < CONFIG_SIZE; i++) {
462 val = getenv(c[i].key);
466 case PKG_CONFIG_LIST:
467 /* Split up comma-separated items from env. */
468 c[i].list = malloc(sizeof(*c[i].list));
469 STAILQ_INIT(c[i].list);
470 for (env_list_item = strtok(val, ",");
471 env_list_item != NULL;
472 env_list_item = strtok(NULL, ",")) {
474 malloc(sizeof(struct config_value));
476 strdup(env_list_item);
477 STAILQ_INSERT_TAIL(c[i].list, cv,
488 /* Read LOCALBASE/etc/pkg.conf first. */
489 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
490 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
493 if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL,
497 /* Then read in all repos from REPOS_DIR list of directories. */
498 if (c[REPOS_DIR].list == NULL) {
499 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
500 STAILQ_INIT(c[REPOS_DIR].list);
501 cv = malloc(sizeof(struct config_value));
502 cv->value = strdup("/etc/pkg");
503 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
504 cv = malloc(sizeof(struct config_value));
505 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
507 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
510 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
511 if (load_repositories(cv->value, requested_repo))
515 if (c[ABI].val == NULL && c[ABI].value == NULL) {
516 if (pkg_get_myabi(abi, BUFSIZ) != 0)
517 errx(EXIT_FAILURE, "Failed to determine the system "
522 subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
528 config_string(pkg_config_key k, const char **val)
530 if (c[k].type != PKG_CONFIG_STRING)
533 if (c[k].value != NULL)
542 config_bool(pkg_config_key k, bool *val)
546 if (c[k].type != PKG_CONFIG_BOOL)
551 if (c[k].value != NULL)
556 if (boolstr_to_bool(value))
563 config_finish(void) {
566 for (i = 0; i < CONFIG_SIZE; i++)