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