pkginfo.c revision 9781:ccf49524d5dc
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
60extern char	*pkgdir;
61extern 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
97static char	*device = NULL;
98static char	*parmlst[] = {
99	"DESC", "PSTAMP", "INSTDATE", "VSTOCK", "SERIALNUM", "HOTLINE",
100	"EMAIL", NULL
101};
102
103static char	contents[PATH_MAX];
104static int	errflg = 0;
105static int	qflag = 0;
106static int	iflag = -1;
107static int	pflag = -1;
108static int	lflag = 0;
109static int	Lflag = 0;
110static int	Nflag = 0;
111static int	xflag = 0;
112static int	rflag = 0; 		/* bug # 1081606 */
113static struct cfent	entry;
114static char	**pkg = NULL;
115static int	pkgcnt = 0;
116static char	*ckcatg[MAXCATG] = {NULL};
117static int	ncatg = 0;
118static char	*ckvers = NULL;
119static char	*ckarch = NULL;
120
121static 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;
135static struct pkginfo info;
136
137static struct	cfstat *fpkg(char *pkginst);
138static int	iscatg(char *list);
139static int	selectp(char *p);
140static void	usage(void), look_for_installed(void),
141		report(void), rdcontents(void);
142static void	pkgusage(struct cfstat *dp, struct cfent *pentry);
143static void	getinfo(struct cfstat *dp);
144static void	dumpinfo(struct cfstat *dp, int pkgLngth);
145
146int
147main(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
323static void
324report(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
434static void
435dumpinfo(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
560static struct cfstat *
561fpkg(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
588static int
589iscatg(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
629static void
630look_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
678static int
679selectp(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
690static void
691rdcontents(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
745static void
746getinfo(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
780static void
781pkgusage(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
806static void
807usage(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
817void
818quit(int retval)
819{
820	exit(retval);
821}
822