2 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/elf_common.h>
34 #include <sys/endian.h>
35 #include <sys/types.h>
50 #include "elf_tables.h"
53 #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
57 STAILQ_ENTRY(config_value) next;
65 STAILQ_HEAD(, config_value) *list;
69 static struct config_entry c[] = {
73 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
94 [ASSUME_ALWAYS_YES] = {
129 elf_corres_to_string(struct _elf_corres *m, int e)
133 for (i = 0; m[i].string != NULL; i++)
134 if (m[i].elf_nb == e)
135 return (m[i].string);
141 pkg_get_myabi(char *dest, size_t sz)
159 if (elf_version(EV_CURRENT) == EV_NONE) {
160 warnx("ELF library initialization failed: %s",
165 if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
170 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
172 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
176 if (gelf_getehdr(elf, &elfhdr) == NULL) {
178 warn("getehdr() failed: %s.", elf_errmsg(-1));
181 while ((scn = elf_nextscn(elf, scn)) != NULL) {
182 if (gelf_getshdr(scn, &shdr) != &shdr) {
184 warn("getshdr() failed: %s.", elf_errmsg(-1));
188 if (shdr.sh_type == SHT_NOTE)
194 warn("failed to get the note section");
198 data = elf_getdata(scn, NULL);
201 memcpy(¬e, src, sizeof(Elf_Note));
202 src += sizeof(Elf_Note);
203 if (note.n_type == NT_VERSION)
205 src += note.n_namesz + note.n_descsz;
208 src += roundup2(note.n_namesz, 4);
209 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
210 version = be32dec(src);
212 version = le32dec(src);
214 for (i = 0; osname[i] != '\0'; i++)
215 osname[i] = (char)tolower(osname[i]);
217 snprintf(dest, sz, "%s:%d:%s:%s",
218 osname, version / 100000,
219 elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
220 elf_corres_to_string(wordsize_corres,
221 (int)elfhdr.e_ident[EI_CLASS]));
225 switch (elfhdr.e_machine) {
227 snprintf(dest + strlen(dest), sz - strlen(dest),
228 ":%s:%s:%s", elf_corres_to_string(endian_corres,
229 (int)elfhdr.e_ident[EI_DATA]),
230 (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
232 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
237 * this is taken from binutils sources:
239 * mapping is figured out from binutils:
240 * gas/config/tc-mips.c
242 switch (elfhdr.e_flags & EF_MIPS_ABI) {
250 if (elfhdr.e_ident[EI_DATA] ==
253 else if (elfhdr.e_ident[EI_DATA] ==
258 snprintf(dest + strlen(dest), sz - strlen(dest),
259 ":%s:%s", elf_corres_to_string(endian_corres,
260 (int)elfhdr.e_ident[EI_DATA]), abi);
273 subst_packagesite(const char *abi)
276 const char *variable_string;
279 if (c[PACKAGESITE].value != NULL)
280 oldval = c[PACKAGESITE].value;
282 oldval = c[PACKAGESITE].val;
284 if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
287 newval = sbuf_new_auto();
288 sbuf_bcat(newval, oldval, variable_string - oldval);
289 sbuf_cat(newval, abi);
290 sbuf_cat(newval, variable_string + strlen("${ABI}"));
293 free(c[PACKAGESITE].value);
294 c[PACKAGESITE].value = strdup(sbuf_data(newval));
298 boolstr_to_bool(const char *str)
300 if (str != NULL && (strcasecmp(str, "true") == 0 ||
301 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
309 config_parse(ucl_object_t *obj, pkg_conf_file_t conftype)
311 struct sbuf *buf = sbuf_new_auto();
312 ucl_object_t *cur, *seq;
313 ucl_object_iter_t it = NULL, itseq = NULL;
314 struct config_entry *temp_config;
315 struct config_value *cv;
320 /* Temporary config for configs that may be disabled. */
321 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
323 while ((cur = ucl_iterate_object(obj, &it, true))) {
324 key = ucl_object_key(cur);
329 if (conftype == CONFFILE_PKG) {
330 for (j = 0; j < strlen(key); ++j)
331 sbuf_putc(buf, key[j]);
333 } else if (conftype == CONFFILE_REPO) {
334 if (strcasecmp(key, "url") == 0)
335 sbuf_cpy(buf, "PACKAGESITE");
336 else if (strcasecmp(key, "mirror_type") == 0)
337 sbuf_cpy(buf, "MIRROR_TYPE");
338 else if (strcasecmp(key, "signature_type") == 0)
339 sbuf_cpy(buf, "SIGNATURE_TYPE");
340 else if (strcasecmp(key, "fingerprints") == 0)
341 sbuf_cpy(buf, "FINGERPRINTS");
342 else if (strcasecmp(key, "enabled") == 0) {
343 if ((cur->type != UCL_BOOLEAN) ||
344 !ucl_object_toboolean(cur))
351 for (i = 0; i < CONFIG_SIZE; i++) {
352 if (strcmp(sbuf_data(buf), c[i].key) == 0)
356 /* Silently skip unknown keys to be future compatible. */
357 if (i == CONFIG_SIZE)
360 /* env has priority over config file */
364 /* Parse sequence value ["item1", "item2"] */
366 case PKG_CONFIG_LIST:
367 if (cur->type != UCL_ARRAY) {
368 warnx("Skipping invalid array "
369 "value for %s.\n", c[i].key);
372 temp_config[i].list =
373 malloc(sizeof(*temp_config[i].list));
374 STAILQ_INIT(temp_config[i].list);
376 while ((seq = ucl_iterate_object(cur, &itseq, true))) {
377 if (seq->type != UCL_STRING)
379 cv = malloc(sizeof(struct config_value));
381 strdup(ucl_object_tostring(seq));
382 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
387 /* Normal string value. */
388 temp_config[i].value = strdup(ucl_object_tostring(cur));
393 /* Repo is enabled, copy over all settings from temp_config. */
394 for (i = 0; i < CONFIG_SIZE; i++) {
398 case PKG_CONFIG_LIST:
399 c[i].list = temp_config[i].list;
402 c[i].value = temp_config[i].value;
413 * Parse new repo style configs in style:
420 parse_repo_file(ucl_object_t *obj)
422 ucl_object_iter_t it = NULL;
426 while ((cur = ucl_iterate_object(obj, &it, true))) {
427 key = ucl_object_key(cur);
432 if (cur->type != UCL_OBJECT)
435 config_parse(cur, CONFFILE_REPO);
441 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
443 struct ucl_parser *p;
444 ucl_object_t *obj = NULL;
446 p = ucl_parser_new(0);
448 if (!ucl_parser_add_file(p, confpath)) {
450 errx(EXIT_FAILURE, "Unable to parse configuration "
451 "file %s: %s", confpath, ucl_parser_get_error(p));
453 /* no configuration present */
457 obj = ucl_parser_get_object(p);
458 if (obj->type != UCL_OBJECT)
459 warnx("Invalid configuration format, ignoring the "
460 "configuration file %s", confpath);
462 if (conftype == CONFFILE_PKG)
463 config_parse(obj, conftype);
464 else if (conftype == CONFFILE_REPO)
465 parse_repo_file(obj);
468 ucl_object_free(obj);
475 load_repositories(const char *repodir)
481 char path[MAXPATHLEN];
486 if ((d = opendir(repodir)) == NULL)
489 while ((ent = readdir(d))) {
490 /* Trim out 'repos'. */
491 if ((n = strlen(ent->d_name)) <= 5)
493 p = &ent->d_name[n - 5];
494 if (strcmp(p, ".conf") == 0) {
495 snprintf(path, sizeof(path), "%s%s%s",
497 repodir[strlen(repodir) - 1] == '/' ? "" : "/",
499 if (access(path, F_OK) == 0 &&
500 read_conf_file(path, CONFFILE_REPO)) {
518 const char *localbase;
520 char confpath[MAXPATHLEN];
521 struct config_value *cv;
524 for (i = 0; i < CONFIG_SIZE; i++) {
525 val = getenv(c[i].key);
529 case PKG_CONFIG_LIST:
530 /* Split up comma-separated items from env. */
531 c[i].list = malloc(sizeof(*c[i].list));
532 STAILQ_INIT(c[i].list);
533 for (env_list_item = strtok(val, ",");
534 env_list_item != NULL;
535 env_list_item = strtok(NULL, ",")) {
537 malloc(sizeof(struct config_value));
539 strdup(env_list_item);
540 STAILQ_INSERT_TAIL(c[i].list, cv,
551 /* Read LOCALBASE/etc/pkg.conf first. */
552 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
553 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
556 if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
560 /* Then read in all repos from REPOS_DIR list of directories. */
561 if (c[REPOS_DIR].list == NULL) {
562 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
563 STAILQ_INIT(c[REPOS_DIR].list);
564 cv = malloc(sizeof(struct config_value));
565 cv->value = strdup("/etc/pkg");
566 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
567 cv = malloc(sizeof(struct config_value));
568 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
570 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
573 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
574 if (load_repositories(cv->value))
578 if (c[ABI].val == NULL && c[ABI].value == NULL) {
579 if (pkg_get_myabi(abi, BUFSIZ) != 0)
580 errx(EXIT_FAILURE, "Failed to determine the system "
585 subst_packagesite(c[ABI].val);
591 config_string(pkg_config_key k, const char **val)
593 if (c[k].type != PKG_CONFIG_STRING)
596 if (c[k].value != NULL)
605 config_bool(pkg_config_key k, bool *val)
609 if (c[k].type != PKG_CONFIG_BOOL)
614 if (c[k].value != NULL)
619 if (boolstr_to_bool(value))
626 config_finish(void) {
629 for (i = 0; i < CONFIG_SIZE; i++)