/*
 * 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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pkgstrct.h>
#include <unistd.h>
#include <pkglib.h>
#include <libintl.h>
#include "libadm.h"
#include "libinst.h"
#include "dryrun.h"

#define	HDR_FSUSAGE	"#name remote_name writeable bfree bused ifree iused"

#define	ERR_NOCREAT	"cannot create %s."
#define	ERR_NOOPEN	"cannot open %s."
#define	ERR_NOWRITE	"cannot write to %s."
#define	ERR_NOREAD	"cannot read from %s."
#define	ERR_FSFAIL	"cannot construct filesystem table entry."
#define	ERR_BADTYPE	"cannot record %s dryrun from %s continuation file."
#define	ERR_NOCONT	"cannot install from continue file due to error " \
			    "stacking."

#define	ISUMASC_SUFFIX	".isum.asc"
#define	FSASC_SUFFIX	".fs.asc"
#define	IPOASC_SUFFIX	".ipo.asc"
#define	IBIN_SUFFIX	".inst.bin"

#define	MALCOUNT	5	/* package entries to allocate in a block */
#define	PKGNAMESIZE	32	/* package entries to allocate in a block */

extern struct cfextra **extlist;
extern char *pkginst;

static struct cfextra **extptr;
static int	dryrun_mode = 0;
static int	continue_mode = 0;
static int	this_exitcode = 0;

/* The dryrun and continuation filenames */
static char *dryrun_sumasc;
static char *dryrun_fsasc;
static char *dryrun_poasc;
static char *dryrun_bin;
static char *continue_bin;

/* These tell us if the actual files are initialized yet. */
static int dryrun_initialized = 0;
static int continue_initialized = 0;

static int this_type;		/* type of transaction from main.c */

static int pkg_handle = -1;
static int tot_pkgs;

/* Their associated file pointers */
static FILE *fp_dra;
static int fd_drb;
static int fd_cnb;

struct dr_pkg_entry {
	char pkginst[PKGNAMESIZE + 2];
	struct dr_pkg_entry *next;
};

static struct drinfo {
	unsigned partial_set:1;	/* 1 if a partial installation was detected. */
	unsigned partial:1;	/* 1 if a partial installation was detected. */
	unsigned runlevel_set:1;
	unsigned runlevel:1;	/* 1 if runlevel test returned an error. */
	unsigned pkgfiles_set:1;
	unsigned pkgfiles:1;
	unsigned depend_set:1;
	unsigned depend:1;
	unsigned space_set:1;
	unsigned space:1;
	unsigned conflict_set:1;
	unsigned conflict:1;
	unsigned setuid_set:1;
	unsigned setuid:1;
	unsigned priv_set:1;
	unsigned priv:1;
	unsigned pkgdirs_set:1;
	unsigned pkgdirs:1;
	unsigned reqexit_set:1;
	unsigned checkexit_set:1;

	int	type;		/* type of operation */
	int	reqexit;	/* request script exit code */
	int	checkexit;	/* checkinstall script exit code */
	int	exitcode;	/* overall program exit code. */

	struct dr_pkg_entry *packages;	/* pointer to the list of packages */

	int total_ext_recs;	/* total extlist entries */
	int total_fs_recs;	/* total fs_tab entries */
	int total_pkgs;		/* total number of dryrun packages */
	int do_not_continue;	/* error stacking is likely */
} dr_info;

static char	*exitmsg;	/* the last meaningful message printed */

/*
 * In the event that live continue (continue from a dryrun source only)
 * becomes a feature, it will be necessary to keep track of those events such
 * as multiply edited files and files dependent upon multiple class action
 * scripts that will lead to "tolerance stacking". Calling this function
 * states that we've lost the level of precision necessary for a live
 * continue.
 */
void
set_continue_not_ok(void)
{
	dr_info.do_not_continue = 1;
}

int
continue_is_ok(void)
{
	return (!dr_info.do_not_continue);
}

static void
wr_OK(FILE *fp, char *parameter, int set, int value)
{
	(void) fprintf(fp, "%s=%s\n", parameter,
		(set ? (value ? "OK" : "NOT_OK") : "NOT_TESTED"));
}

static void
add_pkg_to_list(char *pkgname)
{
	struct dr_pkg_entry **pkg_entry;

	if (pkg_handle == -1) {
		if ((pkg_handle = bl_create(MALCOUNT,
		    sizeof (struct dr_pkg_entry), "package dryrun")) == -1)
			return;
	}

	pkg_entry = &(dr_info.packages);

	while (*pkg_entry != NULL)
		pkg_entry = &((*pkg_entry)->next);

	/* LINTED pointer cast may result in improper alignment */
	*pkg_entry = (struct dr_pkg_entry *)bl_next_avail(pkg_handle);
	dr_info.total_pkgs++;

	(void) snprintf((*pkg_entry)->pkginst, PKGNAMESIZE, "%s%s",
		(pkgname ? pkgname : ""), ((this_exitcode == 0) ? "" : "-"));
}

static void
write_pkglist_ascii(void)
{
	struct dr_pkg_entry *pkg_entry;

	(void) fprintf(fp_dra, "PKG_LIST=\"");

	pkg_entry = dr_info.packages;
	while (pkg_entry) {
		(void) fprintf(fp_dra, " %s", pkg_entry->pkginst);
		pkg_entry = pkg_entry->next;
	}

	(void) fprintf(fp_dra, "\"\n");
}

static int
write_string(int fd, char *string)
{
	int string_size;

	if (string)
		string_size = strlen(string) + 1;
	else
		string_size = 0;

	if (write(fd, &string_size, sizeof (string_size)) == -1) {
		progerr(gettext(ERR_NOWRITE), dryrun_bin);
		return (0);
	}

	if (string_size > 0) {
		if (write(fd, string, string_size) == -1) {
			progerr(gettext(ERR_NOWRITE), dryrun_bin);
			return (0);
		}
	}

	return (1);
}

static char *
read_string(int fd, char *buffer)
{
	size_t string_size;

	if (read(fd, &(string_size), sizeof (string_size)) == -1) {
		progerr(gettext(ERR_NOREAD), continue_bin);
		return (NULL);
	}

	if (string_size != 0) {
		if (read(fd, buffer, string_size) == -1) {
			progerr(gettext(ERR_NOREAD), continue_bin);
			return (NULL);
		}
	} else {
		return (NULL);
	}

	return (buffer);
}

static void
write_dryrun_ascii()
{
	int n;
	char *fs_mntpt, *src_name;

	if ((fp_dra = fopen(dryrun_sumasc, "wb")) == NULL) {
		progerr(gettext(ERR_NOOPEN), dryrun_sumasc);
		return;
	}

	(void) fprintf(fp_dra, "DR_TYPE=%s\n", (dr_info.type == REMOVE_TYPE ?
	    "REMOVE" : "INSTALL"));

	(void) fprintf(fp_dra, "PKG_INSTALL_ROOT=%s\n", (((get_inst_root()) &&
	    (strcmp(get_inst_root(), "/") != 0)) ?
	    get_inst_root() : ""));

	write_pkglist_ascii();

	wr_OK(fp_dra, "CONTINUE", 1, !(dr_info.do_not_continue));

	wr_OK(fp_dra, "PARTIAL", dr_info.partial_set, dr_info.partial);

	wr_OK(fp_dra, "RUNLEVEL", dr_info.runlevel_set, dr_info.runlevel);

	(void) fprintf(fp_dra, "REQUESTEXITCODE=%d\n", dr_info.reqexit);

	(void) fprintf(fp_dra, "CHECKINSTALLEXITCODE=%d\n", dr_info.checkexit);

	wr_OK(fp_dra, "PKGFILES", dr_info.pkgfiles_set, dr_info.pkgfiles);

	wr_OK(fp_dra, "DEPEND", dr_info.depend_set, dr_info.depend);

	wr_OK(fp_dra, "SPACE", dr_info.space_set, dr_info.space);

	wr_OK(fp_dra, "CONFLICT", dr_info.conflict_set, dr_info.conflict);

	wr_OK(fp_dra, "SETUID", dr_info.setuid_set, dr_info.setuid);

	wr_OK(fp_dra, "PRIV", dr_info.priv_set, dr_info.priv);

	wr_OK(fp_dra, "PKGDIRS", dr_info.pkgdirs_set, dr_info.pkgdirs);

	(void) fprintf(fp_dra, "EXITCODE=%d\n", dr_info.exitcode);

	(void) fprintf(fp_dra, "ERRORMSG=%s\n", (exitmsg ? exitmsg : "NONE"));

	(void) fclose(fp_dra);

	if ((fp_dra = fopen(dryrun_fsasc, "wb")) == NULL) {
		progerr(gettext(ERR_NOOPEN), dryrun_fsasc);
		return;
	}

	(void) fprintf(fp_dra, "%s\nFSUSAGE=\\\n\"\\\n", HDR_FSUSAGE);

	for (n = 0; fs_mntpt = get_fs_name_n(n); n++) {
		int bfree, bused;
		bfree = get_blk_free_n(n);
		bused = get_blk_used_n(n);

		if (bfree || bused) {
			(void) fprintf(fp_dra, "%s %s %s %d %d %lu %lu \\\n",
			    fs_mntpt,
			    ((src_name = get_source_name_n(n)) ?
			    src_name : "none?"),
			    (is_fs_writeable_n(n) ? "TRUE" : "FALSE"),
			    bfree,
			    bused,
			    get_inode_free_n(n),
			    get_inode_used_n(n));
		}
	}

	dr_info.total_fs_recs = n;

	(void) fprintf(fp_dra, "\"\n");

	(void) fclose(fp_dra);

	if ((fp_dra = fopen(dryrun_poasc, "wb")) == NULL) {
		progerr(gettext(ERR_NOOPEN), dryrun_poasc);
		return;
	}

	dr_info.total_ext_recs = 0;

	(void) fprintf(fp_dra, "WOULD_INSTALL=\\\n\"\\\n");

	for (n = 0; extptr && extptr[n]; n++) {
		/*
		 * Write it out if it's a successful change or it is from the
		 * prior dryrun file (meaning it was a change back then).
		 */
		if ((this_exitcode == 0 &&
		    (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)) ||
		    extptr[n]->mstat.preloaded) {
			(void) fprintf(fp_dra, "%c %s \\\n",
				extptr[n]->cf_ent.ftype,
				extptr[n]->client_path);

			/* Count it, if it's going into the dryrun file. */
			if (extptr[n]->cf_ent.ftype != 'i')
				dr_info.total_ext_recs++;
		}
	}

	(void) fprintf(fp_dra, "\"\n");

	(void) fclose(fp_dra);
}

/*
 * This writes out a dryrun file.
 */
static void
write_dryrun_bin()
{
	struct fstable *fs_entry;
	struct pinfo *pkginfo;
	struct dr_pkg_entry *pkg_entry;
	int n;
	int fsentry_size = sizeof (struct fstable);
	int extentry_size = sizeof (struct cfextra);
	int pinfoentry_size = sizeof (struct pinfo);

	if ((fd_drb = open(dryrun_bin,
	    O_RDWR | O_APPEND | O_TRUNC)) == -1) {
		progerr(gettext(ERR_NOOPEN), dryrun_bin);
		return;
	}

	/* Write the dryrun info table. */
	if (write(fd_drb, &dr_info, sizeof (struct drinfo)) == -1) {
		progerr(gettext(ERR_NOWRITE), dryrun_bin);
		return;
	}

	/* Write out the package instance list. */
	pkg_entry = dr_info.packages;
	while (pkg_entry) {
		if (write(fd_drb, pkg_entry->pkginst, PKGNAMESIZE) == -1) {
			progerr(gettext(ERR_NOWRITE), dryrun_bin);
			return;
		}
		pkg_entry = pkg_entry->next;
	}

	/* Write out the fstable records. */
	for (n = 0; n < dr_info.total_fs_recs; n++) {
		fs_entry = get_fs_entry(n);

		if (write(fd_drb, fs_entry, fsentry_size) == -1) {
			progerr(gettext(ERR_NOWRITE), dryrun_bin);
			return;
		}

		if (!write_string(fd_drb, fs_entry->name))
			return;

		if (!write_string(fd_drb, fs_entry->fstype))
			return;

		if (!write_string(fd_drb, fs_entry->remote_name))
			return;
	}

	/* Write out the package objects and their attributes. */
	for (n = 0; extptr && extptr[n]; n++) {
		/* Don't save metafiles. */
		if (extptr[n]->cf_ent.ftype == 'i')
			continue;

		/*
		 * If it's a new package object (not left over from the
		 * continuation file) and it indicates no changes to the
		 * system, skip it. Only files that will change the system
		 * are stored.
		 */
		if (extptr[n]->mstat.preloaded == 0 &&
		    !(this_exitcode == 0 &&
		    (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)))
			continue;

		if (write(fd_drb, extptr[n], extentry_size) == -1) {
			progerr(gettext(ERR_NOWRITE), dryrun_bin);
			return;
		}

		if (!write_string(fd_drb, extptr[n]->cf_ent.path))
			return;

		if (!write_string(fd_drb, extptr[n]->cf_ent.ainfo.local))
			return;

		extptr[n]->cf_ent.pinfo = eptstat(&(extptr[n]->cf_ent),
		    pkginst, CONFIRM_CONT);

		/*
		 * Now all of the entries about the various packages that own
		 * this entry.
		 */
		pkginfo = extptr[n]->cf_ent.pinfo;

		do {
			if (write(fd_drb, pkginfo,
			    pinfoentry_size) == -1) {
				progerr(gettext(ERR_NOWRITE), dryrun_bin);
				return;
			}
			pkginfo = pkginfo->next;	/* May be several */
		} while (pkginfo);
	}

	(void) close(fd_drb);
}

static void
init_drinfo(void) {

	if (dr_info.partial != 0)
		dr_info.partial_set = 0;

	if (dr_info.runlevel != 0)
		dr_info.runlevel_set = 0;

	if (dr_info.pkgfiles != 0)
		dr_info.pkgfiles_set = 0;

	if (dr_info.depend != 0)
		dr_info.depend_set = 0;

	if (dr_info.space != 0)
		dr_info.space_set = 0;

	if (dr_info.conflict != 0)
		dr_info.conflict_set = 0;

	if (dr_info.setuid != 0)
		dr_info.setuid_set = 0;

	if (dr_info.priv != 0)
		dr_info.priv_set = 0;

	if (dr_info.pkgdirs != 0)
		dr_info.pkgdirs_set = 0;

	if (dr_info.reqexit == 0)
		dr_info.reqexit_set = 0;

	if (dr_info.checkexit == 0)
		dr_info.checkexit_set = 0;

	dr_info.packages = NULL;
	tot_pkgs = dr_info.total_pkgs;
	dr_info.total_pkgs = 0;
}

/*
 * This function reads in the various continuation file data in order to seed
 * the internal data structures.
 */
static boolean_t
read_continue_bin(void)
{
	int n;
	int fsentry_size = sizeof (struct fstable);
	int extentry_size = sizeof (struct cfextra);
	int pinfoentry_size = sizeof (struct pinfo);

	pkgobjinit();
	if (!init_pkgobjspace())
		return (B_FALSE);

	if ((fd_cnb = open(continue_bin, O_RDONLY)) == -1) {
		progerr(gettext(ERR_NOOPEN), continue_bin);
		return (B_FALSE);
	}

	/* Read the dryrun info structure. */
	if (read(fd_cnb, &dr_info, sizeof (struct drinfo)) == -1) {
		progerr(gettext(ERR_NOREAD), continue_bin);
		return (B_FALSE);
	}

	init_drinfo();

	if (this_type != dr_info.type) {
		progerr(gettext(ERR_BADTYPE),
		    (this_type == REMOVE_TYPE) ?
		    "a remove" : "an install",
		    (dr_info.type == REMOVE_TYPE) ?
		    "a remove" : "an install");
		return (B_FALSE);
	}

	/* Read in the dryrun package records. */
	for (n = 0; n < tot_pkgs; n++) {
		char pkg_name[PKGNAMESIZE];

		if (read(fd_cnb, &pkg_name, PKGNAMESIZE) == -1) {
			progerr(gettext(ERR_NOREAD), continue_bin);
			return (B_FALSE);
		}

		add_pkg_to_list(pkg_name);
	}

	/* Read in the fstable records. */
	for (n = 0; n < dr_info.total_fs_recs; n++) {
		struct fstable fs_entry;
		char name[PATH_MAX], remote_name[PATH_MAX];
		char fstype[200];

		if (read(fd_cnb, &fs_entry, fsentry_size) == -1) {
			progerr(gettext(ERR_NOREAD), continue_bin);
			return (B_FALSE);
		}

		if (read_string(fd_cnb, &name[0]) == NULL)
			return (B_FALSE);

		if (read_string(fd_cnb, &fstype[0]) == NULL)
			return (B_FALSE);

		if (read_string(fd_cnb, &remote_name[0]) == NULL)
			return (B_FALSE);

		if (load_fsentry(&fs_entry, name, fstype, remote_name)) {
			progerr(gettext(ERR_FSFAIL));
			return (B_FALSE);
		}
	}

	/* Read in the package objects and their attributes. */
	for (n = 0; n < dr_info.total_ext_recs; n++) {
		struct cfextra ext_entry;
		struct pinfo pinfo_area, *pinfo_ptr;
		char path[PATH_MAX], local[PATH_MAX], *local_ptr;

		if (read(fd_cnb, &ext_entry, extentry_size) == -1) {
			progerr(gettext(ERR_NOREAD), continue_bin);
			return (B_FALSE);
		}

		/*
		 * If the previous dryrun replaced a directory with a
		 * non-directory and we're going into *another* dryrun, we're
		 * stacking errors and continuation should not be permitted.
		 */
		if (ext_entry.mstat.dir2nondir && dryrun_mode)
			dr_info.do_not_continue = 1;

		/*
		 * Since we just read this from a continuation file; it is,
		 * by definition, preloaded.
		 */
		ext_entry.mstat.preloaded = 1;

		if (read_string(fd_cnb, &path[0]) == NULL)
			return (B_FALSE);

		local_ptr = read_string(fd_cnb, &local[0]);

		ext_entry.cf_ent.pinfo = NULL;

		/*
		 * Now all of the entries about the various packages that own
		 * this entry.
		 */
		do {
			if (read(fd_cnb, &pinfo_area, pinfoentry_size) == -1) {
				progerr(gettext(ERR_NOREAD), continue_bin);
				return (B_FALSE);

			}

			pinfo_ptr = eptstat(&(ext_entry.cf_ent),
			    pinfo_area.pkg, CONFIRM_CONT);

			if (pinfo_ptr->next) {
				pinfo_ptr = pinfo_ptr->next;
			} else {
				pinfo_ptr = NULL;
			}

		} while (pinfo_ptr);

		seed_pkgobjmap(&ext_entry, path, local_ptr);
	}

	(void) close(fd_cnb);

	/*
	 * Return as reading is done, so pkginstall doesn't
	 * read the same info from the system.
	 */

	return (B_TRUE);
}

int
in_dryrun_mode(void)
{
	return (dryrun_mode);
}

void
set_dryrun_mode(void)
{
	dryrun_mode = 1;
}

int
in_continue_mode(void)
{
	return (continue_mode);
}

void
set_continue_mode(void)
{
	continue_mode = 1;
}

/*
 * Initialize a dryrun file by assigning it a name and creating it
 * empty.
 */
static int
init_drfile(char **targ_ptr, char *path)
{
	int n;
	char *targ_file;

	*targ_ptr = strdup(path);
	targ_file = *targ_ptr;

	if (access(targ_file, W_OK) == 0) {
		(void) unlink(targ_file);
	}

	n = open(targ_file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
	if (n < 0) {
		progerr(gettext(ERR_NOCREAT), targ_file);
		return (0);
	} else {
		(void) close(n);
	}

	return (1);
}

/*
 * Initialize all required dryrun files and see that the target directory is
 * present. If all goes well, we're in dryrun mode. If it doesn't, we're not.
 */
void
init_dryrunfile(char *dr_dir)
{
	char temp_path[PATH_MAX];
	char *dot_pos = (temp_path+strlen(dr_dir)+7);

	/* First create or confirm the directory. */
	if (isdir(dr_dir) != 0) {
		(void) mkpath(dr_dir);
	}

	(void) snprintf(temp_path, sizeof (temp_path), "%s/dryrun", dr_dir);

	(void) strcpy(dot_pos, ISUMASC_SUFFIX);

	if (!init_drfile(&dryrun_sumasc, temp_path))
		return;

	(void) strcpy(dot_pos, FSASC_SUFFIX);

	if (!init_drfile(&dryrun_fsasc, temp_path))
		return;

	(void) strcpy(dot_pos, IPOASC_SUFFIX);

	if (!init_drfile(&dryrun_poasc, temp_path))
		return;

	(void) strcpy(dot_pos, IBIN_SUFFIX);

	if (!init_drfile(&dryrun_bin, temp_path))
		return;

	dryrun_initialized = 1;
}

void
init_contfile(char *cn_dir)
{
	char temp_path[PATH_MAX];

	/* First confirm the directory. */
	if (isdir(cn_dir) != 0)
		return;		/* no continuation directory */

	(void) snprintf(temp_path, sizeof (temp_path),
				"%s/dryrun%s", cn_dir, IBIN_SUFFIX);
	continue_bin = strdup(temp_path);

	if (access(continue_bin, W_OK) != 0) {
		free(continue_bin);
		return;
	}

	continue_initialized = 1;
}

void
set_dr_exitmsg(char *value)
{
	exitmsg = value;
}

void
set_dr_info(int type, int value)
{
	switch (type) {
	    case PARTIAL:
		if (dr_info.partial_set == 0) {
			dr_info.partial_set = 1;
			dr_info.partial = (value ? 1 : 0);
		}
		break;

	    case RUNLEVEL:
		if (dr_info.runlevel_set == 0) {
			dr_info.runlevel_set = 1;
			dr_info.runlevel = (value ? 1 : 0);
		}
		break;

	    case PKGFILES:
		if (dr_info.pkgfiles_set == 0) {
			dr_info.pkgfiles_set = 1;
			dr_info.pkgfiles = (value ? 1 : 0);
		}
		break;

	    case DEPEND:
		if (dr_info.depend_set == 0) {
			dr_info.depend_set = 1;
			dr_info.depend = (value ? 1 : 0);
		}
		break;

	    case SPACE:
		if (dr_info.space_set == 0) {
			dr_info.space_set = 1;
			dr_info.space = (value ? 1 : 0);
		}
		break;

	    case CONFLICT:
		if (dr_info.conflict_set == 0) {
			dr_info.conflict_set = 1;
			dr_info.conflict = (value ? 1 : 0);
		}
		break;

	    case SETUID:
		if (dr_info.setuid_set == 0) {
			dr_info.setuid_set = 1;
			dr_info.setuid = (value ? 1 : 0);
		}
		break;

	    case PRIV:
		if (dr_info.priv_set == 0) {
			dr_info.priv_set = 1;
			dr_info.priv = (value ? 1 : 0);
		}

		break;

	    case PKGDIRS:
		if (dr_info.pkgdirs_set == 0) {
			dr_info.pkgdirs_set = 1;
			dr_info.pkgdirs = (value ? 1 : 0);
		}

		break;

	    case REQUESTEXITCODE:
		if (dr_info.reqexit_set == 0) {
			dr_info.reqexit_set = 1;
			dr_info.reqexit = value;
		}

		break;

	    case CHECKEXITCODE:
		if (dr_info.checkexit_set == 0) {
			dr_info.checkexit_set = 1;
			dr_info.checkexit = value;
		}

		break;

	    case EXITCODE:
		if (dr_info.exitcode == 0) {
			dr_info.exitcode = value;
		}

		this_exitcode = value;

		break;

	    /* default to install if the value is kookie. */
	    case DR_TYPE:
		if (value == REMOVE_TYPE)
			this_type = REMOVE_TYPE;
		else
			this_type = INSTALL_TYPE;

		break;
	}
}

void
write_dryrun_file(struct cfextra **extlist)
{
	extptr = extlist;

	if (dryrun_initialized) {
		dr_info.type = this_type;

		add_pkg_to_list(pkginst);
		write_dryrun_ascii();
		write_dryrun_bin();

		if (dryrun_mode) {
			free(dryrun_sumasc);
			free(dryrun_fsasc);
			free(dryrun_poasc);
			free(dryrun_bin);
		}
	}
}

/*
 * Name:		read_continuation
 * Description:		If continuation is initialised, reads the
 *			continuation binary file. The path for the
 *			same is freed, if set,  as this is the last
 *			chance to do so.
 * Sets:		Error condition, through the pointer passed
 *			if read failed.
 * Returns:		B_TRUE - if the continuation binary file
 *			from previous dryrun is read successfully.
 *			B_FALSE - if either continuation is not initialised
 *			or read was not successful.
 */
boolean_t
read_continuation(int *error)
{
	boolean_t ret = B_FALSE;
	*error = 0;
	if (continue_initialized) {
		if (!read_continue_bin()) {
			continue_mode = 0;
			free(continue_bin);
			*error = -1;
			return (ret);
		}

		if (continue_mode) {
			free(continue_bin);
		}
		ret = B_TRUE;
	}
	return (ret);
}