config.c revision 247843
177218Sphk/*- 277218Sphk * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org> 377218Sphk * All rights reserved. 477218Sphk * 577218Sphk * Redistribution and use in source and binary forms, with or without 677218Sphk * modification, are permitted provided that the following conditions 777218Sphk * are met: 877218Sphk * 1. Redistributions of source code must retain the above copyright 977218Sphk * notice, this list of conditions and the following disclaimer. 1077218Sphk * 2. Redistributions in binary form must reproduce the above copyright 1177218Sphk * notice, this list of conditions and the following disclaimer in the 1291454Sbrooks * documentation and/or other materials provided with the distribution. 1391454Sbrooks * 1477218Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1577218Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1677218Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1777218Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1877218Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1977218Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2077218Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2177218Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2277218Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2377218Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2477218Sphk * SUCH DAMAGE. 2577218Sphk */ 2677218Sphk 2777218Sphk#include <sys/cdefs.h> 2877218Sphk__FBSDID("$FreeBSD: head/usr.sbin/pkg/config.c 247843 2013-03-05 14:08:36Z bapt $"); 2977218Sphk 3077218Sphk#include <sys/param.h> 3177218Sphk#include <sys/sbuf.h> 3277218Sphk#include <sys/elf_common.h> 3377218Sphk#include <sys/endian.h> 3477218Sphk 3577218Sphk#include <bsdyml.h> 3677218Sphk#include <ctype.h> 3777218Sphk#include <err.h> 3877218Sphk#include <errno.h> 3977218Sphk#include <fcntl.h> 4077218Sphk#include <gelf.h> 4177218Sphk#include <inttypes.h> 4277218Sphk#include <paths.h> 4377218Sphk#include <stdbool.h> 4477218Sphk#include <string.h> 4577218Sphk#include <unistd.h> 4677218Sphk 4777218Sphk#include "elf_tables.h" 4877218Sphk#include "config.h" 4977218Sphk 5077218Sphk#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ 5177218Sphk 5277218Sphkstruct config_entry { 5377218Sphk uint8_t type; 5477218Sphk const char *key; 5577218Sphk const char *val; 5677218Sphk char *value; 5777218Sphk bool envset; 5877218Sphk}; 5977218Sphk 6077218Sphkstatic struct config_entry c[] = { 6177218Sphk [PACKAGESITE] = { 6277218Sphk PKG_CONFIG_STRING, 6377218Sphk "PACKAGESITE", 6477218Sphk "http://pkg.FreeBSD.org/${ABI}/latest", 6577218Sphk NULL, 6677218Sphk false, 6777218Sphk }, 6877218Sphk [ABI] = { 6977218Sphk PKG_CONFIG_STRING, 7077218Sphk "ABI", 7177218Sphk NULL, 7277218Sphk NULL, 7377218Sphk false, 7477218Sphk }, 7577218Sphk [MIRROR_TYPE] = { 7677218Sphk PKG_CONFIG_STRING, 7777218Sphk "MIRROR_TYPE", 78116957Ssam "SRV", 79120178Ssam NULL, 80116957Ssam false, 8177218Sphk }, 8277218Sphk [ASSUME_ALWAYS_YES] = { 8377218Sphk PKG_CONFIG_BOOL, 8477218Sphk "ASSUME_ALWAYS_YES", 8577218Sphk "NO", 8677218Sphk NULL, 8777218Sphk false, 8877218Sphk } 8977218Sphk}; 9077218Sphk 9177218Sphkstatic const char * 9277218Sphkelf_corres_to_string(struct _elf_corres *m, int e) 9377218Sphk{ 9477218Sphk int i; 9577218Sphk 9677218Sphk for (i = 0; m[i].string != NULL; i++) 9777218Sphk if (m[i].elf_nb == e) 9877218Sphk return (m[i].string); 9977218Sphk 10077218Sphk return ("unknown"); 10177218Sphk} 10277218Sphk 10377218Sphkstatic int 10477218Sphkpkg_get_myabi(char *dest, size_t sz) 10577218Sphk{ 10688748Sambrisko Elf *elf; 10788748Sambrisko Elf_Data *data; 10888748Sambrisko Elf_Note note; 10988748Sambrisko Elf_Scn *scn; 11088748Sambrisko char *src, *osname; 11177218Sphk const char *abi; 11277218Sphk GElf_Ehdr elfhdr; 11377218Sphk GElf_Shdr shdr; 11477218Sphk int fd, i, ret; 11577218Sphk uint32_t version; 11677218Sphk 11777218Sphk version = 0; 11877218Sphk ret = -1; 11977218Sphk scn = NULL; 12077218Sphk abi = NULL; 12177218Sphk 12277218Sphk if (elf_version(EV_CURRENT) == EV_NONE) { 12377218Sphk warnx("ELF library initialization failed: %s", 12477218Sphk elf_errmsg(-1)); 12577218Sphk return (-1); 12677218Sphk } 12777218Sphk 12877218Sphk if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) { 12977218Sphk warn("open()"); 13077218Sphk return (-1); 13177218Sphk } 13277218Sphk 13377218Sphk if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 13477218Sphk ret = -1; 135116957Ssam warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 136116957Ssam goto cleanup; 137116957Ssam } 138116957Ssam 13977218Sphk if (gelf_getehdr(elf, &elfhdr) == NULL) { 14077218Sphk ret = -1; 14177218Sphk warn("getehdr() failed: %s.", elf_errmsg(-1)); 14277218Sphk goto cleanup; 14377218Sphk } 14477218Sphk while ((scn = elf_nextscn(elf, scn)) != NULL) { 14577218Sphk if (gelf_getshdr(scn, &shdr) != &shdr) { 14691454Sbrooks ret = -1; 14777218Sphk warn("getshdr() failed: %s.", elf_errmsg(-1)); 14891454Sbrooks goto cleanup; 14977218Sphk } 15091454Sbrooks 15177218Sphk if (shdr.sh_type == SHT_NOTE) 15277218Sphk break; 15377218Sphk } 15477218Sphk 15577218Sphk if (scn == NULL) { 15677218Sphk ret = -1; 15777218Sphk warn("failed to get the note section"); 15877218Sphk goto cleanup; 15977218Sphk } 16077218Sphk 16177218Sphk data = elf_getdata(scn, NULL); 16277218Sphk src = data->d_buf; 16377218Sphk for (;;) { 16491454Sbrooks memcpy(¬e, src, sizeof(Elf_Note)); 16577218Sphk src += sizeof(Elf_Note); 16691454Sbrooks if (note.n_type == NT_VERSION) 16777218Sphk break; 16891454Sbrooks src += note.n_namesz + note.n_descsz; 16977218Sphk } 17091454Sbrooks osname = src; 17177218Sphk src += roundup2(note.n_namesz, 4); 17291454Sbrooks if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 17377218Sphk version = be32dec(src); 17477218Sphk else 17577218Sphk version = le32dec(src); 17677218Sphk 17777218Sphk for (i = 0; osname[i] != '\0'; i++) 17877218Sphk osname[i] = (char)tolower(osname[i]); 17977218Sphk 18077218Sphk snprintf(dest, sz, "%s:%d:%s:%s", 18177218Sphk osname, version / 100000, 18277218Sphk elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 18377218Sphk elf_corres_to_string(wordsize_corres, 18477218Sphk (int)elfhdr.e_ident[EI_CLASS])); 18577218Sphk 18677218Sphk ret = 0; 18777218Sphk 18877218Sphk switch (elfhdr.e_machine) { 18977218Sphk case EM_ARM: 19077218Sphk snprintf(dest + strlen(dest), sz - strlen(dest), 19177218Sphk ":%s:%s:%s", elf_corres_to_string(endian_corres, 19277218Sphk (int)elfhdr.e_ident[EI_DATA]), 19377218Sphk (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 19477218Sphk "eabi" : "oabi", 19577218Sphk (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 19677218Sphk "softfp" : "vfp"); 19777218Sphk break; 19877218Sphk case EM_MIPS: 19977218Sphk /* 20077218Sphk * this is taken from binutils sources: 20177218Sphk * include/elf/mips.h 20277218Sphk * mapping is figured out from binutils: 20391454Sbrooks * gas/config/tc-mips.c 20477218Sphk */ 20591454Sbrooks switch (elfhdr.e_flags & EF_MIPS_ABI) { 20677218Sphk case E_MIPS_ABI_O32: 20791454Sbrooks abi = "o32"; 20877218Sphk break; 20977218Sphk case E_MIPS_ABI_N32: 21077218Sphk abi = "n32"; 21177218Sphk break; 21277218Sphk default: 21377218Sphk if (elfhdr.e_ident[EI_DATA] == 21477218Sphk ELFCLASS32) 21577218Sphk abi = "o32"; 21677218Sphk else if (elfhdr.e_ident[EI_DATA] == 21777218Sphk ELFCLASS64) 21877218Sphk abi = "n64"; 21977218Sphk break; 22077218Sphk } 22177218Sphk snprintf(dest + strlen(dest), sz - strlen(dest), 22277218Sphk ":%s:%s", elf_corres_to_string(endian_corres, 22377218Sphk (int)elfhdr.e_ident[EI_DATA]), abi); 22477218Sphk break; 22577218Sphk } 22677218Sphk 22777218Sphkcleanup: 22877218Sphk if (elf != NULL) 22977218Sphk elf_end(elf); 23077218Sphk 23177218Sphk close(fd); 23277218Sphk return (ret); 233120178Ssam} 23477218Sphk 23591454Sbrooksstatic void 23677218Sphksubst_packagesite(const char *abi) 23777218Sphk{ 23877218Sphk struct sbuf *newval; 23977218Sphk const char *variable_string; 24077218Sphk const char *oldval; 24177218Sphk 24277218Sphk if (c[PACKAGESITE].value != NULL) 24377218Sphk oldval = c[PACKAGESITE].value; 24477218Sphk else 24577218Sphk oldval = c[PACKAGESITE].val; 24677218Sphk 24777218Sphk if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 24877218Sphk return; 24977218Sphk 25077218Sphk newval = sbuf_new_auto(); 25177218Sphk sbuf_bcat(newval, oldval, variable_string - oldval); 25277218Sphk sbuf_cat(newval, abi); 25377218Sphk sbuf_cat(newval, variable_string + strlen("${ABI}")); 25477218Sphk sbuf_finish(newval); 25577218Sphk 25677218Sphk free(c[PACKAGESITE].value); 257120178Ssam c[PACKAGESITE].value = strdup(sbuf_data(newval)); 25877218Sphk} 25977218Sphk 26077218Sphkstatic void 26191454Sbrooksconfig_parse(yaml_document_t *doc, yaml_node_t *node) 26277218Sphk{ 26377218Sphk yaml_node_pair_t *pair; 26477218Sphk yaml_node_t *key, *val; 26591454Sbrooks struct sbuf *buf = sbuf_new_auto(); 26677218Sphk int i; 26777218Sphk size_t j; 26877218Sphk 26977218Sphk pair = node->data.mapping.pairs.start; 27077218Sphk 27177218Sphk while (pair < node->data.mapping.pairs.top) { 27277218Sphk key = yaml_document_get_node(doc, pair->key); 27377218Sphk val = yaml_document_get_node(doc, pair->value); 27477218Sphk 27577218Sphk /* 27677218Sphk * ignoring silently empty keys can be empty lines 27777218Sphk * or user mistakes 27877218Sphk */ 27977218Sphk if (key->data.scalar.length <= 0) { 28077218Sphk ++pair; 28191454Sbrooks continue; 28277218Sphk } 28377218Sphk 28477218Sphk /* 28577218Sphk * silently skip on purpose to allow user to leave 28677218Sphk * empty lines without complaining 28777218Sphk */ 28877218Sphk if (val->type == YAML_NO_NODE || 28995005Simp (val->type == YAML_SCALAR_NODE && 29077218Sphk val->data.scalar.length <= 0)) { 29177218Sphk ++pair; 29277218Sphk continue; 29377218Sphk } 29477218Sphk 29577218Sphk sbuf_clear(buf); 29677218Sphk for (j = 0; j < strlen(key->data.scalar.value); ++j) 29777218Sphk sbuf_putc(buf, toupper(key->data.scalar.value[j])); 29877218Sphk 29977218Sphk sbuf_finish(buf); 30077218Sphk for (i = 0; i < CONFIG_SIZE; i++) { 30177218Sphk if (strcmp(sbuf_data(buf), c[i].key) == 0) 30277218Sphk break; 30377218Sphk } 30477218Sphk 30577218Sphk if (i == CONFIG_SIZE) { 30677218Sphk ++pair; 30777218Sphk continue; 30877218Sphk } 30988748Sambrisko 31088748Sambrisko /* env has priority over config file */ 31188748Sambrisko if (c[i].envset) { 31288748Sambrisko ++pair; 31388748Sambrisko continue; 31488748Sambrisko } 31588748Sambrisko 31688748Sambrisko c[i].value = strdup(val->data.scalar.value); 31788748Sambrisko ++pair; 31888748Sambrisko } 31988748Sambrisko 32088748Sambrisko sbuf_delete(buf); 32177218Sphk} 32277218Sphk 32377218Sphkint 32477218Sphkconfig_init(void) 32577218Sphk{ 32677218Sphk FILE *fp; 32777218Sphk yaml_parser_t parser; 32877218Sphk yaml_document_t doc; 32977218Sphk yaml_node_t *node; 33077218Sphk const char *val; 33177218Sphk int i; 33277218Sphk const char *localbase; 33377218Sphk char confpath[MAXPATHLEN]; 33477218Sphk char abi[BUFSIZ]; 33577218Sphk 33677218Sphk for (i = 0; i < CONFIG_SIZE; i++) { 33777218Sphk val = getenv(c[i].key); 33877218Sphk if (val != NULL) { 33977218Sphk c[i].val = val; 34077218Sphk c[i].envset = true; 34177218Sphk } 34277218Sphk } 34377218Sphk 34477218Sphk localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; 34577218Sphk snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); 34677218Sphk 34777218Sphk if ((fp = fopen(confpath, "r")) == NULL) { 34877218Sphk if (errno != ENOENT) 34977218Sphk err(EXIT_FAILURE, "Unable to open configuration file %s", confpath); 35077218Sphk /* no configuration present */ 35177218Sphk goto finalize; 35277218Sphk } 35377218Sphk 35477218Sphk yaml_parser_initialize(&parser); 35577218Sphk yaml_parser_set_input_file(&parser, fp); 35677218Sphk yaml_parser_load(&parser, &doc); 35777218Sphk 35877218Sphk node = yaml_document_get_root_node(&doc); 35977218Sphk 36077218Sphk if (node != NULL) { 36177218Sphk if (node->type != YAML_MAPPING_NODE) 36277218Sphk warnx("Invalid configuration format, ignoring the configuration file"); 36377218Sphk else 36477218Sphk config_parse(&doc, node); 36577218Sphk } else { 36677218Sphk warnx("Invalid configuration format, ignoring the configuration file"); 36777218Sphk } 36877218Sphk 36977218Sphk yaml_document_delete(&doc); 37077218Sphk yaml_parser_delete(&parser); 37177218Sphk 37277218Sphkfinalize: 37377218Sphk if (c[ABI].val == NULL && c[ABI].value == NULL) { 37477218Sphk if (pkg_get_myabi(abi, BUFSIZ) != 0) 37577218Sphk errx(EXIT_FAILURE, "Failed to determine the system ABI"); 37691454Sbrooks c[ABI].val = abi; 37777218Sphk } 37877218Sphk 37977218Sphk subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); 38077218Sphk 38177218Sphk return (0); 38277218Sphk} 38377218Sphk 38480315Sbrooksint 38580315Sbrooksconfig_string(pkg_config_key k, const char **val) 38677218Sphk{ 38777218Sphk if (c[k].type != PKG_CONFIG_STRING) 38877218Sphk return (-1); 38977218Sphk 39077218Sphk if (c[k].value != NULL) 39177218Sphk *val = c[k].value; 39277218Sphk else 39377218Sphk *val = c[k].val; 39477218Sphk 39577218Sphk return (0); 39677218Sphk} 39777218Sphk 39877218Sphkint 39977218Sphkconfig_bool(pkg_config_key k, bool *val) 40077218Sphk{ 40177218Sphk const char *value; 40277218Sphk 40377218Sphk if (c[k].type != PKG_CONFIG_BOOL) 40477218Sphk return (-1); 40591454Sbrooks 40677218Sphk *val = false; 40777218Sphk 40877218Sphk if (c[k].value != NULL) 40977218Sphk value = c[k].value; 41077218Sphk else 41177218Sphk value = c[k].val; 41277218Sphk 41377218Sphk if (strcasecmp(value, "true") == 0 || 41477218Sphk strcasecmp(value, "yes") == 0 || 41577218Sphk strcasecmp(value, "on") == 0 || 41677218Sphk *value == '1') 41777218Sphk *val = true; 41877218Sphk 41977218Sphk return (0); 42077218Sphk} 42177218Sphk 42277218Sphkvoid 42377218Sphkconfig_finish(void) { 42477218Sphk int i; 42591454Sbrooks 42677218Sphk for (i = 0; i < CONFIG_SIZE; i++) 42777218Sphk free(c[i].value); 42877218Sphk} 42977218Sphk