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
main(int argc,char ** argv)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
report(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
dumpinfo(struct cfstat * dp,int pkgLngth)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 *
fpkg(char * pkginst)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
iscatg(char * list)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
look_for_installed(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
selectp(char * p)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
rdcontents(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
getinfo(struct cfstat * dp)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
pkgusage(struct cfstat * dp,struct cfent * pentry)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
usage(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
quit(int retval)811 quit(int retval)
812 {
813 exit(retval);
814 }
815