1/*- 2 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 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. 14 * 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 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include <sys/param.h> 32#include <sys/sbuf.h> 33#include <sys/elf_common.h> 34#include <sys/endian.h> 35#include <sys/types.h> 36 37#include <dirent.h> 38#include <ucl.h> 39#include <ctype.h> 40#include <err.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <gelf.h> 44#include <inttypes.h> 45#include <paths.h> 46#include <stdbool.h> 47#include <string.h> 48#include <unistd.h> 49 50#include "elf_tables.h" 51#include "config.h" 52 53#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ 54 55struct config_value { 56 char *value; 57 STAILQ_ENTRY(config_value) next; 58}; 59 60struct config_entry { 61 uint8_t type; 62 const char *key; 63 const char *val; 64 char *value; 65 STAILQ_HEAD(, config_value) *list; 66 bool envset; 67 bool main_only; /* Only set in pkg.conf. */ 68}; 69 70static struct config_entry c[] = { 71 [PACKAGESITE] = { 72 PKG_CONFIG_STRING, 73 "PACKAGESITE", 74 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest", 75 NULL, 76 NULL, 77 false, 78 false, 79 }, 80 [ABI] = { 81 PKG_CONFIG_STRING, 82 "ABI", 83 NULL, 84 NULL, 85 NULL, 86 false, 87 true, 88 }, 89 [MIRROR_TYPE] = { 90 PKG_CONFIG_STRING, 91 "MIRROR_TYPE", 92 "SRV", 93 NULL, 94 NULL, 95 false, 96 false, 97 }, 98 [ASSUME_ALWAYS_YES] = { 99 PKG_CONFIG_BOOL, 100 "ASSUME_ALWAYS_YES", 101 "NO", 102 NULL, 103 NULL, 104 false, 105 true, 106 }, 107 [SIGNATURE_TYPE] = { 108 PKG_CONFIG_STRING, 109 "SIGNATURE_TYPE", 110 NULL, 111 NULL, 112 NULL, 113 false, 114 false, 115 }, 116 [FINGERPRINTS] = { 117 PKG_CONFIG_STRING, 118 "FINGERPRINTS", 119 NULL, 120 NULL, 121 NULL, 122 false, 123 false, 124 }, 125 [REPOS_DIR] = { 126 PKG_CONFIG_LIST, 127 "REPOS_DIR", 128 NULL, 129 NULL, 130 NULL, 131 false, 132 true, 133 }, 134 [PUBKEY] = { 135 PKG_CONFIG_STRING, 136 "PUBKEY", 137 NULL, 138 NULL, 139 NULL, 140 false, 141 false 142 } 143}; 144 145static const char * 146elf_corres_to_string(struct _elf_corres *m, int e) 147{ 148 int i; 149 150 for (i = 0; m[i].string != NULL; i++) 151 if (m[i].elf_nb == e) 152 return (m[i].string); 153 154 return ("unknown"); 155} 156 157static int 158pkg_get_myabi(char *dest, size_t sz) 159{ 160 Elf *elf; 161 Elf_Data *data; 162 Elf_Note note; 163 Elf_Scn *scn; 164 char *src, *osname; 165 const char *abi; 166 GElf_Ehdr elfhdr; 167 GElf_Shdr shdr; 168 int fd, i, ret; 169 uint32_t version; 170 171 version = 0; 172 ret = -1; 173 scn = NULL; 174 abi = NULL; 175 176 if (elf_version(EV_CURRENT) == EV_NONE) { 177 warnx("ELF library initialization failed: %s", 178 elf_errmsg(-1)); 179 return (-1); 180 } 181 182 if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) { 183 warn("open()"); 184 return (-1); 185 } 186 187 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 188 ret = -1; 189 warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 190 goto cleanup; 191 } 192 193 if (gelf_getehdr(elf, &elfhdr) == NULL) { 194 ret = -1; 195 warn("getehdr() failed: %s.", elf_errmsg(-1)); 196 goto cleanup; 197 } 198 while ((scn = elf_nextscn(elf, scn)) != NULL) { 199 if (gelf_getshdr(scn, &shdr) != &shdr) { 200 ret = -1; 201 warn("getshdr() failed: %s.", elf_errmsg(-1)); 202 goto cleanup; 203 } 204 205 if (shdr.sh_type == SHT_NOTE) 206 break; 207 } 208 209 if (scn == NULL) { 210 ret = -1; 211 warn("failed to get the note section"); 212 goto cleanup; 213 } 214 215 data = elf_getdata(scn, NULL); 216 src = data->d_buf; 217 for (;;) { 218 memcpy(¬e, src, sizeof(Elf_Note)); 219 src += sizeof(Elf_Note); 220 if (note.n_type == NT_VERSION) 221 break; 222 src += note.n_namesz + note.n_descsz; 223 } 224 osname = src; 225 src += roundup2(note.n_namesz, 4); 226 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 227 version = be32dec(src); 228 else 229 version = le32dec(src); 230 231 for (i = 0; osname[i] != '\0'; i++) 232 osname[i] = (char)tolower(osname[i]); 233 234 snprintf(dest, sz, "%s:%d:%s:%s", 235 osname, version / 100000, 236 elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 237 elf_corres_to_string(wordsize_corres, 238 (int)elfhdr.e_ident[EI_CLASS])); 239 240 ret = 0; 241 242 switch (elfhdr.e_machine) { 243 case EM_ARM: 244 snprintf(dest + strlen(dest), sz - strlen(dest), 245 ":%s:%s:%s", elf_corres_to_string(endian_corres, 246 (int)elfhdr.e_ident[EI_DATA]), 247 (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 248 "eabi" : "oabi", 249 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 250 "softfp" : "vfp"); 251 break; 252 case EM_MIPS: 253 /* 254 * this is taken from binutils sources: 255 * include/elf/mips.h 256 * mapping is figured out from binutils: 257 * gas/config/tc-mips.c 258 */ 259 switch (elfhdr.e_flags & EF_MIPS_ABI) { 260 case E_MIPS_ABI_O32: 261 abi = "o32"; 262 break; 263 case E_MIPS_ABI_N32: 264 abi = "n32"; 265 break; 266 default: 267 if (elfhdr.e_ident[EI_DATA] == 268 ELFCLASS32) 269 abi = "o32"; 270 else if (elfhdr.e_ident[EI_DATA] == 271 ELFCLASS64) 272 abi = "n64"; 273 break; 274 } 275 snprintf(dest + strlen(dest), sz - strlen(dest), 276 ":%s:%s", elf_corres_to_string(endian_corres, 277 (int)elfhdr.e_ident[EI_DATA]), abi); 278 break; 279 } 280 281cleanup: 282 if (elf != NULL) 283 elf_end(elf); 284 285 close(fd); 286 return (ret); 287} 288 289static void 290subst_packagesite(const char *abi) 291{ 292 struct sbuf *newval; 293 const char *variable_string; 294 const char *oldval; 295 296 if (c[PACKAGESITE].value != NULL) 297 oldval = c[PACKAGESITE].value; 298 else 299 oldval = c[PACKAGESITE].val; 300 301 if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 302 return; 303 304 newval = sbuf_new_auto(); 305 sbuf_bcat(newval, oldval, variable_string - oldval); 306 sbuf_cat(newval, abi); 307 sbuf_cat(newval, variable_string + strlen("${ABI}")); 308 sbuf_finish(newval); 309 310 free(c[PACKAGESITE].value); 311 c[PACKAGESITE].value = strdup(sbuf_data(newval)); 312} 313 314static int 315boolstr_to_bool(const char *str) 316{ 317 if (str != NULL && (strcasecmp(str, "true") == 0 || 318 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 || 319 str[0] == '1')) 320 return (true); 321 322 return (false); 323} 324 325static void 326config_parse(ucl_object_t *obj, pkg_conf_file_t conftype) 327{ 328 struct sbuf *buf = sbuf_new_auto(); 329 ucl_object_t *cur, *seq; 330 ucl_object_iter_t it = NULL, itseq = NULL; 331 struct config_entry *temp_config; 332 struct config_value *cv; 333 const char *key; 334 int i; 335 size_t j; 336 337 /* Temporary config for configs that may be disabled. */ 338 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry)); 339 340 while ((cur = ucl_iterate_object(obj, &it, true))) { 341 key = ucl_object_key(cur); 342 if (key == NULL) 343 continue; 344 sbuf_clear(buf); 345 346 if (conftype == CONFFILE_PKG) { 347 for (j = 0; j < strlen(key); ++j) 348 sbuf_putc(buf, key[j]); 349 sbuf_finish(buf); 350 } else if (conftype == CONFFILE_REPO) { 351 if (strcasecmp(key, "url") == 0) 352 sbuf_cpy(buf, "PACKAGESITE"); 353 else if (strcasecmp(key, "mirror_type") == 0) 354 sbuf_cpy(buf, "MIRROR_TYPE"); 355 else if (strcasecmp(key, "signature_type") == 0) 356 sbuf_cpy(buf, "SIGNATURE_TYPE"); 357 else if (strcasecmp(key, "fingerprints") == 0) 358 sbuf_cpy(buf, "FINGERPRINTS"); 359 else if (strcasecmp(key, "pubkey") == 0) 360 sbuf_cpy(buf, "PUBKEY"); 361 else if (strcasecmp(key, "enabled") == 0) { 362 if ((cur->type != UCL_BOOLEAN) || 363 !ucl_object_toboolean(cur)) 364 goto cleanup; 365 } else 366 continue; 367 sbuf_finish(buf); 368 } 369 370 for (i = 0; i < CONFIG_SIZE; i++) { 371 if (strcmp(sbuf_data(buf), c[i].key) == 0) 372 break; 373 } 374 375 /* Silently skip unknown keys to be future compatible. */ 376 if (i == CONFIG_SIZE) 377 continue; 378 379 /* env has priority over config file */ 380 if (c[i].envset) 381 continue; 382 383 /* Parse sequence value ["item1", "item2"] */ 384 switch (c[i].type) { 385 case PKG_CONFIG_LIST: 386 if (cur->type != UCL_ARRAY) { 387 warnx("Skipping invalid array " 388 "value for %s.\n", c[i].key); 389 continue; 390 } 391 temp_config[i].list = 392 malloc(sizeof(*temp_config[i].list)); 393 STAILQ_INIT(temp_config[i].list); 394 395 while ((seq = ucl_iterate_object(cur, &itseq, true))) { 396 if (seq->type != UCL_STRING) 397 continue; 398 cv = malloc(sizeof(struct config_value)); 399 cv->value = 400 strdup(ucl_object_tostring(seq)); 401 STAILQ_INSERT_TAIL(temp_config[i].list, cv, 402 next); 403 } 404 break; 405 case PKG_CONFIG_BOOL: 406 temp_config[i].value = 407 strdup(ucl_object_toboolean(cur) ? "yes" : "no"); 408 break; 409 default: 410 /* Normal string value. */ 411 temp_config[i].value = strdup(ucl_object_tostring(cur)); 412 break; 413 } 414 } 415 416 /* Repo is enabled, copy over all settings from temp_config. */ 417 for (i = 0; i < CONFIG_SIZE; i++) { 418 if (c[i].envset) 419 continue; 420 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */ 421 if (conftype != CONFFILE_PKG && c[i].main_only == true) 422 continue; 423 switch (c[i].type) { 424 case PKG_CONFIG_LIST: 425 c[i].list = temp_config[i].list; 426 break; 427 default: 428 c[i].value = temp_config[i].value; 429 break; 430 } 431 } 432 433cleanup: 434 free(temp_config); 435 sbuf_delete(buf); 436} 437 438/*- 439 * Parse new repo style configs in style: 440 * Name: 441 * URL: 442 * MIRROR_TYPE: 443 * etc... 444 */ 445static void 446parse_repo_file(ucl_object_t *obj) 447{ 448 ucl_object_iter_t it = NULL; 449 ucl_object_t *cur; 450 const char *key; 451 452 while ((cur = ucl_iterate_object(obj, &it, true))) { 453 key = ucl_object_key(cur); 454 455 if (key == NULL) 456 continue; 457 458 if (cur->type != UCL_OBJECT) 459 continue; 460 461 config_parse(cur, CONFFILE_REPO); 462 } 463} 464 465 466static int 467read_conf_file(const char *confpath, pkg_conf_file_t conftype) 468{ 469 struct ucl_parser *p; 470 ucl_object_t *obj = NULL; 471 472 p = ucl_parser_new(0); 473 474 if (!ucl_parser_add_file(p, confpath)) { 475 if (errno != ENOENT) 476 errx(EXIT_FAILURE, "Unable to parse configuration " 477 "file %s: %s", confpath, ucl_parser_get_error(p)); 478 ucl_parser_free(p); 479 /* no configuration present */ 480 return (1); 481 } 482 483 obj = ucl_parser_get_object(p); 484 if (obj->type != UCL_OBJECT) 485 warnx("Invalid configuration format, ignoring the " 486 "configuration file %s", confpath); 487 else { 488 if (conftype == CONFFILE_PKG) 489 config_parse(obj, conftype); 490 else if (conftype == CONFFILE_REPO) 491 parse_repo_file(obj); 492 } 493 494 ucl_object_free(obj); 495 ucl_parser_free(p); 496 497 return (0); 498} 499 500static int 501load_repositories(const char *repodir) 502{ 503 struct dirent *ent; 504 DIR *d; 505 char *p; 506 size_t n; 507 char path[MAXPATHLEN]; 508 int ret; 509 510 ret = 0; 511 512 if ((d = opendir(repodir)) == NULL) 513 return (1); 514 515 while ((ent = readdir(d))) { 516 /* Trim out 'repos'. */ 517 if ((n = strlen(ent->d_name)) <= 5) 518 continue; 519 p = &ent->d_name[n - 5]; 520 if (strcmp(p, ".conf") == 0) { 521 snprintf(path, sizeof(path), "%s%s%s", 522 repodir, 523 repodir[strlen(repodir) - 1] == '/' ? "" : "/", 524 ent->d_name); 525 if (access(path, F_OK) == 0 && 526 read_conf_file(path, CONFFILE_REPO)) { 527 ret = 1; 528 goto cleanup; 529 } 530 } 531 } 532 533cleanup: 534 closedir(d); 535 536 return (ret); 537} 538 539int 540config_init(void) 541{ 542 char *val; 543 int i; 544 const char *localbase; 545 char *env_list_item; 546 char confpath[MAXPATHLEN]; 547 struct config_value *cv; 548 char abi[BUFSIZ]; 549 550 for (i = 0; i < CONFIG_SIZE; i++) { 551 val = getenv(c[i].key); 552 if (val != NULL) { 553 c[i].envset = true; 554 switch (c[i].type) { 555 case PKG_CONFIG_LIST: 556 /* Split up comma-separated items from env. */ 557 c[i].list = malloc(sizeof(*c[i].list)); 558 STAILQ_INIT(c[i].list); 559 for (env_list_item = strtok(val, ","); 560 env_list_item != NULL; 561 env_list_item = strtok(NULL, ",")) { 562 cv = 563 malloc(sizeof(struct config_value)); 564 cv->value = 565 strdup(env_list_item); 566 STAILQ_INSERT_TAIL(c[i].list, cv, 567 next); 568 } 569 break; 570 default: 571 c[i].val = val; 572 break; 573 } 574 } 575 } 576 577 /* Read LOCALBASE/etc/pkg.conf first. */ 578 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; 579 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", 580 localbase); 581 582 if (access(confpath, F_OK) == 0 && read_conf_file(confpath, 583 CONFFILE_PKG)) 584 goto finalize; 585 586 /* Then read in all repos from REPOS_DIR list of directories. */ 587 if (c[REPOS_DIR].list == NULL) { 588 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list)); 589 STAILQ_INIT(c[REPOS_DIR].list); 590 cv = malloc(sizeof(struct config_value)); 591 cv->value = strdup("/etc/pkg"); 592 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); 593 cv = malloc(sizeof(struct config_value)); 594 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0) 595 goto finalize; 596 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); 597 } 598 599 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next) 600 if (load_repositories(cv->value)) 601 goto finalize; 602 603finalize: 604 if (c[ABI].val == NULL && c[ABI].value == NULL) { 605 if (pkg_get_myabi(abi, BUFSIZ) != 0) 606 errx(EXIT_FAILURE, "Failed to determine the system " 607 "ABI"); 608 c[ABI].val = abi; 609 } 610 611 subst_packagesite(c[ABI].val); 612 613 return (0); 614} 615 616int 617config_string(pkg_config_key k, const char **val) 618{ 619 if (c[k].type != PKG_CONFIG_STRING) 620 return (-1); 621 622 if (c[k].value != NULL) 623 *val = c[k].value; 624 else 625 *val = c[k].val; 626 627 return (0); 628} 629 630int 631config_bool(pkg_config_key k, bool *val) 632{ 633 const char *value; 634 635 if (c[k].type != PKG_CONFIG_BOOL) 636 return (-1); 637 638 *val = false; 639 640 if (c[k].value != NULL) 641 value = c[k].value; 642 else 643 value = c[k].val; 644 645 if (boolstr_to_bool(value)) 646 *val = true; 647 648 return (0); 649} 650 651void 652config_finish(void) { 653 int i; 654 655 for (i = 0; i < CONFIG_SIZE; i++) 656 free(c[i].value); 657} 658