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