1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> 5 * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/queue.h> 35 #include <sys/utsname.h> 36 #include <sys/sbuf.h> 37 #include <sys/sysctl.h> 38 39 #include <dirent.h> 40 #include <ucl.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <libutil.h> 44 #include <paths.h> 45 #include <stdbool.h> 46 #include <unistd.h> 47 48 #include "config.h" 49 50 struct config_value { 51 char *value; 52 STAILQ_ENTRY(config_value) next; 53 }; 54 55 struct config_entry { 56 uint8_t type; 57 const char *key; 58 const char *val; 59 char *value; 60 STAILQ_HEAD(, config_value) *list; 61 bool envset; 62 bool main_only; /* Only set in pkg.conf. */ 63 }; 64 65 static struct config_entry c[] = { 66 [PACKAGESITE] = { 67 PKG_CONFIG_STRING, 68 "PACKAGESITE", 69 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest", 70 NULL, 71 NULL, 72 false, 73 false, 74 }, 75 [ABI] = { 76 PKG_CONFIG_STRING, 77 "ABI", 78 NULL, 79 NULL, 80 NULL, 81 false, 82 true, 83 }, 84 [MIRROR_TYPE] = { 85 PKG_CONFIG_STRING, 86 "MIRROR_TYPE", 87 "SRV", 88 NULL, 89 NULL, 90 false, 91 false, 92 }, 93 [ASSUME_ALWAYS_YES] = { 94 PKG_CONFIG_BOOL, 95 "ASSUME_ALWAYS_YES", 96 "NO", 97 NULL, 98 NULL, 99 false, 100 true, 101 }, 102 [SIGNATURE_TYPE] = { 103 PKG_CONFIG_STRING, 104 "SIGNATURE_TYPE", 105 NULL, 106 NULL, 107 NULL, 108 false, 109 false, 110 }, 111 [FINGERPRINTS] = { 112 PKG_CONFIG_STRING, 113 "FINGERPRINTS", 114 NULL, 115 NULL, 116 NULL, 117 false, 118 false, 119 }, 120 [REPOS_DIR] = { 121 PKG_CONFIG_LIST, 122 "REPOS_DIR", 123 NULL, 124 NULL, 125 NULL, 126 false, 127 true, 128 }, 129 [PUBKEY] = { 130 PKG_CONFIG_STRING, 131 "PUBKEY", 132 NULL, 133 NULL, 134 NULL, 135 false, 136 false 137 } 138 }; 139 140 static int 141 pkg_get_myabi(char *dest, size_t sz) 142 { 143 struct utsname uts; 144 char machine_arch[255]; 145 size_t len; 146 int error; 147 148 error = uname(&uts); 149 if (error) 150 return (errno); 151 152 len = sizeof(machine_arch); 153 error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0); 154 if (error) 155 return (errno); 156 machine_arch[len] = '\0'; 157 158 /* 159 * Use __FreeBSD_version rather than kernel version (uts.release) for 160 * use in jails. This is equivalent to the value of uname -U. 161 */ 162 snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000, 163 machine_arch); 164 165 return (error); 166 } 167 168 static void 169 subst_packagesite(const char *abi) 170 { 171 char *newval; 172 const char *variable_string; 173 const char *oldval; 174 175 if (c[PACKAGESITE].value != NULL) 176 oldval = c[PACKAGESITE].value; 177 else 178 oldval = c[PACKAGESITE].val; 179 180 if ((variable_string = strstr(oldval, "${ABI}")) == NULL) 181 return; 182 183 asprintf(&newval, "%.*s%s%s", 184 (int)(variable_string - oldval), oldval, abi, 185 variable_string + strlen("${ABI}")); 186 if (newval == NULL) 187 errx(EXIT_FAILURE, "asprintf"); 188 189 free(c[PACKAGESITE].value); 190 c[PACKAGESITE].value = newval; 191 } 192 193 static int 194 boolstr_to_bool(const char *str) 195 { 196 if (str != NULL && (strcasecmp(str, "true") == 0 || 197 strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 || 198 str[0] == '1')) 199 return (true); 200 201 return (false); 202 } 203 204 static void 205 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype) 206 { 207 struct sbuf *buf = sbuf_new_auto(); 208 const ucl_object_t *cur, *seq; 209 ucl_object_iter_t it = NULL, itseq = NULL; 210 struct config_entry *temp_config; 211 struct config_value *cv; 212 const char *key; 213 int i; 214 size_t j; 215 216 /* Temporary config for configs that may be disabled. */ 217 temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry)); 218 219 while ((cur = ucl_iterate_object(obj, &it, true))) { 220 key = ucl_object_key(cur); 221 if (key == NULL) 222 continue; 223 sbuf_clear(buf); 224 225 if (conftype == CONFFILE_PKG) { 226 for (j = 0; j < strlen(key); ++j) 227 sbuf_putc(buf, key[j]); 228 sbuf_finish(buf); 229 } else if (conftype == CONFFILE_REPO) { 230 if (strcasecmp(key, "url") == 0) 231 sbuf_cpy(buf, "PACKAGESITE"); 232 else if (strcasecmp(key, "mirror_type") == 0) 233 sbuf_cpy(buf, "MIRROR_TYPE"); 234 else if (strcasecmp(key, "signature_type") == 0) 235 sbuf_cpy(buf, "SIGNATURE_TYPE"); 236 else if (strcasecmp(key, "fingerprints") == 0) 237 sbuf_cpy(buf, "FINGERPRINTS"); 238 else if (strcasecmp(key, "pubkey") == 0) 239 sbuf_cpy(buf, "PUBKEY"); 240 else if (strcasecmp(key, "enabled") == 0) { 241 if ((cur->type != UCL_BOOLEAN) || 242 !ucl_object_toboolean(cur)) 243 goto cleanup; 244 } else 245 continue; 246 sbuf_finish(buf); 247 } 248 249 for (i = 0; i < CONFIG_SIZE; i++) { 250 if (strcmp(sbuf_data(buf), c[i].key) == 0) 251 break; 252 } 253 254 /* Silently skip unknown keys to be future compatible. */ 255 if (i == CONFIG_SIZE) 256 continue; 257 258 /* env has priority over config file */ 259 if (c[i].envset) 260 continue; 261 262 /* Parse sequence value ["item1", "item2"] */ 263 switch (c[i].type) { 264 case PKG_CONFIG_LIST: 265 if (cur->type != UCL_ARRAY) { 266 warnx("Skipping invalid array " 267 "value for %s.\n", c[i].key); 268 continue; 269 } 270 temp_config[i].list = 271 malloc(sizeof(*temp_config[i].list)); 272 STAILQ_INIT(temp_config[i].list); 273 274 while ((seq = ucl_iterate_object(cur, &itseq, true))) { 275 if (seq->type != UCL_STRING) 276 continue; 277 cv = malloc(sizeof(struct config_value)); 278 cv->value = 279 strdup(ucl_object_tostring(seq)); 280 STAILQ_INSERT_TAIL(temp_config[i].list, cv, 281 next); 282 } 283 break; 284 case PKG_CONFIG_BOOL: 285 temp_config[i].value = 286 strdup(ucl_object_toboolean(cur) ? "yes" : "no"); 287 break; 288 default: 289 /* Normal string value. */ 290 temp_config[i].value = strdup(ucl_object_tostring(cur)); 291 break; 292 } 293 } 294 295 /* Repo is enabled, copy over all settings from temp_config. */ 296 for (i = 0; i < CONFIG_SIZE; i++) { 297 if (c[i].envset) 298 continue; 299 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */ 300 if (conftype != CONFFILE_PKG && c[i].main_only == true) 301 continue; 302 switch (c[i].type) { 303 case PKG_CONFIG_LIST: 304 c[i].list = temp_config[i].list; 305 break; 306 default: 307 c[i].value = temp_config[i].value; 308 break; 309 } 310 } 311 312 cleanup: 313 free(temp_config); 314 sbuf_delete(buf); 315 } 316 317 /*- 318 * Parse new repo style configs in style: 319 * Name: 320 * URL: 321 * MIRROR_TYPE: 322 * etc... 323 */ 324 static void 325 parse_repo_file(ucl_object_t *obj, const char *requested_repo) 326 { 327 ucl_object_iter_t it = NULL; 328 const ucl_object_t *cur; 329 const char *key; 330 331 while ((cur = ucl_iterate_object(obj, &it, true))) { 332 key = ucl_object_key(cur); 333 334 if (key == NULL) 335 continue; 336 337 if (cur->type != UCL_OBJECT) 338 continue; 339 340 if (requested_repo != NULL && strcmp(requested_repo, key) != 0) 341 continue; 342 343 config_parse(cur, CONFFILE_REPO); 344 } 345 } 346 347 348 static int 349 read_conf_file(const char *confpath, const char *requested_repo, 350 pkg_conf_file_t conftype) 351 { 352 struct ucl_parser *p; 353 ucl_object_t *obj = NULL; 354 355 p = ucl_parser_new(0); 356 357 if (!ucl_parser_add_file(p, confpath)) { 358 if (errno != ENOENT) 359 errx(EXIT_FAILURE, "Unable to parse configuration " 360 "file %s: %s", confpath, ucl_parser_get_error(p)); 361 ucl_parser_free(p); 362 /* no configuration present */ 363 return (1); 364 } 365 366 obj = ucl_parser_get_object(p); 367 if (obj->type != UCL_OBJECT) 368 warnx("Invalid configuration format, ignoring the " 369 "configuration file %s", confpath); 370 else { 371 if (conftype == CONFFILE_PKG) 372 config_parse(obj, conftype); 373 else if (conftype == CONFFILE_REPO) 374 parse_repo_file(obj, requested_repo); 375 } 376 377 ucl_object_unref(obj); 378 ucl_parser_free(p); 379 380 return (0); 381 } 382 383 static int 384 load_repositories(const char *repodir, const char *requested_repo) 385 { 386 struct dirent *ent; 387 DIR *d; 388 char *p; 389 size_t n; 390 char path[MAXPATHLEN]; 391 int ret; 392 393 ret = 0; 394 395 if ((d = opendir(repodir)) == NULL) 396 return (1); 397 398 while ((ent = readdir(d))) { 399 /* Trim out 'repos'. */ 400 if ((n = strlen(ent->d_name)) <= 5) 401 continue; 402 p = &ent->d_name[n - 5]; 403 if (strcmp(p, ".conf") == 0) { 404 snprintf(path, sizeof(path), "%s%s%s", 405 repodir, 406 repodir[strlen(repodir) - 1] == '/' ? "" : "/", 407 ent->d_name); 408 if (access(path, F_OK) != 0) 409 continue; 410 if (read_conf_file(path, requested_repo, 411 CONFFILE_REPO)) { 412 ret = 1; 413 goto cleanup; 414 } 415 } 416 } 417 418 cleanup: 419 closedir(d); 420 421 return (ret); 422 } 423 424 int 425 config_init(const char *requested_repo) 426 { 427 char *val; 428 int i; 429 const char *localbase; 430 char *env_list_item; 431 char confpath[MAXPATHLEN]; 432 struct config_value *cv; 433 char abi[BUFSIZ]; 434 435 for (i = 0; i < CONFIG_SIZE; i++) { 436 val = getenv(c[i].key); 437 if (val != NULL) { 438 c[i].envset = true; 439 switch (c[i].type) { 440 case PKG_CONFIG_LIST: 441 /* Split up comma-separated items from env. */ 442 c[i].list = malloc(sizeof(*c[i].list)); 443 STAILQ_INIT(c[i].list); 444 for (env_list_item = strtok(val, ","); 445 env_list_item != NULL; 446 env_list_item = strtok(NULL, ",")) { 447 cv = 448 malloc(sizeof(struct config_value)); 449 cv->value = 450 strdup(env_list_item); 451 STAILQ_INSERT_TAIL(c[i].list, cv, 452 next); 453 } 454 break; 455 default: 456 c[i].val = val; 457 break; 458 } 459 } 460 } 461 462 /* Read LOCALBASE/etc/pkg.conf first. */ 463 localbase = getlocalbase(); 464 snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); 465 466 if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL, 467 CONFFILE_PKG)) 468 goto finalize; 469 470 /* Then read in all repos from REPOS_DIR list of directories. */ 471 if (c[REPOS_DIR].list == NULL) { 472 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list)); 473 STAILQ_INIT(c[REPOS_DIR].list); 474 cv = malloc(sizeof(struct config_value)); 475 cv->value = strdup("/etc/pkg"); 476 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); 477 cv = malloc(sizeof(struct config_value)); 478 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0) 479 goto finalize; 480 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); 481 } 482 483 STAILQ_FOREACH(cv, c[REPOS_DIR].list, next) 484 if (load_repositories(cv->value, requested_repo)) 485 goto finalize; 486 487 finalize: 488 if (c[ABI].val == NULL && c[ABI].value == NULL) { 489 if (pkg_get_myabi(abi, BUFSIZ) != 0) 490 errx(EXIT_FAILURE, "Failed to determine the system " 491 "ABI"); 492 c[ABI].val = abi; 493 } 494 495 subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); 496 497 return (0); 498 } 499 500 int 501 config_string(pkg_config_key k, const char **val) 502 { 503 if (c[k].type != PKG_CONFIG_STRING) 504 return (-1); 505 506 if (c[k].value != NULL) 507 *val = c[k].value; 508 else 509 *val = c[k].val; 510 511 return (0); 512 } 513 514 int 515 config_bool(pkg_config_key k, bool *val) 516 { 517 const char *value; 518 519 if (c[k].type != PKG_CONFIG_BOOL) 520 return (-1); 521 522 *val = false; 523 524 if (c[k].value != NULL) 525 value = c[k].value; 526 else 527 value = c[k].val; 528 529 if (boolstr_to_bool(value)) 530 *val = true; 531 532 return (0); 533 } 534 535 void 536 config_finish(void) { 537 int i; 538 539 for (i = 0; i < CONFIG_SIZE; i++) 540 free(c[i].value); 541 } 542