OpenSolaris_b135/cmd/sgs/crle/common/print.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.
 */

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/mman.h>
#include	<fcntl.h>
#include	<stdio.h>
#include	<string.h>
#include	<unistd.h>
#include	<errno.h>
#include	<limits.h>
#include	<alloca.h>
#include	"sgs.h"
#include	"rtc.h"
#include	"conv.h"
#include	"_crle.h"
#include	"msg.h"


/*
 * Display the command line required to regenerate the configuration file.
 *
 * Under normal mode the command is printed on one line to make it more
 * available for grep(1) use.  Under verbose mode the command is separated
 * into each argument (a little more readable perhaps when the arguments are
 * numerous of have long pathnames).
 *
 * Note that for version 1 configuration files we never used to generate any
 * command-line information, and as the attempt to do so is only a best effort
 * don't bother printing anything.
 */
static void
printcmd(Crle_desc *crle, Rtc_head * head, APlist *cmdline)
{
	Aliste		idx, lidx;
	const char	*fmto, *fmtb, *fmtm, *fmte;
	char		*cmd;
	int		output = 0;

	if (crle->c_flags & CRLE_VERBOSE) {
		fmto = MSG_INTL(MSG_DMP_CMD_ONE_V);
		fmtb = MSG_INTL(MSG_DMP_CMD_BGN_V);
		fmtm = MSG_INTL(MSG_DMP_CMD_MID_V);
		fmte = MSG_INTL(MSG_DMP_CMD_END_V);

	} else if (head->ch_version > RTC_VER_ONE) {
		fmto = MSG_INTL(MSG_DMP_CMD_ONE);
		fmtb = MSG_INTL(MSG_DMP_CMD_BGN);
		fmtm = MSG_INTL(MSG_DMP_CMD_MID);
		fmte = MSG_INTL(MSG_DMP_CMD_END);

	} else {
		(void) printf(MSG_ORIG(MSG_STR_NL));
		return;
	}

	(void) printf(MSG_INTL(MSG_DMP_CMD_TITLE));

	lidx = aplist_nitems(cmdline) - 1;
	for (APLIST_TRAVERSE(cmdline, idx, cmd)) {
		if (output++ == 0) {
			if (idx < lidx)
				(void) printf(fmtb, cmd);
			else
				(void) printf(fmto, cmd);
		} else {
			if (idx < lidx)
				(void) printf(fmtm, cmd);
			else
				(void) printf(fmte, cmd);
		}
	}
}

/*
 * Establish the argument required to generate the associated object.
 */
static const char *
getformat(Half flags)
{
	if (flags & RTC_OBJ_ALTER) {
		if (flags & RTC_OBJ_DUMP) {
			if (flags & RTC_OBJ_GROUP)
				return (MSG_ORIG(MSG_CMD_DUMPGRP));
			else
				return (MSG_ORIG(MSG_CMD_DUMPIND));
		} else {
			if (flags & RTC_OBJ_OPTINAL)
				return (MSG_ORIG(MSG_CMD_OPTIONAL));
			else
				return (MSG_ORIG(MSG_CMD_ALTER));
		}
	} else {
		if (flags & RTC_OBJ_GROUP)
			return (MSG_ORIG(MSG_CMD_GRP));
		else
			return (MSG_ORIG(MSG_CMD_IND));
	}
}

/*
 * Fabricate a system default search path.  If an update is requested, and
 * new search paths are specified while no configuration file exists, or if a
 * configuration file does exist but doesn't specify this particular search
 * path, create any system defaults.  The intent is to allow
 * "crle -u -l/usr/local/lib" and have this append the search path to the
 * system default, rather than have the user have to determine and specify
 * this default themselves.
 */
static int
fablib(Crle_desc * crle, int flag)
{
	const char	*path;
	char		**list;

	switch (flag) {
	case CRLE_EDLIB:
#if M_CLASS == ELFCLASS64
#ifndef	SGS_PRE_UNIFIED_PROCESS
		path = MSG_ORIG(MSG_PTH_NEWDLP_64);
#else
		path = MSG_ORIG(MSG_PTH_OLDDLP_64);
#endif
#else
#ifndef	SGS_PRE_UNIFIED_PROCESS
		path = MSG_ORIG(MSG_PTH_NEWDLP);
#else
		path = MSG_ORIG(MSG_PTH_OLDDLP);
#endif
#endif
		list = &crle->c_edlibpath;
		break;

	case CRLE_ESLIB:
#if M_CLASS == ELFCLASS64
#ifndef	SGS_PRE_UNIFIED_PROCESS
		path = MSG_ORIG(MSG_PTH_NEWTD_64);
#else
		path = MSG_ORIG(MSG_PTH_OLDTD_64);
#endif
#else
#ifndef	SGS_PRE_UNIFIED_PROCESS
		path = MSG_ORIG(MSG_PTH_NEWTD);
#else
		path = MSG_ORIG(MSG_PTH_OLDTD);
#endif
#endif
		list = &crle->c_eslibpath;
		break;

	case CRLE_ADLIB:
		path = MSG_ORIG(MSG_PTH_AOUTDLP);
		list = &crle->c_adlibpath;
		break;

	case CRLE_ASLIB:
#ifndef	SGS_PRE_UNIFIED_PROCESS
		path = MSG_ORIG(MSG_PTH_NEWTD);
#else
		path = MSG_ORIG(MSG_PTH_OLDTD);
#endif
		list = &crle->c_aslibpath;
		break;

	default:
		return (1);
	}

	return (addlib(crle, list, path));
}

/*
 * Establish the flags required to generate the associated object.  Actually
 * the flags are already part of the object being inspected from the present
 * configuration file, but instead of using them all, which can cause some
 * unsuspected propagation down the inspect() family, only use those flags that
 * would have been contributed from crle()'s calls to inspect.
 */
static Half
getflags(Half flags)
{
	flags &=
	    (RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP | RTC_OBJ_OPTINAL);
	return (flags | RTC_OBJ_CMDLINE);
}

/*
 * Dump a configuration files information.  This routine is very close to the
 * scanconfig() in libcrle.
 */
/*ARGSUSED2*/
static INSCFG_RET
scanconfig(Crle_desc * crle, Addr addr, int c_class)
{
	Conv_inv_buf_t 		inv_buf1, inv_buf2, inv_buf3, inv_buf4;
	Conv_dl_flag_buf_t	dl_flag_buf;
	Rtc_id		*id;
	Rtc_head	*head;
	Rtc_dir		*dirtbl;
	Rtc_file	*filetbl;
	Rtc_obj		*objtbl, *obj;
	Word		*hash, *chain;
	const char	*strtbl;
	int		ndx, bkts;
	APlist		*cmdline = NULL;
	char		_cmd[PATH_MAX], *cmd;
	char		_objdir[PATH_MAX], *objdir = NULL;

	/*
	 * If there is an Rtc_id present, the Rtc_head follows it.
	 * Otherwise, it is at the top.
	 */
	if (RTC_ID_TEST(addr)) {
		id = (Rtc_id *) addr;
		addr += sizeof (*id);	/* Rtc_head follows */
	} else {
		id = NULL;
		/*
		 * When updating an existing config file that is lacking
		 * the Rtc_id block, don't put one into the resulting file.
		 */
		crle->c_flags &= ~CRLE_ADDID;
	}
	head = (Rtc_head *) addr;


	/*
	 * The rest of the configuration file can only be examined by
	 * a program of the same ELFCLASS, byte order, and hardware
	 * architecture as the one that created it.
	 */
#ifdef _ELF64
	/* 64-bit program with an existing 32-bit file? Abort. */
	if (!(head->ch_cnflags & RTC_HDR_64)) {
		(void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS),
		    crle->c_name, crle->c_confil);
		return (INSCFG_RET_FAIL);
	}
#else
	/* 32-bit program with an existing 64-bit file? Restart. */
	if (head->ch_cnflags & RTC_HDR_64)
		return (INSCFG_RET_NEED64);

	/*
	 * 32-bit program with an existing 32-bit file, but the
	 * user specified the -64 option? Abort
	 */
	if (c_class != ELFCLASS32) {
		(void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS),
		    crle->c_name, crle->c_confil);
		return (INSCFG_RET_FAIL);
	}
#endif
	/*
	 * Now that the ELFCLASS has been settled, ensure that the
	 * byte order and hardware match. Unlike ELFCLASS, where restarting
	 * the other version is an option, we cannot work around a mismatch
	 * of these attributes.
	 */
	if (id) {		/* Rtc_id is present */
		/*
		 * Was the file produced by compatible hardware?
		 * ELFCLASS doesn't matter here, because we can
		 * adjust for that, but byte order and machine type do.
		 */
		if ((id->id_data != M_DATA) || (id->id_machine != M_MACH)) {
			(void) fprintf(stderr, MSG_INTL(MSG_ARG_WRONGARCH),
			    crle->c_name, crle->c_confil,
			    conv_ehdr_data(id->id_data, CONV_FMT_ALT_FILE,
			    &inv_buf1),
			    conv_ehdr_mach(id->id_machine, CONV_FMT_ALT_FILE,
			    &inv_buf2),
			    conv_ehdr_data(M_DATA, CONV_FMT_ALT_FILE,
			    &inv_buf3),
			    conv_ehdr_mach(M_MACH, CONV_FMT_ALT_FILE,
			    &inv_buf4));
			return (INSCFG_RET_FAIL);
		}
	}


	/* LINTED */
	objtbl = (Rtc_obj *)(CAST_PTRINT(char *, head->ch_obj) + addr);
	strtbl = (const char *)(CAST_PTRINT(char *, head->ch_str) + addr);

	/*
	 * If the configuration file has a version higher than we
	 * recognise, we face two issues:
	 *	(1) Updates are not possible because we have no
	 *		way to recognise or propagate the new features.
	 *		This has to be a fatal error.
	 *	(2) Printing has the risk that we may have been
	 *		handed something other than a real config file, as
	 *		well as the fact that we can't display the information
	 *		for the new features. So, we print a warning, but
	 *		continue on to do the best we can with it.
	 */
	if (head->ch_version > RTC_VER_CURRENT) {
		if (crle->c_flags & CRLE_UPDATE) {
			(void) fprintf(stderr, MSG_INTL(MSG_ARG_UPDATEVER),
			    crle->c_name, crle->c_confil,
			    (int)head->ch_version, RTC_VER_CURRENT);
			return (INSCFG_RET_FAIL);
		} else {
			(void) fprintf(stderr, MSG_INTL(MSG_ARG_PRINTVER),
			    crle->c_name, crle->c_confil,
			    (int)head->ch_version, RTC_VER_CURRENT);
		}
	}

	/*
	 * If this is a version 1 configuration file we can't generate accurate
	 * update information, or the command-line used to create the file.
	 */
	if (head->ch_version == RTC_VER_ONE) {
		(void) printf(MSG_INTL(MSG_ARG_UPDATE), crle->c_name,
		    crle->c_confil, (int)head->ch_version);
	}


	if (!(crle->c_flags & CRLE_UPDATE) && (head->ch_cnflags & RTC_HDR_64)) {
		/*
		 * Construct the original command line argument.
		 */
		cmd = strcpy(alloca(MSG_CMD_64_SIZE + 1), MSG_ORIG(MSG_CMD_64));
		if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
			return (INSCFG_RET_FAIL);
	}


	/*
	 * Start analyzing the configuration files header information.
	 */
	if ((crle->c_flags & CRLE_UPDATE) == 0) {
		const char	*fmt;

		if (head->ch_dlflags)
			fmt = conv_dl_flag(head->ch_dlflags, 0, &dl_flag_buf);
		else
			fmt = MSG_ORIG(MSG_STR_EMPTY);

		(void) printf(MSG_INTL(MSG_DMP_HEAD), (int)head->ch_version,
		    crle->c_confil, fmt);

		/*
		 * If the file has an id block, show the information
		 */
		if (id)
			(void) printf(MSG_INTL(MSG_DMP_PLATFORM),
			    conv_ehdr_class(id->id_class, CONV_FMT_ALT_FILE,
			    &inv_buf1),
			    conv_ehdr_data(id->id_data, CONV_FMT_ALT_FILE,
			    &inv_buf2),
			    conv_ehdr_mach(id->id_machine, CONV_FMT_ALT_FILE,
			    &inv_buf3));

		/*
		 * Construct the original command line argument.
		 */
		(void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_CONF),
		    crle->c_confil);
		cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
		if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
			return (INSCFG_RET_FAIL);

		/*
		 * Construct any -f usage.
		 */
		if (head->ch_dlflags &&
		    (head->ch_dlflags != RTLD_REL_RELATIVE)) {
			(void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_FLAGS),
			    conv_dl_flag(head->ch_dlflags, CONV_FMT_ALT_CRLE,
			    &dl_flag_buf));
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	} else {
		/*
		 * Establish any -f usage.
		 */
		if (head->ch_dlflags &&
		    (head->ch_dlflags != RTLD_REL_RELATIVE))
			crle->c_dlflags = head->ch_dlflags;
	}


	/*
	 * Determine if this configuration file is only applicable to a specific
	 * application.
	 */
	if (head->ch_app) {
		char	*alter;

		obj = (Rtc_obj *)(head->ch_app + addr);

		/*
		 * Determine the output directory for the files
		 * alternative name.
		 */
		alter = (char *)(strtbl + obj->co_alter);
		(void) strcpy(_objdir, alter);
		alter = strrchr(_objdir, '/');
		*alter = '\0';

		crle->c_objdir = objdir = _objdir;

		if (crle->c_flags & CRLE_UPDATE) {
			if (inspect(crle, (strtbl + obj->co_name),
			    (RTC_OBJ_DUMP | RTC_OBJ_ALTER |
			    RTC_OBJ_GROUP | RTC_OBJ_CMDLINE)) != 0)
				return (INSCFG_RET_FAIL);
		} else {
			(void) printf(MSG_INTL(MSG_DMP_APP),
			    (strtbl + obj->co_alter), (strtbl + obj->co_name));

			/*
			 * Construct the original command line arguments.
			 */
			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_OUTPUT), crle->c_objdir);
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);

			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_DUMPGRP), (strtbl + obj->co_name));
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	}

	/*
	 * Analyze any alternative library path and trusted directory entries.
	 */
	if (head->ch_edlibpath) {
		const char	*str;

		str = (const char *)(head->ch_edlibpath + addr);

		if (crle->c_flags & CRLE_UPDATE) {
			crle->c_flags &= ~CRLE_AOUT;

#ifndef	SGS_PRE_UNIFIED_PROCESS
			if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
				if (head->ch_cnflags & RTC_HDR_64)
					str = conv_config_upm(str,
					    MSG_ORIG(MSG_PTH_OLDDLP_64),
					    MSG_ORIG(MSG_PTH_UPDLP_64),
					    MSG_PTH_UPDLP_64_SIZE);
				else
					str = conv_config_upm(str,
					    MSG_ORIG(MSG_PTH_OLDDLP),
					    MSG_ORIG(MSG_PTH_UPDLP),
					    MSG_PTH_UPDLP_SIZE);
			}
#endif
			if (addlib(crle, &crle->c_edlibpath, str) != 0)
				return (INSCFG_RET_FAIL);
		} else {
			(void) printf(MSG_INTL(MSG_DMP_DLIBPTH),
			    MSG_ORIG(MSG_STR_ELF), str);

			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_EDLIB), str);
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	} else {
		if (crle->c_flags & CRLE_UPDATE) {
			if (crle->c_flags & CRLE_EDLIB) {
				/*
				 * If we've been asked to update a configuration
				 * file, and no existing default ELF search
				 * path exists, but the user is going to add new
				 * entries, fabricate the system defaults so
				 * that the users get added to them.
				 */
				if (fablib(crle, CRLE_EDLIB) != 0)
					return (INSCFG_RET_FAIL);
			}
		} else {
			/*
			 * Indicate any system default.
			 */
#if M_CLASS == ELFCLASS64
#ifndef	SGS_PRE_UNIFIED_PROCESS
			(void) printf(MSG_INTL(MSG_DEF_NEWDLP_64));
#else
			(void) printf(MSG_INTL(MSG_DEF_OLDDLP_64));
#endif
#else
#ifndef	SGS_PRE_UNIFIED_PROCESS
			(void) printf(MSG_INTL(MSG_DEF_NEWDLP));
#else
			(void) printf(MSG_INTL(MSG_DEF_OLDDLP));
#endif
#endif
		}
	}

	if (head->ch_eslibpath) {
		const char	*str;

		str = (const char *)(head->ch_eslibpath + addr);

		if (crle->c_flags & CRLE_UPDATE) {
			crle->c_flags &= ~CRLE_AOUT;

#ifndef	SGS_PRE_UNIFIED_PROCESS
			if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
				if (head->ch_cnflags & RTC_HDR_64)
					str = conv_config_upm(str,
					    MSG_ORIG(MSG_PTH_OLDTD_64),
					    MSG_ORIG(MSG_PTH_UPTD_64),
					    MSG_PTH_UPTD_64_SIZE);
				else
					str = conv_config_upm(str,
					    MSG_ORIG(MSG_PTH_OLDTD),
					    MSG_ORIG(MSG_PTH_UPTD),
					    MSG_PTH_UPTD_SIZE);
			}
#endif
			if (addlib(crle, &crle->c_eslibpath, str) != 0)
				return (INSCFG_RET_FAIL);
		} else {
			(void) printf(MSG_INTL(MSG_DMP_TLIBPTH),
			    MSG_ORIG(MSG_STR_ELF), str);

			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_ESLIB), str);
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	} else {
		if (crle->c_flags & CRLE_UPDATE) {
			if (crle->c_flags & CRLE_ESLIB) {
				/*
				 * If we've been asked to update a configuration
				 * file, and no existing default ELF secure
				 * path exists, but the user is going to add new
				 * entries, fabricate the system defaults so
				 * that the users get added to them.
				 */
				if (fablib(crle, CRLE_ESLIB) != 0)
					return (INSCFG_RET_FAIL);
			}
		} else {
			/*
			 * Indicate any system default.
			 */
#if M_CLASS == ELFCLASS64
#ifndef	SGS_PRE_UNIFIED_PROCESS
			(void) printf(MSG_INTL(MSG_DEF_NEWTD_64));
#else
			(void) printf(MSG_INTL(MSG_DEF_OLDTD_64));
#endif
#else
#ifndef	SGS_PRE_UNIFIED_PROCESS
			(void) printf(MSG_INTL(MSG_DEF_NEWTD));
#else
			(void) printf(MSG_INTL(MSG_DEF_OLDTD));
#endif
#endif
		}
	}

	if (head->ch_adlibpath) {
		const char	*str;

		str = (const char *)(head->ch_adlibpath + addr);

		if (crle->c_flags & CRLE_UPDATE) {
			crle->c_flags |= CRLE_AOUT;
			if (addlib(crle, &crle->c_adlibpath, str) != 0)
				return (INSCFG_RET_FAIL);
		} else {
			(void) printf(MSG_INTL(MSG_DMP_DLIBPTH),
			    MSG_ORIG(MSG_STR_AOUT), str);

			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_ADLIB), str);
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	} else {
		if (crle->c_flags & CRLE_UPDATE) {
			if (crle->c_flags & CRLE_ADLIB) {
				/*
				 * If we've been asked to update a configuration
				 * file, and no existing default AOUT search
				 * path exists, but the user is going to add new
				 * entries, fabricate the system defaults so
				 * that the users get added to them.
				 */
				if (fablib(crle, CRLE_ADLIB) != 0)
					return (INSCFG_RET_FAIL);
			}
		} else if (crle->c_flags & CRLE_AOUT) {
			/*
			 * Indicate any system default.
			 */
			(void) printf(MSG_INTL(MSG_DEF_AOUTDLP));
		}
	}

	if (head->ch_aslibpath) {
		const char	*str;

		str = (const char *)(head->ch_aslibpath + addr);

		if (crle->c_flags & CRLE_UPDATE) {
			crle->c_flags |= CRLE_AOUT;
			if (addlib(crle, &crle->c_aslibpath, str) != 0)
				return (INSCFG_RET_FAIL);
		} else {
			(void) printf(MSG_INTL(MSG_DMP_TLIBPTH),
			    MSG_ORIG(MSG_STR_AOUT), str);

			(void) snprintf(_cmd, PATH_MAX,
			    MSG_ORIG(MSG_CMD_ASLIB), str);
			cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
			if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL)
				return (INSCFG_RET_FAIL);
		}
	} else {
		if (crle->c_flags & CRLE_UPDATE) {
			if (crle->c_flags & CRLE_ASLIB) {
				/*
				 * If we've been asked to update a configuration
				 * file, and no existing default AOUT secure
				 * path exists, but the user is going to add new
				 * entries, fabricate the system defaults so
				 * that the users get added to them.
				 */
				if (fablib(crle, CRLE_ASLIB) != 0)
					return (INSCFG_RET_FAIL);
			}
		} else if (crle->c_flags & CRLE_AOUT) {
			/*
			 * Indicate any system default.
			 */
#ifndef	SGS_PRE_UNIFIED_PROCESS
			(void) printf(MSG_INTL(MSG_DEF_AOUTNEWTD));
#else
			(void) printf(MSG_INTL(MSG_DEF_AOUTOLDTD));
#endif
		}
	}

	/*
	 * Display any environment variables.
	 */
	if ((head->ch_version >= RTC_VER_THREE) && head->ch_env) {
		Rtc_env	*envtbl;

		if ((crle->c_flags & CRLE_UPDATE) == 0)
			(void) printf(MSG_INTL(MSG_ENV_TITLE));

		for (envtbl = (Rtc_env *)(head->ch_env + addr);
		    envtbl->env_str; envtbl++) {
			const char	*str;

			str = (const char *)(envtbl->env_str + addr);

			if (crle->c_flags & CRLE_UPDATE) {
				if (addenv(crle, str,
				    (envtbl->env_flags | RTC_ENV_CONFIG)) == 0)
					return (INSCFG_RET_FAIL);
			} else {
				const char	*pfmt, *sfmt;

				if (envtbl->env_flags & RTC_ENV_PERMANT) {
					pfmt = MSG_INTL(MSG_ENV_PRM);
					sfmt = MSG_ORIG(MSG_CMD_PRMENV);
				} else {
					pfmt = MSG_INTL(MSG_ENV_RPL);
					sfmt = MSG_ORIG(MSG_CMD_RPLENV);
				}
				(void) printf(pfmt, str);
				(void) snprintf(_cmd, PATH_MAX, sfmt, str);
				cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
				if (aplist_append(&cmdline, cmd,
				    AL_CNT_CRLE) == NULL)
					return (INSCFG_RET_FAIL);
			}
		}
	}

	/*
	 * Display any filter/filtee associations.
	 */
	if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr) {
		if ((crle->c_flags & CRLE_UPDATE) == 0) {
			Rtc_fltr	*fltrtbl;
			Rtc_flte	*fltetbl;

			/* LINTED */
			fltrtbl = (Rtc_fltr *)
			    (CAST_PTRINT(char *, head->ch_fltr) + addr);
			/* LINTED */
			fltetbl = (Rtc_flte *)
			    (CAST_PTRINT(char *, head->ch_flte) + addr);

			(void) printf(MSG_INTL(MSG_FLT_TITLE));

			while (fltrtbl->fr_filter) {
				Rtc_flte	*_fltetbl;

				/*
				 * Print the filter and filtee string pair.
				 */
				(void) printf(MSG_INTL(MSG_FLT_FILTER),
				    (strtbl + fltrtbl->fr_filter),
				    (strtbl + fltrtbl->fr_string));

				/*
				 * Print each filtee.
				 */
				/* LINTED */
				for (_fltetbl = (Rtc_flte *)((char *)fltetbl +
				    fltrtbl->fr_filtee); _fltetbl->fe_filtee;
				    _fltetbl++) {
					(void) printf(MSG_INTL(MSG_FLT_FILTEE),
					    (strtbl + _fltetbl->fe_filtee));
				}
				fltrtbl++;
			}
		}
	}

	/*
	 * Display any memory reservations required for any alternative
	 * objects.
	 */
	if (head->ch_resbgn && ((crle->c_flags & CRLE_UPDATE) == 0))
		(void) printf(MSG_INTL(MSG_DMP_RESV),
		    (u_longlong_t)head->ch_resbgn,
		    (u_longlong_t)head->ch_resend,
		    (u_longlong_t)(head->ch_resend - head->ch_resbgn));

	/*
	 * If there's no hash table there's nothing else to process.
	 */
	if (head->ch_hash == 0) {
		if ((crle->c_flags & CRLE_UPDATE) == 0)
			printcmd(crle, head, cmdline);
		return (INSCFG_RET_OK);
	}

	/*
	 * Traverse the directory and filename arrays.
	 */
	for (dirtbl = (Rtc_dir *)(head->ch_dir + addr);
	    dirtbl->cd_obj; dirtbl++) {
		struct stat	status;
		Rtc_obj		*dobj;
		const char	*str;

		dobj = (Rtc_obj *)(dirtbl->cd_obj + addr);
		filetbl = (Rtc_file *)(dirtbl->cd_file + addr);
		str = strtbl + dobj->co_name;

		/*
		 * Simplify recreation by using any command-line directories.
		 * If we're dealing with a version 1 configuration file use
		 * every directory.
		 */
		if ((dobj->co_flags & RTC_OBJ_CMDLINE) ||
		    (head->ch_version == RTC_VER_ONE)) {
			if (crle->c_flags & CRLE_UPDATE) {
				if (inspect(crle, str,
				    getflags(dobj->co_flags)) != 0)
					return (INSCFG_RET_FAIL);
				if ((dobj->co_flags &
				    (RTC_OBJ_NOEXIST | RTC_OBJ_ALTER)) ==
				    RTC_OBJ_NOEXIST)
					continue;
			} else {
				/* LINTED */
				(void) snprintf(_cmd, PATH_MAX,
				    getformat(dobj->co_flags), str);
				cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
				if (aplist_append(&cmdline, cmd,
				    AL_CNT_CRLE) == NULL)
					return (INSCFG_RET_FAIL);
			}
		}

		/*
		 * If this isn't an update print the directory name.  If the
		 * directory has no entries (possible if the directory is a
		 * symlink to another directory, in which case we record the
		 * real path also), don't bother printing it unless we're in
		 * verbose mode.
		 */
		if ((crle->c_flags & CRLE_UPDATE) == 0) {
			if ((dobj->co_flags &
			    (RTC_OBJ_NOEXIST | RTC_OBJ_ALTER)) ==
			    RTC_OBJ_NOEXIST) {
				(void) printf(MSG_INTL(MSG_DMP_DIR_2), str);
				continue;
			} else if (filetbl->cf_obj ||
			    (crle->c_flags & CRLE_VERBOSE))
				(void) printf(MSG_INTL(MSG_DMP_DIR_1), str);
		}

		/*
		 * Under verbose mode validate any real directory entry - the
		 * same test will be carried out by ld.so.1.
		 */
		if (((crle->c_flags & CRLE_UPDATE) == 0) &&
		    (crle->c_flags & CRLE_VERBOSE) &&
		    (dobj->co_flags & RTC_OBJ_REALPTH)) {
			if (stat(str, &status) != 0) {
				int err = errno;
				(void) printf(MSG_INTL(MSG_DMP_STAT), str,
				    strerror(err));
			} else if (status.st_mtime != dobj->co_info) {
				(void) printf(MSG_INTL(MSG_DMP_DCMP), str);
			}
		}

		for (; filetbl->cf_obj; filetbl++) {
			Rtc_obj	*fobj;
			Half	flags;

			fobj = (Rtc_obj *)(filetbl->cf_obj + addr);
			str = strtbl + fobj->co_name;
			flags = fobj->co_flags;

			/*
			 * Only update individual files that were originally
			 * specified on the command-line.  Or, if this is a
			 * version 1 configuration file use every file that
			 * isn't part of an all-entries directory.
			 */
			if (((flags & RTC_OBJ_CMDLINE) &&
			    ((fobj->co_flags & RTC_OBJ_APP) == 0)) ||
			    ((head->ch_version == RTC_VER_ONE) &&
			    ((dobj->co_flags & RTC_OBJ_ALLENTS) == 0))) {
				char	*alter = NULL, altdir[PATH_MAX];

				/*
				 * Determine whether this file requires an
				 * alternative, and if so, and we haven't
				 * already an alternative in affect, create one.
				 */
				if (fobj->co_flags & RTC_OBJ_ALTER) {
					alter = (char *)(strtbl +
					    fobj->co_alter);
					(void) strcpy(altdir, alter);
					alter = strrchr(altdir, '/');
					*alter = '\0';

					if ((objdir == NULL) ||
					    (strcmp(objdir, altdir) != 0)) {
						(void) strcpy(_objdir, altdir);
						crle->c_objdir = alter =
						    objdir = _objdir;
					} else
						alter = NULL;
				}

				if (crle->c_flags & CRLE_UPDATE) {
					if (inspect(crle, str,
					    getflags(flags)) != 0)
						return (INSCFG_RET_FAIL);
					continue;
				}

				if (alter) {
					(void) snprintf(_cmd, PATH_MAX,
					    MSG_ORIG(MSG_CMD_OUTPUT),
					    crle->c_objdir);
					cmd = strcpy(alloca(strlen(_cmd) + 1),
					    _cmd);
					if (aplist_append(&cmdline, cmd,
					    AL_CNT_CRLE) == NULL)
						return (INSCFG_RET_FAIL);
				}

				/* LINTED */
				(void) snprintf(_cmd, PATH_MAX,
				    getformat(flags), str);
				cmd = strcpy(alloca(strlen(_cmd) + 1), _cmd);
				if (aplist_append(&cmdline, cmd,
				    AL_CNT_CRLE) == NULL)
					return (INSCFG_RET_FAIL);
			}

			if (crle->c_flags & CRLE_UPDATE)
				continue;

			/*
			 * Although we record both full pathnames and their
			 * simple filenames (basename), only print the simple
			 * names unless we're under verbose mode.
			 */
			if ((strchr(str, '/') == 0) ||
			    (crle->c_flags & CRLE_VERBOSE)) {
				if (fobj->co_flags & RTC_OBJ_ALTER)
					(void) printf(MSG_INTL(MSG_DMP_FILE_2),
					    str, (strtbl + fobj->co_alter));
				else
					(void) printf(MSG_INTL(MSG_DMP_FILE_1),
					    str);
			}

			/*
			 * Under verbose mode validate any real file entry - the
			 * same test will be carried out by ld.so.1.
			 */
			if ((crle->c_flags & CRLE_VERBOSE) &&
			    (fobj->co_flags & RTC_OBJ_REALPTH)) {
				if (stat(str, &status) != 0) {
					int err = errno;
					(void) printf(MSG_INTL(MSG_DMP_STAT),
					    str, strerror(err));
				} else if (status.st_size != fobj->co_info) {
					(void) printf(MSG_INTL(MSG_DMP_FCMP),
					    str);
				}
			}
		}
	}

	if ((crle->c_flags & CRLE_UPDATE) == 0)
		printcmd(crle, head, cmdline);

	if ((crle->c_flags & CRLE_VERBOSE) == 0)
		return (INSCFG_RET_OK);

	/*
	 * If we've in verbose mode scan the hash list.
	 */
	/* LINTED */
	hash = (Word *)(CAST_PTRINT(char *, head->ch_hash) + addr);
	bkts = hash[0];
	chain = &hash[2 + bkts];
	hash += 2;

	(void) printf(MSG_INTL(MSG_DMP_HASH));

	/*
	 * Scan the hash buckets looking for valid entries.
	 */
	for (ndx = 0; ndx < bkts; ndx++, hash++) {
		Conv_config_obj_buf_t	config_obj_buf;
		Rtc_obj			*obj;
		const char		*str;
		Word			_ndx;

		if (*hash == NULL)
			continue;

		obj = objtbl + *hash;
		str = strtbl + obj->co_name;

		(void) printf(MSG_INTL(MSG_DMP_HASHENT_1), obj->co_id, ndx,
		    str, conv_config_obj(obj->co_flags, &config_obj_buf));

		/*
		 * Determine whether there are other objects chained to this
		 * bucket.
		 */
		for (_ndx = chain[*hash]; _ndx; _ndx = chain[_ndx]) {
			obj = objtbl + _ndx;
			str = strtbl + obj->co_name;

			(void) printf(MSG_INTL(MSG_DMP_HASHENT_2), obj->co_id,
			    str, conv_config_obj(obj->co_flags,
			    &config_obj_buf));
		}
	}
	(void) printf(MSG_ORIG(MSG_STR_NL));

	return (INSCFG_RET_OK);
}


INSCFG_RET
inspectconfig(Crle_desc * crle, int c_class)
{
	INSCFG_RET	error;
	int		fd;
	Addr		addr;
	struct stat	status;
	const char	*caller = crle->c_name, *file = crle->c_confil;
	Conv_inv_buf_t	inv_buf1, inv_buf2, inv_buf3;

	/*
	 * Open the configuration file, determine its size and map it in.
	 */
	if ((fd = open(file, O_RDONLY, 0)) == -1) {
		int	err = errno;

		if (err == ENOENT) {
#ifndef _ELF64
			/* Must restart if user requested a 64-bit file */
			if (c_class == ELFCLASS64)
				return (INSCFG_RET_NEED64);
#endif

			/*
			 * To allow an update (-u) from scratch, fabricate any
			 * default search and secure paths that the user
			 * intends to add to.
			 */
			if (crle->c_flags & CRLE_UPDATE) {
				if (crle->c_flags & CRLE_EDLIB) {
					if (fablib(crle, CRLE_EDLIB))
						return (INSCFG_RET_FAIL);
				}
				if (crle->c_flags & CRLE_ESLIB) {
					if (fablib(crle, CRLE_ESLIB))
						return (INSCFG_RET_FAIL);
				}
				if (crle->c_flags & CRLE_ADLIB) {
					if (fablib(crle, CRLE_ADLIB))
						return (INSCFG_RET_FAIL);
				}
				if (crle->c_flags & CRLE_ASLIB) {
					if (fablib(crle, CRLE_ASLIB))
						return (INSCFG_RET_FAIL);
				}
				return (INSCFG_RET_OK);

			} else if (crle->c_flags & CRLE_CONFDEF) {
				const char	*fmt1, *fmt2;

				/*
				 * Otherwise if the user is inspecting a default
				 * configuration file that doesn't exist inform
				 * them and display the ELF defaults.
				 */
				(void) printf(MSG_INTL(MSG_DEF_NOCONF), file);
				(void) printf(MSG_INTL(MSG_DMP_PLATFORM),
				    conv_ehdr_class(M_CLASS,
				    CONV_FMT_ALT_FILE, &inv_buf1),
				    conv_ehdr_data(M_DATA,
				    CONV_FMT_ALT_FILE, &inv_buf2),
				    conv_ehdr_mach(M_MACH,
				    CONV_FMT_ALT_FILE, &inv_buf3));


				if (crle->c_flags & CRLE_AOUT) {
					fmt1 = MSG_INTL(MSG_DEF_AOUTDLP);
#ifndef SGS_PRE_UNIFIED_PROCESS
					fmt2 = MSG_INTL(MSG_DEF_AOUTNEWTD);
#else
					fmt2 = MSG_INTL(MSG_DEF_AOUTOLDTD);
#endif
				} else {
#if M_CLASS == ELFCLASS64
#ifndef	SGS_PRE_UNIFIED_PROCESS
					fmt1 = MSG_INTL(MSG_DEF_NEWDLP_64);
					fmt2 = MSG_INTL(MSG_DEF_NEWTD_64);
#else
					fmt1 = MSG_INTL(MSG_DEF_OLDDLP_64);
					fmt2 = MSG_INTL(MSG_DEF_OLDTD_64);
#endif
#else
#ifndef	SGS_PRE_UNIFIED_PROCESS
					fmt1 = MSG_INTL(MSG_DEF_NEWDLP);
					fmt2 = MSG_INTL(MSG_DEF_NEWTD);
#else
					fmt1 = MSG_INTL(MSG_DEF_OLDDLP);
					fmt2 = MSG_INTL(MSG_DEF_OLDTD);
#endif
#endif
				}
				(void) printf(fmt1);
				(void) printf(fmt2);

				return (INSCFG_RET_OK);
			}
		}

		/*
		 * Otherwise there's an error condition in accessing the file.
		 */
		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), caller, file,
		    strerror(err));

		return (INSCFG_RET_FAIL);
	}

	(void) fstat(fd, &status);
	if (status.st_size < sizeof (Rtc_head)) {
		(void) close(fd);
		(void) fprintf(stderr, MSG_INTL(MSG_COR_TRUNC), caller, file);
		return (INSCFG_RET_FAIL);
	}
	if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
	    fd, 0)) == (Addr)MAP_FAILED) {
		int err = errno;
		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), caller, file,
		    strerror(err));
		(void) close(fd);
		return (INSCFG_RET_FAIL);
	}
	(void) close(fd);

	/*
	 * Print the contents of the configuration file.
	 */
	error = scanconfig(crle, addr, c_class);

	(void) munmap((void *)addr, status.st_size);
	return (error);
}