summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svr4pkg/pkgadd
diff options
context:
space:
mode:
authorMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
committerMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
commit5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch)
tree0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/cmd/svr4pkg/pkgadd
parent2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff)
downloadillumos-joyent-5c51f1241dbbdf2656d0e10011981411ed0c9673.tar.gz
6739234 move SVR4 packaging to ONNV gate
Diffstat (limited to 'usr/src/cmd/svr4pkg/pkgadd')
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/Makefile51
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/check.c1029
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/main.c4712
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/msgdefs.h141
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/presvr4.c172
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/quit.c409
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/quit.h64
7 files changed, 6578 insertions, 0 deletions
diff --git a/usr/src/cmd/svr4pkg/pkgadd/Makefile b/usr/src/cmd/svr4pkg/pkgadd/Makefile
new file mode 100644
index 0000000000..36bf01e3fa
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/Makefile
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= pkgadd
+
+OBJS= check.o \
+ main.o \
+ presvr4.o \
+ quit.o
+
+ROOTLINKS= $(ROOTUSRSBIN)/pkgask
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+LDLIBS += -lcrypto -lwanboot
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTLINKS)
+
+$(ROOTLINKS): $(ROOTUSRSBINPROG)
+ $(RM) $@
+ $(LN) $(ROOTUSRSBINPROG) $@
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgadd/check.c b/usr/src/cmd/svr4pkg/pkgadd/check.c
new file mode 100644
index 0000000000..012a3bffe7
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/check.c
@@ -0,0 +1,1029 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <assert.h>
+
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <messages.h>
+
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+extern int npkgs; /* the number of packages yet to be installed */
+
+/*
+ * ckquit is a global that controls 'ckyorn' (defined in libadm)
+ * If ckquit is non-zero, then "quit" is allowed as an answer when
+ * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
+ */
+extern int ckquit;
+
+extern struct admin adm;
+
+/*
+ * each one of these represents a single kind of dependency check
+ */
+
+static depckError_t er_ckconflict = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckdepend = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckcfcontent = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckinstance = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckdirs = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpartinst = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpartrem = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgdirs = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgfilebad = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgfiles = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpriv = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckrunlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_cksetuid = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckspace = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_newonly = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereqinc = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereqinst = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_same = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_overwrite = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_uniq1 = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_attrib = {0, NULL};
+static depckError_t er_setuidf = {0, NULL};
+static depckError_t er_setgidf = {0, NULL};
+static depckError_t er_overwr = {0, NULL};
+
+/*
+ * each one of these represents a localized message for a single kind
+ * of dependency check
+ */
+
+static char *IMSG_ABADFILE = (char *)NULL;
+static char *IMSG_BADFILE = (char *)NULL;
+static char *IMSG_CKRUNLVL = (char *)NULL;
+static char *IMSG_CNFFAILED = (char *)NULL;
+static char *IMSG_DEPEND = (char *)NULL;
+static char *IMSG_CFCONTENT = (char *)NULL;
+static char *IMSG_INSTANCE = "INSTANCE %s <%s> on %s <%s>";
+static char *IMSG_DIRS = (char *)NULL;
+static char *IMSG_NEWONLY = (char *)NULL;
+static char *IMSG_PARTINST = (char *)NULL;
+static char *IMSG_PARTREM = (char *)NULL;
+static char *IMSG_PKGDIRS = (char *)NULL;
+static char *IMSG_PRENCI = (char *)NULL;
+static char *IMSG_PREREQ = (char *)NULL;
+static char *IMSG_PRIV = (char *)NULL;
+static char *IMSG_RUNLEVEL = (char *)NULL;
+static char *IMSG_SAME = (char *)NULL;
+static char *IMSG_OVERWRITE = (char *)NULL;
+static char *IMSG_UNIQ1 = (char *)NULL;
+static char *IMSG_SETUID = (char *)NULL;
+static char *IMSG_SPCFAILED = (char *)NULL;
+static char *IMSG_ATTRIB;
+static char *IMSG_SETUIDF;
+static char *IMSG_SETGIDF;
+static char *IMSG_OVERWR;
+
+/*
+ * each one of these represents a function to handle a single kind of
+ * dependency check
+ */
+
+static int ckconflict(char *a_msg, char *a_pkg);
+static int ckdepend(char *a_msg, char *a_pkg);
+static int ckcfcontent(char *a_msg, char *a_pkg);
+static int ckinstance(char *a_msg, char *a_pkg);
+static int ckdirs(char *a_msg, char *a_pkg);
+static int ckpartinst(char *a_msg, char *a_pkg);
+static int ckpartrem(char *a_msg, char *a_pkg);
+static int ckpkgfilebad(char *a_msg, char *a_pkg);
+static int ckpkgdirs(char *a_msg, char *a_pkg);
+static int ckpkgfiles(char *a_msg, char *a_pkg);
+static int ckprereqinc(char *a_msg, char *a_pkg);
+static int ckprereqinst(char *a_msg, char *a_pkg);
+static int ckpriv(char *a_msg, char *a_pkg);
+static int ckrunlevel(char *a_msg, char *a_pkg);
+static int cksetuid(char *a_msg, char *a_pkg);
+static int ckspace(char *a_msg, char *a_pkg);
+static int attrib(char *a_msg, char *a_pkg);
+static int setuidf(char *a_msg, char *a_pkg);
+static int setgidf(char *a_msg, char *a_pkg);
+static int overwr(char *a_msg, char *a_pkg);
+
+static depckl_t DEPCKL[] = {
+ /*
+ * name, ignore_values, err_msg, depcklFunc, recrd
+ * ---
+ * ignore_values == NULL:
+ * package and zone information is collected in the "record" object for
+ * each occurance - then a message is constructed for each zone that
+ * reported the condition - the message includes that portion of the
+ * check past the "=" - then the specified "depcklFunc" is called to
+ * process each message.
+ * Message format:
+ * %s %s <%s> %s <%s>
+ * Message arguments:
+ * value, "package", package-name, "zone/zones", zone-name
+ * ---
+ * ignore-values == "???":
+ * these checks are ignored if they return one of the listed values
+ * if they do NOT return one of the listed values, then the package
+ * and zone information is collected in the "record" object for each
+ * occurance - then a single unified message is constructed for all
+ * zones that report the same condition; then the specified "depcklFunc"
+ * is called to process the resulting combined message.
+ * Message format:
+ * %s <%s> %s <%s>
+ * Message arguments:
+ * "package", package-name, "zone/zones", zone-name(s)
+ * ---
+ * ignore-values="":
+ * same as above BUT no check to ignore is done; message always reported
+ */
+
+ { "install-same-instance=true", "", &IMSG_SAME,
+ NULL, &er_same
+ },
+ { "ckpkgfilebad=", NULL, &IMSG_ABADFILE,
+ &ckpkgfilebad, &er_ckpkgfilebad
+ },
+ { "ckdirs=", NULL, &IMSG_DIRS,
+ &ckdirs, &er_ckdirs
+ },
+ { "prerequisite-incomplete=", NULL, &IMSG_PRENCI,
+ &ckprereqinc, &er_prereqinc
+ },
+ { "prerequisite-installed=", NULL, &IMSG_PREREQ,
+ &ckprereqinst, &er_prereqinst
+ },
+ { "runlevel=", NULL, &IMSG_RUNLEVEL,
+ NULL, &er_runlevel
+ },
+ { "conflict-contents=", NULL, &IMSG_CFCONTENT,
+ &ckcfcontent, &er_ckcfcontent
+ },
+ { "ckconflict=", "0", &IMSG_CNFFAILED,
+ &ckconflict, &er_ckconflict
+ },
+ { "ckdepend=", "0", &IMSG_DEPEND,
+ &ckdepend, &er_ckdepend
+ },
+ { "ckpartialinstall=", "0", &IMSG_PARTINST,
+ &ckpartinst, &er_ckpartinst
+ },
+ { "ckpartialremove=", "0", &IMSG_PARTREM,
+ &ckpartrem, &er_ckpartrem
+ },
+ { "ckpkgdirs=", "0", &IMSG_PKGDIRS,
+ &ckpkgdirs, &er_ckpkgdirs
+ },
+ { "ckpkgfiles=", "0", &IMSG_BADFILE,
+ &ckpkgfiles, &er_ckpkgfiles
+ },
+ { "ckpriv=", "0", &IMSG_PRIV,
+ &ckpriv, &er_ckpriv
+ },
+ { "ckrunlevel=", "0", &IMSG_CKRUNLVL,
+ &ckrunlevel, &er_ckrunlevel
+ },
+ { "cksetuid=", "0", &IMSG_SETUID,
+ &cksetuid, &er_cksetuid
+ },
+ { "ckspace=", "0", &IMSG_SPCFAILED,
+ &ckspace, &er_ckspace
+ },
+ { "install-new-only=true", "", &IMSG_NEWONLY,
+ NULL, &er_newonly
+ },
+ { "install-ovewrite=true", "", &IMSG_OVERWRITE,
+ NULL, &er_overwrite
+ },
+ { "install-too-many-instances=true", "", &IMSG_UNIQ1,
+ NULL, &er_uniq1
+ },
+ { "ckinstance=", "0", &IMSG_INSTANCE,
+ &ckinstance, &er_ckinstance
+ },
+ { "conflict-attributes=", NULL, &IMSG_ATTRIB,
+ &attrib, &er_attrib
+ },
+ { "setuid=", NULL, &IMSG_SETUIDF,
+ &setuidf, &er_setuidf
+ },
+ { "setgid=", NULL, &IMSG_SETGIDF,
+ &setgidf, &er_setgidf
+ },
+ { "setuid-overwrite=true", "", &IMSG_OVERWR,
+ &overwr, &er_overwr
+ },
+
+ { NULL, NULL, NULL,
+ NULL, NULL }
+};
+
+/*
+ * Name: preinstall_verify
+ * Description: verify results of preinstallation dependency checking
+ * Arguments: a_pkglist - pointer to array of strings representing the names
+ * of all the packages that have been checked
+ * a_zlst - list of zones that dependencies were checked on
+ * a_zoneTempDir - pointer to string representing the path where
+ * the files containing the preinstallation dependency
+ * check data are located
+ * Returns: int
+ * == 0 - continue processing
+ * != 0 - do not continue processing
+ */
+
+int
+preinstall_verify(char **a_pkglist, zoneList_t a_zlst, char *a_zoneTempDir)
+{
+ char *pkginst;
+ int i;
+ int savenpkgs = npkgs;
+
+ /*
+ * entry assertions
+ */
+
+ assert(a_pkglist != (char **)NULL);
+ assert(a_zlst != (zoneList_t)NULL);
+ assert(a_zoneTempDir != (char *)NULL);
+
+ /*
+ * entry debugging info
+ */
+
+ echoDebug(DBG_PREIVFY_ENTRY);
+
+ /*
+ * localize messages
+ */
+
+ IMSG_ABADFILE = MSG_PKGADDCHK_ABADFILE;
+ IMSG_BADFILE = MSG_PKGADDCHK_BADFILE;
+ IMSG_CFCONTENT = MSG_PKGADDCHK_CFCONTENT;
+ IMSG_CKRUNLVL = MSG_PKGADDCHK_CKRUNLVL;
+ IMSG_CNFFAILED = MSG_PKGADDCHK_CNFFAILED;
+ IMSG_DEPEND = MSG_PKGADDCHK_DEPEND;
+ IMSG_DIRS = MSG_PKGADDCHK_DIRS;
+ IMSG_NEWONLY = MSG_PKGADDCHK_NEWONLY;
+ IMSG_OVERWRITE = MSG_PKGADDCHK_OVERWRITE;
+ IMSG_PARTINST = MSG_PKGADDCHK_PARTINST;
+ IMSG_PARTREM = MSG_PKGADDCHK_PARTREM;
+ IMSG_PKGDIRS = MSG_PKGADDCHK_PKGDIRS;
+ IMSG_PRENCI = MSG_PKGADDCHK_PRENCI;
+ IMSG_PREREQ = MSG_PKGADDCHK_PREREQ;
+ IMSG_PRIV = MSG_PKGADDCHK_PRIV;
+ IMSG_RUNLEVEL = MSG_PKGADDCHK_RUNLEVEL;
+ IMSG_SAME = MSG_PKGADDCHK_SAME;
+ IMSG_SETUID = MSG_PKGADDCHK_SETUID;
+ IMSG_SPCFAILED = MSG_PKGADDCHK_SPCFAILED;
+ IMSG_UNIQ1 = MSG_PKGADDCHK_UNIQ1;
+ IMSG_ATTRIB = gettext("\\nattribute change for %s <%s> on %s <%s>\n");
+ IMSG_SETUIDF = gettext("\\nsetuid %s in %s <%s> on %s <%s>\n");
+ IMSG_SETGIDF = gettext("\\nsetgid %s in %s <%s> on %s <%s>\n");
+ IMSG_OVERWR = gettext("\\nFiles that are setuid will be overwritten "
+ "by installation of %s\n<%s> on %s <%s>.\n");
+
+ /*
+ * outer loop - process each package first
+ */
+
+ for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) {
+
+ char *zoneName;
+ int zoneIndex;
+
+ /*
+ * if this package is marked "install in this zone only", then
+ * do not check dependencies in any zone
+ */
+
+ if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+ echoDebug(DBG_PREIVFY_SKIP_THISZONE, pkginst);
+ continue;
+ }
+
+ /*
+ * inner loop - for each package process each zone second
+ */
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+ (char *)NULL; zoneIndex++) {
+
+ FILE *fp;
+ char line[PATH_MAX+1];
+ char preinstallcheckPath[PATH_MAX+1];
+ int len;
+
+ /* skip the zone if it is NOT bootable */
+
+ if (z_zlist_is_zone_runnable(a_zlst,
+ zoneIndex) == B_FALSE) {
+ continue;
+ }
+
+ /* create path to this packages preinstall check data */
+
+ len = snprintf(preinstallcheckPath,
+ sizeof (preinstallcheckPath),
+ "%s/%s.%s.preinstallcheck.txt", a_zoneTempDir,
+ pkginst, zoneName);
+
+ if (len > sizeof (preinstallcheckPath)) {
+ progerr(ERR_CREATE_PATH_3, a_zoneTempDir,
+ pkginst, zoneName);
+ continue;
+ }
+
+ /* error if preinstall check data path is not a file */
+
+ if (isfile((char *)NULL, preinstallcheckPath) != 0) {
+ echoDebug(DBG_PREIVFY_NOFILE,
+ pkginst, zoneName, preinstallcheckPath,
+ strerror(errno));
+ progerr(ERR_PREIVFY_NOFILE,
+ pkginst, zoneName);
+ continue;
+ }
+
+ /* open the preinstall check data file */
+
+ fp = fopen(preinstallcheckPath, "r");
+ if (fp == (FILE *)NULL) {
+ progerr(ERR_PREIVFY_OPEN_FILE,
+ preinstallcheckPath, pkginst, zoneName,
+ strerror(errno));
+ continue;
+ }
+
+ /* read and process each preinstall check data line */
+
+ while (fgets(line, sizeof (line), fp) != (char *)NULL) {
+ int j;
+ int len;
+
+ /* remove all new-lines from end of line */
+
+ len = strlen(line);
+ while ((len > 0) && (line[len-1] == '\n')) {
+ line[--len] = '\0';
+ }
+
+ /* ignore comment lines */
+
+ if (line[0] == '#') {
+ continue;
+ }
+
+ /* ignore empty lines */
+
+ if (line[0] == '\0') {
+ continue;
+ }
+
+ /* scan dependency list for this item */
+
+ for (j = 0;
+ DEPCKL[j].name != (char *)NULL; j++) {
+ len = strlen(DEPCKL[j].name);
+
+ if (strncmp(line, DEPCKL[j].name,
+ len) == 0) {
+ break;
+ }
+ }
+
+ echoDebug(DBG_PREIVFY_SCAN, line, pkginst,
+ zoneName);
+
+ /* ignore line if not found */
+
+ if (DEPCKL[j].name == (char *)NULL) {
+ progerr(ERR_PREIVFY_UNKNOWN_LINE, line,
+ pkginst, zoneName);
+ continue;
+ }
+
+ if ((DEPCKL[j].ignore_values != (char *)NULL) &&
+ (*(DEPCKL[j].ignore_values) != '\0') &&
+ (strchr(DEPCKL[j].ignore_values,
+ line[len]) != (char *)NULL)) {
+ continue;
+ }
+
+ /* found match - record this dependency issue */
+
+ depchkRecordError(DEPCKL[j].record, pkginst,
+ zoneName, &line[len]);
+ }
+
+ /* close preinstall check data file */
+
+ (void) fclose(fp);
+ }
+ }
+
+ /*
+ * all dependency issues have been recorded; report results
+ */
+
+ i = depchkReportErrors(DEPCKL);
+
+ /* restore "npkgs" */
+
+ npkgs = savenpkgs;
+
+ /* return continue/dont dontinue results */
+
+ return (i);
+}
+
+/*
+ * Name: getyorn
+ * Description: Deliver dependency check reason; ask question; return response
+ * Arguments: a_msg - pointer to string representing the message to output
+ * such as 'The package <..> contains <...>'
+ * a_pkg - pointer to string representing the package for which
+ * the question is being asked
+ * a_nocheck - should the message be output?
+ * == 0 - do not output the message
+ * != 0 - output the message
+ * a_quit - should the question NOT be asked?
+ * == 0 - ask the question
+ * != 0 - do not ask the question - return "no"
+ * a_helpMsg - pointer to string representing help message to be
+ * made available if the question is asked
+ * == NULL - no help message is available
+ * a_adminMsg - pointer to string representing the dependency check
+ * failure 'reason' - such as "Privilege checking failed."
+ * == NULL - no failure reason is available
+ * Returns: int - results of question/response actions
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+getyorn(char *a_msg, char *a_pkg, int a_nocheck, int a_quit,
+ char *a_helpMsg, char *a_adminMsg)
+{
+ char ans[MAX_INPUT];
+ char ask_cont[MSG_MAX];
+ int n;
+ int saveCkquit;
+
+ /*
+ * entry assertions
+ */
+
+ assert(a_pkg != (char *)NULL);
+ assert(*a_pkg != '\0');
+
+ /*
+ * entry debugging info
+ */
+
+ echoDebug(DBG_PREIVFY_GETYORN_ARGS, a_pkg, a_nocheck, a_quit, a_msg,
+ a_adminMsg ? a_adminMsg : "");
+
+ /* return success (0) if "nocheck" is non-zero */
+
+ if (a_nocheck != 0) {
+ echoDebug(DBG_PREIVFY_GETYORN_NOCHECK, a_pkg);
+ return (0);
+ }
+
+ /* output reason for this particular failure */
+
+ if ((a_msg != (char *)NULL) && (*a_msg != '\0')) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ /* return "4 (administration)" if "quit" is non-zero */
+
+ if (a_quit != 0) {
+ /* output failure "admin reason" if available */
+ if ((a_adminMsg != (char *)NULL) && (*a_adminMsg != '\0')) {
+ ptext(stderr, a_adminMsg);
+ }
+ echoDebug(DBG_PREIVFY_GETYORN_QUIT, a_pkg);
+ return (4);
+ }
+
+ /* return "5 (administration interaction required)" if -n */
+
+ if (echoGetFlag() == B_FALSE) {
+ ptext(stderr, MSG_PREIVFY_GETYORN_SUSP, a_pkg);
+ echoDebug(DBG_PREIVFY_GETYORN_QUIT_USER, a_pkg);
+ return (5);
+ }
+
+ /* prepare question to ask "continue with pkg <xxx>?" */
+
+ (void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_CONT), a_pkg);
+
+ /* ask question */
+
+ saveCkquit = ckquit;
+ ckquit = 0;
+
+ n = ckyorn(ans, NULL, NULL, a_helpMsg, ask_cont);
+
+ ckquit = saveCkquit;
+
+ if (n != 0) {
+ ptext(stderr, MSG_PREIVFY_GETYORN_TERM, a_pkg);
+ echoDebug(DBG_PREIVFY_GETYORN_CKYORN, a_pkg, n);
+ return (n);
+ }
+
+ /* return "3 (interruption) if not "y" or "Y" */
+
+ if (strchr("yY", *ans) == NULL) {
+ ptext(stderr, MSG_PREIVFY_GETYORN_TERM_USER, a_pkg);
+ echoDebug(DBG_PREIVFY_GETYORN_NOT_Y, a_pkg, ans);
+ return (3);
+ }
+
+ /* return "0 - success" */
+
+ echoDebug(DBG_PREIVFY_GETYORN_SUCCESS, a_pkg);
+
+ return (0);
+}
+
+/*
+ * Trigger: prerequisite-incomplete=<<package>>
+ * Sequence: - one or more: prerequisite-incomplete=<<package>>
+ * - one: ckdepend=<<n>>
+ * Actions: Output message if "idepend!=nocheck"
+ * Return 0
+ * Terminate when 'ckdepend' processed
+ */
+
+static int
+ckprereqinc(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPRENCI, a_pkg, a_msg);
+
+ if (!(ADM(idepend, "nocheck"))) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ return (0);
+}
+
+/*
+ * Trigger: prerequisite-installed=<<package>>
+ * Sequence: - one or more: prerequisite-installed=<<package>>
+ * - one: ckdepend=<<n>>
+ * Actions: Output message if "idepend!=nocheck"
+ * Return 0
+ * Terminate when 'ckdepend' processed
+ */
+
+static int
+ckprereqinst(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPREREQ, a_pkg, a_msg);
+
+ if (!(ADM(idepend, "nocheck"))) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ return (0);
+}
+
+/*
+ * Trigger: ckpartialinstall=<<n>>
+ * Sequence: - one: ckpartialinstall=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckpartinst(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPARTIALINSTALL, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(partial, "nocheck"),
+ ADM(partial, "quit"), HLP_PKGADDCHK_PARTIAL, NULL));
+}
+
+/*
+ * Trigger: ckpartialremove=<<n>>
+ * Sequence: - one: ckpartialremove=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckpartrem(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPARTIALREMOVE, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(partial, "nocheck"),
+ ADM(partial, "quit"), HLP_PKGADDCHK_PARTIAL, NULL));
+}
+
+/*
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ * 99 - fatal error
+ */
+
+static int
+ckrunlevel(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKRUNLEVEL, a_pkg, a_msg);
+ return (0);
+}
+
+/*
+ * Trigger: conflict-contents=<<n>>
+ * Sequence: - one or more of:
+ * -- conflict-contents=<<path>>
+ * -- conflict-attributes=<<path>>
+ * - one: ckconflict=<<n>>
+ * Actions: output message
+ * Return value: int
+ * 0 - success
+ */
+
+static int
+ckcfcontent(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKCFCONTENT, a_pkg, a_msg);
+
+ ptext(stderr, "%s", a_msg);
+
+ return (0);
+}
+
+/*
+ * Trigger: ckinstance=<<n>>
+ * Sequence: - one or more of:
+ * -- install-instance=true
+ * -- install-new-only=true\n
+ * -- install-same-instance=true\n
+ * -- install-ovewrite=true\n
+ * -- install-too-many-instances=true\n
+ * -- install-new-instance=true\n
+ * - one: ckpdepend=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckinstance(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKINSTANCE, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+ ADM(instance, "quit"), HLP_PKGADDCHK_DEPEND,
+ ERR_PKGADDCHK_DEPFAILED));
+}
+
+/*
+ * Trigger: ckdepend=<<n>>
+ * Sequence: - one or more of:
+ * -- incompat=<<package>>
+ * -- prerequisite-incomplete=<<package>>
+ * -- prerequisite-installed=<<package>>
+ * -- dependson=<<package>>
+ * -- dependsonme=<<package>>
+ * - one: ckpdepend=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckdepend(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKDEPEND, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(idepend, "nocheck"),
+ ADM(idepend, "quit"), HLP_PKGADDCHK_DEPEND,
+ ERR_PKGADDCHK_DEPFAILED));
+}
+
+/*
+ * Trigger: ckspace=<<n>>
+ * Sequence: - one: ckspace=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckspace(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKSPACE, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(space, "nocheck"),
+ ADM(space, "quit"), HLP_PKGADDCHK_SPACE,
+ ERR_PKGADDCHK_SPCFAILED));
+}
+
+/*
+ * Trigger: ckpkgdirs=<<n>>
+ * Sequence: - one: ckpkgdirs=<<n>>
+ * Actions: output message
+ * Return 4
+ */
+
+static int
+ckpkgdirs(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPKGDIRS, a_pkg, a_msg);
+
+ ptext(stderr, "%s", a_msg);
+
+ return (4);
+}
+
+/*
+ * Trigger: ckdirs=<<path>>
+ * Sequence: - one: ckdirs=<<path>>
+ * Actions: output message
+ * Return 4
+ */
+
+static int
+ckdirs(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKDIRS, a_pkg, a_msg);
+
+ ptext(stderr, "%s", a_msg);
+
+ ptext(stderr, ERR_PKGADDCHK_MKPKGDIR);
+
+ return (4);
+}
+
+/*
+ * Trigger: ckpkgfilebad=<<path>>
+ * Sequence: - one or more:
+ * -- ckpkgfilebad=<<path>>
+ * - one ckpkgfiles=<n>
+ * Actions: output message
+ * Return 0
+ */
+
+static int
+ckpkgfilebad(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPKGFILEBAD, a_pkg, a_msg);
+
+ ptext(stderr, "%s", a_msg);
+
+ return (0);
+}
+
+/*
+ * Trigger: ckconflict=<<n>>
+ * Sequence: - one or more:
+ * -- conflict-contents=<<path>>
+ * -- conflict-attributes=<<path>>
+ * - one: ckconflict=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckconflict(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKCONFLICT, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(conflict, "nocheck"),
+ ADM(conflict, "quit"), HLP_PKGADDCHK_CONFLICT,
+ ERR_PKGADDCHK_CNFFAILED));
+}
+
+/*
+ * Trigger: cksetuid=<<n>>
+ * Sequence: - one or more:
+ * -- setuid=<path>:<owner>
+ * -- setgid=<path>:<group>
+ * -- setuid-overwrite=true
+ * - one: cksetuid=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+cksetuid(char *a_msg, char *a_pkg)
+{
+ char ans[MAX_INPUT];
+ char ask_cont[MSG_MAX];
+ int n;
+ int saveCkquit;
+
+ echoDebug(DBG_PREIVFY_CKSETUID, a_pkg, a_msg);
+
+ n = getyorn(a_msg, a_pkg, ADM(setuid, "nocheck"),
+ ADM(setuid, "quit"), HLP_PKGADDCHK_SETUID, NULL);
+
+ /* if user did not answer "n" return answer given */
+
+ if (n != 3) {
+ return (n);
+ }
+
+ (void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_CONT), a_pkg);
+
+ saveCkquit = ckquit;
+ ckquit = 0;
+
+ n = ckyorn(ans, NULL, NULL, gettext(HLP_PKGADDCHK_CONT), ask_cont);
+
+ ckquit = saveCkquit;
+
+ if (n != 0) {
+ ptext(stderr, MSG_PREIVFY_GETYORN_TERM, a_pkg);
+ echoDebug(DBG_PREIVFY_GETYORN_CKYORN, a_pkg, n);
+ return (n);
+ }
+
+ /* return "3 (interruption) if not "y" or "Y" */
+
+ if (strchr("yY", *ans) == NULL) {
+ ptext(stderr, MSG_PREIVFY_GETYORN_TERM_USER, a_pkg);
+ echoDebug(DBG_PREIVFY_GETYORN_NOT_Y, a_pkg, ans);
+ return (3);
+ }
+
+ /* return "0 - success" */
+
+ echoDebug(DBG_PREIVFY_GETYORN_SUCCESS, a_pkg);
+
+ return (0);
+}
+
+/*
+ * Trigger: ckpriv=<<n>>
+ * Sequence: - one: ckpriv=<<n>>
+ * Actions: process according to settings
+ * Return value: int
+ * 0 - success
+ * 1 - end of file
+ * 2 - undefined error
+ * 3 - answer was not "y"/was "q"
+ * 4 - quit action taken
+ * 5 - interactive mode required
+ */
+
+static int
+ckpriv(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPRIV, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"),
+ ADM(action, "quit"), HLP_PKGADDCHK_PRIV,
+ ERR_PKGADDCHK_PRIVFAILED));
+}
+
+/*
+ * Trigger: ckpkgfiles=<<n>>
+ * Sequence: - one or more:
+ * -- ckpkgfilebad=<path>
+ * - one: ckpkgfiles=<<n>>
+ * Return value: int
+ * 0 - success
+ * 4 - failure
+ */
+
+static int
+ckpkgfiles(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PREIVFY_CKPKGFILES, a_pkg, a_msg);
+
+ ptext(stderr, "%s", a_msg);
+
+ return (4);
+}
+
+static int
+attrib(char *a_msg, char *a_pkg)
+{
+ return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+ ADM(instance, "quit"), HLP_PKGADDCHK_CONT,
+ ERR_PKGADDCHK_DEPFAILED));
+}
+
+/* ARGSUSED1 */
+static int
+setuidf(char *a_msg, char *a_pkg)
+{
+ char *cp;
+
+ if ((cp = strchr(a_msg, ':')) != NULL)
+ *cp = ' ';
+ return (0);
+}
+
+/* ARGSUSED1 */
+static int
+setgidf(char *a_msg, char *a_pkg)
+{
+ char *cp;
+
+ if ((cp = strchr(a_msg, ':')) != NULL)
+ *cp = ' ';
+ return (0);
+}
+
+static int
+overwr(char *a_msg, char *a_pkg)
+{
+ return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+ ADM(instance, "quit"), HLP_PKGADDCHK_SETUID,
+ ERR_PKGADDCHK_DEPFAILED));
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadd/main.c b/usr/src/cmd/svr4pkg/pkgadd/main.c
new file mode 100644
index 0000000000..a6f91b8b15
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/main.c
@@ -0,0 +1,4712 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Program: pkgadd / pkgask
+ *
+ * Function: public command and private utility functions that
+ * implement the package add and package ask operations.
+ *
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgtrans.h>
+#include <boot_http.h>
+#include <assert.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <pkgweb.h>
+
+#include <instzones_api.h>
+
+/*
+ * local pkg command library includes
+ */
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+
+/*
+ * pkgadd local includes
+ */
+
+#include "quit.h"
+
+/*
+ * imported global variables/functions
+ */
+
+/* presvr4.c */
+extern int presvr4(char **ppkg, int a_nointeract);
+
+/* check.c */
+extern int preinstall_verify(char **a_pkgList, zoneList_t a_zlst,
+ char *a_zoneTempDir);
+
+/*
+ * ckquit is a global that controls 'ckyorn' (defined in libadm)
+ * If ckquit is non-zero, then "quit" is allowed as an answer when
+ * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
+ */
+extern int ckquit;
+
+/*
+ * exported global variables
+ */
+
+/* these globals are set by ckreturn and used by quit.c */
+
+int admnflag = 0; /* != 0 if any pkg op admin setting failure (4) */
+int doreboot = 0; /* != 0 if reboot required after installation */
+int failflag = 0; /* != 0 if fatal error has occurred (1) */
+int intrflag = 0; /* != 0 if user selected quit (3) */
+int ireboot = 0; /* != 0 if immediate reboot required */
+int nullflag = 0; /* != 0 if admin interaction required (5) */
+int warnflag = 0; /* != 0 if non-fatal error has occurred (2) */
+
+/* imported by quit.c */
+int npkgs = 0; /* the number of packages yet to be installed */
+
+/* imported by various (many) */
+char *respfile = NULL; /* response pathname (or NULL) */
+char *tmpdir = NULL; /* location to place temporary files */
+
+struct admin adm; /* holds info about installation admin */
+struct pkgdev pkgdev; /* holds info about the installation device */
+
+/*
+ * internal global variables
+ */
+
+static char *admnfile = NULL; /* file to use for installation admin */
+static char *ids_name = NULL; /* name of data stream device */
+static char *pkgcontsrc = NULL; /* continuation file (-c option) */
+static char *pkgdrtarg = NULL; /* dry run file (-D option) */
+static char *pkginst = NULL; /* current pkg/src instance 2 process */
+static char *respdir = NULL; /* respfile is a directory spec */
+static char *rw_block_size = NULL;
+static char *vfstab_file = NULL;
+static int askflag = 0; /* non-zero if invoked as "pkgask" */
+static int disableAttributes = 0; /* Disabling attribute checking */
+static int disableChecksum = 0; /* Disable checksumming */
+static int disableSaveSpool = 0; /* Disable partial spool dir create */
+static int init_install = 0; /* inform scripts initial install */
+static int no_map_client = 0; /* do not map from vfstab file */
+static int nointeract = 0; /* non-zero - no user interaction */
+static int pkgverbose = 0; /* non-zero if verbose mode selected */
+static int saveSpoolInstall = 0; /* installing from save spool dir */
+static int suppressCopyright = 0; /* suppress copyright notices */
+
+/* set by ckreturn() */
+
+static int interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
+static int needconsult = 0; /* essential ask admin now (1,2,3,5) */
+
+/* Set by -O nozones: do not process any zones */
+
+static boolean_t noZones = B_FALSE;
+
+/* Set by -O zonelist=<names...>: process only named zones */
+
+static boolean_t usedZoneList = B_FALSE;
+
+/* 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 patchPkgRemoval */
+
+static boolean_t patchPkgRemoval = B_FALSE;
+
+/*
+ * Assume the package is ABI and POSIX compliant as regards user
+ * interactiion during procedure scripts.
+ */
+
+static int old_pkg = 0;
+
+/* Assume pkg should be installed according to the ABI */
+
+static int old_symlinks = 0;
+
+/*
+ * Default name length will be 32 chars - if this is set,
+ * disable the 32 char name limit extension
+ */
+
+static int ABI_namelength = 0;
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* printable string - if string is null results in ??? */
+
+#define PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
+
+#define MAX_FDS 20
+
+#define INHERITFS "inherited-filesystem="
+#define INHERITFS_LEN ((sizeof (INHERITFS))-1)
+
+/*
+ * forward declarations
+ */
+
+static int boot_and_pkginstall_check_in_zones(zoneList_t a_zlst,
+ char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir);
+static int boot_and_install_in_zones(zoneList_t a_zlst,
+ char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir);
+static void pkginstall_check_in_one_zone(char **a_inheritedPkgDirs,
+ char *a_zoneName, char *a_idsName,
+ char *a_zoneAdminFile, char *a_zoneTempDir,
+ char *a_altBinDir, char *a_scratchName,
+ zone_state_t a_zoneState);
+static void ckreturn(int retcode);
+static void create_zone_adminfile(char **r_zoneAdminFile,
+ char *a_zoneTempDir, char *a_admnfile);
+static void create_zone_tempdir(char **r_zoneTempDir,
+ char *a_tmpdir);
+static void install_in_one_zone(char **a_inheritedPkgDirs,
+ char *a_zoneName, char *a_idsName,
+ char *a_zoneAdminFile, char *a_zoneTempDir,
+ char *a_altBinDir, zone_state_t a_zoneState);
+static int pkginstall_check_in_zones(zoneList_t a_zlst,
+ char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir);
+static int install_in_zones(zoneList_t a_zlst, char *a_idsName,
+ char *a_altBinDir, char *a_zoneAdminFile,
+ char *a_zoneTempDir);
+static int pkgInstall(char *ir, char *a_idsName, char *a_pkgDir,
+ char *a_altBinDir, char **a_inheritedPkgDirs);
+static int pkgZoneCheckInstall(char *a_zoneName,
+ char **a_inheritedPkgDirs,
+ zone_state_t a_zoneState,
+ char *a_idsName, char *a_altBinDir,
+ char *a_adminFile, char *a_stdoutPath);
+static int pkgZoneInstall(char *a_zoneName,
+ char **a_inheritedPkgDirs,
+ zone_state_t a_zoneState,
+ char *a_idsName, char *a_altBinDir,
+ char *a_adminFile);
+static void resetreturn();
+static void usage(void);
+static boolean_t add_packages(char **a_pkgList, char *a_uri,
+ char *a_idsName, int a_repeat,
+ char *a_altBinDir, char *a_device,
+ boolean_t a_noZones);
+static boolean_t add_packages_in_global_no_zones(char **a_pkgList,
+ char *a_uri, char *a_idsName, int a_repeat,
+ char *a_altBinDir, char *a_device);
+static boolean_t add_packages_in_global_with_zones(char **a_pkgList,
+ char *a_uri, char *a_idsName, int a_repeat,
+ char *a_altBinDir, char *a_device,
+ zoneList_t a_zlst);
+static boolean_t add_packages_in_nonglobal_zone(char **a_pkgList,
+ char *a_uri, char *a_idsName, int a_repeat,
+ char *a_altBinDir, char *a_device);
+static boolean_t check_applicability(char *a_packageDir,
+ char *a_pkgInst, char *a_rootPath,
+ CAF_T a_flags);
+static boolean_t get_package_list(char ***r_pkgList, char **a_argv,
+ char *a_categories, char **a_categoryList,
+ int a_ignoreSignatures, PKG_ERR *a_err,
+ ushort_t a_httpProxyPort, char *a_httpProxyName,
+ keystore_handle_t a_keystore,
+ char *a_keystoreFile, char *a_idsName,
+ int *r_repeat);
+static boolean_t continue_installation(void);
+static boolean_t unpack_and_check_packages(char **a_pkgList,
+ char *a_idsName, char *a_packageDir);
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: main
+ * Description: main entry point for pkgadd/pkgask
+ * Returns: int
+ * 0 Successful completion
+ * 1 Fatal error.
+ * 2 Warning.
+ * 3 Interruption.
+ * 4 Administration.
+ * 5 Administration. Interaction is required. Do not use pkgadd -n.
+ * In addition, one of the following values may be added to the previous value
+ * as appropriate:
+ * 10 Reboot after installation of all packages.
+ * 20 Reboot after installation of this package.
+ * For example, "14" would indicate both "administration" and "reboot after
+ * installation of all packages".
+ */
+
+int
+main(int argc, char **argv)
+{
+ PKG_ERR *err = NULL;
+ WebScheme scheme = none;
+ char **category = NULL;
+ char *abiPtr;
+ char *altBinDir = (char *)NULL;
+ char *catg_arg = NULL;
+ char *device = NULL; /* dev pkg stored on */
+ char *dwnld_dir = NULL;
+ char *keystore_file = NULL;
+ char *p;
+ char *q;
+ char *prog;
+ char *prog_full_name = NULL;
+ char *proxy = NULL;
+ char *spoolDir = NULL; /* specified with -s */
+ char *uri = NULL;
+ char Rpath[PATH_MAX+1] = {'\0'};
+ int c;
+ int ignore_sig = 0;
+ int n;
+ int repeat;
+ int retries = NET_RETRIES_DEFAULT;
+ int timeout = NET_TIMEOUT_DEFAULT;
+ keystore_handle_t keystore = NULL;
+ struct sigaction nact;
+ struct sigaction oact;
+ ushort_t proxy_port = 0;
+
+ /* initialize locale environment */
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* initialize program name */
+
+ prog_full_name = argv[0];
+ prog = set_prog_name(argv[0]);
+
+ /* tell spmi zones interface how to access package output functions */
+
+ z_set_output_functions(echo, echoDebug, progerr);
+
+ askflag = (strcmp(prog, "pkgask") == 0);
+
+ /* set sane umask */
+
+ (void) umask(0022);
+
+ /* tell quit which ckreturn function to call */
+
+ quitSetCkreturnFunc(&ckreturn);
+
+ /* initially no source "device" */
+
+ device = NULL;
+
+ /* reset npkgs (used as pkg remaining count in quit.c) */
+
+ npkgs = 0;
+
+ /* set default password prompt for encrypted packages */
+
+ set_passphrase_prompt(MSG_PASSPROMPT);
+
+ /* initialize security operations structures and libraries */
+
+ sec_init();
+
+ if (z_running_in_global_zone() && !enable_local_fs()) {
+ progerr(ERR_CANNOT_ENABLE_LOCAL_FS);
+ }
+
+ /*
+ * ********************************************************************
+ * parse command line options
+ * ********************************************************************
+ */
+
+ while ((c = getopt(argc, argv,
+ "?Aa:b:B:Cc:D:d:GhIik:MnO:P:R:r:Ss:tV:vx:Y:zZ")) != EOF) {
+ switch (c) {
+
+ /*
+ * Not a public interface: This disables attribute checking.
+ * It speeds up installation a little bit.
+ */
+ case 'A':
+ disableAttributes++;
+ break;
+
+ /*
+ * Public interface: 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;
+
+ /*
+ * Not a public interface: control block size given to
+ * pkginstall - block size used in read()/write() loop;
+ * default is st_blksize from stat() of source file.
+ */
+ case 'B':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ rw_block_size = optarg;
+ break;
+
+ /*
+ * Not a public interface: location where package executables
+ * can be found - default is /usr/sadm/install/bin.
+ */
+ case 'b':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ if (!path_valid(optarg)) {
+ progerr(ERR_PATH, optarg);
+ quit(1);
+ }
+ if (isdir(optarg) != 0) {
+ p = strerror(errno);
+ progerr(ERR_CANNOT_USE_DIR, optarg, p);
+ quit(1);
+ }
+ altBinDir = optarg;
+ break;
+
+ /*
+ * Not a public interface: This disables checksum tests on
+ * the source files. It speeds up installation a little bit.
+ */
+ case 'C':
+ disableChecksum++;
+ break;
+
+ /*
+ * Not a public interface: 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 = flex_device(optarg, 0);
+ break;
+
+ /*
+ * Not a public interface: This allows designation of a
+ * dryrun file. This pkgadd will create dryrun files
+ * in the directory provided.
+ */
+ case 'D':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ pkgdrtarg = flex_device(optarg, 0);
+ break;
+
+ /*
+ * Public interface: 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':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ if (!path_valid(optarg)) {
+ progerr(ERR_PATH, optarg);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ if (strncmp(optarg, HTTP, 7) == 0) {
+ scheme = web_http;
+ } else if (strncmp(optarg, HTTPS, 8) == 0) {
+ scheme = web_https;
+ }
+
+ if (scheme == web_https || scheme == web_http) {
+ uri = optarg;
+ if ((device = malloc(PATH_MAX)) == NULL) {
+ progerr(ERR_MEM);
+ exit(1);
+ }
+ (void) memset(device, '\0', PATH_MAX);
+ } else {
+ device = flex_device(optarg, 1);
+ }
+ break;
+
+ /*
+ * Public interface: install package in global zone only.
+ */
+ case 'G':
+ globalZoneOnly = B_TRUE;
+ break;
+
+ /*
+ * Not a public interface: 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;
+
+ /*
+ * Not a public interface: 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;
+
+ /*
+ * Not a public interface: ignore signatures.
+ */
+ case 'i':
+ ignore_sig++;
+ break;
+
+ /*
+ * Public interface: Use keystore as the location from which to
+ * get trusted certificate authority certificates when verifying
+ * digital signatures found in packages. If no keystore is
+ * specified, then the default keystore locations are searched
+ * for valid trusted certificates.
+ */
+ case 'k':
+ if (!path_valid(optarg)) {
+ progerr(ERR_PATH, optarg);
+ quit(1);
+ /* NOTREACHED */
+ }
+ keystore_file = optarg;
+ break;
+
+ /*
+ * Public interface: 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':
+ no_map_client = 1;
+ break;
+
+ /*
+ * Not a public interface: the -O option allows the behavior
+ * of the package tools to be modified. Recognized options:
+ * -> debug
+ * ---> enable debugging output
+ * -> addzonename
+ * ---> add zone name to appropriate messages
+ * -> nozones
+ * ---> act as though in global zone with no non-global zones
+ * -> inherited-filesystems
+ * ---> add specified file system to list of file systems
+ * ---> that are inherited from the global zone
+ * -> 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 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
+ * -> zonelist="<names...>"
+ * ---> add package to space/colon separated list of zones only
+ */
+
+ case 'O':
+ for (p = strtok(optarg, ","); p != (char *)NULL;
+ p = strtok(NULL, ",")) {
+
+ if (strcmp(p, "debug") == 0) {
+ /* set debug flag/enable debug output */
+ 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;
+ }
+
+ if (strcmp(p,
+ "enable-hollow-package-support") == 0) {
+ set_depend_pkginfo_DB(B_TRUE);
+ continue;
+ }
+
+ if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
+ if (z_add_inherited_file_system(
+ p+INHERITFS_LEN) == B_FALSE) {
+ progerr(ERR_NOSUCH_INHERITED,
+ p+INHERITFS_LEN);
+ quit(1);
+ /* NOTREACHED */
+ }
+ continue;
+ }
+
+ if (strcmp(p, "addzonename") == 0) {
+ quitSetZoneName(z_get_zonename());
+ continue;
+ }
+
+ if (strcmp(p, "nozones") == 0) {
+ noZones = B_TRUE;
+ continue;
+ }
+
+ /*
+ * Private interface: package is being
+ * installed as a patch package.
+ */
+
+ if (strcmp(p, "patchPkgInstall") == 0) {
+ setPatchUpdate();
+ continue;
+ }
+
+ /*
+ * If this is a patch removal
+ * then call setPatchUpdate() and set
+ * patchPkgRemoval flag.
+ */
+ if (strcmp(p, "patchPkgRemoval") == 0) {
+ setPatchUpdate();
+ patchPkgRemoval = B_TRUE;
+ continue;
+ }
+
+ if (strncmp(p, "zonelist=", 9) == 0) {
+ /*
+ * If colons used as separators,
+ * convert to spaces.
+ */
+ q = p + 9;
+ while (*q != '\0') {
+ if (*q == ':') {
+ *q = ' ';
+ }
+ q++;
+ }
+
+ if (z_set_zone_spec(p + 9) == -1)
+ quit(1);
+ usedZoneList = B_TRUE;
+ continue;
+ }
+
+ progerr(ERR_INVALID_O_OPTION, p);
+ continue;
+ }
+ break;
+
+ /*
+ * Public interface: 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;
+
+ /*
+ * Public interface: Password to use to decrypt keystore
+ * specified with -k, if required. See PASS PHRASE
+ * ARGUMENTS for more information about the format of this
+ * option's argument.
+ */
+ case 'P':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ set_passphrase_passarg(optarg);
+ if (ci_strneq(optarg, "pass:", 5)) {
+ /*
+ * passwords on the command line are highly
+ * insecure. complain.
+ */
+ logerr(PASSWD_CMDLINE, "pass:<pass>");
+ }
+ break;
+
+ /*
+ * Public interface: 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 (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ /* determine the real path specified */
+
+ n = resolvepath(optarg, Rpath, sizeof (Rpath)-1);
+
+ /* use supplied path if not resolvable */
+
+ if (n == -1) {
+ (void) strlcpy(Rpath, optarg, sizeof (Rpath));
+ } else {
+ /* null terminate string */
+ Rpath[n] = '\0';
+ }
+
+ /* set the alternative root path */
+
+ if (!set_inst_root(Rpath)) {
+ progerr(ERR_ROOT_CMD);
+ exit(1);
+ }
+ break;
+
+ /*
+ * Public interface: Identify a file or directory which
+ * contains output from a previous pkgask(1M)
+ * 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':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ respfile = flex_device(optarg, 2);
+ if (isdir(respfile) == 0)
+ respdir = respfile;
+ break;
+
+ /*
+ * Not a public interface: suppress copyright notice being
+ * output during installation.
+ */
+ case 'S':
+ suppressCopyright++;
+ break;
+
+ /*
+ * Public interface: Write the package into the directory
+ * spool instead of installing it. The default directory
+ * for spooled packages is /var/sadm/pkg.
+ */
+ case 's':
+ spoolDir = flex_device(optarg, 1);
+ break;
+
+ /*
+ * Not a public interface: disable save spool area creation;
+ * suppress the creation and population of the package save
+ * spool area (var/sadm/pkg/PKG/save/pspool/PKG).
+ */
+ case 't':
+ disableSaveSpool++;
+ break;
+
+ /*
+ * Public interface: 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);
+ no_map_client = 0;
+ break;
+
+ /*
+ * Public interface: 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;
+
+ /*
+ * Public interface: Specify a HTTP[S] proxy to use when
+ * downloading packages The format of proxy is host:port,
+ * where host is the hostname of the HTTP[S] proxy, and
+ * port is the port number associated with the proxy. This
+ * switch overrides all other methods of specifying a
+ * proxy. See ENVIRONMENT VARIABLES for more information
+ * on alternate methods of specifying a default proxy.
+ */
+ case 'x':
+ if (!path_valid(optarg)) {
+ progerr(ERR_PATH, optarg);
+ quit(1);
+ /* NOTREACHED */
+ }
+ proxy = optarg;
+ break;
+
+ /*
+ * Public interface: Install packages based on the value
+ * of the CATEGORY parameter stored in the package's
+ * pkginfo(4) file. All packages on the source medium
+ * whose CATEGORY matches one of the specified categories
+ * will be selected for installation or spooling. Install
+ * packages that contain the same CATEGORY as the one
+ * provided on the command line.
+ */
+ case 'Y':
+ if (optarg[0] == '-') {
+ usage();
+ quit(1);
+ }
+ catg_arg = strdup(optarg);
+
+ if ((category = get_categories(catg_arg)) == NULL) {
+ progerr(ERR_CAT_INV, catg_arg);
+ exit(1);
+ } else if (is_not_valid_length(category)) {
+ progerr(ERR_CAT_LNGTH);
+ exit(1);
+ }
+ break;
+
+ /*
+ * Not a public interface: 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();
+ return (1);
+ }
+ }
+
+ /*
+ * ********************************************************************
+ * validate command line options
+ * ********************************************************************
+ */
+
+ /* set "debug echo" flag according to setting of "-O debug" option */
+
+ (void) echoDebugSetFlag(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());
+ }
+
+ /*
+ * Later, it may be decided to pursue this ability to continue to an
+ * actual installation based only on the dryrun data. At this time,
+ * it is too risky.
+ */
+
+ if (pkgcontsrc && !pkgdrtarg) {
+ progerr(ERR_NO_LIVE_MODE);
+ usage();
+ return (1);
+ }
+
+ /* ignore -G option if not used in the global zone */
+
+ if (!z_running_in_global_zone()) {
+ globalZoneOnly = B_FALSE;
+ }
+
+ /* if zonelist used, must be in global zone */
+
+ if (usedZoneList && !z_running_in_global_zone()) {
+ progerr(ERR_Z_USED_IN_NONGLOBAL_ZONE);
+ return (1);
+ }
+
+ /* -G and zonelist cannot be used together */
+
+ if (globalZoneOnly && usedZoneList) {
+ progerr(ERR_GZ_USED_TOGETHER);
+ usage();
+ return (1);
+ }
+
+ /* -s cannot be used with either -G or zonelist */
+
+ if (spoolDir != NULL) {
+ if (globalZoneOnly) {
+ progerr(ERR_SPOOLDIR_USED_WITH_G);
+ usage();
+ return (1);
+ }
+ if (usedZoneList) {
+ progerr(ERR_SPOOLDIR_USED_WITH_Z);
+ usage();
+ return (1);
+ }
+ if (strcmp(spoolDir, "/var/sadm/pkg") == 0) {
+ progerr(ERR_SPOOLDIR_CANNOT_BE_SYS, "/var/sadm/pkg");
+ usage();
+ return (1);
+ }
+ }
+
+ /* pkgask does not support the same options as pkgadd */
+
+ if (askflag && proxy) {
+ progerr(ERR_PKGASK_AND_PROXY);
+ usage();
+ return (1);
+ }
+
+ if (askflag && uri) {
+ progerr(ERR_PKGASK_AND_URI);
+ usage();
+ return (1);
+ }
+
+ if (askflag && keystore_file) {
+ progerr(ERR_PKGASK_AND_KEYSTORE_FILE);
+ usage();
+ return (1);
+ }
+
+ if (askflag && ignore_sig) {
+ progerr(ERR_PKGASK_AND_IGNORE_SIG);
+ usage();
+ return (1);
+ }
+
+ if (askflag && spoolDir) {
+ progerr(ERR_PKGASK_AND_SPOOLDIR);
+ usage();
+ return (1);
+ }
+
+ if (askflag && nointeract) {
+ progerr(ERR_PKGASK_AND_NOINTERACT);
+ usage();
+ return (1);
+ }
+
+ /* cannot use response file and web address together */
+
+ if (respfile && uri) {
+ progerr(ERR_RESPFILE_AND_URI);
+ usage();
+ return (1);
+ }
+
+ /* cannot use response file/not-interactive and spool-to directory */
+
+ if (spoolDir && nointeract) {
+ progerr(ERR_SPOOLDIR_AND_NOINTERACT);
+ usage();
+ return (1);
+ }
+
+ if (spoolDir && respfile) {
+ progerr(ERR_SPOOLDIR_AND_RESPFILE);
+ usage();
+ return (1);
+ }
+
+ if (usedZoneList) {
+ /* Verify supplied zone list valid for the target */
+ if (z_verify_zone_spec() == -1)
+ return (1);
+
+ /* -z zonelist=global is logically the same as -G */
+ if (z_global_only() && z_running_in_global_zone())
+ globalZoneOnly = B_TRUE;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * This function is in the libadm library; it sets:
+ * -> get_PKGLOC() = <install_root>/var/sadm/pkg
+ * -> get_PKGOLD() = <install_root>/usr/options
+ * -> get_PKGADM() = <install_root>/var/sadm/install
+ * -> pkgdir = <install_root>/var/sadm/pkg
+ * -> pkg_install_root = <install_root>
+ * This controls operations of libadm functions such as:
+ * -> pkginfofind, pkginfopen, fpkgparam, pkgparam, get_PKGLOC,
+ * -> get_PKGOLD, get_PKGADM, get_install_root
+ */
+
+ set_PKGpaths(get_inst_root());
+ echoDebug(DBG_PKGADD_PKGPATHS,
+ get_PKGLOC() ? get_PKGLOC() : "",
+ get_PKGADM() ? get_PKGADM() : "");
+
+ /*
+ * This function is in the libinst library; it reads the specified
+ * admin(4) file and, using fpkgparam(), sets the global "adm" structure
+ * values to match what is in the specified admin file.
+ */
+
+ echoDebug(DBG_PKGADD_ADMINFILE, admnfile ? admnfile : "");
+ setadminFile(admnfile);
+
+ /*
+ * if running in the global zone, and non-global zones exist, then
+ * enable hollow package support so that any packages that are marked
+ * SUNW_PKG_HOLLOW=true will be correctly installed in non-global zones
+ * when added directly in the global zone by the global zone admin.
+ */
+
+ if (is_depend_pkginfo_DB()) {
+ echoDebug(DBG_PKGADD_HOLLOW_ENABLED);
+ } else if ((z_running_in_global_zone() == B_TRUE) &&
+ (z_non_global_zones_exist() == B_TRUE)) {
+ echoDebug(DBG_PKGADD_ENABLING_HOLLOW);
+ set_depend_pkginfo_DB(B_TRUE);
+ }
+
+ /* if no device and no url, get and validate default device */
+
+ if ((device == NULL) && (uri == NULL)) {
+ device = devattr("spool", "pathname");
+ if (device == NULL) {
+ progerr(ERR_NODEVICE);
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ /* must be root if not directing results to spool directory */
+
+ if ((getuid() != 0) && (spoolDir == NULL)) {
+ progerr(ERR_NOT_ROOT, prog);
+ exit(1);
+ }
+
+ /*
+ * process response file argument
+ */
+
+ if (respfile) {
+ echoDebug(DBG_PKGADD_RESPFILE,
+ respfile, respdir ? respdir : "");
+
+ if (respfile[0] != '/') {
+ progerr(ERR_RSP_FILE_NOTFULLPATH, respfile);
+ quit(1);
+ /* NOTREACHED */
+ }
+ if (respdir == NULL) {
+ if (askflag) {
+ if (access(respfile, F_OK) == 0) {
+ progerr(ERR_NORESP, respfile);
+ quit(1);
+ /* NOTREACHED */
+ }
+ } else if (access(respfile, F_OK) != 0) {
+ progerr(ERR_ACCRESP, respfile);
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+ } else if (askflag) {
+ progerr(ERR_RSP_FILE_NOT_GIVEN);
+ usage();
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /* establish temporary directory to use */
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL) {
+ /* use default - no override specified */
+ tmpdir = P_tmpdir;
+ }
+
+ echoDebug(DBG_PKGADD_TMPDIR, tmpdir);
+
+ /*
+ * setup and prepare secure package operations
+ */
+
+ /* initialize error object used by security functions */
+
+ err = pkgerr_new();
+
+ /* validate keystore file */
+
+ if (!check_keystore_admin(&keystore_file)) {
+ progerr(ERR_ADM_KEYSTORE);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /* if uri provided, establish session */
+
+ if (uri != NULL) {
+ boolean_t b;
+ int len;
+ char *bname = (char *)NULL;
+
+ set_web_install();
+
+ if (!get_proxy_port(err, &proxy, &proxy_port)) {
+ pkgerr(err);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ if (proxy == NULL) {
+ if (!get_proxy_port_admin(&proxy, &proxy_port)) {
+ progerr(ERR_ADM_PROXY);
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ if ((retries = web_ck_retries()) == 0) {
+ pkgerr(err);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ if ((timeout = web_ck_timeout()) == 0) {
+ pkgerr(err);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /* create temporary directory */
+
+ b = setup_temporary_directory(&dwnld_dir, tmpdir, "dwnld");
+ if (b != B_TRUE) {
+ progerr(ERR_DWNLDTEMPDIR, tmpdir, strerror(errno));
+ quit(1);
+ /* NOTREACHED */
+ }
+ canonize_slashes(dwnld_dir);
+
+ /* register with quit() so directory is removed on exit */
+
+ quitSetDwnldTmpdir(dwnld_dir); /* DO NOT FREE() */
+
+ /* open keystore if this is a secure download */
+ if (scheme == web_https) {
+ if (open_keystore(err, keystore_file,
+ get_prog_name(), pkg_passphrase_cb,
+ KEYSTORE_DFLT_FLAGS, &keystore) != 0) {
+ pkgerr(err);
+ web_cleanup();
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ if (!web_session_control(err, uri, dwnld_dir, keystore, proxy,
+ proxy_port, retries, timeout, nointeract, &bname)) {
+ pkgerr(err);
+ web_cleanup();
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * reset device to point to newly-downloaded file; note
+ * when (scheme == web_https || scheme == web_http) that
+ * device gets preloaded with a pointer to PATH_MAX bytes
+ * allocated via malloc().
+ */
+
+ len = snprintf(device, PATH_MAX, "%s/%s", dwnld_dir, bname);
+ if ((len < 0) || (len >= PATH_MAX)) {
+ progerr(ERR_DIR_CONST, tmpdir);
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ /*
+ * See if user wants this to be handled as an old style pkg.
+ * NOTE : the ``exception_pkg()'' stuff is to be used only
+ * through on495. This function comes out for on1095. See
+ * PSARC 1993-546. -- JST
+ */
+
+ if (getenv("NONABI_SCRIPTS") != NULL) {
+ old_pkg = 1;
+ }
+
+ /*
+ * See if the user wants to process symlinks consistent with
+ * the old behavior.
+ */
+
+ if (getenv("PKG_NONABI_SYMLINKS") != NULL) {
+ old_symlinks = 1;
+ }
+
+ /*
+ * See if the user wants the package name length restricted.
+ */
+
+ abiPtr = getenv("PKG_ABI_NAMELENGTH");
+ if (abiPtr && strncasecmp(abiPtr, "TRUE", 4) == 0) {
+ ABI_namelength = 1;
+ }
+
+ /*
+ * validate the package source device - return pkgdev info that
+ * describes the package source device.
+ */
+
+ if (devtype(device, &pkgdev)) {
+ progerr(ERR_BAD_DEVICE, device);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * If writing the packages into a spool directory instead of
+ * installing the packages, open the package datastream and
+ * invoke pkgtrans to perform the conversion and exit.
+ */
+
+ if (spoolDir != (char *)NULL) {
+ boolean_t b;
+ int n;
+
+ echoDebug(DBG_INSTALLING_TO_SPOOL, spoolDir);
+
+ b = open_package_datastream(argc, argv, spoolDir, device,
+ &repeat, &ids_name, tmpdir,
+ &pkgdev, optind);
+
+ quitSetIdsName(ids_name);
+
+ if (b != B_TRUE) {
+ progerr(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
+ quit(1);
+ }
+
+ n = pkgtrans(device, spoolDir, &argv[optind],
+ 0, NULL, NULL);
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * error if there are packages on the command line and a category
+ * was specified
+ */
+
+ if ((optind < argc) && (catg_arg != NULL)) {
+ progerr(ERR_PKGS_AND_CAT_PKGADD);
+ usage();
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * ********************************************************************
+ * main package processing "loop"
+ * ********************************************************************
+ */
+
+ ids_name = NULL;
+ quitSetIdsName(ids_name);
+
+ for (;;) {
+ boolean_t b;
+ char **pkglist; /* points to array of pkgs */
+
+ /*
+ * open next package data stream
+ */
+
+ b = open_package_datastream(argc, argv, spoolDir, device,
+ &repeat, &ids_name, tmpdir,
+ &pkgdev, optind);
+
+ quitSetIdsName(ids_name);
+
+ if (b == B_FALSE) {
+ echoDebug(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
+ continue;
+ }
+
+ /*
+ * package source data stream open - get the package list
+ */
+
+ b = get_package_list(&pkglist, argv, catg_arg, category,
+ ignore_sig, err, proxy_port, proxy, keystore,
+ keystore_file, ids_name, &repeat);
+
+ if (b == B_FALSE) {
+ char path[PATH_MAX];
+
+ echoDebug(DBG_CANNOT_GET_PKGLIST);
+
+ /* check for existence of pre-SVR4 package */
+ (void) snprintf(path, sizeof (path),
+ "%s/install/INSTALL", pkgdev.dirname);
+ if (access(path, F_OK) == 0) {
+ pkginst = ((optind < argc) ?
+ argv[optind++] : NULL);
+ ckreturn(presvr4(&pkginst, nointeract));
+ if (repeat || (optind < argc)) {
+ continue;
+ }
+ quit(0);
+ }
+ progerr(ERR_NOPKGS, pkgdev.dirname);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * count the number of packages to install
+ * NOTE: npkgs is a global variable that is referenced by quit.c
+ * when error messages are generated - it is referenced directly
+ * by the other functions called below...
+ */
+
+ for (npkgs = 0; pkglist[npkgs] != (char *)NULL; /* void */) {
+ echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
+ npkgs++;
+ }
+
+ /* output number of packages to be added */
+
+ echoDebug(DBG_NUM_PKGS_TO_ADD, npkgs);
+
+ /*
+ * if pkgask and response container is a file (not a directory),
+ * and there is more than one package to install, then it is an
+ * error - too many packages to install when response container
+ * is a file.
+ */
+
+ if ((askflag != 0) && (respdir == (char *)NULL) &&
+ (npkgs > 1)) {
+ progerr(ERR_TOO_MANY_PKGS);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * package list generated - add packages
+ */
+
+ b = add_packages(pkglist, uri, ids_name, repeat,
+ altBinDir, device, noZones);
+
+ /*
+ * close open input data stream (source package) if left open.
+ */
+
+ if (ids_name) {
+ echoDebug(DBG_CLOSING_STREAM, ids_name,
+ PSTR(pkgdev.dirname));
+ (void) ds_close(1);
+ rrmdir(pkgdev.dirname);
+ ids_name = NULL;
+ quitSetIdsName(ids_name);
+ }
+
+ /*
+ * continue with next sequence of packages if continue set
+ */
+
+ if (b == B_TRUE) {
+ continue;
+ }
+
+ /*
+ * not continuing - quit with 0 exit code
+ */
+
+ quit(0);
+ /* NOTREACHED */
+ }
+
+ /* NOTREACHED */
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: pkgZoneCheckInstall
+ * Description: Invoke pkginstall in a specified zone to perform a preinstall
+ * check of the a single package in the specified zone
+ * Arguments: a_zoneName - pointer to string representing the name of the
+ * zone to check install the package in.
+ * a_inheritedPkgDirs - pointer to array of strings, each one
+ * representing the non-global zones full path of a
+ * directory that is inherited from the global zone.
+ * a_zoneState - current state of the zone; must be mounted or
+ * running.
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be check installed.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_adminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_stdoutPath - pointer to string representing the local path
+ * into which all output written by pkginstall to stdout
+ * is stored.
+ * If this is == NULL stdout is redirected to /dev/null
+ * Returns: int (see ckreturn() function for details)
+ * 0 - success
+ * 1 - package operation failed (fatal error)
+ * 2 - non-fatal error (warning)
+ * 3 - user selected quit (operation interrupted)
+ * 4 - admin settings prevented operation
+ * 5 - interaction required and -n (non-interactive) specified
+ * "10" will be added to indicate "immediate reboot required"
+ * "20" will be added to indicate "reboot after install required"
+ */
+
+static int
+pkgZoneCheckInstall(char *a_zoneName, char **a_inheritedPkgDirs,
+ zone_state_t a_zoneState, char *a_idsName, char *a_altBinDir,
+ char *a_adminFile, char *a_stdoutPath)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char adminfd_path[PATH_MAX];
+ char path[PATH_MAX];
+ char pkgstreamfd_path[PATH_MAX];
+ int fds[MAX_FDS];
+ int maxfds;
+ int n;
+ int nargs;
+
+ /* entry assertions */
+
+ assert(a_zoneName != (char *)NULL);
+ assert(*a_zoneName != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGZONECHECKINSTALL_ENTRY);
+ echoDebug(DBG_PKGZONECHECKINSTALL_ARGS, a_zoneName, PSTR(pkginst),
+ PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
+ a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "/",
+ PSTR(a_idsName), PSTR(a_adminFile), PSTR(a_stdoutPath));
+
+ /* generate full path to 'phatinstall' to run in zone */
+
+ (void) snprintf(path, sizeof (path), "%s/pkginstall",
+ "/usr/sadm/install/bin");
+
+ /* start at first file descriptor */
+
+ maxfds = 0;
+
+ /*
+ * generate argument list for call to pkginstall
+ */
+
+ /* start at argument 0 */
+
+ nargs = 0;
+
+ /* first argument is always: full path to executable */
+
+ arg[nargs++] = path;
+
+ /*
+ * second argument is always: pass -O debug to pkginstall: debug mode
+ */
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* pkgadd -G: pass -G to pkginstall */
+
+ if (globalZoneOnly == B_TRUE) {
+ arg[nargs++] = "-G";
+ }
+
+ /* pkgadd -b dir: pass -b to pkginstall */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /* pkgadd -C: pass -C to pkginstall: disable checksum */
+
+ if (disableChecksum) {
+ arg[nargs++] = "-C";
+ }
+
+ /* pkgadd -A: pass -A to pkginstall: disable attribute checking */
+
+ if (disableAttributes) {
+ arg[nargs++] = "-A";
+ }
+
+ /*
+ * NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
+ * pkg requiring operator interaction during a procedure script
+ * (common before on1093)
+ */
+
+ if (old_pkg) {
+ arg[nargs++] = "-o";
+ }
+
+ /*
+ * PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
+ * symlinks consistent with old behavior
+ */
+
+ if (old_symlinks) {
+ arg[nargs++] = "-y";
+ }
+
+ /*
+ * PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
+ * package name length to be restricted
+ */
+
+ if (ABI_namelength) {
+ arg[nargs++] = "-e";
+ }
+
+ /* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
+
+ arg[nargs++] = "-S";
+
+ /* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
+
+ arg[nargs++] = "-M";
+
+ /* pkgadd -v: pass -v to pkginstall: never trace scripts */
+
+ /* if running pkgask, pass -i to pkginstall: running pkgask */
+
+ if (askflag) {
+ return (0);
+ }
+
+ /* pass "-O enable-hollow-package-support" */
+
+ if (is_depend_pkginfo_DB()) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "enable-hollow-package-support";
+ }
+
+ /* check is always in non-interactive mode */
+
+ arg[nargs++] = "-n";
+
+ /* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
+
+ if (a_adminFile) {
+ int fd;
+ fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+ if (fd < 0) {
+ progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+ errno, strerror(errno));
+ return (1);
+ }
+ (void) snprintf(adminfd_path, sizeof (adminfd_path),
+ "/proc/self/fd/%d", fd);
+ fds[maxfds++] = fd;
+ arg[nargs++] = "-a";
+ arg[nargs++] = adminfd_path;
+ }
+
+ /* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
+
+ if (a_zoneState == ZONE_STATE_MOUNTED) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = "/a";
+ }
+
+ /* pass -N to pkginstall: program name to report */
+
+ arg[nargs++] = "-N";
+ arg[nargs++] = get_prog_name();
+
+ /* pass "-O preinstallcheck" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "preinstallcheck";
+
+ /* add "-O addzonename" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "addzonename";
+
+ if (isPatchUpdate()) {
+ if (patchPkgRemoval == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgRemoval";
+ } else {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgInstall";
+ }
+ }
+
+ /* add all inherited file systems */
+
+ if (a_inheritedPkgDirs != (char **)NULL) {
+ for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+ char ifs[MAXPATHLEN+22];
+ (void) snprintf(ifs, sizeof (ifs),
+ "inherited-filesystem=%s",
+ a_inheritedPkgDirs[n]);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(ifs);
+ }
+ }
+
+ /*
+ * add parent zone info/type
+ */
+
+ p = z_get_zonename();
+ if ((p != NULL) && (*p != '\0')) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-name=%s", p);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* current zone type */
+
+ arg[nargs++] = "-O";
+ if (z_running_in_global_zone() == B_TRUE) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_GLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ } else {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_NONGLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* add in the package stream file */
+
+ if (a_idsName != NULL) {
+ int fd;
+ fd = openLocal(a_idsName, O_RDONLY, tmpdir);
+ if (fd < 0) {
+ progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
+ pkginst, strerror(errno));
+ quit(1);
+ }
+ (void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
+ "/proc/self/fd/%d", fd);
+ fds[maxfds++] = fd;
+ arg[nargs++] = pkgstreamfd_path;
+ } else {
+ progerr(ERR_PKGZONEINSTALL_NO_STREAM);
+ quit(1);
+ }
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate the argument list */
+
+ arg[nargs++] = NULL;
+
+ /*
+ * run the appropriate pkginstall command in the specified zone
+ */
+
+ if (debugFlag == B_TRUE) {
+ echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+ for (n = 0; arg[n]; n++) {
+ echoDebug(DBG_ARG, n, arg[n]);
+ }
+ }
+
+ /* terminate file descriptor list */
+
+ fds[maxfds] = -1;
+
+ /* exec command in zone */
+
+ n = z_zone_exec(a_zoneName, path, arg, a_stdoutPath, (char *)NULL, fds);
+
+ echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n,
+ PSTR(a_stdoutPath));
+
+ /*
+ * close any files that were opened for use by the
+ * /proc/self/fd interface so they could be passed to programs
+ * via the z_zone_exec() interface
+ */
+
+ for (; maxfds > 0; maxfds--) {
+ (void) close(fds[maxfds-1]);
+ }
+
+ /* return results of pkginstall in zone execution */
+
+ return (n);
+}
+
+/*
+ * Name: pkgZoneInstall
+ * Description: Invoke pkginstall in a specified zone to perform an install
+ * of a single package in the specified zone
+ * Arguments: a_zoneName - pointer to string representing the name of the
+ * zone to install the package in.
+ * a_inheritedPkgDirs - pointer to array of strings, each one
+ * representing the non-global zones full path of a
+ * directory that is inherited from the global zone.
+ * a_zoneState - current state of the zone; must be mounted or
+ * running.
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be installed.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_adminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_stdoutPath - pointer to string representing the local path
+ * into which all output written by pkginstall to stdout
+ * is stored.
+ * If this is == NULL stdout is redirected to /dev/null
+ * Returns: int (see ckreturn() function for details)
+ * 0 - success
+ * 1 - package operation failed (fatal error)
+ * 2 - non-fatal error (warning)
+ * 3 - user selected quit (operation interrupted)
+ * 4 - admin settings prevented operation
+ * 5 - interaction required and -n (non-interactive) specified
+ * "10" will be added to indicate "immediate reboot required"
+ * "20" will be added to indicate "reboot after install required"
+ */
+
+static int
+pkgZoneInstall(char *a_zoneName, char **a_inheritedPkgDirs,
+ zone_state_t a_zoneState, char *a_idsName, char *a_altBinDir,
+ char *a_adminFile)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char adminfd_path[PATH_MAX];
+ char path[PATH_MAX];
+ char pkgstreamfd_path[PATH_MAX];
+ char respfilefd_path[PATH_MAX];
+ int fds[MAX_FDS];
+ int maxfds;
+ int n;
+ int nargs;
+
+ /* entry assertions */
+
+ assert(a_zoneName != (char *)NULL);
+ assert(*a_zoneName != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGZONEINSTALL_ENTRY);
+ echoDebug(DBG_PKGZONEINSTALL_ARGS, a_zoneName, PSTR(pkginst),
+ PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
+ a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "", PSTR(a_idsName),
+ a_adminFile);
+
+ /* generate path to pkginstall */
+
+ (void) snprintf(path, sizeof (path), "%s/pkginstall", PKGBIN);
+
+ /* start at first file descriptor */
+
+ maxfds = 0;
+
+ /*
+ * generate argument list for call to pkginstall
+ */
+
+ /* start at argument 0 */
+
+ nargs = 0;
+
+ /* first argument is path to executable */
+
+ arg[nargs++] = path;
+
+ /*
+ * second argument is always: pass -O debug to pkginstall: debug mode
+ */
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* pkgadd -G: pass -G to pkginstall */
+
+ if (globalZoneOnly == B_TRUE) {
+ arg[nargs++] = "-G";
+ }
+
+ /* pkgadd -b dir: pass -b to pkginstall in zone */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /* pkgadd -B blocksize: pass -B to pkginstall in zone */
+
+ if (rw_block_size != NULL) {
+ arg[nargs++] = "-B";
+ arg[nargs++] = rw_block_size;
+ }
+
+ /* pkgadd -C: pass -C to pkgadd in zone: disable checksum */
+
+ if (disableChecksum) {
+ arg[nargs++] = "-C";
+ }
+
+ /* pkgadd -A: pass -A to pkgadd in zone: disable attribute checking */
+
+ if (disableAttributes) {
+ arg[nargs++] = "-A";
+ }
+
+ /* pkgadd -S: pass -S to pkgadd in zone: suppress copyright notices */
+
+ arg[nargs++] = "-S";
+
+ /* pkgadd -I: pass -I to pkgadd in zone: initial install */
+
+ if (init_install) {
+ arg[nargs++] = "-I";
+ }
+
+ /* pkgadd -M: pass -M to pkgadd in zone: dont mount client file sys */
+
+ arg[nargs++] = "-M";
+
+ /* pkgadd -v: pass -v to pkgadd in zone: trace scripts */
+
+ if (pkgverbose) {
+ arg[nargs++] = "-v";
+ }
+
+ /* pkgadd -z: pass -z to pkgadd in zone fresh inst from pkg save area */
+
+ if (saveSpoolInstall) {
+ arg[nargs++] = "-z";
+ }
+
+ /* pass "-O enable-hollow-package-support" */
+
+ if (is_depend_pkginfo_DB()) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "enable-hollow-package-support";
+ }
+
+ /* pkgadd -t pass -t to pkgadd in zone disable save spool area create */
+
+ if (disableSaveSpool) {
+ arg[nargs++] = "-t";
+ }
+
+ /* if running pkgask, pass -i to pkgadd in zone: running pkgask */
+
+ if (askflag) {
+ echo(MSG_BYPASSING_ZONE, a_zoneName);
+ return (0);
+ }
+
+ /*
+ * pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode
+ */
+ if (nointeract && !askflag) {
+ arg[nargs++] = "-n";
+ }
+
+ /* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
+
+ if (a_adminFile) {
+ int fd;
+ fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+ if (fd < 0) {
+ progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+ errno, strerror(errno));
+ return (1);
+ }
+ (void) snprintf(adminfd_path, sizeof (adminfd_path),
+ "/proc/self/fd/%d", fd);
+ fds[maxfds++] = fd;
+ arg[nargs++] = "-a";
+ arg[nargs++] = adminfd_path;
+ }
+
+ /* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
+ if (a_zoneState == ZONE_STATE_MOUNTED) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = "/a";
+ }
+
+ /*
+ * pkgadd -D arg: pass -D dryrun to pkginstall in zone: dryrun
+ * mode/file
+ */
+ if (pkgdrtarg) {
+ arg[nargs++] = "-D";
+ arg[nargs++] = pkgdrtarg;
+ }
+
+ /*
+ * pkgadd -c cont: pass -c cont to pkginstall in zone: continuation
+ * file
+ */
+ if (pkgcontsrc) {
+ arg[nargs++] = "-c";
+ arg[nargs++] = pkgcontsrc;
+ }
+
+ /* pkgadd -r resp: pass -r resp to pkginstall in zone: response file */
+
+ if (respfile) {
+ int fd;
+ fd = openLocal(respfile, O_RDONLY, tmpdir);
+ if (fd < 0) {
+ progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+ errno, strerror(errno));
+ return (1);
+ }
+ (void) snprintf(respfilefd_path,
+ sizeof (respfilefd_path),
+ "/proc/self/fd/%d", fd);
+ fds[maxfds++] = fd;
+ arg[nargs++] = "-r";
+ arg[nargs++] = respfilefd_path;
+ }
+
+ /* add "-O addzonename" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "addzonename";
+
+ if (isPatchUpdate()) {
+ if (patchPkgRemoval == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgRemoval";
+ } else {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgInstall";
+ }
+ }
+
+ /* add all inherited file systems */
+
+ if (a_inheritedPkgDirs != (char **)NULL) {
+ for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+ char ifs[MAXPATHLEN+22];
+ (void) snprintf(ifs, sizeof (ifs),
+ "inherited-filesystem=%s",
+ a_inheritedPkgDirs[n]);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(ifs);
+ }
+ }
+
+ /*
+ * add parent zone info/type
+ */
+
+ p = z_get_zonename();
+ if ((p != NULL) && (*p != '\0')) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-name=%s", p);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* current zone type */
+
+ arg[nargs++] = "-O";
+ if (z_running_in_global_zone() == B_TRUE) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_GLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ } else {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_NONGLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* add in the package stream file */
+
+ if (a_idsName != NULL) {
+ int fd;
+ fd = openLocal(a_idsName, O_RDONLY, tmpdir);
+ if (fd < 0) {
+ progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
+ pkginst, strerror(errno));
+ quit(1);
+ }
+ (void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
+ "/proc/self/fd/%d", fd);
+ fds[maxfds++] = fd;
+ arg[nargs++] = pkgstreamfd_path;
+ } else {
+ progerr(ERR_PKGZONEINSTALL_NO_STREAM);
+ quit(1);
+ }
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate the argument list */
+
+ arg[nargs++] = NULL;
+
+ /*
+ * run the appropriate pkginstall command in the specified zone
+ */
+
+ if (debugFlag == B_TRUE) {
+ echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+ for (n = 0; arg[n]; n++) {
+ echoDebug(DBG_ARG, n, arg[n]);
+ }
+ }
+
+ /* terminate file descriptor list */
+
+ fds[maxfds] = -1;
+
+ /* exec command in zone */
+
+ n = z_zone_exec(a_zoneName, path, arg, (char *)NULL, (char *)NULL, fds);
+
+ echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n, "");
+
+ /*
+ * close any files that were opened for use by the
+ * /proc/self/fd interface so they could be passed to programs
+ * via the z_zone_exec() interface
+ */
+
+ for (; maxfds > 0; maxfds--) {
+ (void) close(fds[maxfds-1]);
+ }
+
+ /* return results of pkginstall in zone execution */
+
+ return (n);
+}
+
+/*
+ * Name: pkgInstall
+ * Description: Invoke pkginstall in the current zone to perform an install
+ * of a single package to the current zone or standalone system
+ * Arguments: a_altRoot - pointer to string representing the alternative
+ * root to use for the install
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be installed.
+ * a_pkgDir - pointer to string representing the path to the
+ * directory containing the package
+ * a_altBinDir - pointer to string representing location of the
+ * pkginstall executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkginstall.
+ * a_inheritedPkgDirs - pointer to array of strings, each one
+ * representing the non-global zones full path of a
+ * directory that is inherited from the global zone.
+ * Returns: int (see ckreturn() function for details)
+ * 0 - success
+ * 1 - package operation failed (fatal error)
+ * 2 - non-fatal error (warning)
+ * 3 - user selected quit (operation interrupted)
+ * 4 - admin settings prevented operation
+ * 5 - interaction required and -n (non-interactive) specified
+ * "10" will be added to indicate "immediate reboot required"
+ * "20" will be added to indicate "reboot after install required"
+ * NOTE: Both a_idsName and a_pkgDir are used to determine where the
+ * package to be installed is located. If a_idsName is != NULL
+ * then it must be the path to a device containing a package
+ * stream that contains the package to be installed. If a_idsName
+ * is == NULL then a_pkgDir must contain a full path to a directory
+ * that contains the package to be installed.
+ */
+
+static int
+pkgInstall(char *a_altRoot, char *a_idsName, char *a_pkgDir, char *a_altBinDir,
+ char **a_inheritedPkgDirs)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char path[PATH_MAX];
+ char buffer[256];
+ int n, nargs;
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGINSTALL_ENTRY);
+ echoDebug(DBG_PKGINSTALL_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
+ PSTR(pkgdev.mount), PSTR(pkgdev.bdevice), PSTR(a_altRoot),
+ PSTR(a_idsName), PSTR(a_pkgDir));
+
+ /* generate full path to 'pkginstall' to run in zone */
+
+ (void) snprintf(path, sizeof (path), "%s/pkginstall",
+ a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+ /*
+ * generate argument list for call to pkginstall
+ */
+
+ /* start at argument 0 */
+
+ nargs = 0;
+
+ /* first argument is path to executable */
+
+ arg[nargs++] = path;
+
+ /*
+ * second argument is always: pass -O debug to pkginstall: debug mode
+ */
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* Installation is from a patch package. */
+
+ if (isPatchUpdate()) {
+ if (patchPkgRemoval == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgRemoval";
+ } else {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "patchPkgInstall";
+ }
+ }
+
+ /*
+ * pkgadd -G: pass -G to pkginstall if:
+ * - the -G option is specified on the pkgadd command line
+ * - this package is marked 'this zone only':
+ * -- package has SUNW_PKG_THISZONE=true, or
+ * -- package has a request script
+ * Setting -G for pkginstall causes pkginstall to install the package
+ * in the target zone. If running in the global zone, will install the
+ * package and mark the package as installed "in the global zone only".
+ * If running in a non-global zone, will just install the package.
+ */
+
+ if (globalZoneOnly == B_TRUE) {
+ arg[nargs++] = "-G";
+ } else if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+ arg[nargs++] = "-G";
+ }
+
+ /* pkgadd -b dir: pass -b to pkginstall */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /* pkgadd -B blocksize: pass -B to pkginstall */
+
+ if (rw_block_size != NULL) {
+ arg[nargs++] = "-B";
+ arg[nargs++] = rw_block_size;
+ }
+
+ /* pkgadd -C: pass -C to pkginstall: disable checksum */
+
+ if (disableChecksum) {
+ arg[nargs++] = "-C";
+ }
+
+ /* pkgadd -A: pass -A to pkginstall: disable attribute checking */
+
+ if (disableAttributes) {
+ arg[nargs++] = "-A";
+ }
+
+ /*
+ * NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
+ * pkg requiring operator interaction during a procedure script
+ * (common before on1093)
+ */
+
+ if (old_pkg) {
+ arg[nargs++] = "-o";
+ }
+
+ /*
+ * PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
+ * symlinks consistent with old behavior
+ */
+
+ if (old_symlinks) {
+ arg[nargs++] = "-y";
+ }
+
+ /*
+ * PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
+ * package name length to be restricted
+ */
+
+ if (ABI_namelength) {
+ arg[nargs++] = "-e";
+ }
+
+ /* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
+
+ if (suppressCopyright) {
+ arg[nargs++] = "-S";
+ }
+
+ /* pkgadd -I: pass -I to pkginstall: initial install being performed */
+
+ if (init_install) {
+ arg[nargs++] = "-I";
+ }
+
+ /* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
+
+ if (no_map_client) {
+ arg[nargs++] = "-M";
+ }
+
+ /* pkgadd -v: pass -v to pkginstall: trace scripts */
+
+ if (pkgverbose) {
+ arg[nargs++] = "-v";
+ }
+
+ /* pkgadd -z: pass -z to pkginstall: fresh install from pkg save area */
+
+ if (saveSpoolInstall) {
+ arg[nargs++] = "-z";
+ }
+
+ /*
+ * if running in a non-global zone and the 'hollow' attribute is
+ * passed in, then pass -h to pkginstall so that it knows how to
+ * handle hollow packages for this local zone.
+ */
+
+ if (!z_running_in_global_zone() && is_depend_pkginfo_DB()) {
+ arg[nargs++] = "-h";
+ }
+
+ /* pkgadd -t: pass -t to pkginstall: disable save spool area creation */
+
+ if (disableSaveSpool) {
+ arg[nargs++] = "-t";
+ }
+
+ /* if running pkgask, pass -i to pkginstall: running pkgask */
+
+ if (askflag) {
+ arg[nargs++] = "-i";
+ }
+
+ /* pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode */
+
+ if (nointeract && !askflag) {
+ arg[nargs++] = "-n";
+ }
+
+ /* pkgadd -a admin: pass -a admin to pkginstall: admin file */
+
+ if (admnfile) {
+ arg[nargs++] = "-a";
+ arg[nargs++] = admnfile;
+ }
+
+ /* pkgadd -D dryrun: pass -D dryrun to pkginstall: dryrun mode/file */
+
+ if (pkgdrtarg) {
+ arg[nargs++] = "-D";
+ arg[nargs++] = pkgdrtarg;
+ }
+
+ /* pkgadd -c cont: pass -c cont to pkginstall: continuation file */
+
+ if (pkgcontsrc) {
+ arg[nargs++] = "-c";
+ arg[nargs++] = pkgcontsrc;
+ }
+
+ /* pkgadd -V vfstab: pass -V vfstab to pkginstall: alternate vfstab */
+
+ if (vfstab_file) {
+ arg[nargs++] = "-V";
+ arg[nargs++] = vfstab_file;
+ }
+
+ /* pkgadd -r resp: pass -r resp to pkginstall: response file */
+
+ if (respfile) {
+ arg[nargs++] = "-r";
+ arg[nargs++] = respfile;
+ }
+
+ /* pkgadd -R root: pass -R root to pkginstall: alternative root */
+
+ if (a_altRoot && *a_altRoot) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = a_altRoot;
+ }
+
+ /*
+ * If input data stream is available,
+ * - add: -d ids_name -p number_of_parts
+ * else,
+ * - add: -d device -m mount [-f type]
+ */
+
+ if (a_idsName != NULL) {
+ arg[nargs++] = "-d";
+ arg[nargs++] = a_idsName;
+ arg[nargs++] = "-p";
+ ds_close(1);
+ ds_putinfo(buffer);
+ arg[nargs++] = buffer;
+ } else if (pkgdev.mount != NULL) {
+ arg[nargs++] = "-d";
+ arg[nargs++] = pkgdev.bdevice;
+ arg[nargs++] = "-m";
+ arg[nargs++] = pkgdev.mount;
+ if (pkgdev.fstyp != NULL) {
+ arg[nargs++] = "-f";
+ arg[nargs++] = pkgdev.fstyp;
+ }
+ }
+
+ /* add all inherited file systems */
+
+ if (a_inheritedPkgDirs != (char **)NULL) {
+ for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+ char ifs[MAXPATHLEN+22];
+ (void) snprintf(ifs, sizeof (ifs),
+ "inherited-filesystem=%s",
+ a_inheritedPkgDirs[n]);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(ifs);
+ }
+ }
+
+ /*
+ * add parent zone info/type
+ */
+
+ p = z_get_zonename();
+ if ((p != NULL) && (*p != '\0')) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-name=%s", p);
+ arg[nargs++] = "-O";
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* current zone type */
+
+ arg[nargs++] = "-O";
+ if (z_running_in_global_zone() == B_TRUE) {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_GLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ } else {
+ char zn[MAXPATHLEN];
+ (void) snprintf(zn, sizeof (zn),
+ "parent-zone-type=%s",
+ TAG_VALUE_NONGLOBAL_ZONE);
+ arg[nargs++] = strdup(zn);
+ }
+
+ /* pass -N to pkginstall: program name to report */
+
+ arg[nargs++] = "-N";
+ arg[nargs++] = get_prog_name();
+
+ /* add package directory name */
+
+ arg[nargs++] = a_pkgDir;
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate the argument list */
+
+ arg[nargs++] = NULL;
+
+ /*
+ * run the appropriate pkginstall command in the specified zone
+ */
+
+ if (debugFlag == B_TRUE) {
+ echoDebug(DBG_ZONE_EXEC_ENTER, "global", arg[0]);
+ for (n = 0; arg[n]; n++) {
+ echoDebug(DBG_ARG, n, arg[n]);
+ }
+ }
+
+ /* execute pkginstall command */
+
+ n = pkgexecv(NULL, NULL, NULL, NULL, arg);
+
+ /* return results of pkginstall execution */
+
+ return (n);
+}
+
+/*
+ * function to clear out any exisiting error return conditions that may have
+ * been set by previous calls to ckreturn()
+ */
+static void
+resetreturn()
+{
+ admnflag = 0; /* != 0 if any pkg op admin setting failure (4) */
+ doreboot = 0; /* != 0 if reboot required after installation (>= 10) */
+ failflag = 0; /* != 0 if fatal error has occurred (1) */
+ intrflag = 0; /* != 0 if user selected quit (3) */
+ ireboot = 0; /* != 0 if immediate reboot required (>= 20) */
+ nullflag = 0; /* != 0 if admin interaction required (5) */
+ warnflag = 0; /* != 0 if non-fatal error has occurred (2) */
+ interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
+ needconsult = 0; /* essential ask admin now (1,2,3,5) */
+}
+
+/*
+ * function which checks the indicated return value
+ * and indicates disposition of installation
+ */
+static void
+ckreturn(int retcode)
+{
+ /*
+ * entry debugging info
+ */
+
+ echoDebug(DBG_PKGADD_CKRETURN, retcode, PSTR(pkginst));
+
+ /* reset needconsult so it only reflects this call to ckreturn */
+ needconsult = 0;
+
+ switch (retcode) {
+ case 0: /* successful */
+ case 10:
+ case 20:
+ break; /* empty case */
+
+ case 1: /* package operation failed (fatal error) */
+ case 11:
+ case 21:
+ failflag++;
+ interrupted++;
+ needconsult++;
+ break;
+
+ case 2: /* non-fatal error (warning) */
+ case 12:
+ case 22:
+ warnflag++;
+ interrupted++;
+ needconsult++;
+ break;
+
+ case 3: /* user selected quit; operation interrupted */
+ case 13:
+ case 23:
+ intrflag++;
+ interrupted++;
+ needconsult++;
+ break;
+
+ case 4: /* admin settings prevented operation */
+ case 14:
+ case 24:
+ admnflag++;
+ interrupted++;
+ break;
+
+ case 5: /* administration: interaction req (no -n) */
+ case 15:
+ case 25:
+ nullflag++;
+ interrupted++;
+ needconsult++;
+ break;
+
+ default:
+ failflag++;
+ interrupted++;
+ needconsult++;
+ return;
+ }
+
+ if (retcode >= 20) {
+ ireboot++;
+ } else if (retcode >= 10) {
+ doreboot++;
+ }
+}
+
+static void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ if (askflag) {
+ (void) fprintf(stderr, ERR_USAGE_PKGASK, prog);
+ } else if (z_running_in_global_zone() == B_FALSE) {
+ (void) fprintf(stderr, ERR_USAGE_PKGADD_NONGLOBALZONE,
+ prog, prog);
+ } else {
+ (void) fprintf(stderr, ERR_USAGE_PKGADD_GLOBALZONE,
+ prog, prog);
+ }
+}
+
+/*
+ * Name: check_applicability
+ * Description: determine if a package is installable in this zone; that is,
+ * does the scope of install conflict with existing installation
+ * or can the package be installed
+ * Arguments: a_packageDir - [RO, *RO] - (char *)
+ * Pointer to string representing the directory where the
+ * package is located
+ * a_pkgInst - [RO, *RO] - (char *)
+ * Pointer to string representing the name of the package
+ * to check
+ * a_rootPath - [RO, *RO] - (char *)
+ * Pointer to string representing path to the root of the
+ * file system where the package is to be installed - this
+ * is usually the same as the "-R" argument to pkgadd
+ * a_flags - [RO, *RO] - (CAF_T)
+ * Flags set by the caller to indicate the conditions
+ * under which the package is to be installed:
+ * CAF_IN_GLOBAL_ZONE - in global zone
+ * CAF_SCOPE_GLOBAL - -G specified
+ * CAF_SCOPE_NONGLOBAL - -Z specified
+ * Returns: boolean_t
+ * B_TRUE - the package can be installed
+ * B_FALSE - the package can not be installed
+ */
+
+static boolean_t
+check_applicability(char *a_packageDir, char *a_pkgInst, char *a_rootPath,
+ CAF_T a_flags)
+{
+ FILE *pkginfoFP;
+ FILE *pkgmapFP;
+ boolean_t all_zones; /* pkg is "all zones" only */
+ boolean_t in_gz_only; /* pkg installed in global zone only */
+ boolean_t is_hollow; /* pkg is "hollow" */
+ boolean_t pkg_installed; /* pkg is installed */
+ boolean_t this_zone; /* pkg is "this zone" only */
+ boolean_t reqfile_found = B_FALSE;
+ char instPkg[PKGSIZ+1]; /* installed pkg instance nam */
+ char instPkgPath[PATH_MAX]; /* installed pkg toplevel dir */
+ char pkginfoPath[PATH_MAX]; /* pkg 2 install pkginfo file */
+ char pkgmapPath[PATH_MAX]; /* pkg 2 install pkgmap file */
+ char pkgpath[PATH_MAX]; /* pkg 2 install toplevel dir */
+ int len;
+ char line[LINE_MAX];
+
+ /* entry assertions */
+
+ assert(a_packageDir != (char *)NULL);
+ assert(*a_packageDir != '\0');
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* normalize root path */
+
+ if (a_rootPath == (char *)NULL) {
+ a_rootPath = "";
+ }
+
+ /* entry debugging info */
+
+ echoDebug(DBG_CHECKAPP_ENTRY);
+ echoDebug(DBG_CHECKAPP_ARGS, a_pkgInst, a_packageDir, a_rootPath);
+
+ /*
+ * calculate paths to various objects
+ */
+
+ /* path to package to be installed top level (main) directory */
+
+ len = snprintf(pkgpath, sizeof (pkgpath), "%s/%s", a_packageDir,
+ a_pkgInst);
+ if (len > sizeof (pkgpath)) {
+ progerr(ERR_CREATE_PATH_2, a_packageDir, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* error if package top level directory does not exist */
+
+ if (isdir(pkgpath) != 0) {
+ progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* path to pkginfo file within the package to be installed */
+
+ len = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/pkginfo",
+ pkgpath);
+ if (len > sizeof (pkginfoPath)) {
+ progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
+ return (B_FALSE);
+ }
+
+ /* path to highest instance of package currently installed */
+
+ pkgLocateHighestInst(instPkgPath, sizeof (instPkgPath),
+ instPkg, sizeof (instPkg), a_rootPath, a_pkgInst);
+
+ /*
+ * gather information from this package's pkginfo file
+ */
+
+ pkginfoFP = fopen(pkginfoPath, "r");
+
+ if (pkginfoFP == (FILE *)NULL) {
+ progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
+ strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* determine "HOLLOW" setting for this package */
+
+ is_hollow = pkginfoParamTruth(pkginfoFP, PKG_HOLLOW_VARIABLE,
+ "true", B_FALSE);
+
+ /* determine "ALLZONES" setting for this package */
+
+ all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
+ "true", B_FALSE);
+
+ /* determine "THISZONE" setting for this package */
+
+ this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
+ "true", B_FALSE);
+
+ /* close pkginfo file */
+
+ (void) fclose(pkginfoFP);
+
+ /*
+ * If request file is not found, it may be in the datastream which
+ * is not yet unpacked. Check in the pkgmap file.
+ */
+ if (isfile(pkgpath, REQUEST_FILE) != 0) {
+
+ /* path to pkgmap file within the package to be installed */
+ (void) snprintf(pkgmapPath, sizeof (pkgmapPath), "%s/pkgmap",
+ pkgpath);
+
+ pkgmapFP = fopen(pkgmapPath, "r");
+
+ if (pkgmapFP == NULL) {
+ progerr(ERR_NO_PKG_MAPFILE, a_pkgInst,
+ pkgmapPath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ while (fgets(line, LINE_MAX, pkgmapFP) != NULL) {
+ if (strstr(line, " i request") != NULL) {
+ reqfile_found = B_TRUE;
+ break;
+ }
+ }
+ (void) fclose(pkgmapFP);
+ } else {
+ reqfile_found = B_TRUE;
+ }
+
+ /*
+ * If this package is not marked for installation in this zone only,
+ * check to see if this package has a request script. If this package
+ * does have a request script, then mark the package for installation
+ * in this zone only. Any package with a request script cannot be
+ * installed outside of the zone the pkgadd command is being run in,
+ * nor can such a package be installed as part of a new zone install.
+ * A new zone install must be non-interactive, which is required
+ * by all packages integrated into the Solaris WOS.
+ */
+
+ if ((!this_zone) && (reqfile_found)) {
+ if (a_flags & CAF_IN_GLOBAL_ZONE) {
+ echoDebug(DBG_CHECKAPP_THISZONE_REQUEST, a_pkgInst);
+ }
+ this_zone = B_TRUE;
+ }
+
+ /*
+ * If this package is already installed, see if the current installation
+ * of the package has a request file - if it does, then act as though
+ * the current package to be added has a request file - install the
+ * package in the current zone only.
+ */
+
+ if ((!this_zone) && (instPkgPath[0] != '\0') &&
+ (isfile(instPkgPath, REQUEST_FILE) == 0)) {
+ if (a_flags & CAF_IN_GLOBAL_ZONE) {
+ echoDebug(DBG_CHECKAPP_THISZONE_INSTREQ,
+ a_pkgInst, instPkg);
+ }
+ this_zone = B_TRUE;
+ }
+
+ /* gather information from the global zone only file */
+
+ in_gz_only = B_FALSE;
+ if (a_flags & CAF_IN_GLOBAL_ZONE) {
+ in_gz_only = pkgIsPkgInGzOnly(a_rootPath, a_pkgInst);
+ }
+
+ /* determine if this package is currently installed */
+
+ pkg_installed = pkginfoIsPkgInstalled((struct pkginfo **)NULL,
+ a_pkgInst);
+
+ /*
+ * verify package applicability based on information gathered,
+ * and validate the three SUNW_PKG_ options:
+ *
+ * -----------|--------------|-------------|-------------|-----------
+ * - - - - - -| GLOBAL ZONE -| GLOBAL ZONE | LOCAL ZONE | LOCAL ZONE
+ * - - - - - -| - - pkgadd - | pkgadd -G | pkgadd | pkgadd -G
+ * ----1------|--------------|-------------|-------------|------------
+ * ALLZONES f | add to gz | add to gz | add to ls | add to ls
+ * HOLLOW f | current lz | not to curr | only - - - -| only - - -
+ * THISZONE f | futr lz - - -| or futr lz | - - - - - - | - - - - - -
+ * ----2------|--------------|-------------|-------------|------------
+ * ALLZONES T | add to gz | operation | operation | operation
+ * HOLLOW f | current lz | not allowed | not allowed | not allowed
+ * THISZONE f | future lz | - - - - - - | - - - - - - | - - - - - -
+ * ----3------|--------------|-------------|-------------|------------
+ * ALLZONES T | add to gz | operation | operation | operation
+ * HOLLOW T | pkg db only | not allowed | not allowed | not allowed
+ * THISZONE f | curr/futr lz | - - - - - - | - - - - - - | - - - - - -
+ * ----4------|--------------|-------------|-------------|------------
+ * ALLZONES T | bad option | bad option | bad option | bad option
+ * HOLLOW * | combo - - - -| combo - - - | combo - - - | combo - -
+ * THISZONE T | - - - - - - -|- - - - - - -|- - - - - - -|- - - - - -
+ * ----5------|--------------|-------------|-------------|------------
+ * ALLZONES f | bad option | bad option | bad option | bad option
+ * HOLLOW T | combo - - - -| combo - - - | combo - - - | combo - - -
+ * THISZONE * | - - - - - - -| - - - - - - | - - - - - - | - - - - - -
+ * ----6------|--------------|-------------|-------------|------------
+ * ALLZONES f | add to gz | add to gz | add to lz | add to lz
+ * HOLLOW f | not current | not current | only - - - | only - - -
+ * THISZONE T | or future lz | or futr lz | - - - - - - | - - - - - -
+ * -----------|--------------|-------------|-------------|-----------
+ */
+
+ /* pkg "all zones" && "this zone" (#4) */
+
+ if (all_zones && this_zone) {
+ progerr(ERR_ALLZONES_AND_THISZONE, a_pkgInst,
+ PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
+ return (B_FALSE);
+ }
+
+ /* pkg "!all zones" && "hollow" (#5) */
+
+ if ((!all_zones) && is_hollow) {
+ progerr(ERR_NOW_ALLZONES_AND_HOLLOW, a_pkgInst,
+ PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
+ return (B_FALSE);
+ }
+
+ /* pkg ALLZONES=true && -Z specified */
+
+ if (all_zones && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+ progerr(ERR_ALLZONES_AND_Z_USED, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg ALLZONES=true & not running in global zone (#2/#3) */
+
+ if (all_zones && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
+ progerr(ERR_ALLZONES_AND_IN_LZ, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg "in gz only" & pkg "NOT installed" */
+
+ if (in_gz_only && (!pkg_installed)) {
+ /* MAKE A WARNING */
+ echo(ERR_IN_GZ_AND_NOT_INSTALLED, a_pkgInst,
+ pkgGetGzOnlyPath());
+ }
+
+ /* pkg ALLZONES=true & pkg "in gz only" & pkg "is installed" */
+
+ if (all_zones && in_gz_only && pkg_installed) {
+ progerr(ERR_IN_GZ_AND_ALLZONES_AND_INSTALLED, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg ALLZONES=true && -G specified (#2/#3) */
+
+ if (all_zones && (a_flags & CAF_SCOPE_GLOBAL)) {
+ progerr(ERR_ALLZONES_AND_G_USED, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg "!this zone" && "in gz only" & -G not specified */
+
+ if ((!this_zone) && in_gz_only && (!(a_flags & CAF_SCOPE_GLOBAL))) {
+ progerr(ERR_IN_GZ_AND_NO_G_USED, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg "NOT in gz only" & -Z specified */
+
+ if ((!in_gz_only) && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+ progerr(ERR_NOT_IN_GZ_AND_Z_USED, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* pkg "this zone" && -Z specified */
+
+ if (this_zone && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+ progerr(ERR_THISZONE_AND_Z_USED, PKG_THISZONE_VARIABLE,
+ a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /*
+ * If this package is marked 'this zone only', then mark the package
+ * as "add to this zone only". This is referenced by the various
+ * add_package_... functions to determine if the package should be
+ * added to the current zone, or to all zones, depending on the
+ * zone in which the command is being run.
+ */
+
+ if (this_zone) {
+ pkgAddThisZonePackage(a_pkgInst);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: create_zone_adminfile
+ * Description: Given a zone temporary directory and optionally an existing
+ * administration file, generate an administration file that
+ * can be used to perform "non-interactive" operations in a
+ * non-global zone.
+ * Arguments: r_zoneAdminFile - pointer to handle that will contain a
+ * string representing the path to the temporary
+ * administration file created - this must be NULL
+ * before the first call to this function - on
+ * subsequent calls if the pointer is NOT null then
+ * the existing string will NOT be overwritten.
+ * a_zoneTempDir - pointer to string representing the path
+ * to the zone temporary directory to create the
+ * temporary administration file in
+ * a_admnfile - pointer to string representing the path to
+ * an existing "user" administration file - the
+ * administration file created will contain the
+ * settings contained in this file, modified as
+ * appropriate to supress any interaction;
+ * If this is == NULL then the administration file
+ * created will not contain any extra settings
+ * Returns: void
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * NOTE: On any error this function will call 'quit(1)'
+ */
+
+static void
+create_zone_adminfile(char **r_zoneAdminFile, char *a_zoneTempDir,
+ char *a_admnfile)
+{
+ boolean_t b;
+
+ /* entry assertions */
+
+ assert(r_zoneAdminFile != (char **)NULL);
+ assert(a_zoneTempDir != (char *)NULL);
+ assert(*a_zoneTempDir != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_CREATE_ZONE_ADMINFILE, a_zoneTempDir, PSTR(a_admnfile));
+
+ /* if temporary name already exists, do not overwrite */
+
+ if (*r_zoneAdminFile != (char *)NULL) {
+ return;
+ }
+
+ /* create temporary name */
+
+ *r_zoneAdminFile = tempnam(a_zoneTempDir, "zadmn");
+ b = z_create_zone_admin_file(*r_zoneAdminFile, a_admnfile);
+ if (b == B_FALSE) {
+ progerr(ERR_CREATE_TMPADMIN, *r_zoneAdminFile,
+ strerror(errno));
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ echoDebug(DBG_CREATED_ZONE_ADMINFILE, *r_zoneAdminFile);
+}
+
+/*
+ * Name: create_zone_tempdir
+ * Description: Given a system temporary directory, create a "zone" specific
+ * temporary directory and return the path to the directory
+ * created.
+ * Arguments: r_zoneTempDir - pointer to handle that will contain a
+ * string representing the path to the temporary
+ * directory created - this must be NULL before the
+ * first call to this function - on subsequent calls
+ * if the pointer is NOT null then the existing string
+ * will NOT be overwritten.
+ * a_zoneTempDir - pointer to string representing the path
+ * to the system temporary directory to create the
+ * temporary zone directory in
+ * Returns: void
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the string is no longer needed.
+ * NOTE: On any error this function will call 'quit(1)'
+ * NOTE: This function calls "quitSetZoneTmpdir" on success to
+ * register the directory created with quit() so that the
+ * directory will be automatically deleted on exit.
+ */
+
+static void
+create_zone_tempdir(char **r_zoneTempDir, char *a_tmpdir)
+{
+ boolean_t b;
+
+ /* entry assertions */
+
+ assert(r_zoneTempDir != (char **)NULL);
+ assert(a_tmpdir != (char *)NULL);
+ assert(*a_tmpdir != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_CREATE_ZONE_TEMPDIR, a_tmpdir);
+
+ /* if temporary directory already exists, do not overwrite */
+
+ if (*r_zoneTempDir != (char *)NULL) {
+ return;
+ }
+
+ /* create temporary directory */
+
+ b = setup_temporary_directory(r_zoneTempDir, a_tmpdir, "ztemp");
+ if (b == B_FALSE) {
+ progerr(ERR_ZONETEMPDIR, a_tmpdir, strerror(errno));
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /* register with quit() so directory is removed on exit */
+
+ quitSetZoneTmpdir(*r_zoneTempDir);
+
+ /* exit debugging info */
+
+ echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
+}
+
+/*
+ * Name: continue_installation
+ * Description: Called from within a loop that is installing packages,
+ * this function examines various global variables and decides
+ * whether or not to ask an appropriate question, and wait for
+ * and appropriate reply.
+ * Arguments: <<global variables>>
+ * Returns: B_TRUE - continue processing with next package
+ * B_FALSE - do not continue processing with next package
+ */
+
+static boolean_t
+continue_installation(void)
+{
+ char ans[MAX_INPUT];
+ int n;
+
+ /* return TRUE if not interrupted */
+
+ if (!interrupted) {
+ return (B_TRUE);
+ }
+
+ /*
+ * process interrupted - determine whether or not to continue
+ */
+
+ /* output appropriate interrupted message */
+
+ if (askflag) {
+ echo(npkgs == 1 ? MSG_1MORE_PROC : MSG_MORE_PROC, npkgs);
+ } else {
+ echo(npkgs == 1 ? MSG_1MORE_INST : MSG_MORE_INST, npkgs);
+ }
+
+ /* if running with no interaction (-n) do not ask question */
+
+ if (nointeract) {
+ /* if admin required return 'dont continue' */
+ if (needconsult) {
+ return (B_FALSE);
+ }
+ ckquit = 1;
+ return (B_TRUE);
+ }
+
+ /* interaction possible: ask question */
+
+ ckquit = 0;
+ n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_ADD);
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+ ckquit = 1;
+ if (strchr("yY", *ans) == NULL) {
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * package can be in a number of formats:
+ * - file containing package stream (pkgadd -d file [pkgs])
+ * - directory containing packages (pkgadd -d /dir [pkgs])
+ * - device containing packages (pkgadd -d diskette1 [pkgs])
+ * non-global zones can be passed open files and strings as arguments
+ * - for file containing package stream
+ * -- the stream can be passed directly to the non-global zone
+ * - for directory
+ * -- convert packages to datastream to pass to the non-global zone
+ * - for device
+ * -- ?
+ */
+
+static boolean_t
+unpack_and_check_packages(char **a_pkgList, char *a_idsName, char *a_packageDir)
+{
+ int savenpkgs = npkgs;
+ int i;
+ CAF_T flags = 0;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_UNPACKCHECK_ENTRY);
+ echoDebug(DBG_UNPACKCHECK_ARGS, PSTR(a_idsName), PSTR(a_packageDir));
+
+ /*
+ * set flags for applicability check
+ */
+
+ /* determine if running in the global zone */
+
+ if (z_running_in_global_zone() == B_TRUE) {
+ flags |= CAF_IN_GLOBAL_ZONE;
+ }
+
+ /* set -G flag */
+
+ if (globalZoneOnly == B_TRUE) {
+ flags |= CAF_SCOPE_GLOBAL;
+ }
+
+ /*
+ * for each package to install:
+ * - if packages from datastream, unpack package into package dir
+ * - check applicability of installing package on this system/zone
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ if (a_idsName != (char *)NULL) {
+ /* create stream out of package if not already one */
+ if (unpack_package_from_stream(a_idsName, pkginst,
+ a_packageDir) == B_FALSE) {
+ progerr(ERR_CANNOT_UNPACK_PKGSTRM,
+ PSTR(pkginst), PSTR(a_idsName),
+ PSTR(a_packageDir));
+
+ npkgs = savenpkgs;
+ return (B_FALSE);
+ }
+ } else {
+ echoDebug(DBG_PKG_IN_DIR, pkginst, a_packageDir);
+ }
+
+ /* check package applicability */
+ if (check_applicability(a_packageDir,
+ pkginst, get_inst_root(), flags) == B_FALSE) {
+ progerr(ERR_PKG_NOT_INSTALLABLE, pkginst);
+ npkgs = savenpkgs;
+ return (B_FALSE);
+ }
+ npkgs--;
+ }
+
+ npkgs = savenpkgs;
+ return (B_TRUE);
+}
+
+/*
+ * returns:
+ * B_TRUE - package list generated
+ * B_FALSE - failed to generate package list
+ * Will call quit(n) on fatal error.
+ */
+
+static boolean_t
+get_package_list(char ***r_pkgList, char **a_argv, char *a_categories,
+ char **a_categoryList, int a_ignoreSignatures, PKG_ERR *a_err,
+ ushort_t a_httpProxyPort, char *a_httpProxyName,
+ keystore_handle_t a_keystore, char *a_keystoreFile,
+ char *a_idsName, int *r_repeat)
+{
+ int n;
+ url_hport_t *proxytmp = NULL;
+
+ /* entry assertions */
+
+ assert(r_repeat != (int *)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_GETPKGLIST_ENTRY);
+ echoDebug(DBG_GETPKGLIST_ARGS, PSTR(a_idsName), PSTR(pkgdev.dirname),
+ *r_repeat);
+
+ /*
+ * get the list of the packages to add
+ */
+
+ n = pkgGetPackageList(r_pkgList, a_argv, optind, a_categories,
+ a_categoryList, &pkgdev);
+
+ switch (n) {
+ case -1: /* no packages found */
+ echoDebug(DBG_PKGLIST_NONFOUND, PSTR(a_idsName),
+ pkgdev.dirname);
+ return (B_FALSE);
+
+ case 0: /* packages found */
+ break;
+
+ default: /* "quit" error */
+ echoDebug(DBG_PKGLIST_ERROR, PSTR(a_idsName),
+ pkgdev.dirname, n);
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * If we are not ignoring signatures, check the package's
+ * signature if one exists. pkgask doesn't care about
+ * signatures though.
+ */
+ if (!askflag && !a_ignoreSignatures && a_idsName &&
+ (web_ck_authentication() == AUTH_QUIT)) {
+
+ PKCS7 *sig = NULL;
+ STACK_OF(X509) *cas = NULL;
+
+ /* Retrieve signature */
+ if (!get_signature(a_err, a_idsName, &pkgdev, &sig)) {
+ pkgerr(a_err);
+ web_cleanup();
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ if (sig != NULL) {
+ /* Found signature. Verify. */
+ if (a_httpProxyName != NULL) {
+ /* Proxy will be needed for OCSP */
+ proxytmp = malloc(sizeof (url_hport_t));
+ if (url_parse_hostport(a_httpProxyName,
+ proxytmp, a_httpProxyPort)
+ != URL_PARSE_SUCCESS) {
+ progerr(ERR_PROXY,
+ a_httpProxyName);
+ PKCS7_free(sig);
+ quit(99);
+ /* NOTREACHED */
+ }
+ }
+
+ /* Start with fresh error stack */
+ pkgerr_clear(a_err);
+
+ if (a_keystore == NULL) {
+ /* keystore not opened - open it */
+ if (open_keystore(a_err, a_keystoreFile,
+ get_prog_name(), pkg_passphrase_cb,
+ KEYSTORE_DFLT_FLAGS,
+ &a_keystore) != 0) {
+ pkgerr(a_err);
+ web_cleanup();
+ PKCS7_free(sig);
+ quit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ /* get trusted CA certs */
+ if (find_ca_certs(a_err, a_keystore, &cas) != 0) {
+ pkgerr(a_err);
+ PKCS7_free(sig);
+ web_cleanup();
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /* Verify signature */
+ if (!ds_validate_signature(a_err, &pkgdev,
+ &a_argv[optind], a_idsName, sig,
+ cas, proxytmp, nointeract)) {
+ pkgerr(a_err);
+ quit(99);
+ /* NOTREACHED */
+ }
+
+ /* cleanup */
+ PKCS7_free(sig);
+ web_cleanup();
+ pkgerr_free(a_err);
+ }
+ }
+
+ /* order package list if input data stream specified */
+
+ if (a_idsName) {
+ ds_order(*r_pkgList);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: install_in_one_zone
+ * Description: Install a single package in a single zone
+ * Arguments: a_inheritedPkgDirs - pointer to array of strings, each one
+ * representing the non-global zones full path of a
+ * directory that is inherited from the global zone.
+ * a_zoneName - pointer to string representing the name of the
+ * zone to install the package into.
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_scratchName - pointer to string representing the name of the
+ * scratch zone to use for installation.
+ * a_zoneState - state of the zone; must be mounted or running.
+ * Returns: void
+ * NOTE: As a side effect, "ckreturn" is called on the result returned
+ * from running 'pkginstall' in the zone; this sets several global
+ * variables which allows the caller to determine the result of
+ * the installation operation.
+ */
+
+static void
+install_in_one_zone(char **a_inheritedPkgDirs, char *a_zoneName,
+ char *a_idsName, char *a_zoneAdminFile, char *a_zoneTempDir,
+ char *a_altBinDir, zone_state_t a_zoneState)
+{
+ char zoneStreamName[PATH_MAX] = {'\0'};
+ int n;
+
+ /* entry assertions */
+
+ assert(a_zoneName != (char *)NULL);
+ assert(*a_zoneName != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_INSTINONEZONE_ENTRY);
+ echoDebug(DBG_INSTINONEZONE_ARGS, a_zoneName, PSTR(a_idsName),
+ PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir),
+ PSTR(a_altBinDir));
+
+ /* echo operation to perform to stdout */
+
+ echo(MSG_INSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+
+ /* determine path to the package stream */
+
+ if (a_idsName == (char *)NULL) {
+ /* locate temp stream created earlier */
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s/%s.dstream", a_zoneTempDir, pkginst);
+ } else {
+ /* use stream passed in on command line */
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s", a_idsName);
+ }
+
+ echoDebug(DBG_INSTALL_IN_ZONE, pkginst, a_zoneName, zoneStreamName);
+
+ n = pkgZoneInstall(a_zoneName, a_inheritedPkgDirs, a_zoneState,
+ zoneStreamName, a_altBinDir, a_zoneAdminFile);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ /* exit debugging info */
+
+ echoDebug(DBG_INSTALL_FLAG_VALUES, "after install", admnflag, doreboot,
+ failflag, interrupted, intrflag, ireboot, needconsult,
+ nullflag, warnflag);
+}
+
+/*
+ * Name: install_in_zones
+ * Description: Install a single package in the zones that are running from
+ * a list of zones
+ * Arguments: a_zlst - list of zones to install the package into
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ */
+
+static int
+install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+ char **inheritedPkgDirs;
+ char *zoneName;
+ int zoneIndex;
+ int zonesSkipped = 0;
+ zone_state_t zst;
+
+ /* entry assertions */
+
+ assert(a_zlst != (zoneList_t)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_INSTALLINZONES_ENTRY);
+ echoDebug(DBG_INSTALLINZONES_ARGS, PSTR(a_idsName),
+ PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+ /* process each zone in the list */
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+ zoneIndex++) {
+
+ /* skip the zone if it is NOT running */
+
+ zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+ if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
+ zonesSkipped++;
+ echoDebug(DBG_SKIPPING_ZONE, zoneName);
+ continue;
+ }
+
+ /* determine list of directories inherited from global zone */
+
+ inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /* install the package in this zone */
+
+ install_in_one_zone(inheritedPkgDirs,
+ z_zlist_get_scratch(a_zlst, zoneIndex), a_idsName,
+ a_zoneAdminFile, a_zoneTempDir, a_altBinDir, zst);
+ }
+
+ return (zonesSkipped);
+}
+
+/*
+ * Name: boot_and_install_in_zones
+ * Description: Install a single package in the zones that are NOT running from
+ * a list of zones - each zone is booted, the package installed,
+ * and the zone is halted
+ * Arguments: a_zlst - list of zones to install the package into
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ */
+
+static int
+boot_and_install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+ boolean_t b;
+ char **inheritedPkgDirs;
+ char *zoneName;
+ int zoneIndex;
+ int zonesSkipped = 0;
+ zone_state_t zst;
+
+ /* entry assertions */
+
+ assert(a_zlst != (zoneList_t)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_BOOTINSTALLINZONES_ENTRY);
+ echoDebug(DBG_BOOTINSTALLINZONES_ARGS, PSTR(a_idsName),
+ PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+ /* process each zone in the list */
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+ zoneIndex++) {
+
+ /* skip the zone if it IS running */
+
+ zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+ if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
+ echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+ continue;
+ }
+
+ /* skip the zone if it is NOT bootable */
+
+ if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
+ echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+ echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+ continue;
+ }
+
+ /* mount up the zone */
+
+ echo(MSG_BOOTING_ZONE, zoneName);
+ echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+ b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+ ZONE_STATE_MOUNTED);
+ if (b == B_FALSE) {
+ progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+ /* set fatal error return condition */
+ ckreturn(1);
+ zonesSkipped++;
+ continue;
+ }
+
+ /* determine list of directories inherited from global zone */
+
+ inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /* install the package in this zone */
+
+ install_in_one_zone(inheritedPkgDirs,
+ z_zlist_get_scratch(a_zlst, zoneIndex), a_idsName,
+ a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+ ZONE_STATE_MOUNTED);
+
+ /* restore original state of zone */
+
+ echo(MSG_RESTORE_ZONE_STATE, zoneName);
+ echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+ b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+ }
+
+ return (zonesSkipped);
+}
+
+/*
+ * Name: pkginstall_check_in_one_zone
+ * Description: Do a pre install check of a single package in a single zone
+ * Arguments: a_inheritedPkgDirs - pointer to array of strings, each one
+ * representing the non-global zones full path of a
+ * directory that is inherited from the global zone.
+ * a_zoneName - pointer to string representing the name of the
+ * zone to check install the package in.
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be check installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when installing the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_scratchName - pointer to string representing the name of the
+ * scratch zone to use for installation.
+ * a_zoneState - state of the zone; must be mounted or running.
+ * Returns: void
+ * NOTE: As a side effect, "ckreturn" is called on the result returned
+ * from running 'pkginstall' in the zone; this sets several global
+ * variables which allows the caller to determine the result of
+ * the pre installation check operation.
+ */
+
+static void
+pkginstall_check_in_one_zone(char **a_inheritedPkgDirs, char *a_zoneName,
+ char *a_idsName, char *a_zoneAdminFile, char *a_zoneTempDir,
+ char *a_altBinDir, char *a_scratchName, zone_state_t a_zoneState)
+{
+ char preinstallcheckPath[PATH_MAX+1];
+ char zoneStreamName[PATH_MAX] = {'\0'};
+ int n;
+
+ echo(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+ echoDebug(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+
+ (void) snprintf(preinstallcheckPath, sizeof (preinstallcheckPath),
+ "%s/%s.%s.preinstallcheck.txt", a_zoneTempDir, pkginst,
+ a_zoneName);
+
+ if (a_idsName == (char *)NULL) {
+ /* locate temporary stream created earlier */
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s/%s.dstream", a_zoneTempDir, pkginst);
+ } else {
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s", a_idsName);
+ }
+
+ echoDebug(DBG_CHECKINSTALL_IN_ZONE, pkginst, a_zoneName,
+ zoneStreamName);
+
+ n = pkgZoneCheckInstall(a_scratchName, a_inheritedPkgDirs,
+ a_zoneState, zoneStreamName, a_altBinDir, a_zoneAdminFile,
+ preinstallcheckPath);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ echoDebug(DBG_INSTALL_FLAG_VALUES, "after preinstall check",
+ admnflag, doreboot, failflag, interrupted, intrflag,
+ ireboot, needconsult, nullflag, warnflag);
+}
+
+/*
+ * Name: pkginstall_check_in_zones
+ * Description: Check installation of a single package in the zones that
+ * are running from a list of zones
+ * Arguments: a_zlst - list of zones to check install the package
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be check installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when checking the installing
+ * of the package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ */
+
+static int
+pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+ char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+ char **inheritedPkgDirs;
+ char *zoneName;
+ int zoneIndex;
+ int zonesSkipped = 0;
+ zone_state_t zst;
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+ zoneIndex++) {
+
+ zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+ if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
+ zonesSkipped++;
+ echoDebug(DBG_SKIPPING_ZONE, zoneName);
+ continue;
+ }
+
+ inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ pkginstall_check_in_one_zone(inheritedPkgDirs, zoneName,
+ a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+ z_zlist_get_scratch(a_zlst, zoneIndex), zst);
+ }
+
+ return (zonesSkipped);
+}
+
+/*
+ * Name: boot_and_pkginstall_check_in_zones
+ * Description: Check installation of a single package in the zones that
+ * are NOT running from a list of zones - each zone is booted,
+ * the package installation is checked, and the zone is halted.
+ * Arguments: a_zlst - list of zones to install the package into
+ * a_idsName - pointer to string representing the data stream
+ * device (input data stream) containing the package to
+ * be check installed.
+ * If this is == NULL the package is assumed to be
+ * spooled in the zone temporary directory.
+ * a_altBinDir - pointer to string representing an alternative
+ * binary location directory to pass to pkginstall.
+ * If this is == NULL no alternative binary location is
+ * passed to pkginstall.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkginstall when check installing the
+ * package.
+ * If this is == NULL no admin file is given to pkginstall.
+ * a_zoneTempDir - pointer to string representing the temporary
+ * directory in which spooled packages can be found if
+ * a_idsName is == NULL.
+ */
+
+static int
+boot_and_pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName,
+ char *a_altBinDir, char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+ int zoneIndex;
+ int zonesSkipped = 0;
+ char *zoneName;
+ boolean_t b;
+ char **inheritedPkgDirs;
+ zone_state_t zst;
+
+ /* entry assertions */
+
+ assert(a_zlst != (zoneList_t)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_BOOTCHECKINSTALLINZONES_ENTRY);
+ echoDebug(DBG_BOOTCHECKINSTALLINZONES_ARGS, PSTR(a_idsName),
+ PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+ /* process each zone in the list */
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+ zoneIndex++) {
+
+ /* skip the zone if it IS running */
+
+ zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+ if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
+ echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+ continue;
+ }
+
+ /* skip the zone if it is NOT bootable */
+
+ if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
+ echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+ echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+ continue;
+ }
+
+ /* mount up the zone */
+
+ echo(MSG_BOOTING_ZONE, zoneName);
+ echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+ b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+ ZONE_STATE_MOUNTED);
+ if (b == B_FALSE) {
+ progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+ /* set fatal error return condition */
+ ckreturn(1);
+ zonesSkipped++;
+ continue;
+ }
+
+ /* determine list of directories inherited from global zone */
+
+ inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /* pre-installation check of the package in this zone */
+
+ pkginstall_check_in_one_zone(inheritedPkgDirs, zoneName,
+ a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+ z_zlist_get_scratch(a_zlst, zoneIndex),
+ ZONE_STATE_MOUNTED);
+
+ /* restore original state of zone */
+
+ echo(MSG_RESTORE_ZONE_STATE, zoneName);
+ echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+ b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+ }
+
+ return (zonesSkipped);
+}
+
+/*
+ * Function: add_packages_in_global_with_zones
+ * Description: call this function to add a list of packages in the global zone
+ * when one or more non-global zones exist
+ * returns:
+ * B_TRUE to process next data stream
+ * B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_global_with_zones(char **a_pkgList, char *a_uri,
+ char *a_idsName, int a_repeat, char *a_altBinDir,
+ char *a_device, zoneList_t a_zlst)
+{
+static char *zoneTempDir = (char *)NULL;
+static char *zoneAdminFile = (char *)NULL;
+
+ boolean_t b;
+ char *packageDir;
+ char instdir[PATH_MAX];
+ char respfile_path[PATH_MAX];
+ char zoneStreamName[PATH_MAX] = {'\0'};
+ int i;
+ int n;
+ int savenpkgs = npkgs;
+ int zonesSkipped;
+ boolean_t globalPresent;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+ assert(a_zlst != (zoneList_t)NULL);
+
+ echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ENTRY);
+ echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ARGS, npkgs, PSTR(a_uri),
+ PSTR(a_idsName), a_repeat, PSTR(a_device));
+
+ /* create temporary directory for use by zone operations */
+
+ create_zone_tempdir(&zoneTempDir, tmpdir);
+
+ /* create hands off settings admin file for use in a non-global zone */
+
+ create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
+
+ /* determine directory where packages can be found */
+
+ if (a_idsName == (char *)NULL) {
+ /* no stream - directory containing packages provided */
+ packageDir = pkgdev.dirname;
+ } else {
+ packageDir = zoneTempDir;
+ }
+
+ /* unpack and check all packages */
+
+ b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
+ if (b != B_TRUE) {
+ quit(1);
+ }
+
+ /*
+ * if the packages are contained in a directory, convert the
+ * packages into individual streams because pkgZoneInstall is only able
+ * to pass a stream to the non-global zone's pkginstall command.
+ * After this code is executed:
+ * if the original input was a datastream:
+ * -> that datastream has been unpacked into "instdir"
+ * if the original input was a directory with packages in it:
+ * -> those packages have been placed into a single datastream
+ */
+
+ if (a_idsName == (char *)NULL) {
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ char *pkgs[2];
+
+ /* package is not a stream - create one */
+
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s/%s.dstream", zoneTempDir, pkginst);
+
+ echoDebug(DBG_CONVERTING_PKG, packageDir, pkginst,
+ zoneStreamName);
+
+ /* set up list of packages to be this package only */
+
+ pkgs[0] = pkginst;
+ pkgs[1] = (char *)NULL;
+
+ n = pkgtrans(packageDir, zoneStreamName, pkgs,
+ PT_SILENT|PT_ODTSTREAM, NULL, NULL);
+ if (n != 0) {
+ progerr(ERR_CANNOT_CONVERT_PKGSTRM,
+ pkginst, packageDir, zoneStreamName);
+ quit(1);
+ }
+ npkgs--;
+ }
+ npkgs = savenpkgs;
+ }
+
+ /*
+ * Phase I - run collect dependency information for all packages for all
+ * zones - this involves running pkginstall with the "preinstallcheck"
+ * option which causes all dependency checks to be performed without
+ * actually doing the installation of the packages. This information is
+ * gathered in the zone temporary directory and is used later to present
+ * the dependency check results to the system administrator depending
+ * on the administration settings.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+
+ /* reset interrupted flag before calling pkginstall */
+
+ interrupted = 0; /* last action was NOT quit */
+
+ /*
+ * if this package is marked "install in this zone only", then
+ * do not check dependencies in any other zone
+ */
+
+ if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+ echoDebug(DBG_VERIFY_SKIP_THISZONE, pkginst);
+ npkgs--;
+ continue;
+ }
+
+ /*
+ * if operation failed in global zone do not propagate
+ * to any non-global zones
+ */
+
+ if (interrupted != 0) {
+ echo(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
+ echoDebug(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
+ break;
+ }
+
+ echoDebug(DBG_INSTALL_FLAG_VALUES, "after pkginstall",
+ admnflag, doreboot, failflag, interrupted, intrflag,
+ ireboot, needconsult, nullflag, warnflag);
+
+ /*
+ * call pkginstall to verify this package for all non-global
+ * zones that are currently booted
+ */
+
+ zonesSkipped = pkginstall_check_in_zones(a_zlst, a_idsName,
+ a_altBinDir, admnfile, zoneTempDir);
+
+ /*
+ * if any zones were skipped (becuase they are not currently
+ * booted), boot each zone one at a time and call pkginstall
+ * to verify this package for each such non-global zone
+ */
+
+ if (zonesSkipped > 0) {
+ echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+ zonesSkipped =
+ boot_and_pkginstall_check_in_zones(a_zlst,
+ a_idsName, a_altBinDir, admnfile,
+ zoneTempDir);
+
+ if (zonesSkipped > 0) {
+ progerr(ERR_INSTALL_ZONES_SKIPPED,
+ zonesSkipped);
+ }
+ }
+
+ npkgs--;
+ }
+
+ /*
+ * At this point, all of the dependency information has been gathered
+ * and is ready to be analyzed. This function processes all of that
+ * dependency information and presents the results to the system
+ * administrator, depending on the current administration settings.
+ */
+
+ i = preinstall_verify(a_pkgList, a_zlst, zoneTempDir);
+ if (i != 0) {
+ /* dependency checks failed - exit */
+ quit(i);
+ }
+
+ npkgs = savenpkgs;
+
+ /*
+ * reset all error return condition variables that may have been
+ * set during package installation dependency checking so that they
+ * do not reflect on the success/failure of the actual package
+ * installation operations
+ */
+
+ resetreturn();
+
+ /*
+ * At this point, all of the dependency checking is completed, and
+ * the installation of the packages can proceed. Install each package
+ * one at a time, starting with the global zone, and the for each
+ * non-global zone that is booted, and then for each non-global zone
+ * that is not currently booted.
+ */
+
+ globalPresent = z_on_zone_spec(GLOBAL_ZONENAME);
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ /*
+ * if immediate reboot required from last package and this is
+ * not 'pkgask' then suspend installation of remaining packages
+ */
+
+ if ((ireboot != 0) && (askflag == 0)) {
+ ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+ continue;
+ }
+
+ /*
+ * handle interrupt if the previous pkginstall was interrupted
+ */
+
+ if (continue_installation() == B_FALSE) {
+ return (B_FALSE);
+ }
+
+ /*
+ * if pkgask, handle response file creation:
+ * - if the response file is a directory, then create a path to
+ * -- a package instance within the response file directory.
+ * - If the response file is NOT a directory, if more than one
+ * -- package is to be installed.
+ */
+
+ if ((askflag != 0) && (respdir != (char *)NULL)) {
+ (void) snprintf(respfile_path, sizeof (respfile_path),
+ "%s/%s", respdir, pkginst);
+ respfile = respfile_path;
+ }
+
+ echo(MSG_PROC_INST, pkginst,
+ (a_uri && a_idsName) ? a_uri : a_device);
+
+ /*
+ * If we're installing another package in the same
+ * session, the second through nth pkginstall, must
+ * continue from where the prior one left off. For this
+ * reason, the continuation feature (implied by the
+ * nature of the command) is used for the remaining
+ * packages.
+ */
+
+ if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+ pkgcontsrc = pkgdrtarg;
+ }
+
+ if (globalPresent) {
+ /*
+ * call pkginstall for this package for the global zone
+ */
+
+ echo(MSG_INSTALLING_PKG_IN_GZ, pkginst);
+
+ /* reset interrupted flag before calling pkginstall */
+
+ interrupted = 0; /* last action was NOT quit */
+
+ n = pkgInstall(get_inst_root(), NULL, packageDir,
+ a_altBinDir, NULL);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ /*
+ * if operation failed in global zone do not propagate
+ * to any non-global zones
+ */
+
+ if (interrupted != 0) {
+ echo(MSG_INSTALL_INTERRUPT_B4_ZONES, pkginst);
+ echoDebug(MSG_INSTALL_INTERRUPT_B4_ZONES,
+ pkginst);
+ break;
+ }
+ }
+
+ /*
+ * if this package is marked "install in this zone only",
+ * then only need to install the package in the global zone;
+ * skip installation in any non-global zones.
+ */
+
+ if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+ echoDebug(DBG_INSTALL_SKIP_THISZONE, pkginst);
+ npkgs--;
+ continue;
+ }
+
+ echoDebug(DBG_INSTALL_FLAG_VALUES, "install in running zones",
+ admnflag, doreboot, failflag, interrupted, intrflag,
+ ireboot, needconsult, nullflag, warnflag);
+
+ /* install package in currently booted zones */
+
+ zonesSkipped = install_in_zones(a_zlst, a_idsName, a_altBinDir,
+ zoneAdminFile, zoneTempDir);
+
+ /* install package in zones that are not currently booted */
+
+ if (zonesSkipped > 0) {
+ echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+ zonesSkipped = boot_and_install_in_zones(a_zlst,
+ a_idsName, a_altBinDir, zoneAdminFile,
+ zoneTempDir);
+
+ if (zonesSkipped > 0) {
+ progerr(ERR_INSTALL_ZONES_SKIPPED,
+ zonesSkipped);
+ }
+ }
+
+ /*
+ * package completely installed - remove any temporary stream
+ * of the package that might have been created
+ */
+
+ if (a_idsName == (char *)NULL) {
+ /* locate temporary stream created earlier */
+ (void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+ "%s/%s.dstream", zoneTempDir, pkginst);
+ /* remove stream - no longer needed */
+ echoDebug(DBG_REMOVING_DSTREAM_PKGDIR, zoneStreamName,
+ pkginst);
+ (void) remove(zoneStreamName);
+ } else {
+ /* remove package - no longer needed */
+ if (snprintf(instdir, sizeof (instdir), "%s/%s",
+ zoneTempDir, pkginst) >= PATH_MAX) {
+ progerr(ERR_CANNOT_CREATE_PKGPATH, tmpdir);
+ quit(1);
+ }
+ echoDebug(DBG_REMOVING_PKG_TMPDIR, instdir, pkginst);
+ (void) remove(instdir);
+ }
+
+ /* decrement number of packages left to install */
+
+ npkgs--;
+
+ /*
+ * if no packages left to install, unmount package source
+ * device if appropriate
+ */
+
+ if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+ (void) chdir("/");
+ if (!a_idsName) {
+ echoDebug(DBG_UNMOUNTING_DEV,
+ PSTR(pkgdev.mount));
+ (void) pkgumount(&pkgdev);
+ }
+ }
+ }
+
+ /*
+ * all packages in the package list have been installed.
+ * Continue with installation if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to install
+ * -- the package source is a path to a file
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0) &&
+ (pkgdev.pathname == (char *)NULL)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Function: add_packages_in_nonglobal_zone
+ * Description: call this function to add a list of packages in a non-global
+ * zone
+ * returns:
+ * B_TRUE to process next data stream
+ * B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_nonglobal_zone(char **a_pkgList, char *a_uri,
+ char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
+{
+static char *zoneTempDir = (char *)NULL;
+
+ char *packageDir;
+ char respfile_path[PATH_MAX];
+ int i;
+ int n;
+ boolean_t b;
+ int savenpkgs = npkgs;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_ADDPACKAGES_LZ_ENTRY);
+ echoDebug(DBG_ADDPACKAGES_LZ_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
+ a_repeat, PSTR(a_device));
+
+ /* create temporary directory for use by zone operations */
+
+ create_zone_tempdir(&zoneTempDir, tmpdir);
+
+ /*
+ * package can be in a number of formats:
+ * - file containing package stream (pkgadd -d file [pkgs])
+ * - directory containing packages (pkgadd -d /dir [pkgs])
+ * - device containing packages (pkgadd -d diskette1 [pkgs])
+ * non-global zones can be passed open file drescriptors and
+ * strings as arguments
+ * - for file containing package stream
+ * -- the stream can be passed directly to the non-global zone
+ * - for directory
+ * -- convert packages to datastream to pass to the non-global zone
+ * - for device
+ */
+
+ /* determine directory where packages can be found */
+
+ if (a_idsName == (char *)NULL) {
+ /* no stream - directory containing packages provided */
+ packageDir = pkgdev.dirname;
+ } else {
+ packageDir = zoneTempDir;
+ }
+
+ b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
+ if (b != B_TRUE) {
+ quit(1);
+ }
+
+ /*
+ * this is the main loop where all of the packages (as listed in the
+ * package list) are added one at a time.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ npkgs--;
+ }
+
+ npkgs = savenpkgs;
+
+ /*
+ * this is the main loop where all of the packages (as listed in the
+ * package list) are added one at a time.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ /*
+ * if immediate reboot required from last package and this is
+ * not 'pkgask' then suspend installation of remaining packages
+ */
+
+ if ((ireboot != 0) && (askflag == 0)) {
+ ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+ continue;
+ }
+
+ /*
+ * handle interrupt if the previous pkginstall was interrupted
+ */
+
+ if (continue_installation() == B_FALSE) {
+ return (B_FALSE);
+ }
+
+ /*
+ * if pkgask, handle response file creation:
+ * - if the response file is a directory, then create a path to
+ * -- a package instance within the response file directory.
+ * - If the response file is NOT a directory, if more than one
+ * -- package is to be installed.
+ */
+
+ if ((askflag != 0) && (respdir != (char *)NULL)) {
+ (void) snprintf(respfile_path, sizeof (respfile_path),
+ "%s/%s", respdir, pkginst);
+ respfile = respfile_path;
+ }
+
+ echo(MSG_PROC_INST, pkginst,
+ (a_uri && a_idsName) ? a_uri : a_device);
+
+ /*
+ * If we're installing another package in the same
+ * session, the second through nth pkginstall, must
+ * continue from where the prior one left off. For this
+ * reason, the continuation feature (implied by the
+ * nature of the command) is used for the remaining
+ * packages.
+ */
+
+ if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+ pkgcontsrc = pkgdrtarg;
+ }
+
+ /* reset interrupted flag before calling pkginstall */
+
+ interrupted = 0; /* last action was NOT quit */
+
+ /* call pkginstall for this package */
+
+ n = pkgInstall(get_inst_root(), NULL,
+ packageDir, a_altBinDir,
+ (char **)NULL);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ /* decrement number of packages left to install */
+
+ npkgs--;
+
+ /*
+ * if no packages left to install, unmount package source
+ * device if appropriate
+ */
+
+ if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+ (void) chdir("/");
+ if (!a_idsName) {
+ (void) pkgumount(&pkgdev);
+ }
+ }
+ }
+
+ /*
+ * all packages in the package list have been installed.
+ * Continue with installation if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to install
+ * -- the package source is a path to a file
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0) &&
+ (pkgdev.pathname == (char *)NULL)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Function: add_packages_in_global_no_zones
+ * Description: call this function to add a list of packages in the global zone
+ * when no non-global zones exist
+ * returns:
+ * B_TRUE to process next data stream
+ * B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_global_no_zones(char **a_pkgList, char *a_uri,
+ char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
+{
+ int n;
+ int i;
+ char respfile_path[PATH_MAX];
+ CAF_T flags = 0;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+
+ echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ENTRY);
+ echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ARGS, npkgs, PSTR(a_uri),
+ PSTR(a_idsName), a_repeat, PSTR(a_device));
+
+ /*
+ * set flags for applicability check
+ */
+
+ /* in the global zone */
+
+ flags |= CAF_IN_GLOBAL_ZONE;
+
+ /* set -G flag */
+
+ if (globalZoneOnly == B_TRUE) {
+ flags |= CAF_SCOPE_GLOBAL;
+ }
+
+ /*
+ * this is the main loop where all of the packages (as listed in the
+ * package list) are added one at a time.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ /*
+ * if immediate reboot required from last package and this is
+ * not 'pkgask' then suspend installation of remaining packages
+ */
+
+ if ((ireboot != 0) && (askflag == 0)) {
+ ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+ continue;
+ }
+
+ /*
+ * handle interrupt if the previous pkginstall was interrupted
+ */
+
+ if (continue_installation() == B_FALSE) {
+ return (B_FALSE);
+ }
+
+ /*
+ * check package applicability to install in this context
+ */
+
+ if (check_applicability(pkgdev.dirname,
+ pkginst, get_inst_root(), flags) == B_FALSE) {
+ progerr(ERR_PKG_NOT_APPLICABLE, pkginst);
+ quit(1);
+ }
+
+ /*
+ * if pkgask, handle response file creation:
+ * - if the response file is a directory, then create a path to
+ * -- a package instance within the response file directory.
+ * - If the response file is NOT a directory, if more than one
+ * -- package is to be installed.
+ */
+
+ if ((askflag != 0) && (respdir != (char *)NULL)) {
+ (void) snprintf(respfile_path, sizeof (respfile_path),
+ "%s/%s", respdir, pkginst);
+ respfile = respfile_path;
+ }
+
+ echo(MSG_PROC_INST, pkginst,
+ (a_uri && a_idsName) ? a_uri : a_device);
+
+ /*
+ * If we're installing another package in the same
+ * session, the second through nth pkginstall, must
+ * continue from where the prior one left off. For this
+ * reason, the continuation feature (implied by the
+ * nature of the command) is used for the remaining
+ * packages.
+ */
+
+ if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+ pkgcontsrc = pkgdrtarg;
+ }
+
+ /* reset interrupted flag before calling pkginstall */
+
+ interrupted = 0; /* last action was NOT quit */
+
+ /* call pkginstall for this package */
+
+ n = pkgInstall(get_inst_root(), a_idsName,
+ pkgdev.dirname, a_altBinDir,
+ z_get_inherited_file_systems());
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ /* decrement number of packages left to install */
+
+ npkgs--;
+
+ /*
+ * if no packages left to install, unmount package source
+ * device if appropriate
+ */
+
+ if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+ (void) chdir("/");
+ if (!a_idsName) {
+ (void) pkgumount(&pkgdev);
+ }
+ }
+ }
+
+ /*
+ * all packages in the package list have been installed.
+ * Continue with installation if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to install
+ * -- the package source is a path to a file
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0) &&
+ (pkgdev.pathname == (char *)NULL)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * returns:
+ * B_TRUE to process next data stream
+ * B_FALSE to exit
+ */
+
+static boolean_t
+add_packages(char **a_pkgList, char *a_uri,
+ char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device,
+ boolean_t a_noZones)
+{
+ zoneList_t zlst;
+ boolean_t b;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+
+ echoDebug(DBG_ADDPACKAGES_ENTRY);
+ echoDebug(DBG_ADDPACKAGES_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
+ a_repeat, PSTR(a_altBinDir), PSTR(a_device));
+
+ /*
+ * if running in the global zone AND one or more non-global
+ * zones exist, add packages in a 'zones aware' manner, else
+ * add packages in the standard 'non-zones aware' manner.
+ */
+
+ if ((a_noZones == B_FALSE) && (z_running_in_global_zone() == B_FALSE)) {
+ /* in non-global zone */
+
+ echoDebug(DBG_IN_LZ);
+
+ b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+ if (b != B_TRUE) {
+ progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+ /* set fatal error return condition */
+ ckreturn(1);
+ return (B_FALSE);
+ }
+
+ b = add_packages_in_nonglobal_zone(a_pkgList, a_uri, a_idsName,
+ a_repeat, a_altBinDir, a_device);
+
+ (void) z_unlock_this_zone(ZLOCKS_ALL);
+
+ return (B_FALSE);
+ }
+
+ /* running in the global zone */
+
+ b = z_non_global_zones_exist();
+ if ((a_noZones == B_FALSE) && (b == B_TRUE) &&
+ (globalZoneOnly == B_FALSE)) {
+
+ echoDebug(DBG_IN_GZ_WITH_LZ);
+
+ /* error if -V specified - what to use in non-global zone? */
+
+ if (vfstab_file) {
+ progerr(ERR_V_USED_WITH_GZS);
+ quit(1);
+ }
+
+ /* get a list of all non-global zones */
+ zlst = z_get_nonglobal_zone_list();
+ if (zlst == (zoneList_t)NULL) {
+ progerr(ERR_CANNOT_GET_ZONE_LIST);
+ quit(1);
+ }
+
+ /* need to lock all of the zones */
+
+ quitSetZonelist(zlst);
+ b = z_lock_zones(zlst, ZLOCKS_PKG_ADMIN);
+ if (b == B_FALSE) {
+ z_free_zone_list(zlst);
+ progerr(ERR_CANNOT_LOCK_ZONES);
+ /* set fatal error return condition */
+ ckreturn(1);
+ return (B_FALSE);
+ }
+
+ /* add packages to all zones */
+
+ b = add_packages_in_global_with_zones(a_pkgList, a_uri,
+ a_idsName, a_repeat, a_altBinDir, a_device, zlst);
+
+ /* unlock all zones */
+
+ (void) z_unlock_zones(zlst, ZLOCKS_ALL);
+ quitSetZonelist((zoneList_t)NULL);
+
+ /* free list of all non-global zones */
+
+ z_free_zone_list(zlst);
+
+ return (B_FALSE);
+ }
+
+ /* in global zone no non-global zones */
+
+ echoDebug(DBG_IN_GZ_NO_LZ);
+
+ b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+ if (b != B_TRUE) {
+ progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+ /* set fatal error return condition */
+ ckreturn(1);
+ return (B_FALSE);
+ }
+
+ b = add_packages_in_global_no_zones(a_pkgList, a_uri, a_idsName,
+ a_repeat, a_altBinDir, a_device);
+
+ (void) z_unlock_this_zone(ZLOCKS_ALL);
+
+ return (B_FALSE);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadd/msgdefs.h b/usr/src/cmd/svr4pkg/pkgadd/msgdefs.h
new file mode 100644
index 0000000000..d14d903f8d
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/msgdefs.h
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#ifndef _MSGDEFS_H
+#define _MSGDEFS_H
+
+
+/*
+ * Module: msgdefs
+ * Group: pkgadd
+ * Description: l10n strings for pkgadd
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ERR_NOPKGS "no packages were found in <%s>"
+
+#define ERR_DSINIT "could not process datastream from <%s>"
+
+#define ERR_STREAMDIR "unable to make temporary directory to unpack " \
+ "datastream"
+
+#define MSG_SUSPEND "Installation of <%s> has been suspended."
+
+#define MSG_VERIFYING "Verifying signer <%s>"
+
+#define MSG_PASSPROMPT "Enter keystore password:"
+
+#define MSG_1MORE_PROC "\nThere is 1 more package to be processed."
+
+#define MSG_1MORE_INST "\nThere is 1 more package to be installed."
+
+#define MSG_MORE_PROC "\nThere are %d more packages to be processed."
+
+#define MSG_MORE_INST "\nThere are %d more packages to be installed."
+
+#define ASK_CONTINUE "Do you want to continue with installation"
+
+#define ERR_ROOTREQD "You must be \"root\" for %s to execute properly."
+
+#define ERR_NODEVICE "unable to determine device to install from"
+
+#define ERR_NORESP "response file <%s> must not exist"
+
+#define ERR_ACCRESP "unable to access response file <%s>"
+
+#define ERR_PKGVOL "unable to obtain package volume"
+
+#define ERR_DSARCH "unable to find archive for <%s> in datastream"
+
+#define MSG_PROC_CONT "\nProcessing continuation packages from <%s>"
+
+#define MSG_PROC_INST "\nProcessing package instance <%s> from <%s>"
+
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+
+#define ERR_CAT_LNGTH "The category argument exceeds the SVr4 ABI\n" \
+ " defined maximum supported length of 16 characters."
+
+#define ERR_CAT_FND "Category argument <%s> cannot be found."
+
+#define ERR_CAT_INV "Category argument <%s> is invalid."
+
+#define ERR_ARG "URL <%s> is not valid"
+
+#define ERR_ADM_PROXY "Admin file proxy setting invalid"
+
+#define ERR_PROXY "Proxy specification <%s> invalid"
+
+#define ERR_ILL_HTTP_OPTS "The -i and (-k or -P) options are mutually" \
+ "exclusive."
+#define ERR_ILL_PASSWD "A password is required to retrieve the public " \
+ "certificate from the keystore."
+
+#define ERR_PATH "The path <%s> is invalid!"
+
+#define ERR_DIR_CONST "unable to construct download directory <%s>"
+
+#define ERR_ADM_KEYSTORE "unable to determine keystore location"
+
+#define PASSWD_CMDLINE "## WARNING: USING \"%s\" MAKES PASSWORD " \
+ "VISIBLE TO ALL USERS."
+
+#define ERR_NO_LIVE_MODE "live continue mode is not supported"
+
+#define ERR_RSP_FILE_NOTFULLPATH "response file <%s> must be " \
+ "full pathname"
+
+#define ERR_RSP_FILE_NOT_GIVEN "response file (to write) is required"
+
+#define ERR_BAD_DEVICE "bad device <%s> specified"
+
+#define MSG_INSERT_VOL "Insert %v into %p."
+
+#define ERR_UNKNOWN_DEV "unknown device <%s>"
+
+#define LOG_GETVOL_RET "getvol() returned <%d>"
+
+#define ERR_GPKGLIST_ERROR "internal error in gpkglist()"
+
+#define ERR_TOO_MANY_PKGS "too many packages referenced!"
+
+/* maximum number of args to exec() calls */
+
+#define MAXARGS 100
+
+#define MAX_CAT_ARGS 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGDEFS_H */
diff --git a/usr/src/cmd/svr4pkg/pkgadd/presvr4.c b/usr/src/cmd/svr4pkg/pkgadd/presvr4.c
new file mode 100644
index 0000000000..45b530eaee
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/presvr4.c
@@ -0,0 +1,172 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * system includes
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <pkginfo.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+#include <pkglib.h>
+#include <messages.h>
+
+/*
+ * local pkg command library includes
+ */
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+/*
+ * pkgadd local includes
+ */
+#include "quit.h"
+
+
+extern struct admin adm;
+extern struct pkgdev pkgdev;
+extern char *respfile;
+extern char *tmpdir;
+extern int warnflag;
+
+static void intf_reloc(void);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+presvr4(char **ppkg, int a_nointeract)
+{
+ int retcode;
+ char *tmpcmd, path[PATH_MAX];
+ void (*tmpfunc)();
+
+ echo(MSG_INSTALLING_PSVR4);
+ if (a_nointeract) {
+ progerr(ERR_NOINT);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ if (respfile) {
+ progerr(ERR_RESPFILE);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * if we were looking for a particular package, verify
+ * the first media has a /usr/options file on it
+ * which matches
+ */
+ psvr4pkg(ppkg);
+
+ /*
+ * check to see if we can guess (via Rlist) what
+ * pathnames this package is likely to install;
+ * if we can, check these against the 'contents'
+ * file and warn the administrator that these
+ * pathnames might be modified in some manner
+ */
+ psvr4cnflct();
+
+ if (chdir(tmpdir)) {
+ progerr(ERR_CHDIR, tmpdir);
+ quit(99);
+ /* NOTREACHED */
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/install/INSTALL",
+ pkgdev.dirname);
+
+ tmpcmd = tempnam(tmpdir, "INSTALL");
+ if (!tmpcmd || copyf(path, tmpcmd, 0L) || chmod(tmpcmd, 0500)) {
+ progerr(ERR_NOCOPY, tmpdir);
+ quit(99);
+ /* NOTREACHED */
+ }
+
+ echo(MSG_EXE_INSTALL_SCRIPT);
+
+ retcode = pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", tmpcmd,
+ pkgdev.bdevice, pkgdev.dirname, NULL);
+
+ echo(retcode ? MSG_FAIL : gettext(MSG_SUCCEED));
+
+ (void) unlink(tmpcmd);
+ (void) chdir("/");
+ (void) pkgumount(&pkgdev);
+
+ psvr4mail(adm.mail, MSG_MAIL, retcode, *ppkg ? *ppkg : MSG_NODENAME);
+
+ /* tell quit to call intf_reloc on exit */
+
+ quitSetIntfReloc(&intf_reloc);
+
+ return (retcode);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * When quit() gains control this function will be invoked if quitSetIntfReloc()
+ * is called specifying this function - see presvr4() above for details.
+ */
+
+static void
+intf_reloc(void)
+{
+ char path[PATH_MAX];
+
+ (void) snprintf(path, sizeof (path), "%s/intf_reloc", PKGBIN);
+ (void) pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", path, NULL);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadd/quit.c b/usr/src/cmd/svr4pkg/pkgadd/quit.c
new file mode 100644
index 0000000000..bbf051fd79
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/quit.c
@@ -0,0 +1,409 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <pkglib.h>
+#include <pkgweb.h>
+#include <messages.h>
+
+#include <libadm.h>
+#include <libinst.h>
+
+#include "quit.h"
+
+/*
+ * imported global variables
+ */
+
+/* imported from main.c */
+
+extern struct pkgdev pkgdev; /* holds info about the installation device */
+
+extern int npkgs; /* the number of packages yet to be installed */
+extern int admnflag; /* != 0 if any pkgop admin setting failed (4) */
+extern int doreboot; /* != 0 if reboot required after installation */
+extern int failflag; /* != 0 if fatal error has occurred (1) */
+extern int intrflag; /* != 0 if user selected quit (3) */
+extern int ireboot; /* != 0 if immediate reboot required */
+extern int nullflag; /* != 0 if admin interaction required (5) */
+extern int warnflag; /* != 0 if non-fatal error has occurred (2) */
+
+/*
+ * forward declarations
+ */
+
+static char *dwnldTempDir = (char *)NULL;
+static char *idsName = (char *)NULL;
+static char *zoneTempDir = (char *)NULL;
+static ckreturnFunc_t *ckreturnFunc = (ckreturnFunc_t *)NULL;
+static int trapEntered = 0;
+static intfRelocFunc_t *intfRelocFunc = (intfRelocFunc_t *)NULL;
+static void trap(int signo);
+static zoneList_t zoneList = (zoneList_t)NULL;
+
+/*
+ * exported functions
+ */
+
+void quit(int retcode);
+void quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+void quitSetDwnldTmpdir(char *a_dwnldTempDir);
+void quitSetIdsName(char *a_idsName);
+void quitSetZoneName(char *a_zoneName);
+void quitSetZoneTmpdir(char *z_zoneTempDir);
+void quitSetZonelist(zoneList_t a_zlst);
+sighdlrFunc_t *quitGetTrapHandler(void);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: quitGetTrapHandler
+ * Description: return address of this modules "signal trap" handler
+ * Arguments: void
+ * Returns: sighdlrFunc_t
+ * The address of the trap handler that can be passed to
+ * the signal() type system calls
+ */
+
+sighdlrFunc_t *
+quitGetTrapHandler()
+{
+ return (&trap);
+}
+
+/*
+ * Name: quitSetIdsName
+ * Description: set the input data stream name to use when quit() is called
+ * Arguments: a_idsName - pointer to string representing the input data
+ * stream object currently open
+ * == NULL - there is no input datastream object to use
+ * Returns: void
+ * NOTE: When quit() is called, if an input datastream object is set,
+ * quit will close the datastream and cleanup certain objects
+ * associated with the datastream
+ */
+
+void
+quitSetIdsName(char *a_idsName)
+{
+ idsName = a_idsName;
+}
+
+/*
+ * Name: quitSetIntfReloc
+ * Description: set the "intf_reloc" interface to run when quit() is called
+ * Arguments: a_intfReloc - pointer to function to call when quit() is called
+ * Returns: void
+ * NOTE: When quit() is called, if an "intf_reloc" function is set, quit
+ * will call that function to perform whatever operations it needs
+ * to perform - typically this is needed to run "intf_reloc" when
+ * pre-SVR4 packages have been installed
+ */
+
+void
+quitSetIntfReloc(intfRelocFunc_t *a_intfReloc)
+{
+ intfRelocFunc = a_intfReloc;
+}
+
+/*
+ * Name: quitSetCkreturnFunc
+ * Description: set the ckreturn() interface to call when quit() is called
+ * Arguments: a_ckreturnFunc - pointer to function to call when quit() is
+ * called
+ * Returns: void
+ * NOTE: When quit() is called if a "ckreturnfunc" is set, then the first
+ * action quit() takes is to call the "ckreturnfunc" specified with
+ * the value passed to quit as the first argument. Quit will then
+ * set the final return code to be used when exit() is called based
+ * on the contents of these global variables:
+ * - admnflag - != 0 if any pkgop admin setting failed (4)
+ * - doreboot - != 0 if reboot required after installation
+ * - failflag - != 0 if fatal error has occurred (1)
+ * - intrflag - != 0 if user selected quit (3)
+ * - ireboot - != 0 if immediate reboot required
+ * - nullflag - != 0 if admin interaction required (5)
+ * - warnflag - != 0 if non-fatal error has occurred (2)
+ */
+
+void
+quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc)
+{
+ ckreturnFunc = a_ckreturnFunc;
+}
+
+/*
+ * Name: quitSetZonelist
+ * Description: set the list of zones that are "locked" so that the zones can
+ * be unlocked if quit() is called to exit
+ * Arguments: a_zlst - list of zones that are "locked"
+ * Returns: void
+ * NOTE: When quit() is called, if this list is set, then z_unlock_zones
+ * is called to unlock all of the zones in the list. If this list
+ * is NOT set, then z_unlock_this_zone is called to unlock this
+ * zone.
+ */
+
+void
+quitSetZonelist(zoneList_t a_zlst)
+{
+ zoneList = a_zlst;
+}
+
+/*
+ * Name: quitSetZoneName
+ * Description: set the zone name the program is running in
+ * Arguments: a_zoneName - pointer to string representing the name of the zone
+ * that the program is running in
+ * Returns: void
+ */
+
+/* ARGSUSED */
+void
+quitSetZoneName(char *a_zoneName)
+{
+}
+
+/*
+ * Name: quitSetZoneTmpdir
+ * Description: set the path to the "zone temporary directory" in use
+ * Arguments: a_zoneTempDir - pointer to string representing the full path to
+ * the temporary directory used to hold files used during
+ * zone operations
+ * Returns: void
+ * NOTE: If a zone temporary directory is set when quit() is called, the
+ * directory is recursively removed before quit() calls exit
+ */
+
+void
+quitSetZoneTmpdir(char *a_zoneTempDir)
+{
+ zoneTempDir = a_zoneTempDir;
+}
+
+/*
+ * Name: quitSetDwnldTmpdir
+ * Description: set the path to the "download temporary directory" in use
+ * Arguments: a_dwnldTempDir - pointer to string representing the full path to
+ * the temporary directory used to hold files used during
+ * download operations
+ * Returns: void
+ * NOTE: If a download temporary directory is set when quit() is called,
+ * the directory is recursively removed before quit() calls exit
+ */
+
+void
+quitSetDwnldTmpdir(char *a_dwnldTempDir)
+{
+ dwnldTempDir = a_dwnldTempDir;
+}
+
+/*
+ * Name: quit
+ * Description: cleanup and exit
+ * Arguments: a_retcode - the code to use to determine final exit status;
+ * if this is NOT "99" and if a "ckreturnFunc" is
+ * set, then that function is called with a_retcode
+ * to set the final exit status.
+ * Valid values are:
+ * 0 - success
+ * 1 - package operation failed (fatal error)
+ * 2 - non-fatal error (warning)
+ * 3 - user selected quit (operation interrupted)
+ * 4 - admin settings prevented operation
+ * 5 - interaction required and -n (non-interactive) specified
+ * "10" is added to indicate "immediate reboot required"
+ * "20" is be added to indicate "reboot after install required"
+ * 99 - do not interpret the code - just exit "99"
+ * Returns: <<this function does not return - calls exit()>>
+ */
+
+void
+quit(int a_retcode)
+{
+ /* disable interrupts */
+
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (!restore_local_fs()) {
+ progerr(ERR_CANNOT_RESTORE_LOCAL_FS);
+ }
+
+ /* process return code if not quit(99) */
+
+ if (a_retcode != 99) {
+ if (ckreturnFunc != (ckreturnFunc_t *)NULL) {
+ (ckreturnFunc)(a_retcode);
+ }
+ if (failflag) {
+ a_retcode = 1;
+ } else if (warnflag) {
+ a_retcode = 2;
+ } else if (intrflag) {
+ a_retcode = 3;
+ } else if (admnflag) {
+ a_retcode = 4;
+ } else if (nullflag) {
+ a_retcode = 5;
+ } else {
+ a_retcode = 0;
+ }
+ if (ireboot) {
+ a_retcode = (a_retcode % 10) + 20;
+ }
+ if (doreboot) {
+ a_retcode = (a_retcode % 10) + 10;
+ }
+ }
+
+ if (doreboot || ireboot) {
+ ptext(stderr, MSG_REBOOT);
+ }
+
+ (void) chdir("/");
+
+ /* if set remove download temporary directory */
+
+ if (dwnldTempDir != (char *)NULL) {
+ echoDebug(DBG_REMOVING_DWNLD_TMPDIR, dwnldTempDir);
+ (void) rrmdir(dwnldTempDir);
+ dwnldTempDir = (char *)NULL;
+ }
+
+ /* if set remove zone temporary directory */
+
+ if (zoneTempDir != (char *)NULL) {
+ echoDebug(DBG_REMOVING_ZONE_TMPDIR, zoneTempDir);
+ (void) rrmdir(zoneTempDir);
+ zoneTempDir = (char *)NULL;
+ }
+
+ /* close and cleanup if input datastream is set */
+
+ if (idsName != (char *)NULL) { /* datastream */
+ if (pkgdev.dirname != NULL) {
+ echoDebug(DBG_REMOVING_DSTREAM_TMPDIR, pkgdev.dirname);
+ (void) rrmdir(pkgdev.dirname); /* from tempnam */
+ }
+ /*
+ * cleanup after a web-based install.
+ * web-based install failures
+ * are indicated by exit codes 10-98
+ * exit code 99 is fatal error exit.
+ */
+ if (pkgdev.pathname != NULL && is_web_install() &&
+ (a_retcode == 0 ||
+ (a_retcode >= 10 && a_retcode < 99))) {
+ (void) web_cleanup();
+ }
+ (void) ds_close(1);
+ } else if (pkgdev.mount) {
+ (void) pkgumount(&pkgdev);
+ }
+
+ /*
+ * issue final exit message depending on number of packages left
+ * to process
+ */
+
+ if (npkgs == 1) {
+ echo(MSG_1_PKG_NOT_PROCESSED);
+ } else if (npkgs) {
+ echo(MSG_N_PKGS_NOT_PROCESSED, npkgs);
+ }
+
+ /* call intf_reloc function if registered */
+
+ if (intfRelocFunc != (intfRelocFunc_t *)NULL) {
+ (intfRelocFunc)();
+ }
+
+ /* if a zone list exists, unlock all zones */
+
+ if (zoneList != (zoneList_t)NULL) {
+ (void) z_unlock_zones(zoneList, ZLOCKS_ALL);
+ } else {
+ (void) z_unlock_this_zone(ZLOCKS_ALL);
+ }
+
+ /* final exit debugging message */
+
+ echoDebug(DBG_EXIT_WITH_CODE, a_retcode);
+
+ exit(a_retcode);
+ /* NOTREACHED */
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: trap
+ * Description: signal handler connected via quitGetTrapHandler()
+ * Arguments: signo - [RO, *RO] - (int)
+ * Integer representing the signal that caused the trap
+ * to this function to occur
+ * Returns: << NONE >>
+ * NOTE: This function exits the program after doing mandatory cleanup.
+ * NOTE: Even though quit() should NOT return, there is a call to _exit()
+ * put after each call to quit() just in case quit() ever returned
+ * by mistake.
+ */
+
+static void
+trap(int signo)
+{
+ /* prevent reentrance */
+
+ if (trapEntered++ != 0) {
+ return;
+ }
+
+ if ((signo == SIGINT) || (signo == SIGHUP)) {
+ quit(3);
+ _exit(3);
+ }
+ quit(1);
+ _exit(1);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadd/quit.h b/usr/src/cmd/svr4pkg/pkgadd/quit.h
new file mode 100644
index 0000000000..eab7f73a57
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadd/quit.h
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Header: pkgadd: quit.c
+ *
+ * Function: external definitions for references to the quit.c module
+ *
+ */
+
+#ifndef __PKGADD_QUIT_H__
+#define __PKGADD_QUIT_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <instzones_api.h>
+#include <libinst.h>
+
+/*
+ * exported (global) functions
+ */
+
+typedef void (intfRelocFunc_t)(void);
+
+extern sighdlrFunc_t *quitGetTrapHandler(void);
+extern void quit(int retcode);
+extern void quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+extern void quitSetDwnldTmpdir(char *z_dwnldTempDir);
+extern void quitSetIdsName(char *a_idsName);
+extern void quitSetIntfReloc(intfRelocFunc_t *a_intfReloc);
+extern void quitSetZoneName(char *a_zoneName);
+extern void quitSetZoneTmpdir(char *z_zoneTempDir);
+extern void quitSetZonelist(zoneList_t a_zlst);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PKGADD_QUIT_H__ */