xref: /illumos-gate/usr/src/cmd/svr4pkg/pkginfo/pkginfo.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 (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
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 *
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
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
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
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
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
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
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
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
811 quit(int retval)
812 {
813 	exit(retval);
814 }
815