diff options
Diffstat (limited to 'usr/src/lib')
| -rw-r--r-- | usr/src/lib/brand/lx/zone/config.xml | 2 | ||||
| -rw-r--r-- | usr/src/lib/brand/native/zone/Makefile | 13 | ||||
| -rw-r--r-- | usr/src/lib/brand/native/zone/config.xml | 13 | ||||
| -rw-r--r-- | usr/src/lib/brand/native/zone/postclone.sh | 61 | ||||
| -rw-r--r-- | usr/src/lib/brand/native/zone/sw_support.c | 3463 | ||||
| -rw-r--r-- | usr/src/lib/brand/sn1/zone/config.xml | 12 | ||||
| -rw-r--r-- | usr/src/lib/libbrand/common/libbrand.c | 182 | ||||
| -rw-r--r-- | usr/src/lib/libbrand/common/libbrand.h | 34 | ||||
| -rw-r--r-- | usr/src/lib/libbrand/common/mapfile-vers | 9 | ||||
| -rw-r--r-- | usr/src/lib/libbrand/dtd/brand.dtd.1 | 237 | ||||
| -rw-r--r-- | usr/src/lib/libzonecfg/Makefile.com | 4 | ||||
| -rw-r--r-- | usr/src/lib/libzonecfg/common/libzonecfg.c | 1621 | ||||
| -rw-r--r-- | usr/src/lib/libzonecfg/common/mapfile-vers | 12 |
13 files changed, 4255 insertions, 1408 deletions
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/postclone.sh b/usr/src/lib/brand/native/zone/postclone.sh deleted file mode 100644 index 63fc5e7bda..0000000000 --- a/usr/src/lib/brand/native/zone/postclone.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" - -PATH=/sbin:/usr/bin:/usr/sbin; export PATH - -ZONENAME=$1 -ZONEROOT=$2 - -# If the zone is already sys-unconfiged, then we're done. -if [ -f $ZONEROOT/etc/.UNCONFIGURED ]; then - exit 0 -fi - -# -# Mount the zone. The zone is still in the INCOMPLETE state, so we have to -# -f(orce) mount it. -# -zoneadm -z $ZONENAME mount -f -if [ $? -ne 0 ]; then - echo `gettext "Could not mount zone for sys-unconfig"` - exit 1 -fi - -# Log into the zone and sys-unconfig it. -zlogin -S $ZONENAME /usr/sbin/sys-unconfig -R /a -err=$? -if [ $err -ne 0 ]; then - echo `gettext "sys-unconfig failed"` -fi - -zoneadm -z $ZONENAME unmount -if [ $? -ne 0 ]; then - echo `gettext "Could not unmount zone"` - exit 1 -fi - -exit $err 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; |
