OpenSolaris_b135/cmd/svr4pkg/pkgchk/main.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */


#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pkginfo.h>
#include <pkglocs.h>
#include <sys/types.h>
#include <pkgstrct.h>
#include <pkgtrans.h>
#include <locale.h>
#include <libintl.h>
#include <pkglib.h>
#include <libadm.h>
#include <libinst.h>

#define	MAXPATHS	1024

#define	MSG_CHK_STRM	"Checking uninstalled stream format package " \
				"<%s> from <%s>\n"
#define	MSG_CHK_DIR	"Checking uninstalled directory format package " \
				"<%s> from <%s>\n"
#define	MSG_NOTROOT	"NOTE: \"root\" permission may be required to " \
				"validate all objects in the client filesystem."
#define	MSG_CONT	"Continuing."

#define	WRN_F_SPOOL	"WARNING: %s is spooled. Ignoring \"f\" argument"

#define	ERR_ROOT_SET	"Could not set install root from the environment."
#define	ERR_ROOT_CMD	"Command line install root contends with environment."
#define	ERR_IOPEN	"unable to open input file <%s>"
#define	ERR_IEMPTY	"no pathnames in file specified by -i option"
#define	ERR_POPTION	"no pathname included with -p option"
#define	ERR_PARTIAL_POPTION	"no pathname included with -P option"
#define	ERR_MAXPATHS	"too many pathnames in option list (limit is %d)"
#define	ERR_NOTROOT	"You must be \"root\" for \"%s -f\" to" \
					"execute properly."
#define	ERR_SEL_PKG "No packages selected for verification."
#define	ERR_CAT_LNGTH "The category argument exceeds the SVr4 ABI\n" \
		"        defined maximum supported length of 16 characters."
#define	ERR_CAT_FND "Category argument <%s> cannot be found."
#define	ERR_CAT_INV "Category argument <%s> is invalid."
#define	ERR_TOO_MANY "too many pathnames in list, limit is %d"
#define	ERR_PATHS_INVALID "Pathnames in %s are not valid."
#define	ERR_MKDIR "unable to make directory <%s>"
#define	ERR_USAGE	"usage:\n" \
		"\t%s [-l|vqacnxf] [-R rootdir] [-p path[, ...] | " \
		"-P path[, ...]]\n" \
		"\t\t[-i file] [options]\n" \
		"\t%s -d device [-f][-l|v] [-p path[, ...] | " \
		"-P path[, ...]]\n" \
		"\t\t[-V ...] [-M] [-i file] [-Y category[, ...] | " \
		"pkginst [...]]\n" \
		"\twhere options may include ONE of the " \
		"following:\n " \
		"\t\t-m pkgmap [-e envfile]\n" \
		"\t\tpkginst [...]\n" \
		"\t\t-Y category[, ...]\n"

#define	LINK	1

char	**pkg = NULL;
int	pkgcnt = 0;
char	*basedir;
char	*pathlist[MAXPATHS], *ppathlist[MAXPATHS], pkgspool[PATH_MAX];
short	used[MAXPATHS];
short	npaths;
struct cfent **eptlist;

int	aflag = (-1);
int	cflag = (-1);
int	vflag = 0;
int	nflag = 0;
int	lflag = 0;
int	Lflag = 0;
int	fflag = 0;
int	xflag = 0;
int	qflag = 0;
int	Rflag = 0;
int	dflag = 0;
char 	*device;

char	*uniTmp;

static char	*mapfile,
		*spooldir,
		*tmpdir,
		*envfile;
static int	errflg = 0;
static int	map_client = 1;

void	quit(int);
static void	setpathlist(char *);
static void	usage(void);

extern	char	**environ;
extern	char	*pkgdir;

/* checkmap.c */
extern int	checkmap(int, int, char *, char *, char *, char *, int);
/* scriptvfy.c */
extern int	checkscripts(char *inst_dir, int silent);

int
main(int argc, char *argv[])
{
	int	pkgfmt = 0;	/* Makes more sense as a pointer, but */
				/*	18N is compromised. */
	char	file[PATH_MAX+1],
		*abi_sym_ptr,
		*vfstab_file = NULL;
	char *all_pkgs[4] = {"all", NULL};
	char **category = NULL;
	char *catg_arg = NULL;
	int	c;
	int	n = 0;
	char	*prog,
		*Rvalue,
		*dvalue;
	int dbcreate = 0;
	int pathtype;

	/* initialize locale mechanism */

	(void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);

	/* determine program name */

	prog = set_prog_name(argv[0]);

	/* establish installation root directory */

	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
		progerr(gettext(ERR_ROOT_SET));
		quit(1);
	}

	/* check if not ABI compliant mode */
	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
		set_nonABI_symlinks();
	}

	/* bugId 4012147 */
	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
		map_client = 0;

	while ((c = getopt(argc, argv, "Y:R:e:p:d:nLli:vaV:Mm:cqxfQP:?"))
			!= EOF) {
		switch (c) {
		case 'p':
			pathlist[npaths] = strtok(optarg, " , ");
			if (pathlist[npaths++] == NULL) {
				progerr(gettext(ERR_POPTION));
				quit(1);
			}
			while (pathlist[npaths] = strtok(NULL, " , ")) {
				if (npaths++ >= MAXPATHS) {
					progerr(gettext(ERR_MAXPATHS),
						MAXPATHS);
					quit(1);
				}
			}
			break;

		case 'd':
			dvalue = optarg;
			dflag = 1;
			break;

		case 'n':
			nflag++;
			break;

		case 'M':
			map_client = 0;
			break;

		/*
		 * Allow admin to establish the client filesystem using a
		 * vfstab-like file of stable format.
		 */
		case 'V':
			vfstab_file = flex_device(optarg, 2);
			map_client = 1;
			break;

		case 'f':
			if (getuid()) {
				progerr(gettext(ERR_NOTROOT), prog);
				quit(1);
			}
			fflag++;
			break;

		case 'i':
			setpathlist(optarg);
			break;

		case 'v':
			vflag++;
			break;

		case 'l':
			lflag++;
			break;

		case 'L':
			Lflag++;
			break;

		case 'x':
			if (aflag < 0)
				aflag = 0;
			if (cflag < 0)
				cflag = 0;
			xflag++;
			break;

		case 'q':
			qflag++;
			break;

		case 'a':
			if (cflag < 0)
				cflag = 0;
			aflag = 1;
			break;

		case 'c':
			if (aflag < 0)
				aflag = 0;
			cflag = 1;
			break;

		case 'e':
			envfile = optarg;
			break;

		case 'm':
			mapfile = optarg;
			break;

		case 'R':
			Rvalue = optarg;
			Rflag = 1;
			break;

		case 'Y':
			catg_arg = strdup(optarg);

			if ((category = get_categories(catg_arg)) == NULL) {
				progerr(gettext(ERR_CAT_INV), catg_arg);
				quit(1);
			} else if (is_not_valid_length(category)) {
				progerr(gettext(ERR_CAT_LNGTH));
				quit(1);
			}
			break;

		case 'Q':
			dbcreate++;
			break;

		case 'P':
			ppathlist[npaths] = strtok(optarg, " , ");
			if ((ppathlist[npaths] == NULL) ||
			    (ppathlist[npaths][0] == '-')) {
				progerr(gettext(ERR_PARTIAL_POPTION));
				quit(1);
			}
			npaths++;
			while (ppathlist[npaths] = strtok(NULL, " , ")) {
				if (npaths++ >= MAXPATHS) {
					progerr(gettext(ERR_MAXPATHS),
						MAXPATHS);
					quit(1);
				}
			}
			break;

		default:
			usage();
			/*NOTREACHED*/
			/*
			 * Although usage() calls a noreturn function,
			 * needed to add return (1);  so that main() would
			 * pass compilation checks. The statement below
			 * should never be executed.
			 */
			return (1);
		}
	}

	/* Check for incompatible options */
	if (dflag && Rflag)
		usage();

	/* Check for root dir and device dir if set */
	if (Rflag) {
		if (!set_inst_root(Rvalue)) {
			progerr(gettext(ERR_ROOT_CMD));
			quit(1);
		}
	}

	if (dflag)
		device = flex_device(dvalue, 1);

	if (lflag || Lflag) {
		/* we're only supposed to list information */
		if ((cflag >= 0) || (aflag >= 0) ||
		qflag || xflag || fflag || nflag || vflag)
			usage();
	}

	set_PKGpaths(get_inst_root());

	if (catg_arg != NULL && device == NULL) {
		if (argc - optind) {
			usage();
		}
		pkg = gpkglist(pkgdir, all_pkgs, category);
		if (pkg == NULL) {
			progerr(gettext(ERR_CAT_FND), catg_arg);
			quit(1);
		} else {
			for (pkgcnt = 0; pkg[pkgcnt] != NULL; pkgcnt++);
		}
	} else if (catg_arg != NULL && optind < argc) {
		usage();
	} else {
		pkg = &argv[optind];
		pkgcnt = (argc - optind);
	}

	/* read the environment for the pkgserver */
	pkgserversetmode(DEFAULTMODE);

	environ = NULL;		/* Sever the parent environment. */

	if (vcfile() == 0) {
		quit(99);
	}

	errflg = 0;
	if (mapfile) {
		/* check for incompatible options */
		if (device || pkgcnt)
			usage();
		put_path_params();	/* Restore what's needed. */

		/* send pathtype if partial path */
		pathtype = (ppathlist[0] != NULL) ? 1 : 0;
		if (checkmap(0, (device != NULL), mapfile, envfile, NULL,
		    NULL, pathtype))
			errflg++;
	} else if (device) {
		/* check for incompatible options */
		if ((cflag >= 0) || (aflag >= 0))
			usage();
		if (qflag || xflag || nflag || envfile)
			usage();
		tmpdir = NULL;
		if ((spooldir = devattr(device, "pathname")) == NULL)
			spooldir = device;
		if (isdir(spooldir)) {
			tmpdir = spooldir = qstrdup(tmpnam(NULL));
			if (fflag) {
				logerr(gettext(WRN_F_SPOOL), *pkg);
				fflag = 0;
			}
			if (mkdir(spooldir, 0755)) {
				progerr(gettext(ERR_MKDIR), spooldir);
				quit(99);
			}
			if (n = pkgtrans(device, spooldir, pkg, PT_SILENT,
				NULL, NULL))
				quit(n);
			if (catg_arg != NULL)
				pkg = gpkglist(spooldir, all_pkgs, category);
			else
				pkg = gpkglist(spooldir, all_pkgs, NULL);
			pkgfmt = 0;
		} else {
			if (catg_arg != NULL)
				pkg = gpkglist(spooldir,
					pkgcnt ? pkg : all_pkgs, category);
			else
				pkg = gpkglist(spooldir,
					pkgcnt ? pkg : all_pkgs, NULL);
			pkgfmt = 1;
		}

		/*
		 * At this point pkg[] is the list of packages to check. They
		 * are in directory format in spooldir.
		 */
		if (pkg == NULL) {
			if (catg_arg != NULL) {
				progerr(gettext(ERR_CAT_FND), catg_arg);
				quit(1);
			} else {
				progerr(gettext(ERR_SEL_PKG));
				quit(1);
			}
		}

		aflag = 0;

		for (n = 0; pkg[n]; n++) {
			char locenv[PATH_MAX];

	/*
	 * *********************************************************************
	 * this feature is removed starting with Solaris 10 - there is no built
	 * in list of packages that should be run "the old way"
	 * *********************************************************************
	 */
#ifdef	ALLOW_EXCEPTION_PKG_LIST
			/* Until 2.9, set it from the execption list */
			if (exception_pkg(pkg[n], LINK))
				set_nonABI_symlinks();
#endif

			if (pkgfmt)
				(void) printf(
					gettext(MSG_CHK_DIR), pkg[n], device);
			else
				(void) printf(
					gettext(MSG_CHK_STRM), pkg[n], device);

			(void) snprintf(pkgspool, sizeof (pkgspool),
				"%s/%s", spooldir, pkg[n]);
			(void) snprintf(file, sizeof (file),
				"%s/install", pkgspool);
			/* Here we check the install scripts. */
			(void) printf(
				gettext("## Checking control scripts.\n"));
			(void) checkscripts(file, 0);
			/* Verify consistency with the pkgmap. */
			(void) printf(
				gettext("## Checking package objects.\n"));
			(void) snprintf(file, sizeof (file),
				"%s/pkgmap", pkgspool);
			(void) snprintf(locenv, sizeof (locenv),
				"%s/pkginfo", pkgspool);
			envfile = locenv;

			/*
			 * NOTE : checkmap() frees the environ data and
			 * pointer when it's through with them.
			 */
			if (checkmap(0, (device != NULL), file, envfile,
					pkg[n], NULL, 0))
				errflg++;
			(void) printf(
				gettext("## Checking is complete.\n"));
		}
	} else {
		if (envfile)
			usage();

		put_path_params();	/* Restore what's needed. */

		/*
		 * If this is a check of a client of some sort, we'll need to
		 * mount up the client's filesystems. If the caller isn't
		 * root, this may not be possible.
		 */
		if (is_an_inst_root()) {
			if (getuid()) {
				logerr(gettext(MSG_NOTROOT));
				logerr(gettext(MSG_CONT));
			} else {
				if (get_mntinfo(map_client, vfstab_file))
					map_client = 0;
				if (map_client)
					mount_client();
			}
		}

		(void) snprintf(file, sizeof (file),
			"%s/contents", get_PKGADM());
		if (ppathlist[0] != NULL) {
			for (n = 0; ppathlist[n]; n++) {
				if (checkmap(1, (device != NULL), file, NULL,
						NULL, ppathlist[n], 1))
					errflg++;
			}
		} else if (pkg[0] != NULL) {
				if (checkmap(1, (device != NULL), file, NULL,
					pkg[0], NULL, 0)) {
					errflg++;
				}
		} else {
			if (checkmap(1, (device != NULL), file, NULL,
					NULL, NULL, 0)) {
				errflg++;
			}
		}

		if (map_client) {
			unmount_client();
		}
	}
	quit(errflg ? 1 : 0);
	/* LINTED: no return */
}

static void
setpathlist(char *file)
{
	int fd;
	struct stat st;
	FILE *fplist;
	char pathname[PATH_MAX];
	/*
	 * This trap laid to catch a mismatch between the declaration above and
	 * the hard-coded constant in the fscanf below
	 */
#if PATH_MAX != 1024
#error "PATH_MAX changed, so we have a bug to fix"
#endif

	if (strcmp(file, "-") == 0) {
		fplist = stdin;
	} else {
		if ((fd = open(file, O_RDONLY)) == -1) {
			progerr(gettext(ERR_IOPEN), file);
			quit(1);
		}
		if (fstat(fd, &st) == -1) {
			progerr(gettext(ERR_IOPEN), file);
			quit(1);
		}
		if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
			progerr(gettext(ERR_PATHS_INVALID), file);
			quit(1);
		}
		if ((fplist = fdopen(fd, "r")) == NULL) {
			progerr(gettext(ERR_IOPEN), file);
			quit(1);
		}
	}
	while (fscanf(fplist, "%1024s", pathname) == 1) {
		if (*pathname == '\0') {
			progerr(gettext(ERR_PATHS_INVALID), file);
			quit(1);
		}
		pathlist[npaths] = qstrdup(pathname);
		if (npaths++ > MAXPATHS) {
			progerr(gettext(ERR_TOO_MANY), MAXPATHS);
			quit(1);
		}
	}
	if (npaths == 0) {
		progerr(gettext(ERR_IEMPTY));
		quit(1);
	}
	(void) fclose(fplist);
}

void
quit(int n)
{
	/* cleanup any temporary directories */
	(void) chdir("/");
	if (tmpdir != NULL) {
		(void) rrmdir(tmpdir);
		free(tmpdir);
		tmpdir = NULL;
	}
	(void) pkghead(NULL);
	exit(n);
	/*NOTREACHED*/
}

static void
usage(void)
{
	char *prog = get_prog_name();

	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
	quit(1);
	/*NOTREACHED*/
}