summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svr4pkg/libinst
diff options
context:
space:
mode:
authorMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
committerMoriah Waterland <Moriah.Waterland@Sun.COM>2009-06-03 20:16:25 -0600
commit5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch)
tree0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/cmd/svr4pkg/libinst
parent2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff)
downloadillumos-joyent-5c51f1241dbbdf2656d0e10011981411ed0c9673.tar.gz
6739234 move SVR4 packaging to ONNV gate
Diffstat (limited to 'usr/src/cmd/svr4pkg/libinst')
-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
40 files changed, 18964 insertions, 0 deletions
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);
+}