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>
52 STAILQ_ENTRY(config_value) next;
60 STAILQ_HEAD(, config_value) *list;
62 bool main_only; /* Only set in pkg.conf. */
65 static struct config_entry c[] = {
69 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
93 [ASSUME_ALWAYS_YES] = {
150 pkg_get_myabi(char *dest, size_t sz)
153 char machine_arch[255];
161 len = sizeof(machine_arch);
162 error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0);
165 machine_arch[len] = '\0';
168 * Use __FreeBSD_version rather than kernel version (uts.release) for
169 * use in jails. This is equivalent to the value of uname -U.
171 snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000,
178 subst_packagesite(const char *abi)
181 const char *variable_string;
184 if (c[PACKAGESITE].value != NULL)
185 oldval = c[PACKAGESITE].value;
187 oldval = c[PACKAGESITE].val;
189 if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
192 asprintf(&newval, "%.*s%s%s",
193 (int)(variable_string - oldval), oldval, abi,
194 variable_string + strlen("${ABI}"));
196 errx(EXIT_FAILURE, "asprintf");
198 free(c[PACKAGESITE].value);
199 c[PACKAGESITE].value = newval;
203 boolstr_to_bool(const char *str)
205 if (str != NULL && (strcasecmp(str, "true") == 0 ||
206 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
214 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
219 const ucl_object_t *cur, *seq, *tmp;
220 ucl_object_iter_t it = NULL, itseq = NULL, it_obj = NULL;
221 struct config_entry *temp_config;
222 struct config_value *cv;
223 const char *key, *evkey;
227 /* Temporary config for configs that may be disabled. */
228 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
229 buffp = open_memstream(&buf, &bufsz);
231 err(EXIT_FAILURE, "open_memstream()");
233 while ((cur = ucl_iterate_object(obj, &it, true))) {
234 key = ucl_object_key(cur);
238 memset(buf, 0, bufsz);
241 if (conftype == CONFFILE_PKG) {
242 for (j = 0; j < strlen(key); ++j)
243 fputc(toupper(key[j]), buffp);
245 } else if (conftype == CONFFILE_REPO) {
246 if (strcasecmp(key, "url") == 0)
247 fputs("PACKAGESITE", buffp);
248 else if (strcasecmp(key, "mirror_type") == 0)
249 fputs("MIRROR_TYPE", buffp);
250 else if (strcasecmp(key, "signature_type") == 0)
251 fputs("SIGNATURE_TYPE", buffp);
252 else if (strcasecmp(key, "fingerprints") == 0)
253 fputs("FINGERPRINTS", buffp);
254 else if (strcasecmp(key, "pubkey") == 0)
255 fputs("PUBKEY", buffp);
256 else if (strcasecmp(key, "enabled") == 0) {
257 if ((cur->type != UCL_BOOLEAN) ||
258 !ucl_object_toboolean(cur))
265 for (i = 0; i < CONFIG_SIZE; i++) {
266 if (strcmp(buf, c[i].key) == 0)
270 /* Silently skip unknown keys to be future compatible. */
271 if (i == CONFIG_SIZE)
274 /* env has priority over config file */
278 /* Parse sequence value ["item1", "item2"] */
280 case PKG_CONFIG_LIST:
281 if (cur->type != UCL_ARRAY) {
282 warnx("Skipping invalid array "
283 "value for %s.\n", c[i].key);
286 temp_config[i].list =
287 malloc(sizeof(*temp_config[i].list));
288 STAILQ_INIT(temp_config[i].list);
290 while ((seq = ucl_iterate_object(cur, &itseq, true))) {
291 if (seq->type != UCL_STRING)
293 cv = malloc(sizeof(struct config_value));
295 strdup(ucl_object_tostring(seq));
296 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
300 case PKG_CONFIG_BOOL:
301 temp_config[i].value =
302 strdup(ucl_object_toboolean(cur) ? "yes" : "no");
304 case PKG_CONFIG_OBJECT:
305 if (strcmp(c[i].key, "PKG_ENV") == 0) {
307 ucl_iterate_object(cur, &it_obj, true))) {
308 evkey = ucl_object_key(tmp);
309 if (evkey != NULL && *evkey != '\0') {
310 setenv(evkey, ucl_object_tostring_forced(tmp), 1);
316 /* Normal string value. */
317 temp_config[i].value = strdup(ucl_object_tostring(cur));
322 /* Repo is enabled, copy over all settings from temp_config. */
323 for (i = 0; i < CONFIG_SIZE; i++) {
326 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
327 if (conftype != CONFFILE_PKG && c[i].main_only == true)
330 case PKG_CONFIG_LIST:
331 c[i].list = temp_config[i].list;
334 c[i].value = temp_config[i].value;
346 * Parse new repo style configs in style:
353 parse_repo_file(ucl_object_t *obj, const char *requested_repo)
355 ucl_object_iter_t it = NULL;
356 const ucl_object_t *cur;
359 while ((cur = ucl_iterate_object(obj, &it, true))) {
360 key = ucl_object_key(cur);
365 if (cur->type != UCL_OBJECT)
368 if (requested_repo != NULL && strcmp(requested_repo, key) != 0)
371 config_parse(cur, CONFFILE_REPO);
377 read_conf_file(const char *confpath, const char *requested_repo,
378 pkg_conf_file_t conftype)
380 struct ucl_parser *p;
381 ucl_object_t *obj = NULL;
383 p = ucl_parser_new(0);
385 if (!ucl_parser_add_file(p, confpath)) {
387 errx(EXIT_FAILURE, "Unable to parse configuration "
388 "file %s: %s", confpath, ucl_parser_get_error(p));
390 /* no configuration present */
394 obj = ucl_parser_get_object(p);
395 if (obj->type != UCL_OBJECT)
396 warnx("Invalid configuration format, ignoring the "
397 "configuration file %s", confpath);
399 if (conftype == CONFFILE_PKG)
400 config_parse(obj, conftype);
401 else if (conftype == CONFFILE_REPO)
402 parse_repo_file(obj, requested_repo);
405 ucl_object_unref(obj);
412 load_repositories(const char *repodir, const char *requested_repo)
418 char path[MAXPATHLEN];
423 if ((d = opendir(repodir)) == NULL)
426 while ((ent = readdir(d))) {
427 /* Trim out 'repos'. */
428 if ((n = strlen(ent->d_name)) <= 5)
430 p = &ent->d_name[n - 5];
431 if (strcmp(p, ".conf") == 0) {
432 snprintf(path, sizeof(path), "%s%s%s",
434 repodir[strlen(repodir) - 1] == '/' ? "" : "/",
436 if (access(path, F_OK) != 0)
438 if (read_conf_file(path, requested_repo,
453 config_init(const char *requested_repo)
457 const char *localbase;
459 char confpath[MAXPATHLEN];
460 struct config_value *cv;
463 for (i = 0; i < CONFIG_SIZE; i++) {
464 val = getenv(c[i].key);
468 case PKG_CONFIG_LIST:
469 /* Split up comma-separated items from env. */
470 c[i].list = malloc(sizeof(*c[i].list));
471 STAILQ_INIT(c[i].list);
472 for (env_list_item = strtok(val, ",");
473 env_list_item != NULL;
474 env_list_item = strtok(NULL, ",")) {
476 malloc(sizeof(struct config_value));
478 strdup(env_list_item);
479 STAILQ_INSERT_TAIL(c[i].list, cv,
490 /* Read LOCALBASE/etc/pkg.conf first. */
491 localbase = getlocalbase();
492 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
494 if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL,
498 /* Then read in all repos from REPOS_DIR list of directories. */
499 if (c[REPOS_DIR].list == NULL) {
500 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
501 STAILQ_INIT(c[REPOS_DIR].list);
502 cv = malloc(sizeof(struct config_value));
503 cv->value = strdup("/etc/pkg");
504 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
505 cv = malloc(sizeof(struct config_value));
506 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
508 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
511 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
512 if (load_repositories(cv->value, requested_repo))
516 if (c[ABI].val == NULL && c[ABI].value == NULL) {
517 if (pkg_get_myabi(abi, BUFSIZ) != 0)
518 errx(EXIT_FAILURE, "Failed to determine the system "
523 subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
529 config_string(pkg_config_key k, const char **val)
531 if (c[k].type != PKG_CONFIG_STRING)
534 if (c[k].value != NULL)
543 config_bool(pkg_config_key k, bool *val)
547 if (c[k].type != PKG_CONFIG_BOOL)
552 if (c[k].value != NULL)
557 if (boolstr_to_bool(value))
564 config_finish(void) {
567 for (i = 0; i < CONFIG_SIZE; i++)