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