summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
authorMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
committerMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
commit5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch)
tree0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/cmd
parent2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff)
downloadillumos-joyent-5c51f1241dbbdf2656d0e10011981411ed0c9673.tar.gz
6739234 move SVR4 packaging to ONNV gate
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/Makefile.cmd21
-rw-r--r--usr/src/cmd/svr4pkg/Makefile64
-rw-r--r--usr/src/cmd/svr4pkg/Makefile.svr4pkg46
-rw-r--r--usr/src/cmd/svr4pkg/Makefile.svr4pkg.targ43
-rw-r--r--usr/src/cmd/svr4pkg/hdrs/dryrun.h69
-rw-r--r--usr/src/cmd/svr4pkg/hdrs/install.h144
-rw-r--r--usr/src/cmd/svr4pkg/hdrs/libadm.h319
-rw-r--r--usr/src/cmd/svr4pkg/hdrs/libinst.h595
-rw-r--r--usr/src/cmd/svr4pkg/hdrs/messages.h1214
-rw-r--r--usr/src/cmd/svr4pkg/installf/Makefile49
-rw-r--r--usr/src/cmd/svr4pkg/installf/dofinal.c236
-rw-r--r--usr/src/cmd/svr4pkg/installf/installf.c308
-rw-r--r--usr/src/cmd/svr4pkg/installf/installf.h57
-rw-r--r--usr/src/cmd/svr4pkg/installf/main.c545
-rw-r--r--usr/src/cmd/svr4pkg/installf/removef.c127
-rw-r--r--usr/src/cmd/svr4pkg/libinst/Makefile81
-rw-r--r--usr/src/cmd/svr4pkg/libinst/copyf.c512
-rw-r--r--usr/src/cmd/svr4pkg/libinst/cvtpath.c55
-rw-r--r--usr/src/cmd/svr4pkg/libinst/depchk.c349
-rw-r--r--usr/src/cmd/svr4pkg/libinst/dockdeps.c452
-rw-r--r--usr/src/cmd/svr4pkg/libinst/doulimit.c164
-rw-r--r--usr/src/cmd/svr4pkg/libinst/dryrun.c926
-rw-r--r--usr/src/cmd/svr4pkg/libinst/echo.c187
-rw-r--r--usr/src/cmd/svr4pkg/libinst/eptstat.c154
-rw-r--r--usr/src/cmd/svr4pkg/libinst/finalck.c205
-rw-r--r--usr/src/cmd/svr4pkg/libinst/findscripts.c197
-rw-r--r--usr/src/cmd/svr4pkg/libinst/fixpath.c983
-rw-r--r--usr/src/cmd/svr4pkg/libinst/flex_dev.c87
-rw-r--r--usr/src/cmd/svr4pkg/libinst/is_local_host.c151
-rw-r--r--usr/src/cmd/svr4pkg/libinst/isreloc.c243
-rw-r--r--usr/src/cmd/svr4pkg/libinst/listmgr.c564
-rw-r--r--usr/src/cmd/svr4pkg/libinst/lockinst.c279
-rw-r--r--usr/src/cmd/svr4pkg/libinst/log.c190
-rw-r--r--usr/src/cmd/svr4pkg/libinst/mntinfo.c1417
-rw-r--r--usr/src/cmd/svr4pkg/libinst/nblk.c84
-rw-r--r--usr/src/cmd/svr4pkg/libinst/ocfile.c864
-rw-r--r--usr/src/cmd/svr4pkg/libinst/open_package_datastream.c298
-rw-r--r--usr/src/cmd/svr4pkg/libinst/pathdup.c158
-rw-r--r--usr/src/cmd/svr4pkg/libinst/pkgdbmerg.c1259
-rw-r--r--usr/src/cmd/svr4pkg/libinst/pkgobjmap.c742
-rw-r--r--usr/src/cmd/svr4pkg/libinst/pkgops.c1426
-rw-r--r--usr/src/cmd/svr4pkg/libinst/procmap.c403
-rw-r--r--usr/src/cmd/svr4pkg/libinst/psvr4ck.c417
-rw-r--r--usr/src/cmd/svr4pkg/libinst/ptext.c51
-rw-r--r--usr/src/cmd/svr4pkg/libinst/putparam.c311
-rw-r--r--usr/src/cmd/svr4pkg/libinst/qreason.c428
-rw-r--r--usr/src/cmd/svr4pkg/libinst/qstrdup.c57
-rw-r--r--usr/src/cmd/svr4pkg/libinst/scriptvfy.l786
-rw-r--r--usr/src/cmd/svr4pkg/libinst/setadmin.c336
-rw-r--r--usr/src/cmd/svr4pkg/libinst/setlist.c437
-rw-r--r--usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.c111
-rw-r--r--usr/src/cmd/svr4pkg/libinst/sml.c3327
-rw-r--r--usr/src/cmd/svr4pkg/libinst/srcpath.c69
-rw-r--r--usr/src/cmd/svr4pkg/libinst/stub.c46
-rw-r--r--usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.c158
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/Makefile51
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/check.c1029
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/main.c4712
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/msgdefs.h141
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/presvr4.c172
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/quit.c409
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/quit.h64
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/Makefile45
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/addcert.c573
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/certs.c239
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/listcert.c245
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/lock.c2150
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/main.c247
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/pkgadm.h84
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/pkgadm_msgs.h625
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/removecert.c201
-rw-r--r--usr/src/cmd/svr4pkg/pkgchk/Makefile45
-rw-r--r--usr/src/cmd/svr4pkg/pkgchk/checkmap.c425
-rw-r--r--usr/src/cmd/svr4pkg/pkgchk/ckentry.c314
-rw-r--r--usr/src/cmd/svr4pkg/pkgchk/main.c622
-rw-r--r--usr/src/cmd/svr4pkg/pkgcond/Makefile41
-rw-r--r--usr/src/cmd/svr4pkg/pkgcond/main.c4468
-rw-r--r--usr/src/cmd/svr4pkg/pkgcond/pkgcond.h65
-rw-r--r--usr/src/cmd/svr4pkg/pkgcond/pkgcond_msgs.h498
-rw-r--r--usr/src/cmd/svr4pkg/pkginfo/Makefile42
-rw-r--r--usr/src/cmd/svr4pkg/pkginfo/pkginfo.c821
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/Makefile55
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/backup.c60
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/check.c1097
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/cppath.c376
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/dockspace.c405
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/getinst.c298
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/instvol.c1781
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/main.c2988
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/merginfo.c621
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/msgdefs.h101
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/pkgenv.c126
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/pkginstall.h107
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/pkgvolume.c89
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/predepend.c89
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/quit.c521
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/reqexec.c392
-rw-r--r--usr/src/cmd/svr4pkg/pkginstall/sortmap.c170
-rw-r--r--usr/src/cmd/svr4pkg/pkgmk/Makefile45
-rw-r--r--usr/src/cmd/svr4pkg/pkgmk/main.c985
-rw-r--r--usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.c827
-rw-r--r--usr/src/cmd/svr4pkg/pkgmk/quit.c74
-rw-r--r--usr/src/cmd/svr4pkg/pkgmk/splpkgmap.c633
-rw-r--r--usr/src/cmd/svr4pkg/pkgname/Makefile40
-rw-r--r--usr/src/cmd/svr4pkg/pkgname/pkgname.c51
-rw-r--r--usr/src/cmd/svr4pkg/pkgparam/Makefile41
-rw-r--r--usr/src/cmd/svr4pkg/pkgparam/pkgparam.c205
-rw-r--r--usr/src/cmd/svr4pkg/pkgproto/Makefile41
-rw-r--r--usr/src/cmd/svr4pkg/pkgproto/main.c512
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/Makefile48
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/check.c328
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/delmap.c159
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/main.c1434
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/predepend.c72
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/quit.c321
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/special.c710
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.c472
-rw-r--r--usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.h45
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/Makefile44
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/check.c622
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/main.c3075
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/msgdefs.h91
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/presvr4.c184
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/quit.c340
-rw-r--r--usr/src/cmd/svr4pkg/pkgrm/quit.h65
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/Makefile54
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/cmdexec.c155
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/default41
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio.sh538
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/i.awk.sh70
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/i.build.sh81
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/i.sed.sh70
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/r.awk.sh60
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/r.build.sh71
-rw-r--r--usr/src/cmd/svr4pkg/pkgscripts/r.sed.sh60
-rw-r--r--usr/src/cmd/svr4pkg/pkgtrans/Makefile41
-rw-r--r--usr/src/cmd/svr4pkg/pkgtrans/main.c250
138 files changed, 63360 insertions, 2 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 92bde29d42..143d2c1e83 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -389,6 +389,7 @@ COMMON_SUBDIRS= \
sulogin \
sunpc \
svc \
+ svr4pkg \
swap \
sync \
sysdef \
@@ -703,6 +704,7 @@ MSGSUBDIRS= \
strings \
su \
svc \
+ svr4pkg \
swap \
syseventadm \
syseventd \
diff --git a/usr/src/cmd/Makefile.cmd b/usr/src/cmd/Makefile.cmd
index 8abf748eab..e2a2540997 100644
--- a/usr/src/cmd/Makefile.cmd
+++ b/usr/src/cmd/Makefile.cmd
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Definitions common to command source.
@@ -56,6 +56,10 @@ ROOTLIBXEN= $(ROOT)/usr/lib/xen/bin
ROOTLIBZONES= $(ROOT)/lib/zones
ROOTSHLIB= $(ROOT)/usr/share/lib
+ROOTPKGBIN= $(ROOT)/usr/sadm/install/bin
+ROOTCLASS_SCR_DIR= $(ROOT)/usr/sadm/install/scripts
+ROOTADMIN_SRC_DIR= $(ROOT)/var/sadm/install/admin
+
ROOTSHLIBCCS= $(ROOTSHLIB)/ccs
ROOTSBIN= $(ROOT)/sbin
ROOTUSRSBIN= $(ROOT)/usr/sbin
@@ -94,8 +98,8 @@ ROOTMAN1M= $(ROOTMAN)/man1m
ROOTMAN3= $(ROOTMAN)/man3
ROOTVARSMB= $(ROOT)/var/smb
-#
+#
# Like ROOTLIBDIR in $(SRC)/Makefile.lib, any lower-level Makefiles that
# put their binaries in a non-standard location should reset this and use
# $(ROOTCMD) in their `install' target. By default we set this to a bogus
@@ -134,6 +138,8 @@ ROOTLIBPROG= $(PROG:%=$(ROOTLIB)/%)
ROOTLIBSHFILES= $(SHFILES:%=$(ROOTLIB)/%)
ROOTSHLIBPROG= $(PROG:%=$(ROOTSHLIB)/%)
ROOTSBINPROG= $(PROG:%=$(ROOTSBIN)/%)
+ROOTPKGBINPROG= $(PROG:%=$(ROOTPKGBIN)/%)
+ROOTCLASS_SCR_FILES= $(SCRIPTS:%=$(ROOTCLASS_SCR_DIR)/%)
ROOTUSRSBINPROG=$(PROG:%=$(ROOTUSRSBIN)/%)
ROOTUSRSBINSCRIPT=$(SCRIPT:%=$(ROOTUSRSBIN)/%)
ROOTETCPROG= $(PROG:%=$(ROOTETC)/%)
@@ -197,6 +203,8 @@ $(ROOTETCZONESFILES) := FILEMODE = 0444
ROOTLIBZONESFILES= $(LIBZONESFILES:%=$(ROOTLIBZONES)/%)
$(ROOTLIBZONESFILES) := FILEMODE = 0555
+ROOTADMIN_SRC_FILE= $(ADMINFILE:%=$(ROOTADMIN_SRC_DIR)/%)
+$(ROOTADMIN_SRC_FILE) := FILEMODE = 0444
#
# Directories for smf(5) service manifests and profiles.
@@ -302,6 +310,15 @@ $(ROOTBIN32)/%: %
$(ROOTSHLIB)/%: %
$(INS.file)
+$(ROOTPKGBIN)/%: %
+ $(INS.file)
+
+$(ROOTCLASS_SCR_DIR)/%: %
+ $(INS.file)
+
+$(ROOTADMIN_SRC_DIR)/%: %
+ $(INS.file)
+
$(ROOTSBIN)/%: %
$(INS.file)
diff --git a/usr/src/cmd/svr4pkg/Makefile b/usr/src/cmd/svr4pkg/Makefile
new file mode 100644
index 0000000000..5281ca8d30
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/Makefile
@@ -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.
+#
+
+CMDSUBDIRS= \
+ installf \
+ pkgadd \
+ pkgadm \
+ pkgchk \
+ pkgcond \
+ pkginfo \
+ pkginstall \
+ pkgmk \
+ pkgname \
+ pkgparam \
+ pkgproto \
+ pkgremove \
+ pkgrm \
+ pkgscripts \
+ pkgtrans
+
+.PARALLEL= $(CMDSUBDIRS)
+
+LIBSUBDIR= libinst
+SUBDIRS= $(CMDSUBDIRS) $(LIBSUBDIR)
+
+all:= TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber:= TARGET= clobber
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all clean clobber install _msg: $(SUBDIRS)
+
+$(CMDSUBDIRS): $(LIBSUBDIR)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/svr4pkg/Makefile.svr4pkg b/usr/src/cmd/svr4pkg/Makefile.svr4pkg
new file mode 100644
index 0000000000..f7d3462665
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/Makefile.svr4pkg
@@ -0,0 +1,46 @@
+#
+# 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 $(SRC)/cmd/Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRC)/cmd/svr4pkg/hdrs \
+ -I$(SRC)/lib/libpkg/common \
+ -I$(SRC)/lib/libinstzones/common \
+ -D_FILE_OFFSET_BITS=64
+
+
+LIBINST = $(SRC)/cmd/svr4pkg/libinst/libinst.a
+
+SRCS= $(OBJS:.o=.c)
+
+
+#
+# For messaging catalog
+#
+POFILE= $(PROG).po
+MSGFILES= $(OBJS:.o=.i)
+CLOBBERFILES += $(PROG) $(POFILE)
diff --git a/usr/src/cmd/svr4pkg/Makefile.svr4pkg.targ b/usr/src/cmd/svr4pkg/Makefile.svr4pkg.targ
new file mode 100644
index 0000000000..9fd3c53dfe
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/Makefile.svr4pkg.targ
@@ -0,0 +1,43 @@
+#
+# 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): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(LIBINST)
+ $(POST_PROCESS)
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+clean:
+ $(RM) $(OBJS) $(MSGFILES)
+
+clobber: clean
+ -$(RM) $(CLOBBERFILES)
+
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/svr4pkg/hdrs/dryrun.h b/usr/src/cmd/svr4pkg/hdrs/dryrun.h
new file mode 100644
index 0000000000..f15e41ed98
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/hdrs/dryrun.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+
+#ifndef __DRYRUN_H__
+#define __DRYRUN_H__
+
+#include "cfext.h"
+
+/* The various types of status entry in the info file. */
+#define PARTIAL 1
+#define RUNLEVEL 2
+#define PKGFILES 3
+#define DEPEND 4
+#define SPACE 5
+#define CONFLICT 6
+#define SETUID 7
+#define PRIV 8
+#define PKGDIRS 9
+#define REQUESTEXITCODE 10
+#define CHECKEXITCODE 11
+#define EXITCODE 12
+#define DR_TYPE 13
+
+#define INSTALL_TYPE 1
+#define REMOVE_TYPE 0
+
+#if defined(__STDC__)
+#define __P(protos) protos
+#else /* __STDC__ */
+#define __P(protos) ()
+#endif /* __STDC__ */
+
+extern void set_dryrun_mode __P((void));
+extern int in_dryrun_mode __P((void));
+extern void set_continue_mode __P((void));
+extern int in_continue_mode __P((void));
+extern void init_contfile __P((char *cn_dir));
+extern void init_dryrunfile __P((char *dr_dir));
+extern void set_dr_info __P((int type, int value));
+extern int cmd_ln_respfile __P((void));
+extern int is_a_respfile __P((void));
+extern void write_dryrun_file __P((struct cfextra **extlist));
+extern boolean_t read_continuation __P((int *error));
+
+#endif /* __DRYRUN_H__ */
diff --git a/usr/src/cmd/svr4pkg/hdrs/install.h b/usr/src/cmd/svr4pkg/hdrs/install.h
new file mode 100644
index 0000000000..829b08dc2a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/hdrs/install.h
@@ -0,0 +1,144 @@
+/*
+ * 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 2004 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 __INSTALL_H
+#define __INSTALL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <limits.h>
+#include <pkgstrct.h>
+
+/* Settings for procedure scripts */
+#define PROC_USER "root"
+#define PROC_GRP "other"
+#define PROC_STDIN "/dev/null"
+#define PROC_XSTDIN "/dev/tty"
+#define PROC_STDOUT "/dev/tty"
+
+/* Settings for class action scripts */
+#define CAS_USER "root"
+#define CAS_GRP "other"
+#define CAS_STDIN "/dev/null"
+#define CAS_STDOUT "/dev/tty"
+
+/* Settings for non-privileged scripts */
+#define CHK_USER "install" /* default user i.d. to use */
+#define CHK_USER_ALT "noaccess" /* alternate non-priv user */
+#define CHK_USER_ROOT "root" /* root user */
+#define CHK_USER_NON "root" /* user for non-compliant pkg's */
+#define CHK_GRP "other"
+#define CHK_STDIN "/dev/null"
+#define CHK_STDOUT "/dev/tty"
+
+/* Settings for admin "rscriptalt" option */
+#define RSCRIPTALT rscriptalt
+#define RSCRIPTALT_KEYWORD "rscriptalt"
+#define RSCRIPTALT_ROOT "root"
+#define RSCRIPTALT_NOACCESS "noaccess"
+
+#define OAMBASE "/usr/sadm/sysadm"
+#define MAILCMD "/usr/bin/mail"
+#define DATSTRM "datastream"
+#define SHELL "/sbin/sh"
+#define PKGINFO "pkginfo"
+#define PKGMAP "pkgmap"
+#define LIVE_CONT "__live_cont__"
+#define RELOC "reloc"
+#define ROOT "root"
+
+/* Additional cfent/cfextra codes. */
+#define BADFSYS (short)(-1) /* an fsys is needed */
+#define BADINDEX (-1) /* pkg class idx not yet set */
+
+/* This holds admin file data. */
+struct admin {
+ char *mail;
+ char *instance;
+ char *partial;
+ char *runlevel;
+ char *idepend;
+ char *rdepend;
+ char *space;
+ char *setuid;
+ char *conflict;
+ char *action;
+ char *networktimeout;
+ char *networkretries;
+ char *authentication;
+ char *keystore;
+ char *proxy;
+ char *basedir;
+ char *rscriptalt;
+};
+
+/*
+ * This table details the status of all filesystems available to the target
+ * host.
+ */
+struct fstable {
+ char *name; /* name of filesystem, (mount point) */
+ int namlen; /* The length of the name (mountpoint) */
+ fsblkcnt_t bsize; /* fundamental file system block size */
+ fsblkcnt_t frsize; /* file system fragment size */
+ fsblkcnt_t bfree; /* total # of free blocks */
+ fsblkcnt_t bused; /* total # of used blocks */
+ fsblkcnt_t ffree; /* total # of free file nodes */
+ fsblkcnt_t fused; /* total # of used file nodes */
+ char *fstype; /* type of filesystem - nfs, lo, ... */
+ char *remote_name; /* client's mounted filesystem */
+ unsigned writeable:1; /* access permission */
+ unsigned write_tested:1; /* access permission fully tested */
+ unsigned remote:1; /* on a remote filesystem */
+ unsigned mounted:1; /* actually mounted right now */
+ unsigned srvr_map:1; /* use server_map() */
+ unsigned cl_mounted:1; /* mounted in client space */
+ unsigned mnt_failed:1; /* attempt to loopback mount failed */
+ unsigned served:1; /* filesystem comes from a server */
+};
+
+#define ADM(x, y) ((adm.x != NULL) && (y != NULL) && \
+ strcmp(adm.x, y) == 0)
+#define ADMSET(x) (adm.x != NULL)
+#define PARAMETRIC(x) (x[0] == '$')
+#define RELATIVE(x) (x[0] != '/')
+
+#if defined(lint) && !defined(gettext)
+#define gettext(x) x
+#endif /* defined(lint) && !defined(gettext) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INSTALL_H */
diff --git a/usr/src/cmd/svr4pkg/hdrs/libadm.h b/usr/src/cmd/svr4pkg/hdrs/libadm.h
new file mode 100644
index 0000000000..503bd6b931
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/hdrs/libadm.h
@@ -0,0 +1,319 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef __PKG_LIBADM_H__
+#define __PKG_LIBADM_H__
+
+#include <sys/types.h>
+#include <sys/vtoc.h>
+#include <limits.h>
+#include <stdio.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <valtools.h>
+#include "install.h"
+
+#if defined(__STDC__)
+#define __P(protos) protos
+#else /* __STDC__ */
+#define __P(protos) ()
+#endif /* __STDC__ */
+
+
+/*
+ * ckdate.c
+ */
+extern int ckdate_err __P((char *fmt, char *error));
+extern int ckdate_hlp __P((char *fmt, char *help));
+extern int ckdate_val __P((char *fmt, char *input));
+extern int ckdate __P((char *date, char *fmt, char *defstr, char *error,
+ char *help, char *prompt));
+
+/*
+ * ckgid.c
+ */
+extern int ckgid_dsp __P((void));
+extern int ckgid_val __P((char *grpnm));
+extern int ckgrpfile __P((void));
+extern void ckgid_err __P((int disp, char *error));
+extern void ckgid_hlp __P((int disp, char *help));
+extern int ckgid __P((char *gid, short disp, char *defstr, char *error,
+ char *help, char *prompt));
+
+/*
+ * ckint.c
+ */
+extern int ckint_val __P((char *value, short base));
+extern void ckint_err __P((short base, char *error));
+extern void ckint_hlp __P((short base, char *help));
+extern int ckint __P((long *intval, short base, char *defstr, char *error,
+ char *help, char *prompt));
+
+/*
+ * ckitem.c
+ */
+extern CKMENU *allocmenu __P((char *label, int attr));
+extern void ckitem_err __P((CKMENU *menup, char *error));
+extern void ckitem_hlp __P((CKMENU *menup, char *help));
+extern int ckitem __P((CKMENU *menup, char *item[], short max,
+ char *defstr, char *error, char *help,
+ char *prompt));
+extern int setitem __P((CKMENU *menup, char *choice));
+extern int setinvis __P((CKMENU *menup, char *choice));
+extern void printmenu __P((CKMENU *menup));
+
+/*
+ * ckkeywd.c
+ */
+extern int ckkeywd __P((char *strval, char *keyword[], char *defstr,
+ char *error, char *help, char *prompt));
+
+/*
+ * ckpath.c
+ */
+extern int ckpath_stx __P((int pflags));
+extern int ckpath_val __P((char *path, int pflags));
+extern void ckpath_err __P((int pflags, char *error, char *input));
+extern void ckpath_hlp __P((int pflags, char *help));
+extern int ckpath __P((char *pathval, int pflags, char *defstr,
+ char *error, char *help, char *prompt));
+
+/*
+ * ckrange.c
+ */
+extern void ckrange_err __P((long lower, long upper, int base,
+ char *error));
+extern void ckrange_hlp __P((long lower, long upper, int base, char *help));
+extern int ckrange_val __P((long lower, long upper, int base,
+ char *input));
+extern int ckrange __P((long *rngval, long lower, long upper, short base,
+ char *defstr, char *error, char *help,
+ char *prompt));
+
+/*
+ * ckstr.c
+ */
+extern int ckstr_val __P((char *regexp[], int length, char *input));
+extern void ckstr_err __P((char *regexp[], int length, char *error,
+ char *input));
+extern void ckstr_hlp __P((char *regexp[], int length, char *help));
+extern int ckstr __P((char *strval, char *regexp[], int length,
+ char *defstr, char *error, char *help,
+ char *prompt));
+
+/*
+ * cktime.c
+ */
+extern int cktime_val __P((char *fmt, char *input));
+extern int cktime_err __P((char *fmt, char *error));
+extern int cktime_hlp __P((char *fmt, char *help));
+extern int fmtcheck __P((char *fmt));
+extern int cktime __P((char *tod, char *fmt, char *defstr, char *error,
+ char *help, char *prompt));
+
+/*
+ * ckuid.c
+ */
+extern int ckuid_dsp __P((void));
+extern int ckuid_val __P((char *usrnm));
+extern int ckpwdfile __P((void));
+extern void ckuid_err __P((short disp, char *error));
+extern void ckuid_hlp __P((int disp, char *help));
+extern int ckuid __P((char *uid, short disp, char *defstr, char *error,
+ char *help, char *prompt));
+
+/*
+ * ckyorn.c
+ */
+extern int ckyorn_val __P((char *str));
+extern void ckyorn_err __P((char *error));
+extern void ckyorn_hlp __P((char *help));
+extern int ckyorn __P((char *yorn, char *defstr, char *error, char *help,
+ char *prompt));
+
+/*
+ * devattr.c
+ */
+extern char *devattr __P((char *device, char *attribute));
+
+/*
+ * devreserv.c
+ */
+extern char *_rsvtabpath __P((void));
+extern int _openlkfile __P((void));
+extern int _closelkfile __P((void));
+extern int unreserv __P((long key, char *device));
+extern char **devreserv __P((long key, char **rsvlst[]));
+extern int devfree __P((long key, char *device));
+extern struct reservdev **reservdev __P((void));
+
+/*
+ * devtab.c
+ */
+extern void _setdevtab __P((void));
+extern void _enddevtab __P((void));
+extern char *_devtabpath __P((void));
+extern int _opendevtab __P((char *mode));
+extern int _validalias __P((char *alias));
+extern struct devtabent *_getdevtabent __P((void));
+extern void _freedevtabent __P((struct devtabent *ent));
+extern struct devtabent *_getdevrec __P((char *device));
+
+/*
+ * dgrpent.c
+ */
+extern void _setdgrptab __P((void));
+extern void _enddgrptab __P((void));
+extern char *_dgrptabpath __P((void));
+extern int _opendgrptab __P((char *mode));
+extern struct dgrptabent *_getdgrptabent __P((void));
+extern void _freedgrptabent __P((struct dgrptabent *ent));
+extern struct dgrptabent *_getdgrprec __P((char *dgroup));
+
+/*
+ * fulldevnm.c
+ */
+extern char *getfullblkname __P((char *cp));
+extern char *getfullrawname __P((char *cp));
+
+/*
+ * getdev.c
+ */
+extern char **getdev __P((char **devices, char **criteria, int options));
+
+/*
+ * getdgrp.c
+ */
+extern char **getdgrp __P((char **dgroups, char **criteria, int options));
+
+/*
+ * getinput.c
+ */
+extern int getinput __P((char *s));
+
+/*
+ * getvol.c
+ */
+extern int getvol __P((char *device, char *label, int options,
+ char *prompt));
+extern int _getvol __P((char *device, char *label, int options,
+ char *prompt, char *norewind));
+extern void doremovecmd __P((char *device, int echo));
+
+/*
+ * listdev.c
+ */
+extern char **listdev __P((char *device));
+
+/*
+ * listdgrp.c
+ */
+extern char **listdgrp __P((char *dgroup));
+
+/*
+ * memory.c
+ */
+extern long sysmem __P((void));
+extern long asysmem __P((void));
+
+/*
+ * pkginfo.c
+ */
+extern int pkginfo __P((struct pkginfo *info, char *pkginst, ...));
+extern int fpkginfo __P((struct pkginfo *info, char *pkginst));
+extern char *fpkginst __P((char *pkg, ...));
+
+/*
+ * pkgnmchk.c
+ */
+extern int pkgnmchk __P((register char *pkg, register char *spec,
+ int presvr4flg));
+extern void set_ABI_namelngth __P((void));
+extern int get_ABI_namelngth __P((void));
+
+/*
+ * pkgparam.c
+ */
+extern char *fpkgparam __P((FILE *fp, char *param));
+extern char *pkgparam __P((char *pkg, char *param));
+extern void set_PKGpaths __P((char *path));
+extern char *get_PKGLOC __P((void));
+extern char *get_PKGOLD __P((void));
+extern char *get_PKGADM __P((void));
+extern void set_PKGADM(char *newpath);
+extern void set_PKGLOC(char *newpath);
+
+/*
+ * putdev.c
+ */
+extern int _putdevtabrec __P((FILE *stream, struct devtabent *rec));
+extern int _adddevtabrec __P((char *alias, char **attrval));
+extern int _moddevtabrec __P((char *device, char **attrval));
+extern int _rmdevtabrec __P((char *device));
+extern int _rmdevtabattrs __P((char *device, char **attributes,
+ char ***notfounds));
+
+/*
+ * putdgrp.c
+ */
+extern int _putdgrptabrec __P((FILE *stream, struct dgrptabent *rec));
+extern int _adddgrptabrec __P((char *dgrp, char **members));
+extern int _rmdgrptabrec __P((char *dgrp));
+extern int _rmdgrpmems __P((char *dgrp, char **mems, char ***notfounds));
+
+/*
+ * puterror.c
+ */
+extern void puterror __P((FILE *fp, char *defmesg, char *error));
+
+/*
+ * puthelp.c
+ */
+extern void puthelp __P((FILE *fp, char *defmesg, char *help));
+
+/*
+ * putprmpt.c
+ */
+extern void putprmpt __P((FILE *fp, char *prompt, char *choices[],
+ char *defstr));
+
+/*
+ * puttext.c
+ */
+extern int puttext __P((FILE *fp, char *str, int lmarg, int rmarg));
+
+/*
+ * rdwr_vtoc.c
+ */
+extern int read_vtoc __P((int fd, struct vtoc *vtoc));
+extern int write_vtoc __P((int fd, struct vtoc *vtoc));
+
+#if defined(lint) && !defined(gettext)
+#define gettext(x) x
+#endif /* defined(lint) && !defined(gettext) */
+
+#endif /* __PKG_LIBADM_H__ */
diff --git a/usr/src/cmd/svr4pkg/hdrs/libinst.h b/usr/src/cmd/svr4pkg/hdrs/libinst.h
new file mode 100644
index 0000000000..fda6b1b3c6
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/hdrs/libinst.h
@@ -0,0 +1,595 @@
+/*
+ * 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.
+ */
+
+#ifndef __HDRS_LIBINST_H__
+#define __HDRS_LIBINST_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include "pkglib.h"
+#include <cfext.h>
+#include "install.h"
+
+#define DEF_NONE_SCR "i.CompCpio"
+
+#define BL_ALL -1 /* refers to all allocated lists */
+
+/* signal handler function definition */
+
+typedef void (sighdlrFunc_t)(int);
+
+/* maximum parameter length */
+
+#define MAX_PKG_PARAM_LENGTH (64+1) /* +1 for null termination */
+
+/* flag for check_applicability */
+
+typedef unsigned long CAF_T;
+
+/* flags for check_applicability */
+
+#define CAF_IN_GLOBAL_ZONE 0x00000001 /* in global zone */
+#define CAF_SCOPE_GLOBAL 0x00000002 /* -G specified */
+#define CAF_SCOPE_NONGLOBAL 0x00000004 /* -Z specified */
+
+/* path to the request file in the package directory */
+
+#define REQUEST_FILE "install/request"
+
+/* path to the copyright file in the package directory */
+
+#define COPYRIGHT_FILE "install/copyright"
+
+/* path to the depend file in the package directory */
+
+#define DEPEND_FILE "install/depend"
+
+/*
+ * name of environment variable set to non-global zone name being installed:
+ * pkgadd/pkginstall expects this name and passes it on to any scripts that
+ * are run if it is set.
+ */
+
+#define PKG_ZONENAME_VARIABLE "SUNW_PKG_INSTALL_ZONENAME"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * in the current zone only - see PSARC/2004/789 - New Pkginfo(4) attributes
+ * for zones
+ */
+
+#define PKG_THISZONE_VARIABLE "SUNW_PKG_THISZONE"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * in all zones, and only from the global zone - see PSARC/2003/460
+ */
+
+#define PKG_ALLZONES_VARIABLE "SUNW_PKG_ALLZONES"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * hollow (db update only) when installed in nonglobal zone - see PSARC/2003/460
+ */
+
+#define PKG_HOLLOW_VARIABLE "SUNW_PKG_HOLLOW"
+
+/*
+ * General purpose return codes used for functions which don't return a basic
+ * success or failure. For those functions wherein a yes/no result is
+ * possible, then 1 means OK and 0 means FAIL.
+ */
+#define RESULT_OK 0x0
+#define RESULT_WRN 0x1
+#define RESULT_ERR 0x2
+
+/* These are the file status indicators for the contents file */
+#define INST_RDY '+' /* entry is ready to installf -f */
+#define RM_RDY '-' /* entry is ready for removef -f */
+#define NOT_FND '!' /* entry (or part of entry) was not found */
+#define SERVED_FILE '%' /* using the file server's RO partition */
+#define STAT_NEXT '@' /* this is awaiting eptstat */
+#define DUP_ENTRY '#' /* there's a duplicate of this */
+#define CONFIRM_CONT '*' /* need to confirm contents */
+#define CONFIRM_ATTR '~' /* need to confirm attributes */
+#define ENTRY_OK '\0' /* entry is a confirmed file */
+
+/* control bits for pkgdbmerg() */
+#define NO_COPY 0x0001
+#define CLIENT_PATHS 0x0002 /* working with a client database */
+
+/* control bits for file verification by class */
+#define DEFAULT 0x0 /* standard full verification */
+#define NOVERIFY 0x1 /* do not verify */
+#define QKVERIFY 0x2 /* do a quick verification instead */
+
+/* control bit for path type to pass to CAS */
+#define DEFAULT 0x0 /* standard server-relative absolute path */
+#define REL_2_CAS 0x1 /* pass pkgmap-type relative path */
+
+/* findscripts() argument */
+#define I_ONLY 0x0 /* find install class action scripts */
+#define R_ONLY 0x1 /* find removal class action scripts */
+
+struct cl_attr {
+ char name[CLSSIZ+1]; /* name of class */
+ char *inst_script; /* install class action script */
+ char *rem_script; /* remove class action script */
+ unsigned src_verify:3; /* source verification level */
+ unsigned dst_verify:4; /* destination verification level */
+ unsigned relpath_2_CAS:1; /* CAS gets relative paths */
+};
+
+#if defined(__STDC__)
+#define __P(protos) protos
+#else /* __STDC__ */
+#define __P(protos) ()
+#endif /* __STDC__ */
+
+/* Common quit declaration used across many package commands */
+extern void quit(int) __NORETURN;
+
+
+/* listmgr.c */
+extern int bl_create __P((int count_per_block, int struct_size,
+ char *desc));
+extern char *bl_next_avail __P((int list_handle));
+extern char *bl_get_record __P((int list_handle, int recno));
+extern void bl_free __P((int list_handle));
+extern int ar_create __P((int count_per_block, int struct_size,
+ char *desc));
+extern char **ar_next_avail __P((int list_handle));
+extern char **ar_get_head __P((int list_handle));
+extern int ar_delete __P((int list_handle, int index));
+extern void ar_free __P((int list_handle));
+
+/* doulimit.c */
+extern int set_ulimit __P((char *script, char *err_msg));
+extern int clr_ulimit __P((void));
+extern int assign_ulimit __P((char *fslimit));
+
+/* dryrun.c */
+extern void set_continue_not_ok __P((void));
+extern int continue_is_ok __P((void));
+extern int in_dryrun_mode __P((void));
+extern int in_continue_mode __P((void));
+extern void init_dryrunfile __P((char *dr_dir));
+extern void init_contfile __P((char *cn_dir));
+extern void set_dr_exitmsg __P((char *value));
+extern void set_dr_info __P((int type, int value));
+extern void write_dryrun_file __P((struct cfextra **extlist));
+
+/* instvol.c */
+extern void regfiles_free __P((void));
+
+/* lockinst.c */
+extern int lockinst __P((char *util_name, char *pkg_name, char *place));
+extern void lockupd __P((char *place));
+extern void unlockinst __P((void));
+
+extern char *pathdup __P((char *s));
+extern char *pathalloc __P((int n));
+extern char *fixpath __P((char *path));
+extern char *get_info_basedir __P((void));
+extern char *get_basedir __P((void));
+extern char *get_client_basedir __P((void));
+extern int set_basedirs __P((int reloc, char *adm_basedir,
+ char *pkginst, int nointeract));
+extern int eval_path __P((char **server_ptr, char **client_ptr,
+ char **map_ptr, char *path));
+extern int get_orig_offset __P((void));
+extern char *get_inst_root __P((void));
+extern char *get_mount_point __P((short n));
+extern char *get_remote_path __P((short n));
+extern void set_env_cbdir __P((void));
+extern int set_inst_root __P((char *path));
+extern void put_path_params __P((void));
+extern int mkpath __P((char *p));
+extern void mkbasedir __P((int flag, char *path));
+extern int is_an_inst_root __P((void));
+extern int is_a_basedir __P((void));
+extern int is_a_cl_basedir __P((void));
+extern int is_relocatable __P((void));
+extern char *orig_path __P((char *path));
+extern char *orig_path_ptr __P((char *path));
+extern char *qreason __P((int caller, int retcode, int started,
+ int includeZonename));
+extern char *qstrdup __P((char *s));
+extern char *srcpath __P((char *d, char *p, int part, int nparts));
+extern int copyf __P((char *from, char *to, time_t mytime));
+extern int copyFile __P((int, int, char *, char *, struct stat *, long));
+extern int openLocal __P((char *a_path, int a_oflag, char *a_tmpdir));
+extern int dockdeps __P((char *depfile, int removeFlag,
+ boolean_t a_preinstallCheck));
+extern int finalck __P((struct cfent *ept, int attrchg, int contchg,
+ boolean_t a_warning));
+
+/* dockdeps.c */
+extern void setUpdate __P((void));
+extern int isUpdate __P((void));
+extern void setPatchUpdate __P((void));
+extern int isPatchUpdate __P((void));
+
+/* mntinfo.c */
+extern int get_mntinfo __P((int map_client, char *vfstab_file));
+extern short fsys __P((char *path));
+extern struct fstable *get_fs_entry __P((short n));
+extern int mount_client __P((void));
+extern int unmount_client __P((void));
+extern short resolved_fsys __P((char *path));
+extern char *get_server_host __P((short n));
+extern char *server_map __P((char *path, short fsys_value));
+extern int use_srvr_map __P((char *path, short *fsys_value));
+extern int use_srvr_map_n __P((short n));
+extern int is_fs_writeable __P((char *path, short *fsys_value));
+extern int is_remote_fs __P((char *path, short *fsys_value));
+extern int is_served __P((char *path, short *fsys_value));
+extern int is_mounted __P((char *path, short *fsys_value));
+extern int is_fs_writeable_n __P((short n));
+extern int is_remote_fs_n __P((short n));
+extern int is_served_n __P((short n));
+extern int is_mounted_n __P((short n));
+extern fsblkcnt_t get_blk_size_n __P((short n));
+extern fsblkcnt_t get_frag_size_n __P((short n));
+extern fsblkcnt_t get_blk_used_n __P((short n));
+extern fsblkcnt_t get_blk_free_n __P((short n));
+extern fsblkcnt_t get_inode_used_n __P((short n));
+extern fsblkcnt_t get_inode_free_n __P((short n));
+extern void set_blk_used_n __P((short n, fsblkcnt_t value));
+extern char *get_source_name_n __P((short n));
+extern char *get_fs_name_n __P((short n));
+extern int load_fsentry __P((struct fstable *fs_entry, char *name,
+ char *fstype, char *remote_name));
+extern int isreloc __P((char *pkginstdir));
+extern int is_local_host __P((char *hostname));
+extern void fs_tab_free __P((void));
+
+/* pkgdbmerg.c */
+extern int pkgdbmerg __P((VFP_T *mapvfp, VFP_T *tmpvfp,
+ struct cfextra **extlist, int notify));
+extern int files_installed __P((void));
+extern void notice __P((int n));
+
+/* ocfile.c */
+extern int trunc_tcfile __P((int fd));
+extern int ocfile __P((VFP_T **mapvfp, VFP_T **tmpvfp,
+ fsblkcnt_t map_blks));
+extern int swapcfile __P((VFP_T **a_mapvfp, VFP_T **a_tmpvfp,
+ char *pkginst, int dbchg));
+extern int set_cfdir __P((char *cfdir));
+extern int socfile __P((VFP_T **vfp));
+extern int relslock __P((void));
+extern int iscfile __P((void));
+extern int vcfile __P((void));
+
+extern fsblkcnt_t nblk __P((fsblkcnt_t size, ulong_t bsize,
+ ulong_t frsize));
+extern struct cfent **procmap __P((VFP_T *vfp, int mapflag, char *ir));
+extern void repl_cfent __P((struct cfent *new, struct cfent *old));
+extern struct cfextra **pkgobjmap __P((VFP_T *vfp, int mapflag, char *ir));
+extern void pkgobjinit __P((void));
+extern int seed_pkgobjmap __P((struct cfextra *ext_entry, char *path,
+ char *local));
+extern int init_pkgobjspace __P((void));
+
+/* eptstat.c */
+extern void pinfo_free __P((void));
+extern struct pinfo *eptstat __P((struct cfent *entry, char *pkg, char c));
+
+/* echo.c */
+/*PRINTFLIKE1*/
+extern void echo __P((char *a_fmt, ...));
+/*PRINTFLIKE1*/
+extern void echoDebug __P((char *a_fmt, ...));
+extern boolean_t echoGetFlag __P((void));
+extern boolean_t echoDebugGetFlag __P((void));
+extern boolean_t echoSetFlag __P((boolean_t a_debugFlag));
+extern boolean_t echoDebugSetFlag __P((boolean_t a_debugFlag));
+
+/* psvr4ck.c */
+extern void psvr4cnflct __P((void));
+extern void psvr4mail __P((char *list, char *msg, int retcode, char *pkg));
+extern void psvr4pkg __P((char **ppkg));
+
+/* ptext.c */
+/*PRINTFLIKE2*/
+extern void ptext __P((FILE *fp, char *fmt, ...));
+
+/* putparam.c */
+extern void putparam __P((char *param, char *value));
+extern void getuserlocale __P((void));
+extern void putuserlocale __P((void));
+extern void putConditionInfo __P((char *, char *));
+
+/* setadmin.c */
+extern void setadminFile __P((char *file));
+extern char *setadminSetting __P((char *a_paramName,
+ char *a_paramValue));
+extern char *set_keystore_admin __P((void));
+extern boolean_t get_proxy_port_admin __P((char **, ushort_t *));
+extern boolean_t check_keystore_admin __P((char **));
+extern int web_ck_retries __P((void));
+extern int web_ck_timeout __P((void));
+extern int web_ck_authentication __P((void));
+
+/* setlist.c */
+extern char *cl_iscript __P((int idx));
+extern char *cl_rscript __P((int idx));
+extern void find_CAS __P((int CAS_type, char *bin_ptr, char *inst_ptr));
+extern int setlist __P((struct cl_attr ***plist, char *slist));
+extern void addlist __P((struct cl_attr ***plist, char *item));
+extern char *cl_nam __P((int cl_idx));
+extern char *flex_device(char *device_name, int dev_ok);
+extern int cl_getn __P((void));
+extern int cl_idx __P((char *cl_nam));
+extern void cl_sets __P((char *slist));
+extern void cl_setl __P((struct cl_attr **cl_lst));
+extern void cl_putl __P((char *parm_name, struct cl_attr **list));
+extern int cl_deliscript __P((int i));
+extern unsigned cl_svfy __P((int i));
+extern unsigned cl_dvfy __P((int i));
+extern unsigned cl_pthrel __P((int i));
+
+/* passwd.c */
+extern int pkg_passphrase_cb __P((char *, int, int, void *));
+extern void set_passarg __P((char *));
+extern void set_prompt __P((char *));
+
+/* fixpath.c */
+extern void __P(export_client_env(char *));
+extern void __P(set_partial_inst(void));
+extern int __P(is_partial_inst(void));
+extern void __P(set_depend_pkginfo_DB(boolean_t a_setting));
+extern boolean_t __P(is_depend_pkginfo_DB(void));
+extern void __P(disable_spool_create(void));
+extern int __P(is_spool_create(void));
+
+/* open_package_datastream.c */
+extern boolean_t open_package_datastream(int a_argc, char **a_argv,
+ char *a_spoolto, char *a_device,
+ int *r_repeat, char **r_idsName,
+ char *a_tmpdir, struct pkgdev *a_pkgdev,
+ int a_optind);
+
+/* setup_temporary_directory.c */
+extern boolean_t setup_temporary_directory(char **r_dirname,
+ char *a_tmpdir, char *a_suffix);
+
+/* unpack_package_from_stream.c */
+extern boolean_t unpack_package_from_stream(char *a_idsName,
+ char *a_pkginst, char *a_tempDir);
+
+/* pkgops.c */
+
+extern boolean_t pkgAddPackageToGzonlyList(char *a_pkgInst,
+ char *a_rootPath);
+extern void pkgAddThisZonePackage(char *a_pkgInst);
+extern boolean_t pkgRemovePackageFromGzonlyList(char *a_rootPath,
+ char *a_pkgInst);
+extern FILE *pkgOpenInGzOnlyFile(char *a_rootPath);
+extern void pkginfoFree(struct pkginfo **r_info);
+extern boolean_t pkginfoIsPkgInstalled(struct pkginfo **r_pinfo,
+ char *a_pkgInst);
+extern boolean_t pkgIsPkgInGzOnly(char *a_rootPath, char *a_pkgInst);
+extern boolean_t pkgIsPkgInGzOnlyFP(FILE *a_fp, char *a_pkgInst);
+extern boolean_t pkginfoParamTruth(FILE *a_fp, char *a_param,
+ char *a_value, boolean_t a_default);
+extern int pkgGetPackageList(char ***r_pkgList, char **a_argv,
+ int a_optind, char *a_categories,
+ char **a_categoryList, struct pkgdev *a_pkgdev);
+extern void pkgLocateHighestInst(char *r_path, int r_pathLen,
+ char *r_pkgInst, int r_pkgInstLen,
+ char *a_rootPath, char *a_pkgInst);
+extern boolean_t pkgPackageIsThisZone(char *a_pkgInst);
+extern boolean_t pkgMatchInherited(char *a_src, char *a_dst,
+ char *a_rootDir, char a_mode, time_t a_modtime,
+ char a_ftype, unsigned long a_cksum);
+extern char *pkgGetGzOnlyPath(void);
+extern boolean_t pkgTestInstalled(char *a_packageName, char *a_rootPath);
+
+/* depchk.c */
+
+struct depckErrorRecord {
+ int ier_numZones;
+ char *ier_packageName;
+ char **ier_zones;
+ char **ier_values;
+};
+
+typedef struct depckErrorRecord depckErrorRecord_t;
+
+struct depckError {
+ int er_numEntries;
+ depckErrorRecord_t *er_theEntries;
+};
+
+typedef struct depckError depckError_t;
+
+typedef int (depcklFunc_t)(char *a_msg, char *a_pkg);
+
+/*
+ * ignore_values:
+ * == NULL - record one message for each instance of "name" found
+ * == "" - record multiple instances
+ * != "" - record multiple instances if value not in ignore_values
+ */
+
+struct depckl_struct {
+ char *name;
+ char *ignore_values;
+ char **err_msg;
+ depcklFunc_t *depcklFunc;
+ depckError_t *record;
+};
+
+typedef struct depckl_struct depckl_t;
+
+extern int depchkReportErrors(depckl_t *depckl);
+extern void depchkRecordError(depckError_t *a_erc,
+ char *a_pkginst, char *a_zoneName,
+ char *a_value);
+
+/* log.c */
+
+/* types of log messages we recognize */
+typedef enum {
+ LOG_MSG_ERR,
+ LOG_MSG_WRN,
+ LOG_MSG_INFO,
+ LOG_MSG_DEBUG
+} LogMsgType;
+
+/*PRINTFLIKE2*/
+extern void log_msg(LogMsgType, const char *, ...);
+extern void log_set_verbose(boolean_t);
+extern boolean_t log_get_verbose(void);
+
+/*
+ * typedef for the 'ckreturn' function
+ */
+typedef void (ckreturnFunc_t)(int a_retcode);
+
+/* sml.c */
+
+/* null reference to SML_TAG object */
+
+#define SML_TAG__NULL ((SML_TAG*)NULL)
+
+/* null reference to SML_TAG * object */
+
+#define SML_TAG__R_NULL ((SML_TAG**)NULL)
+
+/* is reference to SML_TAG object valid? */
+
+#define SML_TAG__ISVALID(tag) ((tag) != (SML_TAG__NULL))
+
+/* is indirect reference to SML_TAG object valid? */
+
+#define SML_TAG__R_ISVALID(r_tag) \
+ ((r_tag) != ((SML_TAG**)(SML_TAG__NULL)))
+
+/* definitions for sml passed from pkginstall to pkgcond */
+
+#define PKGCOND_GLOBAL_VARIABLE "SUNW_PKGCOND_GLOBAL_DATA"
+#define TAG_COND_TOPLEVEL "environmentConditionInformation"
+#define TAG_COND_PARENT_ZONE "parentZone"
+#define TAG_COND_CURRENT_ZONE "currentZone"
+#define TAG_COND_ZONE_NAME "zoneName"
+#define TAG_COND_ZONE_TYPE "zoneType"
+#define TAG_COND_INHERITED_FS "inheritedFileSystem"
+#define TAG_COND_FS_NAME "fileSystemName"
+#define TAG_VALUE_GLOBAL_ZONE "global"
+#define TAG_VALUE_NONGLOBAL_ZONE "nonglobal"
+
+typedef struct _sml_tag_struct SML_TAG;
+typedef struct _sml_parameter_struct SML_PARAM;
+
+struct _sml_tag_struct {
+ char *name; /* tag name */
+ int params_num; /* # params in *params */
+ SML_PARAM *params; /* tag parameters */
+ int tags_num; /* # subtags in *tags */
+ SML_TAG *tags; /* tag subtags */
+};
+
+struct _sml_parameter_struct {
+ char *name; /* tag name */
+ char *value; /* parameters */
+};
+
+SML_TAG *smlAddTag(SML_TAG **r_tag, int a_index,
+ SML_TAG *a_subTag);
+boolean_t smlFstatCompareEq(struct stat *statbuf,
+ SML_TAG *tag, char *path);
+char *smlConvertTagToString(SML_TAG *tag);
+/*PRINTFLIKE2*/
+void smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...);
+void smlDelTag(SML_TAG *tag, SML_TAG *sub_tag);
+void smlDelParam(SML_TAG *tag, char *name);
+SML_TAG *smlDup(SML_TAG *tag);
+boolean_t smlFindAndDelTag(SML_TAG *tag, char *findTag);
+void smlFreeTag(SML_TAG *tag);
+char *smlGetElementName(SML_TAG *a_tag);
+int smlGetNumParams(SML_TAG *a_tag);
+char *smlGetParam(SML_TAG *tag, char *name);
+/*PRINTFLIKE2*/
+char *smlGetParamF(SML_TAG *tag, char *format, ...);
+void smlGetParam_r(SML_TAG *tag, char *name, char *buf,
+ int bufLen);
+char *smlGetParamByTag(SML_TAG *tag, int index,
+ char *tagName, char *parmName);
+char *smlGetParamByTagParam(SML_TAG *tag, int index,
+ char *tagName, char *parmName, char *parmValue,
+ char *parmReturn);
+char *smlGetParamName(SML_TAG *tag, int index);
+SML_TAG *smlGetTag(SML_TAG *tag, int index);
+SML_TAG *smlGetTagByName(SML_TAG *tag, int index, char *name);
+SML_TAG *smlGetTagByTagParam(SML_TAG *tag, int index,
+ char *tagName, char *paramName, char *paramValue);
+boolean_t smlGetVerbose(void);
+int smlLoadTagFromFile(SML_TAG **r_tag, char *a_fileName);
+SML_TAG *smlNewTag(char *name);
+boolean_t smlParamEq(SML_TAG *tag, char *findTag,
+ char *findParam, char *str);
+/*PRINTFLIKE4*/
+boolean_t smlParamEqF(SML_TAG *tag, char *findTag, char *findParam,
+ char *format, ...);
+void smlPrintTag(SML_TAG *tag);
+int smlReadOneTag(SML_TAG **r_tag, char *a_str);
+int smlConvertStringToTag(SML_TAG **r_tag, char *str);
+void smlSetFileStatInfo(SML_TAG **tag,
+ struct stat *statbuf, char *path);
+void smlSetParam(SML_TAG *tag, char *name, char *value);
+/*PRINTFLIKE3*/
+void smlSetParamF(SML_TAG *tag, char *name, char *format, ...);
+void smlSetVerbose(boolean_t a_setting);
+int smlWriteTagToFd(SML_TAG *tag, int fd);
+int smlWriteTagToFile(SML_TAG *tag, char *filename);
+/*PRINTFLIKE3*/
+void sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...);
+/*PRINTFLIKE1*/
+char *sml_strPrintf(char *a_format, ...);
+char *sml_XmlEncodeString(char *a_plainTextString);
+char *sml_XmlDecodeString(char *a_xmlEncodedString);
+
+#if defined(lint) && !defined(gettext)
+#define gettext(x) x
+#endif /* defined(lint) && !defined(gettext) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HDRS_LIBINST_H__ */
diff --git a/usr/src/cmd/svr4pkg/hdrs/messages.h b/usr/src/cmd/svr4pkg/hdrs/messages.h
new file mode 100644
index 0000000000..9f546313ef
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/hdrs/messages.h
@@ -0,0 +1,1214 @@
+/*
+ * 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.
+ */
+
+#ifndef _MESSAGES_H
+#define _MESSAGES_H
+
+
+/*
+ * Module: messages
+ * Group: pkg commands
+ * Description: l10n strings for all pkg commands
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MSG_MAX 1024
+#define MAXARGS 100
+#define MAX_CAT_ARGS 64
+
+/* BEGIN CSTYLED */
+
+/*
+ * I18N: these messages are questions asked of the user
+ */
+
+#define ASK_CONFIRM gettext("Do you want to remove this package?")
+#define ASK_CONT gettext("Do you want to continue with the installation of <%s>")
+#define ASK_CONTINUE_ADD gettext("Do you want to continue with package installation?")
+#define ASK_CONTINUE_RM gettext("Do you want to continue with package removal?")
+#define ASK_PKGREMOVE_CONTINUE gettext("Do you want to continue with the removal of this package")
+#define ASK_PKGRMCHK_CONT gettext("Do you want to continue with the removal of <%s>")
+
+/*
+ * I18N: these messages are debugging message and are only displayed
+ * when special debugging output has been enabled - these messages
+ * will never be displayed during normal product usage
+ */
+
+#define DBG_ADDPACKAGES_ARGS gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> altBinDir <%s> device <%s>")
+#define DBG_ADDPACKAGES_ENTRY gettext("add_packages:")
+#define DBG_ADDPACKAGES_GZ_NO_LZ_ARGS gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define DBG_ADDPACKAGES_GZ_NO_LZ_ENTRY gettext("add_pkgs_in_gz_no_zones: adding packages in global zone with NO non-global zones")
+#define DBG_ADDPACKAGES_GZ_W_LZ_ARGS gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define DBG_ADDPACKAGES_GZ_W_LZ_ENTRY gettext("add_pkgs_in_gz_with_zones: adding packages in global zone with non-global zones present")
+#define DBG_ADDPACKAGES_LZ_ARGS gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define DBG_ADDPACKAGES_LZ_ENTRY gettext("add_pkgs_in_lz: adding packages in non-global zone")
+#define DBG_ARG gettext("argument <%d> = <%s>")
+#define DBG_BOOTCHECKINSTALLINZONES_ARGS gettext("ids <%s> admin <%s> tempdir <%s>")
+#define DBG_BOOTCHECKINSTALLINZONES_ENTRY gettext("boot_and_check_install_in_zones:")
+#define DBG_BOOTING_ZONE gettext("booting up non-running zone <%s>")
+#define DBG_BOOTINSTALLINZONES_ARGS gettext("ids <%s> admin <%s> tempdir <%s>")
+#define DBG_BOOTINSTALLINZONES_ENTRY gettext("boot_and_install_in_zones:")
+#define DBG_BRANDS_ARE_IMPLEMENTED gettext("brands are implemented")
+#define DBG_BRANDS_NOT_IMPLEMENTED gettext("brands are NOT implemented")
+#define DBG_CANNOT_GET_PKGLIST gettext("unable to get package list")
+#define DBG_CHECKAPP_ARGS gettext("package <%s> directory <%s> rootpath <%s>")
+#define DBG_CHECKAPP_ENTRY gettext("check install applicability:")
+#define DBG_CHECKAPP_THISZONE_INSTREQ gettext("WARNING: the package <%s> to be installed does not contain a request script, but the currently installed instance (package <%s>) does contain a request script, so the package to be installed can only be installed in the current zone, and will not be installed in any future zones created.")
+#define DBG_CHECKAPP_THISZONE_REQUEST gettext("WARNING: package <%s> contains a request script, and can only be installed in the current zone, and will not be installed in any future zones created.")
+#define DBG_CHECKINSTALL_IN_ZONE gettext("checking install of package <%s> in zone <%s> from stream <%s>")
+#define DBG_CHECKREMOVE_PKG_IN_ZONE gettext("verifying package <%s> dependencies in zone <%s>")
+#define DBG_CLOSING_STREAM gettext("closing datastream <%s> at <%s>")
+#define DBG_CONVERTING_PKG gettext("converting package <%s/%s> to stream <%s>")
+#define DBG_COPY_FILE gettext("copy <%s> to <%s>")
+#define DBG_CPPATH_ENTRY gettext("copy path: control <0x%02x> mode <0%04lo> source <%s> destination <%s>")
+#define DBG_CREATED_ZONE_ADMINFILE gettext("created temporary zone administration file <%s>")
+#define DBG_CREATED_ZONE_TEMPDIR gettext("created temporary zone directory <%s>")
+#define DBG_CREATE_ZONE_ADMINFILE gettext("create temporary zone administration file in directory <%s> template <%s>")
+#define DBG_CREATE_ZONE_TEMPDIR gettext("create temporary zone directory in temporary directory <%s>")
+#define DBG_DEPCHK_COLLECT_ERROR gettext("dependency report error: ign <null> ret <%d> package <%s> msg <%s>")
+#define DBG_DEPCHK_COLLECT_IGNORE gettext("dependency report error: ign <null> no check function package <%s> msg <%s>")
+#define DBG_DEPCHK_ENTRY gettext("depchkReportErrors:")
+#define DBG_DEPCHK_IGNORE_ERROR gettext("dependency report error: ign <%s> no check function package <%s> msg <%s>")
+#define DBG_DEPCHK_RECORD_ERROR gettext("dependency record error: erc <0x%08lx> add first package <%s> zone <%s> value <%s>")
+#define DBG_DEPCHK_RECORD_PERROR gettext("dependency record error: erc <0x%08lx> add package <%d> <%s> zone <%s> value <%s>")
+#define DBG_DEPCHK_RECORD_ZERROR gettext("dependency record error: erc <0x%08lx> add zone <%s> value <%s> to existing package <%s> # <%d> zones[0] <%s>")
+#define DBG_DEPCHK_REPORT_ERROR gettext("dependency report error: ign <%s> ret <%d> package <%s> msg <%s>")
+#define DBG_DOMERG_NOT_THERE gettext("object does not exist or has incorrect contents: type <%c> class <%s> path <%s>")
+#define DBG_DOMERG_NOT_WRITABLE gettext("object not writable or cannot be created: type <%c> class <%s> path <%s>")
+#define DBG_DOMERG_NO_SUCH_FILE gettext("file does not exist or has incorrect contents: type <%c> class <%s> path <%s>")
+#define DBG_DOREMOVE_ARGS gettext("found package <%s> name <%s> arch <%s> version <%s> basedir <%s> catg <%s> status <%d>\n")
+#define DBG_DOREMOVE_ENTRY gettext("doremove:")
+#define DBG_DOREMOVE_INTERRUPTED gettext("interrupted: package <%s> not installed")
+#define DBG_DO_EXEC_REQUEST_USER gettext("running request script <%s> output <%s> as user <%s> i.d. <%ld> group <%s> i.d. <%ld>")
+#define DBG_ENTRY_IN_GZ gettext("[<%s> in global zone]")
+#define DBG_ENTRY_IN_LZ gettext("[<%s> in non-global zone <%ld>:<%s>]")
+#define DBG_EXIT_WITH_CODE gettext("exiting with code <%d>")
+#define DBG_FINALCK_ERROR gettext("final check (error): attrchg <%d> contchg <%d> ftype <%c> path <%s>")
+#define DBG_FINALCK_ERROR_AVERIFY gettext("final check (error): attribute verification = <%d>")
+#define DBG_FINALCK_ERROR_CVERIFY gettext("final check (error): content verification = <%d>")
+#define DBG_FINALCK_EXIT gettext("final check (return): error <%d> type <%c> path <%s>")
+#define DBG_FINALCK_WARNING gettext("final check (warning): attrchg <%d> contchg <%d> ftype <%c> path <%s>")
+#define DBG_FINALCK_WARNING_AVERIFY gettext("final check (warning): attribute verification = <%d>")
+#define DBG_FINALCK_WARNING_CVERIFY gettext("final check (warning): content verification = <%d>")
+#define DBG_GETPKGLIST_ARGS gettext("stream device <%s> directory <%s> repeat <%d>")
+#define DBG_GETPKGLIST_ENTRY gettext("get_package_list:")
+#define DBG_INSTALLING_TO_SPOOL gettext("installing packages to spool directory <%s>")
+#define DBG_INSTALLINZONES_ARGS gettext("ids <%s> admin <%s> tempdir <%s>")
+#define DBG_INSTALLINZONES_ENTRY gettext("install_in_zones:")
+#define DBG_INSTALL_FLAG_VALUES gettext("%s: admnflag <%d> doreboot <%d> failflag <%d> interrupted <%d> intrflag <%d> ireboot <%d> needconsult <%d> nullflag <%d> warnflag <%d>")
+#define DBG_INSTALL_IN_ZONE gettext("installing package <%s> in zone <%s> from stream <%s>")
+#define DBG_INSTALL_SKIP_THISZONE gettext("skipping installation of package <%s>: marked this zone only")
+#define DBG_INSTINONEZONE_ARGS gettext("zone <%s> ids <%s> admin <%s> tempdir <%s> altbindir <%s>")
+#define DBG_INSTINONEZONE_ENTRY gettext("install_in_one_zone:")
+#define DBG_INSTVOL_CAS_INFO gettext("is partial <%d> updated <%s> skipped <%s> local <%s>\n")
+#define DBG_INSTVOL_NOT_RUNNING_CAS gettext("not running zone <%s> object <%s> class <%s> action script <%s>")
+#define DBG_INSTVOL_OBJ_LOCAL gettext("objects local <%s>")
+#define DBG_INSTVOL_OBJ_SKIPPED gettext("objects skipped <%s>")
+#define DBG_INSTVOL_OBJ_UPDATED gettext("objects updated <%s>")
+#define DBG_INSTVOL_PARTIAL_INST gettext("partial install check: pkgMatchInherited(srcp <%s>, dstp <%s>, get_inst_root() <%s>, ept->ainfo.mode <0%04lo>, ept->cinfo.modtime <%lx>, ept->ftype <%c>, ept->cinfo.cksum <%lx>) = %d")
+#define DBG_INSTVOL_RUNNING_CAS gettext("running zone <%s> object <%s> class <%s> action script <%s>")
+#define DBG_IN_GZ_NO_LZ gettext("running in global zone with NO non-global zones")
+#define DBG_IN_GZ_WITH_LZ gettext("running in global zone with non-global zones")
+#define DBG_IN_LZ gettext("running in non-global zone")
+#define DBG_MERGINFOS_ASK_BASEDIR gettext("merg_pkginfos: ask for BASEDIR change later")
+#define DBG_MERGINFOS_ENTRY gettext("merg_pkginfos: installed pkginfo <%s>")
+#define DBG_MERGINFOS_EXIT gettext("merg_pkginfos: done changing <%s> result <%d>")
+#define DBG_MERGINFOS_SET_BASEDIR gettext("merg_pkginfos: set BASEDIR to <%s>")
+#define DBG_MERGINFOS_SET_CHANGE gettext("merg_pkginfos: change existing attribute <%s> from <%s> to <%s>")
+#define DBG_MERGINFOS_SET_CLASSES gettext("merg_pkginfos: set CLASSES to <%s>")
+#define DBG_MERGINFOS_SET_DUPLICATE gettext("merg_pkginfos: set existing attribute <%s> to current value <%s>")
+#define DBG_MERGINFOS_RETAIN_OLD gettext("merg_pkginfos: retain existing attribute <%s> value <%s>")
+#define DBG_MERGINFOS_SET_TO gettext("merg_pkginfos: validate change attribute <%s> from <%s>")
+#define DBG_MERGINFO_ATTRCOMP gettext("merginfo: attribute <%s> currently set to <%s>")
+#define DBG_MERGINFO_DIFFERENT gettext("merginfo: pkginfo file source <%s> different than merged <%s>: open source pkginfo file")
+#define DBG_MERGINFO_ENTRY gettext("merginfo: instdir <%s> get_inst_root() <%s> saveSpoolInstallDir <%s> pkgloc <%s> is_spool_create <%d> get_info_basedir() <%s> installed pkginfo <%s> merged pkginfo <%s>")
+#define DBG_MERGINFO_EXCLUDING gettext("merginfo: excluding attribute <%s>")
+#define DBG_MERGINFO_FINAL gettext("merginfo: accepting attribute <%s>")
+#define DBG_MERGINFO_GREATER_THAN gettext("merginfo: attribute <%s> greater than last entry <%s>")
+#define DBG_MERGINFO_LESS_THAN gettext("merginfo: attribute <%s> less than first entry <%s>")
+#define DBG_MERGINFO_SAME gettext("merginfo: pkginfo file source and merged <%s> identical: no source pkginfo file used")
+#define DBG_MERGINFO_SEARCHING gettext("merginfo: attribute <%s> within range of <%s> and <%s>: searching")
+#define DBG_NUM_PKGS_TO_ADD gettext("number of packages to add <%d>")
+#define DBG_NUM_PKGS_TO_REMOVE gettext("number of packages to remove <%d> longest package name length <%d>")
+#define DBG_ODS_ARGS gettext("bdevice <%s> cdevice <%s> pathname <%s> argc <%d> spool-device <%s>")
+#define DBG_ODS_DATASTREAM_BDEV gettext("package source is block device <%s>")
+#define DBG_ODS_DATASTREAM_CDEV gettext("package source is character device <%s>")
+#define DBG_ODS_DATASTREAM_INIT gettext("initializing package datastream <%s>")
+#define DBG_ODS_DATASTREAM_ISFILE gettext("package source is ordinary file <%s>")
+#define DBG_ODS_DATASTREAM_MOUNTING gettext("mounting package datastream device <%s> on <%s>")
+#define DBG_ODS_DATASTREAM_UNK gettext("package source not contained in a recognized datastream")
+#define DBG_ODS_ENTRY gettext("open_package_datastream:")
+#define DBG_PKGADD_ADMINFILE gettext("using admin file <%s>")
+#define DBG_PKGADD_CKRETURN gettext("check return code <%d> package <%s> function <add packages>")
+#define DBG_PKGADD_ENABLING_HOLLOW gettext("enabling hollow package support")
+#define DBG_PKGADD_HOLLOW_ENABLED gettext("hollow package support is enabled")
+#define DBG_PKGADD_PKGPATHS gettext("locations set: pkg <%s> adm <%s>")
+#define DBG_PKGADD_RESPFILE gettext("using response file <%s> directory <%s>")
+#define DBG_PKGADD_TMPDIR gettext("using temporary directory <%s>")
+#define DBG_PKGDBMRG_INHERITED gettext("path inherited and assumed correct: <%s>")
+#define DBG_PKGINSTALL_ADMINFILE gettext("using admin file <%s>")
+#define DBG_PKGINSTALL_ARGS gettext("package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> pkgdir <%s>")
+#define DBG_PKGINSTALL_COC_DBUPD gettext("skipping checkinstall package <%s> script <%s> zone <%s> (db update only)")
+#define DBG_PKGINSTALL_COC_NODEL gettext("skipping checkinstall package <%s> script <%s> zone <%s> (nodelete)")
+#define DBG_PKGINSTALL_COC_NONE gettext("no checkinstall in package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_DS_ISFILE gettext("package source <%s> is an ordinary file - treating as a package data stream")
+#define DBG_PKGINSTALL_ENTRY gettext("pkgInstall:")
+#define DBG_PKGINSTALL_EXECOC_GZ gettext("executing checkinstall package <%s> script <%s>")
+#define DBG_PKGINSTALL_EXECOC_LZ gettext("executing checkinstall package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_EXEPIC_GZ gettext("executing postinstall package <%s> script <%s>")
+#define DBG_PKGINSTALL_EXEPIC_LZ gettext("executing postinstall package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_EXEPOC_GZ gettext("executing preinstall package <%s> script <%s>")
+#define DBG_PKGINSTALL_EXEPOC_LZ gettext("executing preinstall package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_HAS_LOCKFILE gettext("before removing package <%s> found existing lockfile <%s> zone <%s>")
+#define DBG_PKGINSTALL_INSDONE gettext("install completed: hollow support <%d> is hollow <%d> fresh install <%d> updated <%s> skipped <%s> script <%s> access <%d>")
+#define DBG_PKGINSTALL_POCALT_NONE gettext("no pkgbin preinstall package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_POC_DBUPD gettext("skipping preinstall package <%s> script <%s> zone <%s> (db update only)")
+#define DBG_PKGINSTALL_POC_NONE gettext("has no media preinstall package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_POIS_DBUPD gettext("skipping postinstall package <%s> script <%s> zone <%s> (db update only)")
+#define DBG_PKGINSTALL_POIS_NONE gettext("no postinstall in package <%s> script <%s> zone <%s>")
+#define DBG_PKGINSTALL_POIS_NOPATH gettext("no postinstall in package <%s> zone <%s>")
+#define DBG_PKGINSTALL_POIS_SKIPPING gettext("all objects skipped when installing in zone <%s>: skipping postinstall package <%s> script <%s>")
+#define DBG_PKGINSTALL_PREINSCHK gettext("preinstallation check of package <%s> zone <%s>")
+#define DBG_PKGINSTALL_PREINSCHK_OK gettext("preinstall check successful")
+#define DBG_PKGINSTALL_RSCRIPT_IS_ROOT gettext("request script run as root = <%d>")
+#define DBG_PKGINSTALL_RSCRIPT_NOT_SET gettext("admin file parameter <%s> is not set")
+#define DBG_PKGINSTALL_RSCRIPT_SET_TO gettext("admin file parameter <%s> is set to <%s>")
+#define DBG_PKGINSTALL_TMPDIR gettext("using temporary directory <%s>")
+#define DBG_PKGLIST_ERROR gettext("unable to get package list from device <%s> directory <%s>: fatal error <%d>")
+#define DBG_PKGLIST_NONFOUND gettext("unable to get package list from device <%s> directory <%s>: no packages found")
+#define DBG_PKGLIST_RM_ERROR gettext("unable to get package list from directory <%s>: fatal error <%d>")
+#define DBG_PKGLIST_RM_NONFOUND gettext("unable to get package list from directory <%s>: no packages found")
+#define DBG_PKGOPS_ADDED_GZPKG gettext("added package <%s> to global zone only file")
+#define DBG_PKGOPS_ADDGZPKG gettext("add package <%s> to global zone only file at <%s>")
+#define DBG_PKGOPS_ADD_TZP gettext("add package entry <%d> instance <%s> as this zone only")
+#define DBG_PKGOPS_CKSUM_MISMATCH gettext("checksum <%s>:<0x%08lx> does not match <%s>:<0x%08lx>")
+#define DBG_PKGOPS_EDITABLE_EXISTS gettext("editable file <%s> exists: ok")
+#define DBG_PKGOPS_GETPKGLIST_ARGS gettext("directory <%s> category <%s>")
+#define DBG_PKGOPS_GETPKGLIST_ENTRY gettext("pkgGetPackageList:")
+#define DBG_PKGOPS_GPKGLIST_CATFAILED gettext("no packages found for category <%s>")
+#define DBG_PKGOPS_GPKGLIST_CATOK gettext("successfully generated package list for category <%s>")
+#define DBG_PKGOPS_GPKGLIST_EINTR gettext("search interrupted looking for packages from list of packages specified")
+#define DBG_PKGOPS_GPKGLIST_ENOPKG gettext("no packages found from list of packages specified")
+#define DBG_PKGOPS_GPKGLIST_ESRCH gettext("search failed looking for packages from list of packages specified")
+#define DBG_PKGOPS_GPKGLIST_OK gettext("successfully generated package list from list of packages specified")
+#define DBG_PKGOPS_GPKGLIST_UNKNOWN gettext("unknown value <%d> returned from gpkglist")
+#define DBG_PKGOPS_IS_INHERITED gettext("path <%s> is inherited from <%s>")
+#define DBG_PKGOPS_IS_NOT_THISZONE gettext("package <%s> is NOT this zone only")
+#define DBG_PKGOPS_IS_THISZONE gettext("package <%s> is this zone only")
+#define DBG_PKGOPS_LOCHIGH_ARGS gettext("rootpath <%s> pkginst <%s>")
+#define DBG_PKGOPS_LOCHIGH_ENTRY gettext("pkgLocateHighestInst:")
+#define DBG_PKGOPS_LOCHIGH_INSTANCE gettext("instance <%d> = pkginst <%s> name <%s> arch <%s> version <%s> vendor <%s> basedir <%s> catg <%s> status <0x%02x>")
+#define DBG_PKGOPS_LOCHIGH_RETURN gettext("npkgs is <%d> returned pkginst <%s> path <%s>")
+#define DBG_PKGOPS_LOCHIGH_WILDCARD gettext("package <%s> wild card specification <%s>")
+#define DBG_PKGOPS_MATCHINHERIT_ARGS gettext("<%s> vs <%s> root <%s> mode <0%04o> modtime <0x%08lx> ftype <%c> cksum <0x%08lx>")
+#define DBG_PKGOPS_MATCHINHERIT_ENTRY gettext("match inherited:")
+#define DBG_PKGOPS_MOD_MISMATCH gettext("mod time <%s>:<0x%08lx> does not match <%s>:<0x%08lx>")
+#define DBG_PKGOPS_NOT_THISZONE gettext("package <%s> is NOT this zone only: no this zone only packages")
+#define DBG_PKGOPS_PARAMTRUTH_RESULTS gettext("lookup param <%s> compare-value <%s> default-value <%s> param-is <%s> result <%s>")
+#define DBG_PKGOPS_PKGINFO_RETURNED gettext("pkginfo for path <%s> returned <%d>")
+#define DBG_PKGOPS_PKG_IS_GZONLY gettext("package <%s> IS recorded as installed in the global zone only")
+#define DBG_PKGOPS_PKG_NOT_GZONLY gettext("package <%s> not recorded as installed in the global zone only")
+#define DBG_PKGOPS_REMOVED_GZPKG gettext("removed package <%s> from global zone only file")
+#define DBG_PKGOPS_VOLATILE_EXISTS gettext("volatile file <%s> exists")
+#define DBG_PKGREMOVE_ADMINFILE gettext("using admin file <%s>")
+#define DBG_PKGREMOVE_ARGS gettext("package <%s> dirname <%s> nodelete <%d> adminFile <%s>")
+#define DBG_PKGREMOVE_ENTRY gettext("pkgRemove:")
+#define DBG_PKGREMOVE_EXEPIC_GZ gettext("executing postremove package <%s> script <%s>.")
+#define DBG_PKGREMOVE_EXEPIC_LZ gettext("executing postremove package <%s> script <%s> zone <%s>.")
+#define DBG_PKGREMOVE_EXEPOC_GZ gettext("executing preremove package <%s> script <%s>.")
+#define DBG_PKGREMOVE_EXEPOC_LZ gettext("executing preremove package <%s> script <%s> zone <%s>.")
+#define DBG_PKGREMOVE_HOLLOW_DISABLED gettext("hollow package support is disabled")
+#define DBG_PKGREMOVE_HOLLOW_ENABLED gettext("hollow package support is enabled")
+#define DBG_PKGREMOVE_PIC_DBUPD gettext("skipping postremove package <%s> script <%s> zone <%s> (db update only)")
+#define DBG_PKGREMOVE_PIC_NODEL gettext("skipping postremove package <%s> script <%s> zone <%s> (nodelete)")
+#define DBG_PKGREMOVE_PIC_NONE gettext("package <%s> zone <%s> has no postremove script")
+#define DBG_PKGREMOVE_POC_DBUPD gettext("skipping preremove package <%s> script <%s> zone <%s> (db update only)")
+#define DBG_PKGREMOVE_POC_NODEL gettext("skipping preremove package <%s> script <%s> zone <%s> (nodelete)")
+#define DBG_PKGREMOVE_POC_NONE gettext("package <%s> zone <%s> has no preremove script")
+#define DBG_PKGREMOVE_PRERMCHK gettext("preremoval check of package <%s> zone <%s>")
+#define DBG_PKGREMOVE_PRERMCHK_OK gettext("preremoval check successful")
+#define DBG_PKGREMOVE_PROCPKG_GZ gettext("begin processing package <%s> information lockfile <%s>")
+#define DBG_PKGREMOVE_PROCPKG_LZ gettext("begin processing package <%s> information lockfile <%s> zone <%s>")
+#define DBG_PKGREMOVE_REM gettext("performing class removal package <%s> zone <%s>")
+#define DBG_PKGREMOVE_REM_DBUPD gettext("skipping class removal package <%s> zone <%s> (db update only)")
+#define DBG_PKGREMOVE_REM_NODEL gettext("skipping class removal package <%s> zone <%s> (nodelete)")
+#define DBG_PKGREMOVE_TMPDIR gettext("using temporary directory <%s>")
+#define DBG_PKGREMPKGSGZNNGZ_ARGS gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s>")
+#define DBG_PKGREMPKGSGZNNGZ_ENTRY gettext("remove_packages_in_global_no_zones:")
+#define DBG_PKGREMPKGSGZWNGZ_ARGS gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s> pkgdir <%s>")
+#define DBG_PKGREMPKGSGZWNGZ_ENTRY gettext("remove_packages_in_global_with_zones:")
+#define DBG_PKGREMPKGSNGZ_ARGS gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s> pkgdir <%s>")
+#define DBG_PKGREMPKGSNGZ_ENTRY gettext("remove_packages_in_nonglobal_zone:")
+#define DBG_PKGRM_ADMINFILE gettext("using admin file <%s>")
+#define DBG_PKGRM_CKRETURN gettext("check return code <%d> package <%s> function <remove packages>")
+#define DBG_PKGRM_ENABLING_HOLLOW gettext("enabling hollow package support")
+#define DBG_PKGRM_HOLLOW_ENABLED gettext("hollow package support is enabled")
+#define DBG_PKGRM_TMPDIR gettext("using temporary directory <%s>")
+#define DBG_PKGZONECHECKINSTALL_ARGS gettext("zone <%s> package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> adminFile <%s> stdout <%s>")
+#define DBG_PKGZONECHECKINSTALL_ENTRY gettext("pkgZoneCheckInstall:")
+#define DBG_PKGZONECHECKREMOVE_ARGS gettext("zone <%s> package <%s> dirname <%s> adminFile <%s> stdoutpath <%s>")
+#define DBG_PKGZONECHECKREMOVE_ENTRY gettext("pkgZoneCheckRemove:")
+#define DBG_PKGZONEINSTALL_ARGS gettext("zone <%s> package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> adminFile <%s>")
+#define DBG_PKGZONEINSTALL_ENTRY gettext("pkgZoneInstall:")
+#define DBG_PKGZONEREMOVE_ARGS gettext("zone <%s> package <%s> dirname <%s> nodelete <%d> adminFile <%s>")
+#define DBG_PKGZONEREMOVE_ENTRY gettext("pkgZoneRemove:")
+#define DBG_PKG_INSTALLED gettext("package <%s> is installed at <%s>")
+#define DBG_PKG_IN_DIR gettext("package <%s> available in directory <%s>")
+#define DBG_PKG_NOT_INSTALLED gettext("package <%s> is not installed at <%s>")
+#define DBG_PKG_SELECTED gettext("-> package [%d] = <%s>")
+#define DBG_PKG_TEST_EXISTENCE gettext("test existence of package <%s> at <%s>")
+#define DBG_PREIVFY_CKCFCONTENT gettext("check content conflict: package <%s> message <%s>")
+#define DBG_PREIVFY_CKCONFLICT gettext("check conflicting installed object: package <%s> message <%s>")
+#define DBG_PREIVFY_CKDEPEND gettext("check dependency: package <%s> message <%s>")
+#define DBG_PREIVFY_CKDIRS gettext("check directories: package <%s> message <%s>")
+#define DBG_PREIVFY_CKINSTANCE gettext("check instance: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPARTIALINSTALL gettext("check partially installed: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPARTIALREMOVE gettext("check partially removed: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPKGDIRS gettext("check package directories: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPKGFILEBAD gettext("check file bad: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPKGFILES gettext("check package files: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPRENCI gettext("check prerequisite incomplete: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPREREQ gettext("check prerequisite installed: package <%s> message <%s>")
+#define DBG_PREIVFY_CKPRIV gettext("check privileges: package <%s> message <%s>")
+#define DBG_PREIVFY_CKRUNLEVEL gettext("check run level: package <%s> message <%s>")
+#define DBG_PREIVFY_CKSETUID gettext("check setuid: package <%s> message <%s>")
+#define DBG_PREIVFY_CKSPACE gettext("check space: package <%s> message <%s>")
+#define DBG_PREIVFY_ENTRY gettext("performing preinstallation dependency verification")
+#define DBG_PREIVFY_GETYORN_ARGS gettext("package <%s> nocheck <%d> quit <%d> message <%s> admin-msg <%s>")
+#define DBG_PREIVFY_GETYORN_CKYORN gettext("package <%s> ckyorn return non-zero <%d>")
+#define DBG_PREIVFY_GETYORN_NOCHECK gettext("package <%s> no check - return <0> (success)")
+#define DBG_PREIVFY_GETYORN_NOT_Y gettext("package <%s> ckyorn answer <%s> - return <3> (interruption)")
+#define DBG_PREIVFY_GETYORN_QUIT gettext("package <%s> quit - return <4> (administration)")
+#define DBG_PREIVFY_GETYORN_QUIT_USER gettext("package <%s> noninteractive mode - return <5> (administration required)")
+#define DBG_PREIVFY_GETYORN_SUCCESS gettext("package <%s> continue installation")
+#define DBG_PREIVFY_NOFILE gettext("unable to perform preinstallation check of package <%s> in zone <%s> data file <%s>: %s")
+#define DBG_PREIVFY_SCAN gettext("scanning for line <%s> found package <%s> zone <%s>")
+#define DBG_PREIVFY_SKIP_THISZONE gettext("skipping preinstall verification of package <%s>: marked this zone only")
+#define DBG_PRERVFY_ENTRY gettext("performing preremoval dependency verification")
+#define DBG_PRERVFY_GETYORN_ARGS gettext("package <%s> nocheck <%d> quit <%d> message <%s> admin-msg <%s>")
+#define DBG_PRERVFY_GETYORN_CKYORN gettext("package <%s> ckyorn return non-zero <%d>")
+#define DBG_PRERVFY_GETYORN_NOCHECK gettext("package <%s> no check - return <0> (success)")
+#define DBG_PRERVFY_GETYORN_NOT_Y gettext("package <%s> ckyorn answer <%s> - return <3> (interruption)")
+#define DBG_PRERVFY_GETYORN_QUIT gettext("package <%s> quit - return <4> (administration)")
+#define DBG_PRERVFY_GETYORN_QUIT_USER gettext("package <%s> noninteractive mode - return <5> (administration required)")
+#define DBG_PRERVFY_GETYORN_SUCCESS gettext("package <%s> continue removal")
+#define DBG_PRERVFY_NOFILE gettext("unable to perform preremoval check of package <%s> in zone <%s> data file <%s>: %s")
+#define DBG_PRERVFY_RCKDEPEND gettext("check dependency: package <%s> message <%s>")
+#define DBG_PRERVFY_RCKDEPSONME gettext("check depends on this package: package <%s> message <%s>")
+#define DBG_PRERVFY_RCKPRENCI gettext("check prerequisite incomplete: package <%s> message <%s>")
+#define DBG_PRERVFY_RCKPREREQ gettext("check prerequisite installed: package <%s> message <%s>")
+#define DBG_PRERVFY_RCKPRIV gettext("check privileges: package <%s> message <%s>")
+#define DBG_PRERVFY_RCKRUNLEVEL gettext("check run level: package <%s> message <%s>")
+#define DBG_PRERVFY_SCAN gettext("scanning for line <%s> found package <%s> zone <%s>")
+#define DBG_PUTPARAM_PUTCONDINFO_ENTRY gettext("generating environment condition information")
+#define DBG_PUTPARAM_PUTCONDINFO_EXIT gettext("environment condition information is <%s>")
+#define DBG_QUIT_REMOVING_PKGDIR gettext("install not yet started and not updating existing: removing package directory <%s>")
+#define DBG_QUIT_REMOVING_PKGSAV gettext("install started and updating existing: removing package temp directory <%s>")
+#define DBG_REMOVEPKGS_ARGS gettext("npkgs <%d> nodelete <%d> longest pkg <%d> repeat <%d> pkgdir <%s> spooldir <%s>")
+#define DBG_REMOVEPKGS_ENTRY gettext("remove_packages:")
+#define DBG_REMOVE_FLAG_VALUES gettext("%s: admnflag <%d> doreboot <%d> failflag <%d> interrupted <%d> intrflag <%d> ireboot <%d> nullflag <%d> warnflag <%d>")
+#define DBG_REMOVE_PKGS_FROM_SPOOL gettext("removing packages from spool directory <%s>")
+#define DBG_REMOVE_PKG_FROM_ZONE gettext("removing package <%s> from zone <%s>")
+#define DBG_REMOVING_DSTREAM_PKGDIR gettext("removing temporary stream <%s> for package <%s>")
+#define DBG_REMOVING_DSTREAM_TMPDIR gettext("removing package datastream temporary directory <%s>")
+#define DBG_REMOVING_DWNLD_TMPDIR gettext("removing download temporary directory <%s>")
+#define DBG_REMOVING_PKG_TMPDIR gettext("removing temporary directory <%s> for package <%s>")
+#define DBG_REMOVING_ZONE_TMPDIR gettext("removing zones temporary directory <%s>")
+#define DBG_RESTORE_ZONE_STATE gettext("restoring state of zone <%s>")
+#define DBG_SETUP_TEMPDIR gettext("created temporary directory <%s>")
+#define DBG_SKIPPING_ZONE gettext("skipping processing of zone <%s>: zone not running")
+#define DBG_SKIPPING_ZONE_BOOT gettext("not booting zone <%s>: zone is running")
+#define DBG_SKIPPING_ZONE_NOT_RUNNABLE gettext("not booting zone <%s>: zone cannot be booted")
+#define DBG_SML_ADD_TAG gettext("add element <%s> to tag <%s>")
+#define DBG_SML_CREATED_NEW_TAG_OBJECT gettext("new tag <0x%08lx> name=<%s> created")
+#define DBG_SML_CREATE_NEW_TAG_OBJECT gettext("create new tag name=<%s>")
+#define DBG_SML_DELETE_PARAM gettext("delete parameter tag <%s> name <%s>: ")
+#define DBG_SML_DELETE_PARAM_FOUND gettext("parameter <%s> value=<%s> - deleted")
+#define DBG_SML_DELETE_PARAM_NOT_FOUND gettext("parameter <%s> not found - not deleted")
+#define DBG_SML_DELETE_PARAM_NO_PARAMS gettext("tag contains no parameters - not deleted")
+#define DBG_SML_DEL_TAG gettext("delete element <%s> from tag <%s>")
+#define DBG_SML_FREE_TAG gettext("freeing tag <0x%08lx> name=<%s>")
+#define DBG_SML_GET_PARAM gettext("get parameter <%s> tag <%s>")
+#define DBG_SML_GET_PARAM_BY_TAG gettext("get param by tag name <%s> index <%d> param <%s>")
+#define DBG_SML_GET_PARAM_NAME gettext("tag <%s> get parameter number <%d>")
+#define DBG_SML_GET_TAG_BY_NAME gettext("get tag by name <%s> index <%d>")
+#define DBG_SML_GOT_PARAM gettext("tag <%s> %s = <%s>")
+#define DBG_SML_GOT_PARAM_NAME gettext("tag <%s> got parameter number <%d> name=<%s>")
+#define DBG_SML_HAVE_PARM_NAME gettext("read tag: parameter name <%s> tag <%s>")
+#define DBG_SML_HAVE_PARM_VALUE gettext("read tag: parameter %s=\"%s\" tag <%s>")
+#define DBG_SML_HAVE_TAG_NAME gettext("read tag: open tag <%s>parent <%s>")
+#define DBG_SML_INT_FREE_PARAMS gettext("freeing parameters at <0x%08lx>")
+#define DBG_SML_INT_FREE_PARAM_NAME gettext("free param name <0x%08lx> name=<0x%08lx> value=<%s>")
+#define DBG_SML_INT_FREE_PARAM_VALUE gettext("free param value <0x%08lx> name=<0x%08lx> value=<%s>")
+#define DBG_SML_INT_FREE_TAG gettext("free tag <0x%08lx> name=<%s> param <%d> tags <%d>")
+#define DBG_SML_INT_FREE_TAGS gettext("freeing tags at <0x%08lx>")
+#define DBG_SML_INT_FREE_TAG_NAME gettext("freeing tag name <0x%08lx> name=<%s>")
+#define DBG_SML_LOADED_TAGS_FROM_STR gettext("tag <0x%08lx> <%s> loaded from string")
+#define DBG_SML_ONE_TAG_READ gettext("one tag read - tag <0x%08lx> name=<%s>")
+#define DBG_SML_PRINTTAG gettext("dump of tag <%s> size <%d> bytes\n\n****************************************************************************\n%s****************************************************************************\n")
+#define DBG_SML_READTAG_BLANKLINE gettext("read tag: blank line (no tag returned) at <%s>")
+#define DBG_SML_READTAG_CLOSED_TAG gettext("read tag: closed tag <%s> inside tag <%s>")
+#define DBG_SML_READTAG_CLOSE_TAG gettext("read tag: close tag <%s> found")
+#define DBG_SML_READTAG_EXPECTED_EOF gettext("read tag: provider <%s> EOF outside of tag input")
+#define DBG_SML_READTAG_UNEXPECTED_EOF gettext("read tag: provider <%s> unexpected EOF in middle of tag input")
+#define DBG_SML_READ_IN_TOP_TAG gettext(" --> read in top tag <%s>")
+#define DBG_SML_READ_ONE_TAG gettext("read one tag from <%s>")
+#define DBG_SML_READ_ONE_TAG_NOTAG gettext("cannot read tag - no tag present")
+#define DBG_SML_READ_TAG gettext("read tag: loading subtag for <%s>")
+#define DBG_SML_SET_PARAM gettext("set parameter tag <%s> %s = <%s>: ")
+#define DBG_SML_SET_PARAM_CREATE_NEW gettext("create new parameter")
+#define DBG_SML_SET_PARAM_LEAVE_ALONE gettext("existing value=<%s> identical - not changed")
+#define DBG_SML_SET_PARAM_MODIFY gettext("modify existing value=<%s>")
+#define DBG_SML_START_CLOSE_TAG gettext("read tag: close tag found current tag=<%s>")
+#define DBG_SML_TAG_HEAD_DONE gettext("read tag: tag <%s> started inside tag <%s>")
+#define DBG_SML_TAG_ONLY gettext("read tag: line with tag name only tag <%s>")
+#define DBG_UNMOUNTING_DEV gettext("unmounting package device <%s>")
+#define DBG_UNPACKCHECK_ARGS gettext("idsname <%s> packagedir <%s>")
+#define DBG_UNPACKCHECK_ENTRY gettext("unpack_and_check_packages:")
+#define DBG_UNPACKSTRM_ARGS gettext("unpack package <%s> from stream <%s> into directory <%s>")
+#define DBG_UNPACKSTRM_ENTRY gettext("unpack_package_from_stream:")
+#define DBG_UNPACKSTRM_UNPACKING gettext("unpacking package <%s> from stream <%s> into temporary directory <%s>")
+#define DBG_VERIFY_SKIP_THISZONE gettext("skipping dependency checking of package <%s>: marked this zone only")
+#define DBG_WRITEFILE_ENTRY gettext("write file: control <0x%02x> mode <0%04lo> file <%s>")
+#define DBG_ZONES_SKIPPED gettext("skipped <%d> zones that are not currently booted")
+#define DBG_ZONE_EXEC_ENTER gettext("zone_exec: enter zone <%s> command <%s> args:")
+#define DBG_ZONE_EXEC_EXIT gettext("zone_exec: exit zone <%s> command <%s> exit code <%d> stdout <%s>")
+
+/*
+ * I18N: these messages are error messages that can be displayed
+ * during the normal usage of the products
+ */
+
+#define ERR_ACCRESP gettext("unable to access response file <%s>")
+#define ERR_ADMBD gettext("%s is already installed at %s. Admin file will force a duplicate installation at %s.")
+#define ERR_ADM_KEYSTORE gettext("unable to determine keystore location")
+#define ERR_ADM_PROXY gettext("Admin file proxy setting invalid")
+#define ERR_ALLZONES_AND_G_USED gettext("The -G option (install packages in the global zone only)\nmay not be used with package <%s> because the package must be\ninstalled in all zones.")
+#define ERR_ALLZONES_AND_IN_LZ gettext("The package <%s> may only be installed by the global zone administrator")
+#define ERR_ALLZONES_AND_IN_LZ_PKGRM gettext("The package <%s> may only be removed by the global zone administrator")
+#define ERR_ALLZONES_AND_THISZONE gettext("The package <%s> has <%s> = true and <%s> = true: the package may set either parameter to true, but may not set both parameters to true. NOTE: if the package contains a request script, it is treated as though it has <SUNW_PKG_THISZONE> = true")
+#define ERR_ALLZONES_AND_Z_USED gettext("The -Z option (extend packages installed in the global zone to all non-global zones) may not be used with the package <%s> which may only be installed in all zones by the global zone administrator")
+#define ERR_ARG gettext("URL <%s> is not valid")
+#define ERR_BADULIMIT gettext("cannot process invalid ULIMIT value of <%s>.")
+#define ERR_BADUSER gettext("unable to find user <%s> or <%s>.")
+#define ERR_BAD_DEVICE gettext("bad device <%s> specified")
+#define ERR_BAD_N_PKGRM gettext("you must specify a category (-Y) or list of packages to remove")
+#define ERR_BRAND_GETBRAND gettext("unable to get zone brand: zonecfg_get_brand: %s")
+#define ERR_CANNOT_BOOT_ZONE gettext("no changes made to zone <%s>: unable to boot zone")
+#define ERR_CANNOT_CKSUM_FILE gettext("unable to determine checksum of file <%s>: %s")
+#define ERR_CANNOT_CONVERT_PKGSTRM gettext("unable to convert package <%s> to stream from <%s> to <%s>")
+#define ERR_CANNOT_COPY gettext("unable to copy <%s>\n\tto <%s>")
+#define ERR_CANNOT_COPY_LOCAL gettext("cannot obtain local copy of <%s>: (%d) %s")
+#define ERR_CANNOT_CREATE_PKGPATH gettext("unable to create package path <%s>")
+#define ERR_CANNOT_GET_ZONE_LIST gettext("unable to determine list of non-global zones installed on this system")
+#define ERR_CANNOT_LOCK_THIS_ZONE gettext("Unable to lock this zone for administration")
+#define ERR_CANNOT_LOCK_ZONES gettext("unable to lock zones to perform operations")
+#define ERR_CANNOT_OPEN_DEPEND_FILE gettext("unable to open depend file <%s>: %s")
+#define ERR_CANNOT_OPEN_FOR_WRITING gettext("unable to open <%s> for writing: %s")
+#define ERR_CANNOT_OPEN_PKG_STREAM gettext("unable to open package datastream <%s>")
+#define ERR_CANNOT_UNPACK_PKGSTRM gettext("unable to unpack package <%s> from stream <%s> into directory <%s>")
+#define ERR_CANNOT_USE_DIR gettext("cannot use directory <%s>: %s")
+#define ERR_CASFAIL gettext("class action script did not complete successfully")
+#define ERR_CAT_FND gettext("Category argument <%s> cannot be found.")
+#define ERR_CAT_INV gettext("Category argument <%s> is invalid.")
+#define ERR_CAT_LNGTH gettext("The category argument exceeds the SVR4 ABI defined maximum supported length of 16 characters.")
+#define ERR_CAT_SYS gettext("Unable to remove packages that are part of the SYSTEM category with the -Y option.")
+#define ERR_CFBAD gettext("bad entry read of contents file")
+#define ERR_CFMISSING gettext("missing entry in contents file for <%s>")
+#define ERR_CHDIR gettext("unable to change current working directory to <%s>")
+#define ERR_CHGDIR gettext("unable to change directory to <%s>")
+#define ERR_CHKINSTALL gettext("checkinstall script did not complete successfully")
+#define ERR_CHKINSTALL_NOSCRIPT gettext("unable to access checkinstall script <%s>")
+#define ERR_CHMOD gettext("unable to change the mode of the response file <%s>")
+#define ERR_CHMOD_CHK gettext("unable to change the mode of the checkinstall script")
+#define ERR_CLASSES gettext("CLASSES parameter undefined in <%s>")
+#define ERR_CLIDX gettext("invalid class index of <%d> detected for file %s.")
+#define ERR_COPY_MEMORY gettext("unable to allocate memory to copy file <%s>: (%d) %s")
+#define ERR_CORRUPT gettext("source path <%s> is corrupt")
+#define ERR_COULD_NOT_INSTALL gettext("%s could not be installed.")
+#define ERR_CREATE_PATH_2 gettext("unable to create path from <%s> and <%s>")
+#define ERR_CREATE_PATH_3 gettext("unable to create path from <%s> and <%s> and <%s>")
+#define ERR_CREATE_PKGOBJ gettext("unable to create package object <%s>.")
+#define ERR_CREATE_TMPADMIN gettext("unable to create temporary admin file <%s>: %s")
+#define ERR_CREAT_CONT gettext("unable to create contents file <%s>")
+#define ERR_CRERESP gettext("unable to create response file <%s>")
+#define ERR_DB gettext("unable to query or modify database")
+#define ERR_DB_QUERY gettext("unable to find <%s> in the database.")
+#define ERR_DB_TBL gettext("unable to remove database entries for package <%s> in table <%s>.")
+#define ERR_DEPENDENCY_IGNORED gettext("\nERROR: %s <%s> on %s <%s>\n")
+#define ERR_DEPENDENCY_REPORT gettext("\nERROR: <%s> %s <%s> on %s <%s>\n")
+#define ERR_DEPNAM gettext("The <%s> package \"%s\" depends on the package currently being removed.")
+#define ERR_DEPONME gettext("The <%s> package depends on the package currently being removed.")
+#define ERR_DIR_CONST gettext("unable to construct download directory <%s>")
+#define ERR_DSARCH gettext("unable to find archive for <%s> in datastream")
+#define ERR_DSINIT gettext("could not process datastream from <%s>")
+#define ERR_DSTREAM gettext("unable to unpack datastream")
+#define ERR_DSTREAMCNT gettext("datastream early termination problem")
+#define ERR_DWNLDTEMPDIR gettext("unable to make temporary directory for download operations in directory <%s>: %s")
+#define ERR_FCHMOD gettext("unable to change mode of file <%s> to <0x%04lx>: (%d) %s")
+#define ERR_FINALCK_ATTR gettext("ERROR: attribute verification of <%s> failed")
+#define ERR_FINALCK_CONT gettext("ERROR: content verification of <%s> failed")
+#define ERR_FSYS_FELLOUT gettext("fsys(): fell out of loop looking for <%s>")
+#define ERR_F_REQUIRES_M gettext("the -f option must be used in conjunction with the -m option")
+#define ERR_FSTAT gettext("unable to fstat fd <%d> pathname <%s>: (%d) %s")
+#define ERR_GPKGLIST_ERROR gettext("unable to determine list of packages to operate on (internal error in gpkglist)")
+#define ERR_GZ_USED_TOGETHER gettext("the -G and zonelist options cannot be used together")
+#define ERR_ILL_HTTP_OPTS gettext("The -i and (-k or -P) options are mutually exclusive.")
+#define ERR_ILL_PASSWD gettext("A password is required to retrieve the public certificate from the keystore.")
+#define ERR_INCOMP_VERS gettext("A version of <%s> package \"%s\" (which is incompatible with the package that is being installed) is currently installed and must be removed.")
+#define ERR_INPUT gettext("error while reading file <%s>: (%d) %s")
+#define ERR_INSTALL_ZONES_SKIPPED gettext("unable to boot <%d> zones that are not currently running - no packages installed on those zones")
+#define ERR_INTONLY gettext("unable to install <%s> without user interaction")
+#define ERR_INTR gettext("Interactive request script supplied by package")
+#define ERR_INVALID_O_OPTION gettext("option <%s> passed to -O is not recognized: option ignored")
+#define ERR_IN_GZ_AND_ALLZONES_AND_INSTALLED gettext("The package <%s> is currently installed on\nthe system in the global zone only. When this package was last installed\nthe -G option was used (install package in the global zone only). The new\ninstance of this package to be installed may only be installed in all zones.\nBefore you can install the latest version of this package, you must first\nremove all instances of this package from the global zone (via pkgrm).")
+#define ERR_IN_GZ_AND_NOT_INSTALLED gettext("WARNING: The package <%s> is marked as being installed in the\nglobal zone only. The package is NOT installed on the system. This condition\nis not possible. The file <%s> must be edited\nand the line for this package removed.")
+
+#ifdef Z_OPTION_IS_SUPPORTED
+#define ERR_IN_GZ_AND_NO_G_USED gettext("The package <%s> is currently installed on the\nsystem in the global zone. To install the new instance of this package in the\nglobal zone only, you must specify the -G option. To install the new instance\nof this package in all zones you must first run pkgadd on the current package\nwith the -Z option so that the existing instance of this package is installed\nin all zones first. You may optionally remove the existing instance of this\npackage from the global zone first (via pkgrm) and then install the new\ninstance of this package in all zones.")
+#else
+#define ERR_IN_GZ_AND_NO_G_USED gettext("The package <%s> is currently installed on the system in the\nglobal zone. To install the new instance of this package in the global\nzone only, you must specify the -G option. To install the new instance\nof this package in all zones you must first remove the existing instance\nof this package from the global zone first (via pkgrm) and then install\nthe new instance of this package in all zones.")
+#endif
+
+#define ERR_LINK gettext("unable to link <%s> to <%s>: %s")
+#define ERR_LIVE_CONTINUE_NOT_SUPPORTED gettext("live continue mode is not supported")
+#define ERR_LOCKFILE gettext("unable to create lockfile <%s>")
+#define ERR_LOG gettext("unable to open logfile <%s>: (%d) %s")
+#define ERR_LOG_FAIL gettext("failed to log message using format <%s>")
+#define ERR_MALLOC gettext("unable to allocate %s memory, errno %d: %s")
+#define ERR_MAPFAILED gettext("unable to mmap <%s> for reading: (%d) %s")
+#define ERR_MEM gettext("unable to allocate memory.")
+#define ERR_MEMORY gettext("memory allocation failure, errno=%d")
+#define ERR_MERGINFOS_CHANGE_ZONEATTR gettext("attempt to change package <%s> version <%s> package zone attribute <%s> from <%s> to <%s>: the package zone attribute values of installed packages cannot be changed")
+#define ERR_MERGINFOS_UNSET_ZONEATTR gettext("attempt to unset package <%s> version <%s> package zone attribute <%s> from <%s>: the package zone attribute values of installed packages that are set to <true> cannot be unset")
+#define ERR_MERGINFOS_SET_ZONEATTR gettext("attempt to set package <%s> version <%s> package zone attribute <%s> that is not currently set to <%s>: the package zone attribute values of installed packages cannot be set to any value except <false>")
+#define ERR_MISSING_DIR_AND_PKG gettext("missing directory containing package to install and package instance (name of package ) to install from end of command line")
+#define ERR_MISSING_PKG_INSTANCE gettext("missing package instance (name of package) to install from end of command line")
+#define ERR_MKDIR gettext("unable to make temporary directory <%s>")
+#define ERR_MAKE_DIR gettext("unable to create directory <%s>: (%d) %s")
+#define ERR_MKTEMP gettext("unable to create unique temporary file <%s>: (%d) %s")
+#define ERR_MNT_NOMOUNTS gettext("get_mntinfo() could find no filesystems")
+#define ERR_MNT_NOROOT gettext("get_mntinfo() identified <%s> as root file system instead of <%s> errno %d: %s")
+#define ERR_MODTIM gettext("unable to reset access/modification time of <%s>: (%d) %s")
+#define ERR_NEWBD gettext("%s is already installed at %s. Duplicate installation attempted at %s.")
+#define ERR_NOCOPY gettext("unable to create copy of UNINSTALL script in <%s>")
+#define ERR_NODIR gettext("unable to create directory <%s>: (%d) %s")
+#define ERR_NORESPCOPY gettext("unable to copy response file <%s> to <%s>")
+#define ERR_NODEVICE gettext("unable to determine device to install from")
+#define ERR_NOINT gettext("-n option cannot be used when removing pre-SVR4 packages")
+#define ERR_NOPKGS gettext("no packages were found in <%s>")
+#define ERR_NOREQUEST gettext("package does not contain an interactive request script")
+#define ERR_NORESP gettext("response file <%s> must not exist")
+#define ERR_NOSUCH_INHERITED gettext("cannot use inherited file system <%s>")
+#define ERR_NOTABLE gettext("unable to open %s table <%s>: %s")
+#define ERR_NOT_IN_GZ_AND_Z_USED gettext("The package <%s> is not currently installed\nin the global zone only; you may not specify the -Z option to install the\npackage in all non-global zones until the package is first install in the\nglobal zone only. You may optionally install the package without the -Z option\nto install the package in all zones.")
+#define ERR_NOT_ROOT gettext("You must be \"root\" for %s to execute properly.")
+#define ERR_NOW_ALLZONES_AND_HOLLOW gettext("The package <%s> has <%s> = false and <%s> = true: a hollow package must also be set to install in all zones")
+#define ERR_NO_LIVE_MODE gettext("live continue mode is not supported")
+#define ERR_NO_PKGDIR gettext("unable to use package directory <%s> for package <%s>: %s")
+#define ERR_NO_PKG_INFOFILE gettext("unable to open package <%s> pkginfo file <%s>: %s")
+#define ERR_NO_PKG_MAPFILE gettext("unable to open package <%s> pkgmap file <%s>: %s")
+#define ERR_NO_SUCH_INSTANCE gettext("instance <%s> does not exist")
+#define ERR_OPEN_ADMIN_FILE gettext("unable to open admin file <%s>: %s")
+#define ERR_OPEN_READ gettext("unable to open <%s> for reading: (%d) %s")
+#define ERR_OPEN_WRITE gettext("unable to open <%s> for writing: (%d) %s")
+#define ERR_OPRESVR4 gettext("unable to unlink options file <%s>")
+#define ERR_OUTPUT_WRITING gettext("error while writing file <%s>: (%d) %s")
+#define ERR_PACKAGEBINREN gettext("unable to rename <%s>\n\tto <%s>")
+#define ERR_PATCHPKG gettext("unable to update patch_table with patches that have been pre installed")
+#define ERR_PATH gettext("the path <%s> is invalid!")
+#define ERR_PKGABRV gettext("illegal package abbreviation <%s> in dependency file")
+#define ERR_PKGADDCHK_CNFFAILED gettext("Conflicting file dependency checking failed.")
+#define ERR_PKGADDCHK_DEPFAILED gettext("Dependency checking failed.")
+#define ERR_PKGADDCHK_MKPKGDIR gettext("Unable to make required packaging directory")
+#define ERR_PKGADDCHK_PRIVFAILED gettext("Privilege checking failed.")
+#define ERR_PKGADDCHK_SPCFAILED gettext("Space checking failed.")
+#define ERR_PKGASK_AND_IGNORE_SIG gettext("cannot use the -i option with pkgask")
+#define ERR_PKGASK_AND_KEYSTORE_FILE gettext("cannot use the -k option with pkgask")
+#define ERR_PKGASK_AND_NOINTERACT gettext("cannot use the -n option with pkgask")
+#define ERR_PKGASK_AND_PROXY gettext("cannot use the -x option with pkgask")
+#define ERR_PKGASK_AND_SPOOLDIR gettext("cannot use the -s option with pkgask")
+#define ERR_PKGASK_AND_URI gettext("cannot use web based sources via -d with pkgask")
+#define ERR_PKGBINCP gettext("unable to copy <%s>\n\tto <%s>")
+#define ERR_PKGBINREN gettext("unable to rename <%s>\n\tto <%s>")
+#define ERR_PKGINFO gettext("unable to open pkginfo file <%s>")
+#define ERR_PKGINFO_ATTR_ADDED gettext("package <%s> is attempting to add the package attribute <%s>: this attribute cannot be added once the package is installed")
+#define ERR_PKGINFO_ATTR_CHANGED gettext("package <%s> is attempting to change the package attribute <%s> from <%s> to <%s>: this attribute cannot be changed once the package is installed")
+#define ERR_PKGINSTALL_GZONLY_ADD gettext("unable to add package <%s> to global zone only package list file")
+#define ERR_PKGINSTALL_STATOF gettext("unable to get space usage of <%s>: %s")
+#define ERR_PKGINSTALL_STATVFS gettext("unable to determine file system space for <%s>: %s")
+#define ERR_PKGMAP gettext("unable to open pkgmap file <%s>")
+#define ERR_PKGOPS_CANNOT_OPEN_GZONLY gettext("unable to open global zone only package list file at <%s>")
+#define ERR_PKGOPS_LOCHIGH_BAD_PKGNAME gettext("package name is not valid: %s")
+#define ERR_PKGOPS_OPEN_GZONLY gettext("unable to open global zone only package list file <%s>: %s")
+#define ERR_PKGOPS_TMPOPEN gettext("unable to create temporary global zone only package list file <%s>: %s")
+#define ERR_PKGREMOVE_GZONLY_REMOVE gettext("unable to remove package <%s> from global zone only package list file")
+#define ERR_PKGRMCHK_DEPFAILED gettext("Dependency checking failed.")
+#define ERR_PKGRMCHK_PRIVFAILED gettext("Privilege checking failed.")
+#define ERR_PKGS_AND_CAT_PKGADD gettext("cannot specify both a list of packages and a category (-Y) to install")
+#define ERR_PKGS_AND_CAT_PKGRM gettext("cannot specify both a list of packages and a category (-Y) to remove")
+#define ERR_PKGUNMOUNT gettext("unable to unmount <%s>")
+#define ERR_PKGVOL gettext("unable to obtain package volume")
+#define ERR_PKGZONEINSTALL_NO_STREAM gettext("internal error - package to install in zone not in stream format")
+#define ERR_PKG_NOT_APPLICABLE gettext("package <%s> cannot be installed on this system/zone")
+#define ERR_PKG_NOT_INSTALLABLE gettext("unable to install package <%s>")
+#define ERR_PKG_NOT_REMOVABLE gettext("unable to remove package <%s>")
+#define ERR_POSTINSTALL gettext("postinstall script did not complete successfully")
+#define ERR_POSTREMOVE gettext("postremove script did not complete successfully")
+#define ERR_PREINSTALL gettext("preinstall script did not complete successfully")
+#define ERR_PREIVFY_NOFILE gettext("unable to perform preinstallation check of package <%s> in zone <%s>")
+#define ERR_PREIVFY_OPEN_FILE gettext("unable to examine preinstallation check file <%s> for package <%s> in zone <%s>: %s")
+#define ERR_PREIVFY_UNKNOWN_LINE gettext("unknown preinstallation dependency check line <%s> for package <%s> zone <%s>: ignored")
+#define ERR_PRENCI gettext("The <%s> package \"%s\" is a prerequisite package and is not completely installed.")
+#define ERR_PREREMOVE gettext("preremove script did not complete successfully")
+#define ERR_PREREQ gettext("The <%s> package \"%s\" is a prerequisite package and should be installed.")
+#define ERR_PRERVFY_NOFILE gettext("unable to perform preremoval check of package <%s> in zone <%s>")
+#define ERR_PRERVFY_OPEN_FILE gettext("unable to examine preremoval check file <%s> for package <%s> in zone <%s>: %s")
+#define ERR_PRERVFY_UNKNOWN_LINE gettext("unknown preremoval dependency check line <%s> for package <%s> zone <%s>: ignored")
+#define ERR_PROXY gettext("Proxy specification <%s> invalid")
+#define ERR_RDONLY gettext("read-only parameter <%s> cannot be assigned a value")
+#define ERR_READ gettext("unable to read <%s>: (%d) %s")
+#define ERR_REMOVE gettext("unable to remove file <%s>: %s")
+#define ERR_RENAME gettext("unable to rename <%s> to <%s>: %s")
+#define ERR_REQUEST gettext("request script did not complete successfully")
+#define ERR_RESOLVEPATH gettext("unable to resolve path <%s>: %s")
+#define ERR_RESPFILE gettext("response file is invalid for pre-SVR4 package")
+#define ERR_RESPFILE_AND_URI gettext("cannot use -r option with web based sources via -d")
+#define ERR_RESPONSE gettext("unable to open response file <%s>")
+#define ERR_RMDIR gettext("unable to remove existing directory at <%s>")
+#define ERR_RMPATH gettext("unable to remove <%s>")
+#define ERR_RMRESP gettext("unable to remove response file <%s>")
+#define ERR_ROOT_CMD gettext("Command line install root contends with environment.")
+#define ERR_ROOT_SET gettext("Could not set install root from the environment.")
+#define ERR_RSP_FILE_NOTFULLPATH gettext("response file <%s> must be full pathname")
+#define ERR_RSP_FILE_NOT_GIVEN gettext("response file (to write) is required")
+#define ERR_RUNSTATE gettext("unable to determine current run-state")
+#define ERR_SCRULIMIT gettext("script <%s> created a file exceeding ULIMIT.")
+#define ERR_SML_CANNOT_READ_TAG gettext("cannot read tag")
+#define ERR_SML_EOF_BEFORE_TAG_NAME gettext("reading tag: unexpected EOF before reading tag name expecting tag=<%s>")
+#define ERR_SML_PARM_SEP_BAD gettext("reading tag: parameter value start found <%c> (0x%02x) expected '\"'")
+#define ERR_SML_READTAG_BADPARMNAME_CLOSE gettext("reading tag: expected '>' after '/' to close parm <%s> tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_BADTAG_CLOSE gettext("reading tag: expected '>' after '/' to close tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_BAD_START_CHAR gettext("reading tag: invalid character <%c> (0x%02x) before start of tag")
+#define ERR_SML_READTAG_CLOSE_EMPTY_TAG gettext("reading tag: no element name provided before close of tag")
+#define ERR_SML_READTAG_CLOSE_NO_PARENT gettext("reading tag: close tag <%s> not within any tag to close")
+#define ERR_SML_READTAG_CLOSE_TAG_EOF gettext("reading tag: unexpected EOF reading close tag name expecting tag=<%s>")
+#define ERR_SML_READTAG_CLOSE_TAG_ILLCHAR gettext("reading tag: invalid character <%c> (0x%02x) in close tag name <%s>")
+#define ERR_SML_READTAG_CLOSE_WRONG_TAG gettext("reading tag: close tag <%s> does not match current tag <%s>")
+#define ERR_SML_READTAG_EMPTY_PARMNAME gettext("reading tag: no parameter name provided tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_EMPTY_TAG gettext("reading tag: no element name provided before close of tag inside tag <%s>")
+#define ERR_SML_READTAG_PARMNAME_ILLCHAR gettext("reading tag: invalid character <%c> (0x%02x) in parameter name <%s> tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_PARMVAL_EOF gettext("reading tag: unexpected EOF reading parameter value name <%s> tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_PARMVAL_NL gettext("reading tag: unexpected newline reading parameter value name <%s> tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_PARM_EOF gettext("reading tag: unexpected EOF reading parameter name tag <%s> inside tag <%s>")
+#define ERR_SML_READTAG_TAG_EOF gettext("reading tag: unexpected EOF reading tag name <%s> inside tag <%s>")
+#define ERR_SML_READTAG_TAG_ILLCHAR gettext("reading tag: invalid character <%c> (0x%02x) in tag name <%s>")
+#define ERR_SNPRINTF gettext("Not enough memory to format, %s")
+#define ERR_SPOOLDIR_AND_ADMNFILE gettext("cannot use the -s option with the -a option")
+#define ERR_SPOOLDIR_AND_INST_ROOT gettext("cannot use the -s option with the -R option")
+#define ERR_SPOOLDIR_AND_NOINTERACT gettext("cannot use the -s option with the -n option")
+#define ERR_SPOOLDIR_AND_PKGRMREMOTE gettext("cannot use the -s option with the -A option")
+#define ERR_SPOOLDIR_AND_PKGVERBOSE gettext("cannot use the -s option with the -v option")
+#define ERR_SPOOLDIR_AND_RESPFILE gettext("cannot use the -s option with the -r option")
+#define ERR_SPOOLDIR_CANNOT_BE_SYS gettext("the -s option cannot specify %s")
+#define ERR_SPOOLDIR_USED_WITH_G gettext("the -G option cannot be used with the -s option")
+#define ERR_SPOOLDIR_USED_WITH_Z gettext("the zonelist option cannot be used with the -s option")
+#define ERR_STAT gettext("unable to stat <%s>: %s")
+#define ERR_STREAMDIR gettext("unable to make temporary directory to unpack datastream: %s")
+#define ERR_STREAM_UNAVAILABLE gettext("unable to open stream <%s> for package <%s>: %s")
+#define ERR_SYSINFO gettext("unable to process installed package information, errno=%d")
+#define ERR_THISZONE_AND_Z_USED gettext("The package <%s> has <%s> = true: the -Z option may not be used to extend this type of package to all non-global zones")
+#define ERR_TMPFILE gettext("unable to establish temporary file")
+#define ERR_TMPFILE_CHK gettext("unable to create temporary checkinstall script")
+#define ERR_TMPRESP gettext("unable to create temporary response file")
+#define ERR_TOO_MANY_CMD_ARGS gettext("too many arguments to command")
+#define ERR_TOO_MANY_PKGS gettext("too many packages referenced specified at the end of the command line: only one package may be specified")
+#define ERR_UNKNOWN_DEPENDENCY gettext("unknown dependency type specified: %c\n")
+#define ERR_UNKNOWN_DEV gettext("unknown device <%s>")
+#define ERR_UNPACK_DSREAD gettext("unable to read part <%d> of stream <%s> to directory <%s> for package <%s>")
+#define ERR_UNPACK_FMKDIR gettext("unable to create temporary package area <%s>: %s")
+#define ERR_UNSUCC gettext("(A previous attempt may have been unsuccessful.)")
+
+#ifdef Z_OPTION_IS_SUPPORTED
+#define ERR_USAGE_PKGADD_GLOBALZONE gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-G|-Z] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-G|-Z] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#else
+#define ERR_USAGE_PKGADD_GLOBALZONE gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-G] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-G] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#endif
+
+#define ERR_USAGE_PKGADD_NONGLOBALZONE gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#define ERR_USAGE_PKGASK gettext("usage: %s -r response [-d device] [-R host_path] [-Y category[,category ...]] | [pkg [pkg ...]]\n")
+#define ERR_USAGE_PKGINSTALL gettext("usage:\n\tpkginstall [-o] [-n] [-d device] [-m mountpt [-f fstype]] [-v] [[-M] -R host_path] [-V fs_file] [-b bindir] [-a admin_file] [-r resp_file] [-N calling_prog] directory pkginst\n")
+#define ERR_USAGE_PKGREMOVE gettext("usage:\n\tpkgremove [-a admin_file] [-n] [-V ...] [[-M|-A] -R host_path] [-v] [-o] [-N calling_prog] pkginst\n")
+#define ERR_USAGE_PKGRM gettext("usage:\n\t%s [-a admin] [-n] [[-M|-A] -R host_path] [-V fs_file] [-v] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s spool [-Y category[,category ...] | pkg [pkg ...]]\n")
+#define ERR_VALINST gettext(" Allowable instances include (in order of preference:)\n")
+#define ERR_V_USED_AND_PKGRMREMOTE gettext("cannot use the -V option with the -A option")
+#define ERR_V_USED_WITH_GZS gettext("cannot use the -V option when non-global zones exist")
+#define ERR_WARNING gettext("WARNING:")
+#define ERR_WRITE gettext("unable to write <%s>: (%d) %s")
+#define ERR_WTMPFILE gettext("unable to write temporary file <%s>")
+#define ERR_ZONETEMPDIR gettext("unable to make temporary directory for non-global zone operations in directory <%s>: %s")
+#define ERR_Z_USED_IN_NONGLOBAL_ZONE gettext("the zonelist option may not be used in a non-global zone")
+#define ERR_PKGINFO_COPY gettext("unable to copy %s to %s")
+#define ERR_CANNOT_ENABLE_LOCAL_FS gettext("Failed to enable the filesystem/local service.\n")
+#define ERR_CANNOT_RESTORE_LOCAL_FS gettext("Failed to bring the filesystem/local service back to its original state.\n")
+
+/*
+ * I18N: these messages are help messages that are displayed when the
+ * user answers a question with "?" - asking for help to be displayed
+ */
+
+#define HLP_PKGADDCHK_CONFLICT gettext("If you choose to install conflicting files, the files listed above will be overwritten and/or have their access permissions changed. If you choose not to install these files, installation will proceed but these specific files will not be installed. Note that sane operation of the software being installed may require these files be installed; thus choosing to not to do so may cause inappropriate operation. If you wish to stop installation of this package, enter 'q' to quit.")
+#define HLP_PKGADDCHK_CONT gettext("If you choose 'y', installation of this package will continue. If you want to stop installation of this package, choose 'n'.")
+#define HLP_PKGADDCHK_DEPEND gettext("The package being installed has indicated a dependency on the existence (or non-existence) of another software package. If this dependency is not met before continuing, the package may not install or operate properly. If you wish to disregard this dependency, answer 'y' to continue the installation process.")
+#define HLP_PKGADDCHK_PARTIAL gettext("Installation of partially installed packages is normally allowable, but some packages providers may suggest that a partially installed package be completely removed before re-attempting installation. Check the documentation provided with this package, and then answer 'y' if you feel it is advisable to continue the installation process.")
+#define HLP_PKGADDCHK_PRIV gettext("During the installation of this package, certain scripts provided with the package will execute with super-user permission. These scripts may modify or otherwise change your system without your knowledge. If you are certain of the origin and trustworthiness of the package being installed, answer 'y' to continue the installation process.")
+#define HLP_PKGADDCHK_SETUID gettext("The package being installed appears to contain processes which will have their effective user or group ids set upon execution. History has shown that these types of processes can be a source of security problems on your system. If you choose not to install these as setuid files, installation will proceed but these specific files will be installed as regular files with setuid and/or setgid permissions reset. Note that sane operation of the software being installed may require that these files be installed with setuid or setgid permissions as delivered; thus choosing to install them as regular files may cause inappropriate operation. If you wish to stop installation of this package, enter 'q' to quit.")
+#define HLP_PKGADDCHK_SPACE gettext("It appears that there is not enough free space on your system in which to install this package. It is possible that one or more filesystems are not properly mounted. Neither installation of the package nor its operation can be guaranteed under these conditions. If you choose to disregard this warning, enter 'y' to continue the installation process.")
+#define HLP_PKGREMOVE_DEPEND gettext("Other packages currently installed on the system have indicated a dependency on the package being removed. If removal of this package occurs, it may render other packages inoperative. If you wish to disregard this dependency, answer 'y' to continue the package removal process.")
+#define HLP_PKGREMOVE_PRIV gettext("During the removal of this package, certain scripts provided with the package will execute with super-user permission. These scripts may modify or otherwise change your system without your knowledge. If you are certain of the origin of the package being removed and trust its worthiness, answer 'y' to continue the package removal process.")
+#define HLP_PKGREMOVE_RUNLEVEL gettext("If this package is not removed in a run-level which has been suggested, it is possible that the package may not remove properly. If you wish to follow the run-level suggestions, answer 'n' to stop the package removal process.")
+#define HLP_PKGREMOVE_WSDEPEND gettext("Installed products in the Solaris Product Registry have registered a\ndependency on the package being removed. If removal of this package\noccurs, it may render these products inoperative. If you wish to disregard this dependency, answer 'y' to continue the package removal process.\n")
+#define HLP_PKGRMCHK_DEPEND gettext("The package being removed has indicated a dependency on the existence (or non-existence) of another software package. If this dependency is not met before continuing, the package may not remove or operate properly. If you wish to disregard this dependency, answer 'y' to continue the removal process.")
+#define HLP_PKGRMCHK_PRIV gettext("During the removal of this package, certain scripts provided with the package will execute with super-user permission. These scripts may modify or otherwise change your system without your knowledge. If you are certain of the origin and trustworthiness of the package being removed, answer 'y' to continue the removal process.")
+
+#define INFO_INSTALL gettext("\nThe following package is currently installed:")
+#define INFO_RMSPOOL gettext("\nRemoving spooled package instance <%s>")
+#define INFO_SPOOLED gettext("\nThe following package is currently spooled:")
+
+#define LOG_GETVOL_RET gettext("getvol() returned <%d>")
+
+#define MSG_1MORETODO gettext("\nThere is 1 more package to be removed.")
+#define MSG_1MORE_INST gettext("\nThere is 1 more package to be installed.")
+#define MSG_1MORE_PROC gettext("\nThere is 1 more package to be processed.")
+#define MSG_1_PKG_NOT_PROCESSED gettext("\n1 package was not processed!\n")
+#define MSG_ATTRIB gettext("%s <attribute change only>")
+#define MSG_BASE_USED gettext("Using <%s> as the package base directory.")
+#define MSG_BOOTING_ZONE gettext("## Booting non-running zone <%s> into administrative state")
+#define MSG_BYPASSING_ZONE gettext("## pkgask - bypassing zone <%s>")
+#define MSG_CHECKINSTALL_INTERRUPT_B4_Z gettext("## interrupted: package <%s> not installed")
+#define MSG_CHECKINSTALL_PKG_IN_ZONE gettext("## Verifying package <%s> dependencies in zone <%s>")
+#define MSG_CHECKREMOVE_PKG_IN_GZ gettext("## Verifying package <%s> dependencies in global zone")
+#define MSG_CHECKREMOVE_PKG_IN_ZONE gettext("## Verifying package <%s> dependencies in zone <%s>")
+#define MSG_DBUPD_N_N gettext("## Database update of part %d of %d is complete.")
+#define MSG_DBUPD_N_N_LZ gettext("## Database update of part %d of %d in zone <%s> is complete.")
+#define MSG_DIRBUSY gettext("%s <mount point not removed>")
+#define MSG_DOREMOVE_INTERRUPTED gettext("## interrupted: package <%s> not installed")
+#define MSG_DOREMOVE_INTERRUPTED_B4_Z gettext("## interrupted: package <%s> not removed")
+#define MSG_DRYRUN_DONE gettext("Dryrun complete.")
+#define MSG_EXE_INSTALL_SCRIPT gettext("## Executing INSTALL script provided by package")
+#define MSG_FAIL gettext("\n## Pre-SVR4 package reports failed installation.")
+#define MSG_HRDLINK gettext("%s <linked pathname>")
+#define MSG_IMPDIR gettext("%s <implied directory>")
+#define MSG_INSERT_VOL gettext("Insert %v into %p.")
+#define MSG_INSTALLING_PKG_IN_GZ gettext("## Installing package <%s> in global zone")
+#define MSG_INSTALLING_PSVR4 gettext("*** Installing Pre-SVR4 Package ***")
+#define MSG_INSTALL_INTERRUPT_B4_ZONES gettext("## Interrupted: package <%s> not installed in any non-global zones")
+#define MSG_INSTALL_PKG_IN_ZONE gettext("## Installing package <%s> in zone <%s>")
+#define MSG_INST_MANY gettext(" %d package pathnames are already properly installed.")
+#define MSG_INST_N_N gettext("## Installation of part %d of %d is complete.")
+#define MSG_INST_N_N_LZ gettext("## Installation of part %d of %d in zone <%s> is complete.")
+#define MSG_INST_ONE gettext(" %d package pathname is already properly installed.")
+#define MSG_INS_N_N gettext("## Installing part %d of %d.")
+#define MSG_INS_N_N_LZ gettext("## Installing part %d of %d in zone <%s>.")
+#define MSG_IS_PRESENT gettext("%s <already present on Read Only file system>")
+#define MSG_LOG_ERROR gettext("ERROR")
+#define MSG_LOG_WARNING gettext("WARNING")
+#define MSG_LOG_DEBUG gettext("DEBUG")
+#define MSG_MAIL gettext("An attempt to install the <%s> pre-SVR4 package on <%s> completed with exit status <%d>.")
+#define MSG_MANMOUNT gettext("Assuming mounts have been provided.")
+#define MSG_MORETODO gettext("\nThere are %d more packages to be removed.")
+#define MSG_MORE_INST gettext("\nThere are %d more packages to be installed.")
+#define MSG_MORE_PROC gettext("\nThere are %d more packages to be processed.")
+#define MSG_NOCHANGE gettext("No changes were made to the system.")
+#define MSG_NODENAME gettext("(unknown)")
+#define MSG_NOTEMPTY gettext("%s <non-empty directory not removed>")
+#define MSG_NOTREMOVED_INHERITED gettext("%s <not removed - inherited from global zone>")
+#define MSG_N_PKGS_NOT_PROCESSED gettext("\n%d packages were not processed!\n")
+#define MSG_PASSPROMPT gettext("Enter keystore password:")
+#define MSG_PKGADDCHK_ABADFILE gettext("\\nPackaging file <%s> is corrupt for %s <%s> on %s <%s>")
+#define MSG_PKGADDCHK_BADFILE gettext("\\nPackaging files are corrupt for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_CFCONTENT gettext("\\nThe file <%s> is already installed and in use by %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_CKRUNLVL gettext("\\nThe current run-level of this machine is <s%>, which is not a run-level suggested for installation of the %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_CNFFAILED gettext("\\nConflict checking issues for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_DEPEND gettext("\\nDependency checking issues for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_DIRS gettext("\\nThe required packaging directory <%s> cannot be created or accessed for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_NEWONLY gettext("\\nA version of %s <%s> is already installed on %s <%s>. Current administration does not allow new instances of an existing package to be created, nor existing instances to be overwritten.")
+#define MSG_PKGADDCHK_OVERWRITE gettext("\\nCurrent administration does not allow new instances of a %s <%s> on %s <%s> to be created. However, the installation service was unable to determine which package instance to overwrite.")
+#define MSG_PKGADDCHK_PARTINST gettext("\\nThe installation of %s <%s> on %s <%s> previously terminated and installation was never successfully completed.")
+#define MSG_PKGADDCHK_PARTREM gettext("\\nThe removal of %s <%s> on %s <%s> was terminated at some point in time, and package removal was only partially completed.")
+#define MSG_PKGADDCHK_PKGDIRS gettext("\\nA required packaging directory cannot be created or accessed for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_PRENCI gettext("\\nThe package <%s> is a prerequisite package and is not completely installed for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_PREREQ gettext("\\nThe package <%s> is a prerequisite package and should be installed for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_PRIV gettext("\\nThe %s <%s> contains scripts which will be executed on %s <%s> with super-user permission during the process of installing this package.")
+#define MSG_PKGADDCHK_RUNLEVEL gettext("\\n run level <%s> for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_SAME gettext("\\nThis appears to be an attempt to install the same architecture and version of %s <%s> which is already installed on %s <%s>. This installation will attempt to overwrite this package.\\n")
+#define MSG_PKGADDCHK_SETUID gettext("\\nFiles that are setuid and/or setgid will be installed and/or modified for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_SPCFAILED gettext("\\nSpace checking failed for %s <%s> on %s <%s>.")
+#define MSG_PKGADDCHK_UNIQ1 gettext("\\nCurrent administration requires that a unique instance of %s <%s> on %s <%s> be created. However, the maximum number of instances of the package which may be supported at one time on the same system has already been met.")
+#define MSG_PKGINSTALL_DRYRUN gettext("\nDryrunning install of %s as <%s>\n")
+#define MSG_PKGINSTALL_EXECOC_GZ gettext("## Executing checkinstall script.")
+#define MSG_PKGINSTALL_EXECOC_LZ gettext("## Executing checkinstall script in zone <%s>.")
+#define MSG_PKGINSTALL_EXEPIC_GZ gettext("## Executing postinstall script.")
+#define MSG_PKGINSTALL_EXEPIC_LZ gettext("## Executing postinstall script in zone <%s>.")
+#define MSG_PKGINSTALL_EXEPOC_GZ gettext("## Executing preinstall script.")
+#define MSG_PKGINSTALL_EXEPOC_LZ gettext("## Executing preinstall script in zone <%s>.")
+#define MSG_PKGINSTALL_INSIN_GZ gettext("\nInstalling %s as <%s>\n")
+#define MSG_PKGINSTALL_INSIN_LZ gettext("\nInstalling %s as <%s> in zone <%s>\n")
+#define MSG_PKGREMOVE_DEPEND gettext("Dependency checking failed.")
+#define MSG_PKGREMOVE_EXEPIC_GZ gettext("## Executing postremove script.")
+#define MSG_PKGREMOVE_EXEPIC_LZ gettext("## Executing postremove script in zone <%s>.")
+#define MSG_PKGREMOVE_EXEPOC_GZ gettext("## Executing preremove script.")
+#define MSG_PKGREMOVE_EXEPOC_LZ gettext("## Executing preremove script in zone <%s>.")
+#define MSG_PKGREMOVE_ID_STR gettext("ID")
+#define MSG_PKGREMOVE_NAME_STR gettext("Name")
+#define MSG_PKGREMOVE_PRIV gettext("\\nThis package contains scripts which will be executed with super-user permission during the process of removing this package.")
+#define MSG_PKGREMOVE_PROCPKG_GZ gettext("## Processing package information.")
+#define MSG_PKGREMOVE_PROCPKG_LZ gettext("## Processing package information in zone <%s>.")
+#define MSG_PKGREMOVE_REMPATHCLASS_GZ gettext("## Removing pathnames in class <%s>")
+#define MSG_PKGREMOVE_REMPATHCLASS_LZ gettext("## Removing pathnames in class <%s> in zone <%s>")
+#define MSG_PKGREMOVE_RUNLEVEL gettext("\\nThe current run-level of this machine is <%s>, which is not a run-level suggested for removal of this package. Suggested run-levels (in order of preference) include:")
+#define MSG_PKGREMOVE_UPDINF_GZ gettext("## Updating system information.")
+#define MSG_PKGREMOVE_UPDINF_LZ gettext("## Updating system information in zone <%s>.")
+#define MSG_PKGREMOVE_WSDEPEND gettext("Product Registry dependency checking failed. This package is assumed\nto be installed by the following products. If the package is removed, it will be 'damaged' - that is, it is lacking essential software that the product\nrequires to function. The recommended way to uninstall products is to\nuse the prodreg(1m) uninstall command.\n\nThe following products depend on the package:\n")
+#define MSG_PKGRMCHK_CKRUNLVL gettext("\\nThe current run-level of this machine is <s%>, which is not a run-level suggested for removal of the %s <%s> on %s <%s>.")
+#define MSG_PKGRMCHK_DEPEND gettext("\\nDependency checking failed for %s <%s> on %s <%s>.")
+#define MSG_PKGRMCHK_DEPSONME gettext("\\nThe package <%s> depends on %s <%s> currently being removed from %s <%s>.")
+#define MSG_PKGRMCHK_PRENCI gettext("\\nThe package <%s> is a prerequisite package and is not completely installed for %s <%s> on %s <%s>.")
+#define MSG_PKGRMCHK_PREREQ gettext("\\nThe package <%s> is a prerequisite package and should be removed for %s <%s> on %s <%s>.")
+#define MSG_PKGRMCHK_PRIV gettext("\\nThe %s <%s> contains scripts which will be executed on %s <%s> with super-user permission during the process of removing this package.")
+#define MSG_PKGRMCHK_RUNLEVEL gettext("\\n run level <%s> for %s <%s> on %s <%s>.")
+#define MSG_PKGSCRIPTS_FOUND gettext("Package scripts were found.")
+#define MSG_PREIVFY_GETYORN_SUSP gettext("\\nInstallation of <%s> was suspended (interaction required).")
+#define MSG_PREIVFY_GETYORN_TERM gettext("\\nInstallation of <%s> was terminated.")
+#define MSG_PREIVFY_GETYORN_TERM_USER gettext("\\nInstallation of <%s> was terminated due to user request.")
+#define MSG_PREREMOVE_REMINST gettext("\n## Removing installed package instance <%s>")
+#define MSG_PRERVFY_GETYORN_SUSP gettext("\\nRemoval of <%s> was suspended (interaction required).")
+#define MSG_PRERVFY_GETYORN_TERM gettext("\\nRemoval of <%s> was terminated.")
+#define MSG_PRERVFY_GETYORN_TERM_USER gettext("\\nRemoval of <%s> was terminated due to user request.")
+#define MSG_PROCMV gettext("- executing process moved to <%s>")
+#define MSG_PROC_CONT gettext("\nProcessing continuation packages from <%s>")
+#define MSG_PROC_INST gettext("\nProcessing package instance <%s> from <%s>")
+#define MSG_REMOVE_PKG_FROM_ZONE gettext("## Removing package <%s> from zone <%s>")
+#define MSG_RESTORE_ZONE_STATE gettext("## Restoring state of global zone <%s>")
+#define MSG_RMSRVR gettext("%s <removed from server's file system>")
+#define MSG_SERVER gettext("%s <server package pathname not removed>")
+#define MSG_SHARED gettext("%s <shared pathname not removed>")
+#define MSG_SHIGN gettext("%s <conflicting pathname not installed>")
+#define MSG_SKIPPING_ZONE_NOT_RUNNABLE gettext("## Not processing zone <%s>: the zone is not running and cannot be booted")
+#define MSG_SLINK gettext("%s <symbolic link>")
+#define MSG_SUCCEED gettext("\n## Pre-SVR4 package reports successful installation.")
+#define MSG_SUSPEND_ADD gettext("Installation of <%s> has been suspended.")
+#define MSG_SUSPEND_RM gettext("Removals of <%s> has been suspended.")
+#define MSG_UGID gettext("%s <installed with setuid/setgid bits reset>")
+#define MSG_UGMOD gettext("%s <reset setuid/setgid bits>")
+#define MSG_VERIFYING gettext("Verifying signer <%s>")
+#define MSG_VERIFYING_CLASS gettext("[ verifying class <%s> ]")
+
+#define PASSWD_CMDLINE gettext("## WARNING: USING \"%s\" MAKES PASSWORD VISIBLE TO ALL USERS.")
+#define SPECIAL_ACCESS gettext("unable to maintain package contents text due to an access failure: %s")
+#define SPECIAL_INPUT gettext("unable to maintain package contents text: alternate root path too long")
+#define SPECIAL_MALLOC gettext("unable to maintain package contents text due to insufficient memory: %s")
+#define SPECIAL_MAP gettext("unable to maintain package contents text due to a failure to map the database into memory: %S")
+
+#define WRN_BAD_FORK gettext("WARNING: bad fork(), errno=%d: %s")
+#define WRN_BAD_WAIT gettext("WARNING: wait for process %ld failed, pid <%ld> status <0x%08lx> errno <%d> (%s)")
+#define WRN_CHKINSTALL gettext("checkinstall script suspends")
+#define WRN_DEF_MODE gettext("WARNING: installing <%s> with default mode of 644")
+#define WRN_SET_DEF_MODE gettext("WARNING: setting mode of <%s> to default mode (%o)")
+#define WRN_FINALCK_ATTR gettext("WARNING: attribute verification of <%s> failed")
+#define WRN_FINALCK_CONT gettext("WARNING: content verification of <%s> failed")
+#define WRN_FLMAIL gettext("WARNING: e-mail notification may have failed")
+#define WRN_FSTAB_MOUNT gettext("WARNING: unable to mount client's file system at %s - errcode=%d")
+#define WRN_FSTAB_UMOUNT gettext("WARNING: unable to unmount client's file system at %s - errcode=%d.")
+#define WRN_INSTVOL_NONE gettext("WARNING: %s <not present on Read Only file system>")
+#define WRN_INSTVOL_NOTDIR gettext("WARNING: %s may not overwrite a populated directory.")
+#define WRN_INSTVOL_NOVERIFY gettext("WARNING: %s <cannot install to or verify on %s>")
+#define WRN_NOMAIL gettext("WARNING: unable to send e-mail notification")
+#define WRN_PKGREMOVE_PATCHES gettext("\\nWARNING: The following patch(es) are installed to <%s>. If <%s> is removed, the patches applied to it will be removed as well leaving the patch partially installed. It is recommended that the patch(es) be removed before removing <%s>.\\n\\t%s")
+#define WRN_RELATIVE gettext("attempting to rename a relative file <%s>")
+#define WRN_RSCRIPTALT_BAD gettext("WARNING: the admin parameter <%s> is set to <%s> which is not recognized; the parameter may only be set to <%s> or <%s>")
+#define WRN_RSCRIPTALT_USING gettext("WARNING: the admin parameter <%s> is assumed to be set to <%s>")
+#define WRN_UNKNOWN_ADM_PARAM gettext("WARNING: unknown admin parameter <%s>")
+
+#define NOTE_INSTVOL_FINALCKFAIL gettext("NOTE: When the package <%s> was installed in the global zone,\nthe file <%s> was also installed. After the file was\ninstalled in the global zone, the contents and/or attributes of the file\nchanged. The contents of this file must never be changed. As a result,\nthe changes in this file have been duplicated in the non-global zone\n<%s> in the file <%s>.")
+
+#define MSG_REBOOT gettext("\\n*** IMPORTANT NOTICE ***\\n" \
+ "\\tThis machine must now be rebooted in order to " \
+ "ensure\\n" \
+ "\\tsane operation. Execute\\n\\t\\tshutdown -y -i6 " \
+ "-g0\\n" \
+ "\\tand wait for the \"Console Login:\" prompt.")
+
+/*
+ * These messages are output by qreason() - they are the "reason"
+ * for the success/fail of the operation
+ */
+
+#define MSG_UNKREQ gettext \
+ ("qreason(): unrecognized message request.")
+#define MSG_RE_SUC gettext \
+ ("Processing of request script was successful.")
+#define MSG_IN_SUC0 gettext \
+ ("Installation of <%s> was successful.")
+#define MSG_IN_SUC1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> was successful.")
+#define MSG_RM_SUC0 gettext \
+ ("Removal of <%s> was successful.")
+#define MSG_RM_SUC1 gettext \
+ ("\nRemoval of <%s> package instance on %s was " \
+ "successful.")
+#define MSG_RE_FAIL gettext \
+ ("Processing of request script failed.")
+#define MSG_IN_FAIL0 gettext \
+ ("Installation of <%s> failed.")
+#define MSG_IN_FAIL1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> failed.")
+#define MSG_RM_FAIL0 gettext \
+ ("Removal of <%s> failed.")
+#define MSG_RM_FAIL1 gettext \
+ ("\nRemoval of <%s> package instance on %s failed.")
+#define MSG_RE_PARFAIL gettext \
+ ("Processing of request script partially failed.")
+#define MSG_IN_PARFAIL0 gettext \
+ ("Installation of <%s> partially failed.")
+#define MSG_IN_PARFAIL1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> partially failed.")
+#define MSG_RM_PARFAIL0 gettext \
+ ("Removal of <%s> partially failed.")
+#define MSG_RM_PARFAIL1 gettext \
+ ("\nRemoval of <%s> package instance on %s partially " \
+ "failed.")
+#define MSG_RE_USER gettext \
+ ("Processing of request script was terminated due to " \
+ "user request.")
+#define MSG_IN_USER0 gettext \
+ ("Installation of <%s> was terminated due to user " \
+ "request.")
+#define MSG_IN_USER1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> was terminated due to user request.")
+#define MSG_RM_USER0 gettext \
+ ("Removal of <%s> was terminated due to user request.")
+#define MSG_RM_USER1 gettext \
+ ("\nRemoval of <%s> package instance on %s was " \
+ "terminated due to user request.")
+#define MSG_RE_SUA gettext \
+ ("Processing of request script was suspended " \
+ "(administration).")
+#define MSG_IN_SUA0 gettext \
+ ("Installation of <%s> was suspended (administration).")
+#define MSG_IN_SUA1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> was suspended (administration).")
+#define MSG_RM_SUA0 gettext \
+ ("Removal of <%s> was suspended (administration).")
+#define MSG_RM_SUA1 gettext \
+ ("\nRemoval of <%s> package instance on %s was " \
+ "suspended (administration).")
+#define MSG_RE_SUI gettext \
+ ("Processing of request script was suspended " \
+ "(interaction required).")
+#define MSG_IN_SUI0 gettext \
+ ("Installation of <%s> was suspended (interaction " \
+ "required).")
+#define MSG_IN_SUI1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> was suspended (interaction required).")
+#define MSG_RM_SUI0 gettext \
+ ("Removal of <%s> was suspended (interaction " \
+ "required).")
+#define MSG_RM_SUI1 gettext \
+ ("\nRemoval of <%s> package instance on %s was " \
+ "suspended (interaction required).")
+#define MSG_RE_IEPI gettext \
+ ("Processing of request script failed (internal " \
+ "error) - package partially installed.")
+#define MSG_IN_IEPI0 gettext \
+ ("Installation of <%s> failed (internal error) - " \
+ "package partially installed.")
+#define MSG_IN_IEPI1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> failed (internal error) - package partially " \
+ "installed.")
+#define MSG_RM_IEPI0 gettext \
+ ("Removal of <%s> failed (internal error) - package " \
+ "partially installed.")
+#define MSG_RM_IEPI1 gettext \
+ ("\nRemoval of <%s> package instance on %s failed " \
+ "(internal error) - package partially installed.")
+#define MSG_RE_IE gettext \
+ ("Processing of request script failed (internal " \
+ "error).")
+#define MSG_IN_IE0 gettext \
+ ("Installation of <%s> failed (internal error).")
+#define MSG_IN_IE1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> failed (internal error).")
+#define MSG_RM_IE0 gettext \
+ ("Removal of <%s> failed (internal error).")
+#define MSG_RM_IE1 gettext \
+ ("\nRemoval of <%s> package instance on %s failed " \
+ "(internal error).")
+#define MSG_RE_UNK gettext \
+ ("Processing of request script failed with an " \
+ "unrecognized error code.")
+#define MSG_IN_UNK0 gettext \
+ ("Installation of <%s> failed with an unrecognized " \
+ "error code.")
+#define MSG_IN_UNK1 gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> failed with an unrecognized error code.")
+#define MSG_RM_UNK0 gettext \
+ ("Removal of <%s> failed with an unrecognized error " \
+ "code.")
+#define MSG_RM_UNK1 gettext \
+ ("\nRemoval of <%s> package instance on %s failed " \
+ "with an unrecognized error code.")
+/* WITH ZONE NAME */
+#define MSG_UNKREQ_ZONE gettext \
+ ("qreason(): unrecognized message request.")
+#define MSG_RE_SUC_ZONE gettext \
+ ("Processing of request script for zone <%s> was " \
+ "successful.")
+#define MSG_IN_SUC0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> was successful.")
+#define MSG_IN_SUC1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> was successful.")
+#define MSG_RM_SUC0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> was successful.")
+#define MSG_RM_SUC1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> was successful.")
+#define MSG_RE_FAIL_ZONE gettext \
+ ("Processing of request script for zone <%s> failed.")
+#define MSG_IN_FAIL0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> failed.")
+#define MSG_IN_FAIL1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> failed.")
+#define MSG_RM_FAIL0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> failed.")
+#define MSG_RM_FAIL1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from zone " \
+ "<%s> failed.")
+#define MSG_RE_PARFAIL_ZONE gettext \
+ ("Processing of request script partially failed on " \
+ "zone <%s>.")
+#define MSG_IN_PARFAIL0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> partially failed.")
+#define MSG_IN_PARFAIL1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> partially failed.")
+#define MSG_RM_PARFAIL0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> partially failed.")
+#define MSG_RM_PARFAIL1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from zone " \
+ "<%s> partially failed.")
+#define MSG_RE_USER_ZONE gettext \
+ ("Processing of request script on zone <%s> was " \
+ "terminated due to user request.")
+#define MSG_IN_USER0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> was terminated " \
+ "due to user request.")
+#define MSG_IN_USER1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> was terminated due to user request.")
+#define MSG_RM_USER0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> was terminated due " \
+ "to user request.")
+#define MSG_RM_USER1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> was terminated due to user request.")
+#define MSG_RE_SUA_ZONE gettext \
+ ("Processing of request script on zone <%s> was " \
+ "suspended (administration).")
+#define MSG_IN_SUA0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> was suspended " \
+ "(administration).")
+#define MSG_IN_SUA1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> was suspended (administration).")
+#define MSG_RM_SUA0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> was suspended " \
+ "(administration).")
+#define MSG_RM_SUA1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> was suspended (administration).")
+#define MSG_RE_SUI_ZONE gettext \
+ ("Processing of request script on zone <%s> was " \
+ "suspended (interaction required).")
+#define MSG_IN_SUI0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> was suspended " \
+ "(interaction required).")
+#define MSG_IN_SUI1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> was suspended (interaction " \
+ "required).")
+#define MSG_RM_SUI0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> was suspended " \
+ "(interaction required).")
+#define MSG_RM_SUI1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> was suspended (interaction required).")
+#define MSG_RE_IEPI_ZONE gettext \
+ ("Processing of request script on zone <%s> " \
+ "failed (internal error) - package partially " \
+ "installed.")
+#define MSG_IN_IEPI0_ZONE gettext \
+ ("Installation of <%s> on zone failed (internal " \
+ "error) on zone <%s> - package partially installed.")
+#define MSG_IN_IEPI1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ " <%s> on zone <%s> failed (internal error) - " \
+ "package partially installed.")
+#define MSG_RM_IEPI0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> failed (internal " \
+ "error) - package partially installed.")
+#define MSG_RM_IEPI1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> failed (internal error) - package " \
+ "partially installed.")
+#define MSG_RE_IE_ZONE gettext \
+ ("Processing of request script on zone <%s> failed " \
+ "(internal error).")
+#define MSG_IN_IE0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> failed (internal " \
+ "error).")
+#define MSG_IN_IE1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> failed (internal error).")
+#define MSG_RM_IE0_ZONE gettext \
+ ("Removal of <%s> on zone <%s> failed (internal " \
+ "error).")
+#define MSG_RM_IE1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> failed (internal error).")
+#define MSG_RE_UNK_ZONE gettext \
+ ("Processing of request script on zone <%s> failed " \
+ "with an unrecognized error code.")
+#define MSG_IN_UNK0_ZONE gettext \
+ ("Installation of <%s> on zone <%s> failed with an " \
+ "unrecognized error code.")
+#define MSG_IN_UNK1_ZONE gettext \
+ ("\nInstallation of %s on %s as package instance " \
+ "<%s> on zone <%s> failed with an unrecognized " \
+ "error code.")
+#define MSG_RM_UNK0_ZONE gettext \
+ ("Removal of <%s> from zone <%s> failed with an " \
+ "unrecognized error code.")
+#define MSG_RM_UNK1_ZONE gettext \
+ ("\nRemoval of <%s> package instance on %s from " \
+ "zone <%s> failed with an unrecognized error code.")
+
+#define MSG_UNIQ1 gettext( \
+ "\\nCurrent administration requires that a unique " \
+ "instance of the <%s> package be created. However, " \
+ "the maximum number of instances of the package " \
+ "which may be supported at one time on the same " \
+ "system has already been met.")
+
+#define MSG_NOINTERACT gettext( \
+ "\\nUnable to determine whether to overwrite an " \
+ "existing package instance, or add a new instance.")
+
+#define MSG_NEWONLY gettext( \
+ "\\nA version of the <%s> package is already " \
+ "installed on this machine. Current administration " \
+ "does not allow new instances of an existing package " \
+ "to be created, nor existing instances to be " \
+ "overwritten.")
+
+#define MSG_SAME gettext( \
+ "\\nThis appears to be an attempt to install the " \
+ "same architecture and version of a package which " \
+ "is already installed. This installation will " \
+ "attempt to overwrite this package.\\n")
+
+#define MSG_OVERWRITE gettext( \
+ "\\nCurrent administration does not allow new " \
+ "instances of an existing package to be created. " \
+ "However, the installation service was unable to " \
+ "determine which package instance to overwrite.")
+
+
+#define MSG_GETINST_PROMPT0 gettext( \
+ "Do you want to overwrite this installed instance")
+
+#define MSG_GETINST_PROMPT1 gettext( \
+ "Do you want to create a new instance of this package")
+
+#define MSG_GETINST_HELP1 gettext( \
+ "The package you are attempting to install already exists " \
+ "on this machine. You may choose to create a new instance " \
+ "of this package by answering 'y' to this prompt. If you " \
+ "answer 'n' you will be asked to choose one of the instances " \
+ "which is already to be overwritten.")
+
+#define MSG_GETINST_HEADER gettext( \
+ "The following instance(s) of the <%s> package are already " \
+ "installed on this machine:")
+
+#define MSG_GETINST_PROMPT2 gettext( \
+ "Enter the identifier for the instance that you want to " \
+ "overwrite")
+
+#define MSG_GETINST_HELP2 gettext( \
+ "The package you are attempting to install already exists on " \
+ "this machine. You may choose to overwrite one of the " \
+ "versions which is already installed by selecting the " \
+ "appropriate entry from the menu.")
+
+/*
+ * I18N: MSG_GZONLY_FILE_HEADER must NOT be translated!
+ * ----- This message is placed at the beginning of an internal (private)
+ * ----- database file. The contents of the message is a warning telling
+ * ----- anyone who examines the contents of the database to not modify the
+ * ----- database manually (by hand).
+ * ----- Do NOT change or translate this text!
+ */
+
+#define MSG_GZONLY_FILE_HEADER \
+"# DO NOT EDIT THIS FILE BY HAND. This file is not a public interface.\n" \
+"# The format and contents of this file are subject to change.\n" \
+"# Any user modification to this file may result in the incorrect\n" \
+"# operation of the package and patch tools.\n" \
+"# Last modified by <%s> to <%s> package <%s>\n# %s"
+
+/* END CSTYLED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESSAGES_H */
diff --git a/usr/src/cmd/svr4pkg/installf/Makefile b/usr/src/cmd/svr4pkg/installf/Makefile
new file mode 100644
index 0000000000..da8ff4b2b7
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/Makefile
@@ -0,0 +1,49 @@
+#
+# 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= installf
+
+OBJS= installf.o \
+ main.o \
+ removef.o \
+ dofinal.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+ROOTUSRSBINLINK = $(ROOTUSRSBIN)/removef
+
+LDLIBS += -lpkg -linstzones -ladm
+LDLIBS += -lnsl -lsocket
+
+.KEEP_STATE:
+
+all: $(PROG)
+install: all $(ROOTUSRSBINPROG) $(ROOTUSRSBINLINK)
+
+$(ROOTUSRSBINLINK): $(ROOTUSRSBINPROG)
+ $(RM) $@; $(LN) $(ROOTUSRSBINPROG) $@
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/installf/dofinal.c b/usr/src/cmd/svr4pkg/installf/dofinal.c
new file mode 100644
index 0000000000..331ea43810
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/dofinal.c
@@ -0,0 +1,236 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+
+extern struct cfextra **extlist;
+extern struct cfent **eptlist;
+
+extern char *pkginst;
+
+#define ERR_WRITE "write of intermediate contents file failed"
+
+static char *check_db_entry(VFP_T *, struct cfextra *, int, char *, int *);
+
+/*ARGSUSED*/
+int
+dofinal(VFP_T *vfp, VFP_T *vfpo, int rmflag, char *myclass, char *prog)
+{
+ struct cfextra entry;
+ int n, indx, dbchg;
+ char *save_path = NULL;
+
+ entry.cf_ent.pinfo = NULL;
+ entry.fsys_value = BADFSYS;
+ entry.fsys_base = BADFSYS;
+ indx = 0;
+
+ while (extlist && extlist[indx] && (extlist[indx]->cf_ent.ftype == 'i'))
+ indx++;
+
+ dbchg = 0;
+
+ while (n = srchcfile(&(entry.cf_ent), "*", vfp, vfpo)) {
+ if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext
+ ("bad entry read in contents file"));
+ logerr(gettext("pathname=%s"),
+ (entry.cf_ent.path &&
+ *(entry.cf_ent.path)) ?
+ entry.cf_ent.path : "Unknown");
+ logerr(gettext("problem=%s"),
+ (errstr && *errstr) ? errstr :
+ "Unknown");
+ quit(99);
+ }
+ save_path = check_db_entry(
+ vfpo, &entry, rmflag, myclass, &dbchg);
+
+ /* Restore original server-relative path, if needed */
+ if (save_path != NULL) {
+ entry.cf_ent.path = save_path;
+ save_path = NULL;
+ }
+ }
+
+ return (dbchg);
+}
+
+static char *
+check_db_entry(VFP_T *vfpo, struct cfextra *entry, int rmflag, char *myclass,
+ int *dbchg)
+{
+ struct pinfo *pinfo;
+ int fs_entry;
+ char *save_path = NULL;
+ char *tp;
+
+ /* write this entry to the contents file */
+
+ if (myclass && strcmp(myclass, entry->cf_ent.pkg_class)) {
+ if (putcvfpfile(&entry->cf_ent, vfpo)) {
+ progerr(gettext(ERR_WRITE));
+ quit(99);
+ }
+ return (NULL);
+ }
+
+ /*
+ * Now scan each package instance holding this file or
+ * directory and see if it matches the package we are
+ * updating here.
+ */
+ pinfo = entry->cf_ent.pinfo;
+ while (pinfo) {
+ if (strcmp(pkginst, pinfo->pkg) == 0)
+ break;
+ pinfo = pinfo->next;
+ }
+
+ /*
+ * If pinfo == NULL at this point, then this file or
+ * directory isn't part of the package of interest.
+ * So the loop below executes only on files in the package
+ * of interest.
+ */
+
+ save_path = NULL;
+
+ if (pinfo) {
+ if (rmflag && (pinfo->status == RM_RDY)) {
+ *dbchg = 1;
+
+ (void) eptstat(&(entry->cf_ent), pkginst, '@');
+
+ if (entry->cf_ent.npkgs) {
+ if (putcvfpfile(&(entry->cf_ent), vfpo)) {
+ progerr(gettext(ERR_WRITE));
+ quit(99);
+ }
+ }
+ return (NULL);
+
+ } else if (!rmflag && (pinfo->status == INST_RDY)) {
+ *dbchg = 1;
+
+ /* tp is the server-relative path */
+ tp = fixpath(entry->cf_ent.path);
+ /* save_path is the cmd line path */
+ save_path = entry->cf_ent.path;
+ /* entry has the server-relative path */
+ entry->cf_ent.path = tp;
+
+ /*
+ * The next if statement figures out how
+ * the contents file entry should be
+ * annotated.
+ *
+ * Don't install or verify objects for
+ * remote, read-only filesystems. We
+ * need only verify their presence and
+ * flag them appropriately from some
+ * server. Otherwise, ok to do final
+ * check.
+ */
+ fs_entry = fsys(entry->cf_ent.path);
+
+ if (is_remote_fs_n(fs_entry) &&
+ !is_fs_writeable_n(fs_entry)) {
+ /*
+ * Mark it shared whether it's present
+ * or not. life's too funny for me
+ * to explain.
+ */
+ pinfo->status = SERVED_FILE;
+
+ /*
+ * restore for now. This may
+ * chg soon.
+ */
+ entry->cf_ent.path = save_path;
+ } else {
+ /*
+ * If the object is accessible, check
+ * the new entry for existence and
+ * attributes. If there's a problem,
+ * mark it NOT_FND; otherwise,
+ * ENTRY_OK.
+ */
+ if (is_mounted_n(fs_entry)) {
+ int n;
+
+ n = finalck((&entry->cf_ent), 1, 1,
+ B_FALSE);
+
+ pinfo->status = ENTRY_OK;
+ if (n != 0) {
+ pinfo->status = NOT_FND;
+ }
+ }
+
+ /*
+ * It's not remote, read-only but it
+ * may look that way to the client.
+ * If it does, overwrite the above
+ * result - mark it shared.
+ */
+ if (is_served_n(fs_entry))
+ pinfo->status = SERVED_FILE;
+
+ /* restore original path */
+ entry->cf_ent.path = save_path;
+ /* and clear save_path */
+ save_path = NULL;
+ }
+ }
+ }
+
+ /* Output entry to contents file. */
+ if (putcvfpfile(&(entry->cf_ent), vfpo)) {
+ progerr(gettext(ERR_WRITE));
+ quit(99);
+ }
+
+ return (save_path);
+}
diff --git a/usr/src/cmd/svr4pkg/installf/installf.c b/usr/src/cmd/svr4pkg/installf/installf.c
new file mode 100644
index 0000000000..b0eab9cedc
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/installf.c
@@ -0,0 +1,308 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include "installf.h"
+
+#define LSIZE 1024
+#define MALSIZ 164
+
+#define ERR_MAJOR "invalid major number <%s> specified for <%s>"
+#define ERR_MINOR "invalid minor number <%s> specified for <%s>"
+#define ERR_MODE "invalid mode <%s> specified for <%s>"
+#define ERR_RELPATH "relative pathname <%s> not permitted"
+#define ERR_NULLPATH "NULL or garbled pathname"
+#define ERR_LINK "invalid link specification <%s>"
+#define ERR_LINKFTYPE "ftype <%c> does not match link specification <%s>"
+#define ERR_LINKARGS "extra arguments in link specification <%s>"
+#define ERR_LINKREL "relative pathname in link specification <%s>"
+#define ERR_FTYPE "invalid ftype <%c> for <%s>"
+#define ERR_ARGC "invalid number of arguments for <%s>"
+#define ERR_SPECALL "ftype <%c> requires all fields to be specified"
+
+static int validate(struct cfextra *ext, int argc, char *argv[]);
+static void checkPaths(char *argv[]);
+
+int
+installf(int argc, char *argv[])
+{
+ struct cfextra *new;
+ char line[LSIZE];
+ char *largv[8];
+ int myerror;
+
+ if (strcmp(argv[0], "-") != 0) {
+ if (argc < 1)
+ usage(); /* at least pathname is required */
+ extlist = calloc(2, sizeof (struct cfextra *));
+ extlist[0] = new = calloc(1, sizeof (struct cfextra));
+ eptnum = 1;
+
+ /* There is only one filename on the command line. */
+ checkPaths(argv);
+ if (validate(new, argc, argv))
+ quit(1);
+ return (0);
+ }
+
+ /* Read stdin to obtain entries, which need to be sorted. */
+ eptnum = 0;
+ myerror = 0;
+ extlist = calloc(MALSIZ, sizeof (struct cfextra *));
+ while (fgets(line, LSIZE, stdin) != NULL) {
+ argc = 0;
+ argv = largv;
+ argv[argc++] = strtok(line, " \t\n");
+ while (argv[argc] = strtok(NULL, " \t\n"))
+ argc++;
+
+ if (argc < 1)
+ usage(); /* at least pathname is required */
+
+ new = calloc(1, sizeof (struct cfextra));
+ if (new == NULL) {
+ progerr(strerror(errno));
+ quit(99);
+ }
+
+ checkPaths(argv);
+
+ if (validate(new, argc, argv))
+ myerror++;
+
+ extlist[eptnum] = new;
+ if ((++eptnum % MALSIZ) == 0) {
+ extlist = realloc(extlist,
+ (sizeof (struct cfextra *) * (eptnum+MALSIZ)));
+ if (!extlist) {
+ progerr(strerror(errno));
+ quit(99);
+ }
+ }
+ }
+ extlist[eptnum] = (struct cfextra *)NULL;
+ qsort((char *)extlist, (unsigned)eptnum, sizeof (struct cfextra *),
+ cfentcmp);
+ return (myerror);
+}
+
+static int
+validate(struct cfextra *ext, int argc, char *argv[])
+{
+ char *ret, *pt;
+ int n, allspec, is_a_link;
+ struct cfent *ept;
+
+ ept = &(ext->cf_ent);
+
+ /* initialize cfent structure */
+ ept->pinfo = NULL;
+ (void) gpkgmapvfp(ept, (VFP_T *)NULL); /* This just clears stuff. */
+
+ n = allspec = 0;
+ if (classname)
+ (void) strncpy(ept->pkg_class, classname, CLSSIZ);
+
+ if (argv[n] == NULL || *(argv[n]) == '\000') {
+ progerr(gettext(ERR_NULLPATH));
+ return (1);
+ }
+
+ /*
+ * It would be a good idea to figure out how to get much of
+ * this done using facilities in procmap.c - JST
+ */
+ if (pt = strchr(argv[n], '=')) {
+ *pt = '\0'; /* cut off pathname at the = sign */
+ is_a_link = 1;
+ } else
+ is_a_link = 0;
+
+ if (RELATIVE(argv[n])) {
+ progerr(gettext(ERR_RELPATH),
+ (argv[n] == NULL) ? "unknown" : argv[n]);
+ return (1);
+ }
+
+ /* get the pathnames */
+ if (eval_path(&(ext->server_path), &(ext->client_path),
+ &(ext->map_path), argv[n++]) == 0)
+ return (1);
+
+ ept->path = ext->client_path;
+
+ /* This isn't likely to happen; but, better safe than sorry. */
+ if (RELATIVE(ept->path)) {
+ progerr(gettext(ERR_RELPATH), ept->path);
+ return (1);
+ }
+
+ if (is_a_link) {
+ /* links specifications should be handled right here */
+ ept->ftype = ((n >= argc) ? 'l' : argv[n++][0]);
+
+ /* If nothing follows the '=', it's invalid */
+ if (!pt[1]) {
+ progerr(gettext(ERR_LINK), ept->path);
+ return (1);
+ }
+
+ /* Test for an argument after the link. */
+ if (argc != n) {
+ progerr(gettext(ERR_LINKARGS), ept->path);
+ return (1);
+ }
+
+ /*
+ * If it's a link but it's neither hard nor symbolic then
+ * it's bad.
+ */
+ if (!strchr("sl", ept->ftype)) {
+ progerr(gettext(ERR_LINKFTYPE), ept->ftype, ept->path);
+ return (1);
+ }
+
+ ext->server_local = pathdup(pt+1);
+ ext->client_local = ext->server_local;
+
+ ept->ainfo.local = ext->client_local;
+
+ return (0);
+ } else if (n >= argc) {
+ /* we are expecting to change object's contents */
+ return (0);
+ }
+
+ ept->ftype = argv[n++][0];
+ if (strchr("sl", ept->ftype)) {
+ progerr(gettext(ERR_LINK), ept->path);
+ return (1);
+ } else if (!strchr("?fvedxcbp", ept->ftype)) {
+ progerr(gettext(ERR_FTYPE), ept->ftype, ept->path);
+ return (1);
+ }
+
+ if (ept->ftype == 'b' || ept->ftype == 'c') {
+ if (n < argc) {
+ ept->ainfo.major = strtol(argv[n++], &ret, 0);
+ if (ret && *ret) {
+ progerr(gettext(ERR_MAJOR), argv[n-1],
+ ept->path);
+ return (1);
+ }
+ }
+ if (n < argc) {
+ ept->ainfo.minor = strtol(argv[n++], &ret, 0);
+ if (ret && *ret) {
+ progerr(gettext(ERR_MINOR), argv[n-1],
+ ept->path);
+ return (1);
+ }
+ allspec++;
+ }
+ }
+
+ allspec = 0;
+ if (n < argc) {
+ ept->ainfo.mode = strtol(argv[n++], &ret, 8);
+ if (ret && *ret) {
+ progerr(gettext(ERR_MODE), argv[n-1], ept->path);
+ return (1);
+ }
+ }
+ if (n < argc)
+ (void) strncpy(ept->ainfo.owner, argv[n++], ATRSIZ);
+ if (n < argc) {
+ (void) strncpy(ept->ainfo.group, argv[n++], ATRSIZ);
+ allspec++;
+ }
+ if (strchr("dxbcp", ept->ftype) && !allspec) {
+ progerr(gettext(ERR_ARGC), ept->path);
+ progerr(gettext(ERR_SPECALL), ept->ftype);
+ return (1);
+ }
+ if (n < argc) {
+ progerr(gettext(ERR_ARGC), ept->path);
+ return (1);
+ }
+ return (0);
+}
+
+int
+cfentcmp(const void *p1, const void *p2)
+{
+ struct cfextra *ext1 = *((struct cfextra **)p1);
+ struct cfextra *ext2 = *((struct cfextra **)p2);
+
+ return (strcmp(ext1->cf_ent.path, ext2->cf_ent.path));
+}
+
+/*
+ * If the path at argv[0] has the value of
+ * PKG_INSTALL_ROOT prepended, remove it
+ */
+static void
+checkPaths(char *argv[])
+{
+ char *root;
+ int rootLen;
+
+ /*
+ * Note- No local copy of argv is needed since this
+ * function is guaranteed to replace argv with a subset of
+ * the original argv.
+ */
+
+ /* We only want to canonize the path if it contains multiple '/'s */
+
+ canonize_slashes(argv[0]);
+
+ if ((root = get_inst_root()) == NULL)
+ return;
+ if (strcmp(root, "/") != 0) {
+ rootLen = strlen(root);
+ if (strncmp(argv[0], root, rootLen) == 0) {
+ argv[0] += rootLen;
+ }
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/installf/installf.h b/usr/src/cmd/svr4pkg/installf/installf.h
new file mode 100644
index 0000000000..6d473aa3af
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/installf.h
@@ -0,0 +1,57 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _INSTALLF_H
+#define _INSTALLF_H
+
+
+/*
+ * Block comment that describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <cfext.h>
+
+extern struct cfextra **extlist;
+extern int eptnum;
+extern int warnflag;
+extern char *classname;
+
+extern int cfentcmp(const void *, const void *);
+extern void quit(int);
+extern void usage(void);
+extern void removef(int, char *[]);
+extern int installf(int, char *[]);
+extern int dofinal(VFP_T *, VFP_T *, int, char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INSTALLF_H */
diff --git a/usr/src/cmd/svr4pkg/installf/main.c b/usr/src/cmd/svr4pkg/installf/main.c
new file mode 100644
index 0000000000..8c70add8ed
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/main.c
@@ -0,0 +1,545 @@
+/*
+ * 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 <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkginfo.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+#include "installf.h"
+
+#define BASEDIR "/BASEDIR/"
+
+#define INSTALF (*prog == 'i')
+#define REMOVEF (*prog == 'r')
+
+#define MSG_MANMOUNT "Assuming mounts were provided."
+
+#define ERR_PKGNAME_TOO_LONG \
+"The package name specified on the command line\n" \
+"exceeds the maximum package name length: a package name may contain a\n" \
+"maximum of <%d> characters; however, the package name specified on\n" \
+"the command line contains <%d> characters, which exceeds the maximum\n" \
+"package name length by <%d> characters. Please specify a package name\n" \
+"that contains no more than <%d> characters."
+
+#define ERR_DB_GET "unable to retrieve entries from the database."
+#define ERR_DB_PUT "unable to update the package database."
+#define ERR_ROOT_SET "Could not set install root from the environment."
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+#define ERR_CLASSLONG "classname argument too long"
+#define ERR_CLASSCHAR "bad character in classname"
+#define ERR_INVAL "package instance <%s> is invalid"
+#define ERR_NOTINST "package instance <%s> is not installed"
+#define ERR_MERG "unable to merge contents file"
+#define ERR_SORT "unable to sort contents file"
+#define ERR_I_FAIL "installf did not complete successfully"
+#define ERR_R_FAIL "removef did not complete successfully"
+#define ERR_NOTROOT "You must be \"root\" for %s to execute properly."
+#define ERR_USAGE0 "usage:\n" \
+ "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path " \
+ "[path ...]\n" \
+ "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path\n"
+
+#define ERR_USAGE1 "usage:\n" \
+ "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
+ "<path>\n" \
+ "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
+ "<path> <specs>\n" \
+ "\t where <specs> may be defined as:\n" \
+ "\t\tf <mode> <owner> <group>\n" \
+ "\t\tv <mode> <owner> <group>\n" \
+ "\t\te <mode> <owner> <group>\n" \
+ "\t\td <mode> <owner> <group>\n" \
+ "\t\tx <mode> <owner> <group>\n" \
+ "\t\tp <mode> <owner> <group>\n" \
+ "\t\tc <major> <minor> <mode> <owner> <group>\n" \
+ "\t\tb <major> <minor> <mode> <owner> <group>\n" \
+ "\t\ts <path>=<srcpath>\n" \
+ "\t\tl <path>=<srcpath>\n" \
+ "\t%s [[-M] -R host_path] [-V ...] [-c class] -f pkginst\n"
+
+#define CMD_SORT "sort +0 -1"
+
+#define LINK 1
+
+extern char dbst; /* libinst/pkgdbmerg.c */
+
+struct cfextra **extlist;
+struct pinfo **eptlist;
+
+char *classname = NULL;
+char *pkginst;
+char *uniTmp;
+char *abi_sym_ptr;
+char *ulim;
+char *script;
+
+int eptnum;
+int sortflag;
+int nosetuid;
+int nocnflct;
+int warnflag = 0;
+
+/* libadm/pkgparam.c */
+extern void set_PKGADM(char *newpath);
+extern void set_PKGLOC(char *newpath);
+
+extern void set_limit(void);
+
+int
+main(int argc, char **argv)
+{
+ FILE *pp;
+ VFP_T *cfTmpVfp;
+ VFP_T *cfVfp;
+ char *cmd;
+ char *tp;
+ char *prog;
+ char *pt;
+ char *vfstab_file = NULL;
+ char line[1024];
+ char outbuf[PATH_MAX];
+ int c;
+ int dbchg;
+ int err;
+ int fflag = 0;
+ int map_client = 1;
+ int n;
+ int pkgrmremote = 0; /* don't remove remote files */
+ struct cfent *ept;
+
+ /* hookup signals */
+
+ (void) signal(SIGHUP, exit);
+ (void) signal(SIGINT, exit);
+ (void) signal(SIGQUIT, exit);
+
+ /* initialize locale mechanism */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif /* !defined(TEXT_DOMAIN) */
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* determine program name */
+
+ prog = set_prog_name(argv[0]);
+
+ /* tell instzones interface how to access package output functions */
+
+ z_set_output_functions(echo, echoDebug, progerr);
+
+ /* only allow root to run this program */
+
+ if (getuid() != 0) {
+ progerr(gettext(ERR_NOTROOT), prog);
+ exit(1);
+ }
+
+ ulim = getenv("PKG_ULIMIT");
+ script = getenv("PKG_PROC_SCRIPT");
+
+ if (ulim && script) {
+ set_limit();
+ clr_ulimit();
+ }
+
+ /* bug id 4244631, not ABI compliant */
+ abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+ if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)
+ set_nonABI_symlinks();
+
+ /* bugId 4012147 */
+ if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
+ map_client = 0;
+ if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+ progerr(gettext(ERR_ROOT_SET));
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) {
+ switch (c) {
+ case 'f':
+ fflag++;
+ break;
+
+ case 'c':
+ classname = optarg;
+ /* validate that classname is acceptable */
+ if (strlen(classname) > (size_t)CLSSIZ) {
+ progerr(gettext(ERR_CLASSLONG));
+ exit(1);
+ }
+ for (pt = classname; *pt; pt++) {
+ if (!isalpha(*pt) && !isdigit(*pt)) {
+ progerr(gettext(ERR_CLASSCHAR));
+ exit(1);
+ }
+ }
+ break;
+
+ /*
+ * Don't map the client filesystem onto the server's. Assume
+ * the mounts have been made for us.
+ */
+ case 'M':
+ map_client = 0;
+ break;
+
+ /*
+ * Allow admin to establish the client filesystem using a
+ * vfstab-like file of stable format.
+ */
+ case 'V':
+ vfstab_file = flex_device(optarg, 2);
+ map_client = 1;
+ break;
+
+ case 'A':
+ pkgrmremote++;
+ break;
+
+ case 'R': /* added for newroot option */
+ if (!set_inst_root(optarg)) {
+ progerr(gettext(ERR_ROOT_CMD));
+ exit(1);
+ }
+ break;
+
+ 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);
+ }
+ }
+
+ if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Get the mount table info and store internally.
+ */
+ if (get_mntinfo(map_client, vfstab_file))
+ exit(1);
+
+ /*
+ * This function defines the standard /var/... directories used later
+ * to construct the paths to the various databases.
+ */
+ (void) 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));
+ }
+
+ sortflag = 0;
+
+ /*
+ * get the package name and verify length is not too long
+ */
+
+ pkginst = argv[optind++];
+ if (pkginst == NULL) {
+ usage();
+ /*NOTREACHED*/
+
+ }
+
+ n = strlen(pkginst);
+ if (n > PKGSIZ) {
+ progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ,
+ PKGSIZ);
+ usage();
+ /*NOTREACHED*/
+ }
+
+ /*
+ * The following is used to setup the environment. Note that the
+ * variable 'BASEDIR' is only meaningful for this utility if there
+ * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this
+ * utility can create a file or directory anywhere unfettered by
+ * the basedir associated with the package instance.
+ */
+ if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0)
+ exit(err);
+
+ if (INSTALF)
+ mkbasedir(0, get_basedir());
+
+ if (fflag) {
+ /* installf and removef must only have pkginst */
+ if (optind != argc) {
+ usage();
+ /*NOTREACHED*/
+ }
+ } else {
+ /*
+ * installf and removef must have at minimum
+ * pkginst & pathname specified on command line
+ */
+ if (optind >= argc) {
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ if (REMOVEF) {
+ if (classname) {
+ usage();
+ }
+ }
+ if (pkgnmchk(pkginst, "all", 0)) {
+ progerr(gettext(ERR_INVAL), pkginst);
+ exit(1);
+ }
+ if (fpkginst(pkginst, NULL, NULL) == NULL) {
+ progerr(gettext(ERR_NOTINST), pkginst);
+ exit(1);
+ }
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+ /* Until 2.9, set it from the execption list */
+ if (pkginst && exception_pkg(pkginst, LINK))
+ set_nonABI_symlinks();
+#endif
+ /*
+ * This maps the client filesystems into the server's space.
+ */
+ if (map_client && !mount_client())
+ logerr(gettext(MSG_MANMOUNT));
+
+ /* open the package database (contents) file */
+
+ if (!ocfile(&cfVfp, &cfTmpVfp, 0L)) {
+ quit(1);
+ }
+
+ if (fflag) {
+ dbchg = dofinal(cfVfp, cfTmpVfp, REMOVEF, classname, prog);
+ } else {
+ if (INSTALF) {
+ dbst = INST_RDY;
+ if (installf(argc-optind, &argv[optind]))
+ quit(1);
+ } else {
+ dbst = RM_RDY;
+ removef(argc-optind, &argv[optind]);
+ }
+
+ dbchg = pkgdbmerg(cfVfp, cfTmpVfp, extlist, 0);
+ if (dbchg < 0) {
+ progerr(gettext(ERR_MERG));
+ quit(99);
+ }
+ }
+
+ if (dbchg) {
+ if ((n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1))
+ == RESULT_WRN) {
+ warnflag++;
+ } else if (n == RESULT_ERR) {
+ quit(99);
+ }
+ }
+
+ relslock();
+
+ if (REMOVEF && !fflag) {
+ for (n = 0; extlist[n]; n++) {
+ ept = &(extlist[n]->cf_ent);
+
+ /* Skip duplicated paths */
+ if ((n > 0) && (strncmp(ept->path,
+ extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
+ continue;
+ }
+
+ if (!extlist[n]->mstat.shared) {
+ /*
+ * Only output paths that can be deleted.
+ * so need to skip if the object is owned
+ * by a remote server and removal is not
+ * being forced.
+ */
+ if (ept->pinfo &&
+ (ept->pinfo->status == SERVED_FILE) &&
+ !pkgrmremote)
+ continue;
+
+ c = 0;
+ if (is_a_cl_basedir() && !is_an_inst_root()) {
+ c = strlen(get_client_basedir());
+ (void) snprintf(outbuf, sizeof (outbuf),
+ "%s/%s\n", get_basedir(),
+ &(ept->path[c]));
+ } else if (is_an_inst_root()) {
+ (void) snprintf(outbuf, sizeof (outbuf),
+ "%s/%s\n", get_inst_root(),
+ &(ept->path[c]));
+ } else {
+ (void) snprintf(outbuf, sizeof (outbuf),
+ "%s\n", &(ept->path[c]));
+ }
+ canonize(outbuf);
+ (void) printf("%s", outbuf);
+ }
+ }
+ } else if (INSTALF && !fflag) {
+ for (n = 0; extlist[n]; n++) {
+ ept = &(extlist[n]->cf_ent);
+
+ if (strchr("dxcbp", ept->ftype)) {
+ tp = fixpath(ept->path);
+ (void) averify(1, &ept->ftype,
+ tp, &ept->ainfo);
+ }
+ }
+ }
+
+ /* Sort the contents files if needed */
+ if (sortflag) {
+ int n;
+
+ warnflag += (ocfile(&cfVfp, &cfTmpVfp, 0L)) ? 0 : 1;
+ if (!warnflag) {
+ size_t len;
+
+ len = strlen(CMD_SORT) + strlen(get_PKGADM()) +
+ strlen("/contents") + 5;
+ cmd = (char *)malloc(len);
+ (void) snprintf(cmd, len, "%s %s/contents",
+ CMD_SORT, get_PKGADM());
+ pp = popen(cmd, "r");
+ if (pp == NULL) {
+ (void) vfpClose(&cfVfp);
+ (void) vfpClose(&cfTmpVfp);
+ free(cmd);
+ progerr(gettext(ERR_SORT));
+ quit(1);
+ }
+ while (fgets(line, 1024, pp) != NULL) {
+ if (line[0] != DUP_ENTRY) {
+ vfpPuts(cfTmpVfp, line);
+ }
+ }
+ free(cmd);
+ (void) pclose(pp);
+ n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1);
+ if (n == RESULT_WRN) {
+ warnflag++;
+ } else if (n == RESULT_ERR) {
+ quit(99);
+ }
+
+ relslock(); /* Unlock the database. */
+ }
+ }
+
+ z_destroyMountTable();
+
+ quit(warnflag ? 1 : 0);
+ /* LINTED: no return */
+}
+
+void
+quit(int n)
+{
+ char *prog = get_prog_name();
+
+ unmount_client();
+
+ if (ulim && script) {
+ if (REMOVEF) {
+ set_ulimit(script, gettext(ERR_R_FAIL));
+ } else {
+ set_ulimit(script, gettext(ERR_I_FAIL));
+ }
+ }
+
+ exit(n);
+}
+
+void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ if (REMOVEF) {
+ (void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
+ } else {
+ (void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
+ }
+ exit(1);
+}
diff --git a/usr/src/cmd/svr4pkg/installf/removef.c b/usr/src/cmd/svr4pkg/installf/removef.c
new file mode 100644
index 0000000000..6b7f8bdead
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/installf/removef.c
@@ -0,0 +1,127 @@
+/*
+ * 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 2008 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 <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include "installf.h"
+
+void
+removef(int argc, char *argv[])
+{
+ struct cfextra *new;
+ char buf[PATH_MAX];
+ char *path;
+ int flag;
+ int len;
+ int max_eptnum;
+
+ flag = strcmp(argv[0], "-") == 0;
+
+ eptnum = 0;
+ max_eptnum = 64; /* starting size of array */
+ extlist = malloc(max_eptnum * sizeof (struct cfextra *));
+
+ for (;;) {
+ if (flag) {
+ if (fgets(buf, PATH_MAX, stdin) == NULL)
+ break;
+
+ /* strip trailing new line */
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ path = buf;
+ } else {
+ if (argc-- <= 0)
+ break;
+ path = argv[argc];
+ }
+
+ /*
+ * This strips the install root from the path using
+ * a questionable algorithm. This should go away as
+ * we define more precisely the command line syntax
+ * with our '-R' option. - JST
+ */
+ path = orig_path_ptr(path);
+
+ if (path == NULL) {
+ logerr(gettext("ERROR: no pathname was provided"));
+ warnflag++;
+ continue;
+ }
+
+ if (*path != '/') {
+ logerr(gettext(
+ "WARNING: relative pathname <%s> ignored"), path);
+ warnflag++;
+ continue;
+ }
+
+ new = calloc(1, sizeof (struct cfextra));
+ if (new == NULL) {
+ progerr(strerror(errno));
+ quit(99);
+ }
+ new->cf_ent.ftype = '-';
+
+ (void) eval_path(&(new->server_path), &(new->client_path),
+ &(new->map_path), path);
+
+ new->cf_ent.path = new->client_path;
+
+ extlist[eptnum++] = new;
+ if (eptnum >= max_eptnum) {
+ /* array size grows exponentially */
+ max_eptnum <<= 1;
+ extlist = realloc(extlist,
+ max_eptnum * sizeof (struct cfextra *));
+ if (extlist == NULL) {
+ progerr(strerror(errno));
+ quit(99);
+ }
+ }
+ }
+ extlist[eptnum] = (struct cfextra *)NULL;
+
+ qsort((char *)extlist,
+ (unsigned)eptnum, sizeof (struct cfextra *), cfentcmp);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/Makefile b/usr/src/cmd/svr4pkg/libinst/Makefile
new file mode 100644
index 0000000000..9c482fe43a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/Makefile
@@ -0,0 +1,81 @@
+#
+# 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= libinst.a
+
+OBJS= copyf.o dockdeps.o echo.o eptstat.o \
+ finalck.o findscripts.o fixpath.o flex_dev.o \
+ isreloc.o lockinst.o mntinfo.o nblk.o \
+ ocfile.o pathdup.o pkgdbmerg.o procmap.o \
+ pkgobjmap.o psvr4ck.o ptext.o putparam.o \
+ qreason.o qstrdup.o setadmin.o setlist.o \
+ srcpath.o scriptvfy.o stub.o doulimit.o \
+ dryrun.o listmgr.o is_local_host.o cvtpath.o \
+ depchk.o pkgops.o sml.o log.o \
+ setup_temporary_directory.o open_package_datastream.o \
+ unpack_package_from_stream.o
+SRCS = $(OBJS:.o=.c)
+
+include $(SRC)/cmd/Makefile.cmd
+
+#
+# For messaging catalog
+POFILE = libinst.po
+MSGFILES=$(OBJS:%.o=%.i)
+
+CPPFLAGS += -I$(SRC)/cmd/svr4pkg/hdrs \
+ -I$(SRC)/lib/libpkg/common \
+ -I$(SRC)/lib/libinstzones/common \
+ -D_FILE_OFFSET_BITS=64
+
+# Lint flags
+#
+LINTFLAGS += -un
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+ $(POST_PROCESS_A)
+
+install: all
+ @echo "$(PROG) is a static library and will not be installed."
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+clean:
+ $(RM) $(OBJS) $(MSGFILES)
+
+clobber: clean
+ $(RM) $(PROG) $(POFILE)
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/svr4pkg/libinst/copyf.c b/usr/src/cmd/svr4pkg/libinst/copyf.c
new file mode 100644
index 0000000000..265341357c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/copyf.c
@@ -0,0 +1,512 @@
+/*
+ * 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 2005 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 <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <libintl.h>
+#include <sys/mman.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * local pkg command library includes
+ */
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * MAXMAPSIZE controls the largest mapping to use at a time; please refer
+ * to mmap(2) for details of how this size is incremented and rounded; briefly
+ * each mapping request has an additional 16Kb added to it - mappings over
+ * 4Mb will be rounded to a 4Mb boundary - thus if there were 8mb, adding
+ * in the 16Kb overhead the mapping would use another 4Mb-16kb - that is
+ * why there is 16Kb subtracted from the total
+ */
+
+#define MAXMAPSIZE (1024*1024*8)-(1024*16) /* map at most 8MB */
+#define SMALLFILESIZE (32*1024) /* dont mmap files less than 32kb */
+
+/*
+ * Name: copyF
+ * Description: fast copy of file - use mmap()/write() loop if possible
+ * Arguments: char *srcPath - name of source file to copy from
+ * char *dstPath - name of target file to copy to
+ * time_t a_mytime: control setting of access/modification times:
+ * == 0 - replicate source file access/modification times
+ * != 0 - use specified time for access/modification times
+ * Returns: int
+ * == 0 - successful
+ * != 0 - failure
+ */
+
+int
+copyf(char *a_srcPath, char *a_dstPath, time_t a_mytime)
+{
+ struct stat srcStatbuf;
+ struct utimbuf times;
+ int srcFd;
+ int dstFd;
+ int status;
+ char *pt;
+
+ /* open source file for reading */
+
+ srcFd = open(a_srcPath, O_RDONLY, 0);
+ if (srcFd < 0) {
+ progerr(ERR_OPEN_READ, a_srcPath, errno, strerror(errno));
+ return (-1);
+ }
+
+ /* obtain file status of source file */
+
+ if (fstat(srcFd, &srcStatbuf) != 0) {
+ progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno));
+ (void) close(srcFd);
+ return (-1);
+ }
+
+ /* open target file for writing */
+
+ dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT,
+ srcStatbuf.st_mode);
+ if (dstFd < 0) {
+ /* create directory structure if missing */
+ pt = a_dstPath;
+ while (pt = strchr(pt+1, '/')) {
+ *pt = '\0';
+ if (isdir(a_dstPath)) {
+ if (mkdir(a_dstPath, 0755)) {
+ progerr(ERR_NODIR, a_dstPath,
+ errno, strerror(errno));
+ *pt = '/';
+ (void) close(srcFd);
+ return (-1);
+ }
+ }
+ *pt = '/';
+ }
+
+ /* attempt to create target file again */
+ dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT,
+ srcStatbuf.st_mode);
+ if (dstFd < 0) {
+ progerr(ERR_OPEN_WRITE, a_dstPath, errno,
+ strerror(errno));
+ (void) close(srcFd);
+ return (-1);
+ }
+ }
+
+ /*
+ * source and target files are open: copy data
+ */
+
+ status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0);
+
+ (void) close(srcFd);
+ (void) close(dstFd);
+
+ /*
+ * determine how to set access/modification times for target:
+ * -- a_mytime == 0: replicate source file access/modification times
+ * -- otherwise: use a_mytime for file access/modification times
+ */
+
+ if (a_mytime == 0) {
+ times.actime = srcStatbuf.st_atime;
+ times.modtime = srcStatbuf.st_mtime;
+ } else {
+ times.actime = a_mytime;
+ times.modtime = a_mytime;
+ }
+
+ /* set access/modification times for target */
+
+ if (utime(a_dstPath, &times) != 0) {
+ progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno));
+ return (-1);
+ }
+
+ /* return error if copy failed */
+
+ if (status != 0) {
+ progerr(ERR_READ, a_srcPath, errno, strerror(errno));
+ return (-1);
+ }
+
+ /* success! */
+
+ return (0);
+}
+
+/*
+ * Name: copyFile
+ * Description: fast copy of file - use mmap()/write() loop if possible
+ * Arguments: int srcFd - file descriptor open on source file
+ * int dstFd - file descriptor open on target file
+ * char *srcPath - name of source file (for error messages)
+ * char *dstPath - name of target file (for error messages)
+ * struct stat *a_srcStatbuf - stat structure for source file
+ * long a_iosize - preferred io size for read/write loop
+ * Returns: int
+ * == 0 - successful
+ * != 0 - failure
+ */
+
+int
+copyFile(int a_srcFd, int a_dstFd, char *a_srcPath, char *a_dstPath,
+ struct stat *a_srcStatbuf, long a_iosize)
+{
+ caddr_t cp;
+ off_t filesize = a_srcStatbuf->st_size;
+ size_t mapsize = 0;
+ size_t munmapsize = 0;
+ off_t offset = 0;
+
+ echoDebug(DBG_COPY_FILE, a_srcPath, a_dstPath);
+
+ /*
+ * if the source is a regular file and is not "too small", then cause
+ * the file to be mapped into memory
+ */
+
+ if (S_ISREG(a_srcStatbuf->st_mode) && (filesize > SMALLFILESIZE)) {
+ /*
+ * Determine size of initial mapping. This will determine the
+ * size of the address space chunk we work with. This initial
+ * mapping size will be used to perform munmap() in the future.
+ */
+
+ mapsize = MAXMAPSIZE;
+ if (filesize < mapsize) {
+ mapsize = filesize;
+ }
+
+ /*
+ * remember size of mapping to "unmap" - if the source file
+ * exceeds MAXMAPSIZE bytes, then the final mapping of the
+ * source file will be less than MAXMAPSIZE, and we need to
+ * make sure that the entire mapping is unmapped when done.
+ */
+
+ munmapsize = mapsize;
+
+ /* map the first segment of the source into memory */
+
+ cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
+ (MAP_SHARED|MAP_ALIGN), a_srcFd, (off_t)0);
+ if (cp == MAP_FAILED) {
+ mapsize = 0; /* can't mmap today */
+ }
+ }
+
+ /*
+ * if the source was not mapped into memory, copy via read/write loop
+ */
+
+ if (mapsize == 0) {
+ char *buf = (char *)NULL;
+ size_t blocksize;
+ int pagesize = getpagesize();
+
+ /* set blocksize for copy */
+
+ blocksize = a_iosize;
+ if ((blocksize == 0) || (blocksize > SMALLFILESIZE)) {
+ blocksize = SMALLFILESIZE;
+ } else if (blocksize < pagesize) {
+ blocksize = pagesize;
+ }
+
+ /* allocate i/o transfer buffer */
+
+ buf = memalign((size_t)pagesize, blocksize);
+ if (buf == (char *)NULL) {
+ progerr(ERR_COPY_MEMORY, a_srcPath, errno,
+ strerror(errno));
+ return (1);
+ }
+
+ /* copy the file contents */
+
+ for (;;) {
+ ssize_t n;
+
+ /* read next block of data */
+
+ n = read(a_srcFd, buf, blocksize);
+ if (n == 0) {
+ /* end of file - return success */
+ (void) free(buf);
+ return (0);
+ } else if (n < 0) {
+ /* read error - return error */
+ progerr(ERR_READ, a_srcPath,
+ errno, strerror(errno));
+ (void) free(buf);
+ return (1);
+ }
+
+ /* write out block of data just read in */
+
+ if (vfpSafeWrite(a_dstFd, buf, (size_t)n) != n) {
+ /* short write/write error - return error */
+ progerr(ERR_WRITE, a_dstPath,
+ errno, strerror(errno));
+ (void) free(buf);
+ return (1);
+ }
+ }
+ }
+
+ /*
+ * the source has been mapped into memory, copy via mappings
+ */
+
+ for (;;) {
+ ssize_t nbytes;
+
+ /* write first mappings worth of data */
+
+ nbytes = write(a_dstFd, cp, mapsize);
+
+ /*
+ * if we write less than the mmaped size it's due to a
+ * media error on the input file or out of space on
+ * the output file. So, try again, and look for errno.
+ */
+
+ if ((nbytes >= 0) && (nbytes != (ssize_t)mapsize)) {
+ size_t remains;
+
+ remains = mapsize - nbytes;
+ while (remains > 0) {
+ nbytes = write(a_dstFd,
+ (cp + mapsize - remains), remains);
+ if (nbytes >= 0) {
+ remains -= nbytes;
+ if (remains == 0) {
+ nbytes = mapsize;
+ }
+ continue;
+ }
+
+ /* i/o error - report and exit */
+
+ if (errno == ENOSPC) {
+ progerr(ERR_WRITE, a_dstPath,
+ errno, strerror(errno));
+ } else {
+ progerr(ERR_READ, a_srcPath,
+ errno, strerror(errno));
+ }
+
+ /* unmap source file mapping */
+ (void) munmap(cp, munmapsize);
+ return (1);
+ }
+ }
+
+ /*
+ * although the write manual page doesn't specify this
+ * as a possible errno, it is set when the nfs read
+ * via the mmap'ed file is accessed, so report the
+ * problem as a source access problem, not a target file
+ * problem
+ */
+
+ if (nbytes < 0) {
+ if (errno == EACCES) {
+ progerr(ERR_READ, a_srcPath,
+ errno, strerror(errno));
+ } else {
+ progerr(ERR_WRITE, a_dstPath,
+ errno, strerror(errno));
+ }
+
+ /* unmap source file mapping */
+ (void) munmap(cp, munmapsize);
+ return (1);
+ }
+
+ filesize -= nbytes;
+ if (filesize == 0) {
+ break;
+ }
+
+ offset += nbytes;
+ if (filesize < mapsize) {
+ mapsize = filesize;
+ }
+
+ /* map next segment of file on top of existing mapping */
+
+ cp = mmap(cp, mapsize, PROT_READ, (MAP_SHARED|MAP_FIXED),
+ a_srcFd, offset);
+
+ if (cp == MAP_FAILED) {
+ progerr(ERR_MAPFAILED, a_srcPath, errno,
+ strerror(errno));
+ /* unmap source file mapping */
+ (void) munmap(cp, munmapsize);
+ return (1);
+ }
+ }
+
+ /* unmap source file mapping */
+
+ (void) munmap(cp, munmapsize);
+
+ return (0);
+}
+
+/*
+ * Name: openLocal
+ * Description: open a file and assure that the descriptor returned is open on
+ * a file that is local to the current system - if the file is not
+ * local to this system, copy the file to a temporary file first,
+ * and then pass a handle back opened on the temporary file
+ * Arguments: a_path - [RO, *RO] - (char *)
+ * Pointer to string representing the path to the file
+ * to open
+ * a_oflag - [RO, *RO] - (int)
+ * Integer representing the "mode" bits for an open(2) call
+ * a_tmpdir - [RO, *RO] - (char *)
+ * Pointer to string representing the path to a directory
+ * where a temporary copy of the file can be placed if
+ * the source file is not local to this system. If this is
+ * NULL or does not exist, P_tmpdir is used.
+ * Returns: int
+ * >= 0 - file descriptor opened on the file
+ * == -1 - failed to open - errno contains error code
+ * NOTE: If the file is not local and is copied locally, the file is
+ * setup in such a way that it will be removed when the last
+ * file descriptor opened on the file is closed - there is no need
+ * to know the path to the temporary file or to remove it
+ * when done.
+ */
+
+int
+openLocal(char *a_path, int a_oflag, char *a_tmpdir)
+{
+ char *bn;
+ char template[PATH_MAX];
+ int fd;
+ int lerrno;
+ int n;
+ int tmpFd;
+ struct stat statbuf;
+
+ /* open source file */
+
+ fd = open(a_path, a_oflag);
+ if (fd < 0) {
+ return (fd);
+ }
+
+ /* return open fd if the source file is not remote */
+
+ if (!isFdRemote(fd)) {
+ return (fd);
+ }
+
+ /*
+ * source file is remote - must make a local copy
+ */
+
+ /* get the source file's status */
+
+ n = fstat(fd, &statbuf);
+ if (n < 0) {
+ lerrno = errno;
+ (void) close(fd);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /* generate unique temporary file name */
+
+ if ((a_tmpdir == (char *)NULL) || (*a_tmpdir == '\0') ||
+ (isdir(a_tmpdir) != 0)) {
+ a_tmpdir = P_tmpdir;
+ }
+ bn = basename(a_path);
+ n = strlen(a_tmpdir);
+ n = snprintf(template, sizeof (template), "%s%s%sXXXXXX",
+ a_tmpdir, a_tmpdir[n-1] == '/' ? "" : "/", bn);
+ if (n > sizeof (template)) {
+ (void) close(fd);
+ return (EINVAL);
+ }
+
+ /* create the temporary file and open it */
+
+ tmpFd = mkstemp(template);
+ if (tmpFd < 0) {
+ lerrno = errno;
+ (void) close(fd);
+ errno = lerrno;
+ return (tmpFd);
+ }
+
+ /* unlink the file so when it is closed it is automatically deleted */
+
+ (void) unlink(template);
+
+ /* copy the source file to the temporary file */
+
+ n = copyFile(fd, tmpFd, a_path, template, &statbuf, 0L);
+ lerrno = errno;
+ (void) close(fd);
+ if (n != 0) {
+ (void) close(tmpFd);
+ errno = lerrno;
+ return (-1);
+ }
+
+ /* return handle to temporary file created */
+
+ return (tmpFd);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/cvtpath.c b/usr/src/cmd/svr4pkg/libinst/cvtpath.c
new file mode 100644
index 0000000000..b3c984a819
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/cvtpath.c
@@ -0,0 +1,55 @@
+/*
+ * 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 1993 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <string.h>
+
+extern char *root, *basedir; /* WHERE? */
+
+void
+cvtpath(char *path, char *copy)
+{
+ *copy++ = '/';
+ if (root || (basedir && (*path != '/'))) {
+ if (root && ((basedir == NULL) || (path[0] == '/') ||
+ (basedir[0] != '/'))) {
+ /* look in root */
+ (void) strcpy(copy, root + (*root == '/' ? 1 : 0));
+ copy += strlen(copy);
+ if (copy[-1] != '/')
+ *copy++ = '/';
+ }
+ if (basedir && (*path != '/')) {
+ (void) strcpy(copy,
+ basedir + (*basedir == '/' ? 1 : 0));
+ copy += strlen(copy);
+ if (copy[-1] != '/')
+ *copy++ = '/';
+ }
+ }
+ (void) strcpy(copy, path + (*path == '/' ? 1 : 0));
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/depchk.c b/usr/src/cmd/svr4pkg/libinst/depchk.c
new file mode 100644
index 0000000000..736cda857a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/depchk.c
@@ -0,0 +1,349 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <assert.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/*
+ * forward declarations
+ */
+
+static int
+collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
+ depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
+ int a_errIndex);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+depchkReportErrors(depckl_t *a_dck)
+{
+ char *packageName;
+ char *zonenames;
+ char msgbuf[4096];
+ int err;
+ int i;
+ int numzones = 0;
+
+ /* entry assertions */
+
+ assert(a_dck != (depckl_t *)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_DEPCHK_ENTRY);
+
+ zonenames = (char *)NULL;
+
+ /* go through dependency table, collect, collapse, report errors */
+
+ for (i = 0; a_dck[i].name != (char *)NULL; i++) {
+ int j;
+ depckError_t *erc;
+
+ if (zonenames != (char *)NULL) {
+ free(zonenames);
+ zonenames = (char *)NULL;
+ }
+
+ erc = a_dck[i].record;
+ if (erc->er_numEntries == 0) {
+ continue;
+ }
+
+ for (j = 0; j < erc->er_numEntries; j++) {
+ int k;
+ depckErrorRecord_t *eir;
+
+ if (zonenames != (char *)NULL) {
+ free(zonenames);
+ zonenames = (char *)NULL;
+ }
+
+ eir = &erc->er_theEntries[j];
+ packageName = eir->ier_packageName;
+ for (k = 0; k < eir->ier_numZones; k++) {
+ int err;
+
+ err = collectError(&numzones, &zonenames,
+ packageName, a_dck, i, eir, k);
+ if (err != 0) {
+ if (zonenames != (char *)NULL) {
+ free(zonenames);
+ zonenames = (char *)NULL;
+ }
+ return (err);
+ }
+ }
+
+ if (a_dck[i].ignore_values == (char *)NULL) {
+ continue;
+ }
+
+ if (a_dck[i].err_msg == (char **)NULL) {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ ERR_DEPENDENCY_IGNORED, a_dck[i].name,
+ packageName,
+ numzones == 1 ? "zone" : "zones",
+ zonenames ? zonenames : "?");
+ } else {
+ /* LINTED variable format specifier to ... */
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ *a_dck[i].err_msg, "package",
+ packageName,
+ numzones == 1 ? "zone" : "zones",
+ zonenames ? zonenames : "??");
+ }
+
+ if (a_dck[i].depcklFunc != NULL) {
+ /* call check function */
+ err = (a_dck[i].depcklFunc)(msgbuf,
+ packageName);
+ echoDebug(DBG_DEPCHK_REPORT_ERROR,
+ a_dck[i].ignore_values, err,
+ packageName, msgbuf);
+ if (err != 0) {
+ if (zonenames != (char *)NULL) {
+ free(zonenames);
+ zonenames = (char *)NULL;
+ }
+ return (err);
+ }
+ } else {
+ /* no check function - just report message */
+ echoDebug(DBG_DEPCHK_IGNORE_ERROR,
+ a_dck[i].ignore_values, packageName,
+ msgbuf);
+ ptext(stderr, "\\n%s", msgbuf);
+ }
+ }
+ }
+
+ if (zonenames != (char *)NULL) {
+ free(zonenames);
+ zonenames = (char *)NULL;
+ }
+
+ return (0);
+}
+
+void
+depchkRecordError(depckError_t *a_erc, char *a_pkginst,
+ char *a_zoneName, char *a_value)
+{
+ depckErrorRecord_t *erc;
+ int i;
+
+ /*
+ * create new error record and entry if first entry
+ * record will look like this:
+ * err->er_#entry=1
+ * err->entry[0]->record->ier_numZones=1
+ * err->entry[0]->record->ier_packageName=a_pkginst
+ * err->entry[0]->record->ier_zones[0]=a_zoneName
+ * err->entry[0]->record->ier_values[0]=a_value
+ */
+
+ if (a_erc->er_numEntries == 0) {
+ depckErrorRecord_t *eir;
+
+ eir = (depckErrorRecord_t *)calloc(1,
+ sizeof (depckErrorRecord_t));
+ eir->ier_packageName = strdup(a_pkginst);
+ eir->ier_numZones = 1;
+ eir->ier_zones = (char **)calloc(1, sizeof (char **));
+ (eir->ier_zones)[eir->ier_numZones-1] = strdup(a_zoneName);
+ eir->ier_values = (char **)calloc(1, sizeof (char *));
+ (eir->ier_values)[eir->ier_numZones-1] = strdup(a_value);
+
+ a_erc->er_numEntries = 1;
+ a_erc->er_theEntries = eir;
+
+ echoDebug(DBG_DEPCHK_RECORD_ERROR, (long)a_erc, a_pkginst,
+ a_zoneName, a_value);
+
+ return;
+ }
+
+ /* see if this package already has an entry if so add zone to list */
+
+ for (i = 0; i < a_erc->er_numEntries; i++) {
+ erc = &a_erc->er_theEntries[i];
+
+ if (strcmp(erc->ier_packageName, a_pkginst) != 0) {
+ continue;
+ }
+
+ echoDebug(DBG_DEPCHK_RECORD_ZERROR, (long)a_erc, a_zoneName,
+ a_value, erc->ier_packageName, erc->ier_numZones,
+ erc->ier_zones[0]);
+
+ /*
+ * this package already has an entry - add zone to
+ * existing package entry the modified records will
+ * look like this:
+ * err->er_#entry++;
+ * err->entry[0]->...
+ * err->entry[i]->
+ * -------------->record->
+ * ---------------------->ier_numZones++;
+ * ---------------------->ier_packageName=a_pkginst
+ * ---------------------->ier_zones[0]=...
+ * ---------------------->ier_zones[...]=...
+ * ---------------------->ier_zones[ier_numZones-1]=a_zoneName
+ * ---------------------->ier_values[0]=...
+ * ---------------------->ier_values[...]=...
+ * ---------------------->ier_values[ier_numZones-1]=a_value
+ * err->entry[i+1]->...
+ */
+ erc->ier_numZones++;
+ erc->ier_zones = (char **)realloc(erc->ier_zones,
+ sizeof (char **)*erc->ier_numZones);
+ (erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
+ erc->ier_values = (char **)realloc(erc->ier_values,
+ sizeof (char **)*erc->ier_numZones);
+ (erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
+ return;
+ }
+
+ /*
+ * this packages does not have an entry - add new package
+ * entry for this zone the modified records will look like this:
+ * err->er_#entry++;
+ * err->entry[0]->record->ier_numZones=...
+ * err->entry[0]->record->ier_packageName=...
+ * err->entry[0]->record->ier_zones[0]=...
+ * err->entry[0]->record->ier_values[0]=...
+ * err->entry[er_#entry-1]->record->ier_numZones=1
+ * err->entry[er_#entry-1]->record->ier_packageName=a_pkginst
+ * err->entry[er_#entry-1]->record->ier_zones[0]=a_zoneName
+ * err->entry[er_#entry-1]->record->ier_values[0]=a_value
+ */
+
+ echoDebug(DBG_DEPCHK_RECORD_PERROR, (long)a_erc,
+ a_erc->er_numEntries, a_pkginst, a_zoneName, a_value);
+
+ a_erc->er_numEntries++;
+
+ a_erc->er_theEntries = realloc(a_erc->er_theEntries,
+ sizeof (depckErrorRecord_t)*a_erc->er_numEntries);
+
+ erc = &a_erc->er_theEntries[a_erc->er_numEntries-1];
+
+ erc->ier_packageName = strdup(a_pkginst);
+ erc->ier_numZones = 1;
+ erc->ier_zones = (char **)calloc(1, sizeof (char *));
+ (erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
+ erc->ier_values = (char **)calloc(1, sizeof (char *));
+ (erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static int
+collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
+ depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
+ int a_errIndex)
+{
+ char msgbuf[4096];
+ char *zn = *r_zoneNames;
+
+ if (a_dck[a_depIndex].ignore_values == (char *)NULL) {
+ if (a_dck[a_depIndex].err_msg == (char **)NULL) {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ ERR_DEPENDENCY_REPORT, a_eir->ier_values[a_errIndex],
+ "package", a_packageName,
+ "zone", a_eir->ier_zones[a_errIndex]);
+ } else {
+ /* LINTED variable format specifier to snprintf(); */
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ *a_dck[a_depIndex].err_msg,
+ a_eir->ier_values[a_errIndex],
+ "package", a_packageName,
+ "zone", a_eir->ier_zones[a_errIndex]);
+ }
+ if (a_dck[a_depIndex].depcklFunc != NULL) {
+ int err;
+
+ err = (a_dck[a_depIndex].depcklFunc)(msgbuf,
+ a_packageName);
+ echoDebug(DBG_DEPCHK_COLLECT_ERROR, err, a_packageName,
+ msgbuf);
+ if (err != 0) {
+ return (err);
+ }
+ } else {
+ echoDebug(DBG_DEPCHK_COLLECT_IGNORE, a_packageName,
+ msgbuf);
+ ptext(stderr, "\\n%s", msgbuf);
+ }
+ return (0);
+ }
+
+ *r_numZones = (*r_numZones)+1;
+ if (zn == (char *)NULL) {
+ zn = strdup(a_eir->ier_zones[a_errIndex]);
+ } else {
+ char *p;
+ int len = strlen(zn)+strlen(a_eir->ier_zones[a_errIndex])+3;
+ p = calloc(1, len);
+ (void) snprintf(p, len, "%s, %s", zn,
+ a_eir->ier_zones[a_errIndex]);
+ free(zn);
+ zn = p;
+
+ }
+ *r_zoneNames = zn;
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/dockdeps.c b/usr/src/cmd/svr4pkg/libinst/dockdeps.c
new file mode 100644
index 0000000000..195a680727
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/dockdeps.c
@@ -0,0 +1,452 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+#define LSIZE 256
+#define NVERS 50
+
+/*
+ * internal global variables
+ */
+
+static struct pkginfo info;
+
+static char type;
+static char *alist[NVERS];
+static char *rmpkginst;
+static char *vlist[NVERS];
+static char file[128];
+static char name[128];
+static char rmpkg[PKGSIZ+1];
+static char wabbrev[128];
+
+static int errflg = 0;
+static int nlist;
+static int pkgexist;
+static int pkgokay;
+static int is_update;
+static int is_patch_update;
+
+/*
+ * IMPORTANT NOTE: THE SIZE OF 'abbrev' IS HARD CODED INTO THE CHARACTER
+ * ARRAY SSCANF_FORMAT -- YOU MUST UPDATE BOTH VALUES AT THE SAME TIME!!
+ */
+
+static char abbrev[128+1];
+static char *SSCANF_FORMAT = "%c %128s %[^\n]";
+
+/*
+ * forward declarations
+ */
+
+static void ckrdeps(boolean_t a_preinstallCheck);
+static void ckpreq(FILE *fp, char *dname, boolean_t a_preinstallCheck);
+static void deponme(char *pkginst, char *pkgname,
+ boolean_t a_preinstallCheck);
+static void prereq(char *pkginst, char *pkgname,
+ boolean_t a_preinstallCheck);
+static void incompat(char *pkginst, char *pkgname,
+ boolean_t a_preinstallCheck);
+static int getline(FILE *fp);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+dockdeps(char *a_depfile, int a_removeFlag, boolean_t a_preinstallCheck)
+{
+ FILE *fp;
+ int i;
+ char *inst;
+
+ if (a_removeFlag) {
+ /* check removal dependencies */
+ rmpkginst = a_depfile;
+ (void) strncpy(rmpkg, rmpkginst, PKGSIZ);
+ (void) strtok(rmpkg, ".");
+ (void) snprintf(file, sizeof (file),
+ "%s/%s/%s", pkgdir, rmpkginst, DEPEND_FILE);
+ if ((fp = fopen(file, "r")) == NULL)
+ goto done;
+ } else {
+ if ((fp = fopen(a_depfile, "r")) == NULL) {
+ progerr(ERR_CANNOT_OPEN_DEPEND_FILE, a_depfile,
+ strerror(errno));
+ quit(99);
+ }
+ }
+
+ while (getline(fp)) {
+ switch (type) {
+ case 'I':
+ case 'P':
+ if (a_removeFlag) {
+ continue;
+ }
+ break;
+
+ case 'R':
+ if (!a_removeFlag) {
+ continue;
+ }
+ break;
+
+ default:
+ errflg++;
+ progerr(ERR_UNKNOWN_DEPENDENCY, type);
+ break;
+ }
+
+ /* check to see if any versions listed are installed */
+ pkgexist = pkgokay = 0;
+ i = 0;
+ if (strchr(abbrev, '.')) {
+ progerr(ERR_PKGABRV, abbrev);
+ }
+ (void) snprintf(wabbrev, sizeof (wabbrev), "%s.*", abbrev);
+
+ do {
+ inst = fpkginst(wabbrev, alist[i], vlist[i]);
+ if (inst && (pkginfo(&info, inst, NULL, NULL) == 0)) {
+ pkgexist++;
+ if ((info.status == PI_INSTALLED) ||
+ (info.status == PI_PRESVR4))
+ pkgokay++;
+ }
+ } while (++i < nlist);
+ (void) fpkginst(NULL); /* force closing/rewind of files */
+
+ if (!info.name) {
+ info.name = name;
+ }
+
+ switch (type) {
+ case 'I':
+ incompat(abbrev, info.name, a_preinstallCheck);
+ break;
+
+ case 'P':
+ prereq(abbrev, name, a_preinstallCheck);
+ break;
+
+ case 'R':
+ deponme(abbrev, info.name, a_preinstallCheck);
+ }
+ }
+ (void) fclose(fp);
+
+done:
+ if (a_removeFlag) {
+ ckrdeps(a_preinstallCheck);
+ }
+
+ return (errflg);
+}
+
+void
+setPatchUpdate(void)
+{
+ is_patch_update = 1;
+}
+
+int
+isPatchUpdate(void)
+{
+ return ((is_patch_update) ? 1 : 0);
+}
+
+void
+setUpdate(void)
+{
+ is_update = 1;
+}
+
+int
+isUpdate(void)
+{
+ return ((is_update) ? 1 : 0);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+incompat(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+ char buf[512];
+
+ if (!pkgexist)
+ return;
+
+ errflg++;
+ if (a_preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "incompat=%s\n", pkginst);
+ return;
+ }
+
+ logerr(ERR_WARNING);
+ (void) snprintf(buf, sizeof (buf), ERR_INCOMP_VERS, pkginst, pkgname);
+ puttext(stderr, buf, 4, 0);
+ (void) putc('\n', stderr);
+}
+
+static void
+prereq(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+ register int i;
+ char buf[512];
+
+ if (pkgokay) {
+ return;
+ }
+
+ errflg++;
+
+ if (a_preinstallCheck == B_TRUE) {
+ if (pkgexist) {
+ (void) fprintf(stdout,
+ "prerequisite-incomplete=%s\n", pkginst);
+ } else {
+ (void) fprintf(stdout,
+ "prerequisite-installed=%s\n", pkginst);
+ }
+ return;
+ }
+
+ logerr(ERR_WARNING);
+ if (pkgexist) {
+ (void) snprintf(buf, sizeof (buf), ERR_PRENCI, pkginst,
+ pkgname);
+ puttext(stderr, buf, 4, 0);
+ (void) putc('\n', stderr);
+ } else {
+ (void) snprintf(buf, sizeof (buf), ERR_PREREQ, pkginst,
+ pkgname);
+ if (nlist) {
+ (void) strcat(buf, ERR_VALINST);
+ }
+ puttext(stderr, buf, 4, 0);
+ (void) putc('\n', stderr);
+ for (i = 0; i < nlist; i++) {
+ (void) printf(" ");
+ if (alist[i])
+ (void) printf("(%s) ", alist[i]);
+ if (vlist[i])
+ (void) printf("%s", vlist[i]);
+ (void) printf("\n");
+ }
+ }
+}
+
+static void
+deponme(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+ char buf[512];
+
+ if (!pkgexist)
+ return;
+
+ errflg++;
+
+ if (a_preinstallCheck == B_TRUE) {
+ if (!pkgname || !pkgname[0]) {
+ (void) snprintf(buf, sizeof (buf),
+ "dependonme=%s", pkginst);
+ } else {
+ (void) snprintf(buf, sizeof (buf),
+ "dependsonme=%s:%s", pkginst, pkgname);
+ }
+ (void) fprintf(stdout, "%s\n", buf);
+ return;
+ }
+
+ logerr(ERR_WARNING);
+ if (!pkgname || !pkgname[0]) {
+ (void) snprintf(buf, sizeof (buf), ERR_DEPONME, pkginst);
+ } else {
+ (void) snprintf(buf, sizeof (buf), ERR_DEPNAM, pkginst,
+ pkgname);
+ }
+ puttext(stderr, buf, 4, 0);
+ (void) putc('\n', stderr);
+}
+
+static int
+getline(FILE *fp)
+{
+ register int i, c, found;
+ char *pt, *new, line[LSIZE];
+
+ abbrev[0] = name[0] = type = '\0';
+
+ for (i = 0; i < nlist; i++) {
+ if (alist[i]) {
+ free(alist[i]);
+ alist[i] = NULL;
+ }
+ if (vlist[i]) {
+ free(vlist[i]);
+ vlist[i] = NULL;
+ }
+ }
+ alist[0] = vlist[0] = NULL;
+
+ found = (-1);
+ nlist = 0;
+ while ((c = getc(fp)) != EOF) {
+ (void) ungetc(c, fp);
+ if ((found >= 0) && !isspace(c))
+ return (1);
+
+ if (!fgets(line, LSIZE, fp))
+ break;
+
+ for (pt = line; isspace(*pt); /* void */)
+ pt++;
+ if (!*pt || (*pt == '#'))
+ continue;
+
+ if (pt == line) {
+ /* begin new definition */
+ /* LINTED variable format specifier to sscanf(): */
+ (void) sscanf(line, SSCANF_FORMAT, &type, abbrev, name);
+ found++;
+ continue;
+ }
+ if (found < 0)
+ return (0);
+
+ if (*pt == '(') {
+ /* architecture is specified */
+ if (new = strchr(pt, ')'))
+ *new++ = '\0';
+ else
+ return (-1); /* bad specification */
+ alist[found] = qstrdup(pt+1);
+ pt = new;
+ }
+ while (isspace(*pt))
+ pt++;
+ if (*pt) {
+ vlist[found] = qstrdup(pt);
+ if (pt = strchr(vlist[found], '\n'))
+ *pt = '\0';
+ }
+ found++;
+ nlist++;
+ }
+ return ((found >= 0) ? 1 : 0);
+}
+
+static void
+ckrdeps(boolean_t a_preinstallCheck)
+{
+ struct dirent *drp;
+ DIR *dirfp;
+ FILE *fp;
+ char depfile[PATH_MAX+1];
+
+ if ((dirfp = opendir(pkgdir)) == NULL)
+ return;
+
+ while ((drp = readdir(dirfp)) != NULL) {
+ if (drp->d_name[0] == '.')
+ continue;
+
+ if (strcmp(drp->d_name, rmpkginst) == 0)
+ continue; /* others don't include me */
+ (void) snprintf(depfile, sizeof (depfile),
+ "%s/%s/%s", pkgdir, drp->d_name, DEPEND_FILE);
+ if ((fp = fopen(depfile, "r")) == NULL)
+ continue;
+
+ ckpreq(fp, drp->d_name, a_preinstallCheck);
+ }
+ (void) closedir(dirfp);
+}
+
+static void
+ckpreq(FILE *fp, char *dname, boolean_t a_preinstallCheck)
+{
+ register int i;
+ char *inst;
+
+ while (getline(fp)) {
+ if (type != 'P')
+ continue;
+
+ if (strcmp(abbrev, rmpkg))
+ continue;
+
+ /* see if package is installed */
+ i = 0;
+ if (strchr(abbrev, '.') == 0) {
+ (void) strcat(abbrev, ".*");
+ }
+ pkgexist = 1;
+
+ do {
+ if (inst = fpkginst(abbrev, alist[i], vlist[i])) {
+ if (strcmp(inst, rmpkginst) == 0) {
+ deponme(dname, "", a_preinstallCheck);
+ (void) fclose(fp);
+ (void) fpkginst(NULL);
+ return;
+ }
+ }
+ } while (++i < nlist);
+ (void) fpkginst(NULL);
+ }
+ (void) fclose(fp);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/doulimit.c b/usr/src/cmd/svr4pkg/libinst/doulimit.c
new file mode 100644
index 0000000000..7400a0c052
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/doulimit.c
@@ -0,0 +1,164 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <string.h>
+#include <signal.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <locale.h>
+#include <libintl.h>
+#include <ctype.h>
+#include <pkglib.h>
+#include <libinst.h>
+
+#define ERR_SET_ULIMIT "unable to set ulimit to <%ld> blocks"
+#define ERR_DO_ULIMIT "An attempt was made to create a file larger than " \
+ "ULIMIT. Source of fault is unknown."
+#define ERR_SCRULIMIT "Script <%s> attempted to create a file exceeding " \
+ "ULIMIT."
+
+static char *script_name = NULL, *scr_error = NULL;
+static struct rlimit ulimit = {RLIM_INFINITY, RLIM_INFINITY};
+static struct rlimit dblimit = {RLIM_INFINITY, RLIM_INFINITY};
+static int limit_is_set = 0, fail_return = 0;
+
+void ulimit_quit(); /* XFSZ controlled signal handler. */
+int clr_ulimit(); /* Clear the user supplied file size limit. */
+void set_limit(); /* Called from installf to undo ulimit */
+int set_ulimit(char *script, char *err_msg);
+int assign_ulimit(char *fslimit);
+
+extern int warnflag;
+
+void
+set_limit()
+{
+ limit_is_set = 1;
+}
+
+int
+clr_ulimit()
+{
+ if (limit_is_set) {
+ if (script_name)
+ free(script_name);
+ script_name = NULL;
+ if (scr_error)
+ free(scr_error);
+ scr_error = NULL;
+ fail_return = 99;
+
+ /* Clear out the limit to infinity. */
+ return (setrlimit(RLIMIT_FSIZE, &dblimit));
+ } else
+ return (0);
+}
+
+/*
+ * This sets up the ULIMIT facility for the signal retrieval. This sets up
+ * the static pointers to the message constants for indicating where the
+ * error occurred.
+ */
+int
+set_ulimit(char *script, char *err_msg)
+{
+ int n;
+
+ if (limit_is_set) {
+ (void) signal(SIGXFSZ, ulimit_quit);
+ if (script_name)
+ free(script_name);
+ script_name = strdup(script);
+ if (scr_error)
+ free(scr_error);
+ scr_error = strdup(err_msg);
+ fail_return = 99;
+
+ n = setrlimit(RLIMIT_FSIZE, &ulimit);
+
+ return (n);
+ } else
+ return (0);
+
+}
+
+/* Validate ULIMIT and set accordingly. */
+int
+assign_ulimit(char *fslimit)
+{
+ rlim_t limit;
+ int cnt = 0;
+
+ if (fslimit && *fslimit) {
+ /* fslimit must be a simple unsigned integer. */
+ do {
+ if (!isdigit(fslimit[cnt]))
+ return (-1);
+ } while (fslimit[++cnt]);
+
+ limit = atol(fslimit);
+
+ ulimit.rlim_cur = (limit * 512); /* fslimit is in blocks */
+
+ limit_is_set = 1;
+
+ return (0);
+ } else
+ return (-1);
+}
+
+/*
+ * This is the signal handler for ULIMIT.
+ */
+void
+ulimit_quit(int n)
+{
+#ifdef lint
+ int i = n;
+ n = i;
+#endif /* lint */
+
+ setrlimit(RLIMIT_FSIZE, &dblimit);
+ signal(SIGXFSZ, SIG_IGN);
+
+ if (script_name) {
+ progerr(gettext(ERR_SCRULIMIT), script_name);
+ if (scr_error)
+ progerr("%s", scr_error);
+ } else
+ progerr(gettext(ERR_DO_ULIMIT));
+
+ quit(fail_return);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/dryrun.c b/usr/src/cmd/svr4pkg/libinst/dryrun.c
new file mode 100644
index 0000000000..c4f08d5f7c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/dryrun.c
@@ -0,0 +1,926 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pkgstrct.h>
+#include <unistd.h>
+#include <pkglib.h>
+#include <libintl.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "dryrun.h"
+
+#define HDR_FSUSAGE "#name remote_name writeable bfree bused ifree iused"
+
+#define ERR_NOCREAT "cannot create %s."
+#define ERR_NOOPEN "cannot open %s."
+#define ERR_NOWRITE "cannot write to %s."
+#define ERR_NOREAD "cannot read from %s."
+#define ERR_FSFAIL "cannot construct filesystem table entry."
+#define ERR_BADTYPE "cannot record %s dryrun from %s continuation file."
+#define ERR_NOCONT "cannot install from continue file due to error " \
+ "stacking."
+
+#define ISUMASC_SUFFIX ".isum.asc"
+#define FSASC_SUFFIX ".fs.asc"
+#define IPOASC_SUFFIX ".ipo.asc"
+#define IBIN_SUFFIX ".inst.bin"
+
+#define MALCOUNT 5 /* package entries to allocate in a block */
+#define PKGNAMESIZE 32 /* package entries to allocate in a block */
+
+extern struct cfextra **extlist;
+extern char *pkginst;
+
+static struct cfextra **extptr;
+static int dryrun_mode = 0;
+static int continue_mode = 0;
+static int this_exitcode = 0;
+
+/* The dryrun and continuation filenames */
+static char *dryrun_sumasc;
+static char *dryrun_fsasc;
+static char *dryrun_poasc;
+static char *dryrun_bin;
+static char *continue_bin;
+
+/* These tell us if the actual files are initialized yet. */
+static int dryrun_initialized = 0;
+static int continue_initialized = 0;
+
+static int this_type; /* type of transaction from main.c */
+
+static int pkg_handle = -1;
+static int tot_pkgs;
+
+/* Their associated file pointers */
+static FILE *fp_dra;
+static int fd_drb;
+static int fd_cnb;
+
+struct dr_pkg_entry {
+ char pkginst[PKGNAMESIZE + 2];
+ struct dr_pkg_entry *next;
+};
+
+static struct drinfo {
+ unsigned partial_set:1; /* 1 if a partial installation was detected. */
+ unsigned partial:1; /* 1 if a partial installation was detected. */
+ unsigned runlevel_set:1;
+ unsigned runlevel:1; /* 1 if runlevel test returned an error. */
+ unsigned pkgfiles_set:1;
+ unsigned pkgfiles:1;
+ unsigned depend_set:1;
+ unsigned depend:1;
+ unsigned space_set:1;
+ unsigned space:1;
+ unsigned conflict_set:1;
+ unsigned conflict:1;
+ unsigned setuid_set:1;
+ unsigned setuid:1;
+ unsigned priv_set:1;
+ unsigned priv:1;
+ unsigned pkgdirs_set:1;
+ unsigned pkgdirs:1;
+ unsigned reqexit_set:1;
+ unsigned checkexit_set:1;
+
+ int type; /* type of operation */
+ int reqexit; /* request script exit code */
+ int checkexit; /* checkinstall script exit code */
+ int exitcode; /* overall program exit code. */
+
+ struct dr_pkg_entry *packages; /* pointer to the list of packages */
+
+ int total_ext_recs; /* total extlist entries */
+ int total_fs_recs; /* total fs_tab entries */
+ int total_pkgs; /* total number of dryrun packages */
+ int do_not_continue; /* error stacking is likely */
+} dr_info;
+
+static char *exitmsg; /* the last meaningful message printed */
+
+/*
+ * In the event that live continue (continue from a dryrun source only)
+ * becomes a feature, it will be necessary to keep track of those events such
+ * as multiply edited files and files dependent upon multiple class action
+ * scripts that will lead to "tolerance stacking". Calling this function
+ * states that we've lost the level of precision necessary for a live
+ * continue.
+ */
+void
+set_continue_not_ok(void)
+{
+ dr_info.do_not_continue = 1;
+}
+
+int
+continue_is_ok(void)
+{
+ return (!dr_info.do_not_continue);
+}
+
+static void
+wr_OK(FILE *fp, char *parameter, int set, int value)
+{
+ (void) fprintf(fp, "%s=%s\n", parameter,
+ (set ? (value ? "OK" : "NOT_OK") : "NOT_TESTED"));
+}
+
+static void
+add_pkg_to_list(char *pkgname)
+{
+ struct dr_pkg_entry **pkg_entry;
+
+ if (pkg_handle == -1) {
+ if ((pkg_handle = bl_create(MALCOUNT,
+ sizeof (struct dr_pkg_entry), "package dryrun")) == -1)
+ return;
+ }
+
+ pkg_entry = &(dr_info.packages);
+
+ while (*pkg_entry != NULL)
+ pkg_entry = &((*pkg_entry)->next);
+
+ /* LINTED pointer cast may result in improper alignment */
+ *pkg_entry = (struct dr_pkg_entry *)bl_next_avail(pkg_handle);
+ dr_info.total_pkgs++;
+
+ (void) snprintf((*pkg_entry)->pkginst, PKGNAMESIZE, "%s%s",
+ (pkgname ? pkgname : ""), ((this_exitcode == 0) ? "" : "-"));
+}
+
+static void
+write_pkglist_ascii(void)
+{
+ struct dr_pkg_entry *pkg_entry;
+
+ (void) fprintf(fp_dra, "PKG_LIST=\"");
+
+ pkg_entry = dr_info.packages;
+ while (pkg_entry) {
+ (void) fprintf(fp_dra, " %s", pkg_entry->pkginst);
+ pkg_entry = pkg_entry->next;
+ }
+
+ (void) fprintf(fp_dra, "\"\n");
+}
+
+static int
+write_string(int fd, char *string)
+{
+ int string_size;
+
+ if (string)
+ string_size = strlen(string) + 1;
+ else
+ string_size = 0;
+
+ if (write(fd, &string_size, sizeof (string_size)) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return (0);
+ }
+
+ if (string_size > 0) {
+ if (write(fd, string, string_size) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+static char *
+read_string(int fd, char *buffer)
+{
+ size_t string_size;
+
+ if (read(fd, &(string_size), sizeof (string_size)) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (NULL);
+ }
+
+ if (string_size != 0) {
+ if (read(fd, buffer, string_size) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (NULL);
+ }
+ } else {
+ return (NULL);
+ }
+
+ return (buffer);
+}
+
+static void
+write_dryrun_ascii()
+{
+ int n;
+ char *fs_mntpt, *src_name;
+
+ if ((fp_dra = fopen(dryrun_sumasc, "wb")) == NULL) {
+ progerr(gettext(ERR_NOOPEN), dryrun_sumasc);
+ return;
+ }
+
+ (void) fprintf(fp_dra, "DR_TYPE=%s\n", (dr_info.type == REMOVE_TYPE ?
+ "REMOVE" : "INSTALL"));
+
+ (void) fprintf(fp_dra, "PKG_INSTALL_ROOT=%s\n", (((get_inst_root()) &&
+ (strcmp(get_inst_root(), "/") != 0)) ?
+ get_inst_root() : ""));
+
+ write_pkglist_ascii();
+
+ wr_OK(fp_dra, "CONTINUE", 1, !(dr_info.do_not_continue));
+
+ wr_OK(fp_dra, "PARTIAL", dr_info.partial_set, dr_info.partial);
+
+ wr_OK(fp_dra, "RUNLEVEL", dr_info.runlevel_set, dr_info.runlevel);
+
+ (void) fprintf(fp_dra, "REQUESTEXITCODE=%d\n", dr_info.reqexit);
+
+ (void) fprintf(fp_dra, "CHECKINSTALLEXITCODE=%d\n", dr_info.checkexit);
+
+ wr_OK(fp_dra, "PKGFILES", dr_info.pkgfiles_set, dr_info.pkgfiles);
+
+ wr_OK(fp_dra, "DEPEND", dr_info.depend_set, dr_info.depend);
+
+ wr_OK(fp_dra, "SPACE", dr_info.space_set, dr_info.space);
+
+ wr_OK(fp_dra, "CONFLICT", dr_info.conflict_set, dr_info.conflict);
+
+ wr_OK(fp_dra, "SETUID", dr_info.setuid_set, dr_info.setuid);
+
+ wr_OK(fp_dra, "PRIV", dr_info.priv_set, dr_info.priv);
+
+ wr_OK(fp_dra, "PKGDIRS", dr_info.pkgdirs_set, dr_info.pkgdirs);
+
+ (void) fprintf(fp_dra, "EXITCODE=%d\n", dr_info.exitcode);
+
+ (void) fprintf(fp_dra, "ERRORMSG=%s\n", (exitmsg ? exitmsg : "NONE"));
+
+ (void) fclose(fp_dra);
+
+ if ((fp_dra = fopen(dryrun_fsasc, "wb")) == NULL) {
+ progerr(gettext(ERR_NOOPEN), dryrun_fsasc);
+ return;
+ }
+
+ (void) fprintf(fp_dra, "%s\nFSUSAGE=\\\n\"\\\n", HDR_FSUSAGE);
+
+ for (n = 0; fs_mntpt = get_fs_name_n(n); n++) {
+ int bfree, bused;
+ bfree = get_blk_free_n(n);
+ bused = get_blk_used_n(n);
+
+ if (bfree || bused) {
+ (void) fprintf(fp_dra, "%s %s %s %d %d %lu %lu \\\n",
+ fs_mntpt,
+ ((src_name = get_source_name_n(n)) ?
+ src_name : "none?"),
+ (is_fs_writeable_n(n) ? "TRUE" : "FALSE"),
+ bfree,
+ bused,
+ get_inode_free_n(n),
+ get_inode_used_n(n));
+ }
+ }
+
+ dr_info.total_fs_recs = n;
+
+ (void) fprintf(fp_dra, "\"\n");
+
+ (void) fclose(fp_dra);
+
+ if ((fp_dra = fopen(dryrun_poasc, "wb")) == NULL) {
+ progerr(gettext(ERR_NOOPEN), dryrun_poasc);
+ return;
+ }
+
+ dr_info.total_ext_recs = 0;
+
+ (void) fprintf(fp_dra, "WOULD_INSTALL=\\\n\"\\\n");
+
+ for (n = 0; extptr && extptr[n]; n++) {
+ /*
+ * Write it out if it's a successful change or it is from the
+ * prior dryrun file (meaning it was a change back then).
+ */
+ if ((this_exitcode == 0 &&
+ (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)) ||
+ extptr[n]->mstat.preloaded) {
+ (void) fprintf(fp_dra, "%c %s \\\n",
+ extptr[n]->cf_ent.ftype,
+ extptr[n]->client_path);
+
+ /* Count it, if it's going into the dryrun file. */
+ if (extptr[n]->cf_ent.ftype != 'i')
+ dr_info.total_ext_recs++;
+ }
+ }
+
+ (void) fprintf(fp_dra, "\"\n");
+
+ (void) fclose(fp_dra);
+}
+
+/*
+ * This writes out a dryrun file.
+ */
+static void
+write_dryrun_bin()
+{
+ struct fstable *fs_entry;
+ struct pinfo *pkginfo;
+ struct dr_pkg_entry *pkg_entry;
+ int n;
+ int fsentry_size = sizeof (struct fstable);
+ int extentry_size = sizeof (struct cfextra);
+ int pinfoentry_size = sizeof (struct pinfo);
+
+ if ((fd_drb = open(dryrun_bin,
+ O_RDWR | O_APPEND | O_TRUNC)) == -1) {
+ progerr(gettext(ERR_NOOPEN), dryrun_bin);
+ return;
+ }
+
+ /* Write the dryrun info table. */
+ if (write(fd_drb, &dr_info, sizeof (struct drinfo)) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return;
+ }
+
+ /* Write out the package instance list. */
+ pkg_entry = dr_info.packages;
+ while (pkg_entry) {
+ if (write(fd_drb, pkg_entry->pkginst, PKGNAMESIZE) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return;
+ }
+ pkg_entry = pkg_entry->next;
+ }
+
+ /* Write out the fstable records. */
+ for (n = 0; n < dr_info.total_fs_recs; n++) {
+ fs_entry = get_fs_entry(n);
+
+ if (write(fd_drb, fs_entry, fsentry_size) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return;
+ }
+
+ if (!write_string(fd_drb, fs_entry->name))
+ return;
+
+ if (!write_string(fd_drb, fs_entry->fstype))
+ return;
+
+ if (!write_string(fd_drb, fs_entry->remote_name))
+ return;
+ }
+
+ /* Write out the package objects and their attributes. */
+ for (n = 0; extptr && extptr[n]; n++) {
+ /* Don't save metafiles. */
+ if (extptr[n]->cf_ent.ftype == 'i')
+ continue;
+
+ /*
+ * If it's a new package object (not left over from the
+ * continuation file) and it indicates no changes to the
+ * system, skip it. Only files that will change the system
+ * are stored.
+ */
+ if (extptr[n]->mstat.preloaded == 0 &&
+ !(this_exitcode == 0 &&
+ (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)))
+ continue;
+
+ if (write(fd_drb, extptr[n], extentry_size) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return;
+ }
+
+ if (!write_string(fd_drb, extptr[n]->cf_ent.path))
+ return;
+
+ if (!write_string(fd_drb, extptr[n]->cf_ent.ainfo.local))
+ return;
+
+ extptr[n]->cf_ent.pinfo = eptstat(&(extptr[n]->cf_ent),
+ pkginst, CONFIRM_CONT);
+
+ /*
+ * Now all of the entries about the various packages that own
+ * this entry.
+ */
+ pkginfo = extptr[n]->cf_ent.pinfo;
+
+ do {
+ if (write(fd_drb, pkginfo,
+ pinfoentry_size) == -1) {
+ progerr(gettext(ERR_NOWRITE), dryrun_bin);
+ return;
+ }
+ pkginfo = pkginfo->next; /* May be several */
+ } while (pkginfo);
+ }
+
+ (void) close(fd_drb);
+}
+
+static void
+init_drinfo(void) {
+
+ if (dr_info.partial != 0)
+ dr_info.partial_set = 0;
+
+ if (dr_info.runlevel != 0)
+ dr_info.runlevel_set = 0;
+
+ if (dr_info.pkgfiles != 0)
+ dr_info.pkgfiles_set = 0;
+
+ if (dr_info.depend != 0)
+ dr_info.depend_set = 0;
+
+ if (dr_info.space != 0)
+ dr_info.space_set = 0;
+
+ if (dr_info.conflict != 0)
+ dr_info.conflict_set = 0;
+
+ if (dr_info.setuid != 0)
+ dr_info.setuid_set = 0;
+
+ if (dr_info.priv != 0)
+ dr_info.priv_set = 0;
+
+ if (dr_info.pkgdirs != 0)
+ dr_info.pkgdirs_set = 0;
+
+ if (dr_info.reqexit == 0)
+ dr_info.reqexit_set = 0;
+
+ if (dr_info.checkexit == 0)
+ dr_info.checkexit_set = 0;
+
+ dr_info.packages = NULL;
+ tot_pkgs = dr_info.total_pkgs;
+ dr_info.total_pkgs = 0;
+}
+
+/*
+ * This function reads in the various continuation file data in order to seed
+ * the internal data structures.
+ */
+static boolean_t
+read_continue_bin(void)
+{
+ int n;
+ int fsentry_size = sizeof (struct fstable);
+ int extentry_size = sizeof (struct cfextra);
+ int pinfoentry_size = sizeof (struct pinfo);
+
+ pkgobjinit();
+ if (!init_pkgobjspace())
+ return (B_FALSE);
+
+ if ((fd_cnb = open(continue_bin, O_RDONLY)) == -1) {
+ progerr(gettext(ERR_NOOPEN), continue_bin);
+ return (B_FALSE);
+ }
+
+ /* Read the dryrun info structure. */
+ if (read(fd_cnb, &dr_info, sizeof (struct drinfo)) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (B_FALSE);
+ }
+
+ init_drinfo();
+
+ if (this_type != dr_info.type) {
+ progerr(gettext(ERR_BADTYPE),
+ (this_type == REMOVE_TYPE) ?
+ "a remove" : "an install",
+ (dr_info.type == REMOVE_TYPE) ?
+ "a remove" : "an install");
+ return (B_FALSE);
+ }
+
+ /* Read in the dryrun package records. */
+ for (n = 0; n < tot_pkgs; n++) {
+ char pkg_name[PKGNAMESIZE];
+
+ if (read(fd_cnb, &pkg_name, PKGNAMESIZE) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (B_FALSE);
+ }
+
+ add_pkg_to_list(pkg_name);
+ }
+
+ /* Read in the fstable records. */
+ for (n = 0; n < dr_info.total_fs_recs; n++) {
+ struct fstable fs_entry;
+ char name[PATH_MAX], remote_name[PATH_MAX];
+ char fstype[200];
+
+ if (read(fd_cnb, &fs_entry, fsentry_size) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (B_FALSE);
+ }
+
+ if (read_string(fd_cnb, &name[0]) == NULL)
+ return (B_FALSE);
+
+ if (read_string(fd_cnb, &fstype[0]) == NULL)
+ return (B_FALSE);
+
+ if (read_string(fd_cnb, &remote_name[0]) == NULL)
+ return (B_FALSE);
+
+ if (load_fsentry(&fs_entry, name, fstype, remote_name)) {
+ progerr(gettext(ERR_FSFAIL));
+ return (B_FALSE);
+ }
+ }
+
+ /* Read in the package objects and their attributes. */
+ for (n = 0; n < dr_info.total_ext_recs; n++) {
+ struct cfextra ext_entry;
+ struct pinfo pinfo_area, *pinfo_ptr;
+ char path[PATH_MAX], local[PATH_MAX], *local_ptr;
+
+ if (read(fd_cnb, &ext_entry, extentry_size) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (B_FALSE);
+ }
+
+ /*
+ * If the previous dryrun replaced a directory with a
+ * non-directory and we're going into *another* dryrun, we're
+ * stacking errors and continuation should not be permitted.
+ */
+ if (ext_entry.mstat.dir2nondir && dryrun_mode)
+ dr_info.do_not_continue = 1;
+
+ /*
+ * Since we just read this from a continuation file; it is,
+ * by definition, preloaded.
+ */
+ ext_entry.mstat.preloaded = 1;
+
+ if (read_string(fd_cnb, &path[0]) == NULL)
+ return (B_FALSE);
+
+ local_ptr = read_string(fd_cnb, &local[0]);
+
+ ext_entry.cf_ent.pinfo = NULL;
+
+ /*
+ * Now all of the entries about the various packages that own
+ * this entry.
+ */
+ do {
+ if (read(fd_cnb, &pinfo_area, pinfoentry_size) == -1) {
+ progerr(gettext(ERR_NOREAD), continue_bin);
+ return (B_FALSE);
+
+ }
+
+ pinfo_ptr = eptstat(&(ext_entry.cf_ent),
+ pinfo_area.pkg, CONFIRM_CONT);
+
+ if (pinfo_ptr->next) {
+ pinfo_ptr = pinfo_ptr->next;
+ } else {
+ pinfo_ptr = NULL;
+ }
+
+ } while (pinfo_ptr);
+
+ seed_pkgobjmap(&ext_entry, path, local_ptr);
+ }
+
+ (void) close(fd_cnb);
+
+ /*
+ * Return as reading is done, so pkginstall doesn't
+ * read the same info from the system.
+ */
+
+ return (B_TRUE);
+}
+
+int
+in_dryrun_mode(void)
+{
+ return (dryrun_mode);
+}
+
+void
+set_dryrun_mode(void)
+{
+ dryrun_mode = 1;
+}
+
+int
+in_continue_mode(void)
+{
+ return (continue_mode);
+}
+
+void
+set_continue_mode(void)
+{
+ continue_mode = 1;
+}
+
+/*
+ * Initialize a dryrun file by assigning it a name and creating it
+ * empty.
+ */
+static int
+init_drfile(char **targ_ptr, char *path)
+{
+ int n;
+ char *targ_file;
+
+ *targ_ptr = strdup(path);
+ targ_file = *targ_ptr;
+
+ if (access(targ_file, W_OK) == 0) {
+ (void) unlink(targ_file);
+ }
+
+ n = open(targ_file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if (n < 0) {
+ progerr(gettext(ERR_NOCREAT), targ_file);
+ return (0);
+ } else {
+ (void) close(n);
+ }
+
+ return (1);
+}
+
+/*
+ * Initialize all required dryrun files and see that the target directory is
+ * present. If all goes well, we're in dryrun mode. If it doesn't, we're not.
+ */
+void
+init_dryrunfile(char *dr_dir)
+{
+ char temp_path[PATH_MAX];
+ char *dot_pos = (temp_path+strlen(dr_dir)+7);
+
+ /* First create or confirm the directory. */
+ if (isdir(dr_dir) != 0) {
+ (void) mkpath(dr_dir);
+ }
+
+ (void) snprintf(temp_path, sizeof (temp_path), "%s/dryrun", dr_dir);
+
+ (void) strcpy(dot_pos, ISUMASC_SUFFIX);
+
+ if (!init_drfile(&dryrun_sumasc, temp_path))
+ return;
+
+ (void) strcpy(dot_pos, FSASC_SUFFIX);
+
+ if (!init_drfile(&dryrun_fsasc, temp_path))
+ return;
+
+ (void) strcpy(dot_pos, IPOASC_SUFFIX);
+
+ if (!init_drfile(&dryrun_poasc, temp_path))
+ return;
+
+ (void) strcpy(dot_pos, IBIN_SUFFIX);
+
+ if (!init_drfile(&dryrun_bin, temp_path))
+ return;
+
+ dryrun_initialized = 1;
+}
+
+void
+init_contfile(char *cn_dir)
+{
+ char temp_path[PATH_MAX];
+
+ /* First confirm the directory. */
+ if (isdir(cn_dir) != 0)
+ return; /* no continuation directory */
+
+ (void) snprintf(temp_path, sizeof (temp_path),
+ "%s/dryrun%s", cn_dir, IBIN_SUFFIX);
+ continue_bin = strdup(temp_path);
+
+ if (access(continue_bin, W_OK) != 0) {
+ free(continue_bin);
+ return;
+ }
+
+ continue_initialized = 1;
+}
+
+void
+set_dr_exitmsg(char *value)
+{
+ exitmsg = value;
+}
+
+void
+set_dr_info(int type, int value)
+{
+ switch (type) {
+ case PARTIAL:
+ if (dr_info.partial_set == 0) {
+ dr_info.partial_set = 1;
+ dr_info.partial = (value ? 1 : 0);
+ }
+ break;
+
+ case RUNLEVEL:
+ if (dr_info.runlevel_set == 0) {
+ dr_info.runlevel_set = 1;
+ dr_info.runlevel = (value ? 1 : 0);
+ }
+ break;
+
+ case PKGFILES:
+ if (dr_info.pkgfiles_set == 0) {
+ dr_info.pkgfiles_set = 1;
+ dr_info.pkgfiles = (value ? 1 : 0);
+ }
+ break;
+
+ case DEPEND:
+ if (dr_info.depend_set == 0) {
+ dr_info.depend_set = 1;
+ dr_info.depend = (value ? 1 : 0);
+ }
+ break;
+
+ case SPACE:
+ if (dr_info.space_set == 0) {
+ dr_info.space_set = 1;
+ dr_info.space = (value ? 1 : 0);
+ }
+ break;
+
+ case CONFLICT:
+ if (dr_info.conflict_set == 0) {
+ dr_info.conflict_set = 1;
+ dr_info.conflict = (value ? 1 : 0);
+ }
+ break;
+
+ case SETUID:
+ if (dr_info.setuid_set == 0) {
+ dr_info.setuid_set = 1;
+ dr_info.setuid = (value ? 1 : 0);
+ }
+ break;
+
+ case PRIV:
+ if (dr_info.priv_set == 0) {
+ dr_info.priv_set = 1;
+ dr_info.priv = (value ? 1 : 0);
+ }
+
+ break;
+
+ case PKGDIRS:
+ if (dr_info.pkgdirs_set == 0) {
+ dr_info.pkgdirs_set = 1;
+ dr_info.pkgdirs = (value ? 1 : 0);
+ }
+
+ break;
+
+ case REQUESTEXITCODE:
+ if (dr_info.reqexit_set == 0) {
+ dr_info.reqexit_set = 1;
+ dr_info.reqexit = value;
+ }
+
+ break;
+
+ case CHECKEXITCODE:
+ if (dr_info.checkexit_set == 0) {
+ dr_info.checkexit_set = 1;
+ dr_info.checkexit = value;
+ }
+
+ break;
+
+ case EXITCODE:
+ if (dr_info.exitcode == 0) {
+ dr_info.exitcode = value;
+ }
+
+ this_exitcode = value;
+
+ break;
+
+ /* default to install if the value is kookie. */
+ case DR_TYPE:
+ if (value == REMOVE_TYPE)
+ this_type = REMOVE_TYPE;
+ else
+ this_type = INSTALL_TYPE;
+
+ break;
+ }
+}
+
+void
+write_dryrun_file(struct cfextra **extlist)
+{
+ extptr = extlist;
+
+ if (dryrun_initialized) {
+ dr_info.type = this_type;
+
+ add_pkg_to_list(pkginst);
+ write_dryrun_ascii();
+ write_dryrun_bin();
+
+ if (dryrun_mode) {
+ free(dryrun_sumasc);
+ free(dryrun_fsasc);
+ free(dryrun_poasc);
+ free(dryrun_bin);
+ }
+ }
+}
+
+/*
+ * Name: read_continuation
+ * Description: If continuation is initialised, reads the
+ * continuation binary file. The path for the
+ * same is freed, if set, as this is the last
+ * chance to do so.
+ * Sets: Error condition, through the pointer passed
+ * if read failed.
+ * Returns: B_TRUE - if the continuation binary file
+ * from previous dryrun is read successfully.
+ * B_FALSE - if either continuation is not initialised
+ * or read was not successful.
+ */
+boolean_t
+read_continuation(int *error)
+{
+ boolean_t ret = B_FALSE;
+ *error = 0;
+ if (continue_initialized) {
+ if (!read_continue_bin()) {
+ continue_mode = 0;
+ free(continue_bin);
+ *error = -1;
+ return (ret);
+ }
+
+ if (continue_mode) {
+ free(continue_bin);
+ }
+ ret = B_TRUE;
+ }
+ return (ret);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/echo.c b/usr/src/cmd/svr4pkg/libinst/echo.c
new file mode 100644
index 0000000000..add6b2f29f
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/echo.c
@@ -0,0 +1,187 @@
+/*
+ * 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 2004 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 <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <zone.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * internal global variables
+ */
+
+static boolean_t debugFlag = B_FALSE; /* debug messages enabled? */
+static boolean_t echoFlag = B_TRUE; /* interactive msgs enabled? */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: echo
+ * Synopsis: Output an interactive message if interaction is enabled
+ * Description: Main method for outputting an interactive message; call to
+ * output interactive message if interation has not been disabled
+ * by a previous call to echoSetFlag(0).
+ * Arguments: format - [RO, RO*] (char *)
+ * printf-style format for debugging message to be output
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ */
+
+/*PRINTFLIKE1*/
+void
+echo(char *fmt, ...)
+{
+ va_list ap;
+
+ /* output message if echoing is enabled */
+
+ if (echoFlag == B_TRUE) {
+ va_start(ap, fmt);
+
+ (void) vfprintf(stderr, fmt, ap);
+
+ va_end(ap);
+
+ (void) putc('\n', stderr);
+ }
+}
+
+/*
+ * Name: echoDebug
+ * Synopsis: Output a debugging message if debugging is enabled
+ * Description: Main method for outputting a debugging message; call to
+ * output debugging message if debugging has been enabled
+ * by a previous call to echoDebugSetFlag(1).
+ * Arguments: format - [RO, RO*] (char *)
+ * printf-style format for debugging message to be output
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ * NOTE: format of message will be:
+ * # [ aaa bbb ccc ] message
+ * where: aaa - process i.d.
+ * bbb - zone i.d.
+ * ccc - name of program
+ * for example:
+ * # [ 25685 0 pkgadd ] unable to get package list
+ */
+
+/*PRINTFLIKE1*/
+void
+echoDebug(char *a_fmt, ...)
+{
+ va_list ap;
+
+ /* output debugging message if debugging is enabled */
+
+ if (debugFlag == B_TRUE) {
+ char *p = get_prog_name();
+
+ (void) fprintf(stderr, "# [%6d %3d", getpid(), getzoneid());
+
+ if ((p != (char *)NULL) && (*p != '\0')) {
+ fprintf(stderr, " %-11s", p);
+ }
+
+ (void) fprintf(stderr, "] ");
+
+ va_start(ap, a_fmt);
+
+ (void) vfprintf(stderr, a_fmt, ap);
+
+ va_end(ap);
+
+ (void) putc('\n', stderr);
+ }
+}
+
+/*
+ * get the "interactive message enabled" flag
+ */
+
+boolean_t
+echoGetFlag(void) {
+ return (echoFlag);
+}
+
+/*
+ * set the "interactive message enabled" flag
+ */
+
+boolean_t
+echoSetFlag(boolean_t a_echoFlag)
+{
+ boolean_t oldvalue;
+
+ oldvalue = echoFlag;
+ echoFlag = a_echoFlag;
+ return (oldvalue);
+}
+
+/*
+ * get the "debugging message enabled" flag
+ */
+
+boolean_t
+echoDebugGetFlag(void) {
+ return (debugFlag);
+}
+
+/*
+ * set the "debugging message enabled" flag
+ */
+
+boolean_t
+echoDebugSetFlag(boolean_t a_debugFlag)
+{
+ boolean_t oldvalue;
+
+ oldvalue = debugFlag;
+ debugFlag = a_debugFlag;
+ return (oldvalue);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/eptstat.c b/usr/src/cmd/svr4pkg/libinst/eptstat.c
new file mode 100644
index 0000000000..0edd523b91
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/eptstat.c
@@ -0,0 +1,154 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+#define PINFOALLOC 200
+
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+
+int otherstoo;
+char *useclass;
+
+static int pinfo_handle = -1;
+
+/* Free all allocated package info structures. */
+void
+pinfo_free(void)
+{
+ bl_free(pinfo_handle);
+}
+
+/*
+ * This function manipulates the pinfo entry corresponding to the package
+ * indicated on the command line.
+ */
+struct pinfo *
+eptstat(struct cfent *entry, char *pkg, char c)
+{
+ struct pinfo *pinfo, *last, *me, *myparent;
+
+ otherstoo = 0;
+ useclass = entry->pkg_class;
+
+ me = myparent = last = (struct pinfo *)0;
+
+ if (pinfo_handle == -1) {
+ pinfo_handle = bl_create(PINFOALLOC, sizeof (struct pinfo),
+ "package data");
+ }
+
+ for (pinfo = entry->pinfo; pinfo; pinfo = pinfo->next) {
+ if (strcmp(pkg, pinfo->pkg) == 0) {
+ if (*pinfo->aclass)
+ useclass = pinfo->aclass;
+ myparent = last;
+ me = pinfo;
+ } else
+ otherstoo++;
+ last = pinfo;
+ }
+
+ if (c) {
+ /*
+ * use a delete/add strategy to keep package list
+ * ordered by modification time
+ */
+ if (me) {
+ /* remove from list first */
+ if (myparent)
+ myparent->next = me->next;
+ else
+ entry->pinfo = me->next;
+ if (me == last)
+ last = myparent;
+ entry->npkgs--;
+ /* leave 'me' around until later! */
+ }
+ if ((c != STAT_NEXT) && (me || (c != RM_RDY))) {
+ /* need to add onto end */
+ entry->npkgs++;
+ if (me == NULL) {
+ /* LINTED pointer cast may result in impro... */
+ me = (struct pinfo *)
+ bl_next_avail(pinfo_handle);
+ if (me == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ } else {
+ me->next = (struct pinfo *)NULL;
+ if (entry->npkgs == 1) {
+ if (me->aclass[0])
+ (void) strcpy(entry->pkg_class,
+ me->aclass);
+ useclass = entry->pkg_class;
+ } else
+ useclass = me->aclass;
+ }
+ (void) strncpy(me->pkg, pkg, PKGSIZ);
+
+ /*
+ * Only change status for local objects. Need
+ * to maintain "shared" status for objects that
+ * are provided from a server.
+ */
+ if (me->status != SERVED_FILE)
+ me->status = ((c == DUP_ENTRY) ? '\0' : c);
+
+ if (last)
+ last->next = me; /* add to end */
+ else
+ entry->pinfo = me; /* only item */
+ } else {
+ /* just wanted to remove this package from list */
+ if (me) {
+ free(me);
+ me = (struct pinfo *)0;
+ }
+ }
+ }
+ return (me);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/finalck.c b/usr/src/cmd/svr4pkg/libinst/finalck.c
new file mode 100644
index 0000000000..502223df62
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/finalck.c
@@ -0,0 +1,205 @@
+/*
+ * 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 2006 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 <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+extern int warnflag;
+
+/*
+ * forward declarations
+ */
+
+static int finalck_warning(struct cfent *ept, int attrchg, int contchg);
+static int finalck_error(struct cfent *ept, int attrchg, int contchg);
+
+int
+finalck(struct cfent *ept, int attrchg, int contchg, boolean_t a_warning)
+{
+ int errflg;
+
+ /*
+ * invoke the correct finalck based on whether warnings or errors
+ * should be generated
+ */
+
+ if (a_warning) {
+ errflg = finalck_warning(ept, attrchg, contchg);
+ } else {
+ errflg = finalck_error(ept, attrchg, contchg);
+ }
+
+ /* exit debug output */
+
+ echoDebug(DBG_FINALCK_EXIT, errflg, ept->ftype,
+ ept->path ? ept->path : "");
+
+ /* return results of the finalck_xxx call */
+
+ return (errflg);
+}
+
+/*
+ * this finalck generates errors on failure
+ */
+
+static int
+finalck_error(struct cfent *ept, int attrchg, int contchg)
+{
+ int errflg = 0;
+
+ /* entry debug info */
+
+ echoDebug(DBG_FINALCK_ERROR, attrchg, contchg, ept->ftype,
+ ept->path ? ept->path : "");
+
+ /* on attribute or content change, verify attributes */
+
+ if (attrchg || contchg) {
+ int n;
+
+ /* verify change, or fix if possible */
+ n = averify(1, &ept->ftype, ept->path, &ept->ainfo);
+ echoDebug(DBG_FINALCK_ERROR_AVERIFY, n);
+ if (n != 0) {
+ logerr(ERR_FINALCK_ATTR, ept->path);
+ logerr(getErrbufAddr());
+ errflg++;
+ warnflag++;
+ if (n == VE_EXIST)
+ return (1); /* no need to check contents */
+ }
+ }
+
+ /* on content change of "f/e/v" type, verify contents */
+
+ if (contchg && strchr("fev", ept->ftype)) {
+ int n;
+
+ /* verify change was executed properly */
+
+ if (contchg < 0) {
+ ept->cinfo.modtime = BADCONT;
+ ept->cinfo.size = BADCONT;
+ ept->cinfo.cksum = BADCONT;
+ }
+
+ n = cverify(1, &ept->ftype, ept->path, &ept->cinfo, 1);
+ echoDebug(DBG_FINALCK_ERROR_CVERIFY, n);
+ if (n != 0) {
+ logerr(ERR_FINALCK_CONT, ept->path);
+ logerr(getErrbufAddr());
+ errflg++;
+ warnflag++;
+ }
+ }
+
+ return (errflg);
+}
+
+/*
+ * this finalck generates warnings on failure
+ */
+
+static int
+finalck_warning(struct cfent *ept, int attrchg, int contchg)
+{
+ int errflg = 0;
+
+ /* entry debug info */
+
+ echoDebug(DBG_FINALCK_WARNING, attrchg, contchg, ept->ftype,
+ ept->path ? ept->path : "");
+
+
+ /* on attribute or content change, verify attributes */
+
+ if (attrchg || contchg) {
+ int n;
+
+ /* verify change, or fix if possible */
+
+ n = averify(1, &ept->ftype, ept->path, &ept->ainfo);
+ echoDebug(DBG_FINALCK_WARNING_AVERIFY, n);
+ if (n != 0) {
+ logerr(WRN_FINALCK_ATTR, ept->path);
+ logerr(getErrbufAddr());
+ errflg++;
+ if (n == VE_EXIST) {
+ return (1); /* no need to check contents */
+ }
+ }
+ }
+
+ /* on content change of "f/e/v" type, verify contents */
+
+ if (contchg && strchr("fev", ept->ftype)) {
+ int n;
+
+ /* verify change was executed properly */
+
+ if (contchg < 0) {
+ ept->cinfo.modtime = BADCONT;
+ ept->cinfo.size = BADCONT;
+ ept->cinfo.cksum = BADCONT;
+ }
+
+ n = cverify(1, &ept->ftype, ept->path, &ept->cinfo, 1);
+ echoDebug(DBG_FINALCK_WARNING_CVERIFY, n);
+ if (n != 0) {
+ logerr(WRN_FINALCK_CONT, ept->path);
+ logerr(getErrbufAddr());
+ }
+ errflg++;
+ }
+
+ return (errflg);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/findscripts.c b/usr/src/cmd/svr4pkg/libinst/findscripts.c
new file mode 100644
index 0000000000..767e3d81c5
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/findscripts.c
@@ -0,0 +1,197 @@
+/*
+ * 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 1995 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include "libinst.h"
+
+#define ERR_INVALID_CAS "%d is an invalid class action script type."
+#define ERR_NO_NONE "Cannot find the default archive install script."
+#define ERR_NO_PATH "No paths for finding class action scripts."
+
+/* setlist.c */
+extern struct cl_attr **cl_Classes;
+extern int cl_NClasses;
+extern char *cl_nam(int idx);
+
+static int pkg_has_arch;
+
+/* Return the install class action script associated with this class index */
+char *
+cl_iscript(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->inst_script);
+ return (NULL);
+}
+
+/*
+ * This resets an input class action script pointer and the various
+ * codes that are associated with special treatment available to a class
+ * action script. It returns 1 if there was a script there in the first
+ * place and 0 if there wasn't.
+ */
+int
+cl_deliscript(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ if (cl_Classes[idx]->inst_script) {
+ free(cl_Classes[idx]->inst_script);
+ cl_Classes[idx]->inst_script = NULL;
+ cl_Classes[idx]->src_verify = DEFAULT;
+ cl_Classes[idx]->dst_verify = DEFAULT;
+ cl_Classes[idx]->relpath_2_CAS = DEFAULT;
+
+ } else
+ return (0);
+ return (1);
+}
+
+/* Return the remove class action script associated with this class index */
+char *
+cl_rscript(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->rem_script);
+ return (NULL);
+}
+
+/*
+ * This scans the admin directories for the class acton scripts associated
+ * with the classes to be installed. It will look for install or remove
+ * scripts and place appropriate pointers into the cl_Classes list. There's
+ * no reason why it couldn't look for both except that I haven't seen a
+ * need for it yet.
+ */
+void
+find_CAS(int CAS_type, char *pkgbin, char *instdir)
+{
+ int i;
+ char path[PATH_MAX];
+
+ if (instdir == NULL || pkgbin == NULL) {
+ progerr(gettext(ERR_NO_PATH));
+ quit(99);
+ }
+
+ if (CAS_type == I_ONLY) {
+ for (i = 0; i < cl_NClasses; i++) {
+ /*
+ * Locate appropriate installation class action
+ * script, if any; look on media for script,
+ * since it might be on the system due to a
+ * previous installation.
+ */
+ (void) sprintf(path, "%s/install/i.%s", instdir,
+ cl_nam(i));
+ if (access(path, R_OK) == 0) {
+ (void) sprintf(path, "%s/i.%s", pkgbin,
+ cl_nam(i));
+ cl_Classes[i]->inst_script = qstrdup(path);
+ continue;
+ }
+
+ (void) sprintf(path, "%s/i.%s", PKGSCR, cl_nam(i));
+ if (access(path, R_OK) == 0) {
+ cl_Classes[i]->inst_script = qstrdup(path);
+ continue;
+ }
+
+ /*
+ * Provide CAS to uncompress and distribute a
+ * compressed cpio archive for those older packages
+ * that don't include their own. This is the first
+ * point at which we know, it's an old package
+ * without all the various pkginfo items set.
+ * The default script is provided for all classes
+ * in an old package which do not have their own
+ * class action script. These are the criteria used
+ * by the script that packs the archives.
+ */
+ (void) sprintf(path, "%s/%s", PKGSCR, DEF_NONE_SCR);
+ if (pkg_has_arch &&
+ cl_Classes[i]->inst_script == NULL) {
+
+ cl_Classes[i]->src_verify = NOVERIFY;
+ cl_Classes[i]->dst_verify = QKVERIFY;
+ cl_Classes[i]->relpath_2_CAS = REL_2_CAS;
+
+ if (access(path, R_OK) == 0) {
+ cl_Classes[i]->inst_script =
+ qstrdup(path);
+ continue;
+ } else {
+ progerr(gettext(ERR_NO_NONE));
+ quit(99);
+ }
+
+ }
+ }
+ } else if (CAS_type == R_ONLY) {
+ for (i = 0; i < cl_NClasses; i++) {
+ (void) sprintf(path, "%s/install/r.%s", instdir,
+ cl_nam(i));
+ if (access(path, R_OK) == 0) {
+ (void) sprintf(path, "%s/r.%s", pkgbin,
+ cl_nam(i));
+ cl_Classes[i]->rem_script = qstrdup(path);
+ continue;
+ }
+
+ (void) sprintf(path, "%s/r.%s", PKGSCR, cl_nam(i));
+ if (access(path, R_OK) == 0) {
+ cl_Classes[i]->rem_script = qstrdup(path);
+ continue;
+ }
+ }
+ } else {
+ progerr(gettext(ERR_INVALID_CAS), CAS_type);
+ quit(99);
+ }
+}
+
+/*
+ * This function deals with the special case of an old WOS package
+ * with a compressed cpio'd file set but no class action script.
+ * We find out it doesn't have a CAS later in find_CAS() and deal
+ * with it then. The only reason for this variable is to let
+ * findscripts() know to get the default script if it can't find it in
+ * the usual places.
+ */
+void
+is_WOS_arch(void)
+{
+ pkg_has_arch++;
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/fixpath.c b/usr/src/cmd/svr4pkg/libinst/fixpath.c
new file mode 100644
index 0000000000..7ed5961b81
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/fixpath.c
@@ -0,0 +1,983 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * This module contains all the code necessary to establish the key base
+ * directories to which the actual components of the package will be
+ * installed or removed. -- JST
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h> /* mkdir() declaration */
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+
+static char *install_root = NULL;
+static int install_root_exists = 0; /* An install root was specified */
+static int install_root_len; /* strlen(install_root) */
+static char *orig_basedir = NULL; /* The unadjusted basedir */
+static char *basedir = NULL; /* basedir (cmb w/ inst rt if req) */
+static int basedir_exists = 0; /* There are relocatable paths */
+static char *client_basedir = NULL;
+static int client_basedir_exists = 0; /* Installing from a host */
+static char *env_cl_bdir = NULL; /* CLIENT_BASEDIR from environment */
+static int ir_accessed = 0; /* install_root has been used */
+static int relocatable; /* set_basedir() assumed this */
+static int partial_inst = 0; /* Installing pkg from partial spool directory */
+static boolean_t depend_pkginfo_DB = B_FALSE; /* Only update depend/pkginfoDB */
+static int partial_spool_create = 0; /* Create partial spool dir */
+
+static int ask_basedir(char *path, int nointeract);
+static char *expand_path(char *path);
+static int set_client_basedir(void);
+static char *fixpath_dup(char *path);
+static int orig_offset_rel;
+
+/*
+ * base_sepr and rel_fmt support construction of absolute paths from
+ * relative paths.
+ */
+static int base_sepr = 1; /* separator length btwn basedir & path */
+static char *rel_fmt[] = { "%s%s", "%s/%s" };
+
+static int eval_valid = 0; /* everything set up to do an eval_path() */
+
+/* libpkg/gpkgmap.c */
+extern int getmapmode();
+
+#define MSG_IR_REPL "Replacing current install root with %s."
+#define ERR_IRSET "Install_root has already been set to <%s> and used."
+#define ERR_IRNOTABS "Install_root (-R option) requires an absolute " \
+ "pathname: <%s>"
+#define ERR_ALLOCFAILED "insufficient memory in %s"
+#define ERR_ADMIN_INVAL "Invalid basedir entry in admin file."
+#define ERR_PATHNAME "Path name is invalid"
+#define ERR_RELINABS "Relative path <%s> found in absolute package."
+#define ERR_CL_MIS "Constructed CLIENT_BASEDIR <%s> and " \
+ "environment CLIENT_BASEDIR <%s> do not match."
+#define ERR_ASKBD "%s is already installed at %s. Cannot create a " \
+ "duplicate installation at %s."
+#define ERR_NO_CL_BD "Cannot resolve CLIENT_BASEDIR conflicts."
+#define ERR_AMBDIRS "Cannot evaluate path due to ambiguous " \
+ "base directories."
+#define ERR_NODELETE "unable to delete <%s>."
+#define ERR_MKBASE "unable to make directory <%s>."
+#define MSG_REQBASEDIR "Installation of this package requires a base " \
+ "directory."
+
+#define MSG_MUSTEXIST "\\nThe selected base directory <%s> must exist " \
+ "before installation is attempted."
+#define MSG_YORNPRMPT "Do you want this directory created now"
+
+#define MSG_ISAFILE "\\nThe selected base directory <%s> must exist " \
+ "before installation is attempted, but a file " \
+ "already exists in it's place."
+#define MSG_YORNFILE "Do you want the file deleted and the directory " \
+ "created now"
+
+#define MSG_PROMPT "Enter path to package base directory"
+
+#define MSG_HELP "Installation of this package requires that a UNIX " \
+ "directory be available for installation of " \
+ "appropriate software. This directory may be part " \
+ "of any mounted filesystem, or may itself be a " \
+ "mount point. In general, it is unwise to select a " \
+ "base directory which already contains other files " \
+ "and/or directories."
+
+/*
+ * Set the install root (-R option).
+ */
+
+int
+set_inst_root(char *path)
+{
+ static char tmp_path[PATH_MAX];
+
+ /*
+ * If we've already set the install_root but no one has used it
+ * yet, we'll complain and allow the change. If it's been used
+ * then we'll deny the switch & return failed.
+ */
+ if (install_root_exists)
+ /* If the two install_roots are different - problem */
+ if (strcmp(install_root, path))
+ /* We are trying to *change* the install_root */
+ if (ir_accessed) {
+ ptext(stderr, gettext(ERR_IRSET), path);
+ return (0);
+ } else { /* !ir_accessed */
+ ptext(stderr, gettext(MSG_IR_REPL), path);
+ install_root_exists = 0; /* reset */
+ install_root = NULL;
+ }
+
+ if (path && *path) {
+ if (*path != '/') {
+ ptext(stderr, gettext(ERR_IRNOTABS), path);
+ return (0);
+ }
+
+ (void) strlcpy(tmp_path, path, sizeof (tmp_path));
+
+ canonize(tmp_path);
+
+ install_root = tmp_path;
+
+ install_root_exists = 1;
+
+ install_root_len = strlen(install_root);
+
+ /* If install_root is '/' then it's trivial. */
+ if (install_root_len == 1)
+ install_root_len = 0;
+ else
+ z_set_zone_root(install_root);
+ } else
+ install_root_exists = 0;
+
+ return (1);
+}
+
+/*
+ * This routine returns a path with the correct install_root prepended.
+ * if the install_root has been set. NOTE : this allocates memory
+ * which will need to be freed at some point.
+ */
+char *
+fixpath(char *path)
+{
+ register char *npath_ptr, *ir_ptr;
+ char *npath = NULL;
+
+ if (path && *path) {
+ if (install_root_exists) {
+ if ((npath =
+ calloc(1, strlen(path) + install_root_len +
+ 1)) == NULL) {
+ progerr(gettext(ERR_ALLOCFAILED), "fixpath()");
+ quit(99);
+ }
+
+ npath_ptr = npath;
+ ir_ptr = get_inst_root();
+
+ while (*ir_ptr) /* for every char in install_root */
+ *npath_ptr++ = *ir_ptr++; /* copy it */
+
+ /*
+ * If install_root == "/", a concatenation will
+ * result in a return value of "//...", same goes
+ * for an install_root ending in '/'. So we back
+ * over a trailing '/' if it's there.
+ */
+ if (*(npath_ptr - 1) == '/')
+ npath_ptr--;
+
+ if (strcmp(path, "/"))
+ (void) strcpy(npath_ptr, path);
+ } else
+ /*
+ * If there's no install root & no client_basedir,
+ * then return the path
+ */
+ npath = strdup(path);
+ } else
+ /*
+ * If there's no path specified, return the install root
+ * since no matter what happens, this is where the
+ * path will have to start.
+ */
+ if (install_root_exists)
+ npath = strdup(get_inst_root());
+
+ return (npath);
+}
+
+/*
+ * This routine does what fixpath() does except it's for high-volume
+ * stuff restricted to the instvol() function. By using
+ * pathdup() and pathalloc() memory fragmentation is reduced. Also, the
+ * memory allocated by pathdup() and pathalloc() gets freed at the end
+ * of each volume installed.
+ */
+char *
+fixpath_dup(char *path)
+{
+ register char *npath_ptr, *ir_ptr;
+ char *npath = NULL;
+
+ if (path && *path) {
+ if (install_root_exists) {
+ npath = pathalloc(strlen(path) + install_root_len + 1);
+
+ npath_ptr = npath;
+ ir_ptr = get_inst_root();
+
+ while (*ir_ptr) /* for every char in install_root */
+ *npath_ptr++ = *ir_ptr++; /* copy it */
+
+ /*
+ * If install_root == "/", a concatenation will
+ * result in a return value of "//...", same goes
+ * for an install_root ending in '/'. So we back
+ * over a trailing '/' if it's there.
+ */
+ if (*(npath_ptr - 1) == '/')
+ npath_ptr--;
+
+ if (strcmp(path, "/"))
+ (void) strcpy(npath_ptr, path);
+ } else
+ /*
+ * If there's no install root & no client_basedir,
+ * then return the path
+ */
+ npath = pathdup(path);
+ } else
+ /*
+ * If there's no path specified, return the install root
+ * since no matter what happens, this is where the
+ * path will have to start.
+ */
+ if (install_root_exists)
+ npath = pathdup(get_inst_root());
+
+ return (npath);
+}
+
+/*
+ * This returns a pointer to a static name. This could be abused.
+ * -- JST (1993-07-21)
+ */
+char *
+get_inst_root(void)
+{
+ ir_accessed = 1; /* we can't change it now */
+ return (install_root);
+}
+
+/*
+ * This routine takes path and removes install_root from the path
+ * if it has already been prepended. If install_root is not prepended to
+ * path or install_root is '/' or path == NULL then path is returned
+ * as is. If the resulting path is somehow relative, a corrupt
+ * package name error is raised and the program quits. NOTE : This
+ * function usually returns a pointer into the original path
+ * argument. It doesn't allocate new memory. This is possible,
+ * of course, because the path being returned is guaranteed to
+ * be a subset of the original argument unless basedir = '/' in
+ * which case a pointer to a static "/" is returned. See
+ * orig_path() below if you want to be handed a new copy of the
+ * return value.
+ */
+char *
+orig_path_ptr(char *path)
+{
+ char *retv = NULL;
+
+ if (path && *path) { /* as long as we got an argument */
+ if (!install_root_exists) /* if no install_root */
+ retv = path; /* path unchanged */
+
+ /*
+ * Otherwise, if install_root is really prepended to the path
+ * then remove it dealing appropriately with special cases.
+ */
+ else if (strncmp(path, install_root, install_root_len) == 0) {
+ retv = path + install_root_len;
+ if (*retv == NULL)
+ retv = "/";
+
+ /*
+ * The result will be relative if install_root = '/'.
+ * If the basedir path was built legally, then moving
+ * the pointer back one character will make it
+ * absolute. If that fails then the path we got was
+ * incorrectly constructed in the first place.
+ */
+ else if (*retv != '/') {
+ retv--;
+ if (*retv != '/') {
+ progerr(gettext(ERR_PATHNAME));
+ quit(99);
+ }
+ }
+ } else
+ retv = path; /* All else failing, return path. */
+
+ canonize(retv);
+ }
+
+ return (retv);
+}
+
+/*
+ * This function does the same as orig_path_ptr() except that it mallocs
+ * new space and provides a new copy of the original basedir path which
+ * needs to be free()'d one way or another later.
+ */
+char *
+orig_path(char *path)
+{
+ char *retv;
+
+ retv = orig_path_ptr(path);
+
+ return ((retv == NULL) ? retv : strdup(retv));
+}
+
+/*
+ * This function lets us hold onto the environment's version of
+ * CLIENT_BASEDIR for later review by set_client_basedir().
+ */
+void
+set_env_cbdir()
+{
+ register char *cb_ptr;
+
+ cb_ptr = getenv("CLIENT_BASEDIR");
+
+ if (cb_ptr && *cb_ptr) {
+ env_cl_bdir = strdup(cb_ptr);
+ canonize(env_cl_bdir);
+ }
+}
+
+/* ask for the basedir */
+static int
+ask_basedir(char *path, int nointeract)
+{
+ int n;
+
+ if (nointeract) {
+ progerr(gettext(MSG_REQBASEDIR));
+ return (5);
+ } else {
+ path[0] = '\0';
+ if (n = ckpath(path, P_ABSOLUTE|P_DIR|P_WRITE,
+ basedir, NULL, gettext(MSG_HELP),
+ gettext(MSG_PROMPT)))
+ return (n); /* FAIL */
+ orig_basedir =
+ expand_path(path);
+ }
+ return (0);
+}
+
+/*
+ * Set the basedir and client_basedir based on install root and config
+ * files. It returns 0 if all OK otherwise returns the error code base
+ * appropriate to the problem.
+ */
+int
+set_basedirs(int reloc, char *adm_basedir, char *pkginst, int nointeract)
+{
+ char path[PATH_MAX];
+ int n;
+
+ relocatable = reloc;
+
+ /*
+ * If there are no relocatable files basedir is probably meaningless
+ * so we skip ahead to the simple tests. Otherwise we do the twisted
+ * stuff below. The BASEDIR is set based on the following heirarchy :
+ * 1. The entry in the admin file
+ * 2. The entry in the pkginfo file delivered on the medium
+ * 3. The entry in the already installed pkginfo file
+ * 4. ask
+ * If it's not a relocatable package, we go with whatever seems
+ * reasonable; if it's relocatable and we've exhausted our
+ * options, we ask.
+ */
+ if (reloc) {
+ int is_adm_basedir = (adm_basedir && *adm_basedir);
+ int is_update = 0;
+ int is_ask = 0;
+
+ if (is_adm_basedir) {
+ if (strcmp(adm_basedir, "update") == 0) {
+ is_update = 1;
+ is_ask = 1;
+ } else if (strcmp(adm_basedir, "ask") == 0)
+ is_ask = 1;
+ }
+
+ /*
+ * If there's a BASEDIR in the admin file & it's a valid
+ * absolute pathname, use it.
+ */
+ if (is_adm_basedir && strchr("/$", *adm_basedir))
+ orig_basedir = expand_path(adm_basedir);
+
+ /* If admin says 'ask regardless', ask and continue */
+ else if (is_adm_basedir && is_ask) {
+ if (n = ask_basedir(path, nointeract))
+ return (n);
+ if (is_update &&
+ strcmp(orig_basedir,
+ (basedir = getenv("BASEDIR"))) != 0) {
+ progerr(gettext(ERR_ASKBD),
+ getenv("PKG"), basedir, orig_basedir);
+ quit(4);
+ }
+ }
+ /*
+ * If it isn't the only other valid option,
+ * namely 'default', quit FAIL.
+ */
+ else if (is_adm_basedir &&
+ strcmp(adm_basedir, "default") != 0) {
+ progerr(gettext(ERR_ADMIN_INVAL));
+ return (1);
+
+ /*
+ * OK, the admin file has no preference, so we go to the
+ * other sources.
+ */
+ } else {
+ /*
+ * Check to see if BASEDIR is set in the environment
+ * (probably from the pkginfo file on the installation
+ * medium).
+ */
+ basedir = getenv("BASEDIR");
+ if (basedir && *basedir)
+ orig_basedir = expand_path(basedir);
+ else {
+ /*
+ * Check to see if the package BASEDIR was
+ * already defined during a previous
+ * installation of this package instance. The
+ * function below looks for an installed
+ * pkginfo file and scans it.
+ */
+ basedir = pkgparam(pkginst, "BASEDIR");
+ if (basedir && *basedir)
+ orig_basedir = expand_path(basedir);
+ else if (n = ask_basedir(path, nointeract))
+ return (n);
+ }
+ }
+ } else { /* not relocatable */
+ /*
+ * Since all paths are absolute the only reason to have a
+ * basedir is if there's an install root meaning there's
+ * really a basedir relative to this host or this package is
+ * absolute only because it's sparse in which case we're
+ * interested in the prior basedir. So we next check for a
+ * prior basedir and then an install root.
+ */
+ basedir = pkgparam(pkginst, "BASEDIR");
+ if (basedir && *basedir)
+ orig_basedir = expand_path(basedir);
+
+ else if (install_root_exists)
+ /*
+ * If we have a basedir *only because*
+ * we have an install_root, we need to
+ * set orig_basedir to '/' to simplify
+ * later attempts to force
+ * client_basedir.
+ */
+ orig_basedir = "/";
+ else {
+ eval_valid++; /* we can run eval_path() now */
+ return (0); /* fixpath below unnecessary */
+ }
+ }
+
+ basedir_exists = 1;
+
+ basedir = fixpath(orig_basedir);
+
+ /*
+ * If basedir == "/" then there's no need for a "/" between
+ * it and the rest of the path.
+ */
+ if (strcmp(basedir, "/") == 0)
+ base_sepr = 0;
+
+ if (set_client_basedir() == 0) {
+ progerr(gettext(ERR_NO_CL_BD));
+ return (1);
+ }
+
+ eval_valid++; /* we've confirmed the validity of everything */
+
+ return (0);
+}
+
+/*
+ * Make a directory from a path and all necessary directories above it as
+ * needed.
+ */
+int
+mkpath(char *p)
+{
+ char *pt;
+
+ /* if entire path exists, return o.k. */
+
+ if (access(p, F_OK) == 0) {
+ return (0);
+ }
+
+ /* entire path not there - check components and create */
+
+ pt = (*p == '/') ? p+1 : p;
+ do {
+ if (pt = strchr(pt, '/')) {
+ *pt = '\0';
+ }
+ if ((access(p, F_OK) != 0) && (mkdir(p, 0755) != 0)) {
+ return (-1);
+ }
+ if (pt) {
+ *pt++ = '/';
+ }
+ } while (pt);
+
+ return (0);
+}
+
+/* This makes the required base directory if necessary */
+void
+mkbasedir(int flag, char *basedir)
+{
+ char ans[MAX_INPUT];
+ int n;
+
+ /*
+ * If a base directory is called for but there's no such directory on
+ * the system, deal with that issue.
+ */
+ if (is_a_basedir() && isdir(basedir)) {
+ if (flag) { /* Interaction is OK. */
+ /*
+ * If there's a non-directory object in the way, ask.
+ */
+ if (access(basedir, F_OK) == 0) {
+ ptext(stderr, gettext(MSG_ISAFILE), basedir);
+
+ if (n = ckyorn(ans, NULL, NULL, NULL,
+ gettext(MSG_YORNFILE)))
+ quit(n);
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+
+ /*
+ * It isn't a directory, so we'll just unlink
+ * it.
+ */
+ if (unlink(basedir) == -1) {
+ progerr(gettext(ERR_NODELETE),
+ basedir);
+ quit(99);
+ }
+
+ } else {
+ ptext(stderr, gettext(MSG_MUSTEXIST), basedir);
+
+ if (n = ckyorn(ans, NULL, NULL, NULL,
+ gettext(MSG_YORNPRMPT)))
+ quit(n);
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+ }
+ }
+
+ if (access(basedir, F_OK) == 0 || mkpath(basedir)) {
+ progerr(gettext(ERR_MKBASE), basedir);
+ quit(99);
+ }
+ }
+}
+
+/*
+ * Create a client_basedir if it is appropriate. If all goes well, resulting
+ * in either a valid client_basedir or a valid lack thereof, it returns 1.
+ * If there is an irreconcileable conflict, it returns 0.
+ */
+static int
+set_client_basedir(void)
+{
+ if (install_root_exists) {
+ if (basedir_exists)
+ client_basedir = strdup(orig_basedir);
+ else
+ client_basedir = "/";
+ client_basedir_exists = 1;
+ }
+
+ /*
+ * In response to an agreement associated with bug report #1133956,
+ * CLIENT_BASEDIR will be defined in all cases where BASEDIR is
+ * defined until the on1094 release. For on1094 delete the else if
+ * and associated expressions below. -- JST (6/25/1993)
+ */
+ else if (basedir_exists) {
+ client_basedir = strdup(basedir);
+ client_basedir_exists = 1;
+ }
+
+ /*
+ * At this point we may or may not have a client_basedir defined. Now
+ * we need to check for one in the environment & make sure it syncs
+ * up with prior findings. If there's no other client_basedir defined,
+ * the environment defines it.
+ */
+ if (env_cl_bdir && *env_cl_bdir) {
+ if (client_basedir_exists) {
+ /* If the two client basedirs mismatch, return fail */
+ if (strcmp(client_basedir, env_cl_bdir)) {
+ ptext(stderr, gettext(ERR_CL_MIS),
+ client_basedir, env_cl_bdir);
+ return (0);
+ }
+ } else {
+ client_basedir = env_cl_bdir;
+ client_basedir_exists = 1;
+ }
+ }
+
+ return (1);
+}
+
+static char *
+expand_path(char *path)
+{
+ char path_buf[PATH_MAX];
+
+ if (!path || !*path)
+ return (path);
+
+ (void) strlcpy(path_buf, path, sizeof (path_buf));
+ mappath(getmapmode(), path_buf);
+ canonize(path_buf);
+
+ return (qstrdup(path_buf));
+}
+
+char *
+get_basedir(void)
+{
+ return (basedir);
+}
+
+char *
+get_client_basedir(void)
+{
+ return (client_basedir);
+}
+
+/*
+ * This function returns the basedir that is appropriate for this package's
+ * pkginfo file.
+ */
+char *
+get_info_basedir(void)
+{
+ if (install_root_exists)
+ return (client_basedir);
+ else if (basedir_exists)
+ return (basedir);
+ else
+ return (NULL);
+}
+
+int
+is_an_inst_root(void)
+{
+ return (install_root_exists);
+}
+
+int
+is_a_basedir(void)
+{
+ return (basedir_exists);
+}
+
+int
+is_relocatable(void)
+{
+ return (relocatable);
+}
+
+int
+is_a_cl_basedir(void)
+{
+ return (client_basedir_exists);
+}
+
+/*
+ * Since calls to putparam() become valid long after much of the above
+ * code has run, this routine allows the insertion of these key
+ * environment variables without passing a bunch of pointers.
+ */
+void
+put_path_params(void)
+{
+ if (install_root_exists)
+ putparam("PKG_INSTALL_ROOT", get_inst_root());
+
+ if (basedir_exists)
+ putparam("BASEDIR", basedir);
+
+ if (client_basedir_exists)
+ putparam("CLIENT_BASEDIR", client_basedir);
+}
+
+/*
+ * This fills three pointers and a buffer which contains the longest
+ * possible path (with install_root and basedir prepended. The pointers
+ * are to the subpaths within the string. This was added so that the
+ * eptlist could be produced with all relevant paths defined without
+ * repeated calls and string scans. For example, given a path of
+ * haberdasher/crute we may return
+ *
+ * server_ptr -----> /export/root/client1/opt/SUNWhab/haberdasher/crute
+ * | |
+ * client_ptr --------------------------- |
+ * map_ptr -------------------------------------------
+ *
+ * We construct the new path based upon the established environment
+ * and the type of path that was passed. Here are the possibilities:
+ *
+ * | | relative path | absolute path |
+ * | --------------------------------|---------------|---------------|
+ * | is_an_inst_root | 1 | 2 |
+ * V ! an_inst_root && is_a_basedir | 1 | 3 |
+ * ! an_inst_root && ! a_basedir | X | 3 |
+ *
+ * METHOD
+ * 1. Prepend the basedir to the path (the basedir is guaranteed to exist
+ * whenever there's an install_root).
+ *
+ * 2. Prepend the install_root (not the basedir) to the path
+ *
+ * 3. Return the path as unchanged.
+ *
+ * X. THIS CAN'T HAPPEN
+ */
+int
+eval_path(char **server_ptr, char **client_ptr, char **map_ptr, char *path)
+{
+ static int client_offset;
+ static int offsets_valid, retcode;
+ int path_size;
+
+ if (!offsets_valid) {
+ /*
+ * This is the offset from the beginning of the evaluated
+ * path to the start of the relative path. Note that we
+ * are accounting for the '/' inserted between the
+ * basedir and the path with the '+ 1'. If there is a
+ * relative path, then there is always a basedir. The
+ * only way this will come up '0' is if this is an
+ * absolute package.
+ */
+ orig_offset_rel = (is_a_basedir()) ? (strlen(basedir) +
+ base_sepr) : 0;
+
+ /*
+ * This is the position of the client-relative path
+ * in that it points to the '/' beginning the base
+ * directory or the absolute path. Once the basedir has
+ * been afixed, the path is absolute. For that reason,
+ * the client path is the same thing as the original path
+ * if it were absolute.
+ */
+ client_offset = (is_an_inst_root()) ? install_root_len : 0;
+
+ offsets_valid = 1;
+ }
+
+ /*
+ * If we've evaluated the base directory and come up trumps,
+ * then we can procede with this operation, otherwise, the
+ * available data is too ambiguous to resolve the issue.
+ */
+ if (eval_valid) {
+ if (RELATIVE(path)) {
+ if (relocatable) {
+ /*
+ * Figure out how long our buffer will
+ * have to be.
+ */
+ path_size = orig_offset_rel + strlen(path);
+
+ (*server_ptr) = pathalloc(path_size);
+
+ *client_ptr = *server_ptr + client_offset;
+
+ if (map_ptr)
+ *map_ptr = *server_ptr +
+ orig_offset_rel;
+
+ /* LINTED warning: variable format specifier */
+ (void) snprintf(*server_ptr, path_size+1,
+ rel_fmt[base_sepr], basedir, path);
+ } else {
+ ptext(stderr, gettext(ERR_RELINABS), path);
+ retcode = 0;
+ }
+ } else { /* NOT RELATIVE */
+ *server_ptr = fixpath_dup(path);
+
+ if ((*client_ptr = *server_ptr + client_offset) == NULL)
+ *client_ptr = "/";
+
+ if (map_ptr)
+ *map_ptr = *client_ptr;
+ }
+
+ retcode = 1;
+ } else {
+ ptext(stderr, gettext(ERR_AMBDIRS));
+ retcode = 0;
+ }
+
+ return (retcode);
+}
+
+void
+export_client_env(char *root_path)
+{
+ char *inst_release_path;
+ char *key;
+ char *value;
+ FILE *inst_fp;
+ size_t len;
+
+ /*
+ * Put the variables found in a clients INST_RELEASE file into the
+ * package environment so procedure scripts can know what
+ * release/version/revision a client is running. Also this function
+ * doesn't return state since the INST_RELEASE file may not exist in
+ * some package installation environments
+ */
+
+ len = strlen(root_path) + strlen(INST_RELEASE) + 2;
+
+ inst_release_path = (char *)malloc(len);
+
+ key = (char *)malloc(PATH_MAX);
+
+ (void) snprintf(inst_release_path, len, "%s/%s", root_path,
+ INST_RELEASE);
+
+ if ((inst_fp = fopen(inst_release_path, "r")) != NULL) {
+ while (value = fpkgparam(inst_fp, key)) {
+ if (strcmp(key, "OS") == 0) {
+ putparam("PKG_CLIENT_OS", value);
+ } else if (strcmp(key, "VERSION") == 0) {
+ putparam("PKG_CLIENT_VERSION", value);
+ } else if (strcmp(key, "REV") == 0) {
+ putparam("PKG_CLIENT_REVISION", value);
+ }
+ *key = '\0';
+ }
+ (void) fclose(inst_fp);
+ }
+ free(inst_release_path);
+ free(key);
+}
+
+/*
+ * Increment variable indicating the installation is from a partially spooled
+ * package.
+ */
+void
+set_partial_inst(void)
+{
+ partial_inst++;
+}
+
+/*
+ * Return variable indicating that the installation is from a partially spooled
+ * package.
+ * Returns: !0 for true
+ * 0 for false
+ */
+int
+is_partial_inst(void)
+{
+ return (partial_inst);
+}
+
+/*
+ * Increment variable indicating that only the depend and pkginfo DB's are to be
+ * updated
+ */
+
+void
+set_depend_pkginfo_DB(boolean_t a_setting)
+{
+ depend_pkginfo_DB = a_setting;
+}
+
+/*
+ * Return variable indicating that the installation only updates the depend
+ * and pkginfo DB's.
+ * Returns: !0 for true
+ * 0 for false
+ */
+
+boolean_t
+is_depend_pkginfo_DB(void)
+{
+ return (depend_pkginfo_DB);
+}
+
+/*
+ * Increment variable indicating that packages should not be spooled in
+ * var/sadm/pkg/<pkgabbrev>/save/pspool/
+ */
+void
+disable_spool_create(void)
+{
+ partial_spool_create++;
+}
+
+/*
+ * Return variable indicating whether or not the partial spool directory
+ * should be created.
+ * Returns: 1 for true
+ * 0 for false
+ */
+int
+is_spool_create(void)
+{
+ return (partial_spool_create);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/flex_dev.c b/usr/src/cmd/svr4pkg/libinst/flex_dev.c
new file mode 100644
index 0000000000..aea0b10c99
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/flex_dev.c
@@ -0,0 +1,87 @@
+/*
+ * 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 1993 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+#define ERR_CHDIR "unable to chdir back to <%s>, errno=%d"
+#define ERR_GETCWD "unable to determine the current working directory, " \
+ "errno=%d"
+
+char *
+flex_device(char *device_name, int dev_ok)
+{
+ char new_device_name[PATH_MAX];
+ char *np = device_name;
+ char *cwd = NULL;
+
+ if (!device_name || !*device_name) /* NULL or empty */
+ return (np);
+
+ if (dev_ok == 1 && listdev(np) != (char **) NULL) /* device.tab */
+ return (np);
+
+ if (!strncmp(np, "/dev", 4)) /* /dev path */
+ return (np);
+
+ if ((cwd = getcwd(NULL, PATH_MAX)) == NULL) {
+ progerr(gettext(ERR_GETCWD), errno);
+ exit(99);
+ }
+
+ if (realpath(np, new_device_name) == NULL) { /* path */
+ if (chdir(cwd) == -1) {
+ progerr(gettext(ERR_CHDIR), cwd, errno);
+ (void) free(cwd);
+ exit(99);
+ }
+ if (*np != '/' && dev_ok == 2) {
+ (void) sprintf(new_device_name, "%s/%s", cwd, np);
+ canonize(new_device_name);
+ if ((np = strdup(new_device_name)) == NULL)
+ np = device_name;
+ }
+ (void) free(cwd);
+ return (np);
+ }
+
+ if (strcmp(np, new_device_name)) {
+ if ((np = strdup(new_device_name)) == NULL)
+ np = device_name;
+ }
+
+ (void) free(cwd);
+ return (np);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/is_local_host.c b/usr/src/cmd/svr4pkg/libinst/is_local_host.c
new file mode 100644
index 0000000000..dde9408b06
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/is_local_host.c
@@ -0,0 +1,151 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+
+static int is_local_if(struct hostent *hp);
+
+/*
+ * Given a host name, check to see if it points to the local host.
+ * If it does, return 1, else return 0.
+ *
+ * The strategy is this: translate the host name argument to a list of
+ * addresses. Then compare each of those addresses to the addresses of
+ * network interfaces on this host.
+ */
+int
+is_local_host(char *host)
+{
+ struct hostent *hp;
+ int err;
+ int flags = AI_DEFAULT;
+
+ if (hp = getipnodebyname((const char *) host, AF_INET, flags, &err))
+ if (is_local_if(hp))
+ return (1);
+ if (hp = getipnodebyname((const char *) host, AF_INET6, flags, &err))
+ if (is_local_if(hp))
+ return (1);
+
+ return (0);
+}
+
+static int
+is_local_if(struct hostent *hp)
+{
+ char *buf;
+ struct lifconf lifc;
+ struct lifnum lifn;
+ struct lifreq lifr;
+ struct lifreq *lifrp;
+ int bufsiz;
+ int nha;
+ int nif;
+ int s;
+
+ if ((s = socket(hp->h_addrtype, SOCK_DGRAM, 0)) == -1) {
+ perror("socket");
+ return (0);
+ }
+
+ lifn.lifn_family = hp->h_addrtype;
+ lifn.lifn_flags = LIFC_EXTERNAL_SOURCE;
+ if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) == -1) {
+ perror("SIOCGLIFNUM");
+ (void) close(s);
+ return (0);
+ }
+ bufsiz = lifn.lifn_count * sizeof (struct lifreq);
+
+ if ((buf = malloc(bufsiz)) == NULL) {
+ perror("malloc");
+ (void) close(s);
+ return (0);
+ }
+
+ lifc.lifc_family = hp->h_addrtype;
+ lifc.lifc_flags = LIFC_EXTERNAL_SOURCE;
+ lifc.lifc_len = bufsiz;
+ lifc.lifc_buf = buf;
+ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) == -1) {
+ perror("SIOCGLIFCONF");
+ (void) close(s);
+ free(buf);
+ return (0);
+ }
+
+#define lifraddrp(lifrp) ((lifrp->lifr_addr.ss_family == AF_INET6) ? \
+ (void *) &((struct sockaddr_in6 *)&lifrp->lifr_addr)->sin6_addr : \
+ (void *) &((struct sockaddr_in *)&lifrp->lifr_addr)->sin_addr)
+
+ for (lifrp = lifc.lifc_req,
+ nif = lifc.lifc_len / sizeof (struct lifreq);
+ nif > 0; nif--, lifrp++) {
+ if (lifrp->lifr_addr.ss_family != hp->h_addrtype) {
+ continue;
+ }
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strncpy(lifr.lifr_name, lifrp->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) == -1) {
+ perror("SIOCGLIFFLAGS");
+ (void) close(s);
+ free(buf);
+ return (0);
+ }
+
+ for (nha = 0; hp->h_addr_list[nha]; nha++) {
+ if (memcmp(hp->h_addr_list[nha], lifraddrp(lifrp),
+ hp->h_length) == 0) {
+ (void) close(s);
+ free(buf);
+ return (1);
+ }
+ }
+ }
+
+#undef lifraddrp
+
+ (void) close(s);
+ free(buf);
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/isreloc.c b/usr/src/cmd/svr4pkg/libinst/isreloc.c
new file mode 100644
index 0000000000..73bed3160b
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/isreloc.c
@@ -0,0 +1,243 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pkglib.h>
+#include <libintl.h>
+#include <libinst.h>
+#include <install.h>
+
+#define ERR_NOPKGMAP "Cannot open pkgmap file."
+
+#define ENTRY_MAX (PATH_MAX + 38)
+#define IGNORE_START ":#!"
+#define IGNORE_TYPE "i"
+
+static int has_rel_path(char *entry);
+static int is_relative(char *entry);
+
+/*
+ * This routine attempts to determine with certainty whether or not
+ * the package is relocatable or not. It first attempts to determine if
+ * there is a reloc directory by scanning pkginstdir. If that fails to
+ * provide a definite result (pkg is coming from a stream device and
+ * the directories aren't in place) it inspects the pkgmap in pkginstdir
+ * in order to determine if the package has relocatable elements. If
+ * there is a single relative pathname or $BASEDIR/... construct,
+ * this returns 1. If no relative pathnames are found it returns 0
+ * meaning absolute package and all the things that implies.
+ *
+ * This does not determine the validity of the pkgmap file. If the pkgmap
+ * is corrupted, this returns 0.
+ */
+int
+isreloc(char *pkginstdir)
+{
+ FILE *pkg_fp;
+ struct dirent *drp;
+ DIR *dirfp;
+ int retcode = 0;
+
+ /* First look in the directory */
+ if ((dirfp = opendir(pkginstdir)) != NULL) {
+ while ((drp = readdir(dirfp)) != NULL) {
+ if (drp->d_name[0] == '.')
+ continue;
+ if (strlen(drp->d_name) < (size_t)5)
+ continue;
+ if (strncmp(drp->d_name, "reloc", 5) == 0) {
+ retcode = 1;
+ break;
+ }
+ }
+ (void) closedir(dirfp);
+ }
+
+ /*
+ * If retcode == 0, meaning we didn't find a reloc directory then we
+ * probably don't have a complete directory structure available to
+ * us. We'll have to determine what type of package it is by scanning
+ * the pkgmap file.
+ */
+ if (retcode == 0) {
+ char path_buffer[ENTRY_MAX];
+
+ (void) snprintf(path_buffer, sizeof (path_buffer),
+ "%s/pkgmap", pkginstdir);
+
+ canonize(path_buffer);
+
+ if ((pkg_fp = fopen(path_buffer, "r")) != NULL) {
+ while (fgets(path_buffer, sizeof (path_buffer), pkg_fp))
+ if (has_rel_path(path_buffer)) {
+ retcode = 1;
+ break;
+ }
+ (void) fclose(pkg_fp);
+ } else {
+ progerr(gettext(ERR_NOPKGMAP));
+ quit(99);
+ }
+ }
+
+ return (retcode);
+}
+
+/*
+ * Test the string for the presence of a relative path. If found, return
+ * 1 otherwise return 0. If we get past the IGNORE_TYPE test, we're working
+ * with a line of the form :
+ *
+ * dpart type classname pathname ...
+ *
+ * It's pathname we're going to test here.
+ *
+ * Yes, yes, I know about sscanf(); but, I don't need to reserve 4K of
+ * space and parse the whole string, I just need to get to two tokens.
+ * We're in a hurry.
+ */
+static int
+has_rel_path(char *entry)
+{
+ register int entry_pos = 1;
+
+ /* If the line is a comment or special directive, return 0 */
+ if (*entry == NULL || strchr(IGNORE_START, *entry))
+ return (0);
+
+ /* Skip past this data entry if it is volume number. */
+ if (isdigit(*entry)) {
+ while (*entry && !isspace(*entry)) {
+ entry++;
+ }
+ }
+
+ /* Skip past this white space */
+ while (*entry && isspace(*entry)) {
+ entry++;
+ }
+
+ /*
+ * Now we're either pointing at the type or we're pointing at
+ * the termination of a degenerate entry. If the line is degenerate
+ * or the type indicates this line should be ignored, we return
+ * as though not relative.
+ */
+ if (*entry == NULL || strchr(IGNORE_TYPE, *entry))
+ return (0);
+
+ /* The pathname is in the third position */
+ do {
+ /* Skip past this data entry */
+ while (*entry && !isspace(*entry)) {
+ entry++;
+ }
+
+ /* Skip past this white space and call this the next entry */
+ while (*entry && isspace(*entry)) {
+ entry++;
+ }
+ } while (++entry_pos < 3 && *entry != NULL);
+
+ /*
+ * Now we're pointing at the first character of the pathname.
+ * If the file is corrupted, we're pointing at NULL. is_relative()
+ * will return FALSE for NULL which will yield the correct return
+ * value.
+ */
+ return (is_relative(entry));
+}
+
+/*
+ * If the path doesn't begin with a variable, the first character in the
+ * path is tested for '/' to determine if it is absolute or not. If the
+ * path begins with a '$', that variable is resolved if possible. If it
+ * isn't defined yet, we exit with error code 1.
+ */
+static int
+is_relative(char *entry)
+{
+ register char *eopath = entry; /* end of full pathname pointer */
+ register char **lasts = &entry;
+
+ /* If there is a path, test it */
+ if (entry && *entry) {
+ if (*entry == '$') { /* it's an environment parameter */
+ entry++; /* skip the '$' */
+
+ while (*eopath && !isspace(*eopath))
+ eopath++;
+
+ *eopath = '\0'; /* terminate the pathname */
+
+ /* isolate the variable */
+ entry = strtok_r(entry, "/", lasts);
+
+ /*
+ * Some packages call out $BASEDIR for relative
+ * paths in the pkgmap even though that is
+ * redundant. This special case is actually
+ * an indication that this is a relative
+ * path.
+ */
+ if (strcmp(entry, "BASEDIR") == 0)
+ return (1);
+ /*
+ * Since entry is pointing to a now-expendable PATH_MAX
+ * size buffer, we can expand the path variable into it
+ * here.
+ */
+ entry = getenv(entry);
+ }
+
+ /*
+ * Return type of path. If pathname was unresolvable
+ * variable, assume relative. This looks like a strange
+ * assumption since the resolved path may end up
+ * absolute and pkgadd may prompt the user for a basedir
+ * incorrectly because of this assumption. Unfortunately,
+ * the request script MUST have a final BASEDIR in the
+ * environment before it executes.
+ */
+ if (entry && *entry)
+ return (RELATIVE(entry));
+ else
+ return (1);
+ } else /* no path, so we skip it */
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/listmgr.c b/usr/src/cmd/svr4pkg/libinst/listmgr.c
new file mode 100644
index 0000000000..e73f414458
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/listmgr.c
@@ -0,0 +1,564 @@
+/*
+ * 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 1996 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include "pkglib.h"
+
+/*
+ * This is the module responsible for allocating and maintaining lists that
+ * require allocation of memory. For certain lists, large chunks are
+ * allocated once to contain a large number of entries in each chunk (bl_*
+ * for block list). The other approach involves the augmentation of linked
+ * lists, each entry of which is alloc'd individually.
+ */
+#define ERR_CS_ALLOC "ERROR: Cannot allocate control structure for %s array."
+#define ERR_MEM_ALLOC "ERROR: Cannot allocate memory for %s array."
+
+#define MAX_ARRAYS 50
+
+#define ARRAY_END(x) (bl_cs_array[x]->cur_segment->avail_ptr)
+#define REC_SIZE(x) (bl_cs_array[x]->struct_size)
+#define EOSEG(x) (bl_cs_array[x]->cur_segment->eoseg_ptr)
+#define GET_AVAIL(x) (ARRAY_END(x) + REC_SIZE(x))
+
+struct alloc_seg {
+ char *seg_ptr; /* ptr to the allocated block */
+ char *avail_ptr; /* ptr to the next available list element */
+ char *eoseg_ptr; /* last byte in the segment */
+ int full; /* segment has no available space */
+ struct alloc_seg *next; /* next record */
+};
+
+struct blk_list_cs {
+ int struct_size; /* size of a single list element */
+ int count_per_block; /* number of list elements per block */
+ int block_size; /* just to save time - alloc size */
+ int data_handle; /* list_handle for pointer array */
+ struct alloc_seg *alloc_segs; /* memory pool */
+
+ struct alloc_seg *cur_segment; /* the current allocated segment */
+ int total_elem; /* total elements stored */
+ int contiguous; /* use realloc to grow */
+ char *desc; /* description of the list */
+};
+
+static struct blk_list_cs *bl_cs_array[MAX_ARRAYS];
+static int next_array_elem;
+
+/* Support functions */
+static int
+invalid_handle(int list_handle)
+{
+ if (list_handle < 0 || list_handle >= next_array_elem)
+ return (1);
+
+ return (0);
+}
+
+static int
+invalid_record(int list_handle, int recno)
+{
+ if (invalid_handle(list_handle))
+ return (1);
+
+ if (recno < 0 || recno > bl_cs_array[list_handle]->total_elem)
+ return (1);
+
+ return (0);
+}
+
+static void
+free_list(int list_handle)
+{
+ struct blk_list_cs *bl_ptr;
+ struct alloc_seg *segstr_ptr, *nextstr_ptr;
+
+ /* Make sure this wasn't free'd earlier */
+ if (bl_cs_array[list_handle] == NULL)
+ return;
+
+ bl_ptr = bl_cs_array[list_handle];
+
+ /* First free the alloc_seg list. */
+ segstr_ptr = bl_ptr->alloc_segs;
+
+ if (segstr_ptr) {
+ do {
+ nextstr_ptr = segstr_ptr->next;
+
+ /* Free the memory block. */
+ free((void *)segstr_ptr->seg_ptr);
+
+ /* Free the control structure. */
+ free((void *)segstr_ptr);
+ segstr_ptr = nextstr_ptr;
+ } while (segstr_ptr);
+ }
+
+ /* Free the block control structure. */
+ free((void *)bl_ptr->desc);
+ free((void *)bl_ptr);
+
+ bl_cs_array[list_handle] = NULL;
+}
+
+/* Allocate another alloc_seg structure. */
+static int
+alloc_next_seg(struct blk_list_cs *bl_ptr)
+{
+ struct alloc_seg *new_alloc_cs;
+
+ if (bl_ptr->contiguous) {
+ int offset_to_avail, seg_size, new_size;
+ struct alloc_seg *alloc_segment;
+
+ if (bl_ptr->alloc_segs) {
+ alloc_segment = bl_ptr->alloc_segs;
+
+ offset_to_avail = (alloc_segment->avail_ptr -
+ alloc_segment->seg_ptr);
+ seg_size = (alloc_segment->eoseg_ptr -
+ alloc_segment->seg_ptr);
+ new_size = (seg_size + bl_ptr->block_size);
+ } else {
+ if ((bl_ptr->alloc_segs =
+ (struct alloc_seg *)calloc(1,
+ sizeof (struct alloc_seg))) == NULL) {
+ logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ?
+ bl_ptr->desc : "an unknown"));
+ return (0);
+ }
+
+ alloc_segment = bl_ptr->alloc_segs;
+
+ offset_to_avail = 0;
+ seg_size = 0;
+ new_size = bl_ptr->block_size;
+ }
+
+ bl_ptr->cur_segment = alloc_segment;
+
+ if ((alloc_segment->seg_ptr =
+ (char *)realloc((void *)alloc_segment->seg_ptr,
+ (unsigned)new_size)) == NULL) {
+ logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ?
+ bl_ptr->desc : "an unknown"));
+ return (0);
+ }
+
+ alloc_segment->next = NULL;
+
+ /* reset the status */
+ alloc_segment->full = 0;
+
+ /* readjust the original pointers */
+ alloc_segment->avail_ptr = alloc_segment->seg_ptr +
+ offset_to_avail;
+ alloc_segment->eoseg_ptr = alloc_segment->seg_ptr + new_size;
+
+ (void) memset(alloc_segment->avail_ptr, '\000',
+ bl_ptr->block_size);
+ } else {
+ /* Allocate the control structure and link it into the list. */
+ if ((new_alloc_cs = (struct alloc_seg *)malloc(
+ sizeof (struct alloc_seg))) == NULL) {
+ logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ?
+ bl_ptr->desc : "an unknown"));
+ return (0);
+ }
+
+ if (bl_ptr->alloc_segs == NULL) {
+ /*
+ * If this is the first allocation, then initialize
+ * the head pointer and set cur_segment to this first
+ * block of memory.
+ */
+ bl_ptr->alloc_segs = new_alloc_cs;
+ } else {
+ /*
+ * Otherwise, point the current cur_segment to the
+ * next one and then point to the new one.
+ */
+ bl_ptr->cur_segment->next = new_alloc_cs;
+ }
+
+ new_alloc_cs->next = NULL;
+ bl_ptr->cur_segment = new_alloc_cs;
+
+ new_alloc_cs->full = 0;
+
+ /* Now allocate the block of memory that this controls. */
+ if ((new_alloc_cs->seg_ptr = calloc(bl_ptr->count_per_block,
+ bl_ptr->struct_size)) == NULL) {
+ logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ?
+ bl_ptr->desc : "an unknown"));
+ return (0);
+ }
+
+ new_alloc_cs->avail_ptr = new_alloc_cs->seg_ptr;
+ new_alloc_cs->eoseg_ptr = (new_alloc_cs->seg_ptr +
+ bl_ptr->block_size);
+ }
+
+ return (1);
+}
+
+/*
+ * These first functions (beginning with bl_*) manage simple block lists. The
+ * pointers returned, may get lost if they aren't assigned to an array or
+ * something. While individual records can be obtained by record number, the
+ * process isn't very efficient. Look to the array management section
+ * (ar_*)for an easily administrable list.
+ */
+
+/*
+ * Create a block list. Allocate memory for a block list structure and
+ * initialize that structure. This doesn't actually allocate memory for the
+ * list yet, just the controlling data structure. Returns -1 on failure and a
+ * valid block list handle otherwise.
+ *
+ * NOTE: At the time of writing, it was not seen as important to recover block
+ * pointers made available with a bl_free() (two of these at most in
+ * pkginstall). If this became important later, we could trade efficiency for
+ * speed by ignoring next_array_elem and actually scanning through the array
+ * for a NULL pointer and then return that.
+ */
+int
+bl_create(int count_per_block, int struct_size, char *desc)
+{
+ struct blk_list_cs *bl_ptr;
+ int retval;
+
+ if ((bl_cs_array[next_array_elem] =
+ (struct blk_list_cs *)calloc(1, sizeof (struct blk_list_cs))) ==
+ NULL) {
+ logerr(gettext(ERR_CS_ALLOC), (desc ? desc : "an unknown"));
+ return (-1);
+ }
+
+ bl_ptr = bl_cs_array[next_array_elem];
+ retval = next_array_elem++;
+
+ bl_ptr->data_handle = -1;
+ bl_ptr->struct_size = struct_size;
+ bl_ptr->count_per_block = count_per_block;
+ bl_ptr->block_size = (count_per_block * struct_size);
+ bl_ptr->desc = strdup((desc ? desc : "unknown"));
+
+ return (retval);
+}
+
+/*
+ * Get the next available entry in the list. This will allocate memory as
+ * required based on the initialization values in bl_create(). Returns a
+ * pointer to the allocated memory segment or NULL if operation was not
+ * possible.
+ */
+char *
+bl_next_avail(int list_handle)
+{
+ struct blk_list_cs *bl_ptr;
+ char *retval;
+
+ if (invalid_handle(list_handle))
+ return (NULL);
+
+ bl_ptr = bl_cs_array[list_handle];
+
+ /*
+ * Allocate more memory if none is allocated yet or our last access
+ * filled the allotted segment.
+ */
+ if (bl_ptr->cur_segment == NULL || bl_ptr->cur_segment->full)
+ if (!alloc_next_seg(bl_ptr))
+ return (NULL);
+
+ /* Get the correct pointer. */
+ retval = bl_ptr->cur_segment->avail_ptr;
+
+ /* Advance it and mark if full. */
+ bl_ptr->cur_segment->avail_ptr += bl_ptr->struct_size;
+ bl_ptr->total_elem++;
+
+ if (bl_ptr->cur_segment->avail_ptr >= bl_ptr->cur_segment->eoseg_ptr)
+ bl_ptr->cur_segment->full = 1;
+
+ return (retval);
+}
+
+char *
+bl_get_record(int list_handle, int recno)
+{
+ struct blk_list_cs *bl_ptr;
+ struct alloc_seg *cur_as_ptr;
+ int cur_rec = 0;
+
+ if (invalid_record(list_handle, recno))
+ return (NULL);
+
+ bl_ptr = bl_cs_array[list_handle];
+
+ cur_as_ptr = bl_ptr->alloc_segs;
+
+ while (recno > (cur_rec + bl_ptr->count_per_block)) {
+ cur_as_ptr = cur_as_ptr->next;
+
+ if (cur_as_ptr == NULL)
+ return (NULL);
+
+ cur_rec += bl_ptr->count_per_block;
+ }
+
+ /*
+ * Now cur_as_ptr points to the allocated segment bearing the
+ * intended record and all we do now is move down that by the
+ * remaining record lengths.
+ */
+
+ return ((char *)cur_as_ptr + ((recno - cur_rec) * bl_ptr->struct_size));
+}
+
+void
+bl_free(int list_handle)
+{
+ int cur_handle;
+
+ if (list_handle == -1) {
+ for (cur_handle = 0; cur_handle < next_array_elem;
+ cur_handle++) {
+ free_list(cur_handle);
+ }
+ } else {
+ if (invalid_handle(list_handle))
+ return;
+
+ free_list(list_handle);
+ }
+}
+
+/*
+ * These are the array management functions. They insert into (and can return
+ * a pointer to) a contiguous list of pointers to stuff. This keeps
+ * everything together in a very handy package and is very similar in
+ * appearance to the arrays created by the old AT&T code. The method for
+ * presenting the interface is entirely different, however.
+ */
+
+/*
+ * This constructs, maintains and returns pointers into a growable array of
+ * pointers to structures of the form
+ * struct something *array[n]
+ * The last element in the array is always NULL.
+ */
+int
+ar_create(int count_per_block, int struct_size, char *desc)
+{
+ int data_handle, retval;
+ char ar_desc[60];
+ struct blk_list_cs *array_ptr;
+
+ if ((data_handle = bl_create(count_per_block, struct_size, desc)) == -1)
+ return (-1);
+
+ sprintf(ar_desc, "%s pointer", desc);
+ if ((retval = bl_create(count_per_block, sizeof (char *),
+ ar_desc)) == -1)
+ return (-1);
+
+ array_ptr = bl_cs_array[retval];
+
+ array_ptr->contiguous = 1;
+ array_ptr->data_handle = data_handle;
+
+ return (retval);
+}
+
+/* Return a pointer to the first element in the array. */
+char **
+ar_get_head(int list_handle)
+{
+ if (invalid_handle(list_handle) ||
+ bl_cs_array[list_handle]->alloc_segs == NULL)
+ return (NULL);
+
+ return ((char **)bl_cs_array[list_handle]->alloc_segs->seg_ptr);
+}
+
+/*
+ * Free up the entry in the array indicated by index, but hold onto it for
+ * future use.
+ */
+int
+ar_delete(int list_handle, int index)
+{
+ char **array;
+ char *deleted_rec;
+ int i;
+ struct blk_list_cs *list_ptr, *data_ptr;
+
+ if ((array = ar_get_head(list_handle)) == NULL)
+ return (0);
+
+ if (invalid_record(list_handle, index))
+ return (0);
+
+ /* Get the pointer to the array control structure. */
+ list_ptr = bl_cs_array[list_handle];
+
+ if (!(list_ptr->contiguous))
+ return (0); /* This isn't an array. */
+
+ data_ptr = bl_cs_array[list_ptr->data_handle];
+
+ /*
+ * Since this looks just like an array. Record the pointer being
+ * deleted for insertion into the avail list at the end and move all
+ * elements below it up one.
+ */
+ deleted_rec = array[index];
+
+ for (i = index; array[i] != NULL; i++)
+ array[i] = array[i+1];
+
+ /*
+ * Now insert the deleted entry into the avails list after the NULL
+ * and adjust the avail_ptr to point to the NULL again.
+ */
+ array[i] = deleted_rec;
+ list_ptr->alloc_segs->avail_ptr -= list_ptr->struct_size;
+
+ /* Adjust other entries in the control structure. */
+ list_ptr->alloc_segs->full = 0;
+ list_ptr->total_elem -= 1;
+
+ /* Clear the deleted data area. */
+ (void) memset(deleted_rec, '\000', data_ptr->struct_size);
+
+ return (1);
+}
+
+/*
+ * Return a new pointer to a structure pointer. Find an available element in
+ * the array and point it at an available element in the data pool
+ * constructed of block lists. Allocate new memory as necessary.
+ */
+char **
+ar_next_avail(int list_handle)
+{
+ struct blk_list_cs *array_ptr;
+ char *data_area, **pointer_area;
+
+ if (invalid_handle(list_handle) ||
+ !(bl_cs_array[list_handle]->contiguous) ||
+ invalid_handle(bl_cs_array[list_handle]->data_handle))
+ return (NULL);
+
+ array_ptr = bl_cs_array[list_handle];
+
+ /*
+ * First see if an avail has already been allocated (it will be right
+ * after the NULL termination of the array if it exists). Return
+ * that, if found.
+ */
+ if ((bl_cs_array[list_handle]->cur_segment != NULL) &&
+ (ARRAY_END(list_handle) + REC_SIZE(list_handle) <
+ EOSEG(list_handle)) &&
+ (*(pointer_area = (char **) GET_AVAIL(list_handle)) != NULL)) {
+ /* We can reclaim a previous deletion. */
+ data_area = *pointer_area;
+
+ *(char **)(ARRAY_END(list_handle)) = data_area; /* reactivate */
+ *pointer_area-- = NULL; /* new end */
+
+ array_ptr->cur_segment->avail_ptr += array_ptr->struct_size;
+ array_ptr->total_elem++;
+ } else {
+ /*
+ * Get the data area first. This is the record we're pointing
+ * to from the array.
+ */
+ data_area = bl_next_avail(array_ptr->data_handle);
+
+ /* Now get the next pointer from the pointer array. */
+ pointer_area = (char **) bl_next_avail(list_handle);
+
+ *pointer_area = data_area;
+
+ /*
+ * The array must be NULL terminated. So, if the block list
+ * structure is full, we have to grow it without resetting
+ * the avail pointer. NOTE: This will only work for a
+ * contiguous list!
+ */
+ if (bl_cs_array[list_handle]->alloc_segs->full) {
+ char **old_list_pointer, **new_list_pointer;
+
+ /*
+ * First grab the old numbers in case realloc() moves
+ * everything.
+ */
+ old_list_pointer = ar_get_head(list_handle);
+
+ /*
+ * Now allocate additional contiguous memory, moving
+ * the original block if necessary.
+ */
+ if (!alloc_next_seg(array_ptr))
+ return (NULL);
+
+ /*
+ * Now determine if everything moved and readjust the
+ * pointer_area if required.
+ */
+ new_list_pointer = ar_get_head(list_handle);
+
+ if (old_list_pointer != new_list_pointer) {
+ pointer_area += (new_list_pointer -
+ old_list_pointer);
+ }
+ }
+ }
+
+ return (pointer_area);
+}
+
+/*
+ * Relinquish the array back to the memory pool. Note that there is no method
+ * provided to free *all* arrays.
+ */
+void
+ar_free(int list_handle)
+{
+ if (invalid_handle(list_handle))
+ return;
+
+ bl_free(bl_cs_array[list_handle]->data_handle);
+ bl_free(list_handle);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/lockinst.c b/usr/src/cmd/svr4pkg/libinst/lockinst.c
new file mode 100644
index 0000000000..00c7ab42f2
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/lockinst.c
@@ -0,0 +1,279 @@
+/*
+ * 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 2005 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/fault.h>
+#include <sys/syscall.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+extern int errno;
+
+#define ST_QUIT 1
+#define ST_OK 0
+
+#define LOCKFILE ".lockfile"
+#define LCKBUFSIZ 128
+#define LOCKWAIT 20 /* seconds between retries */
+#define LOCKRETRY 10 /* number of retries for a DB lock */
+#define LF_SIZE 128 /* size of governing lock file */
+
+#define MSG_WTING "NOTE: Waiting for access to the package database."
+#define MSG_XWTING "NOTE: Waiting for exclusive access to the package " \
+ "database."
+#define MSG_WTFOR "NOTE: Waiting for %s of %s to complete."
+#define WRN_CLRLOCK "WARNING: Stale lock installed for %s, pkg %s quit " \
+ "in %s state."
+#define WRN_CLRLOCK1 "Removing lock."
+#define ERR_MKLOCK "unable to create governing lock file <%s>."
+#define ERR_NOLOCK "unable to install governing lock on <%s>."
+#define ERR_NOOPEN "unable to open <%s>."
+#define ERR_LCKTBL "unable to lock <%s> - lock table full."
+#define ERR_LCKREM "unable to lock <%s> - remote host unavailable."
+#define ERR_BADLCK "unable to lock <%s> - unknown error."
+#define ERR_DEADLCK "unable to lock <%s> - deadlock condition."
+
+static pid_t lock_pid;
+static int lock_fd, lock_is_applied;
+static char lock_name[PKGSIZ];
+static char lock_pkg[PKGSIZ];
+static char lock_place[PKGSIZ];
+static unsigned int lock_state;
+static char lockbuf[LCKBUFSIZ];
+static char lockpath[PATH_MAX];
+
+#define LOCK_NAME_OLD_PKG "old version pkg command"
+#define LOCK_PKG_UNKNOWN "unknown package"
+#define LOCK_PLACE_UNKNOWN "unknown"
+
+/*
+ * This function writes the PID, effective utility name, package name,
+ * current progress of the utility and the exit state to the lockfile in
+ * support of post mortem operations.
+ */
+static int
+wrlockdata(int fd, int this_pid, char *this_name,
+ char *this_pkg, char *this_place, unsigned int this_state)
+{
+ if (this_pid < 0 || *this_name == '\000')
+ return (0);
+
+ (void) memset(lockbuf, 0, LCKBUFSIZ);
+
+ (void) snprintf(lockbuf, sizeof (lockbuf),
+ "%d %s %s %s %d\n", this_pid, this_name, this_pkg,
+ this_place, this_state);
+
+ (void) lseek(fd, 0, SEEK_SET);
+ if (write(fd, lockbuf, LF_SIZE) == LF_SIZE)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * This function reads the lockfile to obtain the PID and name of the lock
+ * holder. Upon those rare circumstances that an older version of pkgadd
+ * created the lock, this detects that zero-length file and provides the
+ * appropriate data. Since this data is only used if an actual lock (from
+ * lockf()) is detected, a manually created .lockfile will not result in a
+ * message.
+ */
+static void
+rdlockdata(int fd)
+{
+ (void) lseek(fd, 0, SEEK_SET);
+ if (read(fd, lockbuf, LF_SIZE) != LF_SIZE) {
+ lock_pid = 0;
+ (void) strlcpy(lock_name, LOCK_NAME_OLD_PKG,
+ sizeof (lock_name));
+
+ (void) strlcpy(lock_pkg, LOCK_PKG_UNKNOWN,
+ sizeof (lock_pkg));
+
+ (void) strlcpy(lock_place, LOCK_PLACE_UNKNOWN,
+ sizeof (lock_place));
+
+ lock_state = ST_OK;
+ } else {
+ /* LINTED format argument contains unbounded string specifier */
+ (void) sscanf(lockbuf, "%ld %s %s %s %u", &lock_pid,
+ lock_name, lock_pkg, lock_place, &lock_state);
+ }
+}
+
+static void
+do_alarm(int n)
+{
+#ifdef lint
+ int i = n;
+ n = i;
+#endif /* lint */
+ (void) signal(SIGALRM, do_alarm);
+ (void) alarm(LOCKWAIT);
+}
+
+/*
+ * This establishes a locked status file for a pkgadd, pkgrm or swmtool - any
+ * of the complex package processes. Since numerous packages currently use
+ * installf and removef in preinstall scripts, we can't enforce a contents
+ * file write lock throughout the install process. In 2.7 we will enforce the
+ * write lock and allow this lock to serve as a simple information carrier
+ * which can be used by installf and removef too.
+ * Arguments:
+ * util_name - the name of the utility that is claiming the lock
+ * pkg_name - the package that is being locked (or "all package")
+ * place - a string of ascii characters that defines the initial "place" where
+ * the current operation is - this is updated by lockupd() and is a string
+ * is used fr post mortem operations if the utility should quit improperly.
+ * Returns (int):
+ * == 0 - failure
+ * != 0 - success
+ */
+
+int
+lockinst(char *util_name, char *pkg_name, char *place)
+{
+ int fd, retry_cnt;
+
+ /* assume "initial" if no "place" during processing specified */
+
+ if ((place == (char *)NULL) || (*place == '\0')) {
+ place = "initial";
+ }
+
+ (void) snprintf(lockpath, sizeof (lockpath),
+ "%s/%s", get_PKGADM(), LOCKFILE);
+
+ /* If the exit file is not present, create it. */
+ /* LINTED O_CREAT without O_EXCL specified in call to open() */
+ if ((fd = open(lockpath, O_RDWR | O_CREAT, 0600)) == -1) {
+ progerr(gettext(ERR_MKLOCK), lockpath);
+ return (0);
+ }
+
+ lock_fd = fd;
+
+ retry_cnt = LOCKRETRY;
+ lock_is_applied = 0;
+
+ (void) signal(SIGALRM, do_alarm);
+ (void) alarm(LOCKWAIT);
+
+ /*
+ * This tries to create the lock LOCKRETRY times waiting LOCKWAIT
+ * seconds between retries.
+ */
+ do {
+
+ if (lockf(fd, F_LOCK, 0)) {
+ /*
+ * Try to read the status of the last (or current)
+ * utility.
+ */
+ rdlockdata(fd);
+
+ logerr(gettext(MSG_WTFOR), lock_name, lock_pkg);
+ } else { /* This process has the lock. */
+ rdlockdata(fd);
+
+ if (lock_state != 0) {
+ logerr(gettext(WRN_CLRLOCK), lock_name,
+ lock_pkg, lock_place);
+ logerr(gettext(WRN_CLRLOCK1));
+ }
+
+ lock_pid = getpid();
+ (void) strlcpy(lock_name, (util_name) ?
+ util_name : gettext("unknown"), sizeof (lock_name));
+
+ (void) strlcpy(lock_pkg, (pkg_name) ?
+ pkg_name : gettext("unknown"), sizeof (lock_pkg));
+
+ (void) wrlockdata(fd, lock_pid, lock_name,
+ lock_pkg, place, ST_QUIT);
+ lock_is_applied = 1;
+ break;
+ }
+ } while (retry_cnt--);
+
+ (void) signal(SIGALRM, SIG_IGN);
+
+ if (!lock_is_applied) {
+ progerr(gettext(ERR_NOLOCK), lockpath);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * This function updates the utility progress data in the lock file. It is
+ * used for post mortem operations if the utility should quit improperly.
+ */
+void
+lockupd(char *place)
+{
+ (void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg, place,
+ ST_QUIT);
+}
+
+/*
+ * This clears the governing lock and closes the lock file. If this was
+ * called already, it just returns.
+ */
+void
+unlockinst(void)
+{
+ if (lock_is_applied) {
+ (void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg,
+ "finished", ST_OK);
+
+ /*
+ * If close() fails, we can't be sure the lock has been
+ * removed, so we assume the worst in case this function is
+ * called again.
+ */
+ if (close(lock_fd) != -1)
+ lock_is_applied = 0;
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/log.c b/usr/src/cmd/svr4pkg/libinst/log.c
new file mode 100644
index 0000000000..65c46b6c34
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/log.c
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/* Should be defined by cc -D */
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* local static data */
+
+static boolean_t verbose = B_FALSE;
+
+/*
+ * Name: log_msg
+ * Description: Outputs messages to logging facility.
+ * Scope: public
+ * Arguments: a_type - the severity of the message
+ * a_format - the printf format, plus its arguments
+ * Returns: none
+ */
+
+/*PRINTFLIKE2*/
+void
+log_msg(LogMsgType a_type, const char *a_format, ...)
+{
+ FILE *out;
+ char *rstr = (char *)NULL;
+ char bfr[1];
+ char *prefix;
+ size_t vres = 0;
+ va_list ap;
+ char *p = get_prog_name();
+
+ /* process message based on type */
+
+ switch (a_type) {
+ case LOG_MSG_ERR:
+ default: /* treat unknown type as LOG_MSG_ERR */
+ out = stderr;
+ prefix = MSG_LOG_ERROR;
+ break;
+ case LOG_MSG_WRN: /* warning message */
+ out = stderr;
+ prefix = MSG_LOG_WARNING;
+ break;
+ case LOG_MSG_INFO: /* information message */
+ out = stdout;
+ prefix = NULL;
+ break;
+ case LOG_MSG_DEBUG: /* debugging message */
+ if (!log_get_verbose()) {
+ /* no debug messages if not verbose mode */
+ return;
+ }
+
+ out = stderr;
+ prefix = NULL;
+
+ /* output debug prefix to match echoDebug() format */
+
+ (void) fprintf(stderr, "# [%6d %3d", getpid(), getzoneid());
+
+ if ((p != (char *)NULL) && (*p != '\0')) {
+ fprintf(stderr, " %-11s", p);
+ }
+
+ (void) fprintf(stderr, "] ");
+ break;
+ }
+
+ /* output prefix if specified */
+
+ if (prefix != NULL) {
+ (void) fprintf(out, "%s: ", prefix);
+ }
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ /* allocate storage to hold the message */
+
+ rstr = (char *)malloc(vres+2);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+
+ /* output formatted message to appropriate destination */
+
+ if (fprintf(out, "%s\n", rstr) < 0) {
+ if (out != stderr) {
+ /*
+ * nothing output, try stderr as a
+ * last resort
+ */
+ (void) fprintf(stderr, ERR_LOG_FAIL, a_format);
+ }
+ }
+
+ /* free temporary message storage */
+
+ free(rstr);
+}
+
+/*
+ * Name: set_verbose
+ * Description: Turns on verbose output
+ * Scope: public
+ * Arguments: verbose = B_TRUE indicates verbose mode
+ * Returns: none
+ */
+
+void
+log_set_verbose(boolean_t setting)
+{
+ verbose = setting;
+}
+
+/*
+ * Name: get_verbose
+ * Description: Returns whether or not to output verbose messages
+ * Scope: public
+ * Arguments: none
+ * Returns: B_TRUE - verbose messages should be output
+ */
+
+boolean_t
+log_get_verbose()
+{
+ return (verbose);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/mntinfo.c b/usr/src/cmd/svr4pkg/libinst/mntinfo.c
new file mode 100644
index 0000000000..a06c1328d9
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/mntinfo.c
@@ -0,0 +1,1417 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <wait.h>
+#include <signal.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/systeminfo.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <sys/vfstab.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+extern char **environ;
+
+static int match_mount; /* This holds the mount of interest. */
+
+int fs_tab_used = 0;
+int fs_tab_alloc = 0;
+static int fs_list = -1;
+
+struct fstable **fs_tab = NULL;
+
+#define PKGDBROOT "/var/sadm"
+#define MOUNT "/sbin/mount"
+#define UMOUNT "/sbin/umount"
+
+#define setmntent fopen
+#define endmntent fclose
+#define MOUNT_TABLE MNTTAB
+
+/* returned by already_mounted() */
+#define MNT_NOT 0
+#define MNT_EXACT 1
+#define MNT_AVAIL 2
+
+/* used with is_remote_src() */
+#define NOT_REMOTE 0
+#define REAL_REMOTE 1
+#define SELF_SERVE 2
+
+/*
+ * Due to /etc/mnttab files containing entries for multiple nfs hosts
+ * HOST_NM_LN needs to be accommodating. The recommended value in the sysinfo
+ * man page of 257 needs to be expanded. See bugid 4076513.
+ * 1024 chars is defined in the mnttab.h header as the max size of an entry.
+ */
+
+#define HOST_NM_LN MNT_LINE_MAX
+
+/* These cachefs definitions should be in mntent.h. Maybe some day. */
+#define MNTTYPE_CFS "cachefs"
+#define MNTOPT_BACKFSTYPE "backfstype"
+#define MNTTYPE_AUTO "autofs"
+
+/*
+ * Utilities for getting filesystem information from the mount table.
+ *
+ * Note: vanilla SVr4 code (pkginstall/dockspace.c) used the output from
+ * popen() on the "/etc/mount" command. However, we need to get more
+ * information about mounted filesystems, so we use the C interfaces to
+ * the mount table, which also happens to be much faster than running
+ * another process. Since several of the pkg commands need access to the
+ * the code has been placed here, to be included in the libinst library.
+ */
+
+#define ALLOC_CHUNK 30
+
+/*
+ * fs_tab_ent_comp - compare fstable entries first by length in reverse
+ * order, then alphabetically.
+ */
+static int
+fs_tab_ent_comp(const void *e1, const void *e2)
+{
+ struct fstable *fs1 = *((struct fstable **)e1);
+ struct fstable *fs2 = *((struct fstable **)e2);
+
+ if (fs1->namlen == fs2->namlen)
+ return (strcmp(fs1->name, fs2->name));
+ else
+ return (fs2->namlen - fs1->namlen);
+}
+
+/*
+ * This determines if the source of the mount is from another host. If it's
+ * from this host, then it might be writable. This returns NOT_REMOTE if it's
+ * pure local, REAL_REMOTE if it's being served from another host and
+ * SELF_SERVE if it's being served by the current host.
+ */
+static int
+is_remote_src(char *source)
+{
+ static char host_name[HOST_NM_LN];
+ char source_host[HOST_NM_LN], *src_ptr, *src_host_ptr;
+ static int hn_len;
+
+ if (hn_len == 0) {
+ /* Find out what host this is. */
+ (void) sysinfo(SI_HOSTNAME, host_name, HOST_NM_LN);
+ hn_len = strlen(host_name);
+ }
+
+ if (source[0] == '/')
+ return (NOT_REMOTE); /* No server name, so it's local. */
+
+ if (strchr(source, ':') == NULL)
+ return (NOT_REMOTE); /* it's a floppy disk or something */
+
+ src_ptr = source;
+ src_host_ptr = source_host;
+
+ /* Scan to the end of the hostname (find the ":"). */
+ while (*src_ptr != ':')
+ *src_host_ptr++ = *src_ptr++;
+ *src_host_ptr = '\0';
+
+ if (strncmp(source, host_name, hn_len) == 0 &&
+ *(source+hn_len) == ':' || is_local_host(source_host))
+ return (SELF_SERVE); /* Exporting from itself, it's local. */
+
+ return (REAL_REMOTE);
+}
+
+/*
+ * This determines if an apparently writeable filesystem is really writeable
+ * or if it's been shared over the network with root-restrictive options.
+ */
+static int
+really_write(char *mountpt)
+{
+ char testfile[PATH_MAX];
+ int fd, retval = 0;
+ struct stat status;
+
+ (void) snprintf(testfile, sizeof (testfile), "%s/testXXXXXX", mountpt);
+
+ if (mktemp(testfile) == NULL)
+ return (0); /* may as well be read-only */
+ /* LINTED do not use creat(); use open(path,... */
+ else if ((fd = creat(testfile, 0777)) == -1)
+ return (0); /* can't write */
+ else if (fstat(fd, &status) == -1)
+ retval = 0; /* may as well be read-only */
+ else if (status.st_uid != 0)
+ retval = 0; /* too many restrictions */
+ else
+ retval = 1;
+
+ (void) close(fd);
+ (void) unlink(testfile);
+
+ return (retval);
+}
+
+/* This returns the hostname portion of a remote path. */
+char *
+get_server_host(short n)
+{
+ static char hostname[HOST_NM_LN], *host_end;
+
+ if (fs_tab_used == 0) {
+ return ("unknown source");
+ }
+
+ if (n >= 0 && n < fs_tab_used) {
+ (void) strcpy(hostname, fs_tab[n]->remote_name);
+ if ((host_end = strchr(hostname, ':')) == NULL) {
+ if ((strcmp(fs_tab[n]->fstype, MNTTYPE_AUTO)) == NULL)
+ return ("automounter");
+ else
+ return (fs_tab[n]->fstype);
+ } else {
+ *host_end = '\0';
+ return (hostname);
+ }
+ }
+
+ return ("unknown source");
+}
+
+/*
+ * This pulls the path out of a hostpath which may be of the form host:path
+ * where path is an absolute path. NOTE: If path turns out to be relative,
+ * this returns NULL.
+ */
+static char *
+path_part(char *hostpath)
+{
+ char *host_end;
+
+ if ((host_end = strchr(hostpath, ':')) == NULL && hostpath[0] == '/')
+ return (hostpath); /* It's already legit. */
+
+ if (*(host_end+1) == '/')
+ return (host_end+1); /* Here's the path part. */
+
+ return (NULL);
+}
+
+/*
+ * This scans the filesystems already mounted to see if this remote mount is
+ * already in place on the server. This scans the fs_tab for a remote_name
+ * exactly matching the client's. It stores the current entry number
+ * corresponding to this mount in the static match_mount.
+ *
+ * Returns:
+ * MNT_NOT Couldn't find it.
+ * MNT_EXACT This has actually been manually mounted for us
+ * MNT_AVAIL This is mounted for the server, but needs to be
+ * loopback mounted from the client's perspective.
+ */
+static int
+already_mounted(struct vfstab *vfs, int is_local_host, char *client_path,
+ char *host_path)
+{
+ int i;
+
+ match_mount = -1;
+
+ if (fs_tab_used == 0) {
+ return (MNT_NOT);
+ }
+
+ for (i = 0; i < fs_tab_used; i++) {
+ /*
+ * Determine if this has been manually mounted exactly as we
+ * require. Begin by finding a mount on our current
+ * mountpoint.
+ */
+ if (strcmp(fs_tab[i]->name, client_path) == 0) {
+ /*
+ * Now see if it is really the same mount. This isn't
+ * smart enough to find mounts on top of mounts, but
+ * assuming there is no conspiracy to fool this
+ * function, it will be good enough.
+ */
+ if (is_local_host &&
+ strcmp(fs_tab[i]->remote_name, host_path) == 0) {
+ match_mount = i;
+ return (MNT_EXACT);
+ }
+ }
+
+ /* Determine if this mount is available to the server. */
+ if (strcmp(fs_tab[i]->remote_name, vfs->vfs_special) == 0) {
+ match_mount = i;
+ return (MNT_AVAIL);
+ }
+ }
+ return (MNT_NOT);
+}
+
+/*
+ * This function unmounts all of the loopback mounts created for the client.
+ * If no client stuff is mounted, this is completely benign, it finds that
+ * nothing is mounted up and returns. It returns "1" for unmounted everything
+ * OK and "0" for failure.
+ */
+int
+unmount_client()
+{
+ int errcode;
+ int exit_no;
+ int n;
+ int retcode = 1;
+ int status;
+ pid_t pid;
+ pid_t pid_return;
+
+ if (fs_tab_used == 0) {
+ return (1);
+ }
+
+ for (n = 0; n < fs_tab_used-1; n++) {
+ /* If the filesystem is mounted and this utility did it ... */
+ if (fs_tab[n]->cl_mounted && fs_tab[n]->srvr_map) {
+ char *arg[3];
+
+ /* create arglist for umount command */
+
+ arg[0] = UMOUNT;
+ arg[1] = fs_tab[n]->name;
+ arg[2] = (char *)NULL;
+
+ /* flush standard i/o before creating new process */
+
+ (void) fflush(stderr);
+ (void) fflush(stdout);
+
+ /*
+ * create new process to execute command in;
+ * vfork is being used to avoid duplicating the parents
+ * memory space - this means that the child process may
+ * not modify any of the parents memory including the
+ * standard i/o descriptors - all the child can do is
+ * adjust interrupts and open files as a prelude to a
+ * call to exec().
+ */
+
+ pid = vfork();
+ if (pid < 0) {
+ /* fork failed! */
+
+ logerr(WRN_BAD_FORK, errno, strerror(errno));
+ retcode = 0;
+ } else if (pid > 0) {
+ /*
+ * this is the parent process
+ */
+
+ status = 0;
+ pid_return = waitpid(pid, &status, 0);
+
+ if (pid_return != pid) {
+ logerr(WRN_BAD_WAIT, pid, pid_return,
+ (unsigned long)status, errno,
+ strerror(errno));
+ retcode = 0;
+ }
+
+ /*
+ * If the child was stopped or killed by a
+ * signal or exied with any code but 0, we
+ * assume the mount has failed.
+ */
+
+ if (!WIFEXITED(status) ||
+ (errcode = WEXITSTATUS(status))) {
+ retcode = 0;
+ logerr(WRN_FSTAB_UMOUNT,
+ fs_tab[n]->name, errcode);
+ } else {
+ fs_tab[n]->cl_mounted = 0;
+ }
+ } else {
+ /*
+ * this is the child process
+ */
+
+ int i;
+
+ /* reset any signals to default */
+
+ for (i = 0; i < NSIG; i++) {
+ (void) sigset(i, SIG_DFL);
+ }
+
+ /*
+ * Redirect output to /dev/null because the
+ * umount error message may be confusing to
+ * the user.
+ */
+
+ i = open("/dev/null", O_WRONLY);
+ if (i >= 0) {
+ dup2(2, STDERR_FILENO);
+ }
+
+ /* close all file descriptors except stdio */
+
+ closefrom(3);
+
+ exit_no = execve(arg[0], arg, environ);
+ _exit(exit_no);
+ }
+ }
+ }
+
+ return (retcode);
+}
+
+/*
+ * This function creates the necessary loopback mounts to emulate the client
+ * configuration with respect to the server. If this is being run on a
+ * standalone or the installation is actually to the local system, this call
+ * is benign since srvr_map won't be set anywhere. It returns "1" for mounted
+ * everything OK and "0" for failure.
+ */
+int
+mount_client()
+{
+ int errcode;
+ int exit_no;
+ int n;
+ int retcode = 1;
+ int status;
+ pid_t pid;
+ pid_t pid_return;
+
+ if (fs_tab_used == 0) {
+ return (1);
+ }
+
+ for (n = fs_tab_used-1; n >= 0; n--) {
+ /*
+ * If the filesystem is mounted (meaning available) and the
+ * apparent filesystem can be mapped to a local filesystem
+ * AND the local filesystem is not the same as the target
+ * filesystem, mount it.
+ */
+ if (fs_tab[n]->mounted && fs_tab[n]->srvr_map) {
+ char *arg[6];
+
+ /* create arglist for mount command */
+
+ arg[0] = MOUNT;
+ arg[1] = "-F";
+ arg[2] = "lofs";
+ arg[3] = fs_tab[n]->remote_name;
+ arg[4] = fs_tab[n]->name;
+ arg[5] = (char *)NULL;
+
+ /* flush standard i/o before creating new process */
+
+ (void) fflush(stderr);
+ (void) fflush(stdout);
+
+ /*
+ * create new process to execute command in;
+ * vfork is being used to avoid duplicating the parents
+ * memory space - this means that the child process may
+ * not modify any of the parents memory including the
+ * standard i/o descriptors - all the child can do is
+ * adjust interrupts and open files as a prelude to a
+ * call to exec().
+ */
+
+ pid = vfork();
+ if (pid < 0) {
+ /* fork failed! */
+
+ logerr(WRN_BAD_FORK, errno, strerror(errno));
+ retcode = 0;
+ } else if (pid > 0) {
+ /*
+ * this is the parent process
+ */
+
+ pid_return = waitpid(pid, &status, 0);
+
+ if (pid_return != pid) {
+ logerr(WRN_BAD_WAIT, pid, pid_return,
+ (unsigned long)status, errno,
+ strerror(errno));
+ retcode = 0;
+ }
+
+ /*
+ * If the child was stopped or killed by a
+ * signal or exied with any code but 0, we
+ * assume the mount has failed.
+ */
+
+ if (!WIFEXITED(status) ||
+ (errcode = WEXITSTATUS(status))) {
+ retcode = 0;
+ fs_tab[n]->mnt_failed = 1;
+ logerr(WRN_FSTAB_MOUNT,
+ fs_tab[n]->name, errcode);
+ } else {
+ fs_tab[n]->cl_mounted = 1;
+ }
+ } else {
+ /*
+ * this is the child process
+ */
+
+ int i;
+
+ /* reset all signals to default */
+
+ for (i = 0; i < NSIG; i++) {
+ (void) sigset(i, SIG_DFL);
+ }
+
+ /*
+ * Redirect output to /dev/null because the
+ * mount error message may be confusing to
+ * the user.
+ */
+
+ i = open("/dev/null", O_WRONLY);
+ if (i >= 0) {
+ dup2(i, STDERR_FILENO);
+ }
+
+ /* close all file descriptors except stdio */
+
+ closefrom(3);
+
+ exit_no = execve(arg[0], arg, environ);
+ _exit(exit_no);
+ /*NOTREACHED*/
+ }
+ }
+ }
+ return (retcode);
+}
+
+/*
+ * This function maps path, on a loopback filesystem, back to the real server
+ * filesystem. fsys_value is the fs_tab[] entry to which the loopback'd path is
+ * mapped. This returns a pointer to a static area. If the result is needed
+ * for further processing, it should be strdup()'d or something.
+ */
+char *
+server_map(char *path, short fsys_value)
+{
+ static char server_construction[PATH_MAX];
+
+ if (fs_tab_used == 0) {
+ (void) strcpy(server_construction, path);
+ } else if (fsys_value >= 0 && fsys_value < fs_tab_used) {
+ (void) snprintf(server_construction,
+ sizeof (server_construction),
+ "%s%s", fs_tab[fsys_value]->remote_name,
+ path+strlen(fs_tab[fsys_value]->name));
+ } else {
+ (void) strcpy(server_construction, path);
+ }
+
+ return (server_construction);
+}
+
+/* This function sets up the standard parts of the fs_tab. */
+static struct fstable *
+fs_tab_init(char *mountp, char *fstype)
+{
+ struct fstable *nfte;
+
+ /* Create the array if necessary. */
+ if (fs_list == -1) {
+ fs_list = ar_create(ALLOC_CHUNK,
+ (unsigned)sizeof (struct fstable),
+ "filesystem mount data");
+ if (fs_list == -1) {
+ progerr(ERR_MALLOC, "fs_list", errno, strerror(errno));
+ return (NULL);
+ }
+ }
+
+ /*
+ * Allocate an fstable entry for this mnttab entry.
+ */
+ if ((nfte = *(struct fstable **)ar_next_avail(fs_list))
+ == NULL) {
+ progerr(ERR_MALLOC, "nfte", errno, strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * Point fs_tab at the head of the array again, since it may have
+ * moved due to realloc in ar_next_avail(). If ar_next_avail() realizes
+ * that there is no more room to grow the array, it reallocates the
+ * array. Because we stored pointer to that array in fs_tab, we need
+ * to make sure that it is updated as well.
+ */
+ if ((fs_tab = (struct fstable **)ar_get_head(fs_list)) == NULL) {
+ progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * Get the length of the 'mount point' name.
+ */
+ nfte->namlen = strlen(mountp);
+ /*
+ * Allocate space for the 'mount point' name.
+ */
+ if ((nfte->name = malloc(nfte->namlen+1)) == NULL) {
+ progerr(ERR_MALLOC, "name", errno, strerror(errno));
+ return (NULL);
+ }
+ (void) strcpy(nfte->name, mountp);
+
+ if ((nfte->fstype = malloc(strlen(fstype)+1)) == NULL) {
+ progerr(ERR_MALLOC, "fstype", errno, strerror(errno));
+ return (NULL);
+ }
+ (void) strcpy(nfte->fstype, fstype);
+
+ fs_tab_used++;
+
+ return (nfte);
+}
+
+/* This function frees all memory associated with the filesystem table. */
+void
+fs_tab_free(void)
+{
+ int n;
+
+ if (fs_tab_used == 0) {
+ return;
+ }
+
+ for (n = 0; n < fs_tab_used; n++) {
+ free(fs_tab[n]->fstype);
+ free(fs_tab[n]->name);
+ free(fs_tab[n]->remote_name);
+ }
+
+ ar_free(fs_list);
+}
+
+/* This function scans a string of mount options for a specific keyword. */
+static int
+hasopt(char *options, char *keyword)
+{
+ char vfs_options[VFS_LINE_MAX], *optptr;
+
+ if (!options) {
+ (void) strcpy(vfs_options, "ro");
+ } else {
+ (void) strcpy(vfs_options, options);
+ }
+
+ while (optptr = strrchr(vfs_options, ',')) {
+ *optptr++ = '\0';
+
+ if (strcmp(optptr, keyword) == 0)
+ return (1);
+ }
+
+ /* Now deal with the remainder. */
+ if (strcmp(vfs_options, keyword) == 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * This function constructs a new filesystem table (fs_tab[]) entry based on
+ * an /etc/mnttab entry. When it returns, the new entry has been inserted
+ * into fs_tab[].
+ */
+static int
+construct_mt(struct mnttab *mt)
+{
+ struct fstable *nfte;
+
+ /*
+ * Initialize fstable structure and make the standard entries.
+ */
+ if ((nfte = fs_tab_init(mt->mnt_mountp, mt->mnt_fstype)) == NULL)
+ return (1);
+
+ /* See if this is served from another host. */
+ if (is_remote_src(mt->mnt_special) == REAL_REMOTE ||
+ strcmp(mt->mnt_fstype, MNTTYPE_AUTO) == 0)
+ nfte->remote = 1;
+ else
+ nfte->remote = 0;
+
+ /* It's mounted now (by definition), so we don't have to remap it. */
+ nfte->srvr_map = 0;
+ nfte->mounted = 1;
+
+ nfte->remote_name = strdup(mt->mnt_special);
+
+ /*
+ * This checks the mount commands which establish the most
+ * basic level of access. Later further tests may be
+ * necessary to fully qualify this. We set this bit
+ * preliminarily because we have access to the mount data
+ * now.
+ */
+ nfte->writeable = 0; /* Assume read-only. */
+ if (hasmntopt(mt, MNTOPT_RO) == NULL) {
+ nfte->writeable = 1;
+ if (!(nfte->remote))
+ /*
+ * There's no network involved, so this
+ * assessment is confirmed.
+ */
+ nfte->write_tested = 1;
+ } else
+ /* read-only is read-only */
+ nfte->write_tested = 1;
+
+ /* Is this coming to us from a server? */
+ if (nfte->remote && !(nfte->writeable))
+ nfte->served = 1;
+
+ return (0);
+}
+
+/*
+ * This function modifies an existing fs_tab[] entry. It was found mounted up
+ * exactly the way we would have mounted it in mount_client() only at the
+ * time we didn't know it was for the client. Now we do, so we're setting the
+ * various permissions to conform to the client view.
+ */
+static void
+mod_existing(struct vfstab *vfsent, int fstab_entry, int is_remote)
+{
+ /*
+ * Establish whether the client will see this as served.
+ */
+ if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO))
+ fs_tab[fstab_entry]->served = 1;
+
+ fs_tab[fstab_entry]->cl_mounted = 1;
+}
+
+/*
+ * This function constructs a new fs_tab[] entry based on
+ * an /etc/vfstab entry. When it returns, the new entry has been inserted
+ * into fstab[].
+ */
+static int
+construct_vfs(struct vfstab *vfsent, char *client_path, char *link_name,
+ int is_remote, int mnt_stat)
+{
+ int use_link;
+ struct fstable *nfte;
+
+ if ((nfte = fs_tab_init(client_path, vfsent->vfs_fstype)) == NULL)
+ return (1);
+
+ nfte->remote = (is_remote == REAL_REMOTE);
+
+ /*
+ * The file system mounted on the client may or may not be writeable.
+ * So we hand it over to fsys() to evaluate. This will have the same
+ * read/write attributes as the corresponding mounted filesystem.
+ */
+ use_link = 0;
+ if (nfte->remote) {
+ /*
+ * Deal here with mount points actually on a system remote
+ * from the server.
+ */
+ if (mnt_stat == MNT_NOT) {
+ /*
+ * This filesystem isn't in the current mount table
+ * meaning it isn't mounted, the current host can't
+ * write to it and there's no point to mapping it for
+ * the server.
+ */
+ link_name = NULL;
+ nfte->mounted = 0;
+ nfte->srvr_map = 0;
+ nfte->writeable = 0;
+ } else { /* It's MNT_AVAIL. */
+ /*
+ * This filesystem is associated with a current
+ * mountpoint. Since it's mounted, it needs to be
+ * remapped and it is writable if the real mounted
+ * filesystem is writeable.
+ */
+ use_link = 1;
+ link_name = strdup(fs_tab[match_mount]->name);
+ nfte->mounted = 1;
+ nfte->srvr_map = 1;
+ nfte->writeable = fs_tab[match_mount]->writeable;
+ nfte->write_tested = fs_tab[match_mount]->write_tested;
+ }
+ } else { /* local filesystem */
+ use_link = 1;
+ nfte->mounted = 1;
+ nfte->srvr_map = 1;
+ nfte->writeable = fs_tab[fsys(link_name)]->writeable;
+ nfte->write_tested = 1;
+ }
+
+ /*
+ * Now we establish whether the client will see this as served.
+ */
+ if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO))
+ nfte->served = 1;
+
+ if (use_link) {
+ nfte->remote_name = link_name;
+ } else {
+ nfte->remote_name = strdup(vfsent->vfs_special);
+ }
+
+ return (0);
+}
+
+/*
+ * get_mntinfo - get the mount table, now dynamically allocated. Returns 0 if
+ * no problem and 1 if there's a fatal error.
+ */
+int
+get_mntinfo(int map_client, char *vfstab_file)
+{
+ static char *rn = "/";
+ FILE *pp;
+ struct mnttab mtbuf;
+ struct mnttab *mt = &mtbuf;
+ char *install_root;
+ int is_remote;
+
+ /*
+ * Open the mount table for the current host and establish a global
+ * table that holds data about current mount status.
+ */
+ if ((pp = setmntent(MOUNT_TABLE, "r")) == NULL) {
+ progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * First, review the mounted filesystems on the managing host. This
+ * may also be the target host but we haven't decided that for sure
+ * yet.
+ */
+ while (!getmntent(pp, mt))
+ if (construct_mt(mt))
+ return (1);
+
+ (void) endmntent(pp);
+
+ /*
+ * Now, we see if this installation is to a client. If it is, we scan
+ * the client's vfstab to determine what filesystems are
+ * inappropriate to write to. This simply adds the vfstab entries
+ * representing what will be remote file systems for the client.
+ * Everything that isn't remote to the client is already accounted
+ * for in the fs_tab[] so far. If the remote filesystem is really on
+ * this server, we will write through to the server from this client.
+ */
+ install_root = get_inst_root();
+ if (install_root && strcmp(install_root, "/") != 0 && map_client) {
+ /* OK, this is a legitimate remote client. */
+ struct vfstab vfsbuf;
+ struct vfstab *vfs = &vfsbuf;
+ char VFS_TABLE[PATH_MAX];
+
+ /*
+ * Since we use the fsys() function later, and it depends on
+ * an ordered list, we have to sort the list here.
+ */
+ qsort(fs_tab, fs_tab_used,
+ sizeof (struct fstable *), fs_tab_ent_comp);
+
+ /*
+ * Here's where the vfstab for the target is. If we can get
+ * to it, we'll scan it for what the client will see as
+ * remote filesystems, otherwise, we'll just skip this.
+ */
+ if (vfstab_file) {
+ (void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s",
+ vfstab_file);
+ } else {
+ (void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s%s",
+ install_root, VFSTAB);
+ }
+
+ if (access(VFS_TABLE, R_OK) == 0) {
+ char *link_name;
+
+ /*
+ * Open the vfs table for the target host.
+ */
+ if ((pp = setmntent(VFS_TABLE, "r")) == NULL) {
+ progerr(ERR_NOTABLE, "vfs", VFS_TABLE,
+ strerror(errno));
+ return (1);
+ }
+
+ /* Do this for each entry in the vfstab. */
+ while (!getvfsent(pp, vfs)) {
+ char client_mountp[PATH_MAX];
+ int mnt_stat;
+
+ /*
+ * We put it into the fs table if it's
+ * remote mounted (even from this server) or
+ * loopback mounted from the client's point
+ * of view.
+ */
+ if (!(is_remote =
+ is_remote_src(vfs->vfs_special)) &&
+ strcmp(vfs->vfs_fstype, MNTTYPE_LOFS) !=
+ 0)
+ continue; /* not interesting */
+
+ /*
+ * Construct client_mountp by prepending the
+ * install_root to the 'mount point' name.
+ */
+ if (strcmp(vfs->vfs_mountp, "/") == 0) {
+ (void) strcpy(client_mountp,
+ install_root);
+ } else {
+ (void) snprintf(client_mountp,
+ sizeof (client_mountp), "%s%s",
+ install_root, vfs->vfs_mountp);
+ }
+
+ /*
+ * We also skip the entry if the vfs_special
+ * path and the client_path are the same.
+ * There's no need to mount it, it's just a
+ * cachefs optimization that mounts a
+ * directory over itself from this server.
+ */
+ if ((is_remote == SELF_SERVE) &&
+ strcmp(path_part(vfs->vfs_special),
+ client_mountp) == 0)
+ continue;
+
+ /* Determine if this is already mounted. */
+ link_name = strdup(path_part(vfs->vfs_special));
+ mnt_stat = already_mounted(vfs,
+ (is_remote != REAL_REMOTE), client_mountp,
+ link_name);
+
+ if (mnt_stat == MNT_EXACT) {
+ mod_existing(vfs, match_mount,
+ is_remote);
+ } else { /* MNT_NOT */
+ if (construct_vfs(vfs, client_mountp,
+ link_name, is_remote, mnt_stat))
+ return (1);
+ }
+ }
+ (void) endmntent(pp);
+ } /* end of if(access()) */
+ } /* end of if(install_root) */
+
+ /* This next one may look stupid, but it can really happen. */
+ if (fs_tab_used <= 0) {
+ progerr(ERR_MNT_NOMOUNTS);
+ return (1);
+ }
+
+ /*
+ * Now that we have the complete list of mounted (or virtually
+ * mounted) filesystems, we sort the mountpoints in reverse order
+ * based on the length of the 'mount point' name.
+ */
+ qsort(fs_tab, fs_tab_used, sizeof (struct fstable *), fs_tab_ent_comp);
+ if (strcmp(fs_tab[fs_tab_used-1]->name, rn) != 0) {
+ progerr(ERR_MNT_NOROOT, fs_tab[fs_tab_used-1]->name, rn, errno,
+ strerror(errno));
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+/*
+ * This function supports dryrun mode by allowing the filesystem table to be
+ * directly loaded from the continuation file.
+ */
+int
+load_fsentry(struct fstable *fs_entry, char *name, char *fstype,
+ char *remote_name)
+{
+ struct fstable *nfte;
+
+ if ((nfte = fs_tab_init(name, fstype)) == NULL)
+ return (1);
+
+ /* Grab the name and fstype from the new structure. */
+ fs_entry->name = nfte->name;
+ fs_entry->fstype = nfte->fstype;
+
+ /* Copy the basic structure into place. */
+ (void) memcpy(nfte, fs_entry, sizeof (struct fstable));
+
+ /*
+ * Allocate space for the 'special' name.
+ */
+ if ((nfte->remote_name = malloc(strlen(remote_name)+1)) == NULL) {
+ progerr(ERR_MALLOC, "remote_name", errno, strerror(errno));
+ return (1);
+ }
+
+ (void) strcpy(nfte->remote_name, remote_name);
+
+ return (0);
+}
+
+/*
+ * Given a path, return the table index of the filesystem the file apparently
+ * resides on. This doesn't put any time into resolving filesystems that
+ * refer to other filesystems. It just returns the entry containing this
+ * path.
+ */
+short
+fsys(char *path)
+{
+ register int i;
+ char real_path[PATH_MAX];
+ char *path2use = NULL;
+ char *cp = NULL;
+ int pathlen;
+
+ path2use = path;
+
+ real_path[0] = '\0';
+
+ (void) realpath(path, real_path);
+
+ if (real_path[0]) {
+ cp = strstr(path, real_path);
+ if (cp != path || cp == NULL)
+ path2use = real_path;
+ }
+
+ pathlen = strlen(path2use);
+
+ /*
+ * The following algorithm scans the list of attached file systems
+ * for the one containing path. At this point the file names in
+ * fs_tab[] are sorted by decreasing length to facilitate the scan.
+ * The first for() scans past all the file system names too short to
+ * contain path. The second for() does the actual string comparison.
+ * It tests first to assure that the comparison is against a complete
+ * token by assuring that the end of the filesystem name aligns with
+ * the end of a token in path2use (ie: '/' or NULL) then it does a
+ * string compare. -- JST
+ */
+
+ if (fs_tab_used == 0) {
+ return (-1);
+ }
+
+ for (i = 0; i < fs_tab_used; i++)
+ if (fs_tab[i] == NULL)
+ continue;
+ else if (fs_tab[i]->namlen <= pathlen)
+ break;
+ for (; i < fs_tab_used; i++) {
+ int fs_namelen;
+ char term_char;
+
+ if (fs_tab[i] == NULL)
+ continue;
+
+ fs_namelen = fs_tab[i]->namlen;
+ term_char = path2use[fs_namelen];
+
+ /*
+ * If we're putting the file "/a/kernel" into the filesystem
+ * "/a", then fs_namelen == 2 and term_char == '/'. If, we're
+ * putting "/etc/termcap" into "/", fs_namelen == 1 and
+ * term_char (unfortunately) == 'e'. In the case of
+ * fs_namelen == 1, we check to make sure the filesystem is
+ * "/" and if it is, we have a guaranteed fit, otherwise we
+ * do the string compare. -- JST
+ */
+ if ((fs_namelen == 1 && *(fs_tab[i]->name) == '/') ||
+ ((term_char == '/' || term_char == NULL) &&
+ strncmp(fs_tab[i]->name, path2use, fs_namelen) == 0))
+ return (i);
+ }
+
+ /*
+ * It only gets here if the root filesystem is fundamentally corrupt.
+ * (This can happen!)
+ */
+ progerr(ERR_FSYS_FELLOUT, path2use);
+
+ return (-1);
+}
+
+/*
+ * This function returns the entry in the fs_tab[] corresponding to the
+ * actual filesystem of record. It won't return a loopback filesystem entry,
+ * it will return the filesystem that the loopback filesystem is mounted
+ * over.
+ */
+short
+resolved_fsys(char *path)
+{
+ int i = -1;
+ char path2use[PATH_MAX];
+
+ (void) strcpy(path2use, path);
+
+ /* If this isn't a "real" filesystem, resolve the map. */
+ do {
+ (void) strcpy(path2use, server_map(path2use, i));
+ i = fsys(path2use);
+ } while (fs_tab[i]->srvr_map);
+
+ return (i);
+}
+
+/*
+ * This function returns the srvr_map status based upon the fs_tab entry
+ * number. This tells us if the server path constructed from the package
+ * install root is really the target filesystem.
+ */
+int
+use_srvr_map_n(short n)
+{
+ return ((int)fs_tab[n]->srvr_map);
+}
+
+/*
+ * This function returns the mount status based upon the fs_tab entry
+ * number. This tells us if there is any hope of gaining access
+ * to this file system.
+ */
+int
+is_mounted_n(short n)
+{
+ return ((int)fs_tab[n]->mounted);
+}
+
+/*
+ * is_fs_writeable_n - given an fstab index, return 1
+ * if it's writeable, 0 if read-only.
+ */
+int
+is_fs_writeable_n(short n)
+{
+ /*
+ * If the write access permissions haven't been confirmed, do that
+ * now. Note that the only reason we need to do the special check is
+ * in the case of an NFS mount (remote) because we can't determine if
+ * root has access in any other way.
+ */
+ if (fs_tab[n]->remote && fs_tab[n]->mounted &&
+ !fs_tab[n]->write_tested) {
+ if (fs_tab[n]->writeable && !really_write(fs_tab[n]->name))
+ fs_tab[n]->writeable = 0; /* not really */
+
+ fs_tab[n]->write_tested = 1; /* confirmed */
+ }
+
+ return ((int)fs_tab[n]->writeable);
+}
+
+/*
+ * is_remote_fs_n - given an fstab index, return 1
+ * if it's a remote filesystem, 0 if local.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+int
+is_remote_fs_n(short n)
+{
+ return ((int)fs_tab[n]->remote);
+}
+
+/* index-driven is_served() */
+int
+is_served_n(short n)
+{
+ return ((int)fs_tab[n]->served);
+}
+
+/*
+ * This returns the number of blocks available on the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_blk_free_n(short n)
+{
+ return (fs_tab[n]->bfree);
+}
+
+/*
+ * This returns the number of blocks being used on the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_blk_used_n(short n)
+{
+ return (fs_tab[n]->bused);
+}
+
+/*
+ * This returns the number of inodes available on the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_inode_free_n(short n)
+{
+ return (fs_tab[n]->ffree);
+}
+
+/*
+ * This returns the number of inodes being used on the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_inode_used_n(short n)
+{
+ return (fs_tab[n]->fused);
+}
+
+/*
+ * Sets the number of blocks being used on the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+void
+set_blk_used_n(short n, fsblkcnt_t value)
+{
+ fs_tab[n]->bused = value;
+}
+
+/* Get the filesystem block size. */
+fsblkcnt_t
+get_blk_size_n(short n)
+{
+ return (fs_tab[n]->bsize);
+}
+
+/* Get the filesystem fragment size. */
+fsblkcnt_t
+get_frag_size_n(short n)
+{
+ return (fs_tab[n]->bsize);
+}
+
+/*
+ * This returns the name of the indicated filesystem.
+ */
+char *
+get_fs_name_n(short n)
+{
+ if (fs_tab_used == 0) {
+ return (NULL);
+ } else if (n >= fs_tab_used) {
+ return (NULL);
+ } else {
+ return (fs_tab[n]->name);
+ }
+}
+
+/*
+ * This returns the remote name of the indicated filesystem.
+ *
+ * Note: Upon entry, a valid fsys() is required.
+ */
+char *
+get_source_name_n(short n)
+{
+ return (fs_tab[n]->remote_name);
+}
+
+/*
+ * This function returns the srvr_map status based upon the path.
+ */
+int
+use_srvr_map(char *path, short *fsys_value)
+{
+ if (*fsys_value == BADFSYS)
+ *fsys_value = fsys(path);
+
+ return (use_srvr_map_n(*fsys_value));
+}
+
+/*
+ * This function returns the mount status based upon the path.
+ */
+int
+is_mounted(char *path, short *fsys_value)
+{
+ if (*fsys_value == BADFSYS)
+ *fsys_value = fsys(path);
+
+ return (is_mounted_n(*fsys_value));
+}
+
+/*
+ * is_fs_writeable - given a cfent entry, return 1
+ * if it's writeable, 0 if read-only.
+ *
+ * Note: Upon exit, a valid fsys() is guaranteed. This is
+ * an interface requirement.
+ */
+int
+is_fs_writeable(char *path, short *fsys_value)
+{
+ if (*fsys_value == BADFSYS)
+ *fsys_value = fsys(path);
+
+ return (is_fs_writeable_n(*fsys_value));
+}
+
+/*
+ * is_remote_fs - given a cfent entry, return 1
+ * if it's a remote filesystem, 0 if local.
+ *
+ * Also Note: Upon exit, a valid fsys() is guaranteed. This is
+ * an interface requirement.
+ */
+int
+is_remote_fs(char *path, short *fsys_value)
+{
+ if (*fsys_value == BADFSYS)
+ *fsys_value = fsys(path);
+
+ return (is_remote_fs_n(*fsys_value));
+}
+
+/*
+ * This function returns the served status of the filesystem. Served means a
+ * client is getting this file from a server and it is not writeable by the
+ * client. It has nothing to do with whether or not this particular operation
+ * (eg: pkgadd or pkgrm) will be writing to it.
+ */
+int
+is_served(char *path, short *fsys_value)
+{
+ if (*fsys_value == BADFSYS)
+ *fsys_value = fsys(path);
+
+ return (is_served_n(*fsys_value));
+}
+
+/*
+ * get_remote_path - given a filesystem table index, return the
+ * path of the filesystem on the remote system. Otherwise,
+ * return NULL if it's a local filesystem.
+ */
+char *
+get_remote_path(short n)
+{
+ char *p;
+
+ if (!is_remote_fs_n(n))
+ return (NULL); /* local */
+ p = strchr(fs_tab[n]->remote_name, ':');
+ if (!p)
+ p = fs_tab[n]->remote_name; /* Loopback */
+ else
+ p++; /* remote */
+ return (p);
+}
+
+/*
+ * get_mount_point - given a filesystem table index, return the
+ * path of the mount point. Otherwise,
+ * return NULL if it's a local filesystem.
+ */
+char *
+get_mount_point(short n)
+{
+ if (!is_remote_fs_n(n))
+ return (NULL); /* local */
+ return (fs_tab[n]->name);
+}
+
+struct fstable *
+get_fs_entry(short n)
+{
+ if (fs_tab_used == 0) {
+ return (NULL);
+ } else if (n >= fs_tab_used) {
+ return (NULL);
+ } else {
+ return (fs_tab[n]);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/nblk.c b/usr/src/cmd/svr4pkg/libinst/nblk.c
new file mode 100644
index 0000000000..54c936f6ab
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/nblk.c
@@ -0,0 +1,84 @@
+/*
+ * 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 2008 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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+
+/*
+ * This should not be a constant, but for ufs it is 12, not 10 like for s5.
+ */
+#define DIRECT 12 /* Number of logical blocks before indirection */
+
+fsblkcnt_t
+nblk(fsblkcnt_t size, ulong_t bsize, ulong_t frsize)
+{
+ fsblkcnt_t tot, count, count1, d_indirect, t_indirect, ind;
+ fsblkcnt_t frags = 0;
+
+ if (size == 0 || bsize == 0)
+ return (1);
+
+ /*
+ * Need to keep track of indirect blocks.
+ */
+
+ ind = howmany(bsize, sizeof (daddr_t));
+ d_indirect = ind + DIRECT; /* double indirection */
+ t_indirect = ind * (ind + 1) + DIRECT; /* triple indirection */
+
+ tot = howmany(size, bsize);
+
+ if (tot > t_indirect) {
+ count1 = (tot - ind * ind - (DIRECT + 1)) / ind;
+ count = count1 + count1 / ind + ind + 3;
+ } else if (tot > d_indirect) {
+ count = (tot - (DIRECT + 1)) / ind + 2;
+ } else if (tot > DIRECT) {
+ count = 1;
+ } else {
+ count = 0;
+ frags = (frsize > 0) ?
+ roundup(size, frsize) :
+ roundup(size, bsize);
+ }
+
+ /* Accounting for the indirect blocks, the total becomes */
+ tot += count;
+
+ /*
+ * calculate number of 512 byte blocks, for frag or full block cases.
+ */
+ if (!frags)
+ tot *= howmany(bsize, DEV_BSIZE);
+ else
+ tot = howmany(frags, DEV_BSIZE);
+ return (tot);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/ocfile.c b/usr/src/cmd/svr4pkg/libinst/ocfile.c
new file mode 100644
index 0000000000..7f4b701d5c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/ocfile.c
@@ -0,0 +1,864 @@
+/*
+ * 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 2008 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 <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/statvfs.h>
+#include <signal.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libinst.h"
+#include "libadm.h"
+
+#define LOCKFILE ".pkg.lock"
+#define LOCKWAIT 10 /* seconds between retries */
+#define LOCKRETRY 20 /* number of retries for a DB lock */
+
+#define ERR_TC_WRITE "WARNING: unable to write temp contents file <%s>"
+#define ERR_NOCLOSE "WARNING: unable to close <%s>"
+#define ERR_NOUNLINK_LATENT "WARNING: unable to unlink latent <%s>"
+#define ERR_LINK_FAIL "link(%s, %s) failed (errno %d)"
+#define ERR_NORENAME_CONTENTS "unable to establish contents file <%s> "\
+ "from <%s>"
+#define ERR_RENAME_FAIL "rename(%s, %s) failed (errno %d)"
+#define ERR_RESTORE_FAIL "attempt to restore <%s> failed"
+#define ERR_NOUNLINK "WARNING: unable to unlink <%s>"
+#define ERR_FCLOSE_FAIL "fclose failed (errno %d)"
+#define ERR_ERRNO "(errno %d: %s)"
+#define ERR_NOTMPOPEN "unable to open temporary contents file image"
+#define ERR_CFBACK "Not enough space to backup <%s>"
+#define ERR_CREAT_CONT "unable to create contents file <%s>: %s"
+#define ERR_ACCESS_CONT "unable to access contents file <%s>: %s"
+#define ERR_CFBACK1 "Need=%llu blocks, Available=%llu blocks " \
+ "(block size=%d)"
+#define ERR_NOCFILE "unable to locate contents file <%s>"
+#define ERR_NOROPEN "unable to open <%s> for reading"
+#define ERR_NOOPEN "unable to open <%s> for writing"
+#define ERR_NOSTAT "unable to stat contents file <%s>"
+#define ERR_NOSTATV "statvfs(%s) failed"
+#define ERR_NOUPD "unable to update contents file"
+#define ERR_DRCONTCP "unable to copy contents file to <%s>"
+
+#define MSG_XWTING "NOTE: Waiting for exclusive access to the package " \
+ "database."
+#define MSG_NOLOCK "NOTE: Couldn't lock the package database."
+
+#define ERR_NOLOCK "Database lock failed."
+#define ERR_OPLOCK "unable to open lock file <%s>."
+#define ERR_MKLOCK "unable to create lock file <%s>."
+#define ERR_LCKREM "unable to lock package database - remote host " \
+ "unavailable."
+#define ERR_BADLCK "unable to lock package database - unknown error."
+#define ERR_DEADLCK "unable to lock package database - deadlock condition."
+#define ERR_TMOUT "unable to lock package database - too many retries."
+#define ERR_CFDIR "unable to locate contents file directory"
+
+static int active_lock;
+static int lock_fd; /* fd of LOCKFILE. */
+static char *pkgadm_dir;
+
+static int pkgWlock(int verbose);
+static int pkgWunlock(void);
+
+/*
+ * This VFP is used to cache the last copy of the contents file that was
+ * written out - upon subsequent open if the contents file has not changed
+ * since it was last written out, use the last cached copy that is still
+ * in memory to avoid re-reading the contents file again. If the contents
+ * file has changed since the cached copy was written out, the previous
+ * copy is discarded and the new contents file contents are read in.
+ */
+
+static VFP_T *contentsVfp = {(VFP_T *)NULL};
+
+/*
+ * This defines the maximum number of bytes that can be added to the contents
+ * file for a single package - this must be higher than the largest expected
+ * pkgmap file will ever be. This controls the amount of memory allocated for
+ * the contents file additions. A pkgmap file with an average line length of
+ * 128/256/512 bytes could add 62500/31250/15625 entries with this size. This
+ * allows the contents file to have a fixed allocation without having to check
+ * size and realloc as necessary with the attendant cost of the realloc. The
+ * real memory used will only be those pages that are actually touched when
+ * the contents file is written.
+ */
+
+#define CONTENTS_DELTA (32*1024*1024) /* 32mb */
+
+/* forward declarations */
+
+int relslock(void);
+
+/*ARGSUSED*/
+static void
+do_alarm(int n)
+{
+ (void) signal(SIGALRM, SIG_IGN);
+ (void) signal(SIGALRM, do_alarm);
+ (void) alarm(LOCKWAIT);
+}
+
+/*
+ * Point packaging to the appropriate contents file. This is primarily used
+ * to establish a dryrun contents file. If the malloc() doesn't work, this
+ * returns 99 (internal error), else 0.
+ */
+int
+set_cfdir(char *cfdir)
+{
+ char realcf[PATH_MAX];
+ char tmpcf[PATH_MAX];
+ int status;
+
+ if (cfdir == NULL) {
+ pkgadm_dir = get_PKGADM();
+ return (0);
+ }
+
+ if ((pkgadm_dir = strdup(cfdir)) == NULL) {
+ return (99);
+ }
+
+ (void) snprintf(tmpcf, sizeof (tmpcf), "%s/contents", pkgadm_dir);
+
+ /*
+ * return if a temporary contents file already exists -
+ * assume it is from a prior package in this series.
+ */
+
+ if (access(tmpcf, F_OK) == 0) {
+ return (0);
+ }
+
+ /*
+ * no temporary contents file exists - create one.
+ */
+
+ (void) snprintf(realcf, sizeof (realcf), "%s/contents", get_PKGADM());
+
+ /*
+ * If there's a contents file there already, copy it
+ * over, otherwise initialize one.
+ */
+
+ /* create new contents file if one does not already exist */
+
+ if (access(realcf, F_OK) != 0) {
+ int n;
+
+ n = open(tmpcf, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if (n < 0) {
+ progerr(gettext(ERR_CREAT_CONT), tmpcf,
+ strerror(errno));
+ return (99);
+ }
+ (void) close(n);
+ } else {
+
+ /* contents file exists, save in pkgadm-dir */
+
+ status = copyf(realcf, tmpcf, (time_t)0);
+ if (status != 0) {
+ progerr(gettext(ERR_DRCONTCP), tmpcf);
+ return (99);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * This function installs the database lock, opens the contents file for
+ * reading and creates and opens the temporary contents file for read/write.
+ * It returns 1 if successful, 0 otherwise.
+ */
+int
+ocfile(VFP_T **r_mapvfp, VFP_T **r_tmpvfp, fsblkcnt_t map_blks)
+{
+ struct stat64 statb;
+ struct statvfs64 svfsb;
+ fsblkcnt_t free_blocks;
+ fsblkcnt_t need_blocks;
+ VFP_T *mapvfp = (VFP_T *)NULL;
+ VFP_T *tmpvfp = (VFP_T *)NULL;
+ char contents[PATH_MAX];
+ int n;
+
+ /* reset return VFP/FILE pointers */
+
+ (*r_mapvfp) = (VFP_T *)NULL;
+ (*r_tmpvfp) = (VFP_T *)NULL;
+
+ /* establish package administration contents directory location */
+
+ if (pkgadm_dir == NULL) {
+ if (set_cfdir(NULL) != 0) {
+ progerr(gettext(ERR_CFDIR));
+ return (0);
+ }
+ }
+
+ /* Lock the file for exclusive access */
+
+ if (!pkgWlock(1)) {
+ progerr(gettext(ERR_NOLOCK));
+ return (0);
+ }
+
+ /* determine path to the primary contents file */
+
+ (void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
+
+ /*
+ * open the contents file to read only - if a previous contents file has
+ * been cached attempt to use that cached copy for the open, otherwise
+ * just open the contents file directly
+ */
+
+ n = vfpCheckpointOpen(&contentsVfp, &mapvfp, contents, "r", VFP_NONE);
+
+ /* return error if contents file cannot be accessed */
+
+ if (n != 0) {
+ int lerrno = errno;
+
+ if (errno == ENOENT) {
+ progerr(gettext(ERR_NOCFILE), contents);
+ } else {
+ progerr(gettext(ERR_NOROPEN), contents);
+ }
+
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ return (0);
+ }
+
+ /*
+ * Check and see if there is enough space for the packaging commands
+ * to back up the contents file, if there is not, then do not allow
+ * execution to continue by failing the ocfile() call.
+ */
+
+ /* Get the contents file size */
+
+ if (fstat64(fileno(mapvfp->_vfpFile), &statb) == -1) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOSTAT), contents);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ (void) vfpClose(&mapvfp);
+ return (0);
+ }
+
+ /* Get the filesystem space */
+
+ if (fstatvfs64(fileno(mapvfp->_vfpFile), &svfsb) == -1) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOSTATV), contents);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ (void) vfpClose(&mapvfp);
+ return (0);
+ }
+
+ free_blocks = (((fsblkcnt_t)svfsb.f_frsize > 0) ?
+ howmany(svfsb.f_frsize, DEV_BSIZE) :
+ howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
+
+ if (map_blks == 0LL) {
+ map_blks = 10LL;
+ }
+
+ /*
+ * Calculate the number of blocks we need to be able to operate on
+ * the contents file.
+ */
+ need_blocks = map_blks +
+ nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
+
+ if ((need_blocks + 10) > free_blocks) {
+ progerr(gettext(ERR_CFBACK), contents);
+ progerr(gettext(ERR_CFBACK1), need_blocks, free_blocks,
+ DEV_BSIZE);
+ (void) vfpClose(&mapvfp);
+ return (0);
+ }
+
+ /*
+ * open the temporary contents file without a path name - this causes
+ * the "vfp" to be opened on in-memory storage only, the size of which
+ * is set following a successful return - this causes the temporary
+ * contents file to be maintained in memory only - if no changes are
+ * made as the primary contents file is processed, the in memory data
+ * is discarded and not written to the disk.
+ */
+
+ if (vfpOpen(&tmpvfp, (char *)NULL, "w", VFP_NONE) != 0) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOTMPOPEN));
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ (void) vfpClose(&mapvfp);
+ return (0);
+ }
+
+ /*
+ * set size of allocation for temporary contents file - this sets the
+ * size of the in-memory buffer associated with the open vfp.
+ */
+
+ if (vfpSetSize(tmpvfp, statb.st_size + CONTENTS_DELTA) != 0) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOTMPOPEN));
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ (void) vfpClose(&tmpvfp);
+ (void) vfpClose(&mapvfp);
+ return (0);
+ }
+
+ /*
+ * now that the temporary file is opened, advise the vm system to start
+ * mapping the real contents file into memory if possible
+ */
+
+ (void) vfpSetFlags(mapvfp, VFP_NEEDNOW);
+
+ /* set return ->s to open vfps */
+
+ (*r_mapvfp) = mapvfp;
+ (*r_tmpvfp) = tmpvfp;
+
+ return (1); /* All OK */
+}
+
+/*
+ * This is a simple open and lock of the contents file. It doesn't create a
+ * temporary contents file and it doesn't need to do any space checking.
+ * Returns 1 for OK and 0 for "didn't do it".
+ */
+int
+socfile(VFP_T **r_mapvfp)
+{
+ VFP_T *mapvfp = (VFP_T *)NULL;
+ char contents[PATH_MAX];
+ int n;
+
+ /* reset return VFP/FILE pointer */
+
+ (*r_mapvfp) = (VFP_T *)NULL;
+
+ if (pkgadm_dir == NULL) {
+ if (set_cfdir(NULL) != 0) {
+ progerr(gettext(ERR_CFDIR));
+ return (0);
+ }
+ }
+
+ /*
+ * Lock the database for exclusive access, but don't make a fuss if
+ * it fails (user may not be root and the .pkg.lock file may not
+ * exist yet).
+ */
+
+ if (!pkgWlock(0)) {
+ logerr(gettext(MSG_NOLOCK));
+ }
+
+ /* open the contents file to read only */
+
+ (void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
+
+ n = vfpCheckpointOpen(&contentsVfp, &mapvfp, contents,
+ "r", VFP_NEEDNOW);
+ if (n != 0) {
+ int lerrno = errno;
+
+ if (errno == ENOENT) {
+ progerr(gettext(ERR_NOCFILE), contents);
+ } else {
+ progerr(gettext(ERR_NOROPEN), contents);
+ }
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ return (0);
+ }
+
+ *r_mapvfp = mapvfp;
+
+ return (1);
+}
+
+/*
+ * Name: swapcfile
+ * Description: This function closes both the current and temporary contents
+ * files specified, and conditionally replaces the old transitory
+ * contents file with the newly updated temporary contents file.
+ * The "ocfile()" or "socfile()" functions must be called to re-
+ * open the real contents file for processing.
+ * Arguments: a_cfVfp - (VFP_T **) - [RW, *RW]
+ * This is the VFP associated with the real contents file
+ * that is being read from and data processed.
+ * a_cfTmpVfp - (VFP_T **) - [RW, *RW]
+ * This is the VFP associated with the temporary contents
+ * file that is being written to.
+ * pkginst - (char) - [RO, *RO]
+ * This is the name of the package being operated on;
+ * this is used to write the "last modified by xxx"
+ * comment at the end of the contents file.
+ * dbchg - (int) - [RO]
+ * == 0 - the temporary contents file has NOT been changed
+ * with respect to the real contents file; do not
+ * update the real contents file with the contents
+ * of the temporary contents file.
+ * != 0 - the temporary contetns file HAS been changed with
+ * respect to the real contents file; DO update the
+ * real contents file with the contents of the
+ * temporary contents file.
+ * Returns: int == RESULT_OK - successful
+ * == RESULT_WRN - successful with warnings
+ * == RESULT_ERR - failed with fatal errors - deserves an
+ * alarming message and a quit()
+ * NOTES: If dbchg != 0, the contents file is always updated. If dbchg == 0,
+ * the contents file is updated IF the data is modified indication
+ * is set on the contents file associated with a_cfTmpVfp.
+ */
+
+int
+swapcfile(VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp, char *pkginst, int dbchg)
+{
+ char *pe;
+ char *pl;
+ char *ps;
+ char contentsPath[PATH_MAX] = {'\0'};
+ char line[256];
+ char sContentsPath[PATH_MAX] = {'\0'};
+ char tContentsPath[PATH_MAX] = {'\0'};
+ char timeb[BUFSIZ];
+ int retval = RESULT_OK;
+ struct tm *timep;
+ time_t clock;
+
+ /* normalize pkginst so its never null */
+
+ if (pkginst == (char *)NULL) {
+ dbchg = 0;
+ pkginst = "<unknown>";
+ }
+
+ /* cache all paths for the associated open files */
+
+ (void) strlcpy(contentsPath, vfpGetPath(*a_cfVfp),
+ sizeof (contentsPath));
+
+ (void) snprintf(tContentsPath, sizeof (tContentsPath),
+ "%s/t.contents", pkgadm_dir);
+
+ (void) snprintf(sContentsPath, sizeof (sContentsPath),
+ "%s/s.contents", pkgadm_dir);
+
+ /* original contents file no longer needed - close */
+
+ if (vfpClose(a_cfVfp) != 0) {
+ int lerrno = errno;
+
+ logerr(gettext(ERR_NOCLOSE), contentsPath);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ retval = RESULT_WRN;
+ }
+
+ /*
+ * If no changes were made to the database, checkpoint the temporary
+ * contents file - if this fails, then just close the file which causes
+ * the contents file to be reopened and reread if it is needed again
+ */
+
+ if ((dbchg == 0) && (vfpGetModified(*a_cfTmpVfp) == 0)) {
+ if (vfpCheckpointFile(&contentsVfp, a_cfTmpVfp,
+ contentsPath) != 0) {
+ vfpClose(a_cfTmpVfp);
+ }
+ (void) pkgWunlock(); /* Free the database lock. */
+ return (retval);
+ }
+
+ /*
+ * changes made to the current temporary contents file -
+ * remove any trailing comment lines in the temp contents file, then
+ * append updated modification info records to temp contents file
+ */
+
+ pe = vfpGetCurrCharPtr(*a_cfTmpVfp); /* last char in contents file */
+ ps = vfpGetFirstCharPtr(*a_cfTmpVfp); /* 1st char in contents file */
+ pl = pe; /* last match is last char in contents file */
+
+ /* skip past all trailing newlines and null bytes */
+
+ while ((pe > ps) && ((*pe == '\n') || (*pe == '\0'))) {
+ pe--;
+ }
+
+ /* remove trailing comments as long as there are lines in the file */
+
+ while (pe > ps) {
+ if (*pe != '\n') {
+ /* curr char is not newline: backup one byte */
+ pl = pe--;
+ } else if (*pl != '#') {
+ /* curr char is newline next char not comment break */
+ break;
+ } else {
+ /* curr char is newline next char is comment - remove */
+ *pl = '\0';
+ vfpSetLastCharPtr(*a_cfTmpVfp, pl);
+ pe--;
+ }
+ }
+
+ /* create two update comment lines */
+
+ (void) time(&clock);
+ timep = localtime(&clock);
+
+ (void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+ (void) snprintf(line, sizeof (line),
+ gettext("# Last modified by %s for %s package\n# %s"),
+ get_prog_name(), pkginst, timeb);
+ vfpPuts(*a_cfTmpVfp, line);
+
+ /* commit temporary contents file bytes to storage */
+
+ if (vfpWriteToFile(*a_cfTmpVfp, tContentsPath) != 0) {
+ int lerrno = errno;
+
+ logerr(gettext(ERR_TC_WRITE), tContentsPath);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ vfpClose(a_cfTmpVfp);
+ (void) remove(tContentsPath);
+ (void) pkgWunlock(); /* Free the database lock. */
+ return (RESULT_ERR);
+ }
+
+ /*
+ * Now we want to make a copy of the old contents file as a
+ * fail-safe. In support of that, we create a hard link to
+ * s.contents.
+ */
+
+ if ((access(sContentsPath, F_OK) == 0) && remove(sContentsPath)) {
+ int lerrno = errno;
+
+ logerr(gettext(ERR_NOUNLINK_LATENT), sContentsPath);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ (void) remove(tContentsPath);
+ (void) pkgWunlock(); /* Free the database lock. */
+ vfpClose(a_cfTmpVfp);
+ return (RESULT_ERR);
+ }
+
+ if (link(contentsPath, sContentsPath) != 0) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOUPD));
+ logerr(gettext(ERR_LINK_FAIL), contentsPath, sContentsPath,
+ lerrno);
+ (void) remove(tContentsPath);
+ (void) pkgWunlock(); /* Free the database lock. */
+ vfpClose(a_cfTmpVfp);
+ return (RESULT_ERR);
+ }
+
+ if (rename(tContentsPath, contentsPath) != 0) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NORENAME_CONTENTS), contentsPath,
+ tContentsPath);
+ logerr(gettext(ERR_RENAME_FAIL), tContentsPath,
+ contentsPath, lerrno);
+ if (rename(sContentsPath, contentsPath)) {
+ lerrno = errno;
+ progerr(gettext(ERR_RESTORE_FAIL), contentsPath);
+ logerr(gettext(ERR_RENAME_FAIL), sContentsPath,
+ contentsPath, lerrno);
+ }
+ (void) remove(tContentsPath);
+ }
+
+ if (remove(sContentsPath) != 0) {
+ int lerrno = errno;
+
+ logerr(gettext(ERR_NOUNLINK), sContentsPath);
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ retval = RESULT_WRN;
+ }
+
+ /*
+ * checkpoint the temporary contents file - if this fails, then
+ * just close the file which causes the contents file to be reopened
+ * and reread if it is needed again
+ */
+
+ if (vfpCheckpointFile(&contentsVfp, a_cfTmpVfp, contentsPath) != 0) {
+ vfpClose(a_cfTmpVfp);
+ }
+
+ return (relslock() == 0 ? RESULT_ERR : retval);
+}
+
+/* This function releases the lock on the package database. */
+int
+relslock(void)
+{
+ /*
+ * This closes the contents file and releases the lock.
+ */
+ if (!pkgWunlock()) {
+ int lerrno = errno;
+
+ progerr(gettext(ERR_NOUPD));
+ logerr(gettext(ERR_FCLOSE_FAIL), lerrno);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * This function attempts to lock the package database. It returns 1 on
+ * success, 0 on failure. The positive logic verbose flag determines whether
+ * or not the function displays the error message upon failure.
+ */
+static int
+pkgWlock(int verbose) {
+ int retry_cnt, retval;
+ char lockpath[PATH_MAX];
+
+ active_lock = 0;
+
+ (void) snprintf(lockpath, sizeof (lockpath),
+ "%s/%s", pkgadm_dir, LOCKFILE);
+
+ retry_cnt = LOCKRETRY;
+
+ /*
+ * If the lock file is not present, create it. The mode is set to
+ * allow any process to lock the database, that's because pkgchk may
+ * be run by a non-root user.
+ */
+ if (access(lockpath, F_OK) == -1) {
+ lock_fd = open(lockpath, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if (lock_fd < 0) {
+ if (verbose)
+ progerr(gettext(ERR_MKLOCK), lockpath);
+ return (0);
+ } else {
+ (void) fchmod(lock_fd, 0644); /* force perms. */
+ }
+ } else {
+ if ((lock_fd = open(lockpath, O_RDWR)) == -1) {
+ if (verbose)
+ progerr(gettext(ERR_OPLOCK), lockpath);
+ return (0);
+ }
+ }
+
+ (void) signal(SIGALRM, do_alarm);
+ (void) alarm(LOCKWAIT);
+
+ do {
+ if (lockf(lock_fd, F_LOCK, 0)) {
+ if (errno == EAGAIN || errno == EINTR)
+ logerr(gettext(MSG_XWTING));
+ else if (errno == ECOMM) {
+ logerr(gettext(ERR_LCKREM));
+ retval = 0;
+ break;
+ } else if (errno == EBADF) {
+ logerr(gettext(ERR_BADLCK));
+ retval = 0;
+ break;
+ } else if (errno == EDEADLK) {
+ logerr(gettext(ERR_DEADLCK));
+ retval = 0;
+ break;
+ }
+ } else {
+ active_lock = 1;
+ retval = 1;
+ break;
+ }
+ } while (retry_cnt--);
+
+ (void) signal(SIGALRM, SIG_IGN);
+
+ if (retval == 0)
+ {
+ if (retry_cnt == -1) {
+ logerr(gettext(ERR_TMOUT));
+ }
+
+ (void) pkgWunlock(); /* close the lockfile. */
+ }
+
+ return (retval);
+}
+
+/*
+ * Release the lock on the package database. Returns 1 on success, 0 on
+ * failure.
+ */
+static int
+pkgWunlock(void) {
+ if (active_lock) {
+ active_lock = 0;
+ if (close(lock_fd))
+ return (0);
+ else
+ return (1);
+ } else
+ return (1);
+}
+
+/*
+ * This function verifies that the contents file is in place.
+ * returns 1 - if it exists
+ * returns 0 - if it does not exist
+ */
+int
+iscfile(void)
+{
+ char contents[PATH_MAX];
+
+ (void) snprintf(contents, PATH_MAX, "%s/contents", get_PKGADM());
+
+ return (access(contents, F_OK) == 0 ? 1 : 0);
+}
+
+/*
+ * This function verifies that the contents file is in place. If it is - no
+ * change. If it isn't - this creates it.
+ * Returns: == 0 : failure
+ * != 0 : success
+ */
+
+int
+vcfile(void)
+{
+ int lerrno;
+ int fd;
+ char contents[PATH_MAX];
+
+ /*
+ * create full path to contents file
+ */
+
+ (void) snprintf(contents, sizeof (contents),
+ "%s/contents", get_PKGADM());
+
+ /*
+ * Attempt to create the file - will only be successful
+ * if the file does not currently exist.
+ */
+
+ fd = open(contents, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd >= 0) {
+ /*
+ * Contents file wasn't there, but is now.
+ */
+
+ echo(gettext("## Software contents file initialized"));
+ (void) close(fd);
+ return (1); /* success */
+ }
+
+ /*
+ * Could not create the file - it may exist or there may be
+ * permissions issues - find out and act accordingly.
+ */
+
+ lerrno = errno;
+
+ /* success if error is 'file exists' */
+
+ if (lerrno == EEXIST) {
+ return (1); /* success */
+ }
+
+ /* success if error is 'permission denied' but file exists */
+
+ if (lerrno == EACCES) {
+ /*
+ * Because O_CREAT and O_EXCL are specified in open(),
+ * if the contents file already exists, the open will
+ * fail with EACCES - determine if this is the case -
+ * if so return success.
+ */
+
+ if (access(contents, F_OK) == 0) {
+ return (1); /* success */
+ }
+
+ /*
+ * access() failed - if because of permissions failure this
+ * means the contents file exists but it cannot be accessed
+ * or the path to the contents file cannot be accessed - in
+ * either case the contents file cannot be accessed.
+ */
+
+ if (errno == EACCES) {
+ progerr(gettext(ERR_ACCESS_CONT), contents,
+ strerror(lerrno));
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ return (0); /* failure */
+ }
+ }
+
+ /*
+ * the contents file does not exist and it cannot be created.
+ */
+
+ progerr(gettext(ERR_CREAT_CONT), contents, strerror(lerrno));
+ logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+ return (0); /* failure */
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/open_package_datastream.c b/usr/src/cmd/svr4pkg/libinst/open_package_datastream.c
new file mode 100644
index 0000000000..2896a99e80
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/open_package_datastream.c
@@ -0,0 +1,298 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#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 <assert.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 <pkglib.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * open package datastream
+ * Arguments: a_argc - (int) - [RO, *RO]
+ * - number of arguments available in a_argv
+ * a_argv - (char **) - [RO, *RO]
+ * - arguments representing package names to add
+ * a_spoolDir - (char *) - [RO, *RO]
+ * - directory to write the package (spool) into
+ * - if == (char *)NULL then install the packages
+ * - if != (char *)NULL then write packages into directory
+ * a_device - (char *) - [RO, *RO]
+ * - device to read packages from when spooling
+ * - ignored if a_spoolDir == (char *)NULL
+ * r_repeat - (int *) - [RO, *RW]
+ * - set == 0 if no further package names in argc/argv
+ * - set != 0 IF there are package names in argc/argv
+ * - if == (int *)NULL - not set
+ * r_idsName - (char **) - [RW, *RW]
+ * - set to the name of package input data stream device
+ * - if == (char *)NULL - no input data stream; that is,
+ * -- the packages are in a directory and not in a stream
+ * - if != (char *)NULL - this is the device/file that
+ * -- is the datastream that contains the packages to add
+ * a_pkgdev - (struct pkgdev *) - [RO, *RW]
+ * - pkgdev structure containing package device to open
+ * Returns: B_TRUE - datastream opened successfully
+ * B_FALSE - datastream failed to open
+ */
+
+boolean_t
+open_package_datastream(int a_argc, char **a_argv, char *a_spoolDir,
+ char *a_device, int *r_repeat, char **r_idsName, char *a_tmpdir,
+ struct pkgdev *a_pkgdev, int a_optind)
+{
+ int n;
+
+ /* entry assertions */
+
+ assert(a_argv != (char **)NULL);
+ assert(r_idsName != (char **)NULL);
+ assert(a_tmpdir != (char *)NULL);
+ assert(a_pkgdev != (struct pkgdev *)NULL);
+
+ /* entry debug information */
+
+ echoDebug(DBG_ODS_ENTRY);
+ echoDebug(DBG_ODS_ARGS,
+ a_pkgdev->bdevice ? a_pkgdev->bdevice : "?",
+ a_pkgdev->cdevice ? a_pkgdev->cdevice : "?",
+ a_pkgdev->pathname ? a_pkgdev->pathname : "?",
+ a_argc, a_device ? a_device : "?");
+
+ /* reset possible return values to defaults */
+
+ *r_idsName = (char *)NULL;
+ if (r_repeat != (int *)NULL) {
+ *r_repeat = 0;
+ }
+
+ /*
+ * Determine how to access the package source "device":
+ * - if a block device is associated with the source:
+ * -- make sure the next "volume" is mounted and ready.
+ * -- input data stream is associated character device
+ * - if char device but no block device associated with device:
+ * -- input data stream is associated character device
+ * - else if a path is associated with device:
+ * -- input data stream is associated path
+ */
+
+ if (a_pkgdev->bdevice != (char *)NULL) {
+ /* package source is block device */
+
+ /*
+ * _getvol verifies that the specified device is accessible and
+ * that a volume of the appropriate medium has been inserted.
+ * _getvol is in libadm.h - delivered by ON as part of SUNWcsl
+ * is somewhat analagous to getvol(1M) - args are:
+ * - char *device
+ * - char *label
+ * - int options
+ * - char *prompt
+ * - char *norewind - no rewind device (NULL to use device)
+ * Returns:
+ * 0 - okay, label matches
+ * 1 - device not accessable
+ * 2 - unknown device (devattr failed)
+ * 3 - user selected quit
+ * 4 - label does not match
+ */
+
+ echoDebug(DBG_ODS_DATASTREAM_BDEV, a_pkgdev->bdevice);
+
+ n = _getvol(a_pkgdev->bdevice, NULL, 0L,
+ MSG_INSERT_VOL, a_pkgdev->norewind);
+
+ switch (n) {
+ case 0: /* volume open, label matches */
+ if (ds_readbuf(a_pkgdev->cdevice)) {
+ (*r_idsName) = a_pkgdev->cdevice;
+ }
+ break;
+ case 3: /* user selected quit */
+ quit(3);
+ /* NOTREACHED */
+ case 2: /* unknown device (devattr failed) */
+ progerr(ERR_UNKNOWN_DEV, a_pkgdev->name);
+ quit(99);
+ /* NOTREACHED */
+ default: /* device not accessable */
+ progerr(ERR_PKGVOL);
+ logerr(LOG_GETVOL_RET, n);
+ quit(99);
+ /* NOTREACHED */
+ }
+ } else if (a_pkgdev->cdevice != (char *)NULL) {
+ /* package source is character device */
+
+ echoDebug(DBG_ODS_DATASTREAM_CDEV, a_pkgdev->cdevice);
+
+ (*r_idsName) = a_pkgdev->cdevice;
+ } else if (a_pkgdev->pathname != (char *)NULL) {
+ /* package source is path name to file */
+
+ echoDebug(DBG_ODS_DATASTREAM_ISFILE, a_pkgdev->pathname);
+
+ (*r_idsName) = a_pkgdev->pathname;
+ } else {
+ echoDebug(DBG_ODS_DATASTREAM_UNK);
+ }
+
+ /*
+ * If writing the packages into a spool directory instead of
+ * installing the packages, invoke pkgtrans to perform the
+ * conversion and exit.
+ */
+
+ if (a_spoolDir) {
+ return (B_TRUE);
+ }
+
+ /* create temp dir for op if input data stream specified */
+
+ if (*r_idsName) {
+ /*
+ * initialize datastream,
+ * dirname is set to directory where package is unstreamed
+ */
+ if (setup_temporary_directory(&a_pkgdev->dirname, a_tmpdir,
+ "dstream") == B_FALSE) {
+ progerr(ERR_STREAMDIR, strerror(errno));
+ quit(99);
+ /* NOTREACHED */
+ }
+ }
+
+ if (r_repeat != (int *)NULL) {
+ *r_repeat = (a_optind >= a_argc);
+ }
+
+ /*
+ * mount source device (e.g. floppy) if no input data stream
+ * specified, and the package source device is mountable. If
+ * the pkgmount fails, go back and try to mount the package
+ * source again. When a package is split up into multiple
+ * volumes (such as floppies), it might be possible to go back
+ * and insert a different copy of the required volume/floppy
+ * if the current one cannot be mounted. Otherwise this could
+ * have just called quit() if the mount failed...
+ */
+
+ if (((*r_idsName) == (char *)NULL) && a_pkgdev->mount) {
+ echoDebug(DBG_ODS_DATASTREAM_MOUNTING, *r_idsName,
+ a_pkgdev->mount);
+ a_pkgdev->rdonly++;
+ n = pkgmount(a_pkgdev, NULL, 0, 0, 0);
+ if (n != 0) {
+ /* pkgmount failed */
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * open and initialize input data stream if specified
+ */
+
+ if ((*r_idsName) != (char *)NULL) {
+ echoDebug(DBG_ODS_DATASTREAM_INIT, *r_idsName);
+
+ /* use character device to force rewind of datastream */
+ if ((a_pkgdev->cdevice != (char *)NULL) &&
+ (a_pkgdev->bdevice == (char *)NULL)) {
+ n = _getvol(a_pkgdev->name, NULL, 0L, NULL,
+ a_pkgdev->norewind);
+
+ switch (n) {
+ case 0: /* volume open, label matches */
+ break;
+ case 3: /* user selected quit */
+ quit(3);
+ /* NOTREACHED */
+ case 2: /* unknown device (devattr failed) */
+ progerr(ERR_UNKNOWN_DEV, a_pkgdev->name);
+ quit(99);
+ /* NOTREACHED */
+ default:
+ progerr(ERR_PKGVOL);
+ logerr(LOG_GETVOL_RET, n);
+ quit(99);
+ /* NOTREACHED */
+ }
+ }
+
+ if (chdir(a_pkgdev->dirname)) {
+ progerr(ERR_CHDIR, a_pkgdev->dirname);
+ quit(99);
+ /* NOTREACHED */
+ }
+
+ /*
+ * initialize datastream for subsequent installation;
+ * read the source device;
+ * aquire the header data and check it for validity;
+ * creates subdirectories in package stream directory
+ * (a_pkgdev->dirname) for each package and retrieves each
+ * packages pkginfo and pkgmap files
+ */
+
+ if (ds_init(*r_idsName, &a_argv[a_optind],
+ a_pkgdev->norewind)) {
+ progerr(ERR_DSINIT, *r_idsName);
+ quit(99);
+ /* NOTREACHED */
+ }
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/pathdup.c b/usr/src/cmd/svr4pkg/libinst/pathdup.c
new file mode 100644
index 0000000000..950d8bc5ed
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/pathdup.c
@@ -0,0 +1,158 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+
+/*
+ * using factor of eight limits maximum
+ * memory fragmentation to 12.5%
+ */
+#define MEMSIZ PATH_MAX*8
+#define NULL 0
+
+struct dup {
+ char mem[MEMSIZ];
+ struct dup *next;
+};
+
+static struct dup *head, *tail, *new;
+
+static int size, initialized;
+static void pathinit();
+static void growstore();
+
+/*
+ * These functions allocate space for all the path names required
+ * in the packaging code. They are all allocated here so as to reduce
+ * memory fragmentation.
+ */
+
+/* Initialize storage area. */
+static void
+pathinit()
+{
+ if (head == NULL)
+ size = (-1);
+ else {
+ /* free all memory used except initial structure */
+ tail = head->next;
+ while (tail) {
+ new = tail->next;
+ free(tail);
+ tail = new;
+ }
+ tail = head;
+ size = MEMSIZ;
+ }
+
+ initialized = 1;
+}
+
+/* Allocate additional space for storage area. */
+static void
+growstore()
+{
+ /* need more memory */
+ new = calloc(1, sizeof (struct dup));
+ if (new == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ if (head == NULL)
+ head = new;
+ else
+ tail->next = new;
+ tail = new;
+ size = MEMSIZ;
+}
+
+/* Allocate and return a pointer. If n == 0, initialize. */
+char *
+pathalloc(int n)
+{
+ char *pt;
+
+ if (n <= 0) {
+ pathinit();
+ pt = NULL;
+ } else {
+ if (!initialized)
+ pathinit();
+
+ n++; /* Account for terminating null. */
+
+ if (size < n)
+ growstore();
+
+ pt = &tail->mem[MEMSIZ-size];
+ size -= n;
+ }
+
+ return (pt);
+}
+
+/* Allocate and insert a pathname returning a pointer to the new string. */
+char *
+pathdup(char *s)
+{
+ char *pt;
+ int n;
+
+ if (s == NULL) {
+ pathinit();
+ pt = NULL;
+ } else {
+ if (!initialized)
+ pathinit();
+
+ n = strlen(s) + 1; /* string + null terminator */
+
+ if (size < n)
+ growstore();
+
+ pt = &tail->mem[MEMSIZ-size];
+ size -= n;
+
+ (void) strcpy(pt, s);
+ }
+
+ return (pt);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/pkgdbmerg.c b/usr/src/cmd/svr4pkg/libinst/pkgdbmerg.c
new file mode 100644
index 0000000000..17389ab72b
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/pkgdbmerg.c
@@ -0,0 +1,1259 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pkgstrct.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkginfo.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <libinst.h>
+#include <messages.h>
+
+/* merg() return codes */
+#define MRG_SAME 0
+#define MRG_DIFFERENT 1
+#define MRG_REPLACE 2
+
+/* typechg() return codes */
+#define TYPE_OK 0
+#define TYPE_WARNING 1
+#define TYPE_IGNORED 2
+#define TYPE_REPLACE 3
+#define TYPE_FATAL 4
+
+/* message pool */
+#define ERR_OUTPUT "unable to update package database"
+#define ERR_PINFO "missing pinfo structure for <%s>"
+#define INFO_PROCESS " %2ld%% of information processed; continuing ..."
+
+#define WRN_NOTFILE "WARNING: %s <no longer a regular file>"
+#define WRN_NOTSYMLN "WARNING: %s <no longer a symbolic link>"
+#define WRN_NOTLINK "WARNING: %s <no longer a linked file>"
+#define WRN_NOTDIR "WARNING: %s <no longer a directory>"
+#define WRN_NOTCHAR "WARNING: %s <no longer a character special device>"
+#define WRN_NOTBLOCK "WARNING: %s <no longer a block special device>"
+#define WRN_NOTPIPE "WARNING: %s <no longer a named pipe>"
+#define WRN_TOEXCL "WARNING: cannot convert %s to an exclusive directory."
+#define WRN_ODDVERIFY "WARNING: quick verify disabled for class %s."
+
+#define MSG_TYPIGN "Object type change ignored."
+#define MSG_TYPE_ERR "Package attempts fatal object type change."
+
+extern char *pkginst;
+extern int nosetuid, nocnflct, otherstoo;
+
+/* pkgobjmap.c */
+extern int cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent);
+
+/* setlist.c */
+extern void cl_def_dverify(int idx);
+
+char dbst = '\0'; /* usually set by installf() or removef() */
+
+int files_installed(void); /* return number of files installed. */
+
+static int errflg = 0;
+static int eptnum;
+static VFP_T *fpvfp = {(VFP_T *)NULL};
+static long sizetot;
+static int seconds;
+static int installed; /* # of files, already properly installed. */
+static struct pinfo *pkgpinfo = (struct pinfo *)0;
+
+static int is_setuid(struct cfent *ent);
+static int is_setgid(struct cfent *ent);
+static int merg(struct cfextra *el_ent, struct cfent *cf_ent);
+static int do_like_ent(VFP_T *vfpo, struct cfextra *el_ent,
+ struct cfent *cf_ent, int ctrl);
+static int do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl);
+static int typechg(struct cfent *el_ent, struct cfent *cf_ent,
+ struct mergstat *mstat);
+
+static void set_change(struct cfextra *el_ent);
+static void chgclass(struct cfent *cf_ent, struct pinfo *pinfo);
+static void output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo);
+
+/* ARGSUNUSED */
+void
+notice(int n)
+{
+#ifdef lint
+ int i = n;
+ n = i;
+#endif /* lint */
+ (void) signal(SIGALRM, SIG_IGN);
+ if (sizetot != 0) {
+ echo(gettext(INFO_PROCESS),
+ vfpGetBytesRemaining(fpvfp) * 100L / sizetot);
+ }
+ (void) signal(SIGALRM, notice);
+ (void) alarm(seconds);
+}
+
+/* ARGSUSED */
+
+/*
+ * This scans the extlist (pkgmap) and the package database to the end,
+ * copying out the merged contents to the file at tmpfp. It updates the mergstat
+ * structures and deals with administrative defaults regarding setuid and
+ * conflict.
+ *
+ * Since both the extlist and the package database entries are in numerical
+ * order, they both scan unidirectionally. If the entry in the extlist is
+ * found in the package database (by pathname) then do_like_ent() is called.
+ * If the extlist entry is not found in the package database then
+ * do_new_ent() is called. srchcfile() is responsible for copying out
+ * non-matching package database entries. At package database EOF, the
+ * eocontents flag is set and the rest of the extlist are assumed to be new
+ * entries. At the end of the extlist, the eoextlist flag is set and the
+ * remaining package database ends up copied out by srchcfile().
+ */
+
+int
+pkgdbmerg(VFP_T *mapvfp, VFP_T *tmpvfp, struct cfextra **extlist, int notify)
+{
+ static struct cfent cf_ent; /* scratch area */
+ struct cfextra *el_ent; /* extlist entry under review */
+ int eocontents = 0;
+ int eoextlist = 0;
+ int n;
+ int changed;
+ int assume_ok = 0;
+
+ cf_ent.pinfo = (NULL);
+ errflg = 0;
+ eptnum = 0;
+ installed = changed = 0;
+
+ fpvfp = mapvfp; /* for notice function ...arg! */
+
+ if (notify) {
+ seconds = notify;
+ (void) signal(SIGALRM, notice);
+ (void) alarm(seconds);
+ }
+
+ (void) sighold(SIGALRM);
+
+ sizetot = (((ptrdiff_t)(mapvfp->_vfpEnd)) -
+ ((ptrdiff_t)(mapvfp->_vfpStart)));
+ vfpRewind(mapvfp);
+ vfpRewind(tmpvfp);
+
+ (void) sigrelse(SIGALRM);
+
+ do {
+ (void) sighold(SIGALRM);
+
+ /*
+ * If there's an entry in the extlist at this position,
+ * process that entry.
+ */
+ if (!eoextlist && (el_ent = extlist[eptnum])) {
+
+ /* Metafiles don't get merged. */
+ if ((el_ent->cf_ent.ftype == 'i') ||
+ (el_ent->cf_ent.ftype == 'n')) {
+ continue;
+ }
+
+ /*
+ * Copy cfextra structure for duplicated paths.
+ * This is not just an optimization, it is
+ * necessary for correct operation of algorithm.
+ */
+ if ((eptnum > 0) && (strncmp(el_ent->cf_ent.path,
+ extlist[eptnum-1]->cf_ent.path, PATH_MAX) == 0)) {
+ memcpy(extlist[eptnum], extlist[eptnum-1],
+ sizeof (struct cfextra));
+ continue;
+ }
+
+ /*
+ * Normally dbst comes to us from installf() or
+ * removef() in order to specify their special
+ * database status codes. They cannot implement a
+ * quick verify (it just doesn't make sense). For
+ * that reason, we can test to see if we already have
+ * a special database status. If we don't (it's from
+ * pkgadd) then we can test to see if this is calling
+ * for a quick verify wherein we assume the install
+ * will work and fix it if it doesn't. In that case
+ * we set our own dbst to be ENTRY_OK.
+ */
+ if (dbst == '\0') {
+ if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
+ QKVERIFY) {
+ assume_ok = 1;
+ }
+ } else {
+ /*
+ * If we DO end up with an installf/quick
+ * verify combination, we fix that by simply
+ * denying the quick verify for this class.
+ * This forces everything to come out alright
+ * by forcing the standard assumptions as
+ * regards package database for the rest of
+ * the load.
+ */
+ if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
+ QKVERIFY) {
+ logerr(gettext(WRN_ODDVERIFY),
+ cl_nam(
+ el_ent->cf_ent.pkg_class_idx));
+ /*
+ * Set destination verification to
+ * default.
+ */
+ cl_def_dverify(
+ el_ent->cf_ent.pkg_class_idx);
+ }
+ }
+
+ /*
+ * Comply with administrative requirements regarding
+ * setuid/setgid processes.
+ */
+ if (is_setuid(&(el_ent->cf_ent))) {
+ el_ent->mstat.setuid = 1;
+ }
+ if (is_setgid(&(el_ent->cf_ent))) {
+ el_ent->mstat.setgid = 1;
+ }
+
+ /*
+ * If setuid/setgid processes are not allowed, reset
+ * those bits.
+ */
+ if (nosetuid && (el_ent->mstat.setgid ||
+ el_ent->mstat.setuid)) {
+ el_ent->cf_ent.ainfo.mode &=
+ ~(S_ISUID | S_ISGID);
+ }
+ } else {
+ eoextlist = 1; /* end of extlist[] */
+ }
+
+ /*
+ * If we're not at the end of the package database, get the
+ * next entry for comparison.
+ */
+ if (!eocontents) {
+
+ /* Search package database for this entry. */
+ n = srchcfile(&cf_ent, el_ent ?
+ el_ent->cf_ent.path : NULL,
+ mapvfp, tmpvfp);
+
+ /*
+ * If there was an error, note it and return an error
+ * flag.
+ */
+ if (n < 0) {
+ char *errstr = getErrstr();
+ logerr(gettext(
+ "bad entry read from contents file"));
+ logerr(gettext("- pathname: %s"),
+ (cf_ent.path && *cf_ent.path) ?
+ cf_ent.path : "Unknown");
+ logerr(gettext("- problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ return (-1);
+ /*
+ * If there was a match, then merge them into a
+ * single entry.
+ */
+ } else if (n == 1) {
+ /*
+ * If this package is overwriting a setuid or
+ * setgid process, set the status bits so we
+ * can inform the administrator.
+ */
+ if (is_setuid(&cf_ent)) {
+ el_ent->mstat.osetuid = 1;
+ }
+
+ if (is_setgid(&cf_ent)) {
+ el_ent->mstat.osetgid = 1;
+ }
+ /*
+ * Detect if a symlink has changed to directory
+ * If so mark all the files/dir supposed to be
+ * iniside this dir, so that they are not miss
+ * understood by do_new_ent later as already
+ * installed.
+ */
+ if ((!eoextlist) && (cf_ent.ftype == 's') &&
+ (el_ent->cf_ent.ftype == 'd')) {
+ int i;
+ int plen = strlen(el_ent->cf_ent.path);
+ for (i = eptnum + 1; extlist[i]; i++) {
+ if (strncmp(el_ent->cf_ent.path,
+ extlist[i]->cf_ent.path,
+ plen) != 0)
+ break;
+ extlist[i]->mstat.parentsyml2dir
+ = 1;
+ }
+ }
+
+ if (do_like_ent(tmpvfp, el_ent, &cf_ent,
+ assume_ok)) {
+ changed++;
+ }
+
+ /*
+ * If the alphabetical position in the package
+ * database is unfilled, then this will be a new
+ * entry. If n == 0, then we're also at the end of
+ * the contents file.
+ */
+ } else {
+ if (n == 0) {
+ eocontents++;
+ }
+
+ /*
+ * If there is an extlist entry in the
+ * hopper, insert it at the end of the
+ * package database.
+ */
+ if (!eoextlist) {
+ if (do_new_ent(tmpvfp, el_ent,
+ assume_ok)) {
+ changed++;
+ }
+ }
+ }
+ /*
+ * We have passed the last entry in the package database,
+ * tagging these extlist entries onto the end.
+ */
+ } else if (!eoextlist) {
+ if (do_new_ent(tmpvfp, el_ent, assume_ok)) {
+ changed++;
+ }
+ }
+ /* Else, we'll drop out of the loop. */
+
+ (void) sigrelse(SIGALRM);
+ } while (eptnum++, (!eocontents || !eoextlist));
+
+ if (notify) {
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_IGN);
+ }
+
+ return (errflg ? -1 : changed);
+}
+
+/*
+ * Merge a new entry with an installed package object of the same name and
+ * insert that object into the package database. Obey administrative defaults
+ * as regards conflicting files.
+ */
+
+static int
+do_like_ent(VFP_T *vfpo, struct cfextra *el_ent, struct cfent *cf_ent, int ctrl)
+{
+ int stflag, ignore, changed, mrg_result;
+
+ ignore = changed = 0;
+
+ /*
+ * Construct the record defining the current package. If there are
+ * other packages involved, this will be appended to the existing
+ * list. If this is an update of the same package, it will get merged
+ * with the existing record. If this is a preloaded record (like from
+ * a dryrun file), it will keep it's current pinfo pointer and will
+ * pass it on to the record from the contents file - because on the
+ * final continuation, the contents file will be wrong.
+ */
+ if (el_ent->mstat.preloaded) {
+ struct pinfo *pkginfo;
+
+ /* Contents file is not to be trusted for this list. */
+ pkginfo = cf_ent->pinfo;
+
+ /* Free the potentially bogus list. */
+ while (pkginfo) {
+ struct pinfo *next;
+ next = pkginfo->next;
+ free(pkginfo);
+ pkginfo = next;
+ }
+
+ cf_ent->pinfo = el_ent->cf_ent.pinfo;
+ }
+
+ pkgpinfo = eptstat(cf_ent, pkginst, DUP_ENTRY);
+
+ stflag = pkgpinfo->status;
+
+ if (otherstoo)
+ el_ent->mstat.shared = 1;
+
+ /* If it's marked for erasure, make it official */
+ if (el_ent->cf_ent.ftype == RM_RDY) {
+ if (!errflg) {
+ pkgpinfo = eptstat(cf_ent, pkginst, RM_RDY);
+
+ /*
+ * Get copy of status character in case the object is
+ * "shared" by a server, in which case we need to
+ * maintain the shared status after the entry is
+ * written to the package database with RM_RDY
+ * status. This is needed to support the `removef'
+ * command.
+ */
+ stflag = pkgpinfo->status;
+ pkgpinfo->status = RM_RDY;
+
+ if (putcvfpfile(cf_ent, vfpo)) {
+ progerr(gettext(ERR_OUTPUT));
+ quit(99);
+ }
+
+ /*
+ * If object is provided by a server, allocate an
+ * info block and set the status to indicate this.
+ * This is needed to support the `removef' command.
+ */
+ if (stflag == SERVED_FILE) {
+ el_ent->cf_ent.pinfo =
+ (struct pinfo *)calloc(1,
+ sizeof (struct pinfo));
+ el_ent->cf_ent.pinfo->next = NULL;
+ el_ent->cf_ent.pinfo->status = SERVED_FILE;
+ }
+ }
+ return (1);
+ }
+
+ /*
+ * If there is no package associated with it, there's something
+ * very wrong.
+ */
+ if (!pkgpinfo) {
+ progerr(gettext(ERR_PINFO), cf_ent->path);
+ quit(99);
+ }
+
+ /*
+ * Do not allow installation if nocnflct is set and other packages
+ * reference this pathname. The cp_cfent() function below writes the
+ * information from the installed file over the new entry, so the
+ * package database will be unchanged.
+ *
+ * By the way, ftype "e" is often shared and that's OK, so ftype
+ * "e" doesn't count here.
+ */
+ if ((nocnflct && el_ent->mstat.shared && el_ent->cf_ent.ftype != 'e')) {
+ /*
+ * First set the attrchg and contchg entries for proper
+ * messaging in the install phase.
+ */
+ set_change(el_ent);
+
+ /*
+ * Now overwrite the new entry with the entry for the
+ * currently installed object.
+ */
+ if (cp_cfent(cf_ent, el_ent) == 0)
+ quit(99);
+
+ ignore++;
+ } else {
+ mrg_result = merg(el_ent, cf_ent);
+
+ switch (mrg_result) {
+ case MRG_SAME:
+ break;
+
+ case MRG_DIFFERENT:
+ changed++;
+ break;
+
+ case MRG_REPLACE:
+ /*
+ * We'll pick one or the other later. For now, cf_ent
+ * will have the fault value and el_ent will retain
+ * the other value. This is the only state that allows
+ * the database and the pkgmap to differ.
+ */
+
+ el_ent->mstat.contchg = 1; /* subject to change */
+ ignore++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* el_ent structure now contains updated entry */
+ if (!el_ent->mstat.contchg && !ignore) {
+ /*
+ * We know the DB entry matches the pkgmap, so now we need to
+ * see if the actual object matches the pkgmap.
+ */
+ set_change(el_ent);
+ }
+
+ if (!errflg) {
+ if (ctrl == 1) { /* quick verify assumes OK */
+ /*
+ * The pkgpinfo entry is already correctly
+ * constructed. Look into dropping this soon.
+ */
+ pkgpinfo = eptstat(&(el_ent->cf_ent), pkginst,
+ ENTRY_OK);
+
+ if (stflag != DUP_ENTRY) {
+ changed++;
+ }
+
+ /*
+ * We could trust the prior pkginfo entry, but things
+ * could have changed and we need to update the
+ * fs_tab[] anyway. We check for a server object
+ * here.
+ */
+ if (is_served(el_ent->server_path,
+ &(el_ent->fsys_value)))
+ pkgpinfo->status = SERVED_FILE;
+ } else {
+ if (!ignore && el_ent->mstat.contchg) {
+ pkgpinfo =
+ eptstat(&(el_ent->cf_ent), pkginst,
+ (dbst ? dbst : CONFIRM_CONT));
+ } else if (!ignore && el_ent->mstat.attrchg) {
+ pkgpinfo =
+ eptstat(&(el_ent->cf_ent), pkginst,
+ (dbst ? dbst : CONFIRM_ATTR));
+ } else if (!ignore && el_ent->mstat.shared) {
+ pkgpinfo =
+ eptstat(&(el_ent->cf_ent), pkginst,
+ dbst);
+ changed++;
+ } else if (stflag != DUP_ENTRY) {
+ pkgpinfo = eptstat(&(el_ent->cf_ent),
+ pkginst, '\0');
+ if (stflag != ENTRY_OK) {
+ changed++;
+ }
+ }
+ }
+
+ if (mrg_result == MRG_REPLACE) {
+ /*
+ * Put the original package database entry back into
+ * the package database for now.
+ */
+ output(vfpo, cf_ent, pkgpinfo);
+ } else {
+ /* Put the merged entry into the package database. */
+ output(vfpo, &(el_ent->cf_ent), pkgpinfo);
+ }
+ }
+
+ if (pkgpinfo->aclass[0] != '\0') {
+ (void) strcpy(el_ent->cf_ent.pkg_class, pkgpinfo->aclass);
+ }
+
+ /*
+ * If a sym link entry exists in the contents file and
+ * and the destination of the link does not exist on the the system
+ * then the contents file needs to be updated appropriately so a
+ * subsequent invocation of "installf -f" will create the destination.
+ */
+ if (el_ent->mstat.contchg && pkgpinfo->status == INST_RDY) {
+ changed++;
+ }
+
+ if (!(el_ent->mstat.preloaded))
+ el_ent->cf_ent.pinfo = NULL;
+
+ /*
+ * If no change during the merg and we don't have a case where types
+ * were different in odd ways, count this as installed.
+ */
+ if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg &&
+ !el_ent->mstat.replace)
+ installed++;
+ return (changed);
+}
+
+/* Insert an entirely new entry into the package database. */
+static int
+do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl)
+{
+ struct pinfo *pinfo;
+ char *tp;
+ int changed = 0;
+
+ if (el_ent->cf_ent.ftype == RM_RDY) {
+ return (0);
+ }
+
+ tp = el_ent->server_path;
+ /*
+ * Check the file/dir existence only if any of the parent directory
+ * of the file/dir has not changed from symbolic link to directory.
+ * At this time we are only doing a dry run, the symlink is not yet
+ * replaced, so if this is done directly then access will result in
+ * incorrect information in case a file with the same attr and cont
+ * exists in the link target.
+ */
+ if ((!el_ent->mstat.parentsyml2dir) && (access(tp, F_OK) == 0)) {
+ /*
+ * Path exists, and although its not referenced by any
+ * package we make it look like it is so it appears as a
+ * conflicting file in case the user doesn't want it
+ * installed. We set the rogue flag to distinguish this from
+ * package object conflicts if the administrator is queried
+ * about this later. Note that noconflict means NO conflict
+ * at the file level. Even rogue files count.
+ */
+ el_ent->mstat.shared = 1;
+ el_ent->mstat.rogue = 1;
+ set_change(el_ent);
+ } else {
+ /* since path doesn't exist, we're changing everything */
+ el_ent->mstat.rogue = 0;
+ el_ent->mstat.contchg = 1;
+ el_ent->mstat.attrchg = 1;
+ }
+
+ if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
+ if (el_ent->cf_ent.ftype == 'd') {
+ el_ent->cf_ent.ainfo.mode = DEFAULT_MODE;
+ } else {
+ el_ent->cf_ent.ainfo.mode = DEFAULT_MODE_FILE;
+ }
+ logerr(WRN_SET_DEF_MODE, el_ent->cf_ent.path,
+ (int)el_ent->cf_ent.ainfo.mode);
+ }
+
+ if (strcmp(el_ent->cf_ent.ainfo.owner, DB_UNDEFINED_ENTRY) == 0)
+ (void) strcpy(el_ent->cf_ent.ainfo.owner,
+ DEFAULT_OWNER);
+ if (strcmp(el_ent->cf_ent.ainfo.group, DB_UNDEFINED_ENTRY) == 0)
+ (void) strcpy(el_ent->cf_ent.ainfo.group,
+ DEFAULT_GROUP);
+
+ /*
+ * Do not allow installation if nocnflct is set and this pathname is
+ * already in place. Since this entry is new (not associated with a
+ * package), we don't issue anything to the database we're building.
+ */
+ if (nocnflct && el_ent->mstat.shared) {
+ return (0);
+ }
+
+ if (!errflg) {
+ if (el_ent->mstat.preloaded) {
+ /* Add this package to the already established list. */
+ pinfo = eptstat(&(el_ent->cf_ent), pkginst, DUP_ENTRY);
+ } else {
+ el_ent->cf_ent.npkgs = 1;
+ pinfo = (struct pinfo *)calloc(1,
+ sizeof (struct pinfo));
+ if (!pinfo) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ el_ent->cf_ent.pinfo = pinfo;
+ (void) strcpy(pinfo->pkg, pkginst);
+ }
+
+ if (ctrl == 1) { /* quick verify assumes OK */
+ pinfo->status = dbst ? dbst : ENTRY_OK;
+ /*
+ * The entry won't be verified, but the entry in the
+ * database isn't necessarily ENTRY_OK. If this is
+ * coming from a server, we need to note that
+ * instead.
+ */
+ if (is_served(el_ent->server_path,
+ &(el_ent->fsys_value)))
+ pinfo->status = SERVED_FILE;
+ } else {
+ pinfo->status = dbst ? dbst : CONFIRM_CONT;
+ }
+
+ output(vfpo, &(el_ent->cf_ent), pinfo);
+ changed++;
+
+ free(pinfo);
+ el_ent->cf_ent.pinfo = NULL;
+ }
+ if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg) {
+ installed++;
+ }
+
+ return (changed);
+}
+
+int
+files_installed(void)
+{
+ return (installed);
+}
+
+/*
+ * This function determines if there is a difference between the file on
+ * the disk and the file to be laid down. It set's mstat flags attrchg
+ * and contchg accordingly.
+ */
+static void
+set_change(struct cfextra *el_ent)
+{
+ int n;
+ char *tp;
+
+ tp = el_ent->server_path;
+ if ((el_ent->cf_ent.ftype == 'f') || (el_ent->cf_ent.ftype == 'e') ||
+ (el_ent->cf_ent.ftype == 'v')) {
+ if (cverify(0, &(el_ent->cf_ent.ftype), tp,
+ &(el_ent->cf_ent.cinfo), 1)) {
+ el_ent->mstat.contchg = 1;
+ } else if (!el_ent->mstat.contchg && !el_ent->mstat.attrchg) {
+ if (averify(0, &(el_ent->cf_ent.ftype), tp,
+ &(el_ent->cf_ent.ainfo)))
+ el_ent->mstat.attrchg = 1;
+ }
+ } else if (!el_ent->mstat.attrchg &&
+ ((el_ent->cf_ent.ftype == 'd') ||
+ (el_ent->cf_ent.ftype == 'x') ||
+ (el_ent->cf_ent.ftype == 'c') ||
+ (el_ent->cf_ent.ftype == 'b') ||
+ (el_ent->cf_ent.ftype == 'p'))) {
+ n = averify(0, &(el_ent->cf_ent.ftype), tp,
+ &(el_ent->cf_ent.ainfo));
+ if (n == VE_ATTR)
+ el_ent->mstat.attrchg = 1;
+ else if (n && (n != VE_EXIST)) {
+ el_ent->mstat.contchg = 1;
+ }
+ } else if (!el_ent->mstat.attrchg &&
+ ((el_ent->cf_ent.ftype == 's') ||
+ (el_ent->cf_ent.ftype == 'l'))) {
+ n = averify(0, &(el_ent->cf_ent.ftype), tp,
+ &(el_ent->cf_ent.ainfo));
+ if (n == VE_ATTR)
+ el_ent->mstat.attrchg = 1;
+ else if (n && (n == VE_EXIST)) {
+ el_ent->mstat.contchg = 1;
+ }
+ }
+}
+
+static int
+is_setuid(struct cfent *ent)
+{
+ return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
+ (ent->ftype == 'e')) &&
+ (ent->ainfo.mode != BADMODE) &&
+ (ent->ainfo.mode != WILDCARD) &&
+ (ent->ainfo.mode & S_ISUID));
+}
+
+static int
+is_setgid(struct cfent *ent)
+{
+ return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
+ (ent->ftype == 'e')) && (ent->ainfo.mode != BADMODE) &&
+ (ent->ainfo.mode != WILDCARD) &&
+ (ent->ainfo.mode & S_ISGID) &&
+ (ent->ainfo.mode & (S_IEXEC|S_IXUSR|S_IXOTH)));
+}
+
+char *types[] = {
+ "fev", /* type 1, regular files */
+ "s", /* type 2, symbolic links */
+ "l", /* type 3, linked files */
+ "dx", /* type 4, directories */
+ "c", /* type 5, character special devices */
+ "b", /* type 6, block special devices */
+ "p", /* type 7, named pipes */
+ NULL
+};
+
+/*
+ * This determines if the ftype of the file on the disk and the file to be
+ * laid down are close enough. If they aren't, this either returns an error
+ * or displays a warning. This returns :
+ * TYPE_OK they're identical or close enough
+ * TYPE_WARNING they're pretty close (probably no problem)
+ * TYPE_IGNORED the type change was not allowed
+ * TYPE_REPLACE to be reviewed later - in endofclass() maybe
+ * TYPE_FATAL something awful happened
+ */
+static int
+typechg(struct cfent *el_ent, struct cfent *cf_ent, struct mergstat *mstat)
+{
+ int i, etype, itype, retcode;
+
+ /* If they are identical, return OK */
+ if (cf_ent->ftype == el_ent->ftype)
+ return (TYPE_OK);
+
+ /*
+ * If package database entry is ambiguous, set it to the new entity's
+ * ftype
+ */
+ if (cf_ent->ftype == BADFTYPE) {
+ cf_ent->ftype = el_ent->ftype;
+ return (TYPE_OK); /* do nothing; not really different */
+ }
+
+ /* If the new entity is ambiguous, wait for the verify */
+ if (el_ent->ftype == BADFTYPE)
+ return (TYPE_OK);
+
+ /*
+ * If we're trying to convert an existing regular directory to an
+ * exclusive directory, this is very dangerous. We will continue, but
+ * we will deny the conversion.
+ */
+ if (el_ent->ftype == 'x' && cf_ent->ftype == 'd') {
+ logerr(gettext(WRN_TOEXCL), el_ent->path);
+ return (TYPE_IGNORED);
+ }
+
+ etype = itype = 0;
+
+ /* Set etype to that of the new entity */
+ for (i = 0; types[i]; ++i) {
+ if (strchr(types[i], el_ent->ftype)) {
+ etype = i+1;
+ break;
+ }
+ }
+
+ /* Set itype to that in the package database. */
+ for (i = 0; types[i]; ++i) {
+ if (strchr(types[i], cf_ent->ftype)) {
+ itype = i+1;
+ break;
+ }
+ }
+
+ if (itype == etype) {
+ /* same basic object type */
+ return (TYPE_OK);
+ }
+
+ retcode = TYPE_WARNING;
+
+ /*
+ * If a simple object (like a file) is overwriting a directory, mark
+ * it for full inspection during installation.
+ */
+ if (etype != 4 && itype == 4) {
+ mstat->dir2nondir = 1;
+ retcode = TYPE_REPLACE;
+ }
+
+ /* allow change, but warn user of possible problems */
+ switch (itype) {
+ case 1:
+ logerr(gettext(WRN_NOTFILE), el_ent->path);
+ break;
+
+ case 2:
+ logerr(gettext(WRN_NOTSYMLN), el_ent->path);
+ break;
+
+ case 3:
+ logerr(gettext(WRN_NOTLINK), el_ent->path);
+ break;
+
+ case 4:
+ logerr(gettext(WRN_NOTDIR), el_ent->path);
+ break;
+
+ case 5:
+ logerr(gettext(WRN_NOTCHAR), el_ent->path);
+ break;
+
+ case 6:
+ logerr(gettext(WRN_NOTBLOCK), el_ent->path);
+ break;
+
+ case 7:
+ logerr(gettext(WRN_NOTPIPE), el_ent->path);
+ break;
+
+ default:
+ break;
+ }
+ return (retcode);
+}
+
+/*
+ * This function takes el_ent (the entry from the pkgmap) and cf_ent (the
+ * entry from the package database) and merge them into el_ent. The rules
+ * are still being figured out, but the comments should make the approach
+ * pretty clear.
+ *
+ * RETURN CODES:
+ * MRG_DIFFERENT The two entries are different and el_ent now contains
+ * the intended new entry to be installed.
+ * MRG_SAME The two entries were identical and the old database
+ * entry will be replaced unchanged.
+ * MRG_REPLACE One or the other entry will be used but the decision
+ * has to be made at install time.
+ */
+static int
+merg(struct cfextra *el_ent, struct cfent *cf_ent)
+{
+ int n, changed = 0;
+
+ /*
+ * We need to change the original entry to make it look like the new
+ * entry (the eptstat() routine has already added appropriate package
+ * information, but not about 'aclass' which may represent a change
+ * in class from the previous installation.
+ *
+ * NOTE: elent->cf_ent.pinfo (the list of associated packages) is NULL
+ * upon entry to this function.
+ */
+
+ el_ent->cf_ent.pinfo = cf_ent->pinfo;
+
+ if (dbst == INST_RDY && el_ent->cf_ent.ftype == '?') {
+ el_ent->cf_ent.ftype = cf_ent->ftype;
+ }
+
+ /*
+ * Evaluate the ftype change. Usually the ftype won't change. If it
+ * does it may be easy (s -> f), not allowed (d -> x), so complex we
+ * can't figure it 'til later (d -> s) or fatal (a hook for later).
+ */
+ if (cf_ent->ftype != el_ent->cf_ent.ftype) {
+ n = typechg(&(el_ent->cf_ent), cf_ent, &(el_ent->mstat));
+
+ switch (n) {
+ case TYPE_OK:
+ break;
+
+ /* This is an allowable change. */
+ case TYPE_WARNING:
+ el_ent->mstat.contchg = 1;
+ break;
+
+ /* Not allowed, but leaving it as is is OK. */
+ case TYPE_IGNORED:
+ logerr(gettext(MSG_TYPIGN));
+ if (cp_cfent(cf_ent, el_ent) == 0)
+ quit(99);
+ return (MRG_SAME);
+
+ /* Future analysis will reveal if this is OK. */
+ case TYPE_REPLACE:
+ el_ent->mstat.replace = 1;
+ return (MRG_REPLACE);
+
+ /* Kill it before it does any damage. */
+ case TYPE_FATAL:
+ logerr(gettext(MSG_TYPE_ERR));
+ quit(99);
+
+ default:
+ break;
+ }
+
+ changed++;
+ }
+
+ /* Evaluate and merge the class. */
+ if (strcmp(cf_ent->pkg_class, el_ent->cf_ent.pkg_class)) {
+ /*
+ * we always allow a class change as long as we have
+ * consistent ftypes, which at this point we must
+ */
+ changed++;
+ if (strcmp(cf_ent->pkg_class, "?")) {
+ (void) strcpy(pkgpinfo->aclass,
+ el_ent->cf_ent.pkg_class);
+ (void) strcpy(el_ent->cf_ent.pkg_class,
+ cf_ent->pkg_class);
+ chgclass(&(el_ent->cf_ent), pkgpinfo);
+ }
+ }
+
+ /*
+ * Evaluate and merge based upon the ftype of the intended package
+ * database entry.
+ */
+ if (((el_ent->cf_ent.ftype == 's') || (el_ent->cf_ent.ftype == 'l'))) {
+
+ /* If both have link sources, then they need to be merged. */
+ if (cf_ent->ainfo.local && el_ent->cf_ent.ainfo.local) {
+ /*
+ * If both sources are identical, the merge is
+ * already done.
+ */
+ if (strcmp(cf_ent->ainfo.local,
+ el_ent->cf_ent.ainfo.local) != NULL) {
+ changed++;
+
+ /*
+ * Otherwise, if the pkgmap entry is
+ * ambiguous, it will inherit the database
+ * entry.
+ */
+ if (strcmp(el_ent->cf_ent.ainfo.local,
+ "?") == NULL) {
+ (void) strlcpy(
+ el_ent->cf_ent.ainfo.local,
+ cf_ent->ainfo.local,
+ PATH_MAX);
+ } else {
+ el_ent->mstat.contchg = 1;
+ }
+ }
+ }
+ return (changed ? MRG_DIFFERENT : MRG_SAME);
+
+ } else if (el_ent->cf_ent.ftype == 'e') {
+
+ /*
+ * The contents of edittable files are assumed to be changing
+ * since some class action script will be doing the work and
+ * we have no way of evaluating what it will actually do.
+ */
+ el_ent->mstat.contchg = 1;
+ changed++;
+ } else if (((el_ent->cf_ent.ftype == 'f') ||
+ (el_ent->cf_ent.ftype == 'v'))) {
+ /*
+ * For regular files, Look at content information; a BADCONT
+ * in any el_ent field indicates the contents are unknown --
+ * since cf_ent is guaranteed to have a valid entry here (bad
+ * assumption?) this function will recognize this as a
+ * change. The ambiguous el_ent values will be evaluated and
+ * set later.
+ */
+
+ /*
+ * for type f/v files, if the file is in an area that is
+ * inherited from the global zone, that area is read only
+ * and the object cannot be changed - ignore any settings
+ * in the current package database that may be present for
+ * any existing object because they are irrelevant - since
+ * the object is in a read-only area shared from the global
+ * zone, accept that file's actual attributes as being correct.
+ */
+
+ if (z_path_is_inherited(el_ent->cf_ent.path,
+ el_ent->cf_ent.ftype, get_inst_root()) == B_TRUE) {
+ echoDebug(DBG_PKGDBMRG_INHERITED, el_ent->cf_ent.path);
+ } else if (cf_ent->cinfo.size != el_ent->cf_ent.cinfo.size) {
+ changed++;
+ el_ent->mstat.contchg = 1;
+ } else if (cf_ent->cinfo.modtime !=
+ el_ent->cf_ent.cinfo.modtime) {
+ changed++;
+ el_ent->mstat.contchg = 1;
+ } else if (cf_ent->cinfo.cksum != el_ent->cf_ent.cinfo.cksum) {
+ changed++;
+ el_ent->mstat.contchg = 1;
+ }
+ } else if (((el_ent->cf_ent.ftype == 'c') ||
+ (el_ent->cf_ent.ftype == 'b'))) {
+ /*
+ * For devices, if major or minor numbers are identical the
+ * merge is trivial. If the el_ent value is ambiguous (BAD),
+ * the cf_ent value is inherited. Otherwise, the el_ent value
+ * is preserved.
+ */
+ if (cf_ent->ainfo.major != el_ent->cf_ent.ainfo.major) {
+ changed++;
+ if (el_ent->cf_ent.ainfo.major == BADMAJOR) {
+ el_ent->cf_ent.ainfo.major =
+ cf_ent->ainfo.major;
+ } else {
+ el_ent->mstat.contchg = 1;
+ }
+ }
+ if (cf_ent->ainfo.minor != el_ent->cf_ent.ainfo.minor) {
+ changed++;
+ if (el_ent->cf_ent.ainfo.minor == BADMINOR)
+ el_ent->cf_ent.ainfo.minor =
+ cf_ent->ainfo.minor;
+ else
+ el_ent->mstat.contchg = 1;
+ }
+ }
+
+ /*
+ * For mode, owner and group follow the same rules as above - if
+ * ambiguous, inherit, otherwise keep the new one.
+ */
+ if (cf_ent->ainfo.mode != el_ent->cf_ent.ainfo.mode) {
+ changed++; /* attribute info is changing */
+ if (el_ent->cf_ent.ainfo.mode == BADMODE) {
+ el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
+ } else if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
+ /*
+ * If pkgmap has a '?' set for mode, use the mode from
+ * the pkg DB (contents file).
+ */
+ el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
+ el_ent->mstat.attrchg = 0;
+ } else {
+ el_ent->mstat.attrchg = 1;
+ }
+ }
+ if (strcmp(cf_ent->ainfo.owner, el_ent->cf_ent.ainfo.owner) != 0) {
+ changed++; /* attribute info is changing */
+ if (strcmp(el_ent->cf_ent.ainfo.owner, BADOWNER) == 0)
+ (void) strcpy(el_ent->cf_ent.ainfo.owner,
+ cf_ent->ainfo.owner);
+ else
+ el_ent->mstat.attrchg = 1;
+ }
+ if (strcmp(cf_ent->ainfo.group, el_ent->cf_ent.ainfo.group) != 0) {
+ changed++; /* attribute info is changing */
+ if (strcmp(el_ent->cf_ent.ainfo.group, BADGROUP) == 0)
+ (void) strcpy(el_ent->cf_ent.ainfo.group,
+ cf_ent->ainfo.group);
+ else
+ el_ent->mstat.attrchg = 1;
+ }
+ return (changed ? MRG_DIFFERENT : MRG_SAME);
+}
+
+/*
+ * This puts the current entry into the package database in the appropriate
+ * intermediate format for this stage of the installation. This also assures
+ * the correct format for the various package object ftypes, stripping the
+ * link name before storing a regular file and stuff like that.
+ */
+
+static void
+output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo)
+{
+ short svvolno;
+ char *svpt;
+
+ /* output without volume information */
+ svvolno = ent->volno;
+ ent->volno = 0;
+
+ pinfo->editflag = 0;
+ if (((ent->ftype == 's') || (ent->ftype == 'l'))) {
+ if (putcvfpfile(ent, vfpo)) {
+ progerr(gettext(ERR_OUTPUT));
+ quit(99);
+ }
+ } else {
+
+ /* output without local pathname */
+ svpt = ent->ainfo.local;
+ ent->ainfo.local = NULL;
+ if (putcvfpfile(ent, vfpo)) {
+ progerr(gettext(ERR_OUTPUT));
+ quit(99);
+ }
+
+ ent->ainfo.local = svpt;
+ /*
+ * If this entry represents a file which is being edited, we
+ * need to store in memory the fact that it is an edittable
+ * file so that when we audit it after installation we do not
+ * worry about its contents; we do this by resetting the ftype
+ * to 'e' in the memory array which is later used to control
+ * the audit
+ */
+ if (pinfo->editflag)
+ ent->ftype = 'e';
+ }
+ /* restore volume information */
+ ent->volno = svvolno;
+}
+
+static void
+chgclass(struct cfent *cf_ent, struct pinfo *pinfo)
+{
+ struct pinfo *pp;
+ char *oldclass, newclass[CLSSIZ+1];
+ int newcnt, oldcnt;
+
+ /*
+ * we use this routine to minimize the use of the aclass element by
+ * optimizing the use of the cf_ent->pkg_class element
+ */
+
+ (void) strlcpy(newclass, pinfo->aclass, sizeof (newclass));
+ newcnt = 1;
+
+ oldclass = cf_ent->pkg_class;
+ oldcnt = 0;
+
+ /*
+ * count the number of times the newclass will be used and see if it
+ * exceeds the number of times the oldclass is referenced
+ */
+ pp = cf_ent->pinfo;
+ while (pp) {
+ if (pp->aclass[0] != '\0') {
+ if (strcmp(pp->aclass, newclass) == 0)
+ newcnt++;
+ else if (strcmp(pp->aclass, oldclass) == 0)
+ oldcnt++;
+ }
+ pp = pp->next;
+ }
+ if (newcnt > oldcnt) {
+ pp = cf_ent->pinfo;
+ while (pp) {
+ if (pp->aclass[0] == '\0') {
+ (void) strcpy(pp->aclass, oldclass);
+ } else if (strcmp(pp->aclass, newclass) == 0) {
+ pp->aclass[0] = '\0';
+ }
+ pp = pp->next;
+ }
+ (void) strcpy(cf_ent->pkg_class, newclass);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/pkgobjmap.c b/usr/src/cmd/svr4pkg/libinst/pkgobjmap.c
new file mode 100644
index 0000000000..13a041418c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/pkgobjmap.c
@@ -0,0 +1,742 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+
+#define WRN_NOPKGOBJ "WARNING: no package objects found"
+
+#define ERR_MEMORY "memory allocation failure"
+#define ERR_DUPPATH "duplicate pathname <%s>"
+
+/* libpkg/gpkgmap */
+extern int getmapmode(void);
+
+#define EPTMALLOC 512
+
+static struct cfextra **extlist;
+
+int eptnum;
+static int array_preloaded = 0;
+static int errflg;
+static int nparts;
+static int xspace = -1;
+
+void pkgobjinit(void);
+static int pkgobjassign(struct cfent *ept, char **server_local,
+ char **client_local, char **server_path,
+ char **client_path, char **map_path, int mapflag,
+ int nc);
+
+static int ckdup(struct cfent *ept1, struct cfent *ept2);
+static int sortentry(int index);
+static int dup_merg(struct cfextra *ext1, struct cfextra *ext2);
+
+void
+pkgobjinit(void)
+{
+ if (array_preloaded) /* Already done. */
+ return;
+
+ errflg = nparts = eptnum = 0;
+
+ if (xspace != -1) {
+ ar_free(xspace);
+ xspace = -1;
+ }
+
+ /*
+ * initialize dynamic memory used to store
+ * path information which is read in
+ */
+ (void) pathdup((char *)0);
+}
+
+/*
+ * This function assigns appropriate values based upon the pkgmap entry
+ * in the cfent structure.
+ */
+static int
+pkgobjassign(struct cfent *ept, char **server_local, char **client_local,
+ char **server_path, char **client_path, char **map_path, int mapflag,
+ int nc)
+{
+ int path_duped = 0;
+ int local_duped = 0;
+ char source[PATH_MAX+1];
+
+ if (nc >= 0 && ept->ftype != 'i')
+ if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
+ return (1);
+
+ if (ept->volno > nparts)
+ nparts++;
+
+ /*
+ * Generate local (delivered source) paths for files
+ * which need them so that the install routine will know
+ * where to get the file from the package. Note that we
+ * do not resolve path environment variables here since
+ * they won't be resolved in the reloc directory.
+ */
+ if ((mapflag > 1) && strchr("fve", ept->ftype)) {
+ if (ept->ainfo.local == NULL) {
+ source[0] = '~';
+ (void) strlcpy(&source[1], ept->path,
+ sizeof (source)-1);
+ ept->ainfo.local = pathdup(source);
+ *server_local = ept->ainfo.local;
+ *client_local = ept->ainfo.local;
+
+ local_duped = 1;
+ }
+ }
+
+ /*
+ * Evaluate the destination path based upon available
+ * environment, then produce a client-relative and
+ * server-relative canonized path.
+ */
+ if (mapflag && (ept->ftype != 'i')) {
+ mappath(getmapmode(), ept->path); /* evaluate variables */
+ canonize(ept->path); /* Fix path as necessary. */
+
+ (void) eval_path(server_path,
+ client_path,
+ map_path,
+ ept->path);
+ path_duped = 1; /* eval_path dup's it */
+ ept->path = *server_path; /* default */
+ }
+
+ /*
+ * Deal with source for hard and soft links.
+ */
+ if (strchr("sl", ept->ftype)) {
+ if (mapflag) {
+ mappath(getmapmode(), ept->ainfo.local);
+ if (!RELATIVE(ept->ainfo.local)) {
+ canonize(ept->ainfo.local);
+
+ /* check for hard link */
+ if (ept->ftype == 'l') {
+ (void) eval_path(
+ server_local,
+ client_local,
+ NULL,
+ ept->ainfo.local);
+ local_duped = 1;
+
+ /* Default to server. */
+ ept->ainfo.local = *server_local;
+ }
+ }
+ }
+ }
+
+ /*
+ * For the paths (both source and target) that were too mundane to
+ * have been copied into dup space yet, do that.
+ */
+ if (!path_duped) {
+ *server_path = pathdup(ept->path);
+ *client_path = *server_path;
+ ept->path = *server_path;
+
+ path_duped = 1;
+ }
+ if (ept->ainfo.local != NULL)
+ if (!local_duped) {
+ *server_local = pathdup(ept->ainfo.local);
+ ept->ainfo.local = *server_local;
+ *client_local = ept->ainfo.local;
+
+ local_duped = 1;
+ }
+
+ return (0);
+}
+
+/* This initializes the package object array. */
+int
+init_pkgobjspace(void)
+{
+ if (array_preloaded) /* Already done. */
+ return (1);
+
+ if (xspace == -1) {
+ xspace = ar_create(EPTMALLOC, sizeof (struct cfextra),
+ "package object");
+ if (xspace == -1) {
+ progerr(gettext(ERR_MEMORY));
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+int
+seed_pkgobjmap(struct cfextra *ext_entry, char *path, char *local)
+{
+ struct cfextra *ext, **ext_ptr;
+
+ /* offsets for the various path images. */
+ int client_path_os;
+ int server_path_os;
+ int map_path_os;
+ int client_local_os;
+ int server_local_os;
+
+ ext_ptr = (struct cfextra **)ar_next_avail(xspace);
+
+ if (ext_ptr == NULL || *ext_ptr == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ ext = *ext_ptr;
+
+ (void) memcpy(ext, ext_entry, sizeof (struct cfextra));
+
+ /* Figure out all of the offsets. */
+ client_path_os = ((ptrdiff_t)ext->client_path -
+ (ptrdiff_t)ext->cf_ent.path);
+ server_path_os = ((ptrdiff_t)ext->server_path -
+ (ptrdiff_t)ext->cf_ent.path);
+ map_path_os = ((ptrdiff_t)ext->map_path -
+ (ptrdiff_t)ext->cf_ent.path);
+ client_local_os = ((ptrdiff_t)ext->client_local -
+ (ptrdiff_t)ext->cf_ent.ainfo.local);
+ server_local_os = ((ptrdiff_t)ext->server_local -
+ (ptrdiff_t)ext->cf_ent.ainfo.local);
+
+ /* Allocate and store the path name. */
+ ext->cf_ent.path = pathdup(path);
+
+ /* Assign the path substring pointers. */
+ ext->client_path = (ext->cf_ent.path + client_path_os);
+ ext->server_path = (ext->cf_ent.path + server_path_os);
+ ext->map_path = (ext->cf_ent.path + map_path_os);
+
+ /* If there's a local entry, allocate and store it as well. */
+ if (local) {
+ ext->cf_ent.ainfo.local = pathdup(local);
+
+ ext->client_local = (ext->cf_ent.ainfo.local + client_local_os);
+ ext->server_local = (ext->cf_ent.ainfo.local + server_local_os);
+ } else {
+ ext->cf_ent.ainfo.local = NULL;
+ ext->client_local = NULL;
+ ext->server_local = NULL;
+ }
+
+ eptnum++;
+ array_preloaded = 1;
+
+ return (0);
+}
+
+/*
+ * This function reads the pkgmap (or any file similarly formatted) and
+ * returns a pointer to a list of struct cfextra (each of which
+ * contains a struct cfent) representing the contents of that file.
+ */
+
+/* ARGSUSED ir in pkgobjmap */
+struct cfextra **
+pkgobjmap(VFP_T *vfp, int mapflag, char *ir)
+{
+ struct cfextra *ext, **ext_ptr;
+ struct cfent *ept, map_entry;
+ int i;
+ int n;
+ int nc;
+
+ pkgobjinit();
+ if (!init_pkgobjspace())
+ quit(99);
+
+ nc = cl_getn();
+ for (;;) {
+ /* Clear the buffer. */
+ (void) memset(&map_entry, '\000', sizeof (struct cfent));
+
+ /*
+ * Fill in a cfent structure in a very preliminary fashion.
+ * ept->path and ept->ainfo.local point to static memory
+ * areas of size PATH_MAX. These are manipulated and
+ * then provided their own allocations later in this function.
+ */
+ n = gpkgmapvfp(&map_entry, vfp);
+
+ if (n == 0)
+ break; /* no more entries in pkgmap */
+ else if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext("bad entry read in pkgmap"));
+ logerr(gettext("pathname=%s"),
+ (map_entry.path && *map_entry.path) ?
+ map_entry.path : "Unknown");
+ logerr(gettext("problem=%s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ return (NULL);
+ }
+
+ /*
+ * A valid entry was found in the map, so allocate an
+ * official record.
+ */
+ ext_ptr = (struct cfextra **)ar_next_avail(xspace);
+ if (ext_ptr == NULL || *ext_ptr == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ ext = *ext_ptr;
+ ept = &(ext->cf_ent);
+
+ /* Transfer what we just read in. */
+ (void) memcpy(ept, &map_entry, sizeof (struct cfent));
+
+ /* And process it into the cfextra structure. */
+ if (pkgobjassign(ept,
+ &(ext->server_local),
+ &(ext->client_local),
+ &(ext->server_path),
+ &(ext->client_path),
+ &(ext->map_path),
+ mapflag, nc)) {
+ /* It didn't take. */
+ (void) ar_delete(xspace, eptnum);
+ continue;
+ }
+
+ eptnum++;
+ ext->fsys_value = BADFSYS; /* No file system data yet */
+ ext->fsys_base = BADFSYS;
+ }
+
+ if (eptnum == 0) {
+ logerr(gettext(WRN_NOPKGOBJ));
+ return (NULL);
+ }
+
+ /* setup a pointer array to point to malloc'd entries space */
+ extlist = (struct cfextra **)ar_get_head(xspace);
+ if (extlist == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ (void) sortentry(-1);
+ for (i = 0; i < eptnum; /* void */) {
+ if (!sortentry(i))
+ i++;
+ }
+
+ return (errflg ? NULL : extlist);
+}
+
+/*
+ * This function sorts the final list of cfextra entries. If index = -1, the
+ * function is initialized. index = 0 doesn't get us anywhere because this
+ * sorts against index-1. Positive natural index values are compared and
+ * sorted into the array appropriately. Yes, it does seem we should use a
+ * quicksort on the whole array or something. The apparent reason for taking
+ * this approach is that there are enough special considerations to be
+ * applied to each package object that inserting them one-by-one doesn't cost
+ * that much.
+ */
+static int
+sortentry(int index)
+{
+ struct cfextra *ext;
+ struct cfent *ept, *ept_i;
+ static int last = 0;
+ int i, n, j;
+ int upper, lower;
+
+ if (index == 0)
+ return (0);
+ else if (index < 0) {
+ last = 0;
+ return (0);
+ }
+
+ /*
+ * Based on the index, this is the package object we're going to
+ * review. It may stay where it is or it may be repositioned in the
+ * array.
+ */
+ ext = extlist[index];
+ ept = &(ext->cf_ent);
+
+ /* quick comparison optimization for pre-sorted arrays */
+ if (strcmp(ept->path, extlist[index-1]->cf_ent.path) > 0) {
+ /* do nothing */
+ last = index-1;
+ return (0);
+ }
+
+ lower = 0; /* lower bound of the unsorted elements */
+ upper = index; /* upper bound */
+ i = last;
+ do {
+ /*
+ * NOTE: This does a binary sort on path. There are lots of
+ * other worthy items in the array, but path is the key into
+ * the package database.
+ */
+ ept_i = &(extlist[i]->cf_ent);
+
+ n = strcmp(ept->path, ept_i->path);
+ if (n == 0) {
+ if (!ckdup(ept, ept_i)) {
+ /*
+ * If the array was seeded then there are
+ * bound to be occasional duplicates.
+ * Otherwise, duplicates are definitely a
+ * sign of major damage.
+ */
+ if (array_preloaded) {
+ if (!dup_merg(ext, extlist[i])) {
+ progerr(gettext(ERR_DUPPATH),
+ ept->path);
+ errflg++;
+ }
+ } else {
+ progerr(gettext(ERR_DUPPATH),
+ ept->path);
+ errflg++;
+ }
+ }
+ /* remove the entry at index */
+ (void) ar_delete(xspace, index);
+
+ eptnum--;
+ return (1); /* Use this index again. */
+ } else if (n < 0) {
+ /*
+ * The path of interest is smaller than the path
+ * under test. Move down array using the method of
+ * division
+ */
+ upper = i;
+ i = lower + (upper-lower)/2;
+ } else {
+ /* Move up array */
+ lower = i+1;
+ i = upper - (upper-lower)/2 - 1;
+ }
+ } while (upper != lower);
+ last = i = upper;
+
+ /* expand to insert at i */
+ for (j = index; j > i; j--)
+ extlist[j] = extlist[j-1];
+
+ extlist[i] = ext;
+
+ return (0);
+}
+
+/* Return the number of blocks required by the package object provided. */
+static fsblkcnt_t
+nblks(short fsys_entry, struct cfextra *ext)
+{
+ fsblkcnt_t blk;
+ ulong_t block_size;
+ ulong_t frag_size;
+
+ block_size = (ulong_t)get_blk_size_n(fsys_entry);
+ frag_size = (ulong_t)get_frag_size_n(fsys_entry);
+
+ if (strchr("dxs", ext->cf_ent.ftype))
+ blk =
+ nblk(block_size, block_size, frag_size);
+ else if (ext->cf_ent.cinfo.size != BADCONT)
+ blk = nblk(ext->cf_ent.cinfo.size, block_size,
+ frag_size);
+ else
+ blk = 0;
+
+ return (blk);
+}
+
+/* Remove ext1 from the filesystem size calculations and add ext2. */
+static void
+size_xchng(struct cfextra *ext1, struct cfextra *ext2)
+{
+ fsblkcnt_t bused;
+ ulong_t block_size;
+ ulong_t frag_size;
+ fsblkcnt_t blks1, blks2;
+ short fsys_entry;
+
+ /*
+ * Since these are on the same filesystem, either one will yield the
+ * correct block and fragment size.
+ */
+ fsys_entry = ext1->fsys_base;
+ block_size = (ulong_t)get_blk_size_n(fsys_entry);
+ frag_size = (ulong_t)get_frag_size_n(fsys_entry);
+
+ blks1 = nblk(ext1->cf_ent.cinfo.size, block_size, frag_size);
+ blks2 = nblk(ext2->cf_ent.cinfo.size, block_size, frag_size);
+
+ if (blks1 != blks2) {
+ /* First, lose the old size, then add the new size. */
+ bused = get_blk_used_n(fsys_entry);
+ bused -= nblks(fsys_entry, ext1);
+ bused += nblks(fsys_entry, ext2);
+
+ set_blk_used_n(fsys_entry, bused);
+ }
+}
+
+/*
+ * This function merges duplicate non-directory entries resulting from a
+ * dryrun or other procedure which preloads the extlist. It uses an odd
+ * heuristic to determine which package object is newest: only package
+ * objects from the dryrun file will have pinfo pointers. Therefore, the
+ * object with a pinfo pointer is from the dryrun file and it will be
+ * overwritten by the object being installed by this package.
+ *
+ * Assumptions:
+ * 1. The newer object will be overwriting the older object.
+ * 2. The two objects are close enough to the same size that
+ * the sizing is still OK.
+ *
+ * The calling routine will overwrite ept1, so this must return ept2 with
+ * the correct data to keep. There being only one logical outcome of a
+ * failure, this returns 1 for OK and 0 for FAIL.
+ */
+static int
+dup_merg(struct cfextra *ext1, struct cfextra *ext2)
+{
+ struct cfent *ept1, *ept2;
+
+ ept1 = &(ext1->cf_ent);
+ ept2 = &(ext2->cf_ent);
+
+ if (strchr("?dx", ept1->ftype))
+ return (0);
+
+ if (strchr("?dx", ept2->ftype))
+ return (0);
+
+ /* First, which is the eldest? */
+ if (ext2->mstat.preloaded) {
+ /*
+ * While ept2 has the correct pinfo list (it was preloaded into
+ * the array before the pkgmap was read), ept1 has everything
+ * else. Here we copy the guts of ept1 into ept2.
+ *
+ * Start by grabbing the pointers to the ext2 items that we
+ * need to either restore or free.
+ */
+ /* to free() */
+ char *path = ept2->path;
+ char *local = ept2->ainfo.local;
+
+ /* to preserve */
+ short npkgs = ept2->npkgs;
+ struct pinfo *pinfo = ept2->pinfo;
+
+ /* Copy everything from the new entry to the old */
+ (void) memcpy(ept2, ept1, sizeof (struct cfent));
+
+ /* Now restore the original stuff.. */
+ ept2->path = path;
+ ept2->ainfo.local = local;
+ ept2->npkgs = npkgs;
+ ept2->pinfo = pinfo;
+
+ size_xchng(ext2, ext1);
+ } else if (ext1->mstat.preloaded) {
+ /*
+ * ept2 is already the one we will keep. All we have to do is
+ * copy over the pinfo pointer.
+ */
+ ept2->pinfo = ept1->pinfo;
+ size_xchng(ext1, ext2);
+ } else
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Check duplicate entries in the package object list. If it's a directory,
+ * this just merges them, if not, it returns a 0 to force further processing.
+ */
+static int
+ckdup(struct cfent *ept1, struct cfent *ept2)
+{
+ /* ept2 will be modified to contain "merged" entries */
+
+ if (!strchr("?dx", ept1->ftype))
+ return (0);
+
+ if (!strchr("?dx", ept2->ftype))
+ return (0);
+
+ if (ept2->ainfo.mode == BADMODE)
+ ept2->ainfo.mode = ept1->ainfo.mode;
+ if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
+ (ept1->ainfo.mode != BADMODE))
+ return (0);
+
+ if (strcmp(ept2->ainfo.owner, "?") == 0)
+ (void) strlcpy(ept2->ainfo.owner, ept1->ainfo.owner,
+ sizeof (ept2->ainfo.owner));
+ if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
+ strcmp(ept1->ainfo.owner, "?"))
+ return (0);
+
+ if (strcmp(ept2->ainfo.group, "?") == 0)
+ (void) strlcpy(ept2->ainfo.group, ept1->ainfo.group,
+ sizeof (ept2->ainfo.group));
+ if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
+ strcmp(ept1->ainfo.group, "?"))
+ return (0);
+
+ if (ept1->pinfo) {
+ ept2->npkgs = ept1->npkgs;
+ ept2->pinfo = ept1->pinfo;
+ }
+
+ return (1);
+}
+
+/*
+ * Replace the old package database entry with the new one preserving the
+ * data which remains constant across the replacement.
+ * copied directly:
+ * ftype, pkg_class
+ *
+ * preserved from old:
+ * path, npkgs, pinfo
+ */
+void
+repl_cfent(struct cfent *new, struct cfent *old)
+{
+ char *path = old->path;
+ short npkgs = old->npkgs;
+ struct pinfo *pinfo = old->pinfo;
+
+ /* Copy everything from the new entry over */
+ (void) memcpy(old, new, sizeof (struct cfent));
+
+ if (strchr("sl", new->ftype) == NULL)
+ old->ainfo.local = NULL;
+
+ old->path = path;
+ old->npkgs = npkgs;
+ old->pinfo = pinfo;
+
+ old->volno = 0;
+}
+
+/*
+ * Copy critical portions of cf_ent (from the package database) and el_ent
+ * (constructed from the pkgmap) into a merged cfent structure, tp. Then copy
+ * that to the el_ent structure. The approach we take here is to copy over
+ * everything from the package database entry, condition the paths based upon
+ * the currently installed path and then insert the following entries from
+ * the new structure :
+ * cfent.volno
+ * pkg_class
+ * pkg_class_idx
+ *
+ * The pinfo list is then copied from the cfent list. While
+ * fsys_value is also copied over, it hasn't been set yet. This function
+ * copies over whatever the default value is from the new structure.
+ *
+ * The copied entry is returned in the el_ent argument and the function
+ * value is 1 on success, 0 on failure. There is no recovery plan for
+ * failure.
+ */
+int
+cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent)
+{
+ struct cfextra *tp;
+
+ /* Allocate space for cfent copy */
+ if ((tp = (struct cfextra *)calloc(1,
+ sizeof (struct cfextra))) == NULL) {
+ progerr(gettext("cp_cfent: memory allocation error"));
+ return (0);
+ }
+
+ /* Copy everything from the package database over */
+ (void) memcpy(&(tp->cf_ent), cf_ent, sizeof (struct cfent));
+
+ /* Now overlay new items from the pkgmap */
+ tp->fsys_value = el_ent->fsys_value;
+ tp->cf_ent.volno = el_ent->cf_ent.volno;
+ (void) strlcpy(tp->cf_ent.pkg_class, el_ent->cf_ent.pkg_class,
+ sizeof (tp->cf_ent.pkg_class));
+ tp->cf_ent.pkg_class_idx = el_ent->cf_ent.pkg_class_idx;
+ tp->cf_ent.pinfo = cf_ent->pinfo;
+
+ /*
+ * The paths are identical, so we get them from the new entry. These
+ * are pointing to a malloc'd section of memory containing a string
+ * that we aren't moving in this operation, so everybody points to
+ * the same thing during these transfers.
+ */
+ tp->cf_ent.path = el_ent->client_path;
+ tp->server_path = el_ent->server_path;
+ tp->client_path = el_ent->client_path;
+ tp->map_path = el_ent->map_path;
+
+ /*
+ * Since instvol() expects to work with the *original* mstat data,
+ * mstat is just copied here. NOTE: mstat looks like a structure, but
+ * it's really a short bit array.
+ */
+ tp->mstat = el_ent->mstat;
+
+ /* Copy everything from the temporary structure to the new entry */
+ (void) memcpy(el_ent, tp, sizeof (struct cfextra));
+ free(tp);
+
+ return (1);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/pkgops.c b/usr/src/cmd/svr4pkg/libinst/pkgops.c
new file mode 100644
index 0000000000..3bb4d90650
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/pkgops.c
@@ -0,0 +1,1426 @@
+/*
+ * 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 <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 <assert.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+/* commands to execute */
+
+#define PKGINFO_CMD "/usr/bin/pkginfo"
+
+#define GLOBALZONE_ONLY_PACKAGE_FILE_PATH \
+ "/var/sadm/install/gz-only-packages"
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/*
+ * forward declarations
+ */
+
+static void _pkginfoInit(struct pkginfo *a_info);
+static struct pkginfo *_pkginfoFactory(void);
+static char **thisZonePackages;
+static int numThisZonePackages;
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: pkginfoFree
+ * Description: free pkginfo structure returned from various functions
+ * Arguments: r_info - pointer to pointer to pkginfo structure to free
+ * Returns: void
+ */
+
+void
+pkginfoFree(struct pkginfo **r_info)
+{
+ struct pkginfo *pinfo;
+
+ /* entry assertions */
+
+ assert(r_info != (struct pkginfo **)NULL);
+
+ /* localize reference to info structure to free */
+
+ pinfo = *r_info;
+
+ /* reset callers handle to info structure */
+
+ *r_info = (struct pkginfo *)NULL;
+
+ assert(pinfo != (struct pkginfo *)NULL);
+
+ /* free up contents of the structure */
+
+ _pkginfoInit(pinfo);
+
+ /* free up structure itself */
+
+ (void) free(pinfo);
+}
+
+/*
+ * Name: pkginfoIsPkgInstalled
+ * Description: determine if specified package is installed, return pkginfo
+ * structure describing package if package is installed
+ * Arguments: r_pinfo - pointer to pointer to pkginfo structure
+ * If this pointer is NOT null:
+ * -On success, this handle is filled in with a pointer
+ * --to a newly allocated pkginfo structure describing
+ * --the package discovered
+ * -On failure, this handle is filled with NULL
+ * If this pointer is NULL:
+ * -no pkginfo structure is returned on success.
+ * a_pkgInst - package instance (name) to lookup
+ * Returns: boolean_t
+ * B_TRUE - package installed, pkginfo returned
+ * B_FALSE - package not installed, no pkginfo returned
+ * NOTE: This function returns the first instance of package that
+ * is installed - see pkginfo() function for details
+ * NOTE: Any pkginfo structure returned is placed in new storage for the
+ * calling function. The caller must use 'pkginfoFree' to dispose
+ * of the storage once the pkginfo structure is no longer needed.
+ */
+
+boolean_t
+pkginfoIsPkgInstalled(struct pkginfo **r_pinfo, char *a_pkgInst)
+{
+ int r;
+ struct pkginfo *pinf;
+
+ /* entry assertions */
+
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* reset returned pkginfo structure handle */
+
+ if (r_pinfo != (struct pkginfo **)NULL) {
+ *r_pinfo = (struct pkginfo *)NULL;
+ }
+
+ /* allocate a new pinfo structure for use in the call to pkginfo */
+
+ pinf = _pkginfoFactory();
+
+ /* lookup the specified package */
+
+ /* NOTE: required 'pkgdir' set to spool directory or NULL */
+ r = pkginfo(pinf, a_pkgInst, NULL, NULL);
+ echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, a_pkgInst, r);
+
+ if (r_pinfo != (struct pkginfo **)NULL) {
+ *r_pinfo = pinf;
+ } else {
+ /* free pkginfo structure */
+ pkginfoFree(&pinf);
+ }
+
+ return (r == 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Name: pkgOpenInGzOnlyFile
+ * Description: Open the global zone only package list file
+ * Arguments: a_rootPath - pointer to string representing the root path
+ * where the global zone only package list file is
+ * located - NULL is the same as "/"
+ * Returns: FILE *
+ * == NULL - failure - file not open
+ * != NULL - success - file pointer returned
+ * NOTE: This function will create the file if it does not exist.
+ */
+
+FILE *
+pkgOpenInGzOnlyFile(char *a_rootPath)
+{
+ FILE *pkgingzonlyFP;
+ char pkgingzonlyPath[PATH_MAX];
+ int len;
+
+ /* normalize root path */
+
+ if (a_rootPath == (char *)NULL) {
+ a_rootPath = "";
+ }
+
+ /* generate path to glocal zone only list file */
+
+ len = snprintf(pkgingzonlyPath, sizeof (pkgingzonlyPath), "%s/%s",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (pkgingzonlyPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return ((FILE *)NULL);
+ }
+
+ /* open global zone only list file */
+
+ pkgingzonlyFP = fopen(pkgingzonlyPath, "r+");
+ if ((pkgingzonlyFP == (FILE *)NULL) && (errno == ENOENT)) {
+ pkgingzonlyFP = fopen(pkgingzonlyPath, "w+");
+ }
+
+ if ((pkgingzonlyFP == (FILE *)NULL) && (errno != ENOENT)) {
+ progerr(ERR_PKGOPS_OPEN_GZONLY, pkgingzonlyPath,
+ strerror(errno));
+ return ((FILE *)NULL);
+ }
+
+ /* success - return FILE pointer open on global zone only list file */
+
+ return (pkgingzonlyFP);
+}
+
+/*
+ * Name: pkgIsPkgInGzOnly
+ * Description: determine if package is recorded as "in global zone only"
+ * by opening the appropriate files and searching for the
+ * specified package
+ * Arguments: a_rootPath - pointer to string representing the root path
+ * where the global zone only package list file is
+ * located - NULL is the same as "/"
+ * a_pkgInst - pointer to string representing the package instance
+ * (name) of the package to lookup
+ * Returns: boolean_t
+ * B_TRUE - package is recorded as "in global zone only"
+ * B_FALSE - package is NOT recorded as "in gz only"
+ * NOTE: This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgIsPkgInGzOnly(char *a_rootPath, char *a_pkgInst)
+{
+ FILE *fp;
+ boolean_t in_gz_only;
+
+ /* normalize root path */
+
+ if (a_rootPath == (char *)NULL) {
+ a_rootPath = "";
+ }
+
+ /* open the global zone only package list file */
+
+ fp = pkgOpenInGzOnlyFile(a_rootPath);
+ if (fp == (FILE *)NULL) {
+ echoDebug(ERR_PKGOPS_CANNOT_OPEN_GZONLY,
+ a_rootPath ? a_rootPath : "/");
+ return (B_FALSE);
+ }
+
+ /* is the package recorded as "in global zone only" ? */
+
+ in_gz_only = pkgIsPkgInGzOnlyFP(fp, a_pkgInst);
+
+ /* close the global zone only package list file */
+
+ (void) fclose(fp);
+
+ /* return results */
+
+ return (in_gz_only);
+}
+
+/*
+ * Name: pkgIsPkgInGzOnly
+ * Description: determine if package is recorded as "in global zone only"
+ * by searching the specified open FILE for the specified package
+ * Arguments: a_fp - pointer to FILE handle open on file to search
+ * a_pkgInst - pointer to string representing the package instance
+ * (name) of the package to lookup
+ * Returns: boolean_t
+ * B_TRUE - package is recorded as "in global zone only"
+ * B_FALSE - package is NOT recorded as "in gz only"
+ */
+
+boolean_t
+pkgIsPkgInGzOnlyFP(FILE *a_fp, char *a_pkgInst)
+{
+ char line[PATH_MAX+1];
+
+ /* entry assertions */
+
+ assert(a_fp != (FILE *)NULL);
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* rewind the file to the beginning */
+
+ rewind(a_fp);
+
+ /* read the file line by line searching for the specified package */
+
+ while (fgets(line, sizeof (line), a_fp) != (char *)NULL) {
+ int len;
+
+ /* strip off trailing newlines */
+ len = strlen(line);
+ while ((len > 0) && (line[len-1] == '\n')) {
+ line[--len] = '\0';
+ }
+
+ /* ignore blank and comment lines */
+ if ((line[0] == '#') || (line[0] == '\0')) {
+ continue;
+ }
+
+ /* return true if this is the package we are looking for */
+ if (strcmp(a_pkgInst, line) == 0) {
+ echoDebug(DBG_PKGOPS_PKG_IS_GZONLY, a_pkgInst);
+ return (B_TRUE);
+ }
+ }
+
+ /* end of file - package not found */
+
+ echoDebug(DBG_PKGOPS_PKG_NOT_GZONLY, a_pkgInst);
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: pkgRemovePackageFromGzonlyList
+ * Description: Remove specified package from the global zone only package list
+ * file located at a specified root path
+ * Arguments: a_rootPath - pointer to string representing the root path
+ * where the global zone only package list file is
+ * located - NULL is the same as "/"
+ * a_pkgInst - pointer to string representing the package instance
+ * (name) of the package to remove
+ * Returns: boolean_t
+ * B_TRUE - package is successfully removed
+ * B_FALSE - failed to remove package from file
+ * NOTE: This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgRemovePackageFromGzonlyList(char *a_rootPath, char *a_pkgInst)
+{
+ FILE *destFP;
+ FILE *srcFP;
+ boolean_t pkgremoved = B_FALSE;
+ char destPath[PATH_MAX];
+ char line[PATH_MAX+1];
+ char savePath[PATH_MAX];
+ char srcPath[PATH_MAX];
+ char timeb[BUFSIZ];
+ int len;
+ struct tm *timep;
+ time_t clock;
+
+ /* entry assertions */
+
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* normalize root path */
+
+ if (a_rootPath == (char *)NULL) {
+ a_rootPath = "";
+ }
+
+ /*
+ * calculate paths to various objects
+ */
+
+ /* path to current "source" ingzonly file */
+
+ len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* path to new "destination" ingzonly file */
+
+ len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* path to temporary "saved" ingzonly file */
+
+ len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* open source file, creating if necessary */
+
+ srcFP = fopen(srcPath, "r+");
+ if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
+ srcFP = fopen(srcPath, "w+");
+ }
+
+ /* error if could not open/create file */
+
+ if (srcFP == (FILE *)NULL) {
+ progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* open/create new destination file */
+
+ (void) remove(destPath);
+ destFP = fopen(destPath, "w");
+ if (destFP == (FILE *)NULL) {
+ progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
+ if (srcFP != (FILE *)NULL) {
+ (void) fclose(srcFP);
+ }
+ return (B_FALSE);
+ }
+
+ /* add standard comment to beginning of file */
+
+ (void) time(&clock);
+ timep = localtime(&clock);
+
+ (void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+
+ /* put standard header at the beginning of the file */
+
+ (void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
+ get_prog_name(), "remove", a_pkgInst, timeb);
+
+ /* read source/write destination - removing specified package */
+
+ while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
+ int len;
+
+ /* strip off trailing newlines */
+ len = strlen(line);
+ while ((len > 0) && (line[len-1] == '\n')) {
+ line[--len] = '\0';
+ }
+
+ /* ignore blank and comment lines */
+ if ((line[0] == '#') || (line[0] == '\0')) {
+ continue;
+ }
+
+ /* add pkg if yet to add and pkg <= line */
+ if ((pkgremoved == B_FALSE) && (strcmp(a_pkgInst, line) == 0)) {
+ pkgremoved = B_TRUE;
+ } else {
+ (void) fprintf(destFP, "%s\n", line);
+ }
+ }
+
+ /* close both files */
+
+ (void) fclose(srcFP);
+
+ (void) fclose(destFP);
+
+ /*
+ * if package not found there is no need to update the original file
+ */
+
+ if (pkgremoved == B_FALSE) {
+ (void) unlink(destPath);
+ return (B_TRUE);
+ }
+
+ /*
+ * Now we want to make a copy of the old gzonly file as a
+ * fail-safe.
+ */
+
+ if ((access(savePath, F_OK) == 0) && remove(savePath)) {
+ progerr(ERR_REMOVE, savePath, strerror(errno));
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (link(srcPath, savePath) != 0) {
+ progerr(ERR_LINK, savePath, srcPath, strerror(errno));
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (rename(destPath, srcPath) != 0) {
+ progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
+ if (rename(savePath, srcPath)) {
+ progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
+ }
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (remove(savePath) != 0) {
+ progerr(ERR_REMOVE, savePath, strerror(errno));
+ }
+
+ /* successfully removed package */
+
+ echoDebug(DBG_PKGOPS_REMOVED_GZPKG, a_pkgInst);
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: pkgAddPackageFromGzonlyList
+ * Description: Add specified package to the global zone only package list
+ * file located at a specified root path
+ * Arguments: a_rootPath - pointer to string representing the root path
+ * where the global zone only package list file is
+ * located - NULL is the same as "/"
+ * a_pkgInst - pointer to string representing the package instance
+ * (name) of the package to add
+ * Returns: boolean_t
+ * B_TRUE - package is successfully added
+ * B_FALSE - failed to add package to the file
+ * NOTE: This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgAddPackageToGzonlyList(char *a_pkgInst, char *a_rootPath)
+{
+ FILE *destFP;
+ FILE *srcFP;
+ boolean_t pkgadded = B_FALSE;
+ char destPath[PATH_MAX];
+ char line[PATH_MAX+1];
+ char savePath[PATH_MAX];
+ char srcPath[PATH_MAX];
+ char timeb[BUFSIZ];
+ int len;
+ struct tm *timep;
+ time_t clock;
+
+ /* entry assertions */
+
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* normalize root path */
+
+ if (a_rootPath == (char *)NULL) {
+ a_rootPath = "";
+ }
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGOPS_ADDGZPKG, a_pkgInst, a_rootPath);
+
+ /*
+ * calculate paths to various objects
+ */
+
+ /* path to current "source" ingzonly file */
+
+ len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* path to new "destination" ingzonly file */
+
+ len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* path to temporary "saved" ingzonly file */
+
+ len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
+ a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ if (len > sizeof (srcPath)) {
+ progerr(ERR_CREATE_PATH_2, a_rootPath,
+ GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+ return (B_FALSE);
+ }
+
+ /* open source file, creating if necessary */
+
+ srcFP = fopen(srcPath, "r+");
+ if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
+ srcFP = fopen(srcPath, "w+");
+ }
+
+ /* error if could not open/create file */
+
+ if (srcFP == (FILE *)NULL) {
+ progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* open/create new destination file */
+
+ (void) remove(destPath);
+ destFP = fopen(destPath, "w");
+ if (destFP == (FILE *)NULL) {
+ progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
+ if (srcFP != (FILE *)NULL) {
+ (void) fclose(srcFP);
+ }
+ return (B_FALSE);
+ }
+
+ /* add standard comment to beginning of file */
+
+ (void) time(&clock);
+ timep = localtime(&clock);
+
+ (void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+
+ /* put standard header at the beginning of the file */
+
+ (void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
+ get_prog_name(), "add", a_pkgInst, timeb);
+
+ /* read source/write destination; add package at appropriate location */
+
+ while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
+ int len;
+
+ /* strip off trailing newlines */
+ len = strlen(line);
+ while ((len > 0) && (line[len-1] == '\n')) {
+ line[--len] = '\0';
+ }
+
+ /* ignore blank and comment lines */
+ if ((line[0] == '#') || (line[0] == '\0')) {
+ continue;
+ }
+
+ /* add pkg if yet to add and pkg <= line */
+ if ((pkgadded == B_FALSE) && (strcmp(a_pkgInst, line) <= 0)) {
+ if (strcmp(a_pkgInst, line) != 0) {
+ (void) fprintf(destFP, "%s\n", a_pkgInst);
+ }
+ pkgadded = B_TRUE;
+ }
+
+ (void) fprintf(destFP, "%s\n", line);
+ }
+
+ /* if package not added yet, add to end of the file */
+
+ if (pkgadded == B_FALSE) {
+ (void) fprintf(destFP, "%s\n", a_pkgInst);
+ }
+
+ /* close both files */
+
+ (void) fclose(srcFP);
+
+ (void) fclose(destFP);
+
+ /*
+ * Now we want to make a copy of the old gzonly file as a
+ * fail-safe.
+ */
+
+ if ((access(savePath, F_OK) == 0) && remove(savePath)) {
+ progerr(ERR_REMOVE, savePath, strerror(errno));
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (link(srcPath, savePath) != 0) {
+ progerr(ERR_LINK, savePath, srcPath, strerror(errno));
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (rename(destPath, srcPath) != 0) {
+ progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
+ if (rename(savePath, srcPath)) {
+ progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
+ }
+ (void) remove(destPath);
+ return (B_FALSE);
+ }
+
+ if (remove(savePath) != 0) {
+ progerr(ERR_REMOVE, savePath, strerror(errno));
+ }
+
+ /* successfully added package */
+
+ echoDebug(DBG_PKGOPS_ADDED_GZPKG, a_pkgInst);
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: pkginfoParamTruth
+ * Description: Search pkginfo file for specified parameter/value pair
+ * Arguments: a_fp - Pointer to FILE handle open on pkginfo file to search
+ * a_param - Pointer to string representing the parameter name
+ * to search for
+ * a_value - Pointer to string representing the "success" value
+ * being searched for
+ * a_default - determine results if parameter NOT found
+ * B_TRUE - parameter is TRUE if not found
+ * B_FALSE - parameter is FALSE if not found
+ * Returns: boolean_t
+ * B_TRUE - the parameter was found and matched the specified value
+ * OR the paramter was not found and a_default == B_TRUE
+ * B_FALSE - the parameter was found and did NOT match the value
+ * OR the paramter was not found and a_default == B_FALSE
+ */
+
+boolean_t
+pkginfoParamTruth(FILE *a_fp, char *a_param, char *a_value, boolean_t a_default)
+{
+ char *param;
+ boolean_t result;
+
+ /* entry assertions */
+
+ assert(a_fp != (FILE *)NULL);
+ assert(a_param != (char *)NULL);
+ assert(*a_param != '\0');
+ assert(a_value != (char *)NULL);
+ assert(*a_value != '\0');
+
+ /* rewind the file to the beginning */
+
+ rewind(a_fp);
+
+ /* search pkginfo file for the specified parameter */
+
+ param = fpkgparam(a_fp, a_param);
+
+ if (param == (char *)NULL) {
+ /* parameter not found - return default */
+ result = a_default;
+ } else if (*param == '\0') {
+ /* parameter found but no value - return default */
+ result = a_default;
+ } else if (strcasecmp(param, a_value) == 0) {
+ /* paramter found - matches value */
+ result = B_TRUE;
+ } else {
+ /* parameter found - does not match value */
+ result = B_FALSE;
+ }
+
+ /* exit debugging info */
+
+ echoDebug(DBG_PKGOPS_PARAMTRUTH_RESULTS,
+ a_param, a_value, a_default == B_TRUE ? "true" : "false",
+ param ? param : "?", result == B_TRUE ? "true" : "false");
+
+ /* if parameter value found, free results */
+
+ if (param != (char *)NULL) {
+ (void) free(param);
+ }
+
+ /* return results of search */
+
+ return (result);
+}
+
+/*
+ * Name: pkgGetPackageList
+ * Description: Determine list of packages based on list of packages that are
+ * available, category of packages to select, and list of packages
+ * to select.
+ * Arguments: r_pkgList - pointer to pointer to string array where the list
+ * of selected packages will be returned
+ * a_argv - pointer to string array containing list of packages
+ * to select
+ * a_optind - index into string array of first package to select
+ * a_categories - pointer to string representing the categories of
+ * packages to select
+ * a_categoryList - pointer to string array representing a list
+ * of categories to select
+ * a_pkgdev - package dev containing packages that can be selected
+ * Returns: int
+ * == 0 - packages found r_pkgList contains results package list retrieved
+ * == -1 - no packages found (errno == ENOPKG)
+ * != 0 - "quit" value entered by user
+ * NOTE: If both a category and a list of packages to select are provided
+ * the category is used over the list of packages provided
+ * NOTE: If neither a category nor a list of packages to select are
+ * provided, an error is returned
+ */
+
+int
+pkgGetPackageList(char ***r_pkgList, char **a_argv, int a_optind,
+ char *a_categories, char **a_categoryList, struct pkgdev *a_pkgdev)
+{
+ char *all_pkgs[4] = {"all", NULL};
+
+ /* entry assertions */
+
+ assert(a_pkgdev != (struct pkgdev *)NULL);
+ assert(a_pkgdev->dirname != (char *)NULL);
+ assert(*a_pkgdev->dirname != '\0');
+ assert(r_pkgList != (char ***)NULL);
+ assert(a_argv != (char **)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGOPS_GETPKGLIST_ENTRY);
+ echoDebug(DBG_PKGOPS_GETPKGLIST_ARGS, a_pkgdev->dirname,
+ a_categories ? a_categories : "?");
+
+ /* reset returned package list handle */
+
+ *r_pkgList = (char **)NULL;
+
+ /*
+ * generate list of packages to be removed: if removing by category,
+ * then generate package list based on all packages by category,
+ * else generate package list based on all packages specified.
+ */
+
+ if (a_categories != NULL) {
+ /* generate package list from all packages in given category */
+
+ *r_pkgList = gpkglist(a_pkgdev->dirname, &all_pkgs[0],
+ a_categoryList);
+
+ if (*r_pkgList == NULL) {
+ echoDebug(DBG_PKGOPS_GPKGLIST_CATFAILED, a_categories);
+ progerr(ERR_CAT_FND, a_categories);
+ return (1);
+ }
+
+ echoDebug(DBG_PKGOPS_GPKGLIST_CATOK, a_categories);
+
+ return (0);
+ }
+
+ /* generate package list from specified packages */
+
+ *r_pkgList = gpkglist(a_pkgdev->dirname, &a_argv[a_optind], NULL);
+
+ /* if list generated return results */
+
+ if (*r_pkgList != NULL) {
+ echoDebug(DBG_PKGOPS_GPKGLIST_OK);
+ return (0);
+ }
+
+ /* handle error from gpkglist */
+
+ switch (errno) {
+ case ENOPKG: /* no packages */
+ echoDebug(DBG_PKGOPS_GPKGLIST_ENOPKG);
+ return (-1);
+
+ case ESRCH:
+ echoDebug(DBG_PKGOPS_GPKGLIST_ESRCH);
+ return (1);
+
+ case EINTR:
+ echoDebug(DBG_PKGOPS_GPKGLIST_EINTR);
+ return (3);
+
+ default:
+ echoDebug(DBG_PKGOPS_GPKGLIST_UNKNOWN, errno);
+ progerr(ERR_GPKGLIST_ERROR);
+ return (99);
+ }
+}
+
+/*
+ * Name: pkgMatchInherited
+ * Description: given a pointer to a "source" and a "destination" for an object,
+ * along with other attributes of the object, determine if the
+ * object is already installed and is current.
+ * Arguments: a_src - pointer to string representing the "source" file to
+ * verify - this would be the current temporary location of
+ * the file that would be installed
+ * a_dst - pointer to string representing the "destination" file to
+ * verify - this would be the ultimate destination for the
+ * file if installed
+ * a_rootDir - pointer to string representing the "root directory"
+ * where the package is being installed
+ * a_mode - final "mode" file should have when installed
+ * a_modtime - final "modtime" file should have when installed
+ * a_ftype - contents "type" of file (f/e/v/s/l)
+ * a_cksum - final "checksum" file should have when installed
+ * Returns: boolean_t
+ * B_TRUE - the specified source file MATCHES the file
+ * located at the specified destination
+ * B_FALSE - the specified source files does NOT match
+ * the file located at the specified destination
+ */
+
+boolean_t
+pkgMatchInherited(char *a_src, char *a_dst, char *a_rootDir,
+ char a_mode, time_t a_modtime, char a_ftype, unsigned long a_cksum)
+{
+ char cwd[PATH_MAX+1] = {'\0'};
+ char dstpath[PATH_MAX+1];
+ int cksumerr;
+ int n;
+ struct stat statbufDst;
+ struct stat statbufSrc;
+ unsigned long dstcksum;
+ unsigned long srcksum;
+
+ /* entry assertions */
+
+ assert(a_src != (char *)NULL);
+ assert(*a_src != '\0');
+ assert(a_dst != (char *)NULL);
+ assert(*a_dst != '\0');
+
+ /* normalize root directory */
+
+ if ((a_rootDir == (char *)NULL) || (*a_rootDir == '\0')) {
+ a_rootDir = "/";
+ }
+
+ /* entry debugging */
+
+ echoDebug(DBG_PKGOPS_MATCHINHERIT_ENTRY);
+ echoDebug(DBG_PKGOPS_MATCHINHERIT_ARGS, a_src, a_dst, a_rootDir,
+ a_mode, a_modtime, a_ftype, a_cksum);
+
+ /* save current working directory - resolvepath can change it */
+
+ (void) getcwd(cwd, sizeof (cwd));
+
+ n = resolvepath(a_dst, dstpath, sizeof (dstpath));
+ if (n <= 0) {
+ if (errno != ENOENT) {
+ progerr(ERR_RESOLVEPATH, a_dst, strerror(errno));
+ }
+ (void) chdir(cwd);
+ return (B_FALSE);
+ }
+ dstpath[n++] = '\0'; /* make sure string is terminated */
+
+ /* return false if path is not in inherited file system space */
+
+ if (!z_path_is_inherited(dstpath, a_ftype, a_rootDir)) {
+ return (B_FALSE);
+ }
+
+ /*
+ * path is in inherited file system space: verify existence
+ */
+
+ /* return false if source file cannot be stat()ed */
+
+ if (stat(a_src, &statbufSrc) != 0) {
+ progerr(ERR_STAT, a_src, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* return false if destination file cannot be stat()ed */
+
+ if (stat(dstpath, &statbufDst) != 0) {
+ progerr(ERR_STAT, dstpath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /*
+ * if this is an editable or volatile file, then the only
+ * thing to guarantee is that the file exists - the file
+ * attributes do not need to match
+ */
+
+ /* editable file only needs to exist */
+
+ if (a_ftype == 'e') {
+ echoDebug(DBG_PKGOPS_EDITABLE_EXISTS, dstpath);
+ return (B_TRUE);
+ }
+
+ /* volatile file only needs to exist */
+
+ if (a_ftype == 'v') {
+ echoDebug(DBG_PKGOPS_VOLATILE_EXISTS, dstpath);
+ return (B_TRUE);
+ }
+
+ /*
+ * verify modtime if file is not modifiable after install
+ */
+
+ /* return false if source and destination have different mod times */
+
+ if (statbufSrc.st_mtim.tv_sec != statbufDst.st_mtim.tv_sec) {
+ echoDebug(DBG_PKGOPS_MOD_MISMATCH, a_src,
+ statbufSrc.st_mtim.tv_sec, dstpath,
+ statbufDst.st_mtim.tv_sec);
+ return (B_FALSE);
+ }
+
+ /* return false if destination does not have required mod time */
+
+ if (statbufDst.st_mtim.tv_sec != a_modtime) {
+ echoDebug(DBG_PKGOPS_MOD_MISMATCH, dstpath,
+ statbufDst.st_mtim.tv_sec, "source", a_modtime);
+ return (B_FALSE);
+ }
+
+ /*
+ * verify checksums of both files
+ */
+
+ /* generate checksum of installed file */
+
+ cksumerr = 0;
+ dstcksum = compute_checksum(&cksumerr, dstpath);
+ if (cksumerr != 0) {
+ progerr(ERR_CANNOT_CKSUM_FILE, dstpath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* return false if destination does not match recorded checksum */
+
+ if (dstcksum != a_cksum) {
+ echoDebug(DBG_PKGOPS_CKSUM_MISMATCH, dstpath, dstcksum,
+ "source", a_cksum);
+ return (B_FALSE);
+ }
+
+ /* generate checksum of file to install */
+
+ cksumerr = 0;
+ srcksum = compute_checksum(&cksumerr, a_src);
+ if (cksumerr != 0) {
+ progerr(ERR_CANNOT_CKSUM_FILE, a_src, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* return false if source to install does not match recorded checksum */
+
+ if (srcksum != dstcksum) {
+ echoDebug(DBG_PKGOPS_CKSUM_MISMATCH, a_src, srcksum, dstpath,
+ dstcksum);
+ return (B_FALSE);
+ }
+
+ /* src/dest identical - return true */
+
+ echoDebug(DBG_PKGOPS_IS_INHERITED, dstpath, "");
+
+ return (B_TRUE);
+}
+
+/*
+ * return string representing path to "global zone only file"
+ */
+
+char *
+pkgGetGzOnlyPath(void)
+{
+ return (GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+}
+
+/*
+ * Name: pkgAddThisZonePackage
+ * Description: Add specified package to internal list of "this zone only" pkgs
+ * Arguments: a_pkgInst - name of package to add to list
+ * Returns: void
+ */
+
+void
+pkgAddThisZonePackage(char *a_pkgInst)
+{
+ /* entry assertions */
+
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* do not duplicate entries */
+
+ if (pkgPackageIsThisZone(a_pkgInst) == B_TRUE) {
+ return;
+ }
+
+ /* add package name to internal list */
+
+ if (thisZonePackages == (char **)NULL) {
+ thisZonePackages =
+ (char **)calloc(2, sizeof (char **));
+ } else {
+ thisZonePackages =
+ (char **)realloc(thisZonePackages,
+ sizeof (char **)*(numThisZonePackages+2));
+ }
+
+ /* handle out of memory error */
+
+ if (thisZonePackages == (char **)NULL) {
+ progerr(ERR_MEMORY, errno);
+ quit(99);
+ }
+
+ /* add this entry to the end of the list */
+
+ thisZonePackages[numThisZonePackages] = strdup(a_pkgInst);
+ if (thisZonePackages[numThisZonePackages] == (char *)NULL) {
+ progerr(ERR_MEMORY, errno);
+ quit(99);
+ }
+
+ numThisZonePackages++;
+
+ /* make sure end of the list is properly terminated */
+
+ thisZonePackages[numThisZonePackages] = (char *)NULL;
+
+ /* exit debugging info */
+
+ echoDebug(DBG_PKGOPS_ADD_TZP, numThisZonePackages,
+ thisZonePackages[numThisZonePackages-1]);
+}
+
+/*
+ * Name: pkgPackageIsThisZone
+ * Description: Determine if the specified package is marked to be installed
+ * in this zone only
+ * Arguments: a_pkgInst - pointer to string representing package name to check
+ * Returns: boolean_t
+ * B_TRUE - the package IS "this zone only"
+ * B_FALSE - the paackage is NOT "this zone only"
+ */
+
+boolean_t
+pkgPackageIsThisZone(char *a_pkgInst)
+{
+ int n;
+
+ /* entry assertions */
+
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* if no inherited file systems, there can be no match */
+
+ if (numThisZonePackages == 0) {
+ echoDebug(DBG_PKGOPS_NOT_THISZONE, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /*
+ * see if this package is in the "this zone only" list
+ */
+
+ for (n = 0; n < numThisZonePackages; n++) {
+ if (strcmp(a_pkgInst, thisZonePackages[n]) == 0) {
+ echoDebug(DBG_PKGOPS_IS_THISZONE, a_pkgInst);
+ return (B_TRUE);
+ }
+ }
+
+ /* path is not in "this zone only" list */
+
+ echoDebug(DBG_PKGOPS_IS_NOT_THISZONE, a_pkgInst);
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: pkgLocateHighestInst
+ * Description: Locate the highest installed instance of a package
+ * Arguments: r_path - [RO, *RW] - (char *)
+ * Pointer to buffer where the full path to the top level
+ * directory containing the latest instance of the
+ * specified package is located is placed.
+ * r_pathLen - [RO, *RO] - (int)
+ * Integer representing the size of r_path in bytes.
+ * r_pkgInst - [RO, *RW] - (char *)
+ * Pointer to buffer where the package instance name of the
+ * latest instance of the specified package is placed.
+ * r_pkgInstLen - [RO, *RO] - (int)
+ * Integer representing the size of r_pkgInst in bytes.
+ * a_rootPath - [RO, *RO] - (char *)
+ * Pointer to string representing the root path to look
+ * for the latest instance of the specified package.
+ * a_pkgInst - [RO, *RO] - (char *)
+ * Pointer to string representing the name of the package
+ * to locate the latest installed instance of.
+ */
+
+void
+pkgLocateHighestInst(char *r_path, int r_pathLen, char *r_pkgInst,
+ int r_pkgInstLen, char *a_rootPath, char *a_pkgInst)
+{
+ char pkgInstPath[PATH_MAX] = {'\0'};
+ char pkgWild[PKGSIZ+1] = {'\0'};
+ char pkgName[PKGSIZ+1] = {'\0'};
+ int npkgs;
+ struct pkginfo *pinf = (struct pkginfo *)NULL;
+
+ /* entry assertions */
+
+ assert(r_path != (char *)NULL);
+ assert(r_pathLen > 0);
+ assert(r_pkgInst != (char *)NULL);
+ assert(r_pkgInstLen > 0);
+ assert(a_pkgInst != (char *)NULL);
+ assert(*a_pkgInst != '\0');
+
+ /* normalize root path */
+
+ if ((a_rootPath == (char *)NULL) || (strcmp(a_rootPath, "/") == 0)) {
+ a_rootPath = "";
+ }
+
+ /* construct path to package repository directory (eg. /var/sadm/pkg) */
+
+ (void) snprintf(pkgInstPath, sizeof (pkgInstPath), "%s%s", a_rootPath,
+ PKGLOC);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGOPS_LOCHIGH_ENTRY);
+ echoDebug(DBG_PKGOPS_LOCHIGH_ARGS, pkgInstPath, a_pkgInst);
+
+ /* reset returned path/package instance so both ares empty */
+
+ *r_path = '\0';
+ *r_pkgInst = '\0';
+
+ /* remove any architecture extension */
+
+ pkgstrGetToken_r((char *)NULL, a_pkgInst, 0, ".",
+ pkgName, sizeof (pkgName));
+
+ /* make sure that the package name is valid and can be wild carded */
+
+ if (pkgnmchk(pkgName, NULL, 0) || strchr(pkgName, '.')) {
+ progerr(ERR_PKGOPS_LOCHIGH_BAD_PKGNAME, pkgName);
+ quit(99);
+ }
+
+ /* create wild card specification for this package instance */
+
+ (void) snprintf(pkgWild, sizeof (pkgWild), "%s.*", pkgName);
+
+ echoDebug(DBG_PKGOPS_LOCHIGH_WILDCARD, pkgName, pkgWild);
+
+ /*
+ * inspect the system to determine if any instances of the
+ * package being installed already exist on the system
+ */
+
+ for (npkgs = 0; ; npkgs++) {
+ char *savePkgdir;
+ int r;
+
+ /* allocate new pinfo structure for use in the pkginfo call */
+
+ pinf = _pkginfoFactory();
+
+ /*
+ * lookup the specified package; the first call will cause the
+ * pkgdir directory to be opened - it will be closed when the
+ * end of directory is read and pkginfo() returns != 0. You must
+ * cycle through all instances until pkginfo() returns != 0.
+ * NOTE: pkginfo() requires the global variable 'pkgdir' be set
+ * to the package installed directory (<root>/var/sadm/pkg).
+ */
+
+ savePkgdir = pkgdir;
+ pkgdir = pkgInstPath;
+
+ r = pkginfo(pinf, pkgWild, NULL, NULL);
+
+ pkgdir = savePkgdir;
+
+ echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, pkgName, r);
+
+ /* break out of loop of no package found */
+
+ if (r != 0) {
+ pkginfoFree(&pinf);
+ break;
+ }
+
+ echoDebug(DBG_PKGOPS_LOCHIGH_INSTANCE, npkgs,
+ pinf->pkginst ? pinf->pkginst : "",
+ pinf->name ? pinf->name : "",
+ pinf->arch ? pinf->arch : "",
+ pinf->version ? pinf->version : "",
+ pinf->vendor ? pinf->vendor : "",
+ pinf->basedir ? pinf->basedir : "",
+ pinf->catg ? pinf->catg : "",
+ pinf->status);
+
+ /* save path/instance name for this instance found */
+
+ (void) strlcpy(r_pkgInst, pinf->pkginst, r_pkgInstLen);
+ pkgstrPrintf_r(r_path, r_pathLen, "%s%s/%s", a_rootPath,
+ PKGLOC, pinf->pkginst);
+
+ pkginfoFree(&pinf);
+ }
+
+ echoDebug(DBG_PKGOPS_LOCHIGH_RETURN, npkgs, r_pkgInst, r_path);
+}
+
+/*
+ * Name: pkgTestInstalled
+ * Description: determine if package is installed at specified root path
+ * Arguments: a_packageName - name of package to test
+ * a_rootPath - root path of alternative root to test
+ * Returns: B_TRUE - package is installed
+ * B_FALSE - package is not installed
+ */
+
+boolean_t
+pkgTestInstalled(char *a_packageName, char *a_rootPath)
+{
+ char cmd[MAXPATHLEN+1];
+ int rc;
+
+ /* entry assertions */
+
+ assert(a_packageName != (char *)NULL);
+ assert(*a_packageName != '\0');
+ assert(a_rootPath != (char *)NULL);
+ assert(a_rootPath != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKG_TEST_EXISTENCE, a_packageName, a_rootPath);
+
+ /*
+ * create pkginfo command to execute:
+ * /usr/bin/pkginfo -q <packageName>
+ */
+ (void) snprintf(cmd, sizeof (cmd),
+ "%s -q %s", PKGINFO_CMD, a_packageName);
+
+ /* execute command */
+
+ rc = system(cmd);
+
+ /* return success if pkginfo returns "0" */
+
+ if (rc == 0) {
+ echoDebug(DBG_PKG_INSTALLED, a_packageName, a_rootPath);
+ return (B_TRUE);
+ }
+
+ /* package not installed */
+
+ echoDebug(DBG_PKG_NOT_INSTALLED, a_packageName, a_rootPath);
+
+ return (B_FALSE);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+_pkginfoInit(struct pkginfo *a_info)
+{
+ /* entry assertions */
+
+ assert(a_info != (struct pkginfo *)NULL);
+
+ /* free previously allocated space */
+
+ if (a_info->pkginst) {
+ free(a_info->pkginst);
+ if (a_info->arch)
+ free(a_info->arch);
+ if (a_info->version)
+ free(a_info->version);
+ if (a_info->basedir)
+ free(a_info->basedir);
+ if (a_info->name)
+ free(a_info->name);
+ if (a_info->vendor)
+ free(a_info->vendor);
+ if (a_info->catg)
+ free(a_info->catg);
+ }
+
+ a_info->pkginst = NULL;
+ a_info->arch = a_info->version = NULL;
+ a_info->basedir = a_info->name = NULL;
+ a_info->vendor = a_info->catg = NULL;
+ a_info->status = PI_UNKNOWN;
+}
+
+static struct pkginfo *
+_pkginfoFactory(void)
+{
+ struct pkginfo *pinf;
+
+ pinf = (struct pkginfo *)calloc(1, sizeof (struct pkginfo));
+ if (pinf == (struct pkginfo *)NULL) {
+ progerr(ERR_MEM);
+ exit(1);
+ }
+
+ _pkginfoInit(pinf);
+ return (pinf);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/procmap.c b/usr/src/cmd/svr4pkg/libinst/procmap.c
new file mode 100644
index 0000000000..f9fd4f8e86
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/procmap.c
@@ -0,0 +1,403 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+
+#define ERR_MEMORY "memory allocation failure"
+#define ERR_DUPPATH "duplicate pathname <%s>"
+
+/* libpkg/gpkgmap */
+extern int getmapmode(void);
+
+#define EPTMALLOC 512
+
+static struct cfent **eptlist;
+
+static int eptnum;
+static int errflg;
+static int nparts;
+static int space = -1;
+
+static void procinit(void);
+static int procassign(struct cfent *ept, char **server_local,
+ char **client_local, char **server_path,
+ char **client_path, char **map_path, int mapflag,
+ int nc);
+
+static int ckdup(struct cfent *ept1, struct cfent *ept2);
+static int sortentry(int index);
+
+static void
+procinit(void)
+{
+ errflg = nparts = eptnum = 0;
+
+ if (space != -1) {
+ ar_free(space);
+ space = -1;
+ }
+
+ /*
+ * initialize dynamic memory used to store
+ * path information which is read in
+ */
+ (void) pathdup((char *)0);
+}
+
+/*
+ * This function assigns appropriate values based upon the pkgmap entry
+ * in the cfent structure.
+ */
+static int
+procassign(struct cfent *ept, char **server_local, char **client_local,
+ char **server_path, char **client_path, char **map_path, int mapflag,
+ int nc)
+{
+ int path_duped = 0;
+ int local_duped = 0;
+ char source[PATH_MAX+1];
+
+ if (nc >= 0 && ept->ftype != 'i')
+ if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
+ return (1);
+
+ if (ept->volno > nparts)
+ nparts++;
+
+ /*
+ * Generate local (delivered source) paths for files
+ * which need them so that the install routine will know
+ * where to get the file from the package. Note that we
+ * do not resolve path environment variables here since
+ * they won't be resolved in the reloc directory.
+ */
+ if ((mapflag > 1) && strchr("fve", ept->ftype)) {
+ if (ept->ainfo.local == NULL) {
+ source[0] = '~';
+ (void) strcpy(&source[1], ept->path);
+ ept->ainfo.local = pathdup(source);
+ *server_local = ept->ainfo.local;
+ *client_local = ept->ainfo.local;
+
+ local_duped = 1;
+ }
+ }
+
+ /*
+ * Evaluate the destination path based upon available
+ * environment, then produce a client-relative and
+ * server-relative canonized path.
+ */
+ if (mapflag && (ept->ftype != 'i')) {
+ mappath(getmapmode(), ept->path); /* evaluate variables */
+ canonize(ept->path); /* Fix path as necessary. */
+
+ (void) eval_path(server_path,
+ client_path,
+ map_path,
+ ept->path);
+ path_duped = 1; /* eval_path dup's it */
+ ept->path = *server_path; /* default */
+ }
+
+ /*
+ * Deal with source for hard and soft links.
+ */
+ if (strchr("sl", ept->ftype)) {
+ if (mapflag) {
+ mappath(getmapmode(), ept->ainfo.local);
+ if (!RELATIVE(ept->ainfo.local)) {
+ canonize(ept->ainfo.local);
+
+ /* check for hard link */
+ if (ept->ftype == 'l') {
+ (void) eval_path(
+ server_local,
+ client_local,
+ NULL,
+ ept->ainfo.local);
+ local_duped = 1;
+
+ /* Default to server. */
+ ept->ainfo.local = *server_local;
+ }
+ }
+ }
+ }
+
+ /*
+ * For the paths (both source and target) that were too mundane to
+ * have been copied into dup space yet, do that.
+ */
+ if (!path_duped) {
+ *server_path = pathdup(ept->path);
+ *client_path = *server_path;
+ ept->path = *server_path;
+
+ path_duped = 1;
+ }
+ if (ept->ainfo.local != NULL)
+ if (!local_duped) {
+ *server_local = pathdup(ept->ainfo.local);
+ ept->ainfo.local = *server_local;
+ *client_local = ept->ainfo.local;
+
+ local_duped = 1;
+ }
+
+ return (0);
+}
+
+/*
+ * This function reads the prototype file and returns a pointer to a list of
+ * struct cfent representing the contents of that file.
+ */
+/*ARGSUSED*/
+struct cfent **
+procmap(VFP_T *vfp, int mapflag, char *ir)
+{
+ struct cfent *ept = (struct cfent *)NULL;
+ struct cfent map_entry;
+ struct cfent **ept_ptr;
+ int i;
+ int n;
+ int nc;
+ static char *server_local, *client_local;
+ static char *server_path, *client_path, *map_path;
+
+ procinit();
+
+ space = ar_create(EPTMALLOC, (unsigned)sizeof (struct cfent),
+ "prototype object");
+ if (space == -1) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ nc = cl_getn();
+ for (;;) {
+ /* Clear the buffer. */
+ (void) memset(&map_entry, '\000', sizeof (struct cfent));
+
+ n = gpkgmapvfp(&map_entry, vfp);
+
+ if (n == 0)
+ break; /* no more entries in pkgmap */
+ else if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext("bad entry read in pkgmap"));
+ logerr(gettext("pathname=%s"),
+ (ept && ept->path && *ept->path) ?
+ ept->path : "Unknown");
+ logerr(gettext("problem=%s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ return (NULL);
+ }
+
+ /*
+ * A valid entry was found in the map, so allocate an
+ * official record.
+ */
+ ept_ptr = (struct cfent **)ar_next_avail(space);
+ if (ept_ptr == NULL || *ept_ptr == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ ept = *ept_ptr;
+
+ /* Transfer what we just read in. */
+ (void) memcpy(ept, &map_entry, sizeof (struct cfent));
+
+ if (procassign(ept, &server_local, &client_local,
+ &server_path, &client_path, &map_path,
+ mapflag, nc)) {
+ /* It didn't take. */
+ (void) ar_delete(space, eptnum);
+ continue;
+ }
+
+ eptnum++;
+ }
+
+ /* setup a pointer array to point to malloc'd entries space */
+ eptlist = (struct cfent **)ar_get_head(space);
+ if (eptlist == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ (void) sortentry(-1);
+ for (i = 0; i < eptnum; /* void */) {
+ if (!sortentry(i))
+ i++;
+ }
+ return (errflg ? NULL : eptlist);
+}
+
+/*
+ * This function sorts the final list of cfent entries. If index = -1, the
+ * function is initialized. index = 0 doesn't get us anywhere because this
+ * sorts against index-1. Positive natural index values are compared and
+ * sorted into the array appropriately. Yes, it does seem we should use a
+ * quicksort on the whole array or something. The apparent reason for taking
+ * this approach is that there are enough special considerations to be
+ * applied to each package object that inserting them one-by-one doesn't cost
+ * that much.
+ */
+static int
+sortentry(int index)
+{
+ struct cfent *ept, *ept_i;
+ static int last = 0;
+ int i, n, j;
+ int upper, lower;
+
+ if (index == 0)
+ return (0);
+ else if (index < 0) {
+ last = 0;
+ return (0);
+ }
+
+ /*
+ * Based on the index, this is the package object we're going to
+ * review. It may stay where it is or it may be repositioned in the
+ * array.
+ */
+ ept = eptlist[index];
+
+ /* quick comparison optimization for pre-sorted arrays */
+ if (strcmp(ept->path, eptlist[index-1]->path) > 0) {
+ /* do nothing */
+ last = index-1;
+ return (0);
+ }
+
+ lower = 0; /* lower bound of the unsorted elements */
+ upper = index; /* upper bound */
+ i = last;
+ do {
+ /*
+ * NOTE: This does a binary sort on path. There are lots of
+ * other worthy items in the array, but path is the key into
+ * the package database.
+ */
+ ept_i = eptlist[i];
+
+ n = strcmp(ept->path, ept_i->path);
+ if (n == 0) {
+ if (!ckdup(ept, ept_i)) {
+ progerr(gettext(ERR_DUPPATH),
+ ept->path);
+ errflg++;
+ }
+ /* remove the entry at index */
+ (void) ar_delete(space, index);
+
+ eptnum--;
+ return (1); /* Use this index again. */
+ } else if (n < 0) {
+ /*
+ * The path of interest is smaller than the path
+ * under test. Move down array using the method of
+ * division
+ */
+ upper = i;
+ i = lower + (upper-lower)/2;
+ } else {
+ /* Move up array */
+ lower = i+1;
+ i = upper - (upper-lower)/2 - 1;
+ }
+ } while (upper != lower);
+ last = i = upper;
+
+ /* expand to insert at i */
+ for (j = index; j > i; j--)
+ eptlist[j] = eptlist[j-1];
+
+ eptlist[i] = ept;
+
+ return (0);
+}
+
+/*
+ * Check duplicate entries in the package object list. If it's a directory,
+ * this just merges them, if not, it returns a 0 to force further processing.
+ */
+static int
+ckdup(struct cfent *ept1, struct cfent *ept2)
+{
+ /* ept2 will be modified to contain "merged" entries */
+
+ if (!strchr("?dx", ept1->ftype))
+ return (0);
+
+ if (!strchr("?dx", ept2->ftype))
+ return (0);
+
+ if (ept2->ainfo.mode == BADMODE)
+ ept2->ainfo.mode = ept1->ainfo.mode;
+ if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
+ (ept1->ainfo.mode != BADMODE))
+ return (0);
+
+ if (strcmp(ept2->ainfo.owner, "?") == 0)
+ (void) strcpy(ept2->ainfo.owner, ept1->ainfo.owner);
+ if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
+ strcmp(ept1->ainfo.owner, "?"))
+ return (0);
+
+ if (strcmp(ept2->ainfo.group, "?") == 0)
+ (void) strcpy(ept2->ainfo.group, ept1->ainfo.group);
+ if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
+ strcmp(ept1->ainfo.group, "?"))
+ return (0);
+
+ if (ept1->pinfo) {
+ ept2->npkgs = ept1->npkgs;
+ ept2->pinfo = ept1->pinfo;
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/psvr4ck.c b/usr/src/cmd/svr4pkg/libinst/psvr4ck.c
new file mode 100644
index 0000000000..505f696b95
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/psvr4ck.c
@@ -0,0 +1,417 @@
+/*
+ * 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 2006 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libinst.h>
+#include <libadm.h>
+
+#ifdef MAILCMD
+#undef MAILCMD
+#define MAILCMD "/bin/mail"
+#endif /* MAILCMD */
+#define ERR_MAIL "unable to send electronic mail notification"
+#define ERR_OVERWRITE "unable to determine overwrite list"
+#define ERR_PIPE "unable to open pipe to process <%s>"
+#define ASK_CONT "Do you want to continue processing this package"
+#define MSG_CONFLICT "The following files are currently being used by " \
+ "other packages on the system, and may be " \
+ "overwritten by the installation of this pre-SVR4 " \
+ "package:"
+#define HLP_CONFLICT "If you choose to continue installation, it is " \
+ "possible that you will overwrite files which are " \
+ "part of another package that is already installed " \
+ "on the system. If you want to assure that the " \
+ "files are not overwritten, answer 'n' to stop the " \
+ "installation process."
+#define MSG_NOTVER "The media being processed is in an old (pre-SVR4) " \
+ "format and it is not possible to verify that the " \
+ "inserted media belongs to the <%s> package."
+#define HLP_NOTVER "If you choose to continue installation, it is " \
+ "possible that you will install the wrong package. " \
+ "If you are sure the media being installed contains " \
+ "the package you wish to install, answer 'y' to " \
+ "continue the installation process."
+#define MSG_CONFIRM "The media being processed is in an old (pre-SVR4) " \
+ "format and appears to be part of the <%s> package."
+#define HLP_CONFIRM "The installation of older-style (pre-SVR4) packages " \
+ "is, in general, not as robust as installing " \
+ "standard packages. Older packages may attempt " \
+ "things during installation which overwrite existing " \
+ "files or otherwise modify the system without your " \
+ "approval. If you wish to allow installation of " \
+ "identified pre-SVR4 package, answer 'y' to continue " \
+ "the installation process."
+
+static char *Rlist[] = {
+ "/install/install/Rlist",
+ "/install/install/RLIST",
+ "/install/install/rlist",
+ NULL
+};
+
+static char ckcmd[] = "/usr/sbin/pkgchk -L -i %s";
+
+/*
+ * Remove the list & both #defines below for on1095 -- JST
+ * Further, please note :
+ * This is NOT a database (Oh, yeah it looks like it, but it isn't). For that
+ * reason these are in alphabetical order. Any additions must maintain this
+ * order and must not increase the list length beyond 120.
+ */
+#define TREEHEIGHT 7
+#define TREEFILL 4 /* number of fill entries per side */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+static char *x_pkg[] =
+{
+ "AAAA1", /* fill to avoid constraint tests in loop */
+ "AAAA2",
+ "AAAA3",
+ "AAAA4",
+ /* '+' means packages known to be non-compliant */
+ "SPROcpl", /* + bugID 1133962 */
+ "SPROlklnt", /* + SW Lock_Lint */
+ "SPROltool", /* + SW Loop Profiling Tools */
+ "SPROssbd", /* + SW ssbd component for SC 3.0 */
+ "SPROtha", /* + Performance Analzyer */
+ "SUNW3270c", /* + SunLink Client 3270 */
+ "SUNW3270g", /* SunLink CG3270 8.0 */
+ "SUNW3270t", /* + SunLink TN3270*Server */
+ "SUNW86nma", /* SunNet Manager Core Tools for x86 */
+ "SUNW86nmc", /* SunNet Manager Agents & Libraries for x86 */
+ "SUNW86nmp", /* SunNet Manager SNMP daemon for x86 */
+ "SUNWabcg", /* SunLink CG320 8.0 User's Guide */
+ "SUNWbf", /* + 2.0 FDDI/S Beta */
+ "SUNWbsccu", /* SunLink BSC Core Util */
+ "SUNWbscdr", /* SunLink BSC Drivers */
+ "SUNWcosiA", /* OSI Core Stack Kernel Files 1 */
+ "SUNWcosiC", /* Stack Mgmnt Utilities 2 */
+ "SUNWcosia", /* + OSI Core Stack Kernel Files */
+ "SUNWcosib", /* OSI Core Stack Configuration Files */
+ "SUNWcosic", /* OSI Core Stack Utilities */
+ "SUNWcosid", /* OSI Core Stack Development Kit (new pakage) */
+ "SUNWcosij", /* OSI Core Stack User Space Utilities */
+ "SUNWdniCU", /* + SunLink DNI Core Utilities 8.0 */
+ "SUNWdniKR", /* + SunLink DNI Kernel 8.0 */
+ "SUNWdniMA", /* SunLink DNI Mail Agent 8.0 */
+ "SUNWflex", /* + FLEX LM DEVEL PKG */
+ "SUNWftama", /* OSI FTAM Configuration Files */
+ "SUNWftamb", /* OSI FTAM Executable, Libraries and Man Pages */
+ "SUNWhsis", /* SunConnect HSI/S */
+ "SUNWjaCL", /* + Frances Ho confirms for SUNpics */
+ "SUNWjncmt", /* SunNet Manager Core Tools(Japan) */
+ "SUNWjnmag", /* SunNet Manager Agents & Libraries (Japan) */
+ "SUNWjnmpd", /* SunNet Manager SNMP daemon(Japan) */
+ "SUNWlicsw", /* + FLEXlm */
+ "SUNWlit", /* STE LIC INSTALL TOOL */
+ "SUNWllc2a", /* X.25 LLC2 KRNL MOD, INCLDS FL */
+ "SUNWllc2b", /* X.25 USR PROG, MAN PAGES */
+ "SUNWmd", /* + Suhas Patil request 1994-07-12 */
+ "SUNWmhs1a", /* MHS Message Transfer Agent Configuration Files */
+ "SUNWmhs1b", /* MHS Message Transfer Agent Executable and Man Pgs */
+ "SUNWomgta", /* OSI Mgmnt Configuration Files */
+ "SUNWomgtb", /* OSI Mgmnt Configuration Files */
+ "SUNWomgtc", /* OSI Mgmnt SunNet Mgr Proxy Agent Executable Files */
+ "SUNWomgtd", /* OSI Mgmnt SunNet Mgr Proxy Agent Config Files */
+ "SUNWp2pnm", /* SunLink SNA Peer-to-Peer Network Management */
+ "SUNWprsto", /* + Varun Mehta request 1994-07-11 */
+ "SUNWrup2p", /* Sunlink SNA Peer-to-Peer Run Time Environment */
+ "SUNWs3270", /* + SunLink SNA3270/RJE */
+ "SUNWscmmd", /* SunLink Comm Daemon */
+ "SUNWsdlc", /* SunLink IBM SDLC */
+ "SUNWsm-ml", /* ShowMe Motif Libs */
+ "SUNWsm-ol", /* ShowMe Online help */
+ "SUNWsmCmg",
+ "SUNWsmap", /* SunLink Mapper */
+ "SUNWsmaud", /* ShowMe Audio */
+ "SUNWsmsha", /* ShowMe SharedApp */
+ "SUNWsmvid", /* ShowMe Video */
+ "SUNWsmwtb", /* ShowMe Whiteboard */
+ "SUNWsnmag", /* + Steve Wong request 1994-02-15 */
+ "SUNWsnmct", /* + Steve Wong request 1994-02-15 */
+ "SUNWsnmja", /* SunNet Manager 2.2 Japanese feature */
+ "SUNWsnmpd", /* SunNet Manager SNMP daemon */
+ "SUNWsnp2p", /* + SunLink SNA P-to-P */
+ "SUNWspii", /* 1.0 SPARCprinterII */
+ "SUNWsrjec", /* + SunLink Client SNA RJE */
+ "SUNWsteCL", /* + Frances Ho confirms for SUNPics */
+ "SUNWsteNP", /* 2.5 NeWSprint */
+ "SUNWte320", /* + TE320 8.0 */
+ "SUNWtris", /* SunConnect TRI/S */
+ "SUNWvtcfg", /* OSI Virtual Terminal Configuration Files */
+ "SUNWvtexe", /* OSI Virtual Terminal User Program and Man Pages */
+ "SUNWx25a", /* + X.25 KRNL MOD, INCLDS FLS */
+ "SUNWx25b", /* + X.25 USR PROG AND LIB */
+ "zzzz1", /* fill to avoid constraint tests in loop */
+ "zzzz2",
+ "zzzz3",
+ "zzzz4"
+};
+#endif
+
+/*
+ * Structure to hold the list of pkg names that are known to not behave
+ * properly when sym link destinations are not followed.
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+static char *x_pkg_link[] =
+{
+ "AAAA1", /* fill to avoid constraint tests in loop */
+ "AAAA2",
+ "AAAA3",
+ "AAAA4",
+ /* '+' means packages known to be non-compliant */
+ "SUNWixfta",
+ "SUNWixsna",
+ "zzzz1", /* fill to avoid constraint tests in loop */
+ "zzzz2",
+ "zzzz3",
+ "zzzz4"
+};
+#endif
+
+/*
+ * This function determines if the package being added is a known old-style
+ * package which requires user interaction during procedure scripts. It is
+ * to be removed for on1095. -- JST
+ * It also is used for the determining if a pkg is known to have symlinks
+ * that need to be processed the old way.
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+int
+exception_pkg(char *pkginst, int pkg_list)
+{
+ int retvalue = 0;
+ int list_sz;
+ int list_cntr; /* starting point for binary search */
+ register int pos; /* current position */
+ register int level; /* current height in the tree */
+ register int incr; /* increment for step */
+ int result; /* result of strcmp */
+ register char **x_ptr = x_pkg;
+ register char **x_ptr_link = x_pkg_link;
+ char *pkgend;
+ char *pkgname = strdup(pkginst);
+
+ /*
+ * NOTE : If more structures need to be defined the following if
+ * statement needs to be revised to handle multiple flags
+ */
+
+ if (pkg_list)
+ list_sz = (sizeof (x_pkg_link) / sizeof (char *));
+ else
+ list_sz = (sizeof (x_pkg) / sizeof (char *));
+
+ /*
+ * NOTE : shifts are used instead of integer division to save
+ * time. Numerous other checks are omitted also. This tree
+ * contains double nodes but is entirely connected and closed.
+ */
+
+ list_cntr = list_sz >> 1;
+ incr = list_cntr - TREEFILL;
+
+ pkgend = strchr(pkgname, '.');
+
+ if (pkgend)
+ *pkgend = '\0'; /* terminate the instance to a name */
+
+ for (level = TREEHEIGHT, /* start at the top level */
+ pos = list_cntr; /* ... in the middle */
+ level; /* for as long as we're in the tree */
+ level--, pos += (result > 0) ? incr : -incr) {
+
+ if (pkg_list)
+ result = strcmp(pkgname, *(x_ptr_link + pos));
+ else
+ result = strcmp(pkgname, *(x_ptr + pos));
+
+ if (result == 0) {
+ retvalue = 1;
+ break;
+ }
+
+ incr = (incr & 0x0001) | (incr >> 1); /* halve it & rnd up */
+ }
+
+ free(pkgname);
+
+ return (retvalue);
+}
+
+#endif
+
+void
+psvr4pkg(char **ppkg)
+{
+ struct dirent *drp;
+ DIR *dirfp;
+ char *pt;
+ int n;
+ char ans[MAX_INPUT], path[PATH_MAX];
+
+ if (*ppkg) {
+ (void) snprintf(path, sizeof (path),
+ "/install/new/usr/options/%s.name",
+ *ppkg);
+ if (access(path, 0)) {
+ ptext(stderr, gettext(MSG_NOTVER), *ppkg);
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_NOTVER),
+ gettext(ASK_CONT)))
+ quit(n);
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+ }
+ return;
+ }
+
+ if (dirfp = opendir("/install/new/usr/options")) {
+ while (drp = readdir(dirfp)) {
+ if (drp->d_name[0] == '.')
+ continue;
+ if (pt = strchr(drp->d_name, '.')) {
+ if (strcmp(pt, ".name") == 0) {
+ *pt = '\0';
+ *ppkg = qstrdup(drp->d_name);
+ break;
+ }
+ }
+ }
+ (void) closedir(dirfp);
+ }
+
+ if (*ppkg) {
+ ptext(stderr, gettext(MSG_CONFIRM), *ppkg);
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFIRM),
+ gettext(ASK_CONT)))
+ quit(n);
+ } else {
+ ptext(stderr, gettext(MSG_NOTVER), *ppkg);
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_NOTVER),
+ gettext(ASK_CONT)))
+ quit(n);
+ }
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+}
+
+void
+psvr4cnflct(void)
+{
+ FILE *pp;
+ int n, found;
+ char *pt,
+ ans[MAX_INPUT],
+ cmd[PATH_MAX+sizeof (ckcmd)],
+ path[PATH_MAX];
+
+ for (n = 0; Rlist[n] != NULL; n++) {
+ if (access(Rlist[n], 0) == 0)
+ break;
+ }
+ if (Rlist[n] == NULL)
+ return; /* Rlist file not found on device */
+
+ (void) sprintf(cmd, ckcmd, Rlist[n]);
+ echo(gettext("## Checking for conflicts with installed packages"));
+ echo(gettext(" (using %s provided by pre-SVR4 package)"), Rlist[n]);
+ if ((pp = popen(cmd, "r")) == NULL) {
+ progerr(gettext(ERR_PIPE), cmd);
+ progerr(gettext(ERR_OVERWRITE));
+ quit(99);
+ }
+
+ found = 0;
+ while (fgets(path, PATH_MAX, pp)) {
+ if (!found++)
+ ptext(stderr, gettext(MSG_CONFLICT));
+ if (pt = strpbrk(path, " \t\n"))
+ *pt = '\0';
+ echo("\t%s", path);
+ }
+ if (pclose(pp)) {
+ progerr(gettext(ERR_OVERWRITE));
+ quit(99);
+ }
+
+ if (found) {
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFLICT),
+ gettext(ASK_CONT)))
+ quit(n);
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+ }
+}
+
+void
+psvr4mail(char *list, char *msg, int retcode, char *pkg)
+{
+ struct utsname utsbuf;
+ FILE *pp;
+ char cmd[BUFSIZ];
+
+ if (list == NULL)
+ return;
+
+ while (isspace(*list))
+ list++;
+ if (*list == '\0')
+ return;
+
+ /* send e-mail notifications */
+ (void) snprintf(cmd, sizeof (cmd), "%s %s", MAILCMD, list);
+ if ((pp = popen(cmd, "w")) == NULL) {
+ progerr(gettext(ERR_PIPE), MAILCMD);
+ progerr(gettext(ERR_MAIL));
+ quit(99);
+ }
+
+ (void) strcpy(utsbuf.nodename, gettext("(unknown)"));
+ (void) uname(&utsbuf);
+ ptext(pp, msg, pkg, utsbuf.nodename, retcode);
+
+ if (pclose(pp)) {
+ progerr(gettext(ERR_MAIL));
+ quit(99);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/ptext.c b/usr/src/cmd/svr4pkg/libinst/ptext.c
new file mode 100644
index 0000000000..c2ab4f2bde
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/ptext.c
@@ -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 1993 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 <stdarg.h>
+#include "libadm.h"
+
+/*VARARGS*/
+void
+ptext(FILE *fp, char *fmt, ...)
+{
+ va_list ap;
+ char buffer[2048];
+
+ va_start(ap, fmt);
+
+ (void) vsprintf(buffer, fmt, ap);
+
+ va_end(ap);
+
+ (void) puttext(fp, buffer, 0, 70);
+ (void) putc('\n', fp);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/putparam.c b/usr/src/cmd/svr4pkg/libinst/putparam.c
new file mode 100644
index 0000000000..532e29bf53
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/putparam.c
@@ -0,0 +1,311 @@
+/*
+ * 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 <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 <assert.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+static char *localeNames[] = {
+ "LC_CTYPE",
+ "LC_NUMERIC",
+ "LC_TIME",
+ "LC_COLLATE",
+ "LC_MESSAGES",
+ "LC_MONETARY",
+ "LC_ALL",
+ "LANG",
+ "TZ",
+ NULL
+};
+
+#define NUM_LOCALE_TYPES 100
+
+static char *envPtr[NUM_LOCALE_TYPES];
+
+/*
+ * extern declarations
+ */
+
+extern char **environ;
+
+/*
+ * this is the initial and incremental allocation used to
+ * populate the environment "environ"
+ */
+
+#define MALSIZ 64
+
+void
+putparam(char *param, char *value)
+{
+ char *pt;
+ int ptlen;
+ int i, n;
+
+ /*
+ * If the environment is NULL, allocate space for the
+ * character pointers.
+ */
+ if (environ == NULL) {
+ environ = (char **)calloc(MALSIZ, sizeof (char *));
+ if (environ == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+
+ /*
+ * If this parameter is already in place and it has a different
+ * value, clear the old value by freeing the memory previously
+ * allocated. Otherwise, we leave well-enough alone.
+ */
+ n = strlen(param);
+ for (i = 0; environ[i]; i++) {
+ if (strncmp(environ[i], param, n) == 0 &&
+ (environ[i][n] == '=')) {
+ if (strcmp((environ[i]) + n + 1, value) == 0)
+ return;
+ else {
+ free(environ[i]);
+ break;
+ }
+ }
+ }
+
+ /* Allocate space for the new environment entry. */
+ ptlen = (strlen(param)+strlen(value)+2)*(sizeof (char));
+ pt = (char *)calloc(strlen(param)+strlen(value)+2, sizeof (char));
+ if (pt == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ /*
+ * Put the statement into the allocated space and point the
+ * environment entry at it.
+ */
+ (void) snprintf(pt, ptlen, "%s=%s", param, value);
+ if (environ[i]) {
+ environ[i] = pt;
+ return;
+ }
+
+ /*
+ * With this parameter in place, if we're at the end of the
+ * allocated environment then allocate more space.
+ */
+ environ[i++] = pt;
+ if ((i % MALSIZ) == 0) {
+ environ = (char **)realloc((void *)environ,
+ (i+MALSIZ)*sizeof (char *));
+ if (environ == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(1);
+ }
+ }
+
+ /* Terminate the environment properly. */
+ environ[i] = (char *)NULL;
+}
+
+/* bugid 4279039 */
+void
+getuserlocale(void)
+{
+ int i;
+
+ for (i = 0; (localeNames[i] != NULL) && (i < NUM_LOCALE_TYPES); i++) {
+ envPtr[i] = getenv(localeNames[i]);
+ if (envPtr[i]) {
+ putparam(localeNames[i], envPtr[i]);
+ }
+ }
+}
+
+/* bugid 4279039 */
+void
+putuserlocale(void)
+{
+ int i;
+
+ for (i = 0; (localeNames[i] != NULL) && (i < NUM_LOCALE_TYPES); i++) {
+ if (envPtr[i]) {
+ putparam(localeNames[i], envPtr[i]);
+ }
+ }
+}
+
+/*
+ * Name: putConditionInfo
+ * Description: put parent "condition" information to environment
+ * Arguments: a_parentZoneName - name of the parent zone
+ * == NULL - no name
+ * a_parentZoneType - parent zone "type"
+ * == NULL - no type
+ * Returns: void
+ */
+
+void
+putConditionInfo(char *a_parentZoneName, char *a_parentZoneType)
+{
+ char **pp;
+ char *p;
+ char *pa;
+ SML_TAG *tag = SML_TAG__NULL;
+ SML_TAG *ntag;
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PUTPARAM_PUTCONDINFO_ENTRY);
+
+ /*
+ * create tag to hold condition information:
+ * <environmentConditionInformation>
+ * <parentZone zoneName=<?> zoneType=<?>/>
+ * <currentZone zoneName=<?> zoneType=<?>/>
+ * <inheritedFileSystem fileSystemName=<?>/>
+ * </environmentConditionInformation>
+ */
+
+ tag = smlNewTag(TAG_COND_TOPLEVEL);
+
+ /*
+ * information about pkgadd or pkgrm environment
+ * <parentZone zoneName=<?> zoneType=<?>/>
+ */
+
+ /* allocate tag for parent info */
+
+ ntag = smlNewTag(TAG_COND_PARENT_ZONE);
+
+ /* parent zone name */
+
+ smlSetParam(ntag, TAG_COND_ZONE_NAME,
+ a_parentZoneName ? a_parentZoneName : "");
+
+ /* parent zone info */
+
+ smlSetParam(ntag, TAG_COND_ZONE_TYPE,
+ a_parentZoneType ? a_parentZoneType : "");
+
+ /* add to top level tag */
+
+ (void) smlAddTag(&tag, -1, ntag);
+ free(ntag);
+
+ /*
+ * information about pkginstall or pkgremove environment
+ * <currentZone zoneName=<?> zoneType=<?>/>
+ */
+
+ /* allocate tag for parent info */
+
+ ntag = smlNewTag(TAG_COND_CURRENT_ZONE);
+
+ /* current zone name */
+
+ p = z_get_zonename();
+ if ((p != NULL) && (*p != '\0')) {
+ smlSetParam(ntag, TAG_COND_ZONE_NAME, p);
+ free(p);
+ }
+
+ /* current zone type */
+
+ smlSetParam(ntag, TAG_COND_ZONE_TYPE,
+ z_running_in_global_zone() == B_TRUE ?
+ TAG_VALUE_GLOBAL_ZONE : TAG_VALUE_NONGLOBAL_ZONE);
+
+ /* add to top level tag */
+
+ (void) smlAddTag(&tag, -1, ntag);
+ free(ntag);
+
+ /*
+ * describe any inherited file systems:
+ * <inheritedFileSystem fileSystemName=<?>/>
+ */
+
+ pp = z_get_inherited_file_systems();
+ if (pp != (char **)NULL) {
+ int n;
+ for (n = 0; pp[n] != (char *)NULL; n++) {
+ /* allocate tag for inherited file system info */
+
+ ntag = smlNewTag(TAG_COND_INHERITED_FS);
+
+ /* inherited file system */
+
+ smlSetParam(ntag, TAG_COND_FS_NAME, pp[n]);
+
+ /* add to top level tag */
+
+ (void) smlAddTag(&tag, -1, ntag);
+ free(ntag);
+ }
+ }
+
+ /*
+ * done filling in tag - convert to string and place in environment
+ */
+
+ p = smlConvertTagToString(tag);
+
+ /* convert all new-line characters to space */
+
+ for (pa = p; *pa != '\0'; pa++) {
+ if (*pa == '\n') {
+ *pa = ' ';
+ }
+ }
+
+ echoDebug(DBG_PUTPARAM_PUTCONDINFO_EXIT, p);
+
+ putparam(PKGCOND_GLOBAL_VARIABLE, p);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/qreason.c b/usr/src/cmd/svr4pkg/libinst/qreason.c
new file mode 100644
index 0000000000..0a3f8cc7ec
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/qreason.c
@@ -0,0 +1,428 @@
+/*
+ * 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 2004 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 <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/*
+ * forward declarations
+ */
+
+static char *qreasonNoZonename(int caller, int retcode, int started);
+static char *qreasonWithZonename(int caller, int retcode, int started);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: qreason
+ * Description: return message describing specified "quit reason"
+ * Arguments: caller - integer describing the "caller:
+ * Caller identities:
+ * 0 - pkginstall - pkgask
+ * 1 - pkginstall - pkgadd
+ * 2 - pkginstall - mailmsg
+ * 3 - pkgremove - quitmsg
+ * 4 - pkgremove - mailmsg
+ * retcode - integer return code describing "reason"
+ * includeZonename - integer describing zone for reason
+ * == 0 - do not include a zone name in the message
+ * != 0 - include a zone name in the message
+ * Returns: char *
+ * NOTE: all messages are returned from static space that does not need
+ * to be free()ed when no longer needed
+ * NOTE: imbedded "%s"s in returned messages are consistent with the
+ * caller and zone name inclusion:
+ * 0 - no %s's
+ * 1 - one %s - package name
+ * 2 - three %s - package name, rootpath, package instance
+ * 3 - one %s - package name
+ * 4 - two %s - package name, rootpath
+ * If "includeZonename" is true, an extra "%s" is added at the
+ * end of each message for the zone name to be included.
+ */
+
+char *
+qreason(int caller, int retcode, int started, int includeZonename)
+{
+ if (includeZonename == 0) {
+ return (qreasonNoZonename(caller, retcode, started));
+ }
+
+ return (qreasonWithZonename(caller, retcode, started));
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static char *
+qreasonNoZonename(int caller, int retcode, int started)
+{
+ switch (retcode) {
+ case 0:
+ case 10:
+ case 20:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUC);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUC0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUC1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUC0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUC1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 1:
+ case 11:
+ case 21:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_FAIL);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_FAIL0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_FAIL1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_FAIL0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_FAIL1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 2:
+ case 12:
+ case 22:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_PARFAIL);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_PARFAIL0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_PARFAIL1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_PARFAIL0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_PARFAIL1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 3:
+ case 13:
+ case 23:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_USER);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_USER0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_USER1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_USER0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_USER1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 4:
+ case 14:
+ case 24:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUA);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUA0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUA1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUA0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUA1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 5:
+ case 15:
+ case 25:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUI);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUI0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUI1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUI0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUI1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ case 99:
+ if (started) {
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_IEPI);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_IEPI0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_IEPI1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_IEPI0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_IEPI1);
+ default:
+ return (MSG_UNKREQ);
+ }
+ }
+
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_IE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_IE0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_IE1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_IE0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_IE1);
+ default:
+ return (MSG_UNKREQ);
+ }
+
+ default:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_UNK);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_UNK0);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_UNK1);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_UNK0);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_UNK1);
+ default:
+ return (MSG_UNKREQ);
+ }
+ }
+}
+
+static char *
+qreasonWithZonename(int caller, int retcode, int started)
+{
+ switch (retcode) {
+ case 0:
+ case 10:
+ case 20:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUC_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUC0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUC1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUC0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUC1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 1:
+ case 11:
+ case 21:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_FAIL_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_FAIL0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_FAIL1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_FAIL0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_FAIL1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 2:
+ case 12:
+ case 22:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_PARFAIL_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_PARFAIL0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_PARFAIL1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_PARFAIL0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_PARFAIL1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 3:
+ case 13:
+ case 23:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_USER_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_USER0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_USER1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_USER0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_USER1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 4:
+ case 14:
+ case 24:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUA_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUA0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUA1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUA0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUA1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 5:
+ case 15:
+ case 25:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_SUI_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_SUI0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_SUI1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_SUI0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_SUI1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ case 99:
+ if (started) {
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_IEPI_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_IEPI0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_IEPI1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_IEPI0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_IEPI1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+ }
+
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_IE_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_IE0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_IE1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_IE0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_IE1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+
+ default:
+ switch (caller) {
+ case 0: /* pkginstall - pkgask */
+ return (MSG_RE_UNK_ZONE);
+ case 1: /* pkginstall - pkgadd */
+ return (MSG_IN_UNK0_ZONE);
+ case 2: /* pkginstall - mailmsg */
+ return (MSG_IN_UNK1_ZONE);
+ case 3: /* pkgremove - quitmsg */
+ return (MSG_RM_UNK0_ZONE);
+ case 4: /* pkgremove - mailmsg */
+ return (MSG_RM_UNK1_ZONE);
+ default:
+ return (MSG_UNKREQ_ZONE);
+ }
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/qstrdup.c b/usr/src/cmd/svr4pkg/libinst/qstrdup.c
new file mode 100644
index 0000000000..e642d96fd1
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/qstrdup.c
@@ -0,0 +1,57 @@
+/*
+ * 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 2006 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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libinst.h>
+
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+
+char *
+qstrdup(char *s)
+{
+ register char *pt = NULL;
+
+ if (s && *s) {
+ pt = calloc((strlen(s) + 1), sizeof (char));
+ if (pt == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ (void) strcpy(pt, s);
+ }
+ return (pt);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/scriptvfy.l b/usr/src/cmd/svr4pkg/libinst/scriptvfy.l
new file mode 100644
index 0000000000..7280d0d100
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/scriptvfy.l
@@ -0,0 +1,786 @@
+/*
+ * 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 1995 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * The purpose of this lex specification is to estimate the
+ * correctness of the various scripts that accompany packages. It
+ * is not flawless, but it is a better review than that of prior
+ * package validators. It looks for indications of interaction,
+ * root calls and attempts to modify locked files.
+ */
+%e 1500
+%p 3500
+%s WHROOT
+%{
+#undef input
+#undef unput
+FILE *scr_fp;
+#define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(scr_fp))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)
+#define unput(p) ungetc(p, scr_fp)
+
+#define INTERACT_D 0x00000001 /* definitely */
+#define ROOT_D 0x00000002
+#define LOCKED_D 0x00000004
+#define INTERACT_M 0x00010000 /* might be true, or we ... */
+#define ROOT_M 0x00020000 /* ... might be reading it wrong. */
+#define LOCKED_M 0x00040000
+#define WPARM1_M 0x00080000 /* attempt to write to $1 */
+#define USEPARM1_M 0x00100000 /* other attempt to use $1 */
+#define ODDPARM_M 0x00200000 /* use of some other parameter */
+#define PKGDB_M 0x00400000 /* read access to DB */
+#define INITVAL 0x40000000
+
+/* Abbreviations */
+#define INTERACT (INTERACT_D | INTERACT_M)
+#define ROOT (ROOT_D | ROOT_M)
+#define LOCKED (LOCKED_D | LOCKED_M)
+#define HASPARM (WPARM1_M | USEPARM1_M | ODDPARM_M)
+
+/* Things the preinstall and preremove scripts can't do. */
+#define PRE_MASK (INTERACT | LOCKED | PKGDB_M | HASPARM)
+/*
+ * Things the class action script can't do. Don't get the impression that
+ * this means the class action script can be interactive; but, it can
+ * legitimately read stdin (which is what INTERACT tests for).
+ */
+#define CAS_MASK (LOCKED | PKGDB_M | WPARM1_M | ODDPARM_M)
+/* Things the postinstall and postremove scripts can't do. */
+#define POST_MASK (INTERACT | HASPARM)
+/* Things the request script can't do. */
+#define REQ_MASK (ROOT | ODDPARM_M)
+/* Things the checkinstall script can't do. */
+#define CHK_MASK (INTERACT | ROOT | ODDPARM_M)
+
+/* Nothing definite - not worth returning an error */
+#define MAYBE_ONLY ~(INTERACT_D | ROOT_D | LOCKED_D)
+
+#define WRN_INST_F "WARNING: script <%s> uses installf but no " \
+ "installf -f was detected."
+#define WRN_REM_F "WARNING: script <%s> uses removef but no " \
+ "removef -f was detected."
+#define WRN_INTERACT "WARNING: script <%s> may require " \
+ "user interaction at line <%d>."
+#define WRN_LOCKED "WARNING: script <%s> may seek access to the " \
+ "transitional package database at line <%d>. " \
+ "This is safest in the postinstall or " \
+ "postremove script."
+#define WRN_ROOT "WARNING: script <%s> may not have permission " \
+ "to execute line <%d>."
+#define WRN_FORM_ARG "WARNING: not sure where script <%s> gets the "\
+ "parameter at line <%d>."
+#define WRN_FORM_USE "WARNING: script <%s> questionable usage of "\
+ "parameter at line <%d>."
+#define WRN_TRANSDB "WARNING: script <%s> questionable read " \
+ "of package database at line <%d>. An " \
+ "intermediate buffer may be appropriate."
+#define WRN_SPACEACC "WARNING: script <%s> updates the package database " \
+ "but provides no space file to account for " \
+ "the additional package object."
+#define ERR_INTERACT "ERROR: script <%s> requires user " \
+ "interaction at line <%d>."
+#define ERR_LOCKED "ERROR: script <%s> attempts to modify locked " \
+ "package database at line <%d>."
+#define ERR_ROOT "ERROR: script <%s> requires root permission at " \
+ "line <%d>."
+#define ERR_FOPEN "ERROR: Cannot evaluate script <%s>, errno=%d."
+#define ERR_ARGS "ERROR: scripteval() - no script provided for " \
+ "evaluation."
+extern int errno;
+
+static int line_no; /* current line number */
+int pipe_release = 0; /* loop level for release of pipe */
+int loop_depth = 0; /* current number of nested loops */
+int case_depth = 0; /* same for case ... */
+int if_depth = 0; /* ... and if statements */
+int cur_level = 0; /* current number of nested anything */
+int braces = 0; /* depth into a function */
+
+int lock_level = 0;
+int root_level = 0;
+
+struct statstrct {
+ unsigned int in_function:1;
+ unsigned int in_pipe:1;
+ unsigned int in_loop:1;
+ unsigned int in_case:1;
+ unsigned int in_if:1;
+ unsigned int in_awk:1;
+ unsigned int allow_int:1; /* Allow an interactive function. */
+ unsigned int pkg_rtn_done:1;
+ unsigned int pkgchk_f:1;
+ unsigned int instf:1;
+ unsigned int instf_f:1;
+ unsigned int remf:1;
+ unsigned int remf_f:1;
+ unsigned int nospacefile:1;
+ unsigned int needspacefile:1;
+} status = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+%}
+%%
+%{
+/*
+ * Validate a few OK patterns that look like bad patterns. These include:
+ * 1. comments
+ * 2. quoted strings
+ * 3. writes to $1 (request script)
+ * 4. reads from $1 (CAS)
+ * 5. writes to /dev/null
+ */
+%}
+#.*$ return INITVAL;
+
+\` unput(' '); /* No executable matching */
+
+%{
+/* Anybody can write to /dev/null and anybody can write to /tmp. */
+%}
+\>[ \t]*"/dev/null" return INITVAL;
+\>[ \t]*"/tmp" return INITVAL;
+
+%{
+/* If it's escaped, the next entry may as well be a space. */
+%}
+\\ {
+ char ch;
+
+ if ((ch = input()) == '\n')
+ line_no++;
+
+ unput(' ');
+}
+
+%{
+/* In the quotes is OK. */
+%}
+\" {
+ char ch;
+ while ((ch = input()) != '\"') {
+ if (ch == '\\') {
+ input(); /* Read this into the bit bucket. */
+ continue;
+ }
+ if (ch == '\n')
+ line_no++;
+ else if (ch == '\0')
+ return (0); /* EOF */
+ }
+}
+
+%{
+/* In the single quotes is OK if they aren't associated with an awk script. */
+%}
+\' {
+ char ch;
+
+ if (status.in_awk != 0)
+ REJECT;;
+
+ while ((ch = input()) != '\'') {
+ if (ch == '\\') {
+ input(); /* Read this into the bit bucket. */
+ continue;
+ }
+ if (ch == '\n')
+ line_no++;
+ else if (ch == '\0')
+ return (0); /* EOF */
+ }
+}
+
+%{
+/*
+ * Check for use of parameters passed to the script.
+ * 1. writes to $1 as though it were a file
+ * 2. use of $1 in any capacity
+ * 3. use of other parameters
+ * Within a function or an awk script, these parameters aren't
+ * the one's of interest.
+ */
+%}
+\>[\t ]*\$1/[\t\n ] {
+ if (status.in_function == 0 && status.in_awk == 0)
+ return (WPARM1_M);
+}
+
+^$1/[\t\n ] |
+[\t ]$1/[\t\n ] {
+ if (status.in_function == 0 && status.in_awk == 0)
+ return (USEPARM1_M);
+}
+
+\$[2-9] |
+\$[0-9][0-9]+ {
+ if (status.in_function == 0 && status.in_awk == 0)
+ return (ODDPARM_M);
+}
+
+%{
+/*
+ * Detect shell function.
+ */
+%}
+"()"[ \t]*\n[ \t]*/\{ { status.in_function = 1; line_no++; }
+"()"[ ]*/\{ status.in_function = 1;
+
+"{" {
+ if (status.in_function == 1)
+ braces++;
+}
+
+"}" {
+ if (status.in_function == 1) {
+ braces--;
+ if (braces == 0)
+ status.in_function = 0;
+ }
+}
+
+%{
+/*
+ * Detect for or while loop.
+ */
+%}
+^for/[\t\n ] |
+[\t ]for/[\t\n ] |
+^while/[\t\n ] |
+[\t ]while/[\t\n ] {
+ status.in_loop = 1;
+ loop_depth++;
+ cur_level++;
+ REJECT; /* What's in the argument is important too. */
+}
+
+^done/[\t\n ] |
+[\t ]done/[\t\n ] {
+ if (status.in_loop == 1)
+ loop_depth--;
+ cur_level--;
+ if (loop_depth == 0)
+ status.in_loop = 0;
+}
+
+%{
+/*
+ * Detect case.
+ */
+%}
+^case/[\t\n ] |
+[\t ]case/[\t\n ] {
+ status.in_case = 1;
+ case_depth++;
+ cur_level++;
+ REJECT; /* What's in the argument is important too. */
+}
+
+^esac/[\t\n ] |
+[\t ]esac/[\t\n ] {
+ if (status.in_case == 1)
+ case_depth--;
+ cur_level--;
+ if (case_depth == 0)
+ status.in_case = 0;
+}
+
+%{
+/*
+ * Detect if.
+ */
+%}
+^if" "*"[" |
+[\t ]if" "*"[" {
+ status.in_if = 1;
+ if_depth++;
+ cur_level++;
+ REJECT; /* What's in the argument is important too. */
+}
+
+^fi/[\t\n ] |
+[\t ]fi/[\t\n ] {
+ if (status.in_if == 1)
+ if_depth--;
+ cur_level--;
+ if (if_depth == 0)
+ status.in_if = 0;
+}
+
+%{
+/*
+ * Detect awk or nawk function. If the function is enclosed in "`"s
+ * the entire line will be grabbed., so we check for that possibility.
+ */
+%}
+^n?awk[^\n^']*\' |
+[\t \\\(\/]n?awk[^\n^']*\' status.in_awk = 1;
+
+
+\' {
+ if (status.in_awk == 1)
+ status.in_awk = 0;
+}
+
+%{
+/* Detect pipe target. */
+%}
+[\$A-Za-z] {
+ if (status.in_pipe == 1 && pipe_release == cur_level)
+ {
+ status.in_pipe = 0; /* target located */
+ pipe_release = 0;
+ status.allow_int = 1; /* this isn't really interactive. */
+ REJECT; /* put it back */
+ }
+}
+
+%{
+/* If it's a pipe, note that and continue. */
+%}
+"||" |
+"|" {
+ if (status.in_pipe == 0) {
+ status.in_pipe = 1;
+ pipe_release = cur_level;
+ }
+}
+
+%{
+/*
+ * Test input for admin-type telltale interactive functions. Definite's
+ * first, maybe's next.
+ */
+%}
+^ckdate/[\t\n ] |
+[\t \/]ckdate/[\t\n ] |
+^ckint/[\t\n ] |
+[\t \/]ckint/[\t\n ] |
+^ckrange/[\t\n ] |
+[\t \/]ckrange/[\t\n ] |
+^cktime/[\t\n ] |
+[\t \/]cktime/[\t\n ] |
+^ckyorn/[\t\n ] |
+[\t \/]ckyorn/[\t\n ] |
+^ckgid/[\t\n ] |
+[\t \/]ckgid/[\t\n ] |
+^ckpath/[\t\n ] |
+[\t \/]ckpath/[\t\n ] |
+^ckstr/[\t\n ] |
+[\t \/]ckstr/[\t\n ] |
+^ckuid/[\t\n ] |
+[\t \/]ckuid/[\t\n ] {
+ if (status.in_pipe == 1 || status.allow_int == 1)
+ return (INITVAL);
+ else
+ return (INTERACT_M); /* maybe should be _D */
+}
+
+^read/[\t\n ] |
+[\t ]read/[\t\n ] |
+"=[ ]+&<"[\t ] {
+ if (status.in_pipe == 1 || status.allow_int == 1)
+ return (INITVAL);
+ else
+ return (INTERACT_M);
+}
+
+%{
+/* Scan for root authority commands. Definite's first, maybe's next. */
+%}
+^mkdir/[\t\n ] |
+[\t \/]mkdir/[\t\n ] |
+^mv/[\t\n ] |
+[\t \/]mv/[\t\n ] |
+^cpio/[\t\n ] |
+[\t \/]cpio/[\t\n ] |
+^tar/[\t\n ] |
+[\t \/]tar/[\t\n ] |
+^(un)?compress/[\t\n ] |
+[\t \/](un)?compress/[\t\n ] |
+^rmdir/[\t\n ] |
+[\t \/]rmdir/[\t\n ] return (ROOT_D);
+
+^r?cp(dir)?/[\t\n ] |
+[\t \/]r?cp(dir)?/[\t\n ] |
+^rm/[\t\n ] |
+[\t \/]rm/[\t\n ] |
+\>[ \t]*[\$\/a-zA-Z0-9] return (ROOT_M);
+
+%{
+/* These root commands may also be locked. */
+
+/* Here we analyze any pkgchk calls. If it's "pkgchk ... -f ..." then that calls for root authority. We then check for a "-R" argument. */
+%}
+^pkgchk[^\n^|^>^;]*"-f" |
+[\t \/]pkgchk[^\n^|^>^;]*"-f" {
+ status.pkgchk_f = 1;
+ REJECT; /* We need the intermediate args. */
+}
+
+%{
+/* If it's "pkgchk ... -R ..." then the local package database is not being tested and no database warning is necessary. */
+%}
+^pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n] |
+[\t \/]pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n] {
+ if (status.pkgchk_f)
+ return (ROOT_D);
+ else
+ return (INITVAL);
+}
+
+%{
+/* If it's just "pkgchk ..." then we need to mention something about access to the package database. With Solaris 2.5, an improved locking mechanism is in place, so this message may be something we can drop later. */
+%}
+^pkgchk/[\t\n ] |
+[\t \/]pkgchk/[\t\n ] {
+ if (status.pkgchk_f) {
+ status.pkgchk_f = 0;
+ return (ROOT_D | PKGDB_M);
+ } else
+ return (PKGDB_M);
+}
+
+%{
+/* The installf and removef utilities require root authority, they modify the package database and they must be invoked at least once with a "-f" argument. */
+
+/* First test for a "-f" argument. */
+%}
+^installf[^\n^|^>^;]*"-f" |
+[\t \/]installf[^\n^|^>^;]*"-f" {
+ status.instf_f = 1;
+
+ REJECT; /* The whole line needs to be re-reviewed. */
+}
+
+^removef[^\n^|^>^;]*"-f" |
+[\t \/]removef[^\n^|^>^;]*"-f" {
+ status.remf_f = 1;
+
+ REJECT; /* The whole line needs to be re-reviewed. */
+}
+
+^installf/[\t\n ] |
+[\t \/]installf/[\t\n ] {
+ status.instf = 1;
+ status.needspacefile = 1;
+
+ root_level = ROOT_D;
+ lock_level = LOCKED_M;
+
+ BEGIN WHROOT;
+}
+
+^removef/[\t\n ] |
+[\t \/]removef/[\t\n ] {
+ status.remf = 1;
+
+ root_level = ROOT_D;
+ lock_level = LOCKED_M;
+ BEGIN WHROOT;
+}
+
+%{
+/* There's no question that use of a pkgadd or pkgrm in a script is bound to cause problems unless it is to a different root. */
+%}
+^pkgadd/[\t\n ] |
+[\t \/]pkgadd/[\t\n ] |
+^pkgrm/[\t\n ] |
+[\t \/]pkgrm/[\t\n ] {
+ root_level = ROOT_D;
+ lock_level = LOCKED_D;
+ BEGIN WHROOT;
+}
+
+%{
+/* The only way to get here is if we are in the middle of a pkg command. */
+%}
+<WHROOT>. {
+ if (status.pkg_rtn_done) {
+ status.pkg_rtn_done = 0;
+ BEGIN 0;
+ } else
+ REJECT;
+}
+<WHROOT>[ \t]+"-R"[ \t][\/\$]/[^ ^\t^\n] {
+ status.pkg_rtn_done = 1;
+ return (root_level); /* "-R" means locking is unlikely. */
+}
+<WHROOT>[\n] {
+ if (status.pkg_rtn_done) {
+ status.pkg_rtn_done = 0;
+ line_no++;
+ BEGIN 0;
+ } else {
+ status.pkg_rtn_done = 1;
+ unput('\n');
+ return (root_level | lock_level); /* No "-R". */
+ }
+}
+<WHROOT>[;|>] {
+ status.pkg_rtn_done = 1;
+ return (root_level | lock_level); /* End of command without a "-R". */
+}
+
+\n { line_no++; status.allow_int = 0; }
+. status.allow_int = 0;
+%%
+#include <stdio.h>
+#include <limits.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#ifdef DEBUG
+/*
+ * Since this is a lex specification twice removed from the binary,
+ * I strongly recommend leaving the DEBUG portions in place. When new
+ * keywords are added, this will be very important. After modifying
+ * the specification, create an executable to test in this way.
+ *
+ * lex scriptvfy.l
+ * cc -o scriptvfy -g lex.yy.c $ROOT/usr/lib/libpkg.a \
+ * -DDEBUG [-DVERBOSE] -ll -lintl
+ * scriptvfy test_directory
+ */
+
+main(int argc, char *argv[])
+{
+ int val;
+
+ line_no = 1;
+
+ if (argc == 1) {
+ printf("No directory provided.\n");
+ exit(1);
+ }
+
+ val = checkscripts(argv[1], 0);
+
+ printf("return code is %d\n", val);
+}
+#endif
+
+/*
+ * This function evaluates the provided script and returns a bit string
+ * describing what patterns were located.
+ */
+static int
+scripteval(char *script_name, char *script_path, int mask, int silent)
+{
+ int val = 0;
+ int error = 0;
+ line_no = 1;
+
+ if ((script_path == NULL) || (*script_path == NULL) ||
+ (script_name == NULL)) {
+ logerr(gettext(ERR_ARGS));
+ return (0);
+ }
+
+#ifdef VERBOSE
+ printf("Evaluating %s\n", script_path);
+#endif
+
+ if ((scr_fp = fopen(script_path, "r")) == NULL) {
+ logerr(gettext(ERR_FOPEN), script_path, errno);
+ return (0);
+ }
+
+#ifdef VERBOSE
+ printf("Openned script\n");
+#endif
+
+ while (val = yylex()) {
+#ifdef VERBOSE
+ printf(" Match is %s, returned 0x%x at line %d\n",
+ yytext, val, line_no);
+ printf(" in_function = %d, in_awk = %d, in_loop = %d, " \
+ "in_case = %d, in_if = %d, in_pipe = %d\n",
+ status.in_function, status.in_awk, status.in_loop,
+ status.in_case, status.in_if, status.in_pipe);
+ printf(" loop_depth = %d, case_depth = %d, " \
+ "if_depth = %d, pipe_release = %d, cur_level = %d\n",
+ loop_depth, case_depth, if_depth, pipe_release, cur_level);
+#endif
+
+ val &= mask;
+ if (val) {
+ error |= ((val & MAYBE_ONLY) ? 1 : 2);
+
+ /*
+ * So at this point, val contains all status bits
+ * appropriate to this script.
+ */
+ if (!silent) {
+ char *msg_ptr;
+ if (val & INTERACT_D)
+ msg_ptr = gettext(ERR_INTERACT);
+ else if (val & ROOT_D)
+ msg_ptr = gettext(ERR_ROOT);
+ else if (val & LOCKED_D)
+ msg_ptr = gettext(ERR_LOCKED);
+ else if (val & INTERACT_M)
+ msg_ptr = gettext(WRN_INTERACT);
+ else if (val & ROOT_M)
+ msg_ptr = gettext(WRN_ROOT);
+ else if (val & LOCKED_M)
+ msg_ptr = gettext(WRN_LOCKED);
+ else if (val & WPARM1_M)
+ msg_ptr = gettext(WRN_FORM_USE);
+ else if (val & USEPARM1_M)
+ msg_ptr = gettext(WRN_FORM_USE);
+ else if (val & ODDPARM_M)
+ msg_ptr = gettext(WRN_FORM_ARG);
+ else if (val & PKGDB_M)
+ msg_ptr = gettext(WRN_TRANSDB);
+ else
+ msg_ptr = gettext("unknown error");
+
+ logerr(msg_ptr, script_name, line_no);
+ }
+ }
+ }
+
+ /* Warn if required about missing "-f" calls. */
+ if (status.instf && !(status.instf_f))
+ logerr(gettext(WRN_INST_F), script_name);
+
+ if (status.remf && !(status.remf_f))
+ logerr(gettext(WRN_REM_F), script_name);
+
+ status.instf = status.instf_f = status.remf = status.remf_f = 0;
+
+ /* Warn if installf was used but no space file is in place. */
+ if (status.nospacefile && status.needspacefile) {
+ logerr(gettext(WRN_SPACEACC), script_name);
+ status.needspacefile = 0;
+ }
+
+ status.in_pipe = 0; /* Pipes may dangle. */
+ fclose(scr_fp);
+
+ if (error == 3)
+ error = 2;
+
+ return (error);
+}
+
+/* Test a preinstall or preremove script for validity. */
+int
+pre_valid(char *script_name, char *script_path, int silent)
+{
+ return (scripteval(script_name, script_path, PRE_MASK, silent));
+}
+
+/* Test a class action script for validity. */
+int
+cas_valid(char *script_name, char *script_path, int silent)
+{
+ return (scripteval(script_name, script_path, CAS_MASK, silent));
+}
+
+/* Test a postinstall or postremove script for validity. */
+int
+post_valid(char *script_name, char *script_path, int silent)
+{
+ return (scripteval(script_name, script_path, POST_MASK, silent));
+}
+
+/* Test a class action script for validity. */
+int
+req_valid(char *script_name, char *script_path, int silent)
+{
+ return (scripteval(script_name, script_path, REQ_MASK, silent));
+}
+
+
+/* Test a class action script for validity. */
+int
+chk_valid(char *script_name, char *script_path, int silent)
+{
+ return (scripteval(script_name, script_path, CHK_MASK, silent));
+}
+
+/* This tests all of the scripts in the provided directory. */
+int
+checkscripts(char *inst_dir, int silent)
+{
+ DIR *dirfp;
+ struct dirent *dp;
+ char path[PATH_MAX];
+ int retval = 0;
+
+ /* For future reference, determine if a space file is present. */
+ sprintf(path, "%s/%s", inst_dir, "space");
+ if (access(path, F_OK) != 0)
+ status.nospacefile = 1;
+
+ if ((dirfp = opendir(inst_dir)) == NULL)
+ return (0);
+
+ while ((dp = readdir(dirfp)) != NULL) {
+#ifdef VERBOSE
+ printf("Looking at file %s\n", dp->d_name);
+#endif
+ if (dp->d_name[0] == '.')
+ continue;
+
+ if ((strcmp(dp->d_name, "preinstall") == 0) ||
+ (strcmp(dp->d_name, "preremove") == 0)) {
+ sprintf(path, "%s/%s", inst_dir, dp->d_name);
+ retval |= pre_valid(dp->d_name, path, silent);
+ continue;
+ }
+
+ if ((strncmp(dp->d_name, "i.", 2) == 0) ||
+ (strncmp(dp->d_name, "r.", 2) == 0)) {
+ sprintf(path, "%s/%s", inst_dir, dp->d_name);
+ retval |= cas_valid(dp->d_name, path, silent);
+ continue;
+ }
+
+ if ((strcmp(dp->d_name, "postinstall") == 0) ||
+ (strcmp(dp->d_name, "postremove") == 0)) {
+ sprintf(path, "%s/%s", inst_dir, dp->d_name);
+ retval |= post_valid(dp->d_name, path, silent);
+ continue;
+ }
+
+ if (strcmp(dp->d_name, "request") == 0) {
+ sprintf(path, "%s/%s", inst_dir, dp->d_name);
+ retval |= req_valid(dp->d_name, path, silent);
+ continue;
+ }
+ if (strcmp(dp->d_name, "checkinstall") == 0) {
+ sprintf(path, "%s/%s", inst_dir, dp->d_name);
+ retval |= chk_valid(dp->d_name, path, silent);
+ continue;
+ }
+ }
+
+ (void) closedir(dirfp);
+
+ return (retval);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/setadmin.c b/usr/src/cmd/svr4pkg/libinst/setadmin.c
new file mode 100644
index 0000000000..e9f36534a4
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/setadmin.c
@@ -0,0 +1,336 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <pkgweb.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+#define DEFMAIL "root"
+
+extern struct admin adm; /* holds info about install admin */
+extern int warnflag; /* != 0 non-fatal error occurred 2 */
+
+static struct {
+ char **memloc;
+ char *tag;
+} admlist[] = {
+ &adm.action, "action",
+ &adm.authentication, "authentication",
+ &adm.basedir, "basedir",
+ &adm.conflict, "conflict",
+ &adm.idepend, "idepend",
+ &adm.instance, "instance",
+ &adm.keystore, "keystore",
+ &adm.mail, "mail",
+ &adm.networkretries, "networkretries",
+ &adm.networktimeout, "networktimeout",
+ &adm.partial, "partial",
+ &adm.proxy, "proxy",
+ &adm.rdepend, "rdepend",
+ &adm.RSCRIPTALT, RSCRIPTALT_KEYWORD,
+ &adm.runlevel, "runlevel",
+ &adm.setuid, "setuid",
+ &adm.space, "space",
+ /* MUST BE LAST ENTRY IN LIST */
+ (char **)NULL, (char *)NULL
+};
+
+/*
+ * Name: setadminSetting
+ * Description: set one administration parameter setting
+ * Arguments: a_paramName - pointer to string representing the name of
+ * the administration parameter to set
+ * a_paramValue - pointer to string representing the value
+ * to set the specified administration parameter to
+ * Returns: char *
+ * - old value the parameter had before being set
+ * == NULL - the old paramter was not set
+ */
+
+char *
+setadminSetting(char *a_paramName, char *a_paramValue)
+{
+ char *oldValue = (char *)NULL;
+ int i;
+
+ /* locate and update the specified admin setting */
+
+ for (i = 0; admlist[i].memloc; i++) {
+ if (strcmp(a_paramName, admlist[i].tag) == 0) {
+ oldValue = *admlist[i].memloc;
+ *admlist[i].memloc = a_paramValue;
+ break;
+ }
+ }
+
+ if (admlist[i].memloc == (char **)NULL) {
+ logerr(WRN_UNKNOWN_ADM_PARAM, a_paramName);
+ }
+
+ return (oldValue);
+}
+
+/*
+ * Name: setadminFile
+ * Description: read and remember settings from administration file
+ * Arguments: file - pointer to string representing the path to the
+ * administration file to read - if this is NULL
+ * then the name "default" is used - if this is
+ * the string "none" then the admin "basedir"
+ * setting is set to "ask" so that the location
+ * of the administration file will be interactively
+ * asked at the appropriate time
+ * Returns: void
+ */
+
+void
+setadminFile(char *file)
+{
+ FILE *fp;
+ int i;
+ char param[MAX_PKG_PARAM_LENGTH];
+ char *value;
+ char path[PATH_MAX];
+ int mail = 0;
+
+ if (file == NULL)
+ file = "default";
+ else if (strcmp(file, "none") == 0) {
+ adm.basedir = "ask";
+ return;
+ }
+
+ if (file[0] == '/')
+ (void) strcpy(path, file);
+ else {
+ (void) snprintf(path, sizeof (path), "%s/admin/%s",
+ get_PKGADM(), file);
+ if (access(path, R_OK)) {
+ (void) snprintf(path, sizeof (path), "%s/admin/%s",
+ PKGADM, file);
+ }
+ }
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ progerr(ERR_OPEN_ADMIN_FILE, file, strerror(errno));
+ quit(99);
+ }
+
+ param[0] = '\0';
+ while (value = fpkgparam(fp, param)) {
+ if (strcmp(param, "mail") == 0) {
+ mail = 1;
+ }
+ if (value[0] == '\0') {
+ param[0] = '\0';
+ continue; /* same as not being set at all */
+ }
+ for (i = 0; admlist[i].memloc; i++) {
+ if (strcmp(param, admlist[i].tag) == 0) {
+ *admlist[i].memloc = value;
+ break;
+ }
+ }
+ if (admlist[i].memloc == NULL) {
+ logerr(WRN_UNKNOWN_ADM_PARAM, param);
+ free(value);
+ }
+ param[0] = '\0';
+ }
+
+ (void) fclose(fp);
+
+ if (!mail) {
+ adm.mail = DEFMAIL; /* if we don't assign anything to it */
+ }
+}
+
+
+/*
+ * Function: web_ck_retries
+ * Description: Reads admin file setting for networkretries, or uses default
+ * Parameters: None
+ * Returns: admin file setting for networkretries, or the default if no
+ * admin file setting exists or if it is outside the
+ * allowable range.
+ */
+int
+web_ck_retries(void)
+{
+ int retries = NET_RETRIES_DEFAULT;
+
+ if (ADMSET(networkretries)) {
+ /* Make sure value is within valid range */
+ if ((retries = atoi(adm.networkretries)) == 0) {
+ return (NET_RETRIES_DEFAULT);
+ } else if (retries <= NET_RETRIES_MIN ||
+ retries > NET_RETRIES_MAX) {
+ return (NET_RETRIES_DEFAULT);
+ }
+ }
+ return (retries);
+}
+
+/*
+ * Function: web_ck_authentication
+ * Description: Retrieves admin file setting for authentication
+ * Parameters: None
+ * Returns: admin file policy for authentication - AUTH_QUIT
+ * or AUTH_NOCHECK.
+ * non-zero failure
+ */
+int
+web_ck_authentication(void)
+{
+ if (ADM(authentication, "nocheck"))
+ return (AUTH_NOCHECK);
+
+ return (AUTH_QUIT);
+}
+
+/*
+ * Function: web_ck_timeout
+ * Description: Retrieves admin file policy for networktimeout's
+ * Parameters: NONE
+ * Returns: Admin file setting for networktimeout, or default
+ * timeout value if admin file does not specify one,
+ * or specifies one that is out of the allowable range.
+ */
+int
+web_ck_timeout(void)
+{
+ int timeout = NET_TIMEOUT_DEFAULT;
+
+ if (ADMSET(networktimeout)) {
+ /* Make sure value is within valid range */
+ if ((timeout = atoi(adm.networktimeout)) == 0) {
+ return (NET_TIMEOUT_DEFAULT);
+ } else if (timeout <= NET_TIMEOUT_MIN ||
+ timeout > NET_TIMEOUT_MAX) {
+ return (NET_TIMEOUT_DEFAULT);
+ }
+ }
+ return (timeout);
+}
+
+/*
+ * Function: check_keystore_admin
+ * Description: Retrieves security keystore setting from admin file,
+ * or validates user-supplied keystore policy.
+ * Parameters: keystore - Where to store resulting keystore policy
+ * Returns: B_TRUE - admin file contained valid keystore, or
+ * user-supplied keystore passed in "keystore" was
+ * valid. Resulting keystore stored in "keystore"
+ *
+ * B_FALSE - No location supplied to store result,
+ * or user-supplied keystore was not valid.
+ */
+boolean_t
+check_keystore_admin(char **keystore)
+{
+
+ if (!keystore) {
+ /* no location to store keystore */
+ return (B_FALSE);
+ }
+
+ if (*keystore != NULL) {
+ if (!path_valid(*keystore)) {
+ /* the given keystore is invalid */
+ return (B_FALSE);
+ }
+
+ /* the user-supplied keystore was valid */
+ return (B_TRUE);
+ }
+
+ /* no user-supplied, so use default */
+ if ((*keystore = set_keystore_admin()) == NULL) {
+ *keystore = PKGSEC;
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Function: get_proxy_port_admin
+ * Description: Retrieves proxy setting from admin file
+ * Parameters: proxy - where to store resulting proxy (host:port or URL)
+ * port - Where to store resulting proxy port
+ * Returns: B_TRUE - admin file had a valid proxy setting,
+ * and it is stored in "proxy".
+ * B_FALSE - no proxy setting in admin file, or
+ * invalid setting in admin file.
+ */
+boolean_t
+get_proxy_port_admin(char **proxy, ushort_t *port)
+{
+ if (ADMSET(proxy) && !path_valid(adm.proxy)) {
+ /* admin file has bad keystore */
+ return (B_FALSE);
+ } else if (ADMSET(proxy)) {
+ *proxy = strdup(adm.proxy);
+ *port = strip_port(adm.proxy);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Function: set_keystore_admin
+ * Description: Retrieves security keystore setting from admin file,
+ * Parameters: NONE
+ * Returns: Keystore file policy from admin file, if set
+ * and valid. NULL otherwise.
+ */
+char *
+set_keystore_admin(void)
+{
+ if (ADMSET(keystore) && !path_valid(adm.keystore)) {
+ return (NULL);
+ }
+
+ if (!ADMSET(keystore)) {
+ return (NULL);
+ }
+
+ return (adm.keystore);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/setlist.c b/usr/src/cmd/svr4pkg/libinst/setlist.c
new file mode 100644
index 0000000000..31fd9f17f1
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/setlist.c
@@ -0,0 +1,437 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include "libinst.h"
+
+int cl_NClasses = -1;
+static int cl_handle = -1; /* list array handle */
+
+struct cl_attr **cl_Classes = NULL;
+
+static int new_order;
+static struct cl_attr *new_cl_attr(char *cl_name);
+
+static unsigned s_verify(char *class_name), d_verify(char *class_name);
+static unsigned s_pathtype(char *class_name);
+
+#define MALSIZ 64
+#define ERR_MEMORY "memory allocation failure"
+
+static struct cl_attr *
+new_cl_attr(char *cl_name)
+{
+ struct cl_attr *class, **class_ptr;
+
+ if (cl_handle == -1) {
+ cl_handle = ar_create(MALSIZ, sizeof (struct cl_attr),
+ "package class");
+ if (cl_handle == -1) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+ }
+
+ class_ptr = (struct cl_attr **)ar_next_avail(cl_handle);
+
+ if (class_ptr == NULL || *class_ptr == NULL) {
+ progerr(gettext(ERR_MEMORY));
+ return (NULL);
+ }
+
+ class = *class_ptr;
+
+ strcpy(class->name, cl_name);
+ class->inst_script = NULL;
+ class->rem_script = NULL;
+ class->src_verify = s_verify(cl_name);
+ class->dst_verify = d_verify(cl_name);
+ class->relpath_2_CAS = s_pathtype(cl_name);
+
+ return (class);
+}
+
+/* Insert a single class into the list. */
+void
+addlist(struct cl_attr ***listp, char *item)
+{
+ int i;
+
+ /* If the list is already there, scan for this item */
+ if (*listp) {
+ for (i = 0; (*listp)[i]; i++)
+ if (strcmp(item, (*listp)[i]->name) == 0)
+ return;
+ } else {
+ i = 0;
+ }
+
+ /* Insert the new entry */
+ if (new_cl_attr(item) == NULL)
+ quit(99);
+
+ /* Point the passed pointer to the head of the list. */
+ (*listp) = (struct cl_attr **)ar_get_head(cl_handle);
+}
+
+/*
+ * Create a list of all classes involved in this installation as well as
+ * their attributes.
+ */
+int
+setlist(struct cl_attr ***plist, char *slist)
+{
+ struct cl_attr **list, *struct_ptr;
+ char *pt;
+ int n;
+ int i;
+ int sn = -1;
+
+ /* Initialize the environment scanners. */
+ (void) s_verify(NULL);
+ (void) d_verify(NULL);
+ (void) s_pathtype(NULL);
+
+ n = 0;
+
+ /*
+ * This looks like a serious memory leak, however pkgmk depends on
+ * this creating a second list and forgetting any prior ones. The
+ * pkgmk utility does a reasonable job of keeping track of a prior
+ * list constructed from the prototype file using addlist() above.
+ * Perhaps this should be reviewed later, but I do not believe this
+ * to be a problem from what I've seen. - JST
+ */
+ cl_handle = -1; /* forget other lists */
+
+ /* Isolate the first token. */
+ pt = strtok(slist, " \t\n");
+ while (pt) {
+ if (sn == -1 && strcmp(pt, "none") == 0)
+ sn = n;
+
+ /* Add new class to list. */
+ if ((struct_ptr = new_cl_attr(pt)) == NULL)
+ quit(99);
+
+ /* Next token. */
+ n++;
+ pt = strtok(NULL, " \t\n");
+ if (pt && sn != -1)
+ if (strcmp(pt, "none") == 0)
+ pt = strtok(NULL, " \t\n");
+ }
+ /*
+ * According to the ABI, if there is a class "none", it will be
+ * the first class to be installed. This insures that iff there
+ * is a class "none", it will be the first to be installed.
+ * If there is no class "none", nothing happens!
+ */
+ new_order = 0;
+
+ /* Get the head of the array. */
+ list = (struct cl_attr **)ar_get_head(cl_handle);
+
+ if (sn > 0) {
+ struct_ptr = list[sn];
+ for (i = sn; i > 0; i--)
+ list[i] = list[i - 1];
+ list[0] = struct_ptr;
+ new_order++; /* the order is different now */
+ }
+
+ /* Point the passed pointer to the head of the list. */
+ *plist = list;
+
+ return (n);
+}
+
+/* Process the class list from the caller. */
+void
+cl_sets(char *slist)
+{
+ char *list_ptr;
+
+ /* If there is a list, process it; else skip it */
+ if (slist && *slist) {
+ list_ptr = qstrdup(slist);
+
+ if (list_ptr && *list_ptr) {
+ cl_NClasses = setlist(&cl_Classes, list_ptr);
+ if (new_order) /* if list order changed ... */
+ /* ... tell the environment. */
+ cl_putl("CLASSES", cl_Classes);
+ }
+ }
+}
+
+int
+cl_getn(void)
+{
+ return (cl_NClasses);
+}
+
+/*
+ * Since the order may have changed, this puts the CLASSES list back into
+ * the environment in the precise order to be used.
+ */
+void
+cl_putl(char *parm_name, struct cl_attr **list)
+{
+ int i;
+ size_t j;
+ char *pt = NULL;
+
+ if (list && *list) {
+ j = 1; /* room for ending null */
+ for (i = 0; list[i]; i++)
+ j += strlen(list[i]->name) + 1;
+ pt = calloc(j, sizeof (char));
+ (void) strcpy(pt, list[0]->name);
+ for (i = 1; list[i]; i++) {
+ (void) strcat(pt, " ");
+ (void) strcat(pt, list[i]->name);
+ }
+ if (parm_name && *parm_name)
+ putparam(parm_name, pt);
+ free(pt);
+ }
+}
+
+
+int
+cl_idx(char *cl_nam)
+{
+ int n;
+
+ for (n = 0; n < cl_NClasses; n++)
+ if (strcmp(cl_Classes[n]->name, cl_nam) == 0)
+ return (n);
+ return (-1);
+}
+
+/* Return source verification level for this class */
+unsigned
+cl_svfy(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->src_verify);
+ return (0);
+}
+
+/* Return destination verify level for this class */
+unsigned
+cl_dvfy(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->dst_verify);
+ return (0);
+}
+
+/* Return path argument type for this class. */
+unsigned
+cl_pthrel(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->relpath_2_CAS);
+ return (0);
+}
+
+/* Return the class name associated with this class index */
+char *
+cl_nam(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ return (cl_Classes[idx]->name);
+ return (NULL);
+}
+
+void
+cl_setl(struct cl_attr **cl_lst)
+{
+ int i;
+ int sn = -1;
+ struct cl_attr *pt;
+
+ if (cl_lst) {
+ for (cl_NClasses = 0; cl_lst[cl_NClasses]; cl_NClasses++)
+ if (strcmp(cl_lst[cl_NClasses]->name, "none") == 0)
+ if (sn == -1)
+ sn = cl_NClasses;
+ if (sn > 0) {
+ pt = cl_lst[sn];
+ for (i = sn; i > 0; i--)
+ cl_lst[i] = cl_lst[i - 1];
+ cl_lst[0] = pt;
+ }
+ i = 1;
+ while (i < cl_NClasses) {
+ if (strcmp(cl_lst[i]->name, "none") == 0)
+ for (sn = i; sn < (cl_NClasses - 1); sn++)
+ cl_lst[sn] = cl_lst[sn + 1];
+ i++;
+ }
+ cl_Classes = cl_lst;
+ } else {
+ cl_Classes = NULL;
+ cl_NClasses = -1;
+ }
+}
+
+/*
+ * Scan the given environment variable for an occurrance of the given
+ * class name. Return 0 if not found or 1 if found.
+ */
+static unsigned
+is_in_env(char *class_name, char *paramname, char **paramvalue, int *noentry)
+{
+ unsigned retval = 0;
+ char *test_class;
+
+ if (class_name && *class_name) {
+ /*
+ * If a prior getenv() has not failed and there is no
+ * environment string then get environment info on
+ * this parameter.
+ */
+ if (!(*noentry) && *paramvalue == NULL) {
+ *paramvalue = getenv(paramname);
+ if (*paramvalue == NULL)
+ (*noentry)++;
+ }
+
+ /* If there's something there, evaluate it. */
+ if (!(*noentry)) {
+ int n;
+
+ n = strlen(class_name); /* end of class name */
+ test_class = *paramvalue; /* environ ptr */
+
+ while (test_class = strstr(test_class, class_name)) {
+ /*
+ * At this point we have a pointer to a
+ * substring within param that matches
+ * class_name for its length, but class_name
+ * may be a substring of the test_class, so
+ * we check that next.
+ */
+ if (isspace(*(test_class + n)) ||
+ *(test_class + n) == '\0') {
+ retval = 1;
+ break;
+ }
+ if (*(++test_class) == '\0')
+ break;
+ }
+ }
+ }
+ return (retval);
+}
+
+/* Assign source path verification level to this class */
+static unsigned
+s_verify(char *class_name)
+{
+ static int noentry;
+ static char *noverify;
+
+ if (class_name == NULL) { /* initialize */
+ noentry = 0;
+ noverify = NULL;
+ } else {
+ if (is_in_env(class_name, "PKG_SRC_NOVERIFY", &noverify,
+ &noentry))
+ return (NOVERIFY);
+ else
+ return (DEFAULT);
+ }
+ return (0);
+}
+
+/*
+ * Set destination verify to default. This is usually called by pkgdbmerg()
+ * in order to correct verification conflicts.
+ */
+void
+cl_def_dverify(int idx)
+{
+ if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+ cl_Classes[idx]->dst_verify = DEFAULT;
+}
+
+/* Assign destination path verification level to this path. */
+static unsigned
+d_verify(char *class_name)
+{
+ static int noentry;
+ static char *qkverify;
+
+ if (class_name == NULL) { /* initialize */
+ noentry = 0;
+ qkverify = NULL;
+ } else {
+ if (is_in_env(class_name, "PKG_DST_QKVERIFY", &qkverify,
+ &noentry))
+ return (QKVERIFY);
+ else
+ return (DEFAULT);
+ }
+ return (0);
+}
+
+/* Assign CAS path type to this class */
+static unsigned
+s_pathtype(char *class_name)
+{
+ static int noentry;
+ static char *type_list;
+
+ if (class_name == NULL) { /* initialize */
+ noentry = 0;
+ type_list = NULL;
+ } else {
+ if (is_in_env(class_name, "PKG_CAS_PASSRELATIVE", &type_list,
+ &noentry))
+ return (REL_2_CAS);
+ else
+ return (DEFAULT);
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.c b/usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.c
new file mode 100644
index 0000000000..e67091acd5
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.c
@@ -0,0 +1,111 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pwd.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * Name: setup_temporary_directory
+ * Description: create a temporary directory from specified components
+ * and return full path to the directory created
+ * Arguments: r_dirname - pointer to handle to string - on success,
+ * the full path to the temporary directory created
+ * is returned in this handle
+ * a_tmpdir - pointer to string representing the directory into
+ * which the new temporary directory should be created
+ * a_suffix - pointer to string representing the 5-character
+ * suffix to be used as the first part of the temporary
+ * directory name invented
+ * Returns: boolean_t
+ * == B_TRUE - temporary directory created, path returned
+ * == B_FALSE - failed to create temporary directory
+ * 'errno' is set to the failure reason
+ * NOTE: Any path returned is placed in new storage for the
+ * calling function. The caller must use 'free' to dispose
+ * of the storage once the path is no longer needed.
+ */
+
+boolean_t
+setup_temporary_directory(char **r_dirname, char *a_tmpdir, char *a_suffix)
+{
+ char *dirname;
+
+ /* entry assertions */
+
+ assert(a_tmpdir != (char *)NULL);
+
+ /* error if no pointer provided to return temporary name in */
+
+ if (r_dirname == (char **)NULL) {
+ errno = EFAULT; /* bad address */
+ return (B_FALSE);
+ }
+
+ /* generate temporary directory name */
+
+ dirname = tempnam(a_tmpdir, a_suffix);
+ if (dirname == (char *)NULL) {
+ return (B_FALSE);
+ }
+
+ /* create the temporary directory */
+
+ if (mkdir(dirname, 0755) != 0) {
+ return (B_FALSE);
+ }
+
+ echoDebug(DBG_SETUP_TEMPDIR, dirname);
+
+ *r_dirname = dirname;
+
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/sml.c b/usr/src/cmd/svr4pkg/libinst/sml.c
new file mode 100644
index 0000000000..bf74432cbc
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/sml.c
@@ -0,0 +1,3327 @@
+/*
+ * 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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: sml.c
+ * Synopsis: simplified markup language (SML) support
+ * Taxonomy: project private
+ * Debug flag: sml
+ * Description:
+ *
+ * This module implements methods that support the processing of a
+ * simplified markup language (SML). Objects that contain SML data
+ * can be created and manipulated, and SML can be imported into
+ * internal SML objects or exported from internal SML objects.
+ *
+ * Public Methods:
+ *
+ * smlAddTag - Add new tag object into existing tag object
+ * smlConvertStringToTag - Convert string into tag object
+ * smlConvertTagToString - Convert a tag object into a string
+ * representation of the XML
+ * smlDbgPrintTag - Print a representation of an XML tag if debugging
+ * smlDelParam - Delete a parameter from a tag object
+ * smlDelTag - Delete element from tag object
+ * smlDup - Duplicate a tag object
+ * smlFindAndDelTag - Delete a tag if found in tag object
+ * smlFreeTag - Free a tag object and all its contents when no
+ * longer needed
+ * smlFstatCompareEq - Compare file status information
+ * smlGetElementName - Return a tag's element name
+ * smlGetNumParams - Get number of parameters set in tag
+ * smlGetParam - Get a parameter from a tag
+ * smlGetParamF - Get a formatted parameter from a tag
+ * smlGetParamByTag - Get a parameter by tag and index
+ * smlGetParamByTagParam Get parameter given tag name, index,
+ * parameter name, and value
+ * smlGetParamName - Get the name of a tag parameter given its index
+ * smlGetParam_r - Get a parameter from a tag into fixed buffer
+ * smlGetTag - Get an element from a tag
+ * smlGetTagByName - Get an element given a name and an index
+ * smlGetTagByTagParam - Get element given tag name, index, parameter name,
+ * and value
+ * smlGetVerbose - get current verbose mode setting
+ * smlLoadTagFromFile - Load a file into a tag object
+ * smlNewTag - Create a new (empty) tag object
+ * smlParamEq - Determine if parameter is equal to a specified value
+ * smlParamEqF - Determine if parameter is equal to a specified value
+ * smlPrintTag - Print a simple XML representation of a tag to stderr
+ * smlReadOneTag - read one complete tag from a datastream
+ * smlReadTagFromDs - read tag object from datastream
+ * smlSetFileStatInfo - encode file status information into tag
+ * smlSetVerbose - set/clear verbose mode for debugging output
+ * smlSetParam - Set parameter value in tag object
+ * smlSetParamF - Set parameter value in tag object
+ * smlWriteTagToDs - Write an XML representation of a tag to a datastream
+ * smlWriteTagToFd - Write an XML representation of a tag to an open file
+ * descriptor
+ * smlWriteTagToFile - Write an XML representation of a tag to a file
+ */
+
+/*
+ * Unix includes
+ */
+
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <strings.h>
+
+/*
+ * liblu Includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/* Should be defined by cc -D */
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/*
+ * Private Method Forward Declarations
+ */
+
+/*PRINTFLIKE2*/
+static void _smlLogMsg(LogMsgType a_type, const char *a_format, ...);
+
+static int _smlReadTag(SML_TAG **r_tag, char **a_str, char *parent);
+
+static int _smlWriteSimpleTag(char **a_str,
+ SML_TAG *tag);
+
+static int _smlWriteParamValue(char **a_str, char *value);
+
+static void _smlFreeTag(SML_TAG *tag);
+
+static char *_sml_fileStatInfoTag = "File-Stat-Info";
+
+static boolean_t verbose = B_FALSE;
+
+/*
+ *
+ * This definition controls the maximum size of any individual sml
+ * component, such as a tag name, tag *value*, etc. The code should
+ * someday be revised to dynamically allocate whatever memory is needed
+ * to hold such components while parsing, but that exercise is left for
+ * another day. Any component that exceeds this length is silently
+ * truncated...
+ */
+
+#define MAX_SML_COMPONENT_LENGTH 16384
+
+/*
+ * Public Methods
+ */
+
+/*
+ * Name: smlAddTag
+ * Description: Add new tag object into existing tag object
+ * Arguments: r_tag - [RO, *RW] - (SML_TAG **)
+ * Pointer to handle to the tag object to update
+ * The handle may be updated if the tag object is
+ * moved in memory
+ * a_index - [RO] - (int)
+ * Add the tag after the "n"th tag in the tag object
+ * -1 == add the tag to the end of the tag object
+ * 0 == add the tag to the beginning of the tag object
+ * a_subTag - [RO, *RW] - (SML_TAG *)
+ * The tag to add to 'tag'
+ * Returns: SML_TAG *
+ * The location within "r_tag" where "a_subTag"
+ * has been added - this is the handle into the r_tag
+ * object to the tag that was just added
+ * Errors: If the tag object cannot be updated, the process exits
+ */
+
+SML_TAG *
+smlAddTag(SML_TAG **r_tag, int a_index, SML_TAG *a_subTag)
+{
+ SML_TAG *tag;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(a_subTag));
+ assert(SML_TAG__R_ISVALID(r_tag));
+
+ /* if no tag to update specified, ignore request */
+
+ tag = *r_tag;
+ if (tag == SML_TAG__NULL) {
+ return (tag);
+ }
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ADD_TAG,
+ a_subTag->name, tag->name);
+
+ /* if index is out of range or -1, append to tag object */
+
+ if ((a_index > tag->tags_num) || (a_index == -1)) {
+ a_index = tag->tags_num;
+ }
+
+ /* bump number of tags in tag object */
+
+ tag->tags_num++;
+
+ /* expand tag object to hold new subtag */
+
+ tag->tags = (SML_TAG *)realloc(tag->tags,
+ sizeof (SML_TAG) * tag->tags_num);
+
+ /* if not appending, adjust tag object to hold new subtag */
+
+ if (a_index < (tag->tags_num - 1)) {
+ (void) memmove(&(tag->tags[a_index + 1]), &(tag->tags[a_index]),
+ sizeof (SML_TAG) * (tag->tags_num - a_index - 1));
+ }
+
+ /* copy new subtag into correct location in tag object */
+
+ (void) memcpy(&(tag->tags[a_index]), a_subTag,
+ sizeof (SML_TAG));
+
+ return (&(tag->tags[a_index]));
+}
+
+/*
+ * Name: smlDelTag
+ * Description: Delete element from tag object
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to update
+ * sub_tag - [RO, *RW] - (SML_TAG *)
+ * Element to be removed from the tag object
+ * Returns: void
+ * The sub_tag is removed from the tag object
+ * NOTE: The sub-tag and all elements contained within it are deallocated
+ * the sub-tag is no longer valid when this method returns
+ */
+
+void
+smlDelTag(SML_TAG *tag, SML_TAG *sub_tag)
+{
+ int index;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(sub_tag));
+
+ /* if no tag to update specified, ignore request */
+
+ if (tag == SML_TAG__NULL) {
+ return;
+ }
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DEL_TAG,
+ sub_tag->name, tag->name);
+
+ /* if tag object is empty, ignore request */
+
+ if (tag->tags_num == 0) {
+ return;
+ }
+
+ /* determine index into tag object of element to remove */
+ for (index = 0; index < tag->tags_num; index++) {
+ if (sub_tag == &tag->tags[index]) {
+ break;
+ }
+ }
+
+ /* if element not found in tag, ignore request */
+
+ if (index >= tag->tags_num) {
+ return;
+ }
+
+ /* free up the subtag to be deleted */
+
+ _smlFreeTag(sub_tag);
+
+ /*
+ * if not removing last element, collapse tag object removing
+ * target element
+ */
+
+ if (index < (tag->tags_num - 1)) {
+ (void) memmove(&(tag->tags[index]), &(tag->tags[index + 1]),
+ sizeof (SML_TAG) *(tag->tags_num - index - 1));
+ }
+
+ /* one less tag object in tag */
+
+ tag->tags_num --;
+
+ /*
+ * If only one tag left, then delete entire tag structure
+ * otherwise reallocate removing unneeded entry
+ */
+
+ if (tag->tags_num > 0) {
+ /* realloc removing last element in tag object */
+
+ tag->tags = (SML_TAG *)realloc(tag->tags,
+ sizeof (SML_TAG) *tag->tags_num);
+ } else {
+ tag->tags = SML_TAG__NULL;
+ }
+}
+
+/*
+ * Name: smlFreeTag
+ * Description: Free a tag object and all its contents when no longer needed
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to be deleted
+ * Returns: void
+ * The tag object and all its contents are deallocated
+ */
+
+void
+smlFreeTag(SML_TAG *tag)
+{
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+
+ /* entry debugging info */
+
+ if (tag->name != (char *)NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_FREE_TAG,
+ (unsigned long)tag, tag->name);
+ }
+
+ /* free the tag object contents */
+
+ _smlFreeTag(tag);
+
+ /* free the tag object handle */
+
+ bzero(tag, sizeof (SML_TAG));
+ free(tag);
+}
+
+/*
+ * Name: smlGetNumParams
+ * Synopsis: Get number of parameters set in tag
+ * Description: Return the number of parameters set in a tag
+ * Arguments: a_tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the # params from
+ * Returns: int
+ * Number of parameters set in tag
+ * 0 = no parameters are set
+ */
+
+int
+smlGetNumParams(SML_TAG *a_tag)
+{
+ return (a_tag ? a_tag->params_num : 0);
+}
+
+
+/*
+ * Name: smlGetParam_r
+ * Description: Get a parameter from a tag into a buffer of fixed size
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the parameter from
+ * name - [RO, *RO] - (char *)
+ * Name of the parameter to retrieve
+ * buf - [RO, *RW] - (char *)
+ * Location of buffer to contain results
+ * bufLen - [RO, *RO] - (int)
+ * Maximum bytes available in buffer to contain results
+ * Returns: void
+ */
+
+void
+smlGetParam_r(SML_TAG *tag, char *name, char *buf, int bufLen)
+{
+ int k;
+
+ /* entry assertions */
+
+ assert(name != (char *)NULL);
+ assert(*name != '\0');
+ assert(buf != (char *)NULL);
+ assert(bufLen > 0);
+
+ /* terminate the buffer */
+
+ buf[0] = '\0';
+ buf[bufLen-1] = '\0';
+
+ bzero(buf, bufLen);
+
+ /* if no tag specified, return NULL */
+
+ if (tag == SML_TAG__NULL) {
+ return;
+ }
+
+ /* if no parameters in tag, return NULL */
+
+ if (tag->params == NULL) {
+ return;
+ }
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
+ name, tag->name);
+
+ /* scan tag object looking for specified parameter */
+
+ for (k = 0; k < tag->params_num; k++) {
+ assert(tag->params[k].name != (char *)NULL);
+ assert(tag->params[k].value != (char *)NULL);
+ if (streq(tag->params[k].name, name)) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_GOT_PARAM,
+ tag->name, name, tag->params[k].value);
+ (void) strncpy(buf, tag->params[k].value, bufLen-1);
+ return;
+ }
+ }
+
+ /* parameter not found - return */
+}
+
+/*
+ * Name: smlGetParam
+ * Description: Get a parameter from a tag
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the parameter from
+ * name - [RO, *RO] - (char *)
+ * Name of the parameter to retrieve
+ * Returns: char *
+ * Value of the specified parameter
+ * == (char *)NULL if the parameter does not exist
+ * NOTE: Any parameter returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the parameter is no longer needed.
+ */
+
+char *
+smlGetParam(SML_TAG *tag, char *name)
+{
+ int k;
+
+ /* entry assertions */
+
+ assert(name != (char *)NULL);
+ assert(*name != '\0');
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, "get param param <%s>", name);
+
+ /* if no tag specified, return NULL */
+
+ if (tag == SML_TAG__NULL) {
+ return ((char *)NULL);
+ }
+
+ /* if no parameters in tag, return NULL */
+
+ if (tag->params == NULL) {
+ return ((char *)NULL);
+ }
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
+ name, tag->name);
+
+ /* scan tag object looking for specified parameter */
+
+ for (k = 0; k < tag->params_num; k++) {
+ assert(tag->params[k].name != (char *)NULL);
+ assert(tag->params[k].value != (char *)NULL);
+ if (streq(tag->params[k].name, name)) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_GOT_PARAM,
+ tag->name, name, tag->params[k].value);
+ return (strdup(tag->params[k].value));
+ }
+ }
+
+ /* parameter not found - return NULL */
+
+ return ((char *)NULL);
+}
+
+/*
+ * Name: smlGetParamName
+ * Description: Get the name of a tag parameter given its index
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the parameter name from
+ * index - [RO] - (int)
+ * Index of parameter name to return
+ * Returns: char *
+ * Name of 'index'th parameter
+ * == (char *)NULL if no such parameter exists in tag
+ * NOTE: Any parameter name returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the parameter name is no longer needed.
+ */
+
+char *
+smlGetParamName(SML_TAG *tag, int index)
+{
+ /* if no tag specified, return NULL */
+
+ if (tag == NULL) {
+ return ((char *)NULL);
+ }
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_NAME,
+ tag->name, index);
+
+ /* if no parameters in tag, return NULL */
+
+ if (tag->params == NULL) {
+ return ((char *)NULL);
+ }
+
+ /* if index not within range, return NULL */
+
+ if (index >= tag->params_num) {
+ return ((char *)NULL);
+ }
+
+ /* index within range - return parameter name */
+
+ assert(tag->params[index].name != (char *)NULL);
+ assert(tag->params[index].value != (char *)NULL);
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GOT_PARAM_NAME,
+ tag->name, index, tag->params[index].name);
+
+ return (strdup(tag->params[index].name));
+}
+
+/*
+ * Name: smlGetParamByTag
+ * Synopsis: Get a parameter value from a tag by name and index
+ * Description: Call to look for a parameter value from a tag with
+ * a given name with a parameter of a given name
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the parameter
+ * index - [RO] - (int)
+ * Index of nth tag by name to look for
+ * tagName - [RO, *RO] - (char *)
+ * Name of tag to look for
+ * paramName - [RO, *RO] - (char *)
+ * Name of parameter to return value of
+ * Returns: char *
+ * == (char *)NULL - no parameter value set
+ * != (char *)NULL - value of parameter set
+ */
+
+char *
+smlGetParamByTag(SML_TAG *tag, int index,
+ char *tagName, char *paramName)
+{
+ SML_TAG *rtag;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(tagName != (char *)NULL);
+ assert(*tagName != '\0');
+ assert(paramName != (char *)NULL);
+ assert(*paramName != '\0');
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_BY_TAG,
+ tagName, index, paramName);
+
+ /* find the requested tag by name and index */
+
+ rtag = smlGetTagByName(tag, index, tagName);
+ if (rtag == SML_TAG__NULL) {
+ return ((char *)NULL);
+ }
+
+ return (smlGetParam(rtag, paramName));
+}
+
+/*
+ * Name: smlGetTagByTagParam
+ * Synopsis: Get element given tag name, index, parameter name, and value
+ * Description: Call to look for a tag with a given nae, that has a parameter
+ * of a given name with a specified value
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the element from
+ * index - [RO] - (int)
+ * Index of nth name to return
+ * tagName - [RO, *RO] - (char *)
+ * Tag name to look up
+ * paramName - [RO, *RO] - (char *)
+ * Parameter name to look up
+ * paramValue - [RO, *RO] - (char *)
+ * Parameter value to match
+ * Returns: SML_TAG *
+ * The 'index'th occurance of element 'name' with
+ * a parameter 'name' with value specified
+ * == SML_TAG__NULL if no such element exists
+ */
+
+SML_TAG *
+smlGetTagByTagParam(SML_TAG *tag, int index,
+ char *tagName, char *paramName, char *paramValue)
+{
+ int ti; /* tag structure index */
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(tagName != (char *)NULL);
+ assert(*tagName != '\0');
+ assert(paramName != (char *)NULL);
+ assert(*paramName != '\0');
+ assert(paramValue != (char *)NULL);
+ assert(*paramValue != '\0');
+
+ /* if tag has no elements, return NULL */
+
+ if (tag->tags == NULL) {
+ return (SML_TAG__NULL);
+ }
+
+ /*
+ * Search algorithm:
+ * -> search tag structure; for each tag with element == "tagName":
+ * -> search tag parameters; if parameter name == "paramName"
+ * -> if parameter value != "paramValue"; to next tag
+ * -> if parameter value == "paramValue":
+ * -> if not the "index"th paramValue found; to next tag
+ * -> return tag found
+ */
+
+ for (ti = 0; ti < tag->tags_num; ti++) {
+ int pi; /* parameter structure index */
+
+ /* if tag element does not match, go on to next tag */
+
+ if (strcmp(tag->tags[ti].name, tagName)) {
+ continue;
+ }
+
+ /* element matches: search for specified parameter name/value */
+
+ for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
+ assert(tag->tags[ti].params[pi].name != (char *)NULL);
+ assert(tag->tags[ti].params[pi].value != (char *)NULL);
+
+ /* if parameter name doesnt match to next parameter */
+
+ if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
+ continue;
+ }
+
+ /* if parameter value doesnt match to next tag */
+
+ if (strcmp(tag->tags[ti].params[pi].value,
+ paramValue)) {
+ break;
+ }
+
+ /*
+ * found element/paramname/paramvalue:
+ * -> if this is not the 'index'th one, go to next tag
+ */
+
+ if (index-- != 0) {
+ break;
+ }
+
+ /*
+ * found specified element/paramname/paramvalue:
+ * -> return the tag found
+ */
+
+ return (&tag->tags[ti]);
+ }
+
+ }
+
+ /* no such element found - return NULL */
+
+ return (SML_TAG__NULL);
+}
+
+/*
+ * Name: smlGetParamByTagParam
+ * Synopsis: Get parameter given tag name, index, parameter name, and value
+ * Description: Call to return the value of a parameter from a tag of a
+ * given name, with a parameter of a given name with a
+ * specified value
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the element from
+ * index - [RO] - (int)
+ * Index of nth name to return
+ * tagName - [RO, *RO] - (char *)
+ * Tag name to look up
+ * paramName - [RO, *RO] - (char *)
+ * Parameter name to look up
+ * paramValue - [RO, *RO] - (char *)
+ * Parameter value to match
+ * paramReturn - [RO, *RO] - (char *)
+ * Parameter name to return the value of
+ * Returns: char *
+ * The value of parameter 'paramReturn' from the
+ * The 'index'th occurance of element 'name' with
+ * a parameter 'name' with value specified
+ * == (char *)NULL if no such parameter exists
+ */
+
+char *
+smlGetParamByTagParam(SML_TAG *tag, int index,
+ char *tagName, char *paramName, char *paramValue, char *paramReturn)
+{
+ int ti; /* tag structure index */
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(tagName != (char *)NULL);
+ assert(*tagName != '\0');
+ assert(paramName != (char *)NULL);
+ assert(*paramName != '\0');
+ assert(paramValue != (char *)NULL);
+ assert(*paramValue != '\0');
+ assert(paramReturn != (char *)NULL);
+ assert(*paramReturn != '\0');
+
+ /* if tag has no elements, return NULL */
+
+ if (tag->tags == NULL) {
+ return ((char *)NULL);
+ }
+
+ /*
+ * Search algorithm:
+ * -> search tag structure; for each tag with element == "tagName":
+ * -> search tag parameters; if parameter name == "paramName"
+ * -> if parameter value != "paramValue"; to next tag
+ * -> if parameter value == "paramValue":
+ * -> if not the "index"th paramValue found; to next tag
+ * -> return value of "paramReturn"
+ */
+
+ for (ti = 0; ti < tag->tags_num; ti++) {
+ int pi; /* parameter structure index */
+
+ /* if tag element does not match, go on to next tag */
+
+ if (strcmp(tag->tags[ti].name, tagName)) {
+ continue;
+ }
+
+ /* element matches: search for specified parameter name/value */
+
+ for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
+ assert(tag->tags[ti].params[pi].name != (char *)NULL);
+ assert(tag->tags[ti].params[pi].value != (char *)NULL);
+
+ /* if parameter name doesnt match to next parameter */
+
+ if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
+ continue;
+ }
+
+ /* if parameter value doesnt match to next tag */
+
+ if (strcmp(tag->tags[ti].params[pi].value,
+ paramValue)) {
+ break;
+ }
+
+ /*
+ * found element/paramname/paramvalue:
+ * -> if this is not the 'index'th one, go to next tag
+ */
+
+ if (index-- != 0) {
+ break;
+ }
+
+ /*
+ * found specified element/paramname/paramvalue:
+ * -> return parameter requested
+ */
+
+ return (smlGetParam(&tag->tags[ti], paramReturn));
+ }
+
+ }
+
+ /* no such element found - return NULL */
+
+ return ((char *)NULL);
+}
+
+/*
+ * Name: smlGetElementName
+ * Description: Return the name of a given tag
+ * Arguments: a_tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the element name from
+ * Returns: char *
+ * Value of name of specified tag
+ * NOTE: Any name string returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the name string is no longer needed.
+ */
+
+char *
+smlGetElementName(SML_TAG *a_tag)
+{
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(a_tag));
+ assert(a_tag->name != (char *)NULL);
+ assert(*a_tag->name != '\0');
+
+ /* return the tag name */
+
+ return (strdup(a_tag->name));
+}
+
+/*
+ * Name: smlGetTag
+ * Description: Get an element from a tag
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the element from
+ * index - [RO] - (int)
+ * Index of element to return
+ * Returns: SML_TAG *
+ * The 'index'th element from the specified tag
+ * == SML_TAG__NULL if no such tag or element
+ */
+
+SML_TAG *
+smlGetTag(SML_TAG *tag, int index)
+{
+ /* if no tag specified, return NULL */
+
+ if (tag == NULL) {
+ return (SML_TAG__NULL);
+ }
+
+ /* if tag has no elements, return NULL */
+
+ if (tag->tags == NULL) {
+ return (SML_TAG__NULL);
+ }
+
+ /* if index not within range, return NULL */
+
+ if (tag->tags_num <= index) {
+ return (SML_TAG__NULL);
+ }
+
+ /* index within range, return element specified */
+
+ assert(SML_TAG__ISVALID(&tag->tags[index]));
+
+ return (&tag->tags[index]);
+}
+
+/*
+ * Name: smlGetTagByName
+ * Description: Get an element given a name and an index
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the element from
+ * index - [RO] - (int)
+ * Index of nth name to return
+ * name - [RO, *RO] - (char *)
+ * Tag name to look up
+ * Returns: SML_TAG *
+ * The 'index'th occurance of element 'name'
+ * == SML_TAG__NULL if no such element exists
+ */
+
+SML_TAG *
+smlGetTagByName(SML_TAG *tag, int index, char *name)
+{
+ int k;
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_TAG_BY_NAME, name, index);
+
+ /* if no tag specified, return NULL */
+
+ if (tag == NULL) {
+ return (SML_TAG__NULL);
+ }
+
+ /* if this tag is the one mentioned, return it */
+
+ if (streq(tag->name, name) && (index == 0)) {
+ return (tag);
+ }
+
+ /* if tag has no elements, return NULL */
+
+ if (tag->tags == NULL) {
+ return (SML_TAG__NULL);
+ }
+
+ /* if index out of range, return NULL */
+
+ if (tag->tags_num <= index) {
+ return (SML_TAG__NULL);
+ }
+
+ /* index within range - search for specified element */
+
+ for (k = 0; k < tag->tags_num; k++) {
+ if (streq(tag->tags[k].name, name)) {
+ if (index == 0) {
+ assert(SML_TAG__ISVALID(&tag->tags[k]));
+ return (&tag->tags[k]);
+ } else {
+ index--;
+ }
+ }
+ }
+
+ /* no such element found - return NULL */
+
+ return (SML_TAG__NULL);
+}
+
+/*
+ * Name: smlConvertStringToTag
+ * Description: Convert string into tag object
+ * Arguments: err - [RO, *RW] (LU_ERR)
+ * Error object - used to contain any errors encountered
+ * and return those errors to this methods caller
+ * r_tag - [RW, *RW] - (SML_TAG **)
+ * Pointer to handle to place new tag object
+ * str - [RO, *RO] - (char *)
+ * String object to convert to tag object
+ * Returns: int
+ * RESULT_OK - string converted to tag object
+ * RESULT_ERR - problem converting string to tag object
+ * NOTE: Any tag object returned is placed in new storage for the
+ * calling method. The caller must use 'smlFreeTag' to dispose
+ * of the storage once the tag object name is no longer needed.
+ */
+
+int
+smlConvertStringToTag(SML_TAG **r_tag, char *str)
+{
+ int r;
+ SML_TAG *tag = SML_TAG__NULL;
+ SML_TAG *tmp_tag;
+
+ /* entry assertions */
+
+ assert(SML_TAG__R_ISVALID(r_tag));
+ assert(str != (char *)NULL);
+ assert(*str != '\0');
+
+ tag = smlNewTag("tagfile");
+
+ for (;;) {
+ r = _smlReadTag(&tmp_tag, &str, NULL);
+ if (r != RESULT_OK) {
+ smlFreeTag(tag);
+ return (r);
+ }
+ if (tmp_tag == SML_TAG__NULL) {
+ if (*str != '\0') {
+ continue;
+ }
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_LOADED_TAGS_FROM_STR,
+ (unsigned long)tag, tag->name);
+ *r_tag = tag;
+ return (RESULT_OK);
+ }
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_IN_TOP_TAG,
+ tmp_tag->name);
+ tag->tags_num++;
+ tag->tags = (SML_TAG *)realloc(tag->tags,
+ sizeof (SML_TAG) *tag->tags_num);
+ (void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
+ sizeof (SML_TAG));
+ }
+}
+
+/*
+ * Name: smlReadOneTag
+ * Description: read one complete tag from a datastream
+ * Arguments: err - [RO, *RW] (LU_ERR)
+ * Error object - used to contain any errors encountered
+ * and return those errors to this methods caller
+ * r_tag - [RW, *RW] - (SML_TAG **)
+ * Pointer to handle to place new tag object
+ * == SML_TAG__NULL if empty tag found (not an error)
+ * ds - [RO, *RO] - (LU_DS)
+ * Handle to datastream to read tag from
+ * Returns: int
+ * RESULT_OK - tag successfully read
+ * RESULT_ERR - problem reading tag
+ * NOTE: Any tag object returned is placed in new storage for the
+ * calling method. The caller must use 'smlFreeTag' to dispose
+ * of the storage once the tag object name is no longer needed.
+ */
+
+int
+smlReadOneTag(SML_TAG **r_tag, char *a_str)
+{
+ int r;
+
+ /* entry assertions */
+
+ assert(SML_TAG__R_ISVALID(r_tag));
+ assert(a_str != (char *)NULL);
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG, a_str);
+
+ /* reset return tag */
+
+ *r_tag = SML_TAG__NULL;
+
+ /* read tag from datastream, no parent tag to attach it to */
+
+ r = _smlReadTag(r_tag, &a_str, NULL);
+ if (r != RESULT_OK) {
+ _smlLogMsg(LOG_MSG_ERR, ERR_SML_CANNOT_READ_TAG);
+ return (r);
+ }
+
+ if (*r_tag != SML_TAG__NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ONE_TAG_READ,
+ (unsigned long)*r_tag,
+ (*r_tag)->name ? (*r_tag)->name : "<no name>");
+ } else {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG_NOTAG);
+ }
+
+ /* exit debugging info */
+
+ return (RESULT_OK);
+}
+
+/*
+ * Name: smlNewTag
+ * Description: Create a new (empty) tag object
+ * Arguments: name - [RO, *RO] - (char *)
+ * Name of tag; NULL to give the tag no name
+ * Returns: SML_TAG *
+ * Tag object created
+ * NOTE: Any tag object returned is placed in new storage for the
+ * calling method. The caller must use 'smlFreeTag' to dispose
+ * of the storage once the tag object name is no longer needed.
+ * Errors: If the tag object cannot be created, the process exits
+ */
+
+SML_TAG *
+smlNewTag(char *name)
+{
+ SML_TAG *tag;
+
+ /* entry assertions */
+
+ assert((name == (char *)NULL) || (*name != '\0'));
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATE_NEW_TAG_OBJECT,
+ name ? name : "<no name>");
+
+ /* allocate zeroed storage for the tag object */
+
+ tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+ assert(tag != SML_TAG__NULL);
+
+ /* if name is provided, duplicate and assign it */
+
+ if (name != (char *)NULL) {
+ tag->name = strdup(name);
+ }
+
+ /* exit assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+
+ /* exit debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATED_NEW_TAG_OBJECT,
+ (unsigned long)tag, name ? name : "<no name>");
+
+ return (tag);
+}
+
+/*
+ * Name: smlConvertTagToString
+ * Description: Convert a tag object into a string representation of the XML
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to convert to a string
+ * Returns: char *
+ * String representation (in XML) of tag object
+ * == (char *)NULL if conversion is not possible
+ * 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.
+ */
+
+char *
+smlConvertTagToString(SML_TAG *tag)
+{
+ char *str = (char *)NULL;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+
+ /* convert the tag object into the datastream */
+
+ (void) _smlWriteSimpleTag(&str, tag);
+
+ assert(str != (char *)NULL);
+ assert(*str != '\0');
+
+ /* return the results */
+
+ return (str);
+}
+
+/*
+ * Name: smlDbgPrintTag
+ * Synopsis: Print a representation of an XML tag if debugging
+ * Arguments: a_tag - [RO, *RO] - (SML_TAG *)
+ * Pointer to tag structure to dump
+ * a_format - [RO, RO*] (char *)
+ * printf-style format for debugging message to be output
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ * If one of the debugging flags is set, the hexdump
+ * is output.
+ */
+
+/*PRINTFLIKE2*/
+void
+smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char bfr[1];
+ char *rstr = (char *)NULL;
+
+ /* entry assertions */
+
+ assert(a_format != (char *)NULL);
+ assert(*a_format != '\0');
+ assert(SML_TAG__ISVALID(a_tag));
+
+ /*
+ * output the message header
+ */
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ rstr = (char *)calloc(1, vres+2);
+ assert(rstr != (char *)NULL);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(*rstr != '\0');
+
+ _smlLogMsg(LOG_MSG_DEBUG, "%s", rstr);
+ free(rstr);
+
+ /* convert the tag into a string to be printed */
+
+ rstr = smlConvertTagToString(a_tag);
+ if (rstr != (char *)NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_PRINTTAG, a_tag->name,
+ strlen(rstr), rstr);
+ }
+ free(rstr);
+}
+
+/*
+ * Name: smlDelParam
+ * Description: Delete a parameter from a tag object
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to delete the parameter from
+ * name - [RO, *RO] - (char *)
+ * The parameter to delete from the tag object
+ * Returns: void
+ * If the parameter exists, it is deleted from the tag
+ */
+
+void
+smlDelParam(SML_TAG *tag, char *name)
+{
+ int k;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(tag->name != (char *)NULL);
+ assert(name != NULL);
+ assert(*name != '\0');
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DELETE_PARAM,
+ tag->name, name);
+
+ /* if tag has no parameters, nothing to delete */
+
+ if (tag->params == NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_DELETE_PARAM_NO_PARAMS);
+ return;
+ }
+
+ assert(tag->params_num > 0);
+
+ /* search the tag for the parameter */
+
+ for (k = 0; k < tag->params_num; k++) {
+ if (streq(tag->params[k].name, name)) {
+ break;
+ }
+ }
+
+ /* if the parameter was not found, nothing to delete */
+
+ if (k >= tag->params_num) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_DELETE_PARAM_NOT_FOUND,
+ name);
+ return;
+ }
+
+ /* parameter found - indicate deleted */
+
+ assert(tag->params[k].name != (char *)NULL);
+ assert(tag->params[k].value != (char *)NULL);
+
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_DELETE_PARAM_FOUND,
+ name, tag->params[k].value);
+
+ /* free up storage fro parameter */
+
+ free(tag->params[k].name);
+ free(tag->params[k].value);
+
+ /* if not at end, compact parameter storage */
+
+ if (k < (tag->params_num -1)) {
+ (void) memmove(&(tag->params[k]), &(tag->params[k + 1]),
+ sizeof (SML_PARAM) *(tag->params_num - k - 1));
+ }
+
+ /* one less parameter object in tag */
+
+ tag->params_num --;
+
+ /*
+ * If only one parameter left, then delete entire parameter storage,
+ * otherwise reallocate removing unneeded entry
+ */
+
+ if (tag->params_num > 0) {
+ /* realloc removing last element in tag object */
+
+ tag->params = (SML_PARAM *)
+ realloc(tag->params,
+ sizeof (SML_PARAM) *tag->params_num);
+ } else {
+ tag->params = (SML_PARAM *)NULL;
+ }
+}
+
+/*
+ * Name: smlSetParamF
+ * Description: Set formatted parameter value in tag object
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to set the parameter in
+ * name - [RO, *RO] - (char *)
+ * The parameter to add to the tag object
+ * format - [RO, RO*] (char *)
+ * printf-style format to create parameter value from
+ * ... - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ * The parameter value is set in the tag object
+ * according to the results of the format string
+ * and arguments
+ */
+
+/*PRINTFLIKE3*/
+void
+smlSetParamF(SML_TAG *tag, char *name, char *format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char *bfr = NULL;
+ char fbfr[1];
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(name != (char *)NULL);
+ assert(*name != '\0');
+ assert(format != NULL);
+ assert(*format != '\0');
+
+ /* determine size of the parameter name in bytes */
+
+ va_start(ap, format);
+ vres = vsnprintf(fbfr, 1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ bfr = (char *)calloc(1, vres+2);
+ assert(bfr != (char *)NULL);
+
+ /* generate the parameter name and store it in the allocated storage */
+
+ va_start(ap, format);
+ vres = vsnprintf(bfr, vres+1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(*bfr != '\0');
+
+ /* add the parameter to the tag */
+
+ smlSetParam(tag, name, bfr);
+
+ /* free up temporary storage and return */
+
+ free(bfr);
+}
+
+/*
+ * Name: smlGetParam
+ * Description: Get a format-generated parameter from a tag
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to obtain the parameter from
+ * format - [RO, RO*] (char *)
+ * printf-style format for parameter name to be
+ * looked up to be formatted
+ * ... - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: char *
+ * Value of the specified parameter
+ * == (char *)NULL if the parameter does not exist
+ * NOTE: Any parameter returned is placed in new storage for the
+ * calling method. The caller must use 'free' to dispose
+ * of the storage once the parameter is no longer needed.
+ */
+
+/*PRINTFLIKE2*/
+char *
+smlGetParamF(SML_TAG *tag, char *format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char *bfr = NULL;
+ char fbfr[1];
+ char *p;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(format != NULL);
+ assert(*format != '\0');
+
+ /* determine size of the parameter name in bytes */
+
+ va_start(ap, format);
+ vres = vsnprintf(fbfr, 1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ bfr = (char *)calloc(1, vres+2);
+ assert(bfr != (char *)NULL);
+
+ /* generate the parameter name and store it in the allocated storage */
+
+ va_start(ap, format);
+ vres = vsnprintf(bfr, vres+1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(*bfr != '\0');
+
+ /* add the parameter to the tag */
+
+ p = smlGetParam(tag, bfr);
+
+ /* free up temporary storage and return */
+
+ free(bfr);
+
+ return (p);
+}
+
+/*
+ * Name: smlSetParam
+ * Description: Set parameter value in tag object
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to set the parameter in
+ * name - [RO, *RO] - (char *)
+ * The parameter to add to the tag object
+ * value - [RO, *RO] - (char *)
+ * The value of the parameter to set in the tag object
+ * Returns: void
+ * The parameter value is set in the tag object
+ */
+
+void
+smlSetParam(SML_TAG *tag, char *name, char *value)
+{
+ SML_PARAM *parameter;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(name != (char *)NULL);
+ assert(*name != '\0');
+ assert(value != (char *)NULL);
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_SET_PARAM,
+ tag->name, name, value);
+
+ /* if parameters exist, see if modifying existing parameter */
+
+ if (tag->params != NULL) {
+ int k;
+ for (k = 0; k < tag->params_num; k++) {
+ assert(tag->params[k].name != (char *)NULL);
+ assert(tag->params[k].value != (char *)NULL);
+
+ /* if name does not match, skip */
+
+ if (!streq(tag->params[k].name, name)) {
+ continue;
+ }
+
+ /* found parameter - if value is same, leave alone */
+
+ if (streq(tag->params[k].value, value)) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_SET_PARAM_LEAVE_ALONE,
+ tag->params[k].value);
+ return;
+ }
+
+ /* exists and has different value - change */
+
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_SET_PARAM_MODIFY,
+ tag->params[k].value);
+ free(tag->params[k].value);
+ tag->params[k].value = strdup(value);
+ return;
+ }
+ }
+
+ /* not modifying existing - add new parameter */
+
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_SET_PARAM_CREATE_NEW);
+
+ parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
+ bzero(parameter, sizeof (SML_PARAM));
+ parameter->name = strdup(name);
+ parameter->value = strdup(value);
+
+ tag->params_num++;
+ tag->params = (SML_PARAM *)realloc(tag->params,
+ sizeof (SML_PARAM) *tag->params_num);
+ (void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
+ sizeof (SML_PARAM));
+ free(parameter);
+}
+
+/*
+ * Name: smlParamEqF
+ * Description: Determine if parameter is equal to a specified formatted value
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to look for the parameter to compare
+ * findTag - [RO, *RO] - (char *)
+ * Tag within tag object to look for the parameter in
+ * findParam - [RO, *RO] - (char *)
+ * Parameter within tag to look for
+ * format - [RO, RO*] (char *)
+ * printf-style format for value to be compared against
+ * parameter value
+ * ... - [RO] (?)
+ * arguments as appropriate to 'format' specified to
+ * generate the value to compare parameter with
+ * Returns: boolean_t
+ * B_TRUE - the parameter exists and matches given value
+ * B_FALSE - parameter does not exist or does not match
+ */
+
+/*PRINTFLIKE4*/
+boolean_t
+smlParamEqF(SML_TAG *tag, char *findTag, char *findParam, char *format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char *bfr = NULL;
+ char fbfr[1];
+ boolean_t b;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(format != NULL);
+ assert(*format != '\0');
+
+ /* determine size of the parameter value in bytes */
+
+ va_start(ap, format);
+ vres = vsnprintf(fbfr, 1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ bfr = (char *)calloc(1, vres+2);
+ assert(bfr != (char *)NULL);
+
+ /* generate the parameter value and store it in the allocated storage */
+
+ va_start(ap, format);
+ vres = vsnprintf(bfr, vres+1, format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(*bfr != '\0');
+
+ /* add the parameter to the tag */
+
+ b = smlParamEq(tag, findTag, findParam, bfr);
+
+ /* free up temporary storage and return */
+
+ free(bfr);
+
+ return (b);
+}
+
+/*
+ * Name: smlParamEq
+ * Description: Determine if parameter is equal to a specified value
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to look for the parameter to compare
+ * findTag - [RO, *RO] - (char *)
+ * Tag within tag object to look for the parameter in
+ * findParam - [RO, *RO] - (char *)
+ * Parameter within tag to look for
+ * str - [RO, *RO] - (char *)
+ * Value to compare parameter with
+ * Returns: boolean_t
+ * B_TRUE - the parameter exists and matches given value
+ * B_FALSE - parameter does not exist or does not match
+ */
+
+boolean_t
+smlParamEq(SML_TAG *tag, char *findTag, char *findParam, char *str)
+{
+ SML_TAG *rtag;
+ char *rparm;
+ boolean_t answer;
+
+ /* entry assertions */
+
+ assert(str != (char *)NULL);
+ assert(findParam != (char *)NULL);
+ assert(findTag != (char *)NULL);
+ assert(SML_TAG__ISVALID(tag));
+
+ /* look for the specified tag - if not found, return false */
+
+ rtag = smlGetTagByName(tag, 0, findTag);
+ if (rtag == SML_TAG__NULL) {
+ return (B_FALSE);
+ }
+
+ /* look for the specified parameter - if not found, return false */
+
+ rparm = smlGetParam(rtag, findParam);
+ if (rparm == (char *)NULL) {
+ return (B_FALSE);
+ }
+
+ /* parameter found - compare against given value */
+
+ answer = strcasecmp(str, rparm);
+
+ /* free up parameter storage */
+
+ free(rparm);
+
+ /* return results of comparison */
+
+ return (answer == 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Name: smlFindAndDelTag
+ * Description: Delete a tag if found in tag object
+ * Arguments: tag - [RO, *RW] - (SML_TAG *)
+ * The tag object to delete the tag from
+ * findTag - [RO, *RO] - (char *)
+ * Tag within tag object to delete
+ * Returns: boolean_t
+ * B_TRUE - tag found and deleted
+ * B_FALSE - tag not found
+ */
+
+boolean_t
+smlFindAndDelTag(SML_TAG *tag, char *findTag)
+{
+ SML_TAG *rtag = SML_TAG__NULL;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+ assert(findTag != (char *)NULL);
+ assert(*findTag != '\0');
+
+ /* find the specified tag - if not found, return false */
+
+ rtag = smlGetTagByName(tag, 0, findTag);
+ if (rtag == SML_TAG__NULL) {
+ return (B_FALSE);
+ }
+
+ /* tag found - delete it and return true */
+
+ smlDelTag(tag, rtag);
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: smlDup
+ * Description: Duplicate a tag object
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to duplicate
+ * Returns: SML_TAG *
+ * A handle to a complete duplicate of the tag provided
+ * NOTE: Any tag object returned is placed in new storage for the
+ * calling method. The caller must use 'smlFreeTag' to dispose
+ * of the storage once the tag object name is no longer needed.
+ * Errors: If the tag object cannot be duplicated, the process exits
+ */
+
+SML_TAG *
+smlDup(SML_TAG *tag)
+{
+ SML_TAG *rtag = SML_TAG__NULL;
+ int i;
+
+ /* entry assertions */
+
+ assert(SML_TAG__ISVALID(tag));
+
+ /* allocate zeroed storage for the tag object */
+
+ rtag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+ assert(rtag != SML_TAG__NULL);
+
+ /* duplicate all parameters of the tag */
+
+ rtag->name = (tag->name ? strdup(tag->name) : (char *)NULL);
+ rtag->params_num = tag->params_num;
+ if (tag->params != (SML_PARAM *)NULL) {
+ rtag->params = (SML_PARAM *)
+ calloc(1, sizeof (SML_PARAM)*rtag->params_num);
+ bzero(rtag->params, sizeof (SML_PARAM)*rtag->params_num);
+ for (i = 0; i < rtag->params_num; i++) {
+ rtag->params[i].name = tag->params[i].name ?
+ strdup(tag->params[i].name) :
+ (char *)NULL;
+ rtag->params[i].value = tag->params[i].value ?
+ strdup(tag->params[i].value) :
+ (char *)NULL;
+ }
+ }
+
+ /* duplicate all elements of the tag */
+
+ rtag->tags_num = tag->tags_num;
+
+ if (tag->tags != SML_TAG__NULL) {
+ rtag->tags = (SML_TAG *)
+ calloc(1, sizeof (SML_TAG)*rtag->tags_num);
+ bzero(rtag->tags, sizeof (SML_TAG)*rtag->tags_num);
+ for (i = 0; i < rtag->tags_num; i++) {
+ SML_TAG *stag;
+ stag = smlDup(&tag->tags[i]);
+ (void) memcpy(&rtag->tags[i], stag,
+ sizeof (SML_TAG));
+ free(stag);
+ }
+ }
+
+ /* exit assertions */
+
+ assert(SML_TAG__ISVALID(rtag));
+
+ /* return */
+
+ return (rtag);
+}
+
+/*
+ * Name: smlSetFileStatInfo
+ * Description; Given a file status structure and path name, encode the
+ * structure and place it and the name into the specified tag
+ * in a "_sml_fileStatInfoTag" (private) element
+ * Arguments: tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to deposit the information into
+ * statbuf - [RO, *RO] - (struct stat *)
+ * Pointer to file status structure to encode
+ * path - [RO, *RO] - (char *)
+ * Pointer to path name of file to encode
+ * Returns: void
+ * The information is placed into the specified tag object
+ */
+
+void
+smlSetFileStatInfo(SML_TAG **tag, struct stat *statbuf, char *path)
+{
+ SML_TAG *rtag;
+
+ /* entry assertions */
+
+ assert(SML_TAG__R_ISVALID(tag));
+ assert(SML_TAG__ISVALID(*tag));
+ assert(statbuf != (struct stat *)NULL);
+
+ /* if stat info exists, delete it */
+
+ (void) smlFindAndDelTag(*tag, _sml_fileStatInfoTag);
+
+ /* create the file stat info inside of the top level tag */
+
+ assert(smlGetTagByName(*tag, 0, _sml_fileStatInfoTag)
+ == SML_TAG__NULL);
+ rtag = smlNewTag(_sml_fileStatInfoTag);
+ assert(SML_TAG__ISVALID(rtag));
+ (void) smlAddTag(tag, 0, rtag);
+ free(rtag);
+
+ /* obtain handle on newly created file stat info tag */
+
+ rtag = smlGetTagByName(*tag, 0, _sml_fileStatInfoTag);
+ assert(SML_TAG__ISVALID(rtag));
+
+ /* add file info as parameters to the tag */
+
+ if (path != (char *)NULL) {
+ smlSetParam(rtag, "st_path", path);
+ }
+
+ smlSetParamF(rtag, "st_ino", "0x%llx",
+ (unsigned long long)statbuf->st_ino);
+ smlSetParamF(rtag, "st_mode", "0x%llx",
+ (unsigned long long)statbuf->st_mode);
+ smlSetParamF(rtag, "st_mtime", "0x%llx",
+ (unsigned long long)statbuf->st_mtime);
+ smlSetParamF(rtag, "st_ctime", "0x%llx",
+ (unsigned long long)statbuf->st_ctime);
+ smlSetParamF(rtag, "st_size", "0x%llx",
+ (unsigned long long)statbuf->st_size);
+}
+
+/*
+ * Name: smlFstatCompareEQ
+ * Description: Given a file status structure and path name, look for the
+ * information placed into a tag object via smlSetFileStatInfo
+ * and if present compare the encoded information with the
+ * arguments provided
+ * Arguments: statbuf - [RO, *RO] - (struct stat *)
+ * Pointer to file status structure to compare
+ * tag - [RO, *RO] - (SML_TAG *)
+ * The tag object to compare against
+ * path - [RO, *RO] - (char *)
+ * Pointer to path name of file to compare
+ * Returns: boolean_t
+ * B_TRUE - both status structures are identical
+ * B_FALSE - the status structures are not equal
+ */
+
+boolean_t
+smlFstatCompareEq(struct stat *statbuf, SML_TAG *tag, char *path)
+{
+ if (tag == SML_TAG__NULL) {
+ return (B_FALSE);
+ }
+
+ assert(SML_TAG__ISVALID(tag));
+
+ if (statbuf == (struct stat *)NULL) {
+ return (B_FALSE);
+ }
+
+ if (path != (char *)NULL) {
+ if (smlParamEq(tag,
+ _sml_fileStatInfoTag, "st_path", path) != B_TRUE) {
+ return (B_FALSE);
+ }
+ }
+
+ if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ino",
+ "0x%llx", (unsigned long long)statbuf->st_ino) != B_TRUE) {
+ return (B_FALSE);
+ }
+
+ if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mode",
+ "0x%llx", (unsigned long long)statbuf->st_mode) != B_TRUE) {
+ return (B_FALSE);
+ }
+
+ if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mtime",
+ "0x%llx", (unsigned long long)statbuf->st_mtime) != B_TRUE) {
+ return (B_FALSE);
+ }
+
+ if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ctime",
+ "0x%llx", (unsigned long long)statbuf->st_ctime) != B_TRUE) {
+ return (B_FALSE);
+ }
+
+ if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_size",
+ "0x%llx", (unsigned long long)statbuf->st_size) != B_TRUE) {
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: set_verbose
+ * Description: Turns on verbose output
+ * Scope: public
+ * Arguments: verbose = B_TRUE indicates verbose mode
+ * Returns: none
+ */
+void
+smlSetVerbose(boolean_t setting)
+{
+ verbose = setting;
+}
+
+/*
+ * Name: get_verbose
+ * Description: Returns whether or not to output verbose messages
+ * Scope: public
+ * Arguments: none
+ * Returns: B_TRUE - verbose messages should be output
+ */
+boolean_t
+smlGetVerbose()
+{
+ return (verbose);
+}
+
+/*
+ * Name: sml_strPrintf
+ * Synopsis: Create string from printf style format and arguments
+ * Description: Call to convert a printf style format and arguments into a
+ * string of characters placed in allocated storage
+ * Arguments: format - [RO, RO*] (char *)
+ * printf-style format for string to be formatted
+ * ... - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: char *
+ * A string representing the printf conversion results
+ * 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.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+/*PRINTFLIKE1*/
+char *
+sml_strPrintf(char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char bfr[1];
+ char *rstr = (char *)NULL;
+
+ /* entry assertions */
+
+ assert(a_format != (char *)NULL);
+ assert(*a_format != '\0');
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ rstr = (char *)calloc(1, vres+2);
+ assert(rstr != (char *)NULL);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(*rstr != '\0');
+
+ /* return the results */
+
+ return (rstr);
+}
+
+/*
+ * Name: sml_strPrintf_r
+ * Synopsis: Create string from printf style format and arguments
+ * Description: Call to convert a printf style format and arguments into a
+ * string of characters placed in allocated storage
+ * Arguments: a_buf - [RO, *RW] - (char *)
+ * - Pointer to buffer used as storage space for the
+ * returned string created
+ * a_bufLen - [RO, *RO] - (int)
+ * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ * bytes will be placed in 'a_buf' - the returned
+ * string is always null terminated
+ * a_format - [RO, RO*] (char *)
+ * printf-style format for string to be formatted
+ * VARG_LIST - [RO] (?)
+ * arguments as appropriate to 'format' specified
+ * Returns: void
+ */
+
+/*PRINTFLIKE3*/
+void
+sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+
+ /* entry assertions */
+
+ assert(a_format != (char *)NULL);
+ assert(*a_format != '\0');
+ assert(a_buf != (char *)NULL);
+ assert(a_bufLen > 1);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+ assert(vres < a_bufLen);
+
+ a_buf[a_bufLen-1] = '\0';
+}
+
+/*
+ * Name: sml_XmlEncodeString
+ * Description: Given a plain text string, convert that string into one that
+ * encoded using the XML character reference encoding format.
+ * Arguments: a_plain_text_string - [RO, *RO] (char *)
+ * The plain text string to convert (encode)
+ * Returns: char *
+ * The encoded form of the plain text string provided
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'lu_memFree' to dispose
+ * of the storage once the string is no longer needed.
+ */
+
+char *
+sml_XmlEncodeString(char *a_plainTextString)
+{
+ char *stringHead; /* -> start of string containing encoded data */
+ long stringTail; /* byte pos of first free byte in stringHead */
+ long stringLength; /* total bytes allocd starting at stringHead */
+ char *p; /* temp -> to retrieve bytes from src string */
+ long textLength = 0; /* length of the string to convert */
+
+ /* entry assertions */
+
+ assert(a_plainTextString != (char *)NULL);
+
+ textLength = strlen(a_plainTextString);
+
+ /* Allocate initial string buffer to hold results */
+
+ stringLength = textLength*2;
+ stringTail = 0;
+ stringHead = (char *)calloc(1, (size_t)stringLength+2);
+ assert(stringHead != (char *)NULL);
+
+ /* Add in the encoded message text */
+
+ for (p = a_plainTextString; textLength > 0; p++, textLength--) {
+ /*
+ * Must have at least 12 bytes: this must be at least the
+ * maximum number of bytes that can be added for a single
+ * byte as the last byte of the stream. Assuming the byte
+ * needs to be encoded, it could be:
+ * &#xxxxxxxx;\0
+ * If not that many bytes left, grow the buffer.
+ */
+
+ if ((stringLength-stringTail) < 12) {
+ stringLength += (textLength*2)+12;
+ stringHead =
+ realloc(stringHead,
+ (size_t)stringLength+2);
+ assert(stringHead != (char *)NULL);
+ }
+
+ /*
+ * See if this byte is a 'printable 7-bit ascii value'.
+ * If so just add it to the new string; otherwise, must
+ * output an XML character value encoding for the byte.
+ */
+
+ switch (*p) {
+ case '!':
+ case '#':
+ case '%':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '[':
+ case ']':
+ case '^':
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ case ' ':
+ /*
+ * It is a printable 7-bit ascii character:
+ * just add it to the end of the new string.
+ */
+
+ stringHead[stringTail++] = *p;
+ break;
+ default:
+ /*
+ * It is not a printable 7-bit ascii character:
+ * add it as an xml character value encoding.
+ */
+
+ stringTail += sprintf(&stringHead[stringTail], "&#%x;",
+ (*p)&0xFF);
+ break;
+ }
+ }
+
+ /* Terminate the new string */
+
+ stringHead[stringTail] = '\0';
+
+ /* realloc the string so it is only as big as it needs to be */
+
+ stringHead = realloc(stringHead, stringTail+1);
+ assert(stringHead != (char *)NULL);
+
+ return (stringHead);
+}
+
+/*
+ * Name: sml_XmlDecodeString
+ * Description: Given a string encoded using the XML character reference format,
+ * convert that string into a plain text (unencoded) string.
+ * Arguments: a_xml_encoded_string - [RO, *RO] (char *)
+ * The XML encoded string to convert to plain text
+ * Returns: char *
+ * The unencoded (plain text) form of the encoded string
+ * NOTE: Any string returned is placed in new storage for the
+ * calling method. The caller must use 'lu_memFree' to dispose
+ * of the storage once the string is no longer needed.
+ */
+
+char *
+sml_XmlDecodeString(char *a_xmlEncodedString)
+{
+ char *s = NULL; /* -> index into encoded bytes string */
+ char *d = NULL; /* -> index into decoded bytes string */
+ char *rs = NULL; /* -> string holding ref bytes allocated */
+ char *ri = NULL; /* -> index into string holding reference */
+ long textLength = 0; /* length of encoded string to decode */
+ unsigned long rv = 0; /* temp to hold scanf results of byte conv */
+ char *i = NULL; /* temp to hold strchr results */
+ char *stringHead = NULL; /* -> plain test buffer */
+ ptrdiff_t tmpdiff;
+
+ /*
+ * A finite state machine is used to convert the xml encoded string
+ * into plain text. The states of the machine are defined below.
+ */
+
+ int fsmsState = -1; /* Finite state machine state */
+#define fsms_text 0 /* Decoding plain text */
+#define fsms_seenAmp 1 /* Found & */
+#define fsms_seenPound 2 /* Found # following & */
+#define fsms_collect 3 /* Collecting character reference bytes */
+
+ /* entry assertions */
+
+ assert(a_xmlEncodedString != (char *)NULL);
+
+ textLength = strlen(a_xmlEncodedString);
+
+ /*
+ * Allocate string that can contain the decoded string.
+ * Since decoding always results in a shorter string (bytes encoded
+ * using the XML character reference are larger in the encoded form)
+ * we can allocate a string the same size as the encoded string.
+ */
+
+ stringHead = (char *)calloc(1, textLength+1);
+ assert(stringHead != (char *)NULL);
+
+ /*
+ * Convert all bytes.
+ */
+
+ /* Decoding plain text */
+ fsmsState = fsms_text;
+
+ for (s = a_xmlEncodedString, d = stringHead; textLength > 0;
+ s++, textLength--) {
+ switch (fsmsState) {
+ case fsms_text: /* Decoding plain text */
+ if (rs != NULL) {
+ free(rs);
+ rs = NULL;
+ ri = NULL;
+ }
+ if (*s == '&') {
+ /* Found & */
+ fsmsState = fsms_seenAmp;
+ continue;
+ }
+ *d++ = *s;
+ continue;
+
+ case fsms_seenAmp: /* Found & */
+ if (*s == '#') {
+ /* Found # following & */
+ fsmsState = fsms_seenPound;
+ continue;
+ }
+ fsmsState = fsms_text; /* Decoding plain text */
+ *d++ = '&';
+ *d++ = *s;
+ continue;
+
+ case fsms_seenPound: /* Found # following & */
+ i = strchr(s, ';');
+ if (i == NULL) {
+ /* Decoding plain text */
+ fsmsState = fsms_text;
+ *d++ = '&';
+ *d++ = '#';
+ *d++ = *s;
+ continue;
+ }
+ tmpdiff = (ptrdiff_t)i - (ptrdiff_t)s;
+ rs = (char *)calloc(1, tmpdiff + 1);
+ assert(rs != (char *)NULL);
+ ri = rs;
+ /* Collecting character reference bytes */
+ fsmsState = fsms_collect;
+
+ /*FALLTHRU*/
+
+ /* Collecting character reference bytes */
+ case fsms_collect:
+ if (*s != ';') {
+ switch (*s) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ *ri++ = *s;
+ break;
+ default:
+ *ri = '\0';
+ *d++ = '&';
+ *d++ = '#';
+ tmpdiff = (ptrdiff_t)ri - (ptrdiff_t)rs;
+ (void) strncpy(d, rs, tmpdiff-1);
+ *d++ = *s;
+ /* Decoding plain text */
+ fsmsState = fsms_text;
+ break;
+ }
+ continue;
+ }
+ *ri = '\0';
+ if (sscanf(rs, "%lx", &rv) != 1) {
+ *d++ = '?';
+ } else {
+ *d++ = (rv & 0xFF);
+ }
+ /* Decoding plain text */
+ fsmsState = fsms_text;
+ }
+ }
+
+ /* Done converting bytes - deallocate reference byte storage */
+
+ free(rs);
+
+ /* terminate the converted (plain text) string */
+
+ *d = '\0';
+
+ /* exit assertions */
+
+ assert(stringHead != (char *)NULL);
+
+ return (stringHead);
+}
+
+/*
+ * Private Methods
+ */
+
+/*
+ * Name: _smlReadTag
+ * Description: read complete tag from a datastream
+ * Arguments: err - [RO, *RW] (LU_ERR)
+ * Error object - used to contain any errors encountered
+ * and return those errors to this methods caller
+ * r_tag - [RW, *RW] - (SML_TAG **)
+ * Pointer to handle to place new tag object
+ * == SML_TAG__NULL if empty tag found (not an error)
+ * ds - [RO, *RO] - (LU_DS)
+ * Handle to datastream to read tag from
+ * parent - [RO, *RO] - (char *)
+ * Name for parent of tag (NONE if top of tag)
+ * Returns: int
+ * RESULT_OK - tag successfully read
+ * RESULT_ERR - problem reading tag
+ * NOTE: Any tag object returned is placed in new storage for the
+ * calling method. The caller must use 'smlFreeTag' to dispose
+ * of the storage once the tag object name is no longer needed.
+ * Errors: If the tag object cannot be duplicated, the process exits
+ */
+
+static int
+_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent)
+{
+ int r;
+ SML_TAG *tag;
+ SML_TAG *tmp_tag;
+ char name[MAX_SML_COMPONENT_LENGTH];
+ int pos = 0;
+ int c;
+ char *p = *a_str;
+
+ /* entry assertions */
+
+ assert(SML_TAG__R_ISVALID(r_tag));
+ assert(a_str != (char **)NULL);
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_TAG,
+ parent ? parent : "<<TOP TAG>>");
+
+ /* reset return tag */
+
+ *r_tag = SML_TAG__NULL;
+
+ /* allocate zeroed storage for the tag object */
+
+ tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+ assert(tag != SML_TAG__NULL);
+
+ /* reset name accumulator storage */
+
+ bzero(name, sizeof (name));
+
+ /* ignore delimters before tag */
+
+ for (;;) {
+ /* read tag character - handle failure/EOF */
+
+ if ((*p == '\0') || ((c = (*p++)) == '\0')) {
+ if (parent == NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_READTAG_EXPECTED_EOF,
+ p ? p : "?");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_OK);
+ }
+
+ /* EOF in middle of processing tag */
+
+ _smlLogMsg(LOG_MSG_ERR,
+ DBG_SML_READTAG_UNEXPECTED_EOF,
+ p ? p : "?");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* if beginning of tag, break out */
+
+ if (c == '<') {
+ break;
+ }
+
+ /* not tag beginning: ignore delimiters if not inside tag yet */
+
+ if (parent == (char *)NULL) {
+ /* ignore delimters */
+
+ if (strchr(" \t", c) != (char *)NULL) {
+ continue;
+ }
+
+ /* on blank lines, return no tag object */
+
+ if (c == '\n') {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_READTAG_BLANKLINE,
+ p ? p : "?");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_OK);
+ }
+
+ /* invalid character before tag start */
+
+ _smlLogMsg(LOG_MSG_ERR, ERR_SML_READTAG_BAD_START_CHAR,
+ c, (unsigned int)c);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+ }
+
+ /*
+ * all delimiters have been ignored and opening tag character seen;
+ * process tag
+ */
+
+ assert(c == '<');
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* handle EOF after tag opening character found */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_EOF_BEFORE_TAG_NAME,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* is this a tag closure? */
+
+ if (c == '/') {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_START_CLOSE_TAG,
+ parent ? parent : "<<NONE>>");
+
+ for (;;) {
+ /* get next character of tag name */
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* EOF inside tag name? */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_CLOSE_TAG_EOF,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* tag close: break out of collection loop */
+
+ if (c == '>') {
+ break;
+ }
+
+ /* see if illegal character in tag name */
+
+ /* CSTYLED */
+ if (strchr("/ \t\n\":<?$'\\`!@#%^&*()+=|[]{};,", c)
+ != NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_CLOSE_TAG_ILLCHAR,
+ c, (unsigned int)c, name);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* valid character - add to name if room left */
+
+ if (pos < sizeof (name)-1) {
+ name[pos] = (c&0xFF);
+ pos++;
+ }
+
+ assert(pos < sizeof (name));
+ }
+
+ /* close of tag found */
+
+ assert(c == '>');
+
+ /* is the tag empty? If so that's an error */
+
+ if (*name == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_CLOSE_EMPTY_TAG);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* if no parent, a close tag outside of any open tag */
+
+ if (parent == (char *)NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_CLOSE_NO_PARENT,
+ name);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* if not close to current parent, error */
+
+ if (!streq(parent, name)) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_CLOSE_WRONG_TAG,
+ name, parent);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* close of current tag found - success */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READTAG_CLOSE_TAG,
+ name);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_OK);
+ }
+
+ /* not starting a close tag */
+
+ assert(c != '/');
+ assert(c != '<');
+
+ /* at start of tag - input tag name */
+
+ bzero(name, sizeof (name));
+ pos = 0;
+
+ for (;;) {
+
+ /* EOF inside of tag name? */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_TAG_EOF,
+ name, parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* if separator or end of line then tag name collected */
+
+ if (strchr(" >\t\n", c) != NULL) {
+ break;
+ }
+
+ /* see if illegal character in tag name */
+
+ /*CSTYLED*/
+ if (strchr("\":<>?$'\\`!@#%^&*()+=|[]{};,", c) != NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_TAG_ILLCHAR,
+ c, (unsigned int)c, name);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* close current tag? */
+
+ if (c == '/') {
+ /* get next character of tag name */
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* tag close not found? */
+
+ if (c != '>') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_BADTAG_CLOSE,
+ name, parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* is the tag empty? If so that's an error */
+
+ if (*name == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_EMPTY_TAG,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* tag closed */
+
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_READTAG_CLOSED_TAG,
+ name, parent ? parent : "<<NONE>>");
+
+ tag->name = strdup(name);
+ *r_tag = tag;
+ *a_str = p;
+ return (RESULT_OK);
+ }
+
+ /* valid character - add to name if room left */
+
+ if (pos < sizeof (name)-1) {
+ name[pos] = (c&0xFF);
+ pos++;
+ }
+
+ assert(pos < sizeof (name));
+
+ /* get next character to parse */
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+ }
+
+ /* have a valid tag name: <tagname */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_TAG_NAME,
+ name, parent ? parent : "<<NONE>>");
+
+ assert(*name != '\0');
+
+ /* place tag name inside of tag object */
+
+ tag->name = strdup(name);
+
+ /* clear out name accumulator to get parameters */
+
+ bzero(name, sizeof (name));
+ pos = 0;
+
+ /* input parameters */
+
+ if (c != '>')
+ for (;;) {
+
+ char *pname;
+ char *pvalue;
+ SML_PARAM *parameter;
+
+ /* pass spaces before parameter name */
+
+ for (;;) {
+
+ /* get next character of parameter name */
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* EOF inside parameter name? */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARM_EOF,
+ tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* if separator/end of line tag parameter collected */
+
+ if (strchr(" \t\n", c) != NULL) {
+ continue;
+ }
+
+ /* see if illegal character in parameter name */
+
+ /*CSTYLED*/
+ if (strchr("\":<?$'\\`!@#%^&*()+=|[]{};,.", c) !=
+ (char *)NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARMNAME_ILLCHAR,
+ c, (unsigned int)c, name, tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* tag close found? */
+
+ if (c == '>') {
+ break;
+ }
+
+ /* close tag found ? */
+
+ if (c == '/') {
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+ if (c == '>') {
+ _smlLogMsg(LOG_MSG_DEBUG,
+ DBG_SML_TAG_ONLY,
+ tag->name);
+ *r_tag = tag;
+ *a_str = p;
+ return (RESULT_OK);
+ }
+
+ /* / not followed by > */
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_BADPARMNAME_CLOSE,
+ name, tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* valid character - add to name if room left */
+
+ if (pos < sizeof (name)-1) {
+ name[pos] = (c&0xFF);
+ pos++;
+ }
+
+ assert(pos < sizeof (name));
+ break;
+ }
+
+ if (c == '>') {
+ break;
+ }
+
+ /* input parameter name */
+
+ for (;;) {
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* EOF inside of parameter name? */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARM_EOF,
+ tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /*CSTYLED*/
+ if (strchr("\t \n\":<>?$'\\`!@%^*()+|[]{},./", c) != NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARMNAME_ILLCHAR,
+ c, (unsigned int)c, name, tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* name - value separator found ? */
+
+ if (c == '=') {
+ break;
+ }
+
+ /* valid character - add to name if room left */
+
+ if (pos < sizeof (name)-1) {
+ name[pos] = (c&0xFF);
+ pos++;
+ }
+
+ assert(pos < sizeof (name));
+ }
+
+ /* is the parameter name empty? If so that's an error */
+
+ if (*name == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_EMPTY_PARMNAME,
+ tag->name, parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* have a parameter name */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_NAME,
+ name, tag->name);
+
+ /* duplicate (save) parameter name */
+
+ pname = strdup(name);
+
+ /* clear out name accumulator to get parameters */
+
+ bzero(name, sizeof (name));
+ pos = 0;
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ if (c != '"') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_PARM_SEP_BAD,
+ c, (unsigned int)c);
+ free(pname);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* input parameter value */
+
+ for (;;) {
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ /* EOF inside of parameter value? */
+
+ if (c == '\0') {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARMVAL_EOF,
+ pname, tag->name,
+ parent ? parent : "<<NONE>>");
+ smlFreeTag(tag);
+ free(pname);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* close of parameter value? */
+
+ if (c == '"') {
+ break;
+ }
+
+ if (strchr("\n", c) != NULL) {
+ _smlLogMsg(LOG_MSG_ERR,
+ ERR_SML_READTAG_PARMVAL_NL,
+ pname, tag->name,
+ parent ? parent : "<<NONE>>");
+ free(pname);
+ smlFreeTag(tag);
+ *a_str = p;
+ return (RESULT_ERR);
+ }
+
+ /* valid character - add to value if room left */
+
+ if (pos < sizeof (name)-1) {
+ name[pos] = (c&0xFF);
+ pos++;
+ }
+
+ assert(pos < sizeof (name));
+ }
+
+ /* got the value */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_VALUE,
+ pname, name, tag->name);
+
+ pvalue = sml_XmlDecodeString(name);
+ bzero(name, sizeof (name));
+ pos = 0;
+
+ parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
+ bzero(parameter, sizeof (SML_PARAM));
+ parameter->name = pname;
+ parameter->value = pvalue;
+ tag->params_num++;
+ tag->params = (SML_PARAM *)
+ realloc(tag->params,
+ sizeof (SML_PARAM) *tag->params_num);
+ (void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
+ sizeof (SML_PARAM));
+
+ free(parameter);
+ if (c == '>') {
+ break;
+ }
+ }
+
+ /* finished processing this tag element entry */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_TAG_HEAD_DONE,
+ tag->name, parent ? parent : "<<NULL>>");
+
+ tag->tags = NULL;
+
+ while (((r = _smlReadTag(&tmp_tag, &p, tag->name))
+ == RESULT_OK) && (tmp_tag != NULL)) {
+ tag->tags_num++;
+ tag->tags = (SML_TAG *)realloc(tag->tags,
+ sizeof (SML_TAG) *tag->tags_num);
+ (void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
+ sizeof (SML_TAG));
+ free(tmp_tag);
+ }
+
+ c = *p;
+ if (*p != '\0') {
+ p++;
+ }
+
+ *r_tag = tag;
+ *a_str = p;
+ return (r);
+}
+
+/*
+ * Name: _smlWriteParamValue
+ * Description: XML Encode a plain text parameter value and write to datastream
+ * Arguments: ds - [RO, *RO] - (LU_DS)
+ * Handle to datastream to write parameter value to
+ * value - [RO, *RO] - (char *)
+ * Parameter value to be encoded and written
+ * Returns: int
+ * RESULT_OK - tag successfully read
+ * RESULT_ERR - problem reading tag
+ */
+
+static int
+_smlWriteParamValue(char **a_str, char *value)
+{
+ char *ns;
+ char *p;
+
+ /* entry assertions */
+
+ assert(a_str != (char **)NULL);
+ assert(value != (char *)NULL);
+
+ /* xml encode the plain text string */
+
+ p = sml_XmlEncodeString(value);
+ assert(p != (char *)NULL);
+
+ /* write the xml encoded parameter value to the datastream */
+
+ ns = sml_strPrintf("%s\"%s\"", *a_str ? *a_str : "", p);
+
+ /* free up xml encoded value storage */
+
+ free(p);
+
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+
+ if (*a_str != NULL) {
+ free(*a_str);
+ }
+ *a_str = ns;
+
+ /* return results */
+
+ return (RESULT_OK);
+}
+
+static int
+_smlWriteSimpleTag(char **a_str, SML_TAG *tag)
+{
+ int r;
+ int k;
+ char *ns;
+ char *np0;
+ char *np1;
+
+ if (tag == NULL) {
+ return (RESULT_OK);
+ }
+
+ if (*a_str == NULL) {
+ *a_str = strdup("");
+ }
+
+ if (tag->params_num == 0) {
+ if (tag->tags_num == 0) {
+ ns = sml_strPrintf("%s<%s/>\n", *a_str, tag->name);
+ free(*a_str);
+ *a_str = ns;
+ return (RESULT_OK);
+ } else {
+ ns = sml_strPrintf("%s<%s>\n", *a_str, tag->name);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+ free(*a_str);
+ *a_str = ns;
+ }
+ } else {
+ ns = sml_strPrintf("%s<%s %s=", *a_str ? *a_str : "", tag->name,
+ tag->params[0].name);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+ free(*a_str);
+ *a_str = ns;
+
+ np0 = NULL;
+ r = _smlWriteParamValue(&np0, tag->params[0].value);
+ if ((np0 == NULL) || (r != RESULT_OK)) {
+ return (RESULT_ERR);
+ }
+
+ ns = sml_strPrintf("%s%s", *a_str, np0);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+
+ free(np0);
+ free(*a_str);
+ *a_str = ns;
+
+ for (k = 1; k < tag->params_num; k++) {
+ np0 = sml_strPrintf(" %s=", tag->params[k].name);
+ if (np0 == NULL) {
+ return (RESULT_ERR);
+ }
+ np1 = NULL;
+ r = _smlWriteParamValue(&np1, tag->params[k].value);
+ if ((np1 == NULL) || (r != RESULT_OK)) {
+ return (RESULT_ERR);
+ }
+
+ ns = sml_strPrintf("%s%s%s", *a_str, np0, np1);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+
+ free(np0);
+ free(np1);
+ free(*a_str);
+ *a_str = ns;
+ }
+
+ if (tag->tags_num == 0) {
+ np0 = sml_strPrintf("/>\n");
+ if (np0 == NULL) {
+ return (RESULT_ERR);
+ }
+ ns = sml_strPrintf("%s%s", *a_str, np0);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+ free(np0);
+ free(*a_str);
+ *a_str = ns;
+ } else {
+ np0 = sml_strPrintf(">\n");
+ if (np0 == NULL) {
+ return (RESULT_ERR);
+ }
+ ns = sml_strPrintf("%s%s", *a_str, np0);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+ free(np0);
+ free(*a_str);
+ *a_str = ns;
+ }
+ }
+
+ for (k = 0; k < tag->tags_num; k++) {
+ r = _smlWriteSimpleTag(a_str, &(tag->tags[k]));
+ if (r != RESULT_OK) {
+ return (r);
+ }
+ }
+
+ if (tag->tags_num > 0) {
+ np0 = sml_strPrintf("</%s>\n", tag->name);
+ if (np0 == NULL) {
+ return (RESULT_ERR);
+ }
+ ns = sml_strPrintf("%s%s", *a_str ? *a_str : "", np0);
+ if (ns == NULL) {
+ return (RESULT_ERR);
+ }
+ free(np0);
+ free(*a_str);
+ *a_str = ns;
+ }
+
+ return (RESULT_OK);
+}
+
+static void
+_smlFreeTag(SML_TAG *tag)
+{
+ int k;
+
+ /* entry assertions */
+
+ assert(tag != SML_TAG__NULL);
+
+ /* entry debugging info */
+
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG,
+ (unsigned long)tag,
+ tag->name ? tag->name : "<<NONE>>",
+ tag->params_num, tag->tags_num);
+
+ for (k = 0; k < tag->params_num; k++) {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_NAME,
+ (unsigned long)(&tag->params[k]),
+ (unsigned long)(tag->params[k].name),
+ tag->params[k].name);
+ free(tag->params[k].name);
+ tag->params[k].name = (char *)NULL;
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_VALUE,
+ (unsigned long)(&tag->params[k]),
+ (unsigned long)(tag->params[k].value),
+ tag->params[k].value);
+ free(tag->params[k].value);
+ tag->params[k].value = (char *)NULL;
+ }
+
+ for (k = 0; k < tag->tags_num; k++) {
+ _smlFreeTag(&tag->tags[k]);
+ }
+
+ if (tag->name != NULL) {
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG_NAME,
+ (unsigned long)tag->name, tag->name);
+ free(tag->name);
+ tag->name = NULL;
+ }
+
+
+ if (tag->params != NULL) {
+ assert(tag->params_num > 0);
+ bzero(tag->params, sizeof (SML_PARAM)*tag->params_num);
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAMS,
+ (unsigned long)tag->params);
+ free(tag->params);
+ tag->params = NULL;
+ tag->params_num = 0;
+ }
+
+ if (tag->tags != NULL) {
+ assert(tag->tags_num > 0);
+ bzero(tag->tags, sizeof (SML_TAG)*tag->tags_num);
+ _smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAGS,
+ (unsigned long)tag->tags);
+ free(tag->tags);
+ tag->tags = NULL;
+ tag->tags_num = 0;
+ }
+}
+
+/*
+ * Name: log_msg
+ * Description: Outputs messages to logging facility.
+ * Scope: public
+ * Arguments: type - the severity of the message
+ * out - where to output the message.
+ * fmt - the printf format, plus its arguments
+ * Returns: none
+ */
+
+/*PRINTFLIKE2*/
+static void
+_smlLogMsg(LogMsgType a_type, const char *a_format, ...)
+{
+ va_list ap;
+ size_t vres = 0;
+ char bfr[1];
+ char *rstr = (char *)NULL;
+ FILE *out;
+ char *prefix;
+
+ switch (a_type) {
+ case LOG_MSG_ERR:
+ default:
+ out = stderr;
+ prefix = MSG_LOG_ERROR;
+ break;
+ case LOG_MSG_WRN:
+ out = stderr;
+ prefix = MSG_LOG_WARNING;
+ break;
+ case LOG_MSG_INFO:
+ out = stdout;
+ prefix = NULL;
+ break;
+ case LOG_MSG_DEBUG:
+ if (!smlGetVerbose()) {
+ /* no debug messages if not verbose mode */
+ return;
+ }
+ out = stderr;
+ prefix = MSG_LOG_DEBUG;
+ break;
+ }
+
+ if (prefix != NULL) {
+ (void) fprintf(out, "%s: ", prefix);
+ }
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ /* allocate storage to hold the message */
+
+ rstr = (char *)malloc(vres+2);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(rstr, vres+1, a_format, ap);
+ va_end(ap);
+
+ if (fprintf(out, "%s\n", rstr) < 0) {
+ /*
+ * nothing output, try stderr as a
+ * last resort
+ */
+ (void) fprintf(stderr, ERR_LOG_FAIL, a_format);
+ }
+
+ free(rstr);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/srcpath.c b/usr/src/cmd/svr4pkg/libinst/srcpath.c
new file mode 100644
index 0000000000..4d0a5dd69a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/srcpath.c
@@ -0,0 +1,69 @@
+/*
+ * 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 */
+
+
+
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+char *
+srcpath(char *dir, char *src, int part, int nparts)
+{
+ static char tmppath[PATH_MAX];
+ char *copy;
+ size_t copyLen;
+
+ copy = tmppath;
+
+ if (dir != NULL) {
+ size_t theLen = strlen(dir);
+
+ (void) strcpy(copy, dir);
+ copy += theLen;
+ copyLen = (sizeof (tmppath) - theLen);
+ } else {
+ copy[0] = '\0';
+ copyLen = sizeof (tmppath);
+ }
+
+ if (nparts > 1) {
+ (void) snprintf(copy, copyLen,
+ ((src[0] == '/') ? "/root.%d%s" : "/reloc.%d/%s"),
+ part, src);
+ } else {
+ (void) snprintf(copy, copyLen,
+ ((src[0] == '/') ? "/root%s" : "/reloc/%s"), src);
+ }
+
+ return (tmppath);
+}
diff --git a/usr/src/cmd/svr4pkg/libinst/stub.c b/usr/src/cmd/svr4pkg/libinst/stub.c
new file mode 100644
index 0000000000..1deca146eb
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/stub.c
@@ -0,0 +1,46 @@
+/*
+ * 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 1993 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#ifdef PRESVR4
+int
+rename(char *x, char *y)
+{
+ return (link(x, y) || unlink(x));
+}
+#else
+static int dummy; /* used to make compillor warning go away */
+#ifdef lint
+_______a()
+{
+ return (dummy++);
+}
+#endif /* lint */
+#endif
diff --git a/usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.c b/usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.c
new file mode 100644
index 0000000000..ea2b407435
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.c
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * System includes
+ */
+
+#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 <assert.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>
+
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * Name: unpack_package_from_stream
+ * Description: unpack a package from a stream into a temporary directory
+ * Arguments: a_idsName - pointer to string representing the input data
+ * stream containing the package to unpack
+ * a_pkginst - pointer to string representing the name of
+ * the package to unpack from the specified stream
+ * a_tempDir - pointer to string representing the path to a
+ * directory into which the package will be unpacked
+ * Returns: boolean_t
+ * == B_TRUE - package successfully unpacked from stream
+ * == B_FALSE - failed to unpack package from stream
+ */
+
+boolean_t
+unpack_package_from_stream(char *a_idsName, char *a_pkginst, char *a_tempDir)
+{
+ int dparts;
+ char instdir[PATH_MAX];
+
+ /* entry assertions */
+
+ assert(a_idsName != (char *)NULL);
+ assert(a_pkginst != (char *)NULL);
+ assert(a_tempDir != (char *)NULL);
+
+ /* entry debug information */
+
+ echoDebug(DBG_UNPACKSTRM_ENTRY);
+ echoDebug(DBG_UNPACKSTRM_ARGS, a_pkginst, a_idsName, a_tempDir);
+
+ /* find the specified package in the datastream */
+
+ dparts = ds_findpkg(a_idsName, a_pkginst);
+ if (dparts < 1) {
+ progerr(gettext(ERR_DSARCH), a_pkginst);
+ return (B_FALSE);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * read in next part from stream, even if we decide
+ * later that we don't need it
+ */
+
+ /* create directory to hold this package instance */
+
+ if (snprintf(instdir, sizeof (instdir), "%s/%s", a_tempDir, a_pkginst)
+ >= PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2, a_tempDir, a_pkginst);
+ return (B_FALSE);
+ }
+
+ switch (fmkdir(instdir, 0755)) {
+ case 0: /* directory created */
+ break;
+ case 1: /* could not remove existing non-directory node */
+ progerr(ERR_REMOVE, instdir, strerror(errno));
+ return (B_FALSE);
+ case 2: /* could not create specified new directory */
+ default:
+ progerr(ERR_UNPACK_FMKDIR, instdir, strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* unpack package instance from stream to dir created */
+
+ echoDebug(DBG_UNPACKSTRM_UNPACKING, a_pkginst, a_idsName, instdir);
+
+ if (chdir(instdir)) {
+ progerr(ERR_CHDIR, instdir);
+ return (B_FALSE);
+ }
+
+ while (dparts--) {
+ if (ds_next(a_idsName, instdir)) {
+ progerr(ERR_UNPACK_DSREAD, dparts+1, a_idsName, instdir,
+ a_pkginst);
+ return (B_FALSE);
+ }
+ }
+
+ if (chdir(get_PKGADM())) {
+ progerr(gettext(ERR_CHDIR), get_PKGADM());
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
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__ */
diff --git a/usr/src/cmd/svr4pkg/pkgadm/Makefile b/usr/src/cmd/svr4pkg/pkgadm/Makefile
new file mode 100644
index 0000000000..f110c4f0de
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/Makefile
@@ -0,0 +1,45 @@
+#
+# 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= pkgadm
+
+OBJS= addcert.o \
+ certs.o \
+ listcert.o \
+ lock.o \
+ main.o \
+ removecert.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -lcrypto -lgen
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgadm/addcert.c b/usr/src/cmd/svr4pkg/pkgadm/addcert.c
new file mode 100644
index 0000000000..0a1c7bdec0
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/addcert.c
@@ -0,0 +1,573 @@
+/*
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+#include <openssl/x509v3.h>
+#include <openssl/ui.h>
+
+#include <pkglib.h>
+#include <libinst.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+typedef enum {
+ VerifyFailed,
+ Accept,
+ Reject
+} VerifyStatus;
+
+static VerifyStatus verify_trust(X509 *);
+static boolean_t is_ca_cert(X509 *);
+
+/*
+ * Name: addcert
+ * Desc: Imports a user certificate into the keystore, along with a
+ * private key.
+ * Returns: 0 on success, non-zero otherwise.
+ */
+int
+addcert(int argc, char **argv)
+{
+ int i;
+ char keystore_file[MAXPATHLEN] = "";
+ char *keystore_base = NULL;
+ char *homedir;
+ char *passarg = NULL;
+ char *import_passarg = NULL;
+ char *altroot = NULL;
+ char *prog = NULL;
+ char *alias = NULL;
+ char *infile = NULL;
+ char *inkeyfile = NULL;
+ keystore_encoding_format_t informat = NULL;
+ char *informat_str = NULL;
+ int ret = 1;
+ boolean_t trusted = B_FALSE;
+ boolean_t implicit_trust = B_FALSE;
+
+ FILE *certfile = NULL;
+ FILE *keyfile = NULL;
+ X509 *cert = NULL;
+ STACK_OF(X509) *trustcerts = NULL;
+ EVP_PKEY *key = NULL;
+ PKG_ERR *err = NULL;
+ keystore_handle_t keystore = NULL;
+
+ while ((i = getopt(argc, argv, ":a:k:e:f:n:P:p:R:ty")) != EOF) {
+ switch (i) {
+ case 'a':
+ prog = optarg;
+ break;
+ case 'k':
+ keystore_base = optarg;
+ break;
+ case 'e':
+ inkeyfile = optarg;
+ break;
+ case 'f':
+ informat_str = optarg;
+ break;
+ case 'n':
+ alias = optarg;
+ break;
+ case 'P':
+ passarg = optarg;
+ break;
+ case 'p':
+ import_passarg = optarg;
+ break;
+ case 'R':
+ altroot = optarg;
+ break;
+ case 't':
+ trusted = B_TRUE;
+ break;
+ case 'y':
+ implicit_trust = B_TRUE;
+ break;
+ case ':':
+ log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+ /* LINTED fallthrough intentional */
+ case '?':
+ default:
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+ }
+
+ if (!trusted && alias == NULL) {
+ /* for untrusted (user) certs, we require a name */
+ log_msg(LOG_MSG_ERR, MSG_USER_NAME);
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ } else if (trusted && alias != NULL) {
+ /* for trusted certs, we cannot have a name */
+ log_msg(LOG_MSG_ERR, MSG_TRUSTED_NAME);
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+
+ if (trusted && inkeyfile != NULL) {
+ /* for trusted certs, we cannot have a private key */
+ log_msg(LOG_MSG_ERR, MSG_TRUSTED_KEY);
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+
+ /* last argument should be the path to the certificate */
+ if ((argc-optind) > 1) {
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ } else if ((argc-optind) < 1) {
+ infile = "stdin";
+ certfile = stdin;
+ log_msg(LOG_MSG_DEBUG, "Loading stdin certificate");
+ } else {
+ infile = argv[optind];
+ log_msg(LOG_MSG_DEBUG, "Loading <%s> certificate",
+ argv[optind]);
+ if ((certfile = fopen(infile, "r")) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_OPEN, infile);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * if specific key file supplied, open it, otherwise open
+ * default (stdin)
+ */
+ if (inkeyfile != NULL) {
+ if ((keyfile = fopen(inkeyfile, "r")) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_OPEN, inkeyfile);
+ goto cleanup;
+ }
+ } else {
+ inkeyfile = "stdin";
+ keyfile = stdin;
+ }
+
+ /* set up proper keystore */
+ if (altroot != NULL) {
+ if (strlcpy(keystore_file, altroot, MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+ goto cleanup;
+ }
+
+ if (strlcat(keystore_file, "/", MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+ goto cleanup;
+ }
+ }
+
+ if (keystore_base == NULL) {
+ if (geteuid() == 0 || altroot != NULL) {
+ /*
+ * If we have an alternate
+ * root, then we have no choice but to use
+ * root's keystore on that alternate root,
+ * since there is no way to resolve a
+ * user's home dir given an alternate root
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if ((homedir = getenv("HOME")) == NULL) {
+ /*
+ * not superuser, but no home dir, so
+ * use superuser's keystore
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if (strlcat(keystore_file, homedir,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ homedir);
+ goto cleanup;
+ }
+ if (strlcat(keystore_file, "/.pkg/security",
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ }
+ }
+ } else {
+ if (strlcat(keystore_file, keystore_base,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_base);
+ goto cleanup;
+ }
+ }
+
+ /* figure out input format */
+ if (informat_str == NULL) {
+ informat = KEYSTORE_FORMAT_PEM;
+ } else {
+ if (ci_streq(informat_str, "pem")) {
+ informat = KEYSTORE_FORMAT_PEM;
+ } else if (ci_streq(informat_str, "der")) {
+ informat = KEYSTORE_FORMAT_DER;
+ } else {
+ log_msg(LOG_MSG_ERR, MSG_BAD_FORMAT, informat_str);
+ goto cleanup;
+ }
+ }
+
+ err = pkgerr_new();
+
+ if (trusted) {
+ /* load all possible certs */
+ if (load_all_certs(err, certfile, informat, import_passarg,
+ &trustcerts) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+
+ /* we must have gotten at least one cert, if not, fail */
+ if (sk_X509_num(trustcerts) < 1) {
+ log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile);
+ goto cleanup;
+ }
+ } else {
+ /* first, try to load user certificate and key */
+ if (load_cert_and_key(err, certfile, informat, import_passarg,
+ &key, &cert) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+
+ /* we must have gotten a cert, if not, fail */
+ if (cert == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile);
+ goto cleanup;
+ }
+
+ if (key == NULL) {
+ /*
+ * if we are importing a user cert, and did not get
+ * a key, try to load it from the key file
+ */
+ if (keyfile == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_NEED_KEY, infile);
+ goto cleanup;
+ } else {
+ log_msg(LOG_MSG_DEBUG,
+ "Loading private key <%s>", inkeyfile);
+ if (load_cert_and_key(err, keyfile, informat,
+ import_passarg,
+ &key, NULL) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR,
+ MSG_NO_ADDKEY, inkeyfile);
+ goto cleanup;
+ }
+
+ if (key == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_NO_PRIVKEY,
+ inkeyfile);
+ log_msg(LOG_MSG_ERR,
+ MSG_NO_ADDKEY, inkeyfile);
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ if (trusted) {
+ /* check validity date of all certificates */
+ for (i = 0; i < sk_X509_num(trustcerts); i++) {
+ /* LINTED pointer cast may result in improper algnmnt */
+ cert = sk_X509_value(trustcerts, i);
+ if (check_cert(err, cert) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT,
+ infile);
+ goto cleanup;
+ }
+ }
+ } else {
+ /* check validity date of user certificate */
+ if (check_cert_and_key(err, cert, key) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+ }
+
+ if (trusted && !implicit_trust) {
+ /*
+ * if importing more than one cert, must use implicit trust,
+ * because we can't ask the user to individually trust
+ * each one, since there may be many
+ */
+ if (sk_X509_num(trustcerts) != 1) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_MULTIPLE_TRUST, infile, "-y");
+ goto cleanup;
+ } else {
+ /* LINTED pointer cast may result in improper algnmnt */
+ cert = sk_X509_value(trustcerts, 0);
+ }
+
+ /* ask the user */
+ switch (verify_trust(cert)) {
+ case Accept:
+ /* user accepted */
+ break;
+ case Reject:
+ /* user aborted operation */
+ log_msg(LOG_MSG_ERR, MSG_ADDCERT_ABORT);
+ goto cleanup;
+ case VerifyFailed:
+ default:
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+ }
+
+ /* now load the key store */
+ log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+ set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+ set_passphrase_passarg(passarg);
+ if (open_keystore(err, keystore_file, prog, pkg_passphrase_cb,
+ KEYSTORE_ACCESS_READWRITE | KEYSTORE_PATH_HARD, &keystore) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+
+ /* now merge the new cert into the keystore */
+ log_msg(LOG_MSG_DEBUG, "Merging certificate <%s>",
+ get_subject_display_name(cert));
+ if (trusted) {
+ /* merge all trusted certs found */
+ for (i = 0; i < sk_X509_num(trustcerts); i++) {
+ /* LINTED pointer cast may result in improper algnmnt */
+ cert = sk_X509_value(trustcerts, i);
+ if (merge_ca_cert(err, cert, keystore) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR,
+ MSG_NO_ADDCERT, infile);
+ goto cleanup;
+
+ } else {
+ log_msg(LOG_MSG_INFO, MSG_TRUSTING,
+ get_subject_display_name(cert));
+ }
+ }
+ } else {
+ /* merge user cert */
+ if (merge_cert_and_key(err, cert, key, alias, keystore) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+ }
+
+ /* now write it back out */
+ log_msg(LOG_MSG_DEBUG, "Closing keystore");
+ set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+ set_passphrase_passarg(passarg);
+ if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+ goto cleanup;
+ }
+
+ if (trusted) {
+ log_msg(LOG_MSG_INFO, MSG_TRUSTED, infile);
+ } else {
+ log_msg(LOG_MSG_INFO, MSG_ADDED, infile, alias);
+ }
+
+ ret = 0;
+
+ /* fallthrough intentional */
+cleanup:
+ if (err != NULL)
+ pkgerr_free(err);
+
+ if (certfile != NULL)
+ (void) fclose(certfile);
+
+ if (keyfile != NULL)
+ (void) fclose(keyfile);
+
+ return (ret);
+ }
+
+/* Asks user to verify certificate data before proceeding */
+static VerifyStatus verify_trust(X509 *cert)
+{
+ char vfy_trust = 'y';
+ VerifyStatus ret = Accept;
+ PKG_ERR *err;
+ UI *ui = NULL;
+
+ err = pkgerr_new();
+ /* print cert data */
+ if (print_cert(err, cert, KEYSTORE_FORMAT_TEXT,
+ get_subject_display_name(cert), B_TRUE, stdout) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if ((ui = UI_new()) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ /*
+ * The prompt is internationalized, but the valid
+ * response values are fixed, to avoid any complex
+ * multibyte processing that results in bugs
+ */
+ if (UI_add_input_boolean(ui, MSG_VERIFY_TRUST,
+ "",
+ "yY", "nN",
+ UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if (UI_process(ui) != 0) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if (vfy_trust != 'y') {
+ ret = Reject;
+ goto cleanup;
+ }
+
+ /*
+ * if the cert does not appear to be a CA cert
+ * r is not self-signed, verify that as well
+ */
+ if (!is_ca_cert(cert)) {
+ UI_free(ui);
+ if ((ui = UI_new()) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if (UI_add_input_boolean(ui,
+ MSG_VERIFY_NOT_CA,
+ "",
+ "yY", "nN",
+ UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) {
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if (UI_process(ui) != 0) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = VerifyFailed;
+ goto cleanup;
+ }
+
+ if (vfy_trust != 'y') {
+ ret = Reject;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (ui != NULL)
+ UI_free(ui);
+
+ if (err != NULL)
+ pkgerr_free(err);
+
+ return (ret);
+}
+/*
+ * Name: is_ca_cert
+ * Desc: Determines if a given certificate has the attributes
+ * of a CA certificate
+ * Returns: B_TRUE if certificate has attributes of a CA cert
+ * B_FALSE otherwise
+ */
+static boolean_t
+is_ca_cert(X509 *x)
+{
+
+ /*
+ * X509_check_purpose causes the extensions that we
+ * care about to be decoded and stored in the X509
+ * structure, so we must call it first
+ * before checking for CA extensions in the X509
+ * structure
+ */
+ (void) X509_check_purpose(x, X509_PURPOSE_ANY, 0);
+
+ /* keyUsage if present should allow cert signing */
+ if ((x->ex_flags & EXFLAG_KUSAGE) &&
+ !(x->ex_kusage & KU_KEY_CERT_SIGN)) {
+ return (B_FALSE);
+ }
+
+ /* If basicConstraints says not a CA then say so */
+ if (x->ex_flags & EXFLAG_BCONS) {
+ if (!(x->ex_flags & EXFLAG_CA)) {
+ return (B_FALSE);
+ }
+ }
+
+ /* no explicit not-a-CA flags set, so assume that it is */
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadm/certs.c b/usr/src/cmd/svr4pkg/pkgadm/certs.c
new file mode 100644
index 0000000000..c7c8f045ae
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/certs.c
@@ -0,0 +1,239 @@
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <openssl/err.h>
+#include <openssl/pkcs7.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/x509v3.h>
+
+#include <pkglib.h>
+#include <p12lib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+
+/*
+ * Function: load_cert_and_key
+ * Description: Loads a public key certificate and associated private key
+ * from a stream.
+ * Parameters: err - Where to write errors to for underlying library calls
+ * incert - File to read certs and keys from
+ * format - The format of the file
+ * passarg - How to collect password if needed to decrypt file
+ * key - Location to store resulting key if found
+ * cert - Location to store resulting cert if found.
+ *
+ * Returns: f one or more certificates are found in the file,
+ * and one or more keys are found, then the first
+ * certificate is used, and the keys are searched for a
+ * match. If no key matches the cert, then only the cert
+ * is returned. If no certs are found, but one or more
+ * keys are found, then the first key is returned.
+ */
+int
+load_cert_and_key(PKG_ERR *err, FILE *incert,
+ keystore_encoding_format_t format, char *passarg, EVP_PKEY **key,
+ X509 **cert)
+{
+ X509 *tmpcert = NULL;
+ EVP_PKEY *tmpkey = NULL;
+ STACK_OF(EVP_PKEY) *keys = NULL;
+ STACK_OF(X509) *certs = NULL;
+ int i, ret = 0;
+ keystore_passphrase_data data;
+ unsigned long crypto_err;
+
+ if (key) *key = NULL;
+ if (cert) *cert = NULL;
+
+ switch (format) {
+ case KEYSTORE_FORMAT_DER:
+ /* first try to load a DER cert, which cannot contain a key */
+ if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_PARSE);
+ ret = 1;
+ }
+ break;
+ case KEYSTORE_FORMAT_PEM:
+ default:
+ data.err = err;
+ set_passphrase_passarg(passarg);
+ set_passphrase_prompt(gettext("Enter PEM passphrase:"));
+ if (sunw_PEM_contents(incert, pkg_passphrase_cb,
+ &data, &keys, &certs) < 0) {
+ /* print out openssl-generated PEM errors */
+ while ((crypto_err = ERR_get_error()) != 0) {
+ log_msg(LOG_MSG_ERR,
+ ERR_reason_error_string(crypto_err));
+ }
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* take the first cert in the file, if any */
+ if (cert && (certs != NULL)) {
+ if (sk_X509_num(certs) != 1) {
+ log_msg(LOG_MSG_ERR, MSG_MULTIPLE_CERTS);
+ ret = 1;
+ goto cleanup;
+ } else {
+ tmpcert = sk_X509_value(certs, 0);
+ }
+ }
+
+ if (key && (keys != NULL)) {
+ if (tmpcert != NULL) {
+ /*
+ * if we found a cert and some keys,
+ * only return the key that
+ * matches the cert
+ */
+ for (i = 0; i < sk_EVP_PKEY_num(keys); i++) {
+ if (X509_check_private_key(tmpcert,
+ sk_EVP_PKEY_value(keys, i))) {
+ tmpkey =
+ sk_EVP_PKEY_value(keys, i);
+ break;
+ }
+ }
+ } else {
+ if (sk_EVP_PKEY_num(keys) > 0) {
+ tmpkey = sk_EVP_PKEY_value(keys, 0);
+ }
+ }
+ }
+ break;
+ }
+
+ /* set results */
+ if (key && tmpkey) {
+ *key = tmpkey;
+ tmpkey = NULL;
+ }
+
+ if (cert && tmpcert) {
+ *cert = tmpcert;
+ tmpcert = NULL;
+ }
+
+cleanup:
+ if (tmpcert != NULL) {
+ X509_free(tmpcert);
+ }
+ if (tmpkey != NULL) {
+ sunw_evp_pkey_free(tmpkey);
+ }
+ return (ret);
+}
+
+/*
+ * Function: load_all_certs
+ * Description: Loads alll certificates from a stream.
+ * Parameters: err - Where to write errors to for underlying library calls
+ * incert - File to read certs and keys from
+ * format - The format of the file
+ * passarg - How to collect password if needed to decrypt file
+ * certs - Location to store resulting cert if found.
+ *
+ * Returns: 0 - success, all certs placed in ''certs'
+ * non-zero failure, errors in 'err'
+ */
+int
+load_all_certs(PKG_ERR *err, FILE *incert,
+ keystore_encoding_format_t format, char *passarg, STACK_OF(X509) **certs)
+{
+ X509 *tmpcert = NULL;
+ STACK_OF(X509) *tmpcerts = NULL;
+ int ret = 0;
+ keystore_passphrase_data data;
+ unsigned long crypto_err;
+ if (certs) *certs = NULL;
+
+ switch (format) {
+ case KEYSTORE_FORMAT_DER:
+ /* first try to load a DER cert, which cannot contain a key */
+ if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_PARSE);
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((tmpcerts = sk_X509_new_null()) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_MEM);
+ ret = 1;
+ goto cleanup;
+ }
+ sk_X509_push(tmpcerts, tmpcert);
+ break;
+ case KEYSTORE_FORMAT_PEM:
+ default:
+ data.err = err;
+ set_passphrase_prompt(MSG_PEM_PASSPROMPT);
+ set_passphrase_passarg(passarg);
+ if (sunw_PEM_contents(incert, pkg_passphrase_cb,
+ &data, NULL, &tmpcerts) < 0) {
+ /* print out openssl-generated PEM errors */
+ while ((crypto_err = ERR_get_error()) != 0) {
+ log_msg(LOG_MSG_ERR,
+ ERR_reason_error_string(crypto_err));
+ }
+ }
+ break;
+ }
+
+ /* set results */
+ if (certs && tmpcerts) {
+ *certs = tmpcerts;
+ tmpcerts = NULL;
+ }
+
+cleanup:
+ if (tmpcerts != NULL) {
+ sk_X509_free(tmpcerts);
+ }
+ return (ret);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadm/listcert.c b/usr/src/cmd/svr4pkg/pkgadm/listcert.c
new file mode 100644
index 0000000000..731427271f
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/listcert.c
@@ -0,0 +1,245 @@
+/*
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/*
+ * Name: listcert
+ * Desc: Lists one or more certificates from the keystore
+ * Syntax: listcert [-a app] [-f format] [-k keystore] \
+ * [-n name] [-o outfile] [-P passarg] [-R altroot]
+ */
+int
+listcert(int argc, char **argv)
+{
+ int i;
+ char keystore_file[MAXPATHLEN] = "";
+ char *keystore_base = NULL;
+ char *homedir;
+ char *passarg = NULL;
+ char *altroot = NULL;
+ char *prog = NULL;
+ char *format_str = NULL;
+ keystore_encoding_format_t format;
+ char *alias = NULL;
+ char *outfile_str = NULL;
+ FILE *outfile = NULL;
+ int ret = 1;
+ PKG_ERR *err = NULL;
+ keystore_handle_t keystore = NULL;
+
+ while ((i = getopt(argc, argv, ":a:f:k:n:o:P:R:")) != EOF) {
+ switch (i) {
+ case 'a':
+ prog = optarg;
+ break;
+ case 'f':
+ format_str = optarg;
+ break;
+ case 'k':
+ keystore_base = optarg;
+ break;
+ case 'n':
+ alias = optarg;
+ break;
+ case 'o':
+ outfile_str = optarg;
+ break;
+ case 'P':
+ passarg = optarg;
+ break;
+ case 'R':
+ altroot = optarg;
+ break;
+ case ':':
+ log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+ /* fallthrough intentional */
+ case '?':
+ default:
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+ }
+
+ /* should be no arguments left */
+ if ((argc-optind) > 0) {
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+
+ /* figure out format */
+ if (format_str == NULL) {
+ format = KEYSTORE_FORMAT_TEXT;
+ } else {
+ if (ci_streq(format_str, "text")) {
+ format = KEYSTORE_FORMAT_TEXT;
+ } else if (ci_streq(format_str, "pem")) {
+ format = KEYSTORE_FORMAT_PEM;
+ } else if (ci_streq(format_str, "der")) {
+ format = KEYSTORE_FORMAT_DER;
+ } else {
+ log_msg(LOG_MSG_ERR, MSG_BAD_FORMAT, format_str);
+ goto cleanup;
+ }
+ }
+
+ /* open output file */
+ if (outfile_str == NULL) {
+ outfile = stdout;
+ outfile_str = "stdout";
+ } else {
+ if ((outfile = fopen(outfile_str, "w+")) == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_OPEN_WRITE, outfile_str);
+ goto cleanup;
+ }
+ }
+
+ /* set up proper keystore */
+ if (altroot != NULL) {
+ if (strlcpy(keystore_file, altroot, MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+ goto cleanup;
+ }
+
+ if (strlcat(keystore_file, "/", MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+ goto cleanup;
+ }
+ }
+
+ if (keystore_base == NULL) {
+ if (geteuid() == 0 || altroot != NULL) {
+ /*
+ * If we have an alternate
+ * root, then we have no choice but to use
+ * root's keystore on that alternate root,
+ * since there is no way to resolve a
+ * user's home dir given an alternate root
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if ((homedir = getenv("HOME")) == NULL) {
+ /*
+ * not superuser, but no home dir, so
+ * use superuser's keystore
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if (strlcat(keystore_file, homedir,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ homedir);
+ goto cleanup;
+ }
+ if (strlcat(keystore_file, "/.pkg/security",
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ }
+ }
+ } else {
+ if (strlcat(keystore_file, keystore_base,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_base);
+ goto cleanup;
+ }
+ }
+ err = pkgerr_new();
+
+ /* now load the key store */
+ log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+ set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+ set_passphrase_passarg(passarg);
+ if (open_keystore(err, keystore_file, prog,
+ pkg_passphrase_cb, KEYSTORE_DFLT_FLAGS,
+ &keystore) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+ goto cleanup;
+ }
+
+ /* list the certs */
+ log_msg(LOG_MSG_DEBUG, "Listing certificates");
+ if (print_certs(err, keystore, alias, format, outfile) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+ goto cleanup;
+ }
+
+ /* now close it out */
+ log_msg(LOG_MSG_DEBUG, "Closing keystore");
+ set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+ set_passphrase_passarg(passarg);
+ if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+ goto cleanup;
+ }
+
+ /* everything worked */
+ ret = 0;
+
+ /* fallthrough intentional */
+cleanup:
+ if (outfile != NULL)
+ (void) fclose(outfile);
+
+ if (err != NULL)
+ pkgerr_free(err);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadm/lock.c b/usr/src/cmd/svr4pkg/pkgadm/lock.c
new file mode 100644
index 0000000000..671f5eaf31
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/lock.c
@@ -0,0 +1,2150 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * Module: lock.c
+ * Program: pkgadm (/usr/bin/pkgadm)
+ * Synopsis: implements the zone/package administrative lock interface
+ * Public methods:
+ * admin_lock
+ * Usage:
+ * Acquire: -a [ -e | -s ] [ -o obj ] [ -k key ] [ -R root ] [ -q ] \
+ * [ -w ] [ -W timeout ]
+ * Release: -r -o object -k key [ -R altRoot ] [ -q ]
+ * Status: [ -o object ] [ -k key ] [ -R altRoot ] [ -q ]
+ */
+
+/* enable extentions to standard Unix libraries */
+
+#define __EXTENSIONS__
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <libgen.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fnmatch.h>
+#include <zone.h>
+
+/* local includes */
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/* definition and conversion of sleep units */
+
+#define SECONDS(x) ((unsigned int)(x))
+#define MINUTES(x) ((unsigned int)(seconds(x)*60))
+
+/* define how waits are timed */
+
+#define WAITER_INITIAL SECONDS(1)
+#define WAITER_MAX SECONDS(60)
+#define WAITER_NEXT(x) ((x)*2)
+
+typedef unsigned int WAITER_T;
+
+/*
+ * The administrative lock file resides in /tmp
+ * It does not survive a reboot
+ * It consists of fixed length records
+ * Each record has the following information:
+ * record number - record position within the lock file
+ * lock count - number of lock holders maintaining this lock
+ * lock object - object being locked
+ * lock key - key needed to manipulate existing lock
+ * lock exclusive - is the lock exclusive (single locker only)
+ */
+
+#define LOCK_OBJECT_MAXLEN 512-1
+#define LOCK_KEY_MAXLEN 37
+
+#define LOCK_DIRECTORY "/tmp"
+
+/*
+ * this is the "well known name" of the lock file that is used by the
+ * package, patch, and zone administration commands to synchronize their
+ * various efforts - it must live in a temporary directory that is cleared
+ * on system reboot but it is NOT a temporary file in that it survives
+ * the process that creates and updates it - if the format of the lock
+ * file ever changes, this path should be updated with a later "uuid"
+ * so that previous (incompatible) pkgadm's will not use the later data.
+ */
+
+#define LOCK_FILENAME \
+ "/tmp/.ai.pkg.zone.lock-afdb66cf-1dd1-11b2-a049-000d560ddc3e"
+
+/* mode to use for LOCK_FILENAME */
+
+#define LOCK_FILEMODE \
+ (S_ISGID|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+
+#define LOCK_SLEEP_INTERVAL SECONDS(2)
+
+/* lock contents types */
+
+typedef unsigned long RECORDNUM_T;
+
+#define RECORDNUM_NONE 0xFFFFFFFF
+
+/* actual lock data */
+
+struct _adminLock
+{
+ RECORDNUM_T lockRecordNum;
+ unsigned long lockCount;
+ unsigned long lockExclusive;
+ pid_t lockPid;
+ zoneid_t lockZoneId;
+ char lockKey[LOCK_KEY_MAXLEN+1];
+ char lockObject[LOCK_OBJECT_MAXLEN+1];
+};
+
+typedef struct _adminLock ADMINLOCK_T;
+
+/* size of an individual "lock" */
+
+#define LOCK_SIZE sizeof (ADMINLOCK_T)
+
+/* union to allow lock to be accessed as raw or structured data */
+
+union _lockRecord
+{
+ char _lrLockData[LOCK_SIZE];
+ ADMINLOCK_T _lrLock;
+};
+
+typedef union _lockRecord LOCK_T;
+
+/* return codes from "_findLock" */
+
+typedef unsigned long FINDLOCK_T;
+
+#define FINDLOCK_FOUND ((FINDLOCK_T)0)
+#define FINDLOCK_ERROR ((FINDLOCK_T)-1)
+#define FINDLOCK_NOTFOUND ((FINDLOCK_T)-2)
+#define FINDLOCK_KEYMISMATCH ((FINDLOCK_T)-3)
+#define FINDLOCK_LOCKED ((FINDLOCK_T)-4)
+#define FINDLOCK_NOTLOCKED ((FINDLOCK_T)-5)
+#define FINDLOCK_LOCKACQUIRED ((FINDLOCK_T)-6)
+
+/*
+ * Forward declarations
+ */
+
+/* local main function implementation methods */
+
+static FINDLOCK_T lock_acquire(LOCK_T *a_lock, int *a_fd, char *a_root,
+ char *a_key, char *a_object, int a_quiet,
+ int a_wait, long a_timeout, int a_exclusive,
+ char *a_altRoot, pid_t a_pid, zoneid_t a_zid);
+static int lock_release(int a_fd, char *a_key, char *a_object,
+ int a_quiet);
+static int lock_status(int a_fd, char *a_key, char *a_object,
+ int a_quiet);
+
+/* local utility functions */
+
+static int _lockMatch(char *a_s1Lock, char *a_s2Lock);
+static FINDLOCK_T _findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
+ int a_fd, char *a_object, char *a_key);
+static int _decrementLockCount(int a_fd, LOCK_T *a_theLock);
+static int _addLock(char *r_key, int a_fd, char *a_object,
+ int a_exclusive, pid_t a_pid, zoneid_t a_zid);
+static int _incrementLockCount(int a_fd, LOCK_T *a_theLock);
+static FINDLOCK_T _lock_acquire(LOCK_T *a_lock, int a_fd, char *a_key,
+ char *a_object, int a_quiet, int a_exclusive,
+ pid_t a_pid, zoneid_t a_zid);
+static char *_getUniqueId(void);
+static int _openLockFile(char *a_root);
+static void sighup_handler(int a_signo);
+static void sigint_handler(int a_signo);
+static boolean_t _validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet);
+
+static int signal_received = 0;
+
+/*
+ * main methods with external entry points
+ */
+
+/*
+ * Name: admin_lock
+ * Synopsis: main entry point for pkgadm "lock" subcommand
+ * Description: Control zone/package administrative locking
+ * Returns: 0 on success, non-zero otherwise.
+ */
+
+int
+admin_lock(int argc, char **argv)
+{
+ FINDLOCK_T tResult;
+ LOCK_T theLock;
+ char *RFlag = "/"; /* altRoot */
+ char *endptr;
+ char *kFlag = ""; /* key */
+ char *oFlag = ""; /* object */
+ char *p;
+ char c;
+ int aFlag = 0; /* acquire lock */
+ int eFlag = 0; /* exclusive lock */
+ int exclusive = 1; /* exclusive vs shared lock */
+ int fd;
+ int qFlag = 0; /* quiet */
+ int rFlag = 0; /* release lock */
+ int result;
+ int sFlag = 0; /* shared lock */
+ int tFlag = 0; /* test comparison */
+ int wFlag = 0; /* wait */
+ long WFlag = 0; /* wait timeout */
+ pid_t pFlag = 0; /* process # */
+ struct sigaction nact;
+ struct sigaction oact;
+ void (*funcSighup)();
+ void (*funcSigint)();
+ zoneid_t zFlag = -1; /* zone i.d. */
+
+ while ((c = getopt(argc, argv, ":aek:o:p:qrR:stwW:z:")) != EOF) {
+ switch (c) {
+ case 'a': /* acquire lock */
+ aFlag++;
+ break;
+
+ case 'e': /* exclusive lock */
+ eFlag++;
+ break;
+
+ case 'k': /* lock-key */
+ kFlag = optarg;
+ if (strlen(optarg) > LOCK_KEY_MAXLEN) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_kARG_TOOLONG,
+ strlen(optarg), LOCK_KEY_MAXLEN);
+ return (1);
+ }
+ break;
+
+ case 'o': /* object */
+ oFlag = optarg;
+ if (strlen(optarg) > LOCK_OBJECT_MAXLEN) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_oARG_TOOLONG,
+ strlen(optarg), LOCK_OBJECT_MAXLEN);
+ return (1);
+ }
+ break;
+
+ case 'p': /* process i.d. */
+ errno = 0;
+ endptr = 0;
+ pFlag = strtol(optarg, &endptr, 10);
+ if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_pFLAG_BADINT,
+ optarg, *endptr);
+ return (1);
+ }
+ if ((pFlag == 0) && (errno != 0)) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_pFLAG_ERROR,
+ optarg, strerror(errno));
+ return (1);
+ }
+ break;
+
+ case 'q': /* quiet */
+ qFlag++;
+ break;
+
+ case 'r': /* release lock */
+ rFlag++;
+ break;
+
+ case 'R': /* alternative root */
+ /* if root directory is not absolute path, error */
+ if (*optarg != '/') {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_RARG_NOT_ABSOLUTE, optarg);
+ return (1);
+ }
+
+ /* if root directory does not exist, create it */
+ if (access(optarg, F_OK) != 0) {
+
+ /* create top level root directory */
+ if (mkdirp(optarg, 0755) != 0) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_ALTROOT_CANTCREATE,
+ optarg, strerror(errno));
+ return (1);
+ }
+ }
+
+ /* if $ALTROOT/tmp directory does not exist create it */
+ p = pkgstrPrintf("%s/tmp", optarg);
+ if (access(p, F_OK) != 0) {
+
+ /* create $ALTROOT/tmp directory */
+ if (mkdirp(p, 0777) != 0) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_ALTROOT_CANTCREATE,
+ p, strerror(errno));
+ return (1);
+ }
+ }
+
+ /* if $ALTROOT/tmp directory cannot be created, exit */
+ if (access(p, F_OK) != 0) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ALTROOT_NONEXIST,
+ optarg, strerror(errno));
+ return (1);
+ }
+
+ (void) free(p);
+
+ RFlag = optarg;
+ break;
+
+ case 's': /* shared */
+ sFlag++;
+ break;
+
+ case 't': /* test comparison */
+ tFlag++;
+ break;
+
+ case 'w': /* wait */
+ wFlag++;
+ break;
+
+ case 'W': /* wait with timeout */
+ errno = 0;
+ endptr = 0;
+ WFlag = strtol(optarg, &endptr, 10);
+ if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_WFLAG_BADINT,
+ optarg, *endptr);
+ return (1);
+ }
+ if ((WFlag == 0) && (errno != 0)) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_WFLAG_ERROR,
+ optarg, strerror(errno));
+ return (1);
+ }
+ wFlag++;
+ break;
+
+ case 'z': /* zone i.d. */
+ errno = 0;
+ endptr = 0;
+ zFlag = strtol(optarg, &endptr, 10);
+ if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_zFLAG_BADINT,
+ optarg, *endptr);
+ return (1);
+ }
+ if ((zFlag == 0) && (errno != 0)) {
+ log_msg(LOG_MSG_ERR,
+ MSG_LOCK_zFLAG_ERROR,
+ optarg, strerror(errno));
+ return (1);
+ }
+ break;
+
+ case ':':
+ log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+ /* LINTED fallthrough on case statement */
+ case '?':
+
+ default:
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ return (1);
+ }
+ }
+
+ /*
+ * validate arguments
+ */
+
+ /* if -t option is specified, override all other options */
+
+ if (tFlag) {
+ int rs;
+ int rx;
+ int a;
+
+ /* only 2 or 3 args are valid */
+
+ a = argc-optind;
+ if ((a < 2) || (a > 3)) {
+ (void) fprintf(stderr, MSG_T_OPTION_ARGS, argc-optind);
+ return (1);
+ }
+
+ /* if 3rd argument given, it is return value to check */
+
+ if (a == 3) {
+ rs = atoi(argv[optind+2]);
+ }
+ rx = _lockMatch(argv[optind+0], argv[optind+1]);
+
+ /* if 3rd argument not given, code to check is code returned */
+
+ if (a == 2) {
+ rs = rx;
+ }
+
+ /* report results */
+
+ if (a == 2) {
+ (void) fprintf(stderr, MSG_T_RESULT_TWO,
+ rx, argv[optind+0], argv[optind+1]);
+ return (rx);
+ }
+
+ if (rx != rs) {
+ (void) fprintf(stderr, MSG_T_RESULT_THREE,
+ rs, rx, argv[optind+0], argv[optind+1]);
+ }
+
+ /* always successful */
+
+ return (rx == rs ? 0 : 1);
+ }
+
+ /* must be no non-option arguments left */
+
+ if ((argc-optind) > 0) {
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ return (1);
+ }
+
+ /* -a and -r cannot be used together */
+
+ if (aFlag && rFlag) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ar_TOGETHER);
+ return (1);
+ }
+
+ /* -e and -s cannot be used together */
+
+ if (eFlag && sFlag) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_es_TOGETHER);
+ return (1);
+ }
+
+ /* -e can only be used if -a is used */
+
+ if (!aFlag && eFlag) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_e_without_a);
+ return (1);
+ }
+
+ /* -s can only be used if -a is used */
+
+ if (!aFlag && sFlag) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_s_without_a);
+ return (1);
+ }
+
+ /*
+ * perform the requested operation
+ */
+
+ /*
+ * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+ */
+
+ /* hold SIGINT/SIGHUP interrupts */
+
+ (void) sighold(SIGHUP);
+ (void) sighold(SIGINT);
+
+ /* connect sigint_handler() to SIGINT */
+
+ nact.sa_handler = sigint_handler;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGINT, &nact, &oact) < 0) {
+ funcSigint = SIG_DFL;
+ } else {
+ funcSigint = oact.sa_handler;
+ }
+
+ /* connect sighupt_handler() to SIGHUP */
+
+ nact.sa_handler = sighup_handler;
+ nact.sa_flags = SA_RESTART;
+ (void) sigemptyset(&nact.sa_mask);
+
+ if (sigaction(SIGHUP, &nact, &oact) < 0) {
+ funcSighup = SIG_DFL;
+ } else {
+ funcSighup = oact.sa_handler;
+ }
+
+ /* release hold on signals */
+
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGINT);
+
+ /* open the lock file */
+
+ fd = _openLockFile(RFlag);
+ if (fd < 0) {
+ return (1);
+ }
+
+ if (aFlag) {
+ /* set "exclusive" mode based on -e/-s flag used */
+
+ if (sFlag) {
+ exclusive = 0;
+ } else if (eFlag) {
+ exclusive = 1;
+ }
+
+ /* acquire lock */
+
+ tResult = lock_acquire(&theLock, &fd, RFlag, kFlag, oFlag,
+ qFlag, wFlag, WFlag, exclusive, RFlag, pFlag, zFlag);
+
+ switch (tResult) {
+ case FINDLOCK_LOCKACQUIRED:
+ (void) fprintf(stdout, "%s\n",
+ theLock._lrLock.lockKey);
+ result = 0;
+ break;
+ case FINDLOCK_LOCKED:
+ (void) fprintf(stdout, "%s\n",
+ theLock._lrLock.lockObject);
+ result = 1;
+ break;
+ default:
+ result = 1;
+ break;
+ }
+
+ } else if (rFlag) {
+ /* release lock */
+ result = lock_release(fd, kFlag, oFlag, qFlag);
+ } else {
+ /* lock status */
+ result = lock_status(fd, kFlag, oFlag, qFlag);
+ }
+
+ /* close the lock file */
+
+ (void) close(fd);
+
+ /* return results of operation */
+
+ return (result);
+}
+
+/*
+ * local main function implementation methods
+ */
+
+/*
+ * Name: lock_acquire
+ * Description: implement lock acquisition implementing the wait/timeouts
+ * Calls _lock_acquire to attempt lock acquisition.
+ * Arguments:
+ * a_theLock - lock object filled with contents of existing lock
+ * a_fd - file descriptor opened on the lock file
+ * a_root - root of file system to manipulate locks on
+ * a_key - key associated with lock to acquire
+ * a_object - object associated with lock to acquire
+ * a_wait - wait if lock cannot be acquired flag:
+ * == 0 - do not wait
+ * != 0 - wait
+ * a_timeout - timeout if waiting to acquire busy lock:
+ * == 0 - no timeout (wait forever)
+ * != 0 - max # seconds to wait to acquire busy lock
+ * a_quiet - quiet mode enabled flag
+ * a_exclusive - exclusive/shared lock flag
+ * a_pid - if != 0 process i.d. to associate with this lock
+ * a_zid - if >= 0 - zone i.d. to associate with this lock
+ * Returns: int
+ * == 0 - successful
+ * != 0 - not successful
+ */
+
+static FINDLOCK_T
+lock_acquire(LOCK_T *a_theLock, int *a_fd, char *a_root, char *a_key,
+ char *a_object, int a_quiet, int a_wait, long a_timeout,
+ int a_exclusive, char *a_altRoot, pid_t a_pid, zoneid_t a_zid)
+{
+ int notified = 0;
+ FINDLOCK_T result;
+ time_t timeout;
+ int closeOnExit = 0;
+
+ /* reset the lock */
+
+ bzero(a_theLock, sizeof (LOCK_T));
+
+ /* open file if not open */
+
+ if ((*a_fd) < 0) {
+ (*a_fd) = _openLockFile(a_altRoot);
+ if ((*a_fd) < 0) {
+ return (FINDLOCK_ERROR);
+ }
+ closeOnExit++;
+ }
+
+ /* compute time after which acquire times out */
+
+ timeout = time((time_t *)NULL) + a_timeout;
+
+ for (;;) {
+ time_t curtime;
+
+ /* attempt to aquire the lock */
+
+ result = _lock_acquire(a_theLock, *a_fd, a_key, a_object,
+ a_quiet, a_exclusive, a_pid, a_zid);
+
+ /* return result if any result other than object is locked */
+
+ switch (result) {
+ case FINDLOCK_LOCKACQUIRED:
+
+ /* close lock file if opened in this function */
+
+ if (closeOnExit) {
+ (void) close(*a_fd);
+ *a_fd = -1;
+ }
+
+ return (FINDLOCK_LOCKACQUIRED);
+
+ case FINDLOCK_FOUND:
+ case FINDLOCK_NOTFOUND:
+ case FINDLOCK_KEYMISMATCH:
+ case FINDLOCK_NOTLOCKED:
+ case FINDLOCK_ERROR:
+ default:
+ /* close lock file if opened in this function */
+
+ if (closeOnExit) {
+ (void) close(*a_fd);
+ *a_fd = -1;
+ }
+
+ return (result);
+
+ case FINDLOCK_LOCKED:
+ ;
+ /* FALLTHROUGH */
+ }
+
+ /*
+ * object locked OR SIGINT/SIGHUP interrupt received;
+ * return error if not waiting for lock OR signal received
+ */
+
+ if ((a_wait == 0) || (signal_received != 0)) {
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_ACQUIRE_BUSY_FIRST,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object, a_key,
+ a_theLock->_lrLock.lockObject,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockExclusive !=
+ a_exclusive ? "" :
+ MSG_LOCK_ACQUIRE_BUSY_ADDITIONAL);
+
+ /* close lock file if opened in this function */
+
+ if (closeOnExit) {
+ (void) close(*a_fd);
+ *a_fd = -1;
+ }
+
+ return (FINDLOCK_LOCKED);
+ }
+
+ /* waiting for lock - if timeout specified see if time left */
+
+ if (a_timeout > 0) {
+ curtime = time((time_t *)NULL);
+ if (curtime > timeout) {
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_ACQUIRE_TIMEDOUT,
+ a_exclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object, a_key);
+
+ /* close lock file if opened in this function */
+
+ if (closeOnExit) {
+ (void) close(*a_fd);
+ *a_fd = -1;
+ }
+
+ return (FINDLOCK_ERROR);
+ }
+ }
+
+ /*
+ * waiting to aquire lock:
+ * - notify waiting (one time only)
+ * - close lock file
+ * - sleep
+ * - open lock file
+ * - try again
+ */
+
+ /* notify once */
+
+ if (notified++ == 0) {
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
+ MSG_LOCK_ACQUIRE_WAITING,
+ a_object);
+ }
+
+ /* close lock file */
+
+ (void) close(*a_fd);
+
+ /* wait (sleep) */
+
+ (void) sleep(LOCK_SLEEP_INTERVAL);
+
+ /* open the lock file and try again */
+
+ *a_fd = _openLockFile(a_root);
+ if (*a_fd < 0) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_REOPEN_FAILED,
+ a_object);
+
+ /* close lock file if opened in this function */
+
+ if (closeOnExit) {
+ (void) close(*a_fd);
+ *a_fd = -1;
+ }
+
+ return (FINDLOCK_ERROR);
+ }
+ }
+}
+
+/*
+ * Name: lock_release
+ * Description: implement lock release
+ * Arguments:
+ * a_fd - file descriptor opened on the lock file
+ * a_key - key associated with lock to release
+ * a_object - object associated with lock to release
+ * a_quiet - quiet mode enabled flag
+ * Returns: int
+ * == 0 - successful
+ * != 0 - not successful
+ */
+
+static int
+lock_release(int a_fd, char *a_key, char *a_object, int a_quiet)
+{
+ RECORDNUM_T recordNum;
+ LOCK_T theLock;
+ FINDLOCK_T result;
+
+ /* entry debugging info */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_ENTRY,
+ a_key, a_object, a_quiet);
+
+ /* find the lock to be released */
+
+ result = _findLock(&theLock, &recordNum, a_fd, a_object, a_key);
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FINDRESULT,
+ result, recordNum);
+
+ /* determine how to release the lock if found */
+
+ switch (result) {
+ /*
+ * object is not locked but a key was specified
+ */
+ case FINDLOCK_NOTLOCKED:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_RELEASE_NOTLOCKED,
+ a_object, a_key);
+ return (result);
+
+ /*
+ * object is locked and no matching key was specified
+ */
+ case FINDLOCK_LOCKED:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_RELEASE_LOCKED,
+ a_object, a_key);
+ return (result);
+
+ /*
+ * object is not locked
+ */
+ case FINDLOCK_NOTFOUND:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_RELEASE_NOTFOUND,
+ a_object, a_key);
+ return (result);
+
+ /*
+ * object is locked and specified key does not match
+ */
+ case FINDLOCK_KEYMISMATCH:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_RELEASE_KEYMISMATCH,
+ a_object);
+ return (result);
+
+ /*
+ * error determining if object is locked
+ */
+ case FINDLOCK_ERROR:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_RELEASE_ERROR,
+ a_object, a_key);
+ perror(LOCK_FILENAME);
+ return (result);
+
+ /*
+ * object is locked and specified key matches
+ */
+ case FINDLOCK_FOUND:
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FOUND,
+ a_object, a_key);
+ (void) _decrementLockCount(a_fd, &theLock);
+ break;
+
+ /*
+ * unknown return
+ */
+ default:
+ result = FINDLOCK_ERROR;
+ break;
+
+ }
+ return (result);
+}
+
+/*
+ * Name: lock_status
+ * Description: implement lock status display/inquiry
+ * Arguments:
+ * a_fd - file descriptor opened on the lock file
+ * a_key - key associated with lock to look up
+ * a_object - object associated with lock to look up
+ * a_quiet - quiet mode enabled flag
+ * Returns: int
+ * == 0 - successful
+ * != 0 - not successful
+ */
+
+static int
+lock_status(int a_fd, char *a_key, char *a_object, int a_quiet)
+{
+ ADMINLOCK_T *pll;
+ LOCK_T theLock;
+ RECORDNUM_T recordNum = 0;
+ char *pld;
+ int found = 0;
+ long pls;
+
+ /* entry debugging info */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_ENTRY,
+ a_key, a_object);
+
+ /* localize references to lock object */
+
+ pld = &theLock._lrLockData[0];
+ pll = &theLock._lrLock;
+ pls = sizeof (theLock._lrLockData);
+
+ bzero(pld, pls);
+
+ /* read and process each lock */
+
+ for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
+ /* debug info on this lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_READRECORD,
+ recordNum, pll->lockCount,
+ pll->lockObject, pll->lockKey, pll->lockPid,
+ pll->lockZoneId);
+
+ /* ignore if key specified and key does not match */
+
+ if ((*a_key != '\0') &&
+ (strcmp(pll->lockKey, a_key) != 0)) {
+ continue;
+ }
+
+ /* ignore if object specified and object does not match */
+
+ if ((*a_object != '\0') &&
+ (strcmp(pll->lockObject, a_object) != 0)) {
+ continue;
+ }
+
+ found++;
+
+ /* process next lock if quiet operation */
+
+ if (a_quiet != 0) {
+ continue;
+ }
+
+ /* output header if first lock object */
+
+ if (found == 1) {
+ (void) fprintf(stdout,
+ "%2s %2s %3s %8s %3s %9s %37s %s\n",
+ "i#", "l#", "cnt", "pid", "zid", "lock-type",
+ "---------------lock-key-------------",
+ "lock-object");
+ }
+
+ /* output status line for this lock object */
+
+ (void) fprintf(stdout,
+ "%2ld %2ld %3ld %8ld %3d %9s %37s %s\n",
+ recordNum, pll->lockRecordNum, pll->lockCount,
+ pll->lockPid, pll->lockZoneId,
+ pll->lockExclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ pll->lockKey,
+ *pll->lockObject == '\0' ? "*" : pll->lockObject);
+ }
+
+ /* return == 0 if found, != 0 if not found */
+
+ return (found == 0 ? 1 : 0);
+}
+
+/*
+ * local utility functions
+ */
+
+/*
+ * Name: _lock_acquire
+ * Description: implement lock acquisition without wait/timeouts
+ * Arguments:
+ * a_theLock - lock object filled with contents of existing lock
+ * a_fd - file descriptor opened on the lock file
+ * a_key - key associated with lock to acquire
+ * a_object - object associated with lock to acquire
+ * a_quiet - quiet mode enabled flag
+ * a_exclusive - exclusive/shared lock flag
+ * a_pid - if != 0 process i.d. to associate with this lock
+ * a_zid - if >= 0 zone i.d. to associate with this lock
+ * Returns: FINDLOCK_T
+ */
+
+static FINDLOCK_T
+_lock_acquire(LOCK_T *a_theLock, int a_fd, char *a_key,
+ char *a_object, int a_quiet, int a_exclusive, pid_t a_pid,
+ zoneid_t a_zid)
+{
+ RECORDNUM_T recordNum;
+ FINDLOCK_T result;
+ char key[LOCK_KEY_MAXLEN+1] = {'\0'};
+
+ /* entry debugging info */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_ENTRY,
+ a_key, a_object, a_quiet, a_exclusive);
+
+ /* is the specified object already locked? */
+
+ for (;;) {
+ result = _findLock(a_theLock, &recordNum, a_fd, a_object,
+ a_key);
+
+ if (result != FINDLOCK_LOCKED) {
+ break;
+ }
+
+ if (_validateLock(a_fd, a_theLock, a_quiet) == B_TRUE) {
+ break;
+ }
+ }
+
+
+ /* debug info on result of find of lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FINDRESULT,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ result, recordNum);
+
+ /* determine how to acquire the lock */
+
+ switch (result) {
+ /*
+ * object is not locked but a key was specified
+ */
+ case FINDLOCK_NOTLOCKED:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_ACQUIRE_NOTLOCKED,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object, a_key);
+ break;
+
+ /*
+ * object is locked and no key was specified:
+ * - if lock is exclusively held, return "locked"
+ * - if exclusive lock requested, return "locked"
+ * - otherwise lock is shared and shared lock requested,
+ * - increment lock count and return the key
+ */
+ case FINDLOCK_LOCKED:
+ /* return error if current lock exclusive */
+
+ if (a_theLock->_lrLock.lockExclusive) {
+ break;
+ }
+
+ /* return error if requesting exclusive lock */
+
+ if (a_exclusive) {
+ break;
+ }
+
+ /* shared requesting shared - add to shared lock */
+
+ log_msg(LOG_MSG_DEBUG,
+ MSG_LOCK_ACQUIRE_LOCKED_SHARED,
+ a_object, a_key);
+
+ /* increment shared lock count */
+
+ if (_incrementLockCount(a_fd, a_theLock) == 0) {
+ result = FINDLOCK_LOCKACQUIRED;
+ } else {
+ result = FINDLOCK_ERROR;
+ }
+
+ break;
+
+ /*
+ * object is not locked
+ */
+ case FINDLOCK_NOTFOUND:
+ log_msg(LOG_MSG_DEBUG,
+ MSG_LOCK_ACQUIRE_NOTFOUND,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object);
+
+ if (_addLock(key, a_fd, a_object, a_exclusive,
+ a_pid, a_zid) == 0) {
+ (void) strncpy(a_theLock->_lrLock.lockKey, key,
+ sizeof (a_theLock->_lrLock.lockKey));
+ result = FINDLOCK_LOCKACQUIRED;
+ } else {
+ result = FINDLOCK_ERROR;
+ }
+ break;
+
+ /*
+ * object is locked, key specified, specified key does not match
+ */
+ case FINDLOCK_KEYMISMATCH:
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+ MSG_LOCK_ACQUIRE_KEYMISMATCH,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object);
+ break;
+
+ /*
+ * error determining if object is locked
+ */
+ case FINDLOCK_ERROR:
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_ERROR,
+ a_object, a_key, strerror(errno));
+ break;
+
+ /*
+ * object is locked and specified key matches
+ */
+ case FINDLOCK_FOUND:
+ /* return locked if object currently locked */
+ if (a_exclusive != a_theLock->_lrLock.lockExclusive) {
+ result = FINDLOCK_LOCKED;
+ break;
+ }
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FOUND_INC,
+ a_object, a_key,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR);
+
+ /* increment shared lock */
+
+ if (_incrementLockCount(a_fd, a_theLock) == 0) {
+ result = FINDLOCK_LOCKACQUIRED;
+ } else {
+ result = FINDLOCK_ERROR;
+ }
+ break;
+
+ /*
+ * unknown return
+ */
+ default:
+ result = FINDLOCK_ERROR;
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Name: _openLockFile
+ * Description: open the lock file, acquiring exclusive record locks
+ * Arguments:
+ * a_root - root of file system to manipulate locks on
+ * Returns: int
+ * >= 0 - successful - file descriptor lock file opened on
+ * < 0 - not successful
+ */
+
+static int
+_openLockFile(char *a_root)
+{
+ WAITER_T waiter;
+ char lockpath[MAXPATHLEN];
+ int fd;
+ int result;
+
+ /* entry debugging info */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_ENTRY,
+ a_root, LOCK_FILENAME);
+
+ /* generate path to lock directory */
+
+ (void) snprintf(lockpath, sizeof (lockpath), "%s/%s",
+ a_root, LOCK_DIRECTORY);
+
+ if (access(lockpath, F_OK) != 0) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ROOTDIR_INVALID,
+ lockpath, strerror(errno));
+ return (-1);
+ }
+
+ /* generate path to lock file */
+
+ (void) snprintf(lockpath, sizeof (lockpath),
+ "%s/%s", a_root, LOCK_FILENAME);
+
+ /* wait for open to succeed up to limits */
+
+ for (waiter = WAITER_INITIAL;
+ waiter < WAITER_MAX;
+ waiter = WAITER_NEXT(waiter)) {
+
+ /* LINTED O_CREAT without O_EXCL specified in call to open() */
+ fd = open(lockpath, O_CREAT|O_RDWR, LOCK_FILEMODE);
+
+ /* break out of loop if file opened */
+
+ if (fd >= 0) {
+ break;
+ }
+
+ /* failed - exit loop if due to access (permissions) failure */
+
+ if (errno == EACCES) {
+ break;
+ }
+
+ /* file is busy - wait and try again */
+
+ if (waiter == WAITER_INITIAL) {
+ log_msg(LOG_MSG_DEBUG,
+ MSG_LOCK_OPENFILE_SLEEPING,
+ strerror(errno), waiter);
+ }
+
+ (void) sleep(waiter);
+ }
+
+ /* if open filed generate error message and return error */
+
+ if (fd < 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAILURE,
+ strerror(errno));
+ perror(lockpath);
+ return (-1);
+ }
+
+ /*
+ * lock file opened - acquire exclusive section lock on entire file;
+ * wait for lockf to succeed up to limits
+ */
+
+ for (waiter = WAITER_INITIAL;
+ waiter < WAITER_MAX;
+ waiter = WAITER_NEXT(waiter)) {
+
+ /* acquire exclusive section lock on entire file */
+
+ result = lockf(fd, F_LOCK, 0xFFFFF);
+
+ /* break out of loop if entire file locked */
+
+ if (result == 0) {
+ break;
+ }
+
+ /* file is busy - wait and try again */
+
+ if (waiter == WAITER_INITIAL) {
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SLEEP2,
+ strerror(errno), waiter);
+ }
+
+ (void) sleep(waiter);
+ }
+
+ /* if section lock failed generate error message and return error */
+
+ if (result < 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAIL2,
+ strerror(errno));
+ perror(lockpath);
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* file opened and locked - return success */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SUCCESS, fd);
+
+ return (fd);
+}
+
+/*
+ * Name: _lockMatch
+ * Description: Compare two lock objects using file name match criteria
+ * Arguments:
+ * a_s1Lock - first lock object to compare against the second
+ * a_s2Lock - second lock object to compare against the first
+ * Returns:
+ * == 0 - the locks match at some level
+ * != 0 - the locks do not match at any level
+ */
+
+static int
+_lockMatch(char *a_s1Lock, char *a_s2Lock)
+{
+ boolean_t s1Sfx = B_FALSE;
+ boolean_t s2Sfx = B_FALSE;
+ char *final1Lock = (char *)NULL;
+ char *final2Lock = (char *)NULL;
+ char s1Buf[MAXPATHLEN] = {'\0'};
+ char s1Prefix[MAXPATHLEN] = {'\0'};
+ char s2Buf[MAXPATHLEN] = {'\0'};
+ char s2Prefix[MAXPATHLEN] = {'\0'};
+ int result = 0;
+ int s1Cnt;
+ int s2Cnt;
+
+ /* entry assertions */
+
+ assert(a_s1Lock != (char *)NULL);
+ assert(a_s2Lock != (char *)NULL);
+
+ /* entry debugging info */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ENTRY, a_s1Lock, a_s2Lock);
+
+ /*
+ * attempt to find a common anchor between the two locks; that is,
+ * find the first node in the first lock that matches any node
+ * in the second lock; for example:
+ * --> a/b/c vs b/c/d
+ * -> common anchor is "b"; comparison would expand to:
+ * --> a/b/c/? vs ?/b/c/d
+ */
+
+ /* process each node in the first lock */
+
+ for (s1Cnt = 0; ; s1Cnt++) {
+ /* get next first lock node */
+
+ pkgstrGetToken_r((char *)NULL, a_s1Lock, s1Cnt, "/",
+ s1Buf, sizeof (s1Buf));
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTNODE, s1Cnt, s1Buf);
+
+ /* exit if no more nodes left */
+
+ if (s1Buf[0] == '\0') {
+ break;
+ }
+
+ /* discover "." prefix for this node */
+
+ pkgstrGetToken_r((char *)NULL, s1Buf, 0, ".", s1Prefix,
+ sizeof (s1Prefix));
+
+ s1Sfx = (strlen(s1Prefix) == strlen(s1Buf) ? B_FALSE : B_TRUE);
+
+ /* search each second lock node; look for the first node lock */
+
+ for (s2Cnt = 0; ; s2Cnt++) {
+ /* get next second lock node */
+
+ pkgstrGetToken_r((char *)NULL, a_s2Lock, s2Cnt, "/",
+ s2Buf, sizeof (s2Buf));
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDNODE, s2Cnt,
+ s2Buf);
+
+ /* exit if no nodes left */
+
+ if (s2Buf[0] == '\0') {
+ break;
+ }
+
+ /* discover "." prefix for this node */
+
+ pkgstrGetToken_r((char *)NULL, s2Buf, 0, ".", s2Prefix,
+ sizeof (s2Prefix));
+
+ s2Sfx = (strlen(s2Prefix) ==
+ strlen(s2Buf) ? B_FALSE : B_TRUE);
+
+ /*
+ * process this pair of nodes:
+ * if both nodes do not have a prefix, then directly
+ * compare the nodes (e.g. a/b vs c/d: a vs c, b vs d)
+ * and break out of the loop if there is a match;
+ * otherwise, compare prefixes and break out of the
+ * loop if there is a match (e.g. a.* / b.* vs
+ * vs c.* / d.*: a.* vs c.*, a.* vs d.*, b.* vs c.*,
+ * b.* vs d.*).
+ */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODES, s1Buf,
+ s1Prefix, s1Sfx, s2Buf, s2Prefix, s2Sfx);
+
+ if ((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE)) {
+ /* one doesnt have a prefix direct comparison */
+
+ if (strcmp(s1Buf, s2Buf) == 0) {
+ log_msg(LOG_MSG_DEBUG,
+ MSG_LCKMCH_DIRMCH,
+ s1Buf, s2Buf);
+ break;
+ }
+
+ /* nodes do not directly match, continue */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_DIRNOMCH,
+ s1Buf, s2Buf);
+ continue;
+ }
+
+ /* both have prefix, compare prefixes */
+
+ if (strcmp(s1Prefix, s2Prefix) == 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXMCH,
+ s1Prefix, s2Prefix);
+ break;
+ }
+
+ /* prefixes do not match, continue */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXNOMCH, s1Prefix,
+ s2Prefix);
+ }
+
+ /*
+ * match found if not at the end of the second lock node list,
+ * break out of loop because some match between the two lock
+ * objects has been found
+ */
+
+ if (s2Buf[0] != '\0') {
+ break;
+ }
+ }
+
+ /*
+ * at this point, either a match has been found between the nodes in
+ * the two lock objects, or there is no commonality at all between
+ * the two lock objects.
+ *
+ * s1Buf[0] == '\0' && s2Buf[0] == '\0':
+ * --> nothing in first lock matches anything in second lock:
+ * ----> (s1Cnt == 1) || (s2Cnt == 1) && (s1Sfx == B_FALSE)
+ * ----> || (s2Sfx == B_FALSE)
+ * --------> an absolute lock do not match
+ * ----> else both object locks have nothing in common - match
+ *
+ * s2Buf[0] != '\0' && s1Buf[0] != '\0' && s1Cnt > 0 && s2Cnt > 0
+ * --> locks have incompatible overlaps - no match, such as:
+ * ----> a.* / b.* / c.* / d.* and y.* / b.* / c.*
+ *
+ * s1Cnt == 0 && s2Cnt == 0:
+ * --> locks begin with same node - do comparison
+ *
+ * s1Cnt != 0 && s2Cnt == 0 && s2Buf[0] != '\0'
+ * --> second lock is subset of first lock
+ *
+ * s2Cnt == 0 && s2Buf[0] != '\0':
+ * --> s1Buf[s1Cnt] matches s2Buf[0] - second is subset of first
+ *
+ * s2Cnt != 0 && s1Cnt == 0 && s1Buf[0] != '\0':
+ * --> first lock is subset of second lock
+ *
+ */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTLCK, s1Cnt, s1Buf,
+ s1Prefix, s1Sfx);
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDLCK, s2Cnt, s2Buf,
+ s2Prefix, s2Sfx);
+
+ /* process any direct comparisons that might be possible */
+
+ if ((s1Buf[0] == '\0') && (s2Buf[0] == '\0')) {
+ /* nothing in first matches anything in second lock */
+
+ if (((s1Cnt == 1) || (s2Cnt == 1)) &&
+ ((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE))) {
+ /* two absolute locks match (e.g. 'file' and 'dir') */
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ABSNOMCH, a_s1Lock,
+ a_s2Lock);
+ return (1);
+ }
+
+ /* two object locks have nothing in common: match */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OBJMCH, a_s1Lock, a_s2Lock);
+
+ return (0);
+ }
+
+ if ((s2Buf[0] != '\0') && (s1Buf[0] != '\0') &&
+ (s1Cnt > 0) && (s2Cnt > 0)) {
+ /* incompatible overlapping objects */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OVLPNOMCH, a_s1Lock, a_s2Lock,
+ s1Cnt+1, s1Buf);
+
+ return (1);
+ }
+
+ /*
+ * must compare each node of each lock to determine match;
+ * start off at the first byte of both locks
+ */
+
+ final1Lock = a_s1Lock;
+ final2Lock = a_s2Lock;
+
+ if ((s1Cnt == 0) && (s2Cnt == 0)) {
+ /* both have first match - start comparison from the begining */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SAME, a_s1Lock, a_s2Lock,
+ s1Buf);
+
+ } else if ((s1Cnt != 0) && (s2Cnt == 0) && (s2Buf[0] != '\0')) {
+ /* second lock begins somewhere inside of the first lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDSUB, a_s2Lock, a_s1Lock,
+ s1Cnt+1, s1Buf);
+
+ /* advance first lock to matching node in second lock */
+
+ if (strchr(a_s1Lock, '/') != (char *)NULL) {
+ for (; s1Cnt > 0 && (*final1Lock != '\0');
+ final1Lock++) {
+ if (*final1Lock == '/') {
+ s1Cnt--;
+ }
+ }
+ }
+ } else if ((s2Cnt != 0) && (s1Cnt == 0) && (s1Buf[0] != '\0')) {
+ /* first lock begins somewhere inside of the second lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FRSTSUB, a_s1Lock, a_s2Lock,
+ s2Cnt+1, s2Buf);
+
+ /* advance second lock to matching node in first lock */
+
+ if (strchr(a_s2Lock, '/') != (char *)NULL) {
+ for (; s2Cnt > 0 && (*final2Lock != '\0');
+ final2Lock++) {
+ if (*final2Lock == '/') {
+ s2Cnt--;
+ }
+ }
+ }
+ } else {
+ /* unknown condition (probably impossible): directly compare */
+
+ log_msg(LOG_MSG_ERR, MSG_LCKMCH_DONTKNOW, a_s1Lock, a_s2Lock);
+ }
+
+ /*
+ * locks have common node - compare from that node forward
+ */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_READY, final1Lock, final2Lock);
+
+ /* compare each node (prefix) - success when no more nodes to compare */
+
+ for (s1Cnt = 0; ; s1Cnt++) {
+ /* get next node from first lock */
+
+ pkgstrGetToken_r((char *)NULL, final1Lock, s1Cnt, "/", s1Buf,
+ sizeof (s1Buf));
+
+ /* success if at end of lock */
+
+ if (s1Buf[0] == '\0') {
+ break;
+ }
+
+ /* get next node from second lock */
+
+ pkgstrGetToken_r((char *)NULL, final2Lock, s1Cnt, "/", s2Buf,
+ sizeof (s2Buf));
+
+ /* success if at end of lock */
+
+ if (s2Buf[0] == '\0') {
+ break;
+ }
+
+ /* compare both nodes */
+
+ result = fnmatch(s1Buf, s2Buf, 0);
+ if (result != 0) {
+ result = fnmatch(s2Buf, s1Buf, 0);
+ }
+
+ /* failure if nodes do not match */
+
+ if (result != 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEFAIL,
+ s1Cnt, s1Buf, s2Buf);
+ return (1);
+ }
+
+ /* nodes match, continue and compare next set of nodes */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEOK, s1Cnt, s1Buf, s2Buf);
+ }
+
+ /* no more nodes to compare - locks match */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_MATCHOK, final1Lock, final2Lock);
+
+ return (0);
+}
+
+/*
+ * Name: _findLock
+ * Description: Locate specified lock in lock file
+ * Arguments:
+ * a_theLock - lock object filled with contents of lock (if found)
+ * r_recordNum - will contain record number if lock found
+ * - will be RECORDNUM_NONE if lock not found
+ * a_fd - file descriptor opened on the lock file
+ * a_key - key associated with lock to look up
+ * a_object - object associated with lock to look up
+ * Returns:
+ * FINDLOCK_FOUND - specified lock found; a_theLock contains contents
+ * of found lock, r_recordNum contain record number of lock
+ * FINDLOCK_ERROR - failed - error occurred looking up the lock
+ * FINDLOCK_NOTFOUND - specified object is not locked
+ * FINDLOCK_KEYMISMATCH - object lock found but specified key doesnt match
+ * FINDLOCK_LOCKED - object lock found but no key specified
+ * FINDLOCK_NOTLOCKED - object not locked
+ */
+
+static FINDLOCK_T
+_findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
+ int a_fd, char *a_object, char *a_key)
+{
+ ADMINLOCK_T *pll;
+ char *pld;
+ int recordNum = 0;
+ long pls;
+ off_t pos;
+
+ /* reset returned record number to "none" */
+
+ *r_recordNum = RECORDNUM_NONE;
+
+ /* localize references to lock object */
+
+ pld = &a_theLock->_lrLockData[0];
+ pll = &a_theLock->_lrLock;
+ pls = sizeof (a_theLock->_lrLockData);
+
+ /* zero out returned lock data */
+
+ bzero(pld, pls);
+
+ /* debug info before processing lock file */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_ENTRY,
+ a_object, a_key);
+
+ /* rewind to beginning of lock file */
+
+ pos = lseek(a_fd, 0L, SEEK_SET);
+ if (pos == (off_t)-1) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_FINDLOCK_LSEEK_FAILURE,
+ a_object, a_key, strerror(errno));
+ return (FINDLOCK_ERROR);
+ }
+
+ /* read and process each lock */
+
+ for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
+ /* debug info on this lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_READRECORD,
+ recordNum, pll->lockCount,
+ pll->lockObject, pll->lockKey, pll->lockPid,
+ pll->lockZoneId);
+
+ /* continue if object is not the one we are looking for */
+
+ if (_lockMatch(a_object, pll->lockObject) != 0) {
+ continue;
+ }
+
+ /*
+ * object found; return locked if searching for no key
+ */
+
+ if (*a_key == '\0') {
+ /* no key specified - object is locked */
+ *r_recordNum = recordNum;
+ return (FINDLOCK_LOCKED);
+ }
+
+ /*
+ * object found and keys present; see if keys match
+ */
+
+ if (strcmp(pll->lockKey, a_key) != 0) {
+ /* keys do not match */
+ *r_recordNum = recordNum;
+ return (FINDLOCK_KEYMISMATCH);
+ }
+
+ /* object found and keys match - return match */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_FOUND);
+
+ *r_recordNum = recordNum;
+ return (FINDLOCK_FOUND);
+ }
+
+ /* object not locked - return error if key supplied */
+
+ if (*a_key != '\0') {
+ return (FINDLOCK_NOTLOCKED);
+ }
+
+ /* object not locked and key not supplied - no lock found */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_NOTFOUND);
+
+ return (FINDLOCK_NOTFOUND);
+}
+
+/*
+ * Name: _addLock
+ * Description: Add a new lock to the lock file
+ * Arguments:
+ * r_key - if lock acquired key is placed here
+ * a_fd - file descriptor opened on the lock file
+ * a_object - object to lock
+ * a_exclusive - type of lock to add:
+ * == 0 - shared lock
+ * != 0 - exclusive lock
+ * a_pid - if != 0 process i.d. to associate with this lock
+ * a_zid - if >= 0 zone i.d. to associate with this lock
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ */
+
+static int
+_addLock(char *r_key, int a_fd, char *a_object, int a_exclusive, pid_t a_pid,
+ zoneid_t a_zid)
+{
+ LOCK_T theLock;
+ char *key;
+ off_t pos;
+ ssize_t result;
+
+ /* get unique i.d. for this lock */
+
+ key = _getUniqueId();
+
+ /* determine record number for next record in lock file */
+
+ pos = lseek(a_fd, 0L, SEEK_END);
+ if (pos == (off_t)-1) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_LSEEK_FAILURE,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object, strerror(errno));
+ return (1);
+ }
+
+ /* allocate storace for this lock */
+
+ bzero(&theLock, sizeof (theLock));
+
+ /* fill in components of the lock */
+
+ (void) strlcpy(theLock._lrLock.lockObject, a_object,
+ LOCK_OBJECT_MAXLEN);
+ (void) strlcpy(theLock._lrLock.lockKey, key, LOCK_KEY_MAXLEN);
+ theLock._lrLock.lockCount = 1;
+ theLock._lrLock.lockPid = (a_pid > 0 ? a_pid : 0);
+ theLock._lrLock.lockRecordNum = (pos == 0 ? 0 : (pos/sizeof (LOCK_T)));
+ theLock._lrLock.lockExclusive = a_exclusive;
+ theLock._lrLock.lockZoneId = (a_zid >= 0 ? a_zid : -1);
+
+ /* debug info on new lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_ADDLOCK_ADDING,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ pos, theLock._lrLock.lockObject, theLock._lrLock.lockKey,
+ theLock._lrLock.lockPid, theLock._lrLock.lockZoneId);
+
+ /* write the new lock record to the end of the lock file */
+
+ result = pwrite(a_fd, &theLock, LOCK_SIZE, pos);
+ if (result != LOCK_SIZE) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_PWRITE_FAILURE,
+ a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_object, strerror(errno));
+ return (1);
+ }
+
+ /* output the key assigned to standard out */
+
+ (void) strncpy(r_key, key, LOCK_KEY_MAXLEN);
+
+ return (0);
+}
+
+static int
+_incrementLockCount(int a_fd, LOCK_T *a_theLock)
+{
+ ADMINLOCK_T *pll;
+ char *pld;
+ long pls;
+ ssize_t result;
+
+ /* localize references to lock object */
+
+ pld = &a_theLock->_lrLockData[0];
+ pll = &a_theLock->_lrLock;
+ pls = sizeof (a_theLock->_lrLockData);
+
+ /* debug info on incrementing lock */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_ENTRY,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ pll->lockRecordNum, pll->lockCount);
+
+ /* increment lock count */
+
+ pll->lockCount++;
+
+ /* write out updated lock */
+
+ result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
+ if (result != pls) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_INCLOCK_PWRITE_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+
+ /* debug info lock incremented */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_DONE,
+ pll->lockRecordNum, pll->lockCount,
+ pll->lockObject, pll->lockKey);
+
+ return (0);
+}
+
+/*
+ * Name: _validateLock
+ * Description: determine if a specified lock is valid; if the lock is not valid
+ * then remove the lock
+ * Arguments: a_fd - file descriptor opened on the lock file
+ * a_theLock - lock object to validate
+ * Returns: boolean_t
+ * B_TRUE - the lock is valid
+ * B_FALSE - the lock is not valid and has been removed
+ */
+
+static boolean_t
+_validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet)
+{
+ ADMINLOCK_T *pll;
+ char *pld;
+ long pls;
+ char path[MAXPATHLEN];
+
+ /* localize references to lock object */
+
+ pld = &a_theLock->_lrLockData[0];
+ pll = &a_theLock->_lrLock;
+ pls = sizeof (a_theLock->_lrLockData);
+
+ /* return true if no process i.d. associated with lock */
+
+ if (pll->lockPid <= 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_VALID_NOPID, pll->lockObject);
+ return (B_TRUE);
+ }
+
+ /* see if the zone i.d. matches */
+
+ if (pll->lockZoneId != getzoneid()) {
+ log_msg(LOG_MSG_DEBUG, MSG_VALID_BADZID, pll->lockObject,
+ pll->lockZoneId, getzoneid());
+ return (B_TRUE);
+ } else {
+ log_msg(LOG_MSG_DEBUG, MSG_VALID_ZIDOK, pll->lockObject,
+ pll->lockZoneId, getzoneid());
+ }
+
+ /* see if the process is still active */
+
+ pkgstrPrintf_r(path, sizeof (path), "/proc/%d", pll->lockPid);
+ if (access(path, F_OK) == 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_VALID_OK, pll->lockObject,
+ pll->lockPid, path);
+ return (B_TRUE);
+ }
+
+ log_msg(LOG_MSG_DEBUG, MSG_VALID_NOTOK, pll->lockObject, pll->lockPid,
+ path);
+
+ /* delete this lock */
+
+ log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
+ MSG_VALID_STALE, pll->lockObject, pll->lockPid,
+ pll->lockZoneId);
+
+ _decrementLockCount(a_fd, a_theLock);
+
+ return (B_FALSE);
+}
+
+static int
+_decrementLockCount(int a_fd, LOCK_T *a_theLock)
+{
+ ADMINLOCK_T *pll;
+ LOCK_T tmpLock;
+ RECORDNUM_T lastRecord;
+ char *pld;
+ long pls;
+ off_t lastPos;
+ ssize_t result;
+ int res;
+
+ /* localize references to lock object */
+
+ pld = &a_theLock->_lrLockData[0];
+ pll = &a_theLock->_lrLock;
+ pls = sizeof (a_theLock->_lrLockData);
+
+ /* decrement lock count */
+
+ pll->lockCount--;
+
+ /* if lock count > 0 then write out and leave locked */
+
+ if (pll->lockCount > 0) {
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DECING,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ pll->lockRecordNum, pll->lockCount);
+
+ result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
+ if (result != pls) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DONE,
+ pll->lockRecordNum, pll->lockCount,
+ pll->lockObject, pll->lockKey);
+
+ return (0);
+ }
+
+ /*
+ * lock count zero - erase the record
+ */
+
+ /* find last record in the lock file */
+
+ lastPos = lseek(a_fd, 0L, SEEK_END); /* get size of lock file */
+ if (lastPos == (off_t)-1) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_LSEEK_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+
+ lastRecord = (lastPos/pls)-1; /* convert size to record # */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVE,
+ lastPos, lastRecord, pll->lockRecordNum);
+
+ /* see if removing last record of file */
+
+ if (lastRecord == pll->lockRecordNum) {
+ /* debug info removing last record */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_LASTONE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ lastRecord, lastPos-pls);
+
+ /* removing last record of file, truncate */
+
+ res = ftruncate(a_fd, lastPos-pls);
+ if (res == -1) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+ return (0);
+ }
+
+ /*
+ * not removing last record of file:
+ * read last record, truncate file one record,
+ * replace record to be removed with last record read
+ */
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVING,
+ pll->lockRecordNum, lastRecord, lastPos-pls);
+
+ /* read in the last record */
+
+ result = pread(a_fd, tmpLock._lrLockData, pls, lastRecord*pls);
+ if (result != pls) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PREAD_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+
+ }
+
+ /* truncate lock file removing the last record (just read in) */
+
+ res = ftruncate(a_fd, lastPos-pls);
+ if (res == -1) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+
+ /* update record to indicate its new position in the lock file */
+
+ tmpLock._lrLock.lockRecordNum = pll->lockRecordNum;
+
+ /* write out the updated record to the new location */
+
+ result = pwrite(a_fd, tmpLock._lrLockData, pls, pll->lockRecordNum*pls);
+ if (result != pls) {
+ log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
+ a_theLock->_lrLock.lockExclusive ?
+ MSG_LOCK_EXC : MSG_LOCK_SHR,
+ a_theLock->_lrLock.lockObject,
+ strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Name: _getUniqueId
+ * Description: Generate a unique ID that can be used as a key for a new lock
+ * Arguments: None
+ * Returns: char *
+ * == NULL - error, no key generated
+ * != NULL - generated key
+ * NOTE: Any results returned is placed in new storage for the
+ * calling method. The caller must use 'lu_memFree' to dispose
+ * of the storage once the results are no longer needed.
+ */
+
+static char *
+_getUniqueId(void)
+{
+ char *args[10];
+ char *execResults;
+ char newkey[LOCK_KEY_MAXLEN];
+ hrtime_t hretime;
+ int b;
+ int execStatus;
+ struct tm tstruct;
+ time_t thetime;
+
+ /*
+ * try and use makeuuid to generate a unique i.d. Such a unique i.d.
+ * will look like:
+ * 7814e3c1-1dd2-11b2-9fe8-000d560ddc82
+ */
+
+ args[0] = "makeuuid";
+ args[1] = (char *)NULL;
+
+ b = e_ExecCmdList(&execStatus, &execResults, (char *)NULL,
+ "/usr/bin/makeuuid", (char *)NULL);
+
+ if ((b == 0) && (execStatus == 0) && (*execResults != '\0')) {
+ char *p;
+ p = strpbrk(execResults, " \t\n");
+ if (p != (char *)NULL) {
+ *p = '\0';
+ }
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_MAKEUUID,
+ execResults);
+ return (execResults);
+ }
+
+ /*
+ * cannot run makeuuid - generate own unique key - the key is the
+ * same length as unique uid but contains different information that
+ * is as unique as can be made - include current hires time (nanosecond
+ * real timer. Such a unique i.d. will look like:
+ * 0203104092-1145345-0004e94d6af481a0
+ */
+
+ hretime = gethrtime();
+
+ thetime = time((time_t *)NULL);
+ (void) localtime_r(&thetime, &tstruct);
+
+ (void) snprintf(newkey, sizeof (newkey),
+ "%02d%02d%02d%03d-%02d%02d%02d%d-%016llx", tstruct.tm_mday,
+ tstruct.tm_mon, tstruct.tm_year, tstruct.tm_yday,
+ tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec,
+ tstruct.tm_wday, hretime);
+
+ log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_INTERNAL, newkey);
+ return (strdup(newkey));
+}
+
+/*
+ * Name: sigint_handler
+ * Synopsis: SIGINT interrupt handler
+ * Description: Catch the "SIGINT" signal; increment signal_received
+ * global variable,
+ * Arguments: signo - [RO, *RO] - (int)
+ * Signal number that was caught
+ * Returns: void
+ */
+
+static void
+sigint_handler(int a_signo)
+{
+ signal_received++;
+}
+
+/*
+ * Name: sighup_handler
+ * Synopsis: SIGHUP interrupt handler
+ * Description: Catch the "SIGHUP" signal; increment signal_received
+ * global variable,
+ * Arguments: signo - [RO, *RO] - (int)
+ * Signal number that was caught
+ * Returns: void
+ */
+
+static void
+sighup_handler(int a_signo)
+{
+ signal_received++;
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadm/main.c b/usr/src/cmd/svr4pkg/pkgadm/main.c
new file mode 100644
index 0000000000..6e5cb17b62
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/main.c
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ */
+
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/* initial error message buffer size */
+
+#define ERR_BUFSIZE 2048
+
+/* Local Function Prototypes */
+
+static void print_version();
+int get_dbstatus(int argc, char **argv);
+
+/* holds subcommands and their definitions */
+struct cmd {
+ char *c_name;
+ int (*c_func)(int, char **);
+};
+
+struct cmd cmds[] = {
+ { "dbstatus", get_dbstatus},
+ { "lock", admin_lock},
+ /* last one must be all NULLs */
+ { NULL, NULL }
+};
+
+struct cmd cert_cmds[] = {
+ { "addcert", addcert},
+ { "listcert", listcert},
+ { "removecert", removecert},
+ /* last one must be all NULLs */
+ { NULL, NULL }
+};
+
+
+/*
+ * Function: main
+ *
+ * Return: 0 - subprocessing successful
+ * scripts and reboot
+ * [other] - subprocessing-specific failure
+ */
+int
+main(int argc, char **argv)
+{
+ char cur_cmd;
+ int newargc;
+ char **newargv;
+ int i;
+
+ /* Should be defined by cc -D */
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+ /* set the default text domain for messaging */
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (getenv("PKGADM_VERBOSE")) {
+ set_verbose(B_TRUE);
+ }
+
+ /* Superficial check of the arguments. */
+ if (argc <= 1) {
+ log_msg(LOG_MSG_INFO, MSG_USAGE);
+ return (1);
+ }
+
+ /* first, process any arguments that can appear before the subcommand */
+ while ((i = getopt(argc, argv, "vV?")) != EOF) {
+ switch (i) {
+ case 'v': /* verbose mode enabled */
+ set_verbose(B_TRUE);
+ break;
+ case 'V':
+ print_version();
+ return (0);
+ case '?':
+ log_msg(LOG_MSG_INFO, MSG_USAGE);
+ return (0);
+ }
+ }
+
+ /* OK, hand it off to the subcommand processors */
+ for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+ if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
+ /* make subcommand the first option */
+ newargc = argc - optind;
+ newargv = argv + optind;
+ opterr = optind = 1; optopt = 0;
+ return (cmds[cur_cmd].c_func(newargc, newargv));
+ }
+ }
+
+ /* initialize security library */
+ sec_init();
+
+ /* OK, hand it off to the subcommand processors */
+ for (cur_cmd = 0; cert_cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+ if (ci_streq(argv[optind], cert_cmds[cur_cmd].c_name)) {
+ /* make subcommand the first option */
+ newargc = argc - optind;
+ newargv = argv + optind;
+ opterr = optind = 1; optopt = 0;
+ return (cert_cmds[cur_cmd].c_func(newargc, newargv));
+ }
+ }
+
+ /* bad subcommand */
+ log_msg(LOG_MSG_ERR, MSG_BAD_SUB, argv[optind]);
+ log_msg(LOG_MSG_INFO, MSG_USAGE);
+ return (1);
+}
+
+/*
+ * Name: set_verbose
+ * Description: Turns on verbose output
+ * Scope: public
+ * Arguments: verbose = B_TRUE indicates verbose mode
+ * Returns: none
+ */
+void
+set_verbose(boolean_t setting)
+{
+ log_set_verbose(setting);
+}
+
+/*
+ * Name: get_verbose
+ * Description: Returns whether or not to output verbose messages
+ * Scope: public
+ * Arguments: none
+ * Returns: B_TRUE - verbose messages should be output
+ */
+boolean_t
+get_verbose()
+{
+ return (log_get_verbose());
+}
+
+/*
+ * Name: log_pkgerr
+ * Description: Outputs pkgerr messages to logging facility.
+ * Scope: public
+ * Arguments: type - the severity of the message
+ * err - error stack to dump to facility
+ * Returns: none
+ */
+void
+log_pkgerr(LogMsgType type, PKG_ERR *err)
+{
+ int i;
+ for (i = 0; i < pkgerr_num(err); i++) {
+ log_msg(type, "%s", pkgerr_get(err, i));
+ }
+}
+
+/*
+ * Name: print_Version
+ * Desc: Prints Version of packaging tools
+ * Arguments: none
+ * Returns: none
+ */
+static void
+print_version()
+{
+ /* ignore any and all arguments, print version only */
+ (void) fprintf(stdout, "%s\n", SUNW_PKGVERS);
+}
+
+/*
+ * usage
+ *
+ * Outputs the usage string.
+ *
+ * Return:1
+ * Side effects: none
+ */
+static int
+usage()
+{
+ log_msg(LOG_MSG_INFO, MSG_USAGE);
+ return (1);
+}
+
+/*
+ * get_dbstatus
+ *
+ * Return 'text' as the db status.
+ * Use the command line to determine if there is an alternate root.
+ *
+ * Return: 0 on success, nonzero on failure
+ * Side effects: none
+ */
+int
+get_dbstatus(int argc, char **argv)
+{
+ /* Either accept 1 argument or 3 arguments where the second is -R */
+ if (argc != 1 && (argc != 3 || strcmp(argv[1], "-R")))
+ return (usage());
+
+ (void) printf("%s\n", PKGADM_DBSTATUS_TEXT);
+
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgadm/pkgadm.h b/usr/src/cmd/svr4pkg/pkgadm/pkgadm.h
new file mode 100644
index 0000000000..5e5c0616b0
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/pkgadm.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef _PKGADM_H
+#define _PKGADM_H
+
+
+/*
+ * Module: patchutil.h
+ * Description: This module contains the interfaces for patchadd
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkglib.h"
+#include "libinst.h"
+
+/* version of packaging interface */
+#define SUNW_PKGVERS "1.0"
+
+/* string comparitor abbreviators */
+
+#define ci_streq(a, b) (strcasecmp((a), (b)) == 0)
+#define ci_strneq(a, b, c) (strncasecmp((a), (b), (c)) == 0)
+#define streq(a, b) (strcmp((a), (b)) == 0)
+#define strneq(a, b, c) (strncmp((a), (b), (c)) == 0)
+
+/* max l10n message length we will display */
+#define MSG_MAX 1024
+
+/* main.c */
+extern void log_msg(LogMsgType, const char *, ...);
+extern void log_pkgerr(LogMsgType, PKG_ERR *);
+extern void set_verbose(boolean_t);
+extern boolean_t get_verbose(void);
+/* lock.c */
+extern int admin_lock(int, char **);
+/* listcert.c */
+extern int listcert(int, char **);
+/* importcert.c */
+extern int addcert(int, char **);
+/* removecert.c */
+extern int removecert(int, char **);
+
+/* certs.c */
+extern int load_cert_and_key(PKG_ERR *, FILE *,
+ keystore_encoding_format_t, char *, EVP_PKEY **, X509 **);
+extern int load_all_certs(PKG_ERR *, FILE *,
+ keystore_encoding_format_t, char *, STACK_OF(X509) **);
+
+#define PKGADM_DBSTATUS_TEXT "text"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGADM_H */
diff --git a/usr/src/cmd/svr4pkg/pkgadm/pkgadm_msgs.h b/usr/src/cmd/svr4pkg/pkgadm/pkgadm_msgs.h
new file mode 100644
index 0000000000..6fbe10c438
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/pkgadm_msgs.h
@@ -0,0 +1,625 @@
+/*
+ * 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.
+ */
+
+#ifndef _PKGADM_MSGS_H
+#define _PKGADM_MSGS_H
+
+
+#include <libintl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef lint
+#define gettext(x) x
+#endif
+
+/* generic messages */
+#define MSG_BAD_SUB gettext(\
+ "\"%s\" is not a valid subcommand")
+
+#define MSG_MISSING_OPERAND gettext(\
+ "-%c requires an operand")
+
+#define MSG_USAGE gettext(\
+"usage:\n" \
+"\n" \
+"pkgadm addcert [-ty] [-a app] [-k keystore] [-e keyfile]\n" \
+"\t[-f format] [-n name] [-P passarg] [-p input_passarg]\n" \
+"\t[-R rootpath] certfile\n" \
+"\n" \
+"\t- Adds a trusted CA certificate or user certificate\n" \
+"\tand private key\n" \
+"\n" \
+"pkgadm removecert [-a app] [-k keystore] -n name [-P passarg]\n" \
+"\t[-R rootpath]\n" \
+"\n" \
+"\t- Removes a trusted CA certificate or user certificate\n" \
+"\tand private key\n" \
+"\n" \
+"pkgadm listcert [-a app] [-f format] [-k keystore] -n name\n" \
+"\t[-P passarg] [-o outfile] [-R rootpath]\n" \
+"\n" \
+"\t- Prints trusted CA certificates or user certificates\n" \
+"\n" \
+"pkgadm dbstatus [-R rootpath]\n" \
+"\n" \
+"\t- Returns 'text' - the text install database in use since Solaris 2.0\n" \
+"\t is the current install database in use.\n" \
+"\n" \
+"pkgadm -V\n" \
+"\t- Displays packaging tools version\n" \
+"\n" \
+"pkgadm -?\n" \
+"\t- Shows this help message\n")
+
+#define MSG_WARNING gettext(\
+ "WARNING")
+
+#define MSG_ERROR gettext(\
+ "ERROR")
+
+#define MSG_T_OPTION_ARGS gettext(\
+ "-t option takes 2 or 3 arguments, not %d!\n")
+
+#define MSG_T_RESULT_TWO gettext(\
+ "result <%d>: <%s> ~= <%s>\n")
+
+#define MSG_T_RESULT_THREE gettext(\
+ "required <%d> actual <%d> <%30s> ~- <%30s>\n")
+
+#define MSG_KEYSTORE_PASSPROMPT gettext(\
+ "Enter Keystore Password: ")
+
+#define MSG_KEYSTORE_PASSOUTPROMPT gettext(\
+ "Type a Keystore protection Password.\n" \
+ "Press ENTER for no protection password (not recommended): ")
+
+#define MSG_PEM_PASSPROMPT gettext(\
+ "Enter PEM Passphrase: ")
+
+#define MSG_ERROR gettext(\
+ "ERROR")
+
+/* warnings */
+
+#define CREATE_PKGDIR_WARN gettext(\
+ "Creating directory <%s>\n")
+
+#define MSG_WRN_UNKNOWN gettext(\
+ "Signer <%s> has unsupported signature, ignoring")
+
+#define MSG_VALID_STALE gettext(\
+ "Removing stale lock on <%s> pid <%ld> zid <%ld>")
+
+/* errors */
+
+#define MSG_FATAL gettext(\
+ "Fatal Error")
+
+#define MSG_TOO_LONG gettext(\
+ "Length of <%s> exceeds maximum allowed length")
+
+#define MSG_INTERNAL gettext(\
+ "Intenal Error <%s>")
+
+#define MSG_OPEN gettext(\
+ "Cannot open <%s> for reading")
+
+#define MSG_OPEN_WRITE gettext(\
+ "Cannot open <%s> for writing")
+
+#define MSG_BAD_PASSARG gettext(\
+ "Invalid password retrieval method <%s>")
+
+#define MSG_BAD_PASS gettext(\
+ "Invalid password")
+
+#define ERR_LOG_FAIL gettext(\
+ "Failed to log message using format <%s>")
+
+#define MSG_BAD_FORMAT gettext(\
+ "Invalid format: <%s>")
+
+#define MSG_USER_NAME gettext(\
+ "An alias is required when adding user certificates")
+
+#define MSG_TRUSTED_NAME gettext(\
+ "Trusted certificates cannot have an explicit alias")
+
+#define MSG_MULTIPLE_TRUST gettext(\
+ "Found multiple certificates in <%s>. You must explicitly trust " \
+ "them using <%s>")
+
+#define MSG_NO_MULTIPLE_TRUST gettext(\
+ "Found multiple certificates in <%s>. You must explicitly trust " \
+ "them using <%s>")
+
+#define MSG_TRUSTED_KEY gettext(\
+ "Cannot supply private key when adding trusted certificates")
+
+#define MSG_TRUST_KEY_FOUND gettext(\
+ "One or more private keys were found in trusted certificate file <%s>")
+
+#define MSG_ADDCERT_ABORT gettext(\
+ "Addition of trusted certificate aborted by user request")
+
+
+#define MSG_NEED_KEY gettext(\
+ "No private key found in <%s>, must specify one with -e")
+
+#define MSG_NO_PRIVKEY gettext(\
+ "No private key found in <%s>")
+
+#define MSG_NO_CERTS gettext(\
+ "No certificates found in <%s>")
+
+#define MSG_MULTIPLE_CERTS gettext(\
+ "Multiple certificates found in <%s>")
+
+#define MSG_NO_ADDCERT gettext(\
+ "Cannot add certificate(s) from <%s>. No changes have been made.")
+
+#define MSG_NO_ADDKEY gettext(\
+ "Cannot add private key from <%s>. No changes have been made.")
+
+#define MSG_NO_REMOVECERT gettext(\
+ "Cannot remove certificate with alias <%s>")
+
+#define MSG_VERIFY_TRUST gettext(\
+ "Are you sure you want to trust this certificate? ")
+
+#define MSG_VERIFY_NOT_CA gettext(\
+ "\n" \
+ "This certificate does not appear to be issued and signed\n" \
+ "by a certificate authority (CA). CA Certificates are normally\n" \
+ "self-signed and have CA Basic Constraints.\n" \
+ "Are you sure you want to trust this certificate? ")
+
+#define MSG_PARSE gettext(\
+ "Parsing error")
+
+#define MSG_TRUSTED gettext(\
+ "Certificate(s) from <%s> are now trusted")
+
+#define MSG_TRUSTING gettext(\
+ "Trusting certificate <%s>")
+
+#define MSG_ADDED gettext(\
+ "Successfully added Certificate <%s> with alias <%s>")
+
+#define MSG_REMOVED gettext(\
+ "Successfully removed Certificate(s) with alias <%s>")
+
+#define MSG_MEM gettext(\
+ "Out of memory")
+
+#define MSG_PRINT gettext(\
+ "Cannot print certificates to <%s>")
+
+#define MSG_PROBLEM_CONVERT gettext(\
+ "Does %s/var/sadm exist? Can the user write to it? (%s)")
+
+#define MSG_CONTENTS_FORMAT gettext(\
+ "Operation failed due to corrupted install contents data file.")
+
+#define MSG_MKDIR_FAILED gettext(\
+ "Could not mkdir for path %s. %s.")
+
+#define MSG_RENAME_FAILED gettext(\
+ "Could not rename %s to %s\n%s")
+
+#define MSG_REMOVE_FAILED gettext(\
+ "Could not remove %s\n%s")
+
+#define MSG_FILE_ACCESS gettext(\
+ "Operation failed: unable to access file %s: %s")
+
+#define MSG_NOT_READABLE gettext(\
+ "Operation failed: unable to read file %s")
+
+#define MSG_PATCH_UPGD gettext(\
+ "Operation failed: unable to process patch information\n")
+
+#define MSG_BUILD_INDEXES gettext(\
+ "Operation failed: unable to build indexes\n")
+
+#define MSG_FILE_NAME_TOO_LONG gettext(\
+ "Operation failed: file name too long: %s\n")
+
+#define MSG_ZONES_MISSING_REQUEST gettext(\
+ "Must specify operation to perform\n")
+
+#define MSG_LOCK_ALTROOT_CANTCREATE gettext(\
+ "lock: cannot create alternative root directory <%s>: %s\n")
+
+#define MSG_LOCK_ALTROOT_NONEXIST gettext(\
+ "lock: argument to -R <%s> is not a directory: %s\n")
+
+#define MSG_LOCK_ROOTDIR_INVALID gettext(\
+ "lock: lock file base directory <%s> not valid: %s\n")
+
+#define MSG_LOCK_WFLAG_BADINT gettext(\
+ "The integer value <%s> given to the -W option includes an " \
+ "invalid character: \"%c\"\n")
+
+#define MSG_LOCK_pFLAG_BADINT gettext(\
+ "The integer value <%s> given to the -p option includes an " \
+ "invalid character: \"%c\"\n")
+
+#define MSG_LOCK_zFLAG_BADINT gettext(\
+ "The integer value <%s> given to the -z option includes an " \
+ "invalid character: \"%c\"\n")
+
+#define MSG_LOCK_nFLAG_BADINT gettext(\
+ "The integer value <%s> given to the -n option includes an " \
+ "invalid character: \"%c\"\n")
+
+#define MSG_LOCK_ar_TOGETHER gettext(\
+ "lock: The -a and -r options cannot be used together: "\
+ "specify only one.\n")
+
+#define MSG_LOCK_kARG_TOOLONG gettext(\
+ "Argument to -k is <%d> characters: may not exceed <%d> characters\n")
+
+#define MSG_LOCK_oARG_TOOLONG gettext(\
+ "Argument to -o is <%d> characters: may not exceed <%d> characters\n")
+
+#define MSG_LOCK_RARG_NOT_ABSOLUTE gettext(\
+ "Argument to -R must be absolute path: %s")
+
+#define MSG_LOCK_WFLAG_ERROR gettext(\
+ "Argument to -W has problem with wait interval <%s>: %s")
+
+#define MSG_LOCK_pFLAG_ERROR gettext(\
+ "Argument to -p has problem with process i.d. value <%s>: %s")
+
+#define MSG_LOCK_zFLAG_ERROR gettext(\
+ "Argument to -p has problem with zone i.d. value <%s>: %s")
+
+#define MSG_LOCK_nFLAG_ERROR gettext(\
+ "Argument to -n has problem with maximum number of retries " \
+ "value <%s>: %s")
+
+#define MSG_LOCK_es_TOGETHER gettext(\
+ "lock: The -e and -s options cannot be used together: "\
+ "specify only one.\n")
+
+#define MSG_LOCK_ak_TOGETHER gettext(\
+ "lock: The -k option cannot be used with the -a option.\n")
+
+#define MSG_LOCK_e_without_a gettext(\
+ "lock: The -e option can only be used with the -a option.\n")
+
+#define MSG_LOCK_s_without_a gettext(\
+ "lock: The -s option can only be used with the -a option.\n")
+
+#define MSG_LOCK_ACQUIRE_KEYMISMATCH gettext(\
+ "cannot acquire %s lock on <%s>: object locked and specified key " \
+ "does not match")
+
+#define MSG_LOCK_ACQUIRE_ERROR gettext(\
+ "cannot determine if object <%s> key <%s> is locked: %s")
+
+#define MSG_LOCK_ACQUIRE_TIMEDOUT gettext(\
+ "cannot acquire %s lock on <%s> key <%s>: object locked, no key " \
+ "was specified, and the wait timed out")
+
+#define MSG_LOCK_ACQUIRE_WAITING gettext(\
+ "object <%s> is locked: waiting for object to become available")
+
+#define MSG_LOCK_ACQUIRE_REOPEN_FAILED gettext(\
+ "cannot reopen lock file after waiting for lock on object " \
+ "<%s> to be released")
+
+#define MSG_LOCK_RELEASE_NOTLOCKED gettext(\
+ "cannot release lock on <%s> key <%s>: object not locked and " \
+ "a key was specified")
+
+#define MSG_LOCK_RELEASE_LOCKED gettext(\
+ "cannot release lock on <%s> key <%s>: object locked but no " \
+ "key was specified")
+
+#define MSG_LOCK_RELEASE_NOTFOUND gettext(\
+ "cannot release lock on <%s> key <%s>: object is not locked")
+
+#define MSG_LOCK_RELEASE_KEYMISMATCH gettext(\
+ "cannot release lock on <%s>: object locked and specified key " \
+ "does not match")
+
+#define MSG_LOCK_RELEASE_ERROR gettext(\
+ "cannot determine if object <%s> key <%s> is locked")
+
+#define MSG_LOCK_EXEC_ACCESS gettext(\
+ "cannot execute command <%s>: %s")
+
+#define MSG_LOCK_EXEC_NOINPUT gettext(\
+ "cannot open input file <%s>: %s")
+
+#define MSG_LOCK_EXEC_NOPIPE gettext(\
+ "cannot create pipe: %s")
+
+#define MSG_LOCK_FINDLOCK_LSEEK_FAILURE gettext(\
+ "cannot find lock <%s> key <%s>: lseek failure: %s")
+
+#define MSG_LOCK_ADDLOCK_PWRITE_FAILURE gettext(\
+ "cannot create %s lock for object <%s>: pwrite failure: %s")
+
+#define MSG_LOCK_ADDLOCK_LSEEK_FAILURE gettext(\
+ "cannot create %s lock for object <%s>: lseek failure: %s")
+
+#define MSG_LOCK_INCLOCK_PWRITE_FAILURE gettext(\
+ "cannot increment %s lock for object <%s>: pwrite failure: %s")
+
+#define MSG_LOCK_DECLOCK_PWRITE_FAILURE gettext(\
+ "cannot decrement %s lock for object <%s>: pwrite failure: %s")
+
+#define MSG_LOCK_DECLOCK_PREAD_FAILURE gettext(\
+ "cannot decrement %s lock for object <%s>: pread failure: %s")
+
+#define MSG_LOCK_DECLOCK_LSEEK_FAILURE gettext(\
+ "cannot decrement %s lock for object <%s>: lseek failure: %s")
+
+#define MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE gettext(\
+ "cannot decrement %s lock for object <%s>: ftruncate failure: %s")
+
+/*
+ * i18n:
+ * next two messages grouped together
+ */
+
+#define MSG_LOCK_ACQUIRE_BUSY_QUASI gettext(\
+ "cannot acquire %s lock on <%s> key <%s>: object matches wildcard " \
+ "<%s> lock%s")
+#define MSG_LOCK_ACQUIRE_BUSY_FIRST gettext(\
+ "cannot acquire %s lock on <%s> key <%s>: object <%s> is locked <%s>%s")
+
+/*
+ * i18n: note this message may be appended to the previous message
+ * by supplying it to the final "%s" at the end of the line above;
+ * that is either:
+ * cannot acquire %s lock on <%s> key <%s>: object is locked <%s>
+ * or:
+ * cannot acquire %s lock on <%s> [...] is locked <%s> and no key specified
+ */
+
+#define MSG_LOCK_ACQUIRE_BUSY_ADDITIONAL gettext(\
+ " and no key specified")
+
+/*
+ * i18n: note these two "messages" are inserted into other
+ * messages, such as:
+ * cannot acquire %s lock on <%s>
+ * will be either:
+ * cannot acquire shared lock on <%s>
+ * or
+ * cannot acquire exclusive lock on <%s>
+ */
+
+#define MSG_LOCK_EXC gettext(\
+ "exclusive")
+
+#define MSG_LOCK_SHR gettext(\
+ "shared")
+
+/*
+ * i18n: note these messages are "debugging" messages and will normally
+ * not be seen unless debugging has been enabled for problem root causing
+ * so they are not meant to be perfectly "human readable"
+ */
+
+#define MSG_VALID_NOPID gettext(\
+ "validate lock <%s>: VALID (no pid)")
+
+#define MSG_VALID_BADZID gettext(\
+ "validate lock <%s>: VALID (lock zid <%ld> this zid <%ld>)")
+
+#define MSG_VALID_ZIDOK gettext(\
+ "validate lock <%s>: zone i.d.s match (lock zid <%ld> this zid <%ld>)")
+
+#define MSG_VALID_OK gettext(\
+ "validate lock <%s> pid <%ld> path <%s>: VALID")
+
+#define MSG_VALID_NOTOK gettext(\
+ "validate lock <%s> pid <%ld> path <%s>: NOT VALID")
+
+#define MSG_LCKMCH_ENTRY gettext(\
+ "lockMatch: *** BEGIN *** compare objects <%s> <%s>")
+
+#define MSG_LCKMCH_FSTNODE gettext(\
+ "lockMatch: first lock node (%d) <%s>")
+
+#define MSG_LCKMCH_SCNDNODE gettext(\
+ "lockMatch: second lock node (%d) <%s>")
+
+#define MSG_LCKMCH_NODES gettext(\
+ "lockMatch: first lock node <%s> prefix <%s> (%d) second lock " \
+ " node <%s> prefix <%s> (%d)")
+
+#define MSG_LCKMCH_DIRMCH gettext(\
+ "lockMatch: no prefix direct comparison: match: <%s> <%s>")
+
+#define MSG_LCKMCH_DIRNOMCH gettext(\
+ "lockMatch: no prefix direct comparison: NO MATCH: <%s> <%s>")
+
+#define MSG_LCKMCH_PFXMCH gettext(\
+ "lockMatch: prefix comparison: match: <%s> <%s>")
+
+#define MSG_LCKMCH_PFXNOMCH gettext(\
+ "lockMatch: prefix comparison: NO MATCH: <%s> <%s>")
+
+#define MSG_LCKMCH_FSTLCK gettext(\
+ "lockMatch: first lock index (%d) last scanned node <%s> prefix " \
+ "<%s> (%d)")
+
+#define MSG_LCKMCH_SCNDLCK gettext(\
+ "lockMatch: second lock index (%d) last scanned node <%s> prefix " \
+ "<%s> (%d)")
+
+#define MSG_LCKMCH_ABSNOMCH gettext(\
+ "lockMatch: absolute locks: NO MATCH: <%s> <%s>")
+
+#define MSG_LCKMCH_OBJMCH gettext(\
+ "lockMatch: object locks: match: <%s> <%s>")
+
+#define MSG_LCKMCH_OVLPNOMCH gettext(\
+ "lockMatch: nonmatching overlapping objects: <%s> <%s> before " \
+ "(%d) <%s>")
+
+#define MSG_LCKMCH_SAME gettext(\
+ "lockMatch: locks begin with same node - compare: <%s> <%s> at <%s>")
+
+#define MSG_LCKMCH_SCNDSUB gettext(\
+ "lockMatch: second lock <%s> subset of <%s> at (%d) <%s>")
+
+#define MSG_LCKMCH_FRSTSUB gettext(\
+ "lockMatch: first lock <%s> subset of <%s> at (%d) <%s>")
+
+#define MSG_LCKMCH_DONTKNOW gettext(\
+ "lockMatch: unable to determine how to compare locks: <%s> <%s>: " \
+ "using direct comparision")
+
+#define MSG_LCKMCH_READY gettext(\
+ "lockMatch: comparing nodes locks <%s> <%s>")
+
+#define MSG_LCKMCH_NODEFAIL gettext(\
+ "lockMatch: node (%d) comparison: NO MATCH: <%s> != <%s>")
+
+#define MSG_LCKMCH_NODEOK gettext(\
+ "lockMatch: node (%d) comparision: match: <%s> == <%s>")
+
+#define MSG_LCKMCH_MATCHOK gettext(\
+ "lockMatch: locks match: <%s> == <%s>")
+
+#define MSG_LOCK_EXEC_RESULTS gettext(\
+ "command <%s> executed: pid <%d> errno <0x%04x> status <0x%04x> " \
+ "final status <0x%04x> output <%s>")
+
+#define MSG_LOCK_GENUID_MAKEUUID gettext(\
+ "generated new unique key using makeuuid: %s")
+
+#define MSG_LOCK_GENUID_INTERNAL gettext(\
+ "generated new unique key using date: %s")
+
+#define MSG_LOCK_DECLOCK_DECING gettext(\
+ "decrement <%s> lock count record <%d> count <%d>")
+
+#define MSG_LOCK_DECLOCK_DONE gettext(\
+ "decrement lock record <%d> count <%d> object <%s> key <%s>")
+
+#define MSG_LOCK_DECLOCK_REMOVE gettext(\
+ "decrement lock remove record lastPos %ld last record %d " \
+ "current record %d")
+
+#define MSG_LOCK_DECLOCK_LASTONE gettext(\
+ "decrement lock removing <%s> lock last record <%d> " \
+ "truncating to <%ld>")
+
+#define MSG_LOCK_DECLOCK_REMOVING gettext(\
+ "decrement lock removing record <%d> last record <%d> " \
+ "truncating to <%ld>")
+
+#define MSG_LOCK_INCLOCK_ENTRY gettext(\
+ "increment <%s> lock count record <%d> count <%d>")
+
+#define MSG_LOCK_INCLOCK_DONE gettext(\
+ "increment lock record <%d> count <%d> object <%s> key <%s>")
+
+#define MSG_LOCK_ADDLOCK_ADDING gettext(\
+ "adding %s lock pos <%d> object <%s> key <%s> pid <%ld> zid <%ld>")
+
+#define MSG_LOCK_FINDLOCK_ENTRY gettext(\
+ "find lock object <%s> key <%s>")
+
+#define MSG_LOCK_FINDLOCK_READRECORD gettext(\
+ "find lock read record <%d>: count <%d> object <%s> key <%s> pid " \
+ "<%ld> zid <%ld>")
+
+#define MSG_LOCK_FINDLOCK_FOUND gettext(\
+ "find lock record found")
+
+#define MSG_LOCK_FINDLOCK_NOTFOUND gettext(\
+ "find lock record not found")
+
+#define MSG_LOCK_OPENFILE_ENTRY gettext(\
+ "open lock file root <%s> file <%s>")
+
+#define MSG_LOCK_OPENFILE_SLEEPING gettext(\
+ "open lock file busy <%s>: sleeping <%d>")
+
+#define MSG_LOCK_OPENFILE_FAILURE gettext(\
+ "open lock file could not be opened: %s")
+
+#define MSG_LOCK_OPENFILE_SLEEP2 gettext(\
+ "open lock file cannot obtain record lock <%s>: sleeping <%d>")
+
+#define MSG_LOCK_OPENFILE_FAIL2 gettext(\
+ "open lock file could not obtain record lock: <%s>")
+
+#define MSG_LOCK_OPENFILE_SUCCESS gettext(\
+ "open lock file: opened and locked fd <%d>")
+
+#define MSG_LOCK_STATUS_READRECORD gettext(\
+ "status read record <%d>: count <%d> object <%s> key <%s> pid <%ld> " \
+ "zid <%ld>")
+
+#define MSG_LOCK_STATUS_ENTRY gettext(\
+ "status key=<%s> object=<%s>")
+
+#define MSG_LOCK_RELEASE_FOUND gettext(\
+ "object <%s> key <%s> is locked: decrementing lock count")
+
+#define MSG_LOCK_RELEASE_ENTRY gettext(\
+ "release lock key=<%s> object=<%s> quiet=<%d>")
+
+#define MSG_LOCK_RELEASE_FINDRESULT gettext(\
+ "release lock result <%d> record <%d>")
+
+#define MSG_LOCK_ACQUIRE_FOUND_INC gettext(\
+ "object <%s> key <%s> is locked: incrementing <%s> lock count")
+
+#define MSG_LOCK_ACQUIRE_ENTRY gettext(\
+ "acquire lock key=<%s> object=<%s> quiet=<%d> exclusive=<%d>")
+
+#define MSG_LOCK_ACQUIRE_FINDRESULT gettext(\
+ "acquire %s lock result <%d> record <%d>")
+
+#define MSG_LOCK_ACQUIRE_LOCKED_SHARED gettext(\
+ "object <%s> key <%s> is locked but shared: incrementing lock count")
+
+#define MSG_LOCK_ACQUIRE_NOTLOCKED gettext(\
+ "cannot acquire %s lock on <%s> key <%s>: object not locked " \
+ "and non-matching key specified")
+
+#define MSG_LOCK_ACQUIRE_NOTFOUND gettext(\
+ "acquiring %s lock on object <%s>")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGADM_MSGS_H */
diff --git a/usr/src/cmd/svr4pkg/pkgadm/removecert.c b/usr/src/cmd/svr4pkg/pkgadm/removecert.c
new file mode 100644
index 0000000000..3a176b6184
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgadm/removecert.c
@@ -0,0 +1,201 @@
+/*
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/*
+ * Name: removecert
+ * Desc: Removes a user certificate and associated private key,
+ * or a trusted certificate, from the keystore.
+ * Syntax: addcert [-a app] [-k keystore] -n name [-P passarg] [-R altroot]
+ */
+int
+removecert(int argc, char **argv)
+{
+ int i;
+ char keystore_file[MAXPATHLEN] = "";
+ char *keystore_base = NULL;
+ char *homedir;
+ char *passarg = NULL;
+ char *altroot = NULL;
+ char *prog = NULL;
+ char *alias = NULL;
+ int ret = 1;
+ PKG_ERR *err = NULL;
+ keystore_handle_t keystore = NULL;
+
+ while ((i = getopt(argc, argv, ":a:k:n:P:R:")) != EOF) {
+ switch (i) {
+ case 'a':
+ prog = optarg;
+ break;
+ case 'k':
+ keystore_base = optarg;
+ break;
+ case 'n':
+ alias = optarg;
+ break;
+ case 'P':
+ passarg = optarg;
+ break;
+ case 'R':
+ altroot = optarg;
+ break;
+ case ':':
+ log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+ /* fallthrough intentional */
+ case '?':
+ default:
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+ }
+
+ /* we require a name */
+ if (alias == NULL) {
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+
+ /* should be no arguments left */
+ if ((argc-optind) > 0) {
+ log_msg(LOG_MSG_ERR, MSG_USAGE);
+ goto cleanup;
+ }
+
+ /* set up proper keystore */
+ if (keystore_base == NULL) {
+ if (geteuid() == 0 || altroot != NULL) {
+ /*
+ * If we have an alternate
+ * root, then we have no choice but to use
+ * root's keystore on that alternate root,
+ * since there is no way to resolve a
+ * user's home dir given an alternate root
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if ((homedir = getenv("HOME")) == NULL) {
+ /*
+ * not superuser, but no home dir, so
+ * use superuser's keystore
+ */
+ if (strlcat(keystore_file, PKGSEC,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ } else {
+ if (strlcat(keystore_file, homedir,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ homedir);
+ goto cleanup;
+ }
+ if (strlcat(keystore_file, "/.pkg/security",
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_file);
+ goto cleanup;
+ }
+ }
+ }
+ } else {
+ if (strlcat(keystore_file, keystore_base,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+ keystore_base);
+ goto cleanup;
+ }
+ }
+
+ err = pkgerr_new();
+
+ /* now load the key store */
+ log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+ set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+ set_passphrase_passarg(passarg);
+
+ if (open_keystore(err, keystore_file, prog, pkg_passphrase_cb,
+ KEYSTORE_ACCESS_READWRITE | KEYSTORE_PATH_HARD, &keystore) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ goto cleanup;
+ }
+
+ /* now remove the selected certs */
+ log_msg(LOG_MSG_DEBUG, "Removing certificate(s) with name <%s>",
+ alias);
+ if (delete_cert_and_keys(err, keystore, alias) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_REMOVECERT, alias);
+ goto cleanup;
+ }
+
+ /* now write it back out */
+ log_msg(LOG_MSG_DEBUG, "Closing keystore");
+ set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+ set_passphrase_passarg(passarg);
+ if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+ log_pkgerr(LOG_MSG_ERR, err);
+ log_msg(LOG_MSG_ERR, MSG_NO_REMOVECERT, alias);
+ goto cleanup;
+ }
+
+ log_msg(LOG_MSG_INFO, MSG_REMOVED, alias);
+
+ ret = 0;
+ /* fallthrough intentional */
+cleanup:
+
+ if (err != NULL)
+ pkgerr_free(err);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgchk/Makefile b/usr/src/cmd/svr4pkg/pkgchk/Makefile
new file mode 100644
index 0000000000..d32d17785a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgchk/Makefile
@@ -0,0 +1,45 @@
+#
+# 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= pkgchk
+
+OBJS= checkmap.o \
+ ckentry.o \
+ main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -ll -lpkg -linstzones -ladm
+LDLIBS += -lnsl -lsocket
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
+
diff --git a/usr/src/cmd/svr4pkg/pkgchk/checkmap.c b/usr/src/cmd/svr4pkg/pkgchk/checkmap.c
new file mode 100644
index 0000000000..a3be26e263
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgchk/checkmap.c
@@ -0,0 +1,425 @@
+/*
+ * 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 2006 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 <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+extern int qflag, lflag, Lflag, pkgcnt;
+extern short npaths;
+
+extern char *basedir, *pathlist[], *ppathlist[], **pkg, **environ;
+
+extern short used[];
+extern struct cfent **eptlist;
+
+/* ocfile.c */
+extern int socfile(VFP_T **vfp); /* simple open & lock of DB. */
+extern int relslock(void); /* unlock the database. */
+
+/* ckentry.c */
+extern int ckentry(int envflag, int maptyp, struct cfent *ept, VFP_T *vfp);
+
+#define NXTENTRY(P, VFP) \
+ (maptyp ? srchcfile((P), "*", (VFP), (VFP_T *)NULL) : \
+ gpkgmapvfp((P), (VFP)))
+
+#define MSG_ARCHIVE "NOTE: some pathnames are in private formats " \
+ "and cannot be verified"
+#define WRN_NOPKG "WARNING: no pathnames were associated with <%s>"
+#define WRN_NOPATH "WARNING: no information associated with pathname <%s>"
+#define EMPTY_PKG "WARNING: Package <%s> is installed but empty"
+#define ERR_NOMEM "unable to allocate dynamic memory, errno=%d"
+#define ERR_PKGMAP "unable to open pkgmap file <%s>"
+#define ERR_ENVFILE "unable to open environment file <%s>"
+
+static struct cfent entry;
+
+static int shellmatch(char *, char *);
+static int is_partial_path_in_DB(char *, char *);
+
+int selpath(char *, int);
+int selpkg(char *);
+
+/*
+ * This routine checks all files which are referenced in the pkgmap which is
+ * identified by the mapfile arg. When the package is installed, the mapfile
+ * may be the contents file or a separate pkgmap (maptyp tells the function
+ * which it is). The variable uninst tells the function whether the package
+ * is in the installed state or not. The envfile entry is usually a pkginfo
+ * file, but it could be any environment parameter list.
+ */
+
+int
+checkmap(int maptyp, int uninst, char *mapfile, char *envfile,
+ char *pkginst, char *path, int pathtype)
+{
+ FILE *fp;
+ char *cl = NULL;
+ char *value;
+ char param[MAX_PKG_PARAM_LENGTH];
+ int count;
+ int errflg;
+ int n;
+ int selected;
+ struct pinfo *pinfo;
+ VFP_T *vfp = (VFP_T *)NULL;
+
+ if (envfile != NULL) {
+ if ((fp = fopen(envfile, "r")) == NULL) {
+ progerr(gettext(ERR_ENVFILE), envfile);
+ return (-1);
+ }
+ param[0] = '\0';
+ while (value = fpkgparam(fp, param)) {
+ if (strcmp("PATH", param) != 0) {
+ /*
+ * If checking an uninstalled package, we
+ * only want two parameters. If we took all
+ * of them, including path definitions, we
+ * wouldn't be looking in the right places in
+ * the reloc and root directories.
+ */
+ if (uninst) {
+ if ((strncmp("PKG_SRC_NOVERIFY", param,
+ 16) == 0) && value) {
+ logerr(gettext(MSG_ARCHIVE));
+ putparam(param, value);
+ }
+ if ((strncmp("CLASSES", param,
+ 7) == 0) && value)
+ putparam(param, value);
+ } else
+ putparam(param, value);
+ }
+
+ free(value);
+
+ param[0] = '\0';
+ }
+ (void) fclose(fp);
+ basedir = getenv("BASEDIR");
+ }
+
+ /*
+ * If we are using a contents file for the map, this locks the
+ * contents file in order to freeze the database and assure it
+ * remains synchronized with the file system against which it is
+ * being compared. There is no practical way to lock another pkgmap
+ * on some unknown medium so we don't bother.
+ */
+ if (maptyp) { /* If this is the contents file */
+ if (!socfile(&vfp)) {
+ progerr(gettext(ERR_PKGMAP), "contents");
+ return (-1);
+ }
+ } else {
+ if (vfpOpen(&vfp, mapfile, "r", VFP_NONE) != 0) {
+ progerr(gettext(ERR_PKGMAP), mapfile);
+ return (-1);
+ }
+ }
+
+ if ((cl = getenv("CLASSES")) != NULL)
+ cl_sets(qstrdup(cl));
+
+ errflg = count = 0;
+
+ do {
+ if ((n = NXTENTRY(&entry, vfp)) == 0) {
+ break;
+ }
+ /*
+ * Search for partial paths in the ext DB.
+ */
+ if (pathtype) {
+ /* LINTED warning: statement has no consequent: if */
+ if (is_partial_path_in_DB(entry.path, path)) {
+ /* Check this entry */
+ ;
+ } else if (entry.ftype == 's' ||
+ entry.ftype == 'l') {
+ if (is_partial_path_in_DB(
+ /* LINTED warning: statement has no consequen */
+ entry.ainfo.local, path)) {
+ /* Check this entry */
+ ;
+ } else {
+ continue;
+ }
+ } else {
+ /* Skip to next DB entry */
+ continue;
+ }
+ }
+
+ if (n < 0) {
+ char *errstr = getErrstr();
+ logerr(gettext("ERROR: garbled entry"));
+ logerr(gettext("pathname: %s"),
+ (entry.path && *entry.path) ? entry.path :
+ "Unknown");
+ logerr(gettext("problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ exit(99);
+ }
+ if (n == 0)
+ break; /* done with file */
+
+ /*
+ * The class list may not be complete for good reason, so
+ * there's no complaining if this returns an index of -1.
+ */
+ if (cl != NULL)
+ entry.pkg_class_idx = cl_idx(entry.pkg_class);
+
+ if (maptyp && pkginst != NULL) {
+ /*
+ * check to see if the entry we just read
+ * is associated with one of the packages
+ * we have listed on the command line
+ */
+ selected = 0;
+ pinfo = entry.pinfo;
+ while (pinfo) {
+ if (selpkg(pinfo->pkg)) {
+ selected++;
+ break;
+ }
+ pinfo = pinfo->next;
+ }
+ if (!selected)
+ continue; /* not selected */
+ }
+
+ /*
+ * Check to see if the pathname associated with the entry
+ * we just read is associated with the list of paths we
+ * supplied on the command line
+ */
+ if (!selpath(entry.path, pathtype))
+ continue; /* not selected */
+
+ /*
+ * Determine if this is a package object wanting
+ * verification. Metafiles are always checked, otherwise, we
+ * rely on the class to discriminate.
+ */
+ if (entry.ftype != 'i')
+ /* If there's no class list... */
+ if (cl != NULL)
+ /*
+ * ... or this entry isn't in that class list
+ * or it's in a private format, then don't
+ * check it.
+ */
+ if (entry.pkg_class_idx == -1 ||
+ cl_svfy(entry.pkg_class_idx) == NOVERIFY)
+ continue;
+
+ count++;
+ if (ckentry((envfile ? 1 : 0), maptyp, &entry, vfp))
+ errflg++;
+ } while (n != 0);
+
+ (void) vfpClose(&vfp);
+
+ if (maptyp)
+ relslock();
+
+ if (environ) {
+ /* free up environment resources */
+ for (n = 0; environ[n]; n++)
+ free(environ[n]);
+ free(environ);
+ environ = NULL;
+ }
+
+ if (maptyp) {
+ /*
+ * make sure each listed package was associated with
+ * an entry from the prototype or pkgmap
+ */
+ (void) selpkg(NULL);
+ }
+ if (!qflag && !lflag && !Lflag) {
+ /*
+ * make sure each listed pathname was associated with an entry
+ * from the prototype or pkgmap
+ */
+ (void) selpath(NULL, pathtype);
+ }
+ return (errflg);
+}
+
+int
+selpkg(char *p)
+{
+ static char *selected;
+ char buf[80];
+ char *root;
+ register int i;
+
+ if (p == NULL) {
+ if (selected == NULL) {
+ if (pkgcnt) {
+ for (i = 0; i < pkgcnt; ++i) {
+ /* bugid 1227628 */
+ root = get_inst_root();
+ if (root)
+ (void) snprintf(buf,
+ sizeof (buf),
+ "%s/var/sadm/pkg/%s/pkginfo",
+ root, pkg[i]);
+ else
+ (void) snprintf(buf,
+ sizeof (buf),
+ "/var/sadm/pkg/%s/pkginfo",
+ pkg[i]);
+
+ if (access(buf, F_OK))
+ logerr(gettext(WRN_NOPKG),
+ pkg[i]);
+ else
+ logerr(gettext(EMPTY_PKG),
+ pkg[i]);
+ }
+ }
+ } else {
+ for (i = 0; i < pkgcnt; ++i) {
+ if (selected[i] == NULL) {
+ root = get_inst_root();
+ if (root)
+ (void) snprintf(buf,
+ sizeof (buf),
+ "%s/var/sadm/pkg/%s/pkginfo",
+ root, pkg[i]);
+ else
+ (void) snprintf(buf,
+ sizeof (buf),
+ "/var/sadm/pkg/%s/pkginfo",
+ pkg[i]);
+
+ if (access(buf, F_OK))
+ logerr(gettext(WRN_NOPKG),
+ pkg[i]);
+ else
+ logerr(gettext(EMPTY_PKG),
+ pkg[i]);
+ }
+ }
+ }
+ return (0); /* return value not important */
+ } else if (pkgcnt == 0)
+ return (1);
+ else if (selected == NULL) {
+ selected =
+ (char *)calloc((unsigned)(pkgcnt+1), sizeof (char));
+ if (selected == NULL) {
+ progerr(gettext(ERR_NOMEM), errno);
+ exit(99);
+ /*NOTREACHED*/
+ }
+ }
+
+ for (i = 0; i < pkgcnt; ++i) {
+ if (pkgnmchk(p, pkg[i], 0) == 0) {
+ if (selected != NULL)
+ selected[i] = 'b';
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+selpath(char *path, int partial_path)
+{
+ int n;
+
+ if (!npaths)
+ return (1); /* everything is selectable */
+
+ for (n = 0; n < npaths; n++) {
+ if (path == NULL) {
+ if (!used[n])
+ logerr(gettext(WRN_NOPATH),
+ partial_path ? ppathlist[n] :
+ pathlist[n]);
+ } else if (partial_path) {
+ used[n] = 1;
+ return (1);
+ } else if (!shellmatch(pathlist[n], path)) {
+ used[n] = 1;
+ return (1);
+ }
+ }
+ return (0); /* not selected */
+}
+
+static int
+shellmatch(char *spec, char *path)
+{
+ /* Check if the value is NULL */
+ if (spec == NULL || path == NULL)
+ return (1);
+
+ while (*spec && (*spec == *path)) {
+ spec++, path++;
+ }
+ if ((*spec == *path) || (*spec == '*'))
+ return (0);
+ return (1);
+}
+
+static int
+is_partial_path_in_DB(char *srcpath, char *trgtpath)
+{
+ if (strstr(srcpath, trgtpath) == NULL) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkgchk/ckentry.c b/usr/src/cmd/svr4pkg/pkgchk/ckentry.c
new file mode 100644
index 0000000000..ba971c2211
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgchk/ckentry.c
@@ -0,0 +1,314 @@
+/*
+ * 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 2006 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 <memory.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pkglib.h"
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+
+extern int Lflag, lflag, aflag, cflag, fflag, qflag, nflag, xflag, vflag;
+extern char *basedir, *device, pkgspool[];
+
+#define NXTENTRY(P, VFP) \
+ (maptyp ? srchcfile((P), "*", (VFP), (VFP_T *)NULL) :\
+ gpkgmapvfp((P), (VFP)))
+
+#define ERR_SPOOLED "ERROR: unable to locate spooled object <%s>"
+#define MSG_NET_OBJ "It is remote and may be available from the network."
+#define ERR_RMHIDDEN "unable to remove hidden file"
+#define ERR_HIDDEN "ERROR: hidden file in exclusive directory"
+
+static char *findspool(struct cfent *ept);
+static int xdir(int maptyp, VFP_T *vfp, char *dirname);
+
+int
+ckentry(int envflag, int maptyp, struct cfent *ept, VFP_T *vfp)
+{
+ int a_err, c_err,
+ errflg;
+ char *path;
+ char *ir = get_inst_root();
+
+ if (ept->ftype != 'i') {
+ if (envflag)
+ mappath(2, ept->path);
+ if (!device)
+ basepath(ept->path, maptyp ? NULL : basedir, ir);
+ }
+ canonize(ept->path);
+ if (strchr("sl", ept->ftype)) {
+ if (envflag) /* -e option */
+ mappath(2, ept->ainfo.local);
+ if (!RELATIVE(ept->ainfo.local)) { /* Absolute Path */
+ if (!device) {
+ if (ept->ftype == 'l') /* Hard Link */
+ basepath(ept->ainfo.local, NULL, ir);
+ }
+ }
+ if (!RELATIVE(ept->ainfo.local)) /* Absolute Path */
+ canonize(ept->ainfo.local);
+ }
+ if (envflag) {
+ if (!strchr("isl", ept->ftype)) {
+ mapvar(2, ept->ainfo.owner);
+ mapvar(2, ept->ainfo.group);
+ }
+ }
+
+ if (lflag) {
+ tputcfent(ept, stdout);
+ return (0);
+ } else if (Lflag)
+ return (putcfile(ept, stdout));
+
+ errflg = 0;
+ if (device) {
+ if (strchr("dxslcbp", ept->ftype))
+ return (0);
+ if ((path = findspool(ept)) == NULL) {
+ logerr(gettext(ERR_SPOOLED), ept->path);
+ return (-1);
+ }
+
+ /*
+ * If the package file attributes are to be sync'd up with
+ * the pkgmap, we fix the attributes here.
+ */
+ if (fflag) {
+ a_err = 0;
+ /* Clear dangerous bits. */
+ ept->ainfo.mode = (ept->ainfo.mode & S_IAMB);
+ /*
+ * Make sure the file is readable by the world and
+ * writeable by root.
+ */
+ ept->ainfo.mode |= 0644;
+ if (!strchr("in", ept->ftype)) {
+ /* Set the safe attributes. */
+ if (a_err = averify(fflag, &ept->ftype,
+ path, &ept->ainfo)) {
+ errflg++;
+ if (!qflag || (a_err != VE_EXIST)) {
+ logerr(gettext("ERROR: %s"),
+ ept->path);
+ logerr(getErrbufAddr());
+ }
+ if (a_err == VE_EXIST)
+ return (-1);
+ }
+ }
+ }
+ /* Report invalid modtimes by passing cverify a -1 */
+ c_err = cverify((!fflag ? (-1) : fflag), &ept->ftype, path,
+ &ept->cinfo, 1);
+ if (c_err) {
+ logerr(gettext("ERROR: %s"), path);
+ logerr(getErrbufAddr());
+ return (-1);
+ }
+ } else {
+ a_err = 0;
+ if (aflag && !strchr("in", ept->ftype)) {
+ /* validate attributes */
+ if (a_err = averify(fflag, &ept->ftype, ept->path,
+ &ept->ainfo)) {
+ errflg++;
+ if (!qflag || (a_err != VE_EXIST)) {
+ logerr(gettext("ERROR: %s"),
+ ept->path);
+ logerr(getErrbufAddr());
+ if (maptyp && ept->pinfo->status ==
+ SERVED_FILE)
+ logerr(gettext(MSG_NET_OBJ));
+ }
+ if (a_err == VE_EXIST)
+ return (-1);
+ }
+ }
+ if (cflag && strchr("fev", ept->ftype) &&
+ (!nflag || ept->ftype != 'v') && /* bug # 1082144 */
+ (!nflag || ept->ftype != 'e')) {
+ /* validate contents */
+ /* Report invalid modtimes by passing cverify a -1 */
+ if (c_err = cverify((!fflag ? (-1) : fflag),
+ &ept->ftype, ept->path, &ept->cinfo, 1)) {
+ errflg++;
+ if (!qflag || (c_err != VE_EXIST)) {
+ if (!a_err)
+ logerr(gettext("ERROR: %s"),
+ ept->path);
+ logerr(getErrbufAddr());
+ if (maptyp && ept->pinfo->status ==
+ SERVED_FILE)
+ logerr(gettext(MSG_NET_OBJ));
+ }
+ if (c_err == VE_EXIST)
+ return (-1);
+ }
+ }
+ if (xflag && (ept->ftype == 'x')) {
+ /* must do verbose here since ept->path will change */
+ path = strdup(ept->path);
+ if (xdir(maptyp, vfp, path))
+ errflg++;
+ (void) strcpy(ept->path, path);
+ free(path);
+ }
+ }
+ if (vflag)
+ (void) fprintf(stderr, "%s\n", ept->path);
+ return (errflg);
+}
+
+static int
+xdir(int maptyp, VFP_T *vfp, char *dirname)
+{
+ DIR *dirfp;
+ char badpath[PATH_MAX+1];
+ int dirfound;
+ int errflg;
+ int len;
+ int n;
+ struct cfent mine;
+ struct dirent *drp;
+ struct pinfo *pinfo;
+ void *pos;
+
+ pos = vfpGetCurrCharPtr(vfp); /* get current position in file */
+
+ if ((dirfp = opendir(dirname)) == NULL) {
+ progerr(gettext("unable to open directory <%s>"), dirname);
+ return (-1);
+ }
+ len = strlen(dirname);
+
+ errflg = 0;
+ (void) memset((char *)&mine, '\0', sizeof (struct cfent));
+ while ((drp = readdir(dirfp)) != NULL) {
+ if (strcmp(drp->d_name, ".") == NULL ||
+ strcmp(drp->d_name, "..") == NULL)
+ continue;
+ dirfound = 0;
+ while ((n = NXTENTRY(&mine, vfp)) != 0) {
+ if (n < 0) {
+ char *errstr = getErrstr();
+ logerr(gettext("ERROR: garbled entry"));
+ logerr(gettext("pathname: %s"),
+ (mine.path && *mine.path) ? mine.path :
+ "Unknown");
+ logerr(gettext("problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ exit(99);
+ }
+ if (strncmp(mine.path, dirname, len) ||
+ (mine.path[len] != '/'))
+ break;
+ if (strcmp(drp->d_name, &mine.path[len+1]) == NULL) {
+ dirfound++;
+ break;
+ }
+ }
+
+ vfpGetCurrCharPtr(vfp) = pos;
+
+ if (!dirfound) {
+ (void) snprintf(badpath, sizeof (badpath),
+ "%s/%s", dirname, drp->d_name);
+ if (fflag) {
+ if (unlink(badpath)) {
+ errflg++;
+ logerr(gettext("ERROR: %s"), badpath);
+ logerr(gettext(ERR_RMHIDDEN));
+ }
+ } else {
+ errflg++;
+ logerr(gettext("ERROR: %s"), badpath);
+ logerr(gettext(ERR_HIDDEN));
+ }
+ }
+ }
+
+ if (maptyp) {
+ /* clear memory we've used */
+ while ((pinfo = mine.pinfo) != NULL) {
+ mine.pinfo = pinfo->next;
+ free((char *)pinfo);
+ }
+ }
+
+ (void) closedir(dirfp);
+ return (errflg);
+}
+
+static char *
+findspool(struct cfent *ept)
+{
+ static char path[2*PATH_MAX+1];
+ char host[PATH_MAX+1];
+
+ (void) strcpy(host, pkgspool);
+ if (ept->ftype == 'i') {
+ if (strcmp(ept->path, "pkginfo"))
+ (void) strcat(host, "/install");
+ } else if (ept->path[0] == '/') {
+ (void) strcat(host, "/root");
+ } else {
+ (void) strcat(host, "/reloc");
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/%s", host,
+ ept->path + (ept->path[0] == '/'));
+
+ if (access(path, 0) == 0) {
+ return (path);
+ }
+
+ if ((ept->ftype != 'i') && (ept->volno > 0)) {
+ (void) snprintf(path, sizeof (path),
+ "%s.%d/%s", host, ept->volno,
+ ept->path + (ept->path[0] == '/'));
+ if (access(path, 0) == 0) {
+ return (path);
+ }
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgchk/main.c b/usr/src/cmd/svr4pkg/pkgchk/main.c
new file mode 100644
index 0000000000..18ef6bbf7a
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgchk/main.c
@@ -0,0 +1,622 @@
+/*
+ * 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 <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkgtrans.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+#define MAXPATHS 1024
+
+#define MSG_CHK_STRM "Checking uninstalled stream format package " \
+ "<%s> from <%s>\n"
+#define MSG_CHK_DIR "Checking uninstalled directory format package " \
+ "<%s> from <%s>\n"
+#define MSG_NOTROOT "NOTE: \"root\" permission may be required to " \
+ "validate all objects in the client filesystem."
+#define MSG_CONT "Continuing."
+
+#define WRN_F_SPOOL "WARNING: %s is spooled. Ignoring \"f\" argument"
+
+#define ERR_ROOT_SET "Could not set install root from the environment."
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+#define ERR_IOPEN "unable to open input file <%s>"
+#define ERR_IEMPTY "no pathnames in file specified by -i option"
+#define ERR_POPTION "no pathname included with -p option"
+#define ERR_PARTIAL_POPTION "no pathname included with -P option"
+#define ERR_MAXPATHS "too many pathnames in option list (limit is %d)"
+#define ERR_NOTROOT "You must be \"root\" for \"%s -f\" to" \
+ "execute properly."
+#define ERR_SEL_PKG "No packages selected for verification."
+#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_TOO_MANY "too many pathnames in list, limit is %d"
+#define ERR_PATHS_INVALID "Pathnames in %s are not valid."
+#define ERR_MKDIR "unable to make directory <%s>"
+#define ERR_USAGE "usage:\n" \
+ "\t%s [-l|vqacnxf] [-R rootdir] [-p path[, ...] | " \
+ "-P path[, ...]]\n" \
+ "\t\t[-i file] [options]\n" \
+ "\t%s -d device [-f][-l|v] [-p path[, ...] | " \
+ "-P path[, ...]]\n" \
+ "\t\t[-V ...] [-M] [-i file] [-Y category[, ...] | " \
+ "pkginst [...]]\n" \
+ "\twhere options may include ONE of the " \
+ "following:\n " \
+ "\t\t-m pkgmap [-e envfile]\n" \
+ "\t\tpkginst [...]\n" \
+ "\t\t-Y category[, ...]\n"
+
+#define LINK 1
+
+char **pkg = NULL;
+int pkgcnt = 0;
+char *basedir;
+char *pathlist[MAXPATHS], *ppathlist[MAXPATHS], pkgspool[PATH_MAX];
+short used[MAXPATHS];
+short npaths;
+struct cfent **eptlist;
+
+int aflag = (-1);
+int cflag = (-1);
+int vflag = 0;
+int nflag = 0;
+int lflag = 0;
+int Lflag = 0;
+int fflag = 0;
+int xflag = 0;
+int qflag = 0;
+int Rflag = 0;
+int dflag = 0;
+char *device;
+
+char *uniTmp;
+
+static char *mapfile,
+ *spooldir,
+ *tmpdir,
+ *envfile;
+static int errflg = 0;
+static int map_client = 1;
+
+void quit(int);
+static void setpathlist(char *);
+static void usage(void);
+
+extern char **environ;
+extern char *pkgdir;
+
+/* checkmap.c */
+extern int checkmap(int, int, char *, char *, char *, char *, int);
+/* scriptvfy.c */
+extern int checkscripts(char *inst_dir, int silent);
+
+int
+main(int argc, char *argv[])
+{
+ int pkgfmt = 0; /* Makes more sense as a pointer, but */
+ /* 18N is compromised. */
+ char file[PATH_MAX+1],
+ *abi_sym_ptr,
+ *vfstab_file = NULL;
+ char *all_pkgs[4] = {"all", NULL};
+ char **category = NULL;
+ char *catg_arg = NULL;
+ int c;
+ int n = 0;
+ char *prog,
+ *Rvalue,
+ *dvalue;
+ int dbcreate = 0;
+ int pathtype;
+
+ /* initialize locale mechanism */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* determine program name */
+
+ prog = set_prog_name(argv[0]);
+
+ /* establish installation root directory */
+
+ if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+ progerr(gettext(ERR_ROOT_SET));
+ quit(1);
+ }
+
+ /* check if not ABI compliant mode */
+ abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+ if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+ set_nonABI_symlinks();
+ }
+
+ /* bugId 4012147 */
+ if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
+ map_client = 0;
+
+ while ((c = getopt(argc, argv, "Y:R:e:p:d:nLli:vaV:Mm:cqxfQP:?"))
+ != EOF) {
+ switch (c) {
+ case 'p':
+ pathlist[npaths] = strtok(optarg, " , ");
+ if (pathlist[npaths++] == NULL) {
+ progerr(gettext(ERR_POPTION));
+ quit(1);
+ }
+ while (pathlist[npaths] = strtok(NULL, " , ")) {
+ if (npaths++ >= MAXPATHS) {
+ progerr(gettext(ERR_MAXPATHS),
+ MAXPATHS);
+ quit(1);
+ }
+ }
+ break;
+
+ case 'd':
+ dvalue = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ nflag++;
+ break;
+
+ case 'M':
+ map_client = 0;
+ break;
+
+ /*
+ * Allow admin to establish the client filesystem using a
+ * vfstab-like file of stable format.
+ */
+ case 'V':
+ vfstab_file = flex_device(optarg, 2);
+ map_client = 1;
+ break;
+
+ case 'f':
+ if (getuid()) {
+ progerr(gettext(ERR_NOTROOT), prog);
+ quit(1);
+ }
+ fflag++;
+ break;
+
+ case 'i':
+ setpathlist(optarg);
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case 'l':
+ lflag++;
+ break;
+
+ case 'L':
+ Lflag++;
+ break;
+
+ case 'x':
+ if (aflag < 0)
+ aflag = 0;
+ if (cflag < 0)
+ cflag = 0;
+ xflag++;
+ break;
+
+ case 'q':
+ qflag++;
+ break;
+
+ case 'a':
+ if (cflag < 0)
+ cflag = 0;
+ aflag = 1;
+ break;
+
+ case 'c':
+ if (aflag < 0)
+ aflag = 0;
+ cflag = 1;
+ break;
+
+ case 'e':
+ envfile = optarg;
+ break;
+
+ case 'm':
+ mapfile = optarg;
+ break;
+
+ case 'R':
+ Rvalue = optarg;
+ Rflag = 1;
+ break;
+
+ case 'Y':
+ catg_arg = strdup(optarg);
+
+ if ((category = get_categories(catg_arg)) == NULL) {
+ progerr(gettext(ERR_CAT_INV), catg_arg);
+ quit(1);
+ } else if (is_not_valid_length(category)) {
+ progerr(gettext(ERR_CAT_LNGTH));
+ quit(1);
+ }
+ break;
+
+ case 'Q':
+ dbcreate++;
+ break;
+
+ case 'P':
+ ppathlist[npaths] = strtok(optarg, " , ");
+ if ((ppathlist[npaths] == NULL) ||
+ (ppathlist[npaths][0] == '-')) {
+ progerr(gettext(ERR_PARTIAL_POPTION));
+ quit(1);
+ }
+ npaths++;
+ while (ppathlist[npaths] = strtok(NULL, " , ")) {
+ if (npaths++ >= MAXPATHS) {
+ progerr(gettext(ERR_MAXPATHS),
+ MAXPATHS);
+ quit(1);
+ }
+ }
+ break;
+
+ 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);
+ }
+ }
+
+ /* Check for incompatible options */
+ if (dflag && Rflag)
+ usage();
+
+ /* Check for root dir and device dir if set */
+ if (Rflag) {
+ if (!set_inst_root(Rvalue)) {
+ progerr(gettext(ERR_ROOT_CMD));
+ quit(1);
+ }
+ }
+
+ if (dflag)
+ device = flex_device(dvalue, 1);
+
+ if (lflag || Lflag) {
+ /* we're only supposed to list information */
+ if ((cflag >= 0) || (aflag >= 0) ||
+ qflag || xflag || fflag || nflag || vflag)
+ usage();
+ }
+
+ set_PKGpaths(get_inst_root());
+
+ if (catg_arg != NULL && device == NULL) {
+ if (argc - optind) {
+ usage();
+ }
+ pkg = gpkglist(pkgdir, all_pkgs, category);
+ if (pkg == NULL) {
+ progerr(gettext(ERR_CAT_FND), catg_arg);
+ quit(1);
+ } else {
+ for (pkgcnt = 0; pkg[pkgcnt] != NULL; pkgcnt++);
+ }
+ } else if (catg_arg != NULL && optind < argc) {
+ usage();
+ } else {
+ pkg = &argv[optind];
+ pkgcnt = (argc - optind);
+ }
+
+ environ = NULL; /* Sever the parent environment. */
+
+ if (vcfile() == 0) {
+ quit(99);
+ }
+
+ errflg = 0;
+ if (mapfile) {
+ /* check for incompatible options */
+ if (device || pkgcnt)
+ usage();
+ put_path_params(); /* Restore what's needed. */
+
+ /* send pathtype if partial path */
+ pathtype = (ppathlist[0] != NULL) ? 1 : 0;
+ if (checkmap(0, (device != NULL), mapfile, envfile, NULL,
+ NULL, pathtype))
+ errflg++;
+ } else if (device) {
+ /* check for incompatible options */
+ if ((cflag >= 0) || (aflag >= 0))
+ usage();
+ if (qflag || xflag || nflag || envfile)
+ usage();
+ tmpdir = NULL;
+ if ((spooldir = devattr(device, "pathname")) == NULL)
+ spooldir = device;
+ if (isdir(spooldir)) {
+ tmpdir = spooldir = qstrdup(tmpnam(NULL));
+ if (fflag) {
+ logerr(gettext(WRN_F_SPOOL), *pkg);
+ fflag = 0;
+ }
+ if (mkdir(spooldir, 0755)) {
+ progerr(gettext(ERR_MKDIR), spooldir);
+ quit(99);
+ }
+ if (n = pkgtrans(device, spooldir, pkg, PT_SILENT,
+ NULL, NULL))
+ quit(n);
+ if (catg_arg != NULL)
+ pkg = gpkglist(spooldir, all_pkgs, category);
+ else
+ pkg = gpkglist(spooldir, all_pkgs, NULL);
+ pkgfmt = 0;
+ } else {
+ if (catg_arg != NULL)
+ pkg = gpkglist(spooldir,
+ pkgcnt ? pkg : all_pkgs, category);
+ else
+ pkg = gpkglist(spooldir,
+ pkgcnt ? pkg : all_pkgs, NULL);
+ pkgfmt = 1;
+ }
+
+ /*
+ * At this point pkg[] is the list of packages to check. They
+ * are in directory format in spooldir.
+ */
+ if (pkg == NULL) {
+ if (catg_arg != NULL) {
+ progerr(gettext(ERR_CAT_FND), catg_arg);
+ quit(1);
+ } else {
+ progerr(gettext(ERR_SEL_PKG));
+ quit(1);
+ }
+ }
+
+ aflag = 0;
+
+ for (n = 0; pkg[n]; n++) {
+ char locenv[PATH_MAX];
+
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ /* Until 2.9, set it from the execption list */
+ if (exception_pkg(pkg[n], LINK))
+ set_nonABI_symlinks();
+#endif
+
+ if (pkgfmt)
+ (void) printf(
+ gettext(MSG_CHK_DIR), pkg[n], device);
+ else
+ (void) printf(
+ gettext(MSG_CHK_STRM), pkg[n], device);
+
+ (void) snprintf(pkgspool, sizeof (pkgspool),
+ "%s/%s", spooldir, pkg[n]);
+ (void) snprintf(file, sizeof (file),
+ "%s/install", pkgspool);
+ /* Here we check the install scripts. */
+ (void) printf(
+ gettext("## Checking control scripts.\n"));
+ (void) checkscripts(file, 0);
+ /* Verify consistency with the pkgmap. */
+ (void) printf(
+ gettext("## Checking package objects.\n"));
+ (void) snprintf(file, sizeof (file),
+ "%s/pkgmap", pkgspool);
+ (void) snprintf(locenv, sizeof (locenv),
+ "%s/pkginfo", pkgspool);
+ envfile = locenv;
+
+ /*
+ * NOTE : checkmap() frees the environ data and
+ * pointer when it's through with them.
+ */
+ if (checkmap(0, (device != NULL), file, envfile,
+ pkg[n], NULL, 0))
+ errflg++;
+ (void) printf(
+ gettext("## Checking is complete.\n"));
+ }
+ } else {
+ if (envfile)
+ usage();
+
+ put_path_params(); /* Restore what's needed. */
+
+ /*
+ * If this is a check of a client of some sort, we'll need to
+ * mount up the client's filesystems. If the caller isn't
+ * root, this may not be possible.
+ */
+ if (is_an_inst_root()) {
+ if (getuid()) {
+ logerr(gettext(MSG_NOTROOT));
+ logerr(gettext(MSG_CONT));
+ } else {
+ if (get_mntinfo(map_client, vfstab_file))
+ map_client = 0;
+ if (map_client)
+ mount_client();
+ }
+ }
+
+ (void) snprintf(file, sizeof (file),
+ "%s/contents", get_PKGADM());
+ if (ppathlist[0] != NULL) {
+ for (n = 0; ppathlist[n]; n++) {
+ if (checkmap(1, (device != NULL), file, NULL,
+ NULL, ppathlist[n], 1))
+ errflg++;
+ }
+ } else if (pkg[0] != NULL) {
+ if (checkmap(1, (device != NULL), file, NULL,
+ pkg[0], NULL, 0)) {
+ errflg++;
+ }
+ } else {
+ if (checkmap(1, (device != NULL), file, NULL,
+ NULL, NULL, 0)) {
+ errflg++;
+ }
+ }
+
+ if (map_client) {
+ unmount_client();
+ }
+ }
+ quit(errflg ? 1 : 0);
+ /* LINTED: no return */
+}
+
+static void
+setpathlist(char *file)
+{
+ int fd;
+ struct stat st;
+ FILE *fplist;
+ char pathname[PATH_MAX];
+ /*
+ * This trap laid to catch a mismatch between the declaration above and
+ * the hard-coded constant in the fscanf below
+ */
+#if PATH_MAX != 1024
+#error "PATH_MAX changed, so we have a bug to fix"
+#endif
+
+ if (strcmp(file, "-") == 0) {
+ fplist = stdin;
+ } else {
+ if ((fd = open(file, O_RDONLY)) == -1) {
+ progerr(gettext(ERR_IOPEN), file);
+ quit(1);
+ }
+ if (fstat(fd, &st) == -1) {
+ progerr(gettext(ERR_IOPEN), file);
+ quit(1);
+ }
+ if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ progerr(gettext(ERR_PATHS_INVALID), file);
+ quit(1);
+ }
+ if ((fplist = fdopen(fd, "r")) == NULL) {
+ progerr(gettext(ERR_IOPEN), file);
+ quit(1);
+ }
+ }
+ while (fscanf(fplist, "%1024s", pathname) == 1) {
+ if (*pathname == '\0') {
+ progerr(gettext(ERR_PATHS_INVALID), file);
+ quit(1);
+ }
+ pathlist[npaths] = qstrdup(pathname);
+ if (npaths++ > MAXPATHS) {
+ progerr(gettext(ERR_TOO_MANY), MAXPATHS);
+ quit(1);
+ }
+ }
+ if (npaths == 0) {
+ progerr(gettext(ERR_IEMPTY));
+ quit(1);
+ }
+ (void) fclose(fplist);
+}
+
+void
+quit(int n)
+{
+ /* cleanup any temporary directories */
+ (void) chdir("/");
+ if (tmpdir != NULL) {
+ (void) rrmdir(tmpdir);
+ free(tmpdir);
+ tmpdir = NULL;
+ }
+ (void) pkghead(NULL);
+ exit(n);
+ /*NOTREACHED*/
+}
+
+static void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ (void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+ quit(1);
+ /*NOTREACHED*/
+}
diff --git a/usr/src/cmd/svr4pkg/pkgcond/Makefile b/usr/src/cmd/svr4pkg/pkgcond/Makefile
new file mode 100644
index 0000000000..8ca34fc61c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgcond/Makefile
@@ -0,0 +1,41 @@
+#
+# 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= pkgcond
+
+OBJS= main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgcond/main.c b/usr/src/cmd/svr4pkg/pkgcond/main.c
new file mode 100644
index 0000000000..dfa4010ef9
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgcond/main.c
@@ -0,0 +1,4468 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * Program: pkgcond
+ *
+ * Function: Implements the package command suite public utility pkgcond(1M)
+ *
+ * Usage: pkgcond [-nv] [-O debug] condition [ argument ]
+ *
+ * command options:
+ * -n - negate results of condition test
+ * -v - verbose output of condition testing
+ *
+ * <condition> may be any one of:
+ * can_add_driver [path]
+ * can_remove_driver [path]
+ * can_update_driver [path]
+ * is_alternative_root [path]
+ * is_boot_environment [path]
+ * is_diskless_client [path]
+ * is_global_zone [path]
+ * is_mounted_miniroot [path]
+ * is_netinstall_image [path]
+ * is_nonglobal_zone [path]
+ * is_path_writable path
+ * is_running_system [path]
+ * is_sparse_root_nonglobal_zone [path]
+ * is_what [path]
+ * is_whole_root_nonglobal_zone [path]
+ *
+ * <option(s)> are specific to the condition used
+ *
+ * Input: depends on command
+ *
+ * Output: depends on command
+ *
+ * Exit status: If the -n option is not specified:
+ * == 0 - the specified condition is true (or exists).
+ * == 1 - the specified condition is false (or does not exist).
+ * == 2 - command line usage errors (including bad keywords)
+ * == 3 - command failed to perform the test due to a fatal error
+ *
+ * If the -n option is specified:
+ * == 0 - the specified condition is false (or does not exist).
+ * == 1 - the specified condition is true (or exists).
+ * == 2 - command line usage errors (including bad keywords)
+ * == 3 - command failed to perform the test due to a fatal error
+ */
+
+#include <stdio.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <assert.h>
+
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+#include "pkgcond.h"
+#include "pkgcond_msgs.h"
+
+/* Should be defined by cc -D */
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* commands to execute */
+
+#define LS_CMD "/usr/bin/ls"
+
+/*
+ * type definition and "types" for testPath()
+ */
+
+typedef enum {
+ TEST_EXISTS = 0x01,
+ TEST_NOT_EXISTS = 0x02,
+ TEST_IS_DIRECTORY = 0x04,
+ TEST_IS_FILE = 0x08,
+ TEST_NOT_DIRECTORY = 0x10,
+ TEST_NOT_FILE = 0x20,
+ TEST_IS_SYMBOLIC_LINK = 0x40,
+ TEST_NOT_SYMBOLIC_LINK = 0x80,
+ TEST_GLOBAL_TOKEN_IN_FILE = 0x100
+} TEST_TYPES;
+
+/* holds file system info */
+
+struct fsi_t {
+ char *fsi_mntOptions;
+ char *fsi_fsType;
+ char *fsi_mntPoint;
+};
+typedef struct fsi_t FSI_T;
+
+/* holds parsed global data */
+
+struct globalData_t {
+ /* sparse root (are any file systems mounted read-only)? */
+ boolean_t gd_srFsMountedRO;
+ /* initial install: PKG_INIT_INSTALL=true */
+ boolean_t gd_initialInstall;
+ /* global zone install: SUNW_PKG_INSTALL_ZONENAME=global */
+ boolean_t gd_globalZoneInstall;
+ /* non-global zone install: SUNW_PKG_INSTALL_ZONENAME!=global */
+ boolean_t gd_nonglobalZoneInstall;
+ /* non-global zone is in a mounted state */
+ boolean_t inMountedState;
+ /* sorted list of all mounted file systems */
+ FSI_T *gd_fileSystemConfig;
+ /* number of mounted file systems in list */
+ long gd_fileSystemConfigLen;
+ /* current zone name */
+ char *gd_zoneName;
+ /* version of target: PATCH_CLIENT_VERSION */
+ char *gd_patchClientVersion;
+ /* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneName */
+ char *gd_parentZoneName;
+ /* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneType */
+ char *gd_parentZoneType;
+ /* root path to target: PKG_INSTALL_ROOT */
+ char *gd_installRoot;
+ /* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneName */
+ char *gd_currentZoneName;
+ /* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneType */
+ char *gd_currentZoneType;
+ /* list of inherited file systems */
+ char **gd_inheritedFileSystems;
+ /* path provided on command line */
+ char *gd_cmdline_path;
+};
+typedef struct globalData_t GLOBALDATA_T;
+
+/* holds subcommands and their definitions */
+
+struct cmd_t {
+ char *c_name;
+ char *c_args;
+ int (*c_func)(int argc, char **argv, GLOBALDATA_T *a_gdt);
+};
+typedef struct cmd_t CMD_T;
+
+/* Command function prototypes */
+
+static int cmd_can_add_driver(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_can_remove_driver(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_can_update_driver(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_alternative_root(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_boot_environment(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_diskless_client(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_global_zone(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_mounted_miniroot(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_netinstall_image(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_nonglobal_zone(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_path_writable(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_running_system(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_sparse_root_ng_zone(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_what(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+static int cmd_is_whole_root_ng_zone(int argc, char **argv,
+ GLOBALDATA_T *a_gdt);
+
+/* Utility function Prototypes */
+
+static boolean_t getNegateResults(void);
+static boolean_t recursionCheck(int *r_recursion, char *a_function);
+static boolean_t checkForReadOnlyMount(GLOBALDATA_T *a_gdt,
+ char *a_mntPoint, char *a_fsType, char *a_mntOptions);
+static int adjustResults(int a_result);
+static int calculateFileSystemConfig(GLOBALDATA_T *a_gdt);
+static int getRootPath(char **r_rootPath);
+static int getZoneName(char **r_zoneName);
+static int mountOptionPresent(char *a_mntOptions, char *a_opt);
+static int parseGlobalData(char *a_envVar, GLOBALDATA_T **a_gdt);
+static int resolvePath(char **r_path);
+static int setRootPath(char *a_path, char *a_envVar,
+ boolean_t a_mustExist);
+static int testPath(TEST_TYPES a_tt, char *format, ...);
+static int usage(char *a_format, ...);
+static int findToken(char *path, char *token);
+static char *getMountOption(char **p);
+static void dumpGlobalData(GLOBALDATA_T *a_gdt);
+static void removeLeadingWhitespace(char **a_str);
+static void setNegateResults(boolean_t setting);
+static void setVerbose(boolean_t);
+static void sortedInsert(FSI_T **r_list, long *a_listSize,
+ char *a_mntPoint, char *a_fsType, char *a_mntOptions);
+static void setCmdLinePath(char **a_path, char **args,
+ int num_args);
+
+/* local static data */
+
+static boolean_t _negateResults = B_FALSE;
+static char *_rootPath = "/";
+
+/* define subcommand data structure */
+
+static CMD_T cmds[] = {
+ { "can_add_driver", " [path]",
+ cmd_can_add_driver },
+ { "can_remove_driver", " [path]",
+ cmd_can_remove_driver },
+ { "can_update_driver", " [path]",
+ cmd_can_update_driver },
+ { "is_alternative_root", " [path]",
+ cmd_is_alternative_root },
+ { "is_boot_environment", " [path]",
+ cmd_is_boot_environment },
+ { "is_diskless_client", " [path]",
+ cmd_is_diskless_client },
+ { "is_global_zone", " [path]",
+ cmd_is_global_zone },
+ { "is_mounted_miniroot", " [path]",
+ cmd_is_mounted_miniroot },
+ { "is_netinstall_image", " [path]",
+ cmd_is_netinstall_image },
+ { "is_nonglobal_zone", " [path]",
+ cmd_is_nonglobal_zone },
+ { "is_path_writable", " path",
+ cmd_is_path_writable },
+ { "is_running_system", " [path]",
+ cmd_is_running_system },
+ { "is_sparse_root_nonglobal_zone", " [path]",
+ cmd_is_sparse_root_ng_zone },
+ { "is_what", " [path]",
+ cmd_is_what },
+ { "is_whole_root_nonglobal_zone", " [path]",
+ cmd_is_whole_root_ng_zone },
+ /* last one must be all NULLs */
+ { NULL, NULL }
+};
+
+/*
+ * *****************************************************************************
+ * main
+ * *****************************************************************************
+ */
+
+/*
+ * Name: main
+ * Description: main processing loop for pkgcond *
+ * Return: 0 - condition is satisfied (true)
+ * 1 - condition is not satisfied (false)
+ * 2 - command line usage errors
+ * 3 - failure to determine condition
+ */
+
+int
+main(int argc, char **argv)
+{
+ GLOBALDATA_T *gdt = NULL;
+ char **newargv;
+ char *p;
+ int cur_cmd;
+ int i;
+ int newargc;
+
+ /* make standard output non-buffered */
+
+ setbuf(stdout, NULL);
+
+ /* set the default text domain for messaging */
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* remember command name */
+
+ set_prog_name(argv[0]);
+
+ /* tell spmi zones interface how to access package output functions */
+
+ z_set_output_functions(echo, echoDebug, progerr);
+
+ /* set verbose mode if appropriate environment variable is set */
+
+ if (getenv(ENV_VAR_VERBOSE)) {
+ /* same as -v */
+ setVerbose(B_TRUE);
+ }
+
+ /* set debug mode if appropriate environment variable is set */
+
+ if (getenv(ENV_VAR_DEBUG)) {
+ /* same as -O debug */
+
+ /* set sml tracing (sml.c) */
+ smlSetVerbose(B_TRUE);
+
+ /* set log and echo (interactive) message tracing */
+ setVerbose(B_TRUE);
+
+ /* enable echoDebug debugging messages */
+ echoDebugSetFlag(B_TRUE);
+ }
+
+ /* generate usage if no options or arguments specified */
+
+ if (argc <= 1) {
+ (void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
+ return (R_USAGE);
+ }
+
+ /*
+ * process any arguments that can appear before the subcommand
+ */
+
+ while ((i = getopt(argc, argv, ":O:vn?")) != EOF) {
+ switch (i) {
+ /*
+ * Not a public interface: the -O option allows the behavior
+ * of the package tools to be modified. Recognized options:
+ * -> debug
+ * ---> enable debugging output
+ */
+
+ case 'O':
+ for (p = strtok(optarg, ","); p != NULL;
+ p = strtok(NULL, ",")) {
+
+ /* debug - enable all tracing */
+
+ if (strcmp(p, "debug") == 0) {
+ /* set sml tracing */
+ smlSetVerbose(B_TRUE);
+ /* set log/echo tracing */
+ setVerbose(B_TRUE);
+ /* enable debugging messages */
+ echoDebugSetFlag(B_TRUE);
+ continue;
+ }
+
+ progerr(ERR_INVALID_O_OPTION, p);
+ return (adjustResults(R_USAGE));
+ }
+ break;
+
+ /*
+ * Public interface: enable verbose (debug) output.
+ */
+
+ case 'v': /* verbose mode enabled */
+ /* set command tracing only */
+ setVerbose(B_TRUE);
+ break;
+
+ /*
+ * Public interface: negate output results.
+ */
+
+ case 'n':
+ setNegateResults(B_TRUE);
+ break;
+
+ /*
+ * unrecognized option
+ */
+
+ case '?':
+ default:
+ (void) usage(MSG_INVALID_OPTION_SPECIFIED, optopt);
+ return (R_USAGE);
+ }
+ }
+
+ /*
+ * done processing options that can preceed subcommand
+ */
+
+ /* error if no subcommand specified */
+
+ if ((argc-optind) <= 0) {
+ (void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
+ return (R_USAGE);
+ }
+
+ /* parse global data if environment variable set */
+
+ if (parseGlobalData(PKGCOND_GLOBAL_VARIABLE, &gdt) != R_SUCCESS) {
+ log_msg(LOG_MSG_ERR, ERR_CANNOT_USE_GLOBAL_DATA,
+ PKGCOND_GLOBAL_VARIABLE);
+ return (R_ERROR);
+ }
+
+ if (setRootPath(gdt->gd_installRoot,
+ (strcmp(gdt->gd_installRoot, "/") == 0) ? NULL :
+ ENV_VAR_SET, B_TRUE) != R_SUCCESS) {
+ log_msg(LOG_MSG_ERR, ERR_CANNOT_SET_ROOT_PATH,
+ ENV_VAR_PKGROOT);
+ return (R_ERROR);
+ }
+
+ /* set path provided on the command line */
+
+ setCmdLinePath(&(gdt->gd_cmdline_path), argv, argc);
+ echoDebug(DBG_CMDLINE_PATH,
+ gdt->gd_cmdline_path == NULL ? "" : gdt->gd_cmdline_path);
+
+ /* determine how file systems are layered in this zone */
+
+ if (calculateFileSystemConfig(gdt) != R_SUCCESS) {
+ log_msg(LOG_MSG_ERR, ERR_CANNOT_CALC_FS_CONFIG);
+ return (R_ERROR);
+ }
+
+ /* dump global data read in (only if debugging) */
+
+ dumpGlobalData(gdt);
+
+ /* search for specified subcommand and execute if found */
+
+ for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+ if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
+ int result;
+
+ /* make subcommand the first option */
+
+ newargc = argc - optind;
+ newargv = argv + optind;
+ opterr = optind = 1; optopt = 0;
+
+
+ /* call subcommand with its own argc/argv */
+
+ result = cmds[cur_cmd].c_func(newargc, newargv, gdt);
+
+ /* process result code and exit */
+
+ result = adjustResults(result);
+ log_msg(LOG_MSG_DEBUG, DBG_RESULTS, result);
+ return (result);
+ }
+ }
+
+ /* subcommand not found - output error message and exit with error */
+
+ log_msg(LOG_MSG_ERR, ERR_BAD_SUB, argv[optind]);
+ (void) usage(MSG_UNRECOGNIZED_CONDITION_SPECIFIED);
+ return (R_USAGE);
+}
+
+/*
+ * *****************************************************************************
+ * command implementation functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: cmd_is_diskless_client
+ * Description: determine if target is a diskless client
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a zone
+ * - must not be a whole root non-global zone
+ * - must not be a non-global zone
+ * - must not be a mounted mini-root
+ * - must not be a netinstall image
+ * - must not be a boot environment
+ * - The package "SUNWdclnt" must be installed at "/"
+ * - The root path must not be "/"
+ * - The path "/export/exec/Solaris_\*\/usr" must exist at "/"
+ * - The directory "$ROOTDIR/../templates" must exist
+ */
+
+static int
+cmd_is_diskless_client(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ char cmd[MAXPATHLEN+1];
+ int c;
+ int r;
+ int rc;
+static char *cmdName = "is_diskless_client";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /*
+ * a diskless client cannot be any of the following
+ */
+
+ /* cannot be whole root non-global zone */
+
+ r = cmd_is_whole_root_ng_zone(argc, argv, a_gdt);
+
+ /* cannot be nonglobal zone */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+ }
+
+ /* cannot be mounted miniroot */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+ }
+
+ /* cannot be a netinstall image */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_netinstall_image(argc, argv, a_gdt);
+ }
+
+ /* cannot be a boot environment */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_boot_environment(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ /* return failure if any of the preceeding are true */
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* SUNWdclnt must be installed */
+
+ if (pkgTestInstalled("SUNWdclnt", "/") != B_TRUE) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_PKG_NOT_INSTALLED,
+ rootPath, "SUNWdclnt", "/");
+ return (R_FAILURE);
+ }
+
+ /* - $ROOTDIR must not be "/" */
+
+ if (strcmp(rootPath, "/") == 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_ROOTPATH_BAD, rootPath, "/");
+ return (R_FAILURE);
+ }
+
+ /* - zone name must be global */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_BAD, rootPath,
+ GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /*
+ * /export/exec/Solaris_"*"/usr must exist;
+ * create ls command to test:
+ * /usr/bin/ls /export/exec/Solaris_"*"/usr
+ */
+
+ (void) snprintf(cmd, sizeof (cmd), "%s %s >/dev/null 2>&1",
+ LS_CMD, "/export/exec/Solaris_*/usr");
+
+ /* execute command */
+
+ rc = system(cmd);
+
+ /* return error if ls returns something other than "0" */
+
+ if (rc != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_MISSING,
+ rootPath, "/export/exec/Solaris_*/usr");
+ return (R_FAILURE);
+ }
+
+ /*
+ * /usr must be empty on a diskless client:
+ * create ls command to test:
+ * /usr/bin/ls -d1 $ROOTDIR/usr/\*
+ */
+ (void) snprintf(cmd, sizeof (cmd), "%s %s %s/%s >/dev/null 2>&1",
+ LS_CMD, "-1d", rootPath, "usr/*");
+
+ /* execute command */
+
+ rc = system(cmd);
+
+ /* return error if ls returns "0" */
+
+ if (rc == 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_USR_IS_NOT_EMPTY,
+ rootPath);
+ return (R_FAILURE);
+ }
+
+ /* there must be a templates directory at ${ROOTPATH}/../templates */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, "../templates");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_NO_TEMPLATES_PATH,
+ rootPath, rootPath, "../templates");
+ return (R_FAILURE);
+ }
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be diskless client */
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be diskless client */
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* the path is a diskless client */
+
+ log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_IS_DISKLESS_CLIENT, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_global_zone
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a non-global zone
+ * - must not be a non-global zone
+ * - must not be a mounted mini-root
+ * - must not be a netinstall image
+ * - must not be a diskless client
+ * - if $ROOTDIR is "/":
+ * -- if zone name is "GLOBAL", then is a global zone;
+ * -- else not a global zone.
+ * - $ROOTDIR/etc/zones must exist and be a directory
+ * - $ROOTDIR/.tmp_proto must not exist
+ * - $ROOTDIR/var must exist and must not be a symbolic link
+ */
+
+static int
+cmd_is_global_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_global_zone";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /*
+ * a global zone cannot be any of the following
+ */
+
+ /* cannot be a non-global zone */
+
+ r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+ /* cannot be a mounted miniroot */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+ }
+
+ /* cannot be a netinstall image */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_netinstall_image(argc, argv, a_gdt);
+ }
+
+ /* cannot be a diskless client */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ /* return failure if any of the preceeding are true */
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a non-global zone */
+
+ if (a_gdt->gd_nonglobalZoneInstall == B_TRUE) {
+ /* initial nonglobal zone install: no path can be global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_NGZ_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* handle if global zone installation to the install root */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* the path is a global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
+ rootPath);
+
+ return (R_SUCCESS);
+ }
+
+ /* true if current root is "/" and zone name is GLOBAL_ZONENAME */
+
+ if (strcmp(rootPath, "/") == 0) {
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+ /* the path is a global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
+ rootPath);
+
+ return (R_SUCCESS);
+ }
+
+ /* inside a non-global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_ZONENAME_ISNT_GLOBAL,
+ rootPath, a_gdt->gd_zoneName);
+
+ return (R_FAILURE);
+ }
+
+ /*
+ * current root is not "/" - see if target looks like a global zone
+ *
+ * - rootpath is not "/"
+ * - and $ROOTDIR/etc/zones exists
+ * - and $ROOTDIR/.tmp_proto does not exist
+ * - and $ROOTDIR/var is not a symbolic link
+ */
+
+ /* not global zone if /etc/zones does not exist */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, "/etc/zones");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_ISNT_DIRECTORY,
+ rootPath, "/etc/zones");
+ return (R_FAILURE);
+ }
+
+ /* .tmp_proto must not exist */
+
+ r = testPath(TEST_NOT_EXISTS,
+ "%s/%s", rootPath, ".tmp_proto");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_EXISTS,
+ rootPath, "/.tmp_proto");
+ return (R_FAILURE);
+ }
+
+ /* /var must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/var");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_SYMLINK,
+ rootPath, "/var");
+ return (R_FAILURE);
+ }
+
+ /* the path is a global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_netinstall_image
+ * Description: determine if target is a net install image
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a zone
+ * - must not be a global zone
+ * - must not be a mounted mini-root
+ * - zone name must be "global"
+ * - $ROOTDIR/.tmp_proto must exist and must be a directory
+ * - $ROOTDIR/var must exist and must be a symbolic link
+ * - $ROOTDIR/tmp/kernel must exist and must be a directory
+ * - $ROOTDIR/.tmp_proto/kernel must exist and must be a symbolic link
+ */
+
+static int
+cmd_is_netinstall_image(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_netinstall_image";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* a netinstall image cannot be a global zone */
+
+ r = cmd_is_global_zone(argc, argv, a_gdt);
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* current zone name must be "global" */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_BAD_CURRENT_ZONE,
+ rootPath, GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /* cannot be a mounted_miniroot */
+
+ if (cmd_is_mounted_miniroot(argc, argv, a_gdt) == R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT,
+ rootPath);
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/.tmp_proto exists */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, ".tmp_proto");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
+ rootPath, "/.tmp_proto");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/var is a symbolic link */
+
+ r = testPath(TEST_IS_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/var");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
+ rootPath, "/var");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/tmp/kernel does exist */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, "/tmp/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
+ rootPath, "/tmp/kernel");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/.tmp_proto/kernel is a symbolic link */
+
+ r = testPath(TEST_IS_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/.tmp_proto/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
+ rootPath, "/.tmp_proto/kernel");
+ return (R_FAILURE);
+ }
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be netinstall image */
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be netinstall image */
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* target is a netinstall image */
+
+ log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_IS_NETINSTALL_IMAGE, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_mounted_miniroot
+ * Description: determine if target is a mounted miniroot image
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a zone
+ * - zone name must be "global"
+ * - $ROOTDIR/tmp/kernel must exist and must be a symbolic link
+ * - $ROOTDIR/tmp/root/kernel must exist and must be a directory
+ */
+
+static int
+cmd_is_mounted_miniroot(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_mounted_miniroot";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+ recursion--;
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* current zone name must be "global" */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_BAD_CURRENT_ZONE,
+ rootPath, GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/tmp/kernel is a symbolic link */
+
+ r = testPath(TEST_IS_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/tmp/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_SYMLINK,
+ rootPath, "/tmp/kernel");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/tmp/root/kernel is a directory */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, "/tmp/root/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_DIRECTORY,
+ rootPath, "/tmp/root/kernel");
+ return (R_FAILURE);
+ }
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be mounted miniroot */
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be mounted miniroot */
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* target is a mounted miniroot */
+
+ log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_nonglobal_zone
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * - must not be initial installation to the install root
+ * - must not be installation of a global zone
+ * - success if installation of a non-global zone
+ */
+
+static int
+cmd_is_nonglobal_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_nonglobal_zone";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+ recursion--;
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* handle if non-global zone installation to the install root */
+
+ if ((a_gdt->gd_nonglobalZoneInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_INSTALL_ZONENAME_IS_NGZ,
+ rootPath, a_gdt->gd_zoneName);
+ return (R_SUCCESS);
+ }
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be non-global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a global zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial global zone install: no path can be nonglobal zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_GLOBAL_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /*
+ * *********************************************************************
+ * if root directory is "/" then the only thing that needs to be done is
+ * to test the zone name directly - if the zone name is "global" then
+ * the target is not a non-global zone; otherwise if the zone name is
+ * not "global" then the target IS a non-global zone.
+ * *********************************************************************
+ */
+
+ if (strcmp(rootPath, "/") == 0) {
+ /* target is current running root */
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+ /* in the global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
+ rootPath, a_gdt->gd_zoneName);
+ return (R_FAILURE);
+ }
+ /* in a non-global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_IS_NGZ,
+ rootPath, a_gdt->gd_zoneName);
+ return (R_SUCCESS);
+ }
+
+ /*
+ * $ROOTDIR/etc/zones/index must exist in a global zone. It also
+ * exists in a non-global zone after s10u4 but we can't check that
+ * since it is undeterministic for all releases so we only check
+ * for the global zone here.
+ */
+
+ r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/zones/index");
+ if (r == R_SUCCESS) {
+
+ /* See if "global" exists in .../etc/zones/index */
+
+ if (testPath(TEST_GLOBAL_TOKEN_IN_FILE, "%s/%s", rootPath,
+ "/etc/zones/index") != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
+ rootPath, GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+ }
+
+ /*
+ * *********************************************************************
+ * If the root directory is "/" then you can use only the zone
+ * name to determine if the zone is non-global or not since the
+ * package is being installed or removed to the current "zone".
+ *
+ * Since the root directory being tested is not "/" then you have to
+ * look into the target to try and infer zone type using means other
+ * than the zone name only.
+ * *********************************************************************
+ */
+
+ /* reject if any items found that cannot be in a non-global zone */
+
+ /* .tmp_proto must not exist */
+
+ r = testPath(TEST_NOT_EXISTS, "%s/%s", rootPath, ".tmp_proto");
+ if (r != R_SUCCESS) {
+ /* $R/.tmp_proto cannot exist in a non-global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
+ rootPath, "/.tmp_proto");
+ return (R_FAILURE);
+ }
+
+ /* /var must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/var");
+ if (r != R_SUCCESS) {
+ /* $R/var cannot be a symbolic link in a non-global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_DOES_NOT_EXIST,
+ rootPath, "/var");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/tmp/root/kernel must not exist */
+
+ r = testPath(TEST_NOT_EXISTS,
+ "%s/%s", rootPath, "/tmp/root/kernel");
+ if (r != R_SUCCESS) {
+ /* $R/tmp/root/kernel cannot exist in a non-global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
+ rootPath, "/tmp/root/kernel");
+ return (R_FAILURE);
+ }
+
+ /*
+ * *********************************************************************
+ * no items exist in $ROOTDIR that identify something other than
+ * a non-global zone.
+ *
+ * if in global zone no more tests possible: is a non-global zone
+ * *********************************************************************
+ */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+ /* in the global zone */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE,
+ rootPath);
+ return (R_SUCCESS);
+ }
+
+ /*
+ * *********************************************************************
+ * In non-global zone: interrogate zone name and type.
+ *
+ * The parent zone is the zone that the "pkgadd" or "pkgrm" command was
+ * run in. The child zone is the zone that the "pkginstall" or
+ * "pkgremove" command was run in.
+ * *********************************************************************
+ */
+
+ /*
+ * If parent zone name and current zone name defined, and
+ * both zone names are the same, since pkgcond is running
+ * inside of a non-global zone, this is how the scratch
+ * zone is implemented, so target is a non-global zone
+ */
+
+ if ((a_gdt->gd_parentZoneName != NULL) &&
+ (a_gdt->gd_currentZoneName != NULL) &&
+ (strcmp(a_gdt->gd_parentZoneName,
+ a_gdt->gd_currentZoneName) == 0)) {
+ /* parent and current zone name identical: non-gz */
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_PARENT_CHILD_SAMEZONE,
+ rootPath, a_gdt->gd_parentZoneName);
+ return (R_SUCCESS);
+ }
+
+ /*
+ * In non-global zone if inherited FS's exits
+ * or zone specific read only FS's exist
+ * or it is in a mounted state.
+ */
+
+ if (a_gdt->gd_inheritedFileSystems != NULL ||
+ a_gdt->gd_srFsMountedRO || a_gdt->inMountedState) {
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
+ return (R_SUCCESS);
+ }
+
+ /*
+ * the parent and current zone name are not the same;
+ * interrogate the zone types: the parent must be global
+ * and the current must be non-global, which would be set
+ * when a package command is run in the global zone that in
+ * turn runs a package command within the non-global zone.
+ */
+
+ /* if defined, parent zone type must be "global" */
+
+ if ((a_gdt->gd_parentZoneType != NULL) &&
+ (strcmp(a_gdt->gd_parentZoneType, "nonglobal") == 0)) {
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_PARENT_ZONETYPE,
+ rootPath, "nonglobal");
+ return (R_FAILURE);
+ }
+
+ /* if defined, current zone type must be "nonglobal" */
+
+ if ((a_gdt->gd_currentZoneType != NULL) &&
+ (strcmp(a_gdt->gd_currentZoneType, GLOBAL_ZONENAME) == 0)) {
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_CURRENT_ZONETYPE,
+ rootPath, GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /*
+ * *********************************************************************
+ * no other tests possible: target is a non-global zone
+ * *********************************************************************
+ */
+
+ log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_running_system
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a zone
+ * - must not be a diskless client
+ * - $ROOTDIR must be "/"
+ * - zone name must be "global"
+ */
+
+static int
+cmd_is_running_system(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_running_system";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* a running system cannot be a diskless client */
+
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* if root path is "/" then check zone name */
+
+ if (strcmp(rootPath, "/") != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IRST_ROOTPATH_BAD, rootPath, "/");
+ return (R_FAILURE);
+ }
+
+ /* zone name must be global */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_BAD, rootPath,
+ GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /* must not be initial installation to the install root */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ /* initial install: install root cannot be the running system */
+ log_msg(LOG_MSG_DEBUG, DBG_IRST_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be running system */
+ log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* target is a running system */
+
+ log_msg(LOG_MSG_DEBUG, DBG_IRST_PATH_IS_RUNNING_SYSTEM, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_can_add_driver
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_add_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "can_add_driver";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* see if this is the current running system */
+
+ r = cmd_is_running_system(argc, argv, a_gdt);
+
+ /* cannot be a diskless client */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ /* is a running system */
+ return (R_FAILURE);
+ case R_FAILURE:
+ /* not a running syste */
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ /* cannot determine if is a running system */
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* /etc must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/etc");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+ rootPath, "/etc");
+ return (R_FAILURE);
+ }
+
+ /* /platform must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/platform");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+ rootPath, "/platform");
+ return (R_FAILURE);
+ }
+
+ /* /kernel must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+ rootPath, "/kernel");
+ return (R_FAILURE);
+ }
+
+ /* can add a driver */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ADDV_YES, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_can_update_driver
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_update_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "can_update_driver";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* see if this is the current running system */
+
+ r = cmd_is_running_system(argc, argv, a_gdt);
+
+ /* cannot be a diskless client */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ /* is a running system */
+ return (R_FAILURE);
+ case R_FAILURE:
+ /* not a running syste */
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ /* cannot determine if is a running system */
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* /etc must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/etc");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+ rootPath, "/etc");
+ return (R_FAILURE);
+ }
+
+ /* /platform must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/platform");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+ rootPath, "/platform");
+ return (R_FAILURE);
+ }
+
+ /* /kernel must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+ rootPath, "/kernel");
+ return (R_FAILURE);
+ }
+
+ /* can update driver */
+
+ log_msg(LOG_MSG_DEBUG, DBG_UPDV_YES, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_can_remove_driver
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_remove_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "can_remove_driver";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* see if this is the current running system */
+
+ r = cmd_is_running_system(argc, argv, a_gdt);
+
+ /* cannot be a diskless client */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ /* is a running system */
+ return (R_FAILURE);
+ case R_FAILURE:
+ /* not a running syste */
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ /* cannot determine if is a running system */
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* /etc must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/etc");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+ rootPath, "/etc");
+ return (R_FAILURE);
+ }
+
+ /* /platform must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/platform");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+ rootPath, "/platform");
+ return (R_FAILURE);
+ }
+
+ /* /kernel must exist and must not be a symbolic link */
+
+ r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+ "%s/%s", rootPath, "/kernel");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+ rootPath, "/kernel");
+ return (R_FAILURE);
+ }
+
+ /* can remove driver */
+
+ log_msg(LOG_MSG_DEBUG, DBG_RMDV_YES, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_path_writable
+ * Description: determine if target path is writable (not inherited)
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - path must be found in the file systems configured
+ * - file system type must not be "inherited"
+ * - mount options must not include "read only"
+ */
+
+static int
+cmd_is_path_writable(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ FSI_T *list;
+ char *rootPath = NULL;
+ int c;
+ int n;
+ int nn;
+ int r;
+ long listSize;
+ long rootPathLen;
+static char *cmdName = "is_path_writable";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+ recursion--;
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc != 1) {
+ (void) usage(ERR_REQUIRED_ROOTPATH_MISSING, cmdName);
+ return (R_USAGE);
+ }
+
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* search file system conf for this path */
+
+ rootPathLen = strlen(rootPath);
+ list = a_gdt->gd_fileSystemConfig;
+ listSize = a_gdt->gd_fileSystemConfigLen;
+ for (nn = 0, n = 0; n < listSize; n++) {
+ long mplen = strlen(list[n].fsi_mntPoint);
+ if (rootPathLen < mplen) {
+ /* root path is longer than target, ignore */
+ continue;
+ }
+ if (strncmp(rootPath, list[n].fsi_mntPoint, mplen) == 0) {
+ /* remember last partial match */
+ nn = n;
+ }
+ }
+
+ log_msg(LOG_MSG_DEBUG, DBG_PWRT_INFO,
+ rootPath, list[nn].fsi_mntPoint, list[nn].fsi_fsType,
+ list[nn].fsi_mntOptions);
+
+ /*
+ * need to determine if the mount point is writeable:
+ * If parent mount point is "inherited" then it is not writeable
+ */
+
+ if (strcmp(list[nn].fsi_fsType, FSTYPE_INHERITED) == 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_PWRT_INHERITED, rootPath,
+ list[nn].fsi_mntOptions);
+ return (R_FAILURE);
+ }
+
+ /* see if the file system is mounted with the "read only" option */
+
+ r = mountOptionPresent(list[nn].fsi_mntOptions, MNTOPT_RO);
+ if (r == R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_PWRT_READONLY,
+ rootPath, list[nn].fsi_mntOptions);
+ return (R_FAILURE);
+ }
+
+ /* target path is writable */
+
+ log_msg(LOG_MSG_DEBUG, DBG_PWRT_IS, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_alternative_root
+ * Description: determine if target is an alternative root
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * Implementation:
+ * - success if an initial installation to the install root
+ * (an initial install to $PKG_INSTALL_ROOT means that $PKG_INSTALL_ROOT
+ * points to an alternative root that is under construction)
+ * - must not be installation of a zone
+ * - must not be a boot environment
+ * - must not be a diskless client
+ * - must not be a mounted miniroot
+ * - must not be a netinstall image
+ * - must not be a nonglobal zone
+ * - must not be a running system
+ * - $ROOTDIR must not be "/"
+ * - $ROOTDIR/var must exist
+ */
+
+static int
+cmd_is_alternative_root(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_alternative_root";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /*
+ * an alternative root cannot be any of the following
+ */
+
+ /* cannot be a boot_environment */
+
+ r = cmd_is_boot_environment(argc, argv, a_gdt);
+
+ /* cannot be a diskless_client */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+ }
+
+ /* cannot be a mounted_miniroot */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+ }
+
+ /* cannot be a netinstall_image */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_netinstall_image(argc, argv, a_gdt);
+ }
+
+ /* cannot be a nonglobal_zone */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+ }
+
+ /* cannot be a running_system */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_running_system(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ /* return failure if any of the preceeding are true */
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* return success if initial installation */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ log_msg(LOG_MSG_DEBUG, DBG_IALR_INITIAL_INSTALL, rootPath);
+ return (R_SUCCESS);
+ }
+
+ /* root path must not be "/" */
+
+ if (strcmp(rootPath, "/") == 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_IALR_BAD_ROOTPATH, rootPath, "/");
+ return (R_FAILURE);
+ }
+
+ /* /var must exist */
+
+ r = testPath(TEST_EXISTS,
+ "%s/%s", rootPath, "/var");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_IALR_PATH_DOES_NOT_EXIST,
+ rootPath, "/var");
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be alternative root */
+ log_msg(LOG_MSG_DEBUG, DBG_IALR_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* target is an alternative root */
+
+ log_msg(LOG_MSG_DEBUG, DBG_IALR_IS, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_boot_environment
+ * Description: determine if target is an alternative, inactive boot environment
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must not be initial installation to the install root
+ * - must not be installation of a zone
+ * - must not be a diskless client
+ * - must not be a netinstall image
+ * - must not be a mounted miniroot
+ * - $ROOTDIR must not be "/"
+ * - $ROOTDIR/etc/lutab must exist
+ * - $ROOTDIR/etc/lu must exist and must be a directory
+ */
+
+static int
+cmd_is_boot_environment(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_boot_environment";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+ /*
+ * a boot environment cannot be any of the following
+ */
+
+ /* cannot be a diskless client */
+
+ r = cmd_is_diskless_client(argc, argv, a_gdt);
+
+ /* cannot be a netinstall_image */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_netinstall_image(argc, argv, a_gdt);
+ }
+
+ /* cannot be a mounted_miniroot */
+
+ if (r != R_SUCCESS) {
+ r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+ }
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ /* return failure if any of the preceeding are true */
+
+ switch (r) {
+ case R_SUCCESS:
+ return (R_FAILURE);
+ case R_FAILURE:
+ break;
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* root path must not be "/" */
+
+ if (strcmp(rootPath, "/") == 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ROOTPATH, rootPath, "/");
+ return (R_FAILURE);
+ }
+
+ /* zone name must be global */
+
+ if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ZONE, rootPath,
+ GLOBAL_ZONENAME);
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/etc/lutab must exist */
+
+ r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/lutab");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLUTAB, rootPath,
+ "/etc/lutab");
+ return (R_FAILURE);
+ }
+
+ /* $ROOTDIR/etc/lu must exist */
+
+ r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+ "%s/%s", rootPath, "/etc/lu");
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLU, rootPath, "/etc/lu");
+ return (R_FAILURE);
+ }
+
+ /* must not be initial installation */
+
+ if ((a_gdt->gd_initialInstall == B_TRUE) &&
+ (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_INITIAL_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* must not be installation of a zone */
+
+ if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+ (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+ /* initial zone install: no path can be boot environment */
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_ZONE_INSTALL, rootPath);
+ return (R_FAILURE);
+ }
+
+ /* target is a boot environment */
+
+ log_msg(LOG_MSG_DEBUG, DBG_BENV_IS, rootPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_sparse_root_ng_zone
+ * Description: determine if target is a sparse non-global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENATION:
+ * - must be a non-global zone
+ * - inherited file systems must be present, and/or
+ * - read-only lofs file systems must be present
+ */
+
+static int
+cmd_is_sparse_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_sparse_root_nonglobal_zone";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* see if this is a non-global zone */
+
+ r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ /* is a non-global zone */
+ break;
+ case R_FAILURE:
+ /* not a non-global zone */
+ return (R_FAILURE);
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ /* cannot determine if non-global zone */
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /*
+ * in a non-global zone:
+ * if any file systems are inherited, or if /usr is read only,
+ * then the target is a sparse root non-global zone.
+ */
+
+ if ((a_gdt->gd_inheritedFileSystems != (char **)NULL) ||
+ (a_gdt->gd_srFsMountedRO == B_TRUE)) {
+ /* no inherited file systems */
+ log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS, rootPath);
+ return (R_SUCCESS);
+ }
+
+ /* target is not a sparse root non-global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS_NOT, rootPath);
+
+ return (R_FAILURE);
+
+}
+
+/*
+ * Name: cmd_is_what
+ * Description: determine what the target is
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ */
+
+static int
+cmd_is_what(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int cur_cmd;
+ int r;
+static char *cmdName = "is_what";
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /*
+ * construct the command line for all of the packages
+ */
+
+ argc = 0;
+ argv[argc++] = strdup(get_prog_name());
+ argv[argc++] = strdup(rootPath);
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /* search for specified subcommand and execute if found */
+
+ for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+ int result;
+
+ /* do not recursively call this function */
+
+ if (cmds[cur_cmd].c_func == cmd_is_what) {
+ continue;
+ }
+
+ /* call subcommand with its own argc/argv */
+
+ result = cmds[cur_cmd].c_func(argc, argv, a_gdt);
+
+ /* process result code and exit */
+
+ result = adjustResults(result);
+ log_msg(LOG_MSG_INFO, MSG_IS_WHAT_RESULT,
+ cmds[cur_cmd].c_name, result);
+ }
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: cmd_is_whole_root_ng_zone
+ * Description: determine if target is a global zone
+ * Scope: public
+ * Arguments: argc,argv:
+ * - optional path to target to test
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ * IMPLEMENTATION:
+ * - must be a non-global zone
+ * - no inherited file systems may be present
+ * - no read-only lofs file systems may be present
+ */
+
+static int
+cmd_is_whole_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+ char *rootPath = NULL;
+ int c;
+ int r;
+static char *cmdName = "is_whole_root_nonglobal_zone";
+static int recursion = 0;
+
+ /* process any command line options */
+
+ while ((c = getopt(argc, argv, ":")) != EOF) {
+ switch (c) {
+ case '\0': /* prevent end-of-loop not reached warning */
+ break;
+ case '?':
+ default:
+ (void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+ return (R_USAGE);
+ }
+ }
+
+ /* prevent recursion */
+
+ if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+ /* see if this is a non-global zone */
+
+ r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+ /* no need to guard against recursion any more */
+
+ recursion--;
+
+ switch (r) {
+ case R_SUCCESS:
+ /* is a non-global zone */
+ break;
+ case R_FAILURE:
+ /* not a non-global zone */
+ return (R_FAILURE);
+ case R_USAGE:
+ case R_ERROR:
+ default:
+ /* cannot determine if non-global zone */
+ return (r);
+ }
+ }
+
+ /* normalize argc/argv */
+
+ argc -= optind;
+ argv += optind;
+
+ /* error if more than one argument */
+
+ if (argc > 1) {
+ log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+ (void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+ return (R_USAGE);
+ }
+
+ /* process root path if first argument present */
+
+ if (argc == 1) {
+ if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+ return (R_ERROR);
+ }
+ }
+
+ /* get current root path */
+
+ r = getRootPath(&rootPath);
+ if (r != R_SUCCESS) {
+ return (r);
+ }
+
+ /* start of command debugging information */
+
+ echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+ /*
+ * in a non-global zone:
+ * if no file systems are inherited, and if /usr is not
+ * read only, then the target is a whole root non-global zone.
+ */
+
+ if ((a_gdt->gd_inheritedFileSystems == (char **)NULL) &&
+ (a_gdt->gd_srFsMountedRO == B_FALSE)) {
+ /* no inherited file systems */
+ log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS, rootPath);
+ return (R_SUCCESS);
+ }
+
+ /* target is not a whole-root non-global zone */
+
+ log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS_NOT, rootPath);
+
+ return (R_FAILURE);
+}
+
+/*
+ * *****************************************************************************
+ * utility support functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: getMountOption
+ * Description: return next mount option in a string
+ * Arguments: p - pointer to string containing mount options
+ * Output: none
+ * Returns: char * - pointer to next option in string "p"
+ * Side Effects: advances input "p" and inserts \0 in place of the
+ * option separator found.
+ */
+
+static char *
+getMountOption(char **p)
+{
+ char *cp = *p;
+ char *retstr;
+
+ /* advance past all white space */
+
+ while (*cp && isspace(*cp))
+ cp++;
+
+ /* remember start of next option */
+
+ retstr = cp;
+
+ /* advance to end of string or option separator */
+
+ while (*cp && *cp != ',')
+ cp++;
+
+ /* replace separator with '\0' if not at end of string */
+ if (*cp) {
+ *cp = '\0';
+ cp++;
+ }
+
+ /* reset caller's pointer and return pointer to option */
+
+ *p = cp;
+ return (retstr);
+}
+
+/*
+ * Name: mountOptionPresent
+ * Description: determine if specified mount option is present in list
+ * of mount point options
+ * Arguments: a_mntOptions - pointer to string containing list of mount
+ * point options to search
+ * a_opt - pointer to string containing option to search for
+ * Output: none
+ * Returns: R_SUCCESS - option is present in list of mount point options
+ * R_FAILURE - options is not present
+ * R_ERROR - unable to determine if option is present or not
+ */
+
+static int
+mountOptionPresent(char *a_mntOptions, char *a_opt)
+{
+ char tmpopts[MNT_LINE_MAX];
+ char *f, *opts = tmpopts;
+
+ /* return false if no mount options present */
+
+ if ((a_opt == NULL) || (*a_opt == '\0')) {
+ return (R_FAILURE);
+ }
+
+ /* return not present if no list of options to search */
+
+ if (a_mntOptions == NULL) {
+ return (R_FAILURE);
+ }
+
+ /* return not present if list of options to search is empty */
+
+ if (*a_mntOptions == '\0') {
+ return (R_FAILURE);
+ }
+
+ /* make local copy of option list to search */
+
+ (void) strcpy(opts, a_mntOptions);
+
+ /* scan each option looking for the specified option */
+
+ f = getMountOption(&opts);
+ for (; *f; f = getMountOption(&opts)) {
+ /* return success if option matches target */
+ if (strncmp(a_opt, f, strlen(a_opt)) == 0) {
+ return (R_SUCCESS);
+ }
+ }
+
+ /* option not found */
+
+ return (R_FAILURE);
+}
+
+/*
+ * Name: sortedInsert
+ * Description: perform an alphabetical sorted insert into a list
+ * Arguments: r_list - pointer to list to insert next entry into
+ * a_listSize - pointer to current list size
+ * a_mntPoint - mount point to insert (is sort key)
+ * a_fsType - file system type for mount point
+ * a_mntOptions - file syste mount options for mount point
+ * Output: None
+ * Returns: None
+ */
+
+static void
+sortedInsert(FSI_T **r_list, long *a_listSize, char *a_mntPoint,
+ char *a_fsType, char *a_mntOptions)
+{
+ int listSize;
+ FSI_T *list;
+ int n;
+
+ /* entry assertions */
+
+ assert(a_listSize != (long *)NULL);
+ assert(a_mntPoint != NULL);
+ assert(a_fsType != NULL);
+ assert(a_mntOptions != NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_SINS_ENTRY, a_mntPoint, a_fsType, a_mntOptions);
+
+ /* localize references to the list and list size */
+
+ listSize = *a_listSize;
+ list = *r_list;
+
+ /*
+ * if list empty insert this entry as the first one in the list
+ */
+
+ if (listSize == 0) {
+ /* allocate new entry for list */
+ listSize++;
+ list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
+
+ /* first entry is data passed to this function */
+ list[0].fsi_mntPoint = strdup(a_mntPoint);
+ list[0].fsi_fsType = strdup(a_fsType);
+ list[0].fsi_mntOptions = strdup(a_mntOptions);
+
+ /* second entry is all NULL - end of entry marker */
+ list[1].fsi_mntPoint = NULL;
+ list[1].fsi_fsType = NULL;
+ list[1].fsi_mntOptions = NULL;
+
+ /* restore list and list size references to caller */
+ *a_listSize = listSize;
+ *r_list = list;
+
+ return;
+ }
+
+ /*
+ * list not empty - scan looking for largest match
+ */
+
+ for (n = 0; n < listSize; n++) {
+ int c;
+
+ /* compare target with current list entry */
+
+ c = strcmp(list[n].fsi_mntPoint, a_mntPoint);
+
+ if (c == 0) {
+ char *me;
+ long len;
+
+ /* entry already in list -- merge entries */
+
+ len = strlen(list[n].fsi_mntOptions) +
+ strlen(a_mntOptions) + 2;
+ me = (char *)calloc(1, len);
+
+ /* merge two mount options lists into one */
+
+ (void) strlcat(me, list[n].fsi_mntOptions, len);
+ (void) strlcat(me, ",", len);
+ (void) strlcat(me, a_mntOptions, len);
+
+ /* free old list, replace with merged one */
+
+ free(list[n].fsi_mntOptions);
+ list[n].fsi_mntOptions = me;
+
+ echoDebug(DBG_SORTEDINS_SKIPPED,
+ n, list[n].fsi_mntPoint, a_fsType,
+ list[n].fsi_fsType, a_mntOptions,
+ list[n].fsi_mntOptions);
+
+ continue;
+ } else if (c < 0) {
+ /* entry before this one - skip */
+ continue;
+ }
+
+ /*
+ * entry after this one - insert new entry
+ */
+
+ /* allocate one more entry and make space for new entry */
+ listSize++;
+ list = (FSI_T *)realloc(list,
+ sizeof (FSI_T)*(listSize+1));
+ (void) memmove(&(list[n+1]), &(list[n]),
+ sizeof (FSI_T)*(listSize-n));
+
+ /* insert this entry into list */
+ list[n].fsi_mntPoint = strdup(a_mntPoint);
+ list[n].fsi_fsType = strdup(a_fsType);
+ list[n].fsi_mntOptions = strdup(a_mntOptions);
+
+ /* restore list and list size references to caller */
+ *a_listSize = listSize;
+ *r_list = list;
+
+ return;
+ }
+
+ /*
+ * all entries are before this one - append to end of list
+ */
+
+ /* allocate new entry at end of list */
+ listSize++;
+ list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
+
+ /* append this entry to the end of the list */
+ list[listSize-1].fsi_mntPoint = strdup(a_mntPoint);
+ list[listSize-1].fsi_fsType = strdup(a_fsType);
+ list[listSize-1].fsi_mntOptions = strdup(a_mntOptions);
+
+ /* restore list and list size references to caller */
+ *a_listSize = listSize;
+ *r_list = list;
+}
+
+/*
+ * Name: calculateFileSystemConfig
+ * Description: generate sorted list of all mounted file systems
+ * Arguments: a_gdt - global data structure to place sorted entries into
+ * Output: None
+ * Returns: R_SUCCESS - successfully generated mounted file systems list
+ * R_FAILURE - options is not present
+ * R_ERROR - unable to determine if option is present or not
+ */
+
+static int
+calculateFileSystemConfig(GLOBALDATA_T *a_gdt)
+{
+ FILE *fp;
+ struct mnttab mntbuf;
+ FSI_T *list;
+ long listSize;
+ boolean_t readOnlyMountFound = B_FALSE;
+
+ /* entry assetions */
+
+ assert(a_gdt != (GLOBALDATA_T *)NULL);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_CALCSCFG_ENTRY);
+
+ /* allocate a list that has one termination entry */
+
+ list = (FSI_T *)calloc(1, sizeof (FSI_T));
+ list[0].fsi_mntPoint = NULL;
+ list[0].fsi_fsType = NULL;
+ list[0].fsi_mntOptions = NULL;
+ listSize = 0;
+
+ /* insert entries for all inherited file systems */
+
+ if (a_gdt->gd_inheritedFileSystems) {
+ int n;
+ char **ifs = a_gdt->gd_inheritedFileSystems;
+
+ /* debugging info */
+
+ echoDebug(DBG_CALCSCFG_INHERITED);
+
+ /* insert all inherited file systems */
+
+ for (n = 0; ifs[n]; n++) {
+ sortedInsert(&list, &listSize, ifs[n],
+ FSTYPE_INHERITED, MNTOPT_RO);
+ }
+ }
+
+ /* open the mount table for reading */
+
+ fp = fopen(MNTTAB, "r");
+ if (fp == (FILE *)NULL) {
+ return (R_ERROR);
+ }
+
+ /* debugging info */
+
+ echoDebug(DBG_CALCSCFG_MOUNTED);
+
+ /* go through all the specials looking for the device */
+
+ while (getmntent(fp, &mntbuf) == 0) {
+ if (mntbuf.mnt_mountp[0] == '/') {
+ sortedInsert(&list, &listSize,
+ strdup(mntbuf.mnt_mountp),
+ strdup(mntbuf.mnt_fstype),
+ strdup(mntbuf.mnt_mntopts ? mntbuf.mnt_mntopts : ""));
+ }
+
+ /*
+ * Set flag if we are in a non-global zone and it is in
+ * the mounted state.
+ */
+
+ if (strcmp(mntbuf.mnt_mountp, "/a") == 0 &&
+ strcmp(mntbuf.mnt_special, "/a") == 0 &&
+ strcmp(mntbuf.mnt_fstype, "lofs") == 0) {
+ a_gdt->inMountedState = B_TRUE;
+ }
+
+ if (!readOnlyMountFound) {
+ readOnlyMountFound = checkForReadOnlyMount(a_gdt,
+ mntbuf.mnt_mountp, mntbuf.mnt_fstype,
+ mntbuf.mnt_mntopts);
+ }
+ }
+
+ /* close mount table file */
+
+ (void) fclose(fp);
+
+ /* store list pointers in global data structure */
+
+ a_gdt->gd_fileSystemConfig = list;
+ a_gdt->gd_fileSystemConfigLen = listSize;
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: checkForReadOnlyMount
+ * Description: given a mount point, type and options, determine if the
+ * mounted file system is part of a "sparse root" configuration
+ * by checking if the known Zone directories a ro LOFS mounted.
+ * Arguments: a_gdt - global data structure to place sorted entries into
+ * a_mntPoint - pointer to string representing mount point
+ * a_fsType - pointer to string representing file system type
+ * a_mntOptions - pointer to string representing the options
+ * used to mount the file system
+ * Returns: B_TRUE - if sparse root mount is found
+ * B_FLASE - if no sparse root mount's are found
+ * Side Effects: set:
+ * a_gdt->gd_srFsMountedRO = B_TRUE
+ * if the mounted file system is part of a 'sparse root' config
+ */
+
+static boolean_t
+checkForReadOnlyMount(GLOBALDATA_T *a_gdt, char *a_mntPoint,
+ char *a_fsType, char *a_mntOptions)
+{
+ /* entry assertions */
+ int i;
+ char mntPoint[MAXPATHLEN];
+ char *zDirs[] = { "/usr", "/lib", "/platform", "/sbin", NULL };
+ char *aZDirs[] = { "/a/usr", "/a/lib", "/a/platform", "/a/sbin", NULL };
+
+ assert(a_gdt != (GLOBALDATA_T *)NULL);
+ assert(a_mntPoint != NULL);
+ assert(a_fsType != NULL);
+
+ /* return if no read-only mount option */
+
+ if (mountOptionPresent(a_mntOptions, MNTOPT_RO) != R_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ /* return if file system is not read-only mounted */
+
+ if (strcmp(a_fsType, MNTTYPE_LOFS) != 0) {
+ return (B_FALSE);
+ }
+
+ /* file system is a read-only lofs mounted. */
+
+ /* Check read-only lofs mount's appended to the command line path */
+
+ if (a_gdt->gd_cmdline_path != NULL) {
+ if (strncmp(a_mntPoint, a_gdt->gd_cmdline_path,
+ strlen(a_gdt->gd_cmdline_path)) == 0) {
+ for (i = 0; zDirs[i] != NULL; i++) {
+ (void) snprintf(mntPoint, sizeof (mntPoint),
+ "%s%s", a_gdt->gd_cmdline_path,
+ zDirs[i]);
+ if (strcmp(a_mntPoint, mntPoint) == 0) {
+ echoDebug(DBG_CKSR_FSREADONLY,
+ a_mntPoint, a_fsType);
+ a_gdt->gd_srFsMountedRO = B_TRUE;
+ return (B_TRUE);
+ }
+ }
+ }
+
+ /* Check read-only lofs mount's in the mounted state */
+
+ } else if (a_gdt->inMountedState) {
+ for (i = 0; aZDirs[i] != NULL; i++) {
+ if (strncmp(a_mntPoint, aZDirs[i],
+ sizeof (aZDirs[i])) == 0) {
+ echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
+ a_fsType);
+ a_gdt->gd_srFsMountedRO = B_TRUE;
+ return (B_TRUE);
+ }
+ }
+
+ /* Check read-only lofs mount's for live system */
+
+ } else {
+ for (i = 0; zDirs[i] != NULL; i++) {
+ if (strncmp(a_mntPoint, zDirs[i],
+ sizeof (zDirs[i])) == 0) {
+ echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
+ a_fsType);
+ a_gdt->gd_srFsMountedRO = B_TRUE;
+ return (B_TRUE);
+ }
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: adjustResults
+ * Description: adjust output result code before existing
+ * Arguments: a_result - result code to adjust
+ * Returns: int - adjusted result code
+ */
+
+static int
+adjustResults(int a_result)
+{
+ boolean_t negate = getNegateResults();
+ int realResult;
+
+ /* adjust code as appropriate */
+
+ switch (a_result) {
+ case R_SUCCESS: /* condition satisfied */
+ realResult = ((negate == B_TRUE) ? 1 : 0);
+ break;
+ case R_FAILURE: /* condition not satisfied */
+ realResult = ((negate == B_TRUE) ? 0 : 1);
+ break;
+ case R_USAGE: /* usage errors */
+ realResult = 2;
+ break;
+ case R_ERROR: /* condition could not be determined */
+ default:
+ realResult = 3;
+ break;
+ }
+
+ /* debugging output */
+
+ log_msg(LOG_MSG_DEBUG, DBG_ADJUST_RESULTS, a_result, negate,
+ realResult);
+
+ /* return results */
+
+ return (realResult);
+}
+
+/*
+ * Name: setCmdLinePath
+ * Description: set global command line path
+ * Arguments: path - path to set from the command line
+ * args - command line args
+ * num_args - number of command line args
+ * Returns: R_SUCCESS - root path successfully set
+ * R_FAILURE - root path could not be set
+ * R_ERROR - fatal error attempting to set root path
+ */
+
+static void
+setCmdLinePath(char **path, char **args, int num_args)
+{
+ char rp[PATH_MAX] = { '\0' };
+ struct stat statbuf;
+
+ if (*path != NULL) {
+ return;
+ }
+
+ /*
+ * If a path "pkgcond is_global_zone [path]" is provided on the
+ * command line it must be the last argument.
+ */
+
+ if (realpath(args[num_args - 1], rp) != NULL) {
+ if (stat(rp, &statbuf) == 0) {
+ /* make sure the target is a directory */
+ if ((statbuf.st_mode & S_IFDIR)) {
+ *path = strdup(rp);
+ } else {
+ *path = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * Name: setRootPath
+ * Description: set global root path returned by getRootPath
+ * Arguments: a_path - root path to set
+ * a_mustExist - B_TRUE if path must exist (else error)
+ * - B_FALSE if path may not exist
+ * Returns: R_SUCCESS - root path successfully set
+ * R_FAILURE - root path could not be set
+ * R_ERROR - fatal error attempting to set root path
+ */
+
+static int
+setRootPath(char *a_path, char *a_envVar, boolean_t a_mustExist)
+{
+ char rp[PATH_MAX] = { '\0' };
+ struct stat statbuf;
+
+ /* if no data then issue warning and return success */
+
+ if ((a_path == NULL) || (*a_path == '\0')) {
+ echoDebug(DBG_NO_DEFAULT_ROOT_PATH_SET);
+ return (R_SUCCESS);
+ }
+
+ /* path present - resolve to absolute path */
+
+ if (realpath(a_path, rp) == NULL) {
+ if (a_mustExist == B_TRUE) {
+ /* must exist ... error */
+ log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
+ a_path, strerror(errno));
+ return (R_ERROR);
+ } else {
+ /* may not exist - use path as specified */
+ (void) strcpy(rp, a_path);
+ }
+ }
+
+ /* debugging output */
+
+ echoDebug(DBG_DEFAULT_ROOT_PATH_SET, rp, a_envVar ? a_envVar : "");
+
+ /* validate path existence if it must exist */
+
+ if (a_mustExist == B_TRUE) {
+
+ /* get node status */
+
+ if (stat(rp, &statbuf) != 0) {
+ log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
+ rp, strerror(errno));
+ return (R_ERROR);
+ }
+
+ /* make sure the target is a directory */
+
+ if (!(statbuf.st_mode & S_IFDIR)) {
+ log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_NOT_DIR, rp);
+ return (R_ERROR);
+ }
+ }
+
+ /* target exists and is a directory - set */
+
+ echoDebug(DBG_SET_ROOT_PATH_TO, rp);
+
+ /* store copy of resolved root path */
+
+ _rootPath = strdup(rp);
+
+ /* success! */
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: testPath
+ * Description: determine if a path meets the specified conditions
+ * Arguments: a_tt - conditions to test path against
+ * a_format - format to use to generate path
+ * arguments following a_format - as needed for a_format
+ * Returns: R_SUCCESS - the path meets all of the specified conditions
+ * R_FAILURE - the path does not meet all of the conditions
+ * R_ERROR - error attempting to test path
+ */
+
+/*PRINTFLIKE2*/
+static int
+testPath(TEST_TYPES a_tt, char *a_format, ...)
+{
+ char *mbPath; /* copy for the path to be returned */
+ char bfr[1];
+ int r;
+ size_t vres = 0;
+ struct stat statbuf;
+ va_list ap;
+ int fd;
+
+ /* entry assertions */
+
+ assert(a_format != NULL);
+ assert(*a_format != '\0');
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ mbPath = (char *)calloc(1, vres+2);
+ assert(mbPath != NULL);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ vres = vsnprintf(mbPath, vres+1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ echoDebug(DBG_TEST_PATH, mbPath, (unsigned long)a_tt);
+
+ /*
+ * When a path given to open(2) contains symbolic links, the
+ * open system call first resolves all symbolic links and then
+ * opens that final "resolved" path. As a result, it is not
+ * possible to check the result of an fstat(2) against the
+ * file descriptor returned by open(2) for S_IFLNK (a symbolic
+ * link) since all symbolic links are resolved before the
+ * target is opened.
+ *
+ * When testing the target as being (or not being) a symbolic
+ * link, first use lstat(2) against the target to determine
+ * whether or not the specified target itself is (or is not) a
+ * symbolic link.
+ */
+
+ if (a_tt & (TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)) {
+ /*
+ * testing target is/is not a symbolic link; use lstat
+ * to determine the status of the target itself rather
+ * than what the target might finally address.
+ */
+
+ if (lstat(mbPath, &statbuf) != 0) {
+ echoDebug(DBG_CANNOT_LSTAT_PATH, mbPath,
+ strerror(errno));
+ free(mbPath);
+ return (R_FAILURE);
+ }
+
+ /* Is the target required to be a symbolic link? */
+
+ if (a_tt & TEST_IS_SYMBOLIC_LINK) {
+ /* target must be a symbolic link */
+ if (!(statbuf.st_mode & S_IFLNK)) {
+ /* failure: target is not a symbolic link */
+ echoDebug(DBG_IS_NOT_A_SYMLINK, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* success: target is a symbolic link */
+ echoDebug(DBG_SYMLINK_IS, mbPath);
+ }
+
+ /* Is the target required to not be a symbolic link? */
+
+ if (a_tt & TEST_NOT_SYMBOLIC_LINK) {
+ /* target must not be a symbolic link */
+ if (statbuf.st_mode & S_IFLNK) {
+ /* failure: target is a symbolic link */
+ echoDebug(DBG_IS_A_SYMLINK, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* success: target is not a symbolic link */
+ echoDebug(DBG_SYMLINK_NOT, mbPath);
+ }
+
+ /*
+ * if only testing is/is not a symbolic link, then
+ * no need to open the target: return success.
+ */
+
+ if (!(a_tt &
+ (~(TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)))) {
+ free(mbPath);
+ return (R_SUCCESS);
+ }
+ }
+
+ /* resolve path and remove any whitespace */
+
+ r = resolvePath(&mbPath);
+ if (r != R_SUCCESS) {
+ echoDebug(DBG_TEST_PATH_NO_RESOLVE, mbPath);
+ free(mbPath);
+ if (a_tt & TEST_NOT_EXISTS) {
+ return (R_SUCCESS);
+ }
+ return (r);
+ }
+
+ echoDebug(DBG_TEST_PATH_RESOLVE, mbPath);
+
+ /* open the file - this is the basic existence test */
+
+ fd = open(mbPath, O_RDONLY|O_LARGEFILE, 0);
+
+ /* existence test failed if file cannot be opened */
+
+ if (fd < 0) {
+ /*
+ * target could not be opened - if testing for non-existence,
+ * return success, otherwise return failure
+ */
+ if (a_tt & TEST_NOT_EXISTS) {
+ echoDebug(DBG_CANNOT_ACCESS_PATH_OK, mbPath);
+ free(mbPath);
+ return (R_SUCCESS);
+ }
+
+ echoDebug(DBG_CANNOT_ACCESS_PATH_BUT_SHOULD,
+ mbPath, strerror(errno));
+ free(mbPath);
+
+ return (R_FAILURE);
+ }
+
+ /*
+ * target successfully opened - if testing for non-existence,
+ * return failure, otherwise continue with specified tests
+ */
+
+ if (a_tt & TEST_NOT_EXISTS) {
+ /* testing for non-existence: return failure */
+ echoDebug(DBG_TEST_EXISTS_SHOULD_NOT, mbPath);
+ free(mbPath);
+ (void) close(fd);
+ return (R_FAILURE);
+ }
+
+ /* get the file status */
+
+ r = fstat(fd, &statbuf);
+ if (r != 0) {
+ echoDebug(DBG_PATH_DOES_NOT_EXIST, mbPath, strerror(errno));
+ (void) close(fd);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+
+ /* required to be a directory? */
+
+ if (a_tt & TEST_IS_DIRECTORY) {
+ if (!(statbuf.st_mode & S_IFDIR)) {
+ /* is not a directory */
+ echoDebug(DBG_IS_NOT_A_DIRECTORY, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* a directory */
+ echoDebug(DBG_DIRECTORY_IS, mbPath);
+ }
+
+ /* required to not be a directory? */
+
+ if (a_tt & TEST_NOT_DIRECTORY) {
+ if (statbuf.st_mode & S_IFDIR) {
+ /* is a directory */
+ echoDebug(DBG_IS_A_DIRECTORY, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* not a directory */
+ echoDebug(DBG_DIRECTORY_NOT, mbPath);
+ }
+
+ /* required to be a file? */
+
+ if (a_tt & TEST_IS_FILE) {
+ if (!(statbuf.st_mode & S_IFREG)) {
+ /* is not a regular file */
+ echoDebug(DBG_IS_NOT_A_FILE, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* a regular file */
+ echoDebug(DBG_FILE_IS, mbPath);
+ }
+
+ /* required to not be a file? */
+
+ if (a_tt & TEST_NOT_FILE) {
+ if (statbuf.st_mode & S_IFREG) {
+ /* is a regular file */
+ echoDebug(DBG_IS_A_FILE, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* not a regular file */
+ echoDebug(DBG_FILE_NOT, mbPath);
+ }
+
+ /*
+ * Find token (global) in file pointed to by mbPath.
+ * token is only compared to first word in mbPath.
+ */
+
+ if (a_tt & TEST_GLOBAL_TOKEN_IN_FILE) {
+ if (!(statbuf.st_mode & S_IFREG)) {
+ /* is not a regular file */
+ echoDebug(DBG_IS_NOT_A_FILE, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ /* If global exists then we're not in a non-global zone */
+ if (findToken(mbPath, GLOBAL_ZONENAME) == R_SUCCESS) {
+ echoDebug(DBG_TOKEN__EXISTS, GLOBAL_ZONENAME, mbPath);
+ free(mbPath);
+ return (R_FAILURE);
+ }
+ }
+
+ (void) close(fd);
+
+ /* success! */
+
+ echoDebug(DBG_TESTPATH_OK, mbPath);
+
+ /* free up temp storage used to hold path to test */
+
+ free(mbPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: findToken
+ * Description: Find first token in file.
+ * Arguments:
+ * path - file to search for token
+ * token - string to search for
+ * Returns:
+ * R_SUCCESS - the token exists
+ * R_FAILURE - the token does not exist
+ * R_ERROR - fatal error attempting to find token
+ */
+
+static int
+findToken(char *path, char *token)
+{
+ FILE *fp;
+ char *cp;
+ char line[MAXPATHLEN];
+
+ if (path == NULL || token == NULL) {
+ return (R_ERROR);
+ }
+ if ((fp = fopen(path, "r")) == NULL) {
+ return (R_ERROR);
+ }
+
+ while (fgets(line, sizeof (line), fp) != NULL) {
+ for (cp = line; *cp && isspace(*cp); cp++);
+ /* skip comments */
+ if (*cp == '#') {
+ continue;
+ }
+ if (pkgstrContainsToken(cp, token, ":")) {
+ (void) fclose(fp);
+ return (R_SUCCESS);
+ }
+ }
+ (void) fclose(fp);
+ return (R_FAILURE);
+}
+
+
+/*
+ * Name: resolvePath
+ * Description: fully resolve a path to an absolute real path
+ * Arguments: r_path - pointer to pointer to malloc()ed storage containing
+ * the path to resolve - this path may be reallocated
+ * as necessary to hold the fully resolved path
+ * Output: r_path - is realloc()ed as necessary
+ * Returns: R_SUCCESS - the path is fully resolved
+ * R_FAILURE - the path could not be resolved
+ * R_ERROR - fatal error attempting to resolve path
+ */
+
+static int
+resolvePath(char **r_path)
+{
+ int i;
+ char resolvedPath[MAXPATHLEN+1] = {'\0'};
+ size_t mbPathlen; /* length of multi-byte path */
+ size_t wcPathlen; /* length of wide-character path */
+ wchar_t *wcPath; /* wide-character version of the path */
+ wchar_t *wptr; /* scratch pointer */
+
+ /* entry assertions */
+
+ assert(r_path != (char **)NULL);
+
+ /* return error if the path is completely empty */
+
+ if (*r_path == '\0') {
+ return (R_FAILURE);
+ }
+
+ /* remove all leading whitespace */
+
+ removeLeadingWhitespace(r_path);
+
+ /*
+ * convert to real path: an absolute pathname that names the same file,
+ * whose resolution does not involve ".", "..", or symbolic links.
+ */
+
+ if (realpath(*r_path, resolvedPath) != NULL) {
+ free(*r_path);
+ *r_path = strdup(resolvedPath);
+ }
+
+ /*
+ * convert the multi-byte version of the path to a
+ * wide-character rendering, for doing our figuring.
+ */
+
+ mbPathlen = strlen(*r_path);
+
+ if ((wcPath = (wchar_t *)
+ calloc(1, sizeof (wchar_t)*(mbPathlen+1))) == NULL) {
+ return (R_FAILURE);
+ }
+
+ /*LINTED*/
+ if ((wcPathlen = mbstowcs(wcPath, *r_path, mbPathlen)) == -1) {
+ free(wcPath);
+ return (R_FAILURE);
+ }
+
+ /*
+ * remove duplicate slashes first ("//../" -> "/")
+ */
+
+ for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
+ *wptr++ = wcPath[i];
+
+ if (wcPath[i] == '/') {
+ i++;
+
+ while (wcPath[i] == '/') {
+ i++;
+ }
+
+ i--;
+ }
+ }
+
+ *wptr = '\0';
+
+ /*
+ * now convert back to the multi-byte format.
+ */
+
+ /*LINTED*/
+ if (wcstombs(*r_path, wcPath, mbPathlen) == -1) {
+ free(wcPath);
+ return (R_FAILURE);
+ }
+
+ /* at this point have a path */
+
+ /* free up temporary storage */
+
+ free(wcPath);
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: removeLeadingWhitespace
+ * Synopsis: Remove leading whitespace from string
+ * Description: Remove all leading whitespace characters from a string
+ * Arguments: a_str - [RO, *RW] - (char **)
+ * Pointer to handle to string (in allocated storage) to
+ * remove all leading whitespace from
+ * Returns: void
+ * The input string is modified as follows:
+ * == NULL:
+ * - input string was NULL
+ * - input string is all whitespace
+ * != NULL:
+ * - copy of input string with leading
+ * whitespace removed
+ * CAUTION: The input string must be allocated space (via malloc() or
+ * strdup()) - it must not be a static or inline character string
+ * NOTE: The input string a_str will be freed with 'free'
+ * if it is all whitespace, or if it contains any leading
+ * whitespace characters
+ * 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.
+ * Errors: If the string cannot be created, the process exits
+ */
+
+static void
+removeLeadingWhitespace(char **a_str)
+{
+ char *o_str;
+
+ /* entry assertions */
+
+ assert(a_str != (char **)NULL);
+
+ /* if string is null, just return */
+
+ if (*a_str == NULL) {
+ return;
+ }
+ o_str = *a_str;
+
+ /* if string is empty, deallocate and return NULL */
+
+ if (*o_str == '\0') {
+ /* free string */
+ free(*a_str);
+ *a_str = NULL;
+ return;
+ }
+
+ /* if first character is not a space, just return */
+
+ if (!isspace(*o_str)) {
+ return;
+ }
+
+ /* advance past all space characters */
+
+ while ((*o_str != '\0') && (isspace(*o_str))) {
+ o_str++;
+ }
+
+ /* if string was all space characters, deallocate and return NULL */
+
+ if (*o_str == '\0') {
+ /* free string */
+ free(*a_str);
+ *a_str = NULL;
+ return;
+ }
+
+ /* have non-space/null byte, return dup, deallocate original */
+
+ o_str = strdup(o_str);
+ free(*a_str);
+ *a_str = o_str;
+}
+
+/*
+ * Name: getZoneName
+ * Description: get the name of the zone this process is running in
+ * Arguments: r_zoneName - pointer to pointer to receive zone name
+ * Output: r_zoneName - a pointer to malloc()ed storage containing
+ * the zone name this process is running in is stored
+ * in the location pointed to by r_zoneName
+ * Returns: R_SUCCESS - the zone name is successfully returned
+ * R_FAILURE - the zone name is not successfully returned
+ * R_ERROR - error attempting to get the zone name
+ */
+
+static int
+getZoneName(char **r_zoneName)
+{
+static char zoneName[ZONENAME_MAX] = { '\0' };
+
+ /* if zone name not already present, retrieve and cache name */
+
+ if (zoneName[0] == '\0') {
+ if (getzonenamebyid(getzoneid(), zoneName,
+ sizeof (zoneName)) < 0) {
+ log_msg(LOG_MSG_ERR, ERR_CANNOT_GET_ZONENAME);
+ return (R_ERROR);
+ }
+ }
+
+ /* return cached zone name */
+
+ *r_zoneName = zoneName;
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: getRootPath
+ * Description: get the root path being tested by this process
+ * Arguments: r_rootPath - pointer to pointer to receive root path
+ * Output: r_rootPath - a pointer to malloc()ed storage containing
+ * the root path name this process is testing
+ * Returns: R_SUCCESS - the root path is successfully returned
+ * R_FAILURE - the root path is not successfully returned
+ * R_ERROR - error attempting to get the root path
+ */
+
+static int
+getRootPath(char **r_rootPath)
+{
+ *r_rootPath = _rootPath;
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: setVerbose
+ * Description: Turns on verbose output
+ * Scope: public
+ * Arguments: verbose = B_TRUE indicates verbose mode
+ * Returns: none
+ */
+
+static void
+setVerbose(boolean_t setting)
+{
+ /* set log verbose messages */
+
+ log_set_verbose(setting);
+
+ /* set interactive messages */
+
+ echoSetFlag(setting);
+}
+
+/*
+ * Name: negate_results
+ * Description: control negation of results
+ * Scope: public
+ * Arguments: setting
+ * == B_TRUE indicates negated results mode
+ * == B_FALSE indicates non-negated results mode
+ * Returns: none
+ */
+
+static void
+setNegateResults(boolean_t setting)
+{
+ log_msg(LOG_MSG_DEBUG, DBG_SET_NEGATE_RESULTS,
+ _negateResults, setting);
+
+ _negateResults = setting;
+}
+
+/*
+ * Name: getNegateResults
+ * Description: Returns whether or not to results are negated
+ * Scope: public
+ * Arguments: none
+ * Returns: B_TRUE - results are negated
+ * B_FALSE - results are not negated
+ */
+
+static boolean_t
+getNegateResults(void)
+{
+ return (_negateResults);
+}
+
+/*
+ * Name: usage
+ * Description: output usage string
+ * Arguments: a_format - format to use to generate message
+ * arguments following a_format - as needed for a_format
+ * Output: Outputs the usage string to stderr.
+ * Returns: R_ERROR
+ */
+
+static int
+usage(char *a_format, ...)
+{
+ int cur_cmd;
+ char cmdlst[LINE_MAX+1] = { '\0' };
+ char *message;
+ char bfr[1];
+ char *p = get_prog_name();
+ size_t vres = 0;
+ va_list ap;
+
+ /* entry assertions */
+
+ assert(a_format != NULL);
+ assert(*a_format != '\0');
+
+ /* determine size of the message in bytes */
+
+ va_start(ap, a_format);
+ /* LINTED warning: variable format specifier to vsnprintf(); */
+ vres = vsnprintf(bfr, 1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* allocate storage to hold the message */
+
+ message = (char *)calloc(1, vres+2);
+ assert(message != NULL);
+
+ /* generate the results of the printf conversion */
+
+ va_start(ap, a_format);
+ /* LINTED warning: variable format specifier to vsnprintf(); */
+ vres = vsnprintf(message, vres+1, a_format, ap);
+ va_end(ap);
+
+ assert(vres > 0);
+
+ /* generate list of all defined conditions */
+
+ for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+ (void) strlcat(cmdlst, "\t", sizeof (cmdlst));
+ (void) strlcat(cmdlst, cmds[cur_cmd].c_name, sizeof (cmdlst));
+ if (cmds[cur_cmd].c_args != NULL) {
+ (void) strlcat(cmdlst, cmds[cur_cmd].c_args,
+ sizeof (cmdlst));
+ }
+ (void) strlcat(cmdlst, "\n", sizeof (cmdlst));
+ }
+
+ /* output usage with conditions */
+
+ log_msg(LOG_MSG_INFO, MSG_USAGE, message, p ? p : "pkgcond", cmdlst);
+
+ return (R_ERROR);
+}
+
+/*
+ * Name: parseGlobalData
+ * Description: parse environment global data and store in global data structure
+ * Arguments: a_envVar - pointer to string representing the name of the
+ * environment variable to get and parse
+ * r_gdt - pointer to pointer to global data structure to fill in
+ * using the parsed data from a_envVar
+ * Output: none
+ * Returns: R_SUCCESS - the global data is successfully parsed
+ * R_FAILURE - problem parsing global data
+ * R_ERROR - fatal error attempting to parse global data
+ */
+
+static int
+parseGlobalData(char *a_envVar, GLOBALDATA_T **r_gdt)
+{
+ int r;
+ int n;
+ char *a;
+ SML_TAG *tag;
+ SML_TAG *ntag;
+
+ assert(r_gdt != (GLOBALDATA_T **)NULL);
+
+ /*
+ * allocate space for global data structure if needed
+ */
+
+ if (*r_gdt == (GLOBALDATA_T *)NULL) {
+ *r_gdt = (GLOBALDATA_T *)calloc(1, sizeof (GLOBALDATA_T));
+ }
+
+ /*
+ * get initial installation indication:
+ * If the initial install variable is set to "true", then an initial
+ * installation of Solaris is underway. When this condition is true:
+ * - if the path being checked is the package install root, then
+ * the path is considered to be an 'alternative root' which is
+ * currently being installed.
+ * - if the path being checked is not the package install root, then
+ * the path needs to be further analyzed to determine what it may
+ * be referring to.
+ */
+
+ a = getenv(ENV_VAR_INITIAL_INSTALL);
+ if ((a != NULL) && (strcasecmp(a, "true") == 0)) {
+ (*r_gdt)->gd_initialInstall = B_TRUE;
+ }
+
+ /* get current zone name */
+
+ r = getZoneName(&(*r_gdt)->gd_zoneName);
+ if (r != R_SUCCESS) {
+ (*r_gdt)->gd_zoneName = "";
+ }
+
+ /*
+ * get zone installation status:
+ * - If the package install zone name is not set, then an installation
+ * of a global zone, or of a non-global zone, is not underway.
+ * - If the package install zone name is set to "global", then an
+ * installation of a global zone is underway. In this case, no path
+ * can be a netinstall image, diskless client, mounted miniroot,
+ * non-global zone, the current running system, alternative root,
+ * or alternative boot environment.
+ * - If the package install zone name is set to a value other than
+ * "global", then an installation of a non-global zone with that name
+ * is underway. In this case, no path can be a netinstall image,
+ * diskless client, mounted miniroot, global zone, the current
+ * running system, alternative root, or alternative boot environment.
+ */
+
+ a = getenv(ENV_VAR_PKGZONENAME);
+ if ((a == NULL) || (*a == '\0')) {
+ /* not installing a zone */
+ (*r_gdt)->gd_globalZoneInstall = B_FALSE;
+ (*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
+ } else if (strcmp(a, GLOBAL_ZONENAME) == 0) {
+ /* installing a global zone */
+ (*r_gdt)->gd_globalZoneInstall = B_TRUE;
+ (*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
+ (*r_gdt)->gd_zoneName = a;
+ } else {
+ /* installing a non-global zone by that name */
+ (*r_gdt)->gd_globalZoneInstall = B_FALSE;
+ (*r_gdt)->gd_nonglobalZoneInstall = B_TRUE;
+ (*r_gdt)->gd_zoneName = a;
+ }
+
+ /*
+ * get package install root. If it doesn't exist check for
+ * patch install root (ROOTDIR)
+ */
+
+ a = getenv(ENV_VAR_PKGROOT);
+ if ((a != NULL) && (*a != '\0')) {
+ (*r_gdt)->gd_installRoot = a;
+ } else {
+ a = getenv(ENV_VAR_PATCHROOT);
+ if ((a != NULL) && (*a != '\0')) {
+ (*r_gdt)->gd_installRoot = a;
+ } else {
+ (*r_gdt)->gd_installRoot = "/";
+ }
+ }
+
+ /*
+ * get patch client version: always set if $ROOTDIR != / and
+ * the $ROOTDIR/var/sadm/softinfo/INST_RELEASE file exists.
+ */
+
+ a = getenv(ENV_VAR_PATCH_CLIENTVER);
+ (*r_gdt)->gd_patchClientVersion = (a ? a : "");
+
+ /* get the global data environment variable */
+
+ a = getenv(a_envVar);
+
+ /* if no data then issue warning and return success */
+
+ if ((a == NULL) || (*a_envVar == '\0')) {
+ log_msg(LOG_MSG_DEBUG, DBG_NO_GLOBAL_DATA_AVAILABLE, a_envVar);
+ return (R_SUCCESS);
+ }
+
+ /* data present - parse into SML structure */
+
+ log_msg(LOG_MSG_DEBUG, DBG_PARSE_GLOBAL, a);
+
+ r = smlConvertStringToTag(&tag, a);
+ if (r != R_SUCCESS) {
+ log_msg(LOG_MSG_ERR, ERR_CANNOT_PARSE_GLOBAL_DATA, a);
+ return (R_FAILURE);
+ }
+
+ smlDbgPrintTag(tag, DBG_PARSED_ENVIRONMENT, a_envVar);
+
+ /* fill in global data structure */
+
+ /* find the environment condition information structure */
+
+ ntag = smlGetTagByName(tag, 0, TAG_COND_TOPLEVEL);
+ if (ntag == SML_TAG__NULL) {
+ log_msg(LOG_MSG_WRN, WRN_PARSED_DATA_MISSING,
+ TAG_COND_TOPLEVEL);
+ return (R_FAILURE);
+ }
+
+ /*
+ * data found - extract what we know about
+ */
+
+ /* parent zone name */
+
+ a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_NAME);
+ (*r_gdt)->gd_parentZoneName = a;
+
+ /* parent zone type */
+
+ a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_TYPE);
+ (*r_gdt)->gd_parentZoneType = a;
+
+ /* current zone name */
+
+ a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
+ TAG_COND_ZONE_NAME);
+ (*r_gdt)->gd_currentZoneName = a;
+
+ /* current zone type */
+
+ a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
+ TAG_COND_ZONE_TYPE);
+ (*r_gdt)->gd_currentZoneType = a;
+
+ /* extract any inherited file systems */
+
+ for (n = 0; ; n++) {
+ char **ifs;
+
+ a = smlGetParamByTag(ntag, n, TAG_COND_INHERITED_FS,
+ TAG_COND_FS_NAME);
+
+ if (a == NULL) {
+ if (n > 0) {
+ /* LINTED warning: variable may be used */
+ (*r_gdt)->gd_inheritedFileSystems = ifs;
+ }
+ break;
+ }
+
+ if (n == 0) {
+ ifs = (char **)calloc(1, sizeof (char **)*(n+2));
+ ifs[n] = a;
+ ifs[n+1] = NULL;
+ } else {
+ ifs = (char **)realloc(ifs, sizeof (char **)*(n+2));
+ ifs[n] = a;
+ ifs[n+1] = NULL;
+ }
+ }
+
+ return (R_SUCCESS);
+}
+
+/*
+ * Name: dumpGlobalData
+ * Description: dump global data structure using echoDebug
+ * Arguments: a_gdt - pointer to global data structure to dump
+ * Outputs: echoDebug is called to output global data strucutre information
+ * Returns: void
+ */
+
+static void
+dumpGlobalData(GLOBALDATA_T *a_gdt)
+{
+ /* entry assertions */
+
+ assert(a_gdt != (GLOBALDATA_T *)NULL);
+
+ /* debugging enabled, dump the global data structure */
+
+ echoDebug(DBG_DUMP_GLOBAL_ENTRY);
+ echoDebug(DBG_DUMP_GLOBAL_PARENT_ZONE,
+ a_gdt->gd_parentZoneName ? a_gdt->gd_parentZoneName : "",
+ a_gdt->gd_parentZoneType ? a_gdt->gd_parentZoneType : "");
+ echoDebug(DBG_DUMP_GLOBAL_CURRENT_ZONE,
+ a_gdt->gd_currentZoneName ? a_gdt->gd_currentZoneName : "",
+ a_gdt->gd_currentZoneType ? a_gdt->gd_currentZoneType : "");
+
+ if (a_gdt->gd_inheritedFileSystems) {
+ int n;
+ char **ifs = a_gdt->gd_inheritedFileSystems;
+ for (n = 0; ifs[n]; n++) {
+ echoDebug(DBG_DUMP_GLOBAL_LINE, n, ifs[n]);
+ }
+ }
+}
+
+/*
+ * Name: recursionCheck
+ * Description: prevent recursive calling of functions
+ * Arguments: r_recursion - pointer to int recursion counter
+ * a_function - pointer to name of function
+ * Returns: B_TRUE - function is recursively called
+ * B_FALSE - function not recursively called
+ */
+
+static boolean_t
+recursionCheck(int *r_recursion, char *a_function)
+{
+ /* prevent recursion */
+
+ (*r_recursion)++;
+ if (*r_recursion > 1) {
+ echoDebug(DBG_RECURSION, a_function, *r_recursion);
+ (*r_recursion)--;
+ return (B_TRUE);
+ }
+
+ echoDebug(DBG_NO_RECURSION, a_function);
+ return (B_FALSE);
+}
+
+/*
+ * 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()>>
+ * NOTE: This is needed because libinst functions can call "quit(99)"
+ * to force an error exit.
+ */
+
+void
+quit(int a_retcode)
+{
+ /* process return code if not quit(99) */
+
+ if (a_retcode == 99) {
+ exit(0x7f); /* processing error (127) */
+ }
+
+ exit(R_FAILURE);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgcond/pkgcond.h b/usr/src/cmd/svr4pkg/pkgcond/pkgcond.h
new file mode 100644
index 0000000000..45a88c3694
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgcond/pkgcond.h
@@ -0,0 +1,65 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGCOND_H
+#define _PKGCOND_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * global definitions
+ */
+
+/* environment variable */
+#define ENV_VAR_DEBUG "PKGCOND_DEBUG"
+#define ENV_VAR_PKGROOT "PKG_INSTALL_ROOT"
+#define ENV_VAR_PATCHROOT "ROOTDIR"
+#define ENV_VAR_SET "SET_FROM_ENVIRONMENT"
+#define ENV_VAR_VERBOSE "PKGCOND_VERBOSE"
+#define ENV_VAR_PKGZONENAME "SUNW_PKG_INSTALL_ZONENAME"
+#define ENV_VAR_INITIAL_INSTALL "PKG_INIT_INSTALL"
+#define ENV_VAR_PATCH_CLIENTVER "PATCH_CLIENT_VERSION"
+
+/* file system types */
+#define FSTYPE_INHERITED "inherited"
+
+/* return codes used with pkgcond itself */
+#define R_SUCCESS 0x0 /* condition match / success */
+#define R_FAILURE 0x1 /* condition no match / failure */
+#define R_USAGE 0x2 /* command usage issue */
+#define R_ERROR 0x3 /* could not determine condition / error */
+
+/* main.c */
+int main(int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGCOND_H */
diff --git a/usr/src/cmd/svr4pkg/pkgcond/pkgcond_msgs.h b/usr/src/cmd/svr4pkg/pkgcond/pkgcond_msgs.h
new file mode 100644
index 0000000000..4071f136db
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgcond/pkgcond_msgs.h
@@ -0,0 +1,498 @@
+/*
+ * 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.
+ */
+
+#ifndef _PKGCOND_MSGS_H
+#define _PKGCOND_MSGS_H
+
+
+#include <libintl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef lint
+#define gettext(x) x
+#endif
+
+/* generic messages */
+
+#define MSG_USAGE gettext(\
+"%s; usage is:\n" \
+"\t%s [-nv] <condition> [ <option(s)> ]\n" \
+"\n" \
+"command options:\n" \
+"\t-n - negate results of condition test\n" \
+"\t-v - verbose output of condition testing\n" \
+"\n" \
+"<condition> may be any one of:\n" \
+"%s\n" \
+"<option(s)> are specific to the condition used\n" \
+"\n" \
+"pkgcond -?\n" \
+"\t- Shows this help message\n")
+
+#define MSG_NO_PKG_ENV_DATA_PRESENT gettext(\
+"no data available from package tools: zone information may be incomplete")
+
+#define MSG_NO_ARGUMENTS_SPECIFIED gettext(\
+"no condition to check specified")
+
+#define MSG_INVALID_OPTION_SPECIFIED gettext(\
+"option <%c> not recognized")
+
+#define MSG_IS_INVALID_OPTION gettext(\
+"option <%c> not recognized by condition <%s>")
+
+#define MSG_UNRECOGNIZED_CONDITION_SPECIFIED gettext(\
+"condition not recognized")
+
+#define MSG_IS_WHAT_RESULT gettext(\
+"%s=%d")
+
+/* debugging messages */
+
+#define DBG_NO_RECURSION gettext(\
+"nonrecursive call to <%s>")
+
+#define DBG_RECURSION gettext(\
+"recursive call to <%s> count <%d> ignored")
+
+#define DBG_TESTPATH_OK gettext(\
+"path <%s> matches all criteria")
+
+#define DBG_ADDV_PATH_IS_SYMLINK gettext(\
+"cannot add driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_ADDV_YES gettext(\
+"root path <%s> can have a driver added")
+
+#define DBG_UPDV_PATH_IS_SYMLINK gettext(\
+"cannot update driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_UPDV_YES gettext(\
+"root path <%s> can have a driver updated")
+
+#define DBG_RMDV_PATH_IS_SYMLINK gettext(\
+"cannot remove driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_RMDV_YES gettext(\
+"root path <%s> can have a driver removed")
+
+#define DBG_ROOTPATH_IS gettext(\
+"root path is <%s>")
+
+#define DBG_CANNOT_ACCESS_PATH_BUT_SHOULD gettext(\
+"test_path: path <%s> must exist and does not: %s")
+
+#define DBG_CANNOT_ACCESS_PATH_OK gettext(\
+"test_path: path <%s> must not (and does not) exist")
+
+#define DBG_PATH_DOES_NOT_EXIST gettext(\
+"test_path: path <%s> does not exist: %s")
+
+#define DBG_CANNOT_LSTAT_PATH gettext(\
+"test_path: cannot lstat path <%s>: %s")
+
+#define DBG_IS_A_DIRECTORY gettext(\
+"test_path: path <%s> is a directory but is not supposed to be")
+
+#define DBG_IS_NOT_A_DIRECTORY gettext(\
+"test_path: path <%s> is not a directory but is supposed to be")
+
+#define DBG_DIRECTORY_NOT gettext(\
+"test_path: path <%s> is not a directory")
+
+#define DBG_DIRECTORY_IS gettext(\
+"test_path: path <%s> is a directory")
+
+#define DBG_IS_A_FILE gettext(\
+"test_path: path <%s> is a file but is not supposed to be")
+
+#define DBG_IS_NOT_A_FILE gettext(\
+"test_path: path <%s> is not a file but is supposed to be")
+
+#define DBG_TOKEN__EXISTS gettext(\
+"test_path: token <%s> exists in path <%s>")
+
+#define DBG_FILE_NOT gettext(\
+"test_path: path <%s> is not a file")
+
+#define DBG_FILE_IS gettext(\
+"test_path: path <%s> is a file")
+
+#define DBG_IS_A_SYMLINK gettext(\
+"test_path: path <%s> is a symlink but is not supposed to be")
+
+#define DBG_IS_NOT_A_SYMLINK gettext(\
+"test_path: path <%s> is not a symlink but is supposed to be")
+
+#define DBG_SORTEDINS_SKIPPED gettext(\
+"duplicate entry <%d> : <%s> (<%s> vs <%s>, <%s> vs <%s>): merged options")
+
+#define DBG_SYMLINK_NOT gettext(\
+"test_path: path <%s> is not a symlink")
+
+#define DBG_SYMLINK_IS gettext(\
+"test_path: path <%s> is a symlink")
+
+#define DBG_SET_NEGATE_RESULTS gettext(\
+"set_negate_results: current setting <%d> new setting <%d>")
+
+#define DBG_ADJUST_RESULTS gettext(\
+"adjust_results: result <%d> negate <%d> returned result <%d>")
+
+#define DBG_PARSE_GLOBAL gettext(\
+"parsing global data <%s>")
+
+#define DBG_NO_DEFAULT_ROOT_PATH_SET gettext(\
+"no default root path set")
+
+#define DBG_DEFAULT_ROOT_PATH_SET gettext(\
+"default root path <%s> set from environment variable <%s>")
+
+#define DBG_RESULTS gettext(\
+"returning results <%d>")
+
+#define DBG_SET_ROOT_PATH_TO gettext(\
+"setting root path to <%s>")
+
+#define DBG_TEST_PATH gettext(\
+"test path <%s> flags <0x%08lx>")
+
+#define DBG_TEST_PATH_NO_RESOLVE gettext(\
+"cannot resolve path <%s>")
+
+#define DBG_TEST_PATH_RESOLVE gettext(\
+"test resolved path <%s>")
+
+#define DBG_TEST_EXISTS_SHOULD_NOT gettext(\
+"path <%s> exists but should not")
+
+#define DBG_PARSED_ENVIRONMENT gettext(\
+"global data parsed from environment variable <%s>")
+
+#define DBG_DUMP_GLOBAL_LINE gettext(\
+"inherited file system <%d> is <%s>")
+
+#define DBG_DUMP_GLOBAL_ENTRY gettext(\
+"global data settings")
+
+#define DBG_DUMP_GLOBAL_PARENT_ZONE gettext(\
+"parentzone zoneName <%s> zoneType <%s>")
+
+#define DBG_DUMP_GLOBAL_CURRENT_ZONE gettext(\
+"currentzone zoneName <%s> zoneType <%s>")
+
+#define DBG_IDLC_INITIAL_INSTALL gettext(\
+"path <%s> is not a diskless client: initial installation in progress")
+
+#define DBG_IDLC_ZONE_INSTALL gettext(\
+"path <%s> is not a diskless client: initial zone installation in progress")
+
+#define DBG_IDLC_PKG_NOT_INSTALLED gettext(\
+"path <%s> is not a diskless client: package <%s> is not installed in <%s>")
+
+#define DBG_IDLC_ROOTPATH_BAD gettext(\
+"path <%s> is not a diskless client: root path cannot be <%s>")
+
+#define DBG_IDLC_ZONE_BAD gettext(\
+"path <%s> is not a diskless client: current zone must be <%s>")
+
+#define DBG_IDLC_PATH_MISSING gettext(\
+"path <%s> is not a diskless client: <%s> does not exist")
+
+#define DBG_IDLC_USR_IS_NOT_EMPTY gettext(\
+"path <%s> is not a diskless client: </usr> is not empty")
+
+#define DBG_IDLC_NO_TEMPLATES_PATH gettext(\
+"path <%s> is not a diskless client: <%s/%s> does not exist")
+
+#define DBG_IDLC_PATH_IS_DISKLESS_CLIENT gettext(\
+"path <%s> is a diskless client")
+
+#define DBG_ISGZ_INITIAL_INSTALL gettext(\
+"path <%s> is not a global zone: initial installation in progress")
+
+#define DBG_ISGZ_NGZ_ZONE_INSTALL gettext(\
+"path <%s> is not a global zone: initial non-global zone " \
+"installation in progress")
+
+#define DBG_ISGZ_PATH_IS_GLOBAL_ZONE gettext(\
+"path <%s> is a global zone")
+
+#define DBG_ISGZ_PATH_ISNT_DIRECTORY gettext(\
+"path <%s> is not a global zone: directory <%s> does not exist")
+
+#define DBG_ISGZ_PATH_EXISTS gettext(\
+"path <%s> is not a global zone: <%s> exists")
+
+#define DBG_ISGZ_ZONENAME_ISNT_GLOBAL gettext(\
+"path <%s> is not a global zone: zone name <%s> is not <global>")
+
+#define DBG_ISGZ_PATH_IS_SYMLINK gettext(\
+"path <%s> is not a global zone: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_INIM_INITIAL_INSTALL gettext(\
+"path <%s> is not a netinstall image: initial installation in progress")
+
+#define DBG_INIM_ZONE_INSTALL gettext(\
+"path <%s> is not a netinstall image: initial zone installation in progress")
+
+#define DBG_INIM_PATH_IS_NETINSTALL_IMAGE gettext(\
+"path <%s> is a netinstall image")
+
+#define DBG_INIM_BAD_CURRENT_ZONE gettext(\
+"path <%s> is not a netinstall image: current zone is not <%s>")
+
+#define DBG_INIM_PATH_ISNT_SYMLINK gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or exists " \
+"but is not a symbolic link")
+
+#define DBG_INIM_PATH_ISNT_DIRECTORY gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or " \
+"is not a directory")
+
+#define DBG_IMRT_INITIAL_INSTALL gettext(\
+"path <%s> is not a mounted miniroot image: initial installation in progress")
+
+#define DBG_IMRT_ZONE_INSTALL gettext(\
+"path <%s> is not a mounted miniroot image: initial zone " \
+"installation in progress")
+
+#define DBG_IMRT_PATH_IS_MOUNTED_MINIROOT gettext(\
+"path <%s> is a mounted miniroot")
+
+#define DBG_IMRT_BAD_CURRENT_ZONE gettext(\
+"path <%s> is not a mounted miniroot image: current zone is not <%s>")
+
+#define DBG_IMRT_ROOTDIR_BAD gettext(\
+"path <%s> is not a mounted miniroot image: root directory is not <%s>")
+
+#define DBG_IMRT_PATH_ISNT_SYMLINK gettext(\
+"path <%s> is not a mounted miniroot image: <%s> does not exist or is " \
+" not a symbolic link")
+
+#define DBG_IMRT_PATH_ISNT_DIRECTORY gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or is not " \
+" a directory")
+
+#define DBG_NGZN_INITIAL_INSTALL gettext(\
+"path <%s> is not a non-global zone: initial installation in progress")
+
+#define DBG_NGZN_GLOBAL_ZONE_INSTALL gettext(\
+"path <%s> is not a non-global zone: initial global zone " \
+"installation in progress")
+
+#define DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE gettext(\
+"path <%s> is a non-global zone: running in global zone")
+
+#define DBG_NGZN_PARENT_CHILD_SAMEZONE gettext(\
+"path <%s> is a non-global zone: parent/child are same zone name <%s>")
+
+#define DBG_NGZN_IS_NONGLOBAL_ZONE gettext(\
+"path <%s> is a non-global zone")
+
+#define DBG_NGZN_ZONENAME_ISNT_NGZ gettext(\
+"path <%s> is not a non-global zone: zone name is <%s>")
+
+#define DBG_NGZN_INSTALL_ZONENAME_IS_NGZ gettext(\
+"path <%s> is a non-global zone: installation of non-global zone name is <%s>")
+
+#define DBG_NGZN_ZONENAME_IS_NGZ gettext(\
+"path <%s> is a non-global zone: zone name is <%s>")
+
+#define DBG_NGZN_PATH_EXISTS gettext(\
+"path <%s> is not a non-global zone: <%s> exists")
+
+#define DBG_NGZN_BAD_PARENT_ZONETYPE gettext(\
+"path <%s> is not a non-global zone: parent zone type is <%s>")
+
+#define DBG_NGZN_BAD_CURRENT_ZONETYPE gettext(\
+"path <%s> is not a non-global zone: current zone type is <%s>")
+
+#define DBG_NGZN_PATH_DOES_NOT_EXIST gettext(\
+"path <%s> is not a non-global zone: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_IRST_INITIAL_INSTALL gettext(\
+"path <%s> is not the current running system: initial installation in progress")
+
+#define DBG_IRST_ZONE_INSTALL gettext(\
+"path <%s> is not the current running system: initial zone installation " \
+"in progress")
+
+#define DBG_IRST_PATH_IS_RUNNING_SYSTEM gettext(\
+"path <%s> is a running system")
+
+#define DBG_IRST_ZONE_BAD gettext(\
+"path <%s> is not the current running system: the current zone name " \
+" is not <%s>")
+
+#define DBG_IRST_ROOTPATH_BAD gettext(\
+"path <%s> is not the current running system: root path is not <%s>")
+
+#define DBG_IALR_INITIAL_INSTALL gettext(\
+"path <%s> is an alternative root: initial installation in progress")
+
+#define DBG_IALR_ZONE_INSTALL gettext(\
+"path <%s> is not an alternative root: initial zone installation in progress")
+
+#define DBG_IALR_PATH_DOES_NOT_EXIST gettext(\
+"path <%s> is not an alternative root: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define DBG_IALR_BAD_ROOTPATH gettext(\
+"path <%s> is not an alternative root: root directory is <%s>")
+
+#define DBG_IALR_IS gettext(\
+"root path <%s> is an alternative root")
+
+#define DBG_WRNG_IS gettext(\
+"root path <%s> is a whole root non-global zone")
+
+#define DBG_WRNG_IS_NOT gettext(\
+"root path <%s> is not a whole root non-global zones: " \
+"file systems are inherited")
+
+#define DBG_SRNG_IS_NOT gettext(\
+"root path <%s> is not a sparse root non-global zones: " \
+"file systems are not inherited")
+
+#define DBG_SRNG_IS gettext(\
+"root path <%s> is a sparse root non-global zone")
+
+#define DBG_BENV_INITIAL_INSTALL gettext(\
+"path <%s> is not an alternative boot environment: initial " \
+"installation in progress")
+
+#define DBG_BENV_ZONE_INSTALL gettext(\
+"path <%s> is not an alternative boot environment: initial zone " \
+"installation in progress")
+
+#define DBG_BENV_IS gettext(\
+"path <%s> is an alternative boot environment")
+
+#define DBG_BENV_NO_ETCLU gettext(\
+"path <%s> is not an alternative boot environment: <%s> does " \
+"not exist or is not a directory")
+
+#define DBG_BENV_NO_ETCLUTAB gettext(\
+"path <%s> is not an alternative boot environment: <%s> does not exist")
+
+#define DBG_BENV_BAD_ZONE gettext(\
+"path <%s> is not an alternative boot environment: " \
+"the current zone name is not <%s>")
+
+#define DBG_BENV_BAD_ROOTPATH gettext(\
+"path <%s> is not an alternative boot environment: root directory is <%s>")
+
+#define DBG_PWRT_INHERITED gettext(\
+"root path <%s> is not writeable: is inherited with <%s>")
+
+#define DBG_PWRT_READONLY gettext(\
+"root path <%s> is not writeable: is read only <%s>")
+
+#define DBG_PWRT_IS gettext(\
+"root path <%s> is writeable")
+
+#define DBG_PWRT_INFO gettext(\
+"root path <%s> is mount point <%s> fstype <%s> options <%s>")
+
+#define DBG_NO_GLOBAL_DATA_AVAILABLE gettext(\
+"no global data available in environment variable <%s>")
+
+#define DBG_CKSR_FSREADONLY gettext(\
+"file system <%s> type <%s> is read-only")
+
+#define DBG_CALCSCFG_ENTRY gettext(\
+"analyzing inherited and mounted file systems")
+
+#define DBG_CALCSCFG_INHERITED gettext(\
+"analyzing inherited file systems")
+
+#define DBG_CALCSCFG_MOUNTED gettext(\
+"analyzing mounted file systems")
+
+#define DBG_SINS_ENTRY gettext(\
+"inserting mount point <%s> type <%s> options <%s>")
+
+#define DBG_NGZN_PATH_EXISTS gettext(\
+"path <%s> is not a non-global zone: <%s> exists")
+
+#define DBG_CMDLINE_PATH gettext(\
+"command line path to check set to: <%s>")
+
+/* warnings */
+
+#define WRN_PARSED_DATA_MISSING gettext(\
+"available global data missing <%s>")
+
+/* errors */
+
+#define MSG_FATAL gettext(\
+ "Fatal Error")
+
+#define ERR_REQUIRED_ROOTPATH_MISSING gettext(\
+"the <%s> condition requires a root path to be specified")
+
+#define ERR_CANNOT_GET_ZONENAME gettext(\
+"could not determine zone name")
+
+#define ERR_CANNOT_CALC_FS_CONFIG gettext(\
+"cannot calculate file system config")
+
+#define ERR_CANNOT_PARSE_GLOBAL_DATA gettext(\
+"cannot parse global data SML: <%s>")
+
+#define ERR_UNRECOGNIZED_OPTION gettext(\
+"unrecognized option <%s>")
+
+#define ERR_DEFAULT_ROOT_INVALID gettext(\
+"cannot set root path to <%s>: %s")
+
+#define ERR_DEFAULT_ROOT_NOT_DIR gettext(\
+"cannot set root path to <%s>: not a directory")
+
+#define ERR_CANNOT_SET_ROOT_PATH gettext(\
+"cannot set root path from environment variable <%s>")
+
+#define ERR_CANNOT_USE_GLOBAL_DATA gettext(\
+"global data from environment variable <%s> cannot be used to determine " \
+"conditions and capabilities")
+
+#define ERR_BAD_SUB gettext(\
+ "\"%s\" is not a valid condition")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGCOND_MSGS_H */
diff --git a/usr/src/cmd/svr4pkg/pkginfo/Makefile b/usr/src/cmd/svr4pkg/pkginfo/Makefile
new file mode 100644
index 0000000000..3a35db2ddb
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginfo/Makefile
@@ -0,0 +1,42 @@
+#
+# 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= pkginfo
+
+OBJS= pkginfo.o
+SRCS= $(OBJS:.o=.c)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkginfo/pkginfo.c b/usr/src/cmd/svr4pkg/pkginfo/pkginfo.c
new file mode 100644
index 0000000000..e30a1c53f0
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginfo/pkginfo.c
@@ -0,0 +1,821 @@
+/*
+ * 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 */
+
+
+#define __EXTENTIONS__
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <libintl.h>
+#include <strings.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <pkginfo.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <pkglib.h>
+#include <instzones_api.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char *pkgdir;
+extern int pkginfofind(char *path, char *pkg_dir, char *pkginst);
+
+#define ERR_USAGE "usage:\n" \
+ "%s [-q] [-pi] [-x|l] [options] [pkg ...]\n" \
+ "%s -d device [-q] [-x|l] [options] [pkg ...]\n" \
+ "where\n" \
+ " -q #quiet mode\n" \
+ " -p #select partially installed packages\n" \
+ " -i #select completely installed packages\n" \
+ " -x #extracted listing\n" \
+ " -l #long listing\n" \
+ " -r #relocation base \n" \
+ "and options may include:\n" \
+ " -c category, [category...]\n" \
+ " -a architecture\n" \
+ " -v version\n"
+
+#define ERR_INCOMP0 "-L and -l/-x/-r flags are incompatible"
+#define ERR_INCOMP1 "-l and -x/-r flags are not compatible"
+#define ERR_INCOMP2 "-x and -l/-r flags are not compatible"
+#define ERR_INCOMP3 "-r and -x/-x flags are not compatible"
+#define ERR_NOINFO "ERROR: information for \"%s\" was not found"
+#define ERR_NOPINFO "ERROR: No partial information for \"%s\" was found"
+#define ERR_BADINFO "pkginfo file is corrupt or missing"
+#define ERR_ROOT_SET "Could not set install root from the environment."
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+
+/* Format for dumping package attributes in dumpinfo() */
+#define FMT "%10s: %s\n"
+#define SFMT "%-11.11s %-*.*s %s\n"
+#define CFMT "%*.*s "
+#define XFMT "%-*.*s %s\n"
+
+#define nblock(size) ((size + (DEV_BSIZE - 1)) / DEV_BSIZE)
+#define MAXCATG 64
+
+static char *device = NULL;
+static char *parmlst[] = {
+ "DESC", "PSTAMP", "INSTDATE", "VSTOCK", "SERIALNUM", "HOTLINE",
+ "EMAIL", NULL
+};
+
+static char contents[PATH_MAX];
+static int errflg = 0;
+static int qflag = 0;
+static int iflag = -1;
+static int pflag = -1;
+static int lflag = 0;
+static int Lflag = 0;
+static int Nflag = 0;
+static int xflag = 0;
+static int rflag = 0; /* bug # 1081606 */
+static struct cfent entry;
+static char **pkg = NULL;
+static int pkgcnt = 0;
+static char *ckcatg[MAXCATG] = {NULL};
+static int ncatg = 0;
+static char *ckvers = NULL;
+static char *ckarch = NULL;
+
+static struct cfstat {
+ char pkginst[32];
+ short exec;
+ short dirs;
+ short link;
+ short partial;
+ long spooled;
+ long installed;
+ short info;
+ short shared;
+ short setuid;
+ long tblks;
+ struct cfstat *next;
+} *data;
+static struct pkginfo info;
+
+static struct cfstat *fpkg(char *pkginst);
+static int iscatg(char *list);
+static int selectp(char *p);
+static void usage(void), look_for_installed(void),
+ report(void), rdcontents(void);
+static void pkgusage(struct cfstat *dp, struct cfent *pentry);
+static void getinfo(struct cfstat *dp);
+static void dumpinfo(struct cfstat *dp, int pkgLngth);
+
+int
+main(int argc, char **argv)
+{
+ int c;
+
+ pkgdir = NULL;
+ setErrstr(NULL);
+
+ /* initialize locale mechanism */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* determine program name */
+
+ (void) set_prog_name(argv[0]);
+
+ /* tell spmi zones interface how to access package output functions */
+
+ z_set_output_functions(echo, echoDebug, progerr);
+
+ /* establish installation root directory */
+
+ if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+ progerr(gettext(ERR_ROOT_SET));
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "LNR:xv:a:d:qrpilc:?")) != EOF) {
+ switch (c) {
+ case 'v':
+ ckvers = optarg;
+ break;
+
+ case 'a':
+ ckarch = optarg;
+ break;
+
+ case 'd':
+ /* -d could specify stream or mountable device */
+ device = flex_device(optarg, 1);
+ break;
+
+ case 'q':
+ qflag++;
+ break;
+
+ case 'i':
+ iflag = 1;
+ if (pflag > 0)
+ usage();
+ pflag = 0;
+ break;
+
+ case 'p':
+ pflag = 1;
+ if (iflag > 0)
+ usage();
+ iflag = 0;
+ break;
+
+ case 'N':
+ Nflag++;
+ break;
+
+ case 'L':
+ if (xflag || lflag || rflag) {
+ progerr(gettext(ERR_INCOMP0));
+ usage();
+ }
+ Lflag++;
+ break;
+
+ case 'l':
+ if (xflag || rflag) {
+ progerr(gettext(ERR_INCOMP1));
+ usage();
+ }
+ lflag++;
+ break;
+
+ case 'x':
+ /* bug # 1081606 */
+ if (lflag || rflag) {
+ progerr(gettext(ERR_INCOMP2));
+ usage();
+ }
+ xflag++;
+ break;
+
+ case 'r':
+ if (lflag || xflag || Lflag) {
+ progerr(gettext(ERR_INCOMP0));
+ usage();
+ }
+ rflag++;
+ break;
+
+ case 'c':
+ ckcatg[ncatg++] = strtok(optarg, " \t\n, ");
+ while (ckcatg[ncatg] = strtok(NULL, " \t\n, "))
+ ncatg++;
+ break;
+
+ /* added for newroot functions */
+ case 'R':
+ if (!set_inst_root(optarg)) {
+ progerr(gettext(ERR_ROOT_CMD));
+ exit(1);
+ }
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ /*
+ * implement the newroot option
+ */
+ set_PKGpaths(get_inst_root()); /* set up /var... directories */
+
+ /*
+ * Open the install DB, if one exists.
+ */
+
+ pkg = &argv[optind];
+ pkgcnt = (argc - optind);
+
+ if (pkg[0] && strcmp(pkg[0], "all") == NULL) {
+ pkgcnt = 0;
+ pkg[0] = NULL;
+ }
+
+ if (pkgdir == NULL)
+ pkgdir = get_PKGLOC(); /* we need this later */
+
+ /* convert device appropriately */
+ if (pkghead(device))
+ exit(1);
+
+ /*
+ * If we are to inspect a spooled package we are only interested in
+ * the pkginfo file in the spooled pkg so we skip any Reg 4 DB
+ * lookups and use the old algorithm. We have a spooled pkg if
+ * device is not NULL.
+ */
+
+
+ look_for_installed();
+
+ if (lflag && strcmp(pkgdir, get_PKGLOC()) == NULL) {
+ /* look at contents file */
+ (void) snprintf(contents, sizeof (contents),
+ "%s/contents", get_PKGADM());
+ rdcontents();
+
+ }
+
+ /*
+ * If we are to inspect a spooled package we are only interested in
+ * the pkginfo file in the spooled pkg so we skip any Reg 4 DB
+ * lookups and use the old algorithm. We have a spooled pkg if
+ * device is not NULL.
+ */
+
+ report();
+
+ (void) pkghead(NULL);
+
+ return (errflg ? 1 : 0);
+}
+
+static void
+report(void)
+{
+ struct cfstat *dp, *choice;
+ int i;
+ int pkgLgth = 0;
+ int longestPkg = 0;
+ boolean_t output = B_FALSE;
+
+ for (;;) {
+ choice = (struct cfstat *)0;
+ for (dp = data; dp; dp = dp->next) {
+ pkgLgth = strlen(dp->pkginst);
+ if (pkgLgth > longestPkg)
+ longestPkg = pkgLgth;
+ }
+ for (dp = data; dp; dp = dp->next) {
+ /* get information about this package */
+ if (dp->installed < 0)
+ continue; /* already used */
+ if (Lflag && pkgcnt) {
+ choice = dp;
+ break;
+ } else if (!choice ||
+ (strcmp(choice->pkginst, dp->pkginst) > 0))
+ choice = dp;
+ }
+ if (!choice)
+ break; /* no more packages */
+
+ if (pkginfo(&info, choice->pkginst, ckarch, ckvers)) {
+ choice->installed = (-1);
+ continue;
+ }
+
+ /*
+ * Confirm that the pkginfo file contains the
+ * required information.
+ */
+ if (info.name == NULL || *(info.name) == NULL ||
+ info.arch == NULL || *(info.arch) == NULL ||
+ info.version == NULL || *(info.version) == NULL ||
+ info.catg == NULL || *(info.catg) == NULL) {
+ progerr(gettext(ERR_BADINFO));
+ errflg++;
+ return;
+ }
+
+ /* is it in an appropriate catgory? */
+ if (iscatg(info.catg)) {
+ choice->installed = (-1);
+ continue;
+ }
+
+ if (!pflag &&
+ /* don't include partially installed packages */
+ (choice->partial || (info.status == PI_PARTIAL) ||
+ (info.status == PI_UNKNOWN))) {
+ choice->installed = (-1);
+ continue;
+ }
+
+ if (Nflag && (info.status == PI_PRESVR4)) {
+ /* don't include preSVR4 packages */
+ choice->installed = (-1);
+ continue;
+ }
+
+ if (!iflag && ((info.status == PI_INSTALLED) ||
+ (info.status == PI_PRESVR4))) {
+ /* don't include completely installed packages */
+ choice->installed = (-1);
+ continue;
+ }
+
+ output = B_TRUE;
+ dumpinfo(choice, longestPkg);
+ choice->installed = (-1);
+ if (pkgcnt) {
+ i = selectp(choice->pkginst);
+ if (i >= 0)
+ pkg[i] = NULL;
+ else {
+ if (qflag) {
+ errflg++;
+ return;
+ }
+ }
+ }
+ }
+
+ /* If no package matched and no output produced set error flag */
+ if (!output)
+ errflg++;
+
+ /* verify that each package listed on command line was output */
+ for (i = 0; i < pkgcnt; ++i) {
+ if (pkg[i]) {
+ errflg++;
+ if (!qflag) {
+ if (pflag == 1)
+ logerr(gettext(ERR_NOPINFO), pkg[i]);
+ else
+ logerr(gettext(ERR_NOINFO), pkg[i]);
+ } else
+ return;
+ }
+ }
+ (void) pkginfo(&info, NULL); /* free up all memory and open fds */
+}
+
+static void
+dumpinfo(struct cfstat *dp, int pkgLngth)
+{
+ register int i;
+ char *pt;
+ char category[128];
+
+ if (qflag) {
+ return; /* print nothing */
+ }
+
+ if (rflag) {
+ (void) puts((info.basedir) ? info.basedir : "none");
+ return;
+ }
+
+ if (Lflag) {
+ (void) puts(info.pkginst);
+ return;
+ } else if (xflag) {
+ (void) printf(XFMT, pkgLngth, pkgLngth, info.pkginst,
+ info.name);
+
+ if (info.arch || info.version) {
+ (void) printf(CFMT, pkgLngth, pkgLngth, "");
+ if (info.arch)
+ (void) printf("(%s) ", info.arch);
+ if (info.version)
+ (void) printf("%s", info.version);
+ (void) printf("\n");
+ }
+ return;
+ } else if (!lflag) {
+ if (info.catg) {
+ (void) sscanf(info.catg, "%[^, \t\n]", category);
+ } else if (info.status == PI_PRESVR4) {
+ (void) strcpy(category, "preSVR4");
+ } else {
+ (void) strcpy(category, "(unknown)");
+ }
+ (void) printf(SFMT, category, pkgLngth, pkgLngth, info.pkginst,
+ info.name);
+ return;
+ }
+ if (info.pkginst)
+ (void) printf(FMT, "PKGINST", info.pkginst);
+ if (info.name)
+ (void) printf(FMT, "NAME", info.name);
+ if (lflag && info.catg)
+ (void) printf(FMT, "CATEGORY", info.catg);
+ if (lflag && info.arch)
+ (void) printf(FMT, "ARCH", info.arch);
+ if (info.version)
+ (void) printf(FMT, "VERSION", info.version);
+ if (info.basedir)
+ (void) printf(FMT, "BASEDIR", info.basedir);
+ if (info.vendor)
+ (void) printf(FMT, "VENDOR", info.vendor);
+
+ if (info.status == PI_PRESVR4)
+ (void) printf(FMT, "STATUS", "preSVR4");
+ else {
+ for (i = 0; parmlst[i]; ++i) {
+ if ((pt = pkgparam(info.pkginst, parmlst[i])) != NULL &&
+ *pt)
+ (void) printf(FMT, parmlst[i], pt);
+ }
+ if (info.status == PI_SPOOLED)
+ (void) printf(FMT, "STATUS", gettext("spooled"));
+ else if (info.status == PI_PARTIAL)
+ (void) printf(FMT, "STATUS",
+ gettext("partially installed"));
+ else if (info.status == PI_INSTALLED)
+ (void) printf(FMT, "STATUS",
+ gettext("completely installed"));
+ else
+ (void) printf(FMT, "STATUS", gettext("(unknown)"));
+ }
+ (void) pkgparam(NULL, NULL);
+
+ if (!lflag) {
+ (void) putchar('\n');
+ return;
+ }
+
+ if (info.status != PI_PRESVR4) {
+ if (strcmp(pkgdir, get_PKGLOC()))
+ getinfo(dp);
+
+ if (dp->spooled)
+ (void) printf(
+ gettext("%10s: %7ld spooled pathnames\n"),
+ "FILES", dp->spooled);
+ if (dp->installed)
+ (void) printf(
+ gettext("%10s: %7ld installed pathnames\n"),
+ "FILES", dp->installed);
+ if (dp->partial)
+ (void) printf(
+ gettext("%20d partially installed pathnames\n"),
+ dp->partial);
+ if (dp->shared)
+ (void) printf(gettext("%20d shared pathnames\n"),
+ dp->shared);
+ if (dp->link)
+ (void) printf(gettext("%20d linked files\n"), dp->link);
+ if (dp->dirs)
+ (void) printf(gettext("%20d directories\n"), dp->dirs);
+ if (dp->exec)
+ (void) printf(gettext("%20d executables\n"), dp->exec);
+ if (dp->setuid)
+ (void) printf(
+ gettext("%20d setuid/setgid executables\n"),
+ dp->setuid);
+ if (dp->info)
+ (void) printf(
+ gettext("%20d package information files\n"),
+ dp->info+1); /* pkgmap counts! */
+
+ if (dp->tblks)
+ (void) printf(gettext("%20ld blocks used (approx)\n"),
+ dp->tblks);
+ }
+ (void) putchar('\n');
+}
+
+static struct cfstat *
+fpkg(char *pkginst)
+{
+ struct cfstat *dp, *last;
+
+ dp = data;
+ last = (struct cfstat *)0;
+ while (dp) {
+ if (strcmp(dp->pkginst, pkginst) == NULL)
+ return (dp);
+ last = dp;
+ dp = dp->next;
+ }
+ dp = (struct cfstat *)calloc(1, sizeof (struct cfstat));
+ if (!dp) {
+ progerr(gettext("no memory, malloc() failed"));
+ exit(1);
+ }
+ if (!last)
+ data = dp;
+ else
+ last->next = dp; /* link list */
+ (void) strcpy(dp->pkginst, pkginst);
+ return (dp);
+}
+
+#define SEPAR ','
+
+static int
+iscatg(char *list)
+{
+ register int i;
+ register char *pt;
+ int match;
+
+ if (!ckcatg[0])
+ return (0); /* no specification implies all packages */
+ if (info.status == PI_PRESVR4) {
+ for (i = 0; ckcatg[i]; /* void */) {
+ if (strcmp(ckcatg[i++], "preSVR4") == NULL)
+ return (0);
+ }
+ return (1);
+ }
+ if (!list)
+ return (1); /* no category specified in pkginfo is a bug */
+
+ match = 0;
+ do {
+ if (pt = strchr(list, ','))
+ *pt = '\0';
+
+ for (i = 0; ckcatg[i]; /* void */) {
+ /* bug id 1081607 */
+ if (!strcasecmp(list, ckcatg[i++])) {
+ match++;
+ break;
+ }
+ }
+
+ if (pt)
+ *pt++ = ',';
+ if (match)
+ return (0);
+ list = pt; /* points to next one */
+ } while (pt);
+ return (1);
+}
+
+static void
+look_for_installed(void)
+{
+ struct dirent *drp;
+ struct stat status;
+ DIR *dirfp;
+ char path[PATH_MAX];
+ int n;
+
+ if (strcmp(pkgdir, get_PKGLOC()) == NULL &&
+ (dirfp = opendir(get_PKGOLD()))) {
+ while (drp = readdir(dirfp)) {
+ if (drp->d_name[0] == '.')
+ continue;
+ n = strlen(drp->d_name);
+ if ((n > 5) &&
+ strcmp(&drp->d_name[n-5], ".name") == NULL) {
+ (void) snprintf(path, sizeof (path),
+ "%s/%s", get_PKGOLD(), drp->d_name);
+ if (lstat(path, &status))
+ continue;
+ if ((status.st_mode & S_IFMT) != S_IFREG)
+ continue;
+ drp->d_name[n-5] = '\0';
+ if (!pkgcnt || (selectp(drp->d_name) >= 0))
+ (void) fpkg(drp->d_name);
+ }
+ }
+ (void) closedir(dirfp);
+ }
+
+ if ((dirfp = opendir(pkgdir)) == NULL)
+ return;
+
+ while (drp = readdir(dirfp)) {
+ if (drp->d_name[0] == '.')
+ continue;
+
+ if (pkgcnt && (selectp(drp->d_name) < 0))
+ continue;
+
+ if (!pkginfofind(path, pkgdir, drp->d_name))
+ continue; /* doesn't appear to be a package */
+
+ (void) fpkg(drp->d_name);
+ }
+ (void) closedir(dirfp);
+}
+
+static int
+selectp(char *p)
+{
+ register int i;
+
+ for (i = 0; i < pkgcnt; ++i) {
+ if (pkg[i] && pkgnmchk(p, pkg[i], 1) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+static void
+rdcontents(void)
+{
+ VFP_T *vfp;
+ struct cfstat *dp;
+ struct pinfo *pinfo;
+ int n;
+
+ if (vfpOpen(&vfp, contents, "r", VFP_NEEDNOW) != 0) {
+ progerr(gettext("unable to open \"%s\" for reading"), contents);
+ exit(1);
+ }
+
+ /* check the contents file to look for referenced packages */
+ while ((n = srchcfile(&entry, "*", vfp, (VFP_T *)NULL)) > 0) {
+ for (pinfo = entry.pinfo; pinfo; pinfo = pinfo->next) {
+ /* see if entry is used by indicated packaged */
+ if (pkgcnt && (selectp(pinfo->pkg) < 0))
+ continue;
+
+ dp = fpkg(pinfo->pkg);
+ pkgusage(dp, &entry);
+
+ if (entry.npkgs > 1)
+ dp->shared++;
+
+ /*
+ * Only objects specifically tagged with '!' event
+ * character are considered "partial", everything
+ * else is considered "installed" (even server
+ * objects).
+ */
+ switch (pinfo->status) {
+ case '!' :
+ dp->partial++;
+ break;
+ default :
+ dp->installed++;
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext("bad entry read in contents file"));
+ logerr(gettext("pathname: %s"),
+ (entry.path && *entry.path) ? entry.path : "Unknown");
+ logerr(gettext("problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ exit(1);
+ }
+
+ (void) vfpClose(&vfp);
+}
+
+static void
+getinfo(struct cfstat *dp)
+{
+ int n;
+ char pkgmap[MAXPATHLEN];
+ VFP_T *vfp;
+
+ (void) snprintf(pkgmap, sizeof (pkgmap),
+ "%s/%s/pkgmap", pkgdir, dp->pkginst);
+
+ if (vfpOpen(&vfp, pkgmap, "r", VFP_NEEDNOW) != 0) {
+ progerr(gettext("unable open \"%s\" for reading"), pkgmap);
+ exit(1);
+ }
+
+ dp->spooled = 1; /* pkgmap counts! */
+
+ while ((n = gpkgmapvfp(&entry, vfp)) > 0) {
+ dp->spooled++;
+ pkgusage(dp, &entry);
+ }
+
+ if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext("bad entry read in pkgmap file"));
+ logerr(gettext("pathname: %s"),
+ (entry.path && *entry.path) ? entry.path : "Unknown");
+ logerr(gettext("problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ exit(1);
+ }
+
+ (void) vfpClose(&vfp);
+}
+
+static void
+pkgusage(struct cfstat *dp, struct cfent *pentry)
+{
+ if (pentry->ftype == 'i') {
+ dp->info++;
+ return;
+ } else if (pentry->ftype == 'l') {
+ dp->link++;
+ } else {
+ if ((pentry->ftype == 'd') || (pentry->ftype == 'x'))
+ dp->dirs++;
+
+ /* Only collect mode stats if they would be meaningful. */
+ if (pentry->ainfo.mode != BADMODE) {
+ if (pentry->ainfo.mode & 06000)
+ dp->setuid++;
+ if (!strchr("dxcbp", pentry->ftype) &&
+ (pentry->ainfo.mode & 0111))
+ dp->exec++;
+ }
+ }
+
+ if (strchr("ifve", pentry->ftype))
+ dp->tblks += nblock(pentry->cinfo.size);
+}
+
+static void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ /* bug # 1081606 */
+ (void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+
+ exit(1);
+}
+
+void
+quit(int retval)
+{
+ exit(retval);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/Makefile b/usr/src/cmd/svr4pkg/pkginstall/Makefile
new file mode 100644
index 0000000000..9c0a0e6852
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/Makefile
@@ -0,0 +1,55 @@
+#
+# 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= pkginstall
+
+OBJS= backup.o \
+ check.o \
+ cppath.o \
+ dockspace.o \
+ getinst.o \
+ instvol.o \
+ main.o \
+ merginfo.o \
+ pkgenv.o \
+ pkgvolume.o \
+ predepend.o \
+ quit.o \
+ reqexec.o \
+ sortmap.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+LDLIBS += -lnsl -lsocket
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkginstall/backup.c b/usr/src/cmd/svr4pkg/pkginstall/backup.c
new file mode 100644
index 0000000000..43d69ededf
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/backup.c
@@ -0,0 +1,60 @@
+/*
+ * 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 1993 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 <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+
+extern char savlog[];
+extern int warnflag;
+
+void
+backup(char *path, int mode)
+{
+ static int count = 0;
+ static FILE *fp;
+
+ /* mode probably used in the future */
+ if (count++ == 0) {
+ if ((fp = fopen(savlog, "w")) == NULL) {
+ logerr(gettext("WARNING: unable to open logfile <%s>"),
+ savlog);
+ warnflag++;
+ }
+ }
+
+ if (fp == NULL)
+ return;
+
+ (void) fprintf(fp, "%s%s", path, mode ? "\n" :
+ gettext(" <attributes only>\n"));
+ /* we don't really back anything up; we just log the pathname */
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/check.c b/usr/src/cmd/svr4pkg/pkginstall/check.c
new file mode 100644
index 0000000000..1f320adab2
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/check.c
@@ -0,0 +1,1097 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h> /* mkdir declaration is here? */
+#include <unistd.h>
+#include <errno.h>
+#include <utmpx.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+
+extern struct admin adm;
+extern struct cfextra **extlist;
+extern int ckquit, nocnflct, nosetuid, rprcflag;
+extern char ilockfile[], rlockfile[], instdir[], savlog[],
+ tmpdir[], pkgloc[], pkgloc_sav[], pkgbin[], pkgsav[],
+ *pkginst, *msgtext;
+extern char saveSpoolInstallDir[];
+
+static boolean_t preinstallCheck = B_FALSE;
+static char *zoneName = (char *)NULL;
+
+static char ask_cont[100];
+
+#define DISPSIZ 20 /* number of items to display on one page */
+
+#define MSG_RUNLEVEL "\\nThe current run-level of this machine is <%s>, " \
+ "which is not a run-level suggested for installation " \
+ "of this package. Suggested run-levels (in order of " \
+ "preference) include:"
+#define HLP_RUNLEVEL "If this package is not installed in a run-level " \
+ "which has been suggested, it is possible that the " \
+ "package may not install or operate properly. If " \
+ "you wish to follow the run-level suggestions, " \
+ "answer 'n' to stop installation of the package."
+#define MSG_STATECHG "\\nTo change states, execute\\n\\tshutdown -y " \
+ "-i%s -g0\\nafter exiting the installation process. " \
+ "Please note that after changing states you " \
+ "may have to mount appropriate filesystem(s) " \
+ "in order to install this package."
+
+#define ASK_CONFLICT "Do you want to install these conflicting files"
+#define MSG_CONFLICT "\\nThe following files are already installed on the " \
+ "system and are being used by another package:"
+#define MSG_ROGUE "\\n* - conflict with a file which does not " \
+ "belong to any package."
+#define HLP_CONFLICT "If you choose to install conflicting files, the " \
+ "files listed above will be overwritten and/or have " \
+ "their access permissions changed. If you choose " \
+ "not to install these files, installation will " \
+ "proceed but these specific files will not be " \
+ "installed. Note that sane operation of the " \
+ "software being installed may require these files " \
+ "be installed; thus choosing to not to do so may " \
+ "cause inapropriate operation. If you wish to stop " \
+ "installation of this package, enter 'q' to quit."
+
+#define ASK_SETUID "Do you want to install these as setuid/setgid files"
+#define MSG_SETUID "\\nThe following files are being installed with " \
+ "setuid and/or setgid permissions:"
+#define MSG_OVERWR "\\n* - overwriting a file which is also " \
+ "setuid/setgid."
+#define HLP_SETUID "The package being installed appears to contain " \
+ "processes which will have their effective user or " \
+ "group ids set upon execution. History has shown " \
+ "that these types of processes can be a source of " \
+ "security problems on your system. If you choose " \
+ "not to install these as setuid files, installation " \
+ "will proceed but these specific files will be " \
+ "installed as regular files with setuid and/or " \
+ "setgid permissions reset. Note that sane " \
+ "operation of the software being installed may " \
+ "require that these files be installed with setuid " \
+ "or setgid permissions as delivered; thus choosing " \
+ "to install them as regular files may cause " \
+ "inapropriate operation. If you wish to stop " \
+ "installation of this package, enter 'q' to quit."
+#define MSG_PARTINST "\\nThe installation of this package was previously " \
+ "terminated and installation was never successfully " \
+ "completed."
+#define MSG_PARTREM "\\nThe removal of this package was terminated at " \
+ "some point in time, and package removal was only " \
+ "partially completed."
+#define HLP_PARTIAL "Installation of partially installed packages is " \
+ "normally allowable, but some packages providers " \
+ "may suggest that a partially installed package be " \
+ "completely removed before re-attempting " \
+ "installation. Check the documentation provided " \
+ "with this package, and then answer 'y' if you feel " \
+ "it is advisable to continue the installation process."
+
+#define HLP_SPACE "It appears that there is not enough free space on " \
+ "your system in which to install this package. It " \
+ "is possible that one or more filesystems are not " \
+ "properly mounted. Neither installation of the " \
+ "package nor its operation can be guaranteed under " \
+ "these conditions. If you choose to disregard this " \
+ "warning, enter 'y' to continue the installation " \
+ "process."
+#define HLP_DEPEND "The package being installed has indicated a " \
+ "dependency on the existence (or non-existence) " \
+ "of another software package. If this dependency is " \
+ "not met before continuing, the package may not " \
+ "install or operate properly. If you wish to " \
+ "disregard this dependency, answer 'y' to continue " \
+ "the installation process."
+
+#define MSG_PRIV "\\nThis package contains scripts which will be " \
+ "executed with super-user permission during the " \
+ "process of installing this package."
+#define HLP_PRIV "During the installation of this package, certain " \
+ "scripts provided with the package will execute with " \
+ "super-user permission. These scripts may modify or " \
+ "otherwise change your system without your " \
+ "knowledge. If you are certain of the origin and " \
+ "trustworthiness of the package being installed, " \
+ "answer 'y' to continue the installation process."
+
+#define ASK_CONT "Do you want to continue with the installation of <%s>"
+#define HLP_CONT "If you choose 'y', installation of this package " \
+ "will continue. If you want to stop installation " \
+ "of this package, choose 'n'."
+
+#define MSG_MKPKGDIR "unable to make packaging directory <%s>"
+
+#define MSG_CKCONFL_GZ "## Checking for conflicts with packages already " \
+ "installed."
+#define MSG_CKCONFL_LZ "## Checking for conflicts with packages already " \
+ "installed in zone <%s>."
+#define MSG_CKDEPEND_GZ "## Verifying package dependencies."
+#define MSG_CKDEPEND_LZ "## Verifying package dependencies in zone <%s>."
+#define MSG_CKSPACE_GZ "## Verifying disk space requirements."
+#define MSG_CKSPACE_LZ "## Verifying disk space requirements in zone <%s>."
+#define MSG_CKUID_GZ "## Checking for setuid/setgid programs."
+#define MSG_CKUID_LZ "## Checking for setuid/setgid programs in zone <%s>."
+
+#define MSG_SCRFND "Package scripts were found."
+#define MSG_UIDFND "Setuid/setgid processes detected."
+#define MSG_ATTRONLY "!%s %s <attribute change only>"
+
+#define MSG_CONTDISP "[Hit <RETURN> to continue display]"
+
+#define ERR_NO_RUNST "unable to determine current run-state"
+#define ERR_DEPFAILED "Dependency checking failed."
+#define ERR_SPCFAILED "Space checking failed."
+#define ERR_CNFFAILED "Conflict checking failed."
+#define ERR_BADFILE "packaging file <%s> is corrupt"
+
+/*
+ * 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
+ * If "preinstallcheck" is set to B_TRUE:
+ * 8 - partial install detected
+ * 9 - partial removal detected
+ */
+
+int
+ckpartial(void)
+{
+ char ans[MAX_INPUT];
+ int n;
+
+ if (ADM(partial, "nocheck")) {
+ return (0);
+ }
+
+ if (access(ilockfile, F_OK) == 0) {
+ if (preinstallCheck == B_TRUE) {
+ return (8); /* partial install detected */
+ }
+
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+ msgtext = gettext(MSG_PARTINST);
+ ptext(stderr, msgtext);
+
+ if (ADM(partial, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PARTIAL),
+ ask_cont)) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ ckquit = 1;
+ }
+
+ if (access(rlockfile, F_OK) == 0) {
+ if (preinstallCheck == B_TRUE) {
+ return (9); /* partial removal detected */
+ }
+
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+
+ msgtext = gettext(MSG_PARTREM);
+ ptext(stderr, msgtext);
+
+ if (ADM(partial, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PARTIAL),
+ ask_cont)) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ ckquit = 1;
+ }
+
+ return (0);
+}
+
+/*
+ * 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
+ */
+
+int
+ckrunlevel(void)
+{
+ struct utmpx utmpx;
+ struct utmpx *putmpx;
+ char ans[MAX_INPUT], *pt, *istates, *pstate;
+ int n;
+ char *uxstate;
+
+ if (ADM(runlevel, "nocheck")) {
+ return (0);
+ }
+
+ pt = getenv("ISTATES");
+ if (pt == NULL) {
+ return (0);
+ }
+
+ utmpx.ut_type = RUN_LVL;
+ putmpx = getutxid(&utmpx);
+ if (putmpx == NULL) {
+ progerr(gettext(ERR_NO_RUNST));
+ return (99);
+ }
+
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+ /*
+ * this cryptic code is trying to pull the run level
+ * out of the utmpx entry...the level starts in column
+ * 11 - looks like "run-level %c"
+ */
+ uxstate = strtok(&putmpx->ut_line[10], " \t\n");
+
+ istates = qstrdup(pt);
+ if ((pt = strtok(pt, " \t\n, ")) == NULL) {
+ return (0); /* no list is no list */
+ }
+
+ pstate = pt;
+ do {
+ if (strcmp(pt, uxstate) == 0) {
+ free(istates);
+ return (0);
+ }
+ } while (pt = strtok(NULL, " \t\n, "));
+
+ if (preinstallCheck == B_FALSE) {
+ msgtext = gettext(MSG_RUNLEVEL);
+ ptext(stderr, msgtext, uxstate);
+ } else {
+ (void) fprintf(stdout, "runlevel=%s", uxstate);
+ }
+
+ pt = strtok(istates, " \t\n, ");
+ do {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, "\\t%s", pt);
+ } else {
+ (void) fprintf(stdout, ":%s", pt);
+ }
+ } while (pt = strtok(NULL, " \t\n, "));
+
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "\n");
+ }
+
+ free(istates);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(runlevel, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_RUNLEVEL),
+ ask_cont)) {
+ return (n);
+ }
+
+ ckquit = 1;
+
+ if (strchr("yY", *ans) != NULL) {
+ return (0);
+ } else {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_STATECHG), pstate);
+ }
+ return (3);
+ }
+}
+
+/*
+ * 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
+ */
+
+int
+ckdepend(void)
+{
+ int n;
+ char ans[MAX_INPUT];
+ char path[PATH_MAX];
+
+ if (ADM(idepend, "nocheck")) {
+ return (0);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/%s", instdir, DEPEND_FILE);
+ if (access(path, F_OK) != 0) {
+ return (0); /* no dependency file provided by package */
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(gettext(MSG_CKDEPEND_GZ));
+ } else {
+ echo(gettext(MSG_CKDEPEND_LZ), zoneName);
+ }
+
+ if (dockdeps(path, 0, preinstallCheck)) {
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+ msgtext = gettext(ERR_DEPFAILED);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(idepend, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_DEPEND),
+ ask_cont)) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+
+ ckquit = 1;
+ }
+
+ return (0);
+}
+
+void
+cksetZoneName(char *a_zoneName)
+{
+ zoneName = a_zoneName;
+}
+
+void
+cksetPreinstallCheck(boolean_t a_preinstallCheck)
+{
+ preinstallCheck = a_preinstallCheck;
+}
+
+/*
+ * 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
+ */
+int
+ckspace(void)
+{
+ int n;
+ char ans[MAX_INPUT];
+ char path[PATH_MAX];
+
+ if (ADM(space, "nocheck")) {
+ return (0);
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(gettext(MSG_CKSPACE_GZ));
+ } else {
+ echo(gettext(MSG_CKSPACE_LZ), zoneName);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/install/space", instdir);
+ if (access(path, F_OK) == 0) {
+ n = dockspace(path);
+ } else {
+ n = dockspace(NULL);
+ }
+
+ if (n) {
+ msgtext = gettext(ERR_SPCFAILED);
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(space, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ n = ckyorn(ans, NULL, NULL, gettext(HLP_SPACE), ask_cont);
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+
+ ckquit = 1;
+ }
+ return (0);
+}
+
+void
+ckdirs(void)
+{
+ char path[PATH_MAX];
+
+ if (mkpath(get_PKGADM())) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", get_PKGADM());
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), get_PKGADM());
+ }
+ quit(99);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/admin", get_PKGADM());
+
+ if (mkpath(path)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", path);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), path);
+ }
+ quit(99);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/logs", get_PKGADM());
+
+ if (mkpath(path)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", path);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), path);
+ }
+ quit(99);
+ }
+
+ if (mkpath(PKGSCR)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", PKGSCR);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), PKGSCR);
+ }
+ quit(99);
+ }
+
+ if (mkpath(get_PKGLOC())) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", get_PKGLOC());
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), get_PKGLOC());
+ }
+ quit(99);
+ }
+}
+
+/*
+ * Return value: int
+ * 0 - success
+ * 99 - failure
+ */
+
+int
+ckpkgdirs(void)
+{
+ boolean_t nonExistentPkgloc = B_FALSE;
+
+ /*
+ * If pkgloc doesn't exist make sure it gets removed after creating
+ * it if this is a preinstall check. All dryrun and preinstallation
+ * checks must not modify the file system.
+ */
+
+ if (access(pkgloc, F_OK) != 0) {
+ nonExistentPkgloc = B_TRUE;
+ }
+
+ if (mkpath(pkgloc)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", pkgloc);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), pkgloc);
+ }
+ return (99);
+ }
+
+ if (mkpath(pkgbin)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", pkgbin);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), pkgbin);
+ }
+ return (99);
+ }
+
+ if (mkpath(pkgsav)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", pkgsav);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), pkgsav);
+ }
+ return (99);
+ }
+
+ if (!is_spool_create() && mkpath(saveSpoolInstallDir)) {
+ if (preinstallCheck == B_TRUE) {
+ (void) fprintf(stdout, "ckdirs=%s\n", pkgsav);
+ } else {
+ progerr(gettext(MSG_MKPKGDIR), pkgsav);
+ }
+ return (99);
+ }
+
+ if (preinstallCheck && nonExistentPkgloc) {
+ rrmdir(pkgloc);
+ }
+
+ return (0);
+}
+
+/*
+ * 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
+ */
+
+int
+ckconflct(void)
+{
+ int i, n, count, has_a_rogue = 0;
+ char ans[MAX_INPUT];
+
+ if (ADM(conflict, "nochange")) {
+ nocnflct++;
+ return (0);
+ }
+
+ if (ADM(conflict, "nocheck")) {
+ return (0);
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(gettext(MSG_CKCONFL_GZ));
+ } else {
+ echo(gettext(MSG_CKCONFL_LZ), zoneName);
+ }
+
+ count = 0;
+ for (i = 0; extlist[i]; i++) {
+ struct cfent *ept;
+ struct mergstat *mstat;
+
+ if (extlist[i]->cf_ent.ftype == 'i') {
+ continue;
+ }
+
+ ept = &(extlist[i]->cf_ent);
+ mstat = &(extlist[i]->mstat);
+
+ if (is_remote_fs(ept->path, &(extlist[i]->fsys_value)) &&
+ !is_fs_writeable(ept->path,
+ &(extlist[i]->fsys_value))) {
+ continue;
+ }
+
+ /*
+ * If no other package claims it or it's from a continuation
+ * file, skip it.
+ */
+ if (!mstat->shared || mstat->preloaded) {
+ continue;
+ }
+
+ if (ept->ftype == 'e') {
+ continue;
+ }
+
+ if (mstat->rogue) {
+ has_a_rogue = 1;
+ }
+
+ if (mstat->contchg) {
+ if (!count++) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_CONFLICT));
+ }
+ } else if ((echoGetFlag() == B_TRUE) &&
+ ((count % DISPSIZ) == 0)) {
+ echo(gettext(MSG_CONTDISP));
+ (void) getc(stdin);
+ }
+ /*
+ * NOTE : The leading "!" in this string forces
+ * puttext() to print leading white space.
+ */
+
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, "!%s %s",
+ (mstat->rogue) ? "*" : " ", ept->path);
+ } else {
+ (void) fprintf(stdout,
+ "conflict-contents=%s\n", ept->path);
+ }
+ } else if (mstat->attrchg) {
+ if (!count++) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_CONFLICT));
+ }
+ } else if ((echoGetFlag() == B_TRUE) &&
+ ((count % DISPSIZ) == 0)) {
+ echo(gettext(MSG_CONTDISP));
+ (void) getc(stdin);
+ }
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_ATTRONLY),
+ (mstat->rogue) ? "*" : " ", ept->path);
+ } else {
+ (void) fprintf(stdout,
+ "conflict-attributes=%s\n", ept->path);
+ }
+ }
+ }
+
+ if (count) {
+ if (has_a_rogue) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_ROGUE));
+ }
+ }
+
+ msgtext = gettext(ERR_CNFFAILED);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(conflict, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFLICT),
+ gettext(ASK_CONFLICT))) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ ckquit = 0;
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONT),
+ ask_cont)) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ ckquit = 1;
+ nocnflct++;
+ rprcflag++;
+ }
+ }
+ return (0);
+}
+
+/*
+ * 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
+ */
+
+int
+cksetuid(void)
+{
+ int i, n, count, overwriting = 0;
+ char ans[MAX_INPUT];
+
+ /* See if the administrative defaults already resolve this check. */
+ if (ADM(setuid, "nocheck")) {
+ return (0);
+ }
+
+ if (ADM(setuid, "nochange")) {
+ nosetuid++; /* Do not install processes as setuid/gid. */
+ return (0);
+ }
+
+ /* The administrative defaults require review of the package. */
+
+ if (zoneName == (char *)NULL) {
+ echo(gettext(MSG_CKUID_GZ));
+ } else {
+ echo(gettext(MSG_CKUID_LZ), zoneName);
+ }
+
+ count = 0;
+ for (i = 0; extlist[i]; i++) {
+ int overwr;
+ struct mergstat *mstat = &(extlist[i]->mstat);
+
+ /*
+ * Provide the administrator with info as to whether there is
+ * already a setuid process in place. This is only necessary
+ * to help the administrator decide whether or not to lay
+ * down the process, it doesn't have anything to do with the
+ * administrative defaults.
+ */
+ if (mstat->osetuid || mstat->osetgid) {
+ overwr = 1;
+ overwriting = 1;
+ } else
+ overwr = 0;
+
+ if (mstat->setuid || mstat->setgid) {
+ if (!count++) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_SETUID));
+ }
+ } else if ((echoGetFlag() == B_TRUE) &&
+ ((count % DISPSIZ) == 0)) {
+ echo(gettext(MSG_CONTDISP));
+ (void) getc(stdin);
+ }
+ /*
+ * NOTE : The leading "!" in these strings forces
+ * puttext() to print leading white space.
+ */
+
+ if (mstat->setuid && mstat->setgid) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(
+ "!%s %s <setuid %s setgid %s>"),
+ (overwr) ? "*" : " ",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.owner,
+ extlist[i]->cf_ent.ainfo.group);
+ } else {
+ (void) fprintf(stdout, "setuid=%s:%s\n",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.owner);
+ (void) fprintf(stdout, "setgid=%s:%s\n",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.group);
+ }
+ } else if (mstat->setuid) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(
+ "!%s %s <setuid %s>"),
+ (overwr) ? "*" : " ",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.owner);
+ } else {
+ (void) fprintf(stdout, "setuid=%s:%s\n",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.owner);
+ }
+ } else if (mstat->setgid) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(
+ "!%s%s <setgid %s>"),
+ (overwr) ? "*" : " ",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.group);
+ } else {
+ (void) fprintf(stdout, "setgid=%s:%s\n",
+ extlist[i]->cf_ent.path,
+ extlist[i]->cf_ent.ainfo.group);
+ }
+ }
+ }
+ }
+
+ if (count) {
+ if (overwriting) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_OVERWR));
+ } else {
+ (void) fprintf(stdout,
+ "setuid-overwrite=true\n");
+ }
+ }
+
+ msgtext = gettext(MSG_UIDFND);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(setuid, "quit")) {
+ return (4);
+ }
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+ msgtext = NULL;
+
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_SETUID),
+ gettext(ASK_SETUID))) {
+ return (n);
+ }
+ if (strchr("yY", *ans) == NULL) {
+ ckquit = 0;
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONT),
+ ask_cont)) {
+ return (n);
+ }
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ ckquit = 1;
+ nosetuid++;
+ rprcflag++;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * 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
+ */
+
+int
+ckpriv(void)
+{
+ struct dirent *dp;
+ DIR *dirfp;
+ int n, found;
+ char ans[MAX_INPUT], path[PATH_MAX];
+
+ if (ADM(action, "nocheck")) {
+ return (0);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/install", instdir);
+ if ((dirfp = opendir(path)) == NULL) {
+ return (0);
+ }
+
+ found = 0;
+ while ((dp = readdir(dirfp)) != NULL) {
+ if (strcmp(dp->d_name, "preinstall") == 0 ||
+ strcmp(dp->d_name, "postinstall") == 0 ||
+ strncmp(dp->d_name, "i.", 2) == 0) {
+ found++;
+ break;
+ }
+ }
+ (void) closedir(dirfp);
+
+ if (found) {
+ if (preinstallCheck == B_FALSE) {
+ ptext(stderr, gettext(MSG_PRIV));
+ msgtext = gettext(MSG_SCRFND);
+ }
+ (void) snprintf(ask_cont, sizeof (ask_cont),
+ gettext(ASK_CONT), pkginst);
+
+ if (preinstallCheck == B_TRUE) {
+ return (4);
+ }
+
+ if (ADM(action, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ ckquit = 0;
+ if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PRIV),
+ ask_cont)) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ ckquit = 1;
+ }
+
+ return (0);
+}
+
+/*
+ * Return value: int
+ * 0 - success
+ * 99 - failure
+ */
+
+int
+ckpkgfiles(void)
+{
+ register int i;
+ struct cfent *ept;
+ int errflg;
+ char source[PATH_MAX];
+
+ errflg = 0;
+ for (i = 0; extlist[i]; i++) {
+ ept = &(extlist[i]->cf_ent);
+ if (ept->ftype != 'i') {
+ continue;
+ }
+
+ if (ept->ainfo.local) {
+ (void) snprintf(source, sizeof (source),
+ "%s/%s", instdir, ept->ainfo.local);
+ } else if (strcmp(ept->path, PKGINFO) == 0) {
+ (void) snprintf(source, sizeof (source),
+ "%s/%s", instdir, ept->path);
+ } else {
+ (void) snprintf(source, sizeof (source),
+ "%s/install/%s", instdir, ept->path);
+ }
+ if (cverify(0, &ept->ftype, source, &ept->cinfo, 1)) {
+ errflg++;
+ if (preinstallCheck == B_FALSE) {
+ progerr(gettext(ERR_BADFILE), source);
+ logerr(getErrbufAddr());
+ } else {
+ (void) fprintf(stdout, "ckpkgfilebad=%s",
+ source);
+ }
+ }
+ }
+
+ if (errflg) {
+ return (99);
+ } else {
+ return (0);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/cppath.c b/usr/src/cmd/svr4pkg/pkginstall/cppath.c
new file mode 100644
index 0000000000..bded634e29
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/cppath.c
@@ -0,0 +1,376 @@
+/*
+ * 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 2005 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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libadm.h"
+#include "libinst.h"
+#include "install.h"
+#include "messages.h"
+#include "pkginstall.h"
+
+/*
+ * forward declarations
+ */
+
+static int write_file(char **r_linknam, int a_ctrl, mode_t a_mode,
+ char *a_file);
+static int create_path(int a_ctrl, char *a_file);
+
+/*
+ * Name: cppath
+ * Description: copy a path object (install new file on system)
+ * Arguments:
+ * - a_cntrl - determine how the destination file mode is set:
+ * |= MODE_0666 - force mode to 0666
+ * |= MODE_SET - mode is a_mode (no mask SET?ID bits)
+ * |= MODE_SRC - mode from source file (mask SET?ID bits)
+ * |= DIR_DISPLAY - display "%s <implied directory>" if directory created
+ * - a_srcPath - path to source to copy
+ * - a_dstPath - path to copy source to
+ * - a_mode - mode to set a_dstpath to (mode controlled by a_ctrl)
+ * Returns: int
+ * == 0 - success
+ * != 0 - failure
+ */
+
+int
+cppath(int a_ctrl, char *a_srcPath, char *a_dstPath, mode_t a_mode)
+{
+ char *linknam = (char *)NULL;
+ int dstFd;
+ int len;
+ int srcFd;
+ long status;
+ struct stat srcStatbuf;
+ struct utimbuf times;
+
+ /* entry debugging info */
+
+ echoDebug(DBG_CPPATH_ENTRY, a_ctrl, a_mode, a_srcPath, a_dstPath);
+
+ /* open source file for reading */
+
+ srcFd = open(a_srcPath, O_RDONLY);
+ if (srcFd < 0) {
+ progerr(ERR_OPEN_READ, a_srcPath,
+ errno, strerror(errno));
+ return (1);
+ }
+
+ /* obtain file status of source file */
+
+ if (fstat(srcFd, &srcStatbuf) != 0) {
+ progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno));
+ (void) close(srcFd);
+ return (1);
+ }
+
+ /*
+ * Determine the permissions mode for the destination:
+ * - if MODE_SET is specified:
+ * --> use a_mode (do not mask off any portion)
+ * --> If a_mode is unknown (? in the pkgmap), then the file gets
+ * --> installed with the default 0644 mode
+ * - if MODE_SRC is specified:
+ * --> use the mode of the source (srcStatbuf.st_mode) but mask off all
+ * --> non-access mode bits (remove SET?UID bits)
+ * - otherwise:
+ * --> use 0666
+ */
+
+ if (a_ctrl & MODE_SET) {
+ mode_t usemode;
+
+ usemode = (a_mode ^ BADMODE) ? a_mode : 0644;
+ if (a_mode != usemode && usemode == 0644) {
+ logerr(WRN_DEF_MODE, a_dstPath);
+ a_mode = usemode;
+ }
+ } else if (a_ctrl & MODE_SRC) {
+ a_mode = (srcStatbuf.st_mode & S_IAMB);
+ } else {
+ a_mode = 0666;
+ }
+
+ /*
+ * Get fd of newly created destination file or, if this
+ * is an overwrite, a temporary file (linknam).
+ */
+
+ dstFd = write_file(&linknam, a_ctrl, a_mode, a_dstPath);
+ if (dstFd < 0) {
+ (void) close(srcFd);
+ return (1);
+ }
+
+ /*
+ * source and target files are open: copy data
+ */
+
+ status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0);
+
+ (void) close(srcFd);
+ (void) close(dstFd);
+
+ if (status != 0) {
+ progerr(ERR_INPUT, a_srcPath, errno, strerror(errno));
+ if (linknam) {
+ (void) remove(linknam);
+ }
+ return (1);
+ }
+
+ /*
+ * If this is an overwrite, rename temp over original
+ */
+
+ if ((linknam != (char *)NULL) && (rename(linknam, a_dstPath) != 0)) {
+ FILE *logfp = (FILE *)NULL;
+ char busylog[PATH_MAX];
+
+ /* output log message if busy else program error */
+
+ if (errno == ETXTBSY) {
+ logerr(MSG_PROCMV, linknam);
+ } else {
+ progerr(ERR_OUTPUT_WRITING, a_dstPath, errno,
+ strerror(errno));
+ }
+
+ (void) remove(linknam);
+
+ /* open the log file and append log entry */
+
+ len = snprintf(busylog, sizeof (busylog),
+ "%s/textbusy", get_PKGADM());
+ if (len > sizeof (busylog)) {
+ progerr(ERR_CREATE_PATH_2, get_PKGADM(),
+ "textbusy");
+ } else {
+ logfp = fopen(busylog, "a");
+ if (logfp == NULL) {
+ progerr(ERR_LOG, busylog, errno,
+ strerror(errno));
+ } else {
+ (void) fprintf(logfp, "%s\n", linknam);
+ (void) fclose(logfp);
+ }
+ }
+ }
+
+ /* set access/modification times for target */
+
+ times.actime = srcStatbuf.st_atime;
+ times.modtime = srcStatbuf.st_mtime;
+
+ if (utime(a_dstPath, &times) != 0) {
+ progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno));
+ return (1);
+ }
+
+ /* success! */
+
+ return (0);
+}
+
+/*
+ * This function creates all of the directory components of the specified path.
+ */
+static int
+create_path(int a_ctrl, char *a_file)
+{
+ char *pt;
+ int found = 0;
+
+ for (pt = a_file; *pt; pt++) {
+ /* continue if not at path separator or at start of path */
+
+ if ((*pt != '/') || (pt == a_file)) {
+ continue;
+ }
+
+ /* at '/' - terminate path at current entry */
+
+ *pt = '\0';
+
+ /* continue if path element exists */
+
+ if (access(a_file, F_OK) == 0) {
+ *pt = '/';
+ continue;
+ }
+
+ /* create directory in path */
+
+ if (mkdir(a_file, 0755)) {
+ progerr(ERR_MAKE_DIR, a_file, errno, strerror(errno));
+ *pt = '/';
+ return (1);
+ }
+
+ /* display 'implied directory created' message */
+
+ if (a_ctrl & DIR_DISPLAY) {
+ echo(MSG_IMPDIR, a_file);
+ }
+
+ found++;
+
+ *pt = '/';
+ }
+
+ return (!found);
+}
+
+/*
+ * Name: write_file
+ * Description: creates a new destination file if the file does not already
+ * exist; otherwise, creates a temporary file and places a
+ * pointer to the temporary file name in 'r_linknam'.
+ * Arguments: r_linknam - pointer to (char*) where name of temporary file
+ * created is returned
+ * a_ctrl - determine if the destination file name is displayed:
+ * |= DIR_DISPLAY - display "%s <implied directory>"
+ * if directory created
+ * a_mode - permissions mode to set a_file to
+ * a_file - name of destination file to open
+ * Returns: int
+ * success - file descriptor of the file it opened.
+ * failure - returns -1
+ */
+
+static int
+write_file(char **r_linknam, int a_ctrl, mode_t a_mode, char *a_file)
+{
+ int len;
+ int fd = -1;
+ static char loc_link[PATH_MAX];
+
+ /* entry debugging */
+
+ echoDebug(DBG_WRITEFILE_ENTRY, a_ctrl, a_mode, a_file);
+
+ /* reset pointer to returned 'temporary file name' */
+
+ *r_linknam = (char *)NULL;
+
+ /*
+ * If we are overwriting an existing file, arrange to replace
+ * it transparently.
+ */
+
+ if (access(a_file, F_OK) == 0) {
+ /*
+ * link the file to be copied to a temporary name in case
+ * it is executing or it is being written/used (e.g., a shell
+ * script currently being executed
+ */
+
+ if (!RELATIVE(a_file)) {
+ len = snprintf(loc_link, sizeof (loc_link),
+ "%sXXXXXX", a_file);
+ if (len > sizeof (loc_link)) {
+ progerr(ERR_CREATE_PATH_2, a_file, "XXXXXX");
+ }
+ } else {
+ logerr(WRN_RELATIVE, a_file);
+ len = snprintf(loc_link, sizeof (loc_link),
+ "./%sXXXXXX", a_file);
+ if (len > sizeof (loc_link)) {
+ progerr(ERR_CREATE_PATH_3, "./", a_file,
+ "XXXXXX");
+ }
+ }
+
+ /* create and open temporary file */
+
+ fd = mkstemp(loc_link);
+ if (fd == -1) {
+ progerr(ERR_MKTEMP, loc_link, errno, strerror(errno));
+ return (-1);
+ }
+
+ /* remember name of temporary file */
+
+ *r_linknam = loc_link;
+
+ /* make sure temporary file has correct mode */
+
+ if (fchmod(fd, a_mode) < 0) {
+ progerr(ERR_FCHMOD, loc_link, a_mode, errno,
+ strerror(errno));
+ }
+
+ return (fd);
+ }
+
+ /*
+ * We are not overwriting an existing file, create a new one directly.
+ */
+
+ fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
+ if (fd == -1) {
+ if (create_path(a_ctrl, a_file) == 0) {
+ fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
+ }
+ }
+
+ if (fd == -1) {
+ progerr(ERR_OPEN_WRITE, a_file, errno, strerror(errno));
+ }
+
+ return (fd);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/dockspace.c b/usr/src/cmd/svr4pkg/pkginstall/dockspace.c
new file mode 100644
index 0000000000..e306e0dbef
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/dockspace.c
@@ -0,0 +1,405 @@
+/*
+ * 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 2008 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 <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <limits.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+
+extern struct cfextra **extlist;
+extern char pkgloc[];
+extern char instdir[];
+
+#define LSIZE 256
+#define LIM_BFREE 150LL
+#define LIM_FFREE 25LL
+
+#define WRN_STATVFS "WARNING: unable to stat filesystem mounted on <%s>"
+
+#define WRN_NOBLKS "The %s filesystem has %llu free blocks. The current " \
+ "installation requires %llu blocks, which includes a " \
+ "required %llu block buffer for open " \
+ "deleted files. %llu more blocks are needed."
+
+#define WRN_NOFILES "The %s filesystem has %llu free file nodes. The " \
+ "current installation requires %llu file nodes, " \
+ "which includes a required %llu file node buffer " \
+ "for temporary files. %llu more file nodes " \
+ "are needed."
+
+#define TYPE_BLCK 0
+#define TYPE_NODE 1
+static void warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail,
+ fsblkcnt_t limit);
+static int fsys_stat(int n);
+static int readmap(int *error);
+static int readspace(char *spacefile, int *error);
+
+int
+dockspace(char *spacefile)
+{
+ struct fstable *fs_tab;
+ int i, error;
+
+ error = 0;
+
+ /*
+ * Also, vanilla SVr4 code used the output from popen()
+ * on the "/etc/mount" command. However, we need to get more
+ * information about mounted filesystems, so we use the C
+ * interfaces to the mount table, which also happens to be
+ * much faster than running another process. Since several
+ * of the pkg commands need access to the mount table, this
+ * code is now in libinst. However, mount table info is needed
+ * at the time the base directory is determined, so the call
+ * to get the mount table information is in main.c
+ */
+
+ if (readmap(&error) || readspace(spacefile, &error))
+ return (-1);
+
+ for (i = 0; fs_tab = get_fs_entry(i); ++i) {
+ if ((!fs_tab->fused) && (!fs_tab->bused))
+ continue; /* not used by us */
+
+ if (fs_tab->bfree < (LIM_BFREE + fs_tab->bused)) {
+ warn(TYPE_BLCK, fs_tab->name, fs_tab->bused,
+ fs_tab->bfree, LIM_BFREE);
+ error++;
+ }
+
+ /* bug id 1091292 */
+ if ((long)fs_tab->ffree == -1L)
+ continue;
+ if (fs_tab->ffree < (LIM_FFREE + fs_tab->fused)) {
+ warn(TYPE_NODE, fs_tab->name, fs_tab->fused,
+ fs_tab->ffree, LIM_FFREE);
+ error++;
+ }
+ }
+ return (error);
+}
+
+static void
+warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail, fsblkcnt_t limit)
+{
+ logerr(gettext("WARNING:"));
+ if (type == TYPE_BLCK) {
+ logerr(gettext(WRN_NOBLKS), name, avail, (need + limit), limit,
+ (need + limit - avail));
+ } else {
+ logerr(gettext(WRN_NOFILES), name, avail, (need + limit), limit,
+ (need + limit - avail));
+ }
+}
+
+static int
+fsys_stat(int n)
+{
+ struct statvfs64 svfsb;
+ struct fstable *fs_tab;
+
+ if (n == BADFSYS)
+ return (1);
+
+ fs_tab = get_fs_entry(n);
+
+ /*
+ * At this point, we know we need information
+ * about a particular filesystem, so we can do the
+ * statvfs() now. For performance reasons, we only want to
+ * stat the filesystem once, at the first time we need to,
+ * and so we can key on whether or not we have the
+ * block size for that filesystem.
+ */
+ if (fs_tab->bsize != 0)
+ return (0);
+
+ if (statvfs64(fs_tab->name, &svfsb)) {
+ logerr(gettext(WRN_STATVFS), fs_tab->name);
+ return (1);
+ }
+
+ /*
+ * statvfs returns number of fragment size blocks
+ * so will change this to number of 512 byte blocks
+ */
+ fs_tab->bsize = svfsb.f_bsize;
+ fs_tab->frsize = svfsb.f_frsize;
+ fs_tab->bfree = ((svfsb.f_frsize > 0) ?
+ howmany(svfsb.f_frsize, DEV_BSIZE) :
+ howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail;
+ fs_tab->ffree = (svfsb.f_favail > 0) ? svfsb.f_favail : svfsb.f_ffree;
+ return (0);
+}
+
+/*
+ * This function reads all of the package objects, maps them to their target
+ * filesystems and adds up the amount of space used on each. Wherever you see
+ * "fsys_value", that's the apparent filesystem which could be a temporary
+ * loopback mount for the purpose of constructing the client filesystem. It
+ * isn't necessarily the real target filesystem. Where you see "fsys_base"
+ * that's the real filesystem to which fsys_value may just refer. If this is
+ * installing to a standalone or a server, fsys_value will almost always be
+ * the same as fsys_base.
+ */
+static int
+readmap(int *error)
+{
+ struct fstable *fs_tab;
+ struct cfextra *ext;
+ struct cfent *ept;
+ struct stat statbuf;
+ char tpath[PATH_MAX];
+ fsblkcnt_t blk;
+ int i, n;
+
+ /*
+ * Handle the installation files (ftype i) that are in the
+ * pkgmap/eptlist.
+ */
+ for (i = 0; (ext = extlist[i]) != NULL; i++) {
+ ept = &(ext->cf_ent);
+
+ if (ept->ftype != 'i')
+ continue;
+
+ /*
+ * These paths are treated differently from the others
+ * since their full pathnames are not included in the
+ * pkgmap.
+ */
+ if (strcmp(ept->path, "pkginfo") == 0)
+ (void) sprintf(tpath, "%s/%s", pkgloc, ept->path);
+ else
+ (void) sprintf(tpath, "%s/install/%s", pkgloc,
+ ept->path);
+
+ /* If we haven't done an fsys() series, do one */
+ if (ext->fsys_value == BADFSYS)
+ ext->fsys_value = fsys(tpath);
+
+ /*
+ * Now check if this is a base or apparent filesystem. If
+ * it's just apparent, get the resolved filesystem entry,
+ * otherwise, base and value are the same.
+ */
+ if (use_srvr_map_n(ext->fsys_value))
+ ext->fsys_base = resolved_fsys(tpath);
+ else
+ ext->fsys_base = ext->fsys_value;
+
+ if (fsys_stat(ext->fsys_base)) {
+ (*error)++;
+ continue;
+ }
+
+ /*
+ * Don't accumulate space requirements on read-only
+ * remote filesystems.
+ */
+ if (is_remote_fs_n(ext->fsys_value) &&
+ !is_fs_writeable_n(ext->fsys_value))
+ continue;
+
+ fs_tab = get_fs_entry(ext->fsys_base);
+
+ fs_tab->fused++;
+ if (ept->cinfo.size != BADCONT)
+ blk = nblk(ept->cinfo.size,
+ fs_tab->bsize,
+ fs_tab->frsize);
+ else
+ blk = 0;
+ fs_tab->bused += blk;
+ }
+
+ /*
+ * Handle the other files in the eptlist.
+ */
+ for (i = 0; (ext = extlist[i]) != NULL; i++) {
+ ept = &(extlist[i]->cf_ent);
+
+ if (ept->ftype == 'i')
+ continue;
+
+ /*
+ * Don't recalculate package objects that are already in the
+ * table.
+ */
+ if (ext->mstat.preloaded)
+ continue;
+
+ /*
+ * Don't accumulate space requirements on read-only
+ * remote filesystems.
+ */
+ if (is_remote_fs(ept->path, &(ext->fsys_value)) &&
+ !is_fs_writeable(ept->path, &(ext->fsys_value)))
+ continue;
+
+ /*
+ * Now check if this is a base or apparent filesystem. If
+ * it's just apparent, get the resolved filesystem entry,
+ * otherwise, base and value are the same.
+ */
+ if (use_srvr_map_n(ext->fsys_value))
+ ext->fsys_base = resolved_fsys(tpath);
+ else
+ ext->fsys_base = ext->fsys_value;
+
+ /* At this point we know we have a good fsys_base. */
+ if (fsys_stat(ext->fsys_base)) {
+ (*error)++;
+ continue;
+ }
+
+ /*
+ * We have to stat this path based upon it's real location.
+ * If this is a server-remap, ept->path isn't the real
+ * location.
+ */
+ if (use_srvr_map_n(ext->fsys_value))
+ strcpy(tpath, server_map(ept->path, ext->fsys_value));
+ else
+ strcpy(tpath, ept->path);
+
+ fs_tab = get_fs_entry(ext->fsys_base);
+ if (stat(tpath, &statbuf)) {
+ /* path cannot be accessed */
+ fs_tab->fused++;
+ if (strchr("dxs", ept->ftype))
+ blk =
+ nblk(fs_tab->bsize,
+ fs_tab->bsize,
+ fs_tab->frsize);
+ else if (ept->cinfo.size != BADCONT)
+ blk = nblk(ept->cinfo.size,
+ fs_tab->bsize,
+ fs_tab->frsize);
+ else
+ blk = 0;
+ } else {
+ /* path already exists */
+ if (strchr("dxs", ept->ftype))
+ blk = 0;
+ else if (ept->cinfo.size != BADCONT) {
+ fsblkcnt_t new_size, old_size;
+ new_size = nblk(ept->cinfo.size,
+ fs_tab->bsize,
+ fs_tab->frsize);
+ old_size = nblk(statbuf.st_size,
+ fs_tab->bsize,
+ fs_tab->frsize);
+ /*
+ * negative blocks show room freed, but since
+ * order of installation is uncertain show
+ * 0 blocks usage
+ */
+ if (new_size < old_size)
+ blk = 0;
+ else
+ blk = new_size - old_size;
+ } else
+ blk = 0;
+ }
+ fs_tab->bused += blk;
+ }
+ return (0);
+}
+
+static int
+readspace(char *spacefile, int *error)
+{
+ FILE *fp;
+ char line[LSIZE];
+ long blocks, nodes;
+ int n;
+
+ if (spacefile == NULL)
+ return (0);
+
+ if ((fp = fopen(spacefile, "r")) == NULL) {
+ progerr(gettext("unable to open spacefile %s"), spacefile);
+ return (-1);
+ }
+
+ while (fgets(line, LSIZE, fp)) {
+ struct fstable *fs_tab;
+ char *pt, path[PATH_MAX];
+
+ blocks = nodes = 0;
+ for (pt = line; isspace(*pt); /* void */)
+ pt++;
+ if (*pt == '#' || *pt == '\0')
+ continue;
+
+ (void) sscanf(line, "%s %ld %ld", path, &blocks, &nodes);
+ mappath(2, path);
+ basepath(path, get_basedir(), get_inst_root());
+ canonize(path);
+
+ n = resolved_fsys(path);
+ if (fsys_stat(n)) {
+ (*error)++;
+ continue;
+ }
+
+ /*
+ * Don't accumulate space requirements on read-only
+ * remote filesystems. NOTE: For some reason, this
+ * used to check for !remote && read only. If this
+ * blows up later, then maybe that was correct -- JST
+ */
+ if (is_remote_fs_n(n) && !is_fs_writeable_n(n))
+ continue;
+
+ fs_tab = get_fs_entry(n);
+
+ fs_tab->bused += blocks;
+ fs_tab->fused += nodes;
+ }
+ (void) fclose(fp);
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/getinst.c b/usr/src/cmd/svr4pkg/pkginstall/getinst.c
new file mode 100644
index 0000000000..35634b3e1b
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/getinst.c
@@ -0,0 +1,298 @@
+/*
+ * 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 2006 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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <valtools.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkginfo.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern struct admin adm;
+extern char *pkgarch, *pkgvers, *msgtext, *pkgabrv;
+extern int opresvr4, maxinst;
+
+static char newinst[PKGSIZ];
+static char *nextinst(void);
+static char *prompt(struct pkginfo *info, int npkgs);
+static int same_pkg; /* same PKG, ARCH and VERSION */
+
+/*
+ * This returns the correct package instance based on how many packages are
+ * already installed. If there are none (npkgs == 0), it just returns the
+ * package abbreviation. Otherwise, it interacts with the user (or reads the
+ * admin file) to determine if we should overwrite an instance which is
+ * already installed, or possibly install a new instance of this package
+ */
+char *
+getinst(int *updatingExisting, struct pkginfo *info, int npkgs,
+ boolean_t a_preinstallCheck)
+{
+ char *inst;
+ char *sameinst;
+ int i;
+ int nsamearch;
+ int samearch;
+
+ /* entry debugging info */
+
+ same_pkg = 0;
+
+ /*
+ * If this is the first instance of the package, it's called the by
+ * the package abbreviation.
+ */
+
+ if (npkgs == 0) {
+ return (pkgabrv);
+ }
+
+ /*
+ * this package is already installed; determine how to handle the
+ * new instance of the package to install
+ */
+
+ if (ADM(instance, "newonly") || ADM(instance, "quit")) {
+ /*
+ * new instance is required, or quit if not new
+ */
+
+ msgtext = MSG_NEWONLY;
+ if (a_preinstallCheck == B_FALSE) {
+ ptext(stderr, msgtext, pkgabrv);
+ } else {
+ (void) fprintf(stdout, "install-new-only=true\n");
+ (void) fprintf(stdout, "ckinstance=4\n");
+ }
+ quit(4);
+ }
+
+ /*
+ * package already installed and new instance not required
+ * see if updating the same instance of the package
+ */
+
+ samearch = nsamearch = 0;
+ sameinst = NULL;
+ for (i = 0; i < npkgs; i++) {
+ if (strcmp(info[i].arch, pkgarch) == NULL) {
+ samearch = i;
+ nsamearch++;
+ if (strcmp(info[i].version, pkgvers) == NULL) {
+ sameinst = info[i].pkginst;
+ }
+ }
+ }
+
+ if (sameinst) {
+ /* same instance of package */
+ if (a_preinstallCheck == B_FALSE) {
+ ptext(stderr, MSG_SAME);
+ } else {
+ (void) fprintf(stdout, "install-same-instance=true\n");
+ (void) fprintf(stdout, "ckinstance=0\n");
+ }
+
+ inst = sameinst; /* can't be overwriting a pre-svr4 package */
+ same_pkg++;
+ (*updatingExisting)++;
+ return (inst);
+ }
+
+ if (ADM(instance, "overwrite")) {
+ /* not the same instance of the package */
+ if (npkgs == 1) {
+ samearch = 0; /* use only package we know about */
+ } else if (nsamearch != 1) {
+ /*
+ * more than one instance of the same ARCH is already
+ * installed on this machine
+ */
+ msgtext = MSG_OVERWRITE;
+ if (a_preinstallCheck == B_FALSE) {
+ ptext(stderr, msgtext);
+ } else {
+ (void) fprintf(stdout,
+ "install-ovewrite=true\n");
+ (void) fprintf(stdout, "ckinstance=4\n");
+ }
+ quit(4);
+ }
+
+ inst = info[samearch].pkginst;
+ if (info[samearch].status == PI_PRESVR4) {
+ opresvr4++; /* overwriting a pre-svr4 package */
+ }
+
+ (*updatingExisting)++;
+ return (inst);
+ }
+
+ if (ADM(instance, "unique")) {
+ if (maxinst <= npkgs) {
+ /* too many instances */
+ msgtext = MSG_UNIQ1;
+ if (a_preinstallCheck == B_FALSE) {
+ ptext(stderr, msgtext, pkgabrv);
+ } else {
+ (void) fprintf(stdout,
+ "install-too-many-instances=true\n");
+ (void) fprintf(stdout, "ckinstance=4\n");
+ }
+ quit(4);
+ }
+ inst = nextinst();
+ return (inst);
+ }
+
+ if (a_preinstallCheck == B_FALSE) {
+ if (echoGetFlag() == B_FALSE) {
+ msgtext = MSG_NOINTERACT;
+ ptext(stderr, msgtext);
+ quit(5);
+ }
+ } else {
+ (void) fprintf(stdout, "install-new-instance=true\n");
+ (void) fprintf(stdout, "ckinstance=1\n");
+ }
+
+ inst = prompt(info, npkgs);
+ if (strcmp(inst, "new") == NULL) {
+ inst = nextinst();
+ return (inst);
+ }
+
+ (*updatingExisting)++;
+
+ /* see if this instance is presvr4 */
+ for (i = 0; i < npkgs; i++) {
+ if (strcmp(inst, info[i].pkginst) == NULL) {
+ if (info[i].status == PI_PRESVR4) {
+ opresvr4++;
+ }
+ break;
+ }
+ }
+
+ return (inst);
+}
+
+/*
+ * This informs the caller whether the package in question is the same
+ * version and architecture as an installed package of the same name.
+ */
+
+int
+is_samepkg(void) {
+ return (same_pkg);
+}
+
+static char *
+nextinst(void)
+{
+ struct pkginfo info;
+ int n;
+
+ n = 2; /* requirements say start at 2 */
+
+ info.pkginst = NULL;
+ (void) strcpy(newinst, pkgabrv);
+ while (pkginfo(&info, newinst, NULL, NULL) == 0) {
+ (void) snprintf(newinst, sizeof (newinst),
+ "%s.%d", pkgabrv, n++);
+ }
+ return (newinst);
+}
+
+static char *
+prompt(struct pkginfo *info, int npkgs)
+{
+ CKMENU *menup;
+ char *inst;
+ char ans[MAX_INPUT];
+ char header[256];
+ char temp[256];
+ int i;
+ int n;
+
+ if (maxinst > npkgs) {
+ /*
+ * the user may choose to install a completely new
+ * instance of this package
+ */
+ n = ckyorn(ans, NULL, NULL, MSG_GETINST_HELP1,
+ MSG_GETINST_PROMPT1);
+ if (n != 0) {
+ quit(n);
+ }
+ if (strchr("yY", *ans) != NULL) {
+ return ("new");
+ }
+ }
+
+ (void) snprintf(header, sizeof (header), MSG_GETINST_HEADER, pkgabrv);
+ menup = allocmenu(header, CKALPHA);
+
+ for (i = 0; i < npkgs; i++) {
+ (void) snprintf(temp, sizeof (temp),
+ "%s %s\n(%s) %s", info[i].pkginst,
+ info[i].name, info[i].arch, info[i].version);
+ if (setitem(menup, temp)) {
+ progerr("no memory");
+ quit(99);
+ }
+ }
+
+ if (npkgs == 1) {
+ printmenu(menup);
+ if (n = ckyorn(ans, NULL, NULL, NULL, MSG_GETINST_PROMPT0))
+ quit(n);
+ if (strchr("yY", *ans) == NULL)
+ quit(3);
+ (void) strcpy(newinst, info[0].pkginst);
+ } else {
+ if (n = ckitem(menup, &inst, 1, NULL, NULL, MSG_GETINST_HELP2,
+ MSG_GETINST_PROMPT2))
+ quit(n);
+ (void) strcpy(newinst, inst);
+ }
+ (void) setitem(menup, 0); /* clear resource usage */
+ free(menup); /* clear resource usage */
+
+ return (newinst);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/instvol.c b/usr/src/cmd/svr4pkg/pkginstall/instvol.c
new file mode 100644
index 0000000000..803538a700
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/instvol.c
@@ -0,0 +1,1781 @@
+/*
+ * 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 <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <archives.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <wait.h>
+
+/*
+ * libinstzones includes
+ */
+
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+#include <pkgweb.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <dryrun.h>
+#include <messages.h>
+
+/*
+ * pkginstall local includes
+ */
+
+#include "pkginstall.h"
+
+extern int pkgverbose;
+extern fsblkcnt_t pkgmap_blks; /* main.c */
+
+extern struct pkgdev pkgdev;
+
+extern char tmpdir[];
+extern char pkgbin[];
+extern char instdir[];
+extern char saveSpoolInstallDir[];
+extern char *pkginst;
+
+extern int dbchg;
+extern int nosetuid;
+extern int nocnflct;
+extern int warnflag;
+
+#define DMRG_DONE -1
+
+#define ck_efile(s, p) \
+ ((p->cinfo.modtime >= 0) && \
+ p->ainfo.local && \
+ cverify(0, &p->ftype, s, &p->cinfo, 1))
+
+static int eocflag;
+
+/*
+ * The variable below indicates that fix_attributes() will be inadequate
+ * because a replacement was permitted.
+ */
+static int repl_permitted = 0;
+
+static int domerg(struct cfextra **extlist, int part, int nparts,
+ int myclass, char **srcp, char **dstp,
+ char **r_updated, char **r_skipped,
+ char **r_anyPathLocal);
+static void endofclass(struct cfextra **extlist, int myclass,
+ int ckflag, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp);
+static int fix_attributes(struct cfextra **, int);
+static int dir_is_populated(char *dirpath);
+static boolean_t absolutepath(char *path);
+static boolean_t parametricpath(char *path, char **relocpath);
+
+/* Used to keep track of the entries in extlist that are regular files. */
+struct reg_files {
+ struct reg_files *next;
+ int val;
+};
+static struct reg_files *regfiles_head = NULL;
+
+/*
+ * This is the function that actually installs one volume (usually that's
+ * all there is). Upon entry, the extlist is entirely correct:
+ *
+ * 1. It contains only those files which are to be installed
+ * from all volumes.
+ * 2. The mode bits in the ainfo structure for each file are set
+ * correctly in accordance with administrative defaults.
+ * 3. mstat.setuid/setgid reflect what the status *was* before
+ * pkgdbmerg() processed compliance.
+ */
+void
+instvol(struct cfextra **extlist, char *srcinst, int part,
+ int nparts, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp,
+ char **r_updated, char **r_skipped,
+ char *a_zoneName)
+{
+ FILE *listfp;
+ char *updated = (char *)NULL;
+ char *skipped = (char *)NULL;
+ char *anyPathLocal = (char *)NULL;
+ char *relocpath = (char *)NULL;
+ char *dstp;
+ char *listfile;
+ char *srcp;
+ char *pspool_loc;
+ char scrpt_dst[PATH_MAX];
+ int count;
+ int entryidx; /* array of current package objects */
+ int n;
+ int nc = 0;
+ int pass; /* pass count through the for loop. */
+ int tcount;
+ struct cfent *ept;
+ struct cfextra *ext;
+ struct mergstat *mstat;
+ struct reg_files *rfp = NULL;
+
+ /*
+ * r_updated and r_skipped are optional parameters that can be passed in
+ * by the caller if the caller wants to know if any objects are either
+ * updated or skipped. Do not initialize either r_updated or r_skipped;
+ * the call to instvol could be cumulative and any previous update or
+ * skipped indication must not be disturbed - these flags are only set,
+ * they must never be reset. These flags are "char *" pointers so that
+ * the object that was skipped or updated can be displayed in debugging
+ * output.
+ */
+
+ if (part == 1) {
+ pkgvolume(&pkgdev, srcinst, part, nparts);
+ }
+
+ tcount = 0;
+ nc = cl_getn();
+
+ /*
+ * For each class in this volume, install those files.
+ *
+ * NOTE : This loop index may be decremented by code below forcing a
+ * second trip through for the same class. This happens only when a
+ * class is split between an archive and the tree. Examples would be
+ * old WOS packages and the occasional class containing dynamic
+ * libraries which require special treatment.
+ */
+
+ if (is_depend_pkginfo_DB() == B_FALSE) {
+ int classidx; /* the current class */
+
+ for (classidx = 0; classidx < nc; classidx++) {
+ int pass_relative = 0;
+ int rel_init = 0;
+
+ eocflag = count = pass = 0;
+ listfp = (FILE *)0;
+ listfile = NULL;
+
+ /* Now what do we pass to the class action script */
+
+ if (cl_pthrel(classidx) == REL_2_CAS) {
+ pass_relative = 1;
+ }
+
+ for (;;) {
+ if (!tcount++) {
+ /* first file to install */
+ if (a_zoneName == (char *)NULL) {
+ echo(MSG_INS_N_N, part, nparts);
+ } else {
+ echo(MSG_INS_N_N_LZ, part, nparts,
+ a_zoneName);
+ }
+ }
+
+ /*
+ * If there's an install class action script and no
+ * list file has been created yet, create that file
+ * and provide the pointer in listfp.
+ */
+ if (cl_iscript(classidx) && !listfp) {
+ /* create list file */
+ putparam("TMPDIR", tmpdir);
+ listfile = tempnam(tmpdir, "list");
+ if ((listfp = fopen(listfile, "w")) == NULL) {
+ progerr(ERR_WTMPFILE, listfile);
+ quit(99);
+ }
+ }
+
+ /*
+ * The following function goes through the package
+ * object list returning the array index of the next
+ * regular file. If it encounters a directory,
+ * symlink, named pipe or device, it just creates it.
+ */
+
+ entryidx = domerg(extlist, (pass++ ? 0 : part), nparts,
+ classidx, &srcp, &dstp, &updated, &skipped,
+ &anyPathLocal);
+
+ /* Evaluate the return code */
+ if (entryidx == DMRG_DONE) {
+ /*
+ * Set ept to the first entry in extlist
+ * which is guaranteed to exist so
+ * later checks against ept->ftype are
+ * not compared to NULL.
+ */
+ ext = extlist[0];
+ ept = &(ext->cf_ent);
+ break; /* no more entries to process */
+ }
+
+ ext = extlist[entryidx];
+ ept = &(ext->cf_ent);
+ mstat = &(ext->mstat);
+
+ /*
+ * If not installing from a partially spooled package
+ * (the "save/pspool" area), and the file contents can
+ * be changed (type is 'e' or 'v'), and the class is not
+ * "none": copy the file from the package (in pristine
+ * state with no actions performed) into the appropriate
+ * location in the packages destination "save/pspool"
+ * area.
+ */
+
+ if ((!is_partial_inst()) &&
+ ((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+ (strcmp(ept->pkg_class, "none") != 0)) {
+
+ if (absolutepath(ext->map_path) == B_TRUE &&
+ parametricpath(ext->cf_ent.ainfo.local,
+ &relocpath) == B_FALSE) {
+ pspool_loc = ROOT;
+ } else {
+ pspool_loc = RELOC;
+ }
+
+ n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+ saveSpoolInstallDir, pspool_loc,
+ relocpath ? relocpath : ext->map_path);
+
+ if (n >= PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2,
+ saveSpoolInstallDir,
+ ext->map_path);
+ quit(99);
+ }
+
+ /* copy, preserve source file mode */
+
+ if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
+ warnflag++;
+ }
+ }
+
+ /*
+ * If this isn't writeable anyway, it's not going
+ * into the list file. Only count it if it's going
+ * into the list file.
+ */
+ if (is_fs_writeable(ext->cf_ent.path,
+ &(ext->fsys_value)))
+ count++;
+
+ pkgvolume(&pkgdev, srcinst, part, nparts);
+
+ /*
+ * If source verification is OK for this class, make
+ * sure the source we're passing to the class action
+ * script is useable.
+ */
+ if (cl_svfy(classidx) != NOVERIFY) {
+ if (cl_iscript(classidx) ||
+ ((ept->ftype == 'e') ||
+ (ept->ftype == 'n'))) {
+ if (ck_efile(srcp, ept)) {
+ progerr(ERR_CORRUPT,
+ srcp);
+ logerr(getErrbufAddr());
+ warnflag++;
+ continue;
+ }
+ }
+ }
+
+ /*
+ * If there's a class action script for this class,
+ * just collect names in a temporary file
+ * that will be used as the stdin when the
+ * class action script is invoked.
+ */
+
+ if ((cl_iscript(classidx)) &&
+ ((is_fs_writeable(ept->path,
+ &(ext->fsys_value))))) {
+ if (pass_relative) {
+ if (!rel_init) {
+ (void) fputs(instdir, listfp);
+ (void) putc('\n', listfp);
+ rel_init++;
+ }
+ (void) fputs(ext->map_path, listfp);
+ (void) putc('\n', listfp);
+ } else {
+ (void) fputs(srcp ?
+ srcp : "/dev/null", listfp);
+ (void) putc(' ', listfp);
+ (void) fputs(dstp, listfp);
+ (void) putc('\n', listfp);
+ }
+ /*
+ * Note which entries in extlist are regular
+ * files to be installed via the class action
+ * script.
+ */
+ if (regfiles_head == NULL) {
+ assert(rfp == NULL);
+ regfiles_head =
+ malloc(sizeof (struct reg_files));
+ if (regfiles_head == NULL) {
+ progerr(ERR_MEMORY, errno);
+ quit(99);
+ }
+ regfiles_head->next = NULL;
+ regfiles_head->val = entryidx;
+ rfp = regfiles_head;
+ } else {
+ assert(rfp != NULL);
+ rfp->next =
+ malloc(sizeof (struct reg_files));
+ if (rfp->next == NULL) {
+ progerr(ERR_MEMORY, errno);
+ quit(99);
+ }
+ rfp = rfp->next;
+ rfp->next = NULL;
+ rfp->val = entryidx;
+ }
+
+ /*
+ * A warning message about unwritable targets
+ * in a class may be appropriate here.
+ */
+ continue;
+ }
+
+ /*
+ * If not installing from a partially spooled package
+ * (the "save/pspool" area), and the file contents can
+ * be changed (type is 'e' or 'v') and the class
+ * identifier is not "none": copy the file from the
+ * package (in pristine state with no actions performed)
+ * into the appropriate location in the packages
+ * destination "save/pspool" area.
+ */
+
+ if ((!is_partial_inst()) &&
+ ((ept->ftype == 'e') || (ept->ftype == 'v') &&
+ (strcmp(ept->pkg_class, "none") != 0))) {
+
+ if (absolutepath(ext->map_path) == B_TRUE &&
+ parametricpath(ext->cf_ent.ainfo.local,
+ &relocpath) == B_FALSE) {
+ pspool_loc = ROOT;
+ } else {
+ pspool_loc = RELOC;
+ }
+
+ n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+ saveSpoolInstallDir, pspool_loc,
+ relocpath ? relocpath : ext->map_path);
+
+ if (n >= PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2,
+ saveSpoolInstallDir,
+ ext->map_path);
+ quit(99);
+ }
+
+ /* copy, preserve source file mode */
+
+ if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
+ warnflag++;
+ }
+ }
+
+ /*
+ * There are several tests here to determine
+ * how we're going to deal with objects
+ * intended for remote read-only filesystems.
+ * We don't use is_served() because this may be
+ * a server. We're actually interested in if
+ * it's *really* remote and *really* not
+ * writeable.
+ */
+
+ n = is_remote_fs(ept->path, &(ext->fsys_value));
+ if ((n != 0) &&
+ !is_fs_writeable(ept->path,
+ &(ext->fsys_value))) {
+
+ /*
+ * Don't change the file, we can't write
+ * to it anyway.
+ */
+
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+
+ /*
+ * If it's currently mounted, we can
+ * at least test it for existence.
+ */
+
+ if (is_mounted(ept->path, &(ext->fsys_value))) {
+ if (!isfile(NULL, dstp)) {
+ echo(MSG_IS_PRESENT, dstp);
+ } else {
+ echo(WRN_INSTVOL_NONE, dstp);
+ }
+ } else {
+ char *server_host;
+
+ server_host = get_server_host(
+ ext->fsys_value);
+
+ /* If not, we're just stuck. */
+ echo(WRN_INSTVOL_NOVERIFY,
+ dstp, server_host);
+ }
+
+ continue;
+ }
+
+ /* echo output destination name */
+
+ echo("%s", dstp);
+
+ /*
+ * if no source then no need to copy/verify
+ */
+
+ if (srcp == (char *)NULL) {
+ continue;
+ }
+
+ /*
+ * If doing a partial installation (creating a
+ * non-global zone), extra steps need to be taken:
+ *
+ * 1) if the file is not type 'e' and not type 'v' and
+ * the class is "none": then the file must already
+ * exist (as a result of the initial non-global zone
+ * installation which caused all non-e/v files to be
+ * copied from the global zone to the non-global
+ * zone). If this is the case, verify that the file
+ * exists and has the correct attributes.
+ *
+ * 2) if the file is not type 'e' and not type 'v'
+ * and the class is NOT "none", *OR* if the file is
+ * type 'e' or type 'v': then check to see if the
+ * file is located in an area inherited from the
+ * global zone. If so, then there is no ability to
+ * change the file since inherited file systems are
+ * "read only" - just verify that the file exists and
+ * verify attributes only if not 'e' or 'v'.
+ */
+
+ if (is_partial_inst() != 0) {
+
+ /*
+ * determine if the destination package is in an
+ * area inherited from the global zone
+ */
+
+ n = pkgMatchInherited(srcp, dstp,
+ get_inst_root(), ept->ainfo.mode,
+ ept->cinfo.modtime, ept->ftype,
+ ept->cinfo.cksum);
+
+ echoDebug(DBG_INSTVOL_PARTIAL_INST,
+ srcp ? srcp : "", dstp ? dstp: "",
+ ((get_inst_root()) &&
+ (strcmp(get_inst_root(), "/") != 0)) ?
+ get_inst_root() : "",
+ ept->ainfo.mode, ept->cinfo.modtime,
+ ept->ftype, ept->cinfo.cksum, n);
+
+ /*
+ * if not type 'e|v' and class 'none', then the
+ * file must already exist.
+ */
+
+ if ((ept->ftype != 'e') &&
+ (ept->ftype != 'v') &&
+ (strcmp(cl_nam(ept->pkg_class_idx),
+ "none") == 0)) {
+
+ /*
+ * if the file is in a space inherited
+ * from the global zone, and if the
+ * contents or attributes are incorrect,
+ * then generate a warning that the
+ * global zone file contents and/or file
+ * attributes have been modified and
+ * that the modifications are extended
+ * to the non-global zone (inherited
+ * from the global zone).
+ */
+
+ if (n == 0) {
+ /* is file changed? */
+ n = finalck(ept, 1, 1, B_TRUE);
+
+ /* no - ok - continue */
+ if (n == 0) {
+ continue;
+ }
+
+ /* output warning message */
+ logerr(NOTE_INSTVOL_FINALCKFAIL,
+ pkginst, ext->map_path,
+ a_zoneName, ept->path);
+ continue;
+ } else if (!finalck(ept, 1, 1,
+ B_FALSE)) {
+ /*
+ * non-e/v file of class "none"
+ * not inherited from the global
+ * zone: verify file already
+ * exists:everything checks here
+ */
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+ }
+ continue;
+ }
+
+ /*
+ * non-e/v file with class action script, or
+ * e/v file: if the file is in an area inherited
+ * from the global zone, then no need (or the
+ * ability) to update just accept the file as is
+ */
+
+ if (n == B_TRUE) {
+ /*
+ * the object is in an area inherited
+ * from the global zone and the objects
+ * attributes are verified
+ */
+
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+
+ /* NOTE: package object skipped */
+
+ if (skipped == (char *)NULL) {
+ skipped = dstp;
+ echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+ skipped);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Copy from source media to target path and fix file
+ * mode and permission now in case installation halted.
+ */
+
+ if (z_path_is_inherited(dstp, ept->ftype,
+ get_inst_root()) == B_FALSE) {
+
+ /*
+ * If the filesystem is read-only don't attempt
+ * to copy a file. Just check that the content
+ * and attributes of the file are correct.
+ *
+ * Normally this doesn't happen, because files,
+ * which don't change, are not returned by
+ * domerg(). However when installing a patch in
+ * a sparse zone, which was already installed
+ * in global zone with -G option, NGZ's
+ * contents db still contains the old record
+ * for this file and therefore domerg()
+ * considers these files to be different even
+ * though they are the same.
+ */
+ n = 0;
+ if (is_fs_writeable(ept->path,
+ &(ext->fsys_value)))
+ n = cppath(MODE_SET|DIR_DISPLAY, srcp,
+ dstp, ept->ainfo.mode);
+
+ if (n != 0) {
+ warnflag++;
+ } else if (!finalck(ept, 1, 1, B_FALSE)) {
+ /*
+ * everything checks here
+ */
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+ }
+ }
+
+ /* NOTE: a package object was updated */
+
+ if (updated == (char *)NULL) {
+ echoDebug(DBG_INSTVOL_OBJ_UPDATED, dstp);
+ updated = dstp;
+ }
+ }
+
+ /*
+ * We have now completed processing of all pathnames
+ * associated with this volume and class.
+ */
+ if (cl_iscript(classidx)) {
+ /*
+ * Execute appropriate class action script
+ * with list of source/destination pathnames
+ * as the input to the script.
+ */
+
+ if (chdir(pkgbin)) {
+ progerr(ERR_CHGDIR, pkgbin);
+ quit(99);
+ }
+
+ if (listfp) {
+ (void) fclose(listfp);
+ }
+
+ /*
+ * if the object associated with the class action script
+ * is in an area inherited from the global zone, then
+ * there is no need to run the class action script -
+ * assume that anything the script would do has already
+ * been done in the area shared from the global zone.
+ */
+
+ /* nothing updated, nothing skipped */
+
+ echoDebug(DBG_INSTVOL_CAS_INFO, is_partial_inst(),
+ updated ? updated : "",
+ skipped ? skipped : "",
+ anyPathLocal ? anyPathLocal : "");
+
+ if ((is_partial_inst() != 0) &&
+ (updated == (char *)NULL) &&
+ (anyPathLocal == (char *)NULL)) {
+
+ /*
+ * installing in non-global zone, and no object
+ * has been updated (installed/verified in non-
+ * inherited area), and no path delivered by the
+ * package is in an area not inherited from the
+ * global zone (all paths delivered are in
+ * areas inherited from the global zone): do not
+ * run the class action script because the only
+ * affected areas are inherited (read only).
+ */
+
+ echoDebug(DBG_INSTVOL_NOT_RUNNING_CAS,
+ a_zoneName ? a_zoneName : "?",
+ eocflag ? "ENDOFCLASS" :
+ cl_iscript(classidx),
+ cl_nam(classidx),
+ cl_iscript(classidx));
+
+ if ((r_skipped != (char **)NULL) &&
+ (*r_skipped == (char *)NULL) &&
+ (skipped == (char *)NULL)) {
+ skipped = "postinstall";
+ echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+ skipped);
+ }
+ } else {
+ /* run the class action script */
+
+ echoDebug(DBG_INSTVOL_RUNNING_CAS,
+ a_zoneName ? a_zoneName : "?",
+ eocflag ? "ENDOFCLASS" :
+ cl_iscript(classidx),
+ cl_nam(classidx),
+ cl_iscript(classidx));
+
+ /* Use ULIMIT if supplied. */
+ set_ulimit(cl_iscript(classidx), ERR_CASFAIL);
+
+ if (eocflag) {
+ /*
+ * end of class detected.
+ * Since there are no more volumes which
+ * contain pathnames associated with
+ * this class, execute class action
+ * script with the ENDOFCLASS argument;
+ * we do this even if none of the path
+ * names associated with this class and
+ * volume needed installation to
+ * guarantee the class action script is
+ * executed at least once during package
+ * installation.
+ */
+ if (pkgverbose) {
+ n = pkgexecl((listfp ?
+ listfile : CAS_STDIN),
+ CAS_STDOUT,
+ CAS_USER, CAS_GRP,
+ SHELL, "-x",
+ cl_iscript(classidx),
+ "ENDOFCLASS", NULL);
+ } else {
+ n = pkgexecl(
+ (listfp ?
+ listfile : CAS_STDIN),
+ CAS_STDOUT, CAS_USER,
+ CAS_GRP, SHELL,
+ cl_iscript(classidx),
+ "ENDOFCLASS", NULL);
+ }
+ ckreturn(n, ERR_CASFAIL);
+ } else if (count) {
+ /* execute class action script */
+ if (pkgverbose) {
+ n = pkgexecl(listfile,
+ CAS_STDOUT, CAS_USER,
+ CAS_GRP, SHELL, "-x",
+ cl_iscript(classidx),
+ NULL);
+ } else {
+ n = pkgexecl(listfile,
+ CAS_STDOUT, CAS_USER,
+ CAS_GRP, SHELL,
+ cl_iscript(classidx),
+ NULL);
+ }
+ ckreturn(n, ERR_CASFAIL);
+ }
+
+ /*
+ * Ensure the mod times on disk match those
+ * in the pkgmap. In this case, call cverify
+ * with checksumming disabled, since the only
+ * action that needs to be done is to verify
+ * that the attributes are correct.
+ */
+
+ if ((rfp = regfiles_head) != NULL) {
+ while (rfp != NULL) {
+ ept = &(extlist[rfp->val]->cf_ent);
+ cverify(1, &ept->ftype, ept->path,
+ &ept->cinfo, 0);
+ rfp = rfp->next;
+ }
+ regfiles_free();
+ }
+
+ clr_ulimit();
+
+ if ((r_updated != (char **)NULL) &&
+ (*r_updated == (char *)NULL) &&
+ (updated == (char *)NULL)) {
+ updated = "postinstall";
+ echoDebug(DBG_INSTVOL_OBJ_UPDATED,
+ updated);
+ }
+ }
+ if (listfile) {
+ (void) remove(listfile);
+ }
+ }
+
+ if (eocflag && (!is_partial_inst() || (is_partial_inst() &&
+ strcmp(cl_nam(classidx), "none") != 0))) {
+ if (cl_dvfy(classidx) == QKVERIFY && !repl_permitted) {
+ /*
+ * The quick verify just fixes everything.
+ * If it returns 0, all is well. If it
+ * returns 1, then the class installation
+ * was incomplete and we retry on the
+ * stuff that failed in the conventional
+ * way (without a CAS). this is primarily
+ * to accomodate old archives such as are
+ * found in pre-2.5 WOS; but, it is also
+ * used when a critical dynamic library
+ * is not archived with its class.
+ */
+ if (!fix_attributes(extlist, classidx)) {
+ /*
+ * Reset the CAS pointer. If the
+ * function returns 0 then there
+ * was no script there in the first
+ * place and we'll just have to
+ * call this a miss.
+ */
+ if (cl_deliscript(classidx))
+ /*
+ * Decrement classidx for
+ * next pass.
+ */
+ classidx--;
+ }
+ } else {
+ /*
+ * Finalize merge. This checks to make sure
+ * file attributes are correct and any links
+ * specified are created.
+ */
+ (void) endofclass(extlist, classidx,
+ (cl_iscript(classidx) ? 0 : 1),
+ a_cfVfp, a_cfTmpVfp);
+ }
+ }
+ }
+ }
+
+ /*
+ * Instead of creating links back to the GZ files the logic is
+ * to let zdo recreate the files from the GZ then invoke pkgadd to
+ * install the editable files and skip over any 'f'type files.
+ * The commented out block is to create the links which should be
+ * removed once the current code is tested to be correct.
+ */
+
+ /*
+ * Go through extlist creating links for 'f'type files
+ * if we're in a global zone. Note that this code lies
+ * here instead of in the main loop to support CAF packages.
+ * In a CAF package the files are installed by the i.none script
+ * and don't exist until all files are done being processed, thus
+ * the additional loop through extlist.
+ */
+
+ /*
+ * output appropriate completion message
+ */
+
+ if (is_depend_pkginfo_DB() == B_TRUE) {
+ /* updating database only (hollow package) */
+ if (a_zoneName == (char *)NULL) {
+ echo(MSG_DBUPD_N_N, part, nparts);
+ } else {
+ echo(MSG_DBUPD_N_N_LZ, part, nparts, a_zoneName);
+ }
+ } else if (tcount == 0) {
+ /* updating package (non-hollow package) */
+ if (a_zoneName == (char *)NULL) {
+ echo(MSG_INST_N_N, part, nparts);
+ } else {
+ echo(MSG_INST_N_N_LZ, part, nparts, a_zoneName);
+ }
+ }
+
+ /*
+ * if any package objects were updated (not inherited from the
+ * global zone or otherwise already in existence), set the updated
+ * flag as appropriate
+ */
+
+ if (updated != (char *)NULL) {
+ echoDebug(DBG_INSTVOL_OBJ_UPDATED, updated);
+ if (r_updated != (char **)NULL) {
+ *r_updated = updated;
+ }
+ }
+
+ /*
+ * if any package objects were skipped (verified inherited from the
+ * global zone), set the skipped flag as appropriate
+ */
+
+ if (skipped != (char *)NULL) {
+ echoDebug(DBG_INSTVOL_OBJ_SKIPPED, skipped);
+ if (r_skipped != (char **)NULL) {
+ *r_skipped = skipped;
+ }
+ }
+}
+
+/*
+ * Name: domerg
+ * Description: For the specified class, review each entry and return the array
+ * index number of the next regular file to process. Hard links are
+ * skipped (they are created in endofclass() and directories,
+ * symlinks, pipes and devices are created here, as well as any
+ * file that already exists and has the correct attributes.
+ * Arguments: struct cfextra **extlist - [RO, *RW]
+ * - Pointer to list of cfextra structures representing
+ * the pkgmap of the package to be installed
+ * int part - [RO, *RO]
+ * - the part of the package currently being processed;
+ * packages begin with part "1" and proceed for the
+ * number (nparts) that comprise the package (volume).
+ * int nparts - [RO, *RO]
+ * - the number of parts the package is divided into
+ * int myclass - [RO, *RO]
+ * - index into class array of the current class
+ * char **srcp - [RW, *RW]
+ * - pointer to pointer to string representing the source
+ * path for the next package to process - if this
+ * function returns != DMRG_DONE then this pointer is
+ * set to a pointer to a string representing the source
+ * path for the next object from the package to process
+ * char **dstp - [RW, *RW]
+ * - pointer to pointer to string representing the target
+ * path for the next package to process - if this
+ * function returns != DMRG_DONE then this pointer is
+ * set to a pointer to a string representing the target
+ * path for the next object from the package to process
+ * char **r_updated - [RO, *RW]
+ * - pointer to pointer to string - set if the last path
+ * returned exists or does not need updating and the
+ * object is NOT located in an area inherited from the
+ * global zone. This is used to determine if the last
+ * path object returned DOES exist in an area that is
+ * inherited from the global zone. If no paths are
+ * inherited from the global zone, this is always set
+ * when a path to be installed exists and has the
+ * correct contents.
+ * char **r_skipped - [RO, *RW]
+ * - pointer to pointer to string - set if the last path
+ * returned exists or does not need updating and the
+ * object IS located in an area inherited from the
+ * global zone. This is used to determine if the last
+ * path object returned does NOT exist in an area that
+ * is inherited from the global zone. If no paths are
+ * inherited from the global zone, this is never set.
+ * char **r_anyPathLocal - [RO, *RW]
+ * - pointer to pointer to string - set if any object
+ * belonging to the package is NOT located in an area
+ * inherited from the global zone. This is used to
+ * determine if the package references ANY objects that
+ * are NOT located in an area inherited from the global
+ * zone - regardless of whether or not they need to be
+ * updated (installed/copied). If no paths are inherited
+ * from the global zone, this is always set when a path
+ * to be installed already exists and has the correct
+ * contents.
+ * Returns: int
+ * != DMRG_DONE - index into extlist of the next path to
+ * be processed - that needs to be installed/copied
+ * == DMRG_DONE - all entries processed
+ */
+
+static int
+domerg(struct cfextra **extlist, int part, int nparts,
+ int myclass, char **srcp, char **dstp,
+ char **r_updated, char **r_skipped,
+ char **r_anyPathLocal)
+{
+ boolean_t stateFlag = B_FALSE;
+ int i;
+ int msg_ugid;
+ static int maxvol = 0;
+ static int svindx = 0;
+ static int svpart = 0;
+ struct cfent *ept = (struct cfent *)NULL;
+ struct mergstat *mstat = (struct mergstat *)NULL;
+
+ /* reset returned path pointers */
+
+ *dstp = (char *)NULL;
+ *srcp = (char *)NULL;
+
+ /* set to start or continue based on which part being processed */
+
+ if (part != 0) {
+ maxvol = 0;
+ svindx = 0;
+ svpart = part;
+ } else {
+ i = svindx;
+ part = svpart;
+ }
+
+ /*
+ * This goes through the pkgmap entries one by one testing them
+ * for inclusion in the package database as well as for validity
+ * against existing files.
+ */
+ for (i = svindx; extlist[i]; i++) {
+ ept = &(extlist[i]->cf_ent);
+ mstat = &(extlist[i]->mstat);
+
+ /*
+ * as paths are processed, if the "anyPathLocal" flag has not
+ * been set, if the object is not of type 'i' (package script),
+ * check to see if the object is in an area inherited from the
+ * global zone - if not, set "anyPathLocal" to the path found,
+ * indicating that at least one path is in an area that is not
+ * inherited from the global zone.
+ */
+
+ if ((r_anyPathLocal != (char **)NULL) &&
+ (*r_anyPathLocal == (char *)NULL) &&
+ (ept->ftype != 'i') &&
+ (z_path_is_inherited(ept->path, ept->ftype,
+ get_inst_root()) == B_FALSE)) {
+ echoDebug(DBG_INSTVOL_OBJ_LOCAL, ept->path);
+ *r_anyPathLocal = ept->path;
+ }
+
+ /* if this isn't the class of current interest, skip it */
+
+ if (myclass != ept->pkg_class_idx) {
+ continue;
+ }
+
+ /* if the class is invalid, announce it & exit */
+ if (ept->pkg_class_idx == -1) {
+ progerr(ERR_CLIDX, ept->pkg_class_idx,
+ (ept->path && *ept->path) ? ept->path : "unknown");
+ logerr(gettext("pathname=%s\n"),
+ (ept->path && *ept->path) ? ept->path : "unknown");
+ logerr(gettext("class=<%s>\n"),
+ (ept->pkg_class && *ept->pkg_class) ?
+ ept->pkg_class : "Unknown");
+ logerr(gettext("CLASSES=<%s>\n"),
+ getenv("CLASSES") ? getenv("CLASSES") : "Not Set");
+ quit(99);
+ }
+
+ /*
+ * Next check to see if we are going to try to delete a
+ * populated directory in some distressing way.
+ */
+ if (mstat->dir2nondir)
+ if (dir_is_populated(ept->path)) {
+ logerr(WRN_INSTVOL_NOTDIR, ept->path);
+ warnflag++;
+ mstat->denied = 1; /* install denied! */
+ continue;
+ } else { /* Replace is OK. */
+ /*
+ * Remove this directory, so it won't
+ * interfere with creation of the new object.
+ */
+ if (rmdir(ept->path)) {
+ /*
+ * If it didn't work, there's nothing
+ * we can do. To continue would
+ * likely corrupt the filesystem
+ * which is unacceptable.
+ */
+ progerr(ERR_RMDIR, ept->path);
+ quit(99);
+ }
+
+ repl_permitted = 1; /* flag it */
+ }
+
+ /* adjust the max volume number appropriately */
+
+ if (ept->volno > maxvol) {
+ maxvol = ept->volno;
+ }
+
+ /* if this part goes into another volume, skip it */
+
+ if (part != ept->volno) {
+ continue;
+ }
+
+ /*
+ * If it's a conflicting file and it's not supposed to be
+ * installed, note it and skip.
+ */
+ if (nocnflct && mstat->shared && ept->ftype != 'e') {
+ if (mstat->contchg || mstat->attrchg) {
+ echo(MSG_SHIGN, ept->path);
+ }
+ continue;
+ }
+
+ /*
+ * If we want to set uid or gid but user says no, note it.
+ * Remember that the actual mode bits in the structure have
+ * already been adjusted and the mstat flag is telling us
+ * about the original mode.
+ */
+ if (nosetuid && (mstat->setuid || mstat->setgid)) {
+ msg_ugid = 1; /* don't repeat attribute message. */
+ if (is_fs_writeable(ept->path,
+ &(extlist[i]->fsys_value))) {
+ if (!(mstat->contchg) && mstat->attrchg) {
+ echo(MSG_UGMOD, ept->path);
+ } else {
+ echo(MSG_UGID, ept->path);
+ }
+ }
+ } else {
+ msg_ugid = 0;
+ }
+
+ switch (ept->ftype) {
+ case 'l': /* hard link */
+ /* links treated as object "update/skip" */
+ stateFlag = B_TRUE;
+ continue; /* defer to final proc */
+
+ case 's': /* for symlink, verify without fix first */
+ /* links treated as object "update/skip" */
+ stateFlag = B_TRUE;
+
+ /* Do this only for default verify */
+ if (cl_dvfy(myclass) == DEFAULT) {
+ if (averify(0, &ept->ftype,
+ ept->path, &ept->ainfo))
+ echo(MSG_SLINK, ept->path);
+ }
+
+ /*FALLTHRU*/
+
+ case 'd': /* directory */
+ case 'x': /* exclusive directory */
+ case 'c': /* character special device */
+ case 'b': /* block special device */
+ case 'p': /* named pipe */
+ /* these NOT treated as object "update/skip" */
+ stateFlag = B_FALSE;
+
+ /*
+ * If we can't get to it for legitimate reasons,
+ * don't try to verify it.
+ */
+ if ((z_path_is_inherited(ept->path, ept->ftype,
+ get_inst_root())) ||
+ is_remote_fs(ept->path,
+ &(extlist[i]->fsys_value)) &&
+ !is_fs_writeable(ept->path,
+ &(extlist[i]->fsys_value))) {
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+ break;
+ }
+
+ if (averify(1, &ept->ftype, ept->path,
+ &ept->ainfo) == 0) {
+ mstat->contchg = mstat->attrchg = 0;
+ } else {
+ progerr(ERR_CREATE_PKGOBJ, ept->path);
+ logerr(getErrbufAddr());
+ warnflag++;
+ }
+
+ break;
+
+ case 'i': /* information file */
+ /* not treated as object "update/skip" */
+ stateFlag = B_FALSE;
+ break;
+
+ default:
+ /* all files treated as object "update/skip" */
+ stateFlag = B_TRUE;
+ break;
+ }
+
+ /*
+ * Both contchg and shared flags have to be taken into
+ * account. contchg is set if the file is already present
+ * in the package database, if it does not exist or if it
+ * exists and is modified.
+ * The shared flag is set when 'e' or 'v' file is not
+ * present in the package database, exists and is not
+ * modified. It also has to be checked here.
+ * Shared flag is also set when file is present in package
+ * database and owned by more than one package, but for
+ * this case contchg has already been set.
+ */
+ if (mstat->contchg || (mstat->shared &&
+ ((ept->ftype == 'e') || (ept->ftype == 'v')))) {
+ *dstp = ept->path;
+ if ((ept->ftype == 'f') || (ept->ftype == 'e') ||
+ (ept->ftype == 'v')) {
+ *srcp = ept->ainfo.local;
+ if (is_partial_inst() != 0) {
+ if (*srcp[0] == '~') {
+ /* translate source pathname */
+ *srcp = srcpath(instdir,
+ extlist[i]->map_path,
+ part, nparts);
+ } else {
+ *srcp = extlist[i]->map_path;
+ }
+ } else {
+ if (*srcp[0] == '~') {
+ /* translate source pathname */
+ *srcp = srcpath(instdir,
+ &(ept->ainfo.local[1]),
+ part, nparts);
+ }
+ }
+
+ echoDebug(DBG_DOMERG_NO_SUCH_FILE,
+ ept->ftype, cl_nam(ept->pkg_class_idx),
+ ept->path);
+ } else {
+ /*
+ * At this point, we're returning a non-file
+ * that couldn't be created in the standard
+ * way. If it refers to a filesystem that is
+ * not writeable by us, don't waste the
+ * calling process's time.
+ */
+ if (!is_fs_writeable(ept->path,
+ &(extlist[i]->fsys_value))) {
+ echoDebug(DBG_DOMERG_NOT_WRITABLE,
+ ept->ftype,
+ cl_nam(ept->pkg_class_idx),
+ ept->path);
+ continue;
+ }
+
+ *srcp = NULL;
+ echoDebug(DBG_DOMERG_NOT_THERE,
+ ept->ftype, cl_nam(ept->pkg_class_idx),
+ ept->path);
+ }
+
+ svindx = i+1;
+ backup(*dstp, 1);
+ return (i);
+ }
+
+ if (mstat->attrchg) {
+ backup(ept->path, 0);
+ if (!msg_ugid)
+ echo(MSG_ATTRIB, ept->path);
+
+ /* fix the attributes now for robustness sake */
+ if (averify(1, &ept->ftype,
+ ept->path,
+ &ept->ainfo) == 0) {
+ mstat->attrchg = 0;
+ }
+ }
+
+ /*
+ * package object exists, or does not need updating: if the path
+ * is in an area inherited from the global zone, then treat
+ * the object as if it were "skipped" - if the path is not in an
+ * area inherited from the global zone, then treat the object as
+ * if it were "updated"
+ */
+
+ /* LINTED warning: statement has no consequent: if */
+ if ((stateFlag == B_FALSE) || (ept == (struct cfent *)NULL)) {
+ /*
+ * the object in question is a directory or special
+ * file - the fact that this type of object already
+ * exists or does not need updating must not trigger
+ * the object updated/object skipped indication -
+ * that would cause class action scripts to be run
+ * when installing a new non-global zone - that action
+ * must only be done when a file object that is in
+ * an area inherited from the global zone is present.
+ */
+ } else if (z_path_is_inherited(ept->path, ept->ftype,
+ get_inst_root()) == B_TRUE) {
+ if (r_skipped != (char **)NULL) {
+ if (*r_skipped == (char *)NULL) {
+ echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+ ept->path);
+ *r_skipped = ept->path;
+ }
+ }
+ } else {
+ if (r_updated != (char **)NULL) {
+ if (*r_updated == (char *)NULL) {
+ echoDebug(DBG_INSTVOL_OBJ_UPDATED,
+ ept->path);
+ }
+ *r_updated = ept->path;
+ }
+ }
+ }
+
+ if (maxvol == part) {
+ eocflag++; /* endofclass */
+ }
+
+ return (DMRG_DONE); /* no remaining entries on this volume */
+}
+
+/*
+ * Determine if the provided directory is populated. Return 0 if so and 1 if
+ * not. This also returns 0 if the dirpath is not a directory or if it does
+ * not exist.
+ */
+static int
+dir_is_populated(char *dirpath) {
+ DIR *dirfp;
+ struct dirent *drp;
+ int retcode = 0;
+
+ if ((dirfp = opendir(dirpath)) != NULL) {
+ while ((drp = readdir(dirfp)) != NULL) {
+ if (strcmp(drp->d_name, ".") == 0) {
+ continue;
+ }
+ if (strcmp(drp->d_name, "..") == 0) {
+ continue;
+ }
+ /*
+ * If we get here, there's a real file in the
+ * directory
+ */
+ retcode = 1;
+ break;
+ }
+ (void) closedir(dirfp);
+ }
+
+ return (retcode);
+}
+
+/*
+ * This is the function that cleans up the installation of this class.
+ * This is where hard links get put in since the stuff they're linking
+ * probably exists by now.
+ */
+static void
+endofclass(struct cfextra **extlist, int myclass, int ckflag,
+ VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp)
+{
+ char *temppath;
+ char *pspool_loc;
+ char *relocpath = (char *)NULL;
+ char scrpt_dst[PATH_MAX];
+ int flag;
+ int idx;
+ int n;
+ struct cfent *ept; /* entry from the internal list */
+ struct cfextra entry; /* entry from the package database */
+ struct mergstat *mstat; /* merge status */
+ struct pinfo *pinfo;
+
+ /* open the package database (contents) file */
+
+ if (!ocfile(a_cfVfp, a_cfTmpVfp, pkgmap_blks)) {
+ quit(99);
+ }
+
+ echo(MSG_VERIFYING_CLASS, cl_nam(myclass));
+
+ for (idx = 0; /* void */; idx++) {
+ /* find next package object in this class */
+ while (extlist[idx]) {
+ if ((extlist[idx]->cf_ent.ftype != 'i') &&
+ extlist[idx]->cf_ent.pkg_class_idx == myclass) {
+ break;
+ }
+ idx++;
+ }
+
+ if (extlist[idx] == NULL) {
+ /* finish copying contents file and exit loop */
+ (void) srchcfile(&(entry.cf_ent), NULL,
+ *a_cfVfp, *a_cfTmpVfp);
+ break;
+ }
+
+ ept = &(extlist[idx]->cf_ent);
+ mstat = &(extlist[idx]->mstat);
+
+ temppath =
+ extlist[idx] ? extlist[idx]->client_path :
+ NULL;
+
+ /*
+ * At this point the only difference between the entry
+ * in the contents file and the entry in extlist[] is
+ * that the status indicator contains CONFIRM_CONT.
+ * So for the new DB we use this knowledge and just
+ * verify everything in accordance with extlist without
+ * trying to retrieve the entry from the DB.
+ */
+
+ n = srchcfile(&(entry.cf_ent),
+ (ept ? temppath : NULL), *a_cfVfp, *a_cfTmpVfp);
+
+ if (n == 0) {
+ break;
+ } else if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(ERR_CFBAD);
+ logerr(gettext("pathname=%s\n"),
+ entry.cf_ent.path && *entry.cf_ent.path ?
+ entry.cf_ent.path : "Unknown");
+ logerr(gettext("problem=%s\n"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ quit(99);
+ } else if (n != 1) {
+ /*
+ * Check if path should be in the package
+ * database.
+ */
+ if ((mstat->shared && nocnflct)) {
+ continue;
+ }
+ progerr(ERR_CFMISSING, ept->path);
+ quit(99);
+ }
+
+ /*
+ * If merge was not appropriate for this object, now is the
+ * time to choose one or the other.
+ */
+ if (mstat->denied) {
+ /*
+ * If installation was denied AFTER the package
+ * database was updated, skip this. We've already
+ * announced the discrepancy and the verifications
+ * that follow will make faulty decisions based on
+ * the ftype, which may not be correct.
+ */
+ progerr(ERR_COULD_NOT_INSTALL, ept->path);
+ warnflag++;
+ } else {
+ if (mstat->replace)
+ /*
+ * This replaces the old entry with the new
+ * one. This should never happen in the new
+ * DB since the entries are already identical.
+ */
+ repl_cfent(ept, &(entry.cf_ent));
+
+ /*
+ * Validate this entry and change the status flag in
+ * the package database.
+ */
+ if (ept->ftype == RM_RDY) {
+ (void) eptstat(&(entry.cf_ent), pkginst,
+ STAT_NEXT);
+ } else {
+ /* check the hard link now. */
+ if (ept->ftype == 'l') {
+ if (averify(0, &ept->ftype,
+ ept->path, &ept->ainfo)) {
+ echo(MSG_HRDLINK,
+ ept->path);
+ mstat->attrchg++;
+ }
+ }
+
+ /*
+ * Don't install or verify objects for
+ * remote, read-only filesystems. We need
+ * only flag them as shared from some server.
+ * Otherwise, ok to do final check.
+ */
+ if (is_remote_fs(ept->path,
+ &(extlist[idx]->fsys_value)) &&
+ !is_fs_writeable(ept->path,
+ &(extlist[idx]->fsys_value))) {
+ flag = -1;
+ } else {
+ boolean_t inheritedFlag;
+ inheritedFlag =
+ z_path_is_inherited(ept->path,
+ ept->ftype, get_inst_root());
+ flag = finalck(ept, mstat->attrchg,
+ (ckflag ? mstat->contchg :
+ (-1)), inheritedFlag);
+ }
+
+ pinfo = entry.cf_ent.pinfo;
+
+ /* Find this package in the list. */
+ while (pinfo) {
+ if (strcmp(pkginst, pinfo->pkg) == 0) {
+ break;
+ }
+ pinfo = pinfo->next;
+ }
+
+ /*
+ * If this package owns this file, then store
+ * it in the database with the appropriate
+ * status. Need to check pinfo in case it
+ * points to NULL which could happen if
+ * pinfo->next = NULL above.
+ */
+ if (pinfo) {
+ if (flag < 0 || is_served(ept->path,
+ &(extlist[idx]->fsys_value))) {
+ /*
+ * This is provided to
+ * clients by a server.
+ */
+ pinfo->status = SERVED_FILE;
+ } else {
+ /*
+ * It's either there or it's
+ * not.
+ */
+ pinfo->status = (flag ?
+ NOT_FND : ENTRY_OK);
+ }
+ }
+ }
+ }
+
+ /*
+ * If not installing from a partially spooled package, the
+ * "save/pspool" area, and the file contents can be
+ * changed (type is 'e' or 'v'), and the class IS "none":
+ * copy the installed volatile file into the appropriate
+ * location in the packages destination "save/pspool" area.
+ */
+
+ if ((!is_partial_inst()) &&
+ ((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+ (strcmp(ept->pkg_class, "none") == 0)) {
+
+ if (absolutepath(extlist[idx]->map_path) == B_TRUE &&
+ parametricpath(extlist[idx]->cf_ent.ainfo.local,
+ &relocpath) == B_FALSE) {
+ pspool_loc = ROOT;
+ } else {
+ pspool_loc = RELOC;
+ }
+
+ n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+ saveSpoolInstallDir, pspool_loc,
+ relocpath ? relocpath : extlist[idx]->map_path);
+
+ if (n >= PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2,
+ saveSpoolInstallDir,
+ extlist[idx]->map_path);
+ quit(99);
+ }
+
+ /* copy, preserve source file mode */
+
+ if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
+ warnflag++;
+ }
+ }
+
+ /*
+ * Now insert this potentially changed package database
+ * entry.
+ */
+ if (entry.cf_ent.npkgs) {
+ if (putcvfpfile(&(entry.cf_ent), *a_cfTmpVfp)) {
+ quit(99);
+ }
+ }
+ }
+
+ n = swapcfile(a_cfVfp, a_cfTmpVfp, pkginst, dbchg);
+ if (n == RESULT_WRN) {
+ warnflag++;
+ } else if (n == RESULT_ERR) {
+ quit(99);
+ }
+}
+
+/*
+ * This function goes through and fixes all the attributes. This is called
+ * out by using DST_QKVERIFY=this_class in the pkginfo file. The primary
+ * use for this is to fix up files installed by a class action script
+ * which is time-critical and reliable enough to assume likely success.
+ * The first such format was for WOS compressed-cpio'd file sets.
+ * The second format is the Class Archive Format.
+ */
+static int
+fix_attributes(struct cfextra **extlist, int idx)
+{
+ struct cfextra *ext;
+ int i, retval = 1;
+ int nc = cl_getn();
+ int n;
+ struct cfent *ept;
+ struct mergstat *mstat;
+ char scrpt_dst[PATH_MAX];
+ char *pspool_loc;
+ char *relocpath = (char *)NULL;
+
+ for (i = 0; extlist[i]; i++) {
+ ext = extlist[i];
+ ept = &(extlist[i]->cf_ent);
+ mstat = &(extlist[i]->mstat);
+
+ /*
+ * We don't care about 'i'nfo files because, they
+ * aren't laid down, 'e'ditable files can change
+ * anyway, so who cares and 's'ymlinks were already
+ * fixed in domerg(); however, certain old WOS
+ * package symlinks depend on a bug in the old
+ * pkgadd which has recently been expunged. For
+ * those packages in 2.2, we repeat the verification
+ * of symlinks.
+ *
+ * By 2.6 or so, ftype == 's' should be added to this.
+ */
+ if (ept->ftype == 'i' || ept->ftype == 'e' ||
+ (mstat->shared && nocnflct))
+ continue;
+
+ if (mstat->denied) {
+ progerr(ERR_COULD_NOT_INSTALL, ept->path);
+ warnflag++;
+ continue;
+ }
+
+ if (ept->pkg_class_idx < 0 || ept->pkg_class_idx > nc) {
+ progerr(ERR_CLIDX, ept->pkg_class_idx,
+ (ept->path && *ept->path) ? ept->path : "unknown");
+ continue;
+ }
+
+ /* If this is the right class, do the fast verify. */
+ if (ept->pkg_class_idx == idx) {
+ if (fverify(1, &ept->ftype, ept->path,
+ &ept->ainfo, &ept->cinfo) == 0) {
+ mstat->attrchg = 0;
+ mstat->contchg = 0;
+ } else /* We'll try full verify later */
+ retval = 0;
+ }
+ /*
+ * Need to copy the installed volitale file back to the
+ * partial spooled area if we are installing to a local zone
+ * or similar installation method.
+ */
+
+ if ((!is_partial_inst()) &&
+ ((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+ (strcmp(ept->pkg_class, "none") == 0)) {
+
+ if (absolutepath(ext->map_path) == B_TRUE &&
+ parametricpath(ext->cf_ent.ainfo.local,
+ &relocpath) == B_FALSE) {
+ pspool_loc = ROOT;
+ } else {
+ pspool_loc = RELOC;
+ }
+
+ n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+ saveSpoolInstallDir, pspool_loc,
+ relocpath ? relocpath : ext->map_path);
+
+ if (n >= PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2,
+ saveSpoolInstallDir,
+ ext->map_path);
+ quit(99);
+ }
+
+ /* copy, preserve source file mode */
+
+ if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
+ warnflag++;
+ }
+ }
+ }
+
+ return (retval);
+}
+
+/*
+ * Check to see if first charcter in path is a '/'.
+ *
+ * Return:
+ * B_TRUE - if path is prepended with '/'
+ * B_FALSE - if not
+ */
+static boolean_t
+absolutepath(char *path)
+{
+ assert(path != NULL);
+ assert(path[0] != '\0');
+
+ return (path[0] == '/' ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Check to see if path contains a '$' which makes it
+ * a parametric path and therefore relocatable.
+ *
+ * Parameters:
+ * path - The path to determine if it is absolute
+ * relocpath - The value of the unconditioned path
+ * i.e. $OPTDIR/usr/ls
+ * Return:
+ * B_TRUE - if path is a parametric path
+ * B_FALSE - if not
+ */
+static boolean_t
+parametricpath(char *path, char **relocpath)
+{
+ assert(path != NULL);
+ assert(path[0] != '\0');
+
+ /*
+ * If this is a valid parametric path then a '$' MUST occur at the
+ * first or second character.
+ */
+
+ if (path[0] == '$' || path[1] == '$') {
+ /*
+ * If a parametric path exists then when copying the
+ * path to the pspool directoy from the installing
+ * pkgs reloc directory we want to use the uncononditional
+ * varaiable path.
+ */
+ *relocpath = (path + 1);
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+void
+regfiles_free()
+{
+ if (regfiles_head != NULL) {
+ struct reg_files *rfp = regfiles_head->next;
+
+ while (rfp != NULL) {
+ free(regfiles_head);
+ regfiles_head = rfp;
+ rfp = regfiles_head->next;
+ }
+ free(regfiles_head);
+ regfiles_head = NULL;
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/main.c b/usr/src/cmd/svr4pkg/pkginstall/main.c
new file mode 100644
index 0000000000..2a6e7fb6cb
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/main.c
@@ -0,0 +1,2988 @@
+/*
+ * 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 <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 opresvr4 = 0;
+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 patchPkgInstall */
+
+static boolean_t patchPkgInstall = B_FALSE;
+
+/* Set by -O patchPkgRemoval */
+
+static boolean_t patchPkgRemoval = 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 */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+#define SCRIPT 0 /* which exception_pkg() pkg list to use (SCRIPTS) */
+#define LINK 1 /* which exception_pkg() pkg list to use (SYMLINKS) */
+#endif
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* This is the text for the "-O inherited-filesystem=" option */
+
+#define INHERITFS "inherited-filesystem="
+#define INHERITFS_LEN ((sizeof (INHERITFS))-1)
+
+/* 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 = (VFP_T *)NULL; /* t.contents */
+ VFP_T *cfVfp = (VFP_T *)NULL; /* contents */
+ 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 *skipped = (char *)NULL;
+ 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];
+ 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;
+
+ /* 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);
+ }
+
+ /* 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 inherited-filesystem= option */
+
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /*
+ * If this is a patch installation
+ * then call setPatchUpdate().
+ */
+
+ if (strcmp(p, "patchPkgInstall") == 0) {
+ setPatchUpdate();
+ patchPkgInstall = B_TRUE;
+ continue;
+ }
+
+ /*
+ * If this is a patch removal
+ * then call setPatchUpdate() and set
+ * patchPkgRemoval flag.
+ */
+
+ if (strcmp(p, "patchPkgRemoval") == 0) {
+ setPatchUpdate();
+ patchPkgRemoval = B_TRUE;
+ 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;
+ }
+
+ /* 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;
+ }
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+
+ else if (exception_pkg(srcinst, SCRIPT)) {
+ /*
+ * Until on1095, set it from exception package names as
+ * well.
+ */
+ putparam("NONABI_SCRIPTS", "TRUE");
+ script_in = PROC_XSTDIN;
+ non_abi_scripts = 1;
+ }
+#endif
+
+ /* Set symlinks to be processed the old way */
+ if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+ set_nonABI_symlinks();
+ }
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ else if (exception_pkg(srcinst, LINK)) {
+ /* Until 2.9, set it from the execption list */
+ putparam("PKG_NONABI_SYMLINKS", "TRUE");
+ set_nonABI_symlinks();
+ }
+#endif
+ /*
+ * 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();
+ }
+
+ /*
+ * Need to force UPDATE to be NULL in case a patch has been applied
+ * before creating a zone. 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 && !isPatchUpdate() && !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(&cfVfp, &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, cfVfp, 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, cfVfp,
+ 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(&cfVfp, &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);
+ /* LINTED warning: do not use cftime(); ... */
+ (void) cftime(cbuf, "%b %d \045Y \045H:\045M", &clock);
+ putparam("INSTDATE", qstrdup(cbuf));
+
+ /*
+ * 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*/
+ }
+
+ if (opresvr4) {
+ /*
+ * we are overwriting a pre-svr4 package, so remove the file
+ * in /usr/options now
+ */
+ (void) snprintf(path, sizeof (path),
+ "%s/%s.name", get_PKGOLD(), pkginst);
+ if (remove(path) && (errno != ENOENT)) {
+ progerr(ERR_OPRESVR4, path);
+ warnflag++;
+ }
+ }
+
+ /*
+ * 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,
+ &cfVfp, &cfTmpVfp, &updated,
+ &skipped, 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) && (!patchPkgInstall) && (!patchPkgRemoval)) {
+ 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 : "",
+ skipped ? skipped : "",
+ 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) &&
+ (skipped != (char *)NULL)) {
+ /*
+ * fresh installing into non-global zone, no object was
+ * updated (installed/verified in non-inherited area),
+ * and and at least one object was skipped (verified in
+ * inherited area) - this means all objects were skipped
+ * so do not run the postinstall script.
+ */
+ echoDebug(DBG_PKGINSTALL_POIS_SKIPPING,
+ 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) {
+ if (pt = getenv("PREDEPEND"))
+ predepend(pt);
+ (void) remove(rlockfile);
+ (void) remove(ilockfile);
+ (void) remove(savlog);
+ }
+
+ /* release the generic package lock */
+
+ (void) unlockinst();
+
+ 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 zone attributes of patch packages haven't been verified
+ * by pdo, 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 ((patchPkgInstall == B_FALSE) && (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;
+ }
+
+ /*
+ * If zone attributes of patch packages haven't been verified
+ * by pdo, check if old and new values differ.
+ * Error if zone parameter
+ */
+
+ if ((patchPkgInstall == B_FALSE) &&
+ (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);
+ }
+
+ /*
+ * Skip this if() section, if zone attributes of patch packages
+ * have been verified by pdo.
+ */
+
+ if (patchPkgInstall == B_FALSE) {
+
+ /*
+ * 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);
+ }
+
+ }
+
+ /* return */
+
+ 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*/
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/merginfo.c b/usr/src/cmd/svr4pkg/pkginstall/merginfo.c
new file mode 100644
index 0000000000..2455f29baa
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/merginfo.c
@@ -0,0 +1,621 @@
+/*
+ * 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 */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+#include "pkglib.h"
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern char instdir[], pkgbin[], pkgloc[], savlog[], *pkginst, **environ;
+extern char saveSpoolInstallDir[];
+extern char pkgsav[]; /* pkginstall/main.c */
+static char *infoloc;
+
+/*
+ * flag definitions for each entry in table
+ */
+
+typedef unsigned int TBL_FLAG_T;
+
+/* no flag set */
+#define FLAG_NONE ((TBL_FLAG_T)0x0000)
+
+/* exclude this attribute if found */
+#define FLAG_EXCLUDE ((TBL_FLAG_T)0x0001)
+
+/* this attribute must not change if found */
+#define FLAG_IDENTICAL ((TBL_FLAG_T)0x0002)
+
+/*
+ * macro to generate an entry in the table:
+ * TBL_ENTRY("PKGINFO_ATTRIBUTE=", FLAG_XXX)
+ * where:
+ * "PKGINFO_ATTRIBUTE=" is the attribute to look for
+ * FLAG_XXX is the action to perform when the attribute is found
+ */
+
+#define TBL_ENTRY(_Y_, _F_) { (_Y_), ((sizeof ((_Y_)))-1), (_F_) }
+
+/*
+ * table containing attributes that require special handling
+ */
+
+struct _namelist {
+ char *_nlName; /* attribute name */
+ int _nlLen; /* attribute length */
+ TBL_FLAG_T _nlFlag; /* attribute disposition flag */
+};
+
+typedef struct _namelist NAMELIST_T;
+
+/*
+ * These are attributes to be acted on in some way when a pkginfo file is
+ * merged. This table MUST be in alphabetical order because it is searched
+ * using a binary search algorithm.
+ */
+
+static NAMELIST_T attrTbl[] = {
+ TBL_ENTRY("BASEDIR=", FLAG_EXCLUDE),
+ TBL_ENTRY("CLASSES=", FLAG_EXCLUDE),
+ TBL_ENTRY("CLIENT_BASEDIR=", FLAG_EXCLUDE),
+ TBL_ENTRY("INST_DATADIR=", FLAG_EXCLUDE),
+ TBL_ENTRY("PKG_CAS_PASSRELATIVE=", FLAG_EXCLUDE),
+ TBL_ENTRY("PKG_DST_QKVERIFY=", FLAG_EXCLUDE),
+ TBL_ENTRY("PKG_INIT_INSTALL=", FLAG_EXCLUDE),
+ TBL_ENTRY("PKG_INSTALL_ROOT=", FLAG_EXCLUDE),
+ TBL_ENTRY("PKG_SRC_NOVERIFY=", FLAG_EXCLUDE),
+ TBL_ENTRY("SUNW_PKGCOND_GLOBAL_DATA=", FLAG_EXCLUDE),
+ TBL_ENTRY("SUNW_PKG_ALLZONES=", FLAG_IDENTICAL),
+ TBL_ENTRY("SUNW_PKG_DIR=", FLAG_EXCLUDE),
+ TBL_ENTRY("SUNW_PKG_HOLLOW=", FLAG_IDENTICAL),
+ TBL_ENTRY("SUNW_PKG_INSTALL_ZONENAME=", FLAG_EXCLUDE),
+ TBL_ENTRY("SUNW_PKG_THISZONE=", FLAG_IDENTICAL),
+};
+
+#define ATTRTBL_SIZE (sizeof (attrTbl) / sizeof (NAMELIST_T))
+
+/*
+ * While pkgsav has to be set up with reference to the server for package
+ * scripts, it has to be client-relative in the pkginfo file. This function
+ * is used to set the client-relative value for use in the pkginfo file.
+ */
+void
+set_infoloc(char *path)
+{
+ if (path && *path) {
+ if (is_an_inst_root()) {
+ /* Strip the server portion of the path. */
+ infoloc = orig_path(path);
+ } else {
+ infoloc = strdup(path);
+ }
+ }
+}
+
+void
+merginfo(struct cl_attr **pclass, int install_from_pspool)
+{
+ DIR *pdirfp;
+ FILE *fp;
+ FILE *pkginfoFP;
+ char path[PATH_MAX];
+ char cmd[PATH_MAX];
+ char pkginfoPath[PATH_MAX];
+ char temp[PATH_MAX];
+ int i;
+ int nc;
+ int out;
+
+ /* remove savelog from previous attempts */
+
+ (void) unlink(savlog);
+
+ /*
+ * create path to appropriate pkginfo file for the package that is
+ * already installed - is_spool_create() will be set (!= 0) if the
+ * -t option is presented to pkginstall - the -t option is used to
+ * 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). This
+ * option is set only when a non-global zone is being created.
+ */
+
+ if (is_spool_create() == 0) {
+ /*
+ * normal package install (not a non-global zone install);
+ * use the standard installed pkginfo file for this package:
+ * --> /var/sadm/pkg/PKGINST/pkginfo
+ * as the source pkginfo file to scan.
+ */
+ i = snprintf(pkginfoPath, sizeof (pkginfoPath),
+ "%s/var/sadm/pkg/%s/%s",
+ ((get_inst_root()) &&
+ (strcmp(get_inst_root(), "/") != 0)) ?
+ get_inst_root() : "", pkginst,
+ PKGINFO);
+ if (i > sizeof (pkginfoPath)) {
+ progerr(ERR_CREATE_PATH_2,
+ ((get_inst_root()) &&
+ (strcmp(get_inst_root(), "/") != 0)) ?
+ get_inst_root() : "/",
+ pkginst);
+ quit(1);
+ }
+ } else {
+ /*
+ * non-global zone installation - use the "saved" pspool
+ * pkginfo file in the global zone for this package:
+ * --> /var/sadm/install/PKG/save/pspool/PKG/pkginfo
+ * as the source pkginfo file to scan.
+ */
+ i = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/%s",
+ saveSpoolInstallDir, PKGINFO);
+ if (i > sizeof (pkginfoPath)) {
+ progerr(ERR_CREATE_PATH_2,
+ saveSpoolInstallDir, PKGINFO);
+ quit(1);
+ }
+ }
+
+ i = snprintf(path, PATH_MAX, "%s/%s", pkgloc, PKGINFO);
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2, pkgloc, PKGINFO);
+ quit(1);
+ }
+
+ /* entry debugging info */
+
+ echoDebug(DBG_MERGINFO_ENTRY,
+ instdir ? instdir : "??",
+ ((get_inst_root()) &&
+ (strcmp(get_inst_root(), "/") != 0)) ?
+ get_inst_root() : "??",
+ saveSpoolInstallDir ? saveSpoolInstallDir : "??",
+ pkgloc ? pkgloc : "??", is_spool_create(),
+ get_info_basedir() ? get_info_basedir() : "??",
+ pkginfoPath, path);
+
+ /*
+ * open the pkginfo file:
+ * if the source pkginfo file to check is the same as the merged one
+ * (e.g. /var/sadm/pkg/PKGINST/pkginfo) then do not open the source
+ * pkginfo file to "verify"
+ */
+
+ if (strcmp(pkginfoPath, path) == 0) {
+ pkginfoFP = (FILE *)NULL;
+ echoDebug(DBG_MERGINFO_SAME, path);
+ } else {
+ echoDebug(DBG_MERGINFO_DIFFERENT, pkginfoPath, path);
+ pkginfoFP = fopen(pkginfoPath, "r");
+
+ if (pkginfoFP == (FILE *)NULL) {
+ echoDebug(ERR_NO_PKG_INFOFILE, pkginst, pkginfoPath,
+ strerror(errno));
+ }
+ }
+
+ /*
+ * output packaging environment to create a pkginfo file in pkgloc[]
+ */
+
+ if ((fp = fopen(path, "w")) == NULL) {
+ progerr(ERR_CANNOT_OPEN_FOR_WRITING, path, strerror(errno));
+ quit(99);
+ }
+
+ /*
+ * output CLASSES attribute
+ */
+
+ out = 0;
+ (void) fputs("CLASSES=", fp);
+ if (pclass) {
+ (void) fputs(pclass[0]->name, fp);
+ out++;
+ for (i = 1; pclass[i]; i++) {
+ (void) putc(' ', fp);
+ (void) fputs(pclass[i]->name, fp);
+ out++;
+ }
+ }
+ nc = cl_getn();
+ for (i = 0; i < nc; i++) {
+ int found = 0;
+
+ if (pclass) {
+ int j;
+
+ for (j = 0; pclass[j]; ++j) {
+ if (cl_nam(i) != NULL &&
+ strcmp(cl_nam(i),
+ pclass[j]->name) == 0) {
+ found++;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ if (out > 0) {
+ (void) putc(' ', fp);
+ }
+ (void) fputs(cl_nam(i), fp);
+ out++;
+ }
+ }
+ (void) putc('\n', fp);
+
+ /*
+ * NOTE : BASEDIR below is relative to the machine that
+ * *runs* the package. If there's an install root, this
+ * is actually the CLIENT_BASEDIR wrt the machine
+ * doing the pkgadd'ing here. -- JST
+ */
+
+ if (is_a_basedir()) {
+ static char *txs1 = "BASEDIR=";
+
+ (void) fputs(txs1, fp);
+ (void) fputs(get_info_basedir(), fp);
+ (void) putc('\n', fp);
+ } else {
+ (void) fputs("BASEDIR=/", fp);
+ (void) putc('\n', fp);
+ }
+
+ /*
+ * output all other environment attributes except those which
+ * are relevant only to install.
+ */
+
+ for (i = 0; environ[i] != (char *)NULL; i++) {
+ char *ep = environ[i];
+ int attrPos = -1;
+ int incr = (ATTRTBL_SIZE >> 1)+1; /* searches possible */
+ int pos = ATTRTBL_SIZE >> 1; /* start in middle */
+ NAMELIST_T *pp = (NAMELIST_T *)NULL;
+
+ /*
+ * find this attribute in the table - accept the attribute if it
+ * is outside of the bounds of the table; otherwise, do a binary
+ * search looking for this attribute.
+ */
+
+ if (strncmp(ep, attrTbl[0]._nlName, attrTbl[0]._nlLen) < 0) {
+
+ /* entry < first entry in attribute table */
+
+ echoDebug(DBG_MERGINFO_LESS_THAN, ep,
+ attrTbl[0]._nlName);
+
+ } else if (strncmp(ep, attrTbl[ATTRTBL_SIZE-1]._nlName,
+ attrTbl[ATTRTBL_SIZE-1]._nlLen) > 0) {
+
+ /* entry > last entry in attribute table */
+
+ echoDebug(DBG_MERGINFO_GREATER_THAN, ep,
+ attrTbl[ATTRTBL_SIZE-1]._nlName);
+
+ } else {
+ /* first entry < entry < last entry in table: search */
+
+ echoDebug(DBG_MERGINFO_SEARCHING, ep,
+ attrTbl[0]._nlName,
+ attrTbl[ATTRTBL_SIZE-1]._nlName);
+
+ while (incr > 0) { /* while possible to divide */
+ int r;
+
+ pp = &attrTbl[pos];
+
+ /* compare current attr with this table entry */
+ r = strncmp(pp->_nlName, ep, pp->_nlLen);
+
+ /* break out of loop if match */
+ if (r == 0) {
+ /* save location/break if match found */
+ attrPos = pos;
+ break;
+ }
+
+ /* no match search to next/prev half */
+ incr = incr >> 1;
+ pos += (r < 0) ? incr : -incr;
+ continue;
+ }
+ }
+
+ /* handle excluded attribute found */
+
+ if ((attrPos >= 0) && (pp->_nlFlag == FLAG_EXCLUDE)) {
+ /* attribute is excluded */
+ echoDebug(DBG_MERGINFO_EXCLUDING, ep);
+ continue;
+ }
+
+ /* handle fixed attribute found */
+
+ if ((pkginfoFP != (FILE *)NULL) && (attrPos >= 0) &&
+ (pp->_nlFlag == FLAG_IDENTICAL)) {
+ /* attribute must not change */
+
+ char *src = ep+pp->_nlLen;
+ char *trg;
+ char theAttr[PATH_MAX+1];
+
+ /* isolate attribute name only without '=' at end */
+
+ (void) strncpy(theAttr, pp->_nlName, pp->_nlLen-1);
+ theAttr[pp->_nlLen-1] = '\0';
+
+ /* lookup attribute in installed package pkginfo file */
+
+ rewind(pkginfoFP);
+ trg = fpkgparam(pkginfoFP, theAttr);
+
+ echoDebug(DBG_MERGINFO_ATTRCOMP, theAttr,
+ trg ? trg : "");
+
+ /* if target not found attribute is being added */
+
+ if (trg == (char *)NULL) {
+ progerr(ERR_PKGINFO_ATTR_ADDED, pkginst, ep);
+ quit(1);
+ }
+
+ /* error if two values are not the same */
+
+ if (strcmp(src, trg) != 0) {
+ progerr(ERR_PKGINFO_ATTR_CHANGED, pkginst,
+ theAttr, src, trg);
+ quit(1);
+ }
+ }
+
+ /* attribute not excluded/has not changed - process */
+
+ if ((strncmp(ep, "PKGSAV=", 7) == 0)) {
+ (void) fputs("PKGSAV=", fp);
+ (void) fputs(infoloc, fp);
+ (void) putc('/', fp);
+ (void) fputs(pkginst, fp);
+ (void) fputs("/save\n", fp);
+ continue;
+ }
+
+ if ((strncmp(ep, "UPDATE=", 7) == 0) &&
+ install_from_pspool != 0 &&
+ !isPatchUpdate() &&
+ !isUpdate()) {
+ continue;
+ }
+
+ echoDebug(DBG_MERGINFO_FINAL, ep);
+
+ (void) fputs(ep, fp);
+ (void) putc('\n', fp);
+ }
+
+ (void) fclose(fp);
+ (void) fclose(pkginfoFP);
+
+ /*
+ * copy all packaging scripts to appropriate directory
+ */
+
+ i = snprintf(path, PATH_MAX, "%s/install", instdir);
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2, instdir, "/install");
+ quit(1);
+ }
+
+ if ((pdirfp = opendir(path)) != NULL) {
+ struct dirent *dp;
+
+ while ((dp = readdir(pdirfp)) != NULL) {
+ if (dp->d_name[0] == '.')
+ continue;
+
+ i = snprintf(path, PATH_MAX, "%s/install/%s",
+ instdir, dp->d_name);
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_3, instdir, "/install/",
+ dp->d_name);
+ quit(1);
+ }
+
+ i = snprintf(temp, PATH_MAX, "%s/%s", pkgbin,
+ dp->d_name);
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2, pkgbin, dp->d_name);
+ quit(1);
+ }
+
+ if (cppath(MODE_SRC|DIR_DISPLAY, path, temp, 0644)) {
+ progerr(ERR_CANNOT_COPY, dp->d_name, pkgbin);
+ quit(99);
+ }
+ }
+ (void) closedir(pdirfp);
+ }
+
+ /*
+ * copy all packaging scripts to the partial spool directory
+ */
+
+ if (!is_spool_create()) {
+ /* packages are being spooled to ../save/pspool/.. */
+ i = snprintf(path, PATH_MAX, "%s/install", instdir);
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_2, instdir, "/install");
+ quit(1);
+ }
+
+ if (((pdirfp = opendir(path)) != NULL) &&
+ !isPatchUpdate()) {
+ struct dirent *dp;
+
+
+ while ((dp = readdir(pdirfp)) != NULL) {
+ if (dp->d_name[0] == '.')
+ continue;
+ /*
+ * Don't copy i.none since if it exists it
+ * contains Class Archive Format procedure
+ * for installing archives. Only Directory
+ * Format packages can exist
+ * in a global spooled area.
+ */
+ if (strcmp(dp->d_name, "i.none") == 0)
+ continue;
+
+ i = snprintf(path, PATH_MAX, "%s/install/%s",
+ instdir, dp->d_name);
+
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_3, instdir,
+ "/install/", dp->d_name);
+ quit(1);
+ }
+
+ i = snprintf(temp, PATH_MAX, "%s/install/%s",
+ saveSpoolInstallDir,
+ dp->d_name);
+
+ if (i > PATH_MAX) {
+ progerr(ERR_CREATE_PATH_3,
+ saveSpoolInstallDir,
+ "/install/", dp->d_name);
+ quit(1);
+ }
+
+ if (cppath(MODE_SRC, path, temp, 0644)) {
+ progerr(ERR_CANNOT_COPY, path, temp);
+ (void) closedir(pdirfp);
+ quit(99);
+ }
+ }
+ (void) closedir(pdirfp);
+ }
+
+ /*
+ * Now copy the original pkginfo and pkgmap files from the
+ * installing package to the spooled directory.
+ */
+
+ i = snprintf(path, sizeof (path), "%s/%s", instdir, PKGINFO);
+ if (i > sizeof (path)) {
+ progerr(ERR_CREATE_PATH_2, instdir, PKGINFO);
+ quit(1);
+ }
+
+ i = snprintf(temp, sizeof (temp), "%s/%s",
+ saveSpoolInstallDir, PKGINFO);
+ if (i > sizeof (temp)) {
+ progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
+ PKGINFO);
+ quit(1);
+ }
+
+ if (cppath(MODE_SRC, path, temp, 0644)) {
+ progerr(ERR_CANNOT_COPY, path, temp);
+ quit(99);
+ }
+
+ /*
+ * Only want to copy the FCS pkgmap if this is not a
+ * patch installation.
+ */
+
+ if (!isPatchUpdate()) {
+ i = snprintf(path, sizeof (path), "%s/pkgmap", instdir);
+ if (i > sizeof (path)) {
+ progerr(ERR_CREATE_PATH_2, instdir, "pkgmap");
+ quit(1);
+ }
+
+ i = snprintf(temp, sizeof (temp), "%s/pkgmap",
+ saveSpoolInstallDir);
+ if (i > sizeof (path)) {
+ progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
+ "pkgmap");
+ quit(1);
+ }
+
+ if (cppath(MODE_SRC, path, temp, 0644)) {
+ progerr(ERR_CANNOT_COPY, path, temp);
+ quit(99);
+ }
+ }
+ }
+
+ /*
+ * If we are installing from a spool directory
+ * copy the save directory from it, it may have
+ * been patched. Duplicate it only if this
+ * installation isn't an update and is not to
+ * an alternate root.
+ */
+ if (strstr(instdir, "pspool") != NULL) {
+ struct stat status;
+
+ i = snprintf(path, sizeof (path), "%s/save", instdir);
+ if (i > sizeof (path)) {
+ progerr(ERR_CREATE_PATH_2, instdir, "save");
+ quit(1);
+ }
+
+ if ((stat(path, &status) == 0) &&
+ (status.st_mode & S_IFDIR) &&
+ !isPatchUpdate()) {
+ i = snprintf(cmd, sizeof (cmd), "cp -pr %s/* %s",
+ path, pkgsav);
+ if (i > sizeof (cmd)) {
+ progerr(ERR_SNPRINTF, "cp -pr %s/* %s");
+ quit(1);
+ }
+
+ if (system(cmd)) {
+ progerr(ERR_PKGBINCP, path, pkgsav);
+ quit(99);
+ }
+ }
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/msgdefs.h b/usr/src/cmd/svr4pkg/pkginstall/msgdefs.h
new file mode 100644
index 0000000000..e88e1e3fd4
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/msgdefs.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef _MSGDEFS_H
+#define _MSGDEFS_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ERR_USAGE "usage:\n" \
+ "\tpkginstall [-o] [-n] [-d device] " \
+ "[-m mountpt [-f fstype]] [-v] " \
+ "[[-M] -R host_path] [-V fs_file] [-b bindir] [-a admin_file] " \
+ "[-r resp_file] [-N calling_prog] directory pkginst\n"
+
+#define ERR_STREAMDIR "unable to make temporary directory to unpack " \
+ "datastream: %s"
+#define ERR_CANNOT_USE_DIR "cannot use directory <%s>: %s"
+#define ERR_PATH "the path <%s> is invalid!"
+
+#define ERR_CHDIR "unable to change current working directory to <%s>"
+#define MSG_MANMOUNT "Assuming mounts have been provided."
+#define ERR_DB "unable to query or modify database"
+#define ERR_DB_TBL "unable to remove database entries for package <%s> " \
+ "in table <%s>."
+#define ERR_DSARCH "unable to find archive for <%s> in datastream"
+#define ERR_CREAT_CONT "unable to create contents file <%s>"
+#define ERR_LIVE_CONTINUE_NOT_SUPPORTED "live continue mode is not supported"
+#define ERR_ROOT_SET "Could not set install root from the environment."
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+#define ERR_SNPRINTF "Not enough memory to format, %s"
+#define ERR_INTONLY "unable to install <%s> without user interaction"
+#define ERR_NOREQUEST "package does not contain an interactive request script"
+#define ERR_LOCKFILE "unable to create lockfile <%s>"
+#define ERR_PKGINFO "unable to open pkginfo file <%s>"
+#define ERR_PKGBINCP "unable to copy <%s>\n\tto <%s>"
+#define ERR_PKGBINREN "unable to rename <%s>\n\tto <%s>"
+#define ERR_RESPONSE "unable to open response file <%s>"
+#define ERR_PKGMAP "unable to open pkgmap file <%s>"
+#define ERR_MKDIR "unable to make temporary directory <%s>"
+#define ERR_ADMBD "%s is already installed at %s. Admin file will " \
+ "force a duplicate installation at %s."
+#define ERR_NEWBD "%s is already installed at %s. Duplicate " \
+ "installation attempted at %s."
+#define ERR_DSTREAM "unable to unpack datastream"
+#define ERR_DSTREAMCNT "datastream early termination problem"
+#define ERR_RDONLY "read-only parameter <%s> cannot be assigned a value"
+#define ERR_REQUEST "request script did not complete successfully"
+#define WRN_CHKINSTALL "checkinstall script suspends"
+#define ERR_CHKINSTALL "checkinstall script did not complete successfully"
+#define ERR_PREINSTALL "preinstall script did not complete successfully"
+#define ERR_POSTINSTALL "postinstall script did not complete successfully"
+#define ERR_OPRESVR4 "unable to unlink options file <%s>"
+#define ERR_SYSINFO "unable to process installed package information, " \
+ "errno=%d"
+#define ERR_BADULIMIT "cannot process invalid ULIMIT value of <%s>."
+#define MSG_INST_ONE " %d package pathname is already properly installed."
+#define MSG_INST_MANY " %d package pathnames are already properly " \
+ "installed."
+#define ERR_PATCHPKG "unable to update patch_table with patches that " \
+ "have been pre installed"
+#define SPECIAL_MALLOC "unable to maintain package contents text due to "\
+ "insufficient memory: %s"
+#define SPECIAL_ACCESS "unable to maintain package contents text due to "\
+ "an access failure: %s"
+#define SPECIAL_MAP "unable to maintain package contents text due to "\
+ "a failure to map the database into memory: %S"
+#define SPECIAL_INPUT "unable to maintain package contents text: alternate "\
+ "root path too long"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGDEFS_H */
diff --git a/usr/src/cmd/svr4pkg/pkginstall/pkgenv.c b/usr/src/cmd/svr4pkg/pkginstall/pkgenv.c
new file mode 100644
index 0000000000..37437772c0
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkgenv.c
@@ -0,0 +1,126 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+
+#define ERR_PKGINFO "unable to access pkginfo file <%s>"
+#define ERR_PKGMAP "unable to access pkgmap file <%s>"
+#define ERR_NOPARAM "%s parameter is not defined in <%s>"
+#define ERR_PKGBAD "PKG parameter is invalid <%s>"
+#define ERR_PKGMTCH "PKG parameter <%s> does not match instance <%s>"
+
+char *pkgarch;
+char *pkgvers;
+char *pkgabrv;
+char *pkgname;
+char pkgwild[PKGSIZ+1];
+
+/*
+ * This function confirms the presence of pkgmap and pkginfo and verifies
+ * that the mandatory parameters are available in the environment.
+ */
+int
+pkgenv(char *pkginst, char *p_pkginfo, char *p_pkgmap)
+{
+ FILE *fp;
+ char *value,
+ path[PATH_MAX],
+ param[MAX_PKG_PARAM_LENGTH];
+ int errflg;
+
+ errflg = 0;
+ if (access(p_pkgmap, 0)) {
+ progerr(gettext(ERR_PKGMAP), p_pkgmap);
+ return (1);
+ }
+ if ((fp = fopen(p_pkginfo, "r")) == NULL) {
+ progerr(gettext(ERR_PKGINFO), p_pkginfo);
+ return (1);
+ }
+ param[0] = '\0';
+ while (value = fpkgparam(fp, param)) {
+ if (strcmp("PATH", param))
+ putparam(param, value);
+ free(value);
+ param[0] = '\0';
+ }
+ (void) fclose(fp);
+ /*
+ * verify that required parameters are now present in
+ * the environment
+ */
+ if ((pkgabrv = getenv("PKG")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "PKG", path);
+ errflg++;
+ }
+ if (pkgnmchk(pkgabrv, NULL, 0) || strchr(pkgabrv, '.')) {
+ progerr(gettext(ERR_PKGBAD), pkgabrv);
+ errflg++;
+ }
+ (void) snprintf(pkgwild, sizeof (pkgwild), "%s.*", pkgabrv);
+ if ((pkgname = getenv("NAME")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "NAME", path);
+ errflg++;
+ }
+ if ((pkgarch = getenv("ARCH")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "ARCH", path);
+ errflg++;
+ }
+ if ((pkgvers = getenv("VERSION")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "VERSION", path);
+ errflg++;
+ }
+ if (getenv("CATEGORY") == NULL) {
+ progerr(gettext(ERR_NOPARAM), "CATEGORY", path);
+ errflg++;
+ }
+ /*
+ * verify consistency between PKG parameter and pkginst that
+ * was determined from the directory structure
+ */
+ (void) snprintf(param, sizeof (param), "%s.*", pkgabrv);
+ if (pkgnmchk(pkginst, param, 0)) {
+ progerr(gettext(ERR_PKGMTCH), pkgabrv, pkginst);
+ errflg++;
+ }
+ return (errflg);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/pkginstall.h b/usr/src/cmd/svr4pkg/pkginstall/pkginstall.h
new file mode 100644
index 0000000000..3779f24f60
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkginstall.h
@@ -0,0 +1,107 @@
+/*
+ * 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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef __PKG_PKGINSTALL_H__
+#define __PKG_PKGINSTALL_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* cppath() variables */
+#define DIR_DISPLAY 0x0001 /* display implied directories created */
+#define MODE_SRC 0x0002 /* set mode to mode of source file */
+#define MODE_SET 0x0004 /* set mode to mode passed in as argument */
+#define MODE_0666 0x0008 /* force mode to 0666 */
+
+/* special stdin for request scripts */
+#define REQ_STDIN "/dev/tty"
+
+/* response file writability status */
+#define RESP_WR 0 /* Response file is writable. */
+#define RESP_RO 1 /* Read only. */
+
+#ifdef __STDC__
+#ifndef __P
+#define __P(x) x
+#endif
+#else
+#ifndef __P
+#define __P(x) ()
+#endif
+#endif /* __STDC__ */
+
+extern int cppath __P((int ctrl, char *f1, char *f2, mode_t mode));
+extern void backup __P((char *path, int mode));
+extern void pkgvolume __P((struct pkgdev *devp, char *pkg, int part,
+ int nparts));
+extern void quit __P((int exitval));
+extern void ckreturn __P((int retcode, char *msg));
+extern int sortmap __P((struct cfextra ***extlist, VFP_T *pkgmapVfp,
+ VFP_T *mapvfp, VFP_T *tmpvfp, char *a_zoneName));
+extern void merginfo __P((struct cl_attr **pclass, int install_from_pspool));
+extern void set_infoloc __P((char *real_pkgsav));
+extern int pkgenv __P((char *pkginst, char *p_pkginfo, char *p_pkgmap));
+extern void instvol __P((struct cfextra **extlist, char *srcinst, int part,
+ int nparts, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp,
+ char **r_updated, char **r_skipped,
+ char *a_zoneName));
+extern int reqexec __P((int update, char *script, int non_abi_scripts,
+ boolean_t enable_root_user));
+extern int chkexec __P((int update, char *script));
+extern int rdonly_respfile __P((void));
+extern int is_a_respfile __P((void));
+extern char *get_respfile __P((void));
+extern int set_respfile __P((char *respfile, char *pkginst,
+ int resp_stat));
+extern void predepend __P((char *oldpkg));
+extern void cksetPreinstallCheck __P((boolean_t a_preinstallCheck));
+extern void cksetZoneName __P((char *a_zoneName));
+extern int cksetuid __P((void));
+extern int ckconflct __P((void));
+extern int ckpkgdirs __P((void));
+extern int ckspace __P((void));
+extern int ckdepend __P((void));
+extern int ckrunlevel __P((void));
+extern int ckpartial __P((void));
+extern int ckpkgfiles __P((void));
+extern int ckpriv __P((void));
+extern void is_WOS_arch __P((void));
+extern void ckdirs __P((void));
+extern char *getinst __P((int *updatingExisting, struct pkginfo *info,
+ int npkgs, boolean_t a_preinstallCheck));
+extern int is_samepkg __P((void));
+extern int dockspace __P((char *spacefile));
+
+extern int special_contents_add(int, struct cfextra **, const char *);
+extern boolean_t rm_all_pkg_entries(char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PKG_PKGINSTALL_H__ */
diff --git a/usr/src/cmd/svr4pkg/pkginstall/pkgvolume.c b/usr/src/cmd/svr4pkg/pkginstall/pkgvolume.c
new file mode 100644
index 0000000000..87db20b697
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkgvolume.c
@@ -0,0 +1,89 @@
+/*
+ * 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 2006 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <pkgdev.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char instdir[], pkgbin[];
+
+void
+pkgvolume(struct pkgdev *devp, char *pkg, int part, int nparts)
+{
+ static int cpart = 0;
+ char path[PATH_MAX];
+ int n;
+
+ if (devp->cdevice)
+ return;
+ if (cpart == part)
+ return;
+ cpart = part;
+
+ if (part == 1) {
+ if (ckvolseq(instdir, 1, nparts)) {
+ progerr(gettext("corrupt directory structure"));
+ quit(99);
+ }
+ cpart = 1;
+ return;
+ }
+
+ if (devp->mount == NULL) {
+ if (ckvolseq(instdir, part, nparts)) {
+ progerr(gettext("corrupt directory structure"));
+ quit(99);
+ }
+ return;
+ }
+
+ for (;;) {
+ (void) chdir("/");
+ if (n = pkgumount(devp)) {
+ progerr(gettext("attempt to unmount <%s> failed (%d)"),
+ devp->bdevice, n);
+ quit(99);
+ }
+ if (n = pkgmount(devp, pkg, part, nparts, 1))
+ quit(n);
+ (void) sprintf(path, "%s/%s", devp->dirname, pkg);
+ if (ckvolseq(path, part, nparts) == 0)
+ break;
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/predepend.c b/usr/src/cmd/svr4pkg/pkginstall/predepend.c
new file mode 100644
index 0000000000..64dcf9be5d
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/predepend.c
@@ -0,0 +1,89 @@
+/*
+ * 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 1993 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 <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+extern char *pkgname, pkgloc[];
+extern int warnflag;
+
+#define ERR_RMLINK "unable to remove options file <%s>"
+#define ERR_SYMLINK "unable to create symbloic link from <%s> to <%s>"
+#define ERR_PREDEPEND "unable to create predepend file <%s>"
+
+void
+predepend(char *oldpkg)
+{
+ FILE *fp;
+ char path[PATH_MAX];
+ char spath[PATH_MAX];
+ struct stat statbuf;
+
+ oldpkg = strtok(oldpkg, " \t\n");
+ if (oldpkg == NULL)
+ return;
+
+ (void) sprintf(path, "%s/predepend", pkgloc);
+ if ((fp = fopen(path, "w")) == NULL) {
+ progerr(gettext(ERR_PREDEPEND), path);
+ warnflag++;
+ return;
+ }
+ (void) fprintf(fp, "%s\n", pkgname);
+ (void) fclose(fp);
+
+ do {
+ (void) sprintf(spath, "%s/%s.name", get_PKGOLD(), oldpkg);
+ if (lstat(spath, &statbuf) == 0) {
+ /* options file already exists */
+ if (statbuf.st_mode & S_IFLNK) {
+ /* remove current link */
+ if (unlink(spath)) {
+ progerr(gettext(ERR_RMLINK), spath);
+ warnflag++;
+ }
+ }
+ }
+ if (symlink(path, spath)) {
+ progerr(gettext(ERR_SYMLINK), path, spath);
+ warnflag++;
+ }
+ } while (oldpkg = strtok(NULL, " \t\n"));
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/quit.c b/usr/src/cmd/svr4pkg/pkginstall/quit.c
new file mode 100644
index 0000000000..af62f4fcfa
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/quit.c
@@ -0,0 +1,521 @@
+/*
+ * 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 2005 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 <string.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+#include <pkglib.h>
+#include "install.h"
+#include "dryrun.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+/* main.c */
+extern char *pkgdrtarg;
+extern struct cfextra **extlist;
+
+extern struct admin adm;
+extern struct pkgdev pkgdev; /* holds info about the installation device */
+
+extern int dparts;
+extern int dreboot; /* != 0 if reboot required after installation */
+extern int failflag; /* != 0 if fatal error has occurred (1) */
+extern int ireboot; /* != 0 if immediate reboot required */
+extern int warnflag; /* != 0 if non-fatal error has occurred (2) */
+
+extern char tmpdir[];
+extern char pkgloc[];
+extern char pkgloc_sav[];
+extern char *msgtext;
+extern char *pkginst;
+extern char *pkgname;
+extern char saveSpoolInstallDir[]; /* pkginstall/main.c */
+
+/*
+ * exported functions
+ */
+
+void quit(int retcode);
+void quitSetZoneName(char *a_zoneName);
+sighdlrFunc_t *quitGetTrapHandler(void);
+
+/*
+ * forward declarations
+ */
+
+static void trap(int signo);
+static void mailmsg(int retcode);
+static void quitmsg(int retcode);
+
+static boolean_t silentExit = B_FALSE;
+static boolean_t pkgaskFlag = B_FALSE;
+static boolean_t installStarted = B_FALSE;
+static boolean_t updatingExistingPackage = B_FALSE;
+
+static char *dstreamTempDir = (char *)NULL;
+static char *zoneName = (char *)NULL;
+static int includeZonename = 0;
+static int trapEntered = 0;
+
+/*
+ * *****************************************************************************
+ * 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(void)
+{
+ return (&trap);
+}
+
+/*
+ * 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
+ */
+
+void
+quitSetZoneName(char *a_zoneName)
+{
+ zoneName = a_zoneName;
+ if ((zoneName == (char *)NULL || *zoneName == '\0')) {
+ includeZonename = 0;
+ } else {
+ includeZonename = 1;
+ }
+}
+
+/*
+ * Name: quitSetDstreamTmpdir
+ * Description: set the name of a temporary directory that contains package
+ * streams to be removed when quit() is called
+ * Arguments: a_dstreamTempDir - pointer to string representing the path
+ * to the temporary directory to remove when quit()
+ * is called
+ * Returns: void
+ */
+
+void
+quitSetDstreamTmpdir(char *a_dstreamTempDir)
+{
+ dstreamTempDir = a_dstreamTempDir;
+}
+
+/*
+ * Name: quitSetUpdatingExisting
+ * Description: set the "updating existing" flag - used in conjunction
+ * with the "install started" flag to determine the type
+ * of cleanup to be done when quit() is called
+ * Arguments: a_updatingExistingPackage - indicates whether or not existing
+ * packages are being updated (B_TRUE) or new packages
+ * are being installed (B_FALSE)
+ * Returns: void
+ */
+
+void
+quitSetUpdatingExisting(boolean_t a_updatingExistingPackage)
+{
+ updatingExistingPackage = a_updatingExistingPackage;
+}
+
+/*
+ * Name: quitSetInstallStarted
+ * Description: set the "install started" flag - used in conjunction
+ * with the "updating existing" flag to determine the type
+ * of cleanup to be done when quit() is called, and the
+ * type of message to be output for the "reason" why quit()
+ * was called
+ * Arguments: a_installStarted - indicates whether or not installation
+ * has started
+ * Returns: void
+ */
+
+void
+quitSetInstallStarted(boolean_t a_installStarted)
+{
+ installStarted = a_installStarted;
+}
+
+/*
+ * Name: quitSetPkgask
+ * Description: set the "pkgask is being run" flag - used to determine
+ * the type of message to be output for the "reason" why
+ * quit() was called
+ * Arguments: a_pkgaskflag - indicates whether or not pkgask is being run
+ * Returns: void
+ */
+
+void
+quitSetPkgask(boolean_t a_pkgaskFlag)
+{
+ pkgaskFlag = a_pkgaskFlag;
+}
+
+/*
+ * Name: quitSetSilentExit
+ * Description: set the "silent exit" flag - if silent exit is TRUE, then
+ * no messages are output by quit() when it is called
+ * Arguments: a_silentExit - indicates whether or not silent exit is set
+ * Returns: void
+ */
+
+void
+quitSetSilentExit(boolean_t a_silentExit)
+{
+ silentExit = a_silentExit;
+}
+
+/*
+ * 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 retcode)
+{
+ char orig_pkginfo_path[PATH_MAX];
+ char pkginfo_path[PATH_MAX];
+
+ /* disable interrupts */
+
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ /* process return code if not quit(99) */
+
+ if (retcode != 99) {
+ if ((retcode % 10) == 0) {
+ if (failflag) {
+ retcode += 1;
+ } else if (warnflag) {
+ retcode += 2;
+ }
+ }
+
+ if (ireboot) {
+ retcode = (retcode % 10) + 20;
+ }
+ if (dreboot) {
+ retcode = (retcode % 10) + 10;
+ }
+ }
+
+ /* if set remove dstream temporary directory */
+
+ if (dstreamTempDir != (char *)NULL) {
+ echoDebug(DBG_REMOVING_DSTREAM_TMPDIR, dstreamTempDir);
+ (void) rrmdir(dstreamTempDir);
+ dstreamTempDir = (char *)NULL;
+ }
+
+ /* If we're in dryrun mode, write out the dryrun file(s). */
+ if (in_dryrun_mode()) {
+ char exit_msg[200];
+ set_dr_info(EXITCODE, retcode);
+ if (failflag || warnflag) {
+ set_dr_exitmsg(msgtext);
+ } else {
+ /* LINTED variable format specified */
+ (void) snprintf(exit_msg, sizeof (exit_msg),
+ qreason(1, retcode, installStarted,
+ includeZonename),
+ (pkginst ? pkginst : "unknown"),
+ zoneName);
+ set_dr_exitmsg(exit_msg);
+ }
+
+ write_dryrun_file(extlist);
+ ptext(stderr, MSG_DRYRUN_DONE);
+ ptext(stderr, MSG_NOCHANGE);
+
+ if (tmpdir[0] != NULL)
+ (void) rrmdir(tmpdir);
+
+ } else {
+ /* fix bug #1082589 that deletes root file */
+ if (tmpdir[0] != NULL) {
+ (void) rrmdir(tmpdir);
+ }
+
+ /* send mail to appropriate user list */
+ mailmsg(retcode);
+
+ /* display message about this installation */
+ quitmsg(retcode);
+ }
+
+ /*
+ * In the event that this quit() was called prior to completion of
+ * the task, do an unlockinst() just in case.
+ */
+ unlockinst();
+
+ /* Unmount anything that's our responsibility. */
+ (void) unmount_client();
+
+ /*
+ * No need to umount device since calling process
+ * was responsible for original mount
+ */
+
+ if (!updatingExistingPackage) {
+ if (!installStarted && pkgloc[0]) {
+ /*
+ * install not yet started; if package install
+ * location is defined, remove the package.
+ */
+ echoDebug(DBG_QUIT_REMOVING_PKGDIR, pkgloc);
+
+ (void) chdir("/");
+ if (pkgloc[0]) {
+ (void) rrmdir(pkgloc);
+ }
+ }
+ } else {
+ if (!installStarted) {
+ /*
+ * If we haven't started, but have already done
+ * the <PKGINST>/install directory rename, then
+ * remove the new <PKGINST>/install directory
+ * and rename <PKGINST>/install.save back to
+ * <PKGINST>/install.
+ */
+ if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
+ if (pkgloc[0] && !access(pkgloc, F_OK))
+ (void) rrmdir(pkgloc);
+ if (rename(pkgloc_sav, pkgloc) == -1) {
+ progerr(ERR_PACKAGEBINREN,
+ pkgloc_sav, pkgloc);
+ }
+ }
+ } else {
+ if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
+ echoDebug(DBG_QUIT_REMOVING_PKGSAV, pkgloc_sav);
+ (void) rrmdir(pkgloc_sav);
+ }
+ }
+
+ if (isPatchUpdate()) {
+ if (pkgloc[0] && !access(pkgloc, F_OK) &&
+ !access(saveSpoolInstallDir, F_OK)) {
+ /*
+ * Copy the pkginfo file to the pspool
+ * directory. This propagates patch
+ * info to the patched pkg in the local
+ * zone.
+ */
+ (void) snprintf(orig_pkginfo_path,
+ sizeof (orig_pkginfo_path),
+ "%s/%s/%s", get_PKGLOC(),
+ pkginst, PKGINFO);
+
+ (void) snprintf(pkginfo_path,
+ sizeof (pkginfo_path), "%s/%s",
+ saveSpoolInstallDir, PKGINFO);
+
+ if (cppath(MODE_SET|DIR_DISPLAY,
+ orig_pkginfo_path, pkginfo_path,
+ 0644)) {
+ progerr(ERR_PKGINFO_COPY,
+ orig_pkginfo_path,
+ pkginfo_path);
+ }
+ }
+ }
+ }
+
+ /*
+ * pkginst can be null if an administration setting doesn't all
+ * the package to be installed. Make sure pkginst exeists before
+ * updating the DB
+ */
+
+ if (dparts > 0)
+ ds_skiptoend(pkgdev.cdevice);
+ (void) ds_close(1);
+
+ /* Free the filesystem table. */
+ fs_tab_free();
+
+ /* Free the package information lists. */
+ pinfo_free();
+
+ /* Free all stragglers. */
+ bl_free(BL_ALL);
+ (void) pathdup(NULL);
+
+ /* Free regfiles. */
+ regfiles_free();
+
+ /* final exit debugging message */
+
+ echoDebug(DBG_EXIT_WITH_CODE, retcode);
+
+ exit(retcode);
+ /*NOTREACHED*/
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+quitmsg(int retcode)
+{
+ if (silentExit == B_TRUE) {
+ return;
+ }
+
+ (void) putc('\n', stderr);
+ if (pkgaskFlag) {
+ ptext(stderr, qreason(0, retcode, installStarted,
+ includeZonename), zoneName);
+ } else if (pkginst) {
+ ptext(stderr, qreason(1, retcode, installStarted,
+ includeZonename), pkginst, zoneName);
+ }
+
+ if (retcode && !installStarted) {
+ ptext(stderr, MSG_NOCHANGE);
+ }
+}
+
+static void
+mailmsg(int retcode)
+{
+ struct utsname utsbuf;
+ FILE *pp;
+ char *cmd;
+ size_t len;
+
+ if (silentExit == B_TRUE) {
+ return;
+ }
+
+ if (!installStarted || pkgaskFlag || (adm.mail == NULL)) {
+ return;
+ }
+
+ len = strlen(adm.mail) + sizeof (MAILCMD) + 2;
+ cmd = calloc(len, sizeof (char));
+ if (cmd == NULL) {
+ logerr(WRN_NOMAIL);
+ return;
+ }
+
+ (void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail);
+ if ((pp = popen(cmd, "w")) == NULL) {
+ logerr(WRN_NOMAIL);
+ return;
+ }
+
+ if (msgtext)
+ ptext(pp, msgtext);
+
+ (void) strcpy(utsbuf.nodename, MSG_NODENAME);
+ (void) uname(&utsbuf);
+
+ ptext(pp, qreason(2, retcode, installStarted, includeZonename),
+ pkgname, utsbuf.nodename, pkginst, zoneName);
+
+ if (pclose(pp)) {
+ logerr(WRN_FLMAIL);
+ }
+}
+
+/*
+ * 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/pkginstall/reqexec.c b/usr/src/cmd/svr4pkg/pkginstall/reqexec.c
new file mode 100644
index 0000000000..86d369175c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/reqexec.c
@@ -0,0 +1,392 @@
+/*
+ * 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 2005 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 <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h> /* creat() declaration */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern char tmpdir[], instdir[];
+extern int pkgverbose;
+
+static int do_exec(int update, char *script, char *output,
+ char *inport, char *alt_user);
+static char path[PATH_MAX];
+static char *resppath = NULL;
+static int fd;
+static int respfile_defined = 0;
+static int respfile_ro = 0; /* read only resp file */
+
+/*
+ * This informs the calling routine if a read-only response file has been
+ * provided on the command line.
+ */
+int
+rdonly_respfile(void)
+{
+ return (respfile_ro);
+}
+
+int
+is_a_respfile(void)
+{
+ return (respfile_defined);
+}
+
+/*
+ * This function creates a working copy of the checkinstall script.
+ * This is needed in situations where the packages parent directories modes
+ * are set too restrictively, i.e. 700.
+ *
+ * Returns: A pointer to the location of the copied checkinstall
+ * script or NULL
+ */
+
+char *
+dup_chkinstall(char *script)
+{
+ char *dstpath;
+ size_t dstpathLen;
+ int r;
+static char *tmpname = "checkinstallXXXXXX";
+
+ /* determine length for destination script path */
+
+ dstpathLen = strlen(tmpdir) + strlen(tmpname) + 3;
+
+ /* allocate storage to hold destination script path */
+
+ dstpath = (char *)malloc(dstpathLen);
+ if (dstpath == (char *)NULL) {
+ return ((char *)NULL);
+ }
+
+ /* create destination script path */
+
+ (void) snprintf(dstpath, dstpathLen, "%s/%s", tmpdir, tmpname);
+
+ if (mktemp(dstpath) == NULL) {
+ progerr(ERR_TMPFILE_CHK);
+ (void) free(dstpath);
+ return (NULL);
+ }
+
+ /* make copy of script */
+
+ r = copyf(script, dstpath, (time_t)0);
+ if (r != 0) {
+ progerr(ERR_CANNOT_COPY, script, dstpath);
+ return (NULL);
+ }
+
+ /* Make the copy of the script readable by all */
+
+ if (chmod(dstpath, 0444) != 0) {
+ progerr(ERR_CHMOD_CHK);
+ (void) free(dstpath);
+ return (NULL);
+ }
+
+ return (dstpath);
+}
+
+/*
+ * This function creates a temporary working copy of a read-only response
+ * file. It changes the resppath pointer to point to the working copy.
+ */
+static int
+dup_respfile(void)
+{
+ char tpath[PATH_MAX];
+ int r;
+
+ (void) strlcpy(tpath, path, sizeof (tpath));
+
+ (void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
+
+ resppath = mktemp(path);
+ if (resppath == NULL) {
+ progerr(ERR_TMPRESP);
+ return (99);
+ }
+
+ /* Copy the contents of the user's response file to the working copy. */
+
+ r = copyf(tpath, resppath, (time_t)0);
+ if (r != 0) {
+ progerr(ERR_NORESPCOPY, tpath, resppath);
+ return (99);
+ }
+
+ /*
+ * Make it writable by the non-privileged installation user-id,
+ * but readable by the world.
+ */
+
+ if (chmod(resppath, 0644) != 0) {
+ progerr(ERR_CHMOD, resppath);
+ return (99);
+ }
+
+ respfile_ro = 0;
+
+ return (0);
+}
+
+/*
+ * This function establishes the response file passed on the command line if
+ * it's called with a valid string. If called with NULL, it checks to see if
+ * there's a response file already. If there isn't, it creates a temporary.
+ */
+int
+set_respfile(char *respfile, char *pkginst, int resp_stat)
+{
+ if (respfile == NULL && !respfile_defined) {
+ /* A temporary response file needs to be constructed. */
+ (void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
+ resppath = mktemp(path);
+ if (resppath == NULL) {
+ progerr(ERR_TMPRESP);
+ return (99);
+ }
+ } else {
+ /* OK, we're being passed a response file or directory. */
+ if (isdir(respfile) == 0) {
+ (void) snprintf(path, sizeof (path),
+ "%s/%s", respfile, pkginst);
+ } else {
+ (void) strlcpy(path, respfile, sizeof (path));
+ }
+
+ resppath = path;
+ respfile_ro = resp_stat;
+ }
+
+ respfile_defined++;
+
+ return (0);
+}
+
+/* This exposes the working response file. */
+char *
+get_respfile(void)
+{
+ return (resppath);
+}
+
+/*
+ * Execute the request script if present assuming the response file
+ * isn't read only.
+ */
+int
+reqexec(int update, char *script, int non_abi_scripts,
+ boolean_t enable_root_user)
+{
+ char *req_user;
+
+ /*
+ * determine which alternative user to execute the request script as
+ * if the default user "install" is not defined.
+ */
+
+ if (enable_root_user == B_TRUE) {
+ /* use the root user */
+ req_user = CHK_USER_ROOT;
+ } else if (non_abi_scripts != 0) {
+ /* non-compliant package user */
+ req_user = CHK_USER_NON;
+ } else {
+ /* standard non-privileged user */
+ req_user = CHK_USER_ALT;
+ }
+
+ /*
+ * If we can't get to the the script or the response file, skip this.
+ */
+ if (access(script, F_OK) != 0 || respfile_ro)
+ return (0);
+
+ /* No interact means no interact. */
+ if (echoGetFlag() == B_FALSE) {
+ ptext(stderr, ERR_INTR);
+ return (5);
+ }
+
+ /* If there's no response file, create one. */
+ if (!respfile_defined)
+ if (set_respfile(NULL, NULL, 0))
+ return (99);
+
+ /* Clear out the old response file (if there is one). */
+ if ((access(resppath, F_OK) == 0) && unlink(resppath)) {
+ progerr(ERR_RMRESP, resppath);
+ return (99);
+ }
+
+ /*
+ * Create a zero length response file which is only writable
+ * by the non-privileged installation user-id, but is readable
+ * by the world
+ */
+ if ((fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) < 0) {
+ progerr(ERR_CRERESP, resppath);
+ return (99);
+ }
+ (void) close(fd);
+
+ return (do_exec(update, script, resppath, REQ_STDIN, req_user));
+}
+
+int
+chkexec(int update, char *script)
+{
+ /*
+ * If we're up against a read-only response file from the command
+ * line. Create a working copy.
+ */
+ if (respfile_ro) {
+ if (dup_respfile())
+
+ return (99);
+
+ /* Make sure we can get to it. */
+ if ((access(resppath, F_OK) != 0)) {
+ progerr(ERR_ACCRESP, resppath);
+ return (7);
+ }
+ }
+
+ /* If there's no response file, create a fresh one. */
+ else if (!respfile_defined) {
+ if (set_respfile(NULL, NULL, 0))
+ return (99);
+
+ /*
+ * create a zero length response file which is only writable
+ * by the non-priveledged installation user-id, but is readable
+ * by the world
+ */
+ fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if (fd < 0) {
+ progerr(ERR_CRERESP, resppath);
+ return (99);
+ }
+ (void) close(fd);
+ }
+
+ return (do_exec(update, script, resppath, CHK_STDIN, CHK_USER_ALT));
+}
+
+static int
+do_exec(int update, char *script, char *output, char *inport, char *alt_user)
+{
+ char *gname;
+ char *tmp_script;
+ char *uname;
+ gid_t instgid;
+ int retcode = 0;
+ struct group *grp;
+ struct passwd *pwp;
+ uid_t instuid;
+
+ /*
+ * Determine which user to run the request script as:
+ * - if CHK_USER is a valid user, run the script as CHK_USER
+ * - otherwise, if alt_user is a valid user, run the script
+ * -- as alt_user
+ * - otherwise, output an error message and return failure
+ */
+
+ if ((pwp = getpwnam(CHK_USER)) != (struct passwd *)NULL) {
+ instuid = pwp->pw_uid;
+ uname = CHK_USER;
+ } else if ((pwp = getpwnam(alt_user)) != (struct passwd *)NULL) {
+ instuid = pwp->pw_uid;
+ uname = alt_user;
+ } else {
+ ptext(stderr, ERR_BADUSER, CHK_USER, CHK_USER_ALT);
+ return (1);
+ }
+
+ /*
+ * Determine which group to run the request script as:
+ * - If CHK_GRP is a valid group, run the script as CHK_GRP
+ * - otherwise, assume group "1" user "other"
+ */
+
+ if ((grp = getgrnam(CHK_GRP)) != (struct group *)NULL) {
+ instgid = grp->gr_gid;
+ gname = CHK_GRP;
+ } else {
+ instgid = (gid_t)1; /* "other" group id */
+ gname = "other"; /* "other" group name */
+ }
+
+ echoDebug(DBG_DO_EXEC_REQUEST_USER, script, output, uname, instuid,
+ gname, instgid);
+
+ (void) chown(output, instuid, instgid);
+
+ /*
+ * Copy the checkinstall script to tmpdir in case parent directories
+ * are restrictive, i.e. 700. Only do this for non updates, i.e.
+ * package installs and not patch package installs.
+ */
+ if (update) {
+ tmp_script = strdup(script);
+ } else if ((tmp_script = dup_chkinstall(script)) == NULL) {
+ /* Use the original checkinstall script */
+ tmp_script = strdup(script);
+ }
+
+ if (pkgverbose)
+ retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
+ "-x", tmp_script, output, NULL);
+ else
+ retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
+ tmp_script, output, NULL);
+
+ free(tmp_script);
+ return (retcode);
+}
diff --git a/usr/src/cmd/svr4pkg/pkginstall/sortmap.c b/usr/src/cmd/svr4pkg/pkginstall/sortmap.c
new file mode 100644
index 0000000000..a30ac15c60
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkginstall/sortmap.c
@@ -0,0 +1,170 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * This module constructs a list of entries from the pkgmap associated
+ * with this package. When finished, this list is sorted in alphabetical
+ * order and an accompanying structure list, mergstat, provides
+ * information about how these new files merge with existing files
+ * already on the system.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <install.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+/* libinst/ocfile.c */
+extern int dbchg;
+
+static int client_refer(struct cfextra **ext);
+static int server_refer(struct cfextra **ext);
+
+int
+sortmap(struct cfextra ***extlist, VFP_T *pkgmapVfp,
+ VFP_T *mapvfp, VFP_T *tmpvfp, char *a_zoneName)
+{
+ int i, n, nparts;
+ char *db_mrg = "unable to merge package and system information";
+
+ if (a_zoneName == (char *)NULL) {
+ echo(gettext("## Processing package information."));
+ } else {
+ echo(gettext("## Processing package information in zone <%s>."),
+ a_zoneName);
+ }
+
+ /*
+ * The following instruction puts the client-relative basedir
+ * into the environment iff it's a relocatable package and
+ * we're installing to a client. Otherwise, it uses the regular
+ * basedir. The only reason for this is so that mappath() upon
+ * finding $BASEDIR in a path will properly resolve it to the
+ * client-relative path. This way eval_path() can properly
+ * construct the server-relative path.
+ */
+ if (is_relocatable() && is_an_inst_root())
+ putparam("BASEDIR", get_info_basedir());
+
+ /*
+ * read the pkgmap provided by this package into
+ * memory; map parameters specified in the pathname
+ * and sort in memory by pathname
+ */
+
+ vfpRewind(pkgmapVfp); /* rewind input file */
+
+ *extlist = pkgobjmap(pkgmapVfp, 2, NULL);
+
+ if (*extlist == NULL) {
+ progerr(gettext("unable to process pkgmap"));
+ quit(99);
+ }
+
+ /* Make all paths client-relative if necessary. */
+ if (is_an_inst_root()) {
+ (void) client_refer(*extlist);
+ }
+
+ if (a_zoneName == (char *)NULL) {
+ echo(gettext("## Processing system information."));
+ } else {
+ echo(gettext("## Processing system information in zone <%s>."),
+ a_zoneName);
+ }
+
+ /*
+ * calculate the number of parts in this package
+ * by locating the entry with the largest "volno"
+ * associated with it
+ */
+ nparts = 0;
+ if (is_depend_pkginfo_DB() == B_FALSE) {
+ for (i = 0; (*extlist)[i]; i++) {
+ n = (*extlist)[i]->cf_ent.volno;
+ if (n > nparts)
+ nparts = n;
+ }
+
+ vfpTruncate(tmpvfp);
+
+ dbchg = pkgdbmerg(mapvfp, tmpvfp, *extlist, 60);
+ if (dbchg < 0) {
+ progerr(gettext(db_mrg));
+ quit(99);
+ }
+ }
+
+ /* Restore the original BASEDIR. */
+ if (is_relocatable() && is_an_inst_root())
+ putparam("BASEDIR", get_basedir());
+
+ if (is_an_inst_root()) {
+ (void) server_refer(*extlist);
+ }
+
+ return (nparts);
+}
+
+static int
+client_refer(struct cfextra **ext)
+{
+ int count;
+
+ for (count = 0; ext[count] != (struct cfextra *)NULL; count++) {
+ ext[count]->cf_ent.path = ext[count]->client_path;
+ ext[count]->cf_ent.ainfo.local = ext[count]->client_local;
+ }
+
+ return (1);
+}
+
+static int
+server_refer(struct cfextra **ext)
+{
+ int count;
+
+ for (count = 0; ext[count] != (struct cfextra *)NULL; count++) {
+ ext[count]->cf_ent.path = ext[count]->server_path;
+ ext[count]->cf_ent.ainfo.local = ext[count]->server_local;
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgmk/Makefile b/usr/src/cmd/svr4pkg/pkgmk/Makefile
new file mode 100644
index 0000000000..401668f8a0
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgmk/Makefile
@@ -0,0 +1,45 @@
+#
+# 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= pkgmk
+
+OBJS= main.o \
+ mkpkgmap.o \
+ quit.o \
+ splpkgmap.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -ll -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgmk/main.c b/usr/src/cmd/svr4pkg/pkgmk/main.c
new file mode 100644
index 0000000000..d3c6da1f22
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgmk/main.c
@@ -0,0 +1,985 @@
+/*
+ * 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 <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <sys/statvfs.h>
+#include <sys/utsname.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char **environ, *pkgdir;
+
+/* mkpkgmap.c */
+extern int mkpkgmap(char *outfile, char *protofile, char **cmdparam);
+/* splpkgmap.c */
+extern int splpkgmap(struct cfent **eptlist, unsigned int eptnum,
+ char *order[], ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit,
+ fsfilcnt_t *pilimit, fsblkcnt_t *pllimit);
+/* scriptvfy.c */
+extern int checkscripts(char *inst_dir, int silent);
+
+/* libpkg/gpkgmap.c */
+extern void setmapmode(int mode_no);
+
+static boolean_t valid_zone_attr(struct cfent **eptlist);
+
+#define MALSIZ 16
+#define NROOT 8
+#define SPOOLDEV "spool"
+
+#define MSG_PROTOTYPE "## Building pkgmap from package prototype file.\n"
+#define MSG_PKGINFO "## Processing pkginfo file.\n"
+#define MSG_VOLUMIZE "## Attempting to volumize %d entries in pkgmap.\n"
+#define MSG_PACKAGE1 "## Packaging one part.\n"
+#define MSG_PACKAGEM "## Packaging %d parts.\n"
+#define MSG_VALSCRIPTS "## Validating control scripts.\n"
+
+/* Other problems */
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+#define ERR_NROOT "too many paths listed with -r option, limit is %d"
+#define ERR_PKGINST "invalid package instance identifier <%s>"
+#define ERR_PKGABRV "invalid package abbreviation <%s>"
+#define ERR_BADDEV "unknown or invalid device specified <%s>"
+#define ERR_TEMP "unable to obtain temporary file resources, errno=%d"
+#define ERR_DSTREAM "invalid device specified (datastream) <%s>"
+#define ERR_SPLIT "unable to volumize package"
+#define ERR_MKDIR "unable to make directory <%s>"
+#define ERR_SYMLINK "unable to create symbolic link for <%s>"
+#define ERR_OVERWRITE "must use -o option to overwrite <%s>"
+#define ERR_UMOUNT "unable to unmount device <%s>"
+#define ERR_NOPKGINFO "required pkginfo file is not specified in prototype " \
+ "file"
+#define ERR_RDPKGINFO "unable to process pkginfo file <%s>"
+#define ERR_PROTOTYPE "unable to locate prototype file"
+#define ERR_STATVFS "unable to stat filesystem <%s>"
+#define ERR_WHATVFS "unable to determine or access output filesystem for " \
+ "device <%s>"
+#define ERR_DEVICE "unable to find info for device <%s>"
+#define ERR_BUILD "unable to build pkgmap from prototype file"
+#define ERR_ONEVOL "other packages found - package must fit on a single " \
+ "volume"
+#define ERR_NOPARAM "parameter <%s> is not defined in <%s>"
+#define ERR_PKGMTCH "PKG parameter <%s> does not match instance <%s>"
+#define ERR_NO_PKG_INFOFILE "unable to open pkginfo file <%s>: %s"
+#define ERR_ALLZONES_AND_THISZONE "The package <%s> has <%s> = true " \
+ "and <%s> = true: the package may " \
+ "set either parameter to true, but " \
+ "may not set both parameters to " \
+ "true. NOTE: if the package " \
+ "contains a request script, it is " \
+ "treated as though it has " \
+ "<SUNW_PKG_THISZONE> = true"
+#define ERR_NO_ALLZONES_AND_HOLLOW "The package <%s> has <%s> = false " \
+ "and <%s> = true: a hollow package " \
+ "must also be set to install in all " \
+ "zones"
+#define ERR_PKGINFO_INVALID_OPTION_COMB "Invalid combinations of zone " \
+ "parameters in pkginfo file"
+
+#define ERR_USAGE "usage: %s [options] [VAR=value [VAR=value]] " \
+ "[pkginst]\n" \
+ " where options may include:\n" \
+ "\t-o\n" \
+ "\t-a arch\n" \
+ "\t-v version\n" \
+ "\t-p pstamp\n" \
+ "\t-l limit\n" \
+ "\t-r rootpath\n" \
+ "\t-b basedir\n" \
+ "\t-d device\n" \
+ "\t-f protofile\n"
+#define WRN_MISSINGDIR "WARNING: missing directory entry for <%s>"
+#define WRN_SETPARAM "WARNING: parameter <%s> set to \"%s\""
+#define WRN_CLASSES "WARNING: unreferenced class <%s> in prototype file"
+
+#define LINK 1
+
+struct pkgdev pkgdev; /* holds info about the installation device */
+int started;
+char pkgloc[PATH_MAX];
+char *basedir;
+char *root;
+char *rootlist[NROOT];
+char *t_pkgmap;
+char *t_pkginfo;
+
+static struct cfent *svept;
+static char *protofile,
+ *device;
+static fsblkcnt_t limit = 0;
+static fsblkcnt_t llimit = 0;
+static fsfilcnt_t ilimit = 0;
+static int overwrite,
+ nflag,
+ sflag;
+static void ckmissing(char *path, char type);
+static void outvol(struct cfent **eptlist, unsigned int eptnum, int part,
+ int nparts);
+static void trap(int n);
+static void usage(void);
+
+static int slinkf(char *from, char *to);
+
+int
+main(int argc, char *argv[])
+{
+ struct utsname utsbuf;
+ struct statvfs64 svfsb;
+ struct cfent **eptlist;
+ FILE *fp;
+ VFP_T *vfp;
+ int c, n, found;
+ int part, nparts, npkgs, objects;
+ char buf[MAX_PKG_PARAM_LENGTH];
+ char temp[MAX_PKG_PARAM_LENGTH];
+ char param[MAX_PKG_PARAM_LENGTH];
+ char *pt, *value, *pkginst, *tmpdir, *abi_sym_ptr,
+ **cmdparam;
+ char *pkgname;
+ char *pkgvers;
+ char *pkgarch;
+ char *pkgcat;
+ void (*func)();
+ time_t clock;
+ ulong_t bsize = 0;
+ ulong_t frsize = 0;
+ struct cl_attr **allclass = NULL;
+ struct cl_attr **order;
+ unsigned int eptnum, i;
+
+ /* initialize locale environment */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* initialize program name */
+
+ (void) set_prog_name(argv[0]);
+
+ /* tell spmi zones interface how to access package output functions */
+
+ z_set_output_functions(echo, echoDebug, progerr);
+
+ func = sigset(SIGINT, trap);
+ if (func != SIG_DFL)
+ func = sigset(SIGINT, func);
+ func = sigset(SIGHUP, trap);
+ setmapmode(MAPBUILD); /* variable binding */
+ if (func != SIG_DFL)
+ func = sigset(SIGHUP, func);
+
+ environ = NULL;
+ while ((c = getopt(argc, argv, "osnp:l:r:b:d:f:a:v:?")) != EOF) {
+ switch (c) {
+ case 'n':
+ nflag++;
+ break;
+
+ case 's':
+ sflag++;
+ break;
+
+ case 'o':
+ overwrite++;
+ break;
+
+ case 'p':
+ putparam("PSTAMP", optarg);
+ break;
+
+ case 'l':
+ llimit = strtoull(optarg, NULL, 10);
+ break;
+
+ case 'r':
+ pt = strtok(optarg, " \t\n, ");
+ n = 0;
+ do {
+ rootlist[n++] = flex_device(pt, 0);
+ if (n >= NROOT) {
+ progerr(gettext(ERR_NROOT), NROOT);
+ quit(1);
+ }
+ } while (pt = strtok(NULL, " \t\n, "));
+ rootlist[n] = NULL;
+ break;
+
+ case 'b':
+ basedir = optarg;
+ break;
+
+ case 'f':
+ protofile = optarg;
+ break;
+
+ case 'd':
+ device = flex_device(optarg, 1);
+ break;
+
+ case 'a':
+ putparam("ARCH", optarg);
+ break;
+
+ case 'v':
+ putparam("VERSION", optarg);
+ break;
+
+ 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);
+ }
+ }
+
+ /*
+ * Store command line variable assignments for later
+ * incorporation into the environment.
+ */
+ cmdparam = &argv[optind];
+
+ /* Skip past equates. */
+ while (argv[optind] && strchr(argv[optind], '='))
+ optind++;
+
+ /* Confirm that the instance name is valid */
+ if ((pkginst = argv[optind]) != NULL) {
+ if (pkgnmchk(pkginst, "all", 0)) {
+ progerr(gettext(ERR_PKGINST), pkginst);
+ quit(1);
+ }
+ argv[optind++] = NULL;
+ }
+ if (optind != argc)
+ usage();
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL)
+ tmpdir = P_tmpdir;
+
+ /* bug id 4244631, not ABI compliant */
+ abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+ if (abi_sym_ptr && (strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)) {
+ set_nonABI_symlinks();
+ }
+
+ if (device == NULL) {
+ device = devattr(SPOOLDEV, "pathname");
+ if (device == NULL) {
+ progerr(gettext(ERR_DEVICE), SPOOLDEV);
+ exit(99);
+ }
+ }
+
+ if (protofile == NULL) {
+ if (access("prototype", 0) == 0)
+ protofile = "prototype";
+ else if (access("Prototype", 0) == 0)
+ protofile = "Prototype";
+ else {
+ progerr(gettext(ERR_PROTOTYPE));
+ quit(1);
+ }
+ }
+
+ if (devtype(device, &pkgdev)) {
+ progerr(gettext(ERR_BADDEV), device);
+ quit(1);
+ }
+ if (pkgdev.norewind) {
+ /* initialize datastream */
+ progerr(gettext(ERR_DSTREAM), device);
+ quit(1);
+ }
+ if (pkgdev.mount) {
+ if (n = pkgmount(&pkgdev, NULL, 0, 0, 1))
+ quit(n);
+ }
+
+ /*
+ * convert prototype file to a pkgmap, while locating
+ * package objects in the current environment
+ */
+ t_pkgmap = tempnam(tmpdir, "tmpmap");
+ if (t_pkgmap == NULL) {
+ progerr(gettext(ERR_TEMP), errno);
+ exit(99);
+ }
+
+ (void) fprintf(stderr, gettext(MSG_PROTOTYPE));
+ if (n = mkpkgmap(t_pkgmap, protofile, cmdparam)) {
+ progerr(gettext(ERR_BUILD));
+ quit(1);
+ }
+
+ setmapmode(MAPNONE); /* All appropriate variables are now bound */
+
+ if (vfpOpen(&vfp, t_pkgmap, "r", VFP_NEEDNOW) != 0) {
+ progerr(gettext(ERR_TEMP), errno);
+ quit(99);
+ }
+
+ eptlist = procmap(vfp, 0, NULL);
+
+ if (eptlist == NULL) {
+ quit(1);
+ }
+
+ (void) vfpClose(&vfp);
+
+ /* Validate the zone attributes in pkginfo, before creation */
+ if (!valid_zone_attr(eptlist)) {
+ progerr(ERR_PKGINFO_INVALID_OPTION_COMB);
+ quit(1);
+ }
+
+ (void) fprintf(stderr, gettext(MSG_PKGINFO));
+ pt = NULL;
+ for (i = 0; eptlist[i]; i++) {
+ ckmissing(eptlist[i]->path, eptlist[i]->ftype);
+ if (eptlist[i]->ftype != 'i')
+ continue;
+ if (strcmp(eptlist[i]->path, "pkginfo") == 0)
+ svept = eptlist[i];
+ }
+ if (svept == NULL) {
+ progerr(gettext(ERR_NOPKGINFO));
+ quit(99);
+ }
+ eptnum = i;
+
+ /*
+ * process all parameters from the pkginfo file
+ * and place them in the execution environment
+ */
+
+ if ((fp = fopen(svept->ainfo.local, "r")) == NULL) {
+ progerr(gettext(ERR_RDPKGINFO), svept->ainfo.local);
+ quit(99);
+ }
+ param[0] = '\0';
+ while (value = fpkgparam(fp, param)) {
+ if (getenv(param) == NULL)
+ putparam(param, value);
+ free((void *)value);
+ param[0] = '\0';
+ }
+ (void) fclose(fp);
+
+ /* add command line variables */
+ while (*cmdparam && (value = strchr(*cmdparam, '=')) != NULL) {
+ *value = NULL; /* terminate the parameter */
+ value++; /* value is now the value (not '=') */
+ putparam(*cmdparam++, value); /* store it in environ */
+ }
+
+ /* make sure parameters are valid */
+ (void) time(&clock);
+ if (pt = getenv("PKG")) {
+ if (pkgnmchk(pt, NULL, 0) || strchr(pt, '.')) {
+ progerr(gettext(ERR_PKGABRV), pt);
+ quit(1);
+ }
+ if (pkginst == NULL)
+ pkginst = pt;
+ } else {
+ progerr(gettext(ERR_NOPARAM), "PKG", svept->path);
+ quit(1);
+ }
+ /*
+ * verify consistency between PKG parameter and pkginst
+ */
+ (void) snprintf(param, sizeof (param), "%s.*", pt);
+ if (pkgnmchk(pkginst, param, 0)) {
+ progerr(gettext(ERR_PKGMTCH), pt, pkginst);
+ quit(1);
+ }
+
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ /* Until 2.9, set it from the execption list */
+ if (exception_pkg(pkginst, LINK))
+ set_nonABI_symlinks();
+#endif
+
+ if ((pkgname = getenv("NAME")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "NAME", svept->path);
+ quit(1);
+ }
+ if (ckparam("NAME", pkgname))
+ quit(1);
+ if ((pkgvers = getenv("VERSION")) == NULL) {
+ /* XXX - I18n */
+ /* LINTED do not use cftime(); use strftime instead */
+ (void) cftime(buf, "\045m/\045d/\045Y", &clock);
+ (void) snprintf(temp, sizeof (temp),
+ gettext("Dev Release %s"), buf);
+ putparam("VERSION", temp);
+ pkgvers = getenv("VERSION");
+ logerr(gettext(WRN_SETPARAM), "VERSION", temp);
+ }
+ if (ckparam("VERSION", pkgvers))
+ quit(1);
+ if ((pkgarch = getenv("ARCH")) == NULL) {
+ (void) uname(&utsbuf);
+ putparam("ARCH", utsbuf.machine);
+ pkgarch = getenv("ARCH");
+ logerr(gettext(WRN_SETPARAM), "ARCH", utsbuf.machine);
+ }
+ if (ckparam("ARCH", pkgarch))
+ quit(1);
+ if (getenv("PSTAMP") == NULL) {
+ /* use octal value of '%' to fight sccs expansion */
+ /* XXX - I18n */
+ /* LINTED do not use cftime(); use strftime instead */
+ (void) cftime(buf, "\045Y\045m\045d\045H\045M\045S", &clock);
+ (void) uname(&utsbuf);
+ (void) snprintf(temp, sizeof (temp), "%s%s",
+ utsbuf.nodename, buf);
+ putparam("PSTAMP", temp);
+ logerr(gettext(WRN_SETPARAM), "PSTAMP", temp);
+ }
+ if ((pkgcat = getenv("CATEGORY")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "CATEGORY", svept->path);
+ quit(1);
+ }
+ if (ckparam("CATEGORY", pkgcat))
+ quit(1);
+
+ /*
+ * warn user of classes listed in package which do
+ * not appear in CLASSES variable in pkginfo file
+ */
+ objects = 0;
+ for (i = 0; eptlist[i]; i++) {
+ if (eptlist[i]->ftype != 'i') {
+ objects++;
+ addlist(&allclass, eptlist[i]->pkg_class);
+ }
+ }
+
+ if ((pt = getenv("CLASSES")) == NULL) {
+ if (allclass && *allclass) {
+ cl_setl(allclass);
+ cl_putl("CLASSES", allclass);
+ logerr(gettext(WRN_SETPARAM), "CLASSES",
+ getenv("CLASSES"));
+ }
+ } else {
+ cl_sets(qstrdup(pt));
+ if (allclass && *allclass) {
+ for (i = 0; allclass[i]; i++) {
+ found = 0;
+ if (cl_idx(allclass[i]->name) != -1) {
+ found++;
+ break;
+ }
+ if (!found) {
+ logerr(gettext(WRN_CLASSES),
+ (char *)allclass[i]);
+ }
+ }
+ }
+ }
+
+ (void) fprintf(stderr, gettext(MSG_VOLUMIZE), objects);
+ order = (struct cl_attr **)0;
+ if (pt = getenv("ORDER")) {
+ pt = qstrdup(pt);
+ (void) setlist(&order, pt);
+ cl_putl("ORDER", order);
+ }
+
+ /* stat the intended output filesystem to get blocking information */
+ if (pkgdev.dirname == NULL) {
+ progerr(gettext(ERR_WHATVFS), device);
+ quit(99);
+ }
+ if (statvfs64(pkgdev.dirname, &svfsb)) {
+ progerr(gettext(ERR_STATVFS), pkgdev.dirname);
+ quit(99);
+ }
+
+ if (bsize == 0) {
+ bsize = svfsb.f_bsize;
+ }
+ if (frsize == 0) {
+ frsize = svfsb.f_frsize;
+ }
+
+ if (limit == 0)
+ /*
+ * bavail is in terms of fragment size blocks - change
+ * to 512 byte blocks
+ */
+ limit = (fsblkcnt_t)(((fsblkcnt_t)frsize > 0) ?
+ howmany(frsize, DEV_BSIZE) :
+ howmany(bsize, DEV_BSIZE)) * svfsb.f_bavail;
+
+ if (ilimit == 0) {
+ ilimit = (svfsb.f_favail > 0) ?
+ svfsb.f_favail : svfsb.f_ffree;
+ }
+
+ nparts = splpkgmap(eptlist, eptnum, (char **)order, bsize, frsize,
+ &limit, &ilimit, &llimit);
+
+ if (nparts <= 0) {
+ progerr(gettext(ERR_SPLIT));
+ quit(1);
+ }
+
+ if (nflag) {
+ for (i = 0; eptlist[i]; i++)
+ (void) ppkgmap(eptlist[i], stdout);
+ exit(0);
+ /*NOTREACHED*/
+ }
+
+ (void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s",
+ pkgdev.dirname, pkginst);
+ if (!isdir(pkgloc) && !overwrite) {
+ progerr(gettext(ERR_OVERWRITE), pkgloc);
+ quit(1);
+ }
+
+ /* output all environment install parameters */
+ t_pkginfo = tempnam(tmpdir, "pkginfo");
+ if ((fp = fopen(t_pkginfo, "w")) == NULL) {
+ progerr(gettext(ERR_TEMP), errno);
+ exit(99);
+ }
+ for (i = 0; environ[i]; i++) {
+ if (isupper(*environ[i])) {
+ (void) fputs(environ[i], fp);
+ (void) fputc('\n', fp);
+ }
+ }
+ (void) fclose(fp);
+
+ started++;
+ (void) rrmdir(pkgloc);
+ if (mkdir(pkgloc, 0755)) {
+ progerr(gettext(ERR_MKDIR), pkgloc);
+ quit(1);
+ }
+
+ /* determine how many packages already reside on the medium */
+ pkgdir = pkgdev.dirname;
+ npkgs = 0;
+ while (pt = fpkginst("all", NULL, NULL))
+ npkgs++;
+ (void) fpkginst(NULL); /* free resource usage */
+
+ if (nparts > 1) {
+ if (pkgdev.mount && npkgs) {
+ progerr(gettext(ERR_ONEVOL));
+ quit(1);
+ }
+ }
+
+ /*
+ * update pkgmap entry for pkginfo file, since it may
+ * have changed due to command line or failure to
+ * specify all neccessary parameters
+ */
+ for (i = 0; eptlist[i]; i++) {
+ if (eptlist[i]->ftype != 'i')
+ continue;
+ if (strcmp(eptlist[i]->path, "pkginfo") == 0) {
+ svept = eptlist[i];
+ svept->ftype = '?';
+ svept->ainfo.local = t_pkginfo;
+ (void) cverify(0, &svept->ftype, t_pkginfo,
+ &svept->cinfo, 1);
+ svept->ftype = 'i';
+ break;
+ }
+ }
+
+ if (nparts > 1)
+ (void) fprintf(stderr, gettext(MSG_PACKAGEM), nparts);
+ else
+ (void) fprintf(stderr, gettext(MSG_PACKAGE1));
+
+ for (part = 1; part <= nparts; part++) {
+ if ((part > 1) && pkgdev.mount) {
+ if (pkgumount(&pkgdev)) {
+ progerr(gettext(ERR_UMOUNT), pkgdev.mount);
+ quit(99);
+ }
+ if (n = pkgmount(&pkgdev, NULL, part, nparts, 1))
+ quit(n);
+ (void) rrmdir(pkgloc);
+ if (mkdir(pkgloc, 0555)) {
+ progerr(gettext(ERR_MKDIR), pkgloc);
+ quit(99);
+ }
+ }
+ outvol(eptlist, eptnum, part, nparts);
+
+ /* Validate (as much as possible) the control scripts. */
+ if (part == 1) {
+ char inst_path[PATH_MAX];
+
+ (void) fprintf(stderr, gettext(MSG_VALSCRIPTS));
+ (void) snprintf(inst_path, sizeof (inst_path),
+ "%s/install", pkgloc);
+ checkscripts(inst_path, 0);
+ }
+ }
+
+ quit(0);
+ /* LINTED: no return */
+}
+
+static void
+trap(int n)
+{
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (n == SIGINT)
+ quit(3);
+ else {
+ (void) fprintf(stderr, gettext("%s terminated (signal %d).\n"),
+ get_prog_name(), n);
+ quit(99);
+ }
+}
+
+static void
+outvol(struct cfent **eptlist, unsigned int eptnum, int part, int nparts)
+{
+ FILE *fp;
+ char *svpt, *path, temp[PATH_MAX];
+ unsigned int i;
+
+
+ if (nparts > 1)
+ (void) fprintf(stderr, gettext(" -- part %2d:\n"), part);
+ if (part == 1) {
+ /* re-write pkgmap, but exclude local pathnames */
+ (void) snprintf(temp, sizeof (temp), "%s/pkgmap", pkgloc);
+ if ((fp = fopen(temp, "w")) == NULL) {
+ progerr(gettext(ERR_TEMP), errno);
+ quit(99);
+ }
+ (void) fprintf(fp, ": %d %ld\n", nparts, limit);
+ for (i = 0; eptlist[i]; i++) {
+ svpt = eptlist[i]->ainfo.local;
+ if (!strchr("sl", eptlist[i]->ftype))
+ eptlist[i]->ainfo.local = NULL;
+ if (ppkgmap(eptlist[i], fp)) {
+ progerr(gettext(ERR_TEMP), errno);
+ quit(99);
+ }
+ eptlist[i]->ainfo.local = svpt;
+ }
+ (void) fclose(fp);
+ (void) fprintf(stderr, "%s\n", temp);
+ }
+
+ (void) snprintf(temp, sizeof (temp), "%s/pkginfo", pkgloc);
+ if (copyf(svept->ainfo.local, temp, svept->cinfo.modtime))
+ quit(1);
+ (void) fprintf(stderr, "%s\n", temp);
+
+ for (i = 0; i < eptnum; i++) {
+ if (eptlist[i]->volno != part)
+ continue;
+ if (strchr("dxslcbp", eptlist[i]->ftype))
+ continue;
+ if (eptlist[i]->ftype == 'i') {
+ if (eptlist[i] == svept)
+ continue; /* don't copy pkginfo file */
+ (void) snprintf(temp, sizeof (temp),
+ "%s/install/%s", pkgloc,
+ eptlist[i]->path);
+ path = temp;
+ } else
+ path = srcpath(pkgloc, eptlist[i]->path, part, nparts);
+ if (sflag) {
+ if (slinkf(eptlist[i]->ainfo.local, path))
+ quit(1);
+ } else if (copyf(eptlist[i]->ainfo.local, path,
+ eptlist[i]->cinfo.modtime)) {
+ quit(1);
+ }
+
+ /*
+ * If the package file attributes can be sync'd up with
+ * the pkgmap, we fix the attributes here.
+ */
+ if (*(eptlist[i]->ainfo.owner) != '$' &&
+ *(eptlist[i]->ainfo.group) != '$' && getuid() == 0) {
+ /* Clear dangerous bits. */
+ eptlist[i]->ainfo.mode=
+ (eptlist[i]->ainfo.mode & S_IAMB);
+ /*
+ * Make sure it can be read by the world and written
+ * by root.
+ */
+ eptlist[i]->ainfo.mode |= 0644;
+ if (!strchr("in", eptlist[i]->ftype)) {
+ /* Set the safe attributes. */
+ averify(1, &(eptlist[i]->ftype),
+ path, &(eptlist[i]->ainfo));
+ }
+ }
+
+ (void) fprintf(stderr, "%s\n", path);
+ }
+}
+
+static void
+ckmissing(char *path, char type)
+{
+ static char **dir;
+ static int ndir;
+ char *pt;
+ int i, found;
+
+ if (dir == NULL) {
+ dir = (char **)calloc(MALSIZ, sizeof (char *));
+ if (dir == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+
+ if (strchr("dx", type)) {
+ dir[ndir] = path;
+ if ((++ndir % MALSIZ) == 0) {
+ dir = (char **)realloc((void *)dir,
+ (ndir+MALSIZ)*sizeof (char *));
+ if (dir == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+ dir[ndir] = (char *)NULL;
+ }
+
+ pt = path;
+ if (*pt == '/')
+ pt++;
+ while (pt = strchr(pt, '/')) {
+ *pt = '\0';
+ found = 0;
+ for (i = 0; i < ndir; i++) {
+ if (strcmp(path, dir[i]) == 0) {
+ found++;
+ break;
+ }
+ }
+ if (!found) {
+ logerr(gettext(WRN_MISSINGDIR), path);
+ ckmissing(qstrdup(path), 'd');
+ }
+ *pt++ = '/';
+ }
+}
+
+static int
+slinkf(char *from, char *to)
+{
+ char *pt;
+
+ pt = to;
+ while (pt = strchr(pt+1, '/')) {
+ *pt = '\0';
+ if (isdir(to) && mkdir(to, 0755)) {
+ progerr(gettext(ERR_MKDIR), to);
+ *pt = '/';
+ return (-1);
+ }
+ *pt = '/';
+ }
+ if (symlink(from, to)) {
+ progerr(gettext(ERR_SYMLINK), to);
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(ERR_USAGE), get_prog_name());
+ exit(1);
+ /*NOTREACHED*/
+}
+
+/*
+ * valid_zone_attr: Validates the zone attributes specified in
+ * pkginfo file for this package. The package
+ * can not be created with certain combinations
+ * of the attributes.
+ */
+static boolean_t
+valid_zone_attr(struct cfent **eptlist)
+{
+ FILE *pkginfoFP;
+ boolean_t all_zones; /* pkg is "all zones" only */
+ boolean_t is_hollow; /* pkg is "hollow" */
+ boolean_t this_zone; /* pkg is "this zone" only */
+ char pkginfoPath[PATH_MAX]; /* pkginfo file path */
+ char *pkgInst;
+ int i;
+
+ /* Path to pkginfo file within the package to be installed */
+
+ this_zone = B_FALSE;
+ for (i = 0; eptlist[i]; i++) {
+ if (eptlist[i]->ftype != 'i')
+ continue;
+ if (strcmp(eptlist[i]->path, "pkginfo") == 0)
+ (void) strcpy(pkginfoPath, eptlist[i]->ainfo.local);
+
+ /*
+ * 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 request file is set in prototype, then this_zone is TRUE.
+ */
+ if (strcmp(eptlist[i]->path, "request") == 0)
+ this_zone = B_TRUE;
+ }
+
+ /* Gather information from the pkginfo file */
+
+ pkginfoFP = fopen(pkginfoPath, "r");
+
+ if (pkginfoFP == NULL) {
+ progerr(ERR_NO_PKG_INFOFILE, pkginfoPath, strerror(errno));
+ return (B_FALSE);
+ }
+
+ if ((pkgInst = fpkgparam(pkginfoFP, "PKG")) == NULL) {
+ progerr(gettext(ERR_NOPARAM), "PKG", pkginfoPath);
+ 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, if no request file */
+ if (!this_zone)
+ this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
+ "true", B_FALSE);
+
+ /* Close pkginfo file */
+ (void) fclose(pkginfoFP);
+
+ /*
+ * Validate zone attributes based on information gathered,
+ * and validate the three SUNW_PKG_ options:
+ *
+ * -----------------------------|---------------|
+ * <ALLZONES><HOLLOW><THISZONE> | If Allowed |
+ * ----1------------------------|---------------|
+ * F F F | OK |
+ * F F T | OK |
+ * F T * | NO |
+ * ----2------------------------|---------------|
+ * T F F | OK |
+ * T T F | OK |
+ * T * T | NO |
+ * -----------------------------|---------------|
+ */
+
+ /* pkg "all zones" && "this zone" (#2) */
+
+ if (all_zones && this_zone) {
+ progerr(ERR_ALLZONES_AND_THISZONE, pkgInst,
+ PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
+ return (B_FALSE);
+ }
+
+ /* pkg "!all zones" && "hollow" (#1) */
+
+ if ((!all_zones) && is_hollow) {
+ progerr(ERR_NO_ALLZONES_AND_HOLLOW, pkgInst,
+ PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.c b/usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.c
new file mode 100644
index 0000000000..a0c327fb0c
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.c
@@ -0,0 +1,827 @@
+/*
+ * 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 2006 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 <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char *basedir, *root, *rootlist[], **environ;
+
+/*
+ * IMPORTANT NOTE: PLEASE SEE THE DEFINITION OF temp[] BELOW BEFORE
+ * CHANGING THE DEFINITION OF PATH_LGTH!!!!
+ */
+
+#define PATH_LGTH 4096
+
+#define MAXPARAMS 256
+#define NRECURS 20
+
+#define MSG_BPARAMC "parametric class specification for <%s> not allowed"
+#define MSG_SRCHLOC "no object for <%s> found in local path"
+#define MSG_SRCHSRCH "no object for <%s> found in search path"
+#define MSG_SRCHROOT "no object for <%s> found in root directory"
+#define MSG_CONTENTS "unable to process contents of object <%s>"
+#define MSG_WRITE "write of entry failed, errno=%d"
+#define MSG_GARBDEFLT "garbled default settings: %s"
+#define MSG_BANG "unknown directive: %s"
+#define MSG_CHDIR "unable to change directory to <%s>"
+#define MSG_INCOMPLETE "processing of <%s> may be incomplete"
+#define MSG_NRECURS "too many levels of include (limit is %d)"
+#define MSG_RDINCLUDE "unable to process include file <%s>, errno=%d"
+#define MSG_IGNINCLUDE "ignoring include file <%s>"
+#define MSG_NODEVICE "device numbers cannot be determined for <%s>"
+
+#define WRN_BADATTR "WARNING: attributes set to %04o %s %s for <%s>"
+#define WRN_BADATTRM "WARNING: attributes set to %s %s %s for <%s>"
+#define WRN_FAKEBD "WARNING: parametric paths may ignore BASEDIR"
+
+#define ERR_TEMP "unable to obtain temporary file resources, errno=%d"
+#define ERR_ENVBUILD "unable to build parameter environment, errno=%d"
+#define ERR_MAXPARAMS "too many parameter definitions (limit is %d)"
+#define ERR_GETCWD "unable to get current directory, errno=%d"
+#define ERR_PATHVAR "cannot resolve all build parameters associated with " \
+ "path <%s>."
+
+static struct cfent entry;
+static FILE *fp,
+ *sfp[20];
+static char *dname[NRECURS],
+ *params[256],
+ *proto[NRECURS],
+ *rootp[NRECURS][16],
+ *srchp[NRECURS][16],
+ *d_own[NRECURS],
+ *d_grp[NRECURS],
+ *rdonly[256];
+static mode_t d_mod[NRECURS];
+static int nfp = (-1);
+static int nrdonly = 0;
+static int errflg = 0;
+static char *separ = " \t\n, ";
+
+/* libpkg/gpkgmap.c */
+extern void attrpreset(int mode, char *owner, char *group);
+extern void attrdefault();
+static char *findfile(char *path, char *local);
+static char *srchroot(char *path, char *copy);
+
+static int popenv(void);
+
+static int doattrib(void);
+static void doinclude(void);
+static void dorsearch(void);
+static void dosearch(void);
+static void error(int flag);
+static void lputenv(char *s);
+static void pushenv(char *file);
+static void translate(register char *pt, register char *copy);
+
+int
+mkpkgmap(char *outfile, char *protofile, char **envparam)
+{
+ FILE *tmpfp;
+ char *pt, *path, mybuff[PATH_LGTH];
+ char **envsave;
+ int c, fakebasedir;
+ int i, n;
+
+ /*
+ * NOTE: THE SIZE OF temp IS HARD CODED INTO CALLS TO fscanf.
+ * YOU *MUST* MAKE SURE TO CHANGE THOSE CALLS IF THE SIZE OF temp
+ * IS EVER CHANGED!!!!!!
+ */
+ char temp[PATH_LGTH];
+
+ if ((tmpfp = fopen(outfile, "w")) == NULL) {
+ progerr(gettext(ERR_TEMP), errno);
+ quit(99);
+ }
+ envsave = environ;
+ environ = params; /* use only local environ */
+ attrdefault(); /* assume no default attributes */
+
+ /*
+ * Environment parameters are optional, so variable
+ * (envparam[i]) could be NULL.
+ */
+ for (i = 0; (envparam[i] != NULL) &&
+ (pt = strchr(envparam[i], '=')); i++) {
+ *pt = '\0';
+ rdonly[nrdonly++] = qstrdup(envparam[i]);
+ *pt = '=';
+ if (putenv(qstrdup(envparam[i]))) { /* bugid 1090920 */
+ progerr(gettext(ERR_ENVBUILD), errno);
+ quit(99);
+ }
+ if (nrdonly >= MAXPARAMS) {
+ progerr(gettext(ERR_MAXPARAMS), MAXPARAMS);
+ quit(1);
+ }
+ }
+
+ pushenv(protofile);
+ errflg = 0;
+again:
+ fakebasedir = 0;
+ while (!feof(fp)) {
+ c = getc(fp);
+ while (isspace(c))
+ c = getc(fp);
+
+ if (c == '#') {
+ do c = getc(fp); while ((c != EOF) && (c != '\n'));
+ continue;
+ }
+ if (c == EOF)
+ break;
+
+ if (c == '!') {
+ /*
+ * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO
+ * the FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS
+ * LINE IF THE SIZE OF fscanf IS EVER CHANGED!!!
+ */
+ (void) fscanf(fp, "%4096s", temp);
+
+ if (strcmp(temp, "include") == 0)
+ doinclude();
+ else if (strcmp(temp, "rsearch") == 0)
+ dorsearch();
+ else if (strcmp(temp, "search") == 0)
+ dosearch();
+ else if (strcmp(temp, "default") == 0) {
+ if (doattrib())
+ break;
+ } else if (strchr(temp, '=')) {
+ translate(temp, mybuff);
+ /* put this into the local environment */
+ lputenv(mybuff);
+ (void) fscanf(fp, "%*[^\n]"); /* rest of line */
+ (void) fscanf(fp, "\n"); /* rest of line */
+ } else {
+ error(1);
+ logerr(gettext(MSG_BANG), temp);
+ (void) fscanf(fp, "%*[^\n]"); /* read of line */
+ (void) fscanf(fp, "\n"); /* read of line */
+ }
+ continue;
+ }
+ (void) ungetc(c, fp);
+
+ if ((n = gpkgmap(&entry, fp)) < 0) {
+ char *errstr;
+
+ error(1);
+ errstr = getErrstr();
+ logerr(gettext("garbled entry"));
+ logerr(gettext("- pathname: %s"),
+ (entry.path && *entry.path) ? entry.path :
+ "Unknown");
+ logerr(gettext("- problem: %s"),
+ (errstr && *errstr) ? errstr : "Unknown");
+ break;
+ }
+ if (n == 0)
+ break; /* done with file */
+
+ /* don't allow classname to be parametric */
+ if (entry.ftype != 'i') {
+ if (entry.pkg_class[0] == '$') {
+ error(1);
+ logerr(gettext(MSG_BPARAMC), entry.path);
+ }
+ }
+
+ if (strchr("dxlscbp", entry.ftype)) {
+ /*
+ * We don't need to search for things without any
+ * contents in them.
+ */
+ if (strchr("cb", entry.ftype)) {
+ if (entry.ainfo.major == BADMAJOR ||
+ entry.ainfo.minor == BADMINOR) {
+ error(1);
+ logerr(gettext(MSG_NODEVICE),
+ entry.path);
+ }
+ }
+ path = NULL;
+ } else {
+ path = findfile(entry.path, entry.ainfo.local);
+ if (!path)
+ continue;
+
+ entry.ainfo.local = path;
+ if (strchr("fevin?", entry.ftype)) {
+ if (cverify(0, &entry.ftype, path,
+ &entry.cinfo, 1)) {
+ error(1);
+ logerr(gettext(MSG_CONTENTS), path);
+ }
+ }
+ }
+
+ /* Warn if attributes are not set correctly. */
+ if (!strchr("isl", entry.ftype)) {
+ int dowarning = 0;
+ int hasbadmode = 0;
+
+ if (entry.ainfo.mode == NOMODE) {
+ entry.ainfo.mode = CURMODE;
+ dowarning = 1;
+ hasbadmode = 1;
+ }
+
+ if (strcmp(entry.ainfo.owner, NOOWNER) == 0) {
+ (void) strlcpy(entry.ainfo.owner, CUROWNER,
+ sizeof (entry.ainfo.owner));
+ dowarning = 1;
+ }
+
+ if (strcmp(entry.ainfo.group, NOGROUP) == 0) {
+ (void) strlcpy(entry.ainfo.group, CURGROUP,
+ sizeof (entry.ainfo.group));
+ dowarning = 1;
+ }
+
+
+ if (dowarning) {
+ if (hasbadmode)
+ logerr(gettext(WRN_BADATTRM),
+ "?",
+ entry.ainfo.owner,
+ entry.ainfo.group,
+ entry.path);
+ else
+ logerr(gettext(WRN_BADATTR),
+ (int)entry.ainfo.mode,
+ entry.ainfo.owner,
+ entry.ainfo.group,
+ entry.path);
+ }
+ }
+
+ /*
+ * Resolve build parameters (initial lower case) in
+ * the link and target paths.
+ */
+ if (strchr("ls", entry.ftype)) {
+ if (!RELATIVE(entry.ainfo.local) ||
+ PARAMETRIC(entry.ainfo.local)) {
+ if (mappath(1, entry.ainfo.local)) {
+ error(1);
+ logerr(gettext(ERR_PATHVAR),
+ entry.ainfo.local);
+ break;
+ }
+
+ canonize(entry.ainfo.local);
+ }
+ }
+
+ /*
+ * Warn if top level file or directory is an install
+ * parameter
+ */
+ if (entry.ftype != 'i') {
+ if (entry.path[0] == '$' && isupper(entry.path[1]))
+ fakebasedir = 1;
+ }
+
+ if (mappath(1, entry.path)) {
+ error(1);
+ logerr(gettext(ERR_PATHVAR), entry.path);
+ break;
+ }
+
+ canonize(entry.path);
+ if (ppkgmap(&entry, tmpfp)) {
+ error(1);
+ logerr(gettext(MSG_WRITE), errno);
+ break;
+ }
+ }
+
+ if (fakebasedir) {
+ logerr(gettext(WRN_FAKEBD));
+ fakebasedir = 0;
+ }
+
+ if (popenv())
+ goto again;
+
+ (void) fclose(tmpfp);
+ environ = envsave; /* restore environment */
+
+ return (errflg ? 1 : 0);
+}
+
+static char *
+findfile(char *path, char *local)
+{
+ struct stat statbuf;
+ static char host[PATH_MAX];
+ register char *pt;
+ char temp[PATH_MAX], *basename;
+ int i;
+
+ /*
+ * map any parameters specified in path to their corresponding values
+ * and make sure the path is in its canonical form; any parmeters for
+ * which a value is not defined will be left unexpanded. Since this
+ * is an actual search for a real file (which will not end up in the
+ * package) - we map ALL variables (both build and Install).
+ */
+ (void) strlcpy(temp, (local && local[0] ? local : path), sizeof (temp));
+ mappath(0, temp);
+ canonize(temp);
+
+ *host = '\0';
+ if (rootlist[0] || (basedir && (*temp != '/'))) {
+ /*
+ * search for path in the pseudo-root/basedir directory; note
+ * that package information files should NOT be included in
+ * this list
+ */
+ if (entry.ftype != 'i')
+ return (srchroot(temp, host));
+ }
+
+ /* looking for local object file */
+ if (local && *local) {
+ basepath(temp, dname[nfp], NULL);
+ /*
+ * If it equals "/dev/null", that just means it's an empty
+ * file. Otherwise, we'll really be writing stuff, so we need
+ * to verify the source.
+ */
+ if (strcmp(temp, "/dev/null") != 0) {
+ if (stat(temp, &statbuf) ||
+ !(statbuf.st_mode & S_IFREG)) {
+ error(1);
+ logerr(gettext(MSG_SRCHLOC), path);
+ return (NULL);
+ }
+ }
+ (void) strlcpy(host, temp, sizeof (host));
+ return (host);
+ }
+
+ for (i = 0; rootp[nfp][i]; i++) {
+ (void) snprintf(host, sizeof (host), "%s/%s", rootp[nfp][i],
+ temp + (*temp == '/' ? 1 : 0));
+ if ((stat(host, &statbuf) == 0) &&
+ (statbuf.st_mode & S_IFREG)) {
+ return (host);
+ }
+ }
+
+ pt = strrchr(temp, '/');
+ if (!pt++)
+ pt = temp;
+
+ basename = pt;
+
+ for (i = 0; srchp[nfp][i]; i++) {
+ (void) snprintf(host, sizeof (host), "%s/%s",
+ srchp[nfp][i], basename);
+ if ((stat(host, &statbuf) == 0) &&
+ (statbuf.st_mode & S_IFREG)) {
+ return (host);
+ }
+ }
+
+ /* check current directory as a last resort */
+ (void) snprintf(host, sizeof (host), "%s/%s", dname[nfp], basename);
+ if ((stat(host, &statbuf) == 0) && (statbuf.st_mode & S_IFREG))
+ return (host);
+
+ error(1);
+ logerr(gettext(MSG_SRCHSRCH), path);
+ return (NULL);
+}
+
+static void
+dosearch(void)
+{
+ char temp[PATH_MAX], lookpath[PATH_MAX], *pt;
+ int n;
+
+ (void) fgets(temp, PATH_MAX, fp);
+ translate(temp, lookpath);
+
+ for (n = 0; srchp[nfp][n]; n++)
+ free(srchp[nfp][n]);
+
+ n = 0;
+ pt = strtok(lookpath, separ);
+ if (pt && *pt) {
+ do {
+ if (*pt != '/') {
+ /* make relative path an absolute directory */
+ (void) snprintf(temp, sizeof (temp),
+ "%s/%s", dname[nfp], pt);
+ pt = temp;
+ }
+ canonize(pt);
+ srchp[nfp][n++] = qstrdup(pt);
+ } while (pt = strtok(NULL, separ));
+ srchp[nfp][n] = NULL;
+ }
+}
+
+static void
+dorsearch(void)
+{
+ char temp[PATH_MAX], lookpath[PATH_MAX], *pt;
+ int n;
+
+ (void) fgets(temp, PATH_MAX, fp);
+ translate(temp, lookpath);
+
+ for (n = 0; rootp[nfp][n]; n++)
+ free(rootp[nfp][n]);
+
+ n = 0;
+ pt = strtok(lookpath, separ);
+ do {
+ if (*pt != '/') {
+ /* make relative path an absolute directory */
+ (void) snprintf(temp, sizeof (temp),
+ "%s/%s", dname[nfp], pt);
+ pt = temp;
+ }
+ canonize(pt);
+ rootp[nfp][n++] = qstrdup(pt);
+ } while (pt = strtok(NULL, separ));
+ rootp[nfp][n] = NULL;
+}
+
+/*
+ * This function reads the default mode, owner and group from the prototype
+ * file and makes that available.
+ */
+static int
+doattrib(void)
+{
+ char *pt, attrib[PATH_MAX], *mode_ptr, *owner_ptr, *group_ptr, *eol;
+ int mode;
+ char owner[ATRSIZ+1], group[ATRSIZ+1], attrib_save[(4*ATRSIZ)];
+
+ (void) fgets(attrib, PATH_MAX, fp);
+
+ (void) strlcpy(attrib_save, attrib, sizeof (attrib_save));
+
+ /*
+ * Now resolve any variables that may be present. Start on group and
+ * move backward since that keeps the resolved string from
+ * overwriting any of the other entries. This is required since
+ * mapvar() writes the resolved string over the string provided.
+ */
+ mode_ptr = strtok(attrib, " \t");
+ owner_ptr = strtok(NULL, " \t");
+ group_ptr = strtok(NULL, " \t\n");
+ eol = strtok(NULL, " \t\n");
+ if (strtok(NULL, " \t\n")) {
+ /* extra tokens on the line */
+ error(1);
+ logerr(gettext(MSG_GARBDEFLT), (eol) ? eol :
+ gettext("unreadable at end of line"));
+ return (1);
+ }
+
+ if (group_ptr && mapvar(1, group_ptr) == 0)
+ (void) strncpy(group, group_ptr, ATRSIZ);
+ else {
+ error(1);
+ logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+ ((attrib_save[0]) ? attrib_save : gettext("none")) :
+ gettext("unreadable at group"));
+ return (1);
+ }
+
+ if (owner_ptr && mapvar(1, owner_ptr) == 0)
+ (void) strncpy(owner, owner_ptr, ATRSIZ);
+ else {
+ error(1);
+ logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+ ((attrib_save[0]) ? attrib_save : gettext("none")) :
+ gettext("unreadable at owner"));
+ return (1);
+ }
+
+ /*
+ * For mode, don't use scanf, since we want to force an octal
+ * interpretation and need to limit the length of the owner and group
+ * specifications.
+ */
+ if (mode_ptr && mapvar(1, mode_ptr) == 0)
+ mode = strtol(mode_ptr, &pt, 8);
+ else {
+ error(1);
+ logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+ ((attrib_save[0]) ? attrib_save : gettext("none")) :
+ gettext("unreadable at mode"));
+ return (1);
+ }
+
+ /* free any previous memory from qstrdup */
+ if (d_own[nfp])
+ free(d_own[nfp]);
+ if (d_grp[nfp])
+ free(d_grp[nfp]);
+
+ d_mod[nfp] = mode;
+ d_own[nfp] = qstrdup(owner);
+ d_grp[nfp] = qstrdup(group);
+
+ attrpreset(d_mod[nfp], d_own[nfp], d_grp[nfp]);
+
+ return (0);
+}
+
+static void
+doinclude(void)
+{
+ char file[PATH_MAX];
+ char temp[PATH_MAX];
+
+ (void) fgets(temp, PATH_MAX, fp);
+
+ /*
+ * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO THE
+ * FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS LINE IF
+ * THE SIZE OF fscanf IS EVER CHANGED!!!
+ */
+ (void) sscanf(temp, "%1024s", file);
+
+ translate(file, temp);
+ canonize(temp);
+
+ if (*temp == NULL)
+ return;
+ else if (*temp != '/')
+ (void) snprintf(file, sizeof (file), "%s/%s", dname[nfp], temp);
+ else
+ (void) strlcpy(file, temp, sizeof (file));
+
+ canonize(file);
+ pushenv(file);
+}
+
+/*
+ * This does what mappath() does except that it does it for ALL variables
+ * using whitespace as a token separator. This is used to resolve search
+ * paths and assignment statements. It doesn't effect the build versus
+ * install decision made for pkgmap variables.
+ */
+static void
+translate(register char *pt, register char *copy)
+{
+ char *pt2, varname[MAX_PKG_PARAM_LENGTH];
+
+token:
+ /* eat white space */
+ while (isspace(*pt))
+ pt++;
+ while (*pt && !isspace(*pt)) {
+ if (*pt == '$') {
+ pt2 = varname;
+ while (*++pt && !strchr("/= \t\n\r", *pt))
+ *pt2++ = *pt;
+ *pt2 = '\0';
+ if (pt2 = getenv(varname)) {
+ while (*pt2)
+ *copy++ = *pt2++;
+ }
+ } else
+ *copy++ = *pt++;
+ }
+ if (*pt) {
+ *copy++ = ' ';
+ goto token;
+ }
+ *copy = '\0';
+}
+
+static void
+error(int flag)
+{
+ static char *lasterr = NULL;
+
+ if (lasterr != proto[nfp]) {
+ lasterr = proto[nfp];
+ (void) fprintf(stderr, gettext("ERROR in %s:\n"), lasterr);
+ }
+ if (flag)
+ errflg++;
+}
+
+/* Set up defaults and change to the build directory. */
+static void
+pushenv(char *file)
+{
+ register char *pt;
+ static char topdir[PATH_MAX];
+
+ if ((nfp+1) >= NRECURS) {
+ error(1);
+ logerr(gettext(MSG_NRECURS), NRECURS);
+ logerr(gettext(MSG_IGNINCLUDE), file);
+ return;
+ }
+
+ if (strcmp(file, "-") == 0) {
+ fp = stdin;
+ } else if ((fp = fopen(file, "r")) == NULL) {
+ error(1);
+ logerr(gettext(MSG_RDINCLUDE), file, errno);
+ if (nfp >= 0) {
+ logerr(gettext(MSG_IGNINCLUDE), file);
+ fp = sfp[nfp];
+ return;
+ } else
+ quit(1);
+ }
+ sfp[++nfp] = fp;
+ srchp[nfp][0] = NULL;
+ rootp[nfp][0] = NULL;
+ d_mod[nfp] = (mode_t)(-1);
+ d_own[nfp] = NULL;
+ d_grp[nfp] = NULL;
+
+ if (!nfp) {
+ /* upper level proto file */
+ proto[nfp] = file;
+ if (file[0] == '/')
+ pt = strcpy(topdir, file);
+ else {
+ /* path is relative to the prototype file specified */
+ pt = getcwd(NULL, PATH_MAX);
+ if (pt == NULL) {
+ progerr(gettext(ERR_GETCWD), errno);
+ quit(99);
+ }
+ (void) snprintf(topdir, sizeof (topdir),
+ "%s/%s", pt, file);
+ }
+ if (pt = strrchr(topdir, '/'))
+ *pt = '\0'; /* should always happen */
+ if (topdir[0] == '\0')
+ (void) strlcpy(topdir, "/", sizeof (topdir));
+ dname[nfp] = topdir;
+ } else {
+ proto[nfp] = qstrdup(file);
+ dname[nfp] = qstrdup(file);
+ if (pt = strrchr(dname[nfp], '/'))
+ *pt = '\0';
+ else {
+ /* same directory as the last prototype */
+ free(dname[nfp]);
+ dname[nfp] = qstrdup(dname[nfp-1]);
+ return; /* no need to canonize() or chdir() */
+ }
+ }
+
+ canonize(dname[nfp]);
+
+ if (chdir(dname[nfp])) {
+ error(1);
+ logerr(gettext(MSG_CHDIR), dname[nfp]);
+ if (!nfp)
+ quit(1); /* must be able to cd to upper level */
+ logerr(gettext(MSG_IGNINCLUDE), proto[nfp]);
+ (void) popenv();
+ }
+}
+
+/* Restore defaults and return to the prior directory. */
+static int
+popenv(void)
+{
+ int i;
+
+ (void) fclose(fp);
+ if (nfp) {
+ if (proto[nfp])
+ free(proto[nfp]);
+ if (dname[nfp])
+ free(dname[nfp]);
+ for (i = 0; srchp[nfp][i]; i++)
+ free(srchp[nfp][i]);
+ for (i = 0; rootp[nfp][i]; i++)
+ free(rootp[nfp][i]);
+ if (d_own[nfp])
+ free(d_own[nfp]);
+ if (d_grp[nfp])
+ free(d_grp[nfp]);
+
+ fp = sfp[--nfp];
+
+ if (chdir(dname[nfp])) {
+ error(1);
+ logerr(gettext(MSG_CHDIR), dname[nfp]);
+ logerr(gettext(MSG_INCOMPLETE), proto[nfp]);
+ return (popenv());
+ }
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * If this parameter isn't already in place, put it into the local
+ * environment. This means that command line directives override prototype
+ * file directives.
+ */
+static void
+lputenv(char *s)
+{
+ char *pt;
+ int i;
+
+ pt = strchr(s, '=');
+ if (!pt)
+ return;
+
+ *pt = '\0';
+ for (i = 0; i < nrdonly; i++) {
+ if (strcmp(rdonly[i], s) == 0) {
+ *pt = '=';
+ return;
+ }
+ }
+ *pt = '=';
+
+ if (putenv(qstrdup(s))) {
+ progerr(gettext(ERR_ENVBUILD), errno);
+ quit(99);
+ }
+}
+
+static char *
+srchroot(char *path, char *copy)
+{
+ struct stat statbuf;
+ int i;
+
+ i = 0;
+ root = rootlist[i++];
+ do {
+ /* convert with root & basedir info */
+ cvtpath(path, copy);
+ /* make it pretty again */
+ canonize(copy);
+
+ if (stat(copy, &statbuf) || !(statbuf.st_mode & S_IFREG)) {
+ root = rootlist[i++];
+ continue; /* host source must be a regular file */
+ }
+ return (copy);
+ } while (root != NULL);
+ error(1);
+ logerr(gettext(MSG_SRCHROOT), path);
+ return (NULL);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgmk/quit.c b/usr/src/cmd/svr4pkg/pkgmk/quit.c
new file mode 100644
index 0000000000..64930060db
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgmk/quit.c
@@ -0,0 +1,74 @@
+/*
+ * 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 1993 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 "libadm.h"
+
+extern struct pkgdev pkgdev;
+extern char pkgloc[], *t_pkgmap, *t_pkginfo;
+
+extern int started;
+
+#define MSG_COMPLETE "## Packaging complete.\n"
+#define MSG_TERM "## Packaging terminated at user request.\n"
+#define MSG_ERROR "## Packaging was not successful.\n"
+
+void
+quit(int retcode)
+{
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (retcode == 3)
+ (void) fprintf(stderr, gettext(MSG_TERM));
+ else if (retcode)
+ (void) fprintf(stderr, gettext(MSG_ERROR));
+ else
+ (void) fprintf(stderr, gettext(MSG_COMPLETE));
+
+ if (retcode && started)
+ (void) rrmdir(pkgloc); /* clean up output directory */
+
+ if (pkgdev.mount)
+ (void) pkgumount(&pkgdev);
+
+ if (t_pkgmap)
+ (void) unlink(t_pkgmap);
+ if (t_pkginfo)
+ (void) unlink(t_pkginfo);
+ exit(retcode);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgmk/splpkgmap.c b/usr/src/cmd/svr4pkg/pkgmk/splpkgmap.c
new file mode 100644
index 0000000000..4781dcc868
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgmk/splpkgmap.c
@@ -0,0 +1,633 @@
+/*
+ * 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 2008 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 <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pkgdev.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern struct pkgdev pkgdev;
+
+#define MALSIZ 500
+#define EFACTOR 128ULL /* typical size of a single entry in a pkgmap file */
+
+#define WRN_LIMIT "WARNING: -l limit (%llu blocks) exceeds device " \
+ "capacity (%llu blocks)"
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+#define ERR_TOOBIG "%s (%llu blocks) does not fit on a volume"
+#define ERR_INFOFIRST "information file <%s> must appear on first part"
+#define ERR_INFOSPACE "all install files must appear on first part"
+#define ERR_VOLBLKS "Objects selected for part %d require %llu blocks, " \
+ "limit=%llu."
+#define ERR_VOLFILES "Objects selected for part %d require %llu files, " \
+ "limit=%llu."
+#define ERR_FREE "package does not fit space currently available in <%s>"
+
+struct data {
+ fsblkcnt_t blks;
+ struct cfent *ept;
+};
+
+struct class_type {
+ char *name;
+ int first;
+ int last;
+};
+
+static fsblkcnt_t btotal; /* blocks stored on current part */
+static fsblkcnt_t bmax; /* maximum number of blocks on any part */
+
+static fsfilcnt_t ftotal; /* files stored on current part */
+static fsfilcnt_t fmax; /* maximum number of files on any part */
+static fsblkcnt_t bpkginfo; /* blocks used by pkginfo file */
+static char **dirlist;
+static short volno; /* current part */
+static int nparts = -1; /* total number of parts */
+static int nclass;
+static fsblkcnt_t DIRSIZE;
+static struct class_type *cl;
+
+static int nodecount(char *path);
+static int store(struct data **, unsigned int, char *, fsblkcnt_t,
+ fsblkcnt_t);
+static void addclass(char *aclass, int vol);
+static void allocnode(char *path);
+static void newvolume(struct data **, unsigned int, fsblkcnt_t limit,
+ fsblkcnt_t);
+static void sortsize(struct data *f, struct data **sf, unsigned int eptnum);
+
+int
+splpkgmap(struct cfent **eptlist, unsigned int eptnum, char *order[],
+ ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit, fsfilcnt_t *pilimit,
+ fsblkcnt_t *pllimit)
+{
+ struct data *f, **sf;
+ struct cfent *ept;
+ register int i, j;
+ int new_vol_set;
+ short new_vol;
+ int flag, errflg;
+ fsblkcnt_t total;
+ fsblkcnt_t btemp;
+ fsfilcnt_t ftemp;
+
+ f = (struct data *)calloc(eptnum, sizeof (struct data));
+ if (f == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ sf = (struct data **)calloc(eptnum, sizeof (struct data *));
+ if (sf == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ nclass = 0;
+ cl = (struct class_type *)calloc(MALSIZ, sizeof (struct class_type));
+ if (cl == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ errflg = 0;
+
+ /*
+ * The next bit of code checks to see if, when creating a package
+ * on a directory, there are enough free blocks and inodes before
+ * continuing.
+ */
+ total = 0;
+ /*
+ * DIRSIZE takes up 1 logical block, iff we have no frags, else
+ * it just takes a frag
+ */
+ DIRSIZE = ((fsblkcnt_t)frsize > 0) ?
+ howmany(frsize, DEV_BSIZE) :
+ howmany(bsize, DEV_BSIZE);
+
+ if (!pkgdev.mount) {
+ allocnode(NULL);
+ /*
+ * If we appear to have a valid value for free inodes
+ * and there's not enough for the package contents,
+ * then exit
+ */
+ if ((*pilimit > 0) && (eptnum+1 > *pilimit)) {
+ progerr(gettext(ERR_FREE), pkgdev.dirname);
+ quit(1);
+ }
+ for (i = 0; i < eptnum; i++) {
+ if (strchr("dxslcbp", eptlist[i]->ftype))
+ continue;
+ else {
+ total +=
+ (nodecount(eptlist[i]->path) * DIRSIZE);
+ total +=
+ nblk(eptlist[i]->cinfo.size, bsize, frsize);
+ if (total > *plimit) {
+ progerr(gettext(ERR_FREE),
+ pkgdev.dirname);
+ quit(1);
+ }
+ allocnode(eptlist[i]->path);
+ }
+ }
+ }
+ /*
+ * if there is a value in pllimit (-l specified limit), use that for
+ * the limit from now on.
+ */
+
+ if (*pllimit != 0) {
+ if (pkgdev.mount && *pllimit > *plimit)
+ logerr(gettext(WRN_LIMIT), *pllimit, *plimit);
+ *plimit = *pllimit;
+ }
+ /*
+ * calculate number of physical blocks used by each object
+ */
+ for (i = 0; i < eptnum; i++) {
+ f[i].ept = ept = eptlist[i];
+ if (ept->volno > nparts)
+ nparts = ept->volno;
+ addclass(ept->pkg_class, 0);
+ if (strchr("dxslcbp", ept->ftype))
+ /*
+ * virtual object (no contents)
+ */
+ f[i].blks = 0;
+ else
+ /*
+ * space consumers
+ *
+ * (directories are space consumers as well, but they
+ * get accounted for later).
+ *
+ */
+
+ f[i].blks = nblk(ept->cinfo.size, bsize, frsize);
+
+ if (!bpkginfo && (strcmp(f[i].ept->path, "pkginfo") == 0))
+ bpkginfo = f[i].blks;
+ }
+
+ /*
+ * Make sure that items slated for a given 'part' do not exceed a single
+ * volume.
+ */
+ for (i = 1; i <= nparts; i++) {
+ btemp = (bpkginfo + 2LL);
+ ftemp = 2LL;
+ if (i == 1) {
+ /*
+ * save room for install directory
+ */
+ ftemp += 2;
+ btemp += nblk(eptnum * EFACTOR, bsize, frsize);
+ btemp += 2;
+ }
+ allocnode(NULL);
+ for (j = 0; j < eptnum; j++) {
+ if (i == 1 && f[j].ept->ftype == 'i' &&
+ (strcmp(f[j].ept->path, "pkginfo") == 0 ||
+ strcmp(f[j].ept->path, "pkgmap") == 0))
+ continue;
+ if (f[j].ept->volno == i ||
+ (f[j].ept->ftype == 'i' && i == 1)) {
+ ftemp += nodecount(f[j].ept->path);
+ btemp += f[j].blks;
+ allocnode(f[j].ept->path);
+ }
+ }
+ btemp += (ftemp * DIRSIZE);
+ if (btemp > *plimit) {
+ progerr(gettext(ERR_VOLBLKS), i, btemp, *plimit);
+ errflg++;
+ /* If we have a valid inode limit, ensure this part will fit */
+ } else if ((*pilimit > 0) && (ftemp+1 > *pilimit)) {
+ progerr(gettext(ERR_VOLFILES), i, ftemp + 1, *pilimit);
+ errflg++;
+ }
+ }
+ if (errflg)
+ quit(1);
+
+ /*
+ * "sf" - array sorted in decreasing file size order, based on "f".
+ */
+ sortsize(f, sf, eptnum);
+
+ /*
+ * initialize first volume
+ */
+ newvolume(sf, eptnum, *plimit, *pilimit);
+
+ /*
+ * reserve room on first volume for pkgmap
+ */
+ btotal += nblk((fsblkcnt_t)(eptnum * EFACTOR), bsize, frsize);
+ ftotal++;
+
+
+ /*
+ * initialize directory info
+ */
+ allocnode(NULL);
+
+ /*
+ * place installation files on first volume!
+ */
+ flag = 0;
+ for (j = 0; j < eptnum; ++j) {
+ if (f[j].ept->ftype != 'i')
+ continue;
+ else if (!flag++) {
+ /*
+ * save room for install directory
+ */
+ ftotal++;
+ btotal += 2ULL;
+ }
+ if (!f[j].ept->volno) {
+ f[j].ept->volno = 1;
+ ftotal++;
+ btotal += f[j].blks;
+ } else if (f[j].ept->volno != 1) {
+ progerr(gettext(ERR_INFOFIRST), f[j].ept->path);
+ errflg++;
+ }
+ }
+
+ if (errflg)
+ quit(1);
+ if (btotal > *plimit) {
+ progerr(gettext(ERR_INFOSPACE));
+ quit(1);
+ }
+
+ /*
+ * Make sure that any given file will fit on a single volume, this
+ * calculation has to take into account packaging overhead, otherwise
+ * the function store() will go into a severe recursive plunge.
+ */
+ for (j = 0; j < eptnum; ++j) {
+ /*
+ * directory overhead.
+ */
+ btemp = nodecount(f[j].ept->path) * DIRSIZE;
+ /*
+ * packaging overhead.
+ */
+ btemp += (bpkginfo + 2L); /* from newvolume() */
+ if ((f[j].blks + btemp) > *plimit) {
+ errflg++;
+ progerr(gettext(ERR_TOOBIG), f[j].ept->path, f[j].blks);
+ }
+ }
+ if (errflg)
+ quit(1);
+
+ /*
+ * place classes listed on command line
+ */
+ if (order) {
+ for (i = 0; order[i]; ++i) {
+ while (store(sf, eptnum, order[i], *plimit, *pilimit))
+ /* stay in loop until store is complete */
+ /* void */;
+ }
+ }
+
+ while (store(sf, eptnum, (char *)0, *plimit, *pilimit))
+ /* stay in loop until store is complete */
+ /* void */;
+
+ /*
+ * place all virtual objects, e.g. links and spec devices
+ */
+ for (i = 0; i < nclass; ++i) {
+ /*
+ * if no objects were associated, attempt to
+ * distribute in order of class list
+ */
+ if (cl[i].first == 0)
+ cl[i].last = cl[i].first = (i ? cl[i-1].last : 1);
+ for (j = 0; j < eptnum; j++) {
+ if ((f[j].ept->volno == 0) &&
+ strcmp(f[j].ept->pkg_class, cl[i].name) == 0) {
+ if (strchr("sl", f[j].ept->ftype))
+ f[j].ept->volno = cl[i].last;
+ else
+ f[j].ept->volno = cl[i].first;
+ }
+ }
+ }
+
+ if (btotal)
+ newvolume(sf, eptnum, *plimit, *pilimit);
+
+ if (nparts > (volno - 1)) {
+ new_vol = volno;
+ for (i = volno; i <= nparts; i++) {
+ new_vol_set = 0;
+ for (j = 0; j < eptnum; j++) {
+ if (f[j].ept->volno == i) {
+ f[j].ept->volno = new_vol;
+ new_vol_set = 1;
+ }
+ }
+ new_vol += new_vol_set;
+ }
+ nparts = new_vol - 1;
+ } else
+ nparts = volno - 1;
+
+ *plimit = bmax;
+ *pilimit = fmax;
+
+ /*
+ * free up dynamic space used by this module
+ */
+ free(f);
+ free(sf);
+ for (i = 0; i < nclass; ++i)
+ free(cl[i].name);
+ free(cl);
+ for (i = 0; dirlist[i]; i++)
+ free(dirlist[i]);
+ free(dirlist);
+
+ return (errflg ? -1 : nparts);
+}
+
+static int
+store(struct data **sf, unsigned int eptnum, char *aclass, fsblkcnt_t limit,
+ fsfilcnt_t ilimit)
+{
+ int i, svnodes, choice, select;
+ long ftemp;
+ fsblkcnt_t btemp;
+
+ select = 0;
+ choice = (-1);
+ for (i = 0; i < eptnum; ++i) {
+ if (sf[i]->ept->volno || strchr("sldxcbp", sf[i]->ept->ftype))
+ continue; /* defer storage until class is selected */
+ if (aclass && strcmp(aclass, sf[i]->ept->pkg_class))
+ continue;
+ select++; /* we need to place at least one object */
+ ftemp = nodecount(sf[i]->ept->path);
+ btemp = sf[i]->blks + (ftemp * DIRSIZE);
+ if (((limit == 0) || ((btotal + btemp) <= limit)) &&
+ ((ilimit == 0) || ((ftotal + ftemp) < ilimit))) {
+ /* largest object which fits on this volume */
+ choice = i;
+ svnodes = ftemp;
+ break;
+ }
+ }
+ if (!select)
+ return (0); /* no more to objects to place */
+
+ if (choice < 0) {
+ newvolume(sf, eptnum, limit, ilimit);
+ return (store(sf, eptnum, aclass, limit, ilimit));
+ }
+ sf[choice]->ept->volno = (char)volno;
+ ftotal += svnodes + 1;
+ btotal += sf[choice]->blks + (svnodes * DIRSIZE);
+ allocnode(sf[i]->ept->path);
+ addclass(sf[choice]->ept->pkg_class, volno);
+ return (++choice); /* return non-zero if more work to do */
+}
+
+static void
+allocnode(char *path)
+{
+ register int i;
+ int found;
+ char *pt;
+
+ if (path == NULL) {
+ if (dirlist) {
+ /*
+ * free everything
+ */
+ for (i = 0; dirlist[i]; i++)
+ free(dirlist[i]);
+ free(dirlist);
+ }
+ dirlist = (char **)calloc(MALSIZ, sizeof (char *));
+ if (dirlist == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ return;
+ }
+
+ pt = path;
+ if (*pt == '/')
+ pt++;
+ /*
+ * since the pathname supplied is never just a directory,
+ * we store only the dirname of of the path.
+ */
+ while (pt = strchr(pt, '/')) {
+ *pt = '\0';
+ found = 0;
+ for (i = 0; dirlist[i] != NULL; i++) {
+ if (strcmp(path, dirlist[i]) == 0) {
+ found++;
+ break;
+ }
+ }
+ if (!found) {
+ /* insert this path in node list */
+ dirlist[i] = qstrdup(path);
+ if ((++i % MALSIZ) == 0) {
+ dirlist = (char **)realloc(dirlist,
+ (i+MALSIZ) * sizeof (char *));
+ if (dirlist == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+ dirlist[i] = (char *)NULL;
+ }
+ *pt++ = '/';
+ }
+}
+
+static int
+nodecount(char *path)
+{
+ char *pt;
+ int i, found, count;
+
+ pt = path;
+ if (*pt == '/')
+ pt++;
+
+ /*
+ * we want to count the number of path
+ * segments that need to be created, not
+ * including the basename of the path;
+ * this works only since we are never
+ * passed a pathname which itself is a
+ * directory
+ */
+ count = 0;
+ while (pt = strchr(pt, '/')) {
+ *pt = '\0';
+ found = 0;
+ for (i = 0; dirlist[i]; i++) {
+ if (strcmp(path, dirlist[i]) != 0) {
+ found++;
+ break;
+ }
+ }
+ if (!found)
+ count++;
+ *pt++ = '/';
+ }
+ return (count);
+}
+
+static void
+newvolume(struct data **sf, unsigned int eptnum, fsblkcnt_t limit,
+ fsblkcnt_t ilimit)
+{
+ register int i;
+ int newnodes;
+
+ if (volno) {
+ (void) fprintf(stderr,
+ gettext("part %2d -- %llu blocks, %llu entries\n"),
+ volno, btotal, ftotal);
+ if (btotal > bmax)
+ bmax = btotal;
+ if (ftotal > fmax)
+ fmax = ftotal;
+ btotal = bpkginfo + 2ULL;
+ ftotal = 2;
+ } else {
+ btotal = 2ULL;
+ ftotal = 1;
+ }
+ volno++;
+
+ /*
+ * zero out directory storage
+ */
+ allocnode((char *)0);
+
+ /*
+ * force storage of files whose volume number has already been assigned
+ */
+ for (i = 0; i < eptnum; i++) {
+ if (sf[i]->ept->volno == volno) {
+ newnodes = nodecount(sf[i]->ept->path);
+ ftotal += newnodes + 1;
+ btotal += sf[i]->blks + (newnodes * DIRSIZE);
+ if (btotal > limit) {
+ progerr(gettext(ERR_VOLBLKS), volno, btotal,
+ limit);
+ quit(1);
+ } else if ((ilimit == 0) && (ftotal+1 > ilimit)) {
+ progerr(gettext(ERR_VOLFILES), volno, ftotal+1,
+ ilimit);
+ quit(1);
+ }
+ }
+ }
+}
+
+static void
+addclass(char *aclass, int vol)
+{
+ int i;
+
+ for (i = 0; i < nclass; ++i) {
+ if (strcmp(cl[i].name, aclass) == 0) {
+ if (vol <= 0)
+ return;
+ if (!cl[i].first || (vol < cl[i].first))
+ cl[i].first = vol;
+ if (vol > cl[i].last)
+ cl[i].last = vol;
+ return;
+ }
+ }
+ cl[nclass].name = qstrdup(aclass);
+ cl[nclass].first = vol;
+ cl[nclass].last = vol;
+ if ((++nclass % MALSIZ) == 0) {
+ cl = (struct class_type *)realloc((char *)cl,
+ sizeof (struct class_type) * (nclass+MALSIZ));
+ if (!cl) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+}
+
+static void
+sortsize(struct data *f, struct data **sf, unsigned int eptnum)
+{
+ int nsf;
+ int j, k;
+ unsigned int i;
+
+ nsf = 0;
+ for (i = 0; i < eptnum; i++) {
+ for (j = 0; j < nsf; ++j) {
+ if (f[i].blks > sf[j]->blks) {
+ for (k = nsf; k > j; k--) {
+ sf[k] = sf[k-1];
+ }
+ break;
+ }
+ }
+ sf[j] = &f[i];
+ nsf++;
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkgname/Makefile b/usr/src/cmd/svr4pkg/pkgname/Makefile
new file mode 100644
index 0000000000..9f3a2eb859
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgname/Makefile
@@ -0,0 +1,40 @@
+#
+# 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= pkgname
+
+OBJS= pkgname.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgname/pkgname.c b/usr/src/cmd/svr4pkg/pkgname/pkgname.c
new file mode 100644
index 0000000000..42d476f31e
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgname/pkgname.c
@@ -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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <libadm.h>
+
+int
+main(int argc, char *argv[])
+{
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while (--argc > 0) {
+ if (pkgnmchk(argv[argc], (char *)0, 1))
+ exit(1);
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgparam/Makefile b/usr/src/cmd/svr4pkg/pkgparam/Makefile
new file mode 100644
index 0000000000..e428d32d21
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgparam/Makefile
@@ -0,0 +1,41 @@
+#
+# 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= pkgparam
+
+OBJS= pkgparam.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgparam/pkgparam.c b/usr/src/cmd/svr4pkg/pkgparam/pkgparam.c
new file mode 100644
index 0000000000..42289f3730
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgparam/pkgparam.c
@@ -0,0 +1,205 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char *pkgfile;
+
+#define ERR_ROOT_SET "Could not set install root from the environment."
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+#define ERR_MESG "unable to locate parameter information for \"%s\""
+#define ERR_FLT "parsing error in parameter file"
+#define ERR_USAGE "usage:\n" \
+ "\t%s [-v] [-d device] pkginst [param [param ...]]\n" \
+ "\t%s [-v] -f file [param [param ...]]\n"
+#define HASHSIZE 151
+#define BSZ 4
+
+
+static char *device = NULL;
+static int errflg = 0;
+static int vflag = 0;
+
+static void print_entry(char *, char *);
+
+static void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ (void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *value, *pkginst;
+ char *param, parambuf[128];
+ int c;
+
+ pkgfile = NULL;
+
+ /* initialize locale mechanism */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* determine program name */
+
+ (void) set_prog_name(argv[0]);
+
+ /* establish installation root directory */
+
+ if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+ progerr(gettext(ERR_ROOT_SET));
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "R:vd:f:?")) != EOF) {
+ switch (c) {
+ case 'v':
+ vflag++;
+ break;
+
+ case 'f':
+ /* -f could specify filename to get parameters from */
+ pkgfile = optarg;
+ break;
+
+ case 'd':
+ /* -d could specify stream or mountable device */
+ device = flex_device(optarg, 1);
+ break;
+
+ case 'R':
+ if (!set_inst_root(optarg)) {
+ progerr(gettext(ERR_ROOT_CMD));
+ exit(1);
+ }
+ break;
+
+ default:
+ case '?':
+ usage();
+ }
+ }
+
+ set_PKGpaths(get_inst_root());
+
+ if (pkgfile) {
+ if (device)
+ usage();
+ pkginst = pkgfile;
+ } else {
+ if ((optind+1) > argc)
+ usage();
+
+ if (pkghead(device))
+ return (1); /* couldn't obtain info about device */
+ pkginst = argv[optind++];
+ }
+
+ /* If a filename was specified or install db does not exist */
+ do {
+ param = argv[optind];
+ if (!param) {
+ param = parambuf;
+ *param = '\0';
+ }
+ value = pkgparam(pkginst, param);
+ if (value == NULL) {
+ if (errno == EFAULT) {
+ progerr(gettext(ERR_FLT));
+ errflg++;
+ break;
+ } else if (errno != EINVAL) {
+ /*
+ * some other error besides no value for this
+ * particular parameter
+ */
+ progerr(gettext(ERR_MESG), pkginst);
+ errflg++;
+ break;
+ }
+ if (!argv[optind])
+ break;
+ continue;
+ }
+
+ print_entry(param, value);
+
+ } while (!argv[optind] || (++optind < argc));
+ (void) pkgparam(NULL, NULL); /* close open FDs so umount won't fail */
+
+ (void) pkghead(NULL);
+ return (errflg ? 1 : 0);
+}
+
+static void
+print_entry(char *param, char *value)
+{
+ if (vflag) {
+ (void) printf("%s='", param);
+ while (*value) {
+ if (*value == '\'') {
+ (void) printf("'\"'\"'");
+ value++;
+ } else
+ (void) putchar(*value++);
+ }
+ (void) printf("'\n");
+ } else
+ (void) printf("%s\n", value);
+}
+
+void
+quit(int retval)
+{
+ exit(retval);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgproto/Makefile b/usr/src/cmd/svr4pkg/pkgproto/Makefile
new file mode 100644
index 0000000000..91cd3ae74e
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgproto/Makefile
@@ -0,0 +1,41 @@
+#
+# 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= pkgproto
+
+OBJS= main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgproto/main.c b/usr/src/cmd/svr4pkg/pkgproto/main.c
new file mode 100644
index 0000000000..e5f1ba0bb2
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgproto/main.c
@@ -0,0 +1,512 @@
+/*
+ * 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 <ctype.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+extern int holdcinfo;
+
+#define WRN_SCARYLINK "WARNING: <%s>, target of symlink <%s>, does not exist."
+
+#define ERR_PATHLONG "path argument too long"
+#define ERR_CLASSLONG "classname argument too long"
+#define ERR_CLASSCHAR "bad character in classname"
+#define ERR_STAT "unable to stat <%s>"
+#define ERR_WRITE "write of entry failed"
+#define ERR_POPEN "unable to create pipe to <%s>"
+#define ERR_PCLOSE "unable to close pipe to <%s>"
+#define ERR_RDLINK "unable to read link for <%s>"
+#define ERR_MEMORY "memory allocation failure, errno=%d"
+
+#define LINK 1
+
+struct link {
+ char *path;
+ ino_t ino;
+ dev_t dev;
+ struct link *next;
+};
+
+static struct link *firstlink = (struct link *)0;
+static struct link *lastlink = (struct link *)0;
+static char *scan_raw_ln(char *targ_name, char *link_name);
+
+static char *def_class = "none";
+
+static int errflg = 0;
+static int iflag = 0; /* follow symlinks */
+static int xflag = 0; /* confirm contents of files */
+static int nflag = 0;
+static char construction[PATH_MAX], mylocal[PATH_MAX];
+
+static void findlink(struct cfent *ept, char *path, char *svpath);
+static void follow(char *path);
+static void output(char *path, int n, char *local);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ char *pt, path[PATH_MAX];
+ char *abi_sym_ptr;
+ extern char *optarg;
+ extern int optind;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ (void) set_prog_name(argv[0]);
+
+ while ((c = getopt(argc, argv, "xnic:?")) != EOF) {
+ switch (c) {
+ case 'x': /* include content info */
+ xflag++;
+ break;
+
+ case 'n':
+ nflag++;
+ break;
+
+ case 'c': /* assign class */
+ def_class = optarg;
+ /* validate that classname is acceptable */
+ if (strlen(def_class) > (size_t)CLSSIZ) {
+ progerr(gettext(ERR_CLASSLONG));
+ exit(1);
+ }
+ for (pt = def_class; *pt; pt++) {
+ if (!isalpha(*pt) && !isdigit(*pt)) {
+ progerr(gettext(ERR_CLASSCHAR));
+ exit(1);
+ }
+ }
+ break;
+
+ case 'i': /* follow symlinks */
+ iflag++;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (iflag) {
+ /* follow symlinks */
+ set_nonABI_symlinks();
+ } else {
+ /* bug id 4244631, not ABI compliant */
+ abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+ if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+ set_nonABI_symlinks();
+ }
+ }
+ holdcinfo = !xflag;
+ if (optind == argc) {
+ /* take path list from stdin */
+ while (fgets(path, sizeof (path), stdin) != (char *)NULL) {
+ output(path, 0, NULL);
+ }
+ } else {
+ while (optind < argc) {
+ follow(argv[optind++]);
+ }
+ }
+
+ return (errflg ? 1 : 0);
+}
+
+static void
+output(char *path, int n, char *local)
+{
+ char mypath[PATH_MAX];
+ int len;
+ int s;
+ struct cfent entry;
+
+ /*
+ * remove any trailing newline characters from the end of path
+ */
+
+ len = strlen(path);
+ while ((len > 0) && (path[len-1] == '\n')) {
+ path[--len] = '\0';
+ }
+
+ entry.volno = 0;
+ entry.ftype = '?';
+ entry.path = mypath;
+ (void) strlcpy(entry.pkg_class, def_class, sizeof (entry.pkg_class));
+ (void) strlcpy(entry.path, path, PATH_MAX);
+ entry.ainfo.local = NULL;
+ entry.ainfo.mode = BADMODE;
+ (void) strlcpy(entry.ainfo.owner, BADOWNER, sizeof (entry.ainfo.owner));
+ (void) strlcpy(entry.ainfo.group, BADGROUP, sizeof (entry.ainfo.group));
+ errflg = 0;
+
+ if (xflag) {
+ entry.ftype = '?';
+ if (cverify(0, &entry.ftype, path, &entry.cinfo, 1)) {
+ errflg++;
+ logerr(gettext("ERROR: %s"), path);
+ logerr(getErrbufAddr());
+ return;
+ }
+ }
+
+ /*
+ * Use averify to figure out the attributes. This has trouble
+ * divining the identity of a symlink which points to a
+ * non-existant target. For that reason, if it comes back as
+ * an existence problem, we fake in a symlink and see if averify
+ * likes that. If it does, all we have is a risky symlink.
+ */
+ if ((s = averify(0, &entry.ftype, path, &entry.ainfo)) == VE_EXIST &&
+ !iflag) {
+ entry.ftype = 's'; /* try again assuming symlink */
+ /* try to read what it points to */
+ if ((s = readlink(path, mylocal, PATH_MAX)) > 0) {
+ mylocal[s] = '\000'; /* terminate it */
+ entry.ainfo.local = mylocal;
+ if (averify(0, &entry.ftype, path, &entry.ainfo)) {
+ errflg++;
+ } else
+ /* It's a link to a file not in this package. */
+ ptext(stderr, gettext(WRN_SCARYLINK),
+ mylocal, path);
+ } else {
+ errflg++;
+ }
+ } else if (s != 0 && s != VE_CONT)
+ errflg++;
+
+ if (errflg) {
+ logerr(gettext("ERROR: %s"), path);
+ logerr(getErrbufAddr());
+ return;
+ }
+
+ if (n) {
+ /* replace first n characters with 'local' */
+ if (strchr("fev", entry.ftype)) {
+ entry.ainfo.local = mylocal;
+ (void) strlcpy(entry.ainfo.local, entry.path,
+ PATH_MAX);
+ canonize(entry.ainfo.local);
+ }
+ if (local[0]) {
+ entry.ainfo.local = mylocal;
+ (void) strlcpy(entry.path, local, PATH_MAX);
+ (void) strcat(entry.path, path+n);
+ } else
+ (void) strlcpy(entry.path,
+ (path[n] == '/') ? path+n+1 : path+n,
+ PATH_MAX);
+ }
+
+ canonize(entry.path);
+ if (entry.path[0]) {
+ findlink(&entry, path, entry.path);
+ if (strchr("dcbp", entry.ftype) ||
+ (nflag && !strchr("sl", entry.ftype)))
+ entry.ainfo.local = NULL;
+ if (ppkgmap(&entry, stdout)) {
+ progerr(gettext(ERR_WRITE));
+ exit(99);
+ }
+ }
+}
+
+static void
+follow(char *path)
+{
+ struct stat stbuf;
+ FILE *pp;
+ char *pt,
+ local[PATH_MAX],
+ newpath[PATH_MAX],
+ cmd[PATH_MAX+32];
+ int n;
+
+ errflg = 0;
+
+ if (pt = strchr(path, '=')) {
+ *pt++ = '\0';
+ n = ((unsigned int)pt - (unsigned int)path - 1);
+ if (n >= PATH_MAX) {
+ progerr(gettext(ERR_PATHLONG));
+ errflg++;
+ return;
+ }
+
+ n = strlen(pt);
+
+ if (n < PATH_MAX) {
+ (void) strlcpy(local, pt, sizeof (local));
+ n = strlen(path);
+ } else {
+ progerr(gettext(ERR_PATHLONG));
+ errflg++;
+ return;
+ }
+ } else {
+ n = 0;
+ local[0] = '\0';
+ }
+
+ if (stat(path, &stbuf)) {
+ progerr(gettext(ERR_STAT), path);
+ errflg++;
+ return;
+ }
+
+ if (stbuf.st_mode & S_IFDIR) {
+ (void) snprintf(cmd, sizeof (cmd), "find %s -print", path);
+ if ((pp = popen(cmd, "r")) == NULL) {
+ progerr(gettext(ERR_POPEN), cmd);
+ exit(1);
+ }
+ while (fscanf(pp, "%[^\n]\n", newpath) == 1)
+ output(newpath, n, local);
+ if (pclose(pp)) {
+ progerr(gettext(ERR_PCLOSE), cmd);
+ errflg++;
+ }
+ } else
+ output(path, n, local);
+}
+
+/*
+ * Scan a raw link for origination errors. Given
+ * targ_name = hlink/path/file1
+ * and
+ * link_name = hlink/path/file2
+ * we don't want the link to be verbatim since link_name must be relative
+ * to it's source. This functions checks for identical directory paths
+ * and if it's clearly a misplaced relative path, the duplicate
+ * directories are stripped. This is necessary because pkgadd is actually
+ * in the source directory (hlink/path) when it creates the link.
+ *
+ * NOTE : The buffer we get with targ_name is going to be used later
+ * and cannot be modified. That's why we have yet another PATH_MAX
+ * size buffer in this function.
+ */
+static char *
+scan_raw_ln(char *targ_name, char *link_name)
+{
+ char *const_ptr; /* what we return */
+ char *file_name; /* name of the file in link_name */
+ char *this_dir; /* current directory in targ_name */
+ char *next_dir; /* next directory in targ_name */
+ char *targ_ptr; /* current character in targ_name */
+
+ const_ptr = targ_name; /* Point to here 'til we know it's different. */
+
+ /*
+ * If the link is absolute or it is in the current directory, no
+ * further testing necessary.
+ */
+ if (RELATIVE(targ_name) &&
+ (file_name = strrchr(link_name, '/')) != NULL) {
+
+ /*
+ * This will be walked down to the highest directory
+ * not common to both the link and the target.
+ */
+ targ_ptr = targ_name;
+
+ /*
+ * At this point targ_name is a relative path through at
+ * least one directory.
+ */
+ this_dir = targ_ptr; /* first directory in targ_name */
+ file_name++; /* point to the name not the '/' */
+
+ /*
+ * Scan across the pathname until we reach a different
+ * directory or the final file name.
+ */
+ do {
+ size_t str_size;
+
+ next_dir = strchr(targ_ptr, '/');
+ if (next_dir)
+ next_dir++; /* point to name not '/' */
+ else /* point to the end of the string */
+ next_dir = targ_ptr+strlen(targ_ptr);
+
+ /* length to compare */
+ str_size = ((ptrdiff_t)next_dir - (ptrdiff_t)this_dir);
+
+ /*
+ * If both paths begin with the same directory, then
+ * skip that common directory in both the link and
+ * the target.
+ */
+ if (strncmp(this_dir, link_name, str_size) == 0) {
+ /* point to the target so far */
+ const_ptr = this_dir = next_dir;
+ /* Skip past it in the target */
+ targ_ptr = (char *)(targ_ptr+str_size);
+ /* Skip past it in the link */
+ link_name = (char *)(link_name+str_size);
+ /*
+ * If these directories don't match then the
+ * directory above is the lowest common directory. We
+ * need to construct a relative path from the lowest
+ * child up to that directory.
+ */
+ } else {
+ int d = 0;
+ char *dptr = link_name;
+
+ /* Count the intermediate directories. */
+ while ((dptr = strchr(dptr, '/')) != NULL) {
+ dptr++;
+ d++;
+ }
+ /*
+ * Now targ_ptr is pointing to the fork in
+ * the path and dptr is pointing to the lowest
+ * child in the link. We now insert the
+ * appropriate number of "../'s" to get to
+ * the first common directory. We'll
+ * construct this in the construction
+ * buffer.
+ */
+ if (d) {
+ char *tptr;
+
+ const_ptr = tptr = construction;
+ while (d--) {
+ (void) strlcpy(tptr,
+ "../", PATH_MAX);
+ tptr += 3;
+ }
+ (void) strlcpy(tptr, targ_ptr,
+ PATH_MAX);
+ }
+ break; /* done */
+ }
+ } while (link_name != file_name); /* at file name */
+ }
+
+ return (const_ptr);
+}
+
+static void
+findlink(struct cfent *ept, char *path, char *svpath)
+{
+ struct stat statbuf;
+ struct link *link, *new;
+ char buf[PATH_MAX];
+ int n;
+
+ if (lstat(path, &statbuf)) {
+ progerr(gettext(ERR_STAT), path);
+ errflg++;
+ }
+ if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
+ if (!iflag) {
+ ept->ainfo.local = mylocal;
+ ept->ftype = 's';
+ n = readlink(path, buf, PATH_MAX);
+ if (n <= 0) {
+ progerr(gettext(ERR_RDLINK), path);
+ errflg++;
+ (void) strlcpy(ept->ainfo.local,
+ "unknown", PATH_MAX);
+ } else {
+ (void) strncpy(ept->ainfo.local, buf, n);
+ ept->ainfo.local[n] = '\0';
+ }
+ }
+ return;
+ }
+
+ if (stat(path, &statbuf))
+ return;
+ if (statbuf.st_nlink <= 1)
+ return;
+
+ for (link = firstlink; link; link = link->next) {
+ if ((statbuf.st_ino == link->ino) &&
+ (statbuf.st_dev == link->dev)) {
+ ept->ftype = 'l';
+ ept->ainfo.local = mylocal;
+ (void) strlcpy(ept->ainfo.local,
+ scan_raw_ln(link->path, ept->path),
+ PATH_MAX);
+ return;
+ }
+ }
+ if ((new = (struct link *)calloc(1, sizeof (struct link))) == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ exit(1);
+ }
+
+ if (firstlink) {
+ lastlink->next = new;
+ lastlink = new;
+ } else
+ firstlink = lastlink = new;
+
+ new->path = strdup(svpath);
+ new->ino = statbuf.st_ino;
+ new->dev = statbuf.st_dev;
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ gettext("usage: %s [-i] [-c class] [path ...]\n"), get_prog_name());
+ exit(1);
+ /*NOTREACHED*/
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/Makefile b/usr/src/cmd/svr4pkg/pkgremove/Makefile
new file mode 100644
index 0000000000..b6ce418e42
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/Makefile
@@ -0,0 +1,48 @@
+#
+# 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= pkgremove
+
+OBJS= check.o \
+ delmap.o \
+ main.o \
+ predepend.o \
+ quit.o \
+ special.o \
+ wsreg_pkgrm.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+LDLIBS += -lnsl -lsocket -lwsreg
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgremove/check.c b/usr/src/cmd/svr4pkg/pkgremove/check.c
new file mode 100644
index 0000000000..b92da7156e
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/check.c
@@ -0,0 +1,328 @@
+/*
+ * 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 2003 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifndef SUNOS41
+#include <utmpx.h>
+#endif
+#include <dirent.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "wsreg_pkgrm.h"
+#include "messages.h"
+
+extern struct admin adm;
+/* extern struct cfent **eptlist; */
+
+extern char pkgloc[], *pkginst, *msgtext;
+
+static boolean_t preremoveCheck = B_FALSE;
+static char *zoneName = (char *)NULL;
+
+
+void
+rcksetPreremoveCheck(boolean_t a_preremoveCheck)
+{
+ preremoveCheck = a_preremoveCheck;
+}
+
+void
+rcksetZoneName(char *a_zoneName)
+{
+ zoneName = a_zoneName;
+}
+
+int
+rckrunlevel(void)
+{
+ struct utmpx utmpx;
+ struct utmpx *putmpx;
+ char ans[MAX_INPUT];
+ char *pt;
+ char *rstates;
+ int n;
+ char *uxstate;
+
+ if (ADM(runlevel, "nocheck")) {
+ return (0);
+ }
+
+ pt = getenv("RSTATES");
+ if (pt == NULL) {
+ return (0);
+ }
+
+ utmpx.ut_type = RUN_LVL;
+ putmpx = getutxid(&utmpx);
+ if (putmpx == NULL) {
+ progerr(ERR_RUNSTATE);
+ return (99);
+ }
+ uxstate = strtok(&putmpx->ut_line[10], " \t\n");
+
+ rstates = qstrdup(pt);
+ if ((pt = strtok(pt, " \t\n, ")) == NULL)
+ return (0); /* no list is no list */
+ do {
+ if (strcmp(pt, uxstate) == NULL) {
+ free(rstates);
+ return (0);
+ }
+ } while (pt = strtok(NULL, " \t\n, "));
+
+ if (preremoveCheck == B_FALSE) {
+ msgtext = MSG_PKGREMOVE_RUNLEVEL;
+ ptext(stderr, msgtext, uxstate);
+ } else {
+ (void) fprintf(stdout, "runlevel=%s", uxstate);
+ }
+
+ pt = strtok(rstates, " \t\n, ");
+ do {
+ if (preremoveCheck == B_FALSE) {
+ ptext(stderr, "\\t%s", pt);
+ } else {
+ (void) fprintf(stdout, ":%s", pt);
+ }
+ } while (pt = strtok(NULL, " \t\n, "));
+
+ if (preremoveCheck == B_TRUE) {
+ (void) fprintf(stdout, "\n");
+ }
+
+ free(rstates);
+
+ if (ADM(runlevel, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_RUNLEVEL,
+ ASK_PKGREMOVE_CONTINUE);
+
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+
+ return (0);
+}
+
+int
+rckpatchpkg(char *p, char *pt)
+{
+ int n;
+ char ans[MAX_INPUT];
+
+ ptext(stderr, WRN_PKGREMOVE_PATCHES, p, p, p, pt);
+
+ n = ckyorn(ans, NULL, NULL, NULL, ASK_PKGREMOVE_CONTINUE);
+
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+
+ return (0);
+}
+
+int
+rckdepend(void)
+{
+ int n;
+ char ans[MAX_INPUT];
+ char **id, **name;
+
+ if (ADM(rdepend, "nocheck")) {
+ return (0);
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(MSG_CHECKREMOVE_PKG_IN_GZ, pkginst);
+ } else {
+ echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+ }
+
+ if (wsreg_pkgrm_check(get_inst_root(), pkginst, &id, &name) > 0) {
+ int i;
+
+ if (ADM(rdepend, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = MSG_PKGREMOVE_WSDEPEND;
+ echo(msgtext);
+
+ (void) printf("%-36s %s", MSG_PKGREMOVE_ID_STR,
+ MSG_PKGREMOVE_NAME_STR);
+ (void) printf("\n------------------------------------ "
+ "--------------------------------------\n");
+
+ for (i = 0; id[i] != NULL; i++) {
+ (void) printf("%-36s %s\n", id[i],
+ (name[i])?(name[i]):"");
+ free(id[i]);
+ if (name[i]) {
+ free(name[i]);
+ }
+ }
+
+ free(id);
+ free(name);
+
+ msgtext = NULL;
+
+ n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_WSDEPEND,
+ ASK_PKGREMOVE_CONTINUE);
+
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ }
+
+ if (dockdeps(pkginst, 1, preremoveCheck)) {
+ msgtext = MSG_PKGREMOVE_DEPEND;
+
+ if (preremoveCheck == B_FALSE) {
+ echo(msgtext);
+ }
+
+ if (ADM(rdepend, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_DEPEND,
+ ASK_PKGREMOVE_CONTINUE);
+
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ }
+
+ return (0);
+}
+
+int
+rckpriv(void)
+{
+ struct dirent *dp;
+ DIR *dirfp;
+ int n;
+ char found;
+ char ans[MAX_INPUT];
+ char path[PATH_MAX];
+
+ if (ADM(action, "nocheck")) {
+ return (0);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/install", pkgloc);
+ if ((dirfp = opendir(path)) == NULL)
+ return (0);
+
+ found = 0;
+ while ((dp = readdir(dirfp)) != NULL) {
+ if ((strcmp(dp->d_name, "preremove") == NULL) ||
+ (strcmp(dp->d_name, "postremove") == NULL) ||
+ (strncmp(dp->d_name, "r.", 2) == NULL)) {
+ found++;
+ break;
+ }
+ }
+ (void) closedir(dirfp);
+
+ if (found) {
+ if (preremoveCheck == B_FALSE) {
+ ptext(stderr, MSG_PKGREMOVE_PRIV);
+ }
+ msgtext = MSG_PKGSCRIPTS_FOUND;
+
+ if (ADM(action, "quit")) {
+ return (4);
+ }
+
+ if (echoGetFlag() == B_FALSE) {
+ return (5);
+ }
+
+ msgtext = NULL;
+
+ n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_PRIV,
+ ASK_PKGREMOVE_CONTINUE);
+
+ if (n != 0) {
+ return (n);
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ return (3);
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/delmap.c b/usr/src/cmd/svr4pkg/pkgremove/delmap.c
new file mode 100644
index 0000000000..807d7bfd72
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/delmap.c
@@ -0,0 +1,159 @@
+/*
+ * 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 2006 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 <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern int dbchg, warnflag, otherstoo;
+extern char *pkginst;
+
+#define EPTMALLOC 128
+
+#define ERR_WRENT "write of entry failed, errno=%d"
+#define ERR_MEMORY "no memory, errno=%d"
+#define ERR_READ_C "bad read of contents file"
+#define ERR_READ_DB "bad read of the database"
+
+extern struct cfent **eptlist;
+extern int eptnum;
+
+int
+delmap(int flag, char *pkginst)
+{
+ struct cfent *ept;
+ struct pinfo *pinfo;
+ VFP_T *vfp;
+ VFP_T *vfpo;
+ int n;
+ char *unknown = "Unknown";
+
+
+ if (!ocfile(&vfp, &vfpo, 0L)) {
+ quit(99);
+ }
+
+ /* re-use any memory used to store pathnames */
+ (void) pathdup(NULL);
+
+ if (eptlist != NULL)
+ free(eptlist);
+ eptlist = (struct cfent **)calloc(EPTMALLOC,
+ sizeof (struct cfent *));
+ if (eptlist == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ ept = (struct cfent *)calloc(1,
+ (unsigned)sizeof (struct cfent));
+ if (!ept) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+
+ eptnum = 0;
+ while (n = srchcfile(ept, "*", vfp, (VFP_T *)NULL)) {
+ if (n < 0) {
+ char *errstr = getErrstr();
+ progerr(gettext("bad read of contents file"));
+ progerr(gettext("pathname=%s"),
+ (ept->path && *ept->path) ? ept->path :
+ unknown);
+ progerr(gettext("problem=%s"),
+ (errstr && *errstr) ? errstr : unknown);
+ exit(99);
+ }
+ pinfo = eptstat(ept, pkginst, (flag ? '@' : '-'));
+ if (ept->npkgs > 0) {
+ if (putcvfpfile(ept, vfpo)) {
+ progerr(gettext(ERR_WRENT), errno);
+ quit(99);
+ }
+ }
+
+ if (flag || (pinfo == NULL))
+ continue;
+
+ dbchg++;
+
+ /*
+ * If (otherstoo > 0), more than one package has an
+ * interest in the ept entry in the database. Setting
+ * ept->ftype = '\0' effectively marks the file as being
+ * "shared", thus ensuring the ept entry will not
+ * subsequently be removed. Shared editable files (ftype
+ * 'e') are a special case: they should be passed to a
+ * class action script if present. Setting ept->ftype =
+ * '^' indicates this special case of shared editable
+ * file, allowing the distinction to be made later.
+ */
+ if (!pinfo->editflag && otherstoo)
+ ept->ftype = (ept->ftype == 'e') ? '^' : '\0';
+ if (*pinfo->aclass)
+ (void) strcpy(ept->pkg_class, pinfo->aclass);
+ eptlist[eptnum] = ept;
+
+ ept->path = pathdup(ept->path);
+ if (ept->ainfo.local != NULL)
+ ept->ainfo.local = pathdup(ept->ainfo.local);
+
+ ept = (struct cfent *)calloc(1, sizeof (struct cfent));
+ if ((++eptnum % EPTMALLOC) == 0) {
+ eptlist = (struct cfent **)realloc(eptlist,
+ (eptnum+EPTMALLOC)*sizeof (struct cfent *));
+ if (eptlist == NULL) {
+ progerr(gettext(ERR_MEMORY), errno);
+ quit(99);
+ }
+ }
+ }
+
+ eptlist[eptnum] = (struct cfent *)NULL;
+
+ n = swapcfile(&vfp, &vfpo, pkginst, dbchg);
+ if (n == RESULT_WRN) {
+ warnflag++;
+ } else if (n == RESULT_ERR) {
+ quit(99);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/main.c b/usr/src/cmd/svr4pkg/pkgremove/main.c
new file mode 100644
index 0000000000..6e484d6676
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/main.c
@@ -0,0 +1,1434 @@
+/*
+ * 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <assert.h>
+#include <cfext.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+struct cfent **eptlist;
+extern int eptnum;
+
+extern char *pkgdir;
+extern char **environ;
+
+/* quit.c */
+extern sighdlrFunc_t *quitGetTrapHandler(void);
+extern void quitSetSilentExit(boolean_t a_silentExit);
+extern void quitSetZoneName(char *a_zoneName);
+
+
+
+/* check.c */
+extern void rcksetPreremoveCheck(boolean_t);
+extern void rcksetZoneName(char *);
+extern int rckpriv(void);
+extern int rckdepend(void);
+extern int rckrunlevel(void);
+
+/* predepend.c */
+extern void predepend(char *oldpkg);
+
+/* delmap.c */
+extern int delmap(int flag, char *pkginst);
+
+#define DEFPATH "/sbin:/usr/sbin:/usr/bin"
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+#define SCRIPT 0 /* Tells exception_pkg() which pkg list to use */
+#define LINK 1
+#endif
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* This is the text for the "-O inherited-filesystem=" option */
+
+#define INHERITFS "inherited-filesystem="
+#define INHERITFS_LEN ((sizeof (INHERITFS))-1)
+
+/* 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)
+
+struct admin adm; /* holds info about installation admin */
+int dreboot; /* non-zero if reboot required after installation */
+int ireboot; /* non-zero if immediate reboot required */
+int failflag; /* non-zero if fatal error has occurred */
+int warnflag; /* non-zero if non-fatal error has occurred */
+int pkgverbose; /* non-zero if verbose mode is selected */
+int started;
+int nocnflct = 0; /* pkgdbmerg needs this defined */
+int nosetuid = 0; /* pkgdbmerg needs this defined */
+
+char *pkginst; /* current package (source) instance to process */
+
+int dbchg;
+char *msgtext;
+char pkgloc[PATH_MAX];
+
+/*
+ * The following variable is the name of the device to which stdin
+ * is connected during execution of a procedure script. /dev/null is
+ * correct for all ABI compliant packages. For non-ABI-compliant
+ * packages, the '-o' command line switch changes this to /dev/tty
+ * to allow user interaction during these scripts. -- JST
+ */
+static char *script_in = PROC_STDIN; /* assume ABI compliance */
+
+static char *client_mntdir; /* mount point for client's basedir */
+static char pkgbin[PATH_MAX],
+ rlockfile[PATH_MAX],
+ *admnfile, /* file to use for installation admin */
+ *tmpdir; /* location to place temporary files */
+
+static boolean_t path_valid(char *path);
+static void ckreturn(int retcode, char *msg);
+static void rmclass(char *aclass, int rm_remote, char *a_zoneName);
+static void usage(void);
+
+/*
+ * Set by -O debug: debug output is enabled?
+ */
+static boolean_t debugFlag = B_FALSE;
+
+/*
+ * Set by -O preremovecheck: do remove dependency checking only
+ */
+static boolean_t preremoveCheck = B_FALSE;
+
+/* Set by -O parent-zone-name= */
+
+static char *parentZoneName = (char *)NULL;
+
+/* Set by -O parent-zone-type= */
+
+static char *parentZoneType = (char *)NULL;
+
+static int nointeract; /* != 0 no interaction with user should occur */
+
+
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ char *abi_comp_ptr;
+ char *abi_sym_ptr;
+ char *p;
+ char *prog_full_name = NULL;
+ char *pt;
+ char *value;
+ char *vfstab_file = NULL;
+ char *zoneName = (char *)NULL;
+ char cmdbin[PATH_MAX];
+ char param[MAX_PKG_PARAM_LENGTH];
+ char path[PATH_MAX];
+ char script[PATH_MAX];
+ int c;
+ int err;
+ int fd;
+ int i;
+ int map_client = 1;
+ int n;
+ int nodelete = 0; /* do not delete file or run scripts */
+ int pkgrmremote = 0; /* dont remove remote objects */
+ struct sigaction nact;
+ struct sigaction oact;
+
+ /* reset contents of all default paths */
+
+ (void) memset(cmdbin, '\0', sizeof (cmdbin));
+
+ /* 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 */
+ }
+
+ /* 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);
+ }
+
+ /* parse command line options */
+
+ while ((c = getopt(argc, argv, "?Aa:b:FMN:nO:oR:V:vy")) != EOF) {
+ switch (c) {
+ /*
+ * Same as pkgrm: Allow admin to remove package objects from
+ * a shared area from a reference client.
+ */
+ case 'A':
+ pkgrmremote++;
+ break;
+
+ /*
+ * Same as pkgrm: Use the installation
+ * administration file, admin, in place of the
+ * default admin file. pkgrm first looks in the
+ * current working directory for the administration
+ * file. If the specified administration file is not
+ * in the current working directory, pkgrm looks in
+ * the /var/sadm/install/admin directory for the
+ * administration file.
+ */
+ case 'a':
+ admnfile = flex_device(optarg, 0);
+ break;
+
+ /*
+ * Same as pkgrm: location where package executables
+ * 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 pkgrm: suppresses the removal of any
+ * files and any class action scripts, and suppresses
+ * the running of any class action scripts. The
+ * package files remain but the package looks like it
+ * is not installed. This is mainly for use by the
+ * upgrade process.
+ */
+ case 'F':
+ nodelete++;
+ break;
+
+ /*
+ * Same as pkgrm: Instruct pkgrm 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 pkgrm: specify program name to use
+ * for messages.
+ */
+ case 'N':
+ (void) set_prog_name(optarg);
+ break;
+
+ /*
+ * Same as pkgrm: package removal occurs in
+ * non-interactive mode. Suppress output of the list of
+ * removed files. The default mode is interactive.
+ */
+ case 'n':
+ nointeract++;
+ (void) echoSetFlag(B_FALSE);
+ break;
+
+ /*
+ * Almost same as pkgrm: the -O option allows the behavior
+ * of the package tools to be modified. Recognized options:
+ * -> debug
+ * ---> enable debugging output
+ * -> preremovecheck
+ * ---> perform a "pre removal" 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 "removability" 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 */
+ 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 inherited-filesystem= option */
+
+ 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;
+ }
+
+ /* process preremovecheck option */
+
+ if (strcmp(p, "preremovecheck") == 0) {
+ preremoveCheck = B_TRUE;
+ nointeract++; /* -n */
+ nodelete++; /* -F */
+ quitSetSilentExit(B_TRUE);
+ continue;
+ }
+
+ /* process addzonename option */
+
+ if (strcmp(p, "addzonename") == 0) {
+ zoneName = z_get_zonename();
+ quitSetZoneName(zoneName);
+ 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;
+ }
+
+ /* option not recognized - issue warning */
+
+ progerr(ERR_INVALID_O_OPTION, p);
+ continue;
+ }
+ break;
+
+ /*
+ * Different from pkgrm: This is an old non-ABI package
+ */
+
+ case 'o':
+ script_in = PROC_XSTDIN;
+ break;
+
+ /*
+ * Same as pkgrm: defines 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.
+ */
+ case 'R':
+ if (!set_inst_root(optarg)) {
+ progerr(ERR_ROOT_CMD);
+ exit(1);
+ }
+ break;
+
+ /*
+ * Same as pkgrm: allow admin to establish the client
+ * filesystem using a vfstab-like file of stable format.
+ */
+ case 'V':
+ vfstab_file = flex_device(optarg, 2);
+ map_client = 1;
+ break;
+
+ /*
+ * Same as pkgrm: trace all of the scripts that
+ * get executed by pkgrm, located in the
+ * pkginst/install directory. This option is used for
+ * debugging the procedural and non-procedural
+ * scripts.
+ */
+ case 'v':
+ pkgverbose++;
+ break;
+
+ /*
+ * Different from pkgrm: process this package using
+ * old non-ABI symlinks
+ */
+ case 'y':
+ set_nonABI_symlinks();
+ break;
+
+ 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
+ * ********************************************************************
+ */
+
+ (void) echoDebugSetFlag(debugFlag);
+ (void) log_set_verbose(debugFlag);
+
+ 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());
+ }
+
+ /* establish cmdbin path */
+
+ if (cmdbin[0] == '\0') {
+ (void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
+ }
+
+ /* Read the mount table */
+
+ if (get_mntinfo(map_client, vfstab_file)) {
+ quit(99);
+ }
+
+ /*
+ * 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 removed from 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));
+ } else {
+ pkgrmremote = 0; /* Makes no sense on local host. */
+ }
+
+ /*
+ * 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);
+
+ pkginst = argv[optind++];
+ if (optind != argc) {
+ usage();
+ }
+
+ /* validate package software database (contents) file */
+
+ if (vcfile() == 0) {
+ quit(99);
+ }
+
+ /*
+ * Acquire the package lock - currently at "remove initialization"
+ */
+
+ if (!lockinst(get_prog_name(), pkginst, "remove-initial")) {
+ quit(99);
+ }
+
+ /* establish temporary directory to use */
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL) {
+ tmpdir = P_tmpdir;
+ }
+
+ echoDebug(DBG_PKGREMOVE_TMPDIR, tmpdir);
+
+ /*
+ * Initialize installation admin parameters by reading
+ * the adminfile.
+ */
+
+ echoDebug(DBG_PKGREMOVE_ADMINFILE, admnfile ? admnfile : "");
+ setadminFile(admnfile);
+
+ /*
+ * about to perform first operation that could be modified by the
+ * preremove check option - if preremove 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 (preremoveCheck == B_TRUE) {
+ (void) echoSetFlag(B_FALSE);
+ echoDebug(DBG_PKGREMOVE_PRERMCHK, pkginst ? pkginst : "",
+ zoneName ? zoneName : "global");
+ rcksetPreremoveCheck(B_TRUE);
+ rcksetZoneName(zoneName);
+ }
+
+ (void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s", get_PKGLOC(),
+ pkginst);
+ (void) snprintf(pkgbin, sizeof (pkgbin), "%s/install", pkgloc);
+ (void) snprintf(rlockfile, sizeof (rlockfile), "%s/!R-Lock!", pkgloc);
+
+ if (chdir(pkgbin)) {
+ progerr(ERR_CHDIR, pkgbin);
+ quit(99);
+ }
+
+ echo(MSG_PREREMOVE_REMINST, pkginst);
+
+ /*
+ * if a lock file is present, then a previous attempt to remove this
+ * package may have been unsuccessful.
+ */
+
+ if (access(rlockfile, F_OK) == 0) {
+ echo(ERR_UNSUCC);
+ echoDebug(DBG_PKGINSTALL_HAS_LOCKFILE, pkginst, rlockfile,
+ zoneName ? zoneName : "global");
+ }
+
+ /*
+ * Process all parameters from the pkginfo file
+ * and place them in the execution environment
+ */
+
+ /* Add DB retreival of the pkginfo parameters here */
+ (void) snprintf(path, sizeof (path), "%s/pkginfo", pkgloc);
+ if ((fp = fopen(path, "r")) == NULL) {
+ progerr(ERR_PKGINFO, path);
+ quit(99);
+ }
+
+ /* Mount up the client if necessary. */
+ if (map_client && !mount_client()) {
+ logerr(MSG_MANMOUNT);
+ }
+
+ /* Get mount point of client */
+ client_mntdir = getenv("CLIENT_MNTDIR");
+
+ 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;
+
+ if (nonABI_symlinks()) {
+ putparam("PKG_NONABI_SYMLINKS", "TRUE");
+ }
+
+ /*
+ * read the pkginfo file and fix any PKGSAV path - the correct
+ * install_root will be prepended to the existing path.
+ */
+
+ param[0] = '\0';
+ while (value = fpkgparam(fp, param)) {
+ int validx = 0;
+ char *newvalue;
+
+ /* strip out any setting of PATH */
+
+ if (strcmp(param, "PATH") == 0) {
+ free(value);
+ param[0] = '\0';
+ continue;
+ }
+
+ /* if not PKGSAV then write out unchanged */
+
+ if (strcmp(param, "PKGSAV") != 0) {
+ putparam(param, value);
+ free(value);
+ param[0] = '\0';
+ continue;
+ }
+
+ /*
+ * PKGSAV parameter found - interpret the directory:
+ * If in host:path format or marked with the leading "//",
+ * then there is no client-relative translation - take it
+ * literally later rather than use fixpath().
+ */
+
+ if (strstr(value, ":/")) {
+ /* no modification needed */
+ validx = 0;
+ } else if (strstr(value, "//") == value) {
+ validx = 1;
+ } else if (is_an_inst_root()) {
+ /* This PKGSAV needs to be made client-relative. */
+ newvalue = fixpath(value);
+ free(value);
+ value = newvalue;
+ }
+ putparam(param, value+validx);
+ free(value);
+ param[0] = '\0';
+ }
+
+ (void) fclose(fp);
+
+ /* write parent condition information to environment */
+
+ putConditionInfo(parentZoneName, parentZoneType);
+
+ putuserlocale();
+
+ /*
+ * Now do all the various setups based on ABI compliance
+ */
+
+ /* Read the environment provided by the pkginfo file */
+ abi_comp_ptr = getenv("NONABI_SCRIPTS");
+
+ /* if not ABI compliant set global flag */
+ abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+ if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+ set_nonABI_symlinks();
+ }
+
+ /*
+ * If pkginfo says it's not compliant then set non_abi_scripts.
+ * Oh, for two releases, set it from exception package names as
+ * well.
+ */
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ if (exception_pkg(pkginst, SCRIPT) ||
+ (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0))
+ script_in = PROC_XSTDIN;
+#else
+ if (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0) {
+ script_in = PROC_XSTDIN;
+ }
+#endif
+ /*
+ * *********************************************************************
+ * this feature is removed starting with Solaris 10 - there is no built
+ * in list of packages that should be run "the old way"
+ * *********************************************************************
+ */
+
+#ifdef ALLOW_EXCEPTION_PKG_LIST
+ /* Until 2.9, set it from the execption list */
+ if (exception_pkg(pkginst, LINK)) {
+ set_nonABI_symlinks();
+ }
+#endif
+
+ /*
+ * Since this is a removal, we can tell whether it's absolute or
+ * not from the resident pkginfo file read above.
+ */
+ if ((err = set_basedirs((getenv("BASEDIR") != NULL), adm.basedir,
+ pkginst, nointeract)) != 0) {
+ quit(err);
+ }
+
+ /*
+ * See if were are removing a package that only wants to update
+ * the database or only remove 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);
+
+ /*
+ * 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 removed.
+ */
+
+ 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);
+ }
+ }
+
+ put_path_params();
+
+ /* If client mount point, add it to pkgremove environment */
+
+ if (client_mntdir != NULL) {
+ putparam("CLIENT_MNTDIR", client_mntdir);
+ }
+
+ /* Establish the class list and the class attributes. */
+
+ if ((value = getenv("CLASSES")) != NULL) {
+ cl_sets(qstrdup(value));
+ } else {
+ progerr(ERR_CLASSES, path);
+ quit(99);
+ }
+
+ /* establish path and tmpdir */
+
+ if (cmdbin[0] == '\0') {
+ (void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
+ }
+
+ (void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
+ putparam("PATH", path);
+
+ putparam("TMPDIR", tmpdir);
+
+ /*
+ * 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 (value = getenv("ULIMIT")) {
+ if (assign_ulimit(value) == -1) {
+ progerr(ERR_BADULIMIT, value);
+ warnflag++;
+ }
+ putparam("PKG_ULIMIT", "TRUE");
+ }
+
+ /*
+ * If only gathering dependencies, check and output status of all
+ * remaining dependencies and exit.
+ */
+
+ if (preremoveCheck == B_TRUE) {
+ /*
+ * make sure current runlevel is appropriate
+ */
+
+ (void) fprintf(stdout, "rckrunlevel=%d\n", rckrunlevel());
+
+ /*
+ * determine if any packaging scripts provided with
+ * this package will execute as a priviledged user
+ */
+
+ (void) fprintf(stdout, "rckpriv=%d\n", rckpriv());
+
+ /*
+ * verify package dependencies
+ */
+
+ (void) fprintf(stdout, "rckdepend=%d\n", rckdepend());
+
+ /*
+ * ****** preremove check done - exit ******
+ */
+
+ echoDebug(DBG_PKGREMOVE_PRERMCHK_OK);
+ quit(0);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Not gathering dependencies only, proceed to check dependencies
+ * and continue with the package removal operation.
+ */
+
+ /*
+ * make sure current runlevel is appropriate
+ */
+
+ n = rckrunlevel();
+
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * determine if any packaging scripts provided with
+ * this package will execute as a priviledged user
+ */
+
+ n = rckpriv();
+
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * verify package dependencies
+ */
+ n = rckdepend();
+
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * *********************************************************************
+ * the actual removal of the package begins here
+ * *********************************************************************
+ */
+
+ /*
+ * create lockfile to indicate start of removal
+ */
+ started++;
+ if ((fd = open(rlockfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
+ progerr(ERR_LOCKFILE, rlockfile);
+ quit(99);
+ } else {
+ (void) close(fd);
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(MSG_PKGREMOVE_PROCPKG_GZ);
+ echoDebug(DBG_PKGREMOVE_PROCPKG_GZ, pkginst, rlockfile);
+ } else {
+ echo(MSG_PKGREMOVE_PROCPKG_LZ, zoneName);
+ echoDebug(DBG_PKGREMOVE_PROCPKG_LZ, pkginst, rlockfile,
+ zoneName);
+ }
+ if (delmap(0, pkginst) != 0) {
+ progerr(ERR_DB_QUERY, pkginst);
+ quit(99);
+ }
+
+ /*
+ * Run a preremove script if one is provided by the package.
+ * Don't execute preremove script if only updating the DB.
+ * Don't execute preremove script if files are not being deleted.
+ * Don't execute preremove script if one or more files reside in
+ * an inherited FS.
+ */
+
+ /* update the lock - at the preremove script */
+ lockupd("preremove");
+
+ /* execute preremove script if one is provided */
+ (void) snprintf(script, sizeof (script), "%s/preremove", pkgbin);
+ if (access(script, F_OK) != 0) {
+ /* no script present */
+ echoDebug(DBG_PKGREMOVE_POC_NONE, pkginst,
+ zoneName ? zoneName : "global");
+ } else if (nodelete) {
+ /* not deleting files: skip preremove script */
+ echoDebug(DBG_PKGREMOVE_POC_NODEL, pkginst, script,
+ zoneName ? zoneName : "global");
+ } else if (is_depend_pkginfo_DB()) {
+ /* updating db only: skip preremove script */
+ echoDebug(DBG_PKGREMOVE_POC_DBUPD, pkginst, script,
+ zoneName ? zoneName : "global");
+ } else {
+ /* script present and ok to run: run the script */
+ set_ulimit("preremove", ERR_PREREMOVE);
+ if (zoneName == (char *)NULL) {
+ echo(MSG_PKGREMOVE_EXEPOC_GZ);
+ echoDebug(DBG_PKGREMOVE_EXEPOC_GZ, pkginst, script);
+ } else {
+ echo(MSG_PKGREMOVE_EXEPOC_LZ, zoneName);
+ echoDebug(DBG_PKGREMOVE_EXEPOC_LZ, pkginst, script,
+ zoneName);
+ }
+ putparam("PKG_PROC_SCRIPT", "preremove");
+ if (pkgverbose) {
+ ckreturn(pkgexecl(script_in, PROC_STDOUT,
+ PROC_USER, PROC_GRP, SHELL, "-x",
+ script, NULL), ERR_PREREMOVE);
+ } else {
+ ckreturn(pkgexecl(script_in, PROC_STDOUT,
+ PROC_USER, PROC_GRP, SHELL, script,
+ NULL), ERR_PREREMOVE);
+ }
+ clr_ulimit();
+ }
+
+ /* update the lock - doing removal */
+
+ lockupd("remove");
+
+ /*
+ * Ensure that the contents file is updated even if the db has
+ * been upgraded, in the case that there are relevant entries
+ * in a special_contents file. The return value is ignored
+ * since we do not want special_contents operation to prevent
+ * pkgremove from succeeding. We do report errors to stderr.
+ */
+
+ /*
+ * Remove all components belonging to this package.
+ * Don't remove components if only updating the DB.
+ * Don't remove components if files are not being deleted.
+ */
+
+ if (nodelete) {
+ echoDebug(DBG_PKGREMOVE_REM_NODEL, pkginst,
+ zoneName ? zoneName : "global");
+ } else if (is_depend_pkginfo_DB()) {
+ echoDebug(DBG_PKGREMOVE_REM_DBUPD, pkginst,
+ zoneName ? zoneName : "global");
+ } else {
+ echoDebug(DBG_PKGREMOVE_REM, pkginst,
+ zoneName ? zoneName : "global");
+ /*
+ * remove package one class at a time
+ */
+
+ /* reverse order of classes */
+ for (i = cl_getn() - 1; i >= 0; i--) {
+ rmclass(cl_nam(i), pkgrmremote, zoneName);
+ }
+
+ rmclass(NULL, pkgrmremote, zoneName);
+ }
+
+ z_destroyMountTable();
+
+ /*
+ * Execute postremove script, if any
+ * Don't execute postremove script if only updating the DB.
+ * Don't execute postremove script if files are not being deleted.
+ */
+
+ /* update the lock - at the postremove script */
+ lockupd("postremove");
+
+ /* execute postremove script if one is provided */
+ (void) snprintf(script, sizeof (script), "%s/postremove", pkgbin);
+ if (access(script, F_OK) != 0) {
+ /* no script present */
+ echoDebug(DBG_PKGREMOVE_PIC_NONE, pkginst,
+ zoneName ? zoneName : "global");
+ } else if (nodelete) {
+ /* not deleting files: skip postremove script */
+ echoDebug(DBG_PKGREMOVE_PIC_NODEL, pkginst, script,
+ zoneName ? zoneName : "global");
+ } else if (is_depend_pkginfo_DB()) {
+ /* updating db only: skip postremove script */
+ echoDebug(DBG_PKGREMOVE_PIC_DBUPD, pkginst, script,
+ zoneName ? zoneName : "global");
+ } else {
+ /* script present and ok to run: run the script */
+ set_ulimit("postremove", ERR_POSTREMOVE);
+ if (zoneName == (char *)NULL) {
+ echo(MSG_PKGREMOVE_EXEPIC_GZ);
+ echoDebug(DBG_PKGREMOVE_EXEPIC_GZ, pkginst, script);
+ } else {
+ echo(MSG_PKGREMOVE_EXEPIC_LZ, zoneName);
+ echoDebug(DBG_PKGREMOVE_EXEPIC_LZ, pkginst, script,
+ zoneName);
+ }
+ putparam("PKG_PROC_SCRIPT", "postremove");
+ putparam("TMPDIR", tmpdir);
+ if (pkgverbose) {
+ ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
+ PROC_GRP, SHELL, "-x", script, NULL),
+ ERR_POSTREMOVE);
+ } else {
+ ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
+ PROC_GRP, SHELL, script, NULL),
+ ERR_POSTREMOVE);
+ }
+ clr_ulimit();
+ }
+
+ if (zoneName == (char *)NULL) {
+ echo(MSG_PKGREMOVE_UPDINF_GZ);
+ } else {
+ echo(MSG_PKGREMOVE_UPDINF_LZ, zoneName);
+ }
+
+ if (delmap(1, pkginst) != 0) {
+ progerr(ERR_DB_QUERY, pkginst);
+ quit(99);
+ }
+
+ if (!warnflag && !failflag) {
+ if (pt = getenv("PREDEPEND"))
+ predepend(pt);
+ (void) chdir("/");
+ if (rrmdir(pkgloc))
+ warnflag++;
+ }
+
+ if ((z_running_in_global_zone() == B_TRUE) &&
+ (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE)) {
+ boolean_t b;
+
+ b = pkgRemovePackageFromGzonlyList(get_inst_root(), pkginst);
+ if (b == B_FALSE) {
+ progerr(ERR_PKGREMOVE_GZONLY_REMOVE, pkginst);
+ ckreturn(1, NULL);
+ }
+ }
+
+ /* release the generic package lock */
+
+ (void) unlockinst();
+
+ quit(0);
+ /* LINTED: no return */
+}
+
+int
+issymlink(char *path)
+{
+ struct stat statbuf;
+
+ /*
+ * Obtain status of path; if symbolic link get link's status
+ */
+
+ if (lstat(path, &statbuf) != 0) {
+ return (1); /* not symlink */
+ }
+
+ /*
+ * Status obtained - if symbolic link, return 0
+ */
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
+ return (0); /* is a symlink */
+ }
+
+ /*
+ * Not a symbolic link - return 1
+ */
+
+ return (1); /* not symlink */
+}
+
+static void
+rmclass(char *aclass, int rm_remote, char *a_zoneName)
+{
+ struct cfent *ept;
+ FILE *fp;
+ char tmpfile[PATH_MAX];
+ char script[PATH_MAX];
+ int i;
+ char *tmp_path;
+ char *save_path = NULL;
+ struct stat st;
+
+ if (aclass == NULL) {
+ for (i = 0; i < eptnum; i++) {
+ if (eptlist[i] != NULL) {
+ rmclass(eptlist[i]->pkg_class,
+ rm_remote, a_zoneName);
+ }
+ }
+ return;
+ }
+
+ /* locate class action script to execute */
+ (void) snprintf(script, sizeof (script), "%s/r.%s", pkgbin, aclass);
+ if (access(script, F_OK) != 0) {
+ (void) snprintf(script, sizeof (script), "%s/r.%s",
+ PKGSCR, aclass);
+ if (access(script, F_OK) != 0)
+ script[0] = '\0';
+ }
+ if (script[0] != '\0') {
+ int td;
+
+ (void) snprintf(tmpfile, sizeof (tmpfile), "%s/RMLISTXXXXXX",
+ tmpdir);
+ td = mkstemp(tmpfile);
+ if (td == -1) {
+ progerr(ERR_TMPFILE);
+ quit(99);
+ }
+ if ((fp = fdopen(td, "w")) == NULL) {
+ progerr(ERR_WTMPFILE, tmpfile);
+ quit(99);
+ }
+ }
+
+ if (a_zoneName == (char *)NULL) {
+ echo(MSG_PKGREMOVE_REMPATHCLASS_GZ, aclass);
+ } else {
+ echo(MSG_PKGREMOVE_REMPATHCLASS_LZ, aclass, a_zoneName);
+ }
+
+ /* process paths in reverse order */
+ i = eptnum;
+ while (--i >= 0) {
+ ept = eptlist[i];
+
+ if ((ept == NULL) || strcmp(aclass, ept->pkg_class)) {
+ continue;
+ }
+
+ /* save the path, and prepend the ir */
+ if (is_an_inst_root()) {
+ save_path = ept->path;
+ tmp_path = fixpath(ept->path);
+ ept->path = tmp_path;
+ }
+
+ if (!ept->ftype || (ept->ftype == '^' && !script[0])) {
+ /*
+ * A path owned by more than one package is marked with
+ * a NULL ftype (seems odd, but that's how it's
+ * done). Such files are sacro sanct. Shared editable
+ * files are a special case, and are marked with an
+ * ftype of '^'. These files should only be ignored if
+ * no class action script is present. It is the CAS's
+ * responsibility to not remove the editable object.
+ */
+ echo(MSG_SHARED, ept->path);
+ } else if (ept->pinfo->status == SERVED_FILE && !rm_remote) {
+ /*
+ * If the path is provided to the client from a
+ * server, don't remove anything unless explicitly
+ * requested through the "-f" option.
+ */
+ echo(MSG_SERVER, ept->path);
+ } else if (z_path_is_inherited(ept->path, ept->ftype,
+ get_inst_root())) {
+ /*
+ * object is in an area inherited from the global zone,
+ * and the object cannot be removed - output a message
+ * indicating the object cannot be removed and continue.
+ */
+ echo(MSG_NOTREMOVED_INHERITED, ept->path);
+ } else if (script[0]) {
+ /*
+ * If there's a class action script, just put the
+ * path name into the list.
+ */
+ (void) fprintf(fp, "%s\n", ept->path);
+ } else if (strchr("dx", ept->ftype) != NULL ||
+ (lstat(ept->path, &st) == 0 && S_ISDIR(st.st_mode))) {
+ /* Directories are rmdir()'d. */
+
+ if (rmdir(ept->path)) {
+ if (errno == EBUSY) {
+ echo(MSG_DIRBUSY, ept->path);
+ } else if (errno == EEXIST) {
+ echo(MSG_NOTEMPTY, ept->path);
+ } else if (errno != ENOENT) {
+ progerr(ERR_RMDIR, ept->path);
+ warnflag++;
+ }
+ } else {
+ if (ept->pinfo->status == SERVED_FILE) {
+ echo(MSG_RMSRVR, ept->path);
+ } else {
+ echo("%s", ept->path);
+ }
+ }
+
+ } else {
+ /*
+ * Before removing this object one more
+ * check should be done to assure that a
+ * shared object is not removed.
+ * This can happen if the original object
+ * was incorrectly updated with the
+ * incorrect class identifier.
+ * This handles pathologcal cases that
+ * weren't handled above.
+ */
+ if (ept->npkgs > 1) {
+ echo(MSG_SHARED, ept->path);
+ continue;
+ }
+
+ /* Regular files are unlink()'d. */
+
+ if (unlink(ept->path)) {
+ if (errno != ENOENT) {
+ progerr(ERR_RMPATH, ept->path);
+ warnflag++;
+ }
+ } else {
+ if (ept->pinfo->status == SERVED_FILE) {
+ echo(MSG_RMSRVR, ept->path);
+ } else {
+ echo("%s", ept->path);
+ }
+ }
+ }
+
+ /* restore the original path */
+
+ if (is_an_inst_root()) {
+ ept->path = save_path;
+ }
+
+ /*
+ * free memory allocated for this entry memory used for
+ * pathnames will be freed later by a call to pathdup()
+ */
+
+ if (eptlist[i]) {
+ free(eptlist[i]);
+ }
+ eptlist[i] = NULL;
+ }
+ if (script[0]) {
+ (void) fclose(fp);
+ set_ulimit(script, ERR_CASFAIL);
+ if (pkgverbose)
+ ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
+ CAS_GRP, SHELL, "-x", script, NULL),
+ ERR_CASFAIL);
+ else
+ ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
+ CAS_GRP, SHELL, script, NULL),
+ ERR_CASFAIL);
+ clr_ulimit();
+ if (isfile(NULL, tmpfile) == 0) {
+ if (unlink(tmpfile) == -1)
+ progerr(ERR_RMPATH, tmpfile);
+ }
+ }
+}
+
+static void
+ckreturn(int retcode, char *msg)
+{
+ switch (retcode) {
+ case 2:
+ case 12:
+ case 22:
+ warnflag++;
+ /*FALLTHRU*/
+ if (msg)
+ progerr(msg);
+ case 10:
+ case 20:
+ if (retcode >= 10)
+ 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(msg);
+ /*FALLTHRU*/
+ case 3:
+ case 13:
+ case 23:
+ quit(retcode);
+ /* NOT REACHED */
+ default:
+ if (msg)
+ progerr(msg);
+ quit(1);
+ }
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, ERR_USAGE_PKGREMOVE);
+
+ exit(1);
+}
+
+/*
+ * Name: path_valid
+ * Description: Checks a string for being a valid path
+ *
+ * Arguments: path - path to validate
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise.
+ * B_FALSE means path was null, too long (>PATH_MAX),
+ * or too short (<1)
+ */
+static boolean_t
+path_valid(char *path)
+{
+ if (path == NULL) {
+ return (B_FALSE);
+ } else if (strlen(path) > PATH_MAX) {
+ return (B_FALSE);
+ } else if (strlen(path) >= 1) {
+ return (B_TRUE);
+ } else {
+ /* path < 1 */
+ return (B_FALSE);
+ }
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/predepend.c b/usr/src/cmd/svr4pkg/pkgremove/predepend.c
new file mode 100644
index 0000000000..eda6f3da41
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/predepend.c
@@ -0,0 +1,72 @@
+/*
+ * 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 1993 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 <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include "pkglib.h"
+#include "libinst.h"
+#include "libadm.h"
+
+extern int warnflag;
+
+#define ERR_UNLINK "unable to unlink <%s>"
+
+void
+predepend(char *oldpkg)
+{
+ struct stat status;
+ char spath[PATH_MAX];
+
+ oldpkg = strtok(oldpkg, " \t\n");
+ if (oldpkg == NULL)
+ return;
+
+ do {
+ (void) sprintf(spath, "%s/%s.name", get_PKGOLD(), oldpkg);
+ if (lstat(spath, &status) == 0) {
+ if (status.st_mode & S_IFLNK) {
+ if (unlink(spath)) {
+ progerr(gettext(ERR_UNLINK), spath);
+ warnflag++;
+ }
+ return;
+ }
+ }
+ } while (oldpkg = strtok(NULL, " \t\n"));
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/quit.c b/usr/src/cmd/svr4pkg/pkgremove/quit.c
new file mode 100644
index 0000000000..6ff46d0e89
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/quit.c
@@ -0,0 +1,321 @@
+/*
+ * 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 2004 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 <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "messages.h"
+
+#define MAILCMD "/usr/bin/mail"
+
+/* lockinst.c */
+extern void unlockinst(void);
+
+/* mntinfo.c */
+extern int unmount_client(void);
+
+extern char *msgtext;
+extern char *pkginst;
+
+extern int started;
+extern int dreboot; /* != 0 if reboot required after installation */
+extern int failflag; /* != 0 if fatal error has occurred (1) */
+extern int ireboot; /* != 0 if immediate reboot required */
+extern int warnflag; /* != 0 if non-fatal error has occurred (2) */
+
+extern struct admin adm;
+
+/*
+ * exported functions
+ */
+
+void quit(int retcode);
+void quitSetSilentExit(boolean_t a_silentExit);
+void quitSetZoneName(char *a_zoneName);
+sighdlrFunc_t *quitGetTrapHandler(void);
+
+/*
+ * forward declarations
+ */
+
+static void mailmsg(int retcode);
+static void quitmsg(int retcode);
+static void trap(int signo);
+
+static char *zoneName = (char *)NULL;
+static boolean_t silentExit = B_FALSE;
+static int includeZonename = 0;
+static int trapEntered = 0;
+
+/*
+ * *****************************************************************************
+ * 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: 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
+ */
+
+void
+quitSetZoneName(char *a_zoneName)
+{
+ zoneName = a_zoneName;
+ if ((zoneName == (char *)NULL || *zoneName == '\0')) {
+ includeZonename = 0;
+ } else {
+ includeZonename = 1;
+ }
+}
+
+/*
+ * Name: quitSetSilentExit
+ * Description: set the "silent exit" flag - if silent exit is TRUE, then
+ * no messages are output by quit() when it is called
+ * Arguments: a_silentExit - indicates whether or not silent exit is set
+ * Returns: void
+ */
+
+void
+quitSetSilentExit(boolean_t a_silentExit)
+{
+ silentExit = a_silentExit;
+}
+
+/*
+ * 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 retcode)
+{
+ /* disable interrupts */
+
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ /* process return code if not quit(99) */
+
+ if (retcode != 99) {
+ if ((retcode % 10) == 0) {
+ if (failflag) {
+ retcode += 1;
+ } else if (warnflag) {
+ retcode += 2;
+ }
+ }
+
+ if (ireboot) {
+ retcode = (retcode % 10) + 20;
+ }
+
+ if (dreboot) {
+ retcode = (retcode % 10) + 10;
+ }
+ }
+
+ /*
+ * In the event that this quit() was called prior to completion of
+ * the task, do an unlockinst() just in case.
+ */
+ unlockinst();
+
+ /* unmount the mounts that are our responsibility. */
+ (void) unmount_client();
+
+ /* send mail to appropriate user list */
+ mailmsg(retcode);
+
+ /* display message about this installation */
+ quitmsg(retcode);
+
+ /* final exit debugging message */
+
+ echoDebug(DBG_EXIT_WITH_CODE, retcode);
+
+ exit(retcode);
+ /*NOTREACHED*/
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+quitmsg(int retcode)
+{
+ if (silentExit == B_TRUE) {
+ return;
+ }
+
+ (void) putc('\n', stderr);
+
+ /* if there is no pkgname, no message to report */
+ if (pkginst != (char *)NULL) {
+ ptext(stderr, qreason(3, retcode, 0, includeZonename),
+ pkginst, zoneName);
+ }
+
+ if (retcode && !started) {
+ ptext(stderr, MSG_NOCHANGE);
+ }
+}
+
+static void
+mailmsg(int retcode)
+{
+ struct utsname utsbuf;
+ FILE *pp;
+ char *cmd;
+ size_t len;
+
+ if (silentExit == B_TRUE) {
+ return;
+ }
+
+ if (!started || (adm.mail == NULL))
+ return;
+
+ len = strlen(adm.mail) + sizeof (MAILCMD) + 2;
+ cmd = calloc(len, sizeof (char));
+ if (cmd == NULL) {
+ logerr(WRN_NOMAIL);
+ return;
+ }
+
+ (void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail);
+ if ((pp = popen(cmd, "w")) == NULL) {
+ logerr(WRN_NOMAIL);
+ return;
+ }
+
+ if (msgtext) {
+ ptext(pp, gettext(msgtext));
+ }
+
+ (void) strcpy(utsbuf.nodename, gettext("(unknown)"));
+ (void) uname(&utsbuf);
+ ptext(pp, qreason(4, retcode, 0, includeZonename), pkginst,
+ utsbuf.nodename, zoneName);
+
+ if (pclose(pp)) {
+ logerr(WRN_FLMAIL);
+ }
+}
+
+/*
+ * 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/pkgremove/special.c b/usr/src/cmd/svr4pkg/pkgremove/special.c
new file mode 100644
index 0000000000..ea08003cc9
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/special.c
@@ -0,0 +1,710 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * special.c
+ *
+ * This module contains code required to remove special contents from
+ * the contents file when a pkgrm is done on a system upgraded to use
+ * the new database.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <fnmatch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include "pkglib.h"
+#include <libintl.h>
+
+/* This specifies the maximum length of a contents file line read in. */
+#define LINESZ 8192
+
+#define SPECIAL_MALLOC "unable to maintain package contents text due to "\
+ "insufficient memory."
+#define SPECIAL_ACCESS "unable to maintain package contents text due to "\
+ "an access failure."
+#define SPECIAL_INPUT "unable to maintain package contents text: alternate "\
+ "root path too long"
+
+/*
+ * strcompare
+ *
+ * This function is used by qsort to sort an array of special contents
+ * rule strings. This array must be sorted to facilitate efficient
+ * rule processing. See qsort(3c) regarding qsort compare functions.
+ */
+static int
+strcompare(const void *pv1, const void *pv2)
+{
+ char **ppc1 = (char **) pv1;
+ char **ppc2 = (char **) pv2;
+ int i = strcmp(*ppc1, *ppc2);
+ if (i < 0)
+ return (-1);
+ if (i > 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * match
+ *
+ * This function determines whether a file name (pc) matches a rule
+ * from the special contents file (pcrule). We assume that neither
+ * string is ever NULL.
+ *
+ * Return: 1 on match, 0 on no match.
+ * Side effects: none.
+ */
+static int
+match(const char *pc, char *pcrule)
+{
+ int n = strlen(pcrule);
+ int wild = 0;
+ if (pcrule[n - 1] == '*') {
+ wild = 1;
+ pcrule[n - 1] = '\0';
+ }
+
+ if (!wild) {
+ if (fnmatch(pc, pcrule, FNM_PATHNAME) == 0 ||
+ fnmatch(pc, pcrule, 0) == 0)
+ return (1);
+ } else {
+ int j;
+ j = strncmp(pc, pcrule, n - 1);
+ pcrule[n - 1] = '*';
+ if (j == 0)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * search_special_contents
+ *
+ * This function assumes that a series of calls will be made requesting
+ * whether a given path matches the special contents rules or not. We
+ * assume that
+ *
+ * a) the special_contents array is sorted
+ * b) the calls will be made with paths in a sorted order
+ *
+ * Given that, we can keep track of where the last search ended and
+ * begin the new search at that point. This reduces the cost of a
+ * special contents matching search to O(n) from O(n^2).
+ *
+ * ppcSC A pointer to an array of special contents obtained via
+ * get_special_contents().
+ * path A path: determine whether it matches the special
+ * contents rules or not.
+ * piX The position in the special_contents array we have already
+ * arrived at through searching. This must be initialized to
+ * zero before initiating a series of search_special_contents
+ * operations.
+ *
+ * Example:
+ * {
+ * int i = 0, j, max;
+ * char **ppSC = NULL;
+ * if (get_special_contents(NULL, &ppcSC, &max) != 0) exit(1);
+ * for (j = 0; paths != NULL && paths[j] != NULL; j++) {
+ * if (search_special_contents(ppcSC, path[j], &i)) {
+ * do_something_with_special_path(path[j]);
+ * }
+ * }
+ * }
+ *
+ * Return: 1 if there is a match, 0 otherwise.
+ * Side effects: The value of *piX will be set between calls to this
+ * function. To make this function thread safe, use search arrays.
+ * Also: Nonmatching entries are eliminated, set to NULL.
+ */
+static int
+search_special_contents(char **ppcSC, const char *pcpath, int *piX, int max)
+{
+ int wild;
+ if (ppcSC == NULL || *piX == max)
+ return (0);
+
+ while (*piX < max) {
+
+ int j, k;
+ if (ppcSC[*piX] == NULL) {
+ (*piX)++;
+ continue;
+ }
+
+ j = strlen(ppcSC[*piX]);
+ k = strcmp(pcpath, ppcSC[*piX]);
+ wild = (ppcSC[*piX][j - 1] == '*');
+
+ /*
+ * Depending on whether the path string compared with the
+ * rule, we take different actions. If the path is less
+ * than the rule, we keep the rule. If the path equals
+ * the rule, we advance the rule (as long as the rule is
+ * not a wild card). If the path is greater than the rule,
+ * we have to advance the rule list until we are less or equal
+ * again. This way we only have to make one pass through the
+ * rules, as we make one pass through the path strings. We
+ * assume that the rules and the path strings are sorted.
+ */
+ if (k < 0) {
+
+ if (wild == 0)
+ return (0);
+
+ if (match(pcpath, ppcSC[*piX]))
+ return (1);
+ break;
+
+ } else if (k == 0) {
+
+ int x = match(pcpath, ppcSC[*piX]);
+ if (wild == 0) (*piX)++;
+ return (x);
+
+ } else {
+ /* One last try. */
+ if (match(pcpath, ppcSC[*piX]))
+ return (1);
+
+ /*
+ * As pcpath > ppcSC[*piX] we have passed up this
+ * rule - it cannot apply. Therefore, we do not
+ * need to retain it. Removing the rule will make
+ * subsequent searching more efficient.
+ */
+ free(ppcSC[*piX]);
+ ppcSC[*piX] = NULL;
+
+ (*piX)++;
+ }
+ }
+ return (0);
+}
+
+/*
+ * get_special_contents
+ *
+ * Retrieves the special contents file entries, if they exist. These
+ * are sorted. We do not assume the special_contents file is in sorted
+ * order.
+ *
+ * pcroot The root of the install database. If NULL assume '/'.
+ * pppcSC A pointer to a char **. This pointer will be set to
+ * point at NULL if there is no special_contents file or
+ * to a sorted array of strings, NULL terminated, otherwise.
+ * piMax The # of entries in the special contents result.
+ *
+ * Returns: 0 on no error, nonzero on error.
+ * Side effects: the pppcSC pointer is set to point at a newly
+ * allocated array of pointers to strings.. The caller must
+ * free this buffer. The value of *piMax is set to the # of
+ * entries in ppcSC.
+ */
+static int
+get_special_contents(const char *pcroot, char ***pppcSC, int *piMax)
+{
+ int e, i;
+ FILE *fp;
+ char line[2048];
+ char **ppc;
+ char *pc = "var/sadm/install/special_contents";
+ char path[PATH_MAX];
+ struct stat s;
+
+ /* Initialize the return values. */
+ *piMax = 0;
+ *pppcSC = NULL;
+
+ if (pcroot == NULL) {
+ pcroot = "/";
+ }
+
+ if (pcroot[strlen(pcroot) - 1] == '/') {
+ if (snprintf(path, PATH_MAX, "%s%s", pcroot, pc) >= PATH_MAX) {
+ progerr(gettext(SPECIAL_INPUT));
+ return (1);
+ }
+ } else {
+ if (snprintf(path, PATH_MAX, "%s/%s", pcroot, pc)
+ >= PATH_MAX) {
+ progerr(gettext(SPECIAL_INPUT));
+ return (1);
+ }
+ }
+
+ errno = 0;
+ e = stat(path, &s);
+ if (e != 0 && errno == ENOENT)
+ return (0); /* No special contents file. Do nothing. */
+
+ if (access(path, R_OK) != 0 || (fp = fopen(path, "r")) == NULL) {
+ /* Could not open special contents which exists */
+ progerr(gettext(SPECIAL_ACCESS));
+ return (1);
+ }
+
+ for (i = 0; fgets(line, 2048, fp) != NULL; i++);
+ rewind(fp);
+ if ((ppc = (char **) calloc(i + 1, sizeof (char *))) == NULL) {
+ progerr(gettext(SPECIAL_MALLOC));
+ return (1);
+ }
+
+ for (i = 0; fgets(line, 2048, fp) != NULL; ) {
+ int n;
+ if (line[0] == '#' || line[0] == ' ' || line[0] == '\n' ||
+ line[0] == '\t' || line[0] == '\r')
+ continue;
+ n = strlen(line);
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+ ppc[i++] = strdup(line);
+ }
+
+ qsort(ppc, i, sizeof (char *), strcompare);
+
+ *pppcSC = ppc;
+ *piMax = i;
+ return (0);
+}
+
+/*
+ * free_special_contents
+ *
+ * This function frees special_contents which have been allocated using
+ * get_special_contents.
+ *
+ * pppcSC A pointer to a buffer allocated using get_special_contents.
+ * max The number of entries allocated.
+ *
+ * Result: None.
+ * Side effects: Frees memory allocated using get_special_contents and
+ * sets the pointer passed in to NULL.
+ */
+static void
+free_special_contents(char ***pppcSC, int max)
+{
+ int i;
+ char **ppc = NULL;
+ if (*pppcSC == NULL)
+ return;
+
+ ppc = *pppcSC;
+ for (i = 0; ppc != NULL && i < max; i++)
+ if (ppc[i] == NULL)
+ free(ppc[i]);
+
+ if (ppc != NULL)
+ free(ppc);
+
+ *pppcSC = NULL;
+}
+
+/*
+ * get_path
+ *
+ * Return the first field of a string delimited by a space.
+ *
+ * pcline A line from the contents file.
+ *
+ * Return: NULL if an error. Otherwise a string allocated by this
+ * function. The caller must free the string.
+ * Side effects: none.
+ */
+static char *
+get_path(const char *pcline)
+{
+ int i = strcspn(pcline, " ");
+ char *pc = NULL;
+ if (i <= 1 || (pc = (char *) calloc(i + 1, 1)) == NULL)
+ return (NULL);
+ (void) memcpy(pc, pcline, i);
+ return (pc);
+}
+
+/*
+ * generate_special_contents_rules
+ *
+ * This procedure will generate an array of integers which will be a mask
+ * to apply to the ppcfextra array. If set to 1, then the content must be
+ * added to the contents file. Otherwise it will not be: The old contents
+ * file will be used for this path value, if one even exists.
+ *
+ * ient The number of ppcfextra contents installed.
+ * ppcfent The contents installed.
+ * ppcSC The rules (special contents)
+ * max The number of special contents rules.
+ * ppiIndex The array of integer values, determining whether
+ * individual ppcfextra items match special contents rules.
+ * This array will be created and set in this function and
+ * returned.
+ *
+ * Return: 0 success, nonzero failure
+ * Side effects: allocates an array of integers that the caller must free.
+ */
+static int
+generate_special_contents_rules(int ient, struct cfent **ppcfent,
+ char **ppcSC, int max, int **ppiIndex)
+{
+ int i, j;
+ int *pi = (int *) calloc(ient, sizeof (int));
+ if (pi == NULL) {
+ progerr(gettext(SPECIAL_MALLOC));
+ return (1);
+ }
+
+ /*
+ * For each entry in ppcfextra, check if it matches a rule.
+ * If it does not, set the entry in the index to -1.
+ */
+ for (i = 0, j = 0; i < ient && j < max; i++) {
+ if (search_special_contents(ppcSC, ppcfent[i]->path,
+ &j, max) == 1) {
+ pi[i] = 1;
+
+ } else {
+ pi[i] = 0;
+ }
+ }
+
+ /*
+ * In case we ran out of rules before contents, we will not use
+ * those contents. Make sure these contents are set to 0 and
+ * will not be copied from the ppcfent array into the contents
+ * file.
+ */
+ for (i = i; i < ient; i++)
+ pi[i] = 0;
+
+ *ppiIndex = pi;
+ return (0);
+}
+
+
+/*
+ * pathcmp
+ *
+ * Compare a path to a cfent. It will match either if the path is
+ * equal to the cfent path, or if the cfent is a symbolic or link
+ * and *that* matches.
+ *
+ * path a path
+ * pent a contents entry
+ *
+ * Returns: as per strcmp
+ * Side effects: none.
+ */
+static int
+pathcmp(const char *pc, const struct cfent *pent)
+{
+ int i;
+ if ((pent->ftype == 's' || pent->ftype == 'l') &&
+ pent->ainfo.local) {
+ char *p, *q;
+ if ((p = strstr(pc, "=")) == NULL) {
+
+ i = strcmp(pc, pent->path);
+
+ /* A path without additional chars strcmp's to less */
+ if (i == 0)
+ i = -1;
+
+ } else {
+ /* Break the link path into two pieces. */
+ *p = '\0';
+
+ /* Compare the first piece. */
+ i = strcmp(pc, pent->path);
+
+ /* If equal we must compare the second piece. */
+ if (i == 0) {
+ q = p + 1;
+ i = strcmp(q, pent->ainfo.local);
+ }
+
+ /* Restore the link path. */
+ *p = '=';
+ }
+ } else {
+ i = strcmp(pc, pent->path);
+ }
+
+ return (i);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ * Externally visible function.
+ */
+
+/*
+ * special_contents_remove
+ *
+ * Given a set of entries to remove and an alternate root, this function
+ * will do everything required to ensure that the entries are removed
+ * from the contents file if they are listed in the special_contents
+ * file. The contents file will get changed only in the case that the
+ * entire operation has succeeded.
+ *
+ * ient The number of entries.
+ * ppcfent The entries to remove.
+ * pcroot The alternate install root. Could be NULL. In this
+ * case, assume root is '/'
+ *
+ * Result: 0 on success, nonzero on failure. If an error occurs, an
+ * error string will get output to standard error alerting the user.
+ * Side effects: The contents file may change as a result of this call,
+ * such that lines in the in the file will be changed or removed.
+ * If the call fails, a t.contents file may be left behind. This
+ * temporary file should be removed subsequently.
+ */
+int
+special_contents_remove(int ient, struct cfent **ppcfent, const char *pcroot)
+{
+ int result = 0; /* Assume we will succeed. Return result. */
+ char **ppcSC = NULL; /* The special contents rules, sorted. */
+ int i, j; /* Indexes into contents & special contents */
+ FILE *fpi = NULL, /* Input of contents file */
+ *fpo = NULL; /* Output to temp contents file */
+ char cpath[PATH_MAX], /* Contents file path */
+ tcpath[PATH_MAX]; /* Temp contents file path */
+ const char *pccontents = "var/sadm/install/contents";
+ const char *pctcontents = "var/sadm/install/t.contents";
+ char line[LINESZ]; /* Reads in and writes out contents lines. */
+ time_t t; /* Used to create a timestamp comment. */
+ int max; /* Max number of special contents entries. */
+ int *piIndex; /* An index to ppcfents to remove from cfile */
+
+ cpath[0] = tcpath[0] = '\0';
+
+ if (ient == 0 || ppcfent == NULL || ppcfent[0] == NULL) {
+ goto remove_done;
+ }
+
+ if ((get_special_contents(pcroot, &ppcSC, &max)) != 0) {
+ result = 1;
+ goto remove_done;
+ }
+
+ /* Check if there are no special contents actions to take. */
+ if (ppcSC == NULL) {
+ goto remove_done;
+ }
+
+ if (pcroot == NULL) pcroot = "/";
+ if (pcroot[strlen(pcroot) - 1] == '/') {
+ if (snprintf(cpath, PATH_MAX, "%s%s", pcroot, pccontents)
+ >= PATH_MAX ||
+ snprintf(tcpath, PATH_MAX, "%s%s", pcroot, pctcontents)
+ >= PATH_MAX) {
+ progerr(gettext(SPECIAL_INPUT));
+ result = -1;
+ goto remove_done;
+ }
+ } else {
+ if (snprintf(cpath, PATH_MAX, "%s/%s", pcroot, pccontents)
+ >= PATH_MAX ||
+ snprintf(tcpath, PATH_MAX, "%s/%s", pcroot, pctcontents)
+ >= PATH_MAX) {
+ progerr(gettext(SPECIAL_INPUT));
+ result = -1;
+ goto remove_done;
+ }
+ }
+
+ /* Open the temporary contents file to write, contents to read. */
+ if (access(cpath, F_OK | R_OK) != 0) {
+ /*
+ * This is not a problem since no contents means nothing
+ * to remove due to special contents rules.
+ */
+ result = 0;
+ cpath[0] = '\0'; /* This signals omission of 'rename cleanup' */
+ goto remove_done;
+ }
+
+ if (access(cpath, W_OK) != 0) {
+ /* can't write contents file, something is wrong. */
+ progerr(gettext(SPECIAL_ACCESS));
+ result = 1;
+ goto remove_done;
+
+ }
+
+ if ((fpi = fopen(cpath, "r")) == NULL) {
+ /* Given the access test above, this should not happen. */
+ progerr(gettext(SPECIAL_ACCESS));
+ result = 1;
+ goto remove_done;
+ }
+
+ if ((fpo = fopen(tcpath, "w")) == NULL) {
+ /* open t.contents failed */
+ progerr(gettext(SPECIAL_ACCESS));
+ result = 1;
+ goto remove_done;
+ }
+
+ if (generate_special_contents_rules(ient, ppcfent, ppcSC, max, &piIndex)
+ != 0) {
+ result = 1;
+ goto remove_done;
+ }
+
+ /*
+ * Copy contents to t.contents unless there is an entry in
+ * the ppcfent array which corresponds to an index set to 1.
+ *
+ * These items are the removed package contents which matche an
+ * entry in ppcSC (the special_contents rules).
+ *
+ * Since both the contents and rules are sorted, we can
+ * make a single efficient pass.
+ */
+ (void) memset(line, 0, LINESZ);
+
+ for (i = 0, j = 0; fgets(line, LINESZ, fpi) != NULL; ) {
+
+ char *pcpath = NULL;
+
+ /*
+ * Note: This could be done better: We should figure out
+ * which are the last 2 lines and only trim those off.
+ * This will suffice to do this and will only be done as
+ * part of special_contents handling.
+ */
+ if (line[0] == '#')
+ continue; /* Do not copy the final 2 comment lines */
+
+ pcpath = get_path(line);
+
+ if (pcpath != NULL && i < ient) {
+ int k;
+ while (piIndex[i] == 0)
+ i++;
+
+ if (i < ient)
+ k = pathcmp(pcpath, ppcfent[i]);
+
+ if (k < 0 || i >= ient) {
+ /* Just copy contents -> t.contents */
+ /*EMPTY*/
+ } else if (k == 0) {
+ /* We have a match. Do not copy the content. */
+ i++;
+ free(pcpath);
+ (void) memset(line, 0, LINESZ);
+ continue;
+ } else while (i < ient) {
+
+ /*
+ * This is a complex case: The content
+ * entry is further along alphabetically
+ * than the rule. Skip over all rules which
+ * apply until we come to a rule which is
+ * greater than the current entry, or equal
+ * to it. If equal, do not copy, otherwise
+ * do copy the entry.
+ */
+ if (piIndex[i] == 0) {
+ i++;
+ continue;
+ } else if ((k = pathcmp(pcpath, ppcfent[i]))
+ >= 0) {
+ i++;
+ if (k == 0) {
+ free(pcpath);
+ (void) memset(line, 0, LINESZ);
+ break;
+ }
+ } else {
+ /* path < rule, end special case */
+ break;
+ }
+ }
+
+ /*
+ * Avoid copying the old content when path == rule
+ * This occurs when the complex case ends on a match.
+ */
+ if (k == 0)
+ continue;
+ }
+
+ if (fprintf(fpo, "%s", line) < 0) {
+ /* Failing to write output would be catastrophic. */
+ progerr(gettext(SPECIAL_ACCESS));
+ result = 1;
+ break;
+ }
+ (void) memset(line, 0, LINESZ);
+ }
+
+ t = time(NULL);
+ (void) fprintf(fpo, "# Last modified by pkgremove\n");
+ (void) fprintf(fpo, "# %s", ctime(&t));
+
+remove_done:
+ free_special_contents(&ppcSC, max);
+
+ if (fpi != NULL)
+ (void) fclose(fpi);
+
+ if (fpo != NULL)
+ (void) fclose(fpo);
+
+ if (result == 0) {
+ if (tcpath[0] != '\0' && cpath[0] != '\0' &&
+ rename(tcpath, cpath) != 0) {
+ progerr(gettext(SPECIAL_ACCESS));
+ result = 1;
+ }
+ } else {
+ if (tcpath[0] != '\0' && remove(tcpath) != 0) {
+ /*
+ * Do not output a diagnostic message. This condition
+ * occurs only when we are unable to clean up after
+ * a failure. A temporary file will linger.
+ */
+ result = 1;
+ }
+ }
+
+ return (result);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.c b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.c
new file mode 100644
index 0000000000..816a94d647
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.c
@@ -0,0 +1,472 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * wsreg_pkgrm.c
+ *
+ * Background information:
+ *
+ * In the past, pkgrm did not check whether a package was needed by
+ * products in the product registry. The only check that pkgrm does
+ * is whether any packages depend on the package to be removed. This
+ * meant that it was trivial to use pkgrm correctly and damage products
+ * (installed by webstart wizards) - without even receiving a warning.
+ *
+ * This enhancement to pkgrm will determine if the package to remove is
+ * needed by any registered products. If not, a '0' is returned and the
+ * pkgrm can proceed. If there is a conflict, nonzero is returned and
+ * a list of all products which will be effected. Note that removing
+ * one package may damage several products. This is because some
+ * packages are used by several products, and some components are shared
+ * by several products.
+ *
+ * The list returned is a string, which the caller must free by calling
+ * free().
+ *
+ * The purpose of the list is to inform the user, exactly as is done with
+ * the 'depends' information. The user must be presented with the list
+ * as a warning and be able to either abort the operation or proceed -
+ * well advised of the consequences.
+ *
+ * How this works
+ *
+ * Installed products are associated with 'components' in a product
+ * registry database. Components in the product registry are often
+ * associated with packages. Packages are the mechanism in which
+ * software is actually installed, on Solaris. For example, when a
+ * webstart wizard install occurs, one or more packages are added.
+ * These are associated with 'components' (install metadata containers)
+ * in the product registry. The product registry interface acts as
+ * though these packages *really are* installed.
+ *
+ * In order to ensure that this remains the case, the product registry
+ * is examined for instances of a package before that package is removed.
+ *
+ * See libwsreg(3LIB) for general information about the product
+ * registry library used to determine if removing a package is OK.
+ *
+ * See prodreg(1M) for information about a tool which can be used
+ * to inspect the product registry. Any component which has an
+ * attribute 'pkgs' will list those packages which cannot be removed
+ * safely. For example: 'pkgs= SUNWfoo SUNWbar' would imply that
+ * neither SUNWfoo or SUNWbar can be removed.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+
+#include "wsreg_pkgrm.h"
+
+struct dstrp {
+ char **ppc;
+ int len;
+ int max;
+};
+
+static int append_dstrp(struct dstrp *pd, const char *str);
+static int in_list(const char *pcList, const char *pcItem);
+static void get_all_dependents_r(struct dstrp *, struct dstrp *,
+ Wsreg_component *, int *, const char *);
+static char *get_locale();
+
+/*
+ * wsreg_pkgrm_check
+ *
+ * This routine determines if removing a particular package will
+ * 'damage' a product.
+ *
+ * pcRoot IN: The alternate root directory. If this parameter
+ * is NULL - then the root "/" is assumed.
+ *
+ * pcPKG IN: The name of the package to remove (a normal NULL-
+ * terminated string.)
+ * This parameter must not be NULL.
+ *
+ * pppcID OUT: The location of a char ** pointer is passed in.
+ * This parameter must not be NULL. The result
+ * will be a NULL terminated array of ID strings.
+ * The caller must free both the array of strings
+ * and each individual string. Example:
+ *
+ * char ** ppcID;
+ * int i;
+ *
+ * if (wsreg_pkgrm_check(NULL, "SUNWblah", &ppcID, ..)
+ * > 0) {
+ *
+ * for (i = 0; ppcID[i]; i++) {
+ * do_something(ppcID[i]);
+ * free(ppcID[i]);
+ * }
+ * free(ppcID);
+ * }
+ *
+ * pppcName OUT: As pppcID, except this contains the human readable
+ * localized name of the component. The index of the
+ * name array coincides with that of the ID array, so
+ * there will be the same number of items in both and
+ * the component whose name is *pppcName[0] has the
+ * id *pppcID[0].
+ *
+ * Returns: 0 if there is no problem. pkgrm my proceed.
+ * positive - there is a conflict. pppcID & pppcName return strings.
+ * negative - there was a problem running this function.
+ * Error conditions include: (errno will be set)
+ * ENOENT The pcRoot directory was not valid.
+ * ENOMEM The string to return could not be allocated.
+ * EACCES The registry database could not be read.
+ *
+ * Side effects: The pppcID and pppcName parameters may be changed and set
+ * to the value of arrays of strings which the caller must free.
+ */
+int
+wsreg_pkgrm_check(const char *pcRoot, const char *pcPKG,
+ char ***pppcID, char ***pppcName)
+{
+ Wsreg_component **ppws;
+ struct dstrp id = { NULL, 0, 0}, nm = {NULL, 0, 0};
+ int i, r;
+ char *locale = get_locale();
+ if (locale == NULL)
+ locale = "en";
+
+ if (locale == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ assert(pcPKG != NULL && pppcName != NULL && pppcID != NULL);
+
+ *pppcID = NULL;
+ *pppcName = NULL;
+
+ errno = 0;
+ r = 0; /* A return value 0 indicates nothing was found. */
+
+ if (pcRoot == NULL)
+ pcRoot = "/";
+
+ if (wsreg_initialize(WSREG_INIT_NORMAL, pcRoot) != WSREG_SUCCESS ||
+ wsreg_can_access_registry(O_RDONLY) == 0) {
+ errno = EACCES;
+ return (-1);
+ }
+
+ ppws = wsreg_get_all();
+
+ for (i = 0; ((ppws != NULL) && (ppws[i] != NULL)); i++) {
+ char *pcpkgs = wsreg_get_data(ppws[i], "pkgs");
+ if (pcpkgs != NULL && in_list(pcpkgs, pcPKG)) {
+ char *pcID = wsreg_get_id(ppws[i]);
+ char *pcName = wsreg_get_display_name(ppws[i],
+ locale);
+ int depth;
+
+ depth = 0;
+ r = 1;
+
+ if (append_dstrp(&id, pcID) ||
+ append_dstrp(&nm, pcName)) {
+ errno = ENOMEM;
+ r = -1;
+ break;
+ }
+
+ if (pcID) free(pcID);
+ if (pcName) free(pcName);
+ get_all_dependents_r(&id, &nm, ppws[i], &depth, locale);
+ }
+ }
+
+ if (r > 0) {
+ *pppcID = id.ppc;
+ *pppcName = nm.ppc;
+ }
+
+ free(locale);
+
+ if (ppws != NULL)
+ wsreg_free_component_array(ppws);
+
+ return (r);
+}
+
+/*
+ * in_list
+ *
+ * pcList A white space delimited list of words (non-white characters)
+ * pcItem A word (not NULL, an empty string or containing white space)
+ *
+ * Returns 0 if pcItem is not in pcList. nonzero if pcItem is in pcList
+ * Side effects: None
+ */
+static int
+in_list(const char *pcList, const char *pcItem)
+{
+
+ int i = 0, j = 0, k = 0;
+
+ assert(pcItem);
+ k = strlen(pcItem);
+
+ if (pcList == NULL || k == 0)
+ return (0);
+
+ while (pcList[i] != '\0') {
+
+ if (isspace(pcList[i])) {
+ if (i == j) {
+ i++;
+ j++;
+ } else {
+
+ if ((i - j) == k &&
+ strncmp(&pcList[j], pcItem, i - j) == 0) {
+ return (1);
+ } else {
+ j = i;
+ }
+
+ }
+ } else {
+ i++;
+ }
+
+ /* last element in the list case */
+ if (pcList[i] == '\0' && j < i &&
+ strncmp(&pcList[j], pcItem, i - j) == 0)
+ return (1);
+ }
+
+ return (0);
+}
+
+#define APPEND_INCR 20
+
+/*
+ * append_dstrp
+ *
+ * This routine manages a dynamic array of strings in a very minimal way.
+ * It assumes it has been passed a cleared struct dstrp = { NULL, 0, 0 }
+ * It will add the appended string to the end of the array. When needed,
+ * the array of strings is grown to the next APPEND_INCR in size.
+ *
+ * Note this routine is different than append_dstr since that accumulates
+ * char, this accumulates char *.
+ *
+ * pd The dynamic string. Must be initialized to {NULL,0,0}. Must not
+ * be NULL.
+ *
+ * str The string to add. May be of 0 length. If NULL, a string of 0
+ * length will be added (NOT a NULL).
+ *
+ * Returns: 0 if OK, -1 if malloc failed.
+ * Side effects: The value of pd->ppc[pd->len] changes, taking strdup(str)
+ * The final entry in the array will be NULL. There will be pd->len
+ * entries. To free this, free each string in the array and the array
+ * itself. The caller must free the allocated memory.
+ */
+static int
+append_dstrp(struct dstrp *pd, const char *str)
+{
+ if (str == NULL) str = "";
+
+ if (pd->max == 0) {
+
+ /* Initialize if necessary */
+ pd->len = 0;
+ pd->max = APPEND_INCR;
+ pd->ppc = (char **)calloc(APPEND_INCR * sizeof (char *), 1);
+ if (pd->ppc == NULL)
+ return (-1);
+
+ } else if ((pd->len + 2) == pd->max) {
+
+ /*
+ * Grow the array.
+ * Always leave room for a single NULL end item: That is
+ * why we grow when +2 equals the max, not +1.
+ */
+ size_t s = (pd->max + APPEND_INCR) * sizeof (char *);
+ pd->ppc = realloc(pd->ppc, s);
+ if (pd->ppc == NULL) {
+ return (-1);
+ } else {
+ memset(pd->ppc + pd->max, '\0',
+ APPEND_INCR * sizeof (char *));
+ }
+
+ pd->max += APPEND_INCR;
+ }
+
+ if (str == NULL) {
+ pd->ppc[pd->len] = NULL;
+ pd->len++;
+ } else {
+ pd->ppc[pd->len] = (char *)strdup(str);
+ if (pd->ppc[pd->len] == NULL)
+ return (-1);
+ pd->len++;
+ }
+
+ return (0);
+}
+
+#define DEPTH_MAX 100
+
+/*
+ * get_all_dependents_r
+ *
+ * This routine accumulates the id and name of all components which
+ * depend (directly or indirectly) on a component which has a pkg which
+ * may be removed. By calling this routine recursively, the entire list
+ * of existing dependencies can be accumulated.
+ *
+ * id The dynamic accumulation of all ids of dependent components.
+ * nm The dynamic accumulation of all names of dep. components.
+ * pws The component to check for dependencies, record their
+ * ids and names, then call check these components for redun-
+ * dancy also.
+ * pdepth The depth of the recursion. This must be set to 0 upon the
+ * first call to this function. Only DEPTH_MAX calls will be
+ * attempted.
+ * locale The locale to use for querying for display names.
+ *
+ * Return value: None.
+ * Side effects. strings will be added to id and nm. The depth counter
+ * will increase.
+ */
+static void
+get_all_dependents_r(struct dstrp *id, struct dstrp *nm, Wsreg_component *pws,
+ int *pdepth, const char *locale)
+{
+ int i;
+
+ /* Get the list of dependent components. */
+ Wsreg_component **ppws = wsreg_get_dependent_components(pws);
+ if (ppws == NULL)
+ return;
+
+ if (locale == NULL)
+ locale = "en";
+ if (locale == NULL)
+ return;
+
+ /*
+ * Prevent infinite loops in the case where there is a cycle
+ * in the dependency graph. Such a cycle should never happen,
+ * but a clueless user of the libwsreg API could construct such
+ * a failure case. This is defensive programming.
+ */
+ if (*pdepth > DEPTH_MAX)
+ return;
+
+ (*pdepth)++;
+
+ for (i = 0; ppws[i]; i++) {
+ char *pcID = wsreg_get_id(ppws[i]);
+ char *pcName = wsreg_get_display_name(ppws[i], locale);
+ if (append_dstrp(id, pcID) ||
+ append_dstrp(nm, pcName))
+ /*
+ * Errors in append_dstrp happen only due to malloc
+ * failing on small allocations. If we fail here
+ * this is the least of the user's problems. We
+ * can just stop accumulating new info at this point.
+ */
+ return;
+ get_all_dependents_r(id, nm, ppws[i], pdepth, locale);
+ }
+
+ wsreg_free_component_array(ppws);
+}
+
+/*
+ * init_locale
+ *
+ * Set locale and textdomain for localization. Note that the return value
+ * of setlocale is the locale string. It is in the form
+ *
+ * "/" LC_CTYPE "/" LC_COLLATE "/" LC_CTIME "/" LC_NUMERIC "/"
+ * LC_MONETARY "/ LC_MESSAGES
+ *
+ * This routine parses this result line to determine the value of
+ * the LC_MESSAGES field. If it is "C", the default language "en"
+ * is selected. If not, the string is disected to get only the
+ * ISO 639 two letter tag: "en_US.ISO8859-1" becomes "en".
+ *
+ * Returns: Returns a newly allocated language tag string.
+ * Returns NULL if setlocale() returns a null pointer.
+ * Side effects:
+ * (1) setlocale changes behavior of the application.
+ */
+static char *
+get_locale()
+{
+ int i = 0, c, n;
+ char lang[32];
+ char *pc = setlocale(LC_ALL, "");
+ char *tag = NULL;
+
+ if (pc == NULL) {
+ return (NULL);
+ }
+
+ (void *) memset(lang, 0, 32);
+ if (pc[0] == '/') {
+
+ /* Skip to the 6th field, which is 'LC_MESSAGES.' */
+ c = 0;
+ for (i = 0; (pc[i] != NULL) && (c < 6); i++) {
+ if (pc[i] == '/') c++;
+ }
+
+ /* Strip off any dialect tag and character encoding. */
+ n = 0;
+ while ((pc[i] != NULL) && (pc[i] != '_') &&
+ (n < 32) && (pc[i] != '.')) {
+ lang[n++] = pc[i++];
+ }
+ }
+
+ if (i > 2) {
+ if (strcmp(lang, "C") == 0) {
+ tag = strdup("en");
+ } else {
+ tag = strdup(lang);
+ }
+ } else {
+ tag = strdup("en");
+ }
+
+ return (tag);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.h b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.h
new file mode 100644
index 0000000000..ee536ea536
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.h
@@ -0,0 +1,45 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef __WSREG_PKGRM__
+#define __WSREG_PKGRM__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <wsreg.h>
+#include <fcntl.h>
+
+int wsreg_pkgrm_check(const char *, const char *, char ***, char ***);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WSREG_PKGRM__ */
diff --git a/usr/src/cmd/svr4pkg/pkgrm/Makefile b/usr/src/cmd/svr4pkg/pkgrm/Makefile
new file mode 100644
index 0000000000..3689ed74f9
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/Makefile
@@ -0,0 +1,44 @@
+#
+# 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= pkgrm
+
+OBJS= check.o \
+ main.o \
+ presvr4.o \
+ quit.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgrm/check.c b/usr/src/cmd/svr4pkg/pkgrm/check.c
new file mode 100644
index 0000000000..8aee1dbc74
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/check.c
@@ -0,0 +1,622 @@
+/*
+ * 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 <utmpx.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <assert.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+#include <instzones_api.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_depsonme = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prenci = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereq = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckdepend = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckpriv = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckrunlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL};
+
+/*
+ * each one of these represents a localized message for a single kind
+ * of dependency check
+ */
+
+static char *IMSG_PKGRMCHK_CKRUNLVL = (char *)NULL;
+static char *IMSG_PKGRMCHK_DEPEND = (char *)NULL;
+static char *IMSG_PKGRMCHK_DEPSONME = (char *)NULL;
+static char *IMSG_PKGRMCHK_PRENCI = (char *)NULL;
+static char *IMSG_PKGRMCHK_PREREQ = (char *)NULL;
+static char *IMSG_PKGRMCHK_PRIV = (char *)NULL;
+static char *IMSG_PKGRMCHK_RUNLEVEL = (char *)NULL;
+
+/*
+ * each one of these represents a function to handle a single kind of
+ * dependency check
+ */
+
+static int rckdepend(char *a_msg, char *a_pkg);
+static int rckdepsonme(char *a_msg, char *a_pkg);
+static int rckprenci(char *a_msg, char *a_pkg);
+static int rckprereq(char *a_msg, char *a_pkg);
+static int rckpriv(char *a_msg, char *a_pkg);
+static int rckrunlevel(char *a_msg, char *a_pkg);
+
+static depckl_t DEPCKL[] = {
+ /*
+ * Message hierarchy:
+ * -- runlevel=%s
+ * --- rckrunlevel=%d
+ * --- rckpriv=%d ****
+ * -- incompat=%s
+ * -- prerequisite-incomplete=%s
+ * -- dependonme=%s
+ * -- dependsonme=%s:%s
+ * -- prerequisite-installed=%s
+ * ---rckdepend=%d ****
+ */
+
+ /* name, ignore_values, err_msg, depcklFunc, recrd */
+ /*
+ * 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
+ */
+
+ { "dependsonme=", NULL, &IMSG_PKGRMCHK_DEPSONME,
+ &rckdepsonme, &er_depsonme
+ },
+ { "dependonme=", NULL, &IMSG_PKGRMCHK_DEPSONME,
+ &rckdepsonme, &er_depsonme
+ },
+ { "prerequisite-incomplete=", NULL, &IMSG_PKGRMCHK_PRENCI,
+ &rckprenci, &er_prenci
+ },
+ { "prerequisite-installed=", NULL, &IMSG_PKGRMCHK_PREREQ,
+ &rckprereq, &er_prereq
+ },
+ { "runlevel=", NULL, &IMSG_PKGRMCHK_RUNLEVEL,
+ NULL, &er_runlevel
+ },
+
+ /*
+ * 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)
+ */
+
+ { "rckdepend=", "0", &IMSG_PKGRMCHK_DEPEND,
+ &rckdepend, &er_rckdepend
+ },
+ { "rckpriv=", "0", &IMSG_PKGRMCHK_PRIV,
+ &rckpriv, &er_rckpriv
+ },
+ { "rckrunlevel=", "0", &IMSG_PKGRMCHK_CKRUNLVL,
+ &rckrunlevel, &er_rckrunlevel
+ },
+
+ /*
+ * same as above BUT no check to ignore is done; message always reported
+ */
+
+ { NULL, NULL, NULL,
+ NULL, NULL
+ }
+};
+
+/*
+ * Name: preremove_verify
+ * Description: verify results of preremoval 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 preremoval dependency
+ * check data are located
+ * Returns: int
+ * == 0 - continue processing
+ * != 0 - do not continue processing
+ */
+
+int
+preremove_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_PRERVFY_ENTRY);
+
+ /*
+ * localize messages
+ */
+
+ IMSG_PKGRMCHK_DEPSONME = MSG_PKGRMCHK_DEPSONME;
+ IMSG_PKGRMCHK_PRENCI = MSG_PKGRMCHK_PRENCI;
+ IMSG_PKGRMCHK_PREREQ = MSG_PKGRMCHK_PREREQ;
+ IMSG_PKGRMCHK_RUNLEVEL = MSG_PKGRMCHK_RUNLEVEL;
+ IMSG_PKGRMCHK_DEPEND = MSG_PKGRMCHK_DEPEND;
+ IMSG_PKGRMCHK_PRIV = MSG_PKGRMCHK_PRIV;
+ IMSG_PKGRMCHK_CKRUNLVL = MSG_PKGRMCHK_CKRUNLVL;
+
+ /*
+ * outer loop - process each package first
+ */
+
+ for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) {
+
+ char *zoneName;
+ int zoneIndex;
+
+ /*
+ * inner loop - for each package process each zone second
+ */
+
+ if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
+ continue;
+ }
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+ (char *)NULL; zoneIndex++) {
+
+ FILE *fp;
+ char line[PATH_MAX+1];
+ char preremovecheckPath[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 preremove check data */
+
+ len = snprintf(preremovecheckPath,
+ sizeof (preremovecheckPath),
+ "%s/%s.%s.preremovecheck.txt",
+ a_zoneTempDir, pkginst,
+ z_zlist_get_scratch(a_zlst, zoneIndex));
+
+ if (len > sizeof (preremovecheckPath)) {
+ progerr(ERR_CREATE_PATH_3, a_zoneTempDir,
+ pkginst, zoneName);
+ continue;
+ }
+
+ /* error if preremove check data path is not a file */
+
+ if (isfile((char *)NULL, preremovecheckPath) != 0) {
+ echoDebug(DBG_PRERVFY_NOFILE, pkginst, zoneName,
+ preremovecheckPath, strerror(errno));
+ progerr(ERR_PRERVFY_NOFILE, pkginst, zoneName);
+ continue;
+ }
+
+ /* open the preremove check data file */
+
+ fp = fopen(preremovecheckPath, "r");
+ if (fp == (FILE *)NULL) {
+ progerr(ERR_PRERVFY_OPEN_FILE,
+ preremovecheckPath, pkginst, zoneName,
+ strerror(errno));
+ continue;
+ }
+
+ /* read and process each preremove check data line */
+
+ while (fgets(line, sizeof (line), fp) != (char *)NULL) {
+ int len;
+ int j;
+
+ /* 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_PRERVFY_SCAN, line, pkginst,
+ zoneName);
+
+ /* ignore line if not found */
+
+ if (DEPCKL[j].name == (char *)NULL) {
+ progerr(ERR_PRERVFY_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 preremove 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_PRERVFY_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_PRERVFY_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_PRERVFY_GETYORN_QUIT, a_pkg);
+ return (4);
+ }
+
+ /* return "5 (administration interaction required)" if -n */
+
+ if (echoGetFlag() == B_FALSE) {
+ ptext(stderr, MSG_PRERVFY_GETYORN_SUSP, a_pkg);
+ echoDebug(DBG_PRERVFY_GETYORN_QUIT_USER, a_pkg);
+ return (5);
+ }
+
+ /* prepare question to ask "continue with removal of pkg <xxx>?" */
+
+ (void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_PKGRMCHK_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_PRERVFY_GETYORN_TERM, a_pkg);
+ echoDebug(DBG_PRERVFY_GETYORN_CKYORN, a_pkg, n);
+ return (n);
+ }
+
+ /* return "3 (interruption) if not "y" or "Y" */
+
+ if (strchr("yY", *ans) == NULL) {
+ ptext(stderr, MSG_PRERVFY_GETYORN_TERM_USER, a_pkg);
+ echoDebug(DBG_PRERVFY_GETYORN_NOT_Y, a_pkg, ans);
+ return (3);
+ }
+
+ /* return "0 - success" */
+
+ echoDebug(DBG_PRERVFY_GETYORN_SUCCESS, a_pkg);
+
+ return (0);
+}
+
+/*
+ * Trigger: dependsonme=<<package>>
+ * Sequence: - one or more: dependsonme=<<package>>
+ * - one: rckdepend=<<n>>
+ * Actions: Output message if "rdepend!=nocheck"
+ * Return 0
+ * Terminate when 'rckdepend' processed
+ */
+
+static int
+rckdepsonme(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKDEPSONME, a_pkg, a_msg);
+
+ if (!(ADM(rdepend, "nocheck"))) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ return (0);
+}
+
+/*
+ * Trigger: prerequisite-incomplete=<<package>>
+ * Sequence: - one or more: prerequisite-incomplete=<<package>>
+ * - one: rckdepend=<<n>>
+ * Actions: Output message if "rdepend!=nocheck"
+ * Return 0
+ * Terminate when 'rckdepend' processed
+ */
+
+static int
+rckprenci(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKPRENCI, a_pkg, a_msg);
+
+ if (!(ADM(rdepend, "nocheck"))) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ return (0);
+}
+
+/*
+ * Trigger: prerequisite-installed=<<package>>
+ * Sequence: - one or more: prerequisite-installed=<<package>>
+ * - one: rckdepend=<<n>>
+ * Actions: Output message if "rdepend!=nocheck"
+ * Return 0
+ * Terminate when 'rckdepend' processed
+ */
+
+static int
+rckprereq(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKPREREQ, a_pkg, a_msg);
+
+ if (!(ADM(rdepend, "nocheck"))) {
+ ptext(stderr, "%s", a_msg);
+ }
+
+ return (0);
+}
+
+/*
+ * 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
+rckrunlevel(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKRUNLEVEL, a_pkg, a_msg);
+ /*
+ * For now, we are ignoring runlevel removal issues within
+ * non-global zones. This is questionable, but the RSTATES
+ * feature is rarely used and known uses within Solaris are
+ * effectively no-ops as of this time
+ */
+ return (0);
+}
+
+/*
+ * Trigger: rckdepend=<<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
+rckdepend(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKDEPEND, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(rdepend, "nocheck"),
+ ADM(rdepend, "quit"), HLP_PKGRMCHK_DEPEND,
+ ERR_PKGRMCHK_DEPFAILED));
+}
+
+/*
+ * Trigger: rckpriv=<<n>>
+ * Sequence: - one: rckpriv=<<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
+rckpriv(char *a_msg, char *a_pkg)
+{
+ echoDebug(DBG_PRERVFY_RCKPRIV, a_pkg, a_msg);
+
+ return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"),
+ ADM(action, "quit"), HLP_PKGRMCHK_PRIV,
+ ERR_PKGRMCHK_PRIVFAILED));
+}
diff --git a/usr/src/cmd/svr4pkg/pkgrm/main.c b/usr/src/cmd/svr4pkg/pkgrm/main.c
new file mode 100644
index 0000000000..92cb448251
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/main.c
@@ -0,0 +1,3075 @@
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include <assert.h>
+
+/*
+ * libinstzones includes
+ */
+
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * pkgrm local includes
+ */
+
+#include "quit.h"
+
+/*
+ * 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 presvr4.c */
+int started = 0;
+char *tmpdir = NULL; /* location to place temporary files */
+
+/* imported by various (many) */
+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 *pkginst = NULL; /* current pkg/src instance 2 process */
+static char *vfstab_file = NULL;
+static char *zoneTempDir = (char *)NULL;
+
+/* set by ckreturn() */
+
+static int interrupted = 0; /* last pkg op was quit (1,2,3,4,5) */
+
+static int nointeract = 0; /* non-zero - no user interaction */
+static int pkgrmremote = 0; /* remove pkg objs stored remotely */
+static int pkgverbose = 0; /* non-zero if verbose mode selected */
+
+/*
+ * Assume the package complies with the standards as regards user
+ * interaction during procedure scripts.
+ */
+
+static int old_pkg = 0;
+static int old_symlinks = 0;
+static int no_map_client = 0;
+
+/* 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;
+
+/*
+ * imported (external) functions
+ */
+
+/* presvr4.c */
+
+extern int presvr4(char *pkg, int a_nointeract);
+
+/* check.c */
+
+extern int preremove_verify(char **a_pkgList, zoneList_t a_zlst,
+ char *a_zoneTempDir);
+/* quit.c */
+
+extern void quitSetZonelist(zoneList_t a_zlst);
+
+/*
+ * imported (external) variables
+ */
+
+extern char *pkgdir;
+
+/* printable string - if string is null results in ??? */
+
+#define PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
+
+#define MAX_FDS 20
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+#define INHERITFS "inherited-filesystem="
+#define INHERITFS_LEN ((sizeof (INHERITFS))-1)
+
+/*
+ * forward declarations
+ */
+
+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 int doRemove(int a_nodelete, char *a_altBinDir,
+ int a_longestPkg, char *a_adminFile,
+ char *a_zoneAdminFile, zoneList_t zlst);
+static int pkgRemove(int a_nodelete, char *a_altBinDir,
+ char *a_adminFile, char **a_inheritedPkgDirs);
+static int pkgZoneCheckRemove(char *a_zoneName,
+ char **a_inheritedPkgDirs, char *a_altBinDir,
+ char *a_adminFile, char *a_stdoutPath,
+ zone_state_t a_zoneState);
+static int pkgZoneRemove(char *a_zoneName,
+ char **a_inheritedPkgDirs, int a_nodelete,
+ char *a_altBinDir, char *a_adminFile,
+ zone_state_t a_zoneState);
+static void resetreturn();
+static void usage(void);
+static boolean_t check_applicability(char *a_packageDir,
+ char *a_pkgInst, char *a_rootPath,
+ CAF_T a_flags);
+static boolean_t check_packages(char **a_pkgList, char *a_packageDir);
+static boolean_t path_valid(char *path);
+static boolean_t remove_packages(char **a_pkgList, int a_nodelete,
+ int a_longestPkg, int a_repeat,
+ char *a_altBinDir, char *a_pkgdir,
+ char *a_spoolDir, boolean_t a_noZones);
+static boolean_t remove_packages_from_spool_directory(char **a_pkgList,
+ int a_nodelete, int a_longestPkg, int a_repeat,
+ char *a_altBinDir);
+static boolean_t remove_packages_in_global_no_zones(char **a_pkgList,
+ int a_nodelete, int a_longestPkg, int a_repeat,
+ char *a_altBinDir);
+static boolean_t remove_packages_in_global_with_zones(char **a_pkgList,
+ int a_nodelete, int a_longestPkg, int a_repeat,
+ char *a_altBinDir, char *a_pkgdir,
+ zoneList_t a_zlst);
+static boolean_t remove_packages_in_nonglobal_zone(char **a_pkgList,
+ int a_nodelete, int a_longestPkg, int a_repeat,
+ char *a_altBinDir, char *a_pkgdir);
+static boolean_t shall_we_continue(char *a_pkgInst, int a_npkgs);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: main
+ * Description: main entry point for pkgrm
+ * Returns: int
+ * 0 Successful completion
+ * 1 Fatal error.
+ * 2 Warning.
+ * 3 Interruption.
+ * 4 Administration.
+ * 5 Administration. Interaction is required. Do not use pkgrm -n.
+ * 10 Reboot after removal of all packages.
+ * 20 Reboot after removal of this package.
+ */
+
+int
+main(int argc, char **argv)
+{
+ char **category = NULL;
+ char *altBinDir = (char *)NULL;
+ char *catg_arg = NULL;
+ char *p;
+ char *prog_full_name = NULL;
+ char *spoolDir = 0;
+ int c;
+ int longestPkg = 0;
+ int n;
+ int nodelete = 0; /* dont rm files/run scripts */
+ int pkgLgth = 0;
+ int repeat;
+ struct sigaction nact;
+ struct sigaction oact;
+
+ /* 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);
+
+ /* tell quit which ckreturn function to call */
+
+ quitSetCkreturnFunc(&ckreturn);
+
+ /* 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);
+ }
+
+ 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:FMnO:R:s:V:vY:Z")) != EOF) {
+ switch (c) {
+ /*
+ * Public interface: Allow admin to remove objects
+ * from a service area via a reference client.
+ * Remove the package files from the client's file
+ * system, absolutely. If a file is shared with other
+ * packages, the default behavior is to not remove
+ * the file from the client's file system.
+ */
+ case 'A':
+ pkgrmremote++;
+ break;
+
+ /*
+ * Public interface: Use the installation
+ * administration file, admin, in place of the
+ * default admin file. pkgrm first looks in the
+ * current working directory for the administration
+ * file. If the specified administration file is not
+ * in the current working directory, pkgrm looks in
+ * the /var/sadm/install/admin directory for the
+ * administra- tion file.
+ */
+ case 'a':
+ admnfile = flex_device(optarg, 0);
+ break;
+
+ /*
+ * Not a public interface: location where package executables
+ * can be found - default is /usr/sadm/install/bin.
+ */
+ case 'b':
+ 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: pass -F option to
+ * pkgremove which suppresses the removal of any
+ * files and any class action scripts, and suppresses
+ * the running of any class action scripts. The
+ * package files remain but the package looks like it
+ * is not installed. This is mainly for use by the
+ * upgrade process.
+ */
+ case 'F':
+ nodelete++;
+ break;
+
+ /*
+ * Public interface: Instruct pkgrm 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;
+
+ /*
+ * Public interface: package removal occurs in
+ * non-interactive mode. Suppress output of the list of
+ * removed files. The default mode is interactive.
+ */
+ case 'n':
+ nointeract++;
+ (void) echoSetFlag(B_FALSE);
+ break;
+
+ /*
+ * Not a public interface: the -O option allows the behavior
+ * of the package tools to be modified. Recognized options:
+ * -> debug
+ * ---> enable debugging output
+ * -> 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 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
+ * -> zonelist="<names...>"
+ * ---> add package to space-separated list of zones only
+ */
+
+ case 'O':
+ for (p = strtok(optarg, ","); p != (char *)NULL;
+ p = strtok(NULL, ",")) {
+
+ if (strcmp(p, "nozones") == 0) {
+ noZones = 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,
+ "enable-hollow-package-support") == 0) {
+ set_depend_pkginfo_DB(B_TRUE);
+ continue;
+ }
+
+ 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 (strncmp(p, "zonelist=", 9) == 0) {
+ if (z_set_zone_spec(p + 9) == -1)
+ quit(1);
+ usedZoneList = B_TRUE;
+ continue;
+ }
+
+ /* -O option not recognized - issue warning */
+
+ progerr(ERR_INVALID_O_OPTION, p);
+ continue;
+ }
+ break;
+
+ /*
+ * Public interface: defines 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.
+ */
+ case 'R':
+ if (!set_inst_root(optarg)) {
+ progerr(ERR_ROOT_CMD);
+ exit(1);
+ }
+ break;
+
+ /*
+ * Public interface: remove the specified package(s)
+ * from the directory spool. The default directory
+ * for spooled packages is /var/sadm/pkg.
+ */
+ case 's':
+ spoolDir = flex_device(optarg, 1);
+ break;
+
+ /*
+ * Public interface: Allow admin to establish the client
+ * filesystem using a 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 pkgrm, located in the
+ * pkginst/install directory. This option is used for
+ * debugging the procedural and non- procedural
+ * scripts.
+ */
+ case 'v':
+ pkgverbose++;
+ break;
+
+ /*
+ * Public interface: remove packages based on the
+ * CATEGORY variable from the installed/spooled
+ * pkginfo file
+ */
+ case 'Y':
+ catg_arg = strdup(optarg);
+
+ if ((category = get_categories(catg_arg)) == NULL) {
+ progerr(ERR_CAT_INV, catg_arg);
+ exit(1);
+ } else if (is_not_valid_category(category,
+ get_prog_name())) {
+ progerr(ERR_CAT_SYS);
+ exit(1);
+ } else if (is_not_valid_length(category)) {
+ progerr(ERR_CAT_LNGTH);
+ exit(1);
+ }
+
+ break;
+
+ /*
+ * unrecognized option
+ */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ /*
+ * ********************************************************************
+ * 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());
+ }
+
+ /* -s cannot be used with several */
+
+ if (spoolDir != (char *)NULL) {
+ if (admnfile != (char *)NULL) {
+ progerr(ERR_SPOOLDIR_AND_ADMNFILE);
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (pkgrmremote != 0) {
+ progerr(ERR_SPOOLDIR_AND_PKGRMREMOTE);
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (pkgverbose != 0) {
+ progerr(ERR_SPOOLDIR_AND_PKGVERBOSE);
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (is_an_inst_root() != 0) {
+ progerr(ERR_SPOOLDIR_AND_INST_ROOT);
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ /* -V cannot be used with -A */
+
+ if (no_map_client && pkgrmremote) {
+ progerr(ERR_V_USED_AND_PKGRMREMOTE);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /* -n used without pkg names or category */
+
+ if (nointeract && (optind == argc) && (catg_arg == NULL)) {
+ progerr(ERR_BAD_N_PKGRM);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /* Error if specified zone list isn't valid on target */
+ if (usedZoneList && z_verify_zone_spec() == -1)
+ usage();
+
+ /*
+ * 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);
+
+ /* establish temporary directory to use */
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL) {
+ tmpdir = P_tmpdir;
+ }
+
+ echoDebug(DBG_PKGRM_TMPDIR, tmpdir);
+
+ /* initialize path parameters */
+
+ set_PKGpaths(get_inst_root());
+
+ /*
+ * initialize installation admin parameters - if removing from a spool
+ * directory then the admin file is ignore.
+ */
+
+ if (spoolDir == NULL) {
+ echoDebug(DBG_PKGRM_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 removed in non-global zones
+ * when removed directly in the global zone by the global zone admin.
+ */
+
+ if (is_depend_pkginfo_DB()) {
+ echoDebug(DBG_PKGRM_HOLLOW_ENABLED);
+ } else if ((z_running_in_global_zone() == B_TRUE) &&
+ (z_non_global_zones_exist() == B_TRUE)) {
+ echoDebug(DBG_PKGRM_ENABLING_HOLLOW);
+ set_depend_pkginfo_DB(B_TRUE);
+ }
+
+ /*
+ * 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;
+ }
+
+ if (devtype((spoolDir ? spoolDir : get_PKGLOC()), &pkgdev) ||
+ pkgdev.dirname == NULL) {
+ progerr(ERR_BAD_DEVICE, spoolDir ? spoolDir : get_PKGLOC());
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ pkgdir = pkgdev.dirname;
+ repeat = ((optind >= argc) && pkgdev.mount);
+
+ /*
+ * 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_PKGRM);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * ********************************************************************
+ * main package processing "loop"
+ * ********************************************************************
+ */
+
+ for (;;) {
+ boolean_t b;
+ char **pkglist; /* points to array of pkgs */
+
+ /*
+ * mount the spool device if required
+ */
+
+ if (pkgdev.mount) {
+ if (n = pkgmount(&pkgdev, NULL, 0, 0, 1)) {
+ quit(n);
+ /* NOTREACHED */
+ }
+ }
+
+ if (chdir(pkgdev.dirname)) {
+ progerr(ERR_CHDIR, pkgdev.dirname);
+ quit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * spool device mounted/available - get the list of the
+ * packages to remove
+ */
+
+ n = pkgGetPackageList(&pkglist, argv, optind,
+ catg_arg, category, &pkgdev);
+
+ switch (n) {
+ case -1: /* no packages found */
+ echoDebug(DBG_PKGLIST_RM_NONFOUND,
+ PSTR(pkgdev.dirname));
+ progerr(ERR_NOPKGS, pkgdev.dirname);
+ quit(1);
+ /* NOTREACHED */
+
+ case 0: /* packages found */
+ break;
+
+ default: /* "quit" error */
+ echoDebug(DBG_PKGLIST_RM_ERROR,
+ pkgdev.dirname, n);
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ /*
+ * count the number of packages to remove
+ * 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 */) {
+ pkgLgth = strlen(pkglist[npkgs]);
+ if (pkgLgth > longestPkg) {
+ longestPkg = pkgLgth;
+ }
+ echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
+ npkgs++;
+ }
+
+ /* output number of packages to be removed */
+
+ echoDebug(DBG_NUM_PKGS_TO_REMOVE, npkgs, longestPkg);
+
+ /*
+ * package list generated - remove packages
+ */
+
+ b = remove_packages(pkglist, nodelete, longestPkg, repeat,
+ altBinDir, pkgdev.dirname, spoolDir, noZones);
+
+ /*
+ * unmount the spool directory if necessary
+ */
+
+ if (pkgdev.mount) {
+ (void) chdir("/");
+ if (pkgumount(&pkgdev)) {
+ progerr(ERR_PKGUNMOUNT, pkgdev.bdevice);
+ quit(99);
+ /* NOTREACHED */
+
+ }
+ }
+
+ /*
+ * continue with next sequence of packages if continue set
+ */
+
+ if (b == B_TRUE) {
+ continue;
+ }
+
+ /*
+ * not continuing - quit with 0 exit code
+ */
+
+ quit(0);
+ /* NOTREACHED */
+#ifdef lint
+ return (0);
+#endif /* lint */
+ }
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name: doRemove
+ * Description: Remove a package from the global zone, and optionally from one
+ * or more non-global zones.
+ * Arguments: a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_adminFile - pointer to string representing the admin
+ * file to pass to pkgremove when removing a package from
+ * the global zone only. Typically the admin file used for
+ * the global zone is the admin file passed in by the user.
+ * If this is == NULL no admin file is given to pkgremove.
+ * a_zoneAdminFile - pointer to string representing the admin
+ * file to pass to pkgremove when removing the package
+ * from a non-global zone only. Typically the admin file
+ * used for non-global zones supresses all checks since
+ * the dependency checking is done for all zones first
+ * before proceeding.
+ * A zoneAdminFile MUST be specified if a_zlst != NULL.
+ * A zoneAdminFile must NOT be specified if a_zlst == NULL.
+ * a_zlst - list of zones to process; NULL if no zones to process.
+ * 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
+doRemove(int a_nodelete, char *a_altBinDir, int a_longestPkg, char *a_adminFile,
+ char *a_zoneAdminFile, zoneList_t a_zlst)
+{
+ boolean_t b;
+ char **inheritedPkgDirs;
+ char *zoneName;
+ char ans[MAX_INPUT];
+ int n;
+ int zoneIndex;
+ int zonesSkipped;
+ struct pkginfo *pinfo = (struct pkginfo *)NULL;
+ zone_state_t zst;
+
+ /* entry assertions */
+
+ if (a_zlst != (zoneList_t)NULL) {
+ /* zone list specified - zone admin file required */
+ assert(a_zoneAdminFile != (char *)NULL);
+ assert(*a_zoneAdminFile != '\0');
+ } else {
+ /* no zone list specified - no zone admin file needed */
+ assert(a_zoneAdminFile == (char *)NULL);
+ }
+
+ /* NOTE: required 'pkgdir' set to spool directory or NULL */
+ b = pkginfoIsPkgInstalled(&pinfo, pkginst);
+ if (b == B_FALSE) {
+ progerr(ERR_NO_SUCH_INSTANCE, pkginst);
+ pkginfoFree(&pinfo);
+ return (2);
+ }
+
+ /* entry debugging info */
+
+ echoDebug(DBG_DOREMOVE_ENTRY);
+ echoDebug(DBG_DOREMOVE_ARGS, PSTR(pinfo->pkginst), PSTR(pinfo->name),
+ PSTR(pinfo->arch), PSTR(pinfo->version), PSTR(pinfo->basedir),
+ PSTR(pinfo->catg), pinfo->status);
+
+ if (!nointeract) {
+ char fmt1[100];
+
+ /* create format based on max pkg name length */
+
+ (void) snprintf(fmt1, sizeof (fmt1), " %%-%d.%ds %%s",
+ a_longestPkg, a_longestPkg);
+
+ if (pinfo->status == PI_SPOOLED) {
+ echo(INFO_SPOOLED);
+ } else {
+ if (getuid()) {
+ progerr(ERR_NOT_ROOT, get_prog_name());
+ exit(1);
+ }
+ echo(INFO_INSTALL);
+ }
+
+ echo(fmt1, pinfo->pkginst, pinfo->name);
+
+ if (pinfo->arch || pinfo->version) {
+ char fmt2[100];
+
+ /* create format based on max pkg name length */
+
+ (void) snprintf(fmt2, sizeof (fmt2), " %%%d.%ds ",
+ a_longestPkg, a_longestPkg);
+
+ /* LINTED variable format specifier to fprintf() */
+ (void) fprintf(stderr, fmt2, "");
+
+ if (pinfo->arch) {
+ (void) fprintf(stderr, "(%s) ", pinfo->arch);
+ }
+
+ if (pinfo->version) {
+ (void) fprintf(stderr, "%s", pinfo->version);
+ }
+
+ (void) fprintf(stderr, "\n");
+ }
+
+ n = ckyorn(ans, NULL, NULL, NULL, ASK_CONFIRM);
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ pkginfoFree(&pinfo);
+ return (0);
+ }
+ }
+
+ if (pinfo->status == PI_PRESVR4) {
+ pkginfoFree(&pinfo);
+ return (presvr4(pkginst, nointeract));
+ }
+
+ if (pinfo->status == PI_SPOOLED) {
+ /* removal from a directory */
+ echo(INFO_RMSPOOL, pkginst);
+ pkginfoFree(&pinfo);
+ return (rrmdir(pkginst));
+ }
+
+ /* exit if not root */
+
+ if (getuid()) {
+ progerr(ERR_NOT_ROOT, get_prog_name());
+ exit(1);
+ }
+
+ pkginfoFree(&pinfo);
+
+ zonesSkipped = 0;
+
+ if (interrupted != 0) {
+ echo(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
+ echoDebug(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
+ return (n);
+ }
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES, "before pkgZoneRemove",
+ admnflag, doreboot, failflag, interrupted,
+ intrflag, ireboot, nullflag, warnflag);
+
+ for (zoneIndex = 0;
+ a_zlst != NULL &&
+ (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;
+ }
+
+ echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+ echoDebug(DBG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+
+ /* determine list of directories inherited from global zone */
+
+ inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /*
+ * remove package from zone; use the zone admin file which
+ * suppresses all checks.
+ */
+
+ n = pkgZoneRemove(z_zlist_get_scratch(a_zlst, zoneIndex),
+ inheritedPkgDirs, a_nodelete, a_altBinDir,
+ a_zoneAdminFile, zst);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
+ admnflag, doreboot, failflag, interrupted, intrflag,
+ ireboot, nullflag, warnflag);
+ }
+
+ if (zonesSkipped > 0) {
+ echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+ (char *)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) {
+ zonesSkipped++;
+ 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);
+ continue;
+ }
+
+ echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+
+ /* determine list of dirs inherited from global zone */
+
+ inheritedPkgDirs =
+ z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /*
+ * remove package from zone; use the zone admin file
+ * which suppresses all checks.
+ */
+
+ n = pkgZoneRemove(z_zlist_get_scratch(a_zlst,
+ zoneIndex), inheritedPkgDirs,
+ a_nodelete, a_altBinDir, a_zoneAdminFile,
+ ZONE_STATE_MOUNTED);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
+ admnflag, doreboot, failflag, interrupted,
+ intrflag, ireboot, nullflag, warnflag);
+
+ /* 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);
+ }
+ }
+
+ /*
+ * Process global zone if it was either the only possible
+ * target (no list of zones specified) or it appears in the list
+ */
+ if (a_zlst == NULL || z_on_zone_spec(GLOBAL_ZONENAME)) {
+ /* reset interrupted flag before calling pkgremove */
+ interrupted = 0; /* last action was NOT quit */
+
+ /*
+ * call pkgremove for this package for the global zone;
+ * use the admin file passed in by the user via -a.
+ */
+ n = pkgRemove(a_nodelete, a_altBinDir, a_adminFile,
+ z_get_inherited_file_systems());
+
+ /* set success/fail condition variables */
+ ckreturn(n);
+ }
+
+ 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) */
+}
+
+/*
+ * function which checks the indicated return value
+ * and indicates disposition of installation
+ */
+static void
+ckreturn(int retcode)
+{
+ /*
+ * entry debugging info
+ */
+
+ echoDebug(DBG_PKGRM_CKRETURN, retcode, PSTR(pkginst));
+
+ 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++;
+ break;
+
+ case 2: /* non-fatal error (warning) */
+ case 12:
+ case 22:
+ warnflag++;
+ interrupted++;
+ break;
+
+ case 3: /* user selected quit; operation interrupted */
+ case 13:
+ case 23:
+ intrflag++;
+ interrupted++;
+ 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++;
+ break;
+
+ default:
+ failflag++;
+ interrupted++;
+ return;
+ }
+
+ if (retcode >= 20) {
+ ireboot++;
+ } else if (retcode >= 10) {
+ doreboot++;
+ }
+}
+
+static int
+pkgZoneCheckRemove(char *a_zoneName, char **a_inheritedPkgDirs,
+ char *a_altBinDir, char *a_adminFile, char *a_stdoutPath,
+ zone_state_t a_zoneState)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char adminfd_path[PATH_MAX];
+ char 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_PKGZONECHECKREMOVE_ENTRY);
+ echoDebug(DBG_PKGZONECHECKREMOVE_ARGS, a_zoneName, PSTR(pkginst),
+ PSTR(pkgdev.dirname), PSTR(a_adminFile), PSTR(a_stdoutPath));
+
+ /* generate path to pkgremove */
+
+ (void) snprintf(path, sizeof (path), "%s/pkgremove",
+ a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+ /* start at first file descriptor */
+
+ maxfds = 0;
+
+ /*
+ * generate argument list for call to pkgremove
+ */
+
+ /* start at argument 0 */
+
+ nargs = 0;
+
+ /* first argument is path to executable */
+
+ arg[nargs++] = strdup(path);
+
+ /* second argument is always: pass -O debug to pkgremove: debug mode */
+
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* pkgrm -b dir: pass -b to pkgremove */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /*
+ * NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
+ * symlinks consistent with old behavior
+ */
+
+ if (old_symlinks) {
+ arg[nargs++] = "-y";
+ }
+
+ /* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
+
+ arg[nargs++] = "-M";
+
+ /* pkgrm -A: pass -A to pkgremove */
+
+ if (pkgrmremote) {
+ arg[nargs++] = "-A";
+ }
+
+ /* pkgrm -v: pass -v to pkgremove: never trace scripts */
+
+ /* pass "-O enable-hollow-package-support" */
+
+ if (is_depend_pkginfo_DB()) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "enable-hollow-package-support";
+ }
+
+ /* pass -n to pkgremove: always in noninteractive mode */
+
+ arg[nargs++] = "-n";
+
+ /* pkgrm -a admin: pass -a admin to pkgremove: 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++] = strdup(adminfd_path);
+ }
+
+ /*
+ * pkgadd -R root: pass -R /a to pkgremove in mounted zone
+ */
+ if (a_zoneState == ZONE_STATE_MOUNTED) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = "/a";
+ }
+
+ /* pkgrm -F: pass -F to pkgremove: always update DB only */
+
+ arg[nargs++] = "-F";
+
+ /* pass "-O preremovecheck" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "preremovecheck";
+
+ /* add "-O addzonename" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "addzonename";
+
+ /* 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 pkgremove: program name to report */
+
+ arg[nargs++] = "-N";
+ arg[nargs++] = get_prog_name();
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate argument list */
+
+ arg[nargs++] = NULL;
+
+ /* execute pkgremove command */
+
+ 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 pkgremove in zone execution */
+
+ return (n);
+}
+
+static int
+pkgZoneRemove(char *a_zoneName, char **a_inheritedPkgDirs,
+ int a_nodelete, char *a_altBinDir, char *a_adminFile,
+ zone_state_t a_zoneState)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char adminfd_path[PATH_MAX];
+ char 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_PKGZONEREMOVE_ENTRY);
+ echoDebug(DBG_PKGZONEREMOVE_ARGS, a_zoneName, PSTR(pkginst),
+ PSTR(pkgdev.dirname), a_nodelete, PSTR(a_adminFile));
+
+ /* generate path to pkgremove */
+
+ (void) snprintf(path, sizeof (path), "%s/pkgremove",
+ a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+ /* start at first file descriptor */
+
+ maxfds = 0;
+
+ /*
+ * generate argument list for call to pkgremove
+ */
+
+ /* start at argument 0 */
+
+ nargs = 0;
+
+ /* first argument is path to executable */
+
+ arg[nargs++] = strdup(path);
+
+ /* second argument is always: pass -O debug to pkgremove: debug mode */
+
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* pkgrm -b dir: pass -b to pkgremove */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /*
+ * NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
+ * symlinks consistent with old behavior
+ */
+
+ if (old_symlinks) {
+ arg[nargs++] = "-y";
+ }
+
+ /* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
+
+ arg[nargs++] = "-M";
+
+ /* pkgrm -A: pass -A to pkgremove */
+
+ if (pkgrmremote) {
+ arg[nargs++] = "-A";
+ }
+
+ /* pkgrm -v: pass -v to pkgremove: trace scripts */
+
+ if (pkgverbose) {
+ arg[nargs++] = "-v";
+ }
+
+ /* pass "-O enable-hollow-package-support" */
+
+ if (is_depend_pkginfo_DB()) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "enable-hollow-package-support";
+ }
+
+ /* pkgrm -n: pass -n to pkgremove: noninteractive mode */
+
+ if (nointeract) {
+ arg[nargs++] = "-n";
+ }
+
+ /* pkgrm -a admin: pass -a admin to pkgremove: 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 pkgremove in mounted zone
+ */
+ if (a_zoneState == ZONE_STATE_MOUNTED) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = "/a";
+ }
+
+ /* pkgrm -F: pass -F to pkgremove: update DB only */
+
+ if (a_nodelete) {
+ arg[nargs++] = "-F";
+ }
+
+ /* add "-O addzonename" */
+
+ arg[nargs++] = "-O";
+ arg[nargs++] = "addzonename";
+
+ /* 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 pkgremove: program name to report */
+
+ arg[nargs++] = "-N";
+ arg[nargs++] = get_prog_name();
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate argument list */
+
+ arg[nargs++] = NULL;
+
+ /* execute pkgremove command */
+
+ 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);
+
+ /*
+ * 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 (n);
+}
+
+/*
+ * Name: pkgRemove
+ * Description: Invoke pkgremove in the current zone to perform a remove
+ * of a single package from the current zone or standalone system
+ * Arguments: a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * a_adminFile - pointer to string representing the admin
+ * file to pass to pkgremove when removing the package.
+ * If this is == NULL no admin file is given to pkgremove.
+ * 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"
+ */
+
+static int
+pkgRemove(int a_nodelete, char *a_altBinDir, char *a_adminFile,
+ char **a_inheritedPkgDirs)
+{
+ char *arg[MAXARGS];
+ char *p;
+ char path[PATH_MAX];
+ int n;
+ int nargs;
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGREMOVE_ENTRY);
+ echoDebug(DBG_PKGREMOVE_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
+ a_nodelete, PSTR(a_adminFile));
+
+ (void) snprintf(path, sizeof (path), "%s/pkgremove",
+ a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+ nargs = 0;
+
+ /* first argument is path to executable */
+
+ arg[nargs++] = strdup(path);
+
+ /* second argument is always: pass -O debug to pkgremove: debug mode */
+
+ if (debugFlag == B_TRUE) {
+ arg[nargs++] = "-O";
+ arg[nargs++] = "debug";
+ }
+
+ /* pkgrm -b dir: pass -b to pkgremove */
+
+ if (a_altBinDir != (char *)NULL) {
+ arg[nargs++] = "-b";
+ arg[nargs++] = a_altBinDir;
+ }
+
+ /*
+ * NONABI_SCRIPTS defined: pass -o to pkgremove; 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 pkgremove; process
+ * symlinks consistent with old behavior
+ */
+
+ if (old_symlinks) {
+ arg[nargs++] = "-y";
+ }
+
+ /* pkgrm -M: pass -M to pkgrm: dont mount client file systems */
+
+ if (no_map_client) {
+ arg[nargs++] = "-M";
+ }
+
+ /* pkgrm -A: pass -A to pkgrm */
+
+ if (pkgrmremote) {
+ arg[nargs++] = "-A";
+ }
+
+ /* pkgrm -v: pass -v to pkgremove: trace scripts */
+
+ if (pkgverbose) {
+ arg[nargs++] = "-v";
+ }
+
+ /* pkgrm -n: pass -n to pkgremove: noninteractive mode */
+
+ if (nointeract) {
+ arg[nargs++] = "-n";
+ }
+
+ /* pkgrm -a admin: pass -a admin to pkgremove: admin file */
+
+ if (a_adminFile) {
+ arg[nargs++] = "-a";
+ arg[nargs++] = strdup(a_adminFile);
+ }
+
+ /* pkgrm -V vfstab: pass -V vfstab to pkgremove: alternate vfstab */
+
+ if (vfstab_file) {
+ arg[nargs++] = "-V";
+ arg[nargs++] = vfstab_file;
+ }
+
+ /* pkgrm -R root: pass -R root to pkgremove: alternative root */
+
+ if (is_an_inst_root()) {
+ arg[nargs++] = "-R";
+ arg[nargs++] = get_inst_root();
+ }
+
+ /* pkgrm -F: pass -F to pkgremove: update DB only */
+
+ if (a_nodelete) {
+ arg[nargs++] = "-F";
+ }
+
+ /* 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 pkgremove: program name to report */
+
+ arg[nargs++] = "-N";
+ arg[nargs++] = get_prog_name();
+
+ /* add package instance name */
+
+ arg[nargs++] = pkginst;
+
+ /* terminate argument list */
+
+ arg[nargs++] = NULL;
+
+ /*
+ * run the appropriate pkgremove 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 pkgremove command */
+
+ n = pkgexecv(NULL, NULL, NULL, NULL, arg);
+
+ /* return results of pkgrm in this zone */
+
+ return (n);
+}
+
+static void
+usage(void)
+{
+ char *prog = get_prog_name();
+
+ (void) fprintf(stderr, ERR_USAGE_PKGRM, prog, prog);
+ exit(1);
+}
+
+/*
+ * Name: remove_packages_in_global_with_zones
+ * Description: Remove packages from the global zone and from non-global zones
+ * when run from the global zone and when non-global zones are
+ * present.
+ * Arguments: a_pkgList - pointer to array of strings, each string specifying
+ * the name of one package to be removed.
+ * a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_repeat - are there more packages avialable in "optind"
+ * - B_TRUE - process packages from optind
+ * - B_FALSE - do not process packages from optind
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * a_pkgdir - pointer to string representing the directory
+ * where the packages to be removed are located.
+ * a_zlst - list of zones to process; NULL if no zones to process.
+ * 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 boolean_t
+remove_packages_in_global_with_zones(char **a_pkgList, int a_nodelete,
+ int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir,
+ zoneList_t a_zlst)
+{
+static char *zoneAdminFile = (char *)NULL;
+
+ boolean_t b;
+ char **inheritedPkgDirs;
+ char *zoneName;
+ char *scratchName;
+ char preremovecheckPath[PATH_MAX+1];
+ int i;
+ int n;
+ int savenpkgs = npkgs;
+ int zoneIndex;
+ int zonesSkipped;
+ zone_state_t zst;
+
+ /* entry assertions */
+
+ assert(a_zlst != (zoneList_t)NULL);
+ assert(a_pkgList != (char **)NULL);
+ assert(a_longestPkg > 0);
+ assert(a_pkgdir != (char *)NULL);
+ assert(*a_pkgdir != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGREMPKGSGZWNGZ_ENTRY);
+ echoDebug(DBG_PKGREMPKGSGZWNGZ_ARGS, a_nodelete, a_longestPkg,
+ a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
+
+ /* check all packages */
+
+ if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
+ quit(1);
+ }
+
+ /* 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);
+
+ /*
+ * all of the packages (as listed in the package list) are
+ * removed one at a time from all non-global zones and then
+ * from the global zone.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ /* reset interrupted flag before calling pkgremove */
+
+ interrupted = 0; /* last action was NOT quit */
+
+ /* skip package if it is "in the global zone only" */
+
+ if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
+ continue;
+ }
+
+ /*
+ * if operation failed in global zone do not propagate to
+ * non-global zones
+ */
+
+ zonesSkipped = 0;
+
+ if (interrupted != 0) {
+ echo(MSG_DOREMOVE_INTERRUPTED, pkginst);
+ echoDebug(DBG_DOREMOVE_INTERRUPTED, pkginst);
+ break;
+ }
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES, "before loop",
+ admnflag, doreboot, failflag, interrupted,
+ intrflag, ireboot, nullflag, warnflag);
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+ (char *)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;
+ }
+
+ echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+ echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
+ zoneName);
+
+ scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
+
+ (void) snprintf(preremovecheckPath,
+ sizeof (preremovecheckPath),
+ "%s/%s.%s.preremovecheck.txt",
+ zoneTempDir, pkginst, scratchName);
+
+ /* determine list of dirs inherited from global zone */
+
+ inheritedPkgDirs =
+ z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /*
+ * dependency check this package this zone; use the
+ * user supplied admin file so that the appropriate
+ * level of dependency checking is (or is not) done.
+ */
+
+ n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
+ a_altBinDir, admnfile, preremovecheckPath,
+ zst);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES,
+ "after pkgzonecheckremove",
+ admnflag, doreboot, failflag, interrupted,
+ intrflag, ireboot, nullflag, warnflag);
+ }
+
+ if (zonesSkipped == 0) {
+ continue;
+ }
+
+ echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+ for (zoneIndex = 0;
+ (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+ (char *)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) {
+ zonesSkipped++;
+ 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);
+ continue;
+ }
+
+ echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+ echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
+ zoneName);
+
+ scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
+
+ (void) snprintf(preremovecheckPath,
+ sizeof (preremovecheckPath),
+ "%s/%s.%s.preremovecheck.txt",
+ zoneTempDir, pkginst, scratchName);
+
+ /* determine list of dirs inherited from global zone */
+
+ inheritedPkgDirs =
+ z_zlist_get_inherited_pkg_dirs(a_zlst,
+ zoneIndex);
+
+ /*
+ * dependency check this package this zone; use the
+ * user supplied admin file so that the appropriate
+ * level of dependency checking is (or is not) done.
+ */
+
+ n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
+ a_altBinDir, admnfile, preremovecheckPath,
+ ZONE_STATE_MOUNTED);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ echoDebug(DBG_REMOVE_FLAG_VALUES,
+ "after pkgzonecheckremove",
+ admnflag, doreboot, failflag, interrupted,
+ intrflag, ireboot, nullflag, warnflag);
+
+ /* 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);
+ }
+ npkgs--;
+ }
+
+ /*
+ * look at all pre-remove check files
+ */
+
+ i = preremove_verify(a_pkgList, a_zlst, zoneTempDir);
+ if (i != 0) {
+ quit(i);
+ }
+
+ npkgs = savenpkgs;
+
+ /*
+ * reset all error return condition variables that may have been
+ * set during package removal dependency checking so that they
+ * do not reflect on the success/failure of the actual package
+ * removal operations
+ */
+
+ resetreturn();
+
+ /*
+ * all of the packages (as listed in the package list) are
+ * removed one at a time.
+ */
+
+ interrupted = 0;
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ boolean_t in_gz_only;
+ started = 0;
+
+ if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+ continue;
+ }
+
+ in_gz_only = pkgIsPkgInGzOnly(get_inst_root(), pkginst);
+
+ /* reset interrupted flag before calling pkgremove */
+
+ interrupted = 0;
+
+ /*
+ * pkgrm invoked from within the global zone and there are
+ * non-global zones configured:
+ * Remove the package from the global zone.
+ * If not removing the package from the global zone only,
+ * then remove the package from the list of zones specified.
+ */
+
+ if (in_gz_only) {
+ /* global zone only */
+ n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+ admnfile, (char *)NULL, (zoneList_t)NULL);
+ } else {
+ /* global zone and non-global zones */
+ n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+ zoneAdminFile, zoneAdminFile, a_zlst);
+ }
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ npkgs--;
+ }
+
+ /*
+ * all packages in the package list have been removed.
+ * Continue with removal if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to remove
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: remove_packages_in_nonglobal_zone
+ * Description: Remove packages in a non-global zone when run from a
+ * non-global zone.
+ * Arguments: a_pkgList - pointer to array of strings, each string specifying
+ * the name of one package to be removed.
+ * a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_repeat - are there more packages avialable in "optind"
+ * - B_TRUE - process packages from optind
+ * - B_FALSE - do not process packages from optind
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * a_pkgdir - pointer to string representing the directory
+ * where the packages to be removed are located.
+ * 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 boolean_t
+remove_packages_in_nonglobal_zone(char **a_pkgList, int a_nodelete,
+ int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir)
+{
+static char *zoneAdminFile = (char *)NULL;
+
+ int n;
+ int i;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+ assert(a_longestPkg > 0);
+ assert(a_pkgdir != (char *)NULL);
+ assert(*a_pkgdir != '\0');
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGREMPKGSNGZ_ENTRY);
+ echoDebug(DBG_PKGREMPKGSNGZ_ARGS, a_nodelete, a_longestPkg,
+ a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
+
+ /* check all package */
+
+ if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
+ quit(1);
+ }
+
+ /* 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);
+
+ /*
+ * all of the packages (as listed in the package list) are
+ * removed one at a time.
+ */
+
+ interrupted = 0;
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ started = 0;
+
+ if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+ continue;
+ }
+
+ interrupted = 0;
+
+ /*
+ * pkgrm invoked from within a non-global zone: remove
+ * the package from the current zone only - no non-global
+ * zones are possible.
+ */
+
+ n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+ admnfile, (char *)NULL, (zoneList_t)NULL);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ npkgs--;
+ }
+
+ /*
+ * all packages in the package list have been removed.
+ * Continue with removal if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to remove
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: remove_packages_in_global_no_zones
+ * Description: Remove packages from the global zone only when run in the
+ * global zone and no non-global zones are installed.
+ * Arguments: a_pkgList - pointer to array of strings, each string specifying
+ * the name of one package to be removed.
+ * a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_repeat - are there more packages avialable in "optind"
+ * - B_TRUE - process packages from optind
+ * - B_FALSE - do not process packages from optind
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * 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 boolean_t
+remove_packages_in_global_no_zones(char **a_pkgList, int a_nodelete,
+ int a_longestPkg, int a_repeat, char *a_altBinDir)
+{
+ int n;
+ int i;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+ assert(a_longestPkg > 0);
+
+ /* entry debugging info */
+
+ echoDebug(DBG_PKGREMPKGSGZNNGZ_ENTRY);
+ echoDebug(DBG_PKGREMPKGSGZNNGZ_ARGS, a_nodelete, a_longestPkg,
+ a_repeat, PSTR(a_altBinDir));
+
+ /*
+ * all of the packages (as listed in the package list) are
+ * removed one at a time.
+ */
+
+ interrupted = 0;
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ started = 0;
+
+ if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+ continue;
+ }
+
+ interrupted = 0;
+
+ /*
+ * pkgrm invoked from within the global zone and there are
+ * NO non-global zones configured:
+ * Remove the package from the global zone only.
+ */
+
+ n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+ admnfile, (char *)NULL, (zoneList_t)NULL);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ npkgs--;
+ }
+
+ /*
+ * all packages in the package list have been removed.
+ * Continue with removal if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to remove
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: remove_packages_from_spool_directory
+ * Description: Remove packages from a spool directory only.
+ * Arguments: a_pkgList - pointer to array of strings, each string specifying
+ * the name of one package to be removed.
+ * a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_repeat - are there more packages avialable in "optind"
+ * - B_TRUE - process packages from optind
+ * - B_FALSE - do not process packages from optind
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * 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 boolean_t
+remove_packages_from_spool_directory(char **a_pkgList, int a_nodelete,
+ int a_longestPkg, int a_repeat, char *a_altBinDir)
+{
+ int n;
+ int i;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+ assert(a_longestPkg > 0);
+
+ /*
+ * all of the packages (as listed in the package list) are
+ * removed one at a time.
+ */
+
+ interrupted = 0;
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ started = 0;
+
+ if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+ continue;
+ }
+
+ interrupted = 0;
+
+ /*
+ * pkgrm invoked from any type of zone BUT the target
+ * to be removed is a local spool directory: remove the
+ * packages from the spool directory only.
+ */
+
+ n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+ admnfile, (char *)NULL, (zoneList_t)NULL);
+
+ /* set success/fail condition variables */
+
+ ckreturn(n);
+
+ npkgs--;
+ }
+
+ /*
+ * all packages in the package list have been removed.
+ * Continue with removal if:
+ * -- immediate reboot is NOT required
+ * -- there are more packages to remove
+ * else return do NOT continue.
+ */
+
+ if ((ireboot == 0) && (a_repeat != 0)) {
+ return (B_TRUE);
+ }
+
+ /* return 'dont continue' */
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: remove_packages
+ * Description: Remove packages from the global zone, and optionally from one
+ * or more non-global zones, or from a specified spool directory.
+ * Arguments: a_pkgList - pointer to array of strings, each string specifying
+ * the name of one package to be removed.
+ * a_nodelete: should the files and scripts remain installed?
+ * - if != 0 pass -F flag to pkgremove - suppress
+ * the removal of any files and any class action scripts
+ * and suppress the running of any class action scripts.
+ * The package files remain but the package looks like it
+ * is not installed. This is mainly for use by upgrade.
+ * - if == 0 do not pass -F flag to pkgremove - all
+ * files and class action scripts are removed, and any
+ * appropriate class action scripts are run.
+ * a_longestPkg - length of the longest package "name" (for
+ * output format alignment)
+ * a_repeat - are there more packages avialable in "optind"
+ * - B_TRUE - process packages from optind
+ * - B_FALSE - do not process packages from optind
+ * a_altBinDir - pointer to string representing location of the
+ * pkgremove executable to run. If not NULL, then pass
+ * the path specified to the -b option to pkgremove.
+ * a_pkgdir - pointer to string representing the directory
+ * where the packages to be removed are located.
+ * a_spoolDir - pointer to string specifying spool directory
+ * to remove packages from. If != NULL then all zones
+ * processing is bypassed and the packages are removed
+ * from the specified spool directory only.
+ * a_noZones - if non-global zones are configured, should the
+ * packages be removed from the non-global zones?
+ * - B_TRUE - do NOT remove packages from non-global zones
+ * - B_FALSE - remove packages from non-global zones
+ * 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 boolean_t
+remove_packages(char **a_pkgList, int a_nodelete, int a_longestPkg,
+ int a_repeat, char *a_altBinDir, char *a_pkgdir, char *a_spoolDir,
+ boolean_t a_noZones)
+{
+ zoneList_t zlst;
+ boolean_t b;
+
+ /* entry assertions */
+
+ assert(a_pkgList != (char **)NULL);
+
+ echoDebug(DBG_REMOVEPKGS_ENTRY);
+ echoDebug(DBG_REMOVEPKGS_ARGS, npkgs, a_nodelete, a_longestPkg,
+ a_repeat, PSTR(a_pkgdir), PSTR(a_spoolDir));
+
+ /*
+ * if removing from spool directory, bypass all zones checks
+ */
+
+ if (a_spoolDir != (char *)NULL) {
+ /* in non-global zone */
+
+ echoDebug(DBG_REMOVE_PKGS_FROM_SPOOL, a_spoolDir);
+
+ b = remove_packages_from_spool_directory(a_pkgList, a_nodelete,
+ a_longestPkg, a_repeat, a_altBinDir);
+
+ return (B_FALSE);
+ }
+
+ /* exit if not root */
+
+ if (getuid()) {
+ progerr(ERR_NOT_ROOT, get_prog_name());
+ exit(1);
+ }
+
+ /*
+ * 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 = remove_packages_in_nonglobal_zone(a_pkgList, a_nodelete,
+ a_longestPkg, a_repeat, a_altBinDir, a_pkgdir);
+
+ (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)) {
+
+ echoDebug(DBG_IN_GZ_WITH_LZ);
+
+ /* 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 = remove_packages_in_global_with_zones(a_pkgList, a_nodelete,
+ a_longestPkg, a_repeat, a_altBinDir, a_pkgdir, 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 = remove_packages_in_global_no_zones(a_pkgList, a_nodelete,
+ a_longestPkg, a_repeat, a_altBinDir);
+
+ (void) z_unlock_this_zone(ZLOCKS_ALL);
+
+ return (B_FALSE);
+}
+
+/*
+ * Name: path_valid
+ * Description: Checks a string for being a valid path
+ *
+ * Arguments: path - path to validate
+ *
+ * Returns : B_TRUE - success, B_FALSE otherwise.
+ * B_FALSE means path was null, too long (>PATH_MAX),
+ * or too short (<1)
+ */
+static boolean_t
+path_valid(char *path)
+{
+ if (path == NULL) {
+ return (B_FALSE);
+ } else if (strlen(path) > PATH_MAX) {
+ return (B_FALSE);
+ } else if (strlen(path) >= 1) {
+ return (B_TRUE);
+ } else {
+ /* path < 1 */
+ return (B_FALSE);
+ }
+}
+
+/*
+ */
+
+static boolean_t
+check_packages(char **a_pkgList, char *a_packageDir)
+{
+ int savenpkgs = npkgs;
+ int i;
+ CAF_T flags = 0;
+
+ /* set flags for applicability check */
+
+ if (z_running_in_global_zone() == B_TRUE) {
+ flags |= CAF_IN_GLOBAL_ZONE;
+ }
+
+ /*
+ * for each package to remove, verify that the package is installed
+ * and is removable.
+ */
+
+ for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+ /* check package applicability */
+ if (check_applicability(a_packageDir, pkginst, get_inst_root(),
+ flags) == B_FALSE) {
+ progerr(ERR_PKG_NOT_REMOVABLE, pkginst);
+ npkgs = savenpkgs;
+ return (B_FALSE);
+ }
+ npkgs--;
+ }
+
+ npkgs = savenpkgs;
+ return (B_TRUE);
+}
+
+/*
+ * - is this package removable from this zone?
+ * - does the scope of remove conflict with existing installation
+ */
+
+static boolean_t
+check_applicability(char *a_packageDir, char *a_pkgInst,
+ char *a_rootPath, CAF_T a_flags)
+{
+ FILE *pkginfoFP;
+ boolean_t all_zones; /* pkg is "all zones" only */
+ char pkginfoPath[PATH_MAX];
+ char pkgpath[PATH_MAX];
+ int len;
+
+ /* 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 = "";
+ }
+
+ /*
+ * determine if this package is currently installed
+ * if not installed return success - operation will fail
+ * when the removal is attempted
+ */
+
+ if (pkginfoIsPkgInstalled((struct pkginfo **)NULL, a_pkgInst) !=
+ B_TRUE) {
+ return (B_TRUE);
+ }
+
+ /*
+ * calculate paths to various objects
+ */
+
+ 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);
+ }
+
+ /* if not installed then just return */
+
+ if (isdir(pkgpath) != 0) {
+ progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
+ return (B_TRUE);
+ }
+
+ len = snprintf(pkginfoPath, sizeof (pkginfoPath),
+ "%s/pkginfo", pkgpath);
+ if (len > sizeof (pkgpath)) {
+ progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
+ return (B_FALSE);
+ }
+
+ /*
+ * gather information from this packages pkginfo file
+ */
+
+ pkginfoFP = fopen(pkginfoPath, "r");
+
+ if (pkginfoFP == (FILE *)NULL) {
+ progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
+ strerror(errno));
+ return (B_FALSE);
+ }
+
+ /* determine "ALLZONES" setting for this package */
+
+ all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
+ "true", B_FALSE);
+
+ /* close pkginfo file */
+
+ (void) fclose(pkginfoFP);
+
+ /* gather information from the global zone only file */
+
+ /*
+ * verify package applicability based on information gathered;
+ * the package IS currently installed....
+ */
+
+ /* pkg ALLZONES=true & not running in global zone */
+
+ if ((all_zones == B_TRUE) && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
+ progerr(ERR_ALLZONES_AND_IN_LZ_PKGRM, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Name: shall_we_continue
+ * 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
+shall_we_continue(char *a_pkgInst, int a_npkgs)
+{
+ char ans[MAX_INPUT];
+ int n;
+
+ /* return FALSE if immediate reboot required */
+
+ if (ireboot) {
+ ptext(stderr, MSG_SUSPEND_RM, a_pkgInst);
+ return (B_FALSE);
+ }
+
+ /* return TRUE if not interrupted */
+
+ if (!interrupted) {
+ return (B_TRUE);
+ }
+
+ /* output appropriate interrupt message */
+
+ echo(a_npkgs == 1 ? MSG_1MORETODO : MSG_MORETODO, a_npkgs);
+
+ /* if running with no interaction (-n) do not ask question */
+
+ if (nointeract) {
+ quit(0);
+ /* NOTREACHED */
+ }
+
+ /* interaction possible: ask question */
+
+ n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_RM);
+ if (n != 0) {
+ quit(n);
+ /* NOTREACHED */
+ }
+
+ if (strchr("yY", *ans) == NULL) {
+ quit(0);
+ /* NOTREACHED */
+ }
+ 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() to directory is removed on exit */
+
+ quitSetZoneTmpdir(*r_zoneTempDir);
+
+ /* exit debugging info */
+
+ echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgrm/msgdefs.h b/usr/src/cmd/svr4pkg/pkgrm/msgdefs.h
new file mode 100644
index 0000000000..23b252cfb4
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/msgdefs.h
@@ -0,0 +1,91 @@
+/*
+ * 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
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ASK_CONTINUE "Do you want to continue with package removal?"
+
+#define ERR_NOPKGS "no packages were found in <%s>"
+
+#define ERR_CHDIR "unable to change directory to <%s>"
+
+#define MSG_SUSPEND "Removals of <%s> has been suspended."
+
+#define MSG_1MORETODO "\nThere is 1 more package to be removed."
+
+#define MSG_MORETODO "\nThere are %d more packages to be removed."
+
+#define ERR_NOTROOT "You must be \"root\" for %s to execute properly."
+
+#define INFO_SPOOLED "\nThe following package is currently spooled:"
+
+#define INFO_INSTALL "\nThe following package is currently installed:"
+
+#define INFO_RMSPOOL "\nRemoving spooled package instance <%s>"
+
+#define ASK_CONFIRM "Do you want to remove this package?"
+
+#define ERR_ROOT_CMD "Command line install root contends with environment."
+
+#define ERR_ROOT_SET "Could not set install root from the environment."
+
+#define ERR_WITH_S "illegal combination of options with \"s\"."
+
+#define ERR_WITH_A "illegal option combination \"-M\" with \"-A\"."
+
+#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_SYS "Unable to remove packages that are part of the " \
+ "SYSTEM category with the -Y option."
+
+#define ERR_CAT_INV "Category argument <%s> is invalid."
+
+#define ERR_USAGE "usage:\n" \
+ "\t%s [-a admin] [-n] [[-M|-A] -R host_path] " \
+ "[-V fs_file] [-v] [-Y category[,category ...] | " \
+ "pkg [pkg ...]]" \
+ "\n\t%s -s spool [-Y category[,category ...] | " \
+ "pkg [pkg ...]]\n"
+
+#define MAX_CAT_ARGS 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGDEFS_H */
diff --git a/usr/src/cmd/svr4pkg/pkgrm/presvr4.c b/usr/src/cmd/svr4pkg/pkgrm/presvr4.c
new file mode 100644
index 0000000000..346839cfd9
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/presvr4.c
@@ -0,0 +1,184 @@
+/*
+ * 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 2004 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 <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h> /* chmod()? definition */
+#include <valtools.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+/*
+ * pkgadd local includes
+ */
+
+#include "quit.h"
+
+extern struct admin adm;
+extern struct pkgdev pkgdev;
+extern char *tmpdir;
+extern int started;
+
+static void intf_reloc(void);
+
+#define PATH_FLAGS P_EXIST|P_ABSOLUTE|P_BLK
+
+#define MSG_DEVICE "Removal of a pre-SVR4 package requires the original " \
+ "medium from which the package was installed."
+
+#define ASK_DEVICE "Enter the alias or pathname for the device to be " \
+ "used (e.g., diskette1 or /dev/diskette)"
+
+#define ASK_INSERT "Insert the first volume for package <%s> into %s"
+
+#define ERR_NOCOPY "unable to create copy of UNINSTALL script in <%s>"
+
+#define ERR_NOINT "-n option cannot be used when removing pre-SVR4 " \
+ "packages"
+
+#define ERR_BADDEV "Unknown or bad device <%s> specified"
+
+#define MSG_MAIL "An attempt to remove the <%s> pre-SVR4 package on " \
+ "<%s> completed with exit status <%d>."
+
+#define INFO_P4RMOK "\nPre-SVR4 package reported successful removal.\n"
+
+int
+presvr4(char *pkg, int a_nointeract)
+{
+ char alias[PATH_MAX];
+ char path[PATH_MAX];
+ char *tmpcmd;
+ int n, retcode;
+ void (*tmpfunc)();
+
+ echo(gettext("*** Removing Pre-SVR4 Package ***"));
+ if (a_nointeract != 0) {
+ progerr(gettext(ERR_NOINT));
+ quit(1);
+ }
+
+ /* should accept device alias?? */
+
+ echo(gettext(MSG_DEVICE));
+ for (;;) {
+ if (n = ckstr(alias, NULL, PATH_MAX, NULL, NULL, NULL,
+ gettext(ASK_DEVICE)))
+ return (n);
+
+ if (devtype(alias, &pkgdev))
+ continue;
+ if (!pkgdev.mount || !pkgdev.bdevice) {
+ logerr(gettext(ERR_BADDEV), alias);
+ continue;
+ }
+ break;
+ }
+ pkgdev.mount = pkgdev.dirname = "/install";
+ pkgdev.rdonly = 1;
+
+ if (n = pkgmount(&pkgdev, pkg, 1, 0, 1))
+ quit(n);
+
+ psvr4pkg(&pkg);
+
+ /*
+ * check to see if we can guess (via Rlist) what
+ * pathnames this package is likely to remove;
+ * 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(gettext("unable to change directory to <%s>"), tmpdir);
+ quit(99);
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/install/UNINSTALL",
+ "/install");
+ tmpcmd = tempnam(tmpdir, "UNINSTALL");
+ if (!tmpcmd || copyf(path, tmpcmd, 0) || chmod(tmpcmd, 0500)) {
+ progerr(gettext(ERR_NOCOPY), tmpdir);
+ quit(99);
+ }
+
+ started++;
+
+ echo(gettext("## Executing package UNINSTALL script"));
+
+ retcode = pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", tmpcmd, NULL);
+
+ (void) unlink(tmpcmd);
+ if (retcode) {
+ echo(gettext("\nPre-SVR4 package reported failed removal.\n"));
+ } else {
+ echo(gettext(INFO_P4RMOK));
+ }
+
+ psvr4mail(adm.mail, gettext(MSG_MAIL), retcode, pkg);
+ (void) pkgumount(&pkgdev);
+
+ /* 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/pkgrm/quit.c b/usr/src/cmd/svr4pkg/pkgrm/quit.c
new file mode 100644
index 0000000000..41e995be75
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/quit.c
@@ -0,0 +1,340 @@
+/*
+ * 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 <locale.h>
+#include <libintl.h>
+#include <pkgdev.h>
+#include <pkglib.h>
+#include <instzones_api.h>
+#include <libadm.h>
+#include <libinst.h>
+#include <messages.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 ckreturnFunc_t *ckreturnFunc = (ckreturnFunc_t *)NULL;
+static intfRelocFunc_t *intfRelocFunc = (intfRelocFunc_t *)NULL;
+static char *zoneTempDir = (char *)NULL;
+static void trap(int signo);
+static zoneList_t zoneList = (zoneList_t)NULL;
+static int trapEntered = 0;
+
+/*
+ * exported functions
+ */
+
+void quit(int retcode);
+void quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+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: 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 removed
+ */
+
+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: 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 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 (retcode != 99) {
+ if (ckreturnFunc != (ckreturnFunc_t *)NULL) {
+ (ckreturnFunc)(retcode);
+ }
+ if (failflag) {
+ retcode = 1;
+ } else if (warnflag) {
+ retcode = 2;
+ } else if (intrflag) {
+ retcode = 3;
+ } else if (admnflag) {
+ retcode = 4;
+ } else if (nullflag) {
+ retcode = 5;
+ } else {
+ retcode = 0;
+ }
+ if (ireboot) {
+ retcode += 20;
+ }
+ if (doreboot) {
+ retcode += 10;
+ }
+ }
+
+ if (doreboot || ireboot) {
+ ptext(stderr, gettext(MSG_REBOOT));
+ }
+
+ if (pkgdev.mount) {
+ (void) chdir("/");
+ (void) pkgumount(&pkgdev);
+ }
+
+ /* if set remove zone temporary directory */
+
+ if (zoneTempDir != (char *)NULL) {
+ echoDebug(DBG_REMOVING_ZONE_TMPDIR, zoneTempDir);
+ (void) rrmdir(zoneTempDir);
+ zoneTempDir = (char *)NULL;
+ }
+
+ /*
+ * 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, retcode);
+
+ exit(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/pkgrm/quit.h b/usr/src/cmd/svr4pkg/pkgrm/quit.h
new file mode 100644
index 0000000000..c25cf07cb3
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgrm/quit.h
@@ -0,0 +1,65 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Header: pkgrm: quit.c
+ *
+ * Function: external definitions for references to the quit.c module
+ *
+ */
+
+#ifndef __PKGRM_QUIT_H__
+#define __PKGRM_QUIT_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * required include files
+ */
+
+#include "libinst.h"
+
+/*
+ * exported (global) functions
+ */
+
+typedef void (intfRelocFunc_t)(void);
+
+extern void quit(int retcode);
+extern void quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+extern void quitSetZoneName(char *a_zoneName);
+extern void quitSetZoneTmpdir(char *z_zoneTempDir);
+extern sighdlrFunc_t *quitGetTrapHandler(void);
+extern void quitSetIntfReloc(intfRelocFunc_t *a_intfReloc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PKGRM_QUIT_H__ */
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/Makefile b/usr/src/cmd/svr4pkg/pkgscripts/Makefile
new file mode 100644
index 0000000000..dd2ad4b3b3
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/Makefile
@@ -0,0 +1,54 @@
+#
+# 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= cmdexec
+
+OBJS= cmdexec.o
+SRCS= $(OBJS:.o=.c)
+
+CLASS_ACTION_SCRIPTS = i.awk \
+ i.build \
+ i.CompCpio \
+ i.sed \
+ r.awk \
+ r.build \
+ r.sed
+ADMINFILE = default
+
+# cmdexec also installed in usr/sadm/install/scripts
+SCRIPTS = $(CLASS_ACTION_SCRIPTS) $(PROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+CLOBBERFILES += $(CLASS_ACTION_SCRIPTS)
+
+LDLIBS += -lpkg
+
+.KEEP_STATE:
+all: $(PROG) $(CLASS_ACTION_SCRIPTS)
+
+install: all $(ROOTCLASS_SCR_FILES) $(ROOTADMIN_SRC_FILE)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/cmdexec.c b/usr/src/cmd/svr4pkg/pkgscripts/cmdexec.c
new file mode 100644
index 0000000000..5db27b2941
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/cmdexec.c
@@ -0,0 +1,155 @@
+/*
+ * 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 <ctype.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkglib.h>
+
+#define COMMAND '!'
+#define LSIZE 256
+
+#define ERR_NOTROOT "You must be \"root\" for %s to execute properly."
+
+static void usage(void);
+static int docmd(char *cmd, char *file, char *input);
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fpout, *fp;
+ char line[LSIZE],
+ *pt,
+ *keyword, /* keyword = install || remove */
+ *input, /* sed input file */
+ *cmd,
+ *srcfile, /* sed data file */
+ *destfile; /* target file to be updated */
+ int flag;
+ char *prog;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ prog = set_prog_name(argv[0]);
+
+ if (getuid()) {
+ progerr(gettext(ERR_NOTROOT), prog);
+ exit(1);
+ }
+
+ if (argc != 5)
+ usage();
+
+ cmd = argv[1];
+ keyword = argv[2];
+ srcfile = argv[3];
+ destfile = argv[4];
+
+ srcfile = argv[3];
+ if ((fp = fopen(srcfile, "r")) == NULL) {
+ progerr(gettext("unable to open %s"), srcfile);
+ exit(1);
+ }
+
+ input = tempnam(NULL, "sedinp");
+ if ((fpout = fopen(input, "w")) == NULL) {
+ progerr(gettext("unable to open %s"), input);
+ exit(2);
+ }
+
+ flag = (-1);
+ while (fgets(line, LSIZE, fp)) {
+ for (pt = line; isspace(*pt); /* void */)
+ ++pt;
+ if (*pt == '#')
+ continue;
+ if (*pt == COMMAND) {
+ if (flag > 0)
+ break; /* no more lines to read */
+ pt = strtok(pt+1, " \t\n");
+ if (!pt) {
+ progerr(gettext("null token after '!'"));
+ exit(1);
+ }
+ flag = (strcmp(pt, keyword) ? 0 : 1);
+ } else if (flag == 1) { /* bug # 1083359 */
+ (void) fputs(line, fpout);
+ }
+ }
+ (void) fclose(fpout);
+ if (flag > 0) {
+ if (docmd(cmd, destfile, input)) {
+ progerr(gettext("command failed <%s>"), cmd);
+ exit(1);
+ }
+ }
+ (void) unlink(input);
+ return (0);
+}
+
+static int
+docmd(char *cmd, char *file, char *input)
+{
+ char *tempout;
+ char command[256];
+
+ tempout = tempnam(NULL, "temp1");
+ if (!tempout)
+ return (-1);
+
+ (void) sprintf(command, "%s -f %s <%s >%s", cmd, input, file, tempout);
+ if (system(command))
+ return (-1);
+
+ (void) sprintf(command, "cp %s %s", tempout, file);
+ if (system(command))
+ return (-1);
+
+ (void) unlink(tempout);
+ free(tempout);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext("usage: %s cmd keyword src dest\n"),
+ get_prog_name());
+ exit(2);
+}
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/default b/usr/src/cmd/svr4pkg/pkgscripts/default
new file mode 100644
index 0000000000..5774d36838
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/default
@@ -0,0 +1,41 @@
+#
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+mail=
+instance=unique
+partial=ask
+runlevel=ask
+idepend=ask
+rdepend=ask
+space=ask
+setuid=ask
+conflict=ask
+action=ask
+networktimeout=60
+networkretries=3
+authentication=quit
+keystore=/var/sadm/security
+proxy=
+basedir=default
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio.sh b/usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio.sh
new file mode 100644
index 0000000000..247f973ec7
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio.sh
@@ -0,0 +1,538 @@
+#!/bin/sh
+#
+# 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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+# i.CompCpio
+#
+# This shell script uncompresses and installs files archived in
+# old-style WOS packages using the utilities cpio and compress. It
+# looks in the PKGSRC directory for the archives which may be called
+# out in one of eight ways :
+#
+# reloc.cpio.Z relocatable paths, less old style
+# root.cpio.Z absolute paths, less old style
+# reloc.cpio relocatable paths less old style, not compressed
+# root.cpio absolute paths, less old style, not compressed
+# reloc.Z relocatable paths, old style, compressed
+# root.Z absolute paths, old style, compressed
+# reloc relocatable paths, old style, not compressed
+# root absolute paths, old style, not compressed
+#
+# stdin carries the source directory as the first entry followed by the
+# paths of the files to be installed as indicated in the pkgmap. Since
+# all operations take place from the declared base directory, both relative
+# and absolute paths will install correctly. There are three methods and
+# since speed is of the essence, we skip straight to the right one :
+#
+# If it's an initial install
+# do a full cpio for each archive
+# else
+# If there's only the reloc archive
+# make a file list, rm executables, do a selective cpio
+# else
+# rm executables, do a full cpio for each archive
+#
+# Since the old-style archives contain no execute permissions, this
+# script saves the executables it requires so it can clean up after
+# unloading the archive. If /usr/lib/ld.so or .so.1 is included in the
+# package, no cleanup will be possible (nothing will run) so we clean
+# up first and then unload the entire archive without a file list.
+#
+NAME="i.CompCpio"
+FILELIST=${PKGSAV:?undefined}/filelist
+BD=${BASEDIR:-/}
+IR=${PKG_INSTALL_ROOT:-/}
+MAXLIST=550 # This is arbitrary based upon 2.4 cpio
+count=0
+
+reloc_cpio_Z=0
+root_cpio_Z=0
+reloc_cpio=0
+root_cpio=0
+Reloc_Arch=""
+Root_Arch=""
+is_an_archive=0
+is_a_filelist=0
+mk_filelist=0
+list_empty=1
+local_install=0
+Spcl_init=0
+Rm_alt_sav=0
+
+# critical archived dynamic libraries and executables
+Spcl_lib=0
+Spcl_exec=0
+Movelist=""
+Ld_Preload=""
+Ld1=usr/lib/ld.so.1
+Ld=usr/lib/ld.so
+Libintl=usr/lib/libintl.so.1
+Libmalloc=usr/lib/libmapmalloc.so.1
+Libc=usr/lib/libc.so.1
+Libw=usr/lib/libw.so.1
+Libdl=usr/lib/libdl.so.1
+Cpio=usr/bin/cpio
+Rm=usr/bin/rm
+Ln=usr/bin/ln
+Mv=usr/bin/mv
+Nawk=usr/bin/nawk
+Zcat=usr/bin/zcat
+
+# Set up the default paths
+MV_xpath=/usr/bin
+MV_cmd=$MV_xpath/mv
+CPIO_xpath=/usr/bin
+CPIO_cmd=$CPIO_xpath/cpio
+ZCAT_xpath=/usr/bin
+ZCAT_cmd=$ZCAT_xpath/zcat
+LN_xpath=/usr/bin
+LN_cmd=$LN_xpath/ln
+NAWK_xpath=/usr/bin
+NAWK_cmd=$NAWK_xpath/nawk
+RM_xpath=/usr/bin
+RM_cmd=$RM_xpath/rm
+Tmp_xpath=/usr/tmp$$dir
+Tmp_Creat=0
+rm_cpio=0
+rm_ln=0
+rm_zcat=0
+rm_nawk=0
+rm_rm=0
+rm_mv=0
+no_select=0
+
+# Functions
+
+#
+# This creates the temporary directory for holding the old dynamic
+# libraries and executables.
+#
+mktempdir() {
+ if [ ! -d $Tmp_xpath ]; then
+ mkdir $Tmp_xpath
+ if [ $? -ne 0 ]; then
+ echo `gettext "ERROR : $NAME cannot create $Tmp_xpath."`
+ exit 1
+ fi
+ fi
+ Tmp_Creat=1
+}
+
+#
+# Test a path to see if it represents a dynamic library or executable that
+# we use in this script. If it is, deal with the special case.
+#
+spclcase() { # $1 is the pathname to special case
+ if [ $local_install -eq 1 ]; then
+ case $1 in
+ $Ld) no_select=1;;
+ $Ld1) no_select=1;;
+ $Libintl) Spcl_lib=1; file=libintl.so.1;;
+ $Libmalloc) Spcl_lib=1; file=libmapmalloc.so.1;;
+ $Libc) Spcl_lib=1; file=libc.so.1;;
+ $Libw) Spcl_lib=1; file=libw.so.1;;
+ $Libdl) Spcl_lib=1; file=libdl.so.1;;
+ $Cpio) rm_cpio=1; Spcl_exec=1;;
+ $Ln) rm_ln=1; Spcl_exec=1;;
+ $Zcat) rm_zcat=1; Spcl_exec=1;;
+ $Nawk) rm_nawk=1; Spcl_exec=1;;
+ $Rm) rm_rm=1; Spcl_exec=1;;
+ $Mv) rm_mv=1; Spcl_exec=1;;
+ esac
+
+ if [ $no_select -eq 1 ]; then
+ is_a_filelist=0
+ list_empty=1
+ $RM_cmd $FILELIST
+ if [ $Rm_alt_sav -eq 1 ]; then
+ $RM_cmd -r $PKGSAV
+ Rm_alt_sav=0
+ fi
+ exec_clean 1
+ return 1
+ elif [ $Spcl_lib -eq 1 ]; then
+ if [ $Tmp_Creat -eq 0 ]; then
+ mktempdir
+ fi
+
+ if [ $Spcl_init -eq 0 ]; then
+ Org_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
+ LD_LIBRARY_PATH="$Org_LD_LIBRARY_PATH $Tmp_xpath"
+ export LD_LIBRARY_PATH
+ Spcl_init=1
+ fi
+ Ld_Preload="$Ld_Preload $Tmp_xpath/$file"
+ LD_PRELOAD=$Ld_Preload
+ export LD_PRELOAD
+ Movelist="$1 $file $Movelist"
+ $MV_cmd $1 $Tmp_xpath
+ $LN_cmd -s ../..$Tmp_xpath/$file $1
+ Spcl_lib=0
+ elif [ $Spcl_exec -eq 1 ]; then
+ if [ $Tmp_Creat -eq 0 ]; then
+ mktempdir
+ fi
+
+ $MV_cmd $1 $Tmp_xpath
+ if [ $rm_cpio -eq 1 ]; then
+ $LN_cmd -s ../..$Tmp_xpath/cpio $1
+ CPIO_cmd="$Tmp_xpath/cpio"
+ Movelist="$1 cpio $Movelist"
+ rm_cpio=0
+ elif [ $rm_ln -eq 1 ]; then
+ $Tmp_xpath/ln -s ../..$Tmp_xpath/ln $1
+ LN_cmd="$Tmp_xpath/ln"
+ Movelist="$1 ln $Movelist"
+ rm_ln=0
+ elif [ $rm_nawk -eq 1 ]; then
+ $LN_cmd -s ../..$Tmp_xpath/nawk $1
+ NAWK_cmd="$Tmp_xpath/nawk"
+ Movelist="$1 nawk $Movelist"
+ rm_nawk=0
+ elif [ $rm_zcat -eq 1 ]; then
+ $LN_cmd -s ../..$Tmp_xpath/zcat $1
+ ZCAT_cmd="$Tmp_xpath/zcat"
+ Movelist="$1 zcat $Movelist"
+ rm_zcat=0
+ elif [ $rm_rm -eq 1 ]; then
+ $LN_cmd -s ../..$Tmp_xpath/rm $1
+ RM_cmd="$Tmp_xpath/rm"
+ Movelist="$Movelist $1 rm"
+ rm_rm=0
+ elif [ $rm_mv -eq 1 ]; then
+ $LN_cmd -s ../..$Tmp_xpath/mv $1
+ MV_cmd="$Tmp_xpath/mv"
+ Movelist="$Movelist $1 mv"
+ rm_mv=0
+ fi
+ Spcl_exec=0
+ fi
+ fi
+
+ return 0
+}
+
+#
+# Clean up the libraries and executables that were moved.
+#
+exec_clean() { # $1 =1 means be quiet
+ if [ ! -z "${Movelist}" ]; then
+ echo $Movelist | $NAWK_cmd '
+ { split ($0, line)
+ for (n=1; n <= NF; n++) {
+ print line[n]
+ }
+ }' | while read path; do
+ read file
+ if [ -h $path ]; then # If it's our slink
+ # then put the original back
+ if [ $1 -eq 0 ]; then
+ echo `gettext "WARNING : $path not found in archive."`
+ fi
+ $MV_cmd $Tmp_xpath/$file $path
+ else # if the archive put something down
+ # remove the temporary copy
+ $RM_cmd $Tmp_xpath/$file
+ fi
+ done
+ for path in $Movelist; do
+ if [ -x $path ]; then
+ case $path in
+ $Cpio) CPIO_cmd="$CPIO_xpath/cpio";;
+ $Ln) LN_cmd="$LN_xpath/ln";;
+ $Zcat) ZCAT_cmd="$ZCAT_xpath/zcat";;
+ $Nawk) NAWK_cmd="$NAWK_xpath/nawk";;
+ $Rm) RM_cmd="$RM_xpath/rm";;
+ $Mv) MV_cmd="$MV_xpath/mv";;
+ esac
+ fi
+ done
+ Movelist=""
+
+ if [ $Tmp_Creat -eq 1 ]; then
+ $RM_cmd -r $Tmp_xpath
+ Tmp_Creat=0
+ fi
+ fi
+}
+
+#
+# Figure out what kind of package this is
+#
+eval_pkg() {
+
+ # Any archive, whether compressed or not needs to be handled
+ # the same. i.e. reloc.cpio.Z and root.cpio.Z should cause
+ # the global is_an_archive to be set to 1.
+
+ read path
+ if [ ${path:-NULL} != NULL ]; then # get the package source directory
+ PKGSRC=${path:?undefined}
+
+ if [ ${PKG_INSTALL_ROOT:-/} = "/" ]; then
+ local_install=1
+ fi
+
+ if [ -r $PKGSRC/reloc.cpio.Z ]; then
+ reloc_cpio_Z=1
+ Reloc_Arch=$PKGSRC/reloc.cpio.Z
+ is_an_archive=1
+ fi
+
+ if [ -r $PKGSRC/root.cpio.Z ]; then
+ root_cpio_Z=1
+ Root_Arch=$PKGSRC/root.cpio.Z
+ is_an_archive=1
+ fi
+
+ if [ -r $PKGSRC/reloc.cpio ]; then
+ reloc_cpio=1
+ Reloc_Arch=$PKGSRC/reloc.cpio
+ is_an_archive=1
+ fi
+
+ if [ -r $PKGSRC/root.cpio ]; then
+ root_cpio=1
+ Root_Arch=$PKGSRC/root.cpio
+ is_an_archive=1
+ fi
+
+ if [ -r $PKGSRC/reloc.Z ]; then
+ reloc_cpio_Z=1
+ Reloc_Arch=$PKGSRC/reloc.Z
+ is_an_archive=2
+ fi
+
+ if [ -r $PKGSRC/root.Z ]; then
+ root_cpio_Z=1
+ Root_Arch=$PKGSRC/root.Z
+ is_an_archive=2
+ fi
+
+ if [ -f $PKGSRC/reloc ]; then
+ reloc_cpio=1
+ Reloc_Arch=$PKGSRC/reloc
+ is_an_archive=2
+ fi
+
+ if [ -f $PKGSRC/root ]; then
+ root_cpio=1
+ Root_Arch=$PKGSRC/root
+ is_an_archive=2
+ fi
+ else
+ exit 0 # empty pipe, we're done
+ fi
+}
+
+#
+# main
+#
+
+eval_pkg
+
+if [ $BD = "/" ]; then
+ Client_BD=""
+else
+ Client_BD=`echo $BD | sed s@/@@`
+fi
+
+if [ $is_an_archive -eq 0 ]; then
+ echo `gettext "ERROR : $NAME cannot find archived files in $PKGSRC."`
+ exit 1
+fi
+
+if [ ! -d $PKGSAV ]; then
+ echo `gettext "WARNING : $NAME cannot find save directory $PKGSAV."`
+ PKGSAV=/tmp/$PKG.sav
+
+ if [ ! -d $PKGSAV ]; then
+ /usr/bin/mkdir $PKGSAV
+ fi
+
+ if [ $? -eq 0 ]; then
+ echo `gettext " Using alternate save directory" $PKGSAV`
+ FILELIST=$PKGSAV/filelist
+ Rm_alt_sav=1
+ else
+ echo `gettext "ERROR : cannot create alternate save directory"` $PKGSAV
+ exit 1
+ fi
+fi
+
+if [ -f $FILELIST ]; then
+ rm $FILELIST
+fi
+
+cd $BD
+
+# If there's one old-style archive and it is relocatable and this is
+# not an initial install then make a file list for extraction.
+if [ $is_an_archive -eq 1 -a ${PKG_INIT_INSTALL:-null} = null ]; then
+ mk_filelist=1
+fi
+
+# If this is not an initial install then clear out potentially executing
+# files and libraries for cpio and create an extraction list if necessary
+if [ ${PKG_INIT_INSTALL:-null} = null ]; then
+ if [ $local_install -eq 1 ]; then
+ # If extraction list is desired, create it
+ if [ $mk_filelist -eq 1 ]; then
+ is_a_filelist=1
+ while read path
+ do
+ echo $path >> $FILELIST
+ list_empty=0
+ if [ -x ${path:-NULL} ]; then
+ full_path=`echo $Client_BD/$path | sed s@//@/@g`
+ spclcase $full_path
+ if [ $? -eq 1 ]; then
+ break
+ fi
+ fi
+ done
+
+ # If there's a path containing a '$' then we can't
+ # use the extraction list because of the shell
+ if [ $list_empty -eq 0 ]; then
+ s=`LD_PRELOAD="$Ld_Preload" $NAWK_cmd ' /\\$/ { print } ' $FILELIST`
+
+ if [ ! -z "${s}" ]; then
+ is_a_filelist=0
+ fi
+ fi
+ else # No extraction list is desired
+ while read path
+ do
+ if [ -x ${path:-NULL} ]; then
+ full_path=`echo $Client_BD/$path | sed s@//@/@g`
+ spclcase $full_path
+ if [ $? -eq 1 ]; then
+ break
+ fi
+ fi
+ done
+ fi # $mk_filelist -eq 1
+ else # ! ($local_install -eq 1)
+ # If extraction list is desired, create it
+ if [ $mk_filelist -eq 1 ]; then
+ is_a_filelist=1
+ while read path
+ do
+ echo $path >> $FILELIST
+ list_empty=0
+ done
+
+ # If there's a path containing a '$' then we can't
+ # use the extraction list because of the shell
+ if [ $list_empty -eq 0 ]; then
+ s=`LD_PRELOAD="$Ld_Preload" $NAWK_cmd ' /\\$/ { print } ' $FILELIST`
+
+ if [ ! -z "${s}" ]; then
+ is_a_filelist=0
+ fi
+ fi
+ fi # $mk_filelist -eq 1
+ fi # $local_install -eq 1
+fi # ${PKG_INIT_INSTALL:-null} = null
+
+# Now extract the data from the archive(s)
+# extract compressed cpio relocatable archive
+if [ $reloc_cpio_Z -eq 1 ]; then
+ cd $BD
+ if [ $is_a_filelist -eq 1 ]; then
+ if [ $list_empty -eq 0 ]; then
+ $ZCAT_cmd $Reloc_Arch | $CPIO_cmd -idukm -E $FILELIST
+ if [ $? -ne 0 ]; then
+ echo `gettext "cpio of $Reloc_Arch failed with error $?."`
+ exit 1
+ fi
+
+ fi
+ else
+ $ZCAT_cmd $Reloc_Arch | $CPIO_cmd -idukm
+ fi
+fi
+
+# extract compressed cpio absolute archive
+if [ $root_cpio_Z -eq 1 ]; then
+ cd $IR
+ $ZCAT_cmd $Root_Arch | $CPIO_cmd -idukm
+ if [ $? -ne 0 ]; then
+ echo `gettext "cpio of $Root_Arch failed with error $?."`
+ exit 1
+ fi
+fi
+
+# extract cpio relocatable archive
+if [ $reloc_cpio -eq 1 ]; then
+ cd $BD
+ if [ $is_a_filelist -eq 1 ]; then
+ if [ $list_empty -eq 0 ]; then
+ $CPIO_cmd -idukm -I $Reloc_Arch -E $FILELIST
+
+ if [ $? -ne 0 ]; then
+ echo `gettext "cpio of $Reloc_Arch failed with error $?."`
+ exit 1
+ fi
+ fi
+ else
+ $CPIO_cmd -idukm -I $Reloc_Arch
+ fi
+fi
+
+# extract cpio absolute archive
+if [ $root_cpio -eq 1 ]; then
+ cd $IR
+ $CPIO_cmd -idukm -I $Root_Arch
+ if [ $? -ne 0 ]; then
+ echo `gettext "cpio of $Root_Arch failed with error $?."`
+ exit 1
+ fi
+fi
+
+if [ -f $FILELIST ]; then
+ $RM_cmd $FILELIST
+fi
+
+if [ $Rm_alt_sav -eq 1 ]; then
+ $RM_cmd -r $PKGSAV
+ Rm_alt_sav=0
+fi
+
+exec_clean 0
+
+if [ $Tmp_Creat -eq 1 ]; then
+ $RM_cmd -r $Tmp_xpath
+fi
+
+if [ $Spcl_init -eq 1 ]; then
+ LD_LIBRARY_PATH=$Org_LD_LIBRARY_PATH
+ export LD_LIBRARY_PATH
+ Spcl_init=0
+fi
+
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/i.awk.sh b/usr/src/cmd/svr4pkg/pkgscripts/i.awk.sh
new file mode 100644
index 0000000000..7cb24b1b0b
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.awk.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+ [ "$src" = /dev/null ] && continue
+
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/awk${client_dest}
+ else
+ savepath=$PKGSAV/awk${dest}
+ fi
+
+ dirname=`dirname $savepath`
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ continue
+ fi
+ if [ ! -d $dirname ]
+ then
+ # ignore return code since mkdir has bug
+ mkdir -p $dirname
+ fi
+
+ cp $src $savepath &&
+ /usr/sadm/install/scripts/cmdexec /usr/bin/awk install $savepath $dest
+
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ continue
+ fi
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/i.build.sh b/usr/src/cmd/svr4pkg/pkgscripts/i.build.sh
new file mode 100644
index 0000000000..c307677595
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.build.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+ [ "$src" = /dev/null ] && continue
+
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/build${client_dest}
+ else
+ savepath=$PKGSAV/build${dest}
+ fi
+
+ dirname=`dirname $savepath`
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ continue
+ fi
+
+ if [ ! -d $dirname ]
+ then
+ # ignore return since mkdir has bug
+ mkdir -p $dirname
+ fi
+
+ cp $src $savepath &&
+ chmod 500 $savepath
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ continue
+ fi
+
+ if $savepath install > /tmp/$$build
+ then
+ if [ -s /tmp/$$build ]
+ then
+ cp /tmp/$$build $dest || error=yes
+ fi
+ else
+ error=yes
+ fi
+ rm -f /tmp/$$build
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/i.sed.sh b/usr/src/cmd/svr4pkg/pkgscripts/i.sed.sh
new file mode 100644
index 0000000000..a89f28b6a5
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.sed.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+ [ "$src" = /dev/null ] && continue
+
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/sed${client_dest}
+ else
+ savepath=$PKGSAV/sed${dest}
+ fi
+
+ dirname=`dirname $savepath`
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ continue
+ fi
+
+ if [ ! -d $dirname ]
+ then
+ # ignore return since mkdir has bug
+ mkdir -p $dirname
+ fi
+
+ cp $src $savepath &&
+ /usr/sadm/install/scripts/cmdexec /bin/sed install $savepath $dest
+
+ if [ $? -ne 0 ]
+ then
+ error=yes
+ fi
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/r.awk.sh b/usr/src/cmd/svr4pkg/pkgscripts/r.awk.sh
new file mode 100644
index 0000000000..4a166965e6
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.awk.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+ if [ -d $dest ]
+ then
+ echo "$dest"
+ rmdir $dest || error=yes
+ elif [ -f $dest ]
+ then
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/awk${client_dest}
+ else
+ savepath=$PKGSAV/awk${dest}
+ fi
+
+ /usr/sadm/install/scripts/cmdexec /usr/bin/awk remove $savepath $dest ||
+ error=yes
+ else
+ [ -r $dest ] && echo "$dest"
+ rm -f $dest || error=yes
+ fi
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/r.build.sh b/usr/src/cmd/svr4pkg/pkgscripts/r.build.sh
new file mode 100644
index 0000000000..ad09e64d02
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.build.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+ if [ -d $dest ]
+ then
+ echo "$dest"
+ rmdir $dest || error=yes
+ elif [ -f $dest ]
+ then
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/build${client_dest}
+ else
+ savepath=$PKGSAV/build${dest}
+ fi
+
+ chmod +x $savepath
+ if $savepath remove > /tmp/$$build
+ then
+ if [ ! -s /tmp/$$build ]
+ then
+ rm -f $dest
+ else
+ cp /tmp/$$build $dest || error=yes
+ fi
+ else
+ error=yes
+ fi
+ rm -f /tmp/$$build
+ else
+ [ -r $dest ] && echo "$dest"
+ rm -f $dest || error=yes
+ fi
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgscripts/r.sed.sh b/usr/src/cmd/svr4pkg/pkgscripts/r.sed.sh
new file mode 100644
index 0000000000..9c881cf5ee
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.sed.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+ if [ -d $dest ]
+ then
+ echo "$dest"
+ rmdir $dest || error=yes
+ elif [ -f $dest ]
+ then
+ echo "Modifying $dest"
+
+ # Strip PKG_INSTALL_ROOT from dest if installation is to an
+ # alternate root.
+
+ if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+ client_dest=`echo $dest | \
+ /usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+ { print substr($0, length(rootdir)+1)} }'`
+ savepath=$PKGSAV/sed${client_dest}
+ else
+ savepath=$PKGSAV/sed${dest}
+ fi
+
+ /usr/sadm/install/scripts/cmdexec /bin/sed remove $savepath $dest ||
+ error=yes
+ else
+ [ -r $dest ] && echo "$dest"
+ rm -f $dest || error=yes
+ fi
+done
+[ "$error" = yes ] &&
+ exit 2
+exit 0
diff --git a/usr/src/cmd/svr4pkg/pkgtrans/Makefile b/usr/src/cmd/svr4pkg/pkgtrans/Makefile
new file mode 100644
index 0000000000..e72d4c2155
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgtrans/Makefile
@@ -0,0 +1,41 @@
+#
+# 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= pkgtrans
+
+OBJS= main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS += -lpkg -ladm
+
+
+.KEEP_STATE:
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
diff --git a/usr/src/cmd/svr4pkg/pkgtrans/main.c b/usr/src/cmd/svr4pkg/pkgtrans/main.c
new file mode 100644
index 0000000000..bf1373f07b
--- /dev/null
+++ b/usr/src/cmd/svr4pkg/pkgtrans/main.c
@@ -0,0 +1,250 @@
+/*
+ * 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 <locale.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkgtrans.h>
+#include <pkglib.h>
+#include <pkglocs.h>
+#include <libadm.h>
+#include <libinst.h>
+
+static int options;
+static keystore_handle_t keystore = NULL;
+
+static void usage(void);
+static void trap(int signo);
+
+#define PASSWD_CMDLINE \
+ "## WARNING: USING <%s> MAKES PASSWORD " \
+ "VISIBLE TO ALL USERS."
+
+#define PASSPHRASE_PROMPT "Enter keystore password:"
+#define KEYSTORE_OPEN "Retrieving signing certificates from keystore <%s>"
+#define PARAM_LEN "Parameter <%s> too long"
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ void (*func)();
+ extern char *optarg;
+ extern int optind;
+ char *keystore_alias = NULL;
+ char *keystore_file = NULL;
+ boolean_t create_sig = B_FALSE;
+ char *homedir = NULL;
+ PKG_ERR *err;
+ int ret, len, homelen;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ (void) set_prog_name(argv[0]);
+
+ while ((c = getopt(argc, argv, "ga:P:k:snio?")) != EOF) {
+ switch (c) {
+ case 'n':
+ options |= PT_RENAME;
+ break;
+
+ case 'i':
+ options |= PT_INFO_ONLY;
+ break;
+
+ case 'o':
+ options |= PT_OVERWRITE;
+ break;
+
+ case 's':
+ options |= PT_ODTSTREAM;
+ break;
+
+ case 'g':
+ /* this should eventually be a PT_ option */
+ create_sig = B_TRUE;
+ break;
+
+ case 'k':
+ keystore_file = optarg;
+ break;
+
+ case 'a':
+ keystore_alias = optarg;
+ break;
+
+ case 'P':
+ set_passphrase_passarg(optarg);
+ if (ci_strneq(optarg, "pass:", 5)) {
+ /*
+ * passwords on the command line are highly
+ * insecure. complain.
+ */
+ logerr(gettext(PASSWD_CMDLINE), "pass:<pass>");
+ }
+ break;
+
+ default:
+ usage();
+ return (1);
+ }
+ }
+ func = signal(SIGINT, trap);
+ if (func != SIG_DFL)
+ (void) signal(SIGINT, func);
+ (void) signal(SIGHUP, trap);
+ (void) signal(SIGQUIT, trap);
+ (void) signal(SIGTERM, trap);
+ (void) signal(SIGPIPE, trap);
+#ifndef SUNOS41
+ (void) signal(SIGPWR, trap);
+#endif
+
+ if ((argc-optind) < 2) {
+ usage();
+ return (1);
+ }
+
+ if (create_sig) {
+ sec_init();
+ err = pkgerr_new();
+
+ /* figure out which keystore to use */
+ if (keystore_file == NULL) {
+ if (geteuid() == 0) {
+ /* we are superuser, so use their keystore */
+ keystore_file = PKGSEC;
+ } else {
+ if ((homedir = getenv("HOME")) == NULL) {
+ /*
+ * not superuser, but no home dir, so
+ * use superuser's keystore
+ */
+ keystore_file = PKGSEC;
+ } else {
+ /* $HOME/.pkg/security\0 */
+ homelen = strlen(homedir) + 15;
+ keystore_file =
+ malloc(strlen(homedir) + 15);
+ if (((len = snprintf(keystore_file,
+ homelen, "%s/%s", homedir,
+ ".pkg/security")) < 0) ||
+ (len >= homelen)) {
+ logerr(gettext(PARAM_LEN),
+ "$HOME");
+ quit(1);
+ }
+ }
+ }
+ }
+
+ logerr(gettext(KEYSTORE_OPEN), keystore_file);
+
+ set_passphrase_prompt(gettext(PASSPHRASE_PROMPT));
+
+ /* open keystore for reading */
+ if (open_keystore(err, keystore_file, get_prog_name(),
+ pkg_passphrase_cb, KEYSTORE_DFLT_FLAGS, &keystore) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ quit(1);
+ }
+
+ } else {
+ /* no signature, so don't use a keystore */
+ keystore = NULL;
+ }
+
+ ret = pkgtrans(flex_device(argv[optind], 1),
+ flex_device(argv[optind+1], 1), &argv[optind+2], options,
+ keystore, keystore_alias);
+
+ if (create_sig) {
+ /* close keystore */
+ if (close_keystore(err, keystore, NULL) != 0) {
+ pkgerr(err);
+ pkgerr_free(err);
+ quit(1);
+ }
+ keystore = NULL;
+ }
+
+ quit(ret);
+ /*NOTREACHED*/
+}
+
+void
+quit(int retcode)
+{
+ PKG_ERR *err;
+
+ err = pkgerr_new();
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) ds_close(1);
+ (void) pkghead(NULL);
+ if (keystore != NULL) {
+ (void) close_keystore(err, keystore, NULL);
+ pkgerr_free(err);
+ }
+ exit(retcode);
+}
+
+static void
+trap(int signo)
+{
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (signo == SIGINT) {
+ progerr(gettext("aborted at user request.\n"));
+ quit(3);
+ }
+ progerr(gettext("aborted by signal %d\n"), signo);
+ quit(1);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ gettext("usage: %s [-ionsg] [-k keystore] " \
+ "[-a alias] [-P password] srcdev dstdev [pkg [pkg...]]\n"),
+ get_prog_name());
+}