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