config.c revision 247841
1247841Sbapt/*- 2247841Sbapt * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org> 3247841Sbapt * All rights reserved. 4247841Sbapt * 5247841Sbapt * Redistribution and use in source and binary forms, with or without 6247841Sbapt * modification, are permitted provided that the following conditions 7247841Sbapt * are met: 8247841Sbapt * 1. Redistributions of source code must retain the above copyright 9247841Sbapt * notice, this list of conditions and the following disclaimer. 10247841Sbapt * 2. Redistributions in binary form must reproduce the above copyright 11247841Sbapt * notice, this list of conditions and the following disclaimer in the 12247841Sbapt * documentation and/or other materials provided with the distribution. 13247841Sbapt * 14247841Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15247841Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16247841Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17247841Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18247841Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19247841Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20247841Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21247841Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22247841Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23247841Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24247841Sbapt * SUCH DAMAGE. 25247841Sbapt */ 26247841Sbapt 27247841Sbapt#include <sys/cdefs.h> 28247841Sbapt__FBSDID("$FreeBSD: head/usr.sbin/pkg/config.c 247841 2013-03-05 13:31:06Z bapt $"); 29247841Sbapt 30247841Sbapt#include <sys/param.h> 31247841Sbapt#include <sys/sbuf.h> 32247841Sbapt#include <sys/elf_common.h> 33247841Sbapt#include <sys/endian.h> 34247841Sbapt 35247841Sbapt#include <bsdyml.h> 36247841Sbapt#include <ctype.h> 37247841Sbapt#include <err.h> 38247841Sbapt#include <errno.h> 39247841Sbapt#include <fcntl.h> 40247841Sbapt#include <gelf.h> 41247841Sbapt#include <inttypes.h> 42247841Sbapt#include <paths.h> 43247841Sbapt#include <stdbool.h> 44247841Sbapt#include <string.h> 45247841Sbapt#include <unistd.h> 46247841Sbapt 47247841Sbapt#include "elf_tables.h" 48247841Sbapt#include "config.h" 49247841Sbapt 50247841Sbapt#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ 51247841Sbapt 52247841Sbaptstruct config_entry { 53247841Sbapt uint8_t type; 54247841Sbapt const char *key; 55247841Sbapt const char *val; 56247841Sbapt char *value; 57247841Sbapt bool envset; 58247841Sbapt}; 59247841Sbapt 60247841Sbaptstatic struct config_entry c[] = { 61247841Sbapt [PACKAGESITE] = { 62247841Sbapt PKG_CONFIG_STRING, 63247841Sbapt "PACKAGESITE", 64247841Sbapt "http://pkg.FreeBSD.org/${ABI}/latest", 65247841Sbapt NULL, 66247841Sbapt false, 67247841Sbapt }, 68247841Sbapt [ABI] = { 69247841Sbapt PKG_CONFIG_STRING, 70247841Sbapt "ABI", 71247841Sbapt NULL, 72247841Sbapt NULL, 73247841Sbapt false, 74247841Sbapt }, 75247841Sbapt [MIRROR_TYPE] = { 76247841Sbapt PKG_CONFIG_STRING, 77247841Sbapt "MIRROR_TYPE", 78247841Sbapt "SRV", 79247841Sbapt NULL, 80247841Sbapt false, 81247841Sbapt }, 82247841Sbapt [ASSUME_ALWAYS_YES] = { 83247841Sbapt PKG_CONFIG_BOOL, 84247841Sbapt "ASSUME_ALWAYS_YES", 85247841Sbapt "NO", 86247841Sbapt NULL, 87247841Sbapt false, 88247841Sbapt } 89247841Sbapt}; 90247841Sbapt 91247841Sbaptstatic const char * 92247841Sbaptelf_corres_to_string(struct _elf_corres *m, int e) 93247841Sbapt{ 94247841Sbapt int i; 95247841Sbapt 96247841Sbapt for (i = 0; m[i].string != NULL; i++) 97247841Sbapt if (m[i].elf_nb == e) 98247841Sbapt return (m[i].string); 99247841Sbapt 100247841Sbapt return ("unknown"); 101247841Sbapt} 102247841Sbapt 103247841Sbaptstatic int 104247841Sbaptpkg_get_myabi(char *dest, size_t sz) 105247841Sbapt{ 106247841Sbapt Elf *elf; 107247841Sbapt Elf_Data *data; 108247841Sbapt Elf_Note note; 109247841Sbapt Elf_Scn *scn; 110247841Sbapt char *src, *osname; 111247841Sbapt const char *abi; 112247841Sbapt GElf_Ehdr elfhdr; 113247841Sbapt GElf_Shdr shdr; 114247841Sbapt int fd, i, ret; 115247841Sbapt uint32_t version; 116247841Sbapt 117247841Sbapt version = 0; 118247841Sbapt ret = -1; 119247841Sbapt scn = NULL; 120247841Sbapt abi = NULL; 121247841Sbapt 122247841Sbapt if (elf_version(EV_CURRENT) == EV_NONE) { 123247841Sbapt warnx("ELF library initialization failed: %s", 124247841Sbapt elf_errmsg(-1)); 125247841Sbapt return (-1); 126247841Sbapt } 127247841Sbapt 128247841Sbapt if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) { 129247841Sbapt warn("open()"); 130247841Sbapt return (-1); 131247841Sbapt } 132247841Sbapt 133247841Sbapt if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 134247841Sbapt ret = -1; 135247841Sbapt warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 136247841Sbapt goto cleanup; 137247841Sbapt } 138247841Sbapt 139247841Sbapt if (gelf_getehdr(elf, &elfhdr) == NULL) { 140247841Sbapt ret = -1; 141247841Sbapt warn("getehdr() failed: %s.", elf_errmsg(-1)); 142247841Sbapt goto cleanup; 143247841Sbapt } 144247841Sbapt while ((scn = elf_nextscn(elf, scn)) != NULL) { 145247841Sbapt if (gelf_getshdr(scn, &shdr) != &shdr) { 146247841Sbapt ret = -1; 147247841Sbapt warn("getshdr() failed: %s.", elf_errmsg(-1)); 148247841Sbapt goto cleanup; 149247841Sbapt } 150247841Sbapt 151247841Sbapt if (shdr.sh_type == SHT_NOTE) 152247841Sbapt break; 153247841Sbapt } 154247841Sbapt 155247841Sbapt if (scn == NULL) { 156247841Sbapt ret = -1; 157247841Sbapt warn("failed to get the note section"); 158247841Sbapt goto cleanup; 159247841Sbapt } 160247841Sbapt 161247841Sbapt data = elf_getdata(scn, NULL); 162247841Sbapt src = data->d_buf; 163247841Sbapt for (;;) { 164247841Sbapt memcpy(¬e, src, sizeof(Elf_Note)); 165247841Sbapt src += sizeof(Elf_Note); 166247841Sbapt if (note.n_type == NT_VERSION) 167247841Sbapt break; 168247841Sbapt src += note.n_namesz + note.n_descsz; 169247841Sbapt } 170247841Sbapt osname = src; 171247841Sbapt src += roundup2(note.n_namesz, 4); 172247841Sbapt if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 173247841Sbapt version = be32dec(src); 174247841Sbapt else 175247841Sbapt version = le32dec(src); 176247841Sbapt 177247841Sbapt for (i = 0; osname[i] != '\0'; i++) 178247841Sbapt osname[i] = (char)tolower(osname[i]); 179247841Sbapt 180247841Sbapt snprintf(dest, sz, "%s:%d:%s:%s", 181247841Sbapt osname, version / 100000, 182247841Sbapt elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 183247841Sbapt elf_corres_to_string(wordsize_corres, 184247841Sbapt (int)elfhdr.e_ident[EI_CLASS])); 185247841Sbapt 186247841Sbapt ret = 0; 187247841Sbapt 188247841Sbapt switch (elfhdr.e_machine) { 189247841Sbapt case EM_ARM: 190247841Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 191247841Sbapt ":%s:%s:%s", elf_corres_to_string(endian_corres, 192247841Sbapt (int)elfhdr.e_ident[EI_DATA]), 193247841Sbapt (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 194247841Sbapt "eabi" : "oabi", 195247841Sbapt (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 196247841Sbapt "softfp" : "vfp"); 197247841Sbapt break; 198247841Sbapt case EM_MIPS: 199247841Sbapt /* 200247841Sbapt * this is taken from binutils sources: 201247841Sbapt * include/elf/mips.h 202247841Sbapt * mapping is figured out from binutils: 203247841Sbapt * gas/config/tc-mips.c 204247841Sbapt */ 205247841Sbapt switch (elfhdr.e_flags & EF_MIPS_ABI) { 206247841Sbapt case E_MIPS_ABI_O32: 207247841Sbapt abi = "o32"; 208247841Sbapt break; 209247841Sbapt case E_MIPS_ABI_N32: 210247841Sbapt abi = "n32"; 211247841Sbapt break; 212247841Sbapt default: 213247841Sbapt if (elfhdr.e_ident[EI_DATA] == 214247841Sbapt ELFCLASS32) 215247841Sbapt abi = "o32"; 216247841Sbapt else if (elfhdr.e_ident[EI_DATA] == 217247841Sbapt ELFCLASS64) 218247841Sbapt abi = "n64"; 219247841Sbapt break; 220247841Sbapt } 221247841Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 222247841Sbapt ":%s:%s", elf_corres_to_string(endian_corres, 223247841Sbapt (int)elfhdr.e_ident[EI_DATA]), abi); 224247841Sbapt break; 225247841Sbapt } 226247841Sbapt 227247841Sbaptcleanup: 228247841Sbapt if (elf != NULL) 229247841Sbapt elf_end(elf); 230247841Sbapt 231247841Sbapt close(fd); 232247841Sbapt return (ret); 233247841Sbapt} 234247841Sbapt 235247841Sbaptstatic void 236247841Sbaptsubst_packagesite(const char *abi) 237247841Sbapt{ 238247841Sbapt struct sbuf *newval; 239247841Sbapt const char *variable_string; 240247841Sbapt const char *oldval; 241247841Sbapt 242247841Sbapt if (c[PACKAGESITE].value != NULL) 243247841Sbapt oldval = c[PACKAGESITE].value; 244247841Sbapt else 245247841Sbapt oldval = c[PACKAGESITE].val; 246247841Sbapt 247247841Sbapt if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 248247841Sbapt return; 249247841Sbapt 250247841Sbapt newval = sbuf_new_auto(); 251247841Sbapt sbuf_bcat(newval, oldval, variable_string - oldval); 252247841Sbapt sbuf_cat(newval, abi); 253247841Sbapt sbuf_cat(newval, variable_string + strlen("${ABI}")); 254247841Sbapt sbuf_finish(newval); 255247841Sbapt 256247841Sbapt free(c[PACKAGESITE].value); 257247841Sbapt c[PACKAGESITE].value = strdup(sbuf_data(newval)); 258247841Sbapt} 259247841Sbapt 260247841Sbaptstatic void 261247841Sbaptconfig_parse(yaml_document_t *doc, yaml_node_t *node) 262247841Sbapt{ 263247841Sbapt yaml_node_pair_t *pair; 264247841Sbapt yaml_node_t *key, *val; 265247841Sbapt struct sbuf *buf = sbuf_new_auto(); 266247841Sbapt int i; 267247841Sbapt size_t j; 268247841Sbapt 269247841Sbapt pair = node->data.mapping.pairs.start; 270247841Sbapt 271247841Sbapt while (pair < node->data.mapping.pairs.top) { 272247841Sbapt key = yaml_document_get_node(doc, pair->key); 273247841Sbapt val = yaml_document_get_node(doc, pair->value); 274247841Sbapt 275247841Sbapt /* 276247841Sbapt * ignoring silently empty keys can be empty lines 277247841Sbapt * or user mistakes 278247841Sbapt */ 279247841Sbapt if (key->data.scalar.length <= 0) { 280247841Sbapt ++pair; 281247841Sbapt continue; 282247841Sbapt } 283247841Sbapt 284247841Sbapt /* 285247841Sbapt * silently skip on purpose to allow user to leave 286247841Sbapt * empty lines without complaining 287247841Sbapt */ 288247841Sbapt if (val->type == YAML_NO_NODE || 289247841Sbapt (val->type == YAML_SCALAR_NODE && 290247841Sbapt val->data.scalar.length <= 0)) { 291247841Sbapt ++pair; 292247841Sbapt continue; 293247841Sbapt } 294247841Sbapt 295247841Sbapt sbuf_clear(buf); 296247841Sbapt for (j = 0; j < strlen(key->data.scalar.value); ++j) 297247841Sbapt sbuf_putc(buf, toupper(key->data.scalar.value[j])); 298247841Sbapt 299247841Sbapt sbuf_finish(buf); 300247841Sbapt for (i = 0; i < CONFIG_SIZE; i++) { 301247841Sbapt if (strcmp(sbuf_data(buf), c[i].key) == 0) 302247841Sbapt break; 303247841Sbapt } 304247841Sbapt 305247841Sbapt if (i == CONFIG_SIZE) { 306247841Sbapt ++pair; 307247841Sbapt continue; 308247841Sbapt } 309247841Sbapt 310247841Sbapt /* env has priority over config file */ 311247841Sbapt if (c[i].envset) { 312247841Sbapt ++pair; 313247841Sbapt continue; 314247841Sbapt } 315247841Sbapt 316247841Sbapt c[i].value = strdup(val->data.scalar.value); 317247841Sbapt ++pair; 318247841Sbapt } 319247841Sbapt 320247841Sbapt sbuf_delete(buf); 321247841Sbapt} 322247841Sbapt 323247841Sbaptint 324247841Sbaptconfig_init(void) 325247841Sbapt{ 326247841Sbapt FILE *fp; 327247841Sbapt yaml_parser_t parser; 328247841Sbapt yaml_document_t doc; 329247841Sbapt yaml_node_t *node; 330247841Sbapt const char *val; 331247841Sbapt int i; 332247841Sbapt const char *localbase; 333247841Sbapt char confpath[MAXPATHLEN]; 334247841Sbapt char abi[BUFSIZ]; 335247841Sbapt 336247841Sbapt for (i = 0; i < CONFIG_SIZE; i++) { 337247841Sbapt val = getenv(c[i].key); 338247841Sbapt if (val != NULL) { 339247841Sbapt c[i].val = val; 340247841Sbapt c[i].envset = true; 341247841Sbapt } 342247841Sbapt } 343247841Sbapt 344247841Sbapt localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; 345247841Sbapt snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); 346247841Sbapt 347247841Sbapt if ((fp = fopen(confpath, "r")) == NULL) { 348247841Sbapt if (errno != ENOENT) 349247841Sbapt err(EXIT_FAILURE, "Unable to open configuration file %s", confpath); 350247841Sbapt /* no configuration present */ 351247841Sbapt goto finalize; 352247841Sbapt } 353247841Sbapt 354247841Sbapt yaml_parser_initialize(&parser); 355247841Sbapt yaml_parser_set_input_file(&parser, fp); 356247841Sbapt yaml_parser_load(&parser, &doc); 357247841Sbapt 358247841Sbapt node = yaml_document_get_root_node(&doc); 359247841Sbapt 360247841Sbapt if (node != NULL) { 361247841Sbapt if (node->type != YAML_MAPPING_NODE) 362247841Sbapt warnx("Invalid configuration format, ignoring the configuration file"); 363247841Sbapt else 364247841Sbapt config_parse(&doc, node); 365247841Sbapt } else { 366247841Sbapt warnx("Invalid configuration format, ignoring the configuration file"); 367247841Sbapt } 368247841Sbapt 369247841Sbapt yaml_document_delete(&doc); 370247841Sbapt yaml_parser_delete(&parser); 371247841Sbapt 372247841Sbaptfinalize: 373247841Sbapt if (c[ABI].val == NULL && c[ABI].value == NULL) { 374247841Sbapt if (pkg_get_myabi(abi, BUFSIZ) != 0) 375247841Sbapt errx(EXIT_FAILURE, "Failed to determine the system ABI"); 376247841Sbapt c[ABI].val = abi; 377247841Sbapt } 378247841Sbapt 379247841Sbapt subst_packagesite(c[ABI].val); 380247841Sbapt 381247841Sbapt return (0); 382247841Sbapt} 383247841Sbapt 384247841Sbaptint 385247841Sbaptconfig_string(pkg_config_key k, const char **val) 386247841Sbapt{ 387247841Sbapt if (c[k].type != PKG_CONFIG_STRING) 388247841Sbapt return (-1); 389247841Sbapt 390247841Sbapt if (c[k].value != NULL) 391247841Sbapt *val = c[k].value; 392247841Sbapt else 393247841Sbapt *val = c[k].val; 394247841Sbapt 395247841Sbapt return (0); 396247841Sbapt} 397247841Sbapt 398247841Sbaptint 399247841Sbaptconfig_bool(pkg_config_key k, bool *val) 400247841Sbapt{ 401247841Sbapt const char *value; 402247841Sbapt 403247841Sbapt if (c[k].type != PKG_CONFIG_BOOL) 404247841Sbapt return (-1); 405247841Sbapt 406247841Sbapt *val = false; 407247841Sbapt 408247841Sbapt if (c[k].value != NULL) 409247841Sbapt value = c[k].value; 410247841Sbapt else 411247841Sbapt value = c[k].val; 412247841Sbapt 413247841Sbapt if (strcasecmp(value, "true") == 0 || 414247841Sbapt strcasecmp(value, "yes") == 0 || 415247841Sbapt strcasecmp(value, "on") == 0 || 416247841Sbapt *value == '1') 417247841Sbapt *val = true; 418247841Sbapt 419247841Sbapt return (0); 420247841Sbapt} 421247841Sbapt 422247841Sbaptvoid 423247841Sbaptconfig_finish(void) { 424247841Sbapt int i; 425247841Sbapt 426247841Sbapt for (i = 0; i < CONFIG_SIZE; i++) 427247841Sbapt free(c[i].value); 428247841Sbapt} 429