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