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 (!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
dumpinfo(struct cfstat * dp,int pkgLngth)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 *
fpkg(char * pkginst)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
iscatg(char * list)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
look_for_installed(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
selectp(char * p)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
rdcontents(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
getinfo(struct cfstat * dp)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
pkgusage(struct cfstat * dp,struct cfent * pentry)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
usage(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
quit(int retval)762 quit(int retval)
763 {
764 exit(retval);
765 }
766