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 (!iflag && (info.status == PI_INSTALLED)) { 381 /* don't include completely installed packages */ 382 choice->installed = (-1); 383 continue; 384 } 385 386 output = B_TRUE; 387 dumpinfo(choice, longestPkg); 388 choice->installed = (-1); 389 if (pkgcnt) { 390 i = selectp(choice->pkginst); 391 if (i >= 0) 392 pkg[i] = NULL; 393 else { 394 if (qflag) { 395 errflg++; 396 return; 397 } 398 } 399 } 400 } 401 402 /* If no package matched and no output produced set error flag */ 403 if (!output) 404 errflg++; 405 406 /* verify that each package listed on command line was output */ 407 for (i = 0; i < pkgcnt; ++i) { 408 if (pkg[i]) { 409 errflg++; 410 if (!qflag) { 411 if (pflag == 1) 412 logerr(gettext(ERR_NOPINFO), pkg[i]); 413 else 414 logerr(gettext(ERR_NOINFO), pkg[i]); 415 } else 416 return; 417 } 418 } 419 (void) pkginfo(&info, NULL); /* free up all memory and open fds */ 420 } 421 422 static void 423 dumpinfo(struct cfstat *dp, int pkgLngth) 424 { 425 register int i; 426 char *pt; 427 char category[128]; 428 429 if (qflag) { 430 return; /* print nothing */ 431 } 432 433 if (rflag) { 434 (void) puts((info.basedir) ? info.basedir : "none"); 435 return; 436 } 437 438 if (Lflag) { 439 (void) puts(info.pkginst); 440 return; 441 } else if (xflag) { 442 (void) printf(XFMT, pkgLngth, pkgLngth, info.pkginst, 443 info.name); 444 445 if (info.arch || info.version) { 446 (void) printf(CFMT, pkgLngth, pkgLngth, ""); 447 if (info.arch) 448 (void) printf("(%s) ", info.arch); 449 if (info.version) 450 (void) printf("%s", info.version); 451 (void) printf("\n"); 452 } 453 return; 454 } else if (!lflag) { 455 if (info.catg) { 456 (void) sscanf(info.catg, "%[^, \t\n]", category); 457 } else { 458 (void) strcpy(category, "(unknown)"); 459 } 460 (void) printf(SFMT, category, pkgLngth, pkgLngth, info.pkginst, 461 info.name); 462 return; 463 } 464 if (info.pkginst) 465 (void) printf(FMT, "PKGINST", info.pkginst); 466 if (info.name) 467 (void) printf(FMT, "NAME", info.name); 468 if (lflag && info.catg) 469 (void) printf(FMT, "CATEGORY", info.catg); 470 if (lflag && info.arch) 471 (void) printf(FMT, "ARCH", info.arch); 472 if (info.version) 473 (void) printf(FMT, "VERSION", info.version); 474 if (info.basedir) 475 (void) printf(FMT, "BASEDIR", info.basedir); 476 if (info.vendor) 477 (void) printf(FMT, "VENDOR", info.vendor); 478 479 for (i = 0; parmlst[i]; ++i) { 480 if ((pt = pkgparam(info.pkginst, parmlst[i])) != NULL && *pt) 481 (void) printf(FMT, parmlst[i], pt); 482 } 483 if (info.status == PI_SPOOLED) 484 (void) printf(FMT, "STATUS", gettext("spooled")); 485 else if (info.status == PI_PARTIAL) 486 (void) printf(FMT, "STATUS", 487 gettext("partially installed")); 488 else if (info.status == PI_INSTALLED) 489 (void) printf(FMT, "STATUS", 490 gettext("completely installed")); 491 else 492 (void) printf(FMT, "STATUS", gettext("(unknown)")); 493 494 (void) pkgparam(NULL, NULL); 495 496 if (!lflag) { 497 (void) putchar('\n'); 498 return; 499 } 500 501 if (strcmp(pkgdir, get_PKGLOC())) 502 getinfo(dp); 503 504 if (dp->spooled) 505 (void) printf(gettext("%10s: %7ld spooled pathnames\n"), 506 "FILES", dp->spooled); 507 if (dp->installed) 508 (void) printf(gettext("%10s: %7ld installed pathnames\n"), 509 "FILES", dp->installed); 510 if (dp->partial) 511 (void) printf(gettext("%20d partially installed pathnames\n"), 512 dp->partial); 513 if (dp->shared) 514 (void) printf(gettext("%20d shared pathnames\n"), dp->shared); 515 if (dp->link) 516 (void) printf(gettext("%20d linked files\n"), dp->link); 517 if (dp->dirs) 518 (void) printf(gettext("%20d directories\n"), dp->dirs); 519 if (dp->exec) 520 (void) printf(gettext("%20d executables\n"), dp->exec); 521 if (dp->setuid) 522 (void) printf(gettext("%20d setuid/setgid executables\n"), 523 dp->setuid); 524 if (dp->info) 525 (void) printf(gettext("%20d package information files\n"), 526 dp->info+1); /* pkgmap counts! */ 527 528 if (dp->tblks) 529 (void) printf(gettext("%20ld blocks used (approx)\n"), 530 dp->tblks); 531 532 (void) putchar('\n'); 533 } 534 535 static struct cfstat * 536 fpkg(char *pkginst) 537 { 538 struct cfstat *dp, *last; 539 540 dp = data; 541 last = (struct cfstat *)0; 542 while (dp) { 543 if (strcmp(dp->pkginst, pkginst) == NULL) 544 return (dp); 545 last = dp; 546 dp = dp->next; 547 } 548 dp = (struct cfstat *)calloc(1, sizeof (struct cfstat)); 549 if (!dp) { 550 progerr(gettext("no memory, malloc() failed")); 551 exit(1); 552 } 553 if (!last) 554 data = dp; 555 else 556 last->next = dp; /* link list */ 557 (void) strcpy(dp->pkginst, pkginst); 558 return (dp); 559 } 560 561 #define SEPAR ',' 562 563 static int 564 iscatg(char *list) 565 { 566 register int i; 567 register char *pt; 568 int match; 569 570 if (!ckcatg[0]) 571 return (0); /* no specification implies all packages */ 572 573 if (!list) 574 return (1); /* no category specified in pkginfo is a bug */ 575 576 match = 0; 577 do { 578 if (pt = strchr(list, ',')) 579 *pt = '\0'; 580 581 for (i = 0; ckcatg[i]; /* void */) { 582 /* bug id 1081607 */ 583 if (!strcasecmp(list, ckcatg[i++])) { 584 match++; 585 break; 586 } 587 } 588 589 if (pt) 590 *pt++ = ','; 591 if (match) 592 return (0); 593 list = pt; /* points to next one */ 594 } while (pt); 595 return (1); 596 } 597 598 static void 599 look_for_installed(void) 600 { 601 struct dirent *drp; 602 struct stat status; 603 DIR *dirfp; 604 char path[PATH_MAX]; 605 606 if ((dirfp = opendir(pkgdir)) == NULL) 607 return; 608 609 while (drp = readdir(dirfp)) { 610 if (drp->d_name[0] == '.') 611 continue; 612 613 if (pkgcnt && (selectp(drp->d_name) < 0)) 614 continue; 615 616 if (!pkginfofind(path, pkgdir, drp->d_name)) 617 continue; /* doesn't appear to be a package */ 618 619 (void) fpkg(drp->d_name); 620 } 621 (void) closedir(dirfp); 622 } 623 624 static int 625 selectp(char *p) 626 { 627 register int i; 628 629 for (i = 0; i < pkgcnt; ++i) { 630 if (pkg[i] && pkgnmchk(p, pkg[i], 1) == 0) 631 return (i); 632 } 633 return (-1); 634 } 635 636 static void 637 rdcontents(void) 638 { 639 struct cfstat *dp; 640 struct pinfo *pinfo; 641 int n; 642 PKGserver server; 643 644 if (!socfile(&server, B_TRUE) || 645 pkgopenfilter(server, pkgcnt == 1 ? pkg[0] : NULL) != 0) 646 exit(1); 647 648 /* check the contents file to look for referenced packages */ 649 while ((n = srchcfile(&entry, "*", server)) > 0) { 650 for (pinfo = entry.pinfo; pinfo; pinfo = pinfo->next) { 651 /* see if entry is used by indicated packaged */ 652 if (pkgcnt && (selectp(pinfo->pkg) < 0)) 653 continue; 654 655 dp = fpkg(pinfo->pkg); 656 pkgusage(dp, &entry); 657 658 if (entry.npkgs > 1) 659 dp->shared++; 660 661 /* 662 * Only objects specifically tagged with '!' event 663 * character are considered "partial", everything 664 * else is considered "installed" (even server 665 * objects). 666 */ 667 switch (pinfo->status) { 668 case '!' : 669 dp->partial++; 670 break; 671 default : 672 dp->installed++; 673 break; 674 } 675 } 676 } 677 if (n < 0) { 678 char *errstr = getErrstr(); 679 progerr(gettext("bad entry read in contents file")); 680 logerr(gettext("pathname: %s"), 681 (entry.path && *entry.path) ? entry.path : "Unknown"); 682 logerr(gettext("problem: %s"), 683 (errstr && *errstr) ? errstr : "Unknown"); 684 exit(1); 685 } 686 pkgcloseserver(server); 687 } 688 689 static void 690 getinfo(struct cfstat *dp) 691 { 692 int n; 693 char pkgmap[MAXPATHLEN]; 694 VFP_T *vfp; 695 696 (void) snprintf(pkgmap, sizeof (pkgmap), 697 "%s/%s/pkgmap", pkgdir, dp->pkginst); 698 699 if (vfpOpen(&vfp, pkgmap, "r", VFP_NEEDNOW) != 0) { 700 progerr(gettext("unable open \"%s\" for reading"), pkgmap); 701 exit(1); 702 } 703 704 dp->spooled = 1; /* pkgmap counts! */ 705 706 while ((n = gpkgmapvfp(&entry, vfp)) > 0) { 707 dp->spooled++; 708 pkgusage(dp, &entry); 709 } 710 711 if (n < 0) { 712 char *errstr = getErrstr(); 713 progerr(gettext("bad entry read in pkgmap file")); 714 logerr(gettext("pathname: %s"), 715 (entry.path && *entry.path) ? entry.path : "Unknown"); 716 logerr(gettext("problem: %s"), 717 (errstr && *errstr) ? errstr : "Unknown"); 718 exit(1); 719 } 720 721 (void) vfpClose(&vfp); 722 } 723 724 static void 725 pkgusage(struct cfstat *dp, struct cfent *pentry) 726 { 727 if (pentry->ftype == 'i') { 728 dp->info++; 729 return; 730 } else if (pentry->ftype == 'l') { 731 dp->link++; 732 } else { 733 if ((pentry->ftype == 'd') || (pentry->ftype == 'x')) 734 dp->dirs++; 735 736 /* Only collect mode stats if they would be meaningful. */ 737 if (pentry->ainfo.mode != BADMODE) { 738 if (pentry->ainfo.mode & 06000) 739 dp->setuid++; 740 if (!strchr("dxcbp", pentry->ftype) && 741 (pentry->ainfo.mode & 0111)) 742 dp->exec++; 743 } 744 } 745 746 if (strchr("ifve", pentry->ftype)) 747 dp->tblks += nblock(pentry->cinfo.size); 748 } 749 750 static void 751 usage(void) 752 { 753 char *prog = get_prog_name(); 754 755 /* bug # 1081606 */ 756 (void) fprintf(stderr, gettext(ERR_USAGE), prog, prog); 757 758 exit(1); 759 } 760 761 void 762 quit(int retval) 763 { 764 exit(retval); 765 } 766