main.c revision 1.3
1/* $NetBSD: main.c,v 1.3 2018/03/25 04:04:36 sevan Exp $ */ 2 3#if HAVE_CONFIG_H 4#include "config.h" 5#endif 6#include <nbcompat.h> 7#if HAVE_SYS_CDEFS_H 8#include <sys/cdefs.h> 9#endif 10__RCSID("$NetBSD: main.c,v 1.3 2018/03/25 04:04:36 sevan Exp $"); 11 12/*- 13 * Copyright (c) 1999-2009 The NetBSD Foundation, Inc. 14 * All rights reserved. 15 * 16 * This code is derived from software contributed to The NetBSD Foundation 17 * by Hubert Feyrer <hubert@feyrer.de> and 18 * by Joerg Sonnenberger <joerg@NetBSD.org>. 19 * 20 * Redistribution and use in source and binary forms, with or without 21 * modification, are permitted provided that the following conditions 22 * are met: 23 * 1. Redistributions of source code must retain the above copyright 24 * notice, this list of conditions and the following disclaimer. 25 * 2. Redistributions in binary form must reproduce the above copyright 26 * notice, this list of conditions and the following disclaimer in the 27 * documentation and/or other materials provided with the distribution. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42#if HAVE_SYS_TYPES_H 43#include <sys/types.h> 44#endif 45#if HAVE_SYS_STAT_H 46#include <sys/stat.h> 47#endif 48#if HAVE_DIRENT_H 49#include <dirent.h> 50#endif 51#if HAVE_ERR_H 52#include <err.h> 53#endif 54#if HAVE_ERRNO_H 55#include <errno.h> 56#endif 57#if HAVE_FCNTL_H 58#include <fcntl.h> 59#endif 60#ifndef NETBSD 61#include <nbcompat/md5.h> 62#else 63#include <md5.h> 64#endif 65#if HAVE_LIMITS_H 66#include <limits.h> 67#endif 68#if HAVE_STDIO_H 69#include <stdio.h> 70#endif 71#if HAVE_STRING_H 72#include <string.h> 73#endif 74 75#ifndef BOOTSTRAP 76#include <archive.h> 77#include <fetch.h> 78#endif 79 80#include "admin.h" 81#include "lib.h" 82 83#define DEFAULT_SFX ".t[bg]z" /* default suffix for ls{all,best} */ 84 85struct pkgdb_count { 86 size_t files; 87 size_t directories; 88 size_t packages; 89}; 90 91static const char Options[] = "C:K:SVbd:qs:v"; 92 93int quiet, verbose; 94 95static void set_unset_variable(char **, Boolean); 96 97/* print usage message and exit */ 98void 99usage(void) 100{ 101 (void) fprintf(stderr, "usage: %s [-bqSVv] [-C config] [-d lsdir] [-K pkg_dbdir] [-s sfx] command [args ...]\n" 102 "Where 'commands' and 'args' are:\n" 103 " rebuild - rebuild pkgdb from +CONTENTS files\n" 104 " rebuild-tree - rebuild +REQUIRED_BY files from forward deps\n" 105 " check [pkg ...] - check md5 checksum of installed files\n" 106 " add pkg ... - add pkg files to database\n" 107 " set variable=value pkg ... - set installation variable for package\n" 108 " unset variable pkg ... - unset installation variable for package\n" 109 " lsall /path/to/pkgpattern - list all pkgs matching the pattern\n" 110 " lsbest /path/to/pkgpattern - list pkgs matching the pattern best\n" 111 " dump - dump database\n" 112 " pmatch pattern pkg - returns true if pkg matches pattern, otherwise false\n" 113 " fetch-pkg-vulnerabilities [-s] - fetch new vulnerability file\n" 114 " check-pkg-vulnerabilities [-s] <file> - check syntax and checksums of the vulnerability file\n" 115 " audit [-eis] [-t type] ... - check installed packages for vulnerabilities\n" 116 " audit-pkg [-eis] [-t type] ... - check listed packages for vulnerabilities\n" 117 " audit-batch [-eis] [-t type] ... - check packages in listed files for vulnerabilities\n" 118 " audit-history [-t type] ... - print all advisories for package names\n" 119 " check-license <condition> - check if condition is acceptable\n" 120 " check-single-license <license> - check if license is acceptable\n" 121 " config-var name - print current value of the configuration variable\n" 122 " check-signature ... - verify the signature of packages\n" 123 " x509-sign-package pkg spkg key cert - create X509 signature\n" 124 " gpg-sign-package pkg spkg - create GPG signature\n", 125 getprogname()); 126 exit(EXIT_FAILURE); 127} 128 129/* 130 * add1pkg(<pkg>) 131 * adds the files listed in the +CONTENTS of <pkg> into the 132 * pkgdb.byfile.db database file in the current package dbdir. It 133 * returns the number of files added to the database file. 134 */ 135static int 136add_pkg(const char *pkgdir, void *vp) 137{ 138 FILE *f; 139 plist_t *p; 140 package_t Plist; 141 char *contents; 142 char *PkgName, *dirp; 143 char file[MaxPathSize]; 144 struct pkgdb_count *count; 145 146 if (!pkgdb_open(ReadWrite)) 147 err(EXIT_FAILURE, "cannot open pkgdb"); 148 149 count = vp; 150 ++count->packages; 151 152 contents = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME); 153 if ((f = fopen(contents, "r")) == NULL) 154 errx(EXIT_FAILURE, "%s: can't open `%s'", pkgdir, CONTENTS_FNAME); 155 free(contents); 156 157 read_plist(&Plist, f); 158 if ((p = find_plist(&Plist, PLIST_NAME)) == NULL) { 159 errx(EXIT_FAILURE, "Package `%s' has no @name, aborting.", pkgdir); 160 } 161 162 PkgName = p->name; 163 dirp = NULL; 164 for (p = Plist.head; p; p = p->next) { 165 switch(p->type) { 166 case PLIST_FILE: 167 if (dirp == NULL) { 168 errx(EXIT_FAILURE, "@cwd not yet found, please send-pr!"); 169 } 170 (void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name); 171 if (!(isfile(file) || islinktodir(file))) { 172 if (isbrokenlink(file)) { 173 warnx("%s: Symlink `%s' exists and is in %s but target does not exist!", 174 PkgName, file, CONTENTS_FNAME); 175 } else { 176 warnx("%s: File `%s' is in %s but not on filesystem!", 177 PkgName, file, CONTENTS_FNAME); 178 } 179 } else { 180 pkgdb_store(file, PkgName); 181 ++count->files; 182 } 183 break; 184 case PLIST_PKGDIR: 185 add_pkgdir(PkgName, dirp, p->name); 186 ++count->directories; 187 break; 188 case PLIST_CWD: 189 if (strcmp(p->name, ".") != 0) 190 dirp = p->name; 191 else 192 dirp = pkgdb_pkg_dir(pkgdir); 193 break; 194 case PLIST_IGNORE: 195 p = p->next; 196 break; 197 case PLIST_SHOW_ALL: 198 case PLIST_SRC: 199 case PLIST_CMD: 200 case PLIST_CHMOD: 201 case PLIST_CHOWN: 202 case PLIST_CHGRP: 203 case PLIST_COMMENT: 204 case PLIST_NAME: 205 case PLIST_UNEXEC: 206 case PLIST_DISPLAY: 207 case PLIST_PKGDEP: 208 case PLIST_DIR_RM: 209 case PLIST_OPTION: 210 case PLIST_PKGCFL: 211 case PLIST_BLDDEP: 212 break; 213 } 214 } 215 free_plist(&Plist); 216 fclose(f); 217 pkgdb_close(); 218 219 return 0; 220} 221 222static void 223rebuild(void) 224{ 225 char *cachename; 226 struct pkgdb_count count; 227 228 count.files = 0; 229 count.directories = 0; 230 count.packages = 0; 231 232 cachename = pkgdb_get_database(); 233 if (unlink(cachename) != 0 && errno != ENOENT) 234 err(EXIT_FAILURE, "unlink %s", cachename); 235 236 setbuf(stdout, NULL); 237 238 iterate_pkg_db(add_pkg, &count); 239 240 printf("\n"); 241 printf("Stored %" PRIzu " file%s and %" PRIzu " explicit director%s" 242 " from %"PRIzu " package%s in %s.\n", 243 count.files, count.files == 1 ? "" : "s", 244 count.directories, count.directories == 1 ? "y" : "ies", 245 count.packages, count.packages == 1 ? "" : "s", 246 cachename); 247} 248 249static int 250lspattern(const char *pkg, void *vp) 251{ 252 const char *dir = vp; 253 printf("%s/%s\n", dir, pkg); 254 return 0; 255} 256 257static int 258lsbasepattern(const char *pkg, void *vp) 259{ 260 puts(pkg); 261 return 0; 262} 263 264static int 265remove_required_by(const char *pkgname, void *cookie) 266{ 267 char *path; 268 269 path = pkgdb_pkg_file(pkgname, REQUIRED_BY_FNAME); 270 271 if (unlink(path) == -1 && errno != ENOENT) 272 err(EXIT_FAILURE, "Cannot remove %s", path); 273 274 free(path); 275 276 return 0; 277} 278 279static void 280add_required_by(const char *pattern, const char *required_by) 281{ 282 char *best_installed, *path; 283 int fd; 284 size_t len; 285 286 best_installed = find_best_matching_installed_pkg(pattern); 287 if (best_installed == NULL) { 288 warnx("Dependency %s of %s unresolved", pattern, required_by); 289 return; 290 } 291 292 path = pkgdb_pkg_file(best_installed, REQUIRED_BY_FNAME); 293 free(best_installed); 294 295 if ((fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) 296 errx(EXIT_FAILURE, "Cannot write to %s", path); 297 free(path); 298 299 len = strlen(required_by); 300 if (write(fd, required_by, len) != (ssize_t)len || 301 write(fd, "\n", 1) != 1 || 302 close(fd) == -1) 303 errx(EXIT_FAILURE, "Cannot write to %s", path); 304} 305 306 307static int 308add_depends_of(const char *pkgname, void *cookie) 309{ 310 FILE *fp; 311 plist_t *p; 312 package_t plist; 313 char *path; 314 315 path = pkgdb_pkg_file(pkgname, CONTENTS_FNAME); 316 if ((fp = fopen(path, "r")) == NULL) 317 errx(EXIT_FAILURE, "Cannot read %s of package %s", 318 CONTENTS_FNAME, pkgname); 319 free(path); 320 read_plist(&plist, fp); 321 fclose(fp); 322 323 for (p = plist.head; p; p = p->next) { 324 if (p->type == PLIST_PKGDEP) 325 add_required_by(p->name, pkgname); 326 } 327 328 free_plist(&plist); 329 330 return 0; 331} 332 333static void 334rebuild_tree(void) 335{ 336 if (iterate_pkg_db(remove_required_by, NULL) == -1) 337 errx(EXIT_FAILURE, "cannot iterate pkgdb"); 338 if (iterate_pkg_db(add_depends_of, NULL) == -1) 339 errx(EXIT_FAILURE, "cannot iterate pkgdb"); 340} 341 342int 343main(int argc, char *argv[]) 344{ 345 Boolean use_default_sfx = TRUE; 346 Boolean show_basename_only = FALSE; 347 char lsdir[MaxPathSize]; 348 char sfx[MaxPathSize]; 349 char *lsdirp = NULL; 350 int ch; 351 352 setprogname(argv[0]); 353 354 if (argc < 2) 355 usage(); 356 357 while ((ch = getopt(argc, argv, Options)) != -1) 358 switch (ch) { 359 case 'C': 360 config_file = optarg; 361 break; 362 363 case 'K': 364 pkgdb_set_dir(optarg, 3); 365 break; 366 367 case 'S': 368 sfx[0] = 0x0; 369 use_default_sfx = FALSE; 370 break; 371 372 case 'V': 373 show_version(); 374 /* NOTREACHED */ 375 376 case 'b': 377 show_basename_only = TRUE; 378 break; 379 380 case 'd': 381 (void) strlcpy(lsdir, optarg, sizeof(lsdir)); 382 lsdirp = lsdir; 383 break; 384 385 case 'q': 386 quiet = 1; 387 break; 388 389 case 's': 390 (void) strlcpy(sfx, optarg, sizeof(sfx)); 391 use_default_sfx = FALSE; 392 break; 393 394 case 'v': 395 ++verbose; 396 break; 397 398 default: 399 usage(); 400 /* NOTREACHED */ 401 } 402 403 argc -= optind; 404 argv += optind; 405 406 if (argc <= 0) { 407 usage(); 408 } 409 410 /* 411 * config-var is reading the config file implicitly, 412 * so skip it here. 413 */ 414 if (strcasecmp(argv[0], "config-var") != 0) 415 pkg_install_config(); 416 417 if (use_default_sfx) 418 (void) strlcpy(sfx, DEFAULT_SFX, sizeof(sfx)); 419 420 if (strcasecmp(argv[0], "pmatch") == 0) { 421 422 char *pattern, *pkg; 423 424 argv++; /* "pmatch" */ 425 426 if (argv[0] == NULL || argv[1] == NULL) { 427 usage(); 428 } 429 430 pattern = argv[0]; 431 pkg = argv[1]; 432 433 if (pkg_match(pattern, pkg)){ 434 return 0; 435 } else { 436 return 1; 437 } 438 439 } else if (strcasecmp(argv[0], "rebuild") == 0) { 440 441 rebuild(); 442 printf("Done.\n"); 443 444 445 } else if (strcasecmp(argv[0], "rebuild-tree") == 0) { 446 447 rebuild_tree(); 448 printf("Done.\n"); 449 450 } else if (strcasecmp(argv[0], "check") == 0) { 451 argv++; /* "check" */ 452 453 check(argv); 454 455 if (!quiet) { 456 printf("Done.\n"); 457 } 458 459 } else if (strcasecmp(argv[0], "lsall") == 0) { 460 argv++; /* "lsall" */ 461 462 while (*argv != NULL) { 463 /* args specified */ 464 int rc; 465 const char *basep, *dir; 466 467 dir = lsdirp ? lsdirp : dirname_of(*argv); 468 basep = basename_of(*argv); 469 470 if (show_basename_only) 471 rc = match_local_files(dir, use_default_sfx, 1, basep, lsbasepattern, NULL); 472 else 473 rc = match_local_files(dir, use_default_sfx, 1, basep, lspattern, __UNCONST(dir)); 474 if (rc == -1) 475 errx(EXIT_FAILURE, "Error from match_local_files(\"%s\", \"%s\", ...)", 476 dir, basep); 477 478 argv++; 479 } 480 481 } else if (strcasecmp(argv[0], "lsbest") == 0) { 482 argv++; /* "lsbest" */ 483 484 while (*argv != NULL) { 485 /* args specified */ 486 const char *basep, *dir; 487 char *p; 488 489 dir = lsdirp ? lsdirp : dirname_of(*argv); 490 basep = basename_of(*argv); 491 492 p = find_best_matching_file(dir, basep, use_default_sfx, 1); 493 494 if (p) { 495 if (show_basename_only) 496 printf("%s\n", p); 497 else 498 printf("%s/%s\n", dir, p); 499 free(p); 500 } 501 502 argv++; 503 } 504 } else if (strcasecmp(argv[0], "list") == 0 || 505 strcasecmp(argv[0], "dump") == 0) { 506 507 pkgdb_dump(); 508 509 } else if (strcasecmp(argv[0], "add") == 0) { 510 struct pkgdb_count count; 511 512 count.files = 0; 513 count.directories = 0; 514 count.packages = 0; 515 516 for (++argv; *argv != NULL; ++argv) 517 add_pkg(*argv, &count); 518 } else if (strcasecmp(argv[0], "set") == 0) { 519 argv++; /* "set" */ 520 set_unset_variable(argv, FALSE); 521 } else if (strcasecmp(argv[0], "unset") == 0) { 522 argv++; /* "unset" */ 523 set_unset_variable(argv, TRUE); 524 } else if (strcasecmp(argv[0], "config-var") == 0) { 525 argv++; 526 if (argv == NULL || argv[1] != NULL) 527 errx(EXIT_FAILURE, "config-var takes exactly one argument"); 528 pkg_install_show_variable(argv[0]); 529 } else if (strcasecmp(argv[0], "check-license") == 0) { 530 if (argv[1] == NULL) 531 errx(EXIT_FAILURE, "check-license takes exactly one argument"); 532 533 load_license_lists(); 534 535 switch (acceptable_pkg_license(argv[1])) { 536 case 0: 537 puts("no"); 538 return 0; 539 case 1: 540 puts("yes"); 541 return 0; 542 case -1: 543 errx(EXIT_FAILURE, "invalid license condition"); 544 } 545 } else if (strcasecmp(argv[0], "check-single-license") == 0) { 546 if (argv[1] == NULL) 547 errx(EXIT_FAILURE, "check-license takes exactly one argument"); 548 load_license_lists(); 549 550 switch (acceptable_license(argv[1])) { 551 case 0: 552 puts("no"); 553 return 0; 554 case 1: 555 puts("yes"); 556 return 0; 557 case -1: 558 errx(EXIT_FAILURE, "invalid license"); 559 } 560 } 561#ifndef BOOTSTRAP 562 else if (strcasecmp(argv[0], "findbest") == 0) { 563 struct url *url; 564 char *output; 565 int rc; 566 567 process_pkg_path(); 568 569 rc = 0; 570 for (++argv; *argv != NULL; ++argv) { 571 url = find_best_package(NULL, *argv, 1); 572 if (url == NULL) { 573 rc = 1; 574 continue; 575 } 576 output = fetchStringifyURL(url); 577 puts(output); 578 fetchFreeURL(url); 579 free(output); 580 } 581 582 return rc; 583 } else if (strcasecmp(argv[0], "fetch-pkg-vulnerabilities") == 0) { 584 fetch_pkg_vulnerabilities(--argc, ++argv); 585 } else if (strcasecmp(argv[0], "check-pkg-vulnerabilities") == 0) { 586 check_pkg_vulnerabilities(--argc, ++argv); 587 } else if (strcasecmp(argv[0], "audit") == 0) { 588 audit_pkgdb(--argc, ++argv); 589 } else if (strcasecmp(argv[0], "audit-pkg") == 0) { 590 audit_pkg(--argc, ++argv); 591 } else if (strcasecmp(argv[0], "audit-batch") == 0) { 592 audit_batch(--argc, ++argv); 593 } else if (strcasecmp(argv[0], "audit-history") == 0) { 594 audit_history(--argc, ++argv); 595 } else if (strcasecmp(argv[0], "check-signature") == 0) { 596 struct archive *pkg; 597 int rc; 598 599 rc = 0; 600 for (--argc, ++argv; argc > 0; --argc, ++argv) { 601 char *archive_name; 602 603 pkg = open_archive(*argv, &archive_name); 604 if (pkg == NULL) { 605 warnx("%s could not be opened", *argv); 606 continue; 607 } 608 if (pkg_full_signature_check(archive_name, &pkg)) 609 rc = 1; 610 free(archive_name); 611 if (pkg != NULL) 612 archive_read_free(pkg); 613 } 614 return rc; 615 } else if (strcasecmp(argv[0], "x509-sign-package") == 0) { 616#ifdef HAVE_SSL 617 --argc; 618 ++argv; 619 if (argc != 4) 620 errx(EXIT_FAILURE, "x509-sign-package takes exactly four arguments"); 621 pkg_sign_x509(argv[0], argv[1], argv[2], argv[3]); 622#else 623 errx(EXIT_FAILURE, "OpenSSL support is not included"); 624#endif 625 } else if (strcasecmp(argv[0], "gpg-sign-package") == 0) { 626 --argc; 627 ++argv; 628 if (argc != 2) 629 errx(EXIT_FAILURE, "gpg-sign-package takes exactly two arguments"); 630 pkg_sign_gpg(argv[0], argv[1]); 631 } 632#endif 633 else { 634 usage(); 635 } 636 637 return 0; 638} 639 640struct set_installed_info_arg { 641 char *variable; 642 char *value; 643 int got_match; 644}; 645 646static int 647set_installed_info_var(const char *name, void *cookie) 648{ 649 struct set_installed_info_arg *arg = cookie; 650 char *filename; 651 int retval; 652 653 filename = pkgdb_pkg_file(name, INSTALLED_INFO_FNAME); 654 655 retval = var_set(filename, arg->variable, arg->value); 656 657 free(filename); 658 arg->got_match = 1; 659 660 return retval; 661} 662 663static void 664set_unset_variable(char **argv, Boolean unset) 665{ 666 struct set_installed_info_arg arg; 667 char *eq; 668 char *variable; 669 int ret = 0; 670 671 if (argv[0] == NULL || argv[1] == NULL) 672 usage(); 673 674 variable = NULL; 675 676 if (unset) { 677 arg.variable = argv[0]; 678 arg.value = NULL; 679 } else { 680 eq = NULL; 681 if ((eq=strchr(argv[0], '=')) == NULL) 682 usage(); 683 684 variable = xmalloc(eq-argv[0]+1); 685 strlcpy(variable, argv[0], eq-argv[0]+1); 686 687 arg.variable = variable; 688 arg.value = eq+1; 689 690 if (strcmp(variable, AUTOMATIC_VARNAME) == 0 && 691 strcasecmp(arg.value, "yes") != 0 && 692 strcasecmp(arg.value, "no") != 0) { 693 errx(EXIT_FAILURE, 694 "unknown value `%s' for " AUTOMATIC_VARNAME, 695 arg.value); 696 } 697 } 698 if (strpbrk(arg.variable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != NULL) { 699 free(variable); 700 errx(EXIT_FAILURE, 701 "variable name must not contain uppercase letters"); 702 } 703 704 argv++; 705 while (*argv != NULL) { 706 arg.got_match = 0; 707 if (match_installed_pkgs(*argv, set_installed_info_var, &arg) == -1) 708 errx(EXIT_FAILURE, "Cannot process pkdbdb"); 709 if (arg.got_match == 0) { 710 char *pattern; 711 712 if (ispkgpattern(*argv)) { 713 warnx("no matching pkg for `%s'", *argv); 714 ret++; 715 } else { 716 pattern = xasprintf("%s-[0-9]*", *argv); 717 718 if (match_installed_pkgs(pattern, set_installed_info_var, &arg) == -1) 719 errx(EXIT_FAILURE, "Cannot process pkdbdb"); 720 721 if (arg.got_match == 0) { 722 warnx("cannot find package %s", *argv); 723 ++ret; 724 } 725 free(pattern); 726 } 727 } 728 729 argv++; 730 } 731 732 if (ret > 0) 733 exit(EXIT_FAILURE); 734 735 free(variable); 736 737 return; 738} 739