diff options
| author | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
|---|---|---|
| committer | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
| commit | 5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch) | |
| tree | 0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/cmd/svr4pkg/pkgadd | |
| parent | 2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff) | |
| download | illumos-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/Makefile | 51 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/check.c | 1029 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/main.c | 4712 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/msgdefs.h | 141 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/presvr4.c | 172 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/quit.c | 409 | ||||
| -rw-r--r-- | usr/src/cmd/svr4pkg/pkgadd/quit.h | 64 |
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__ */ |
