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