root/usr/src/cmd/svr4pkg/pkginstall/main.c
/*
 * 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 (c) 2017 Peter Tribble.
 */

/*
 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
 */

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


#include <stdio.h>
#include <time.h>
#include <wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <ulimit.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include <signal.h>
#include <locale.h>
#include <libintl.h>
#include <pkgstrct.h>
#include <pkginfo.h>
#include <pkgdev.h>
#include <pkglocs.h>
#include <pwd.h>
#include <assert.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <install.h>
#include <libinst.h>
#include <libadm.h>
#include <dryrun.h>
#include <messages.h>
#include "pkginstall.h"

/* imported globals */

extern char     **environ;
extern char     *pkgabrv;
extern char     *pkgname;
extern char     *pkgarch;
extern char     *pkgvers;
extern char     pkgwild[];

/* libadm(3LIB) */

extern char     *get_install_root(void);

/* quit.c */

extern sighdlrFunc_t    *quitGetTrapHandler(void);
extern void             quitSetDstreamTmpdir(char *a_dstreamTempDir);
extern void             quitSetInstallStarted(boolean_t a_installStarted);
extern void             quitSetPkgask(boolean_t a_pkgaskFlag);
extern void             quitSetSilentExit(boolean_t a_silentExit);
extern void             quitSetUpdatingExisting(boolean_t a_updatingExisting);
extern void             quitSetZoneName(char *a_zoneName);


/* static globals */

static char     path[PATH_MAX];
static int      ck_instbase(void);
static int      cp_pkgdirs(void);
static int      merg_pkginfos(struct cl_attr **pclass,
                struct cl_attr ***mpclass);
static int      merg_respfile(void);
static int      mv_pkgdirs(void);
static int      rdonly(char *p);
static void     ck_w_dryrun(int (*func)(), int type);
static void     copyright(void);
static void     usage(void) __NORETURN;
static void     do_pkgask(boolean_t a_run_request_as_root);
static void     rm_icas(char *casdir);
static void     set_dryrun_dir_loc(void);
static void     unpack(void);

void    ckreturn(int retcode, char *msg);

static char     *ro_params[] = {
        "PATH", "NAME", "PKG", "PKGINST",
        "VERSION", "ARCH",
        "INSTDATE", "CATEGORY",
        NULL
};

/*
 * The following variable is the name of the device to which stdin
 * is connected during execution of a procedure script. PROC_STDIN is
 * correct for all ABI compliant packages. For non-ABI-compliant
 * packages, the '-o' command line switch changes this to PROC_XSTDIN
 * to allow user interaction during these scripts. -- JST
 */
static char     *script_in = PROC_STDIN;        /* assume ABI compliance */

static char     *pkgdrtarg = NULL;
static char     *pkgcontsrc = NULL;
static int      non_abi_scripts = 0;
static char     *respfile = NULL;
static char     *srcinst = NULL;
static int      suppressCopyright = 0;
static int      nointeract = 0;

/* exported globals */

char            *msgtext;
char            *pkginst = (char *)NULL;
char            *rw_block_size = NULL;
char            ilockfile[PATH_MAX];
char            instdir[PATH_MAX];
char            saveSpoolInstallDir[PATH_MAX];
char            pkgbin[PATH_MAX];
char            pkgloc[PATH_MAX];
char            pkgloc_sav[PATH_MAX];
char            pkgsav[PATH_MAX];
char            rlockfile[PATH_MAX];
char            savlog[PATH_MAX];
char            tmpdir[PATH_MAX];
int             dbchg;
int             dparts = 0;
int             dreboot = 0;
int             failflag = 0;
static int      askflag = 0;            /* non-zero if invoked as "pkgask" */
int             ireboot = 0;
int             maxinst = 1;
int             nocnflct;
int             nosetuid;
int             pkgverbose = 0;
int             rprcflag;
int             warnflag = 0;
struct admin    adm;
struct cfextra  **extlist; /* pkgmap structure and other path info */
struct pkgdev   pkgdev;
fsblkcnt_t      pkgmap_blks = 0LL;

/*
 * this global is referenced by:
 * getinst - [RW] - incremented if:
 * - installing same instance again
 * - overwriting an existing instance
 * - not installing a new instance
 * quit - [RO] - if non-zero and started non-zero:
 * - the new <PKGINST>/install directory and rename <PKGINST>/install.save
 * - back to <PKGINST>/install
 * main.c - [RO] - if non-zero:
 * - alter manner in which parameters are setup for scripts
 * - set UPDATE=yes in environment
 */
static int              update = 0;

/* Set by -O debug: debug output is enabled? */

static boolean_t        debugFlag = B_FALSE;

/* Set by the -G option: install packages in global zone only */

static boolean_t        globalZoneOnly = B_FALSE;

/* Set by -O preinstallcheck */

static boolean_t        preinstallCheck = B_FALSE;

/* Set by -O parent-zone-name= */

static char             *parentZoneName = (char *)NULL;

/* Set by -O parent-zone-type= */

static char             *parentZoneType = (char *)NULL;

#define DEFPATH         "/sbin:/usr/sbin:/usr/bin"
#define MALSIZ  4       /* best guess at likely maximum value of MAXINST */
#define LSIZE   256     /* maximum line size supported in copyright file */

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

/* This is the text for the "-O parent-zone-name=" option */

#define PARENTZONENAME  "parent-zone-name="
#define PARENTZONENAME_LEN      ((sizeof (PARENTZONENAME))-1)

/* This is the text for the "-O parent-zone-type=" option */

#define PARENTZONETYPE  "parent-zone-type="
#define PARENTZONETYPE_LEN      ((sizeof (PARENTZONETYPE))-1)

static char *cpio_names[] = {
        "root",
        "root.cpio",
        "reloc",
        "reloc.cpio",
        "root.Z",
        "root.cpio.Z",
        "reloc.Z",
        "reloc.cpio.Z",
        0
};

int
main(int argc, char *argv[])
{
        VFP_T                   *cfTmpVfp = NULL;       /* temporary */
        VFP_T                   *pkgmapVfp;     /* "../pkgmap" file */
        boolean_t               run_request_as_root = B_FALSE;
        char                    **np;
        char                    *abi_comp_ptr;
        char                    *abi_nm_ptr;
        char                    *abi_sym_ptr;
        char                    *admnfile = NULL;
        char                    *device;
        char                    *p;
        char                    *prog_full_name = NULL;
        char                    *pt;
        char                    *updated = (char *)NULL;
        char                    *vfstab_file = NULL;
        char                    *zoneName = (char *)NULL;
        char                    cbuf[MAX_PKG_PARAM_LENGTH];
        char                    cmdbin[PATH_MAX];
        char                    p_pkginfo[PATH_MAX];
        char                    p_pkgmap[PATH_MAX];
        char                    param[MAX_PKG_PARAM_LENGTH];
        char                    script[PATH_MAX];
        char                    altscript[PATH_MAX];
        char                    *temp;
        int                     c;
        int                     disableAttributes = 0;
        int                     err;
        int                     init_install = 0;
        int                     is_comp_arch;
        int                     live_continue = 0;
        int                     map_client = 1;
        int                     n;
        int                     nparts;
        int                     npkgs;
        int                     part;
        int                     saveSpoolInstall = 0;
        boolean_t               cont_file_read;
        struct cl_attr          **pclass = NULL;
        struct cl_attr          **mergd_pclass = NULL;
        struct pkginfo          *prvinfo;
        struct sigaction        nact;
        struct sigaction        oact;
        struct stat             statb;
        struct statvfs64        svfsb;
        time_t                  clock;
        PKGserver               pkgserver = NULL;

        /* reset contents of all default paths */

        (void) memset(path, '\0', sizeof (path));
        (void) memset(cmdbin, '\0', sizeof (cmdbin));
        (void) memset(script, '\0', sizeof (script));
        (void) memset(cbuf, '\0', sizeof (cbuf));
        (void) memset(param, '\0', sizeof (param));

        /* initialize locale environment */

        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        /* initialize program name */

        prog_full_name = argv[0];
        (void) set_prog_name(argv[0]);

        /* tell spmi zones interface how to access package output functions */

        z_set_output_functions(echo, echoDebug, progerr);

        /* exit if not root */

        if (getuid()) {
                progerr(ERR_NOT_ROOT, get_prog_name());
                exit(1);
                /* NOTREACHED */
        }

        /*
         * determine how pkgmap() deals with environment variables:
         *  - MAPALL - resolve all variables
         *  - MAPBUILD - map only build variables
         *  - MAPINSTALL - map only install variables
         *  - MAPNONE - map no variables
         */

        setmapmode(MAPINSTALL);

        /* set sane umask */

        (void) umask(0022);

        /* initially no source "device" */

        device = NULL;

        /* reset npkgs (used as pkg remaining count in quit.c) */

        npkgs = 0;

        /* Read PKG_INSTALL_ROOT from the environment, if it's there. */

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

        pkgserversetmode(DEFAULTMODE);

        /* parse command line options */

        while ((c = getopt(argc, argv,
                "?Aa:B:b:Cc:D:d:eFf:GhIiMm:N:noO:p:R:r:StV:vyz")) != EOF) {

                switch (c) {

                /*
                 * Same as pkgadd: This disables attribute checking.
                 * It speeds up installation a little bit.
                 */
                case 'A':
                        disableAttributes++;
                        break;

                /*
                 * Same as pkgadd: Define an installation administration
                 * file, admin, to be used in place of the default
                 * administration file.  The token none overrides the use
                 * of any admin file, and thus forces interaction with the
                 * user. Unless a full path name is given, pkgadd first
                 * looks in the current working directory for the
                 * administration file.  If the specified administration
                 * file is not in the current working directory, pkgadd
                 * looks in the /var/sadm/install/admin directory for the
                 * administration file.
                 */
                case 'a':
                        admnfile = flex_device(optarg, 0);
                        break;

                /*
                 * Same as pkgadd: control block size given to
                 * pkginstall - block size used in read()/write() loop;
                 * default is st_blksize from stat() of source file.
                 */
                case 'B':
                        rw_block_size = optarg;
                        break;

                /*
                 * Same as pkgadd: location where executables needed
                 * by procedure scripts can be found
                 * default is /usr/sadm/install/bin.
                 */
                case 'b':
                        if (!path_valid(optarg)) {
                                progerr(ERR_PATH, optarg);
                                exit(1);
                        }
                        if (isdir(optarg) != 0) {
                                char *p = strerror(errno);
                                progerr(ERR_CANNOT_USE_DIR, optarg, p);
                                exit(1);
                        }
                        (void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
                        break;

                /*
                 * Same as pkgadd: This disables checksum tests on
                 * the source files. It speeds up installation a little bit.
                 */
                case 'C':
                        (void) checksum_off();
                        break;

                /*
                 * Same as pkgadd: This allows designation of a
                 * continuation file. It is the same format as a dryrun file
                 * but it is used to take up where the dryrun left off.
                 */
                case 'c':
                        pkgcontsrc = optarg;
                        set_continue_mode();
                        set_dr_info(DR_TYPE, INSTALL_TYPE);
                        init_contfile(pkgcontsrc);
                        break;

                /*
                 * Same as pkgadd: This allows designation of a
                 * dryrun file. This pkgadd will create dryrun files
                 * in the directory provided.
                 */
                case 'D':
                        pkgdrtarg = optarg;
                        set_dryrun_mode();
                        set_dr_info(DR_TYPE, INSTALL_TYPE);
                        break;

                /*
                 * Same as pkgadd: Install or copy a package from
                 * device. device can be a full path name to a directory
                 * or the identifiers for tape, floppy disk, or removable
                 * disk - for example, /var/tmp or /floppy/floppy_name.
                 * It can also be a device alias - for example,
                 * /floppy/floppy0, or a datastream created by pkgtrans.
                 */
                case 'd':
                        device = flex_device(optarg, 1);
                        break;

                /*
                 * Different from pkgadd: disable the 32 char name
                 * limit extension
                 */
                case 'e':
                        (void) set_ABI_namelngth();
                        break;

                /*
                 * Different from pkgadd: specify file system type for
                 * the package device. Must be used with -m.
                 */
                case 'f':
                        pkgdev.fstyp = optarg;
                        break;

                /*
                 * Same as pkgadd: install package in global zone only.
                 */
                case 'G':
                        globalZoneOnly = B_TRUE;
                        break;

                /*
                 * Same as pkgadd: Enable hollow package support. When
                 * specified, for any package that has SUNW_PKG_HOLLOW=true:
                 *  Do not calculate and verify package size against target.
                 *  Do not run any package procedure or class action scripts.
                 *  Do not create any target directories.
                 *  Do not perform any script locking.
                 *  Do not install any components of any package.
                 *  Do not output any status or database update messages.
                 */
                case 'h':
                        set_depend_pkginfo_DB(B_TRUE);
                        break;

                /*
                 * Same as pkgadd: Informs scripts that this is
                 * an initial install by setting the environment parameter
                 * PKG_INIT_INSTALL=TRUE for all scripts. They may use it as
                 * they see fit, safe in the knowledge that the target
                 * filesystem is tabula rasa.
                 */
                case 'I':
                        init_install++;
                        break;

                /*
                 * Different from pkgadd: use by pkgask.
                 */
                case 'i':
                        askflag++;
                        quitSetPkgask(B_TRUE);
                        break;

                /*
                 * Same as pkgadd: Instruct pkgadd not to use the
                 * $root_path/etc/vfstab file for determining the client's
                 * mount points. This option assumes the mount points are
                 * correct on the server and it behaves consistently with
                 * Solaris 2.5 and earlier releases.
                 */
                case 'M':
                        map_client = 0;
                        break;

                /*
                 * Different from pkgadd: specify device to use for package
                 * source.
                 */
                case 'm':
                        pkgdev.mount = optarg;
                        pkgdev.rdonly++;
                        pkgdev.mntflg++;
                        break;

                /*
                 * Different from pkgadd: specify program name to use
                 * for messages.
                 */
                case 'N':
                        (void) set_prog_name(optarg);
                        break;

                /*
                 * Same as pkgadd: installation occurs in
                 * non-interactive mode.  Suppress output of the list of
                 * installed files. The default mode is interactive.
                 */
                case 'n':
                        nointeract++;
                        (void) echoSetFlag(B_FALSE);
                        break;

                /*
                 * Almost same as pkgadd: the -O option allows the behavior
                 * of the package tools to be modified. Recognized options:
                 * -> debug
                 * ---> enable debugging output
                 * -> preinstallcheck
                 * ---> perform a "pre installation" check of the specified
                 * ---> package - suppress all regular output and cause a
                 * ---> series of one or more "name=value" pair format lines
                 * ---> to be output that describes the "installability" of
                 * ---> the specified package
                 * -> enable-hollow-package-support
                 * --> Enable hollow package support. When specified, for any
                 * --> package that has SUNW_PKG_HOLLOW=true:
                 * --> Do not calculate and verify package size against target
                 * --> Do not run any package procedure or class action scripts
                 * --> Do not create or remove any target directories
                 * --> Do not perform any script locking
                 * --> Do not install or uninstall any components of any package
                 * --> Do not output any status or database update messages
                 */
                case 'O':
                        for (p = strtok(optarg, ","); p != (char *)NULL;
                                p = strtok(NULL, ",")) {

                                /* process debug option */

                                if (strcmp(p, "debug") == 0) {
                                        /* set debug flag/enable debug output */
                                        if (debugFlag == B_TRUE) {
                                                smlSetVerbose(B_TRUE);
                                        }
                                        debugFlag = B_TRUE;
                                        (void) echoDebugSetFlag(debugFlag);

                                        /* debug info on arguments to pkgadd */
                                        for (n = 0; n < argc && argv[n]; n++) {
                                                echoDebug(DBG_ARG, n, argv[n]);
                                        }

                                        continue;
                                }

                                /* process enable-hollow-package-support opt */

                                if (strcmp(p,
                                        "enable-hollow-package-support") == 0) {
                                        set_depend_pkginfo_DB(B_TRUE);
                                        continue;
                                }

                                /* process preinstallcheck option */

                                if (strcmp(p, "preinstallcheck") == 0) {
                                        preinstallCheck = B_TRUE;
                                        nointeract++;   /* -n */
                                        suppressCopyright++;    /* -S */
                                        quitSetSilentExit(B_TRUE);
                                        continue;
                                }

                                /* process addzonename option */

                                if (strcmp(p, "addzonename") == 0) {
                                        /*
                                         * set zone name to add to messages;
                                         * first look in the current environment
                                         * and use the default package zone name
                                         * if it is set; otherwise, use the name
                                         * of the current zone
                                         */
                                        zoneName =
                                                getenv(PKG_ZONENAME_VARIABLE);

                                        if ((zoneName == (char *)NULL) ||
                                                        (*zoneName == '\0')) {
                                                zoneName = z_get_zonename();
                                        }

                                        if (zoneName != (char *)NULL) {
                                                if (*zoneName != '\0') {
                                                        quitSetZoneName(
                                                                zoneName);
                                                } else {
                                                        zoneName = (char *)NULL;
                                                }
                                        }
                                        continue;
                                }

                                /* process parent-zone-name option */

                                if (strncmp(p, PARENTZONENAME,
                                                PARENTZONENAME_LEN) == 0) {
                                        parentZoneName = p+PARENTZONENAME_LEN;
                                        continue;
                                }

                                /* process parent-zone-type option */

                                if (strncmp(p, PARENTZONETYPE,
                                                PARENTZONETYPE_LEN) == 0) {
                                        parentZoneType = p+PARENTZONETYPE_LEN;
                                        continue;
                                }

                                if (strncmp(p, PKGSERV_MODE,
                                    PKGSERV_MODE_LEN) == 0) {
                                        pkgserversetmode(pkgparsemode(p +
                                            PKGSERV_MODE_LEN));
                                        continue;
                                }

                                /* option not recognized - issue warning */

                                progerr(ERR_INVALID_O_OPTION, p);
                                continue;

                        }
                        break;

                /*
                 * Different from pkgadd: This is an old non-ABI package
                 */
                case 'o':
                        non_abi_scripts++;
                        break;

                /*
                 * Different from pkgadd: specify number of parts to package.
                 */
                case 'p':
                        dparts = ds_getinfo(optarg);
                        break;

                /*
                 * Same as pkgadd: Define the full path name of a
                 * directory to use as the root_path.  All files,
                 * including package system information files, are
                 * relocated to a directory tree starting in the specified
                 * root_path. The root_path may be specified when
                 * installing to a client from a server (for example,
                 * /export/root/client1).
                 */
                case 'R':
                        if (!set_inst_root(optarg)) {
                                progerr(ERR_ROOT_CMD);
                                exit(1);
                        }
                        break;

                /*
                 * Same as pkgadd: Identify a file or directory which
                 * contains output from a previous pkgask(8)
                 * session. This file supplies the interaction responses
                 * that would be requested by the package in interactive
                 * mode. response must be a full pathname.
                 */
                case 'r':
                        respfile = flex_device(optarg, 2);
                        break;

                /*
                 * Same as pkgadd: suppress copyright notice being
                 * output during installation.
                 */
                case 'S':
                        suppressCopyright++;
                        break;

                /*
                 * Same as pkgadd: disable save spool area creation;
                 * do not spool any partial package contents, that is,
                 * suppress the creation and population of the package save
                 * spool area (var/sadm/pkg/PKG/save/pspool/PKG).
                 */
                case 't':
                        disable_spool_create();
                        break;

                /*
                 * Same as pkgadd: Specify an alternative fs_file to map
                 * the client's file systems.  For example, used in
                 * situations where the $root_path/etc/vfstab file is
                 * non-existent or unreliable. Informs the pkginstall
                 * portion to mount up a client filesystem based upon the
                 * supplied vfstab-like file of stable format.
                 */
                case 'V':
                        vfstab_file = flex_device(optarg, 2);
                        map_client = 1;
                        break;

                /*
                 * Same as pkgadd: Trace all of the scripts that get
                 * executed by pkgadd, located in the pkginst/install
                 * directory. This option is used for debugging the
                 * procedural and non-procedural scripts
                 */
                case 'v':
                        pkgverbose++;
                        break;

                /*
                 * Different from pkgadd: process this package using
                 * old non-ABI symlinks
                 */
                case 'y':
                        set_nonABI_symlinks();
                        break;

                /*
                 * Same as pkgadd: perform fresh install from
                 * package save spool area. When set, the package contents
                 * are installed from the package spool save area instead
                 * of from the package root area, so that the original
                 * source packages are not required to install the
                 * package. If the -h option is also specified and the
                 * package is hollow, then this option is ignored. When -z
                 * is specified:
                 *  - Editable files are installed from the package instance
                 *    save area.
                 *  - Volatile files are installed from the package instance
                 *    save area.
                 *  - Executable and data files are installed from the final
                 *    installed location as specified in the pkgmap file.
                 *  - Installation scripts are run from the package spool
                 *    save area.
                 */
                case 'z':
                        saveSpoolInstall++;
                        break;

                /*
                 * unrecognized option
                 */
                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);
                }
        }

        /*
         * ********************************************************************
         * validate command line options
         * ********************************************************************
         */

        /* set "debug echo" flag according to setting of "-O debug" option */

        (void) echoDebugSetFlag(debugFlag);
        (void) log_set_verbose(debugFlag);

        /* output entry debugging information */

        if (z_running_in_global_zone()) {
                echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
        } else {
                echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
                    z_get_zonename());
        }

        if (in_continue_mode() && !in_dryrun_mode()) {
                progerr(ERR_LIVE_CONTINUE_NOT_SUPPORTED);
                usage();
                /*NOTREACHED*/
        }

        /* pkgask requires a response file */

        if (askflag && (respfile == NULL)) {
                usage();
                /*NOTREACHED*/
        }

        /* if device specified, set appropriate device in pkgdev */

        if (device) {
                if (pkgdev.mount) {
                        pkgdev.bdevice = device;
                } else {
                        pkgdev.cdevice = device;
                }
        }

        /* if file system type specified, must have a device to mount */

        if (pkgdev.fstyp && !pkgdev.mount) {
                progerr(ERR_F_REQUIRES_M);
                usage();
                /*NOTREACHED*/
        }

        /* BEGIN DATA GATHERING PHASE */

        /*
         * Get the mount table info and store internally.
         */
        cont_file_read = B_FALSE;
        if (in_continue_mode()) {
                int error;
                cont_file_read = read_continuation(&error);
                if (error == -1) {
                        quit(99);
                        /*NOTREACHED*/
                }
                if (!in_dryrun_mode()) {
                        live_continue = 1;
                }
        }
        /* Read the mount table if not done in continuation mode */
        if (!cont_file_read) {
                if (get_mntinfo(map_client, vfstab_file)) {
                        quit(99);
                        /*NOTREACHED*/
                }
        }

        /*
         * This function defines the standard /var/... directories used later
         * to construct the paths to the various databases.
         */

        set_PKGpaths(get_inst_root());

        /*
         * If this is being installed on a client whose /var filesystem is
         * mounted in some odd way, remap the administrative paths to the
         * real filesystem. This could be avoided by simply mounting up the
         * client now; but we aren't yet to the point in the process where
         * modification of the filesystem is permitted.
         */
        if (is_an_inst_root()) {
                int fsys_value;

                fsys_value = fsys(get_PKGLOC());
                if (use_srvr_map_n(fsys_value))
                        set_PKGLOC(server_map(get_PKGLOC(), fsys_value));

                fsys_value = fsys(get_PKGADM());
                if (use_srvr_map_n(fsys_value))
                        set_PKGADM(server_map(get_PKGADM(), fsys_value));
        }

        /*
         * Initialize pkginfo PKGSAV entry, just in case we dryrun to
         * somewhere else.
         */
        set_infoloc(get_PKGLOC());

        /* pull off directory and package name from end of command line */

        switch (argc-optind) {
        case 0: /* missing directory and package instance */
                progerr(ERR_MISSING_DIR_AND_PKG);
                usage();
                /*NOTREACHED*/
        case 1: /* missing package instance */
                progerr(ERR_MISSING_PKG_INSTANCE);
                usage();
                /*NOTREACHED*/
        case 2: /* just right! */
                pkgdev.dirname = argv[optind++];
                srcinst = argv[optind++];
                break;
        default:        /* too many args! */
                progerr(ERR_TOO_MANY_CMD_ARGS);
                usage();
                break;
        }

        (void) pkgparam(NULL, NULL);  /* close up prior pkg file if needed */

        /*
         * Initialize installation admin parameters by reading
         * the adminfile.
         */

        if (!askflag && !live_continue) {
                echoDebug(DBG_PKGINSTALL_ADMINFILE, admnfile ? admnfile : "");
                setadminFile(admnfile);
        }

        /*
         * about to perform first operation that could be modified by the
         * preinstall check option - if preinstall check is selected (that is,
         * only gathering dependencies), then output a debug message to
         * indicate that the check is beginning. Also turn echo() output
         * off and set various other flags.
         */

        if (preinstallCheck == B_TRUE) {
                (void) echoSetFlag(B_FALSE);
                echoDebug(DBG_PKGINSTALL_PREINSCHK,
                        pkginst ? pkginst : (srcinst ? srcinst : ""),
                        zoneName ? zoneName : "global");
                cksetPreinstallCheck(B_TRUE);
                cksetZoneName(zoneName);
                /* inform quit that the install has started */
                quitSetInstallStarted(B_TRUE);
        }

        /*
         * validate the "rscriptalt" admin file setting
         * The rscriptalt admin file parameter may be set to either
         * RSCRIPTALT_ROOT or RSCRIPTALT_NOACCESS:
         * --> If rscriptalt is not set, or is set to RSCRIPTALT_NOACCESS,
         * --> or is set to any value OTHER than RSCRIPTALT_ROOT, then
         * --> assume that the parameter is set to RSCRIPTALT_NOACCESS
         * If rscriptalt is set to RSCRIPTALT_ROOT, then run request scripts
         * as the "root" user if user "install" is not defined.
         * Otherwise, assume rscriptalt is set to RSCRIPTALT_NOACCESS, and run
         * request scripts as the "alternative" user if user "install" is not
         * defined, as appropriate for the current setting of the NONABI_SCRIPTS
         * environment variable.
         */

        if (ADMSET(RSCRIPTALT)) {
                p = adm.RSCRIPTALT;
                echoDebug(DBG_PKGINSTALL_RSCRIPT_SET_TO, RSCRIPTALT_KEYWORD, p);
                if (strcasecmp(p, RSCRIPTALT_ROOT) == 0) {
                        /* rscriptalt=root */
                        run_request_as_root = B_TRUE;
                } else if (strcasecmp(p, RSCRIPTALT_NOACCESS) == 0) {
                        /* rscriptalt=noaccess */
                        run_request_as_root = B_FALSE;
                } else {
                        /* rscriptalt=??? */
                        logerr(WRN_RSCRIPTALT_BAD, RSCRIPTALT_KEYWORD, p,
                                RSCRIPTALT_ROOT, RSCRIPTALT_NOACCESS);
                        logerr(WRN_RSCRIPTALT_USING, RSCRIPTALT_KEYWORD,
                                RSCRIPTALT_NOACCESS);
                        run_request_as_root = B_FALSE;
                }
        } else {
                /* rscriptalt not set - assume rscriptalt=noaccess */
                echoDebug(DBG_PKGINSTALL_RSCRIPT_NOT_SET, RSCRIPTALT_KEYWORD);
                run_request_as_root = B_FALSE;
        }

        echoDebug(DBG_PKGINSTALL_RSCRIPT_IS_ROOT, run_request_as_root);

        /*
         * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
         */

        /* hold SIGINT/SIGHUP interrupts */

        (void) sighold(SIGHUP);
        (void) sighold(SIGINT);

        /* connect quit.c:trap() to SIGINT */

        nact.sa_handler = quitGetTrapHandler();
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        (void) sigaction(SIGINT, &nact, &oact);

        /* connect quit.c:trap() to SIGHUP */

        nact.sa_handler = quitGetTrapHandler();
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        (void) sigaction(SIGHUP, &nact, &oact);

        /* release hold on signals */

        (void) sigrelse(SIGHUP);
        (void) sigrelse(SIGINT);

        /*
         * create required /var... directories if they do not exist;
         * this function will call quit(99) if any required path cannot
         * be created.
         */

        ckdirs();

        tzset();

        /*
         * create path to temporary directory "installXXXXXX" - if TMPDIR
         * environment variable is set, create the directory in $TMPDIR;
         * otherwise, create the directory in P_tmpdir.
         */

        pt = getenv("TMPDIR");
        (void) snprintf(tmpdir, sizeof (tmpdir), "%s/installXXXXXX",
                ((pt != (char *)NULL) && (*pt != '\0')) ? pt : P_tmpdir);

        echoDebug(DBG_PKGINSTALL_TMPDIR, tmpdir);

        if ((mktemp(tmpdir) == NULL) || mkdir(tmpdir, 0771)) {
                progerr(ERR_MKDIR, tmpdir);
                quit(99);
                /*NOTREACHED*/
        }

        /*
         * if the package device is a file containing a package stream,
         * unpack the stream into a temporary directory
         */

        if ((isdir(pkgdev.dirname) != 0) &&
                (pkgdev.cdevice == (char *)NULL) &&
                (pkgdev.bdevice == (char *)NULL) &&
                (isfile((char *)NULL, pkgdev.dirname) == 0)) {

                char            *idsName = (char *)NULL;
                char            *pkgnames[2];
                char            *device = pkgdev.dirname;
                boolean_t       b;

                echoDebug(DBG_PKGINSTALL_DS_ISFILE, pkgdev.dirname);

                /*
                 * validate the package source device - return pkgdev info that
                 * describes the package source device.
                 */

                if (devtype(device, &pkgdev)) {
                        progerr(ERR_BAD_DEVICE, device);
                        quit(99);
                        /* NOTREACHED */
                }

                /* generate the list of packages to verify */

                pkgnames[0] = srcinst;
                pkgnames[1] = (char *)NULL;

                b = open_package_datastream(1, pkgnames, (char *)NULL,
                        pkgdev.dirname, (int *)NULL, &idsName, tmpdir, &pkgdev,
                        1);

                if (b == B_FALSE) {
                        progerr(ERR_CANNOT_OPEN_PKG_STREAM,
                                pkgdev.dirname ? pkgdev.dirname : "?");
                        quit(99);
                        /*NOTREACHED*/
                }

                /* make sure temporary directory is removed on exit */

                quitSetDstreamTmpdir(pkgdev.dirname);

                /* unpack the package instance from the data stream */

                b = unpack_package_from_stream(idsName, srcinst,
                                                        pkgdev.dirname);
                if (b == B_FALSE) {
                        progerr(ERR_CANNOT_UNPACK_PKGSTRM,
                                srcinst ? srcinst : "?",
                                idsName ? idsName : "?",
                                pkgdev.dirname ? pkgdev.dirname : "?");
                        quit(99);
                        /*NOTREACHED*/
                }

                /* close the datastream - no longer needed */

                echoDebug(DBG_CLOSING_STREAM, idsName, pkgdev.dirname);
                (void) ds_close(1);
        }

        if (snprintf(instdir, PATH_MAX, "%s/%s", pkgdev.dirname, srcinst)
            >= PATH_MAX) {
                progerr(ERR_SNPRINTF, instdir);
                quit(99);
                /*NOTREACHED*/
        }

        zoneName = getenv(PKG_ZONENAME_VARIABLE);

        /*
         * If the environment has a CLIENT_BASEDIR, that takes precedence
         * over anything we will construct. We need to save it here because
         * in three lines, the current environment goes away.
         */
        (void) set_env_cbdir(); /* copy over environ */

        getuserlocale();

        /*
         * current environment has been read; clear environment out
         * so putparam() can be used to populate the new environment
         * to be passed to any executables/scripts.
         */

        environ = NULL;

        /* write parent condition information to environment */

        putConditionInfo(parentZoneName, parentZoneType);

        putuserlocale();

        if (init_install) {
                putparam("PKG_INIT_INSTALL", "TRUE");
        }

        if (is_an_inst_root()) {
                export_client_env(get_inst_root());
        }

        if (zoneName != (char *)NULL) {
                putparam(PKG_ZONENAME_VARIABLE, zoneName);
        }

        putparam("INST_DATADIR", pkgdev.dirname);

        if (non_abi_scripts) {
                putparam("NONABI_SCRIPTS", "TRUE");
        }

        if (nonABI_symlinks()) {
                putparam("PKG_NONABI_SYMLINKS", "TRUE");
        }

        if (get_ABI_namelngth()) {
                putparam("PKG_ABI_NAMELENGTH", "TRUE");
        }

        /* establish path and oambase */

        if (cmdbin[0] == '\0') {
                (void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
        }

        (void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);

        putparam("PATH", path);

        putparam("OAMBASE", OAMBASE);

        (void) snprintf(p_pkginfo, sizeof (p_pkginfo),
                        "%s/%s", instdir, PKGINFO);
        (void) snprintf(p_pkgmap, sizeof (p_pkgmap),
                        "%s/%s", instdir, PKGMAP);

        /* Read the environment (from pkginfo or '-e') ... */
        abi_nm_ptr = getenv("PKG_ABI_NAMELENGTH");

        /* Disable the 32 char name limit extension */
        if (abi_nm_ptr && strncasecmp(abi_nm_ptr, "TRUE", 4) == 0) {
                (void) set_ABI_namelngth();
        }

        /*
         * This tests the pkginfo and pkgmap files for validity and
         * puts all delivered pkginfo variables (except for PATH) into
         * our environment. This is where a delivered pkginfo BASEDIR
         * would come from. See set_basedirs() below.
         */

        if (pkgenv(srcinst, p_pkginfo, p_pkgmap)) {
                quit(1);
                /*NOTREACHED*/
        }

        echo("\n%s(%s) %s", pkgname, pkgarch, pkgvers);

        /*
         * If this script was invoked by 'pkgask', just
         * execute request script and quit (do_pkgask()).
         */

        if (askflag) {
                do_pkgask(run_request_as_root);
        }

        /* validate package contents file */

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

        /* if not in dryrun mode aquire packaging lock */

        if (!in_dryrun_mode()) {
                /* acquire the package lock - at install initialization */
                if (!lockinst(get_prog_name(), srcinst, "install-initial")) {
                        quit(99);
                        /*NOTREACHED*/
                }
        }

        /*
         * Now do all the various setups based on ABI compliance
         */

        /* Read the environment (from pkginfo or '-o') ... */
        abi_comp_ptr = getenv("NONABI_SCRIPTS");

        /* Read the environment (from pkginfo or '-y') ... */
        abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");

        /* bug id 4244631, not ABI compliant */
        if (abi_comp_ptr && strncasecmp(abi_comp_ptr, "TRUE", 4) == 0) {
                script_in = PROC_XSTDIN;
                non_abi_scripts = 1;
        }

        /* Set symlinks to be processed the old way */
        if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
                set_nonABI_symlinks();
        }

        /*
         * At this point, script_in, non_abi_scripts & the environment are
         * all set correctly for the ABI status of the package.
         */

        if (pt = getenv("MAXINST")) {
                maxinst = atol(pt);
        }

        /*
         * See if were are installing a package that only wants to update
         * the database or only install files associated with CAS's. We
         * only check the PKG_HOLLOW_VARIABLE variable if told to do so by
         * the caller.
         */

        if (is_depend_pkginfo_DB()) {
                pt = getenv(PKG_HOLLOW_VARIABLE);
                if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
                        echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
                        if (disableAttributes) {
                                disable_attribute_check();
                        }

                        /*
                         * this is a hollow package and hollow package support
                         * is enabled -- override admin settings to suppress
                         * checks that do not make sense since no scripts will
                         * be executed and no files will be installed.
                         */

                        setadminSetting("conflict", "nocheck");
                        setadminSetting("setuid", "nocheck");
                        setadminSetting("action", "nocheck");
                        setadminSetting("partial", "nocheck");
                        setadminSetting("space", "nocheck");
                        setadminSetting("authentication", "nocheck");
                } else {
                        echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
                        set_depend_pkginfo_DB(B_FALSE);
                }
        }

        /*
         * if performing a fresh install to a non-global zone, and doing
         * more than just updating the package database (that is, the
         * package to install is NOT "hollow"), then set the global flag
         * that directs installation is from partially spooled packages
         * (that is, packages installed in the global zone).
         */

        if (saveSpoolInstall && (!is_depend_pkginfo_DB())) {
                set_partial_inst();
        } else {
                saveSpoolInstall = 0;
        }

        /*
         * verify that we are not trying to install an
         * INTONLY package with no interaction
         */

        if (pt = getenv("INTONLY")) {
                if (askflag || nointeract) {
                        progerr(ERR_INTONLY, pkgabrv ? pkgabrv : "?");
                        quit(1);
                        /*NOTREACHED*/
                }
        }

        if (!suppressCopyright && !pkgdev.cdevice) {
                copyright();
        }

        /*
         * inspect the system to determine if any instances of the
         * package being installed already exist on the system
         */

        prvinfo = (struct pkginfo *)calloc(MALSIZ, sizeof (struct pkginfo));
        if (prvinfo == NULL) {
                progerr(ERR_MEMORY, errno);
                quit(99);
                /*NOTREACHED*/
        }

        for (;;) {
                if (pkginfo(&prvinfo[npkgs], pkgwild, NULL, NULL)) {
                        if ((errno == ESRCH) || (errno == ENOENT)) {
                                break;
                        }
                        progerr(ERR_SYSINFO, errno);
                        quit(99);
                        /*NOTREACHED*/
                }
                if ((++npkgs % MALSIZ) == 0) {
                        prvinfo = (struct pkginfo *)realloc(prvinfo,
                                (npkgs+MALSIZ) * sizeof (struct pkginfo));
                        if (prvinfo == NULL) {
                                progerr(ERR_MEMORY, errno);
                                quit(99);
                                /*NOTREACHED*/
                        }
                }
        }

        /*
         * Determine the correct package instance based on how many packages are
         * already installed. If there are none (npkgs == 0), getinst() just
         * returns the package abbreviation. Otherwise, getinst() interacts with
         * the user (or reads the admin file) to determine if an instance which
         * is already installed should be overwritten, or possibly install a new
         * instance of this package
         */

        pkginst = getinst(&update, prvinfo, npkgs, preinstallCheck);

        /* set "update flag" if updating an existing instance of this package */

        if (update) {
                setUpdate();
        }

        /*
         * Some pkgs (SUNWcsr) already spooled to the zone, check the
         * value of UPDATE in their postinstall script.  After a pkg
         * has been patched UPDATE exists statically in the pkginfo
         * file and this value must be reset when installing a zone.
         */

        if (saveSpoolInstall != 0 && !isUpdate()) {
                putparam("UPDATE", "");
        }

        /* inform quit() if updating existing or installing new instance */

        quitSetUpdatingExisting(update ? B_TRUE : B_FALSE);

        if (respfile) {
                (void) set_respfile(respfile, pkginst, RESP_RO);
        }

        (void) snprintf(pkgloc, sizeof (pkgloc),
                        "%s/%s", get_PKGLOC(), pkginst);

        (void) snprintf(pkgbin, sizeof (pkgbin),
                        "%s/install", pkgloc);

        (void) snprintf(pkgsav, sizeof (pkgsav),
                        "%s/save", pkgloc);

        if (snprintf(saveSpoolInstallDir, PATH_MAX, "%s/pspool/%s", pkgsav,
                        pkginst) < 0) {
                progerr(ERR_SNPRINTF, saveSpoolInstallDir);
                quit(99);
                /*NOTREACHED*/
        }

        (void) snprintf(ilockfile, sizeof (ilockfile),
                        "%s/!I-Lock!", pkgloc);
        (void) snprintf(rlockfile, sizeof (rlockfile),
                        "%s/!R-Lock!", pkgloc);
        (void) snprintf(savlog, sizeof (savlog),
                        "%s/logs/%s", get_PKGADM(), pkginst);

        putparam("PKGINST", pkginst);
        putparam("PKGSAV", pkgsav);

        /*
         * Be sure request script has access to PKG_INSTALL_ROOT if there is
         * one
         */

        put_path_params();

        if (!map_client) {
                putparam("PKG_NO_UNIFIED", "TRUE");
        }

        /*
         * This maps the client filesystems into the server's space.
         */

        if (map_client && !mount_client()) {
                logerr(MSG_MANMOUNT);
        }

        /*
         * If this is an UPDATE then either this is exactly the same version
         * and architecture of an installed package or a different package is
         * intended to entirely replace an installed package of the same name
         * with a different VERSION or ARCH string.
         * Don't merge any databases if only gathering dependencies.
         */

        if ((preinstallCheck == B_FALSE) && (update)) {
                /*
                 * If this version and architecture is already installed,
                 * merge the installed and installing parameters and inform
                 * all procedure scripts by defining UPDATE in the
                 * environment.
                 */

                if (is_samepkg()) {
                        /*
                         * If it's the same ARCH and VERSION, then a merge
                         * and copy operation is necessary.
                         */

                        if (n = merg_pkginfos(pclass, &mergd_pclass)) {
                                quit(n);
                                /*NOTREACHED*/
                        }

                        if (n = cp_pkgdirs()) {
                                quit(n);
                                /*NOTREACHED*/
                        }

                } else {
                        /*
                         * If it's a different ARCH and/or VERSION then this
                         * is an "instance=overwrite" situation. The
                         * installed base needs to be confirmed and the
                         * package directories renamed.
                         */

                        if (n = ck_instbase()) {
                                quit(n);
                                /*NOTREACHED*/
                        }

                        if (n = mv_pkgdirs()) {
                                quit(n);
                                /*NOTREACHED*/
                        }
                }

                putparam("UPDATE", "yes");

        }

        if (in_dryrun_mode()) {
                set_dryrun_dir_loc();
        }

        if (preinstallCheck == B_FALSE) {
                /*
                 * Determine if the package has been partially installed on or
                 * removed from this system.
                 */
                ck_w_dryrun(ckpartial, PARTIAL);

                /*
                 * make sure current runlevel is appropriate
                 */
                ck_w_dryrun(ckrunlevel, RUNLEVEL);
        } else {
                int     r;

                /*
                 * Just gathering dependencies - determine if the package has
                 * been partially installed on or removed from this system and
                 * output information to stdout
                 */
                r = ckpartial();
                (void) fprintf(stdout, "ckpartialinstall=%d\n", r == 8 ? 1 : 0);
                (void) fprintf(stdout, "ckpartialremove=%d\n", r == 9 ? 1 : 0);

                /*
                 * make sure current runlevel is appropriate
                 */
                r = ckrunlevel();
                (void) fprintf(stdout, "ckrunlevel=%d\n", r);
        }

        if (pkgdev.cdevice) {
                /* get first volume which contains info files */
                unpack();
                if (!suppressCopyright) {
                        copyright();
                }
        }

        /* update the lock - at the request script */

        lockupd("request");

        /*
         * If no response file has been provided, initialize response file by
         * executing any request script provided by this package. Initialize
         * the response file if not gathering dependencies only.
         */

        if ((!rdonly_respfile()) && (preinstallCheck == B_FALSE)) {
                (void) snprintf(path, sizeof (path),
                        "%s/%s", instdir, REQUEST_FILE);
                n = reqexec(update, path, non_abi_scripts,
                        run_request_as_root);
                if (in_dryrun_mode()) {
                        set_dr_info(REQUESTEXITCODE, n);
                }

                ckreturn(n, ERR_REQUEST);
        }

        /*
         * Look for all parameters in response file which begin with a
         * capital letter, and place them in the environment.
         */

        if ((is_a_respfile()) && (preinstallCheck == B_FALSE)) {
                if (n = merg_respfile()) {
                        quit(n);
                        /*NOTREACHED*/
                }
        }

        /*
         * Run a checkinstall script if one is provided by the package.
         * Don't execute checkinstall script if we are only updating the DB.
         * Don't execute checkinstall script if only gathering dependencies.
         */

        /* update the lock - at the checkinstall script */
        lockupd("checkinstall");

        /* Execute checkinstall script if one is provided. */
        (void) snprintf(script, sizeof (script), "%s/install/checkinstall",
                        instdir);
        if (access(script, F_OK) != 0) {
                /* no script present */
                echoDebug(DBG_PKGINSTALL_COC_NONE, pkginst, script,
                        zoneName ? zoneName : "global");
        } else if (is_depend_pkginfo_DB()) {
                /* updating db only: skip checkinstall script */
                echoDebug(DBG_PKGINSTALL_COC_DBUPD, pkginst, script,
                        zoneName ? zoneName : "global");
        } else if (preinstallCheck == B_TRUE) {
                /* only gathering dependencies: skip checkinstall script */
                echoDebug(DBG_PKGINSTALL_COC_NODEL, pkginst, script,
                        zoneName ? zoneName : "global");
        } else {
                /* script present and ok to run: run the script */
                if (zoneName == (char *)NULL) {
                        echo(MSG_PKGINSTALL_EXECOC_GZ);
                        echoDebug(DBG_PKGINSTALL_EXECOC_GZ, pkginst, script);
                } else {
                        echo(MSG_PKGINSTALL_EXECOC_LZ, zoneName);
                        echoDebug(DBG_PKGINSTALL_EXECOC_LZ, pkginst, script,
                                zoneName);
                }
                n = chkexec(update, script);
                if (in_dryrun_mode()) {
                        set_dr_info(CHECKEXITCODE, n);
                }

                if (n == 3) {
                        echo(WRN_CHKINSTALL);
                        ckreturn(4, NULL);
                } else if (n == 7) {
                        /* access returned error */
                        progerr(ERR_CHKINSTALL_NOSCRIPT, script);
                        ckreturn(4, ERR_CHKINSTALL);
                } else {
                        ckreturn(n, ERR_CHKINSTALL);
                }
        }

        /*
         * Now that the internal data structures are initialized, we can
         * initialize the dryrun files (which may be the same files).
         */

        if (pkgdrtarg) {
                init_dryrunfile(pkgdrtarg);
        }

        /*
         * Look for all parameters in response file which begin with a
         * capital letter, and place them in the environment.
         */
        if (is_a_respfile()) {
                if (n = merg_respfile()) {
                        quit(n);
                        /*NOTREACHED*/
                }
        }

        /* update the lock - doing analysis */

        lockupd("analysis");

        /*
         * Determine package base directory and client base directory
         * if appropriate. Then encapsulate them for future retrieval.
         */
        if ((err = set_basedirs(isreloc(instdir), adm.basedir, pkginst,
                nointeract)) != 0) {
                quit(err);
                /*NOTREACHED*/
        }

        /*
         * Create the base directory if specified.
         * Don't create if we are only updating the DB.
         * Don't create if only gathering dependencies.
         */

        if (!is_depend_pkginfo_DB() &&
                !preinstallCheck && is_a_basedir()) {
                mkbasedir(!nointeract, get_basedir());
                echo(MSG_BASE_USED, get_basedir());
        }

        /*
         * Store PKG_INSTALL_ROOT, BASEDIR & CLIENT_BASEDIR in our
         * environment for later use by procedure scripts.
         */
        put_path_params();

        /*
         * the following two checks are done in the corresponding
         * ck() routine, but are repeated here to avoid re-processing
         * the database if we are administered to not include these
         * processes
         */
        if (ADM(setuid, "nochange")) {
                nosetuid++;     /* Clear setuid/gid bits. */
        }

        if (ADM(conflict, "nochange")) {
                nocnflct++;     /* Don't install conflicting files. */
        }

        /*
         * Get the filesystem space information for the filesystem on which
         * the "contents" file resides.
         */

        svfsb.f_bsize = 8192;
        svfsb.f_frsize = 1024;

        if (statvfs64(get_PKGADM(), &svfsb) == -1) {
                int     lerrno = errno;
                if (!access(get_PKGADM(), F_OK)) {
                        progerr(ERR_PKGINSTALL_STATVFS, get_PKGADM(),
                                strerror(errno));
                        logerr("(errno %d)", lerrno);
                        quit(99);
                        /*NOTREACHED*/
                }
        }

        /*
         * Get the number of blocks used by the pkgmap, ocfile()
         * needs this to properly determine its space requirements.
         */

        if (stat(p_pkgmap, &statb) == -1) {
                progerr(ERR_PKGINSTALL_STATOF, p_pkgmap, strerror(errno));
                quit(99);
                /*NOTREACHED*/
        }

        pkgmap_blks = nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);

        /*
         * Merge information in memory with the "contents" file; this creates
         * a temporary version of the "contents" file. Note that in dryrun
         * mode, we still need to record the contents file data somewhere,
         * but we do it in the dryrun directory.
         */

        if (in_dryrun_mode()) {
                if (n = set_cfdir(pkgdrtarg)) {
                        quit(n);
                        /*NOTREACHED*/
                }
        } else {
                if (n = set_cfdir(NULL)) {
                        quit(n);
                        /*NOTREACHED*/
                }
        }
        if (!ocfile(&pkgserver, &cfTmpVfp, pkgmap_blks)) {
                quit(99);
                /*NOTREACHED*/
        }

        /*
         * if cpio is being used,  tell pkgdbmerg since attributes will
         * have to be check and repaired on all file and directories
         */
        for (np = cpio_names; *np != NULL; np++) {
                (void) snprintf(path, sizeof (path),
                        "%s/%s", instdir, *np);
                if (iscpio(path, &is_comp_arch)) {
                        is_WOS_arch();
                        break;
                }
        }

        /* Establish the class list and the class attributes. */
        cl_sets(getenv("CLASSES"));
        find_CAS(I_ONLY, pkgbin, instdir);

        if (vfpOpen(&pkgmapVfp, p_pkgmap, "r", VFP_NEEDNOW) != 0) {
                progerr(ERR_PKGMAP, p_pkgmap);
                quit(99);
                /*NOTREACHED*/
        }

        /*
         * This modifies the path list entries in memory to reflect
         * how they should look after the merg is complete
         */

        nparts = sortmap(&extlist, pkgmapVfp, pkgserver, cfTmpVfp, zoneName);

        if ((n = files_installed()) > 0) {
                if (n > 1) {
                        echo(MSG_INST_MANY, n);
                } else {
                        echo(MSG_INST_ONE, n);
                }
        }

        /*
         * Check ulimit requirement (provided in pkginfo). The purpose of
         * this limit is to terminate pathological file growth resulting from
         * file edits in scripts. It does not apply to files in the pkgmap
         * and it does not apply to any database files manipulated by the
         * installation service.
         */
        if (pt = getenv("ULIMIT")) {
                if (assign_ulimit(pt) == -1) {
                        progerr(ERR_BADULIMIT, pt);
                        quit(99);
                        /*NOTREACHED*/
                }
                putparam("PKG_ULIMIT", "TRUE");
        }

        /*
         * If only gathering dependencies, check and output status of all
         * remaining dependencies and exit.
         */

        if (preinstallCheck == B_TRUE) {
                /* update the lock file - final checking */

                lockupd("preinstallcheck");

                /* verify package information files are not corrupt */

                (void) fprintf(stdout, "ckpkgfiles=%d\n", ckpkgfiles());

                /* verify package dependencies */

                (void) fprintf(stdout, "ckdepend=%d\n", ckdepend());

                /* Check space requirements */

                (void) fprintf(stdout, "ckspace=%d\n", ckspace());

                /*
                 * Determine if any objects provided by this package conflict
                 * with the files of previously installed packages.
                 */

                (void) fprintf(stdout, "ckconflict=%d\n", ckconflct());

                /*
                 * Determine if any objects provided by this package will be
                 * installed with setuid or setgid enabled.
                 */

                (void) fprintf(stdout, "cksetuid=%d\n", cksetuid());

                /*
                 * Determine if any packaging scripts provided with this package
                 * will execute as a priviledged user.
                 */

                (void) fprintf(stdout, "ckpriv=%d\n", ckpriv());

                /* Verify neccessary package installation directories exist */

                (void) fprintf(stdout, "ckpkgdirs=%d\n", ckpkgdirs());

                /*
                 * ****** preinstall check done - exit ******
                 */

                echoDebug(DBG_PKGINSTALL_PREINSCHK_OK);
                quit(0);
                /*NOTREACHED*/
        }

        /*
         * Not gathering dependencies only, proceed to check dependencies
         * and continue with the package installation operation.
         */

        /*
         * verify package information files are not corrupt
         */
        ck_w_dryrun(ckpkgfiles, PKGFILES);

        /*
         * verify package dependencies
         */
        ck_w_dryrun(ckdepend, DEPEND);

        /*
         * Check space requirements.
         */
        ck_w_dryrun(ckspace, SPACE);

        /*
         * Determine if any objects provided by this package conflict with
         * the files of previously installed packages.
         */
        ck_w_dryrun(ckconflct, CONFLICT);

        /*
         * Determine if any objects provided by this package will be
         * installed with setuid or setgid enabled.
         */
        ck_w_dryrun(cksetuid, SETUID);

        /*
         * Determine if any packaging scripts provided with this package will
         * execute as a priviledged user.
         */
        ck_w_dryrun(ckpriv, PRIV);

        /*
         * Verify neccessary package installation directories exist.
         */
        ck_w_dryrun(ckpkgdirs, PKGDIRS);

        /*
         * If we have assumed that we were installing setuid or conflicting
         * files, and the user chose to do otherwise, we need to read in the
         * package map again and re-merg with the "contents" file
         */

        if (rprcflag) {
                nparts = sortmap(&extlist, pkgmapVfp, pkgserver,
                                cfTmpVfp, zoneName);
        }

        (void) vfpClose(&pkgmapVfp);

        /* BEGIN INSTALLATION PHASE */
        if (in_dryrun_mode()) {
                echo(MSG_PKGINSTALL_DRYRUN, pkgname, pkginst);
        } else if (zoneName == (char *)NULL) {
                echo(MSG_PKGINSTALL_INSIN_GZ, pkgname, pkginst);
        } else {
                echo(MSG_PKGINSTALL_INSIN_LZ, pkgname, pkginst, zoneName);
        }

        /* inform quit that the install has started */

        quitSetInstallStarted(B_TRUE);

        /*
         * This replaces the contents file with recently created temp version
         * which contains information about the objects being installed.
         * Under old lock protocol it closes both files and releases the
         * locks. Beginning in Solaris 2.7, this lock method should be
         * reviewed.
         */

        n = swapcfile(pkgserver, &cfTmpVfp, pkginst, dbchg);
        if (n == RESULT_WRN) {
                warnflag++;
        } else if (n == RESULT_ERR) {
                quit(99);
                /*NOTREACHED*/
        }

        /*
         * Create install-specific lockfile to indicate start of
         * installation. This is really just an information file. If the
         * process dies, the initial lockfile (from lockinst(), is
         * relinquished by the kernel, but this one remains in support of the
         * post-mortem.
         */

        if (access(ilockfile, F_OK) == 0) {
                (void) remove(ilockfile);
        }

        if (open(ilockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644) < 0) {
                progerr(ERR_LOCKFILE, ilockfile);
                quit(99);
                /*NOTREACHED*/
        }

        (void) time(&clock);

        /*
         * We do not want the time in locale in the pkginfo.
         * save the LC_TIME and set it to C. Reset it with saved one
         * after cftime().
         */
        temp = setlocale(LC_TIME, NULL);
        (void) setlocale(LC_TIME, "C");

        /* LINTED warning: do not use cftime(); ... */
        (void) cftime(cbuf, "%b %d \045Y \045H:\045M", &clock);
        putparam("INSTDATE", qstrdup(cbuf));
        (void) setlocale(LC_TIME, temp);

        /*
         * Store information about package being installed;
         * modify installation parameters as neccessary and
         * copy contents of 'install' directory into $pkgloc
         */
        merginfo(mergd_pclass, saveSpoolInstall);

        /* If this was just a dryrun, then quit() will write out that file. */
        if (in_dryrun_mode()) {
                quit(0);
                /*NOTREACHED*/
        }

        /*
         * Execute preinstall script, if one was provided with the
         * package. We check the package to avoid running an old
         * preinstall script if one was provided with a prior instance.
         * Don't execute preinstall script if we are only updating the DB.
         */

        /* update the lock - at the preinstall altscript */
        lockupd("preinstall");

        /* preinstall script in the media (package source) */
        (void) snprintf(altscript, sizeof (altscript), "%s/install/preinstall",
                        instdir);

        /* preinstall script in the pkgbin instead of media */
        (void) snprintf(script, sizeof (script), "%s/preinstall", pkgbin);

        if (access(altscript, F_OK) != 0) {
                /* no script present */
                echoDebug(DBG_PKGINSTALL_POCALT_NONE, pkginst, altscript,
                        zoneName ? zoneName : "global");
        } else if (access(script, F_OK) != 0) {
                /* no script present */
                echoDebug(DBG_PKGINSTALL_POC_NONE, pkginst, script,
                        zoneName ? zoneName : "global");
        } else if (is_depend_pkginfo_DB()) {
                /* updating db only: skip preinstall script */
                echoDebug(DBG_PKGINSTALL_POC_DBUPD, pkginst, script,
                        zoneName ? zoneName : "global");
        } else {
                /* script present and ok to run: run the script */
                assert(preinstallCheck == B_FALSE);

                set_ulimit("preinstall", ERR_PREINSTALL);
                if (zoneName == (char *)NULL) {
                        echo(MSG_PKGINSTALL_EXEPOC_GZ);
                        echoDebug(DBG_PKGINSTALL_EXEPOC_GZ, pkginst, script);
                } else {
                        echo(MSG_PKGINSTALL_EXEPOC_LZ, zoneName);
                        echoDebug(DBG_PKGINSTALL_EXEPOC_LZ, pkginst, script,
                                zoneName);
                }
                putparam("PKG_PROC_script", "preinstall");
                if (pkgverbose) {
                        ckreturn(pkgexecl(script_in, PROC_STDOUT,
                                PROC_USER, PROC_GRP, SHELL, "-x",
                                script, NULL), ERR_PREINSTALL);
                } else {
                        ckreturn(pkgexecl(script_in, PROC_STDOUT,
                                PROC_USER, PROC_GRP, SHELL, script,
                                NULL), ERR_PREINSTALL);
                }

                clr_ulimit();
                (void) remove(script);  /* no longer needed. */
        }

        /*
         * Check delivered package for a postinstall script while
         * we're still on volume 1.
         */

        (void) snprintf(script, sizeof (script),
                        "%s/install/postinstall", instdir);
        if (access(script, F_OK) == 0) {
                (void) snprintf(script, sizeof (script),
                                        "%s/postinstall", pkgbin);
        } else {
                script[0] = '\0';
        }

        /* update the lock - at the install phase */

        lockupd("install");

        /*
         * install package one part (volume) at a time
         */

        part = 1;
        while (part <= nparts) {
                if ((part > 1) && pkgdev.cdevice) {
                        unpack();
                }

                instvol(extlist, srcinst, part, nparts,
                        pkgserver, &cfTmpVfp, &updated, zoneName);

                if (part++ >= nparts) {
                        break;
                }
        }

        z_destroyMountTable();

        /*
         * Now that all install class action scripts have been used, we
         * delete them from the package directory.
         */
        rm_icas(pkgbin);

        if (globalZoneOnly) {
                boolean_t   b;
                b = pkgAddPackageToGzonlyList(pkginst, get_inst_root());
                if (b == B_FALSE) {
                        progerr(ERR_PKGINSTALL_GZONLY_ADD, pkginst);
                        ckreturn(1, NULL);
                }
        }

        /*
         * Execute postinstall script, if any
         * Don't execute postinstall script if we are only updating the DB.
         */

        echoDebug(DBG_PKGINSTALL_INSDONE, is_depend_pkginfo_DB(),
                is_depend_pkginfo_DB(), saveSpoolInstall,
                updated ? updated : "",
                script ? script : "",
                script ? access(script, F_OK) : -1);

        /* update the lock - at the postinstall script */
        lockupd("postinstall");

        if ((script == (char *)NULL) || (*script == '\0')) {
                echoDebug(DBG_PKGINSTALL_POIS_NOPATH, pkginst,
                        zoneName ? zoneName : "global");
        } else if (access(script, F_OK) != 0) {
                echoDebug(DBG_PKGINSTALL_POIS_NONE, pkginst, script,
                        zoneName ? zoneName : "global");
        } else if (is_depend_pkginfo_DB()) {
                echoDebug(DBG_PKGINSTALL_POIS_DBUPD, pkginst, script,
                        zoneName ? zoneName : "global");
        } else if ((saveSpoolInstall != 0) && (updated == (char *)NULL)) {
                /*
                 * fresh installing into non-global zone, no object was
                 * updated (installed/verified in area), so do not run
                 * the postinstall script.
                 */
                echoDebug(DBG_PKGINSTALL_POIS_NOUPDATING,
                        zoneName ? zoneName : "global", pkginst, script);
        } else {
                /* script present and ok to run: run the script */
                set_ulimit("postinstall", ERR_POSTINSTALL);
                if (zoneName == (char *)NULL) {
                        echo(MSG_PKGINSTALL_EXEPIC_GZ);
                        echoDebug(DBG_PKGINSTALL_EXEPIC_GZ, pkginst, script);
                } else {
                        echo(MSG_PKGINSTALL_EXEPIC_LZ, zoneName);
                        echoDebug(DBG_PKGINSTALL_EXEPIC_LZ, pkginst, script,
                                zoneName);
                }
                putparam("PKG_PROC_SCRIPT", "postinstall");
                putparam("TMPDIR", tmpdir);
                if (pkgverbose) {
                        ckreturn(pkgexecl(script_in, PROC_STDOUT,
                                PROC_USER, PROC_GRP, SHELL, "-x",
                                script, NULL), ERR_POSTINSTALL);
                } else {
                        ckreturn(pkgexecl(script_in, PROC_STDOUT,
                                PROC_USER, PROC_GRP, SHELL, script,
                                NULL), ERR_POSTINSTALL);
                }

                clr_ulimit();
                (void) remove(script);  /* no longer needed */
        }

        if (!warnflag && !failflag) {
                (void) remove(rlockfile);
                (void) remove(ilockfile);
                (void) remove(savlog);
        }

        /* release the generic package lock */

        (void) unlockinst();

        pkgcloseserver(pkgserver);

        quit(0);
        /* LINTED: no return */
}

/*
 * This function merges the environment data in the response file with the
 * current environment.
 */
static int
merg_respfile()
{
        int retcode = 0;
        char *resppath = get_respfile();
        char *locbasedir;
        char param[MAX_PKG_PARAM_LENGTH], *value;
        FILE *fp;

        if ((fp = fopen(resppath, "r")) == NULL) {
                progerr(ERR_RESPONSE, resppath);
                return (99);
        }

        param[0] = '\0';

        while (value = fpkgparam(fp, param)) {
                if (!isupper(param[0])) {
                        param[0] = '\0';
                        continue;
                }

                if (rdonly(param)) {
                        progerr(ERR_RDONLY, param);
                        param[0] = '\0';
                        continue;
                }

                /*
                 * If this is an update, and the response file
                 * specifies the BASEDIR, make sure it matches the
                 * existing installation base. If it doesn't, we have
                 * to quit.
                 */
                if (update && strcmp("BASEDIR", param) == 0) {
                        locbasedir = getenv("BASEDIR");
                        if (locbasedir && strcmp(value, locbasedir) != 0) {
                                char *dotptr;
                                /* Get srcinst down to a name. */
                                if (dotptr = strchr(srcinst, '.'))
                                        *dotptr = '\000';
                                progerr(ERR_NEWBD, srcinst,
                                        locbasedir, value);
                                retcode = 99;
                        }
                }

                putparam(param, value);
                param[0] = '\0';
        }
        (void) fclose(fp);

        return (retcode);
}

/*
 * This scans the installed pkginfo file for the current BASEDIR. If this
 * BASEDIR is different from the current BASEDIR, there will definitely be
 * problems.
 */
static int
ck_instbase(void)
{
        int retcode = 0;
        char param[MAX_PKG_PARAM_LENGTH], *value;
        char pkginfo_path[PATH_MAX];
        FILE *fp;

        /* Open the old pkginfo file. */
        (void) snprintf(pkginfo_path, sizeof (pkginfo_path),
                        "%s/%s", pkgloc, PKGINFO);
        if ((fp = fopen(pkginfo_path, "r")) == NULL) {
                progerr(ERR_PKGINFO, pkginfo_path);
                return (99);
        }

        param[0] = '\000';

        while (value = fpkgparam(fp, param)) {
                if (strcmp("BASEDIR", param) == 0) {
                        if (adm.basedir && *(adm.basedir) &&
                                strchr("/$", *(adm.basedir))) {
                                char *dotptr;

                                /*
                                 * Get srcinst down to a name.
                                 */
                                if (dotptr = strchr(srcinst, '.'))
                                        *dotptr = '\000';
                                if (strcmp(value,
                                        adm.basedir) != 0) {
                                        progerr(ERR_ADMBD, srcinst,
                                                value, adm.basedir);
                                        retcode = 4;
                                        break;
                                }
                        } else if (ADM(basedir, "ask"))
                                /*
                                 * If it's going to ask later, let it know
                                 * that it *must* agree with the BASEDIR we
                                 * just picked up.
                                 */
                                adm.basedir = "update";

                        putparam(param, value);
                        break;
                }

                param[0] = '\0';
        }
        (void) fclose(fp);

        return (retcode);
}

/*
 * Since this is an overwrite of a different version of the package, none of
 * the old files should remain, so we rename them.
 */
static int
mv_pkgdirs(void)
{
        /*
         * If we're not in dryrun mode and we can find an old set of package
         * files over which the new ones will be written, do the rename.
         */
        if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
                (void) snprintf(pkgloc_sav, sizeof (pkgloc_sav),
                        "%s/.save.%s", get_PKGLOC(),
                        pkginst);
                if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
                        (void) rrmdir(pkgloc_sav);
                }

                if (rename(pkgloc, pkgloc_sav) == -1) {
                        progerr(ERR_PKGBINREN, pkgloc, pkgloc_sav);
                        return (99);
                }
        }

        return (0);
}

/*
 * Name:        merg_pkginfos
 * Description: This function scans the installed pkginfo and merges that
 *              environment with the installing environment according to
 *              the following rules:
 *
 *              1. CLASSES is a union of the installed and installing CLASSES
 *                      lists.
 *              2. The installed BASEDIR takes precedence. If it doesn't agree
 *                 with an administratively imposed BASEDIR, an ERROR is issued.
 *              3. All other installing parameters are preserved.
 *              4. All installed parameters are added if they do not overwrite
 *                 an existing installing parameter.
 *
 *              The current environment contains the pkginfo settings for the
 *              new package to be installed or to be updated.
 *
 * Arguments:   pclass - returned list of current classes involved in install
 *              mpclass - pointer to returned list of current install classes
 * Returns:     int
 *              == 0 - all OK
 *              != 0 - an error code if a fatal error occurred
 */

static int
merg_pkginfos(struct cl_attr **pclass, struct cl_attr ***mpclass)
{
        FILE    *fp;
        char    SUNW_PKG_ALLZONES[MAX_PKG_PARAM_LENGTH] = {'\0'};
        char    SUNW_PKG_HOLLOW[MAX_PKG_PARAM_LENGTH] = {'\0'};
        char    SUNW_PKG_THISZONE[MAX_PKG_PARAM_LENGTH] = {'\0'};
        char    *newValue;
        char    *oldValue;
        char    *pkgName;
        char    *pkgVersion;
        char    param[MAX_PKG_PARAM_LENGTH];
        char    pkginfo_path[PATH_MAX];
        int     retcode = 0;

        /* obtain the name of the package (for error messages) */

        pkgName = getenv("PKG");
        if (pkgName == NULL) {
                pkgName = "*current*";  /* default name */
        }

        /* obtain the version of the package (for error messages) */

        pkgVersion = getenv("VERSION");
        if (pkgVersion == NULL) {
                pkgVersion = "*current*";       /* default version */
        }

        /* open installed package pkginfo file */

        (void) snprintf(pkginfo_path, sizeof (pkginfo_path),
                        "%s/%s", pkgloc, PKGINFO);
        if ((fp = fopen(pkginfo_path, "r")) == NULL) {
                progerr(ERR_PKGINFO, pkginfo_path);
                return (99);
        }

        /* entry debugging info */

        echoDebug(DBG_MERGINFOS_ENTRY, pkginfo_path);

        /*
         * cycle through the currently installed package's pkginfo parameters
         * and let the currently installed package's settings survive if the
         * update to the package does not provide an overriding value
         */

        for (param[0] = '\0'; (oldValue = fpkgparam(fp, param)) != NULL;
                param[0] = '\0') {

                boolean_t       setZoneAttribute = B_FALSE;

                /* debug info - attribute currently set to value */

                echoDebug(DBG_MERGINFOS_SET_TO, param, oldValue);

                /*
                 * if zone package attribute is present in the currently
                 * installed package, then remember the value for the
                 * specific zone package attribute, and set the flag that
                 * indicates a zone package attribute is being processed.
                 */

                if (strcmp(param, PKG_THISZONE_VARIABLE) == 0) {
                        /* SUNW_PKG_THISZONE currently set */
                        setZoneAttribute = B_TRUE;
                        (void) strlcpy(SUNW_PKG_THISZONE, oldValue,
                                        sizeof (SUNW_PKG_THISZONE));
                } else if (strcmp(param, PKG_ALLZONES_VARIABLE) == 0) {
                        /* SUNW_PKG_ALLZONES currently set */
                        setZoneAttribute = B_TRUE;
                        (void) strlcpy(SUNW_PKG_ALLZONES, oldValue,
                                        sizeof (SUNW_PKG_ALLZONES));
                } else if (strcmp(param, PKG_HOLLOW_VARIABLE) == 0) {
                        /* SUNW_PKG_THISZONE currently set */
                        setZoneAttribute = B_TRUE;
                        (void) strlcpy(SUNW_PKG_HOLLOW, oldValue,
                                        sizeof (SUNW_PKG_HOLLOW));
                }

                /* handle CLASSES currently being set */

                if (strcmp(param, "CLASSES") == 0) {
                        echoDebug(DBG_MERGINFOS_SET_CLASSES, oldValue);
                        /* create a list of the current classes */
                        (void) setlist(&pclass, qstrdup(oldValue));
                        /* set pointer to list of current classes */
                        *mpclass = pclass;
                        continue;
                }

                /* handle BASEDIR currently being set */

                if (strcmp("BASEDIR", param) == 0) {
                        if (adm.basedir && *(adm.basedir) &&
                                strchr("/$", *(adm.basedir))) {
                                char *dotptr;

                                /* Get srcinst down to a* name */

                                if (dotptr = strchr(srcinst, '.')) {
                                        *dotptr = '\000';
                                }
                                if (strcmp(oldValue, adm.basedir) != 0) {
                                        progerr(ERR_ADMBD, srcinst,
                                                oldValue, adm.basedir);
                                        /* administration */
                                        retcode = 4;
                                        break;
                                }
                        } else if (ADM(basedir, "ask")) {
                                /*
                                 * If it's going to ask
                                 * later, let it know that it
                                 * *must* agree with the
                                 * BASEDIR we just picked up.
                                 */
                                adm.basedir = "update";
                                echoDebug(DBG_MERGINFOS_ASK_BASEDIR);
                        }

                        echoDebug(DBG_MERGINFOS_SET_BASEDIR, oldValue);
                        putparam(param, oldValue);
                        continue;
                }

                /*
                 * determine if there is a new value for this attribute.
                 */

                newValue = getenv(param);

                /*
                 * If there is no new value, and a zone attribute
                 * is being changed, it is the same as setting the zone package
                 * attribute to 'false' - make sure current setting is 'false'.
                 */

                if ((newValue == NULL) &&
                    (setZoneAttribute == B_TRUE) &&
                    (strcasecmp(oldValue, "false") != 0)) {

                        /* unset existing non-"false" zone pkg attr */
                        progerr(ERR_MERGINFOS_UNSET_ZONEATTR,
                                pkgName, pkgVersion, param, oldValue);
                        retcode = 1;
                        break;
                }

                /* retain old value if no new value specified */

                if (newValue == NULL) {
                        /* no new value - retain the old value */
                        echoDebug(DBG_MERGINFOS_RETAIN_OLD, param, oldValue);
                        putparam(param, oldValue);
                        continue;
                }

                /* note if the old and new values are the same */

                if (strcmp(newValue, oldValue) == 0) {
                        /* set existing package parameter to same value */
                        echoDebug(DBG_MERGINFOS_SET_DUPLICATE, param, oldValue);
                        continue;
                }

                /*
                 * Check if old and new values differ.
                 * Error if zone parameter
                 */

                if (setZoneAttribute == B_TRUE) {
                        /* illegal change to zone attribute */

                        progerr(ERR_MERGINFOS_CHANGE_ZONEATTR, pkgName,
                                pkgVersion, param, oldValue, newValue);

                        /* set return code to "fatal error" */
                        retcode = 1;
                        break;
                }

                /* note valid change to existing package parameter */

                echoDebug(DBG_MERGINFOS_SET_CHANGE, param,
                                oldValue, newValue);
        }

        /* close handle on currently installed package's pkginfo file */

        (void) fclose(fp);

        /* return error if not successful up to this point */

        if (retcode != 0) {
                echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, retcode);

                return (retcode);
        }

        /*
         * verify that no zone attribute has been
         * set to an invalid value
         */

        /* SUNW_PKG_ALLZONES */

        newValue = getenv(PKG_ALLZONES_VARIABLE);

        /*
         * complain if setting SUNW_PKG_ALLZONES to other than "false"
         */


        if ((newValue != NULL) && (*SUNW_PKG_ALLZONES == '\0') &&
            (strcasecmp(newValue, "false") != 0)) {
                /* change ALLZONES from "true" to "false" (unset) */
                progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
                    pkgVersion, PKG_ALLZONES_VARIABLE, newValue);
                return (1);
        }

        /* SUNW_PKG_THISZONE */

        newValue = getenv(PKG_THISZONE_VARIABLE);

        /*
         * complain if setting SUNW_PKG_THISZONE to other than "false"
         */

        if ((newValue != NULL) && (*SUNW_PKG_THISZONE == '\0') &&
            (strcasecmp(newValue, "false") != 0)) {
                /* change THISZONE from "true" to "false" (unset) */
                progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
                    pkgVersion, PKG_THISZONE_VARIABLE, newValue);
                return (1);
        }

        /* SUNW_PKG_HOLLOW */

        newValue = getenv(PKG_HOLLOW_VARIABLE);

        /* complain if setting SUNW_PKG_HOLLOW to other than "false" */

        if ((newValue != NULL) && (*SUNW_PKG_HOLLOW == '\0') &&
            (strcasecmp(newValue, "false") != 0)) {
                /* change HOLLOW from "true" to 'false" (unset) */
                progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
                    pkgVersion, PKG_HOLLOW_VARIABLE, newValue);
                return (1);
        }

        echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, 0);

        return (0);
}

static void
set_dryrun_dir_loc(void)
{
        /* Set pkg location to the dryrun directory */
        set_PKGLOC(pkgdrtarg);
        (void) snprintf(pkgloc, sizeof (pkgloc),
                        "%s/%s", get_PKGLOC(), pkginst);
        (void) snprintf(pkgbin, sizeof (pkgbin),
                        "%s/install", pkgloc);
        (void) snprintf(pkgsav, sizeof (pkgsav),
                        "%s/save", pkgloc);
        (void) snprintf(ilockfile, sizeof (ilockfile),
                        "%s/!I-Lock!", pkgloc);
        (void) snprintf(rlockfile, sizeof (rlockfile),
                        "%s/!R-Lock!", pkgloc);
        (void) snprintf(savlog, sizeof (savlog),
                        "%s/logs/%s", get_PKGADM(), pkginst);
}

/*
 * If we are updating a pkg, then we need to copy the "old" pkgloc so that
 * any scripts that got removed in the new version aren't left around.  So we
 * copy it here to .save.pkgloc, then in quit() we can restore our state, or
 * remove it.
 */
static int
cp_pkgdirs(void)
{
        if (in_dryrun_mode()) {
                set_dryrun_dir_loc();
        }

        /*
         * If we're not in dryrun mode and we can find an old set of package
         * files over which the new ones will be written, do the copy.
         */
        if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
                int status;
                int r;

                (void) snprintf(pkgloc_sav, sizeof (pkgloc_sav), "%s/.save.%s",
                        get_PKGLOC(), pkginst);

                /*
                 * Even though it takes a while, we use a recursive copy here
                 * because if the current pkgadd fails for any reason, we
                 * don't want to lose this data.
                 */
                r = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
                        "/usr/bin/cp", "cp", "-r", pkgloc, pkgloc_sav,
                        (char *)NULL);

                if ((r != 0) || (status == -1) || (WEXITSTATUS(status) != 0)) {
                        progerr(ERR_PKGBINCP, pkgloc, pkgloc_sav);
                        return (99);
                }
        }

        return (0);
}

/*
 * This implements the pkgask function. It just executes the request script
 * and stores the results in a response file.
 */
static void
do_pkgask(boolean_t a_run_request_as_root)
{
        if (pkgdev.cdevice) {
                unpack();
                if (!suppressCopyright) {
                        copyright();
                }
        }
        (void) snprintf(path, sizeof (path), "%s/%s", instdir, REQUEST_FILE);
        if (access(path, F_OK)) {
                progerr(ERR_NOREQUEST);
                quit(1);
                /*NOTREACHED*/
        }

        (void) set_respfile(respfile, srcinst, RESP_WR);

        if (is_a_respfile()) {
                ckreturn(reqexec(update, path, non_abi_scripts,
                        a_run_request_as_root), ERR_REQUEST);
        } else {
                failflag++;
        }

        if (warnflag || failflag) {
                (void) remove(respfile);
                echo("\nResponse file <%s> was not created.",
                        get_respfile());
        } else {
                echo("\nResponse file <%s> was created.",
                        get_respfile());
        }

        quit(0);
        /*NOTREACHED*/
}

/*
 * This function runs a check utility and acts appropriately based upon the
 * return code. It deals appropriately with the dryrun file if it is present.
 */
static void
ck_w_dryrun(int (*func)(), int type)
{
        int n;

        n = func();
        if (in_dryrun_mode())
                set_dr_info(type, !n);

        if (n) {
                quit(n);
                /*NOTREACHED*/
        }
}

/*
 * This function deletes all install class action scripts from the package
 * directory on the root filesystem.
 */
static void
rm_icas(char *cas_dir)
{
        DIR     *pdirfp;
        struct  dirent *dp;
        char path[PATH_MAX];

        if ((pdirfp = opendir(cas_dir)) == NULL)
                return;

        while ((dp = readdir(pdirfp)) != NULL) {
                if (dp->d_name[0] == '.')
                        continue;

                if (dp->d_name[0] == 'i' && dp->d_name[1] == '.') {
                        (void) snprintf(path, sizeof (path),
                                "%s/%s", cas_dir, dp->d_name);
                        (void) remove(path);
                }
        }
        (void) closedir(pdirfp);
}

void
ckreturn(int retcode, char *msg)
{
        switch (retcode) {
                case 2:
                case 12:
                case 22:
                warnflag++;
                if (msg) {
                        progerr("%s", msg);
                }
                /*FALLTHRU*/
                case 10:
                case 20:
                if (retcode >= 10 && retcode < 20) {
                        dreboot++;
                }
                if (retcode >= 20) {
                        ireboot++;
                }
                /*FALLTHRU*/
                case 0:
                break; /* okay */

                case -1:
                retcode = 99;
                /*FALLTHRU*/
                case 99:
                case 1:
                case 11:
                case 21:
                case 4:
                case 14:
                case 24:
                case 5:
                case 15:
                case 25:
                if (msg) {
                        progerr("%s", msg);
                }
                /*FALLTHRU*/
                case 3:
                case 13:
                case 23:
                quit(retcode);
                /*NOTREACHED*/
                default:
                if (msg) {
                        progerr("%s", msg);
                }
                quit(1);
                /*NOTREACHED*/
        }
}

static void
copyright(void)
{
        FILE    *fp;
        char    line[LSIZE];
        char    path[PATH_MAX];

        /* Compose full path for copyright file */
        (void) snprintf(path, sizeof (path), "%s/%s", instdir, COPYRIGHT_FILE);

        if ((fp = fopen(path, "r")) == NULL) {
                if (getenv("VENDOR") != NULL)
                        echo(getenv("VENDOR"));
        } else {
                while (fgets(line, LSIZE, fp))
                        (void) fprintf(stdout, "%s", line); /* bug #1083713 */
                (void) fclose(fp);
        }
}

static int
rdonly(char *p)
{
        int     i;

        for (i = 0; ro_params[i]; i++) {
                if (strcmp(p, ro_params[i]) == 0)
                        return (1);
        }
        return (0);
}

static void
unpack(void)
{
        /*
         * read in next part from stream, even if we decide
         * later that we don't need it
         */
        if (dparts < 1) {
                progerr(ERR_DSTREAMCNT);
                quit(99);
                /*NOTREACHED*/
        }
        if ((access(instdir, F_OK) == 0) && rrmdir(instdir)) {
                progerr(ERR_RMDIR, instdir);
                quit(99);
                /*NOTREACHED*/
        }
        if (mkdir(instdir, 0755)) {
                progerr(ERR_MKDIR, instdir);
                quit(99);
                /*NOTREACHED*/
        }
        if (chdir(instdir)) {
                progerr(ERR_CHDIR, instdir);
                quit(99);
                /*NOTREACHED*/
        }
        if (!ds_fd_open()) {
                dparts = ds_findpkg(pkgdev.cdevice, srcinst);
                if (dparts < 1) {
                        progerr(ERR_DSARCH, srcinst);
                        quit(99);
                        /*NOTREACHED*/
                }
        }

        dparts--;

        if (ds_next(pkgdev.cdevice, instdir)) {
                progerr(ERR_DSTREAM);
                quit(99);
                /*NOTREACHED*/
        }
        if (chdir(get_PKGADM())) {
                progerr(ERR_CHDIR, get_PKGADM());
                quit(99);
                /*NOTREACHED*/
        }
        ds_close(1);
}

static void
usage(void)
{
        (void) fprintf(stderr, ERR_USAGE_PKGINSTALL);
        exit(1);
        /*NOTREACHED*/
}