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; 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 snprintf(dest + strlen(dest), sz - strlen(dest), 191 ":%s:%s:%s", elf_corres_to_string(endian_corres, 192 (int)elfhdr.e_ident[EI_DATA]), 193 (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 194 "eabi" : "oabi", 195 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 196 "softfp" : "vfp"); 197 break; 198 case EM_MIPS: 199 /* 200 * this is taken from binutils sources: 201 * include/elf/mips.h 202 * mapping is figured out from binutils: 203 * gas/config/tc-mips.c 204 */ 205 switch (elfhdr.e_flags & EF_MIPS_ABI) { 206 case E_MIPS_ABI_O32: 207 abi = "o32"; 208 break; 209 case E_MIPS_ABI_N32: 210 abi = "n32"; 211 break; 212 default: 213 if (elfhdr.e_ident[EI_DATA] == 214 ELFCLASS32) 215 abi = "o32"; 216 else if (elfhdr.e_ident[EI_DATA] == 217 ELFCLASS64) 218 abi = "n64"; 219 break; 220 } 221 snprintf(dest + strlen(dest), sz - strlen(dest), 222 ":%s:%s", elf_corres_to_string(endian_corres, 223 (int)elfhdr.e_ident[EI_DATA]), abi); 224 break; 225 } 226 227 cleanup: 228 if (elf != NULL) 229 elf_end(elf); 230 231 close(fd); 232 return (ret); 233 } 234 235 static void 236 subst_packagesite(const char *abi) 237 { 238 struct sbuf *newval; 239 const char *variable_string; 240 const char *oldval; 241 242 if (c[PACKAGESITE].value != NULL) 243 oldval = c[PACKAGESITE].value; 244 else 245 oldval = c[PACKAGESITE].val; 246 247 if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 248 return; 249 250 newval = sbuf_new_auto(); 251 sbuf_bcat(newval, oldval, variable_string - oldval); 252 sbuf_cat(newval, abi); 253 sbuf_cat(newval, variable_string + strlen("${ABI}")); 254 sbuf_finish(newval); 255 256 free(c[PACKAGESITE].value); 257 c[PACKAGESITE].value = strdup(sbuf_data(newval)); 258 } 259 260 static void 261 config_parse(yaml_document_t *doc, yaml_node_t *node) 262 { 263 yaml_node_pair_t *pair; 264 yaml_node_t *key, *val; 265 struct sbuf *buf = sbuf_new_auto(); 266 int i; 267 size_t j; 268 269 pair = node->data.mapping.pairs.start; 270 271 while (pair < node->data.mapping.pairs.top) { 272 key = yaml_document_get_node(doc, pair->key); 273 val = yaml_document_get_node(doc, pair->value); 274 275 /* 276 * ignoring silently empty keys can be empty lines 277 * or user mistakes 278 */ 279 if (key->data.scalar.length <= 0) { 280 ++pair; 281 continue; 282 } 283 284 /* 285 * silently skip on purpose to allow user to leave 286 * empty lines without complaining 287 */ 288 if (val->type == YAML_NO_NODE || 289 (val->type == YAML_SCALAR_NODE && 290 val->data.scalar.length <= 0)) { 291 ++pair; 292 continue; 293 } 294 295 sbuf_clear(buf); 296 for (j = 0; j < strlen(key->data.scalar.value); ++j) 297 sbuf_putc(buf, toupper(key->data.scalar.value[j])); 298 299 sbuf_finish(buf); 300 for (i = 0; i < CONFIG_SIZE; i++) { 301 if (strcmp(sbuf_data(buf), c[i].key) == 0) 302 break; 303 } 304 305 if (i == CONFIG_SIZE) { 306 ++pair; 307 continue; 308 } 309 310 /* env has priority over config file */ 311 if (c[i].envset) { 312 ++pair; 313 continue; 314 } 315 316 c[i].value = strdup(val->data.scalar.value); 317 ++pair; 318 } 319 320 sbuf_delete(buf); 321 } 322 323 int 324 config_init(void) 325 { 326 FILE *fp; 327 yaml_parser_t parser; 328 yaml_document_t doc; 329 yaml_node_t *node; 330 const char *val; 331 int i; 332 const char *localbase; 333 char confpath[MAXPATHLEN]; 334 char abi[BUFSIZ]; 335 336 for (i = 0; i < CONFIG_SIZE; i++) { 337 val = getenv(c[i].key); 338 if (val != NULL) { 339 c[i].val = val; 340 c[i].envset = true; 341 } 342 } 343 344 localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; 345 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); 346 347 if ((fp = fopen(confpath, "r")) == NULL) { 348 if (errno != ENOENT) 349 err(EXIT_FAILURE, "Unable to open configuration file %s", confpath); 350 /* no configuration present */ 351 goto finalize; 352 } 353 354 yaml_parser_initialize(&parser); 355 yaml_parser_set_input_file(&parser, fp); 356 yaml_parser_load(&parser, &doc); 357 358 node = yaml_document_get_root_node(&doc); 359 360 if (node != NULL) { 361 if (node->type != YAML_MAPPING_NODE) 362 warnx("Invalid configuration format, ignoring the configuration file"); 363 else 364 config_parse(&doc, node); 365 } else { 366 warnx("Invalid configuration format, ignoring the configuration file"); 367 } 368 369 yaml_document_delete(&doc); 370 yaml_parser_delete(&parser); 371 372 finalize: 373 if (c[ABI].val == NULL && c[ABI].value == NULL) { 374 if (pkg_get_myabi(abi, BUFSIZ) != 0) 375 errx(EXIT_FAILURE, "Failed to determine the system ABI"); 376 c[ABI].val = abi; 377 } 378 379 subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); 380 381 return (0); 382 } 383 384 int 385 config_string(pkg_config_key k, const char **val) 386 { 387 if (c[k].type != PKG_CONFIG_STRING) 388 return (-1); 389 390 if (c[k].value != NULL) 391 *val = c[k].value; 392 else 393 *val = c[k].val; 394 395 return (0); 396 } 397 398 int 399 config_bool(pkg_config_key k, bool *val) 400 { 401 const char *value; 402 403 if (c[k].type != PKG_CONFIG_BOOL) 404 return (-1); 405 406 *val = false; 407 408 if (c[k].value != NULL) 409 value = c[k].value; 410 else 411 value = c[k].val; 412 413 if (strcasecmp(value, "true") == 0 || 414 strcasecmp(value, "yes") == 0 || 415 strcasecmp(value, "on") == 0 || 416 *value == '1') 417 *val = true; 418 419 return (0); 420 } 421 422 void 423 config_finish(void) { 424 int i; 425 426 for (i = 0; i < CONFIG_SIZE; i++) 427 free(c[i].value); 428 } 429