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