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;
67 bool main_only; /* Only set in pkg.conf. */
70 static struct config_entry c[] = {
74 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
98 [ASSUME_ALWAYS_YES] = {
137 elf_corres_to_string(struct _elf_corres *m, int e)
141 for (i = 0; m[i].string != NULL; i++)
142 if (m[i].elf_nb == e)
143 return (m[i].string);
149 pkg_get_myabi(char *dest, size_t sz)
167 if (elf_version(EV_CURRENT) == EV_NONE) {
168 warnx("ELF library initialization failed: %s",
173 if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
178 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
180 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
184 if (gelf_getehdr(elf, &elfhdr) == NULL) {
186 warn("getehdr() failed: %s.", elf_errmsg(-1));
189 while ((scn = elf_nextscn(elf, scn)) != NULL) {
190 if (gelf_getshdr(scn, &shdr) != &shdr) {
192 warn("getshdr() failed: %s.", elf_errmsg(-1));
196 if (shdr.sh_type == SHT_NOTE)
202 warn("failed to get the note section");
206 data = elf_getdata(scn, NULL);
209 memcpy(¬e, src, sizeof(Elf_Note));
210 src += sizeof(Elf_Note);
211 if (note.n_type == NT_VERSION)
213 src += note.n_namesz + note.n_descsz;
216 src += roundup2(note.n_namesz, 4);
217 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
218 version = be32dec(src);
220 version = le32dec(src);
222 for (i = 0; osname[i] != '\0'; i++)
223 osname[i] = (char)tolower(osname[i]);
225 snprintf(dest, sz, "%s:%d:%s:%s",
226 osname, version / 100000,
227 elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
228 elf_corres_to_string(wordsize_corres,
229 (int)elfhdr.e_ident[EI_CLASS]));
233 switch (elfhdr.e_machine) {
235 snprintf(dest + strlen(dest), sz - strlen(dest),
236 ":%s:%s:%s", elf_corres_to_string(endian_corres,
237 (int)elfhdr.e_ident[EI_DATA]),
238 (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
240 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
245 * this is taken from binutils sources:
247 * mapping is figured out from binutils:
248 * gas/config/tc-mips.c
250 switch (elfhdr.e_flags & EF_MIPS_ABI) {
258 if (elfhdr.e_ident[EI_DATA] ==
261 else if (elfhdr.e_ident[EI_DATA] ==
266 snprintf(dest + strlen(dest), sz - strlen(dest),
267 ":%s:%s", elf_corres_to_string(endian_corres,
268 (int)elfhdr.e_ident[EI_DATA]), abi);
281 subst_packagesite(const char *abi)
284 const char *variable_string;
287 if (c[PACKAGESITE].value != NULL)
288 oldval = c[PACKAGESITE].value;
290 oldval = c[PACKAGESITE].val;
292 if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
295 newval = sbuf_new_auto();
296 sbuf_bcat(newval, oldval, variable_string - oldval);
297 sbuf_cat(newval, abi);
298 sbuf_cat(newval, variable_string + strlen("${ABI}"));
301 free(c[PACKAGESITE].value);
302 c[PACKAGESITE].value = strdup(sbuf_data(newval));
306 boolstr_to_bool(const char *str)
308 if (str != NULL && (strcasecmp(str, "true") == 0 ||
309 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
317 config_parse(ucl_object_t *obj, pkg_conf_file_t conftype)
319 struct sbuf *buf = sbuf_new_auto();
320 ucl_object_t *cur, *seq;
321 ucl_object_iter_t it = NULL, itseq = NULL;
322 struct config_entry *temp_config;
323 struct config_value *cv;
328 /* Temporary config for configs that may be disabled. */
329 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
331 while ((cur = ucl_iterate_object(obj, &it, true))) {
332 key = ucl_object_key(cur);
337 if (conftype == CONFFILE_PKG) {
338 for (j = 0; j < strlen(key); ++j)
339 sbuf_putc(buf, key[j]);
341 } else if (conftype == CONFFILE_REPO) {
342 if (strcasecmp(key, "url") == 0)
343 sbuf_cpy(buf, "PACKAGESITE");
344 else if (strcasecmp(key, "mirror_type") == 0)
345 sbuf_cpy(buf, "MIRROR_TYPE");
346 else if (strcasecmp(key, "signature_type") == 0)
347 sbuf_cpy(buf, "SIGNATURE_TYPE");
348 else if (strcasecmp(key, "fingerprints") == 0)
349 sbuf_cpy(buf, "FINGERPRINTS");
350 else if (strcasecmp(key, "enabled") == 0) {
351 if ((cur->type != UCL_BOOLEAN) ||
352 !ucl_object_toboolean(cur))
359 for (i = 0; i < CONFIG_SIZE; i++) {
360 if (strcmp(sbuf_data(buf), c[i].key) == 0)
364 /* Silently skip unknown keys to be future compatible. */
365 if (i == CONFIG_SIZE)
368 /* env has priority over config file */
372 /* Parse sequence value ["item1", "item2"] */
374 case PKG_CONFIG_LIST:
375 if (cur->type != UCL_ARRAY) {
376 warnx("Skipping invalid array "
377 "value for %s.\n", c[i].key);
380 temp_config[i].list =
381 malloc(sizeof(*temp_config[i].list));
382 STAILQ_INIT(temp_config[i].list);
384 while ((seq = ucl_iterate_object(cur, &itseq, true))) {
385 if (seq->type != UCL_STRING)
387 cv = malloc(sizeof(struct config_value));
389 strdup(ucl_object_tostring(seq));
390 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
394 case PKG_CONFIG_BOOL:
395 temp_config[i].value =
396 strdup(ucl_object_toboolean(cur) ? "yes" : "no");
399 /* Normal string value. */
400 temp_config[i].value = strdup(ucl_object_tostring(cur));
405 /* Repo is enabled, copy over all settings from temp_config. */
406 for (i = 0; i < CONFIG_SIZE; i++) {
409 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
410 if (conftype != CONFFILE_PKG && c[i].main_only == true)
413 case PKG_CONFIG_LIST:
414 c[i].list = temp_config[i].list;
417 c[i].value = temp_config[i].value;
428 * Parse new repo style configs in style:
435 parse_repo_file(ucl_object_t *obj)
437 ucl_object_iter_t it = NULL;
441 while ((cur = ucl_iterate_object(obj, &it, true))) {
442 key = ucl_object_key(cur);
447 if (cur->type != UCL_OBJECT)
450 config_parse(cur, CONFFILE_REPO);
456 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
458 struct ucl_parser *p;
459 ucl_object_t *obj = NULL;
461 p = ucl_parser_new(0);
463 if (!ucl_parser_add_file(p, confpath)) {
465 errx(EXIT_FAILURE, "Unable to parse configuration "
466 "file %s: %s", confpath, ucl_parser_get_error(p));
468 /* no configuration present */
472 obj = ucl_parser_get_object(p);
473 if (obj->type != UCL_OBJECT)
474 warnx("Invalid configuration format, ignoring the "
475 "configuration file %s", confpath);
477 if (conftype == CONFFILE_PKG)
478 config_parse(obj, conftype);
479 else if (conftype == CONFFILE_REPO)
480 parse_repo_file(obj);
483 ucl_object_free(obj);
490 load_repositories(const char *repodir)
496 char path[MAXPATHLEN];
501 if ((d = opendir(repodir)) == NULL)
504 while ((ent = readdir(d))) {
505 /* Trim out 'repos'. */
506 if ((n = strlen(ent->d_name)) <= 5)
508 p = &ent->d_name[n - 5];
509 if (strcmp(p, ".conf") == 0) {
510 snprintf(path, sizeof(path), "%s%s%s",
512 repodir[strlen(repodir) - 1] == '/' ? "" : "/",
514 if (access(path, F_OK) == 0 &&
515 read_conf_file(path, CONFFILE_REPO)) {
533 const char *localbase;
535 char confpath[MAXPATHLEN];
536 struct config_value *cv;
539 for (i = 0; i < CONFIG_SIZE; i++) {
540 val = getenv(c[i].key);
544 case PKG_CONFIG_LIST:
545 /* Split up comma-separated items from env. */
546 c[i].list = malloc(sizeof(*c[i].list));
547 STAILQ_INIT(c[i].list);
548 for (env_list_item = strtok(val, ",");
549 env_list_item != NULL;
550 env_list_item = strtok(NULL, ",")) {
552 malloc(sizeof(struct config_value));
554 strdup(env_list_item);
555 STAILQ_INSERT_TAIL(c[i].list, cv,
566 /* Read LOCALBASE/etc/pkg.conf first. */
567 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
568 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
571 if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
575 /* Then read in all repos from REPOS_DIR list of directories. */
576 if (c[REPOS_DIR].list == NULL) {
577 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
578 STAILQ_INIT(c[REPOS_DIR].list);
579 cv = malloc(sizeof(struct config_value));
580 cv->value = strdup("/etc/pkg");
581 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
582 cv = malloc(sizeof(struct config_value));
583 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
585 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
588 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
589 if (load_repositories(cv->value))
593 if (c[ABI].val == NULL && c[ABI].value == NULL) {
594 if (pkg_get_myabi(abi, BUFSIZ) != 0)
595 errx(EXIT_FAILURE, "Failed to determine the system "
600 subst_packagesite(c[ABI].val);
606 config_string(pkg_config_key k, const char **val)
608 if (c[k].type != PKG_CONFIG_STRING)
611 if (c[k].value != NULL)
620 config_bool(pkg_config_key k, bool *val)
624 if (c[k].type != PKG_CONFIG_BOOL)
629 if (c[k].value != NULL)
634 if (boolstr_to_bool(value))
641 config_finish(void) {
644 for (i = 0; i < CONFIG_SIZE; i++)