1 /*- 2 * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/sbuf.h> 32 #include <sys/elf_common.h> 33 #include <sys/endian.h> 34 35 #include <bsdyml.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <gelf.h> 41 #include <inttypes.h> 42 #include <paths.h> 43 #include <stdbool.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "elf_tables.h" 48 #include "config.h" 49 50 #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ 51 52 struct config_entry { 53 uint8_t type; 54 const char *key; 55 const char *val; 56 char *value; 57 bool envset; 58 }; 59 60 static struct config_entry c[] = { 61 [PACKAGESITE] = { 62 PKG_CONFIG_STRING, 63 "PACKAGESITE", 64 "http://pkg.FreeBSD.org/${ABI}/latest", 65 NULL, 66 false, 67 }, 68 [ABI] = { 69 PKG_CONFIG_STRING, 70 "ABI", 71 NULL, 72 NULL, 73 false, 74 }, 75 [MIRROR_TYPE] = { 76 PKG_CONFIG_STRING, 77 "MIRROR_TYPE", 78 "SRV", 79 NULL, 80 false, 81 }, 82 [ASSUME_ALWAYS_YES] = { 83 PKG_CONFIG_BOOL, 84 "ASSUME_ALWAYS_YES", 85 "NO", 86 NULL, 87 false, 88 } 89 }; 90 91 static const char * 92 elf_corres_to_string(struct _elf_corres *m, int e) 93 { 94 int i; 95 96 for (i = 0; m[i].string != NULL; i++) 97 if (m[i].elf_nb == e) 98 return (m[i].string); 99 100 return ("unknown"); 101 } 102 103 static int 104 pkg_get_myabi(char *dest, size_t sz) 105 { 106 Elf *elf; 107 Elf_Data *data; 108 Elf_Note note; 109 Elf_Scn *scn; 110 char *src, *osname; 111 const char *abi, *fpu; 112 GElf_Ehdr elfhdr; 113 GElf_Shdr shdr; 114 int fd, i, ret; 115 uint32_t version; 116 117 version = 0; 118 ret = -1; 119 scn = NULL; 120 abi = NULL; 121 122 if (elf_version(EV_CURRENT) == EV_NONE) { 123 warnx("ELF library initialization failed: %s", 124 elf_errmsg(-1)); 125 return (-1); 126 } 127 128 if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) { 129 warn("open()"); 130 return (-1); 131 } 132 133 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 134 ret = -1; 135 warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 136 goto cleanup; 137 } 138 139 if (gelf_getehdr(elf, &elfhdr) == NULL) { 140 ret = -1; 141 warn("getehdr() failed: %s.", elf_errmsg(-1)); 142 goto cleanup; 143 } 144 while ((scn = elf_nextscn(elf, scn)) != NULL) { 145 if (gelf_getshdr(scn, &shdr) != &shdr) { 146 ret = -1; 147 warn("getshdr() failed: %s.", elf_errmsg(-1)); 148 goto cleanup; 149 } 150 151 if (shdr.sh_type == SHT_NOTE) 152 break; 153 } 154 155 if (scn == NULL) { 156 ret = -1; 157 warn("failed to get the note section"); 158 goto cleanup; 159 } 160 161 data = elf_getdata(scn, NULL); 162 src = data->d_buf; 163 for (;;) { 164 memcpy(¬e, src, sizeof(Elf_Note)); 165 src += sizeof(Elf_Note); 166 if (note.n_type == NT_VERSION) 167 break; 168 src += note.n_namesz + note.n_descsz; 169 } 170 osname = src; 171 src += roundup2(note.n_namesz, 4); 172 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 173 version = be32dec(src); 174 else 175 version = le32dec(src); 176 177 for (i = 0; osname[i] != '\0'; i++) 178 osname[i] = (char)tolower(osname[i]); 179 180 snprintf(dest, sz, "%s:%d:%s:%s", 181 osname, version / 100000, 182 elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 183 elf_corres_to_string(wordsize_corres, 184 (int)elfhdr.e_ident[EI_CLASS])); 185 186 ret = 0; 187 188 switch (elfhdr.e_machine) { 189 case EM_ARM: 190 /* FreeBSD doesn't support the hard-float ABI yet */ 191 fpu = "softfp"; 192 if ((elfhdr.e_flags & 0xFF000000) != 0) { 193 /* This is an EABI file, the conformance level is set */ 194 abi = "eabi"; 195 } else if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_NONE) { 196 /* 197 * EABI executables all have this field set to 198 * ELFOSABI_NONE, therefore it must be an oabi file. 199 */ 200 abi = "oabi"; 201 } else { 202 ret = 1; 203 goto cleanup; 204 } 205 snprintf(dest + strlen(dest), sz - strlen(dest), 206 ":%s:%s:%s", elf_corres_to_string(endian_corres, 207 (int)elfhdr.e_ident[EI_DATA]), 208 abi, fpu); 209 break; 210 case EM_MIPS: 211 /* 212 * this is taken from binutils sources: 213 * include/elf/mips.h 214 * mapping is figured out from binutils: 215 * gas/config/tc-mips.c 216 */ 217 switch (elfhdr.e_flags & EF_MIPS_ABI) { 218 case E_MIPS_ABI_O32: 219 abi = "o32"; 220 break; 221 case E_MIPS_ABI_N32: 222 abi = "n32"; 223 break; 224 default: 225 if (elfhdr.e_ident[EI_DATA] == 226 ELFCLASS32) 227 abi = "o32"; 228 else if (elfhdr.e_ident[EI_DATA] == 229 ELFCLASS64) 230 abi = "n64"; 231 break; 232 } 233 snprintf(dest + strlen(dest), sz - strlen(dest), 234 ":%s:%s", elf_corres_to_string(endian_corres, 235 (int)elfhdr.e_ident[EI_DATA]), abi); 236 break; 237 } 238 239 cleanup: 240 if (elf != NULL) 241 elf_end(elf); 242 243 close(fd); 244 return (ret); 245 } 246 247 static void 248 subst_packagesite(const char *abi) 249 { 250 struct sbuf *newval; 251 const char *variable_string; 252 const char *oldval; 253 254 if (c[PACKAGESITE].value != NULL) 255 oldval = c[PACKAGESITE].value; 256 else 257 oldval = c[PACKAGESITE].val; 258 259 if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 260 return; 261 262 newval = sbuf_new_auto(); 263 sbuf_bcat(newval, oldval, variable_string - oldval); 264 sbuf_cat(newval, abi); 265 sbuf_cat(newval, variable_string + strlen("${ABI}")); 266 sbuf_finish(newval); 267 268 free(c[PACKAGESITE].value); 269 c[PACKAGESITE].value = strdup(sbuf_data(newval)); 270 } 271 272 static void 273 config_parse(yaml_document_t *doc, yaml_node_t *node) 274 { 275 yaml_node_pair_t *pair; 276 yaml_node_t *key, *val; 277 struct sbuf *buf = sbuf_new_auto(); 278 int i; 279 size_t j; 280 281 pair = node->data.mapping.pairs.start; 282 283 while (pair < node->data.mapping.pairs.top) { 284 key = yaml_document_get_node(doc, pair->key); 285 val = yaml_document_get_node(doc, pair->value); 286 287 /* 288 * ignoring silently empty keys can be empty lines 289 * or user mistakes 290 */ 291 if (key->data.scalar.length <= 0) { 292 ++pair; 293 continue; 294 } 295 296 /* 297 * silently skip on purpose to allow user to leave 298 * empty lines without complaining 299 */ 300 if (val->type == YAML_NO_NODE || 301 (val->type == YAML_SCALAR_NODE && 302 val->data.scalar.length <= 0)) { 303 ++pair; 304 continue; 305 } 306 307 sbuf_clear(buf); 308 for (j = 0; j < strlen(key->data.scalar.value); ++j) 309 sbuf_putc(buf, toupper(key->data.scalar.value[j])); 310 311 sbuf_finish(buf); 312 for (i = 0; i < CONFIG_SIZE; i++) { 313 if (strcmp(sbuf_data(buf), c[i].key) == 0) 314 break; 315 } 316 317 if (i == CONFIG_SIZE) { 318 ++pair; 319 continue; 320 } 321 322 /* env has priority over config file */ 323 if (c[i].envset) { 324 ++pair; 325 continue; 326 } 327 328 c[i].value = strdup(val->data.scalar.value); 329 ++pair; 330 } 331 332 sbuf_delete(buf); 333 } 334 335 int 336 config_init(void) 337 { 338 FILE *fp; 339 yaml_parser_t parser; 340 yaml_document_t doc; 341 yaml_node_t *node; 342 const char *val; 343 int i; 344 const char *localbase; 345 char confpath[MAXPATHLEN]; 346 char abi[BUFSIZ]; 347 348 for (i = 0; i < CONFIG_SIZE; i++) { 349 val = getenv(c[i].key); 350 if (val != NULL) { 351 c[i].val = val; 352 c[i].envset = true; 353 } 354 } 355 356 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; 357 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); 358 359 if ((fp = fopen(confpath, "r")) == NULL) { 360 if (errno != ENOENT) 361 err(EXIT_FAILURE, "Unable to open configuration file %s", confpath); 362 /* no configuration present */ 363 goto finalize; 364 } 365 366 yaml_parser_initialize(&parser); 367 yaml_parser_set_input_file(&parser, fp); 368 yaml_parser_load(&parser, &doc); 369 370 node = yaml_document_get_root_node(&doc); 371 372 if (node != NULL) { 373 if (node->type != YAML_MAPPING_NODE) 374 warnx("Invalid configuration format, ignoring the configuration file"); 375 else 376 config_parse(&doc, node); 377 } else { 378 warnx("Invalid configuration format, ignoring the configuration file"); 379 } 380 381 yaml_document_delete(&doc); 382 yaml_parser_delete(&parser); 383 384 finalize: 385 if (c[ABI].val == NULL && c[ABI].value == NULL) { 386 if (pkg_get_myabi(abi, BUFSIZ) != 0) 387 errx(EXIT_FAILURE, "Failed to determine the system ABI"); 388 c[ABI].val = abi; 389 } 390 391 subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); 392 393 return (0); 394 } 395 396 int 397 config_string(pkg_config_key k, const char **val) 398 { 399 if (c[k].type != PKG_CONFIG_STRING) 400 return (-1); 401 402 if (c[k].value != NULL) 403 *val = c[k].value; 404 else 405 *val = c[k].val; 406 407 return (0); 408 } 409 410 int 411 config_bool(pkg_config_key k, bool *val) 412 { 413 const char *value; 414 415 if (c[k].type != PKG_CONFIG_BOOL) 416 return (-1); 417 418 *val = false; 419 420 if (c[k].value != NULL) 421 value = c[k].value; 422 else 423 value = c[k].val; 424 425 if (strcasecmp(value, "true") == 0 || 426 strcasecmp(value, "yes") == 0 || 427 strcasecmp(value, "on") == 0 || 428 *value == '1') 429 *val = true; 430 431 return (0); 432 } 433 434 void 435 config_finish(void) { 436 int i; 437 438 for (i = 0; i < CONFIG_SIZE; i++) 439 free(c[i].value); 440 } 441