/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include <stdio.h>
#include <time.h>
#include <wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <ulimit.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#include <signal.h>
#include <locale.h>
#include <libintl.h>
#include <pkgstrct.h>
#include <pkginfo.h>
#include <pkgdev.h>
#include <pkglocs.h>
#include <pwd.h>
#include <assert.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <pkgweb.h>
#include <install.h>
#include <libinst.h>
#include <libadm.h>
#include <dryrun.h>
#include <messages.h>
#include "pkginstall.h"
/* imported globals */
extern char **environ;
extern char *pkgabrv;
extern char *pkgname;
extern char *pkgarch;
extern char *pkgvers;
extern char pkgwild[];
/* libadm(3LIB) */
extern char *get_install_root(void);
/* quit.c */
extern sighdlrFunc_t *quitGetTrapHandler(void);
extern void quitSetDstreamTmpdir(char *a_dstreamTempDir);
extern void quitSetInstallStarted(boolean_t a_installStarted);
extern void quitSetPkgask(boolean_t a_pkgaskFlag);
extern void quitSetSilentExit(boolean_t a_silentExit);
extern void quitSetUpdatingExisting(boolean_t a_updatingExisting);
extern void quitSetZoneName(char *a_zoneName);
/* static globals */
static char path[PATH_MAX];
static int ck_instbase(void);
static int cp_pkgdirs(void);
static int merg_pkginfos(struct cl_attr **pclass,
struct cl_attr ***mpclass);
static int merg_respfile(void);
static int mv_pkgdirs(void);
static int rdonly(char *p);
static void ck_w_dryrun(int (*func)(), int type);
static void copyright(void), usage(void);
static void do_pkgask(boolean_t a_run_request_as_root);
static void rm_icas(char *casdir);
static void set_dryrun_dir_loc(void);
static void unpack(void);
void ckreturn(int retcode, char *msg);
static char *ro_params[] = {
"PATH", "NAME", "PKG", "PKGINST",
"VERSION", "ARCH",
"INSTDATE", "CATEGORY",
NULL
};
/*
* The following variable is the name of the device to which stdin
* is connected during execution of a procedure script. PROC_STDIN is
* correct for all ABI compliant packages. For non-ABI-compliant
* packages, the '-o' command line switch changes this to PROC_XSTDIN
* to allow user interaction during these scripts. -- JST
*/
static char *script_in = PROC_STDIN; /* assume ABI compliance */
static char *pkgdrtarg = NULL;
static char *pkgcontsrc = NULL;
static int non_abi_scripts = 0;
static char *respfile = NULL;
static char *srcinst = NULL;
static int suppressCopyright = 0;
static int nointeract = 0;
/* exported globals */
char *msgtext;
char *pkginst = (char *)NULL;
char *rw_block_size = NULL;
char ilockfile[PATH_MAX];
char instdir[PATH_MAX];
char saveSpoolInstallDir[PATH_MAX];
char pkgbin[PATH_MAX];
char pkgloc[PATH_MAX];
char pkgloc_sav[PATH_MAX];
char pkgsav[PATH_MAX];
char rlockfile[PATH_MAX];
char savlog[PATH_MAX];
char tmpdir[PATH_MAX];
int dbchg;
int dparts = 0;
int dreboot = 0;
int failflag = 0;
static int askflag = 0; /* non-zero if invoked as "pkgask" */
int ireboot = 0;
int maxinst = 1;
int nocnflct;
int nosetuid;
int pkgverbose = 0;
int rprcflag;
int warnflag = 0;
struct admin adm;
struct cfextra **extlist; /* pkgmap structure and other path info */
struct pkgdev pkgdev;
fsblkcnt_t pkgmap_blks = 0LL;
/*
* this global is referenced by:
* getinst - [RW] - incremented if:
* - installing same instance again
* - overwriting an existing instance
* - not installing a new instance
* quit - [RO] - if non-zero and started non-zero:
* - the new <PKGINST>/install directory and rename <PKGINST>/install.save
* - back to <PKGINST>/install
* main.c - [RO] - if non-zero:
* - alter manner in which parameters are setup for scripts
* - set UPDATE=yes in environment
*/
static int update = 0;
/* Set by -O debug: debug output is enabled? */
static boolean_t debugFlag = B_FALSE;
/* Set by the -G option: install packages in global zone only */
static boolean_t globalZoneOnly = B_FALSE;
/* Set by -O preinstallcheck */
static boolean_t preinstallCheck = B_FALSE;
/* Set by -O parent-zone-name= */
static char *parentZoneName = (char *)NULL;
/* Set by -O parent-zone-type= */
static char *parentZoneType = (char *)NULL;
#define DEFPATH "/sbin:/usr/sbin:/usr/bin"
#define MALSIZ 4 /* best guess at likely maximum value of MAXINST */
#define LSIZE 256 /* maximum line size supported in copyright file */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
/* This is the text for the "-O parent-zone-name=" option */
#define PARENTZONENAME "parent-zone-name="
#define PARENTZONENAME_LEN ((sizeof (PARENTZONENAME))-1)
/* This is the text for the "-O parent-zone-type=" option */
#define PARENTZONETYPE "parent-zone-type="
#define PARENTZONETYPE_LEN ((sizeof (PARENTZONETYPE))-1)
static char *cpio_names[] = {
"root",
"root.cpio",
"reloc",
"reloc.cpio",
"root.Z",
"root.cpio.Z",
"reloc.Z",
"reloc.cpio.Z",
0
};
int
main(int argc, char *argv[])
{
VFP_T *cfTmpVfp = NULL; /* temporary */
VFP_T *pkgmapVfp; /* "../pkgmap" file */
boolean_t run_request_as_root = B_FALSE;
char **np;
char *abi_comp_ptr;
char *abi_nm_ptr;
char *abi_sym_ptr;
char *admnfile = NULL;
char *device;
char *p;
char *prog_full_name = NULL;
char *pt;
char *updated = (char *)NULL;
char *vfstab_file = NULL;
char *zoneName = (char *)NULL;
char cbuf[MAX_PKG_PARAM_LENGTH];
char cmdbin[PATH_MAX];
char p_pkginfo[PATH_MAX];
char p_pkgmap[PATH_MAX];
char param[MAX_PKG_PARAM_LENGTH];
char script[PATH_MAX];
char altscript[PATH_MAX];
char *temp;
int c;
int disableAttributes = 0;
int err;
int init_install = 0;
int is_comp_arch;
int live_continue = 0;
int map_client = 1;
int n;
int nparts;
int npkgs;
int part;
int saveSpoolInstall = 0;
boolean_t cont_file_read;
struct cl_attr **pclass = NULL;
struct cl_attr **mergd_pclass = NULL;
struct pkginfo *prvinfo;
struct sigaction nact;
struct sigaction oact;
struct stat statb;
struct statvfs64 svfsb;
time_t clock;
PKGserver pkgserver = NULL;
/* reset contents of all default paths */
(void) memset(path, '\0', sizeof (path));
(void) memset(cmdbin, '\0', sizeof (cmdbin));
(void) memset(script, '\0', sizeof (script));
(void) memset(cbuf, '\0', sizeof (cbuf));
(void) memset(param, '\0', sizeof (param));
/* initialize locale environment */
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
/* initialize program name */
prog_full_name = argv[0];
(void) set_prog_name(argv[0]);
/* tell spmi zones interface how to access package output functions */
z_set_output_functions(echo, echoDebug, progerr);
/* exit if not root */
if (getuid()) {
progerr(ERR_NOT_ROOT, get_prog_name());
exit(1);
/* NOTREACHED */
}
/*
* determine how pkgmap() deals with environment variables:
* - MAPALL - resolve all variables
* - MAPBUILD - map only build variables
* - MAPINSTALL - map only install variables
* - MAPNONE - map no variables
*/
setmapmode(MAPINSTALL);
/* set sane umask */
(void) umask(0022);
/* initially no source "device" */
device = NULL;
/* reset npkgs (used as pkg remaining count in quit.c) */
npkgs = 0;
/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
progerr(ERR_ROOT_SET);
exit(1);
}
pkgserversetmode(DEFAULTMODE);
/* parse command line options */
while ((c = getopt(argc, argv,
"?Aa:B:b:Cc:D:d:eFf:GhIiMm:N:noO:p:R:r:StV:vyz")) != EOF) {
switch (c) {
/*
* Same as pkgadd: This disables attribute checking.
* It speeds up installation a little bit.
*/
case 'A':
disableAttributes++;
break;
/*
* Same as pkgadd: Define an installation administration
* file, admin, to be used in place of the default
* administration file. The token none overrides the use
* of any admin file, and thus forces interaction with the
* user. Unless a full path name is given, pkgadd first
* looks in the current working directory for the
* administration file. If the specified administration
* file is not in the current working directory, pkgadd
* looks in the /var/sadm/install/admin directory for the
* administration file.
*/
case 'a':
admnfile = flex_device(optarg, 0);
break;
/*
* Same as pkgadd: control block size given to
* pkginstall - block size used in read()/write() loop;
* default is st_blksize from stat() of source file.
*/
case 'B':
rw_block_size = optarg;
break;
/*
* Same as pkgadd: location where executables needed
* by procedure scripts can be found
* default is /usr/sadm/install/bin.
*/
case 'b':
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
exit(1);
}
if (isdir(optarg) != 0) {
char *p = strerror(errno);
progerr(ERR_CANNOT_USE_DIR, optarg, p);
exit(1);
}
(void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
break;
/*
* Same as pkgadd: This disables checksum tests on
* the source files. It speeds up installation a little bit.
*/
case 'C':
(void) checksum_off();
break;
/*
* Same as pkgadd: This allows designation of a
* continuation file. It is the same format as a dryrun file
* but it is used to take up where the dryrun left off.
*/
case 'c':
pkgcontsrc = optarg;
set_continue_mode();
set_dr_info(DR_TYPE, INSTALL_TYPE);
init_contfile(pkgcontsrc);
break;
/*
* Same as pkgadd: This allows designation of a
* dryrun file. This pkgadd will create dryrun files
* in the directory provided.
*/
case 'D':
pkgdrtarg = optarg;
set_dryrun_mode();
set_dr_info(DR_TYPE, INSTALL_TYPE);
break;
/*
* Same as pkgadd: Install or copy a package from
* device. device can be a full path name to a directory
* or the identifiers for tape, floppy disk, or removable
* disk - for example, /var/tmp or /floppy/floppy_name.
* It can also be a device alias - for example,
* /floppy/floppy0, or a datastream created by pkgtrans.
*/
case 'd':
device = flex_device(optarg, 1);
break;
/*
* Different from pkgadd: disable the 32 char name
* limit extension
*/
case 'e':
(void) set_ABI_namelngth();
break;
/*
* Different from pkgadd: specify file system type for
* the package device. Must be used with -m.
*/
case 'f':
pkgdev.fstyp = optarg;
break;
/*
* Same as pkgadd: install package in global zone only.
*/
case 'G':
globalZoneOnly = B_TRUE;
break;
/*
* Same as pkgadd: Enable hollow package support. When
* specified, for any package that has SUNW_PKG_HOLLOW=true:
* Do not calculate and verify package size against target.
* Do not run any package procedure or class action scripts.
* Do not create any target directories.
* Do not perform any script locking.
* Do not install any components of any package.
* Do not output any status or database update messages.
*/
case 'h':
set_depend_pkginfo_DB(B_TRUE);
break;
/*
* Same as pkgadd: Informs scripts that this is
* an initial install by setting the environment parameter
* PKG_INIT_INSTALL=TRUE for all scripts. They may use it as
* they see fit, safe in the knowledge that the target
* filesystem is tabula rasa.
*/
case 'I':
init_install++;
break;
/*
* Different from pkgadd: use by pkgask.
*/
case 'i':
askflag++;
quitSetPkgask(B_TRUE);
break;
/*
* Same as pkgadd: Instruct pkgadd not to use the
* $root_path/etc/vfstab file for determining the client's
* mount points. This option assumes the mount points are
* correct on the server and it behaves consistently with
* Solaris 2.5 and earlier releases.
*/
case 'M':
map_client = 0;
break;
/*
* Different from pkgadd: specify device to use for package
* source.
*/
case 'm':
pkgdev.mount = optarg;
pkgdev.rdonly++;
pkgdev.mntflg++;
break;
/*
* Different from pkgadd: specify program name to use
* for messages.
*/
case 'N':
(void) set_prog_name(optarg);
break;
/*
* Same as pkgadd: installation occurs in
* non-interactive mode. Suppress output of the list of
* installed files. The default mode is interactive.
*/
case 'n':
nointeract++;
(void) echoSetFlag(B_FALSE);
break;
/*
* Almost same as pkgadd: the -O option allows the behavior
* of the package tools to be modified. Recognized options:
* -> debug
* ---> enable debugging output
* -> preinstallcheck
* ---> perform a "pre installation" check of the specified
* ---> package - suppress all regular output and cause a
* ---> series of one or more "name=value" pair format lines
* ---> to be output that describes the "installability" of
* ---> the specified package
* -> enable-hollow-package-support
* --> Enable hollow package support. When specified, for any
* --> package that has SUNW_PKG_HOLLOW=true:
* --> Do not calculate and verify package size against target
* --> Do not run any package procedure or class action scripts
* --> Do not create or remove any target directories
* --> Do not perform any script locking
* --> Do not install or uninstall any components of any package
* --> Do not output any status or database update messages
*/
case 'O':
for (p = strtok(optarg, ","); p != (char *)NULL;
p = strtok(NULL, ",")) {
/* process debug option */
if (strcmp(p, "debug") == 0) {
/* set debug flag/enable debug output */
if (debugFlag == B_TRUE) {
smlSetVerbose(B_TRUE);
}
debugFlag = B_TRUE;
(void) echoDebugSetFlag(debugFlag);
/* debug info on arguments to pkgadd */
for (n = 0; n < argc && argv[n]; n++) {
echoDebug(DBG_ARG, n, argv[n]);
}
continue;
}
/* process enable-hollow-package-support opt */
if (strcmp(p,
"enable-hollow-package-support") == 0) {
set_depend_pkginfo_DB(B_TRUE);
continue;
}
/* process preinstallcheck option */
if (strcmp(p, "preinstallcheck") == 0) {
preinstallCheck = B_TRUE;
nointeract++; /* -n */
suppressCopyright++; /* -S */
quitSetSilentExit(B_TRUE);
continue;
}
/* process addzonename option */
if (strcmp(p, "addzonename") == 0) {
/*
* set zone name to add to messages;
* first look in the current environment
* and use the default package zone name
* if it is set; otherwise, use the name
* of the current zone
*/
zoneName =
getenv(PKG_ZONENAME_VARIABLE);
if ((zoneName == (char *)NULL) ||
(*zoneName == '\0')) {
zoneName = z_get_zonename();
}
if (zoneName != (char *)NULL) {
if (*zoneName != '\0') {
quitSetZoneName(
zoneName);
} else {
zoneName = (char *)NULL;
}
}
continue;
}
/* process parent-zone-name option */
if (strncmp(p, PARENTZONENAME,
PARENTZONENAME_LEN) == 0) {
parentZoneName = p+PARENTZONENAME_LEN;
continue;
}
/* process parent-zone-type option */
if (strncmp(p, PARENTZONETYPE,
PARENTZONETYPE_LEN) == 0) {
parentZoneType = p+PARENTZONETYPE_LEN;
continue;
}
if (strncmp(p, PKGSERV_MODE,
PKGSERV_MODE_LEN) == 0) {
pkgserversetmode(pkgparsemode(p +
PKGSERV_MODE_LEN));
continue;
}
/* option not recognized - issue warning */
progerr(ERR_INVALID_O_OPTION, p);
continue;
}
break;
/*
* Different from pkgadd: This is an old non-ABI package
*/
case 'o':
non_abi_scripts++;
break;
/*
* Different from pkgadd: specify number of parts to package.
*/
case 'p':
dparts = ds_getinfo(optarg);
break;
/*
* Same as pkgadd: Define the full path name of a
* directory to use as the root_path. All files,
* including package system information files, are
* relocated to a directory tree starting in the specified
* root_path. The root_path may be specified when
* installing to a client from a server (for example,
* /export/root/client1).
*/
case 'R':
if (!set_inst_root(optarg)) {
progerr(ERR_ROOT_CMD);
exit(1);
}
break;
/*
* Same as pkgadd: Identify a file or directory which
* contains output from a previous pkgask(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':
respfile = flex_device(optarg, 2);
break;
/*
* Same as pkgadd: suppress copyright notice being
* output during installation.
*/
case 'S':
suppressCopyright++;
break;
/*
* Same as pkgadd: disable save spool area creation;
* do not spool any partial package contents, that is,
* suppress the creation and population of the package save
* spool area (var/sadm/pkg/PKG/save/pspool/PKG).
*/
case 't':
disable_spool_create();
break;
/*
* Same as pkgadd: Specify an alternative fs_file to map
* the client's file systems. For example, used in
* situations where the $root_path/etc/vfstab file is
* non-existent or unreliable. Informs the pkginstall
* portion to mount up a client filesystem based upon the
* supplied vfstab-like file of stable format.
*/
case 'V':
vfstab_file = flex_device(optarg, 2);
map_client = 1;
break;
/*
* Same as pkgadd: Trace all of the scripts that get
* executed by pkgadd, located in the pkginst/install
* directory. This option is used for debugging the
* procedural and non-procedural scripts
*/
case 'v':
pkgverbose++;
break;
/*
* Different from pkgadd: process this package using
* old non-ABI symlinks
*/
case 'y':
set_nonABI_symlinks();
break;
/*
* Same as pkgadd: perform fresh install from
* package save spool area. When set, the package contents
* are installed from the package spool save area instead
* of from the package root area, so that the original
* source packages are not required to install the
* package. If the -h option is also specified and the
* package is hollow, then this option is ignored. When -z
* is specified:
* - Editable files are installed from the package instance
* save area.
* - Volatile files are installed from the package instance
* save area.
* - Executable and data files are installed from the final
* installed location as specified in the pkgmap file.
* - Installation scripts are run from the package spool
* save area.
*/
case 'z':
saveSpoolInstall++;
break;
/*
* unrecognized option
*/
default:
usage();
/*NOTREACHED*/
/*
* Although usage() calls a noreturn function,
* needed to add return (1); so that main() would
* pass compilation checks. The statement below
* should never be executed.
*/
return (1);
}
}
/*
* ********************************************************************
* validate command line options
* ********************************************************************
*/
/* set "debug echo" flag according to setting of "-O debug" option */
(void) echoDebugSetFlag(debugFlag);
(void) log_set_verbose(debugFlag);
/* output entry debugging information */
if (z_running_in_global_zone()) {
echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
} else {
echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
z_get_zonename());
}
if (in_continue_mode() && !in_dryrun_mode()) {
progerr(ERR_LIVE_CONTINUE_NOT_SUPPORTED);
usage();
/*NOTREACHED*/
}
/* pkgask requires a response file */
if (askflag && (respfile == NULL)) {
usage();
/*NOTREACHED*/
}
/* if device specified, set appropriate device in pkgdev */
if (device) {
if (pkgdev.mount) {
pkgdev.bdevice = device;
} else {
pkgdev.cdevice = device;
}
}
/* if file system type specified, must have a device to mount */
if (pkgdev.fstyp && !pkgdev.mount) {
progerr(ERR_F_REQUIRES_M);
usage();
/*NOTREACHED*/
}
/* BEGIN DATA GATHERING PHASE */
/*
* Get the mount table info and store internally.
*/
cont_file_read = B_FALSE;
if (in_continue_mode()) {
int error;
cont_file_read = read_continuation(&error);
if (error == -1) {
quit(99);
/*NOTREACHED*/
}
if (!in_dryrun_mode()) {
live_continue = 1;
}
}
/* Read the mount table if not done in continuation mode */
if (!cont_file_read) {
if (get_mntinfo(map_client, vfstab_file)) {
quit(99);
/*NOTREACHED*/
}
}
/*
* This function defines the standard /var/... directories used later
* to construct the paths to the various databases.
*/
set_PKGpaths(get_inst_root());
/*
* If this is being installed on a client whose /var filesystem is
* mounted in some odd way, remap the administrative paths to the
* real filesystem. This could be avoided by simply mounting up the
* client now; but we aren't yet to the point in the process where
* modification of the filesystem is permitted.
*/
if (is_an_inst_root()) {
int fsys_value;
fsys_value = fsys(get_PKGLOC());
if (use_srvr_map_n(fsys_value))
set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
fsys_value = fsys(get_PKGADM());
if (use_srvr_map_n(fsys_value))
set_PKGADM(server_map(get_PKGADM(), fsys_value));
}
/*
* Initialize pkginfo PKGSAV entry, just in case we dryrun to
* somewhere else.
*/
set_infoloc(get_PKGLOC());
/* pull off directory and package name from end of command line */
switch (argc-optind) {
case 0: /* missing directory and package instance */
progerr(ERR_MISSING_DIR_AND_PKG);
usage();
/*NOTREACHED*/
case 1: /* missing package instance */
progerr(ERR_MISSING_PKG_INSTANCE);
usage();
/*NOTREACHED*/
case 2: /* just right! */
pkgdev.dirname = argv[optind++];
srcinst = argv[optind++];
break;
default: /* too many args! */
progerr(ERR_TOO_MANY_CMD_ARGS);
usage();
break;
}
(void) pkgparam(NULL, NULL); /* close up prior pkg file if needed */
/*
* Initialize installation admin parameters by reading
* the adminfile.
*/
if (!askflag && !live_continue) {
echoDebug(DBG_PKGINSTALL_ADMINFILE, admnfile ? admnfile : "");
setadminFile(admnfile);
}
/*
* about to perform first operation that could be modified by the
* preinstall check option - if preinstall check is selected (that is,
* only gathering dependencies), then output a debug message to
* indicate that the check is beginning. Also turn echo() output
* off and set various other flags.
*/
if (preinstallCheck == B_TRUE) {
(void) echoSetFlag(B_FALSE);
echoDebug(DBG_PKGINSTALL_PREINSCHK,
pkginst ? pkginst : (srcinst ? srcinst : ""),
zoneName ? zoneName : "global");
cksetPreinstallCheck(B_TRUE);
cksetZoneName(zoneName);
/* inform quit that the install has started */
quitSetInstallStarted(B_TRUE);
}
/*
* validate the "rscriptalt" admin file setting
* The rscriptalt admin file parameter may be set to either
* RSCRIPTALT_ROOT or RSCRIPTALT_NOACCESS:
* --> If rscriptalt is not set, or is set to RSCRIPTALT_NOACCESS,
* --> or is set to any value OTHER than RSCRIPTALT_ROOT, then
* --> assume that the parameter is set to RSCRIPTALT_NOACCESS
* If rscriptalt is set to RSCRIPTALT_ROOT, then run request scripts
* as the "root" user if user "install" is not defined.
* Otherwise, assume rscriptalt is set to RSCRIPTALT_NOACCESS, and run
* request scripts as the "alternative" user if user "install" is not
* defined, as appropriate for the current setting of the NONABI_SCRIPTS
* environment variable.
*/
if (ADMSET(RSCRIPTALT)) {
p = adm.RSCRIPTALT;
echoDebug(DBG_PKGINSTALL_RSCRIPT_SET_TO, RSCRIPTALT_KEYWORD, p);
if (strcasecmp(p, RSCRIPTALT_ROOT) == 0) {
/* rscriptalt=root */
run_request_as_root = B_TRUE;
} else if (strcasecmp(p, RSCRIPTALT_NOACCESS) == 0) {
/* rscriptalt=noaccess */
run_request_as_root = B_FALSE;
} else {
/* rscriptalt=??? */
logerr(WRN_RSCRIPTALT_BAD, RSCRIPTALT_KEYWORD, p,
RSCRIPTALT_ROOT, RSCRIPTALT_NOACCESS);
logerr(WRN_RSCRIPTALT_USING, RSCRIPTALT_KEYWORD,
RSCRIPTALT_NOACCESS);
run_request_as_root = B_FALSE;
}
} else {
/* rscriptalt not set - assume rscriptalt=noaccess */
echoDebug(DBG_PKGINSTALL_RSCRIPT_NOT_SET, RSCRIPTALT_KEYWORD);
run_request_as_root = B_FALSE;
}
echoDebug(DBG_PKGINSTALL_RSCRIPT_IS_ROOT, run_request_as_root);
/*
* hook SIGINT and SIGHUP interrupts into quit.c's trap handler
*/
/* hold SIGINT/SIGHUP interrupts */
(void) sighold(SIGHUP);
(void) sighold(SIGINT);
/* connect quit.c:trap() to SIGINT */
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGINT, &nact, &oact);
/* connect quit.c:trap() to SIGHUP */
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGHUP, &nact, &oact);
/* release hold on signals */
(void) sigrelse(SIGHUP);
(void) sigrelse(SIGINT);
/*
* create required /var... directories if they do not exist;
* this function will call quit(99) if any required path cannot
* be created.
*/
ckdirs();
tzset();
/*
* create path to temporary directory "installXXXXXX" - if TMPDIR
* environment variable is set, create the directory in $TMPDIR;
* otherwise, create the directory in P_tmpdir.
*/
pt = getenv("TMPDIR");
(void) snprintf(tmpdir, sizeof (tmpdir), "%s/installXXXXXX",
((pt != (char *)NULL) && (*pt != '\0')) ? pt : P_tmpdir);
echoDebug(DBG_PKGINSTALL_TMPDIR, tmpdir);
if ((mktemp(tmpdir) == NULL) || mkdir(tmpdir, 0771)) {
progerr(ERR_MKDIR, tmpdir);
quit(99);
/*NOTREACHED*/
}
/*
* if the package device is a file containing a package stream,
* unpack the stream into a temporary directory
*/
if ((isdir(pkgdev.dirname) != 0) &&
(pkgdev.cdevice == (char *)NULL) &&
(pkgdev.bdevice == (char *)NULL) &&
(isfile((char *)NULL, pkgdev.dirname) == 0)) {
char *idsName = (char *)NULL;
char *pkgnames[2];
char *device = pkgdev.dirname;
boolean_t b;
echoDebug(DBG_PKGINSTALL_DS_ISFILE, pkgdev.dirname);
/*
* validate the package source device - return pkgdev info that
* describes the package source device.
*/
if (devtype(device, &pkgdev)) {
progerr(ERR_BAD_DEVICE, device);
quit(99);
/* NOTREACHED */
}
/* generate the list of packages to verify */
pkgnames[0] = srcinst;
pkgnames[1] = (char *)NULL;
b = open_package_datastream(1, pkgnames, (char *)NULL,
pkgdev.dirname, (int *)NULL, &idsName, tmpdir, &pkgdev,
1);
if (b == B_FALSE) {
progerr(ERR_CANNOT_OPEN_PKG_STREAM,
pkgdev.dirname ? pkgdev.dirname : "?");
quit(99);
/*NOTREACHED*/
}
/* make sure temporary directory is removed on exit */
quitSetDstreamTmpdir(pkgdev.dirname);
/* unpack the package instance from the data stream */
b = unpack_package_from_stream(idsName, srcinst,
pkgdev.dirname);
if (b == B_FALSE) {
progerr(ERR_CANNOT_UNPACK_PKGSTRM,
srcinst ? srcinst : "?",
idsName ? idsName : "?",
pkgdev.dirname ? pkgdev.dirname : "?");
quit(99);
/*NOTREACHED*/
}
/* close the datastream - no longer needed */
echoDebug(DBG_CLOSING_STREAM, idsName, pkgdev.dirname);
(void) ds_close(1);
}
if (snprintf(instdir, PATH_MAX, "%s/%s", pkgdev.dirname, srcinst)
>= PATH_MAX) {
progerr(ERR_SNPRINTF, instdir);
quit(99);
/*NOTREACHED*/
}
zoneName = getenv(PKG_ZONENAME_VARIABLE);
/*
* If the environment has a CLIENT_BASEDIR, that takes precedence
* over anything we will construct. We need to save it here because
* in three lines, the current environment goes away.
*/
(void) set_env_cbdir(); /* copy over environ */
getuserlocale();
/*
* current environment has been read; clear environment out
* so putparam() can be used to populate the new environment
* to be passed to any executables/scripts.
*/
environ = NULL;
/* write parent condition information to environment */
putConditionInfo(parentZoneName, parentZoneType);
putuserlocale();
if (init_install) {
putparam("PKG_INIT_INSTALL", "TRUE");
}
if (is_an_inst_root()) {
export_client_env(get_inst_root());
}
if (zoneName != (char *)NULL) {
putparam(PKG_ZONENAME_VARIABLE, zoneName);
}
putparam("INST_DATADIR", pkgdev.dirname);
if (non_abi_scripts) {
putparam("NONABI_SCRIPTS", "TRUE");
}
if (nonABI_symlinks()) {
putparam("PKG_NONABI_SYMLINKS", "TRUE");
}
if (get_ABI_namelngth()) {
putparam("PKG_ABI_NAMELENGTH", "TRUE");
}
/* establish path and oambase */
if (cmdbin[0] == '\0') {
(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
}
(void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
putparam("PATH", path);
putparam("OAMBASE", OAMBASE);
(void) snprintf(p_pkginfo, sizeof (p_pkginfo),
"%s/%s", instdir, PKGINFO);
(void) snprintf(p_pkgmap, sizeof (p_pkgmap),
"%s/%s", instdir, PKGMAP);
/* Read the environment (from pkginfo or '-e') ... */
abi_nm_ptr = getenv("PKG_ABI_NAMELENGTH");
/* Disable the 32 char name limit extension */
if (abi_nm_ptr && strncasecmp(abi_nm_ptr, "TRUE", 4) == 0) {
(void) set_ABI_namelngth();
}
/*
* This tests the pkginfo and pkgmap files for validity and
* puts all delivered pkginfo variables (except for PATH) into
* our environment. This is where a delivered pkginfo BASEDIR
* would come from. See set_basedirs() below.
*/
if (pkgenv(srcinst, p_pkginfo, p_pkgmap)) {
quit(1);
/*NOTREACHED*/
}
echo("\n%s(%s) %s", pkgname, pkgarch, pkgvers);
/*
* If this script was invoked by 'pkgask', just
* execute request script and quit (do_pkgask()).
*/
if (askflag) {
do_pkgask(run_request_as_root);
}
/* validate package contents file */
if (vcfile() == 0) {
quit(99);
}
/* if not in dryrun mode aquire packaging lock */
if (!in_dryrun_mode()) {
/* acquire the package lock - at install initialization */
if (!lockinst(get_prog_name(), srcinst, "install-initial")) {
quit(99);
/*NOTREACHED*/
}
}
/*
* Now do all the various setups based on ABI compliance
*/
/* Read the environment (from pkginfo or '-o') ... */
abi_comp_ptr = getenv("NONABI_SCRIPTS");
/* Read the environment (from pkginfo or '-y') ... */
abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
/* bug id 4244631, not ABI compliant */
if (abi_comp_ptr && strncasecmp(abi_comp_ptr, "TRUE", 4) == 0) {
script_in = PROC_XSTDIN;
non_abi_scripts = 1;
}
/* Set symlinks to be processed the old way */
if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
set_nonABI_symlinks();
}
/*
* At this point, script_in, non_abi_scripts & the environment are
* all set correctly for the ABI status of the package.
*/
if (pt = getenv("MAXINST")) {
maxinst = atol(pt);
}
/*
* See if were are installing a package that only wants to update
* the database or only install files associated with CAS's. We
* only check the PKG_HOLLOW_VARIABLE variable if told to do so by
* the caller.
*/
if (is_depend_pkginfo_DB()) {
pt = getenv(PKG_HOLLOW_VARIABLE);
if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
if (disableAttributes) {
disable_attribute_check();
}
/*
* this is a hollow package and hollow package support
* is enabled -- override admin settings to suppress
* checks that do not make sense since no scripts will
* be executed and no files will be installed.
*/
setadminSetting("conflict", "nocheck");
setadminSetting("setuid", "nocheck");
setadminSetting("action", "nocheck");
setadminSetting("partial", "nocheck");
setadminSetting("space", "nocheck");
setadminSetting("authentication", "nocheck");
} else {
echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
set_depend_pkginfo_DB(B_FALSE);
}
}
/*
* if performing a fresh install to a non-global zone, and doing
* more than just updating the package database (that is, the
* package to install is NOT "hollow"), then set the global flag
* that directs installation is from partially spooled packages
* (that is, packages installed in the global zone).
*/
if (saveSpoolInstall && (!is_depend_pkginfo_DB())) {
set_partial_inst();
} else {
saveSpoolInstall = 0;
}
/*
* verify that we are not trying to install an
* INTONLY package with no interaction
*/
if (pt = getenv("INTONLY")) {
if (askflag || nointeract) {
progerr(ERR_INTONLY, pkgabrv ? pkgabrv : "?");
quit(1);
/*NOTREACHED*/
}
}
if (!suppressCopyright && !pkgdev.cdevice) {
copyright();
}
/*
* inspect the system to determine if any instances of the
* package being installed already exist on the system
*/
prvinfo = (struct pkginfo *)calloc(MALSIZ, sizeof (struct pkginfo));
if (prvinfo == NULL) {
progerr(ERR_MEMORY, errno);
quit(99);
/*NOTREACHED*/
}
for (;;) {
if (pkginfo(&prvinfo[npkgs], pkgwild, NULL, NULL)) {
if ((errno == ESRCH) || (errno == ENOENT)) {
break;
}
progerr(ERR_SYSINFO, errno);
quit(99);
/*NOTREACHED*/
}
if ((++npkgs % MALSIZ) == 0) {
prvinfo = (struct pkginfo *)realloc(prvinfo,
(npkgs+MALSIZ) * sizeof (struct pkginfo));
if (prvinfo == NULL) {
progerr(ERR_MEMORY, errno);
quit(99);
/*NOTREACHED*/
}
}
}
/*
* Determine the correct package instance based on how many packages are
* already installed. If there are none (npkgs == 0), getinst() just
* returns the package abbreviation. Otherwise, getinst() interacts with
* the user (or reads the admin file) to determine if an instance which
* is already installed should be overwritten, or possibly install a new
* instance of this package
*/
pkginst = getinst(&update, prvinfo, npkgs, preinstallCheck);
/* set "update flag" if updating an existing instance of this package */
if (update) {
setUpdate();
}
/*
* Some pkgs (SUNWcsr) already spooled to the zone, check the
* value of UPDATE in their postinstall script. After a pkg
* has been patched UPDATE exists statically in the pkginfo
* file and this value must be reset when installing a zone.
*/
if (saveSpoolInstall != 0 && !isUpdate()) {
putparam("UPDATE", "");
}
/* inform quit() if updating existing or installing new instance */
quitSetUpdatingExisting(update ? B_TRUE : B_FALSE);
if (respfile) {
(void) set_respfile(respfile, pkginst, RESP_RO);
}
(void) snprintf(pkgloc, sizeof (pkgloc),
"%s/%s", get_PKGLOC(), pkginst);
(void) snprintf(pkgbin, sizeof (pkgbin),
"%s/install", pkgloc);
(void) snprintf(pkgsav, sizeof (pkgsav),
"%s/save", pkgloc);
if (snprintf(saveSpoolInstallDir, PATH_MAX, "%s/pspool/%s", pkgsav,
pkginst) < 0) {
progerr(ERR_SNPRINTF, saveSpoolInstallDir);
quit(99);
/*NOTREACHED*/
}
(void) snprintf(ilockfile, sizeof (ilockfile),
"%s/!I-Lock!", pkgloc);
(void) snprintf(rlockfile, sizeof (rlockfile),
"%s/!R-Lock!", pkgloc);
(void) snprintf(savlog, sizeof (savlog),
"%s/logs/%s", get_PKGADM(), pkginst);
putparam("PKGINST", pkginst);
putparam("PKGSAV", pkgsav);
/*
* Be sure request script has access to PKG_INSTALL_ROOT if there is
* one
*/
put_path_params();
if (!map_client) {
putparam("PKG_NO_UNIFIED", "TRUE");
}
/*
* This maps the client filesystems into the server's space.
*/
if (map_client && !mount_client()) {
logerr(MSG_MANMOUNT);
}
/*
* If this is an UPDATE then either this is exactly the same version
* and architecture of an installed package or a different package is
* intended to entirely replace an installed package of the same name
* with a different VERSION or ARCH string.
* Don't merge any databases if only gathering dependencies.
*/
if ((preinstallCheck == B_FALSE) && (update)) {
/*
* If this version and architecture is already installed,
* merge the installed and installing parameters and inform
* all procedure scripts by defining UPDATE in the
* environment.
*/
if (is_samepkg()) {
/*
* If it's the same ARCH and VERSION, then a merge
* and copy operation is necessary.
*/
if (n = merg_pkginfos(pclass, &mergd_pclass)) {
quit(n);
/*NOTREACHED*/
}
if (n = cp_pkgdirs()) {
quit(n);
/*NOTREACHED*/
}
} else {
/*
* If it's a different ARCH and/or VERSION then this
* is an "instance=overwrite" situation. The
* installed base needs to be confirmed and the
* package directories renamed.
*/
if (n = ck_instbase()) {
quit(n);
/*NOTREACHED*/
}
if (n = mv_pkgdirs()) {
quit(n);
/*NOTREACHED*/
}
}
putparam("UPDATE", "yes");
}
if (in_dryrun_mode()) {
set_dryrun_dir_loc();
}
if (preinstallCheck == B_FALSE) {
/*
* Determine if the package has been partially installed on or
* removed from this system.
*/
ck_w_dryrun(ckpartial, PARTIAL);
/*
* make sure current runlevel is appropriate
*/
ck_w_dryrun(ckrunlevel, RUNLEVEL);
} else {
int r;
/*
* Just gathering dependencies - determine if the package has
* been partially installed on or removed from this system and
* output information to stdout
*/
r = ckpartial();
(void) fprintf(stdout, "ckpartialinstall=%d\n", r == 8 ? 1 : 0);
(void) fprintf(stdout, "ckpartialremove=%d\n", r == 9 ? 1 : 0);
/*
* make sure current runlevel is appropriate
*/
r = ckrunlevel();
(void) fprintf(stdout, "ckrunlevel=%d\n", r);
}
if (pkgdev.cdevice) {
/* get first volume which contains info files */
unpack();
if (!suppressCopyright) {
copyright();
}
}
/* update the lock - at the request script */
lockupd("request");
/*
* If no response file has been provided, initialize response file by
* executing any request script provided by this package. Initialize
* the response file if not gathering dependencies only.
*/
if ((!rdonly_respfile()) && (preinstallCheck == B_FALSE)) {
(void) snprintf(path, sizeof (path),
"%s/%s", instdir, REQUEST_FILE);
n = reqexec(update, path, non_abi_scripts,
run_request_as_root);
if (in_dryrun_mode()) {
set_dr_info(REQUESTEXITCODE, n);
}
ckreturn(n, ERR_REQUEST);
}
/*
* Look for all parameters in response file which begin with a
* capital letter, and place them in the environment.
*/
if ((is_a_respfile()) && (preinstallCheck == B_FALSE)) {
if (n = merg_respfile()) {
quit(n);
/*NOTREACHED*/
}
}
/*
* Run a checkinstall script if one is provided by the package.
* Don't execute checkinstall script if we are only updating the DB.
* Don't execute checkinstall script if only gathering dependencies.
*/
/* update the lock - at the checkinstall script */
lockupd("checkinstall");
/* Execute checkinstall script if one is provided. */
(void) snprintf(script, sizeof (script), "%s/install/checkinstall",
instdir);
if (access(script, F_OK) != 0) {
/* no script present */
echoDebug(DBG_PKGINSTALL_COC_NONE, pkginst, script,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
/* updating db only: skip checkinstall script */
echoDebug(DBG_PKGINSTALL_COC_DBUPD, pkginst, script,
zoneName ? zoneName : "global");
} else if (preinstallCheck == B_TRUE) {
/* only gathering dependencies: skip checkinstall script */
echoDebug(DBG_PKGINSTALL_COC_NODEL, pkginst, script,
zoneName ? zoneName : "global");
} else {
/* script present and ok to run: run the script */
if (zoneName == (char *)NULL) {
echo(MSG_PKGINSTALL_EXECOC_GZ);
echoDebug(DBG_PKGINSTALL_EXECOC_GZ, pkginst, script);
} else {
echo(MSG_PKGINSTALL_EXECOC_LZ, zoneName);
echoDebug(DBG_PKGINSTALL_EXECOC_LZ, pkginst, script,
zoneName);
}
n = chkexec(update, script);
if (in_dryrun_mode()) {
set_dr_info(CHECKEXITCODE, n);
}
if (n == 3) {
echo(WRN_CHKINSTALL);
ckreturn(4, NULL);
} else if (n == 7) {
/* access returned error */
progerr(ERR_CHKINSTALL_NOSCRIPT, script);
ckreturn(4, ERR_CHKINSTALL);
} else {
ckreturn(n, ERR_CHKINSTALL);
}
}
/*
* Now that the internal data structures are initialized, we can
* initialize the dryrun files (which may be the same files).
*/
if (pkgdrtarg) {
init_dryrunfile(pkgdrtarg);
}
/*
* Look for all parameters in response file which begin with a
* capital letter, and place them in the environment.
*/
if (is_a_respfile()) {
if (n = merg_respfile()) {
quit(n);
/*NOTREACHED*/
}
}
/* update the lock - doing analysis */
lockupd("analysis");
/*
* Determine package base directory and client base directory
* if appropriate. Then encapsulate them for future retrieval.
*/
if ((err = set_basedirs(isreloc(instdir), adm.basedir, pkginst,
nointeract)) != 0) {
quit(err);
/*NOTREACHED*/
}
/*
* Create the base directory if specified.
* Don't create if we are only updating the DB.
* Don't create if only gathering dependencies.
*/
if (!is_depend_pkginfo_DB() &&
!preinstallCheck && is_a_basedir()) {
mkbasedir(!nointeract, get_basedir());
echo(MSG_BASE_USED, get_basedir());
}
/*
* Store PKG_INSTALL_ROOT, BASEDIR & CLIENT_BASEDIR in our
* environment for later use by procedure scripts.
*/
put_path_params();
/*
* the following two checks are done in the corresponding
* ck() routine, but are repeated here to avoid re-processing
* the database if we are administered to not include these
* processes
*/
if (ADM(setuid, "nochange")) {
nosetuid++; /* Clear setuid/gid bits. */
}
if (ADM(conflict, "nochange")) {
nocnflct++; /* Don't install conflicting files. */
}
/*
* Get the filesystem space information for the filesystem on which
* the "contents" file resides.
*/
svfsb.f_bsize = 8192;
svfsb.f_frsize = 1024;
if (statvfs64(get_PKGADM(), &svfsb) == -1) {
int lerrno = errno;
if (!access(get_PKGADM(), F_OK)) {
progerr(ERR_PKGINSTALL_STATVFS, get_PKGADM(),
strerror(errno));
logerr("(errno %d)", lerrno);
quit(99);
/*NOTREACHED*/
}
}
/*
* Get the number of blocks used by the pkgmap, ocfile()
* needs this to properly determine its space requirements.
*/
if (stat(p_pkgmap, &statb) == -1) {
progerr(ERR_PKGINSTALL_STATOF, p_pkgmap, strerror(errno));
quit(99);
/*NOTREACHED*/
}
pkgmap_blks = nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
/*
* Merge information in memory with the "contents" file; this creates
* a temporary version of the "contents" file. Note that in dryrun
* mode, we still need to record the contents file data somewhere,
* but we do it in the dryrun directory.
*/
if (in_dryrun_mode()) {
if (n = set_cfdir(pkgdrtarg)) {
quit(n);
/*NOTREACHED*/
}
} else {
if (n = set_cfdir(NULL)) {
quit(n);
/*NOTREACHED*/
}
}
if (!ocfile(&pkgserver, &cfTmpVfp, pkgmap_blks)) {
quit(99);
/*NOTREACHED*/
}
/*
* if cpio is being used, tell pkgdbmerg since attributes will
* have to be check and repaired on all file and directories
*/
for (np = cpio_names; *np != NULL; np++) {
(void) snprintf(path, sizeof (path),
"%s/%s", instdir, *np);
if (iscpio(path, &is_comp_arch)) {
is_WOS_arch();
break;
}
}
/* Establish the class list and the class attributes. */
cl_sets(getenv("CLASSES"));
find_CAS(I_ONLY, pkgbin, instdir);
if (vfpOpen(&pkgmapVfp, p_pkgmap, "r", VFP_NEEDNOW) != 0) {
progerr(ERR_PKGMAP, p_pkgmap);
quit(99);
/*NOTREACHED*/
}
/*
* This modifies the path list entries in memory to reflect
* how they should look after the merg is complete
*/
nparts = sortmap(&extlist, pkgmapVfp, pkgserver, cfTmpVfp, zoneName);
if ((n = files_installed()) > 0) {
if (n > 1) {
echo(MSG_INST_MANY, n);
} else {
echo(MSG_INST_ONE, n);
}
}
/*
* Check ulimit requirement (provided in pkginfo). The purpose of
* this limit is to terminate pathological file growth resulting from
* file edits in scripts. It does not apply to files in the pkgmap
* and it does not apply to any database files manipulated by the
* installation service.
*/
if (pt = getenv("ULIMIT")) {
if (assign_ulimit(pt) == -1) {
progerr(ERR_BADULIMIT, pt);
quit(99);
/*NOTREACHED*/
}
putparam("PKG_ULIMIT", "TRUE");
}
/*
* If only gathering dependencies, check and output status of all
* remaining dependencies and exit.
*/
if (preinstallCheck == B_TRUE) {
/* update the lock file - final checking */
lockupd("preinstallcheck");
/* verify package information files are not corrupt */
(void) fprintf(stdout, "ckpkgfiles=%d\n", ckpkgfiles());
/* verify package dependencies */
(void) fprintf(stdout, "ckdepend=%d\n", ckdepend());
/* Check space requirements */
(void) fprintf(stdout, "ckspace=%d\n", ckspace());
/*
* Determine if any objects provided by this package conflict
* with the files of previously installed packages.
*/
(void) fprintf(stdout, "ckconflict=%d\n", ckconflct());
/*
* Determine if any objects provided by this package will be
* installed with setuid or setgid enabled.
*/
(void) fprintf(stdout, "cksetuid=%d\n", cksetuid());
/*
* Determine if any packaging scripts provided with this package
* will execute as a priviledged user.
*/
(void) fprintf(stdout, "ckpriv=%d\n", ckpriv());
/* Verify neccessary package installation directories exist */
(void) fprintf(stdout, "ckpkgdirs=%d\n", ckpkgdirs());
/*
* ****** preinstall check done - exit ******
*/
echoDebug(DBG_PKGINSTALL_PREINSCHK_OK);
quit(0);
/*NOTREACHED*/
}
/*
* Not gathering dependencies only, proceed to check dependencies
* and continue with the package installation operation.
*/
/*
* verify package information files are not corrupt
*/
ck_w_dryrun(ckpkgfiles, PKGFILES);
/*
* verify package dependencies
*/
ck_w_dryrun(ckdepend, DEPEND);
/*
* Check space requirements.
*/
ck_w_dryrun(ckspace, SPACE);
/*
* Determine if any objects provided by this package conflict with
* the files of previously installed packages.
*/
ck_w_dryrun(ckconflct, CONFLICT);
/*
* Determine if any objects provided by this package will be
* installed with setuid or setgid enabled.
*/
ck_w_dryrun(cksetuid, SETUID);
/*
* Determine if any packaging scripts provided with this package will
* execute as a priviledged user.
*/
ck_w_dryrun(ckpriv, PRIV);
/*
* Verify neccessary package installation directories exist.
*/
ck_w_dryrun(ckpkgdirs, PKGDIRS);
/*
* If we have assumed that we were installing setuid or conflicting
* files, and the user chose to do otherwise, we need to read in the
* package map again and re-merg with the "contents" file
*/
if (rprcflag) {
nparts = sortmap(&extlist, pkgmapVfp, pkgserver,
cfTmpVfp, zoneName);
}
(void) vfpClose(&pkgmapVfp);
/* BEGIN INSTALLATION PHASE */
if (in_dryrun_mode()) {
echo(MSG_PKGINSTALL_DRYRUN, pkgname, pkginst);
} else if (zoneName == (char *)NULL) {
echo(MSG_PKGINSTALL_INSIN_GZ, pkgname, pkginst);
} else {
echo(MSG_PKGINSTALL_INSIN_LZ, pkgname, pkginst, zoneName);
}
/* inform quit that the install has started */
quitSetInstallStarted(B_TRUE);
/*
* This replaces the contents file with recently created temp version
* which contains information about the objects being installed.
* Under old lock protocol it closes both files and releases the
* locks. Beginning in Solaris 2.7, this lock method should be
* reviewed.
*/
n = swapcfile(pkgserver, &cfTmpVfp, pkginst, dbchg);
if (n == RESULT_WRN) {
warnflag++;
} else if (n == RESULT_ERR) {
quit(99);
/*NOTREACHED*/
}
/*
* Create install-specific lockfile to indicate start of
* installation. This is really just an information file. If the
* process dies, the initial lockfile (from lockinst(), is
* relinquished by the kernel, but this one remains in support of the
* post-mortem.
*/
if (access(ilockfile, F_OK) == 0) {
(void) remove(ilockfile);
}
if (open(ilockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644) < 0) {
progerr(ERR_LOCKFILE, ilockfile);
quit(99);
/*NOTREACHED*/
}
(void) time(&clock);
/*
* We do not want the time in locale in the pkginfo.
* save the LC_TIME and set it to C. Reset it with saved one
* after cftime().
*/
temp = setlocale(LC_TIME, NULL);
(void) setlocale(LC_TIME, "C");
/* LINTED warning: do not use cftime(); ... */
(void) cftime(cbuf, "%b %d \045Y \045H:\045M", &clock);
putparam("INSTDATE", qstrdup(cbuf));
(void) setlocale(LC_TIME, temp);
/*
* Store information about package being installed;
* modify installation parameters as neccessary and
* copy contents of 'install' directory into $pkgloc
*/
merginfo(mergd_pclass, saveSpoolInstall);
/* If this was just a dryrun, then quit() will write out that file. */
if (in_dryrun_mode()) {
quit(0);
/*NOTREACHED*/
}
/*
* Execute preinstall script, if one was provided with the
* package. We check the package to avoid running an old
* preinstall script if one was provided with a prior instance.
* Don't execute preinstall script if we are only updating the DB.
*/
/* update the lock - at the preinstall altscript */
lockupd("preinstall");
/* preinstall script in the media (package source) */
(void) snprintf(altscript, sizeof (altscript), "%s/install/preinstall",
instdir);
/* preinstall script in the pkgbin instead of media */
(void) snprintf(script, sizeof (script), "%s/preinstall", pkgbin);
if (access(altscript, F_OK) != 0) {
/* no script present */
echoDebug(DBG_PKGINSTALL_POCALT_NONE, pkginst, altscript,
zoneName ? zoneName : "global");
} else if (access(script, F_OK) != 0) {
/* no script present */
echoDebug(DBG_PKGINSTALL_POC_NONE, pkginst, script,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
/* updating db only: skip preinstall script */
echoDebug(DBG_PKGINSTALL_POC_DBUPD, pkginst, script,
zoneName ? zoneName : "global");
} else {
/* script present and ok to run: run the script */
assert(preinstallCheck == B_FALSE);
set_ulimit("preinstall", ERR_PREINSTALL);
if (zoneName == (char *)NULL) {
echo(MSG_PKGINSTALL_EXEPOC_GZ);
echoDebug(DBG_PKGINSTALL_EXEPOC_GZ, pkginst, script);
} else {
echo(MSG_PKGINSTALL_EXEPOC_LZ, zoneName);
echoDebug(DBG_PKGINSTALL_EXEPOC_LZ, pkginst, script,
zoneName);
}
putparam("PKG_PROC_script", "preinstall");
if (pkgverbose) {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, "-x",
script, NULL), ERR_PREINSTALL);
} else {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, script,
NULL), ERR_PREINSTALL);
}
clr_ulimit();
(void) remove(script); /* no longer needed. */
}
/*
* Check delivered package for a postinstall script while
* we're still on volume 1.
*/
(void) snprintf(script, sizeof (script),
"%s/install/postinstall", instdir);
if (access(script, F_OK) == 0) {
(void) snprintf(script, sizeof (script),
"%s/postinstall", pkgbin);
} else {
script[0] = '\0';
}
/* update the lock - at the install phase */
lockupd("install");
/*
* install package one part (volume) at a time
*/
part = 1;
while (part <= nparts) {
if ((part > 1) && pkgdev.cdevice) {
unpack();
}
instvol(extlist, srcinst, part, nparts,
pkgserver, &cfTmpVfp, &updated, zoneName);
if (part++ >= nparts) {
break;
}
}
z_destroyMountTable();
/*
* Now that all install class action scripts have been used, we
* delete them from the package directory.
*/
rm_icas(pkgbin);
if (globalZoneOnly) {
boolean_t b;
b = pkgAddPackageToGzonlyList(pkginst, get_inst_root());
if (b == B_FALSE) {
progerr(ERR_PKGINSTALL_GZONLY_ADD, pkginst);
ckreturn(1, NULL);
}
}
/*
* Execute postinstall script, if any
* Don't execute postinstall script if we are only updating the DB.
*/
echoDebug(DBG_PKGINSTALL_INSDONE, is_depend_pkginfo_DB(),
is_depend_pkginfo_DB(), saveSpoolInstall,
updated ? updated : "",
script ? script : "",
script ? access(script, F_OK) : -1);
/* update the lock - at the postinstall script */
lockupd("postinstall");
if ((script == (char *)NULL) || (*script == '\0')) {
echoDebug(DBG_PKGINSTALL_POIS_NOPATH, pkginst,
zoneName ? zoneName : "global");
} else if (access(script, F_OK) != 0) {
echoDebug(DBG_PKGINSTALL_POIS_NONE, pkginst, script,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGINSTALL_POIS_DBUPD, pkginst, script,
zoneName ? zoneName : "global");
} else if ((saveSpoolInstall != 0) && (updated == (char *)NULL)) {
/*
* fresh installing into non-global zone, no object was
* updated (installed/verified in area), so do not run
* the postinstall script.
*/
echoDebug(DBG_PKGINSTALL_POIS_NOUPDATING,
zoneName ? zoneName : "global", pkginst, script);
} else {
/* script present and ok to run: run the script */
set_ulimit("postinstall", ERR_POSTINSTALL);
if (zoneName == (char *)NULL) {
echo(MSG_PKGINSTALL_EXEPIC_GZ);
echoDebug(DBG_PKGINSTALL_EXEPIC_GZ, pkginst, script);
} else {
echo(MSG_PKGINSTALL_EXEPIC_LZ, zoneName);
echoDebug(DBG_PKGINSTALL_EXEPIC_LZ, pkginst, script,
zoneName);
}
putparam("PKG_PROC_SCRIPT", "postinstall");
putparam("TMPDIR", tmpdir);
if (pkgverbose) {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, "-x",
script, NULL), ERR_POSTINSTALL);
} else {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, script,
NULL), ERR_POSTINSTALL);
}
clr_ulimit();
(void) remove(script); /* no longer needed */
}
if (!warnflag && !failflag) {
(void) remove(rlockfile);
(void) remove(ilockfile);
(void) remove(savlog);
}
/* release the generic package lock */
(void) unlockinst();
pkgcloseserver(pkgserver);
quit(0);
/* LINTED: no return */
}
/*
* This function merges the environment data in the response file with the
* current environment.
*/
static int
merg_respfile()
{
int retcode = 0;
char *resppath = get_respfile();
char *locbasedir;
char param[MAX_PKG_PARAM_LENGTH], *value;
FILE *fp;
if ((fp = fopen(resppath, "r")) == NULL) {
progerr(ERR_RESPONSE, resppath);
return (99);
}
param[0] = '\0';
while (value = fpkgparam(fp, param)) {
if (!isupper(param[0])) {
param[0] = '\0';
continue;
}
if (rdonly(param)) {
progerr(ERR_RDONLY, param);
param[0] = '\0';
continue;
}
/*
* If this is an update, and the response file
* specifies the BASEDIR, make sure it matches the
* existing installation base. If it doesn't, we have
* to quit.
*/
if (update && strcmp("BASEDIR", param) == 0) {
locbasedir = getenv("BASEDIR");
if (locbasedir && strcmp(value, locbasedir) != 0) {
char *dotptr;
/* Get srcinst down to a name. */
if (dotptr = strchr(srcinst, '.'))
*dotptr = '\000';
progerr(ERR_NEWBD, srcinst,
locbasedir, value);
retcode = 99;
}
}
putparam(param, value);
param[0] = '\0';
}
(void) fclose(fp);
return (retcode);
}
/*
* This scans the installed pkginfo file for the current BASEDIR. If this
* BASEDIR is different from the current BASEDIR, there will definitely be
* problems.
*/
static int
ck_instbase(void)
{
int retcode = 0;
char param[MAX_PKG_PARAM_LENGTH], *value;
char pkginfo_path[PATH_MAX];
FILE *fp;
/* Open the old pkginfo file. */
(void) snprintf(pkginfo_path, sizeof (pkginfo_path),
"%s/%s", pkgloc, PKGINFO);
if ((fp = fopen(pkginfo_path, "r")) == NULL) {
progerr(ERR_PKGINFO, pkginfo_path);
return (99);
}
param[0] = '\000';
while (value = fpkgparam(fp, param)) {
if (strcmp("BASEDIR", param) == 0) {
if (adm.basedir && *(adm.basedir) &&
strchr("/$", *(adm.basedir))) {
char *dotptr;
/*
* Get srcinst down to a name.
*/
if (dotptr = strchr(srcinst, '.'))
*dotptr = '\000';
if (strcmp(value,
adm.basedir) != 0) {
progerr(ERR_ADMBD, srcinst,
value, adm.basedir);
retcode = 4;
break;
}
} else if (ADM(basedir, "ask"))
/*
* If it's going to ask later, let it know
* that it *must* agree with the BASEDIR we
* just picked up.
*/
adm.basedir = "update";
putparam(param, value);
break;
}
param[0] = '\0';
}
(void) fclose(fp);
return (retcode);
}
/*
* Since this is an overwrite of a different version of the package, none of
* the old files should remain, so we rename them.
*/
static int
mv_pkgdirs(void)
{
/*
* If we're not in dryrun mode and we can find an old set of package
* files over which the new ones will be written, do the rename.
*/
if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
(void) snprintf(pkgloc_sav, sizeof (pkgloc_sav),
"%s/.save.%s", get_PKGLOC(),
pkginst);
if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
(void) rrmdir(pkgloc_sav);
}
if (rename(pkgloc, pkgloc_sav) == -1) {
progerr(ERR_PKGBINREN, pkgloc, pkgloc_sav);
return (99);
}
}
return (0);
}
/*
* Name: merg_pkginfos
* Description: This function scans the installed pkginfo and merges that
* environment with the installing environment according to
* the following rules:
*
* 1. CLASSES is a union of the installed and installing CLASSES
* lists.
* 2. The installed BASEDIR takes precedence. If it doesn't agree
* with an administratively imposed BASEDIR, an ERROR is issued.
* 3. All other installing parameters are preserved.
* 4. All installed parameters are added if they do not overwrite
* an existing installing parameter.
*
* The current environment contains the pkginfo settings for the
* new package to be installed or to be updated.
*
* Arguments: pclass - returned list of current classes involved in install
* mpclass - pointer to returned list of current install classes
* Returns: int
* == 0 - all OK
* != 0 - an error code if a fatal error occurred
*/
static int
merg_pkginfos(struct cl_attr **pclass, struct cl_attr ***mpclass)
{
FILE *fp;
char SUNW_PKG_ALLZONES[MAX_PKG_PARAM_LENGTH] = {'\0'};
char SUNW_PKG_HOLLOW[MAX_PKG_PARAM_LENGTH] = {'\0'};
char SUNW_PKG_THISZONE[MAX_PKG_PARAM_LENGTH] = {'\0'};
char *newValue;
char *oldValue;
char *pkgName;
char *pkgVersion;
char param[MAX_PKG_PARAM_LENGTH];
char pkginfo_path[PATH_MAX];
int retcode = 0;
/* obtain the name of the package (for error messages) */
pkgName = getenv("PKG");
if (pkgName == NULL) {
pkgName = "*current*"; /* default name */
}
/* obtain the version of the package (for error messages) */
pkgVersion = getenv("VERSION");
if (pkgVersion == NULL) {
pkgVersion = "*current*"; /* default version */
}
/* open installed package pkginfo file */
(void) snprintf(pkginfo_path, sizeof (pkginfo_path),
"%s/%s", pkgloc, PKGINFO);
if ((fp = fopen(pkginfo_path, "r")) == NULL) {
progerr(ERR_PKGINFO, pkginfo_path);
return (99);
}
/* entry debugging info */
echoDebug(DBG_MERGINFOS_ENTRY, pkginfo_path);
/*
* cycle through the currently installed package's pkginfo parameters
* and let the currently installed package's settings survive if the
* update to the package does not provide an overriding value
*/
for (param[0] = '\0'; (oldValue = fpkgparam(fp, param)) != NULL;
param[0] = '\0') {
boolean_t setZoneAttribute = B_FALSE;
/* debug info - attribute currently set to value */
echoDebug(DBG_MERGINFOS_SET_TO, param, oldValue);
/*
* if zone package attribute is present in the currently
* installed package, then remember the value for the
* specific zone package attribute, and set the flag that
* indicates a zone package attribute is being processed.
*/
if (strcmp(param, PKG_THISZONE_VARIABLE) == 0) {
/* SUNW_PKG_THISZONE currently set */
setZoneAttribute = B_TRUE;
(void) strlcpy(SUNW_PKG_THISZONE, oldValue,
sizeof (SUNW_PKG_THISZONE));
} else if (strcmp(param, PKG_ALLZONES_VARIABLE) == 0) {
/* SUNW_PKG_ALLZONES currently set */
setZoneAttribute = B_TRUE;
(void) strlcpy(SUNW_PKG_ALLZONES, oldValue,
sizeof (SUNW_PKG_ALLZONES));
} else if (strcmp(param, PKG_HOLLOW_VARIABLE) == 0) {
/* SUNW_PKG_THISZONE currently set */
setZoneAttribute = B_TRUE;
(void) strlcpy(SUNW_PKG_HOLLOW, oldValue,
sizeof (SUNW_PKG_HOLLOW));
}
/* handle CLASSES currently being set */
if (strcmp(param, "CLASSES") == 0) {
echoDebug(DBG_MERGINFOS_SET_CLASSES, oldValue);
/* create a list of the current classes */
(void) setlist(&pclass, qstrdup(oldValue));
/* set pointer to list of current classes */
*mpclass = pclass;
continue;
}
/* handle BASEDIR currently being set */
if (strcmp("BASEDIR", param) == 0) {
if (adm.basedir && *(adm.basedir) &&
strchr("/$", *(adm.basedir))) {
char *dotptr;
/* Get srcinst down to a* name */
if (dotptr = strchr(srcinst, '.')) {
*dotptr = '\000';
}
if (strcmp(oldValue, adm.basedir) != 0) {
progerr(ERR_ADMBD, srcinst,
oldValue, adm.basedir);
/* administration */
retcode = 4;
break;
}
} else if (ADM(basedir, "ask")) {
/*
* If it's going to ask
* later, let it know that it
* *must* agree with the
* BASEDIR we just picked up.
*/
adm.basedir = "update";
echoDebug(DBG_MERGINFOS_ASK_BASEDIR);
}
echoDebug(DBG_MERGINFOS_SET_BASEDIR, oldValue);
putparam(param, oldValue);
continue;
}
/*
* determine if there is a new value for this attribute.
*/
newValue = getenv(param);
/*
* If there is no new value, and a zone attribute
* is being changed, it is the same as setting the zone package
* attribute to 'false' - make sure current setting is 'false'.
*/
if ((newValue == NULL) &&
(setZoneAttribute == B_TRUE) &&
(strcasecmp(oldValue, "false") != 0)) {
/* unset existing non-"false" zone pkg attr */
progerr(ERR_MERGINFOS_UNSET_ZONEATTR,
pkgName, pkgVersion, param, oldValue);
retcode = 1;
break;
}
/* retain old value if no new value specified */
if (newValue == NULL) {
/* no new value - retain the old value */
echoDebug(DBG_MERGINFOS_RETAIN_OLD, param, oldValue);
putparam(param, oldValue);
continue;
}
/* note if the old and new values are the same */
if (strcmp(newValue, oldValue) == 0) {
/* set existing package parameter to same value */
echoDebug(DBG_MERGINFOS_SET_DUPLICATE, param, oldValue);
continue;
}
/*
* Check if old and new values differ.
* Error if zone parameter
*/
if (setZoneAttribute == B_TRUE) {
/* illegal change to zone attribute */
progerr(ERR_MERGINFOS_CHANGE_ZONEATTR, pkgName,
pkgVersion, param, oldValue, newValue);
/* set return code to "fatal error" */
retcode = 1;
break;
}
/* note valid change to existing package parameter */
echoDebug(DBG_MERGINFOS_SET_CHANGE, param,
oldValue, newValue);
}
/* close handle on currently installed package's pkginfo file */
(void) fclose(fp);
/* return error if not successful up to this point */
if (retcode != 0) {
echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, retcode);
return (retcode);
}
/*
* verify that no zone attribute has been
* set to an invalid value
*/
/* SUNW_PKG_ALLZONES */
newValue = getenv(PKG_ALLZONES_VARIABLE);
/*
* complain if setting SUNW_PKG_ALLZONES to other than "false"
*/
if ((newValue != NULL) && (*SUNW_PKG_ALLZONES == '\0') &&
(strcasecmp(newValue, "false") != 0)) {
/* change ALLZONES from "true" to "false" (unset) */
progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
pkgVersion, PKG_ALLZONES_VARIABLE, newValue);
return (1);
}
/* SUNW_PKG_THISZONE */
newValue = getenv(PKG_THISZONE_VARIABLE);
/*
* complain if setting SUNW_PKG_THISZONE to other than "false"
*/
if ((newValue != NULL) && (*SUNW_PKG_THISZONE == '\0') &&
(strcasecmp(newValue, "false") != 0)) {
/* change THISZONE from "true" to "false" (unset) */
progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
pkgVersion, PKG_THISZONE_VARIABLE, newValue);
return (1);
}
/* SUNW_PKG_HOLLOW */
newValue = getenv(PKG_HOLLOW_VARIABLE);
/* complain if setting SUNW_PKG_HOLLOW to other than "false" */
if ((newValue != NULL) && (*SUNW_PKG_HOLLOW == '\0') &&
(strcasecmp(newValue, "false") != 0)) {
/* change HOLLOW from "true" to 'false" (unset) */
progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
pkgVersion, PKG_HOLLOW_VARIABLE, newValue);
return (1);
}
echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, 0);
return (0);
}
static void
set_dryrun_dir_loc(void)
{
/* Set pkg location to the dryrun directory */
set_PKGLOC(pkgdrtarg);
(void) snprintf(pkgloc, sizeof (pkgloc),
"%s/%s", get_PKGLOC(), pkginst);
(void) snprintf(pkgbin, sizeof (pkgbin),
"%s/install", pkgloc);
(void) snprintf(pkgsav, sizeof (pkgsav),
"%s/save", pkgloc);
(void) snprintf(ilockfile, sizeof (ilockfile),
"%s/!I-Lock!", pkgloc);
(void) snprintf(rlockfile, sizeof (rlockfile),
"%s/!R-Lock!", pkgloc);
(void) snprintf(savlog, sizeof (savlog),
"%s/logs/%s", get_PKGADM(), pkginst);
}
/*
* If we are updating a pkg, then we need to copy the "old" pkgloc so that
* any scripts that got removed in the new version aren't left around. So we
* copy it here to .save.pkgloc, then in quit() we can restore our state, or
* remove it.
*/
static int
cp_pkgdirs(void)
{
if (in_dryrun_mode()) {
set_dryrun_dir_loc();
}
/*
* If we're not in dryrun mode and we can find an old set of package
* files over which the new ones will be written, do the copy.
*/
if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
int status;
int r;
(void) snprintf(pkgloc_sav, sizeof (pkgloc_sav), "%s/.save.%s",
get_PKGLOC(), pkginst);
/*
* Even though it takes a while, we use a recursive copy here
* because if the current pkgadd fails for any reason, we
* don't want to lose this data.
*/
r = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
"/usr/bin/cp", "cp", "-r", pkgloc, pkgloc_sav,
(char *)NULL);
if ((r != 0) || (status == -1) || (WEXITSTATUS(status) != 0)) {
progerr(ERR_PKGBINCP, pkgloc, pkgloc_sav);
return (99);
}
}
return (0);
}
/*
* This implements the pkgask function. It just executes the request script
* and stores the results in a response file.
*/
static void
do_pkgask(boolean_t a_run_request_as_root)
{
if (pkgdev.cdevice) {
unpack();
if (!suppressCopyright) {
copyright();
}
}
(void) snprintf(path, sizeof (path), "%s/%s", instdir, REQUEST_FILE);
if (access(path, F_OK)) {
progerr(ERR_NOREQUEST);
quit(1);
/*NOTREACHED*/
}
(void) set_respfile(respfile, srcinst, RESP_WR);
if (is_a_respfile()) {
ckreturn(reqexec(update, path, non_abi_scripts,
a_run_request_as_root), ERR_REQUEST);
} else {
failflag++;
}
if (warnflag || failflag) {
(void) remove(respfile);
echo("\nResponse file <%s> was not created.",
get_respfile());
} else {
echo("\nResponse file <%s> was created.",
get_respfile());
}
quit(0);
/*NOTREACHED*/
}
/*
* This function runs a check utility and acts appropriately based upon the
* return code. It deals appropriately with the dryrun file if it is present.
*/
static void
ck_w_dryrun(int (*func)(), int type)
{
int n;
n = func();
if (in_dryrun_mode())
set_dr_info(type, !n);
if (n) {
quit(n);
/*NOTREACHED*/
}
}
/*
* This function deletes all install class action scripts from the package
* directory on the root filesystem.
*/
static void
rm_icas(char *cas_dir)
{
DIR *pdirfp;
struct dirent *dp;
char path[PATH_MAX];
if ((pdirfp = opendir(cas_dir)) == NULL)
return;
while ((dp = readdir(pdirfp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
if (dp->d_name[0] == 'i' && dp->d_name[1] == '.') {
(void) snprintf(path, sizeof (path),
"%s/%s", cas_dir, dp->d_name);
(void) remove(path);
}
}
(void) closedir(pdirfp);
}
void
ckreturn(int retcode, char *msg)
{
switch (retcode) {
case 2:
case 12:
case 22:
warnflag++;
if (msg) {
progerr("%s", msg);
}
/*FALLTHRU*/
case 10:
case 20:
if (retcode >= 10 && retcode < 20) {
dreboot++;
}
if (retcode >= 20) {
ireboot++;
}
/*FALLTHRU*/
case 0:
break; /* okay */
case -1:
retcode = 99;
/*FALLTHRU*/
case 99:
case 1:
case 11:
case 21:
case 4:
case 14:
case 24:
case 5:
case 15:
case 25:
if (msg) {
progerr("%s", msg);
}
/*FALLTHRU*/
case 3:
case 13:
case 23:
quit(retcode);
/*NOTREACHED*/
default:
if (msg) {
progerr("%s", msg);
}
quit(1);
/*NOTREACHED*/
}
}
static void
copyright(void)
{
FILE *fp;
char line[LSIZE];
char path[PATH_MAX];
/* Compose full path for copyright file */
(void) snprintf(path, sizeof (path), "%s/%s", instdir, COPYRIGHT_FILE);
if ((fp = fopen(path, "r")) == NULL) {
if (getenv("VENDOR") != NULL)
echo(getenv("VENDOR"));
} else {
while (fgets(line, LSIZE, fp))
(void) fprintf(stdout, "%s", line); /* bug #1083713 */
(void) fclose(fp);
}
}
static int
rdonly(char *p)
{
int i;
for (i = 0; ro_params[i]; i++) {
if (strcmp(p, ro_params[i]) == 0)
return (1);
}
return (0);
}
static void
unpack(void)
{
/*
* read in next part from stream, even if we decide
* later that we don't need it
*/
if (dparts < 1) {
progerr(ERR_DSTREAMCNT);
quit(99);
/*NOTREACHED*/
}
if ((access(instdir, F_OK) == 0) && rrmdir(instdir)) {
progerr(ERR_RMDIR, instdir);
quit(99);
/*NOTREACHED*/
}
if (mkdir(instdir, 0755)) {
progerr(ERR_MKDIR, instdir);
quit(99);
/*NOTREACHED*/
}
if (chdir(instdir)) {
progerr(ERR_CHDIR, instdir);
quit(99);
/*NOTREACHED*/
}
if (!ds_fd_open()) {
dparts = ds_findpkg(pkgdev.cdevice, srcinst);
if (dparts < 1) {
progerr(ERR_DSARCH, srcinst);
quit(99);
/*NOTREACHED*/
}
}
dparts--;
if (ds_next(pkgdev.cdevice, instdir)) {
progerr(ERR_DSTREAM);
quit(99);
/*NOTREACHED*/
}
if (chdir(get_PKGADM())) {
progerr(ERR_CHDIR, get_PKGADM());
quit(99);
/*NOTREACHED*/
}
ds_close(1);
}
static void
usage(void)
{
(void) fprintf(stderr, ERR_USAGE_PKGINSTALL);
exit(1);
/*NOTREACHED*/
}