summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgjelinek <none@none>2008-07-13 10:43:22 -0700
committergjelinek <none@none>2008-07-13 10:43:22 -0700
commitff17c8bf86c3e567734be83f90267edee20f580f (patch)
tree9d59d65b3daf859d8c1ffcc50fb84da8934c1ea8
parent019c3c436f78111e4ecf8382da622143f7b45c6d (diff)
downloadillumos-gate-ff17c8bf86c3e567734be83f90267edee20f580f.tar.gz
6553514 native zone svr4 pkg code should be moved into zone callbacks
6621020 zlogin tries to write unneeded zero-length message and hangs 6704382 Upgrade process with zones freezes everytime using GUI installer on ultra 45 with 1024 MB of memory 6724480 wrong reference in embedded help for zoneadm 6588602 libbrand '%*' token expansion is a mess 6705863 brand dtd says zoneroot but it should be zonepath --HG-- rename : usr/src/cmd/zoneadm/sw_cmp.c => deleted_files/usr/src/cmd/zoneadm/sw_cmp.c rename : usr/src/lib/brand/native/zone/postclone.sh => deleted_files/usr/src/lib/brand/native/zone/postclone.sh
-rw-r--r--deleted_files/usr/src/cmd/zoneadm/sw_cmp.c (renamed from usr/src/cmd/zoneadm/sw_cmp.c)0
-rw-r--r--deleted_files/usr/src/lib/brand/native/zone/postclone.sh (renamed from usr/src/lib/brand/native/zone/postclone.sh)0
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/zlogin/zlogin.c148
-rw-r--r--usr/src/cmd/zoneadm/Makefile6
-rw-r--r--usr/src/cmd/zoneadm/zfs.c162
-rw-r--r--usr/src/cmd/zoneadm/zoneadm.c2038
-rw-r--r--usr/src/cmd/zoneadm/zoneadm.h17
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c12
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c14
-rw-r--r--usr/src/head/libzonecfg.h19
-rw-r--r--usr/src/lib/brand/lx/zone/config.xml2
-rw-r--r--usr/src/lib/brand/native/zone/Makefile13
-rw-r--r--usr/src/lib/brand/native/zone/config.xml13
-rw-r--r--usr/src/lib/brand/native/zone/sw_support.c3463
-rw-r--r--usr/src/lib/brand/sn1/zone/config.xml12
-rw-r--r--usr/src/lib/libbrand/common/libbrand.c182
-rw-r--r--usr/src/lib/libbrand/common/libbrand.h34
-rw-r--r--usr/src/lib/libbrand/common/mapfile-vers9
-rw-r--r--usr/src/lib/libbrand/dtd/brand.dtd.1237
-rw-r--r--usr/src/lib/libzonecfg/Makefile.com4
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c1621
-rw-r--r--usr/src/lib/libzonecfg/common/mapfile-vers12
-rw-r--r--usr/src/pkgdefs/SUNWzoneu/prototype_com2
24 files changed, 5224 insertions, 2797 deletions
diff --git a/usr/src/cmd/zoneadm/sw_cmp.c b/deleted_files/usr/src/cmd/zoneadm/sw_cmp.c
index 45f3b938a9..45f3b938a9 100644
--- a/usr/src/cmd/zoneadm/sw_cmp.c
+++ b/deleted_files/usr/src/cmd/zoneadm/sw_cmp.c
diff --git a/usr/src/lib/brand/native/zone/postclone.sh b/deleted_files/usr/src/lib/brand/native/zone/postclone.sh
index 63fc5e7bda..63fc5e7bda 100644
--- a/usr/src/lib/brand/native/zone/postclone.sh
+++ b/deleted_files/usr/src/lib/brand/native/zone/postclone.sh
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index aefa677f83..43aa2c8735 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -303,6 +303,7 @@ COMMON_SUBDIRS = \
cmd/ztest \
lib/abi \
lib/auditd_plugins \
+ lib/brand/native \
lib/brand/sn1 \
lib/crypt_modules \
lib/extendedFILE \
diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c
index 493d117616..3c65b5fd9f 100644
--- a/usr/src/cmd/zlogin/zlogin.c
+++ b/usr/src/cmd/zlogin/zlogin.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -500,6 +500,8 @@ sigwinch(int s)
(void) ioctl(masterfd, TIOCSWINSZ, &ws);
}
+static volatile int close_on_sig = -1;
+
static void
/*ARGSUSED*/
sigcld(int s)
@@ -513,10 +515,16 @@ sigcld(int s)
*/
if ((pid = waitpid(child_pid, &status, WNOHANG|WNOWAIT)) != -1) {
if (pid == child_pid &&
- (WIFEXITED(status) || WIFSIGNALED(status)))
+ (WIFEXITED(status) || WIFSIGNALED(status))) {
dead = 1;
- else
+ if (close_on_sig != -1) {
+ (void) write(close_on_sig, "a", 1);
+ (void) close(close_on_sig);
+ close_on_sig = -1;
+ }
+ } else {
(void) waitpid(pid, &status, WNOHANG);
+ }
}
}
@@ -576,15 +584,29 @@ canonify(char c, char *cc)
* to prevent deadlock between the two processes.
*
* This routine returns -1 when the 'quit' escape sequence has been issued,
- * and 0 otherwise.
+ * or an error is encountered, 1 if stdin is EOF, and 0 otherwise.
*/
static int
-process_user_input(int outfd, int infd, char *buf, size_t nbytes)
+process_user_input(int outfd, int infd)
{
static boolean_t beginning_of_line = B_TRUE;
static boolean_t local_echo = B_FALSE;
-
+ char ibuf[ZLOGIN_BUFSIZ];
+ int nbytes;
+ char *buf = ibuf;
char c = *buf;
+
+ nbytes = read(STDIN_FILENO, ibuf, ZLOGIN_RDBUFSIZ);
+ if (nbytes == -1 && (errno != EINTR || dead))
+ return (-1);
+
+ if (nbytes == -1) /* The read was interrupted. */
+ return (0);
+
+ /* 0 read means EOF, close the pipe to the child */
+ if (nbytes == 0)
+ return (1);
+
for (c = *buf; nbytes > 0; c = *buf, --nbytes) {
buf++;
if (beginning_of_line && !nocmdchar) {
@@ -625,10 +647,10 @@ retry:
if (errno == EAGAIN) {
struct timespec rqtp;
int ln;
- char ibuf[ZLOGIN_BUFSIZ];
+ char obuf[ZLOGIN_BUFSIZ];
- if ((ln = read(infd, ibuf, ZLOGIN_BUFSIZ)) > 0)
- (void) write(STDOUT_FILENO, ibuf, ln);
+ if ((ln = read(infd, obuf, ZLOGIN_BUFSIZ)) > 0)
+ (void) write(STDOUT_FILENO, obuf, ln);
/* sleep for 10 milliseconds */
rqtp.tv_sec = 0;
@@ -664,9 +686,9 @@ retry:
* read from our stdin. If the pipe already is pretty full, we bypass the read
* for now. We'll circle back here again after the poll() so that we can
* try again. When this function is called, we already know there is data
- * ready to read on STDIN_FILENO. We return -1 if there is a problem, 0
- * if everything is ok (even though we might not have read/written any data
- * into the pipe on this iteration).
+ * ready to read on STDIN_FILENO. We return -1 if there is a problem, 1 if
+ * stdin is EOF, and 0 if everything is ok (even though we might not have
+ * read/written any data into the pipe on this iteration).
*/
static int
process_raw_input(int stdin_fd, int appin_fd)
@@ -698,10 +720,13 @@ process_raw_input(int stdin_fd, int appin_fd)
if (cc == -1) /* The read was interrupted. */
return (0);
+ /* 0 read means EOF, close the pipe to the child */
+ if (cc == 0)
+ return (1);
+
/*
* stdin_fd is stdin of the target; so, the thing we'll write the user
- * data *to*. Also, unlike on the output side, we propagate
- * zero-length messages to the other side.
+ * data *to*.
*/
if (write(stdin_fd, ibuf, cc) == -1)
return (-1);
@@ -766,10 +791,10 @@ process_output(int in_fd, int out_fd)
*
*/
static void
-doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd,
+doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd, int sig_fd,
boolean_t raw_mode)
{
- struct pollfd pollfds[3];
+ struct pollfd pollfds[4];
char ibuf[ZLOGIN_BUFSIZ];
int cc, ret;
@@ -785,15 +810,31 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd,
pollfds[2].fd = STDIN_FILENO;
pollfds[2].events = pollfds[0].events;
+ /* read from signalling pipe so we know when child dies */
+ pollfds[3].fd = sig_fd;
+ pollfds[3].events = pollfds[0].events;
+
for (;;) {
pollfds[0].revents = pollfds[1].revents =
- pollfds[2].revents = 0;
+ pollfds[2].revents = pollfds[3].revents = 0;
if (dead)
break;
+ /*
+ * There is a race condition here where we can receive the
+ * child death signal, set the dead flag, but since we have
+ * passed the test above, we would go into poll and hang.
+ * To avoid this we use the sig_fd as an additional poll fd.
+ * The signal handler writes into the other end of this pipe
+ * when the child dies so that the poll will always see that
+ * input and proceed. We just loop around at that point and
+ * then notice the dead flag.
+ */
+
ret = poll(pollfds,
sizeof (pollfds) / sizeof (struct pollfd), -1);
+
if (ret == -1 && errno != EINTR) {
perror("poll failed");
break;
@@ -838,27 +879,30 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd,
* the thing we'll write the user data *to*.
*
* Also, unlike on the output side, we
- * propagate zero-length messages to the
- * other side.
+ * close the pipe on a zero-length message.
*/
- if (raw_mode == B_TRUE) {
- if (process_raw_input(stdin_fd,
- appin_fd) == -1)
- break;
- } else {
- cc = read(STDIN_FILENO, ibuf,
- ZLOGIN_RDBUFSIZ);
- if (cc == -1 &&
- (errno != EINTR || dead))
- break;
+ int res;
+
+ if (raw_mode)
+ res = process_raw_input(stdin_fd,
+ appin_fd);
+ else
+ res = process_user_input(stdin_fd,
+ stdout_fd);
- if (cc != -1 &&
- process_user_input(stdin_fd,
- stdout_fd, ibuf, cc) == -1)
+ if (res < 0)
+ break;
+ if (res > 0) {
+ /* EOF (close) child's stdin_fd */
+ pollfds[2].fd = -1;
+ while ((res = close(stdin_fd)) != 0 &&
+ errno == EINTR)
+ ;
+ if (res != 0)
break;
}
- } else if (raw_mode == B_TRUE &&
- pollfds[2].revents & POLLHUP) {
+
+ } else if (raw_mode && pollfds[2].revents & POLLHUP) {
/*
* It's OK to get a POLLHUP on STDIN-- it
* always happens if you do:
@@ -866,11 +910,17 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd,
* echo foo | zlogin <zone> <command>
*
* We reset fd to -1 in this case to clear
- * the condition and write an EOF to the
- * other side in order to wrap things up.
+ * the condition and close the pipe (EOF) to
+ * the other side in order to wrap things up.
*/
+ int res;
+
pollfds[2].fd = -1;
- (void) write(stdin_fd, ibuf, 0);
+ while ((res = close(stdin_fd)) != 0 &&
+ errno == EINTR)
+ ;
+ if (res != 0)
+ break;
} else {
pollerr = pollfds[2].revents;
break;
@@ -1454,7 +1504,7 @@ noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
char **new_args, char **new_env)
{
pid_t retval;
- int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
+ int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2], dead_child_pipe[2];
int child_status;
int tmpl_fd;
sigset_t block_cld;
@@ -1489,6 +1539,12 @@ noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
return (1);
}
+ if (pipe(dead_child_pipe) != 0) {
+ zperror(gettext("could not create signalling pipe"));
+ return (1);
+ }
+ close_on_sig = dead_child_pipe[0];
+
/*
* If any of the pipe FD's winds up being less than STDERR, then we
* have a mess on our hands-- and we are lacking some of the I/O
@@ -1499,7 +1555,9 @@ noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
stdout_pipe[0] <= STDERR_FILENO ||
stdout_pipe[1] <= STDERR_FILENO ||
stderr_pipe[0] <= STDERR_FILENO ||
- stderr_pipe[1] <= STDERR_FILENO) {
+ stderr_pipe[1] <= STDERR_FILENO ||
+ dead_child_pipe[0] <= STDERR_FILENO ||
+ dead_child_pipe[1] <= STDERR_FILENO) {
zperror(gettext("process lacks valid STDIN, STDOUT, STDERR"));
return (1);
}
@@ -1579,6 +1637,11 @@ noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
_exit(1);
}
/* parent */
+
+ /* close pipe sides written by child */
+ (void) close(stdout_pipe[1]);
+ (void) close(stderr_pipe[1]);
+
(void) sigset(SIGINT, sig_forward);
postfork_dropprivs();
@@ -1588,7 +1651,7 @@ noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
(void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL);
doio(stdin_pipe[0], stdin_pipe[1], stdout_pipe[0], stderr_pipe[0],
- B_TRUE);
+ dead_child_pipe[1], B_TRUE);
do {
retval = waitpid(child_pid, &child_status, 0);
if (retval == -1) {
@@ -1762,7 +1825,6 @@ main(int argc, char **argv)
* it first.
*/
if (console) {
-
/*
* Ensure that zoneadmd for this zone is running.
*/
@@ -1790,7 +1852,7 @@ main(int argc, char **argv)
/*
* Run the I/O loop until we get disconnected.
*/
- doio(masterfd, -1, masterfd, -1, B_FALSE);
+ doio(masterfd, -1, masterfd, -1, -1, B_FALSE);
reset_tty();
(void) printf(gettext("\n[Connection to zone '%s' console "
"closed]\n"), zonename);
@@ -2042,7 +2104,7 @@ main(int argc, char **argv)
postfork_dropprivs();
(void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL);
- doio(masterfd, -1, masterfd, -1, B_FALSE);
+ doio(masterfd, -1, masterfd, -1, -1, B_FALSE);
reset_tty();
(void) fprintf(stderr,
diff --git a/usr/src/cmd/zoneadm/Makefile b/usr/src/cmd/zoneadm/Makefile
index 0ec4d19bdc..8dbc8aa0e7 100644
--- a/usr/src/cmd/zoneadm/Makefile
+++ b/usr/src/cmd/zoneadm/Makefile
@@ -34,13 +34,13 @@ include ../Makefile.cmd
ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
-OBJS= zoneadm.o sw_cmp.o zfs.o
+OBJS= zoneadm.o zfs.o
SRCS = $(OBJS:.o=.c)
POFILE=zoneadm_all.po
POFILES= $(OBJS:%.o=%.po)
-LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid -lnvpair -lbrand \
- -ldlpi -luutil
+LDLIBS += -lzonecfg -lsocket -lgen -lpool -lzfs -luuid -lnvpair -lbrand \
+ -ldlpi
.KEEP_STATE:
diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c
index 68a90dbfe2..cb06a06a49 100644
--- a/usr/src/cmd/zoneadm/zfs.c
+++ b/usr/src/cmd/zoneadm/zfs.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -155,76 +155,43 @@ is_mountpnt(char *path)
}
/*
- * Perform any necessary housekeeping tasks we need to do before we take
- * a ZFS snapshot of the zone. What this really entails is that we are
- * taking a sw inventory of the source zone, like we do when we detach,
- * so that there is the XML manifest in the snapshot. We use that to
- * validate the snapshot if it is the source of a clone at some later time.
+ * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
*/
static int
-pre_snapshot(char *source_zone)
+pre_snapshot(char *presnapbuf)
{
- int err;
- zone_dochandle_t handle;
+ int status;
- if ((handle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- return (Z_ERR);
- }
+ /* No brand-specific handler */
+ if (presnapbuf[0] == '\0')
+ return (Z_OK);
- if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) {
- errno = err;
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- zonecfg_fini_handle(handle);
+ /* Run the hook */
+ status = do_subproc_interactive(presnapbuf);
+ if ((status = subproc_status(gettext("brand-specific presnapshot"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK)
return (Z_ERR);
- }
-
- if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) {
- errno = err;
- zperror(gettext("getting the software version information "
- "failed"), B_TRUE);
- zonecfg_fini_handle(handle);
- return (Z_ERR);
- }
- if ((err = zonecfg_detach_save(handle, 0)) != Z_OK) {
- errno = err;
- zperror(gettext("saving the software version manifest failed"),
- B_TRUE);
- zonecfg_fini_handle(handle);
- return (Z_ERR);
- }
-
- zonecfg_fini_handle(handle);
return (Z_OK);
}
/*
- * Perform any necessary housekeeping tasks we need to do after we take
- * a ZFS snapshot of the zone. What this really entails is removing the
- * sw inventory XML file from the zone. It is still in the snapshot where
- * we want it, but we don't want it in the source zone itself.
+ * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
*/
static int
-post_snapshot(char *source_zone)
+post_snapshot(char *postsnapbuf)
{
- int err;
- zone_dochandle_t handle;
+ int status;
- if ((handle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- return (Z_ERR);
- }
+ /* No brand-specific handler */
+ if (postsnapbuf[0] == '\0')
+ return (Z_OK);
- if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) {
- errno = err;
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- zonecfg_fini_handle(handle);
+ /* Run the hook */
+ status = do_subproc_interactive(postsnapbuf);
+ if ((status = subproc_status(gettext("brand-specific postsnapshot"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK)
return (Z_ERR);
- }
-
- zonecfg_rm_detached(handle, B_FALSE);
- zonecfg_fini_handle(handle);
return (Z_OK);
}
@@ -265,8 +232,8 @@ get_snap_max(zfs_handle_t *zhp, void *data)
* Take a ZFS snapshot to be used for cloning the zone.
*/
static int
-take_snapshot(char *source_zone, zfs_handle_t *zhp, char *snapshot_name,
- int snap_size)
+take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
+ char *presnapbuf, char *postsnapbuf)
{
int res;
char template[ZFS_MAXNAMELEN];
@@ -294,10 +261,10 @@ take_snapshot(char *source_zone, zfs_handle_t *zhp, char *snapshot_name,
zfs_get_name(zhp), cb.max) >= snap_size)
return (Z_ERR);
- if (pre_snapshot(source_zone) != Z_OK)
+ if (pre_snapshot(presnapbuf) != Z_OK)
return (Z_ERR);
res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE);
- if (post_snapshot(source_zone) != Z_OK)
+ if (post_snapshot(postsnapbuf) != Z_OK)
return (Z_ERR);
if (res != 0)
@@ -307,66 +274,32 @@ take_snapshot(char *source_zone, zfs_handle_t *zhp, char *snapshot_name,
/*
* We are using an explicit snapshot from some earlier point in time so
- * we need to validate it. This involves checking the sw inventory that
- * we took when we made the snapshot to verify that the current sw config
- * on the host is still valid to run a zone made from this snapshot.
+ * we need to validate it. Run the brand specific hook.
*/
static int
-validate_snapshot(char *snapshot_name, char *snap_path)
+validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
{
- int err;
- zone_dochandle_t handle;
- zone_dochandle_t athandle = NULL;
+ int status;
+ char cmdbuf[MAXPATHLEN];
- if ((handle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- return (Z_ERR);
- }
+ /* No brand-specific handler */
+ if (validsnapbuf[0] == '\0')
+ return (Z_OK);
- if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
- errno = err;
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- zonecfg_fini_handle(handle);
+ /* pass args - snapshot_name & snap_path */
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
+ snapshot_name, snap_path) >= sizeof (cmdbuf)) {
+ zerror("Command line too long");
return (Z_ERR);
}
- if ((athandle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_CLONE), B_TRUE);
- goto done;
- }
-
- if ((err = zonecfg_get_attach_handle(snap_path, target_zone, B_TRUE,
- athandle)) != Z_OK) {
- if (err == Z_NO_ZONE)
- (void) fprintf(stderr, gettext("snapshot %s was not "
- "taken\n\tby a 'zoneadm clone' command. It can "
- "not be used to clone zones.\n"), snapshot_name);
- else
- (void) fprintf(stderr, gettext("snapshot %s is "
- "out-dated\n\tIt can no longer be used to clone "
- "zones on this system.\n"), snapshot_name);
- goto done;
- }
-
- /* Get the detach information for the locally defined zone. */
- if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) {
- errno = err;
- zperror(gettext("getting the attach information failed"),
- B_TRUE);
- goto done;
- }
-
- if ((err = sw_cmp(handle, athandle, SW_CMP_SILENT)) != Z_OK)
- (void) fprintf(stderr, gettext("snapshot %s is out-dated\n\t"
- "It can no longer be used to clone zones on this "
- "system.\n"), snapshot_name);
-
-done:
- zonecfg_fini_handle(handle);
- if (athandle != NULL)
- zonecfg_fini_handle(athandle);
+ /* Run the hook */
+ status = do_subproc_interactive(cmdbuf);
+ if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK)
+ return (Z_ERR);
- return (err);
+ return (Z_OK);
}
/*
@@ -569,7 +502,7 @@ snap2path(char *snap_name, char *path, int len)
* possible, or by copying the data from the snapshot to the zonepath.
*/
int
-clone_snapshot_zfs(char *snap_name, char *zonepath)
+clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
{
int err = Z_OK;
char clone_name[MAXPATHLEN];
@@ -581,7 +514,7 @@ clone_snapshot_zfs(char *snap_name, char *zonepath)
return (Z_ERR);
}
- if (validate_snapshot(snap_name, snap_path) != Z_OK)
+ if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
return (Z_NO_ENTRY);
/*
@@ -635,7 +568,8 @@ clone_snapshot_zfs(char *snap_name, char *zonepath)
* Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
*/
int
-clone_zfs(char *source_zone, char *source_zonepath, char *zonepath)
+clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
+ char *postsnapbuf)
{
zfs_handle_t *zhp;
char clone_name[MAXPATHLEN];
@@ -678,8 +612,8 @@ clone_zfs(char *source_zone, char *source_zonepath, char *zonepath)
return (Z_ERR);
}
- if (take_snapshot(source_zone, zhp, snap_name, sizeof (snap_name))
- != Z_OK) {
+ if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
+ postsnapbuf) != Z_OK) {
zfs_close(zhp);
return (Z_ERR);
}
diff --git a/usr/src/cmd/zoneadm/zoneadm.c b/usr/src/cmd/zoneadm/zoneadm.c
index 65c522b9fc..79d25f2733 100644
--- a/usr/src/cmd/zoneadm/zoneadm.c
+++ b/usr/src/cmd/zoneadm/zoneadm.c
@@ -135,11 +135,12 @@ struct cmd {
#define SHELP_LIST "list [-cipv]"
#define SHELP_VERIFY "verify"
#define SHELP_INSTALL "install [-x nodataset] [brand-specific args]"
-#define SHELP_UNINSTALL "uninstall [-F]"
-#define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] zonename"
+#define SHELP_UNINSTALL "uninstall [-F] [brand-specific args]"
+#define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] "\
+ "[brand-specific args] zonename"
#define SHELP_MOVE "move zonepath"
-#define SHELP_DETACH "detach [-n]"
-#define SHELP_ATTACH "attach [-F] [-n <path>] [-u]"
+#define SHELP_DETACH "detach [-n] [brand-specific args]"
+#define SHELP_ATTACH "attach [-F] [-n <path>] [brand-specific args]"
#define SHELP_MARK "mark incomplete"
#define EXEC_PREFIX "exec "
@@ -206,8 +207,6 @@ static char *target_uuid;
/* used in do_subproc() and signal handler */
static volatile boolean_t child_killed;
-/* used in attach_func() and signal handler */
-static volatile boolean_t attach_interupted;
static int do_subproc_cnt = 0;
/*
@@ -216,14 +215,6 @@ static int do_subproc_cnt = 0;
*/
static boolean_t zoneadm_is_nested = B_FALSE;
-/* used to track nested zone-lock operations */
-static int zone_lock_cnt = 0;
-
-/* used to communicate lock status to children */
-#define LOCK_ENV_VAR "_ZONEADM_LOCK_HELD"
-static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1";
-static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0";
-
char *
cmd_to_str(int cmd_num)
{
@@ -278,10 +269,12 @@ long_help(int cmd_num)
"creation of a new ZFS file system for the\n\tzone "
"(assuming the zonepath is within a ZFS file system).\n\t"
"All other arguments are passed to the brand installation "
- "function;\n\tsee brand(4) for more information."));
+ "function;\n\tsee brands(5) for more information."));
case CMD_UNINSTALL:
return (gettext("Uninstall the configuration from the system. "
- "The -F flag can be used\n\tto force the action."));
+ "The -F flag can be used\n\tto force the action. All "
+ "other arguments are passed to the brand\n\tuninstall "
+ "function; see brands(5) for more information."));
case CMD_CLONE:
return (gettext("Clone the installation of another zone. "
"The -m option can be used to\n\tspecify 'copy' which "
@@ -289,7 +282,9 @@ long_help(int cmd_num)
"can be used to specify the name of a ZFS snapshot "
"that was taken from\n\ta previous clone command. The "
"snapshot will be used as the source\n\tinstead of "
- "creating a new ZFS snapshot."));
+ "creating a new ZFS snapshot. All other arguments are "
+ "passed\n\tto the brand clone function; see "
+ "brands(5) for more information."));
case CMD_MOVE:
return (gettext("Move the zone to a new zonepath."));
case CMD_DETACH:
@@ -300,22 +295,25 @@ long_help(int cmd_num)
"attached there. The -n option can be used to specify\n\t"
"'no-execute' mode. When -n is used, the information "
"needed to attach\n\tthe zone is sent to standard output "
- "but the zone is not actually\n\tdetached."));
+ "but the zone is not actually\n\tdetached. All other "
+ "arguments are passed to the brand detach function;\n\tsee "
+ "brands(5) for more information."));
case CMD_ATTACH:
return (gettext("Attach the zone to the system. The zone "
"state must be 'configured'\n\tprior to attach; upon "
"successful completion, the zone state will be\n\t"
"'installed'. The system software on the current "
"system must be\n\tcompatible with the software on the "
- "zone's original system or use\n\tthe -u option to update "
- "the zone to the current system software.\n\tSpecify -F "
+ "zone's original system.\n\tSpecify -F "
"to force the attach and skip software compatibility "
"tests.\n\tThe -n option can be used to specify "
"'no-execute' mode. When -n is\n\tused, the information "
"needed to attach the zone is read from the\n\tspecified "
"path and the configuration is only validated. The path "
- "can\n\tbe '-' to specify standard input. The -F, -n and "
- "-u options are\n\tmutually exclusive."));
+ "can\n\tbe '-' to specify standard input. The -F and -n "
+ "options are mutually\n\texclusive. All other arguments "
+ "are passed to the brand attach\n\tfunction; see "
+ "brands(5) for more information."));
case CMD_MARK:
return (gettext("Set the state of the zone. This can be used "
"to force the zone\n\tstate to 'incomplete' "
@@ -1118,351 +1116,6 @@ validate_zonepath(char *path, int cmd_num)
return (err ? Z_ERR : Z_OK);
}
-/*
- * The following two routines implement a simple locking mechanism to
- * ensure that only one instance of zoneadm at a time is able to manipulate
- * a given zone. The lock is built on top of an fcntl(2) lock of
- * [<altroot>]/var/run/zones/<zonename>.zoneadm.lock. If a zoneadm instance
- * can grab that lock, it is allowed to manipulate the zone.
- *
- * Since zoneadm may call external applications which in turn invoke
- * zoneadm again, we introduce the notion of "lock inheritance". Any
- * instance of zoneadm that has another instance in its ancestry is assumed
- * to be acting on behalf of the original zoneadm, and is thus allowed to
- * manipulate its zone.
- *
- * This inheritance is implemented via the _ZONEADM_LOCK_HELD environment
- * variable. When zoneadm is granted a lock on its zone, this environment
- * variable is set to 1. When it releases the lock, the variable is set to
- * 0. Since a child process inherits its parent's environment, checking
- * the state of this variable indicates whether or not any ancestor owns
- * the lock.
- */
-static void
-release_lock_file(int lockfd)
-{
- /*
- * If we are cleaning up from a failed attempt to lock the zone for
- * the first time, we might have a zone_lock_cnt of 0. In that
- * error case, we don't want to do anything but close the lock
- * file.
- */
- assert(zone_lock_cnt >= 0);
- if (zone_lock_cnt > 0) {
- assert(getenv(LOCK_ENV_VAR) != NULL);
- assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
- if (--zone_lock_cnt > 0) {
- assert(lockfd == -1);
- return;
- }
- if (putenv(zoneadm_lock_not_held) != 0) {
- zperror(target_zone, B_TRUE);
- exit(Z_ERR);
- }
- }
- assert(lockfd >= 0);
- (void) close(lockfd);
-}
-
-static int
-grab_lock_file(const char *zone_name, int *lockfd)
-{
- char pathbuf[PATH_MAX];
- struct flock flock;
-
- /*
- * If we already have the lock, we can skip this expensive song
- * and dance.
- */
- if (zone_lock_cnt > 0) {
- zone_lock_cnt++;
- *lockfd = -1;
- return (Z_OK);
- }
- assert(getenv(LOCK_ENV_VAR) != NULL);
- assert(atoi(getenv(LOCK_ENV_VAR)) == 0);
-
- if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(),
- ZONES_TMPDIR) >= sizeof (pathbuf)) {
- zerror(gettext("alternate root path is too long"));
- return (Z_ERR);
- }
- if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) {
- zerror(gettext("could not mkdir %s: %s"), pathbuf,
- strerror(errno));
- return (Z_ERR);
- }
- (void) chmod(pathbuf, S_IRWXU);
-
- /*
- * One of these lock files is created for each zone (when needed).
- * The lock files are not cleaned up (except on system reboot),
- * but since there is only one per zone, there is no resource
- * starvation issue.
- */
- if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock",
- zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) {
- zerror(gettext("alternate root path is too long"));
- return (Z_ERR);
- }
- if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
- zerror(gettext("could not open %s: %s"), pathbuf,
- strerror(errno));
- return (Z_ERR);
- }
- /*
- * Lock the file to synchronize with other zoneadmds
- */
- flock.l_type = F_WRLCK;
- flock.l_whence = SEEK_SET;
- flock.l_start = (off_t)0;
- flock.l_len = (off_t)0;
- if ((fcntl(*lockfd, F_SETLKW, &flock) < 0) ||
- (putenv(zoneadm_lock_held) != 0)) {
- zerror(gettext("unable to lock %s: %s"), pathbuf,
- strerror(errno));
- release_lock_file(*lockfd);
- return (Z_ERR);
- }
- zone_lock_cnt = 1;
- return (Z_OK);
-}
-
-static boolean_t
-get_doorname(const char *zone_name, char *buffer)
-{
- return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH,
- zonecfg_get_root(), zone_name) < PATH_MAX);
-}
-
-/*
- * system daemons are not audited. For the global zone, this occurs
- * "naturally" since init is started with the default audit
- * characteristics. Since zoneadmd is a system daemon and it starts
- * init for a zone, it is necessary to clear out the audit
- * characteristics inherited from whomever started zoneadmd. This is
- * indicated by the audit id, which is set from the ruid parameter of
- * adt_set_user(), below.
- */
-
-static void
-prepare_audit_context()
-{
- adt_session_data_t *ah;
- char *failure = gettext("audit failure: %s");
-
- if (adt_start_session(&ah, NULL, 0)) {
- zerror(failure, strerror(errno));
- return;
- }
- if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT,
- ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) {
- zerror(failure, strerror(errno));
- (void) adt_end_session(ah);
- return;
- }
- if (adt_set_proc(ah))
- zerror(failure, strerror(errno));
-
- (void) adt_end_session(ah);
-}
-
-static int
-start_zoneadmd(const char *zone_name)
-{
- char doorpath[PATH_MAX];
- pid_t child_pid;
- int error = Z_ERR;
- int doorfd, lockfd;
- struct door_info info;
-
- if (!get_doorname(zone_name, doorpath))
- return (Z_ERR);
-
- if (grab_lock_file(zone_name, &lockfd) != Z_OK)
- return (Z_ERR);
-
- /*
- * Now that we have the lock, re-confirm that the daemon is
- * *not* up and working fine. If it is still down, we have a green
- * light to start it.
- */
- if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
- if (errno != ENOENT) {
- zperror(doorpath, B_FALSE);
- goto out;
- }
- } else {
- if (door_info(doorfd, &info) == 0 &&
- ((info.di_attributes & DOOR_REVOKED) == 0)) {
- error = Z_OK;
- (void) close(doorfd);
- goto out;
- }
- (void) close(doorfd);
- }
-
- if ((child_pid = fork()) == -1) {
- zperror(gettext("could not fork"), B_FALSE);
- goto out;
- } else if (child_pid == 0) {
- const char *argv[6], **ap;
-
- /* child process */
- prepare_audit_context();
-
- ap = argv;
- *ap++ = "zoneadmd";
- *ap++ = "-z";
- *ap++ = zone_name;
- if (zonecfg_in_alt_root()) {
- *ap++ = "-R";
- *ap++ = zonecfg_get_root();
- }
- *ap = NULL;
-
- (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv);
- /*
- * TRANSLATION_NOTE
- * zoneadmd is a literal that should not be translated.
- */
- zperror(gettext("could not exec zoneadmd"), B_FALSE);
- _exit(Z_ERR);
- } else {
- /* parent process */
- pid_t retval;
- int pstatus = 0;
-
- do {
- retval = waitpid(child_pid, &pstatus, 0);
- } while (retval != child_pid);
- if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) &&
- WEXITSTATUS(pstatus) != 0)) {
- zerror(gettext("could not start %s"), "zoneadmd");
- goto out;
- }
- }
- error = Z_OK;
-out:
- release_lock_file(lockfd);
- return (error);
-}
-
-static int
-ping_zoneadmd(const char *zone_name)
-{
- char doorpath[PATH_MAX];
- int doorfd;
- struct door_info info;
-
- if (!get_doorname(zone_name, doorpath))
- return (Z_ERR);
-
- if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
- return (Z_ERR);
- }
- if (door_info(doorfd, &info) == 0 &&
- ((info.di_attributes & DOOR_REVOKED) == 0)) {
- (void) close(doorfd);
- return (Z_OK);
- }
- (void) close(doorfd);
- return (Z_ERR);
-}
-
-static int
-call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg)
-{
- char doorpath[PATH_MAX];
- int doorfd, result;
- door_arg_t darg;
-
- zoneid_t zoneid;
- uint64_t uniqid = 0;
-
- zone_cmd_rval_t *rvalp;
- size_t rlen;
- char *cp, *errbuf;
-
- rlen = getpagesize();
- if ((rvalp = malloc(rlen)) == NULL) {
- zerror(gettext("failed to allocate %lu bytes: %s"), rlen,
- strerror(errno));
- return (-1);
- }
-
- if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) {
- (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid,
- sizeof (uniqid));
- }
- arg->uniqid = uniqid;
- (void) strlcpy(arg->locale, locale, sizeof (arg->locale));
- if (!get_doorname(zone_name, doorpath)) {
- zerror(gettext("alternate root path is too long"));
- free(rvalp);
- return (-1);
- }
-
- /*
- * Loop trying to start zoneadmd; if something goes seriously
- * wrong we break out and fail.
- */
- for (;;) {
- if (start_zoneadmd(zone_name) != Z_OK)
- break;
-
- if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
- zperror(gettext("failed to open zone door"), B_FALSE);
- break;
- }
-
- darg.data_ptr = (char *)arg;
- darg.data_size = sizeof (*arg);
- darg.desc_ptr = NULL;
- darg.desc_num = 0;
- darg.rbuf = (char *)rvalp;
- darg.rsize = rlen;
- if (door_call(doorfd, &darg) != 0) {
- (void) close(doorfd);
- /*
- * We'll get EBADF if the door has been revoked.
- */
- if (errno != EBADF) {
- zperror(gettext("door_call failed"), B_FALSE);
- break;
- }
- continue; /* take another lap */
- }
- (void) close(doorfd);
-
- if (darg.data_size == 0) {
- /* Door server is going away; kick it again. */
- continue;
- }
-
- errbuf = rvalp->errbuf;
- while (*errbuf != '\0') {
- /*
- * Remove any newlines since zerror()
- * will append one automatically.
- */
- cp = strchr(errbuf, '\n');
- if (cp != NULL)
- *cp = '\0';
- zerror("%s", errbuf);
- if (cp == NULL)
- break;
- errbuf = cp + 1;
- }
- result = rvalp->rval == 0 ? 0 : -1;
- free(rvalp);
- return (result);
- }
-
- free(rvalp);
- return (-1);
-}
-
static int
invoke_brand_handler(int cmd_num, char *argv[])
{
@@ -1520,7 +1173,7 @@ ready_func(int argc, char *argv[])
return (Z_ERR);
zarg.cmd = Z_READY;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
+ if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) {
zerror(gettext("call to %s failed"), "zoneadmd");
return (Z_ERR);
}
@@ -1595,7 +1248,7 @@ boot_func(int argc, char *argv[])
if (verify_details(CMD_BOOT, argv) != Z_OK)
return (Z_ERR);
zarg.cmd = force ? Z_FORCEBOOT : Z_BOOT;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
+ if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) {
zerror(gettext("call to %s failed"), "zoneadmd");
return (Z_ERR);
}
@@ -1822,7 +1475,7 @@ do_subproc(char *cmdbuf)
return (pclose(file));
}
-static int
+int
do_subproc_interactive(char *cmdbuf)
{
void (*saveint)(int);
@@ -1861,7 +1514,7 @@ do_subproc_interactive(char *cmdbuf)
return (pid == -1 ? -1 : status);
}
-static int
+int
subproc_status(const char *cmd, int status, boolean_t verbose_failure)
{
if (WIFEXITED(status)) {
@@ -2132,7 +1785,8 @@ halt_func(int argc, char *argv[])
return (Z_ERR);
zarg.cmd = Z_HALT;
- return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR);
+ return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale,
+ B_TRUE) == 0) ? Z_OK : Z_ERR);
}
static int
@@ -2186,7 +1840,24 @@ reboot_func(int argc, char *argv[])
return (Z_ERR);
zarg.cmd = Z_REBOOT;
- return ((call_zoneadmd(target_zone, &zarg) == 0) ? Z_OK : Z_ERR);
+ return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0)
+ ? Z_OK : Z_ERR);
+}
+
+static int
+get_hook(brand_handle_t bh, char *cmd, size_t len, int (*bp)(brand_handle_t,
+ const char *, const char *, char *, size_t), char *zonename, char *zonepath)
+{
+ if (strlcpy(cmd, EXEC_PREFIX, len) >= len)
+ return (Z_ERR);
+
+ if (bp(bh, zonename, zonepath, cmd + EXEC_LEN, len - EXEC_LEN) != 0)
+ return (Z_ERR);
+
+ if (strlen(cmd) <= EXEC_LEN)
+ cmd[0] = '\0';
+
+ return (Z_OK);
}
static int
@@ -2203,8 +1874,10 @@ verify_brand(zone_dochandle_t handle, int cmd_num, char *argv[])
* "exec" the command so that the returned status is that of
* the command and not the shell.
*/
- if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) !=
- Z_OK) {
+ if (handle == NULL) {
+ (void) strlcpy(zonepath, "-", sizeof (zonepath));
+ } else if ((err = zonecfg_get_zonepath(handle, zonepath,
+ sizeof (zonepath))) != Z_OK) {
errno = err;
zperror(cmd_to_str(cmd_num), B_TRUE);
return (Z_ERR);
@@ -2220,13 +1893,12 @@ verify_brand(zone_dochandle_t handle, int cmd_num, char *argv[])
* operation for the specific brand. The zoneadm subcommand and
* all its arguments are passed to the routine.
*/
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- err = brand_get_verify_adm(bh, target_zone, zonepath,
- cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN, 0, NULL);
+ err = get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_verify_adm,
+ target_zone, zonepath);
brand_close(bh);
- if (err != 0)
+ if (err != Z_OK)
return (Z_BRAND_ERROR);
- if (strlen(cmdbuf) <= EXEC_LEN)
+ if (cmdbuf[0] == '\0')
return (Z_OK);
if (strlcat(cmdbuf, cmd_to_str(cmd_num),
@@ -3122,6 +2794,25 @@ verify_func(int argc, char *argv[])
}
static int
+addoptions(char *buf, char *argv[], size_t len)
+{
+ int i = 0;
+
+ if (buf[0] == '\0')
+ return (Z_OK);
+
+ while (argv[i] != NULL) {
+ if (strlcat(buf, " ", len) >= len ||
+ strlcat(buf, argv[i++], len) >= len) {
+ zerror("Command line too long");
+ return (Z_ERR);
+ }
+ }
+
+ return (Z_OK);
+}
+
+static int
addopt(char *buf, int opt, char *optarg, size_t bufsize)
{
char optstring[4];
@@ -3133,11 +2824,14 @@ addopt(char *buf, int opt, char *optarg, size_t bufsize)
if ((strlcat(buf, optstring, bufsize) > bufsize))
return (Z_ERR);
+
if ((optarg != NULL) && (strlcat(buf, optarg, bufsize) > bufsize))
return (Z_ERR);
+
return (Z_OK);
}
+/* ARGSUSED */
static int
install_func(int argc, char *argv[])
{
@@ -3150,6 +2844,7 @@ install_func(int argc, char *argv[])
int status;
boolean_t nodataset = B_FALSE;
boolean_t do_postinstall = B_FALSE;
+ boolean_t brand_help = B_FALSE;
char opts[128];
if (target_zone == NULL) {
@@ -3175,149 +2870,135 @@ install_func(int argc, char *argv[])
return (Z_ERR);
}
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_install(bh, target_zone, zonepath, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL) != 0) {
+ if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_install,
+ target_zone, zonepath) != Z_OK) {
zerror("invalid brand configuration: missing install resource");
brand_close(bh);
return (Z_ERR);
}
- (void) strcpy(postcmdbuf, EXEC_PREFIX);
- if (brand_get_postinstall(bh, target_zone, zonepath,
- postcmdbuf + EXEC_LEN, sizeof (postcmdbuf) - EXEC_LEN, 0, NULL)
- != 0) {
+ if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postinstall,
+ target_zone, zonepath) != Z_OK) {
zerror("invalid brand configuration: missing postinstall "
"resource");
brand_close(bh);
return (Z_ERR);
- } else if (strlen(postcmdbuf) > EXEC_LEN) {
- do_postinstall = B_TRUE;
}
+ if (postcmdbuf[0] != '\0')
+ do_postinstall = B_TRUE;
+
(void) strcpy(opts, "?x:");
- if (!is_native_zone) {
- /*
- * Fetch the list of recognized command-line options from
- * the brand configuration file.
- */
- if (brand_get_installopts(bh, opts + strlen(opts),
- sizeof (opts) - strlen(opts)) != 0) {
- zerror("invalid brand configuration: missing "
- "install options resource");
- brand_close(bh);
- return (Z_ERR);
- }
+ /*
+ * Fetch the list of recognized command-line options from
+ * the brand configuration file.
+ */
+ if (brand_get_installopts(bh, opts + strlen(opts),
+ sizeof (opts) - strlen(opts)) != 0) {
+ zerror("invalid brand configuration: missing "
+ "install options resource");
+ brand_close(bh);
+ return (Z_ERR);
}
+
brand_close(bh);
+ if (cmdbuf[0] == '\0') {
+ zerror("Missing brand install command");
+ return (Z_ERR);
+ }
+
+ /* Check the argv string for args we handle internally */
optind = 0;
+ opterr = 0;
while ((arg = getopt(argc, argv, opts)) != EOF) {
switch (arg) {
case '?':
- sub_usage(SHELP_INSTALL, CMD_INSTALL);
- return (optopt == '?' ? Z_OK : Z_USAGE);
- case 'x':
- if (strcmp(optarg, "nodataset") != 0) {
+ if (optopt == '?') {
sub_usage(SHELP_INSTALL, CMD_INSTALL);
- return (Z_USAGE);
+ brand_help = B_TRUE;
}
- nodataset = B_TRUE;
+ /* Ignore unknown options - may be brand specific. */
break;
- default:
- if (is_native_zone) {
- sub_usage(SHELP_INSTALL, CMD_INSTALL);
- return (Z_USAGE);
- }
-
- /*
- * This option isn't for zoneadm, so append it to
- * the command line passed to the brand-specific
- * install and postinstall routines.
- */
- if (addopt(cmdbuf, optopt, optarg,
- sizeof (cmdbuf)) != Z_OK) {
- zerror("Install command line too long");
- return (Z_ERR);
- }
- if (addopt(postcmdbuf, optopt, optarg,
- sizeof (postcmdbuf)) != Z_OK) {
- zerror("Post-Install command line too long");
- return (Z_ERR);
+ case 'x':
+ /* Handle this option internally, don't pass to brand */
+ if (strcmp(optarg, "nodataset") == 0) {
+ /* Handle this option internally */
+ nodataset = B_TRUE;
}
+ continue;
+ default:
+ /* Ignore unknown options - may be brand specific. */
break;
}
- }
- if (!is_native_zone) {
- for (; optind < argc; optind++) {
- if (addopt(cmdbuf, 0, argv[optind],
- sizeof (cmdbuf)) != Z_OK) {
- zerror("Install command line too long");
- return (Z_ERR);
- }
- if (addopt(postcmdbuf, 0, argv[optind],
- sizeof (postcmdbuf)) != Z_OK) {
- zerror("Post-Install command line too long");
- return (Z_ERR);
- }
+ /*
+ * Append the option to the command line passed to the
+ * brand-specific install and postinstall routines.
+ */
+ if (addopt(cmdbuf, optopt, optarg, sizeof (cmdbuf)) != Z_OK) {
+ zerror("Install command line too long");
+ return (Z_ERR);
+ }
+ if (addopt(postcmdbuf, optopt, optarg, sizeof (postcmdbuf))
+ != Z_OK) {
+ zerror("Post-Install command line too long");
+ return (Z_ERR);
}
}
- if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE, B_FALSE)
- != Z_OK)
- return (Z_ERR);
- if (verify_details(CMD_INSTALL, argv) != Z_OK)
- return (Z_ERR);
+ for (; optind < argc; optind++) {
+ if (addopt(cmdbuf, 0, argv[optind], sizeof (cmdbuf)) != Z_OK) {
+ zerror("Install command line too long");
+ return (Z_ERR);
+ }
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
- zerror(gettext("another %s may have an operation in progress."),
- "zoneadm");
- return (Z_ERR);
- }
- err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE);
- if (err != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("could not set state"));
- goto done;
+ if (addopt(postcmdbuf, 0, argv[optind], sizeof (postcmdbuf))
+ != Z_OK) {
+ zerror("Post-Install command line too long");
+ return (Z_ERR);
+ }
}
- if (!nodataset)
- create_zfs_zonepath(zonepath);
-
- /*
- * According to the Application Packaging Developer's Guide, a
- * "checkinstall" script when included in a package is executed as
- * the user "install", if such a user exists, or by the user
- * "nobody". In order to support this dubious behavior, the path
- * to the zone being constructed is opened up during the life of
- * the command laying down the zone's root file system. Once this
- * has completed, regardless of whether it was successful, the
- * path to the zone is again restricted.
- */
- if (chmod(zonepath, DEFAULT_DIR_MODE) != 0) {
- zperror(zonepath, B_FALSE);
- err = Z_ERR;
- goto done;
- }
+ if (!brand_help) {
+ if (sanity_check(target_zone, CMD_INSTALL, B_FALSE, B_TRUE,
+ B_FALSE) != Z_OK)
+ return (Z_ERR);
+ if (verify_details(CMD_INSTALL, argv) != Z_OK)
+ return (Z_ERR);
- if (is_native_zone)
- status = do_subproc(cmdbuf);
- else
- status = do_subproc_interactive(cmdbuf);
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ zerror(gettext("another %s may have an operation in "
+ "progress."), "zoneadm");
+ return (Z_ERR);
+ }
+ err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE);
+ if (err != Z_OK) {
+ errno = err;
+ zperror2(target_zone, gettext("could not set state"));
+ goto done;
+ }
- if (chmod(zonepath, S_IRWXU) != 0) {
- zperror(zonepath, B_FALSE);
- err = Z_ERR;
- goto done;
+ if (!nodataset)
+ create_zfs_zonepath(zonepath);
}
+
+ status = do_subproc_interactive(cmdbuf);
if ((subproc_err =
subproc_status(gettext("brand-specific installation"), status,
B_FALSE)) != ZONE_SUBPROC_OK) {
+ if (subproc_err == ZONE_SUBPROC_USAGE && !brand_help) {
+ sub_usage(SHELP_INSTALL, CMD_INSTALL);
+ zonecfg_release_lock_file(target_zone, lockfd);
+ return (Z_ERR);
+ }
err = Z_ERR;
goto done;
}
+ if (brand_help)
+ return (Z_OK);
+
if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) {
errno = err;
zperror2(target_zone, gettext("could not set state"));
@@ -3338,13 +3019,12 @@ install_func(int argc, char *argv[])
done:
/*
- * If the install script exited with ZONE_SUBPROC_USAGE or
- * ZONE_SUBPROC_NOTCOMPLETE, try to clean up the zone and leave the
- * zone in the CONFIGURED state so that another install can be
- * attempted without requiring an uninstall first.
+ * If the install script exited with ZONE_SUBPROC_NOTCOMPLETE, try to
+ * clean up the zone and leave the zone in the CONFIGURED state so that
+ * another install can be attempted without requiring an uninstall
+ * first.
*/
- if ((subproc_err == ZONE_SUBPROC_USAGE) ||
- (subproc_err == ZONE_SUBPROC_NOTCOMPLETE)) {
+ if (subproc_err == ZONE_SUBPROC_NOTCOMPLETE) {
if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) {
errno = err;
zperror2(target_zone,
@@ -3356,7 +3036,8 @@ done:
}
}
- release_lock_file(lockfd);
+ if (!brand_help)
+ zonecfg_release_lock_file(target_zone, lockfd);
return ((err == Z_OK) ? Z_OK : Z_ERR);
}
@@ -3837,39 +3518,6 @@ copy_zone(char *src, char *dst)
return (Z_OK);
}
-static int
-zone_postclone(char *zonepath)
-{
- char cmdbuf[MAXPATHLEN];
- int status;
- brand_handle_t bh;
- int err = Z_OK;
-
- /*
- * Fetch the post-clone command, if any, from the brand
- * configuration.
- */
- if ((bh = brand_open(target_brand)) == NULL) {
- zerror(gettext("missing or invalid brand"));
- return (Z_ERR);
- }
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- err = brand_get_postclone(bh, target_zone, zonepath, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL);
- brand_close(bh);
-
- if (err == 0 && strlen(cmdbuf) > EXEC_LEN) {
- status = do_subproc(cmdbuf);
- if ((err = subproc_status("postclone", status, B_FALSE))
- != ZONE_SUBPROC_OK) {
- zerror(gettext("post-clone configuration failed."));
- err = Z_ERR;
- }
- }
-
- return (err);
-}
-
/* ARGSUSED */
static int
zfm_print(const char *p, void *r) {
@@ -3919,18 +3567,32 @@ clone_func(int argc, char *argv[])
zone_entry_t *zent;
char *method = NULL;
char *snapshot = NULL;
+ char cmdbuf[MAXPATHLEN];
+ char postcmdbuf[MAXPATHLEN];
+ char presnapbuf[MAXPATHLEN];
+ char postsnapbuf[MAXPATHLEN];
+ char validsnapbuf[MAXPATHLEN];
+ brand_handle_t bh = NULL;
+ int status;
+ boolean_t brand_help = B_FALSE;
if (zonecfg_in_alt_root()) {
zerror(gettext("cannot clone zone in alternate root"));
return (Z_ERR);
}
+ /* Check the argv string for args we handle internally */
optind = 0;
- if ((arg = getopt(argc, argv, "?m:s:")) != EOF) {
+ opterr = 0;
+ while ((arg = getopt(argc, argv, "?m:s:")) != EOF) {
switch (arg) {
case '?':
- sub_usage(SHELP_CLONE, CMD_CLONE);
- return (optopt == '?' ? Z_OK : Z_USAGE);
+ if (optopt == '?') {
+ sub_usage(SHELP_CLONE, CMD_CLONE);
+ brand_help = B_TRUE;
+ }
+ /* Ignore unknown options - may be brand specific. */
+ break;
case 'm':
method = optarg;
break;
@@ -3938,78 +3600,84 @@ clone_func(int argc, char *argv[])
snapshot = optarg;
break;
default:
- sub_usage(SHELP_CLONE, CMD_CLONE);
- return (Z_USAGE);
+ /* Ignore unknown options - may be brand specific. */
+ break;
}
}
- if (argc != (optind + 1) ||
- (method != NULL && strcmp(method, "copy") != 0)) {
+
+ if (argc != (optind + 1)) {
sub_usage(SHELP_CLONE, CMD_CLONE);
return (Z_USAGE);
}
+
source_zone = argv[optind];
- if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE, B_FALSE)
- != Z_OK)
- return (Z_ERR);
- if (verify_details(CMD_CLONE, argv) != Z_OK)
- return (Z_ERR);
- /*
- * We also need to do some extra validation on the source zone.
- */
- if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) {
- zerror(gettext("%s operation is invalid for the global zone."),
- cmd_to_str(CMD_CLONE));
- return (Z_ERR);
- }
+ if (!brand_help) {
+ if (sanity_check(target_zone, CMD_CLONE, B_FALSE, B_TRUE,
+ B_FALSE) != Z_OK)
+ return (Z_ERR);
+ if (verify_details(CMD_CLONE, argv) != Z_OK)
+ return (Z_ERR);
- if (strncmp(source_zone, "SUNW", 4) == 0) {
- zerror(gettext("%s operation is invalid for zones starting "
- "with SUNW."), cmd_to_str(CMD_CLONE));
- return (Z_ERR);
- }
+ /*
+ * We also need to do some extra validation on the source zone.
+ */
+ if (strcmp(source_zone, GLOBAL_ZONENAME) == 0) {
+ zerror(gettext("%s operation is invalid for the "
+ "global zone."), cmd_to_str(CMD_CLONE));
+ return (Z_ERR);
+ }
- zent = lookup_running_zone(source_zone);
- if (zent != NULL) {
- /* check whether the zone is ready or running */
- if ((err = zone_get_state(zent->zname, &zent->zstate_num))
- != Z_OK) {
- errno = err;
- zperror2(zent->zname, gettext("could not get state"));
- /* can't tell, so hedge */
- zent->zstate_str = "ready/running";
- } else {
- zent->zstate_str = zone_state_str(zent->zstate_num);
+ if (strncmp(source_zone, "SUNW", 4) == 0) {
+ zerror(gettext("%s operation is invalid for zones "
+ "starting with SUNW."), cmd_to_str(CMD_CLONE));
+ return (Z_ERR);
}
- zerror(gettext("%s operation is invalid for %s zones."),
- cmd_to_str(CMD_CLONE), zent->zstate_str);
- return (Z_ERR);
- }
- if ((err = zone_get_state(source_zone, &state)) != Z_OK) {
- errno = err;
- zperror2(source_zone, gettext("could not get state"));
- return (Z_ERR);
- }
- if (state != ZONE_STATE_INSTALLED) {
- (void) fprintf(stderr,
- gettext("%s: zone %s is %s; %s is required.\n"),
- execname, source_zone, zone_state_str(state),
- zone_state_str(ZONE_STATE_INSTALLED));
- return (Z_ERR);
- }
+ zent = lookup_running_zone(source_zone);
+ if (zent != NULL) {
+ /* check whether the zone is ready or running */
+ if ((err = zone_get_state(zent->zname,
+ &zent->zstate_num)) != Z_OK) {
+ errno = err;
+ zperror2(zent->zname, gettext("could not get "
+ "state"));
+ /* can't tell, so hedge */
+ zent->zstate_str = "ready/running";
+ } else {
+ zent->zstate_str =
+ zone_state_str(zent->zstate_num);
+ }
+ zerror(gettext("%s operation is invalid for %s zones."),
+ cmd_to_str(CMD_CLONE), zent->zstate_str);
+ return (Z_ERR);
+ }
- /*
- * The source zone checks out ok, continue with the clone.
- */
+ if ((err = zone_get_state(source_zone, &state)) != Z_OK) {
+ errno = err;
+ zperror2(source_zone, gettext("could not get state"));
+ return (Z_ERR);
+ }
+ if (state != ZONE_STATE_INSTALLED) {
+ (void) fprintf(stderr,
+ gettext("%s: zone %s is %s; %s is required.\n"),
+ execname, source_zone, zone_state_str(state),
+ zone_state_str(ZONE_STATE_INSTALLED));
+ return (Z_ERR);
+ }
- if (validate_clone(source_zone, target_zone) != Z_OK)
- return (Z_ERR);
+ /*
+ * The source zone checks out ok, continue with the clone.
+ */
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
- zerror(gettext("another %s may have an operation in progress."),
- "zoneadm");
- return (Z_ERR);
+ if (validate_clone(source_zone, target_zone) != Z_OK)
+ return (Z_ERR);
+
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ zerror(gettext("another %s may have an operation in "
+ "progress."), "zoneadm");
+ return (Z_ERR);
+ }
}
if ((err = zone_get_zonepath(source_zone, source_zonepath,
@@ -4026,36 +3694,140 @@ clone_func(int argc, char *argv[])
goto done;
}
- if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE))
- != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("could not set state"));
+ /*
+ * Fetch the clone and postclone hooks from the brand configuration.
+ */
+ if ((bh = brand_open(target_brand)) == NULL) {
+ zerror(gettext("missing or invalid brand"));
+ err = Z_ERR;
goto done;
}
- if (snapshot != NULL) {
- err = clone_snapshot_zfs(snapshot, zonepath);
- } else {
- /*
- * We always copy the clone unless the source is ZFS and a
- * ZFS clone worked. We fallback to copying if the ZFS clone
- * fails for some reason.
- */
+ if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_clone, target_zone,
+ zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing clone resource");
+ brand_close(bh);
err = Z_ERR;
- if (method == NULL && is_zonepath_zfs(source_zonepath))
- err = clone_zfs(source_zone, source_zonepath, zonepath);
+ goto done;
+ }
- if (err != Z_OK)
- err = clone_copy(source_zonepath, zonepath);
+ if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postclone,
+ target_zone, zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing postclone "
+ "resource");
+ brand_close(bh);
+ err = Z_ERR;
+ goto done;
+ }
+
+ if (get_hook(bh, presnapbuf, sizeof (presnapbuf), brand_get_presnap,
+ source_zone, source_zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing presnap "
+ "resource");
+ brand_close(bh);
+ err = Z_ERR;
+ goto done;
+ }
+
+ if (get_hook(bh, postsnapbuf, sizeof (postsnapbuf), brand_get_postsnap,
+ source_zone, source_zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing postsnap "
+ "resource");
+ brand_close(bh);
+ err = Z_ERR;
+ goto done;
+ }
+
+ if (get_hook(bh, validsnapbuf, sizeof (validsnapbuf),
+ brand_get_validatesnap, target_zone, zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing validatesnap "
+ "resource");
+ brand_close(bh);
+ err = Z_ERR;
+ goto done;
+ }
+ brand_close(bh);
+
+ /* Append all options to clone hook. */
+ if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK) {
+ err = Z_ERR;
+ goto done;
+ }
+
+ /* Append all options to postclone hook. */
+ if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK) {
+ err = Z_ERR;
+ goto done;
+ }
+
+ if (!brand_help) {
+ if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE))
+ != Z_OK) {
+ errno = err;
+ zperror2(target_zone, gettext("could not set state"));
+ goto done;
+ }
}
/*
- * Trusted Extensions requires that cloned zones use the same sysid
- * configuration, so it is not appropriate to perform any
- * post-clone reconfiguration.
+ * The clone hook is optional. If it exists, use the hook for
+ * cloning, otherwise use the built-in clone support
*/
- if ((err == Z_OK) && !is_system_labeled())
- err = zone_postclone(zonepath);
+ if (cmdbuf[0] != '\0') {
+ /* Run the clone hook */
+ status = do_subproc_interactive(cmdbuf);
+ if ((status = subproc_status(gettext("brand-specific clone"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK) {
+ if (status == ZONE_SUBPROC_USAGE && !brand_help)
+ sub_usage(SHELP_CLONE, CMD_CLONE);
+ err = Z_ERR;
+ goto done;
+ }
+
+ if (brand_help)
+ return (Z_OK);
+
+ } else {
+ /* If just help, we're done since there is no brand help. */
+ if (brand_help)
+ return (Z_OK);
+
+ /* Run the built-in clone support. */
+
+ /* The only explicit built-in method is "copy". */
+ if (method != NULL && strcmp(method, "copy") != 0) {
+ sub_usage(SHELP_CLONE, CMD_CLONE);
+ err = Z_USAGE;
+ goto done;
+ }
+
+ if (snapshot != NULL) {
+ err = clone_snapshot_zfs(snapshot, zonepath,
+ validsnapbuf);
+ } else {
+ /*
+ * We always copy the clone unless the source is ZFS
+ * and a ZFS clone worked. We fallback to copying if
+ * the ZFS clone fails for some reason.
+ */
+ err = Z_ERR;
+ if (method == NULL && is_zonepath_zfs(source_zonepath))
+ err = clone_zfs(source_zonepath, zonepath,
+ presnapbuf, postsnapbuf);
+
+ if (err != Z_OK)
+ err = clone_copy(source_zonepath, zonepath);
+ }
+ }
+
+ if (err == Z_OK && postcmdbuf[0] != '\0') {
+ status = do_subproc(postcmdbuf);
+ if ((err = subproc_status("postclone", status, B_FALSE))
+ != ZONE_SUBPROC_OK) {
+ zerror(gettext("post-clone configuration failed."));
+ err = Z_ERR;
+ }
+ }
done:
/*
@@ -4068,7 +3840,8 @@ done:
zperror2(target_zone, gettext("could not set state"));
}
}
- release_lock_file(lockfd);
+ if (!brand_help)
+ zonecfg_release_lock_file(target_zone, lockfd);
return ((err == Z_OK) ? Z_OK : Z_ERR);
}
@@ -4317,7 +4090,7 @@ move_func(int argc, char *argv[])
return (Z_ERR);
}
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
zerror(gettext("another %s may have an operation in progress."),
"zoneadm");
zonecfg_fini_handle(handle);
@@ -4347,7 +4120,7 @@ move_func(int argc, char *argv[])
zperror(gettext("could not rmdir new zone path"),
B_FALSE);
zonecfg_fini_handle(handle);
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
return (Z_ERR);
}
@@ -4359,7 +4132,7 @@ move_func(int argc, char *argv[])
*/
zperror(gettext("could not move zone"), B_FALSE);
zonecfg_fini_handle(handle);
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
return (Z_ERR);
}
@@ -4399,7 +4172,7 @@ move_func(int argc, char *argv[])
done:
zonecfg_fini_handle(handle);
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
/*
* Clean up the file system based on how things went. We either
@@ -4467,6 +4240,7 @@ done:
return ((err == Z_OK) ? Z_OK : Z_ERR);
}
+/* ARGSUSED */
static int
detach_func(int argc, char *argv[])
{
@@ -4474,30 +4248,42 @@ detach_func(int argc, char *argv[])
int err, arg;
char zonepath[MAXPATHLEN];
char cmdbuf[MAXPATHLEN];
+ char precmdbuf[MAXPATHLEN];
zone_dochandle_t handle;
boolean_t execute = B_TRUE;
+ boolean_t brand_help = B_FALSE;
brand_handle_t bh = NULL;
+ int status;
if (zonecfg_in_alt_root()) {
zerror(gettext("cannot detach zone in alternate root"));
return (Z_ERR);
}
+ /* Check the argv string for args we handle internally */
optind = 0;
- if ((arg = getopt(argc, argv, "?n")) != EOF) {
+ opterr = 0;
+ while ((arg = getopt(argc, argv, "?n")) != EOF) {
switch (arg) {
case '?':
- sub_usage(SHELP_DETACH, CMD_DETACH);
- return (optopt == '?' ? Z_OK : Z_USAGE);
+ if (optopt == '?') {
+ sub_usage(SHELP_DETACH, CMD_DETACH);
+ brand_help = B_TRUE;
+ }
+ /* Ignore unknown options - may be brand specific. */
+ break;
case 'n':
execute = B_FALSE;
break;
default:
- sub_usage(SHELP_DETACH, CMD_DETACH);
- return (Z_USAGE);
+ /* Ignore unknown options - may be brand specific. */
+ break;
}
}
+ if (brand_help)
+ execute = B_FALSE;
+
if (execute) {
if (sanity_check(target_zone, CMD_DETACH, B_FALSE, B_TRUE,
B_FALSE) != Z_OK)
@@ -4528,14 +4314,6 @@ detach_func(int argc, char *argv[])
return (Z_ERR);
}
- /* Don't detach the zone if anything is still mounted there */
- if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) {
- zerror(gettext("These file systems are mounted on "
- "subdirectories of %s.\n"), zonepath);
- (void) zonecfg_find_mounts(zonepath, zfm_print, NULL);
- return (Z_ERR);
- }
-
if ((handle = zonecfg_init_handle()) == NULL) {
zperror(cmd_to_str(CMD_DETACH), B_TRUE);
return (Z_ERR);
@@ -4548,15 +4326,21 @@ detach_func(int argc, char *argv[])
return (Z_ERR);
}
- /* Fetch the predetach hook from the brand configuration. */
+ /* Fetch the detach and predetach hooks from the brand configuration. */
if ((bh = brand_open(target_brand)) == NULL) {
zerror(gettext("missing or invalid brand"));
return (Z_ERR);
}
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_predetach(bh, target_zone, zonepath, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL) != 0) {
+ if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_detach, target_zone,
+ zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing detach resource");
+ brand_close(bh);
+ return (Z_ERR);
+ }
+
+ if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_predetach,
+ target_zone, zonepath) != Z_OK) {
zerror("invalid brand configuration: missing predetach "
"resource");
brand_close(bh);
@@ -4564,43 +4348,88 @@ detach_func(int argc, char *argv[])
}
brand_close(bh);
- /* If we have a brand predetach hook, run it. */
- if (strlen(cmdbuf) > EXEC_LEN) {
- int status;
-
- /* If this is a dry-run, pass that flag to the hook. */
- if (!execute && addopt(cmdbuf, 0, "-n", sizeof (cmdbuf))
- != Z_OK) {
- zerror("Predetach command line too long");
- return (Z_ERR);
- }
+ /* Append all options to predetach hook. */
+ if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK)
+ return (Z_ERR);
- status = do_subproc(cmdbuf);
- if (subproc_status(gettext("brand-specific predetach"),
- status, B_FALSE) != ZONE_SUBPROC_OK) {
- return (Z_ERR);
- }
- }
+ /* Append all options to detach hook. */
+ if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK)
+ return (Z_ERR);
- if (execute && grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ if (execute && zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
zerror(gettext("another %s may have an operation in progress."),
"zoneadm");
zonecfg_fini_handle(handle);
return (Z_ERR);
}
- if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) {
- errno = err;
- zperror(gettext("getting the detach information failed"),
- B_TRUE);
- goto done;
+ /* If we have a brand predetach hook, run it. */
+ if (!brand_help && precmdbuf[0] != '\0') {
+ status = do_subproc(precmdbuf);
+ if (subproc_status(gettext("brand-specific predetach"),
+ status, B_FALSE) != ZONE_SUBPROC_OK) {
+
+ if (execute)
+ zonecfg_release_lock_file(target_zone, lockfd);
+ return (Z_ERR);
+ }
}
- if ((err = zonecfg_detach_save(handle, (execute ? 0 : ZONE_DRY_RUN)))
- != Z_OK) {
- errno = err;
- zperror(gettext("saving the detach manifest failed"), B_TRUE);
- goto done;
+ if (cmdbuf[0] != '\0') {
+ /* Run the detach hook */
+ status = do_subproc_interactive(cmdbuf);
+ if ((status = subproc_status(gettext("brand-specific detach"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK) {
+ if (status == ZONE_SUBPROC_USAGE && !brand_help)
+ sub_usage(SHELP_DETACH, CMD_DETACH);
+
+ if (execute)
+ zonecfg_release_lock_file(target_zone, lockfd);
+
+ return (Z_ERR);
+ }
+
+ } else {
+ /* If just help, we're done since there is no brand help. */
+ if (brand_help)
+ return (Z_OK);
+
+ /*
+ * Run the built-in detach support. Just generate a simple
+ * zone definition XML file and detach.
+ */
+
+ /* Don't detach the zone if anything is still mounted there */
+ if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) {
+ (void) fprintf(stderr, gettext("These file systems are "
+ "mounted on subdirectories of %s.\n"), zonepath);
+ (void) zonecfg_find_mounts(zonepath, zfm_print, NULL);
+ err = ZONE_SUBPROC_NOTCOMPLETE;
+ goto done;
+ }
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ zperror(cmd_to_str(CMD_DETACH), B_TRUE);
+ err = ZONE_SUBPROC_NOTCOMPLETE;
+ goto done;
+ }
+
+ if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
+ errno = err;
+ zperror(cmd_to_str(CMD_DETACH), B_TRUE);
+ zonecfg_fini_handle(handle);
+ goto done;
+ }
+
+ if ((err = zonecfg_detach_save(handle,
+ (execute ? 0 : ZONE_DRY_RUN))) != Z_OK) {
+ errno = err;
+ zperror(gettext("saving the detach manifest failed"),
+ B_TRUE);
+ goto done;
+ }
+
+ zonecfg_fini_handle(handle);
}
/*
@@ -4616,124 +4445,73 @@ detach_func(int argc, char *argv[])
done:
zonecfg_fini_handle(handle);
if (execute)
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
return ((err == Z_OK) ? Z_OK : Z_ERR);
}
/*
- * During attach we go through and fix up the /dev entries for the zone
- * we are attaching. In order to regenerate /dev with the correct devices,
- * the old /dev will be removed, the zone readied (which generates a new
- * /dev) then halted, then we use the info from the manifest to update
- * the modes, owners, etc. on the new /dev.
+ * Determine the brand when doing a dry-run attach. The zone does not have to
+ * exist, so we have to read the incoming manifest to determine the zone's
+ * brand.
+ *
+ * Because the manifest has to be processed twice; once to determine the brand
+ * and once to do the brand-specific attach logic, we always read it into a tmp
+ * file. This handles the manifest coming from stdin or a regular file. The
+ * tmpname parameter returns the name of the temporary file that the manifest
+ * was read into.
*/
static int
-dev_fix(zone_dochandle_t handle)
+dryrun_get_brand(char *manifest_path, char *tmpname, int size)
{
- int res;
- int err;
- int status;
- struct zone_devpermtab devtab;
- zone_cmd_arg_t zarg;
- char devpath[MAXPATHLEN];
- /* 6: "exec " and " " */
- char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6];
-
- if ((res = zonecfg_get_zonepath(handle, devpath, sizeof (devpath)))
- != Z_OK)
- return (res);
-
- if (strlcat(devpath, "/dev", sizeof (devpath)) >= sizeof (devpath))
- return (Z_TOO_BIG);
+ int fd;
+ int err;
+ int res = Z_OK;
+ zone_dochandle_t local_handle;
+ zone_dochandle_t rem_handle = NULL;
+ int len;
+ int ofd;
+ char buf[512];
- /*
- * "exec" the command so that the returned status is that of
- * RMCOMMAND and not the shell.
- */
- (void) snprintf(cmdbuf, sizeof (cmdbuf), EXEC_PREFIX RMCOMMAND " %s",
- devpath);
- status = do_subproc(cmdbuf);
- if ((err = subproc_status(RMCOMMAND, status, B_TRUE)) !=
- ZONE_SUBPROC_OK) {
- (void) fprintf(stderr,
- gettext("could not remove existing /dev\n"));
- return (Z_ERR);
+ if (strcmp(manifest_path, "-") == 0) {
+ fd = STDIN_FILENO;
+ } else {
+ if ((fd = open(manifest_path, O_RDONLY)) < 0) {
+ if (getcwd(buf, sizeof (buf)) == NULL)
+ (void) strlcpy(buf, "/", sizeof (buf));
+ zerror(gettext("could not open manifest path %s%s: %s"),
+ (*manifest_path == '/' ? "" : buf), manifest_path,
+ strerror(errno));
+ return (Z_ERR);
+ }
}
- /* In order to ready the zone, it must be in the installed state */
- if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) {
- errno = err;
- zperror(gettext("could not reset state"), B_TRUE);
- return (Z_ERR);
- }
+ (void) snprintf(tmpname, size, "/var/run/zone.%d", getpid());
- /* We have to ready the zone to regen the dev tree */
- zarg.cmd = Z_READY;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
- zerror(gettext("call to %s failed"), "zoneadmd");
- /* attempt to restore zone to configured state */
- (void) zone_set_state(target_zone, ZONE_STATE_CONFIGURED);
+ if ((ofd = open(tmpname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
+ zperror(gettext("could not save manifest"), B_FALSE);
+ (void) close(fd);
return (Z_ERR);
}
- zarg.cmd = Z_HALT;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
- zerror(gettext("call to %s failed"), "zoneadmd");
- /* attempt to restore zone to configured state */
- (void) zone_set_state(target_zone, ZONE_STATE_CONFIGURED);
- return (Z_ERR);
+ while ((len = read(fd, buf, sizeof (buf))) > 0) {
+ if (write(ofd, buf, len) == -1) {
+ zperror(gettext("could not save manifest"), B_FALSE);
+ (void) close(ofd);
+ (void) close(fd);
+ return (Z_ERR);
+ }
}
- /* attempt to restore zone to configured state */
- (void) zone_set_state(target_zone, ZONE_STATE_CONFIGURED);
-
- if (zonecfg_setdevperment(handle) != Z_OK) {
- (void) fprintf(stderr,
- gettext("unable to enumerate device entries\n"));
+ if (close(ofd) != 0) {
+ zperror(gettext("could not save manifest"), B_FALSE);
+ (void) close(fd);
return (Z_ERR);
}
- while (zonecfg_getdevperment(handle, &devtab) == Z_OK) {
- int err;
-
- if ((err = zonecfg_devperms_apply(handle,
- devtab.zone_devperm_name, devtab.zone_devperm_uid,
- devtab.zone_devperm_gid, devtab.zone_devperm_mode,
- devtab.zone_devperm_acl)) != Z_OK && err != Z_INVAL)
- (void) fprintf(stderr, gettext("error updating device "
- "%s: %s\n"), devtab.zone_devperm_name,
- zonecfg_strerror(err));
-
- free(devtab.zone_devperm_acl);
- }
-
- (void) zonecfg_enddevperment(handle);
-
- return (Z_OK);
-}
+ (void) close(fd);
-/*
- * Validate attaching a zone but don't actually do the work. The zone
- * does not have to exist, so there is some complexity getting a new zone
- * configuration set up so that we can perform the validation. This is
- * handled within zonecfg_attach_manifest() which returns two handles; one
- * for the the full configuration to validate (rem_handle) and the other
- * (local_handle) containing only the zone configuration derived from the
- * manifest.
- */
-static int
-dryrun_attach(char *manifest_path, char *argv[])
-{
- int fd;
- int err;
- int res;
- zone_dochandle_t local_handle;
- zone_dochandle_t rem_handle = NULL;
-
- if (strcmp(manifest_path, "-") == 0) {
- fd = 0;
- } else if ((fd = open(manifest_path, O_RDONLY)) < 0) {
+ if ((fd = open(tmpname, O_RDONLY)) < 0) {
zperror(gettext("could not open manifest path"), B_FALSE);
return (Z_ERR);
}
@@ -4785,281 +4563,22 @@ dryrun_attach(char *manifest_path, char *argv[])
goto done;
}
- /*
- * Retrieve remote handle brand type and determine whether it is
- * native or not.
- */
+ /* Retrieve remote handle brand type. */
if (zonecfg_get_brand(rem_handle, target_brand, sizeof (target_brand))
!= Z_OK) {
zerror(gettext("missing or invalid brand"));
exit(Z_ERR);
}
- is_native_zone = (strcmp(target_brand, NATIVE_BRAND_NAME) == 0);
- is_cluster_zone =
- (strcmp(target_brand, CLUSTER_BRAND_NAME) == 0);
-
- res = verify_handle(CMD_ATTACH, local_handle, argv);
-
- /* Get the detach information for the locally defined zone. */
- if ((err = zonecfg_get_detach_info(local_handle, B_FALSE)) != Z_OK) {
- errno = err;
- zperror(gettext("getting the attach information failed"),
- B_TRUE);
- res = Z_ERR;
- } else {
- /* sw_cmp prints error msgs as necessary */
- if (sw_cmp(local_handle, rem_handle, SW_CMP_NONE) != Z_OK)
- res = Z_ERR;
- }
done:
- if (strcmp(manifest_path, "-") != 0)
- (void) close(fd);
-
zonecfg_fini_handle(local_handle);
zonecfg_fini_handle(rem_handle);
+ (void) close(fd);
return ((res == Z_OK) ? Z_OK : Z_ERR);
}
-/*
- * Attempt to generate the information we need to make the zone look like
- * it was properly detached by using the pkg information contained within
- * the zone itself.
- *
- * We will perform a dry-run detach within the zone to generate the xml file.
- * To do this we need to be able to get a handle on the zone so we can see
- * how it is configured. In order to get a handle, we need a copy of the
- * zone configuration within the zone. Since the zone's configuration is
- * not available within the zone itself, we need to temporarily copy it into
- * the zone.
- *
- * The sequence of actions we are doing is this:
- * [set zone state to installed]
- * [mount zone]
- * zlogin {zone} </etc/zones/{zone}.xml 'cat >/etc/zones/{zone}.xml'
- * zlogin {zone} 'zoneadm -z {zone} detach -n' >{zonepath}/SUNWdetached.xml
- * zlogin {zone} 'rm -f /etc/zones/{zone}.xml'
- * [unmount zone]
- * [set zone state to configured]
- *
- * The successful result of this function is that we will have a
- * SUNWdetached.xml file in the zonepath and we can use that to attach the zone.
- */
-static boolean_t
-gen_detach_info(char *zonepath)
-{
- int status;
- boolean_t mounted = B_FALSE;
- boolean_t res = B_FALSE;
- char cmdbuf[2 * MAXPATHLEN];
-
- /*
- * The zone has to be installed to mount and zlogin. Temporarily set
- * the state to 'installed'.
- */
- if (zone_set_state(target_zone, ZONE_STATE_INSTALLED) != Z_OK)
- return (B_FALSE);
-
- /* Mount the zone so we can zlogin. */
- if (mount_func(0, NULL) != Z_OK)
- goto cleanup;
- mounted = B_TRUE;
-
- /*
- * We need to copy the zones xml configuration file into the
- * zone so we can get a handle for the zone while running inside
- * the zone.
- */
- if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/sbin/zlogin -S %s "
- "</etc/zones/%s.xml '/usr/bin/cat >/etc/zones/%s.xml'",
- target_zone, target_zone, target_zone) >= sizeof (cmdbuf))
- goto cleanup;
-
- status = do_subproc_interactive(cmdbuf);
- if (subproc_status("copy", status, B_TRUE) != ZONE_SUBPROC_OK)
- goto cleanup;
-
- /* Now run the detach command within the mounted zone. */
- if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/sbin/zlogin -S %s "
- "'/usr/sbin/zoneadm -z %s detach -n' >%s/SUNWdetached.xml",
- target_zone, target_zone, zonepath) >= sizeof (cmdbuf))
- goto cleanup;
-
- status = do_subproc_interactive(cmdbuf);
- if (subproc_status("detach", status, B_TRUE) != ZONE_SUBPROC_OK)
- goto cleanup;
-
- res = B_TRUE;
-
-cleanup:
- /* Cleanup from the previous actions. */
- if (mounted) {
- if (snprintf(cmdbuf, sizeof (cmdbuf),
- "/usr/sbin/zlogin -S %s '/usr/bin/rm -f /etc/zones/%s.xml'",
- target_zone, target_zone) >= sizeof (cmdbuf)) {
- res = B_FALSE;
- } else {
- status = do_subproc_interactive(cmdbuf);
- if (subproc_status("rm", status, B_TRUE)
- != ZONE_SUBPROC_OK)
- res = B_FALSE;
- }
-
- if (unmount_func(0, NULL) != Z_OK)
- res = B_FALSE;
- }
-
- if (zone_set_state(target_zone, ZONE_STATE_CONFIGURED) != Z_OK)
- res = B_FALSE;
-
- return (res);
-}
-
-/*
- * The zone needs to be updated so set it up for the update and initiate the
- * update within the scratch zone. First set the state to incomplete so we can
- * force-mount the zone for the update operation. We pass the -U option to the
- * mount so that the scratch zone is mounted without the zone's /etc and /var
- * being lofs mounted back into the scratch zone root. This is done by
- * overloading the bootbuf string in the zone_cmd_arg_t to pass -U as an option
- * to the mount cmd.
- */
-static int
-attach_update(zone_dochandle_t handle, char *zonepath)
-{
- int err;
- int update_res;
- int status;
- zone_cmd_arg_t zarg;
- FILE *fp;
- struct zone_fstab fstab;
- char cmdbuf[(4 * MAXPATHLEN) + 20];
-
- if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE))
- != Z_OK) {
- errno = err;
- zperror(gettext("could not set state"), B_TRUE);
- return (Z_ERR);
- }
-
- zarg.cmd = Z_FORCEMOUNT;
- (void) strlcpy(zarg.bootbuf, "-U", sizeof (zarg.bootbuf));
- if (call_zoneadmd(target_zone, &zarg) != 0) {
- zerror(gettext("could not mount zone"));
-
- /* We reset the state since the zone wasn't modified yet. */
- if ((err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED))
- != Z_OK) {
- errno = err;
- zperror(gettext("could not reset state"), B_TRUE);
- }
- return (Z_ERR);
- }
-
- /*
- * Move data files generated by sw_up_to_date() into the scratch
- * zone's /tmp.
- */
- (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec /usr/bin/mv "
- "%s/pkg_add %s/pkg_rm %s/lu/tmp",
- zonepath, zonepath, zonepath);
-
- status = do_subproc_interactive(cmdbuf);
- if (subproc_status("mv", status, B_TRUE) != ZONE_SUBPROC_OK) {
- zperror(gettext("could not mv data files"), B_FALSE);
- goto fail;
- }
-
- /*
- * Save list of inherit-pkg-dirs into zone. Since the file is in
- * /tmp we don't have to worry about deleting it.
- */
- (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/lu/tmp/inherited",
- zonepath);
- if ((fp = fopen(cmdbuf, "w")) == NULL) {
- zperror(gettext("could not save inherit-pkg-dirs"), B_FALSE);
- goto fail;
- }
- if (zonecfg_setipdent(handle) != Z_OK) {
- zperror(gettext("could not enumerate inherit-pkg-dirs"),
- B_TRUE);
- goto fail;
- }
- while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
- if (fprintf(fp, "%s\n", fstab.zone_fs_dir) < 0) {
- zperror(gettext("could not save inherit-pkg-dirs"),
- B_FALSE);
- (void) fclose(fp);
- goto fail;
- }
- }
- (void) zonecfg_endipdent(handle);
- if (fclose(fp) != 0) {
- zperror(gettext("could not save inherit-pkg-dirs"), B_FALSE);
- goto fail;
- }
-
- /* run the updater inside the scratch zone */
- (void) snprintf(cmdbuf, sizeof (cmdbuf),
- "exec /usr/sbin/zlogin -S %s "
- "/usr/lib/brand/native/attach_update %s", target_zone, target_zone);
-
- update_res = Z_OK;
- status = do_subproc_interactive(cmdbuf);
- if (subproc_status("attach_update", status, B_TRUE)
- != ZONE_SUBPROC_OK) {
- zerror(gettext("could not update zone"));
- update_res = Z_ERR;
- }
-
- zarg.cmd = Z_UNMOUNT;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
- zerror(gettext("could not unmount zone"));
- return (Z_ERR);
- }
-
- /*
- * If the update script within the scratch zone failed for some reason
- * we will now leave the zone in the incomplete state since we no
- * longer know the state of the files within the zonepath.
- */
- if (update_res == Z_ERR)
- return (Z_ERR);
-
- zonecfg_rm_detached(handle, B_FALSE);
-
- if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) {
- errno = err;
- zperror(gettext("could not set state"), B_TRUE);
- return (Z_ERR);
- }
-
- return (Z_OK);
-
-fail:
- zarg.cmd = Z_UNMOUNT;
- if (call_zoneadmd(target_zone, &zarg) != 0)
- zerror(gettext("could not unmount zone"));
-
- /* We reset the state since the zone wasn't modified yet. */
- if ((err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED))
- != Z_OK) {
- errno = err;
- zperror(gettext("could not reset state"), B_TRUE);
- }
-
- return (Z_ERR);
-}
-
/* ARGSUSED */
-static void
-sigcleanup(int sig)
-{
- attach_interupted = B_TRUE;
-}
-
-
static int
attach_func(int argc, char *argv[])
{
@@ -5067,91 +4586,124 @@ attach_func(int argc, char *argv[])
int err, arg;
boolean_t force = B_FALSE;
zone_dochandle_t handle;
- zone_dochandle_t athandle = NULL;
char zonepath[MAXPATHLEN];
- char brand[MAXNAMELEN], atbrand[MAXNAMELEN];
char cmdbuf[MAXPATHLEN];
+ char postcmdbuf[MAXPATHLEN];
boolean_t execute = B_TRUE;
- boolean_t retried = B_FALSE;
- boolean_t update = B_FALSE;
+ boolean_t brand_help = B_FALSE;
char *manifest_path;
+ char tmpmanifest[80];
+ int manifest_pos;
brand_handle_t bh = NULL;
+ int status;
if (zonecfg_in_alt_root()) {
zerror(gettext("cannot attach zone in alternate root"));
return (Z_ERR);
}
+ /* Check the argv string for args we handle internally */
optind = 0;
- if ((arg = getopt(argc, argv, "?Fn:u")) != EOF) {
+ opterr = 0;
+ while ((arg = getopt(argc, argv, "?Fn:")) != EOF) {
switch (arg) {
case '?':
- sub_usage(SHELP_ATTACH, CMD_ATTACH);
- return (optopt == '?' ? Z_OK : Z_USAGE);
+ if (optopt == '?') {
+ sub_usage(SHELP_ATTACH, CMD_ATTACH);
+ brand_help = B_TRUE;
+ }
+ /* Ignore unknown options - may be brand specific. */
+ break;
case 'F':
force = B_TRUE;
break;
case 'n':
execute = B_FALSE;
manifest_path = optarg;
- break;
- case 'u':
- update = B_TRUE;
+ manifest_pos = optind - 1;
break;
default:
- sub_usage(SHELP_ATTACH, CMD_ATTACH);
- return (Z_USAGE);
+ /* Ignore unknown options - may be brand specific. */
+ break;
}
}
- /* dry-run and update flags are mutually exclusive */
- if (!execute && update) {
- zerror(gettext("-n and -u flags are mutually exclusive"));
+ if (brand_help) {
+ force = B_FALSE;
+ execute = B_TRUE;
+ }
+
+ /* dry-run and force flags are mutually exclusive */
+ if (!execute && force) {
+ zerror(gettext("-F and -n flags are mutually exclusive"));
return (Z_ERR);
}
/*
- * If the no-execute option was specified, we need to branch down
- * a completely different path since there is no zone required to be
+ * If the no-execute option was specified, we don't do validation and
+ * need to figure out the brand, since there is no zone required to be
* configured for this option.
*/
- if (!execute)
- return (dryrun_attach(manifest_path, argv));
+ if (execute) {
+ if (!brand_help) {
+ if (sanity_check(target_zone, CMD_ATTACH, B_FALSE,
+ B_TRUE, B_FALSE) != Z_OK)
+ return (Z_ERR);
+ if (verify_details(CMD_ATTACH, argv) != Z_OK)
+ return (Z_ERR);
+ }
- if (sanity_check(target_zone, CMD_ATTACH, B_FALSE, B_TRUE, B_FALSE)
- != Z_OK)
- return (Z_ERR);
- if (verify_details(CMD_ATTACH, argv) != Z_OK)
- return (Z_ERR);
+ if ((err = zone_get_zonepath(target_zone, zonepath,
+ sizeof (zonepath))) != Z_OK) {
+ errno = err;
+ zperror2(target_zone,
+ gettext("could not get zone path"));
+ return (Z_ERR);
+ }
- if ((err = zone_get_zonepath(target_zone, zonepath, sizeof (zonepath)))
- != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("could not get zone path"));
- return (Z_ERR);
- }
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
+ return (Z_ERR);
+ }
- if ((handle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
- return (Z_ERR);
- }
+ if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
+ errno = err;
+ zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
- if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
- errno = err;
- zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
- zonecfg_fini_handle(handle);
- return (Z_ERR);
+ } else {
+ if (dryrun_get_brand(manifest_path, tmpmanifest,
+ sizeof (tmpmanifest)) != Z_OK)
+ return (Z_ERR);
+
+ argv[manifest_pos] = tmpmanifest;
+ target_zone = "-";
+ (void) strlcpy(zonepath, "-", sizeof (zonepath));
+
+ /* Run the brand's verify_adm hook. */
+ if (verify_brand(NULL, CMD_ATTACH, argv) != Z_OK)
+ return (Z_ERR);
}
- /* Fetch the postattach hook from the brand configuration. */
+ /*
+ * Fetch the attach and postattach hooks from the brand configuration.
+ */
if ((bh = brand_open(target_brand)) == NULL) {
zerror(gettext("missing or invalid brand"));
return (Z_ERR);
}
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_postattach(bh, target_zone, zonepath, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL) != 0) {
+ if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_attach, target_zone,
+ zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing attach resource");
+ brand_close(bh);
+ return (Z_ERR);
+ }
+
+ if (get_hook(bh, postcmdbuf, sizeof (postcmdbuf), brand_get_postattach,
+ target_zone, zonepath) != Z_OK) {
zerror("invalid brand configuration: missing postattach "
"resource");
brand_close(bh);
@@ -5159,133 +4711,55 @@ attach_func(int argc, char *argv[])
}
brand_close(bh);
- /* If we have a brand postattach hook and the force flag, append it. */
- if (strlen(cmdbuf) > EXEC_LEN && force) {
- if (addopt(cmdbuf, 0, "-F", sizeof (cmdbuf)) != Z_OK) {
- zerror("Postattach command line too long");
- return (Z_ERR);
- }
- }
+ /* Append all options to attach hook. */
+ if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK)
+ return (Z_ERR);
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
- zerror(gettext("another %s may have an operation in progress."),
- "zoneadm");
- zonecfg_fini_handle(handle);
+ /* Append all options to postattach hook. */
+ if (addoptions(postcmdbuf, argv, sizeof (postcmdbuf)) != Z_OK)
return (Z_ERR);
+
+ if (execute && !brand_help) {
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ zerror(gettext("another %s may have an operation in "
+ "progress."), "zoneadm");
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
}
if (force)
- goto forced;
-
- if ((athandle = zonecfg_init_handle()) == NULL) {
- zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
goto done;
- }
-
-retry:
- if ((err = zonecfg_get_attach_handle(zonepath, target_zone, B_TRUE,
- athandle)) != Z_OK) {
- if (err == Z_NO_ZONE) {
- /*
- * Zone was not detached. Try to fall back to getting
- * the needed information from within the zone.
- * However, we can only try to generate the attach
- * information for native branded zones, so fail if the
- * zone is not native.
- */
- if (!is_native_zone) {
- zerror(gettext("Not a detached zone."));
- goto done;
- }
- if (!retried) {
- zerror(gettext("The zone was not properly "
- "detached.\n\tAttempting to attach "
- "anyway."));
- if (gen_detach_info(zonepath)) {
- retried = B_TRUE;
- goto retry;
- }
- }
- zerror(gettext("Cannot generate the information "
- "needed to attach this zone."));
- } else if (err == Z_INVALID_DOCUMENT) {
- zerror(gettext("Cannot attach to an earlier release "
- "of the operating system"));
- } else {
- zperror(cmd_to_str(CMD_ATTACH), B_TRUE);
- }
- goto done;
- }
+ if (cmdbuf[0] != '\0') {
+ /* Run the attach hook */
+ status = do_subproc_interactive(cmdbuf);
+ if ((status = subproc_status(gettext("brand-specific attach"),
+ status, B_FALSE)) != ZONE_SUBPROC_OK) {
+ if (status == ZONE_SUBPROC_USAGE && !brand_help)
+ sub_usage(SHELP_ATTACH, CMD_ATTACH);
- /* Get the detach information for the locally defined zone. */
- if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) {
- errno = err;
- zperror(gettext("getting the attach information failed"),
- B_TRUE);
- goto done;
- }
+ if (execute && !brand_help)
+ zonecfg_release_lock_file(target_zone, lockfd);
- /*
- * Ensure that the detached and locally defined zones are both of
- * the same brand.
- */
- if ((zonecfg_get_brand(handle, brand, sizeof (brand)) != 0) ||
- (zonecfg_get_brand(athandle, atbrand, sizeof (atbrand)) != 0)) {
- err = Z_ERR;
- zerror(gettext("missing or invalid brand"));
- goto done;
- }
+ return (Z_ERR);
+ }
- if (strcmp(atbrand, brand) != NULL) {
- err = Z_ERR;
- zerror(gettext("Trying to attach a '%s' zone to a '%s' "
- "configuration."), atbrand, brand);
- goto done;
}
/*
- * If we're doing an update on attach, and the zone does need to be
- * updated, then run the update.
+ * Else run the built-in attach support.
+ * This is a no-op since there is nothing to validate.
*/
- if (update) {
- char fname[MAXPATHLEN];
-
- (void) sigset(SIGINT, sigcleanup);
-
- if ((err = sw_up_to_date(handle, athandle, zonepath)) != Z_OK) {
- if (err != Z_FATAL && !attach_interupted) {
- err = Z_FATAL;
- err = attach_update(handle, zonepath);
- }
- if (!attach_interupted || err == Z_OK)
- goto done;
- }
-
- (void) sigset(SIGINT, SIG_DFL);
-
- /* clean up data files left behind by sw_up_to_date() */
- (void) snprintf(fname, sizeof (fname), "%s/pkg_add", zonepath);
- (void) unlink(fname);
- (void) snprintf(fname, sizeof (fname), "%s/pkg_rm", zonepath);
- (void) unlink(fname);
-
- if (attach_interupted) {
- err = Z_FATAL;
- goto done;
- }
- } else {
- /* sw_cmp prints error msgs as necessary */
- if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK)
- goto done;
-
- if ((err = dev_fix(athandle)) != Z_OK)
- goto done;
+ /* If dry-run or help, then we're done. */
+ if (!execute || brand_help) {
+ if (!execute)
+ (void) unlink(tmpmanifest);
+ return (Z_OK);
}
-forced:
-
+done:
zonecfg_rm_detached(handle, force);
if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) {
@@ -5293,17 +4767,12 @@ forced:
zperror(gettext("could not reset state"), B_TRUE);
}
-done:
zonecfg_fini_handle(handle);
- release_lock_file(lockfd);
- if (athandle != NULL)
- zonecfg_fini_handle(athandle);
+ zonecfg_release_lock_file(target_zone, lockfd);
/* If we have a brand postattach hook, run it. */
- if (err == Z_OK && strlen(cmdbuf) > EXEC_LEN) {
- int status;
-
- status = do_subproc(cmdbuf);
+ if (err == Z_OK && !force && postcmdbuf[0] != '\0') {
+ status = do_subproc(postcmdbuf);
if (subproc_status(gettext("brand-specific postattach"),
status, B_FALSE) != ZONE_SUBPROC_OK) {
if ((err = zone_set_state(target_zone,
@@ -5344,62 +4813,70 @@ ask_yesno(boolean_t default_answer, const char *question)
}
}
+/* ARGSUSED */
static int
uninstall_func(int argc, char *argv[])
{
char line[ZONENAME_MAX + 128]; /* Enough for "Are you sure ..." */
char rootpath[MAXPATHLEN], zonepath[MAXPATHLEN];
char cmdbuf[MAXPATHLEN];
+ char precmdbuf[MAXPATHLEN];
boolean_t force = B_FALSE;
int lockfd, answer;
int err, arg;
+ boolean_t brand_help = B_FALSE;
brand_handle_t bh = NULL;
+ int status;
if (zonecfg_in_alt_root()) {
zerror(gettext("cannot uninstall zone in alternate root"));
return (Z_ERR);
}
+ /* Check the argv string for args we handle internally */
optind = 0;
+ opterr = 0;
while ((arg = getopt(argc, argv, "?F")) != EOF) {
switch (arg) {
case '?':
- sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL);
- return (optopt == '?' ? Z_OK : Z_USAGE);
+ if (optopt == '?') {
+ sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL);
+ brand_help = B_TRUE;
+ }
+ /* Ignore unknown options - may be brand specific. */
+ break;
case 'F':
force = B_TRUE;
break;
default:
- sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL);
- return (Z_USAGE);
+ /* Ignore unknown options - may be brand specific. */
+ break;
}
}
- if (argc > optind) {
- sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL);
- return (Z_USAGE);
- }
- if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE, B_FALSE)
- != Z_OK)
- return (Z_ERR);
-
- /*
- * Invoke brand-specific handler.
- */
- if (invoke_brand_handler(CMD_UNINSTALL, argv) != Z_OK)
- return (Z_ERR);
+ if (!brand_help) {
+ if (sanity_check(target_zone, CMD_UNINSTALL, B_FALSE, B_TRUE,
+ B_FALSE) != Z_OK)
+ return (Z_ERR);
- if (!force) {
- (void) snprintf(line, sizeof (line),
- gettext("Are you sure you want to %s zone %s"),
- cmd_to_str(CMD_UNINSTALL), target_zone);
- if ((answer = ask_yesno(B_FALSE, line)) == 0) {
- return (Z_OK);
- } else if (answer == -1) {
- zerror(gettext("Input not from terminal and -F "
- "not specified: %s not done."),
- cmd_to_str(CMD_UNINSTALL));
+ /*
+ * Invoke brand-specific handler.
+ */
+ if (invoke_brand_handler(CMD_UNINSTALL, argv) != Z_OK)
return (Z_ERR);
+
+ if (!force) {
+ (void) snprintf(line, sizeof (line),
+ gettext("Are you sure you want to %s zone %s"),
+ cmd_to_str(CMD_UNINSTALL), target_zone);
+ if ((answer = ask_yesno(B_FALSE, line)) == 0) {
+ return (Z_OK);
+ } else if (answer == -1) {
+ zerror(gettext("Input not from terminal and -F "
+ "not specified: %s not done."),
+ cmd_to_str(CMD_UNINSTALL));
+ return (Z_ERR);
+ }
}
}
@@ -5409,50 +4886,26 @@ uninstall_func(int argc, char *argv[])
zperror2(target_zone, gettext("could not get zone path"));
return (Z_ERR);
}
- if ((err = zone_get_rootpath(target_zone, rootpath,
- sizeof (rootpath))) != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("could not get root path"));
- return (Z_ERR);
- }
/*
- * If there seems to be a zoneadmd running for this zone, call it
- * to tell it that an uninstall is happening; if all goes well it
- * will then shut itself down.
+ * Fetch the uninstall and preuninstall hooks from the brand
+ * configuration.
*/
- if (ping_zoneadmd(target_zone) == Z_OK) {
- zone_cmd_arg_t zarg;
- zarg.cmd = Z_NOTE_UNINSTALLING;
- /* we don't care too much if this fails... just plow on */
- (void) call_zoneadmd(target_zone, &zarg);
- }
-
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
- zerror(gettext("another %s may have an operation in progress."),
- "zoneadm");
- return (Z_ERR);
- }
-
- /* Don't uninstall the zone if anything is mounted there */
- err = zonecfg_find_mounts(rootpath, NULL, NULL);
- if (err) {
- zerror(gettext("These file systems are mounted on "
- "subdirectories of %s.\n"), rootpath);
- (void) zonecfg_find_mounts(rootpath, zfm_print, NULL);
+ if ((bh = brand_open(target_brand)) == NULL) {
+ zerror(gettext("missing or invalid brand"));
return (Z_ERR);
}
- /* Fetch the uninstall hook from the brand configuration. */
- if ((bh = brand_open(target_brand)) == NULL) {
- zerror(gettext("missing or invalid brand"));
+ if (get_hook(bh, cmdbuf, sizeof (cmdbuf), brand_get_uninstall,
+ target_zone, zonepath) != Z_OK) {
+ zerror("invalid brand configuration: missing uninstall "
+ "resource");
+ brand_close(bh);
return (Z_ERR);
}
- (void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_preuninstall(bh, target_zone, zonepath,
- cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN, 0, NULL)
- != 0) {
+ if (get_hook(bh, precmdbuf, sizeof (precmdbuf), brand_get_preuninstall,
+ target_zone, zonepath) != Z_OK) {
zerror("invalid brand configuration: missing preuninstall "
"resource");
brand_close(bh);
@@ -5460,33 +4913,102 @@ uninstall_func(int argc, char *argv[])
}
brand_close(bh);
- if (strlen(cmdbuf) > EXEC_LEN) {
- int status;
+ /* Append all options to preuninstall hook. */
+ if (addoptions(precmdbuf, argv, sizeof (precmdbuf)) != Z_OK)
+ return (Z_ERR);
+
+ /* Append all options to uninstall hook. */
+ if (addoptions(cmdbuf, argv, sizeof (cmdbuf)) != Z_OK)
+ return (Z_ERR);
+
+ if (!brand_help) {
+ if ((err = zone_get_rootpath(target_zone, rootpath,
+ sizeof (rootpath))) != Z_OK) {
+ errno = err;
+ zperror2(target_zone, gettext("could not get root "
+ "path"));
+ return (Z_ERR);
+ }
+
+ /*
+ * If there seems to be a zoneadmd running for this zone, call
+ * it to tell it that an uninstall is happening; if all goes
+ * well it will then shut itself down.
+ */
+ if (zonecfg_ping_zoneadmd(target_zone) == Z_OK) {
+ zone_cmd_arg_t zarg;
+ zarg.cmd = Z_NOTE_UNINSTALLING;
+ /* we don't care too much if this fails, just plow on */
+ (void) zonecfg_call_zoneadmd(target_zone, &zarg, locale,
+ B_TRUE);
+ }
+
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ zerror(gettext("another %s may have an operation in "
+ "progress."), "zoneadm");
+ return (Z_ERR);
+ }
- /* If we have the force flag, append it. */
- if (force && addopt(cmdbuf, 0, "-F", sizeof (cmdbuf)) != Z_OK) {
- zerror("Preuninstall command line too long");
+ /* Don't uninstall the zone if anything is mounted there */
+ err = zonecfg_find_mounts(rootpath, NULL, NULL);
+ if (err) {
+ zerror(gettext("These file systems are mounted on "
+ "subdirectories of %s.\n"), rootpath);
+ (void) zonecfg_find_mounts(rootpath, zfm_print, NULL);
+ zonecfg_release_lock_file(target_zone, lockfd);
return (Z_ERR);
}
+ }
+ /* If we have a brand preuninstall hook, run it. */
+ if (!brand_help && precmdbuf[0] != '\0') {
status = do_subproc(cmdbuf);
if (subproc_status(gettext("brand-specific preuninstall"),
status, B_FALSE) != ZONE_SUBPROC_OK) {
+ zonecfg_release_lock_file(target_zone, lockfd);
return (Z_ERR);
}
}
- err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE);
- if (err != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("could not set state"));
- goto bad;
+ if (!brand_help) {
+ err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE);
+ if (err != Z_OK) {
+ errno = err;
+ zperror2(target_zone, gettext("could not set state"));
+ goto bad;
+ }
}
- if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) {
- errno = err;
- zperror2(target_zone, gettext("cleaning up zonepath failed"));
- goto bad;
+ /*
+ * If there is a brand uninstall hook, use it, otherwise use the
+ * built-in uninstall code.
+ */
+ if (cmdbuf[0] != '\0') {
+ /* Run the uninstall hook */
+ status = do_subproc_interactive(cmdbuf);
+ if ((status = subproc_status(gettext("brand-specific "
+ "uninstall"), status, B_FALSE)) != ZONE_SUBPROC_OK) {
+ if (status == ZONE_SUBPROC_USAGE && !brand_help)
+ sub_usage(SHELP_UNINSTALL, CMD_UNINSTALL);
+ if (!brand_help)
+ zonecfg_release_lock_file(target_zone, lockfd);
+ return (Z_ERR);
+ }
+
+ if (brand_help)
+ return (Z_OK);
+ } else {
+ /* If just help, we're done since there is no brand help. */
+ if (brand_help)
+ return (Z_OK);
+
+ /* Run the built-in uninstall support. */
+ if ((err = cleanup_zonepath(zonepath, B_FALSE)) != Z_OK) {
+ errno = err;
+ zperror2(target_zone, gettext("cleaning up zonepath "
+ "failed"));
+ goto bad;
+ }
}
err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED);
@@ -5495,7 +5017,7 @@ uninstall_func(int argc, char *argv[])
zperror2(target_zone, gettext("could not reset state"));
}
bad:
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
return (err);
}
@@ -5532,7 +5054,7 @@ mount_func(int argc, char *argv[])
zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT;
zarg.bootbuf[0] = '\0';
- if (call_zoneadmd(target_zone, &zarg) != 0) {
+ if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) {
zerror(gettext("call to %s failed"), "zoneadmd");
return (Z_ERR);
}
@@ -5552,7 +5074,7 @@ unmount_func(int argc, char *argv[])
return (Z_ERR);
zarg.cmd = Z_UNMOUNT;
- if (call_zoneadmd(target_zone, &zarg) != 0) {
+ if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) {
zerror(gettext("call to %s failed"), "zoneadmd");
return (Z_ERR);
}
@@ -5576,7 +5098,7 @@ mark_func(int argc, char *argv[])
if (invoke_brand_handler(CMD_MARK, argv) != Z_OK)
return (Z_ERR);
- if (grab_lock_file(target_zone, &lockfd) != Z_OK) {
+ if (zonecfg_grab_lock_file(target_zone, &lockfd) != Z_OK) {
zerror(gettext("another %s may have an operation in progress."),
"zoneadm");
return (Z_ERR);
@@ -5587,7 +5109,7 @@ mark_func(int argc, char *argv[])
errno = err;
zperror2(target_zone, gettext("could not set state"));
}
- release_lock_file(lockfd);
+ zonecfg_release_lock_file(target_zone, lockfd);
return (err);
}
@@ -6022,17 +5544,9 @@ main(int argc, char **argv)
* a zoneadm instance in our ancestry. If so, set zone_lock_cnt to
* indicate it. If not, make that explicit in our environment.
*/
- zone_lock_env = getenv(LOCK_ENV_VAR);
- if (zone_lock_env == NULL) {
- if (putenv(zoneadm_lock_not_held) != 0) {
- zperror(target_zone, B_TRUE);
- exit(Z_ERR);
- }
- } else {
+ zonecfg_init_lock_file(target_zone, &zone_lock_env);
+ if (zone_lock_env != NULL)
zoneadm_is_nested = B_TRUE;
- if (atoi(zone_lock_env) == 1)
- zone_lock_cnt = 1;
- }
/*
* If we are going to be operating on a single zone, retrieve its
diff --git a/usr/src/cmd/zoneadm/zoneadm.h b/usr/src/cmd/zoneadm/zoneadm.h
index 9cf02b82a5..2d09f83842 100644
--- a/usr/src/cmd/zoneadm/zoneadm.h
+++ b/usr/src/cmd/zoneadm/zoneadm.h
@@ -69,6 +69,9 @@ extern char *target_zone;
extern int clone_copy(char *source_zonepath, char *zonepath);
extern char *cmd_to_str(int cmd_num);
+extern int do_subproc_interactive(char *cmdbuf);
+extern int subproc_status(const char *cmd, int status,
+ boolean_t verbose_failure);
extern void zerror(const char *fmt, ...);
extern void zperror(const char *str, boolean_t zonecfg_error);
extern void zperror2(const char *zone, const char *str);
@@ -76,8 +79,10 @@ extern void zperror2(const char *zone, const char *str);
/*
* zfs.c
*/
-extern int clone_snapshot_zfs(char *snap_name, char *zonepath);
-extern int clone_zfs(char *source_zone, char *source_zonepath, char *zonepath);
+extern int clone_snapshot_zfs(char *snap_name, char *zonepath,
+ char *validatesnap);
+extern int clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
+ char *postsnapbuf);
extern void create_zfs_zonepath(char *zonepath);
extern int destroy_zfs(char *zonepath);
extern boolean_t is_zonepath_zfs(char *zonepath);
@@ -86,12 +91,4 @@ extern int verify_datasets(zone_dochandle_t handle);
extern int verify_fs_zfs(struct zone_fstab *fstab);
extern int init_zfs(void);
-/*
- * sw_cmp.c
- */
-extern int sw_cmp(zone_dochandle_t l_handle, zone_dochandle_t s_handle,
- uint_t flag);
-extern int sw_up_to_date(zone_dochandle_t l_handle, zone_dochandle_t s_handle,
- char *zonepath);
-
#endif /* _ZONEADM_H */
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index 0d11899f03..cd5a5681e9 100644
--- a/usr/src/cmd/zoneadmd/vplat.c
+++ b/usr/src/cmd/zoneadmd/vplat.c
@@ -4531,7 +4531,7 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
zoneid_t zoneid;
int res;
char pool_err[128];
- char zroot[MAXPATHLEN];
+ char zpath[MAXPATHLEN];
char cmdbuf[MAXPATHLEN];
char brand[MAXNAMELEN];
brand_handle_t bh = NULL;
@@ -4568,9 +4568,9 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
goto error;
}
- /* Get the path to the root of this zone */
- if (zone_get_zonepath(zone_name, zroot, sizeof (zroot)) != Z_OK) {
- zerror(zlogp, B_FALSE, "unable to determine zone root");
+ /* Get the zonepath of this zone */
+ if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
+ zerror(zlogp, B_FALSE, "unable to determine zone path");
goto error;
}
@@ -4585,8 +4585,8 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
* brand a chance to cleanup any custom configuration.
*/
(void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_halt(bh, zone_name, zroot, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL) < 0) {
+ if (brand_get_halt(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+ sizeof (cmdbuf) - EXEC_LEN) < 0) {
brand_close(bh);
zerror(zlogp, B_FALSE, "unable to determine branded zone's "
"halt callback.");
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c
index b7f9dfadf7..2651779301 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.c
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c
@@ -675,7 +675,7 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
{
zoneid_t zoneid;
struct stat st;
- char zroot[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN];
+ char zpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN];
char nbootargs[BOOTARGS_MAX];
char cmdbuf[MAXPATHLEN];
fs_callback_t cb;
@@ -716,14 +716,14 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
/*
* Get the brand's boot callback if it exists.
*/
- if (zone_get_zonepath(zone_name, zroot, sizeof (zroot)) != Z_OK) {
- zerror(zlogp, B_FALSE, "unable to determine zone root");
+ if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
+ zerror(zlogp, B_FALSE, "unable to determine zone path");
brand_close(bh);
return (-1);
}
(void) strcpy(cmdbuf, EXEC_PREFIX);
- if (brand_get_boot(bh, zone_name, zroot, cmdbuf + EXEC_LEN,
- sizeof (cmdbuf) - EXEC_LEN, 0, NULL) != 0) {
+ if (brand_get_boot(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+ sizeof (cmdbuf) - EXEC_LEN) != 0) {
zerror(zlogp, B_FALSE,
"unable to determine branded zone's boot callback");
brand_close(bh);
@@ -750,12 +750,12 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
assert(init_file[0] != '\0');
/* Try to anticipate possible problems: Make sure init is executable. */
- if (zone_get_rootpath(zone_name, zroot, sizeof (zroot)) != Z_OK) {
+ if (zone_get_rootpath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
zerror(zlogp, B_FALSE, "unable to determine zone root");
return (-1);
}
- (void) snprintf(initpath, sizeof (initpath), "%s%s", zroot, init_file);
+ (void) snprintf(initpath, sizeof (initpath), "%s%s", zpath, init_file);
if (stat(initpath, &st) == -1) {
zerror(zlogp, B_TRUE, "could not stat %s", initpath);
diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h
index 6f1cfe8bde..70f4110c63 100644
--- a/usr/src/head/libzonecfg.h
+++ b/usr/src/head/libzonecfg.h
@@ -426,14 +426,15 @@ extern int zonecfg_get_attach_handle(const char *, const char *,
extern int zonecfg_attach_manifest(int, zone_dochandle_t,
zone_dochandle_t);
extern int zonecfg_detach_save(zone_dochandle_t, uint_t);
-extern int zonecfg_get_detach_info(zone_dochandle_t, boolean_t);
extern boolean_t zonecfg_detached(const char *);
extern void zonecfg_rm_detached(zone_dochandle_t, boolean_t forced);
-extern int zonecfg_devwalk(zone_dochandle_t handle,
- int (*cb)(const char *, uid_t, gid_t, mode_t, const char *, void *),
- void *data);
+extern int zonecfg_dev_manifest(zone_dochandle_t);
extern int zonecfg_devperms_apply(zone_dochandle_t, const char *,
uid_t, gid_t, mode_t, const char *);
+extern void zonecfg_set_swinv(zone_dochandle_t);
+extern int zonecfg_add_pkg(zone_dochandle_t, char *, char *);
+extern int zonecfg_add_patch(zone_dochandle_t, char *, void **);
+extern int zonecfg_add_patch_obs(char *, void *);
/*
* External zone verification support.
@@ -542,6 +543,16 @@ extern int zonecfg_add_scratch(FILE *, const char *, const char *,
extern int zonecfg_delete_scratch(FILE *, const char *);
extern boolean_t zonecfg_is_scratch(const char *);
+/*
+ * zoneadmd support functions. Shared between zoneadm and brand hook code.
+ */
+extern void zonecfg_init_lock_file(const char *, char **);
+extern void zonecfg_release_lock_file(const char *, int);
+extern int zonecfg_grab_lock_file(const char *, int *);
+extern int zonecfg_ping_zoneadmd(const char *);
+extern int zonecfg_call_zoneadmd(const char *, zone_cmd_arg_t *, char *,
+ boolean_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/brand/lx/zone/config.xml b/usr/src/lib/brand/lx/zone/config.xml
index 0f252c502d..2bcf8c60c4 100644
--- a/usr/src/lib/brand/lx/zone/config.xml
+++ b/usr/src/lib/brand/lx/zone/config.xml
@@ -38,7 +38,7 @@
<login_cmd>/bin/login -h zone:%Z -f %u</login_cmd>
<user_cmd>/usr/bin/getent passwd %u</user_cmd>
- <install>/usr/lib/brand/lx/lx_install %z %R %*</install>
+ <install>/usr/lib/brand/lx/lx_install %z %R</install>
<installopts>d:hsvX</installopts>
<boot>/usr/lib/brand/lx/lx_support boot %R %z</boot>
<halt>/usr/lib/brand/lx/lx_support halt %R %z</halt>
diff --git a/usr/src/lib/brand/native/zone/Makefile b/usr/src/lib/brand/native/zone/Makefile
index 1d1a92fe92..7600a19e2c 100644
--- a/usr/src/lib/brand/native/zone/Makefile
+++ b/usr/src/lib/brand/native/zone/Makefile
@@ -26,16 +26,21 @@
# ident "%Z%%M% %I% %E% SMI"
#
+PROG= sw_support
BRAND= native
-PROGS= postclone attach_update
+PROGS= attach_update $(PROG)
XMLDOCS= config.xml platform.xml
TEMPLATES= SUNWdefault.xml SUNWblank.xml
+CLOBBERFILES= $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES)
all: $(PROGS)
include $(SRC)/cmd/Makefile.cmd
include ../../Makefile.brand
+CPPFLAGS += -I/usr/include/libxml2 -D_REENTRANT
+LDLIBS += -lzonecfg -luutil
+
POFILES= $(PROGS:%=%.po)
POFILE= native_zone.po
@@ -45,14 +50,12 @@ $(POFILE): $(POFILES)
_msg: $(MSGDOMAINPOFILE)
-lint:
+lint: lint_PROG
install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES)
clean:
-$(RM) $(PROGS)
-clobber: clean
- -$(RM) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES)
-
include $(SRC)/Makefile.msg.targ
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/native/zone/config.xml b/usr/src/lib/brand/native/zone/config.xml
index 42318a647b..e8202c32d8 100644
--- a/usr/src/lib/brand/native/zone/config.xml
+++ b/usr/src/lib/brand/native/zone/config.xml
@@ -38,14 +38,15 @@
<login_cmd>/usr/bin/login -z %Z -f %u</login_cmd>
<user_cmd>/usr/bin/getent passwd %u</user_cmd>
- <install>/usr/lib/lu/lucreatezone -z %z</install>
- <installopts></installopts>
- <boot></boot>
- <halt></halt>
+ <install>/usr/lib/brand/native/sw_support install %z %R</install>
<verify_cfg></verify_cfg>
<verify_adm></verify_adm>
- <postclone>/usr/lib/brand/native/postclone %z %R</postclone>
- <postinstall></postinstall>
+ <postclone>/usr/lib/brand/native/sw_support postclone %z %R</postclone>
+ <attach>/usr/lib/brand/native/sw_support attach %z %R</attach>
+ <detach>/usr/lib/brand/native/sw_support detach %z %R</detach>
+ <presnap>/usr/lib/brand/native/sw_support presnap %z %R</presnap>
+ <postsnap>/usr/lib/brand/native/sw_support postsnap %z %R</postsnap>
+ <validatesnap>/usr/lib/brand/native/sw_support validatesnap %z %R</validatesnap>
<privilege set="default" name="contract_event" />
<privilege set="default" name="contract_identity" />
diff --git a/usr/src/lib/brand/native/zone/sw_support.c b/usr/src/lib/brand/native/zone/sw_support.c
new file mode 100644
index 0000000000..e3a53e6e5b
--- /dev/null
+++ b/usr/src/lib/brand/native/zone/sw_support.c
@@ -0,0 +1,3463 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * sw_support does install, detach and attach processing for svr4 pkgs.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wait.h>
+#include <zone.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libzonecfg.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <dirent.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+#include <fcntl.h>
+#include <door.h>
+#include <macros.h>
+#include <libgen.h>
+#include <fnmatch.h>
+#include <strings.h>
+
+#include <libzonecfg.h>
+
+#define ZONE_SUBPROC_OK 0
+#define ZONE_SUBPROC_USAGE 253
+#define ZONE_SUBPROC_NOTCOMPLETE 254
+#define ZONE_SUBPROC_FATAL 255
+
+#define Z_ERR 1
+#define Z_USAGE 2
+#define Z_FATAL 3
+
+#define SW_CMP_NONE 0x0
+#define SW_CMP_SRC 0x01
+#define SW_CMP_SILENT 0x02
+
+#define DETACHED "SUNWdetached.xml"
+#define ATTACH_FORCED "SUNWattached.xml"
+#define PKG_PATH "/var/sadm/pkg"
+#define CONTENTS_FILE "/var/sadm/install/contents"
+#define SUNW_PKG_ALL_ZONES "SUNW_PKG_ALLZONES=true\n"
+#define SUNW_PKG_THIS_ZONE "SUNW_PKG_THISZONE=true\n"
+#define VERSION "VERSION="
+#define PATCHLIST "PATCHLIST="
+#define PATCHINFO "PATCH_INFO_"
+#define PKGINFO_RD_LEN 128
+#define MY_BRAND_NAME "native"
+
+#define EXEC_PREFIX "exec "
+#define EXEC_LEN (strlen(EXEC_PREFIX))
+#define RMCOMMAND "/usr/bin/rm -rf"
+
+/* 0755 is the default directory mode. */
+#define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+
+enum zn_ipd_fs {ZONE_IPD, ZONE_FS};
+
+struct zone_pkginfo {
+ boolean_t zpi_all_zones;
+ boolean_t zpi_this_zone;
+ int zpi_patch_cnt;
+ char *zpi_version;
+ char **zpi_patchinfo;
+};
+
+typedef struct {
+ uu_avl_node_t patch_node;
+ char *patch_num;
+ char *patch_vers;
+ uu_list_t *obs_patches;
+} patch_node_t;
+
+typedef struct {
+ uu_list_node_t link;
+ char *patch_num;
+} obs_patch_node_t;
+
+typedef struct {
+ uu_avl_t *obs_patches_avl;
+ zone_dochandle_t handle;
+ int res;
+} patch_parms_t;
+
+static char *locale;
+static char *zonename;
+static char *zonepath;
+
+/* used in attach_func() and signal handler */
+static volatile boolean_t attach_interupted;
+
+void
+zperror(const char *str, boolean_t zonecfg_error)
+{
+ (void) fprintf(stderr, "%s: %s\n", str,
+ zonecfg_error ? zonecfg_strerror(errno) : strerror(errno));
+}
+
+static int sw_cmp(zone_dochandle_t, zone_dochandle_t, uint_t);
+
+/* PRINTFLIKE1 */
+void
+zerror(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+ (void) fprintf(stderr, "zone '%s': ", zonename);
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
+
+static int
+do_subproc(char *cmdbuf)
+{
+ void (*saveint)(int);
+ void (*saveterm)(int);
+ void (*savequit)(int);
+ void (*savehup)(int);
+ int pid, child, status;
+
+ /*
+ * do_subproc() links stdin to /dev/null, which would break any
+ * interactive subprocess we try to launch here.
+ */
+ if ((child = vfork()) == 0) {
+ (void) execl("/bin/sh", "sh", "-c", cmdbuf, (char *)NULL);
+ }
+
+ if (child == -1)
+ return (-1);
+
+ saveint = sigset(SIGINT, SIG_IGN);
+ saveterm = sigset(SIGTERM, SIG_IGN);
+ savequit = sigset(SIGQUIT, SIG_IGN);
+ savehup = sigset(SIGHUP, SIG_IGN);
+
+ while ((pid = waitpid(child, &status, 0)) != child && pid != -1)
+ ;
+
+ (void) sigset(SIGINT, saveint);
+ (void) sigset(SIGTERM, saveterm);
+ (void) sigset(SIGQUIT, savequit);
+ (void) sigset(SIGHUP, savehup);
+
+ return (pid == -1 ? -1 : status);
+}
+
+static int
+subproc_status(const char *cmd, int status, boolean_t verbose_failure)
+{
+ if (WIFEXITED(status)) {
+ int exit_code = WEXITSTATUS(status);
+
+ if ((verbose_failure) && (exit_code != ZONE_SUBPROC_OK))
+ zerror(gettext("'%s' failed with exit code %d."), cmd,
+ exit_code);
+
+ return (exit_code);
+ } else if (WIFSIGNALED(status)) {
+ int signal = WTERMSIG(status);
+ char sigstr[SIG2STR_MAX];
+
+ if (sig2str(signal, sigstr) == 0) {
+ zerror(gettext("'%s' terminated by signal SIG%s."), cmd,
+ sigstr);
+ } else {
+ zerror(gettext("'%s' terminated by an unknown signal."),
+ cmd);
+ }
+ } else {
+ zerror(gettext("'%s' failed for unknown reasons."), cmd);
+ }
+
+ /*
+ * Assume a subprocess that died due to a signal or an unknown error
+ * should be considered an exit code of ZONE_SUBPROC_FATAL, as the
+ * user will likely need to do some manual cleanup.
+ */
+ return (ZONE_SUBPROC_FATAL);
+}
+
+/*
+ * Maintain a space separated list of unique pkg names. PATH_MAX is used in
+ * the pkg code as the maximum size for a pkg name.
+ */
+static int
+add_pkg_to_str(char **str, char *pkg)
+{
+ int len, newlen;
+ char tstr[PATH_MAX + 3];
+ char *tmp;
+
+ len = strlen(pkg);
+ if (*str == NULL) {
+ /* space for str + 2 spaces + NULL */
+ if ((*str = (char *)malloc(len + 3)) == NULL)
+ return (Z_NOMEM);
+ (void) snprintf(*str, len + 3, " %s ", pkg);
+ return (Z_OK);
+ }
+
+ (void) snprintf(tstr, sizeof (tstr), " %s ", pkg);
+ if (strstr(*str, tstr) != NULL)
+ return (Z_OK);
+
+ /* space for str + 1 space + NULL */
+ newlen = strlen(*str) + len + 2;
+ if ((tmp = (char *)realloc(*str, newlen)) == NULL)
+ return (Z_NOMEM);
+ *str = tmp;
+ (void) strlcat(*str, pkg, newlen);
+ (void) strlcat(*str, " ", newlen);
+ return (Z_OK);
+}
+
+/*
+ * Process a list of pkgs from an entry in the contents file, adding each pkg
+ * name to the list of pkgs.
+ *
+ * It is possible for the pkg name to be preceeded by a special character
+ * which indicates some bookkeeping information for pkging. Check if the
+ * first char is not an Alpha char. If so, skip over it.
+ */
+static int
+add_pkg_list(char *lastp, char ***plist, int *pcnt, char **pkg_warn)
+{
+ char *p;
+ int pkg_cnt = *pcnt;
+ char **pkgs = *plist;
+ int res = Z_OK;
+
+ while ((p = strtok_r(NULL, " ", &lastp)) != NULL) {
+ char **tmpp;
+ int i;
+
+ /* skip over any special pkg bookkeeping char */
+ if (!isalpha(*p)) {
+ p++;
+ if ((res = add_pkg_to_str(pkg_warn, p)) != Z_OK)
+ break;
+ }
+
+ /* Check if the pkg is already in the list */
+ for (i = 0; i < pkg_cnt; i++) {
+ if (strcmp(p, pkgs[i]) == 0)
+ break;
+ }
+
+ if (i < pkg_cnt)
+ continue;
+
+ /* The pkg is not in the list; add it. */
+ if ((tmpp = (char **)realloc(pkgs,
+ sizeof (char *) * (pkg_cnt + 1))) == NULL) {
+ res = Z_NOMEM;
+ break;
+ }
+ pkgs = tmpp;
+
+ if ((pkgs[pkg_cnt] = strdup(p)) == NULL) {
+ res = Z_NOMEM;
+ break;
+ }
+ pkg_cnt++;
+ }
+
+ *plist = pkgs;
+ *pcnt = pkg_cnt;
+
+ return (res);
+}
+
+/*
+ * Process an entry from the contents file (type "directory"). If the
+ * directory path is in the list of ipds and is not under a lofs mount within
+ * the ipd then add the associated list of pkgs to the pkg list. The input
+ * parameter "entry" will be broken up by the parser within this function so
+ * its value will be modified when this function exits.
+ *
+ * The entries we are looking for will look something like:
+ * /usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
+ */
+static int
+get_path_pkgs(char *entry, char **ipds, char **fss, char ***pkgs, int *pkg_cnt,
+ char **pkg_warn)
+{
+ char *f1;
+ char *f2;
+ char *lastp;
+ int i;
+ char *nlp;
+
+ if ((f1 = strtok_r(entry, " ", &lastp)) == NULL ||
+ (f2 = strtok_r(NULL, " ", &lastp)) == NULL || strcmp(f2, "d") != 0)
+ return (Z_OK);
+
+ /* Check if this directory entry is in the list of ipds. */
+ for (i = 0; ipds[i] != NULL; i++) {
+ char wildcard[MAXPATHLEN];
+
+ /*
+ * We want to match on the path and any other directory
+ * entries under this path. When we use FNM_PATHNAME then
+ * that means '/' will not be matched by a wildcard (*) so
+ * we omit FNM_PATHNAME on the call with the wildcard matching.
+ */
+ (void) snprintf(wildcard, sizeof (wildcard), "%s/*", ipds[i]);
+ if (fnmatch(ipds[i], f1, FNM_PATHNAME) == 0 ||
+ fnmatch(wildcard, f1, 0) == 0) {
+ /* It looks like we do want the pkgs for this path. */
+ break;
+ }
+ }
+
+ /* This entry did not match any of the ipds. */
+ if (ipds[i] == NULL)
+ return (Z_OK);
+
+ /*
+ * Check if there is a fs mounted under the ipd. If so, ignore this
+ * entry.
+ */
+ for (i = 0; fss[i] != NULL; i++) {
+ char wildcard[MAXPATHLEN];
+
+ (void) snprintf(wildcard, sizeof (wildcard), "%s/*", fss[i]);
+ if (fnmatch(fss[i], f1, FNM_PATHNAME) == 0 ||
+ fnmatch(wildcard, f1, 0) == 0) {
+ /* We should ignore this path. */
+ break;
+ }
+ }
+
+ /* If not null, then we matched an fs mount point so ignore entry. */
+ if (fss[i] != NULL)
+ return (Z_OK);
+
+ /*
+ * We do want the pkgs for this entry. First, skip over the next 4
+ * fields in the entry so that we call add_pkg_list starting with the
+ * pkg names.
+ */
+ for (i = 0; i < 4 && strtok_r(NULL, " ", &lastp) != NULL; i++)
+ ;
+ /* If there are < 4 fields this entry is corrupt, just skip it. */
+ if (i < 4)
+ return (Z_OK);
+
+ /* strip newline from the line */
+ nlp = (lastp + strlen(lastp) - 1);
+ if (*nlp == '\n')
+ *nlp = '\0';
+
+ return (add_pkg_list(lastp, pkgs, pkg_cnt, pkg_warn));
+}
+
+/*
+ * Read an entry from a pkginfo or contents file. Some of these lines can
+ * either be arbitrarily long or be continued by a backslash at the end of
+ * the line. This function coalesces lines that are longer than the read
+ * buffer, and lines that are continued, into one buffer which is returned.
+ * The caller must free this memory. NULL is returned when we hit EOF or
+ * if we run out of memory (errno is set to ENOMEM).
+ */
+static char *
+read_pkg_data(FILE *fp)
+{
+ char *start;
+ char *inp;
+ char *p;
+ int char_cnt = 0;
+
+ errno = 0;
+ if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ inp = start;
+ while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
+ int len;
+
+ len = strlen(inp);
+ if (inp[len - 1] == '\n' &&
+ (len == 1 || inp[len - 2] != '\\')) {
+ char_cnt = len;
+ break;
+ }
+
+ if (inp[len - 2] == '\\')
+ char_cnt += len - 2;
+ else
+ char_cnt += PKGINFO_RD_LEN - 1;
+
+ if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
+ errno = ENOMEM;
+ break;
+ }
+
+ start = p;
+ inp = start + char_cnt;
+ }
+
+ if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
+ free(start);
+ start = NULL;
+ }
+
+ return (start);
+}
+
+static void
+free_ipd_pkgs(char **pkgs, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ free(pkgs[i]);
+ free(pkgs);
+}
+
+/*
+ * Get a list of the inherited pkg dirs or fs entries configured for the
+ * zone. The type parameter will be either ZONE_IPD or ZONE_FS.
+ */
+static int
+get_ipd_fs_list(zone_dochandle_t handle, enum zn_ipd_fs type, char ***list)
+{
+ int res;
+ struct zone_fstab fstab;
+ int cnt = 0;
+ char **entries = NULL;
+ int i;
+ int (*fp)(zone_dochandle_t, struct zone_fstab *);
+
+ if (type == ZONE_IPD) {
+ fp = zonecfg_getipdent;
+ res = zonecfg_setipdent(handle);
+ } else {
+ fp = zonecfg_getfsent;
+ res = zonecfg_setfsent(handle);
+ }
+
+ if (res != Z_OK)
+ return (res);
+
+ while (fp(handle, &fstab) == Z_OK) {
+ char **p;
+
+ if ((p = (char **)realloc(entries,
+ sizeof (char *) * (cnt + 1))) == NULL) {
+ res = Z_NOMEM;
+ break;
+ }
+ entries = p;
+
+ if ((entries[cnt] = strdup(fstab.zone_fs_dir)) == NULL) {
+ res = Z_NOMEM;
+ break;
+ }
+
+ cnt++;
+ }
+
+ if (type == ZONE_IPD)
+ (void) zonecfg_endipdent(handle);
+ else
+ (void) zonecfg_endfsent(handle);
+
+ /* Add a NULL terminating element. */
+ if (res == Z_OK) {
+ char **p;
+
+ if ((p = (char **)realloc(entries,
+ sizeof (char *) * (cnt + 1))) == NULL) {
+ res = Z_NOMEM;
+ } else {
+ entries = p;
+ entries[cnt] = NULL;
+ }
+ }
+
+ if (res != Z_OK) {
+ if (entries != NULL) {
+ for (i = 0; i < cnt; i++)
+ free(entries[i]);
+ free(entries);
+ }
+ return (res);
+ }
+
+ *list = entries;
+ return (Z_OK);
+}
+
+/*
+ * Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
+ * list of pkgs that deliver into those dirs.
+ */
+static int
+get_ipd_pkgs(zone_dochandle_t handle, char ***pkg_list, int *cnt)
+{
+ int res;
+ char **ipds;
+ char **fss;
+ int pkg_cnt = 0;
+ char **pkgs = NULL;
+ int i;
+
+ if ((res = get_ipd_fs_list(handle, ZONE_IPD, &ipds)) != Z_OK)
+ return (res);
+
+ if ((res = get_ipd_fs_list(handle, ZONE_FS, &fss)) != Z_OK) {
+ for (i = 0; ipds[i] != NULL; i++)
+ free(ipds[i]);
+ free(ipds);
+ return (res);
+ }
+
+ /* We only have to process the contents file if we have ipds. */
+ if (ipds != NULL) {
+ FILE *fp;
+
+ if ((fp = fopen(CONTENTS_FILE, "r")) != NULL) {
+ char *buf;
+ char *pkg_warn = NULL;
+
+ while ((buf = read_pkg_data(fp)) != NULL) {
+ res = get_path_pkgs(buf, ipds, fss, &pkgs,
+ &pkg_cnt, &pkg_warn);
+ free(buf);
+ if (res != Z_OK)
+ break;
+ }
+
+ (void) fclose(fp);
+
+ if (pkg_warn != NULL) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: package operation in progress "
+ "on the following packages:\n %s\n"),
+ pkg_warn);
+ free(pkg_warn);
+ }
+ }
+ }
+
+ for (i = 0; ipds[i] != NULL; i++)
+ free(ipds[i]);
+ free(ipds);
+
+ for (i = 0; fss[i] != NULL; i++)
+ free(fss[i]);
+ free(fss);
+
+ if (res != Z_OK) {
+ free_ipd_pkgs(pkgs, pkg_cnt);
+ } else {
+ *pkg_list = pkgs;
+ *cnt = pkg_cnt;
+ }
+
+ return (res);
+}
+
+/*
+ * Return true if pkg_name is in the list of pkgs that deliver into an
+ * inherited pkg directory for the zone.
+ */
+static boolean_t
+dir_pkg(char *pkg_name, char **pkg_list, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (strcmp(pkg_name, pkg_list[i]) == 0)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Keep track of obsoleted patches for this specific patch. We don't need to
+ * keep track of the patch version since once a patch is obsoleted, all prior
+ * versions are also obsolete and there won't be any new versions.
+ */
+static int
+add_obs_patch(patch_node_t *patch, char *num, uu_list_pool_t *patches_pool)
+{
+ obs_patch_node_t *obs;
+
+ if (patch->obs_patches == NULL) {
+ if ((patch->obs_patches = uu_list_create(patches_pool, NULL,
+ 0)) == NULL)
+ return (Z_NOMEM);
+ }
+
+ if ((obs = (obs_patch_node_t *)malloc(sizeof (obs_patch_node_t)))
+ == NULL)
+ return (Z_NOMEM);
+
+ if ((obs->patch_num = strdup(num)) == NULL) {
+ free(obs);
+ return (Z_NOMEM);
+ }
+
+ uu_list_node_init(obs, &obs->link, patches_pool);
+ (void) uu_list_insert_before(patch->obs_patches, NULL, obs);
+
+ return (Z_OK);
+}
+
+/*
+ * Keep track of obsoleted patches. We don't need to keep track of the patch
+ * version since once a patch is obsoleted, all prior versions are also
+ * obsolete and there won't be any new versions.
+ */
+static int
+save_obs_patch(char *num, uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches)
+{
+ patch_node_t *patch;
+ uu_avl_index_t where;
+
+ if ((patch = (patch_node_t *)malloc(sizeof (patch_node_t))) == NULL)
+ return (Z_NOMEM);
+
+ if ((patch->patch_num = strdup(num)) == NULL) {
+ free(patch);
+ return (Z_NOMEM);
+ }
+
+ patch->patch_vers = NULL;
+ patch->obs_patches = NULL;
+
+ uu_avl_node_init(patch, &patch->patch_node, patches_pool);
+
+ if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL) {
+ free(patch->patch_num);
+ free(patch);
+ return (Z_OK);
+ }
+
+ uu_avl_insert(obs_patches, patch, where);
+ return (Z_OK);
+}
+
+/*
+ * Keep a list of patches for a pkg. If we see a newer version of a patch,
+ * we only keep track of the newer version.
+ */
+static boolean_t
+save_patch(patch_node_t *patch, uu_avl_t *patches_avl)
+{
+ patch_node_t *existing;
+ uu_avl_index_t where;
+
+ /*
+ * Check if this is a newer version of a patch we already have.
+ * If it is an older version of a patch we already have, ignore it.
+ */
+ if ((existing = (patch_node_t *)uu_avl_find(patches_avl, patch, NULL,
+ &where)) != NULL) {
+ char *endptr;
+ ulong_t pvers, evers;
+
+ pvers = strtoul(patch->patch_vers, &endptr, 10);
+ evers = strtoul(existing->patch_vers, &endptr, 10);
+
+ if (pvers <= evers)
+ return (B_FALSE);
+
+ /*
+ * Remove the lower version patch from the tree so we can
+ * insert the new higher version one. We also discard the
+ * obsolete patch list from the old version since the new
+ * version will have its own, likely different, list.
+ */
+ uu_avl_remove(patches_avl, existing);
+ free(existing->patch_num);
+ free(existing->patch_vers);
+ if (existing->obs_patches != NULL) {
+ obs_patch_node_t *op;
+ void *cookie2 = NULL;
+
+ while ((op = uu_list_teardown(existing->obs_patches,
+ &cookie2)) != NULL) {
+ free(op->patch_num);
+ free(op);
+ }
+ uu_list_destroy(existing->obs_patches);
+ }
+ free(existing);
+
+ /*
+ * Now that the old one is gone, find the new location
+ * in the tree.
+ */
+ (void) uu_avl_find(patches_avl, patch, NULL, &where);
+ }
+
+ uu_avl_insert(patches_avl, patch, where);
+ return (B_TRUE);
+}
+
+/*
+ * Check if a patch is on the list of obsoleted patches. We don't need to
+ * check the patch version since once a patch is obsoleted, all prior versions
+ * are also obsolete and there won't be any new versions.
+ */
+static boolean_t
+obsolete_patch(patch_node_t *patch, uu_avl_t *obs_patches)
+{
+ uu_avl_index_t where;
+
+ if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/* ARGSUSED */
+static int
+patch_node_compare(const void *l_arg, const void *r_arg, void *private)
+{
+ patch_node_t *l = (patch_node_t *)l_arg;
+ patch_node_t *r = (patch_node_t *)r_arg;
+ char *endptr;
+ ulong_t lnum, rnum;
+
+ lnum = strtoul(l->patch_num, &endptr, 10);
+ rnum = strtoul(r->patch_num, &endptr, 10);
+
+ if (lnum > rnum)
+ return (1);
+ if (lnum < rnum)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Parse the patchinfo string for the patch.
+ *
+ * We are parsing entries of the form:
+ * PATCH_INFO_121454-02=Installed: Wed Dec 7 07:13:51 PST 2005 From: mum \
+ * Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
+ * 119255-06 Incompatibles:
+ *
+ * A backed out patch will have "backed out\n" as the status. We should
+ * skip these patches. We also ignore any entries that seem to be
+ * corrupted. Obsolete patches are saved in the obs_patches parameter
+ * AVL list.
+ */
+static int
+parse_info(char *patchinfo, uu_avl_pool_t *patches_pool, uu_avl_t *patches_avl,
+ uu_avl_t *obs_patches, uu_list_pool_t *list_pool)
+{
+ char *p;
+ char *lastp;
+ char *ep;
+ char *pvers;
+ boolean_t add_info = B_FALSE;
+ patch_node_t *patch;
+
+ if (strlen(patchinfo) < (sizeof (PATCHINFO) - 1))
+ return (Z_OK);
+
+ /* Skip over "PATCH_INFO_" to get the patch id. */
+ p = patchinfo + sizeof (PATCHINFO) - 1;
+ if ((ep = strchr(p, '=')) == NULL)
+ return (Z_OK);
+
+ *ep++ = '\0';
+
+ /* Ignore all but installed patches. */
+ if (strncmp(ep, "Installed:", 10) != 0)
+ return (Z_OK);
+
+ /* remove newline */
+ lastp = (ep + strlen(ep) - 1);
+ if (*lastp == '\n')
+ *lastp = '\0';
+
+ if ((patch = (patch_node_t *)malloc(sizeof (patch_node_t))) == NULL)
+ return (Z_NOMEM);
+
+ if ((pvers = strchr(p, '-')) != NULL)
+ *pvers++ = '\0';
+ else
+ pvers = "";
+
+ if ((patch->patch_num = strdup(p)) == NULL) {
+ free(patch);
+ return (Z_NOMEM);
+ }
+ if ((patch->patch_vers = strdup(pvers)) == NULL) {
+ free(patch->patch_num);
+ free(patch);
+ return (Z_NOMEM);
+ }
+ patch->obs_patches = NULL;
+
+ uu_avl_node_init(patch, &patch->patch_node, patches_pool);
+ if (!save_patch(patch, patches_avl)) {
+ free(patch->patch_num);
+ free(patch->patch_vers);
+ assert(patch->obs_patches == NULL);
+ free(patch);
+ return (Z_OK);
+ }
+
+ /*
+ * Start with the first token. This will probably be "Installed:".
+ * If we can't tokenize this entry, just return.
+ */
+ if ((p = strtok_r(ep, " ", &lastp)) == NULL)
+ return (Z_OK);
+
+ do {
+ if (strcmp(p, "Installed:") == 0 ||
+ strcmp(p, "Requires:") == 0 ||
+ strcmp(p, "From:") == 0 ||
+ strcmp(p, "Incompatibles:") == 0) {
+ add_info = B_FALSE;
+ continue;
+ } else if (strcmp(p, "Obsoletes:") == 0) {
+ add_info = B_TRUE;
+ continue;
+ }
+
+ if (!add_info)
+ continue;
+
+ if ((pvers = strchr(p, '-')) != NULL)
+ *pvers = '\0';
+
+ /*
+ * We save all of the obsolete patches in one big list in the
+ * obs_patches AVL tree so that we know not to output those as
+ * part of the sw dependencies. However, we also need to save
+ * the obsolete patch information for this sepcific patch so
+ * so that we can do the cross manifest patch checking
+ * correctly.
+ */
+ if (save_obs_patch(p, patches_pool, obs_patches) != Z_OK)
+ return (Z_NOMEM);
+ if (add_obs_patch(patch, p, list_pool) != Z_OK)
+ return (Z_NOMEM);
+ } while ((p = strtok_r(NULL, " ", &lastp)) != NULL);
+
+ return (Z_OK);
+}
+
+/*
+ * AVL walker callback used to add patch to XML manifest.
+ *
+ * PATH_MAX is used in the pkg/patch code as the maximum size for the patch
+ * number/version string.
+ */
+static int
+avl_add_patch(void *e, void *p)
+{
+ xmlNodePtr node;
+ char id[PATH_MAX];
+ patch_node_t *patch;
+ patch_parms_t *args;
+
+ patch = e;
+ args = p;
+
+ /* skip this patch if it has been obsoleted */
+ if (obsolete_patch(patch, args->obs_patches_avl))
+ return (UU_WALK_NEXT);
+
+ if (patch->patch_vers[0] == '\0')
+ (void) snprintf(id, sizeof (id), "%s", patch->patch_num);
+ else
+ (void) snprintf(id, sizeof (id), "%s-%s", patch->patch_num,
+ patch->patch_vers);
+
+ if ((args->res = zonecfg_add_patch(args->handle, id, (void **)&node))
+ != Z_OK)
+ return (UU_WALK_DONE);
+
+ if (patch->obs_patches != NULL) {
+ obs_patch_node_t *op;
+
+ for (op = uu_list_first(patch->obs_patches); op != NULL;
+ op = uu_list_next(patch->obs_patches, op)) {
+ (void) snprintf(id, sizeof (id), "%s", op->patch_num);
+ if ((args->res = zonecfg_add_patch_obs(id, node))
+ != Z_OK)
+ return (UU_WALK_DONE);
+ }
+ }
+
+ return (UU_WALK_NEXT);
+}
+
+static void
+patch_avl_delete(uu_avl_t *patches_avl)
+{
+ if (patches_avl != NULL) {
+ patch_node_t *p;
+ void *cookie = NULL;
+
+ while ((p = (patch_node_t *)uu_avl_teardown(patches_avl,
+ &cookie)) != NULL) {
+ free(p->patch_num);
+ free(p->patch_vers);
+
+ if (p->obs_patches != NULL) {
+ obs_patch_node_t *op;
+ void *cookie2 = NULL;
+
+ while ((op = uu_list_teardown(p->obs_patches,
+ &cookie2)) != NULL) {
+ free(op->patch_num);
+ free(op);
+ }
+ uu_list_destroy(p->obs_patches);
+ }
+
+ free(p);
+ }
+
+ uu_avl_destroy(patches_avl);
+ }
+}
+
+/*
+ * Add the unique, highest version patches that are associated with this pkg
+ * to the sw inventory on the handle.
+ */
+static int
+add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop,
+ uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches,
+ uu_list_pool_t *list_pool)
+{
+ int i;
+ int res;
+ uu_avl_t *patches_avl;
+ patch_parms_t args;
+
+ if ((patches_avl = uu_avl_create(patches_pool, NULL, UU_DEFAULT))
+ == NULL)
+ return (Z_NOMEM);
+
+ for (i = 0; i < infop->zpi_patch_cnt; i++) {
+ if ((res = parse_info(infop->zpi_patchinfo[i], patches_pool,
+ patches_avl, obs_patches, list_pool)) != Z_OK) {
+ patch_avl_delete(patches_avl);
+ return (res);
+ }
+ }
+
+ args.obs_patches_avl = obs_patches;
+ args.handle = handle;
+ args.res = Z_OK;
+
+ (void) uu_avl_walk(patches_avl, avl_add_patch, &args, 0);
+
+ patch_avl_delete(patches_avl);
+ return (args.res);
+}
+
+/*
+ * Keep track of the pkgs we have already processed so that we can quickly
+ * skip those pkgs while recursively doing dependents.
+ */
+static boolean_t
+pkg_in_manifest(uu_avl_t *saw_pkgs, char *pname, uu_avl_pool_t *pkgs_pool)
+{
+ uu_avl_index_t where;
+
+ if (uu_avl_find(saw_pkgs, pname, NULL, &where) == NULL) {
+ zone_pkg_entry_t *pkg;
+
+ /*
+ * We need to add it. If we don't have memory we just skip
+ * this pkg since this routine improves performance but the
+ * algorithm is still correct without it.
+ */
+ if ((pkg = (zone_pkg_entry_t *)
+ malloc(sizeof (zone_pkg_entry_t))) == NULL)
+ return (B_FALSE);
+
+ if ((pkg->zpe_name = strdup(pname)) == NULL) {
+ free(pkg);
+ return (B_FALSE);
+ }
+
+ pkg->zpe_vers = NULL;
+ pkg->zpe_patches_avl = NULL;
+
+ /* Insert pkg into the AVL tree. */
+ uu_avl_node_init(pkg, &pkg->zpe_entry, pkgs_pool);
+ uu_avl_insert(saw_pkgs, pkg, where);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static void
+free_pkginfo(struct zone_pkginfo *infop)
+{
+ free(infop->zpi_version);
+ if (infop->zpi_patch_cnt > 0) {
+ int i;
+
+ for (i = 0; i < infop->zpi_patch_cnt; i++)
+ free(infop->zpi_patchinfo[i]);
+ free(infop->zpi_patchinfo);
+ }
+}
+
+/*
+ * Read the pkginfo file and populate the structure with the data we need
+ * from this pkg for a sw inventory.
+ */
+static int
+get_pkginfo(char *pkginfo, struct zone_pkginfo *infop)
+{
+ FILE *fp;
+ char *buf;
+ int err = 0;
+
+ infop->zpi_all_zones = B_FALSE;
+ infop->zpi_this_zone = B_FALSE;
+ infop->zpi_version = NULL;
+ infop->zpi_patch_cnt = 0;
+ infop->zpi_patchinfo = NULL;
+
+ if ((fp = fopen(pkginfo, "r")) == NULL)
+ return (errno);
+
+ while ((buf = read_pkg_data(fp)) != NULL) {
+ if (strncmp(buf, VERSION, sizeof (VERSION) - 1) == 0) {
+ int len;
+
+ if ((infop->zpi_version =
+ strdup(buf + sizeof (VERSION) - 1)) == NULL) {
+ err = ENOMEM;
+ break;
+ }
+
+ /* remove trailing newline */
+ len = strlen(infop->zpi_version);
+ *(infop->zpi_version + len - 1) = 0;
+
+ } else if (strcmp(buf, SUNW_PKG_ALL_ZONES) == 0) {
+ infop->zpi_all_zones = B_TRUE;
+
+ } else if (strcmp(buf, SUNW_PKG_THIS_ZONE) == 0) {
+ infop->zpi_this_zone = B_TRUE;
+
+ } else if (strncmp(buf, PATCHINFO, sizeof (PATCHINFO) - 1)
+ == 0) {
+ char **p;
+
+ if ((p = (char **)realloc(infop->zpi_patchinfo,
+ sizeof (char *) * (infop->zpi_patch_cnt + 1)))
+ == NULL) {
+ err = ENOMEM;
+ break;
+ }
+ infop->zpi_patchinfo = p;
+
+ if ((infop->zpi_patchinfo[infop->zpi_patch_cnt] =
+ strdup(buf)) == NULL) {
+ err = ENOMEM;
+ break;
+ }
+ infop->zpi_patch_cnt++;
+ }
+
+ free(buf);
+ }
+
+ free(buf);
+
+ if (errno == ENOMEM) {
+ err = ENOMEM;
+ /* Clean up anything we did manage to allocate. */
+ free_pkginfo(infop);
+ }
+
+ (void) fclose(fp);
+
+ return (err);
+}
+
+/*
+ * Add any dependent pkgs to the list. The pkg depend file lists pkg
+ * dependencies, one per line with an entry that looks like:
+ * P SUNWcar Core Architecture, (Root)
+ * See the depend(4) man page.
+ */
+static int
+add_dependents(zone_dochandle_t handle, char *pname,
+ uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches,
+ uu_list_pool_t *list_pool, uu_avl_t *saw_pkgs, uu_avl_pool_t *pkgs_pool)
+{
+ int res = Z_OK;
+ FILE *fp;
+ char depend[MAXPATHLEN];
+ char *buf;
+ struct stat sbuf;
+
+ (void) snprintf(depend, sizeof (depend), "%s/%s/install/depend",
+ PKG_PATH, pname);
+
+ if (stat(depend, &sbuf) == -1 || !S_ISREG(sbuf.st_mode))
+ return (Z_OK);
+
+ if ((fp = fopen(depend, "r")) == NULL)
+ return (Z_OK);
+
+ while ((buf = read_pkg_data(fp)) != NULL) {
+ char *deppkg;
+ char *delims = " \t";
+ char pkginfo[MAXPATHLEN];
+ struct zone_pkginfo info;
+
+ if (*buf != 'P') {
+ free(buf);
+ continue;
+ }
+
+ /* Skip past the leading 'P '. */
+ if ((deppkg = strtok(buf + 2, delims)) == NULL) {
+ free(buf);
+ continue;
+ }
+
+ /* If the pkg is already in the manifest don't add it again. */
+ if (pkg_in_manifest(saw_pkgs, deppkg, pkgs_pool)) {
+ free(buf);
+ continue;
+ }
+
+ (void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
+ PKG_PATH, deppkg);
+
+ if (stat(pkginfo, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)) {
+ free(buf);
+ continue;
+ }
+
+ if (get_pkginfo(pkginfo, &info) != 0) {
+ res = Z_NOMEM;
+ free(buf);
+ break;
+ }
+
+ if ((res = add_dependents(handle, deppkg, patches_pool,
+ obs_patches, list_pool, saw_pkgs, pkgs_pool)) == Z_OK &&
+ (res = zonecfg_add_pkg(handle, deppkg, info.zpi_version))
+ == Z_OK) {
+ if (info.zpi_patch_cnt > 0)
+ res = add_patches(handle, &info, patches_pool,
+ obs_patches, list_pool);
+ }
+
+ free(buf);
+ free_pkginfo(&info);
+
+ if (res != Z_OK)
+ break;
+ }
+
+ (void) fclose(fp);
+ return (res);
+}
+
+/* ARGSUSED */
+static int
+pkg_entry_nm_compare(const void *l_arg, const void *r_arg, void *private)
+{
+ zone_pkg_entry_t *pkg = (zone_pkg_entry_t *)l_arg;
+ char *name = (char *)r_arg;
+
+ return (strcmp(pkg->zpe_name, name));
+}
+
+/* ARGSUSED */
+static int
+pkg_entry_compare(const void *l_arg, const void *r_arg, void *private)
+{
+ zone_pkg_entry_t *l = (zone_pkg_entry_t *)l_arg;
+ zone_pkg_entry_t *r = (zone_pkg_entry_t *)r_arg;
+
+ return (strcmp(l->zpe_name, r->zpe_name));
+}
+
+static void
+pkg_avl_delete(uu_avl_t *pavl)
+{
+ if (pavl != NULL) {
+ zone_pkg_entry_t *p;
+ void *cookie = NULL;
+
+ while ((p = uu_avl_teardown(pavl, &cookie)) != NULL) {
+ free(p->zpe_name);
+ free(p);
+ }
+
+ uu_avl_destroy(pavl);
+ }
+}
+
+/*
+ * Take a software inventory of the global zone. We need to get the set of
+ * packages and patches that are on the global zone that the specified
+ * non-global zone depends on. The packages we need in the inventory are:
+ *
+ * - skip the package if SUNW_PKG_THISZONE is 'true'
+ * otherwise,
+ * - add the package if
+ * a) SUNW_PKG_ALLZONES is 'true',
+ * or
+ * b) any file delivered by the package is in a file system that is inherited
+ * from the global zone.
+ * If the zone does not inherit any file systems (whole root)
+ * then (b) will be skipped.
+ *
+ * For each of the packages that is being added to the inventory, we will also
+ * add its dependent packages to the inventory.
+ *
+ * For each of the packages that is being added to the inventory, we will also
+ * add all of the associated, unique patches to the inventory.
+ *
+ * See the comment for zonecfg_getpkgdata() for compatability restrictions on
+ * how we must save the XML representation of the software inventory.
+ */
+static int
+sw_inventory(zone_dochandle_t handle)
+{
+ char pkginfo[MAXPATHLEN];
+ int res;
+ struct dirent *dp;
+ DIR *dirp;
+ struct stat buf;
+ struct zone_pkginfo info;
+ int pkg_cnt = 0;
+ char **pkgs = NULL;
+ uu_avl_pool_t *pkgs_pool = NULL;
+ uu_avl_pool_t *patches_pool = NULL;
+ uu_list_pool_t *list_pool = NULL;
+ uu_avl_t *saw_pkgs = NULL;
+ uu_avl_t *obs_patches = NULL;
+
+ if ((pkgs_pool = uu_avl_pool_create("pkgs_pool",
+ sizeof (zone_pkg_entry_t), offsetof(zone_pkg_entry_t, zpe_entry),
+ pkg_entry_nm_compare, UU_DEFAULT)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if ((saw_pkgs = uu_avl_create(pkgs_pool, NULL, UU_DEFAULT)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if ((patches_pool = uu_avl_pool_create("patches_pool",
+ sizeof (patch_node_t), offsetof(patch_node_t, patch_node),
+ patch_node_compare, UU_DEFAULT)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if ((list_pool = uu_list_pool_create("list_pool",
+ sizeof (obs_patch_node_t), offsetof(obs_patch_node_t, link), NULL,
+ UU_DEFAULT)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ /*
+ * The obs_patches AVL tree saves all of the obsolete patches so
+ * that we know not to output those as part of the sw dependencies.
+ */
+ if ((obs_patches = uu_avl_create(patches_pool, NULL, UU_DEFAULT))
+ == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if ((res = get_ipd_pkgs(handle, &pkgs, &pkg_cnt)) != Z_OK) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if ((dirp = opendir(PKG_PATH)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ while ((dp = readdir(dirp)) != (struct dirent *)0) {
+ if (strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0)
+ continue;
+
+ (void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
+ PKG_PATH, dp->d_name);
+
+ if (stat(pkginfo, &buf) == -1 || !S_ISREG(buf.st_mode))
+ continue;
+
+ if (get_pkginfo(pkginfo, &info) != 0) {
+ res = Z_NOMEM;
+ break;
+ }
+
+ if (!info.zpi_this_zone &&
+ (info.zpi_all_zones ||
+ dir_pkg(dp->d_name, pkgs, pkg_cnt)) &&
+ !pkg_in_manifest(saw_pkgs, dp->d_name, pkgs_pool)) {
+ /*
+ * Add dependents first so any patches will get
+ * associated with the right pkg in the xml file.
+ */
+ if ((res = add_dependents(handle, dp->d_name,
+ patches_pool, obs_patches, list_pool, saw_pkgs,
+ pkgs_pool)) == Z_OK &&
+ (res = zonecfg_add_pkg(handle, dp->d_name,
+ info.zpi_version)) == Z_OK) {
+ if (info.zpi_patch_cnt > 0)
+ res = add_patches(handle, &info,
+ patches_pool, obs_patches,
+ list_pool);
+ }
+ }
+
+ free_pkginfo(&info);
+
+ if (res != Z_OK)
+ break;
+ }
+
+ (void) closedir(dirp);
+
+done:
+ pkg_avl_delete(saw_pkgs);
+ patch_avl_delete(obs_patches);
+ if (pkgs_pool != NULL)
+ uu_avl_pool_destroy(pkgs_pool);
+ if (patches_pool != NULL)
+ uu_avl_pool_destroy(patches_pool);
+ if (list_pool != NULL)
+ uu_list_pool_destroy(list_pool);
+ free_ipd_pkgs(pkgs, pkg_cnt);
+
+ if (res == Z_OK)
+ zonecfg_set_swinv(handle);
+
+ return (res);
+}
+
+/*
+ * Get the information required to support detaching a zone. This is
+ * called on the source system when detaching (the detaching parameter should
+ * be set to true) and on the destination system before attaching (the
+ * detaching parameter should be false).
+ *
+ * For native Solaris zones, the detach/attach process involves validating
+ * that the software on the global zone can support the zone when we attach.
+ * To do this we take a software inventory of the global zone. We also
+ * have to keep track of the device configuration so that we can properly
+ * recreate it on the destination.
+ */
+static int
+get_detach_info(zone_dochandle_t handle, boolean_t detaching)
+{
+ int res;
+
+ if ((res = sw_inventory(handle)) != Z_OK)
+ return (res);
+
+ if (detaching)
+ res = zonecfg_dev_manifest(handle);
+
+ return (res);
+}
+
+/* ARGSUSED */
+static int
+zfm_print(const char *p, void *r) {
+ (void) fprintf(stderr, " %s\n", p);
+ return (0);
+}
+
+static int
+detach_func(int argc, char *argv[])
+{
+ int err, arg;
+ zone_dochandle_t handle;
+ boolean_t execute = B_TRUE;
+
+ opterr = 0;
+ optind = 0;
+ if ((arg = getopt(argc, argv, "?n")) != EOF) {
+ switch (arg) {
+ case '?':
+ if (optopt != '?') {
+ (void) fprintf(stderr, gettext("%s brand: "
+ "invalid option: %c\n"), MY_BRAND_NAME,
+ optopt);
+ }
+ (void) fprintf(stderr, gettext("usage:\t%s brand "
+ "options: none\n"), MY_BRAND_NAME);
+ return (optopt == '?' ? Z_OK : ZONE_SUBPROC_USAGE);
+ case 'n':
+ execute = B_FALSE;
+ break;
+ default:
+ (void) fprintf(stderr, gettext("%s brand: invalid "
+ "option: %c\n"), MY_BRAND_NAME, arg);
+ return (ZONE_SUBPROC_USAGE);
+ }
+ }
+
+ /* Don't detach the zone if anything is still mounted there */
+ if (execute && zonecfg_find_mounts(zonepath, NULL, NULL)) {
+ (void) fprintf(stderr, gettext("These file systems are "
+ "mounted on subdirectories of %s.\n"), zonepath);
+ (void) zonecfg_find_mounts(zonepath, zfm_print, NULL);
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand detach program error: "
+ "%s\n"), strerror(errno));
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((err = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand detach program error: "
+ "%s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((err = get_detach_info(handle, B_TRUE)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand detach program error: "
+ "%s\n"), zonecfg_strerror(err));
+ goto done;
+ }
+
+ if ((err = zonecfg_detach_save(handle, (execute ? 0 : ZONE_DRY_RUN)))
+ != Z_OK) {
+ (void) fprintf(stderr, gettext("saving the detach manifest "
+ "failed: %s\n"), zonecfg_strerror(err));
+ goto done;
+ }
+
+done:
+ zonecfg_fini_handle(handle);
+
+ return ((err == Z_OK) ? ZONE_SUBPROC_OK : ZONE_SUBPROC_FATAL);
+}
+
+/*
+ * Validate attaching a zone but don't actually do the work. The zone
+ * does not have to exist, so there is some complexity getting a new zone
+ * configuration set up so that we can perform the validation. This is
+ * handled within zonecfg_attach_manifest() which returns two handles; one
+ * for the the full configuration to validate (rem_handle) and the other
+ * (local_handle) containing only the zone configuration derived from the
+ * manifest.
+ */
+static int
+dryrun_attach(char *manifest_path)
+{
+ int fd;
+ int err;
+ int res;
+ char atbrand[MAXNAMELEN];
+ zone_dochandle_t local_handle;
+ zone_dochandle_t rem_handle = NULL;
+
+ if ((fd = open(manifest_path, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, gettext("could not open manifest path: "
+ "%s\n"), strerror(errno));
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((local_handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand attach program error: "
+ "%s\n"), strerror(errno));
+ res = ZONE_SUBPROC_NOTCOMPLETE;
+ goto done;
+ }
+
+ if ((rem_handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand attach program error: "
+ "%s\n"), strerror(errno));
+ res = ZONE_SUBPROC_NOTCOMPLETE;
+ goto done;
+ }
+
+ if ((err = zonecfg_attach_manifest(fd, local_handle, rem_handle))
+ != Z_OK) {
+ res = ZONE_SUBPROC_NOTCOMPLETE;
+
+ if (err == Z_INVALID_DOCUMENT) {
+ char buf[6];
+
+ bzero(buf, sizeof (buf));
+ (void) lseek(fd, 0L, SEEK_SET);
+ if (read(fd, buf, sizeof (buf) - 1) < 0 ||
+ strncmp(buf, "<?xml", 5) != 0)
+ (void) fprintf(stderr, gettext("%s is not an "
+ "XML file\n"), manifest_path);
+ else
+ (void) fprintf(stderr, gettext("Cannot attach "
+ "to an earlier release of the operating "
+ "system\n"));
+ } else {
+ (void) fprintf(stderr, gettext("brand attach program "
+ "error: %s\n"), zonecfg_strerror(err));
+ }
+ goto done;
+ }
+
+ /*
+ * Retrieve remote handle brand type and determine whether it is
+ * native or not.
+ */
+ if (zonecfg_get_brand(rem_handle, atbrand, sizeof (atbrand)) != Z_OK) {
+ (void) fprintf(stderr, gettext("missing or invalid brand\n"));
+ exit(ZONE_SUBPROC_FATAL);
+ }
+
+ if (strcmp(atbrand, MY_BRAND_NAME) != 0) {
+ err = Z_ERR;
+ (void) fprintf(stderr, gettext("Trying to attach a '%s' zone "
+ "to a '%s' configuration.\n"), atbrand, MY_BRAND_NAME);
+ exit(ZONE_SUBPROC_FATAL);
+ }
+
+ /* Get the detach information for the locally defined zone. */
+ res = Z_OK;
+ if ((err = get_detach_info(local_handle, B_FALSE)) != Z_OK) {
+ (void) fprintf(stderr, gettext("getting the attach information "
+ "failed: %s\n"), zonecfg_strerror(err));
+ res = ZONE_SUBPROC_FATAL;
+ } else {
+ /* sw_cmp prints error msgs as necessary */
+ if (sw_cmp(local_handle, rem_handle, SW_CMP_NONE) != Z_OK)
+ res = ZONE_SUBPROC_FATAL;
+ }
+
+done:
+ (void) close(fd);
+
+ zonecfg_fini_handle(local_handle);
+ zonecfg_fini_handle(rem_handle);
+
+ return ((res == Z_OK) ? Z_OK : ZONE_SUBPROC_FATAL);
+}
+
+static int
+mount_func(boolean_t force)
+{
+ zone_cmd_arg_t zarg;
+
+ zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT;
+ zarg.bootbuf[0] = '\0';
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ zerror(gettext("call to %s failed"), "zoneadmd");
+ return (Z_ERR);
+ }
+ return (Z_OK);
+}
+
+static int
+unmount_func()
+{
+ zone_cmd_arg_t zarg;
+
+ zarg.cmd = Z_UNMOUNT;
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ zerror(gettext("call to %s failed"), "zoneadmd");
+ return (Z_ERR);
+ }
+ return (Z_OK);
+}
+
+/*
+ * Attempt to generate the information we need to make the zone look like
+ * it was properly detached by using the pkg information contained within
+ * the zone itself.
+ *
+ * We will perform a dry-run detach within the zone to generate the xml file.
+ * To do this we need to be able to get a handle on the zone so we can see
+ * how it is configured. In order to get a handle, we need a copy of the
+ * zone configuration within the zone. Since the zone's configuration is
+ * not available within the zone itself, we need to temporarily copy it into
+ * the zone.
+ *
+ * The sequence of actions we are doing is this:
+ * [set zone state to installed]
+ * [mount zone]
+ * zlogin {zone} </etc/zones/{zone}.xml 'cat >/etc/zones/{zone}.xml'
+ * zlogin {zone} 'zoneadm -z {zone} detach -n' >{zonepath}/SUNWdetached.xml
+ * zlogin {zone} 'rm -f /etc/zones/{zone}.xml'
+ * [unmount zone]
+ * [set zone state to configured]
+ *
+ * The successful result of this function is that we will have a
+ * SUNWdetached.xml file in the zonepath and we can use that to attach the zone.
+ */
+static boolean_t
+gen_detach_info()
+{
+ int status;
+ boolean_t mounted = B_FALSE;
+ boolean_t res = B_FALSE;
+ char cmdbuf[2 * MAXPATHLEN];
+
+ /*
+ * The zone has to be installed to mount and zlogin. Temporarily set
+ * the state to 'installed'.
+ */
+ if (zone_set_state(zonename, ZONE_STATE_INSTALLED) != Z_OK)
+ return (B_FALSE);
+
+ /* Mount the zone so we can zlogin. */
+ if (mount_func(B_FALSE) != Z_OK)
+ goto cleanup;
+ mounted = B_TRUE;
+
+ /*
+ * We need to copy the zones xml configuration file into the
+ * zone so we can get a handle for the zone while running inside
+ * the zone.
+ */
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/sbin/zlogin -S %s "
+ "</etc/zones/%s.xml '/usr/bin/cat >/etc/zones/%s.xml'",
+ zonename, zonename, zonename) >= sizeof (cmdbuf))
+ goto cleanup;
+
+ status = do_subproc(cmdbuf);
+ if (subproc_status("copy", status, B_TRUE) != ZONE_SUBPROC_OK)
+ goto cleanup;
+
+ /* Now run the detach command within the mounted zone. */
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/sbin/zlogin -S %s "
+ "'/usr/sbin/zoneadm -z %s detach -n' >%s/SUNWdetached.xml",
+ zonename, zonename, zonepath) >= sizeof (cmdbuf))
+ goto cleanup;
+
+ status = do_subproc(cmdbuf);
+ if (subproc_status("detach", status, B_TRUE) != ZONE_SUBPROC_OK)
+ goto cleanup;
+
+ res = B_TRUE;
+
+cleanup:
+ /* Cleanup from the previous actions. */
+ if (mounted) {
+ if (snprintf(cmdbuf, sizeof (cmdbuf),
+ "/usr/sbin/zlogin -S %s '/usr/bin/rm -f /etc/zones/%s.xml'",
+ zonename, zonename) >= sizeof (cmdbuf)) {
+ res = B_FALSE;
+ } else {
+ status = do_subproc(cmdbuf);
+ if (subproc_status("rm", status, B_TRUE)
+ != ZONE_SUBPROC_OK)
+ res = B_FALSE;
+ }
+
+ if (unmount_func() != Z_OK)
+ res = B_FALSE;
+ }
+
+ if (zone_set_state(zonename, ZONE_STATE_CONFIGURED) != Z_OK)
+ res = B_FALSE;
+
+ return (res);
+}
+
+/*
+ * The zone needs to be updated so set it up for the update and initiate the
+ * update within the scratch zone. First set the state to incomplete so we can
+ * force-mount the zone for the update operation. We pass the -U option to the
+ * mount so that the scratch zone is mounted without the zone's /etc and /var
+ * being lofs mounted back into the scratch zone root. This is done by
+ * overloading the bootbuf string in the zone_cmd_arg_t to pass -U as an option
+ * to the mount cmd.
+ */
+static int
+attach_update(zone_dochandle_t handle)
+{
+ int err;
+ int update_res;
+ int status;
+ zone_cmd_arg_t zarg;
+ FILE *fp;
+ struct zone_fstab fstab;
+ char cmdbuf[(4 * MAXPATHLEN) + 20];
+
+ if ((err = zone_set_state(zonename, ZONE_STATE_INCOMPLETE))
+ != Z_OK) {
+ (void) fprintf(stderr, gettext("could not set state: %s\n"),
+ zonecfg_strerror(err));
+ return (Z_FATAL);
+ }
+
+ zarg.cmd = Z_FORCEMOUNT;
+ (void) strlcpy(zarg.bootbuf, "-U", sizeof (zarg.bootbuf));
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ (void) fprintf(stderr, gettext("could not mount zone\n"));
+
+ /* We reset the state since the zone wasn't modified yet. */
+ if ((err = zone_set_state(zonename, ZONE_STATE_CONFIGURED))
+ != Z_OK) {
+ (void) fprintf(stderr, gettext("could not reset state: "
+ "%s\n"), zonecfg_strerror(err));
+ }
+ return (Z_FATAL);
+ }
+
+ /*
+ * Move data files generated by sw_up_to_date() into the scratch
+ * zone's /tmp.
+ */
+ (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec /usr/bin/mv "
+ "%s/pkg_add %s/pkg_rm %s/lu/tmp",
+ zonepath, zonepath, zonepath);
+
+ status = do_subproc(cmdbuf);
+ if (subproc_status("mv", status, B_TRUE) != ZONE_SUBPROC_OK) {
+ (void) fprintf(stderr, gettext("could not mv data files: %s\n"),
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Save list of inherit-pkg-dirs into zone. Since the file is in
+ * /tmp we don't have to worry about deleting it.
+ */
+ (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/lu/tmp/inherited",
+ zonepath);
+ if ((fp = fopen(cmdbuf, "w")) == NULL) {
+ (void) fprintf(stderr, gettext("could not save "
+ "inherit-pkg-dirs: %s\n"), strerror(errno));
+ goto fail;
+ }
+ if (zonecfg_setipdent(handle) != Z_OK) {
+ (void) fprintf(stderr, gettext("could not enumerate "
+ "inherit-pkg-dirs: %s\n"), zonecfg_strerror(err));
+ goto fail;
+ }
+ while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
+ if (fprintf(fp, "%s\n", fstab.zone_fs_dir) < 0) {
+ (void) fprintf(stderr, gettext("could not save "
+ "inherit-pkg-dirs: %s\n"), strerror(errno));
+ (void) fclose(fp);
+ goto fail;
+ }
+ }
+ (void) zonecfg_endipdent(handle);
+ if (fclose(fp) != 0) {
+ (void) fprintf(stderr, gettext("could not save "
+ "inherit-pkg-dirs: %s\n"), strerror(errno));
+ goto fail;
+ }
+
+ /* run the updater inside the scratch zone */
+ (void) snprintf(cmdbuf, sizeof (cmdbuf),
+ "exec /usr/sbin/zlogin -S %s "
+ "/usr/lib/brand/native/attach_update %s", zonename, zonename);
+
+ update_res = Z_OK;
+ status = do_subproc(cmdbuf);
+ if (subproc_status("attach_update", status, B_TRUE)
+ != ZONE_SUBPROC_OK) {
+ (void) fprintf(stderr, gettext("could not update zone\n"));
+ update_res = Z_ERR;
+ }
+
+ zarg.cmd = Z_UNMOUNT;
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ (void) fprintf(stderr, gettext("could not unmount zone\n"));
+ return (Z_ERR);
+ }
+
+ /*
+ * If the update script within the scratch zone failed for some reason
+ * we will now leave the zone in the incomplete state since we no
+ * longer know the state of the files within the zonepath.
+ */
+ if (update_res == Z_ERR)
+ return (Z_ERR);
+
+ zonecfg_rm_detached(handle, B_FALSE);
+
+ if ((err = zone_set_state(zonename, ZONE_STATE_INSTALLED)) != Z_OK) {
+ errno = err;
+ (void) fprintf(stderr, gettext("could not set state: %s\n"),
+ zonecfg_strerror(err));
+ return (Z_ERR);
+ }
+
+ return (Z_OK);
+
+fail:
+ zarg.cmd = Z_UNMOUNT;
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0)
+ (void) fprintf(stderr, gettext("could not unmount zone\n"));
+
+ /* We reset the state since the zone wasn't modified yet. */
+ if ((err = zone_set_state(zonename, ZONE_STATE_CONFIGURED))
+ != Z_OK) {
+ errno = err;
+ (void) fprintf(stderr, gettext("could not reset state: %s\n"),
+ zonecfg_strerror(err));
+ }
+
+ return (Z_ERR);
+}
+
+/* ARGSUSED */
+static void
+sigcleanup(int sig)
+{
+ attach_interupted = B_TRUE;
+}
+
+static boolean_t
+valid_num(char *n)
+{
+ for (; isdigit(*n); n++)
+ ;
+
+ if (*n != NULL)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+/*
+ * Take an input field, which must look like a positive int, and return the
+ * numeric value of the field. Return -1 if the input field does not look
+ * like something we can convert.
+ */
+static int
+fld2num(char *fld, char **nfld)
+{
+ char *ppoint;
+ long n;
+
+ if ((ppoint = strchr(fld, '.')) != NULL) {
+ *ppoint = '\0';
+ *nfld = ppoint + 1;
+ } else {
+ *nfld = NULL;
+ }
+
+ if (!valid_num(fld))
+ return (-1);
+
+ errno = 0;
+ n = strtol(fld, (char **)NULL, 10);
+ if (errno != 0)
+ return (-1);
+
+ return ((int)n);
+}
+
+/*
+ * Step through two version strings that look like postive ints delimited by
+ * decimals and compare them. Example input can look like 2, 010.3, 75.02.09,
+ * etc. If the input does not look like this then we do a simple lexical
+ * comparison of the two strings. The string can be modified on exit of
+ * this function.
+ */
+static int
+fld_cmp(char *v1, char *v2)
+{
+ char *nxtfld1, *nxtfld2;
+ int n1, n2;
+
+ for (;;) {
+ n1 = fld2num(v1, &nxtfld1);
+ n2 = fld2num(v2, &nxtfld2);
+
+ /*
+ * If either field is not a postive int, just compare them
+ * lexically.
+ */
+ if (n1 < 0 || n2 < 0)
+ return (strcmp(v1, v2));
+
+ if (n1 > n2)
+ return (1);
+
+ if (n1 < n2)
+ return (-1);
+
+ /* They're equal */
+
+ /* No more fields */
+ if (nxtfld1 == NULL && nxtfld2 == NULL)
+ return (0);
+
+ /* Field 2 still has data so it is greater than field 1 */
+ if (nxtfld1 == NULL)
+ return (-1);
+
+ /* Field 1 still has data so it is greater than field 2 */
+ if (nxtfld2 == NULL)
+ return (1);
+
+ /* Both fields still have data, keep going. */
+ v1 = nxtfld1;
+ v2 = nxtfld2;
+ }
+}
+
+/*
+ * The result of the comparison is returned in the cmp parameter:
+ * 0 if both versions are equal.
+ * <0 if version1 is less than version 2.
+ * >0 if version1 is greater than version 2.
+ * The function returns B_TRUE if there was an ENOMEM error, B_FALSE otherwise.
+ *
+ * This function handles the various version strings we can get from the
+ * dependent pkg versions. They usually look like:
+ * "1.21,REV=2005.01.17.23.31"
+ * "2.6.0,REV=10.0.3.2004.12.16.18.02"
+ *
+ * We can't do a simple lexical comparison since:
+ * 2.6.0 would be greater than 2.20.0
+ * 12 would be greater than 110
+ *
+ * If the input strings do not look like decimal delimted version strings
+ * then we fall back to doing a simple lexical comparison.
+ */
+static boolean_t
+pkg_vers_cmp(char *vers1, char *vers2, int *cmp)
+{
+ char *v1, *v2;
+ char *rev1, *rev2;
+ int res;
+
+ /* We need to modify the input strings so we dup them. */
+ if ((v1 = strdup(vers1)) == NULL)
+ return (B_TRUE);
+ if ((v2 = strdup(vers2)) == NULL) {
+ free(v1);
+ return (B_TRUE);
+ }
+
+ /* Strip off a revision delimited by a comma. */
+ if ((rev1 = strchr(v1, ',')) != NULL)
+ *rev1++ = '\0';
+ if ((rev2 = strchr(v2, ',')) != NULL)
+ *rev2++ = '\0';
+
+ res = fld_cmp(v1, v2);
+ /* If the primary versions are not equal, return the result */
+ if (res != 0) {
+ *cmp = res;
+ goto done;
+ }
+
+ /*
+ * All of the fields in the primary version strings are equal, check
+ * the rev, if it exists.
+ */
+
+ /* No revs */
+ if (rev1 == NULL && rev2 == NULL) {
+ *cmp = 0;
+ goto done;
+ }
+
+ /* Field 2 has a rev so it is greater than field 1 */
+ if (rev1 == NULL) {
+ *cmp = -1;
+ goto done;
+ }
+
+ /* Field 1 has a rev so it is greater than field 2 */
+ if (rev2 == NULL) {
+ *cmp = 1;
+ goto done;
+ }
+
+ /* If no recognized REV data then just lexically compare them */
+ if (strncmp(rev1, "REV=", 4) != 0 || strncmp(rev2, "REV=", 4) != 0) {
+ *cmp = strcmp(rev1, rev2);
+ goto done;
+ }
+
+ /* Both fields have revs, check them. */
+ *cmp = fld_cmp(rev1 + 4, rev2 + 4);
+
+done:
+ free(v1);
+ free(v2);
+
+ return (B_FALSE);
+}
+
+/*
+ * Walk all of the patches on the pkg, looking to see if the specified patch
+ * has been obsoleted by one of those patches.
+ */
+static boolean_t
+is_obsolete(zone_pkg_entry_t *pkg, zone_pkg_entry_t *patchid)
+{
+ uu_avl_walk_t *patch_walk;
+ zone_pkg_entry_t *patch;
+ boolean_t res;
+
+ if (pkg->zpe_patches_avl == NULL)
+ return (B_FALSE);
+
+ patch_walk = uu_avl_walk_start(pkg->zpe_patches_avl, UU_WALK_ROBUST);
+ if (patch_walk == NULL)
+ return (B_FALSE);
+
+ res = B_FALSE;
+ while ((patch = uu_avl_walk_next(patch_walk)) != NULL) {
+ uu_avl_index_t where;
+
+ if (patch->zpe_patches_avl == NULL)
+ continue;
+
+ /* Check the obsolete list on the patch. */
+ if (uu_avl_find(patch->zpe_patches_avl, patchid, NULL, &where)
+ != NULL) {
+ res = B_TRUE;
+ break;
+ }
+ }
+
+ uu_avl_walk_end(patch_walk);
+ return (res);
+}
+
+/*
+ * Build a list of unique patches from the input pkg_patches list.
+ * If the pkg parameter is not null then we will check the patches on that
+ * pkg to see if any of the pkg_patches have been obsoleted. We don't
+ * add those obsoleted patches to the unique list.
+ * Returns B_FALSE if an error occurs.
+ */
+static boolean_t
+add_patch(uu_avl_t *pkg_patches, uu_avl_t *unique, zone_pkg_entry_t *pkg,
+ uu_avl_pool_t *pkg_pool)
+{
+ uu_avl_walk_t *walk;
+ zone_pkg_entry_t *pkg_patch;
+
+ if (pkg_patches == NULL)
+ return (B_TRUE);
+
+ walk = uu_avl_walk_start(pkg_patches, UU_WALK_ROBUST);
+ if (walk == NULL)
+ return (B_FALSE);
+
+ while ((pkg_patch = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+ zone_pkg_entry_t *patch;
+
+ /* Skip adding it if we already have it. */
+ if (uu_avl_find(unique, pkg_patch, NULL, &where) != NULL)
+ continue;
+
+ /* Likewise, skip adding it if it has been obsoleted. */
+ if (pkg != NULL && is_obsolete(pkg, pkg_patch))
+ continue;
+
+ /* We need to add it so make a duplicate. */
+ if ((patch = (zone_pkg_entry_t *)
+ malloc(sizeof (zone_pkg_entry_t))) == NULL) {
+ uu_avl_walk_end(walk);
+ return (B_FALSE);
+ }
+
+ if ((patch->zpe_name = strdup(pkg_patch->zpe_name)) == NULL) {
+ free(patch);
+ uu_avl_walk_end(walk);
+ return (B_FALSE);
+ }
+ if ((patch->zpe_vers = strdup(pkg_patch->zpe_vers)) == NULL) {
+ free(patch->zpe_name);
+ free(patch);
+ uu_avl_walk_end(walk);
+ return (B_FALSE);
+ }
+ patch->zpe_patches_avl = NULL;
+
+ /* Insert patch into the unique patch AVL tree. */
+ uu_avl_node_init(patch, &patch->zpe_entry, pkg_pool);
+ uu_avl_insert(unique, patch, where);
+ }
+ uu_avl_walk_end(walk);
+
+ return (B_TRUE);
+}
+
+/*
+ * Common code for sw_cmp which will check flags, update res and print the
+ * section header. Return true if we should be silent.
+ */
+static boolean_t
+prt_header(int *res, uint_t flag, boolean_t *do_header, char *hdr)
+{
+ *res = Z_ERR;
+ if (flag & SW_CMP_SILENT)
+ return (B_TRUE);
+
+ if (*do_header) {
+ /* LINTED E_SEC_PRINTF_VAR_FMT */
+ (void) fprintf(stderr, hdr);
+ *do_header = B_FALSE;
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Compare the software on the local global zone and source system global
+ * zone. Used when we are trying to attach a zone during migration or
+ * when checking if a ZFS snapshot is still usable for a ZFS clone.
+ * l_handle is for the local system and s_handle is for the source system.
+ * These have a snapshot of the appropriate packages and patches in the global
+ * zone for the two machines.
+ * The functions called here can print any messages that are needed to
+ * inform the user about package or patch problems.
+ * The flag parameter controls how the messages are printed. If the
+ * SW_CMP_SILENT bit is set in the flag then no messages will be printed
+ * but we still compare the sw and return an error if there is a mismatch.
+ */
+static int
+sw_cmp(zone_dochandle_t l_handle, zone_dochandle_t s_handle, uint_t flag)
+{
+ char *hdr;
+ int res;
+ int err;
+ boolean_t do_header;
+ uu_avl_pool_t *pkg_pool = NULL;
+ uu_avl_t *src_pkgs = NULL;
+ uu_avl_t *dst_pkgs = NULL;
+ uu_avl_t *src_patches = NULL;
+ uu_avl_t *dst_patches = NULL;
+ zone_pkg_entry_t *src_pkg;
+ zone_pkg_entry_t *dst_pkg;
+ zone_pkg_entry_t *src_patch;
+ zone_pkg_entry_t *dst_patch;
+ uu_avl_walk_t *walk;
+
+ /* Set res to cover any of these memory allocation errors. */
+ res = Z_NOMEM;
+ if ((pkg_pool = uu_avl_pool_create("pkgs_pool",
+ sizeof (zone_pkg_entry_t), offsetof(zone_pkg_entry_t, zpe_entry),
+ pkg_entry_compare, UU_DEFAULT)) == NULL)
+ goto done;
+
+ if ((src_pkgs = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto done;
+
+ if ((dst_pkgs = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto done;
+
+ if ((src_patches = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto done;
+
+ if ((dst_patches = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto done;
+
+ res = Z_OK;
+ if ((err = zonecfg_getpkgdata(s_handle, pkg_pool, src_pkgs)) != Z_OK) {
+ res = errno = err;
+ zperror(gettext("could not get package data for detached zone"),
+ B_TRUE);
+ goto done;
+ }
+ if ((err = zonecfg_getpkgdata(l_handle, pkg_pool, dst_pkgs)) != Z_OK) {
+ res = errno = err;
+ zperror(gettext("could not get package data for global zone"),
+ B_TRUE);
+ goto done;
+ }
+
+ /*
+ * Check the source host for pkgs (and versions) that are not on the
+ * local host.
+ */
+ hdr = gettext("These packages installed on the source system "
+ "are inconsistent with this system:\n");
+ do_header = B_TRUE;
+
+ if ((walk = uu_avl_walk_start(src_pkgs, UU_WALK_ROBUST)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+ while ((src_pkg = uu_avl_walk_next(walk)) != NULL) {
+ int cmp;
+ uu_avl_index_t where;
+
+ dst_pkg = uu_avl_find(dst_pkgs, src_pkg, NULL, &where);
+
+ /*
+ * Build up a list of unique patches for the src system but
+ * don't track patches that are obsoleted on the dst system
+ * since they don't matter.
+ */
+ if (!add_patch(src_pkg->zpe_patches_avl, src_patches, dst_pkg,
+ pkg_pool)) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if (dst_pkg == NULL) {
+ /* src pkg is not installed on dst */
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr,
+ gettext("\t%s: not installed\n\t\t(%s)\n"),
+ src_pkg->zpe_name, src_pkg->zpe_vers);
+ continue;
+ }
+
+ /* Check pkg version */
+ if (pkg_vers_cmp(src_pkg->zpe_vers, dst_pkg->zpe_vers, &cmp)) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ if (cmp != 0) {
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr, gettext(
+ "\t%s: version mismatch\n\t\t(%s)\n\t\t(%s)\n"),
+ src_pkg->zpe_name, src_pkg->zpe_vers,
+ dst_pkg->zpe_vers);
+ }
+ }
+ uu_avl_walk_end(walk);
+
+ /*
+ * Now check the local host for pkgs that were not on the source host.
+ * We already handled version mismatches in the loop above.
+ */
+ hdr = gettext("These packages installed on this system were "
+ "not installed on the source system:\n");
+ do_header = B_TRUE;
+
+ if ((walk = uu_avl_walk_start(dst_pkgs, UU_WALK_ROBUST)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+ while ((dst_pkg = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+
+ /*
+ * Build up a list of unique patches for the dst system. We
+ * don't worry about tracking obsolete patches that were on the
+ * src since we only want to report the results of moving to
+ * the dst system.
+ */
+ if (!add_patch(dst_pkg->zpe_patches_avl, dst_patches, NULL,
+ pkg_pool)) {
+ res = Z_NOMEM;
+ goto done;
+ }
+
+ src_pkg = uu_avl_find(src_pkgs, dst_pkg, NULL, &where);
+ if (src_pkg == NULL) {
+ /* dst pkg is not installed on src */
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr, gettext("\t%s (%s)\n"),
+ dst_pkg->zpe_name, dst_pkg->zpe_vers);
+ }
+ }
+ uu_avl_walk_end(walk);
+
+ /*
+ * Check the source host for patches that are not on the local host.
+ */
+ hdr = gettext("These patches installed on the source system "
+ "are inconsistent with this system:\n");
+ do_header = B_TRUE;
+
+ if ((walk = uu_avl_walk_start(src_patches, UU_WALK_ROBUST)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+ while ((src_patch = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+
+ dst_patch = uu_avl_find(dst_patches, src_patch, NULL, &where);
+ if (dst_patch == NULL) {
+ /* src patch is not installed on dst */
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr,
+ gettext("\t%s-%s: not installed\n"),
+ src_patch->zpe_name, src_patch->zpe_vers);
+ continue;
+ }
+
+ /*
+ * Check patch version. We assume the patch versions are
+ * properly structured with a leading 0 if necessary (e.g. 01).
+ */
+ assert(strlen(src_patch->zpe_vers) ==
+ strlen(dst_patch->zpe_vers));
+ if (strcmp(src_patch->zpe_vers, dst_patch->zpe_vers) != 0) {
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr,
+ gettext("\t%s: version mismatch\n\t\t(%s) (%s)\n"),
+ src_patch->zpe_name, src_patch->zpe_vers,
+ dst_patch->zpe_vers);
+ }
+ }
+ uu_avl_walk_end(walk);
+
+ /*
+ * Check the local host for patches that were not on the source host.
+ * We already handled version mismatches in the loop above.
+ */
+ hdr = gettext("These patches installed on this system were "
+ "not installed on the source system:\n");
+ do_header = B_TRUE;
+
+ if ((walk = uu_avl_walk_start(dst_patches, UU_WALK_ROBUST)) == NULL) {
+ res = Z_NOMEM;
+ goto done;
+ }
+ while ((dst_patch = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+
+ src_patch = uu_avl_find(src_patches, dst_patch, NULL, &where);
+ if (src_patch == NULL) {
+ /* dst patch is not installed on src */
+ if (prt_header(&res, flag, &do_header, hdr))
+ break;
+
+ (void) fprintf(stderr, gettext("\t%s-%s\n"),
+ dst_patch->zpe_name, dst_patch->zpe_vers);
+ }
+ }
+ uu_avl_walk_end(walk);
+
+done:
+ if (res == Z_NOMEM)
+ zerror(gettext("Out of memory"));
+
+ /* free avl structs */
+ pkg_avl_delete(src_pkgs);
+ pkg_avl_delete(dst_pkgs);
+ pkg_avl_delete(src_patches);
+ pkg_avl_delete(dst_patches);
+ if (pkg_pool != NULL)
+ uu_avl_pool_destroy(pkg_pool);
+
+ return (res);
+}
+
+/*
+ * Compare the software on the local global zone and source system global
+ * zone. Used to determine if/how we have to update the zone during attach.
+ * We generate the data files needed by the update process in this case.
+ * l_handle is for the local system and s_handle is for the source system.
+ * These have a snapshot of the appropriate packages and patches in the global
+ * zone for the two machines.
+ *
+ * The algorithm we use to compare the pkgs is as follows:
+ * 1) pkg on src but not on dst
+ * remove src pkg (allowed in order to handle obsolete pkgs - note that
+ * this only applies to dependent pkgs, not generic pkgs installed into
+ * the zone by the zone admin)
+ * 2) pkg on dst but not on src
+ * add pkg
+ * 3) pkg on src with higher rev than on dst
+ * fail (downgrade)
+ * 4) pkg on dst with higher rev than on src
+ * remove src pkg & add new
+ * 5) pkg version is the same
+ * a) patch on src but not on dst
+ * fail (downgrade, unless obsoleted)
+ * b) patch on dst but not on src
+ * remove src pkg & add new
+ * c) patch on src with higher rev than on dst
+ * fail (downgrade, unless obsoleted)
+ * d) patch on dst with higher rev than on src
+ * remove src pkg & add new
+ *
+ * We run this algorithm in 2 passes, first looking at the pkgs from the src
+ * system and then looking at the pkgs from the dst system.
+ *
+ * As with the sw_cmp function, we return Z_OK if there is no work to be
+ * done (the attach can just happen) or Z_ERR if we have to update the pkgs
+ * within the zone. We can also return Z_FATAL if we had a real error during
+ * this process.
+ */
+static int
+sw_up_to_date(zone_dochandle_t l_handle, zone_dochandle_t s_handle)
+{
+ int res = Z_OK;
+ int err;
+ int cmp;
+ FILE *fp_add = NULL, *fp_rm = NULL;
+ uu_avl_pool_t *pkg_pool = NULL;
+ uu_avl_t *src_pkgs = NULL;
+ uu_avl_t *dst_pkgs = NULL;
+ uu_avl_walk_t *walk;
+ zone_pkg_entry_t *src_pkg;
+ zone_pkg_entry_t *dst_pkg;
+ char fname[MAXPATHLEN];
+
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_add", zonepath);
+ if ((fp_add = fopen(fname, "w")) == NULL) {
+ zperror(gettext("could not save list of packages to add"),
+ B_FALSE);
+ goto fatal;
+ }
+
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_rm", zonepath);
+ if ((fp_rm = fopen(fname, "w")) == NULL) {
+ zperror(gettext("could not save list of packages to remove"),
+ B_FALSE);
+ goto fatal;
+ }
+
+ if ((pkg_pool = uu_avl_pool_create("pkgs_pool",
+ sizeof (zone_pkg_entry_t), offsetof(zone_pkg_entry_t, zpe_entry),
+ pkg_entry_compare, UU_DEFAULT)) == NULL)
+ goto fatal;
+
+ if ((src_pkgs = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto fatal;
+
+ if ((dst_pkgs = uu_avl_create(pkg_pool, NULL, UU_DEFAULT)) == NULL)
+ goto fatal;
+
+ if ((err = zonecfg_getpkgdata(s_handle, pkg_pool, src_pkgs)) != Z_OK) {
+ errno = err;
+ zperror(gettext("could not get package data for detached zone"),
+ B_TRUE);
+ goto fatal;
+ }
+ if ((err = zonecfg_getpkgdata(l_handle, pkg_pool, dst_pkgs)) != Z_OK) {
+ errno = err;
+ zperror(gettext("could not get package data for global zone"),
+ B_TRUE);
+ goto fatal;
+ }
+
+ /*
+ * First Pass
+ *
+ * Start by checking each pkg from the src system. We need to handle
+ * the following:
+ * 1) pkg on src but not on dst
+ * rm old pkg (allowed in order to handle obsolete pkgs)
+ * 3) pkg on src with higher rev than on dst
+ * fail (downgrade)
+ * 5) pkg ver same
+ * a) patch on src but not on dst
+ * fail (downgrade)
+ * c) patch on src with higher rev than on dst
+ * fail (downgrade)
+ */
+ if ((walk = uu_avl_walk_start(src_pkgs, UU_WALK_ROBUST)) == NULL) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ while ((src_pkg = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+ uu_avl_walk_t *patch_walk;
+ zone_pkg_entry_t *src_patch;
+
+ dst_pkg = uu_avl_find(dst_pkgs, src_pkg, NULL, &where);
+
+ if (dst_pkg == NULL) {
+ /* src pkg is not installed on dst */
+ if (fprintf(fp_rm, "%s\n", src_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to remove"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ /* Check pkg version to determine how to proceed. */
+ if (pkg_vers_cmp(src_pkg->zpe_vers, dst_pkg->zpe_vers, &cmp)) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ if (cmp > 0) {
+ /* src pkg has higher vers than dst pkg */
+ zerror(gettext("ERROR: attempt to downgrade package "
+ "%s %s to version %s"), src_pkg->zpe_name,
+ src_pkg->zpe_vers, dst_pkg->zpe_vers);
+ goto fatal;
+ }
+
+ /*
+ * src pkg has lower vers than dst pkg, we'll handle
+ * this in the loop where we process the dst pkgs.
+ */
+ if (cmp < 0)
+ continue;
+
+ /* src and dst pkgs have the same version. */
+
+ /*
+ * If src pkg has no patches, then we're done with this pkg.
+ * Any patches on the dst pkg are handled in the 2nd pass.
+ */
+ if (src_pkg->zpe_patches_avl == NULL)
+ continue;
+
+ if (dst_pkg->zpe_patches_avl == NULL) {
+ /*
+ * We have the same pkg on the src and dst but the src
+ * pkg has patches and the dst pkg does not, so this
+ * would be a downgrade! Disallow this.
+ */
+ zerror(gettext("ERROR: attempt to downgrade package "
+ "%s, the source had patches but this system does "
+ "not\n"), src_pkg->zpe_name);
+ goto fatal;
+ }
+
+ patch_walk = uu_avl_walk_start(src_pkg->zpe_patches_avl,
+ UU_WALK_ROBUST);
+ if (patch_walk == NULL) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ while ((src_patch = uu_avl_walk_next(patch_walk)) != NULL) {
+ zone_pkg_entry_t *dst_patch;
+
+ dst_patch = uu_avl_find(dst_pkg->zpe_patches_avl,
+ src_patch, NULL, &where);
+
+ if (dst_patch == NULL) {
+ /*
+ * We have the same pkg on the src and dst but
+ * the src pkg has a patch that the dst pkg
+ * does not, so this would be a downgrade! We
+ * need to disallow this but first double check
+ * that this patch has not been obsoleted by
+ * some other patch that is installed on the
+ * dst. If the patch is obsolete, the pkg will
+ * be handled in the 2nd pass.
+ */
+ if (is_obsolete(dst_pkg, src_patch))
+ continue;
+
+ zerror(gettext("ERROR: attempt to downgrade "
+ "package %s, the source had patch %s-%s "
+ "which is not installed on this system\n"),
+ src_pkg->zpe_name, src_patch->zpe_name,
+ src_patch->zpe_vers);
+
+ goto fatal;
+ }
+
+ /* Check if the src patch is newer than the dst patch */
+ if (strcmp(src_patch->zpe_vers, dst_patch->zpe_vers)
+ > 0) {
+ /*
+ * We have a patch on the src with higher rev
+ * than the patch on the dst so this would be a
+ * downgrade! We need to disallow this but
+ * first double check that this patch has not
+ * been obsoleted by some other patch that is
+ * installed on the dst. If the patch is
+ * obsolete, the pkg will be handled in the 2nd
+ * pass.
+ */
+ if (is_obsolete(dst_pkg, src_patch))
+ continue;
+
+ zerror(gettext("ERROR: attempt to downgrade "
+ "package %s, the source had patch %s-%s "
+ "but this system only has %s-%s\n"),
+ src_pkg->zpe_name, src_patch->zpe_name,
+ src_patch->zpe_vers, dst_patch->zpe_name,
+ dst_patch->zpe_vers);
+ goto fatal;
+ }
+
+ /*
+ * If the src patch is the same rev or older than the
+ * dst patch we'll handle that in the second pass.
+ */
+ }
+
+ uu_avl_walk_end(patch_walk);
+ }
+
+ uu_avl_walk_end(walk);
+
+ /*
+ * Second Pass
+ *
+ * Now check each pkg from the dst system. We need to handle
+ * the following:
+ * 2) pkg on dst but not on src
+ * add pkg
+ * 4) pkg on dst with higher rev than on src
+ * remove old pkg & add current
+ * 5) pkg ver same
+ * b) patch on dst but not on src
+ * remove old pkg & add
+ * d) patch on dst with higher rev than on src
+ * remove old pkg & add
+ */
+ if ((walk = uu_avl_walk_start(dst_pkgs, UU_WALK_ROBUST)) == NULL) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ while ((dst_pkg = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_index_t where;
+ uu_avl_walk_t *patch_walk;
+ zone_pkg_entry_t *dst_patch;
+
+ src_pkg = uu_avl_find(src_pkgs, dst_pkg, NULL, &where);
+
+ if (src_pkg == NULL) {
+ /* dst pkg was not installed on src */
+ if (fprintf(fp_add, "%s\n", dst_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to add"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ /* Check pkg version to determine how to proceed. */
+ if (pkg_vers_cmp(dst_pkg->zpe_vers, src_pkg->zpe_vers, &cmp)) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ if (cmp > 0) {
+ /* dst pkg has higher vers than src pkg */
+ if (fprintf(fp_rm, "%s\n", dst_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to remove"), B_FALSE);
+ goto fatal;
+ }
+ if (fprintf(fp_add, "%s\n", dst_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to add"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ /*
+ * cmp < 0 was handled in the first loop. This would
+ * be a downgrade so we should have already failed.
+ */
+ assert(cmp >= 0);
+
+ /* src and dst pkgs have the same version. */
+
+ /* If dst pkg has no patches, then we're done with this pkg. */
+ if (dst_pkg->zpe_patches_avl == NULL)
+ continue;
+
+ if (src_pkg->zpe_patches_avl == NULL) {
+ /*
+ * We have the same pkg on the src and dst
+ * but the dst pkg has patches and the src
+ * pkg does not. Just replace the pkg.
+ */
+ if (fprintf(fp_rm, "%s\n", dst_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to remove"), B_FALSE);
+ goto fatal;
+ }
+ if (fprintf(fp_add, "%s\n", dst_pkg->zpe_name) < 0) {
+ zperror(gettext("could not save list of "
+ "packages to add"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ patch_walk = uu_avl_walk_start(dst_pkg->zpe_patches_avl,
+ UU_WALK_ROBUST);
+ if (patch_walk == NULL) {
+ zerror(gettext("Out of memory"));
+ goto fatal;
+ }
+
+ while ((dst_patch = uu_avl_walk_next(patch_walk)) != NULL) {
+ zone_pkg_entry_t *src_patch;
+
+ src_patch = uu_avl_find(src_pkg->zpe_patches_avl,
+ dst_patch, NULL, &where);
+
+ if (src_patch == NULL) {
+ /*
+ * We have the same pkg on the src and dst but
+ * the dst pkg has a patch that the src pkg
+ * does not. Just replace the pkg.
+ */
+ if (fprintf(fp_rm, "%s\n", dst_pkg->zpe_name)
+ < 0) {
+ zperror(gettext("could not save list "
+ "of packages to remove"), B_FALSE);
+ goto fatal;
+ }
+ if (fprintf(fp_add, "%s\n", dst_pkg->zpe_name)
+ < 0) {
+ zperror(gettext("could not save list "
+ "of packages to add"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ /* Check if the dst patch is newer than the src patch */
+ if (strcmp(dst_patch->zpe_vers, src_patch->zpe_vers)
+ > 0) {
+ /*
+ * We have a patch on the dst with higher rev
+ * than the patch on the src. Just replace the
+ * pkg.
+ */
+ if (fprintf(fp_rm, "%s\n", dst_pkg->zpe_name)
+ < 0) {
+ zperror(gettext("could not save list "
+ "of packages to remove"), B_FALSE);
+ goto fatal;
+ }
+ if (fprintf(fp_add, "%s\n", dst_pkg->zpe_name)
+ < 0) {
+ zperror(gettext("could not save list "
+ "of packages to add"), B_FALSE);
+ goto fatal;
+ }
+ res = Z_ERR;
+ continue;
+ }
+
+ /*
+ * If the dst patch is the same rev then we can ignore
+ * this pkg. If it is older than the src patch we
+ * handled that in the first pass and we should have
+ * already failed.
+ */
+ assert(strcmp(dst_patch->zpe_vers, src_patch->zpe_vers)
+ >= 0);
+ }
+
+ uu_avl_walk_end(patch_walk);
+ }
+
+ uu_avl_walk_end(walk);
+
+ if (fclose(fp_add) != 0) {
+ zperror(gettext("could not save list of packages to add"),
+ B_FALSE);
+ goto fatal;
+ }
+ fp_add = NULL;
+ if (fclose(fp_rm) != 0) {
+ zperror(gettext("could not save list of packages to remove"),
+ B_FALSE);
+ goto fatal;
+ }
+
+ /* free avl structs */
+ pkg_avl_delete(src_pkgs);
+ pkg_avl_delete(dst_pkgs);
+ uu_avl_pool_destroy(pkg_pool);
+
+ return (res);
+
+fatal:
+ /* free avl structs */
+ pkg_avl_delete(src_pkgs);
+ pkg_avl_delete(dst_pkgs);
+ if (pkg_pool != NULL)
+ uu_avl_pool_destroy(pkg_pool);
+
+ if (fp_add != NULL)
+ (void) fclose(fp_add);
+ if (fp_rm != NULL)
+ (void) fclose(fp_rm);
+
+ /* clean up data files left behind */
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_add", zonepath);
+ (void) unlink(fname);
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_rm", zonepath);
+ (void) unlink(fname);
+
+ return (Z_FATAL);
+}
+
+/*
+ * During attach we go through and fix up the /dev entries for the zone
+ * we are attaching. In order to regenerate /dev with the correct devices,
+ * the old /dev will be removed, the zone readied (which generates a new
+ * /dev) then halted, then we use the info from the manifest to update
+ * the modes, owners, etc. on the new /dev.
+ */
+static int
+dev_fix(char *zonename, zone_dochandle_t handle)
+{
+ int err;
+ int status;
+ struct zone_devpermtab devtab;
+ zone_cmd_arg_t zarg;
+ char devpath[MAXPATHLEN];
+ /* 6: "exec " and " " */
+ char cmdbuf[sizeof (RMCOMMAND) + MAXPATHLEN + 6];
+
+ if (snprintf(devpath, sizeof (devpath), "%s/dev", zonepath)
+ >= sizeof (devpath))
+ return (Z_TOO_BIG);
+
+ /*
+ * "exec" the command so that the returned status is that of
+ * RMCOMMAND and not the shell.
+ */
+ (void) snprintf(cmdbuf, sizeof (cmdbuf), EXEC_PREFIX RMCOMMAND " %s",
+ devpath);
+ status = do_subproc(cmdbuf);
+ if ((err = subproc_status(RMCOMMAND, status, B_TRUE)) !=
+ ZONE_SUBPROC_OK) {
+ (void) fprintf(stderr,
+ gettext("could not remove existing /dev\n"));
+ return (Z_ERR);
+ }
+
+ /* In order to ready the zone, it must be in the installed state */
+ if ((err = zone_set_state(zonename, ZONE_STATE_INSTALLED)) != Z_OK) {
+ errno = err;
+ zperror(gettext("could not reset state"), B_TRUE);
+ return (Z_ERR);
+ }
+
+ /* We have to ready the zone to regen the dev tree */
+ zarg.cmd = Z_READY;
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ zerror(gettext("call to %s failed"), "zoneadmd");
+ /* attempt to restore zone to configured state */
+ (void) zone_set_state(zonename, ZONE_STATE_CONFIGURED);
+ return (Z_ERR);
+ }
+
+ zarg.cmd = Z_HALT;
+ if (zonecfg_call_zoneadmd(zonename, &zarg, locale, B_FALSE) != 0) {
+ zerror(gettext("call to %s failed"), "zoneadmd");
+ /* attempt to restore zone to configured state */
+ (void) zone_set_state(zonename, ZONE_STATE_CONFIGURED);
+ return (Z_ERR);
+ }
+
+ /* attempt to restore zone to configured state */
+ (void) zone_set_state(zonename, ZONE_STATE_CONFIGURED);
+
+ if (zonecfg_setdevperment(handle) != Z_OK) {
+ (void) fprintf(stderr,
+ gettext("unable to enumerate device entries\n"));
+ return (Z_ERR);
+ }
+
+ while (zonecfg_getdevperment(handle, &devtab) == Z_OK) {
+ int err;
+
+ if ((err = zonecfg_devperms_apply(handle,
+ devtab.zone_devperm_name, devtab.zone_devperm_uid,
+ devtab.zone_devperm_gid, devtab.zone_devperm_mode,
+ devtab.zone_devperm_acl)) != Z_OK && err != Z_INVAL)
+ (void) fprintf(stderr, gettext("error updating device "
+ "%s: %s\n"), devtab.zone_devperm_name,
+ zonecfg_strerror(err));
+
+ free(devtab.zone_devperm_acl);
+ }
+
+ (void) zonecfg_enddevperment(handle);
+
+ return (Z_OK);
+}
+
+static int
+attach_func(int argc, char *argv[])
+{
+ int err, arg;
+ zone_dochandle_t handle;
+ zone_dochandle_t athandle = NULL;
+ char brand[MAXNAMELEN], atbrand[MAXNAMELEN];
+ boolean_t execute = B_TRUE;
+ boolean_t retried = B_FALSE;
+ boolean_t update = B_FALSE;
+ char *manifest_path;
+
+ opterr = 0;
+ optind = 0;
+ if ((arg = getopt(argc, argv, "?Fn:u")) != EOF) {
+ switch (arg) {
+ case '?':
+ if (optopt != '?') {
+ (void) fprintf(stderr, gettext("%s brand: "
+ "invalid option: %c\n"), MY_BRAND_NAME,
+ optopt);
+ }
+ (void) fprintf(stderr, gettext("usage:\t%s brand "
+ "options: [-u]\n"), MY_BRAND_NAME);
+ (void) fprintf(stderr, gettext("\tSpecify "
+ "-u to update the zone to the current "
+ "system software.\n"));
+ return (optopt == '?' ? Z_OK : ZONE_SUBPROC_USAGE);
+ case 'n':
+ execute = B_FALSE;
+ manifest_path = optarg;
+ break;
+ case 'u':
+ update = B_TRUE;
+ break;
+ default:
+ (void) fprintf(stderr, gettext("%s brand: invalid "
+ "option: %c\n"), MY_BRAND_NAME, optopt);
+ return (ZONE_SUBPROC_USAGE);
+ }
+ }
+
+ /* dry-run and update flags are mutually exclusive */
+ if (!execute && update) {
+ (void) fprintf(stderr, gettext("-n and -u flags are mutually "
+ "exclusive\n"));
+ return (ZONE_SUBPROC_USAGE);
+ }
+
+ /*
+ * If the no-execute option was specified, we need to branch down
+ * a completely different path since there is no zone required to be
+ * configured for this option.
+ */
+ if (!execute)
+ return (dryrun_attach(manifest_path));
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand attach program error: "
+ "%s\n"), strerror(errno));
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((err = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand attach program error: "
+ "%s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (ZONE_SUBPROC_NOTCOMPLETE);
+ }
+
+ if ((athandle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand attach program error: "
+ "%s\n"), strerror(errno));
+ goto done;
+ }
+
+retry:
+ if ((err = zonecfg_get_attach_handle(zonepath, zonename, B_TRUE,
+ athandle)) != Z_OK) {
+ if (err == Z_NO_ZONE) {
+ /*
+ * Zone was not detached. Try to fall back to getting
+ * the needed information from within the zone.
+ */
+ if (!retried) {
+ (void) fprintf(stderr, gettext("The zone was "
+ "not properly detached.\n\tAttempting to "
+ "attach anyway.\n"));
+ if (gen_detach_info()) {
+ retried = B_TRUE;
+ goto retry;
+ }
+ }
+ (void) fprintf(stderr, gettext("Cannot generate the "
+ "information needed to attach this zone.\n"));
+ } else if (err == Z_INVALID_DOCUMENT) {
+ (void) fprintf(stderr, gettext("Cannot attach to an "
+ "earlier release of the operating system\n"));
+ } else {
+ (void) fprintf(stderr, gettext("brand attach program "
+ "error: %s\n"), zonecfg_strerror(err));
+ }
+ goto done;
+ }
+
+ /* Get the detach information for the locally defined zone. */
+ if ((err = get_detach_info(handle, B_FALSE)) != Z_OK) {
+ (void) fprintf(stderr, gettext("getting the attach information "
+ "failed: %s\n"), zonecfg_strerror(err));
+ goto done;
+ }
+
+ /*
+ * Ensure that the detached and locally defined zones are both of
+ * the same brand.
+ */
+ if ((zonecfg_get_brand(handle, brand, sizeof (brand)) != 0) ||
+ (zonecfg_get_brand(athandle, atbrand, sizeof (atbrand)) != 0)) {
+ err = Z_ERR;
+ (void) fprintf(stderr, gettext("missing or invalid brand\n"));
+ goto done;
+ }
+
+ if (strcmp(atbrand, brand) != 0) {
+ err = Z_ERR;
+ (void) fprintf(stderr, gettext("Trying to attach a '%s' zone "
+ "to a '%s' configuration.\n"), atbrand, brand);
+ goto done;
+ }
+
+ /*
+ * If we're doing an update on attach, and the zone does need to be
+ * updated, then run the update.
+ */
+ if (update) {
+ char fname[MAXPATHLEN];
+
+ (void) sigset(SIGINT, sigcleanup);
+
+ if ((err = sw_up_to_date(handle, athandle)) != Z_OK) {
+ if (err != Z_FATAL && !attach_interupted) {
+ err = Z_FATAL;
+ err = attach_update(handle);
+ }
+ if (!attach_interupted || err == Z_OK)
+ goto done;
+ }
+
+ (void) sigset(SIGINT, SIG_DFL);
+
+ /* clean up data files left behind by sw_up_to_date() */
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_add", zonepath);
+ (void) unlink(fname);
+ (void) snprintf(fname, sizeof (fname), "%s/pkg_rm", zonepath);
+ (void) unlink(fname);
+
+ if (attach_interupted) {
+ err = Z_FATAL;
+ goto done;
+ }
+
+ } else {
+ /* sw_cmp prints error msgs as necessary */
+ if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK)
+ goto done;
+
+ if ((err = dev_fix(zonename, athandle)) != Z_OK)
+ goto done;
+ }
+
+ if ((err = zone_set_state(zonename, ZONE_STATE_INSTALLED)) != Z_OK) {
+ (void) fprintf(stderr, gettext("could not reset state: %s\n"),
+ zonecfg_strerror(err));
+ }
+
+done:
+ zonecfg_fini_handle(handle);
+ if (athandle != NULL)
+ zonecfg_fini_handle(athandle);
+
+ return ((err == Z_OK) ? Z_OK : ZONE_SUBPROC_FATAL);
+}
+
+static int
+install_func(int argc, char *argv[])
+{
+ char cmdbuf[MAXPATHLEN];
+ int arg;
+ int status;
+
+ opterr = 0;
+ optind = 0;
+ while ((arg = getopt(argc, argv, "?x:")) != EOF) {
+ switch (arg) {
+ case '?':
+ if (optopt != '?') {
+ (void) fprintf(stderr, gettext("%s brand: "
+ "invalid option: %c\n"), MY_BRAND_NAME,
+ optopt);
+ }
+ (void) fprintf(stderr, gettext("usage:\t%s brand "
+ "options: none\n"), MY_BRAND_NAME);
+ return (optopt == '?' ? Z_OK : ZONE_SUBPROC_USAGE);
+ case 'x':
+ if (strcmp(optarg, "nodataset") != 0) {
+ (void) fprintf(stderr, gettext("%s brand: "
+ "invalid option: %c\n"), MY_BRAND_NAME,
+ arg);
+ return (ZONE_SUBPROC_USAGE);
+ }
+ /* Ignore option, handled in zoneadm. */
+ break;
+ default:
+ (void) fprintf(stderr, gettext("%s brand: invalid "
+ "option: %c\n"), MY_BRAND_NAME, optopt);
+ return (ZONE_SUBPROC_USAGE);
+ }
+ }
+
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/lib/lu/lucreatezone -z %s",
+ zonename) >= sizeof (cmdbuf))
+ return (Z_ERR);
+
+ /*
+ * According to the Application Packaging Developer's Guide, a
+ * "checkinstall" script when included in a package is executed as
+ * the user "install", if such a user exists, or by the user
+ * "nobody". In order to support this dubious behavior, the path
+ * to the zone being constructed is opened up during the life of
+ * the command laying down the zone's root file system. Once this
+ * has completed, regardless of whether it was successful, the
+ * path to the zone is again restricted.
+ */
+ if (chmod(zonepath, DEFAULT_DIR_MODE) != 0) {
+ zperror(zonepath, B_FALSE);
+ return (Z_ERR);
+ }
+
+ status = do_subproc(cmdbuf);
+
+ if (chmod(zonepath, S_IRWXU) != 0) {
+ zperror(zonepath, B_FALSE);
+ return (Z_ERR);
+ }
+
+ if (subproc_status("install", status, B_FALSE) != ZONE_SUBPROC_OK)
+ return (Z_ERR);
+
+ return (Z_OK);
+}
+
+/* ARGSUSED */
+static int
+postclone_func(int argc, char *argv[])
+{
+ int status;
+ boolean_t res = B_TRUE;
+ struct stat sbuf;
+ char cmdbuf[2 * MAXPATHLEN];
+
+ /* Ignore any arguments. */
+
+ /*
+ * Trusted Extensions requires that cloned zones use the same sysid
+ * configuration, so it is not appropriate to perform any
+ * post-clone reconfiguration.
+ */
+ if (is_system_labeled())
+ return (ZONE_SUBPROC_OK);
+
+ /* If the zone is already sys-unconfiged, then we're done. */
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "%s/root/etc/.UNCONFIGURED",
+ zonepath) >= sizeof (cmdbuf))
+ return (ZONE_SUBPROC_FATAL);
+
+ if (stat(cmdbuf, &sbuf) == 0 && S_ISREG(sbuf.st_mode))
+ return (ZONE_SUBPROC_OK);
+
+ /*
+ * Mount the zone. The zone is still in the INCOMPLETE state, so we
+ * have to force mount it.
+ */
+ if (mount_func(B_TRUE) != Z_OK)
+ return (ZONE_SUBPROC_FATAL);
+
+ /* sys-unconfig the zone */
+ if (snprintf(cmdbuf, sizeof (cmdbuf), "/usr/sbin/zlogin -S %s "
+ "'/usr/sbin/sys-unconfig -R /a'", zonename) >= sizeof (cmdbuf)) {
+ res = B_FALSE;
+ } else {
+ status = do_subproc(cmdbuf);
+ if (subproc_status("sys-unconfig failed", status, B_TRUE)
+ != ZONE_SUBPROC_OK)
+ res = B_FALSE;
+ }
+
+ if (unmount_func() != Z_OK)
+ res = B_FALSE;
+
+ return (res ? ZONE_SUBPROC_OK : ZONE_SUBPROC_FATAL);
+}
+
+/*
+ * Perform any necessary housekeeping tasks we need to do before we take
+ * a ZFS snapshot of the zone. What this really entails is that we are
+ * taking a sw inventory of the source zone, like we do when we detach,
+ * so that there is the XML manifest in the snapshot. We use that to
+ * validate the snapshot if it is the source of a clone at some later time.
+ */
+static int
+presnap_func(int argc, char *argv[])
+{
+ int err;
+ zone_dochandle_t handle;
+
+ opterr = 0;
+ optind = 0;
+ if (getopt(argc, argv, "") != EOF)
+ return (ZONE_SUBPROC_USAGE);
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand pre-snapshot program "
+ "error: %s\n"), strerror(errno));
+ return (Z_ERR);
+ }
+
+ if ((err = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand pre-snapshot program "
+ "error: %s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
+
+ if ((err = get_detach_info(handle, B_TRUE)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand pre-snapshot program "
+ "error: %s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
+
+ if ((err = zonecfg_detach_save(handle, 0)) != Z_OK) {
+ (void) fprintf(stderr, gettext("saving the detach manifest "
+ "failed: %s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
+
+ zonecfg_fini_handle(handle);
+
+ return (Z_OK);
+}
+
+/*
+ * Perform any necessary housekeeping tasks we need to do after we take
+ * a ZFS snapshot of the zone. What this really entails is removing the
+ * sw inventory XML file from the zone. It is still in the snapshot where
+ * we want it, but we don't want it in the source zone itself.
+ */
+static int
+postsnap_func(int argc, char *argv[])
+{
+ int err;
+ zone_dochandle_t handle;
+
+ opterr = 0;
+ optind = 0;
+ if (getopt(argc, argv, "") != EOF)
+ return (ZONE_SUBPROC_USAGE);
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand post-snapshot program "
+ "error: %s\n"), strerror(errno));
+ return (Z_ERR);
+ }
+
+ if ((err = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand post-snapshot program "
+ "error: %s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
+
+ zonecfg_rm_detached(handle, B_FALSE);
+ zonecfg_fini_handle(handle);
+
+ return (Z_OK);
+}
+
+/*
+ * We are using an explicit snapshot from some earlier point in time so
+ * we need to validate it. This involves checking the sw inventory that
+ * we took when we made the snapshot to verify that the current sw config
+ * on the host is still valid to run a zone made from this snapshot.
+ */
+static int
+validatesnap_func(int argc, char *argv[])
+{
+ int err;
+ zone_dochandle_t handle;
+ zone_dochandle_t athandle = NULL;
+ char *snapshot_name;
+ char *snap_path;
+
+ opterr = 0;
+ optind = 0;
+ if (getopt(argc, argv, "") != EOF)
+ return (ZONE_SUBPROC_USAGE);
+
+ if (argc < 2)
+ return (ZONE_SUBPROC_USAGE);
+
+ snapshot_name = argv[0];
+ snap_path = argv[1];
+
+ if ((handle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand validate-snapshot "
+ "program error: %s\n"), strerror(errno));
+ return (Z_ERR);
+ }
+
+ if ((err = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ (void) fprintf(stderr, gettext("brand validate-snapshot "
+ "program error: %s\n"), zonecfg_strerror(err));
+ zonecfg_fini_handle(handle);
+ return (Z_ERR);
+ }
+
+ if ((athandle = zonecfg_init_handle()) == NULL) {
+ (void) fprintf(stderr, gettext("brand validate-snapshot "
+ "program error: %s\n"), strerror(errno));
+ goto done;
+ }
+
+ if ((err = zonecfg_get_attach_handle(snap_path, zonename, B_TRUE,
+ athandle)) != Z_OK) {
+ if (err == Z_NO_ZONE)
+ (void) fprintf(stderr, gettext("snapshot %s was not "
+ "taken\n\tby a 'zoneadm clone' command. It can "
+ "not be used to clone zones.\n"), snapshot_name);
+ else
+ (void) fprintf(stderr, gettext("snapshot %s is "
+ "out-dated\n\tIt can no longer be used to clone "
+ "zones on this system.\n"), snapshot_name);
+ goto done;
+ }
+
+ /* Get the detach information for the locally defined zone. */
+ if ((err = get_detach_info(handle, B_FALSE)) != Z_OK) {
+ errno = err;
+ zperror(gettext("getting the attach information failed"),
+ B_TRUE);
+ goto done;
+ }
+
+ if ((err = sw_cmp(handle, athandle, SW_CMP_SILENT)) != Z_OK)
+ (void) fprintf(stderr, gettext("snapshot %s is out-dated\n\t"
+ "It can no longer be used to clone zones on this "
+ "system.\n"), snapshot_name);
+
+done:
+ zonecfg_fini_handle(handle);
+ if (athandle != NULL)
+ zonecfg_fini_handle(athandle);
+
+ return ((err == Z_OK) ? Z_OK : ZONE_SUBPROC_FATAL);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, gettext("sw_support invalid arguments\n"));
+ exit(253);
+}
+
+int
+main(int argc, char **argv)
+{
+ int err = ZONE_SUBPROC_FATAL;
+ char *cmd = NULL;
+
+ if ((locale = setlocale(LC_ALL, "")) == NULL)
+ locale = "C";
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (argc < 4)
+ usage();
+
+ cmd = argv[1];
+ zonename = argv[2];
+ zonepath = argv[3];
+
+ argc -= 4;
+ argv = &argv[4];
+
+ if (strcmp(cmd, "attach") == 0)
+ err = attach_func(argc, argv);
+ else if (strcmp(cmd, "detach") == 0)
+ err = detach_func(argc, argv);
+ else if (strcmp(cmd, "install") == 0)
+ err = install_func(argc, argv);
+ else if (strcmp(cmd, "postclone") == 0)
+ err = postclone_func(argc, argv);
+ else if (strcmp(cmd, "presnap") == 0)
+ err = presnap_func(argc, argv);
+ else if (strcmp(cmd, "postsnap") == 0)
+ err = postsnap_func(argc, argv);
+ else if (strcmp(cmd, "validatesnap") == 0)
+ err = validatesnap_func(argc, argv);
+
+ return (err);
+}
diff --git a/usr/src/lib/brand/sn1/zone/config.xml b/usr/src/lib/brand/sn1/zone/config.xml
index de3cf2a7b2..43402091c1 100644
--- a/usr/src/lib/brand/sn1/zone/config.xml
+++ b/usr/src/lib/brand/sn1/zone/config.xml
@@ -38,14 +38,16 @@
<login_cmd>/usr/bin/login -z %Z -f %u</login_cmd>
<user_cmd>/usr/bin/getent passwd %u</user_cmd>
- <install>/usr/lib/lu/lucreatezone -z %z</install>
- <installopts></installopts>
+ <install>/usr/lib/brand/native/sw_support install %z %R</install>
<boot>/usr/lib/brand/sn1/sn1_boot %R</boot>
- <halt></halt>
<verify_cfg></verify_cfg>
<verify_adm></verify_adm>
- <postclone></postclone>
- <postinstall></postinstall>
+ <postclone>/usr/lib/brand/native/sw_support postclone %z %R</postclone>
+ <attach>/usr/lib/brand/native/sw_support attach %z %R</attach>
+ <detach>/usr/lib/brand/native/sw_support detach %z %R</detach>
+ <presnap>/usr/lib/brand/native/sw_support presnap %z %R</presnap>
+ <postsnap>/usr/lib/brand/native/sw_support postsnap %z %R</postsnap>
+ <validatesnap>/usr/lib/brand/native/sw_support validatesnap %z %R</validatesnap>
<privilege set="default" name="contract_event" />
<privilege set="default" name="contract_identity" />
diff --git a/usr/src/lib/libbrand/common/libbrand.c b/usr/src/lib/libbrand/common/libbrand.c
index acfead1e10..a751fbe30c 100644
--- a/usr/src/lib/libbrand/common/libbrand.c
+++ b/usr/src/lib/libbrand/common/libbrand.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -47,9 +47,12 @@
#include <libbrand_impl.h>
#include <libbrand.h>
+#define DTD_ELEM_ATTACH ((const xmlChar *) "attach")
#define DTD_ELEM_BOOT ((const xmlChar *) "boot")
#define DTD_ELEM_BRAND ((const xmlChar *) "brand")
+#define DTD_ELEM_CLONE ((const xmlChar *) "clone")
#define DTD_ELEM_COMMENT ((const xmlChar *) "comment")
+#define DTD_ELEM_DETACH ((const xmlChar *) "detach")
#define DTD_ELEM_DEVICE ((const xmlChar *) "device")
#define DTD_ELEM_GLOBAL_MOUNT ((const xmlChar *) "global_mount")
#define DTD_ELEM_HALT ((const xmlChar *) "halt")
@@ -62,11 +65,15 @@
#define DTD_ELEM_POSTATTACH ((const xmlChar *) "postattach")
#define DTD_ELEM_POSTCLONE ((const xmlChar *) "postclone")
#define DTD_ELEM_POSTINSTALL ((const xmlChar *) "postinstall")
+#define DTD_ELEM_POSTSNAP ((const xmlChar *) "postsnap")
#define DTD_ELEM_PREDETACH ((const xmlChar *) "predetach")
+#define DTD_ELEM_PRESNAP ((const xmlChar *) "presnap")
#define DTD_ELEM_PREUNINSTALL ((const xmlChar *) "preuninstall")
#define DTD_ELEM_PRIVILEGE ((const xmlChar *) "privilege")
#define DTD_ELEM_SYMLINK ((const xmlChar *) "symlink")
+#define DTD_ELEM_UNINSTALL ((const xmlChar *) "uninstall")
#define DTD_ELEM_USER_CMD ((const xmlChar *) "user_cmd")
+#define DTD_ELEM_VALIDSNAP ((const xmlChar *) "validatesnap")
#define DTD_ELEM_VERIFY_CFG ((const xmlChar *) "verify_cfg")
#define DTD_ELEM_VERIFY_ADM ((const xmlChar *) "verify_adm")
@@ -306,13 +313,10 @@ brand_close(brand_handle_t bh)
static int
i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
- const char *zonename, const char *zoneroot, const char *username,
- const char *curr_zone, int argc, char **argv)
+ const char *zonename, const char *zonepath, const char *username,
+ const char *curr_zone)
{
- int dst, src, i;
-
- assert(argc >= 0);
- assert((argc == 0) || (argv != NULL));
+ int dst, src;
/*
* Walk through the characters, substituting values as needed.
@@ -330,9 +334,9 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
dst += strlcpy(dbuf + dst, "%", dbuf_size - dst);
break;
case 'R':
- if (zoneroot == NULL)
+ if (zonepath == NULL)
break;
- dst += strlcpy(dbuf + dst, zoneroot, dbuf_size - dst);
+ dst += strlcpy(dbuf + dst, zonepath, dbuf_size - dst);
break;
case 'u':
if (username == NULL)
@@ -351,13 +355,6 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
break;
dst += strlcpy(dbuf + dst, zonename, dbuf_size - dst);
break;
- case '*':
- if (argv == NULL)
- break;
- for (i = 0; i < argc; i++)
- dst += snprintf(dbuf + dst, dbuf_size - dst,
- " \"%s\"", argv[i]);
- break;
}
}
@@ -376,15 +373,14 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
* %u Username
* %z Name of target zone
* %Z Name of current zone
- * %R Root of zone
- * %* Additional arguments (argc, argv)
+ * %R Zonepath of zone
*
* Returns 0 on success, -1 on failure.
*/
static int
brand_get_value(struct brand_handle *bhp, const char *zonename,
- const char *zoneroot, const char *username, const char *curr_zone,
- char *buf, size_t len, int argc, char **argv, const xmlChar *tagname,
+ const char *zonepath, const char *username, const char *curr_zone,
+ char *buf, size_t len, const xmlChar *tagname,
boolean_t substitute, boolean_t optional)
{
xmlNodePtr node;
@@ -433,8 +429,7 @@ brand_get_value(struct brand_handle *bhp, const char *zonename,
/* Substitute token values as needed. */
if (substitute) {
if (i_substitute_tokens((char *)content, buf, len,
- zonename, zoneroot, username, curr_zone,
- argc, argv) != 0)
+ zonename, zonepath, username, curr_zone) != 0)
err = -1;
} else {
if (strlcpy(buf, (char *)content, len) >= len)
@@ -448,12 +443,21 @@ brand_get_value(struct brand_handle *bhp, const char *zonename,
}
int
+brand_get_attach(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_ATTACH, B_TRUE, B_TRUE));
+}
+
+int
brand_get_boot(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_BOOT, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_BOOT, B_TRUE, B_TRUE));
}
int
@@ -469,12 +473,30 @@ brand_get_brandname(brand_handle_t bh, char *buf, size_t len)
}
int
+brand_get_clone(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_CLONE, B_TRUE, B_TRUE));
+}
+
+int
+brand_get_detach(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_DETACH, B_TRUE, B_TRUE));
+}
+
+int
brand_get_halt(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_HALT, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_HALT, B_TRUE, B_TRUE));
}
int
@@ -482,7 +504,7 @@ brand_get_initname(brand_handle_t bh, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
- buf, len, 0, NULL, DTD_ELEM_INITNAME, B_FALSE, B_FALSE));
+ buf, len, DTD_ELEM_INITNAME, B_FALSE, B_FALSE));
}
int
@@ -492,7 +514,7 @@ brand_get_login_cmd(brand_handle_t bh, const char *username,
struct brand_handle *bhp = (struct brand_handle *)bh;
const char *curr_zone = get_curr_zone();
return (brand_get_value(bhp, NULL, NULL, username, curr_zone,
- buf, len, 0, NULL, DTD_ELEM_LOGIN_CMD, B_TRUE, B_FALSE));
+ buf, len, DTD_ELEM_LOGIN_CMD, B_TRUE, B_FALSE));
}
int
@@ -502,16 +524,16 @@ brand_get_user_cmd(brand_handle_t bh, const char *username,
struct brand_handle *bhp = (struct brand_handle *)bh;
return (brand_get_value(bhp, NULL, NULL, username, NULL,
- buf, len, 0, NULL, DTD_ELEM_USER_CMD, B_TRUE, B_FALSE));
+ buf, len, DTD_ELEM_USER_CMD, B_TRUE, B_FALSE));
}
int
brand_get_install(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_INSTALL, B_TRUE, B_FALSE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_INSTALL, B_TRUE, B_FALSE));
}
int
@@ -519,7 +541,7 @@ brand_get_installopts(brand_handle_t bh, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
- buf, len, 0, NULL, DTD_ELEM_INSTALLOPTS, B_FALSE, B_TRUE));
+ buf, len, DTD_ELEM_INSTALLOPTS, B_FALSE, B_TRUE));
}
int
@@ -527,52 +549,88 @@ brand_get_modname(brand_handle_t bh, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
- buf, len, 0, NULL, DTD_ELEM_MODNAME, B_FALSE, B_TRUE));
+ buf, len, DTD_ELEM_MODNAME, B_FALSE, B_TRUE));
}
int
brand_get_postattach(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_POSTATTACH, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_POSTATTACH, B_TRUE, B_TRUE));
}
int
brand_get_postclone(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_POSTCLONE, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_POSTCLONE, B_TRUE, B_TRUE));
}
int
brand_get_postinstall(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_POSTINSTALL, B_TRUE, B_TRUE));
+}
+
+int
+brand_get_postsnap(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_POSTINSTALL, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_POSTSNAP, B_TRUE, B_TRUE));
}
int
brand_get_predetach(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_PREDETACH, B_TRUE, B_TRUE));
+}
+
+int
+brand_get_presnap(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_PREDETACH, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_PRESNAP, B_TRUE, B_TRUE));
}
int
brand_get_preuninstall(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_PREUNINSTALL, B_TRUE, B_TRUE));
+}
+
+int
+brand_get_uninstall(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
+{
+ struct brand_handle *bhp = (struct brand_handle *)bh;
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_UNINSTALL, B_TRUE, B_TRUE));
+}
+
+int
+brand_get_validatesnap(brand_handle_t bh, const char *zonename,
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_PREUNINSTALL, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_VALIDSNAP, B_TRUE, B_TRUE));
}
int
@@ -580,16 +638,16 @@ brand_get_verify_cfg(brand_handle_t bh, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
- buf, len, 0, NULL, DTD_ELEM_VERIFY_CFG, B_FALSE, B_TRUE));
+ buf, len, DTD_ELEM_VERIFY_CFG, B_FALSE, B_TRUE));
}
int
brand_get_verify_adm(brand_handle_t bh, const char *zonename,
- const char *zoneroot, char *buf, size_t len, int argc, char **argv)
+ const char *zonepath, char *buf, size_t len)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
- buf, len, argc, argv, DTD_ELEM_VERIFY_ADM, B_TRUE, B_TRUE));
+ return (brand_get_value(bhp, zonename, zonepath, NULL, NULL,
+ buf, len, DTD_ELEM_VERIFY_ADM, B_TRUE, B_TRUE));
}
int
@@ -683,7 +741,7 @@ brand_config_iter_privilege(brand_handle_t bh,
}
static int
-i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zoneroot,
+i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonepath,
int (*func)(void *, const char *, const char *, const char *,
const char *), void *data, const xmlChar *mount_type)
{
@@ -714,7 +772,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zoneroot,
/* Substitute token values as needed. */
if ((ret = i_substitute_tokens((char *)special,
special_exp, sizeof (special_exp),
- NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
+ NULL, zonepath, NULL, NULL)) != 0)
goto next;
/* opt might not be defined */
@@ -724,7 +782,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zoneroot,
} else {
if ((ret = i_substitute_tokens((char *)opt,
opt_exp, sizeof (opt_exp),
- NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
+ NULL, zonepath, NULL, NULL)) != 0)
goto next;
}
@@ -755,15 +813,15 @@ next:
*
* Perform the following substitutions as necessary:
*
- * %R Root of zone
+ * %R Zonepath of zone
*/
int
-brand_platform_iter_gmounts(brand_handle_t bh, const char *zoneroot,
+brand_platform_iter_gmounts(brand_handle_t bh, const char *zonepath,
int (*func)(void *, const char *, const char *, const char *,
const char *), void *data)
{
struct brand_handle *bhp = (struct brand_handle *)bh;
- return (i_brand_platform_iter_mounts(bhp, zoneroot, func, data,
+ return (i_brand_platform_iter_mounts(bhp, zonepath, func, data,
DTD_ELEM_GLOBAL_MOUNT));
}
@@ -884,7 +942,7 @@ brand_platform_iter_devices(brand_handle_t bh, const char *zonename,
/* Substitute token values as needed. */
if ((ret = i_substitute_tokens((char *)match,
match_exp, sizeof (match_exp),
- zonename, NULL, NULL, NULL, 0, NULL)) != 0) {
+ zonename, NULL, NULL, NULL)) != 0) {
err = B_TRUE;
goto next;
}
diff --git a/usr/src/lib/libbrand/common/libbrand.h b/usr/src/lib/libbrand/common/libbrand.h
index b0ddbc5143..ef408565a2 100644
--- a/usr/src/lib/libbrand/common/libbrand.h
+++ b/usr/src/lib/libbrand/common/libbrand.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -50,31 +50,45 @@ extern int brand_is_native(brand_handle_t);
extern boolean_t brand_allow_exclusive_ip(brand_handle_t);
+extern int brand_get_attach(brand_handle_t, const char *, const char *,
+ char *, size_t);
extern int brand_get_boot(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_get_brandname(brand_handle_t, char *, size_t);
+extern int brand_get_clone(brand_handle_t, const char *, const char *,
+ char *, size_t);
+extern int brand_get_detach(brand_handle_t, const char *, const char *,
+ char *, size_t);
extern int brand_get_halt(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_get_initname(brand_handle_t, char *, size_t);
extern int brand_get_install(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_get_installopts(brand_handle_t, char *, size_t);
extern int brand_get_login_cmd(brand_handle_t, const char *, char *, size_t);
extern int brand_get_modname(brand_handle_t, char *, size_t);
extern int brand_get_postattach(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_get_postclone(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_get_postinstall(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
+extern int brand_get_postsnap(brand_handle_t, const char *, const char *,
+ char *, size_t);
extern int brand_get_predetach(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
+extern int brand_get_presnap(brand_handle_t, const char *, const char *,
+ char *, size_t);
extern int brand_get_preuninstall(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
+extern int brand_get_uninstall(brand_handle_t, const char *, const char *,
+ char *, size_t);
+extern int brand_get_validatesnap(brand_handle_t, const char *, const char *,
+ char *, size_t);
extern int brand_get_user_cmd(brand_handle_t, const char *, char *, size_t);
extern int brand_get_verify_cfg(brand_handle_t, char *, size_t);
extern int brand_get_verify_adm(brand_handle_t, const char *, const char *,
- char *, size_t, int, char **);
+ char *, size_t);
extern int brand_config_iter_privilege(brand_handle_t,
int (*func)(void *, priv_iter_t *), void *);
diff --git a/usr/src/lib/libbrand/common/mapfile-vers b/usr/src/lib/libbrand/common/mapfile-vers
index 5d9ae97a5b..3879d8a834 100644
--- a/usr/src/lib/libbrand/common/mapfile-vers
+++ b/usr/src/lib/libbrand/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -30,8 +30,11 @@ SUNWprivate {
brand_allow_exclusive_ip;
brand_close;
brand_config_iter_privilege;
+ brand_get_attach;
brand_get_boot;
brand_get_brandname;
+ brand_get_clone;
+ brand_get_detach;
brand_get_halt;
brand_get_initname;
brand_get_install;
@@ -41,9 +44,13 @@ SUNWprivate {
brand_get_postattach;
brand_get_postclone;
brand_get_postinstall;
+ brand_get_postsnap;
brand_get_predetach;
+ brand_get_presnap;
brand_get_preuninstall;
+ brand_get_uninstall;
brand_get_user_cmd;
+ brand_get_validatesnap;
brand_get_verify_adm;
brand_get_verify_cfg;
brand_is_native;
diff --git a/usr/src/lib/libbrand/dtd/brand.dtd.1 b/usr/src/lib/libbrand/dtd/brand.dtd.1
index cf7a84b28f..141ef19db3 100644
--- a/usr/src/lib/libbrand/dtd/brand.dtd.1
+++ b/usr/src/lib/libbrand/dtd/brand.dtd.1
@@ -20,7 +20,7 @@
CDDL HEADER END
- Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2008 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
ident "%Z%%M% %I% %E% SMI"
@@ -54,8 +54,8 @@
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
The program should return 0 on success and non-0 on failure. Any
detailed error messages should be displayed to stderr.
@@ -73,8 +73,8 @@
replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -107,8 +107,8 @@
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -139,8 +139,8 @@
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -201,20 +201,45 @@
<!ATTLIST user_cmd>
<!--
- postattach
+ attach
- Path to a script that will perform any necessary post-processing on
- a zone after it has been attached. The additional argument could be '-F'
- if this is a forced attach.
+ Path to a hook that will perform any necessary processing on
+ a zone to allow it to be attached. The zone will be in the "configured"
+ state when this hook is run. This hook is never called when the zone
+ is "force attached" (-F).
- If this program exits with a non-zero exit status, the attach operation
+ If this hook exits with a non-zero exit status, the attach operation
will fail.
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
+
+ If no hook is provided, the internal zoneadm attach code will be used.
+
+ It has no attributes.
+-->
+<!ELEMENT attach (#PCDATA) >
+<!ATTLIST attach>
+
+<!--
+ postattach
+
+ Path to a hook that will perform any necessary post-processing on
+ a zone after it has been attached. The zone will be in the "installed"
+ state when this hook is run. This hook is never called when the zone
+ is "force attached" (-F).
+
+ If this hook exits with a non-zero exit status, the attach operation
+ will fail and the zone state will be reset to "configured".
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -224,14 +249,19 @@
<!--
postclone
- Path to a script that will perform any necessary post-processing on
- a zone after it has been freshly copied.
+ Path to a hook that will perform any necessary post-processing on
+ a zone after it has been cloned. The zone will be in the "incomplete"
+ state when this hook is run.
+
+ If this hook exits with a non-zero exit status, the clone operation
+ will fail and the zone will be left in the "incomplete" state,
+ otherwise the state will be changed to the "installed" state.
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -249,8 +279,8 @@
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -260,21 +290,26 @@
<!--
predetach
- Path to a script that will perform any necessary pre-processing on
- a zone before it is detached. The additional argument could be '-n'
- if this is a dry-run detach.
+ Path to a hook that will perform any necessary pre-processing on
+ a zone before it is detached. The zone will be in the "installed"
+ state when this hook is run.
It is possible that if the zone fails to detach after invoking this
- program, future attempts to detach the zone will invoke this program again.
- So this program should be designed to gracefully handle the case where
- it is run multiple times on the same zone. If this program exits with
+ hook, future attempts to detach the zone will invoke this hook again.
+ So this hook should be designed to gracefully handle the case where
+ it is run multiple times on the same zone. If this hook exits with
a non-zero exit status, the detach operation will fail.
+ This hook is most commonly used when there is pre-processing for detaching
+ a zone but the built-in detach support will be used for the actual
+ detach. Otherwise, if a detach hook is provided, then it can be used
+ to do both preprocessing as well as the actual detach.
+
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -282,23 +317,74 @@
<!ATTLIST predetach>
<!--
+ detach
+
+ Path to a hook that will perform any necessary processing on
+ a zone to allow it to be detached. The zone will be in the "installed"
+ state when this hook is run.
+
+ It is possible that if the zone fails to detach while running this
+ hook, future attempts to detach the zone will invoke this hook again.
+ So this hook should be designed to gracefully handle the case where
+ it is run multiple times on the same zone. If this hook exits with
+ a non-zero exit status, the detach operation will fail and the zone will
+ be left in the "installed" state, otherwise the state will be changed
+ to "configured".
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
+
+ If no hook is provided, the internal zoneadm detach code will be used.
+
+ It has no attributes.
+-->
+<!ELEMENT detach (#PCDATA) >
+<!ATTLIST detach>
+
+<!--
+ clone
+ Path to a hook that will perform any necessary processing on a zone to
+ allow it to be installed via cloning. Cloning is an alternative to
+ installing so this hook should result in the same effect for the zone.
+ The zone will be in the "incomplete" state when this hook is run.
+
+ If this hook exits with a non-zero exit status, the clone operation
+ will fail and the zone will be left in the "incomplete" state, otherwise
+ the state will be changed to "installed".
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+ 1st arg name of source zone
+ Additional arguments, if any, are appended.
+
+ If no hook is provided, the internal zoneadm cloning code will be used.
+-->
+<!ELEMENT clone (#PCDATA) >
+<!ATTLIST clone>
+
+<!--
preuninstall
Path to a script that will perform any necessary pre-processing on
- a zone before it is uninstalled. The additional argument could be '-F'
- if this is a forced uninstall.
+ a zone before it is uninstalled. The zone will be in the "installed"
+ state when this hook is run.
It is possible that if the zone fails to uninstall after invoking this
- program, future attempts to uninstall the zone will invoke this program
- again. So this program should be designed to gracefully handle the case
- where it is run multiple times on the same zone. If this program exits
+ hook, future attempts to uninstall the zone will invoke this hook
+ again. So this hook should be designed to gracefully handle the case
+ where it is run multiple times on the same zone. If this hook exits
with a non-zero exit status, the uninstall operation will fail.
The following replacements are performed:
%z Name of zone
- %R Root of zone
- %* Additional arguments, if any
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
It has no attributes.
-->
@@ -306,6 +392,79 @@
<!ATTLIST preuninstall>
<!--
+ uninstall
+ Identifies the hook to invoke when uninstalling a zone. The zone will
+ be in the "incomplete" state when this hook is run.
+
+ If this hook exits with a non-zero exit status, the uninstall operation
+ will fail and the zone will be left in the "incomplete" state, otherwise
+ the state will be changed to "configured".
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+ Additional arguments, if any, are appended.
+
+ If no hook is provided, the internal zoneadm uninstall code will be used.
+-->
+<!ELEMENT uninstall (#PCDATA) >
+<!ATTLIST uninstall>
+
+<!--
+ presnap
+ Identifies the hook to invoke before snapshotting a zone using the
+ built-in ZFS clone support.
+
+ If this hook exits with a non-zero exit status, the snapshot operation
+ will fail and the zfs clone operation will fail.
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+-->
+<!ELEMENT presnap (#PCDATA) >
+<!ATTLIST presnap>
+
+<!--
+ postsnap
+ Identifies the hook to invoke after snapshotting a zone using the
+ built-in ZFS clone support.
+
+ If this hook exits with a non-zero exit status, the zfs clone operation
+ will fail.
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+-->
+<!ELEMENT postsnap (#PCDATA) >
+<!ATTLIST postsnap>
+
+<!--
+ validatesnap
+ Identifies the hook to invoke to validate a snapshot of a zone using the
+ built-in ZFS clone support. This will validate a snapshot that was
+ explicitly specified to the clone command when the user wants to
+ re-use a snapshot from an earlier clone operation.
+
+ If this hook exits with a non-zero exit status, the snapshot validation
+ operation will fail, meaning the zfs snapshot cannot be used to install
+ the zone.
+
+ The following replacements are performed:
+
+ %z Name of zone
+ %R Zonepath of zone
+ 1st arg snapshot name
+ 2nd arg snapshot path
+-->
+<!ELEMENT validatesnap (#PCDATA) >
+<!ATTLIST validatesnap>
+
+<!--
privilege
Add a privilege to the default, prohibited, or required set for all
@@ -348,6 +507,8 @@
<!ELEMENT brand (modname?, initname, login_cmd, user_cmd, install,
installopts?, boot?, halt?, verify_cfg?, verify_adm?,
postattach?, postclone?, postinstall?, predetach?,
- preuninstall?, privilege+)>
+ attach?, detach?, clone?,
+ presnap?, postsnap?, validatesnap?,
+ preuninstall?, uninstall?, privilege+)>
<!ATTLIST brand name CDATA #REQUIRED>
diff --git a/usr/src/lib/libzonecfg/Makefile.com b/usr/src/lib/libzonecfg/Makefile.com
index b7999ed78b..f2b2bd03b9 100644
--- a/usr/src/lib/libzonecfg/Makefile.com
+++ b/usr/src/lib/libzonecfg/Makefile.com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -33,7 +33,7 @@ include ../../Makefile.lib
LIBS = $(DYNLIB) $(LINTLIB)
LDLIBS += -lc -lsocket -lnsl -luuid -lnvpair -lsysevent -lsec -lbrand \
- -lpool -lscf -lproc -luutil
+ -lpool -lscf -lproc -luutil -lbsm
# DYNLIB libraries do not have lint libs and are not linted
$(DYNLIB) := LDLIBS += -lxml2
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index e538cc6108..75fb6700c2 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -52,6 +52,8 @@
#include <libproc.h>
#include <sys/priocntl.h>
#include <libuutil.h>
+#include <wait.h>
+#include <bsm/adt.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -138,22 +140,12 @@
#define DETACHED "SUNWdetached.xml"
#define ATTACH_FORCED "SUNWattached.xml"
-#define PKG_PATH "/var/sadm/pkg"
-#define CONTENTS_FILE "/var/sadm/install/contents"
-#define SUNW_PKG_ALL_ZONES "SUNW_PKG_ALLZONES=true\n"
-#define SUNW_PKG_THIS_ZONE "SUNW_PKG_THISZONE=true\n"
-#define VERSION "VERSION="
-#define PATCHLIST "PATCHLIST="
-#define PATCHINFO "PATCH_INFO_"
-#define PKGINFO_RD_LEN 128
#define TMP_POOL_NAME "SUNWtmp_%s"
#define MAX_TMP_POOL_NAME (ZONENAME_MAX + 9)
#define RCAP_SERVICE "system/rcap:default"
#define POOLD_SERVICE "system/pools/dynamic:default"
-enum zn_ipd_fs {ZONE_IPD, ZONE_FS};
-
/*
* rctl alias definitions
*
@@ -216,31 +208,13 @@ struct znotify {
int zn_failure_count;
};
-struct zone_pkginfo {
- boolean_t zpi_all_zones;
- boolean_t zpi_this_zone;
- int zpi_patch_cnt;
- char *zpi_version;
- char **zpi_patchinfo;
-};
-
-typedef struct {
- uu_avl_node_t patch_node;
- char *patch_num;
- char *patch_vers;
- uu_list_t *obs_patches;
-} patch_node_t;
+/* used to track nested zone-lock operations */
+static int zone_lock_cnt = 0;
-typedef struct {
- uu_list_node_t link;
- char *patch_num;
-} obs_patch_node_t;
-
-typedef struct {
- uu_avl_t *obs_patches_avl;
- zone_dochandle_t handle;
- int res;
-} patch_parms_t;
+/* used to communicate lock status to children */
+#define LOCK_ENV_VAR "_ZONEADM_LOCK_HELD"
+static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1";
+static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0";
char *zonecfg_root = "";
@@ -1329,12 +1303,6 @@ zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
if (zonecfg_check_handle(handle) != Z_OK)
return (Z_BAD_HANDLE);
- /*
- * We can only detach if we have taken a sw inventory.
- */
- if (!handle->zone_dh_sw_inv)
- return (Z_INVAL);
-
if (flags & ZONE_DRY_RUN) {
(void) strlcpy(migpath, "-", sizeof (migpath));
} else {
@@ -2468,22 +2436,58 @@ zonecfg_modify_dev(
return (Z_OK);
}
-/* Lock to serialize all zonecfg_devwalks */
+/* Lock to serialize all devwalks */
static pthread_mutex_t zonecfg_devwalk_lock = PTHREAD_MUTEX_INITIALIZER;
/*
- * Global variables used to pass data from zonecfg_devwalk to the nftw
+ * Global variables used to pass data from zonecfg_dev_manifest to the nftw
* call-back (zonecfg_devwalk_cb). g_devwalk_data is really the void*
- * parameter and g_devwalk_cb is really the *cb parameter from zonecfg_devwalk.
+ * parameter and g_devwalk_cb is really the *cb parameter from
+ * zonecfg_dev_manifest.
*/
-static void *g_devwalk_data;
+typedef struct __g_devwalk_data *g_devwalk_data_t;
+static g_devwalk_data_t g_devwalk_data;
static int (*g_devwalk_cb)(const char *, uid_t, gid_t, mode_t, const char *,
void *);
static size_t g_devwalk_skip_prefix;
/*
- * This is the nftw call-back function used by zonecfg_devwalk. It is
- * responsible for calling the actual call-back that is passed in to
- * zonecfg_devwalk as the *cb argument.
+ * zonecfg_dev_manifest call-back function used during detach to generate the
+ * dev info in the manifest.
+ */
+static int
+get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
+ const char *acl, void *hdl)
+{
+ zone_dochandle_t handle = (zone_dochandle_t)hdl;
+ xmlNodePtr newnode;
+ xmlNodePtr cur;
+ int err;
+ char buf[128];
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
+ return (err);
+ (void) snprintf(buf, sizeof (buf), "%lu", uid);
+ if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
+ return (err);
+ (void) snprintf(buf, sizeof (buf), "%lu", gid);
+ if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
+ return (err);
+ (void) snprintf(buf, sizeof (buf), "%o", mode);
+ if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
+ return (err);
+ if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
+ return (err);
+ return (Z_OK);
+}
+
+/*
+ * This is the nftw call-back function used by zonecfg_dev_manifest. It is
+ * responsible for calling the actual call-back.
*/
/* ARGSUSED2 */
static int
@@ -2514,19 +2518,17 @@ zonecfg_devwalk_cb(const char *path, const struct stat *st, int f,
}
/*
- * Walk the dev tree for the zone specified by hdl and call the call-back (cb)
- * function for each entry in the tree. The call-back will be passed the
- * name, uid, gid, mode, acl string and the void *data input parameter
- * for each dev entry.
+ * Walk the dev tree for the zone specified by hdl and call the
+ * get_detach_dev_entry call-back function for each entry in the tree. The
+ * call-back will be passed the name, uid, gid, mode, acl string and the
+ * handle input parameter for each dev entry.
*
- * Data is passed to the zonecfg_devwalk_cb through the global variables
+ * Data is passed to get_detach_dev_entry through the global variables
* g_devwalk_data, *g_devwalk_cb, and g_devwalk_skip_prefix. The
- * zonecfg_devwalk_cb function will actually call *cb.
+ * zonecfg_devwalk_cb function will actually call get_detach_dev_entry.
*/
int
-zonecfg_devwalk(zone_dochandle_t hdl,
- int (*cb)(const char *, uid_t, gid_t, mode_t, const char *, void *),
- void *data)
+zonecfg_dev_manifest(zone_dochandle_t hdl)
{
char path[MAXPATHLEN];
int ret;
@@ -2536,16 +2538,16 @@ zonecfg_devwalk(zone_dochandle_t hdl,
if (strlcat(path, "/dev", sizeof (path)) >= sizeof (path))
return (Z_TOO_BIG);
- g_devwalk_skip_prefix = strlen(path) + 1;
/*
- * We have to serialize all zonecfg_devwalks in the same process
+ * We have to serialize all devwalks in the same process
* (which should be fine), since nftw() is so badly designed.
*/
(void) pthread_mutex_lock(&zonecfg_devwalk_lock);
- g_devwalk_data = data;
- g_devwalk_cb = cb;
+ g_devwalk_skip_prefix = strlen(path) + 1;
+ g_devwalk_data = (g_devwalk_data_t)hdl;
+ g_devwalk_cb = get_detach_dev_entry;
(void) nftw(path, zonecfg_devwalk_cb, 0, FTW_PHYS);
(void) pthread_mutex_unlock(&zonecfg_devwalk_lock);
@@ -3155,6 +3157,64 @@ zonecfg_remove_rctl_value(
return (Z_NO_PROPERTY_ID);
}
+void
+zonecfg_set_swinv(zone_dochandle_t handle)
+{
+ handle->zone_dh_sw_inv = B_TRUE;
+}
+
+/*
+ * Add the pkg to the sw inventory on the handle.
+ */
+int
+zonecfg_add_pkg(zone_dochandle_t handle, char *name, char *version)
+{
+ xmlNodePtr newnode;
+ xmlNodePtr cur;
+ int err;
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
+ return (err);
+ if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
+ return (err);
+ return (Z_OK);
+}
+
+int
+zonecfg_add_patch(zone_dochandle_t handle, char *id, void **pnode)
+{
+ xmlNodePtr node = (xmlNodePtr)*pnode;
+ xmlNodePtr cur;
+ int err;
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ node = xmlNewTextChild(cur, NULL, DTD_ELEM_PATCH, NULL);
+ if ((err = newprop(node, DTD_ATTR_ID, id)) != Z_OK)
+ return (err);
+ *pnode = (void *)node;
+ return (Z_OK);
+}
+
+int
+zonecfg_add_patch_obs(char *id, void *cur)
+{
+ xmlNodePtr node;
+ int err;
+
+ node = xmlNewTextChild((xmlNodePtr)cur, NULL, DTD_ELEM_OBSOLETES, NULL);
+ if ((err = newprop(node, DTD_ATTR_ID, id)) != Z_OK)
+ return (err);
+ return (Z_OK);
+}
+
char *
zonecfg_strerror(int errnum)
{
@@ -6813,1261 +6873,392 @@ zonecfg_enddevperment(zone_dochandle_t handle)
return (zonecfg_endent(handle));
}
-/*
- * Maintain a space separated list of unique pkg names. PATH_MAX is used in
- * the pkg code as the maximum size for a pkg name.
- */
-static int
-add_pkg_to_str(char **str, char *pkg)
+/* PRINTFLIKE1 */
+static void
+zerror(const char *zone_name, const char *fmt, ...)
{
- int len, newlen;
- char tstr[PATH_MAX + 3];
- char *tmp;
-
- len = strlen(pkg);
- if (*str == NULL) {
- /* space for str + 2 spaces + NULL */
- if ((*str = (char *)malloc(len + 3)) == NULL)
- return (Z_NOMEM);
- (void) snprintf(*str, len + 3, " %s ", pkg);
- return (Z_OK);
- }
+ va_list alist;
- (void) snprintf(tstr, sizeof (tstr), " %s ", pkg);
- if (strstr(*str, tstr) != NULL)
- return (Z_OK);
-
- /* space for str + 1 space + NULL */
- newlen = strlen(*str) + len + 2;
- if ((tmp = (char *)realloc(*str, newlen)) == NULL)
- return (Z_NOMEM);
- *str = tmp;
- (void) strlcat(*str, pkg, newlen);
- (void) strlcat(*str, " ", newlen);
- return (Z_OK);
+ va_start(alist, fmt);
+ (void) fprintf(stderr, "zone '%s': ", zone_name);
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
}
-/*
- * Process a list of pkgs from an entry in the contents file, adding each pkg
- * name to the list of pkgs.
- *
- * It is possible for the pkg name to be preceeded by a special character
- * which indicates some bookkeeping information for pkging. Check if the
- * first char is not an Alpha char. If so, skip over it.
- */
-static int
-add_pkg_list(char *lastp, char ***plist, int *pcnt, char **pkg_warn)
+static void
+zperror(const char *str)
{
- char *p;
- int pkg_cnt = *pcnt;
- char **pkgs = *plist;
- int res = Z_OK;
-
- while ((p = strtok_r(NULL, " ", &lastp)) != NULL) {
- char **tmpp;
- int i;
-
- /* skip over any special pkg bookkeeping char */
- if (!isalpha(*p)) {
- p++;
- if ((res = add_pkg_to_str(pkg_warn, p)) != Z_OK)
- break;
- }
-
- /* Check if the pkg is already in the list */
- for (i = 0; i < pkg_cnt; i++) {
- if (strcmp(p, pkgs[i]) == 0)
- break;
- }
-
- if (i < pkg_cnt)
- continue;
-
- /* The pkg is not in the list; add it. */
- if ((tmpp = (char **)realloc(pkgs,
- sizeof (char *) * (pkg_cnt + 1))) == NULL) {
- res = Z_NOMEM;
- break;
- }
- pkgs = tmpp;
-
- if ((pkgs[pkg_cnt] = strdup(p)) == NULL) {
- res = Z_NOMEM;
- break;
- }
- pkg_cnt++;
- }
-
- *plist = pkgs;
- *pcnt = pkg_cnt;
-
- return (res);
+ (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
}
/*
- * Process an entry from the contents file (type "directory"). If the
- * directory path is in the list of ipds and is not under a lofs mount within
- * the ipd then add the associated list of pkgs to the pkg list. The input
- * parameter "entry" will be broken up by the parser within this function so
- * its value will be modified when this function exits.
+ * The following three routines implement a simple locking mechanism to
+ * ensure that only one instance of zoneadm at a time is able to manipulate
+ * a given zone. The lock is built on top of an fcntl(2) lock of
+ * [<altroot>]/var/run/zones/<zonename>.zoneadm.lock. If a zoneadm instance
+ * can grab that lock, it is allowed to manipulate the zone.
+ *
+ * Since zoneadm may call external applications which in turn invoke
+ * zoneadm again, we introduce the notion of "lock inheritance". Any
+ * instance of zoneadm that has another instance in its ancestry is assumed
+ * to be acting on behalf of the original zoneadm, and is thus allowed to
+ * manipulate its zone.
*
- * The entries we are looking for will look something like:
- * /usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
+ * This inheritance is implemented via the _ZONEADM_LOCK_HELD environment
+ * variable. When zoneadm is granted a lock on its zone, this environment
+ * variable is set to 1. When it releases the lock, the variable is set to
+ * 0. Since a child process inherits its parent's environment, checking
+ * the state of this variable indicates whether or not any ancestor owns
+ * the lock.
*/
-static int
-get_path_pkgs(char *entry, char **ipds, char **fss, char ***pkgs, int *pkg_cnt,
- char **pkg_warn)
-{
- char *f1;
- char *f2;
- char *lastp;
- int i;
- char *nlp;
-
- if ((f1 = strtok_r(entry, " ", &lastp)) == NULL ||
- (f2 = strtok_r(NULL, " ", &lastp)) == NULL || strcmp(f2, "d") != 0)
- return (Z_OK);
-
- /* Check if this directory entry is in the list of ipds. */
- for (i = 0; ipds[i] != NULL; i++) {
- char wildcard[MAXPATHLEN];
-
- /*
- * We want to match on the path and any other directory
- * entries under this path. When we use FNM_PATHNAME then
- * that means '/' will not be matched by a wildcard (*) so
- * we omit FNM_PATHNAME on the call with the wildcard matching.
- */
- (void) snprintf(wildcard, sizeof (wildcard), "%s/*", ipds[i]);
- if (fnmatch(ipds[i], f1, FNM_PATHNAME) == 0 ||
- fnmatch(wildcard, f1, 0) == 0) {
- /* It looks like we do want the pkgs for this path. */
- break;
+void
+zonecfg_init_lock_file(const char *zone_name, char **lock_env)
+{
+ *lock_env = getenv(LOCK_ENV_VAR);
+ if (*lock_env == NULL) {
+ if (putenv(zoneadm_lock_not_held) != 0) {
+ zerror(zone_name, gettext("could not set env: %s"),
+ strerror(errno));
+ exit(1);
}
+ } else {
+ if (atoi(*lock_env) == 1)
+ zone_lock_cnt = 1;
}
+}
- /* This entry did not match any of the ipds. */
- if (ipds[i] == NULL)
- return (Z_OK);
-
+void
+zonecfg_release_lock_file(const char *zone_name, int lockfd)
+{
/*
- * Check if there is a fs mounted under the ipd. If so, ignore this
- * entry.
+ * If we are cleaning up from a failed attempt to lock the zone for
+ * the first time, we might have a zone_lock_cnt of 0. In that
+ * error case, we don't want to do anything but close the lock
+ * file.
*/
- for (i = 0; fss[i] != NULL; i++) {
- char wildcard[MAXPATHLEN];
-
- (void) snprintf(wildcard, sizeof (wildcard), "%s/*", fss[i]);
- if (fnmatch(fss[i], f1, FNM_PATHNAME) == 0 ||
- fnmatch(wildcard, f1, 0) == 0) {
- /* We should ignore this path. */
- break;
+ assert(zone_lock_cnt >= 0);
+ if (zone_lock_cnt > 0) {
+ assert(getenv(LOCK_ENV_VAR) != NULL);
+ assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
+ if (--zone_lock_cnt > 0) {
+ assert(lockfd == -1);
+ return;
+ }
+ if (putenv(zoneadm_lock_not_held) != 0) {
+ zerror(zone_name, gettext("could not set env: %s"),
+ strerror(errno));
+ exit(1);
}
}
+ assert(lockfd >= 0);
+ (void) close(lockfd);
+}
- /* If not null, then we matched an fs mount point so ignore entry. */
- if (fss[i] != NULL)
- return (Z_OK);
+int
+zonecfg_grab_lock_file(const char *zone_name, int *lockfd)
+{
+ char pathbuf[PATH_MAX];
+ struct flock flock;
/*
- * We do want the pkgs for this entry. First, skip over the next 4
- * fields in the entry so that we call add_pkg_list starting with the
- * pkg names.
+ * If we already have the lock, we can skip this expensive song
+ * and dance.
*/
- for (i = 0; i < 4 && strtok_r(NULL, " ", &lastp) != NULL; i++)
- ;
- /* If there are < 4 fields this entry is corrupt, just skip it. */
- if (i < 4)
+ assert(zone_lock_cnt >= 0);
+ assert(getenv(LOCK_ENV_VAR) != NULL);
+ if (zone_lock_cnt > 0) {
+ assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
+ zone_lock_cnt++;
+ *lockfd = -1;
return (Z_OK);
-
- /* strip newline from the line */
- nlp = (lastp + strlen(lastp) - 1);
- if (*nlp == '\n')
- *nlp = '\0';
-
- return (add_pkg_list(lastp, pkgs, pkg_cnt, pkg_warn));
-}
-
-/*
- * Read an entry from a pkginfo or contents file. Some of these lines can
- * either be arbitrarily long or be continued by a backslash at the end of
- * the line. This function coalesces lines that are longer than the read
- * buffer, and lines that are continued, into one buffer which is returned.
- * The caller must free this memory. NULL is returned when we hit EOF or
- * if we run out of memory (errno is set to ENOMEM).
- */
-static char *
-read_pkg_data(FILE *fp)
-{
- char *start;
- char *inp;
- char *p;
- int char_cnt = 0;
-
- errno = 0;
- if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
- errno = ENOMEM;
- return (NULL);
}
+ assert(getenv(LOCK_ENV_VAR) != NULL);
+ assert(atoi(getenv(LOCK_ENV_VAR)) == 0);
- inp = start;
- while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
- int len;
-
- len = strlen(inp);
- if (inp[len - 1] == '\n' &&
- (len == 1 || inp[len - 2] != '\\')) {
- char_cnt = len;
- break;
- }
-
- if (inp[len - 2] == '\\')
- char_cnt += len - 2;
- else
- char_cnt += PKGINFO_RD_LEN - 1;
-
- if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
- errno = ENOMEM;
- break;
- }
-
- start = p;
- inp = start + char_cnt;
- }
-
- if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
- free(start);
- start = NULL;
+ if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(),
+ ZONES_TMPDIR) >= sizeof (pathbuf)) {
+ zerror(zone_name, gettext("alternate root path is too long"));
+ return (-1);
}
-
- return (start);
-}
-
-static void
-free_ipd_pkgs(char **pkgs, int cnt)
-{
- int i;
-
- for (i = 0; i < cnt; i++)
- free(pkgs[i]);
- free(pkgs);
-}
-
-/*
- * Get a list of the inherited pkg dirs or fs entries configured for the
- * zone. The type parameter will be either ZONE_IPD or ZONE_FS.
- */
-static int
-get_ipd_fs_list(zone_dochandle_t handle, enum zn_ipd_fs type, char ***list)
-{
- int res;
- struct zone_fstab fstab;
- int cnt = 0;
- char **entries = NULL;
- int i;
- int (*fp)(zone_dochandle_t, struct zone_fstab *);
-
- if (type == ZONE_IPD) {
- fp = zonecfg_getipdent;
- res = zonecfg_setipdent(handle);
- } else {
- fp = zonecfg_getfsent;
- res = zonecfg_setfsent(handle);
+ if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) {
+ zerror(zone_name, gettext("could not mkdir %s: %s"), pathbuf,
+ strerror(errno));
+ return (-1);
}
+ (void) chmod(pathbuf, S_IRWXU);
- if (res != Z_OK)
- return (res);
-
- while (fp(handle, &fstab) == Z_OK) {
- char **p;
-
- if ((p = (char **)realloc(entries,
- sizeof (char *) * (cnt + 1))) == NULL) {
- res = Z_NOMEM;
- break;
- }
- entries = p;
-
- if ((entries[cnt] = strdup(fstab.zone_fs_dir)) == NULL) {
- res = Z_NOMEM;
- break;
- }
-
- cnt++;
+ /*
+ * One of these lock files is created for each zone (when needed).
+ * The lock files are not cleaned up (except on system reboot),
+ * but since there is only one per zone, there is no resource
+ * starvation issue.
+ */
+ if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock",
+ zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) {
+ zerror(zone_name, gettext("alternate root path is too long"));
+ return (-1);
}
-
- if (type == ZONE_IPD)
- (void) zonecfg_endipdent(handle);
- else
- (void) zonecfg_endfsent(handle);
-
- /* Add a NULL terminating element. */
- if (res == Z_OK) {
- char **p;
-
- if ((p = (char **)realloc(entries,
- sizeof (char *) * (cnt + 1))) == NULL) {
- res = Z_NOMEM;
- } else {
- entries = p;
- entries[cnt] = NULL;
- }
+ if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
+ zerror(zone_name, gettext("could not open %s: %s"), pathbuf,
+ strerror(errno));
+ return (-1);
}
-
- if (res != Z_OK) {
- if (entries != NULL) {
- for (i = 0; i < cnt; i++)
- free(entries[i]);
- free(entries);
- }
- return (res);
+ /*
+ * Lock the file to synchronize with other zoneadmds
+ */
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET;
+ flock.l_start = (off_t)0;
+ flock.l_len = (off_t)0;
+ if ((fcntl(*lockfd, F_SETLKW, &flock) < 0) ||
+ (putenv(zoneadm_lock_held) != 0)) {
+ zerror(zone_name, gettext("unable to lock %s: %s"), pathbuf,
+ strerror(errno));
+ zonecfg_release_lock_file(zone_name, *lockfd);
+ return (-1);
}
-
- *list = entries;
+ zone_lock_cnt = 1;
return (Z_OK);
}
-/*
- * Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
- * list of pkgs that deliver into those dirs.
- */
-static int
-get_ipd_pkgs(zone_dochandle_t handle, char ***pkg_list, int *cnt)
-{
- int res;
- char **ipds;
- char **fss;
- int pkg_cnt = 0;
- char **pkgs = NULL;
- int i;
-
- if ((res = get_ipd_fs_list(handle, ZONE_IPD, &ipds)) != Z_OK)
- return (res);
-
- if ((res = get_ipd_fs_list(handle, ZONE_FS, &fss)) != Z_OK) {
- for (i = 0; ipds[i] != NULL; i++)
- free(ipds[i]);
- free(ipds);
- return (res);
- }
-
- /* We only have to process the contents file if we have ipds. */
- if (ipds != NULL) {
- FILE *fp;
-
- if ((fp = fopen(CONTENTS_FILE, "r")) != NULL) {
- char *buf;
- char *pkg_warn = NULL;
-
- while ((buf = read_pkg_data(fp)) != NULL) {
- res = get_path_pkgs(buf, ipds, fss, &pkgs,
- &pkg_cnt, &pkg_warn);
- free(buf);
- if (res != Z_OK)
- break;
- }
-
- (void) fclose(fp);
-
- if (pkg_warn != NULL) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: package operation in progress "
- "on the following packages:\n %s\n"),
- pkg_warn);
- free(pkg_warn);
- }
- }
- }
-
- for (i = 0; ipds[i] != NULL; i++)
- free(ipds[i]);
- free(ipds);
-
- for (i = 0; fss[i] != NULL; i++)
- free(fss[i]);
- free(fss);
-
- if (res != Z_OK) {
- free_ipd_pkgs(pkgs, pkg_cnt);
- } else {
- *pkg_list = pkgs;
- *cnt = pkg_cnt;
- }
-
- return (res);
-}
-
-/*
- * Return true if pkg_name is in the list of pkgs that deliver into an
- * inherited pkg directory for the zone.
- */
static boolean_t
-dir_pkg(char *pkg_name, char **pkg_list, int cnt)
+get_doorname(const char *zone_name, char *buffer)
{
- int i;
-
- for (i = 0; i < cnt; i++) {
- if (strcmp(pkg_name, pkg_list[i]) == 0)
- return (B_TRUE);
- }
-
- return (B_FALSE);
+ return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH,
+ zonecfg_get_root(), zone_name) < PATH_MAX);
}
/*
- * Keep track of obsoleted patches for this specific patch. We don't need to
- * keep track of the patch version since once a patch is obsoleted, all prior
- * versions are also obsolete and there won't be any new versions.
+ * system daemons are not audited. For the global zone, this occurs
+ * "naturally" since init is started with the default audit
+ * characteristics. Since zoneadmd is a system daemon and it starts
+ * init for a zone, it is necessary to clear out the audit
+ * characteristics inherited from whomever started zoneadmd. This is
+ * indicated by the audit id, which is set from the ruid parameter of
+ * adt_set_user(), below.
*/
-static int
-add_obs_patch(patch_node_t *patch, char *num, uu_list_pool_t *patches_pool)
-{
- obs_patch_node_t *obs;
-
- if (patch->obs_patches == NULL) {
- if ((patch->obs_patches = uu_list_create(patches_pool, NULL,
- 0)) == NULL)
- return (Z_NOMEM);
- }
-
- if ((obs = (obs_patch_node_t *)malloc(sizeof (obs_patch_node_t)))
- == NULL)
- return (Z_NOMEM);
- if ((obs->patch_num = strdup(num)) == NULL) {
- free(obs);
- return (Z_NOMEM);
- }
-
- uu_list_node_init(obs, &obs->link, patches_pool);
- (void) uu_list_insert_before(patch->obs_patches, NULL, obs);
-
- return (Z_OK);
-}
-
-/*
- * Keep track of obsoleted patches. We don't need to keep track of the patch
- * version since once a patch is obsoleted, all prior versions are also
- * obsolete and there won't be any new versions.
- */
-static int
-save_obs_patch(char *num, uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches)
+static void
+prepare_audit_context(const char *zone_name)
{
- patch_node_t *patch;
- uu_avl_index_t where;
-
- if ((patch = (patch_node_t *)malloc(sizeof (patch_node_t))) == NULL)
- return (Z_NOMEM);
-
- if ((patch->patch_num = strdup(num)) == NULL) {
- free(patch);
- return (Z_NOMEM);
- }
-
- patch->patch_vers = NULL;
- patch->obs_patches = NULL;
+ adt_session_data_t *ah;
+ char *failure = gettext("audit failure: %s");
- uu_avl_node_init(patch, &patch->patch_node, patches_pool);
-
- if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL) {
- free(patch->patch_num);
- free(patch);
- return (Z_OK);
+ if (adt_start_session(&ah, NULL, 0)) {
+ zerror(zone_name, failure, strerror(errno));
+ return;
}
-
- uu_avl_insert(obs_patches, patch, where);
- return (Z_OK);
-}
-
-/*
- * Keep a list of patches for a pkg. If we see a newer version of a patch,
- * we only keep track of the newer version.
- */
-static boolean_t
-save_patch(patch_node_t *patch, uu_avl_t *patches_avl)
-{
- patch_node_t *existing;
- uu_avl_index_t where;
-
- /*
- * Check if this is a newer version of a patch we already have.
- * If it is an older version of a patch we already have, ignore it.
- */
- if ((existing = (patch_node_t *)uu_avl_find(patches_avl, patch, NULL,
- &where)) != NULL) {
- char *endptr;
- ulong_t pvers, evers;
-
- pvers = strtoul(patch->patch_vers, &endptr, 10);
- evers = strtoul(existing->patch_vers, &endptr, 10);
-
- if (pvers <= evers)
- return (B_FALSE);
-
- /*
- * Remove the lower version patch from the tree so we can
- * insert the new higher version one. We also discard the
- * obsolete patch list from the old version since the new
- * version will have its own, likely different, list.
- */
- uu_avl_remove(patches_avl, existing);
- free(existing->patch_num);
- free(existing->patch_vers);
- if (existing->obs_patches != NULL) {
- obs_patch_node_t *op;
- void *cookie2 = NULL;
-
- while ((op = uu_list_teardown(existing->obs_patches,
- &cookie2)) != NULL) {
- free(op->patch_num);
- free(op);
- }
- uu_list_destroy(existing->obs_patches);
- }
- free(existing);
-
- /*
- * Now that the old one is gone, find the new location
- * in the tree.
- */
- (void) uu_avl_find(patches_avl, patch, NULL, &where);
+ if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT,
+ ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) {
+ zerror(zone_name, failure, strerror(errno));
+ (void) adt_end_session(ah);
+ return;
}
+ if (adt_set_proc(ah))
+ zerror(zone_name, failure, strerror(errno));
- uu_avl_insert(patches_avl, patch, where);
- return (B_TRUE);
-}
-
-/*
- * Check if a patch is on the list of obsoleted patches. We don't need to
- * check the patch version since once a patch is obsoleted, all prior versions
- * are also obsolete and there won't be any new versions.
- */
-static boolean_t
-obsolete_patch(patch_node_t *patch, uu_avl_t *obs_patches)
-{
- uu_avl_index_t where;
-
- if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL)
- return (B_TRUE);
-
- return (B_FALSE);
+ (void) adt_end_session(ah);
}
-/* ARGSUSED */
static int
-patch_node_compare(const void *l_arg, const void *r_arg, void *private)
+start_zoneadmd(const char *zone_name, boolean_t lock)
{
- patch_node_t *l = (patch_node_t *)l_arg;
- patch_node_t *r = (patch_node_t *)r_arg;
- char *endptr;
- ulong_t lnum, rnum;
+ char doorpath[PATH_MAX];
+ pid_t child_pid;
+ int error = -1;
+ int doorfd, lockfd;
+ struct door_info info;
- lnum = strtoul(l->patch_num, &endptr, 10);
- rnum = strtoul(r->patch_num, &endptr, 10);
-
- if (lnum > rnum)
- return (1);
- if (lnum < rnum)
+ if (!get_doorname(zone_name, doorpath))
return (-1);
- return (0);
-}
-
-/*
- * Parse the patchinfo string for the patch.
- *
- * We are parsing entries of the form:
- * PATCH_INFO_121454-02=Installed: Wed Dec 7 07:13:51 PST 2005 From: mum \
- * Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
- * 119255-06 Incompatibles:
- *
- * A backed out patch will have "backed out\n" as the status. We should
- * skip these patches. We also ignore any entries that seem to be
- * corrupted. Obsolete patches are saved in the obs_patches parameter
- * AVL list.
- */
-static int
-parse_info(char *patchinfo, uu_avl_pool_t *patches_pool, uu_avl_t *patches_avl,
- uu_avl_t *obs_patches, uu_list_pool_t *list_pool)
-{
- char *p;
- char *lastp;
- char *ep;
- char *pvers;
- boolean_t add_info = B_FALSE;
- patch_node_t *patch;
- if (strlen(patchinfo) < (sizeof (PATCHINFO) - 1))
- return (Z_OK);
-
- /* Skip over "PATCH_INFO_" to get the patch id. */
- p = patchinfo + sizeof (PATCHINFO) - 1;
- if ((ep = strchr(p, '=')) == NULL)
- return (Z_OK);
-
- *ep++ = '\0';
-
- /* Ignore all but installed patches. */
- if (strncmp(ep, "Installed:", 10) != 0)
- return (Z_OK);
-
- /* remove newline */
- lastp = (ep + strlen(ep) - 1);
- if (*lastp == '\n')
- *lastp = '\0';
-
- if ((patch = (patch_node_t *)malloc(sizeof (patch_node_t))) == NULL)
- return (Z_NOMEM);
-
- if ((pvers = strchr(p, '-')) != NULL)
- *pvers++ = '\0';
- else
- pvers = "";
-
- if ((patch->patch_num = strdup(p)) == NULL) {
- free(patch);
- return (Z_NOMEM);
- }
- if ((patch->patch_vers = strdup(pvers)) == NULL) {
- free(patch->patch_num);
- free(patch);
- return (Z_NOMEM);
- }
- patch->obs_patches = NULL;
-
- uu_avl_node_init(patch, &patch->patch_node, patches_pool);
- if (!save_patch(patch, patches_avl)) {
- free(patch->patch_num);
- free(patch->patch_vers);
- assert(patch->obs_patches == NULL);
- free(patch);
- return (Z_OK);
- }
+ if (lock)
+ if (zonecfg_grab_lock_file(zone_name, &lockfd) != Z_OK)
+ return (-1);
/*
- * Start with the first token. This will probably be "Installed:".
- * If we can't tokenize this entry, just return.
+ * Now that we have the lock, re-confirm that the daemon is
+ * *not* up and working fine. If it is still down, we have a green
+ * light to start it.
*/
- if ((p = strtok_r(ep, " ", &lastp)) == NULL)
- return (Z_OK);
-
- do {
- if (strcmp(p, "Installed:") == 0 ||
- strcmp(p, "Requires:") == 0 ||
- strcmp(p, "From:") == 0 ||
- strcmp(p, "Incompatibles:") == 0) {
- add_info = B_FALSE;
- continue;
- } else if (strcmp(p, "Obsoletes:") == 0) {
- add_info = B_TRUE;
- continue;
+ if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
+ if (errno != ENOENT) {
+ zperror(doorpath);
+ goto out;
}
-
- if (!add_info)
- continue;
-
- if ((pvers = strchr(p, '-')) != NULL)
- *pvers = '\0';
-
- /*
- * We save all of the obsolete patches in one big list in the
- * obs_patches AVL tree so that we know not to output those as
- * part of the sw dependencies. However, we also need to save
- * the obsolete patch information for this sepcific patch so
- * so that we can do the cross manifest patch checking
- * correctly.
- */
- if (save_obs_patch(p, patches_pool, obs_patches) != Z_OK)
- return (Z_NOMEM);
- if (add_obs_patch(patch, p, list_pool) != Z_OK)
- return (Z_NOMEM);
- } while ((p = strtok_r(NULL, " ", &lastp)) != NULL);
-
- return (Z_OK);
-}
-
-/*
- * AVL walker callback used to add patch to XML manifest.
- *
- * PATH_MAX is used in the pkg/patch code as the maximum size for the patch
- * number/version string.
- */
-static int
-add_patch(void *e, void *p)
-{
- xmlNodePtr node;
- xmlNodePtr cur;
- char id[PATH_MAX];
- patch_node_t *patch;
- patch_parms_t *args;
-
- patch = e;
- args = p;
-
- /* skip this patch if it has been obsoleted */
- if (obsolete_patch(patch, args->obs_patches_avl))
- return (UU_WALK_NEXT);
-
- if (patch->patch_vers[0] == '\0')
- (void) snprintf(id, sizeof (id), "%s", patch->patch_num);
- else
- (void) snprintf(id, sizeof (id), "%s-%s", patch->patch_num,
- patch->patch_vers);
-
- if ((args->res = operation_prep(args->handle)) != Z_OK)
- return (UU_WALK_DONE);
-
- cur = args->handle->zone_dh_cur;
- node = xmlNewTextChild(cur, NULL, DTD_ELEM_PATCH, NULL);
- if ((args->res = newprop(node, DTD_ATTR_ID, id)) != Z_OK)
- return (UU_WALK_DONE);
-
- if (patch->obs_patches != NULL) {
- obs_patch_node_t *op;
- xmlNodePtr node2;
-
- for (op = uu_list_first(patch->obs_patches); op != NULL;
- op = uu_list_next(patch->obs_patches, op)) {
- (void) snprintf(id, sizeof (id), "%s", op->patch_num);
- node2 = xmlNewTextChild(node, NULL, DTD_ELEM_OBSOLETES,
- NULL);
- if ((args->res = newprop(node2, DTD_ATTR_ID, id))
- != Z_OK)
- return (UU_WALK_DONE);
+ } else {
+ if (door_info(doorfd, &info) == 0 &&
+ ((info.di_attributes & DOOR_REVOKED) == 0)) {
+ error = Z_OK;
+ (void) close(doorfd);
+ goto out;
}
+ (void) close(doorfd);
}
- return (UU_WALK_NEXT);
-}
-
-static void
-patch_avl_delete(uu_avl_t *patches_avl)
-{
- if (patches_avl != NULL) {
- patch_node_t *p;
- void *cookie = NULL;
-
- while ((p = (patch_node_t *)uu_avl_teardown(patches_avl,
- &cookie)) != NULL) {
- free(p->patch_num);
- free(p->patch_vers);
-
- if (p->obs_patches != NULL) {
- obs_patch_node_t *op;
- void *cookie2 = NULL;
-
- while ((op = uu_list_teardown(p->obs_patches,
- &cookie2)) != NULL) {
- free(op->patch_num);
- free(op);
- }
- uu_list_destroy(p->obs_patches);
- }
-
- free(p);
- }
-
- uu_avl_destroy(patches_avl);
+ if ((child_pid = fork()) == -1) {
+ zperror(gettext("could not fork"));
+ goto out;
}
-}
-/*
- * Add the unique, highest version patches that are associated with this pkg
- * to the sw inventory on the handle.
- */
-static int
-add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop,
- uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches,
- uu_list_pool_t *list_pool)
-{
- int i;
- int res;
- uu_avl_t *patches_avl;
- patch_parms_t args;
+ if (child_pid == 0) {
+ const char *argv[6], **ap;
- if ((patches_avl = uu_avl_create(patches_pool, NULL, UU_DEFAULT))
- == NULL)
- return (Z_NOMEM);
+ /* child process */
+ prepare_audit_context(zone_name);
- for (i = 0; i < infop->zpi_patch_cnt; i++) {
- if ((res = parse_info(infop->zpi_patchinfo[i], patches_pool,
- patches_avl, obs_patches, list_pool)) != Z_OK) {
- patch_avl_delete(patches_avl);
- return (res);
+ ap = argv;
+ *ap++ = "zoneadmd";
+ *ap++ = "-z";
+ *ap++ = zone_name;
+ if (zonecfg_in_alt_root()) {
+ *ap++ = "-R";
+ *ap++ = zonecfg_get_root();
}
- }
-
- args.obs_patches_avl = obs_patches;
- args.handle = handle;
- args.res = Z_OK;
-
- (void) uu_avl_walk(patches_avl, add_patch, &args, 0);
-
- patch_avl_delete(patches_avl);
- return (args.res);
-}
-
-/*
- * Keep track of the pkgs we have already processed so that we can quickly
- * skip those pkgs while recursively doing dependents.
- */
-static boolean_t
-pkg_in_manifest(uu_avl_t *saw_pkgs, char *pname, uu_avl_pool_t *pkgs_pool)
-{
- uu_avl_index_t where;
-
- if (uu_avl_find(saw_pkgs, pname, NULL, &where) == NULL) {
- zone_pkg_entry_t *pkg;
+ *ap = NULL;
+ (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv);
/*
- * We need to add it. If we don't have memory we just skip
- * this pkg since this routine improves performance but the
- * algorithm is still correct without it.
+ * TRANSLATION_NOTE
+ * zoneadmd is a literal that should not be translated.
*/
- if ((pkg = (zone_pkg_entry_t *)
- malloc(sizeof (zone_pkg_entry_t))) == NULL)
- return (B_FALSE);
-
- if ((pkg->zpe_name = strdup(pname)) == NULL) {
- free(pkg);
- return (B_FALSE);
+ zperror(gettext("could not exec zoneadmd"));
+ _exit(1);
+ } else {
+ /* parent process */
+ pid_t retval;
+ int pstatus = 0;
+
+ do {
+ retval = waitpid(child_pid, &pstatus, 0);
+ } while (retval != child_pid);
+ if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) &&
+ WEXITSTATUS(pstatus) != 0)) {
+ zerror(zone_name, gettext("could not start %s"),
+ "zoneadmd");
+ goto out;
}
-
- pkg->zpe_vers = NULL;
- pkg->zpe_patches_avl = NULL;
-
- /* Insert pkg into the AVL tree. */
- uu_avl_node_init(pkg, &pkg->zpe_entry, pkgs_pool);
- uu_avl_insert(saw_pkgs, pkg, where);
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-/*
- * Add the pkg to the sw inventory on the handle.
- */
-static int
-add_pkg(zone_dochandle_t handle, char *name, char *version)
-{
- xmlNodePtr newnode;
- xmlNodePtr cur;
- int err;
-
- if ((err = operation_prep(handle)) != Z_OK)
- return (err);
-
- cur = handle->zone_dh_cur;
- newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
- if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
- return (err);
- if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
- return (err);
- return (Z_OK);
-}
-
-static void
-free_pkginfo(struct zone_pkginfo *infop)
-{
- free(infop->zpi_version);
- if (infop->zpi_patch_cnt > 0) {
- int i;
-
- for (i = 0; i < infop->zpi_patch_cnt; i++)
- free(infop->zpi_patchinfo[i]);
- free(infop->zpi_patchinfo);
}
+ error = Z_OK;
+out:
+ if (lock)
+ zonecfg_release_lock_file(zone_name, lockfd);
+ return (error);
}
-/*
- * Read the pkginfo file and populate the structure with the data we need
- * from this pkg for a sw inventory.
- */
-static int
-get_pkginfo(char *pkginfo, struct zone_pkginfo *infop)
+int
+zonecfg_ping_zoneadmd(const char *zone_name)
{
- FILE *fp;
- char *buf;
- int err = 0;
-
- infop->zpi_all_zones = B_FALSE;
- infop->zpi_this_zone = B_FALSE;
- infop->zpi_version = NULL;
- infop->zpi_patch_cnt = 0;
- infop->zpi_patchinfo = NULL;
-
- if ((fp = fopen(pkginfo, "r")) == NULL)
- return (errno);
-
- while ((buf = read_pkg_data(fp)) != NULL) {
- if (strncmp(buf, VERSION, sizeof (VERSION) - 1) == 0) {
- int len;
-
- if ((infop->zpi_version =
- strdup(buf + sizeof (VERSION) - 1)) == NULL) {
- err = ENOMEM;
- break;
- }
-
- /* remove trailing newline */
- len = strlen(infop->zpi_version);
- *(infop->zpi_version + len - 1) = 0;
+ char doorpath[PATH_MAX];
+ int doorfd;
+ struct door_info info;
- } else if (strcmp(buf, SUNW_PKG_ALL_ZONES) == 0) {
- infop->zpi_all_zones = B_TRUE;
-
- } else if (strcmp(buf, SUNW_PKG_THIS_ZONE) == 0) {
- infop->zpi_this_zone = B_TRUE;
-
- } else if (strncmp(buf, PATCHINFO, sizeof (PATCHINFO) - 1)
- == 0) {
- char **p;
-
- if ((p = (char **)realloc(infop->zpi_patchinfo,
- sizeof (char *) * (infop->zpi_patch_cnt + 1)))
- == NULL) {
- err = ENOMEM;
- break;
- }
- infop->zpi_patchinfo = p;
-
- if ((infop->zpi_patchinfo[infop->zpi_patch_cnt] =
- strdup(buf)) == NULL) {
- err = ENOMEM;
- break;
- }
- infop->zpi_patch_cnt++;
- }
-
- free(buf);
- }
-
- free(buf);
+ if (!get_doorname(zone_name, doorpath))
+ return (-1);
- if (errno == ENOMEM) {
- err = ENOMEM;
- /* Clean up anything we did manage to allocate. */
- free_pkginfo(infop);
+ if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
+ return (-1);
}
-
- (void) fclose(fp);
-
- return (err);
-}
-
-/*
- * Add any dependent pkgs to the list. The pkg depend file lists pkg
- * dependencies, one per line with an entry that looks like:
- * P SUNWcar Core Architecture, (Root)
- * See the depend(4) man page.
- */
-static int
-add_dependents(zone_dochandle_t handle, char *pname,
- uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches,
- uu_list_pool_t *list_pool, uu_avl_t *saw_pkgs, uu_avl_pool_t *pkgs_pool)
-{
- int res = Z_OK;
- FILE *fp;
- char depend[MAXPATHLEN];
- char *buf;
- struct stat sbuf;
-
- (void) snprintf(depend, sizeof (depend), "%s/%s/install/depend",
- PKG_PATH, pname);
-
- if (stat(depend, &sbuf) == -1 || !S_ISREG(sbuf.st_mode))
+ if (door_info(doorfd, &info) == 0 &&
+ ((info.di_attributes & DOOR_REVOKED) == 0)) {
+ (void) close(doorfd);
return (Z_OK);
-
- if ((fp = fopen(depend, "r")) == NULL)
- return (Z_OK);
-
- while ((buf = read_pkg_data(fp)) != NULL) {
- char *deppkg;
- char *delims = " \t";
- char pkginfo[MAXPATHLEN];
- struct zone_pkginfo info;
-
- if (*buf != 'P') {
- free(buf);
- continue;
- }
-
- /* Skip past the leading 'P '. */
- if ((deppkg = strtok(buf + 2, delims)) == NULL) {
- free(buf);
- continue;
- }
-
- /* If the pkg is already in the manifest don't add it again. */
- if (pkg_in_manifest(saw_pkgs, deppkg, pkgs_pool)) {
- free(buf);
- continue;
- }
-
- (void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
- PKG_PATH, deppkg);
-
- if (stat(pkginfo, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)) {
- free(buf);
- continue;
- }
-
- if (get_pkginfo(pkginfo, &info) != 0) {
- res = Z_NOMEM;
- free(buf);
- break;
- }
-
- if ((res = add_dependents(handle, deppkg, patches_pool,
- obs_patches, list_pool, saw_pkgs, pkgs_pool)) == Z_OK &&
- (res = add_pkg(handle, deppkg, info.zpi_version)) == Z_OK) {
- if (info.zpi_patch_cnt > 0)
- res = add_patches(handle, &info, patches_pool,
- obs_patches, list_pool);
- }
-
- free(buf);
- free_pkginfo(&info);
-
- if (res != Z_OK)
- break;
}
-
- (void) fclose(fp);
- return (res);
+ (void) close(doorfd);
+ return (-1);
}
-/* ARGSUSED */
-static int
-pkg_entry_compare(const void *l_arg, const void *r_arg, void *private)
-{
- zone_pkg_entry_t *pkg = (zone_pkg_entry_t *)l_arg;
- char *name = (char *)r_arg;
-
- return (strcmp(pkg->zpe_name, name));
-}
-
-static void
-pkg_avl_delete(uu_avl_t *pavl)
+int
+zonecfg_call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg, char *locale,
+ boolean_t lock)
{
- if (pavl != NULL) {
- zone_pkg_entry_t *p;
- void *cookie = NULL;
+ char doorpath[PATH_MAX];
+ int doorfd, result;
+ door_arg_t darg;
- while ((p = uu_avl_teardown(pavl, &cookie)) != NULL) {
- free(p->zpe_name);
- free(p);
- }
+ zoneid_t zoneid;
+ uint64_t uniqid = 0;
- uu_avl_destroy(pavl);
- }
-}
+ zone_cmd_rval_t *rvalp;
+ size_t rlen;
+ char *cp, *errbuf;
-/*
- * Take a software inventory of the global zone. We need to get the set of
- * packages and patches that are on the global zone that the specified
- * non-global zone depends on. The packages we need in the inventory are:
- *
- * - skip the package if SUNW_PKG_THISZONE is 'true'
- * otherwise,
- * - add the package if
- * a) SUNW_PKG_ALLZONES is 'true',
- * or
- * b) any file delivered by the package is in a file system that is inherited
- * from the global zone.
- * If the zone does not inherit any file systems (whole root)
- * then (b) will be skipped.
- *
- * For each of the packages that is being added to the inventory, we will also
- * add its dependent packages to the inventory.
- *
- * For each of the packages that is being added to the inventory, we will also
- * add all of the associated, unique patches to the inventory.
- *
- * See the comment for zonecfg_getpkgdata() for compatability restrictions on
- * how we must save the XML representation of the software inventory.
- */
-static int
-zonecfg_sw_inventory(zone_dochandle_t handle)
-{
- char pkginfo[MAXPATHLEN];
- int res;
- struct dirent *dp;
- DIR *dirp;
- struct stat buf;
- struct zone_pkginfo info;
- int pkg_cnt = 0;
- char **pkgs = NULL;
- uu_avl_pool_t *pkgs_pool = NULL;
- uu_avl_pool_t *patches_pool = NULL;
- uu_list_pool_t *list_pool = NULL;
- uu_avl_t *saw_pkgs = NULL;
- uu_avl_t *obs_patches = NULL;
-
- if ((pkgs_pool = uu_avl_pool_create("pkgs_pool",
- sizeof (zone_pkg_entry_t), offsetof(zone_pkg_entry_t, zpe_entry),
- pkg_entry_compare, UU_DEFAULT)) == NULL) {
- res = Z_NOMEM;
- goto done;
- }
-
- if ((saw_pkgs = uu_avl_create(pkgs_pool, NULL, UU_DEFAULT)) == NULL) {
- res = Z_NOMEM;
- goto done;
+ rlen = getpagesize();
+ if ((rvalp = malloc(rlen)) == NULL) {
+ zerror(zone_name, gettext("failed to allocate %lu bytes: %s"),
+ rlen, strerror(errno));
+ return (-1);
}
- if ((patches_pool = uu_avl_pool_create("patches_pool",
- sizeof (patch_node_t), offsetof(patch_node_t, patch_node),
- patch_node_compare, UU_DEFAULT)) == NULL) {
- res = Z_NOMEM;
- goto done;
+ if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) {
+ (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid,
+ sizeof (uniqid));
}
-
- if ((list_pool = uu_list_pool_create("list_pool",
- sizeof (obs_patch_node_t), offsetof(obs_patch_node_t, link), NULL,
- UU_DEFAULT)) == NULL) {
- res = Z_NOMEM;
- goto done;
+ arg->uniqid = uniqid;
+ (void) strlcpy(arg->locale, locale, sizeof (arg->locale));
+ if (!get_doorname(zone_name, doorpath)) {
+ zerror(zone_name, gettext("alternate root path is too long"));
+ free(rvalp);
+ return (-1);
}
/*
- * The obs_patches AVL tree saves all of the obsolete patches so
- * that we know not to output those as part of the sw dependencies.
+ * Loop trying to start zoneadmd; if something goes seriously
+ * wrong we break out and fail.
*/
- if ((obs_patches = uu_avl_create(patches_pool, NULL, UU_DEFAULT))
- == NULL) {
- res = Z_NOMEM;
- goto done;
- }
-
- if ((res = get_ipd_pkgs(handle, &pkgs, &pkg_cnt)) != Z_OK) {
- res = Z_NOMEM;
- goto done;
- }
-
- if ((dirp = opendir(PKG_PATH)) == NULL) {
- res = Z_NOMEM;
- goto done;
- }
-
- while ((dp = readdir(dirp)) != (struct dirent *)0) {
- if (strcmp(dp->d_name, ".") == 0 ||
- strcmp(dp->d_name, "..") == 0)
- continue;
-
- (void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
- PKG_PATH, dp->d_name);
-
- if (stat(pkginfo, &buf) == -1 || !S_ISREG(buf.st_mode))
- continue;
+ for (;;) {
+ if (start_zoneadmd(zone_name, lock) != Z_OK)
+ break;
- if (get_pkginfo(pkginfo, &info) != 0) {
- res = Z_NOMEM;
+ if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
+ zperror(gettext("failed to open zone door"));
break;
}
- if (!info.zpi_this_zone &&
- (info.zpi_all_zones ||
- dir_pkg(dp->d_name, pkgs, pkg_cnt)) &&
- !pkg_in_manifest(saw_pkgs, dp->d_name, pkgs_pool)) {
+ darg.data_ptr = (char *)arg;
+ darg.data_size = sizeof (*arg);
+ darg.desc_ptr = NULL;
+ darg.desc_num = 0;
+ darg.rbuf = (char *)rvalp;
+ darg.rsize = rlen;
+ if (door_call(doorfd, &darg) != 0) {
+ (void) close(doorfd);
/*
- * Add dependents first so any patches will get
- * associated with the right pkg in the xml file.
+ * We'll get EBADF if the door has been revoked.
*/
- if ((res = add_dependents(handle, dp->d_name,
- patches_pool, obs_patches, list_pool, saw_pkgs,
- pkgs_pool)) == Z_OK &&
- (res = add_pkg(handle, dp->d_name,
- info.zpi_version)) == Z_OK) {
- if (info.zpi_patch_cnt > 0)
- res = add_patches(handle, &info,
- patches_pool, obs_patches,
- list_pool);
+ if (errno != EBADF) {
+ zperror(gettext("door_call failed"));
+ break;
}
+ continue; /* take another lap */
}
+ (void) close(doorfd);
- free_pkginfo(&info);
+ if (darg.data_size == 0) {
+ /* Door server is going away; kick it again. */
+ continue;
+ }
- if (res != Z_OK)
- break;
+ errbuf = rvalp->errbuf;
+ while (*errbuf != '\0') {
+ /*
+ * Remove any newlines since zerror()
+ * will append one automatically.
+ */
+ cp = strchr(errbuf, '\n');
+ if (cp != NULL)
+ *cp = '\0';
+ zerror(zone_name, "%s", errbuf);
+ if (cp == NULL)
+ break;
+ errbuf = cp + 1;
+ }
+ result = rvalp->rval == 0 ? 0 : -1;
+ free(rvalp);
+ return (result);
}
- (void) closedir(dirp);
-
-done:
- pkg_avl_delete(saw_pkgs);
- patch_avl_delete(obs_patches);
- if (pkgs_pool != NULL)
- uu_avl_pool_destroy(pkgs_pool);
- if (patches_pool != NULL)
- uu_avl_pool_destroy(patches_pool);
- if (list_pool != NULL)
- uu_list_pool_destroy(list_pool);
- free_ipd_pkgs(pkgs, pkg_cnt);
-
- if (res == Z_OK)
- handle->zone_dh_sw_inv = B_TRUE;
-
- return (res);
-}
-
-/*
- * zonecfg_devwalk call-back function used during detach to generate the
- * dev info in the manifest.
- */
-static int
-get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
- const char *acl, void *hdl)
-{
- zone_dochandle_t handle = (zone_dochandle_t)hdl;
- xmlNodePtr newnode;
- xmlNodePtr cur;
- int err;
- char buf[128];
-
- if ((err = operation_prep(handle)) != Z_OK)
- return (err);
-
- cur = handle->zone_dh_cur;
- newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
- if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
- return (err);
- (void) snprintf(buf, sizeof (buf), "%lu", uid);
- if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
- return (err);
- (void) snprintf(buf, sizeof (buf), "%lu", gid);
- if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
- return (err);
- (void) snprintf(buf, sizeof (buf), "%o", mode);
- if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
- return (err);
- if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
- return (err);
- return (Z_OK);
-}
-
-/*
- * Get the information required to support detaching a zone. This is
- * called on the source system when detaching (the detaching parameter should
- * be set to true) and on the destination system before attaching (the
- * detaching parameter should be false).
- *
- * For native Solaris zones, the detach/attach process involves validating
- * that the software on the global zone can support the zone when we attach.
- * To do this we take a software inventory of the global zone. We also
- * have to keep track of the device configuration so that we can properly
- * recreate it on the destination.
- */
-int
-zonecfg_get_detach_info(zone_dochandle_t handle, boolean_t detaching)
-{
- int res;
-
- if ((res = zonecfg_sw_inventory(handle)) != Z_OK)
- return (res);
-
- if (detaching)
- res = zonecfg_devwalk(handle, get_detach_dev_entry, handle);
-
- return (res);
+ free(rvalp);
+ return (-1);
}
diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers
index e36565f19d..e4572f3374 100644
--- a/usr/src/lib/libzonecfg/common/mapfile-vers
+++ b/usr/src/lib/libzonecfg/common/mapfile-vers
@@ -40,6 +40,9 @@ SUNWprivate_1.1 {
zonecfg_add_fs_option;
zonecfg_add_ipd;
zonecfg_add_nwif;
+ zonecfg_add_patch;
+ zonecfg_add_patch_obs;
+ zonecfg_add_pkg;
zonecfg_add_pset;
zonecfg_add_rctl;
zonecfg_add_rctl_value;
@@ -49,6 +52,7 @@ SUNWprivate_1.1 {
zonecfg_attach_manifest;
zonecfg_bind_pool;
zonecfg_bind_tmp_pool;
+ zonecfg_call_zoneadmd;
zonecfg_check_handle;
zonecfg_close_scratch;
zonecfg_construct_rctlblk;
@@ -71,7 +75,7 @@ SUNWprivate_1.1 {
zonecfg_detached;
zonecfg_detach_save;
zonecfg_devperms_apply;
- zonecfg_devwalk;
+ zonecfg_dev_manifest;
zonecfg_enable_rcapd;
zonecfg_endattrent;
zonecfg_enddevent;
@@ -96,7 +100,6 @@ SUNWprivate_1.1 {
zonecfg_get_autoboot;
zonecfg_get_bootargs;
zonecfg_get_brand;
- zonecfg_get_detach_info;
zonecfg_get_dflt_sched_class;
zonecfg_getdevent;
zonecfg_getdevperment;
@@ -123,9 +126,11 @@ SUNWprivate_1.1 {
zonecfg_get_uuid;
zonecfg_get_xml_handle;
zonecfg_get_zonepath;
+ zonecfg_grab_lock_file;
zonecfg_ifname_exists;
zonecfg_in_alt_root;
zonecfg_init_handle;
+ zonecfg_init_lock_file;
zonecfg_is_rctl;
zonecfg_is_scratch;
zonecfg_lock_scratch;
@@ -154,6 +159,8 @@ SUNWprivate_1.1 {
zonecfg_notify_unbind;
zonecfg_num_resources;
zonecfg_open_scratch;
+ zonecfg_ping_zoneadmd;
+ zonecfg_release_lock_file;
zonecfg_remove_fs_option;
zonecfg_remove_rctl_value;
zonecfg_reverse_scratch;
@@ -179,6 +186,7 @@ SUNWprivate_1.1 {
zonecfg_setrctlent;
zonecfg_set_root;
zonecfg_set_sched;
+ zonecfg_set_swinv;
zonecfg_set_zonepath;
zonecfg_strerror;
zonecfg_str_to_bytes;
diff --git a/usr/src/pkgdefs/SUNWzoneu/prototype_com b/usr/src/pkgdefs/SUNWzoneu/prototype_com
index 2020693704..244b89a429 100644
--- a/usr/src/pkgdefs/SUNWzoneu/prototype_com
+++ b/usr/src/pkgdefs/SUNWzoneu/prototype_com
@@ -55,7 +55,7 @@ d none usr/lib/brand/native 755 root sys
f none usr/lib/brand/native/attach_update 755 root bin
f none usr/lib/brand/native/config.xml 444 root bin
f none usr/lib/brand/native/platform.xml 444 root bin
-f none usr/lib/brand/native/postclone 755 root bin
+f none usr/lib/brand/native/sw_support 755 root bin
f none usr/lib/libbrand.so.1 755 root bin
f none usr/lib/libzonecfg.so.1 755 root bin
d none usr/lib/zones 755 root bin