1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #define __EXTENTIONS__ 32 33 #include <stdio.h> 34 #include <limits.h> 35 #include <unistd.h> 36 #include <stdlib.h> 37 #include <locale.h> 38 #include <libintl.h> 39 #include <strings.h> 40 #include <string.h> 41 #include <dirent.h> 42 #include <sys/param.h> 43 #include <sys/stat.h> 44 #include <pkginfo.h> 45 #include <fcntl.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/param.h> 49 #include <sys/mman.h> 50 #include <pkgstrct.h> 51 #include <pkglocs.h> 52 #include <errno.h> 53 #include <ctype.h> 54 55 #include <pkglib.h> 56 #include <instzones_api.h> 57 #include <libadm.h> 58 #include <libinst.h> 59 60 extern char *pkgdir; 61 extern int pkginfofind(char *path, char *pkg_dir, char *pkginst); 62 63 #define ERR_USAGE "usage:\n" \ 64 "%s [-q] [-pi] [-x|l] [options] [pkg ...]\n" \ 65 "%s -d device [-q] [-x|l] [options] [pkg ...]\n" \ 66 "where\n" \ 67 " -q #quiet mode\n" \ 68 " -p #select partially installed packages\n" \ 69 " -i #select completely installed packages\n" \ 70 " -x #extracted listing\n" \ 71 " -l #long listing\n" \ 72 " -r #relocation base \n" \ 73 "and options may include:\n" \ 74 " -c category, [category...]\n" \ 75 " -a architecture\n" \ 76 " -v version\n" 77 78 #define ERR_INCOMP0 "-L and -l/-x/-r flags are incompatible" 79 #define ERR_INCOMP1 "-l and -x/-r flags are not compatible" 80 #define ERR_INCOMP2 "-x and -l/-r flags are not compatible" 81 #define ERR_INCOMP3 "-r and -x/-x flags are not compatible" 82 #define ERR_NOINFO "ERROR: information for \"%s\" was not found" 83 #define ERR_NOPINFO "ERROR: No partial information for \"%s\" was found" 84 #define ERR_BADINFO "pkginfo file is corrupt or missing" 85 #define ERR_ROOT_SET "Could not set install root from the environment." 86 #define ERR_ROOT_CMD "Command line install root contends with environment." 87 88 /* Format for dumping package attributes in dumpinfo() */ 89 #define FMT "%10s: %s\n" 90 #define SFMT "%-11.11s %-*.*s %s\n" 91 #define CFMT "%*.*s " 92 #define XFMT "%-*.*s %s\n" 93 94 #define nblock(size) ((size + (DEV_BSIZE - 1)) / DEV_BSIZE) 95 #define MAXCATG 64 96 97 static char *device = NULL; 98 static char *parmlst[] = { 99 "DESC", "PSTAMP", "INSTDATE", "VSTOCK", "SERIALNUM", "HOTLINE", 100 "EMAIL", NULL 101 }; 102 103 static char contents[PATH_MAX]; 104 static int errflg = 0; 105 static int qflag = 0; 106 static int iflag = -1; 107 static int pflag = -1; 108 static int lflag = 0; 109 static int Lflag = 0; 110 static int Nflag = 0; 111 static int xflag = 0; 112 static int rflag = 0; /* bug # 1081606 */ 113 static struct cfent entry; 114 static char **pkg = NULL; 115 static int pkgcnt = 0; 116 static char *ckcatg[MAXCATG] = {NULL}; 117 static int ncatg = 0; 118 static char *ckvers = NULL; 119 static char *ckarch = NULL; 120 121 static struct cfstat { 122 char pkginst[32]; 123 short exec; 124 short dirs; 125 short link; 126 short partial; 127 long spooled; 128 long installed; 129 short info; 130 short shared; 131 short setuid; 132 long tblks; 133 struct cfstat *next; 134 } *data; 135 static struct pkginfo info; 136 137 static struct cfstat *fpkg(char *pkginst); 138 static int iscatg(char *list); 139 static int selectp(char *p); 140 static void usage(void), look_for_installed(void), 141 report(void), rdcontents(void); 142 static void pkgusage(struct cfstat *dp, struct cfent *pentry); 143 static void getinfo(struct cfstat *dp); 144 static void dumpinfo(struct cfstat *dp, int pkgLngth); 145 146 int 147 main(int argc, char **argv) 148 { 149 int c; 150 151 pkgdir = NULL; 152 setErrstr(NULL); 153 154 /* initialize locale mechanism */ 155 156 (void) setlocale(LC_ALL, ""); 157 158 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 159 #define TEXT_DOMAIN "SYS_TEST" 160 #endif 161 (void) textdomain(TEXT_DOMAIN); 162 163 /* determine program name */ 164 165 (void) set_prog_name(argv[0]); 166 167 /* tell spmi zones interface how to access package output functions */ 168 169 z_set_output_functions(echo, echoDebug, progerr); 170 171 /* establish installation root directory */ 172 173 if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) { 174 progerr(gettext(ERR_ROOT_SET)); 175 exit(1); 176 } 177 178 while ((c = getopt(argc, argv, "LNR:xv:a:d:qrpilc:?")) != EOF) { 179 switch (c) { 180 case 'v': 181 ckvers = optarg; 182 break; 183 184 case 'a': 185 ckarch = optarg; 186 break; 187 188 case 'd': 189 /* -d could specify stream or mountable device */ 190 device = flex_device(optarg, 1); 191 break; 192 193 case 'q': 194 qflag++; 195 break; 196 197 case 'i': 198 iflag = 1; 199 if (pflag > 0) 200 usage(); 201 pflag = 0; 202 break; 203 204 case 'p': 205 pflag = 1; 206 if (iflag > 0) 207 usage(); 208 iflag = 0; 209 break; 210 211 case 'N': 212 Nflag++; 213 break; 214 215 case 'L': 216 if (xflag || lflag || rflag) { 217 progerr(gettext(ERR_INCOMP0)); 218 usage(); 219 } 220 Lflag++; 221 break; 222 223 case 'l': 224 if (xflag || rflag) { 225 progerr(gettext(ERR_INCOMP1)); 226 usage(); 227 } 228 lflag++; 229 break; 230 231 case 'x': 232 /* bug # 1081606 */ 233 if (lflag || rflag) { 234 progerr(gettext(ERR_INCOMP2)); 235 usage(); 236 } 237 xflag++; 238 break; 239 240 case 'r': 241 if (lflag || xflag || Lflag) { 242 progerr(gettext(ERR_INCOMP0)); 243 usage(); 244 } 245 rflag++; 246 break; 247 248 case 'c': 249 ckcatg[ncatg++] = strtok(optarg, " \t\n, "); 250 while (ckcatg[ncatg] = strtok(NULL, " \t\n, ")) 251 ncatg++; 252 break; 253 254 /* added for newroot functions */ 255 case 'R': 256 if (!set_inst_root(optarg)) { 257 progerr(gettext(ERR_ROOT_CMD)); 258 exit(1); 259 } 260 break; 261 262 default: 263 usage(); 264 } 265 } 266 267 /* 268 * implement the newroot option 269 */ 270 set_PKGpaths(get_inst_root()); /* set up /var... directories */ 271 272 /* 273 * Open the install DB, if one exists. 274 */ 275 276 pkg = &argv[optind]; 277 pkgcnt = (argc - optind); 278 279 if (pkg[0] && strcmp(pkg[0], "all") == NULL) { 280 pkgcnt = 0; 281 pkg[0] = NULL; 282 } 283 284 if (pkgdir == NULL) 285 pkgdir = get_PKGLOC(); /* we need this later */ 286 287 /* convert device appropriately */ 288 if (pkghead(device)) 289 exit(1); 290 291 /* 292 * If we are to inspect a spooled package we are only interested in 293 * the pkginfo file in the spooled pkg so we skip any Reg 4 DB 294 * lookups and use the old algorithm. We have a spooled pkg if 295 * device is not NULL. 296 */ 297 298 299 look_for_installed(); 300 301 if (lflag && strcmp(pkgdir, get_PKGLOC()) == NULL) { 302 /* look at contents file */ 303 (void) snprintf(contents, sizeof (contents), 304 "%s/contents", get_PKGADM()); 305 rdcontents(); 306 307 } 308 309 /* 310 * If we are to inspect a spooled package we are only interested in 311 * the pkginfo file in the spooled pkg so we skip any Reg 4 DB 312 * lookups and use the old algorithm. We have a spooled pkg if 313 * device is not NULL. 314 */ 315 316 report(); 317 318 (void) pkghead(NULL); 319 320 return (errflg ? 1 : 0); 321 } 322 323 static void 324 report(void) 325 { 326 struct cfstat *dp, *choice; 327 int i; 328 int pkgLgth = 0; 329 int longestPkg = 0; 330 boolean_t output = B_FALSE; 331 332 for (;;) { 333 choice = (struct cfstat *)0; 334 for (dp = data; dp; dp = dp->next) { 335 pkgLgth = strlen(dp->pkginst); 336 if (pkgLgth > longestPkg) 337 longestPkg = pkgLgth; 338 } 339 for (dp = data; dp; dp = dp->next) { 340 /* get information about this package */ 341 if (dp->installed < 0) 342 continue; /* already used */ 343 if (Lflag && pkgcnt) { 344 choice = dp; 345 break; 346 } else if (!choice || 347 (strcmp(choice->pkginst, dp->pkginst) > 0)) 348 choice = dp; 349 } 350 if (!choice) 351 break; /* no more packages */ 352 353 if (pkginfo(&info, choice->pkginst, ckarch, ckvers)) { 354 choice->installed = (-1); 355 continue; 356 } 357 358 /* 359 * Confirm that the pkginfo file contains the 360 * required information. 361 */ 362 if (info.name == NULL || *(info.name) == NULL || 363 info.arch == NULL || *(info.arch) == NULL || 364 info.version == NULL || *(info.version) == NULL || 365 info.catg == NULL || *(info.catg) == NULL) { 366 progerr(gettext(ERR_BADINFO)); 367 errflg++; 368 return; 369 } 370 371 /* is it in an appropriate catgory? */ 372 if (iscatg(info.catg)) { 373 choice->installed = (-1); 374 continue; 375 } 376 377 if (!pflag && 378 /* don't include partially installed packages */ 379 (choice->partial || (info.status == PI_PARTIAL) || 380 (info.status == PI_UNKNOWN))) { 381 choice->installed = (-1); 382 continue; 383 } 384 385 if (Nflag && (info.status == PI_PRESVR4)) { 386 /* don't include preSVR4 packages */ 387 choice->installed = (-1); 388 continue; 389 } 390 391 if (!iflag && ((info.status == PI_INSTALLED) || 392 (info.status == PI_PRESVR4))) { 393 /* don't include completely installed packages */ 394 choice->installed = (-1); 395 continue; 396 } 397 398 output = B_TRUE; 399 dumpinfo(choice, longestPkg); 400 choice->installed = (-1); 401 if (pkgcnt) { 402 i = selectp(choice->pkginst); 403 if (i >= 0) 404 pkg[i] = NULL; 405 else { 406 if (qflag) { 407 errflg++; 408 return; 409 } 410 } 411 } 412 } 413 414 /* If no package matched and no output produced set error flag */ 415 if (!output) 416 errflg++; 417 418 /* verify that each package listed on command line was output */ 419 for (i = 0; i < pkgcnt; ++i) { 420 if (pkg[i]) { 421 errflg++; 422 if (!qflag) { 423 if (pflag == 1) 424 logerr(gettext(ERR_NOPINFO), pkg[i]); 425 else 426 logerr(gettext(ERR_NOINFO), pkg[i]); 427 } else 428 return; 429 } 430 } 431 (void) pkginfo(&info, NULL); /* free up all memory and open fds */ 432 } 433 434 static void 435 dumpinfo(struct cfstat *dp, int pkgLngth) 436 { 437 register int i; 438 char *pt; 439 char category[128]; 440 441 if (qflag) { 442 return; /* print nothing */ 443 } 444 445 if (rflag) { 446 (void) puts((info.basedir) ? info.basedir : "none"); 447 return; 448 } 449 450 if (Lflag) { 451 (void) puts(info.pkginst); 452 return; 453 } else if (xflag) { 454 (void) printf(XFMT, pkgLngth, pkgLngth, info.pkginst, 455 info.name); 456 457 if (info.arch || info.version) { 458 (void) printf(CFMT, pkgLngth, pkgLngth, ""); 459 if (info.arch) 460 (void) printf("(%s) ", info.arch); 461 if (info.version) 462 (void) printf("%s", info.version); 463 (void) printf("\n"); 464 } 465 return; 466 } else if (!lflag) { 467 if (info.catg) { 468 (void) sscanf(info.catg, "%[^, \t\n]", category); 469 } else if (info.status == PI_PRESVR4) { 470 (void) strcpy(category, "preSVR4"); 471 } else { 472 (void) strcpy(category, "(unknown)"); 473 } 474 (void) printf(SFMT, category, pkgLngth, pkgLngth, info.pkginst, 475 info.name); 476 return; 477 } 478 if (info.pkginst) 479 (void) printf(FMT, "PKGINST", info.pkginst); 480 if (info.name) 481 (void) printf(FMT, "NAME", info.name); 482 if (lflag && info.catg) 483 (void) printf(FMT, "CATEGORY", info.catg); 484 if (lflag && info.arch) 485 (void) printf(FMT, "ARCH", info.arch); 486 if (info.version) 487 (void) printf(FMT, "VERSION", info.version); 488 if (info.basedir) 489 (void) printf(FMT, "BASEDIR", info.basedir); 490 if (info.vendor) 491 (void) printf(FMT, "VENDOR", info.vendor); 492 493 if (info.status == PI_PRESVR4) 494 (void) printf(FMT, "STATUS", "preSVR4"); 495 else { 496 for (i = 0; parmlst[i]; ++i) { 497 if ((pt = pkgparam(info.pkginst, parmlst[i])) != NULL && 498 *pt) 499 (void) printf(FMT, parmlst[i], pt); 500 } 501 if (info.status == PI_SPOOLED) 502 (void) printf(FMT, "STATUS", gettext("spooled")); 503 else if (info.status == PI_PARTIAL) 504 (void) printf(FMT, "STATUS", 505 gettext("partially installed")); 506 else if (info.status == PI_INSTALLED) 507 (void) printf(FMT, "STATUS", 508 gettext("completely installed")); 509 else 510 (void) printf(FMT, "STATUS", gettext("(unknown)")); 511 } 512 (void) pkgparam(NULL, NULL); 513 514 if (!lflag) { 515 (void) putchar('\n'); 516 return; 517 } 518 519 if (info.status != PI_PRESVR4) { 520 if (strcmp(pkgdir, get_PKGLOC())) 521 getinfo(dp); 522 523 if (dp->spooled) 524 (void) printf( 525 gettext("%10s: %7ld spooled pathnames\n"), 526 "FILES", dp->spooled); 527 if (dp->installed) 528 (void) printf( 529 gettext("%10s: %7ld installed pathnames\n"), 530 "FILES", dp->installed); 531 if (dp->partial) 532 (void) printf( 533 gettext("%20d partially installed pathnames\n"), 534 dp->partial); 535 if (dp->shared) 536 (void) printf(gettext("%20d shared pathnames\n"), 537 dp->shared); 538 if (dp->link) 539 (void) printf(gettext("%20d linked files\n"), dp->link); 540 if (dp->dirs) 541 (void) printf(gettext("%20d directories\n"), dp->dirs); 542 if (dp->exec) 543 (void) printf(gettext("%20d executables\n"), dp->exec); 544 if (dp->setuid) 545 (void) printf( 546 gettext("%20d setuid/setgid executables\n"), 547 dp->setuid); 548 if (dp->info) 549 (void) printf( 550 gettext("%20d package information files\n"), 551 dp->info+1); /* pkgmap counts! */ 552 553 if (dp->tblks) 554 (void) printf(gettext("%20ld blocks used (approx)\n"), 555 dp->tblks); 556 } 557 (void) putchar('\n'); 558 } 559 560 static struct cfstat * 561 fpkg(char *pkginst) 562 { 563 struct cfstat *dp, *last; 564 565 dp = data; 566 last = (struct cfstat *)0; 567 while (dp) { 568 if (strcmp(dp->pkginst, pkginst) == NULL) 569 return (dp); 570 last = dp; 571 dp = dp->next; 572 } 573 dp = (struct cfstat *)calloc(1, sizeof (struct cfstat)); 574 if (!dp) { 575 progerr(gettext("no memory, malloc() failed")); 576 exit(1); 577 } 578 if (!last) 579 data = dp; 580 else 581 last->next = dp; /* link list */ 582 (void) strcpy(dp->pkginst, pkginst); 583 return (dp); 584 } 585 586 #define SEPAR ',' 587 588 static int 589 iscatg(char *list) 590 { 591 register int i; 592 register char *pt; 593 int match; 594 595 if (!ckcatg[0]) 596 return (0); /* no specification implies all packages */ 597 if (info.status == PI_PRESVR4) { 598 for (i = 0; ckcatg[i]; /* void */) { 599 if (strcmp(ckcatg[i++], "preSVR4") == NULL) 600 return (0); 601 } 602 return (1); 603 } 604 if (!list) 605 return (1); /* no category specified in pkginfo is a bug */ 606 607 match = 0; 608 do { 609 if (pt = strchr(list, ',')) 610 *pt = '\0'; 611 612 for (i = 0; ckcatg[i]; /* void */) { 613 /* bug id 1081607 */ 614 if (!strcasecmp(list, ckcatg[i++])) { 615 match++; 616 break; 617 } 618 } 619 620 if (pt) 621 *pt++ = ','; 622 if (match) 623 return (0); 624 list = pt; /* points to next one */ 625 } while (pt); 626 return (1); 627 } 628 629 static void 630 look_for_installed(void) 631 { 632 struct dirent *drp; 633 struct stat status; 634 DIR *dirfp; 635 char path[PATH_MAX]; 636 int n; 637 638 if (strcmp(pkgdir, get_PKGLOC()) == NULL && 639 (dirfp = opendir(get_PKGOLD()))) { 640 while (drp = readdir(dirfp)) { 641 if (drp->d_name[0] == '.') 642 continue; 643 n = strlen(drp->d_name); 644 if ((n > 5) && 645 strcmp(&drp->d_name[n-5], ".name") == NULL) { 646 (void) snprintf(path, sizeof (path), 647 "%s/%s", get_PKGOLD(), drp->d_name); 648 if (lstat(path, &status)) 649 continue; 650 if ((status.st_mode & S_IFMT) != S_IFREG) 651 continue; 652 drp->d_name[n-5] = '\0'; 653 if (!pkgcnt || (selectp(drp->d_name) >= 0)) 654 (void) fpkg(drp->d_name); 655 } 656 } 657 (void) closedir(dirfp); 658 } 659 660 if ((dirfp = opendir(pkgdir)) == NULL) 661 return; 662 663 while (drp = readdir(dirfp)) { 664 if (drp->d_name[0] == '.') 665 continue; 666 667 if (pkgcnt && (selectp(drp->d_name) < 0)) 668 continue; 669 670 if (!pkginfofind(path, pkgdir, drp->d_name)) 671 continue; /* doesn't appear to be a package */ 672 673 (void) fpkg(drp->d_name); 674 } 675 (void) closedir(dirfp); 676 } 677 678 static int 679 selectp(char *p) 680 { 681 register int i; 682 683 for (i = 0; i < pkgcnt; ++i) { 684 if (pkg[i] && pkgnmchk(p, pkg[i], 1) == 0) 685 return (i); 686 } 687 return (-1); 688 } 689 690 static void 691 rdcontents(void) 692 { 693 VFP_T *vfp; 694 struct cfstat *dp; 695 struct pinfo *pinfo; 696 int n; 697 698 if (vfpOpen(&vfp, contents, "r", VFP_NEEDNOW) != 0) { 699 progerr(gettext("unable to open \"%s\" for reading"), contents); 700 exit(1); 701 } 702 703 /* check the contents file to look for referenced packages */ 704 while ((n = srchcfile(&entry, "*", vfp, (VFP_T *)NULL)) > 0) { 705 for (pinfo = entry.pinfo; pinfo; pinfo = pinfo->next) { 706 /* see if entry is used by indicated packaged */ 707 if (pkgcnt && (selectp(pinfo->pkg) < 0)) 708 continue; 709 710 dp = fpkg(pinfo->pkg); 711 pkgusage(dp, &entry); 712 713 if (entry.npkgs > 1) 714 dp->shared++; 715 716 /* 717 * Only objects specifically tagged with '!' event 718 * character are considered "partial", everything 719 * else is considered "installed" (even server 720 * objects). 721 */ 722 switch (pinfo->status) { 723 case '!' : 724 dp->partial++; 725 break; 726 default : 727 dp->installed++; 728 break; 729 } 730 } 731 } 732 if (n < 0) { 733 char *errstr = getErrstr(); 734 progerr(gettext("bad entry read in contents file")); 735 logerr(gettext("pathname: %s"), 736 (entry.path && *entry.path) ? entry.path : "Unknown"); 737 logerr(gettext("problem: %s"), 738 (errstr && *errstr) ? errstr : "Unknown"); 739 exit(1); 740 } 741 742 (void) vfpClose(&vfp); 743 } 744 745 static void 746 getinfo(struct cfstat *dp) 747 { 748 int n; 749 char pkgmap[MAXPATHLEN]; 750 VFP_T *vfp; 751 752 (void) snprintf(pkgmap, sizeof (pkgmap), 753 "%s/%s/pkgmap", pkgdir, dp->pkginst); 754 755 if (vfpOpen(&vfp, pkgmap, "r", VFP_NEEDNOW) != 0) { 756 progerr(gettext("unable open \"%s\" for reading"), pkgmap); 757 exit(1); 758 } 759 760 dp->spooled = 1; /* pkgmap counts! */ 761 762 while ((n = gpkgmapvfp(&entry, vfp)) > 0) { 763 dp->spooled++; 764 pkgusage(dp, &entry); 765 } 766 767 if (n < 0) { 768 char *errstr = getErrstr(); 769 progerr(gettext("bad entry read in pkgmap file")); 770 logerr(gettext("pathname: %s"), 771 (entry.path && *entry.path) ? entry.path : "Unknown"); 772 logerr(gettext("problem: %s"), 773 (errstr && *errstr) ? errstr : "Unknown"); 774 exit(1); 775 } 776 777 (void) vfpClose(&vfp); 778 } 779 780 static void 781 pkgusage(struct cfstat *dp, struct cfent *pentry) 782 { 783 if (pentry->ftype == 'i') { 784 dp->info++; 785 return; 786 } else if (pentry->ftype == 'l') { 787 dp->link++; 788 } else { 789 if ((pentry->ftype == 'd') || (pentry->ftype == 'x')) 790 dp->dirs++; 791 792 /* Only collect mode stats if they would be meaningful. */ 793 if (pentry->ainfo.mode != BADMODE) { 794 if (pentry->ainfo.mode & 06000) 795 dp->setuid++; 796 if (!strchr("dxcbp", pentry->ftype) && 797 (pentry->ainfo.mode & 0111)) 798 dp->exec++; 799 } 800 } 801 802 if (strchr("ifve", pentry->ftype)) 803 dp->tblks += nblock(pentry->cinfo.size); 804 } 805 806 static void 807 usage(void) 808 { 809 char *prog = get_prog_name(); 810 811 /* bug # 1081606 */ 812 (void) fprintf(stderr, gettext(ERR_USAGE), prog, prog); 813 814 exit(1); 815 } 816 817 void 818 quit(int retval) 819 { 820 exit(retval); 821 } 822