diff options
author | gjelinek <none@none> | 2008-01-14 13:01:24 -0800 |
---|---|---|
committer | gjelinek <none@none> | 2008-01-14 13:01:24 -0800 |
commit | 6cfd72c6361fc164bc537fc17e829cccc62b0b1f (patch) | |
tree | 64b671e3638b18df6d852721d5c5ee93649e1a9f | |
parent | 8899fcfafcee5a0a80d70ea19c2ad0870c5d8cb1 (diff) | |
download | illumos-joyent-6cfd72c6361fc164bc537fc17e829cccc62b0b1f.tar.gz |
PSARC 2007/621 zone update on attach
6480464 RFE: zoneadm attach should patch/update the zone to the new hosts level
6576592 RFE: zoneadm detach/attach should work between sun4u and sun4v architecture
6637869 zone attach doesn't handle obsolete patches correctly
-rw-r--r-- | usr/src/cmd/zoneadm/Makefile | 5 | ||||
-rw-r--r-- | usr/src/cmd/zoneadm/sw_cmp.c | 1115 | ||||
-rw-r--r-- | usr/src/cmd/zoneadm/zoneadm.c | 224 | ||||
-rw-r--r-- | usr/src/cmd/zoneadm/zoneadm.h | 5 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 113 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/zoneadmd.c | 18 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/zoneadmd.h | 19 | ||||
-rw-r--r-- | usr/src/head/libzonecfg.h | 18 | ||||
-rw-r--r-- | usr/src/lib/brand/native/zone/Makefile | 4 | ||||
-rwxr-xr-x | usr/src/lib/brand/native/zone/attach_update.ksh | 626 | ||||
-rw-r--r-- | usr/src/lib/libzonecfg/common/libzonecfg.c | 590 | ||||
-rw-r--r-- | usr/src/lib/libzonecfg/common/mapfile-vers | 9 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWzoneu/prototype_com | 3 |
13 files changed, 2383 insertions, 366 deletions
diff --git a/usr/src/cmd/zoneadm/Makefile b/usr/src/cmd/zoneadm/Makefile index 4428c23cb3..0ec4d19bdc 100644 --- a/usr/src/cmd/zoneadm/Makefile +++ b/usr/src/cmd/zoneadm/Makefile @@ -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. # # ident "%Z%%M% %I% %E% SMI" @@ -39,7 +39,8 @@ SRCS = $(OBJS:.o=.c) POFILE=zoneadm_all.po POFILES= $(OBJS:%.o=%.po) -LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid -lnvpair -lbrand -ldlpi +LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs -luuid -lnvpair -lbrand \ + -ldlpi -luutil .KEEP_STATE: diff --git a/usr/src/cmd/zoneadm/sw_cmp.c b/usr/src/cmd/zoneadm/sw_cmp.c index 67554c76c2..7dcb06e958 100644 --- a/usr/src/cmd/zoneadm/sw_cmp.c +++ b/usr/src/cmd/zoneadm/sw_cmp.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,217 +31,337 @@ #include <string.h> #include <locale.h> #include <libintl.h> +#include <stddef.h> +#include <ctype.h> +#include <stdlib.h> +#include <assert.h> #include <libzonecfg.h> #include "zoneadm.h" +extern int errno; + +/* 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 boolean_t +valid_num(char *n) +{ + for (; isdigit(*n); n++) + ; + + if (*n != NULL) + return (B_FALSE); + return (B_TRUE); +} + /* - * Find the specified package in the sw inventory on the handle and check - * if the version matches what is passed in. - * Return 0 if the packages match - * 1 if the package is found but we have a version mismatch - * -1 if the package is not found + * 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 -pkg_cmp(zone_dochandle_t handle, char *pkg_name, char *pkg_vers, - char *return_vers, int vers_size) +fld2num(char *fld, char **nfld) { - int res = -1; - struct zone_pkgtab pkgtab; + char *ppoint; + long n; - if (zonecfg_setpkgent(handle) != Z_OK) { - (void) fprintf(stderr, - gettext("unable to enumerate packages\n")); - return (Z_ERR); + if ((ppoint = strchr(fld, '.')) != NULL) { + *ppoint = '\0'; + *nfld = ppoint + 1; + } else { + *nfld = NULL; } - while (zonecfg_getpkgent(handle, &pkgtab) == Z_OK) { - if (strcmp(pkg_name, pkgtab.zone_pkg_name) != 0) - continue; - - if (strcmp(pkg_vers, pkgtab.zone_pkg_version) == 0) { - res = 0; - break; - } + if (!valid_num(fld)) + return (-1); - (void) strlcpy(return_vers, pkgtab.zone_pkg_version, vers_size); - res = 1; - break; - } + errno = 0; + n = strtol(fld, (char **)NULL, 10); + if (errno != 0) + return (-1); - (void) zonecfg_endpkgent(handle); - return (res); + return ((int)n); } /* - * Used in software comparisons to check the packages between the two zone - * handles. The packages have to match or we print a message telling the - * user what is out of sync. If flag has SW_CMP_SRC this tells us the first - * handle is the source machine global zone. This is used to enable the - * right messages to be printed and also to enable extra version checking - * that is not needed for the opposite comparison. + * 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 -pkg_check(char *header, zone_dochandle_t handle1, zone_dochandle_t handle2, - uint_t flag) +fld_cmp(char *v1, char *v2) { - int err; - int res = Z_OK; - boolean_t do_header = B_TRUE; - char other_vers[ZONE_PKG_VERSMAX]; - struct zone_pkgtab pkgtab; - - if (zonecfg_setpkgent(handle1) != Z_OK) { - (void) fprintf(stderr, - gettext("unable to enumerate packages\n")); - return (Z_ERR); - } - - while (zonecfg_getpkgent(handle1, &pkgtab) == Z_OK) { - if ((err = pkg_cmp(handle2, pkgtab.zone_pkg_name, - pkgtab.zone_pkg_version, other_vers, sizeof (other_vers))) - != 0) { - res = Z_ERR; - if (flag & SW_CMP_SILENT) - break; + char *nxtfld1, *nxtfld2; + int n1, n2; - if (do_header && (err < 0 || flag & SW_CMP_SRC)) { - /* LINTED E_SEC_PRINTF_VAR_FMT */ - (void) fprintf(stderr, header); - do_header = B_FALSE; - } - if (err < 0) - (void) fprintf(stderr, - (flag & SW_CMP_SRC) ? - gettext("\t%s: not installed\n\t\t(%s)\n") : - gettext("\t%s (%s)\n"), - pkgtab.zone_pkg_name, - pkgtab.zone_pkg_version); - else if (flag & SW_CMP_SRC) - (void) fprintf(stderr, gettext( - "\t%s: version mismatch\n\t\t(%s)" - "\n\t\t(%s)\n"), - pkgtab.zone_pkg_name, - pkgtab.zone_pkg_version, other_vers); - } - } + for (;;) { + n1 = fld2num(v1, &nxtfld1); + n2 = fld2num(v2, &nxtfld2); - (void) zonecfg_endpkgent(handle1); + /* + * If either field is not a postive int, just compare them + * lexically. + */ + if (n1 < 0 || n2 < 0) + return (strcmp(v1, v2)); - return (res); + 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; + } } /* - * Find the specified patch in the sw inventory on the handle and check - * if the version matches what is passed in. - * Return 0 if the patches match - * 1 if the patches is found but we have a version mismatch - * -1 if the patches is not found + * 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 int -patch_cmp(zone_dochandle_t handle, char *patch_id, char *patch_vers, - char *return_vers, int vers_size) +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); +} + +static void +pkg_avl_delete(uu_avl_t *pavl) { - int res = -1; - struct zone_patchtab patchtab; + zone_pkg_entry_t *p; + void *cookie = NULL; + + if (pavl == NULL) + return; - if (zonecfg_setpatchent(handle) != Z_OK) { - (void) fprintf(stderr, - gettext("unable to enumerate patches\n")); - return (Z_ERR); + while ((p = uu_avl_teardown(pavl, &cookie)) != NULL) { + free(p->zpe_name); + free(p->zpe_vers); + pkg_avl_delete(p->zpe_patches_avl); + free(p); } - while (zonecfg_getpatchent(handle, &patchtab) == Z_OK) { - char *p; + uu_avl_destroy(pavl); +} - if ((p = strchr(patchtab.zone_patch_id, '-')) != NULL) - *p++ = '\0'; - else - p = ""; +/* + * 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); - if (strcmp(patch_id, patchtab.zone_patch_id) != 0) + res = B_FALSE; + while ((patch = uu_avl_walk_next(patch_walk)) != NULL) { + uu_avl_index_t where; + + if (patch->zpe_patches_avl == NULL) continue; - if (strcmp(patch_vers, p) == 0) { - res = 0; + /* Check the obsolete list on the patch. */ + if (uu_avl_find(patch->zpe_patches_avl, patchid, NULL, &where) + != NULL) { + res = B_TRUE; break; } - - (void) strlcpy(return_vers, p, vers_size); - /* - * Keep checking. This handles the case where multiple - * versions of the same patch is installed. - */ - res = 1; } - (void) zonecfg_endpatchent(handle); + uu_avl_walk_end(patch_walk); return (res); } /* - * Used in software comparisons to check the patches between the two zone - * handles. The patches have to match or we print a message telling the - * user what is out of sync. If flag has SW_CMP_SRC this tells us the first - * handle is the source machine global zone. This is used to enable the - * right messages to be printed. For patches we do need to compare the - * versions both ways and print the right header and error message. This - * is because it is possible to have multiple versions of a patch installed - * and we need to detect the case where the target has a newer version of - * a patch in addition to the version that was installed on the source. + * 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 int -patch_check(char *header, zone_dochandle_t handle1, zone_dochandle_t handle2, - uint_t flag) +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) { - int err; - int res = Z_OK; - boolean_t do_header = B_TRUE; - char other_vers[MAXNAMELEN]; - struct zone_patchtab patchtab; + uu_avl_walk_t *walk; + zone_pkg_entry_t *pkg_patch; - if (zonecfg_setpatchent(handle1) != Z_OK) { - (void) fprintf(stderr, - gettext("unable to enumerate patches\n")); - return (Z_ERR); - } + if (pkg_patches == NULL) + return (B_TRUE); - while (zonecfg_getpatchent(handle1, &patchtab) == Z_OK) { - char *patch_vers; + walk = uu_avl_walk_start(pkg_patches, UU_WALK_ROBUST); + if (walk == NULL) + return (B_FALSE); - if ((patch_vers = strchr(patchtab.zone_patch_id, '-')) != NULL) - *patch_vers++ = '\0'; - else - patch_vers = ""; + while ((pkg_patch = uu_avl_walk_next(walk)) != NULL) { + uu_avl_index_t where; + zone_pkg_entry_t *patch; - if ((err = patch_cmp(handle2, patchtab.zone_patch_id, - patch_vers, other_vers, sizeof (other_vers))) != 0) { - res = Z_ERR; - if (flag & SW_CMP_SILENT) - break; + /* Skip adding it if we already have it. */ + if (uu_avl_find(unique, pkg_patch, NULL, &where) != NULL) + continue; - if (do_header) { - /* LINTED E_SEC_PRINTF_VAR_FMT */ - (void) fprintf(stderr, header); - do_header = B_FALSE; - } - if (err < 0) - (void) fprintf(stderr, - (flag & SW_CMP_SRC) ? - gettext("\t%s-%s: not installed\n") : - gettext("\t%s-%s\n"), - patchtab.zone_patch_id, patch_vers); - else - (void) fprintf(stderr, - gettext("\t%s: version mismatch\n\t\t(%s) " - "(%s)\n"), patchtab.zone_patch_id, - patch_vers, other_vers); + /* 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); - (void) zonecfg_endpatchent(handle1); + return (B_TRUE); +} - return (res); +/* + * 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); } /* @@ -261,46 +381,681 @@ int sw_cmp(zone_dochandle_t l_handle, zone_dochandle_t s_handle, uint_t flag) { char *hdr; - int res = Z_OK; + 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. */ - if (!(flag & SW_CMP_SILENT)) - hdr = gettext("These packages installed on the source system " - "are inconsistent with this system:\n"); - if (pkg_check(hdr, s_handle, l_handle, flag | SW_CMP_SRC) != Z_OK) - res = Z_ERR; + 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. */ - if (!(flag & SW_CMP_SILENT)) - hdr = gettext("These packages installed on this system were " - "not installed on the source system:\n"); - if (pkg_check(hdr, l_handle, s_handle, flag | SW_CMP_NONE) != Z_OK) - res = Z_ERR; + 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. */ - if (!(flag & SW_CMP_SILENT)) - hdr = gettext("These patches installed on the source system " - "are inconsistent with this system:\n"); - if (patch_check(hdr, s_handle, l_handle, flag | SW_CMP_SRC) != Z_OK) - res = Z_ERR; + 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. */ - if (!(flag & SW_CMP_SILENT)) - hdr = gettext("These patches installed on this system were " - "not installed on the source system:\n"); - if (patch_check(hdr, l_handle, s_handle, flag | SW_CMP_NONE) != Z_OK) - res = Z_ERR; + 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. + */ +int +sw_up_to_date(zone_dochandle_t l_handle, zone_dochandle_t s_handle, + char *zonepath) +{ + 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); } diff --git a/usr/src/cmd/zoneadm/zoneadm.c b/usr/src/cmd/zoneadm/zoneadm.c index f8337ae207..1fb836f3a2 100644 --- a/usr/src/cmd/zoneadm/zoneadm.c +++ b/usr/src/cmd/zoneadm/zoneadm.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. */ @@ -139,7 +139,7 @@ struct cmd { #define SHELP_CLONE "clone [-m method] [-s <ZFS snapshot>] zonename" #define SHELP_MOVE "move zonepath" #define SHELP_DETACH "detach [-n]" -#define SHELP_ATTACH "attach [-F] [-n <path>]" +#define SHELP_ATTACH "attach [-F] [-n <path>] [-u]" #define SHELP_MARK "mark incomplete" #define EXEC_PREFIX "exec " @@ -206,6 +206,8 @@ static char *target_uuid; /* used in do_subproc() and signal handler */ static volatile boolean_t child_killed; +/* used in attach_func() and signal handler */ +static volatile boolean_t attach_interupted; static int do_subproc_cnt = 0; /* @@ -305,13 +307,15 @@ long_help(int cmd_num) "successful completion, the zone state will be\n\t" "'installed'. The system software on the current " "system must be\n\tcompatible with the software on the " - "zone's original system.\n\tSpecify -F to force the attach " - "and skip software compatibility tests.\n\tThe -n option " - "can be used to specify 'no-execute' mode. When -n is\n\t" - "used, the information needed to attach the zone is read " - "from the\n\tspecified path and the configuration is only " - "validated. The path can\n\tbe '-' to specify standard " - "input.")); + "zone's original system or use\n\tthe -u option to update " + "the zone to the current system software.\n\tSpecify -F " + "to force the attach and skip software compatibility " + "tests.\n\tThe -n option can be used to specify " + "'no-execute' mode. When -n is\n\tused, the information " + "needed to attach the zone is read from the\n\tspecified " + "path and the configuration is only validated. The path " + "can\n\tbe '-' to specify standard input. The -F, -n and " + "-u options are\n\tmutually exclusive.")); case CMD_MARK: return (gettext("Set the state of the zone. This can be used " "to force the zone\n\tstate to 'incomplete' " @@ -4882,7 +4886,7 @@ cleanup: target_zone, target_zone) >= sizeof (cmdbuf)) { res = B_FALSE; } else { - status = do_subproc(cmdbuf); + status = do_subproc_interactive(cmdbuf); if (subproc_status("rm", status, B_TRUE) != ZONE_SUBPROC_OK) res = B_FALSE; @@ -4898,6 +4902,150 @@ cleanup: return (res); } +/* + * The zone needs to be updated so set it up for the update and initiate the + * update within the scratch zone. First set the state to incomplete so we can + * force-mount the zone for the update operation. We pass the -U option to the + * mount so that the scratch zone is mounted without the zone's /etc and /var + * being lofs mounted back into the scratch zone root. This is done by + * overloading the bootbuf string in the zone_cmd_arg_t to pass -U as an option + * to the mount cmd. + */ +static int +attach_update(zone_dochandle_t handle, char *zonepath) +{ + int err; + int update_res; + int status; + zone_cmd_arg_t zarg; + FILE *fp; + struct zone_fstab fstab; + char cmdbuf[(4 * MAXPATHLEN) + 20]; + + if ((err = zone_set_state(target_zone, ZONE_STATE_INCOMPLETE)) + != Z_OK) { + errno = err; + zperror(gettext("could not set state"), B_TRUE); + return (Z_ERR); + } + + zarg.cmd = Z_FORCEMOUNT; + (void) strlcpy(zarg.bootbuf, "-U", sizeof (zarg.bootbuf)); + if (call_zoneadmd(target_zone, &zarg) != 0) { + zerror(gettext("could not mount zone")); + + /* We reset the state since the zone wasn't modified yet. */ + if ((err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED)) + != Z_OK) { + errno = err; + zperror(gettext("could not reset state"), B_TRUE); + } + return (Z_ERR); + } + + /* + * Move data files generated by sw_up_to_date() into the scratch + * zone's /tmp. + */ + (void) snprintf(cmdbuf, sizeof (cmdbuf), "exec /usr/bin/mv " + "%s/pkg_add %s/pkg_rm %s/lu/tmp", + zonepath, zonepath, zonepath); + + status = do_subproc_interactive(cmdbuf); + if (subproc_status("mv", status, B_TRUE) != ZONE_SUBPROC_OK) { + zperror(gettext("could not mv data files"), B_FALSE); + goto fail; + } + + /* + * Save list of inherit-pkg-dirs into zone. Since the file is in + * /tmp we don't have to worry about deleting it. + */ + (void) snprintf(cmdbuf, sizeof (cmdbuf), "%s/lu/tmp/inherited", + zonepath); + if ((fp = fopen(cmdbuf, "w")) == NULL) { + zperror(gettext("could not save inherit-pkg-dirs"), B_FALSE); + goto fail; + } + if (zonecfg_setipdent(handle) != Z_OK) { + zperror(gettext("could not enumerate inherit-pkg-dirs"), + B_TRUE); + goto fail; + } + while (zonecfg_getipdent(handle, &fstab) == Z_OK) { + if (fprintf(fp, "%s\n", fstab.zone_fs_dir) < 0) { + zperror(gettext("could not save inherit-pkg-dirs"), + B_FALSE); + (void) fclose(fp); + goto fail; + } + } + (void) zonecfg_endipdent(handle); + if (fclose(fp) != 0) { + zperror(gettext("could not save inherit-pkg-dirs"), B_FALSE); + goto fail; + } + + /* run the updater inside the scratch zone */ + (void) snprintf(cmdbuf, sizeof (cmdbuf), + "exec /usr/sbin/zlogin -S %s " + "/usr/lib/brand/native/attach_update %s", target_zone, target_zone); + + update_res = Z_OK; + status = do_subproc_interactive(cmdbuf); + if (subproc_status("attach_update", status, B_TRUE) + != ZONE_SUBPROC_OK) { + zerror(gettext("could not update zone")); + update_res = Z_ERR; + } + + zarg.cmd = Z_UNMOUNT; + if (call_zoneadmd(target_zone, &zarg) != 0) { + zerror(gettext("could not unmount zone")); + return (Z_ERR); + } + + /* + * If the update script within the scratch zone failed for some reason + * we will now leave the zone in the incomplete state since we no + * longer know the state of the files within the zonepath. + */ + if (update_res == Z_ERR) + return (Z_ERR); + + zonecfg_rm_detached(handle, B_FALSE); + + if ((err = zone_set_state(target_zone, ZONE_STATE_INSTALLED)) != Z_OK) { + errno = err; + zperror(gettext("could not set state"), B_TRUE); + return (Z_ERR); + } + + return (Z_OK); + +fail: + zarg.cmd = Z_UNMOUNT; + if (call_zoneadmd(target_zone, &zarg) != 0) + zerror(gettext("could not unmount zone")); + + /* We reset the state since the zone wasn't modified yet. */ + if ((err = zone_set_state(target_zone, ZONE_STATE_CONFIGURED)) + != Z_OK) { + errno = err; + zperror(gettext("could not reset state"), B_TRUE); + } + + return (Z_ERR); +} + +/* ARGSUSED */ +static void +sigcleanup(int sig) +{ + attach_interupted = B_TRUE; +} + + static int attach_func(int argc, char *argv[]) { @@ -4911,6 +5059,7 @@ attach_func(int argc, char *argv[]) char cmdbuf[MAXPATHLEN]; boolean_t execute = B_TRUE; boolean_t retried = B_FALSE; + boolean_t update = B_FALSE; char *manifest_path; brand_handle_t bh = NULL; @@ -4920,7 +5069,7 @@ attach_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?Fn:")) != EOF) { + if ((arg = getopt(argc, argv, "?Fn:u")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_ATTACH, CMD_ATTACH); @@ -4932,12 +5081,21 @@ attach_func(int argc, char *argv[]) execute = B_FALSE; manifest_path = optarg; break; + case 'u': + update = B_TRUE; + break; default: sub_usage(SHELP_ATTACH, CMD_ATTACH); return (Z_USAGE); } } + /* dry-run and update flags are mutually exclusive */ + if (!execute && update) { + zerror(gettext("-n and -u flags are mutually exclusive")); + return (Z_ERR); + } + /* * If the no-execute option was specified, we need to branch down * a completely different path since there is no zone required to be @@ -5072,12 +5230,45 @@ retry: goto done; } - /* sw_cmp prints error msgs as necessary */ - if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK) - 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]; - if ((err = dev_fix(athandle)) != Z_OK) - goto done; + (void) sigset(SIGINT, sigcleanup); + + if ((err = sw_up_to_date(handle, athandle, zonepath)) != Z_OK) { + if (err != Z_FATAL && !attach_interupted) { + err = Z_FATAL; + err = attach_update(handle, zonepath); + } + if (!attach_interupted || err == Z_OK) + goto done; + } + + (void) sigset(SIGINT, SIG_DFL); + + /* clean up data files left behind by sw_up_to_date() */ + (void) snprintf(fname, sizeof (fname), "%s/pkg_add", zonepath); + (void) unlink(fname); + (void) snprintf(fname, sizeof (fname), "%s/pkg_rm", zonepath); + (void) unlink(fname); + + if (attach_interupted) { + err = Z_FATAL; + goto done; + } + + } else { + /* sw_cmp prints error msgs as necessary */ + if ((err = sw_cmp(handle, athandle, SW_CMP_NONE)) != Z_OK) + goto done; + + if ((err = dev_fix(athandle)) != Z_OK) + goto done; + } forced: @@ -5326,6 +5517,7 @@ mount_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT; + zarg.bootbuf[0] = '\0'; if (call_zoneadmd(target_zone, &zarg) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); diff --git a/usr/src/cmd/zoneadm/zoneadm.h b/usr/src/cmd/zoneadm/zoneadm.h index a299ece135..9cf02b82a5 100644 --- a/usr/src/cmd/zoneadm/zoneadm.h +++ b/usr/src/cmd/zoneadm/zoneadm.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -56,6 +56,7 @@ #define Z_ERR 1 #define Z_USAGE 2 +#define Z_FATAL 3 #define SW_CMP_NONE 0x0 #define SW_CMP_SRC 0x01 @@ -90,5 +91,7 @@ extern int init_zfs(void); */ extern int sw_cmp(zone_dochandle_t l_handle, zone_dochandle_t s_handle, uint_t flag); +extern int sw_up_to_date(zone_dochandle_t l_handle, zone_dochandle_t s_handle, + char *zonepath); #endif /* _ZONEADM_H */ diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 346fd2ac86..12f0b01eee 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.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. */ @@ -133,6 +133,8 @@ #define DFSTYPES "/etc/dfs/fstypes" #define MAXTNZLEN 2048 +#define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT) + /* for routing socket */ static int rts_seqno = 0; @@ -1322,6 +1324,10 @@ free_fs_data(struct zone_fstab *fsarray, uint_t nelem) * So mounting of localdirs[](/etc and /var) have been moved to the * build_mounted_post_var() which gets called only after the zone * specific filesystems are mounted. + * + * Note that the scratch zone we set up for updating the zone (Z_MNT_UPDATE) + * does not loopback mount the zone's own /etc and /var into the root of the + * scratch zone. */ static boolean_t build_mounted_pre_var(zlog_t *zlogp, char *rootpath, @@ -1405,42 +1411,61 @@ build_mounted_pre_var(zlog_t *zlogp, char *rootpath, static boolean_t -build_mounted_post_var(zlog_t *zlogp, char *rootpath, const char *luroot) +build_mounted_post_var(zlog_t *zlogp, zone_mnt_t mount_cmd, char *rootpath, + const char *luroot) { char tmp[MAXPATHLEN], fromdir[MAXPATHLEN]; const char **cpp; + const char **loopdirs; + const char **tmpdirs; static const char *localdirs[] = { "/etc", "/var", NULL }; - static const char *loopdirs[] = { + static const char *scr_loopdirs[] = { "/etc/lib", "/etc/fs", "/lib", "/sbin", "/platform", "/usr", NULL }; - static const char *tmpdirs[] = { + static const char *upd_loopdirs[] = { + "/etc", "/kernel", "/lib", "/opt", "/platform", "/sbin", + "/usr", "/var", NULL + }; + static const char *scr_tmpdirs[] = { "/tmp", "/var/run", NULL }; + static const char *upd_tmpdirs[] = { + "/tmp", "/var/run", "/var/tmp", NULL + }; struct stat st; - /* - * These are mounted read-write from the zone undergoing upgrade. We - * must be careful not to 'leak' things from the main system into the - * zone, and this accomplishes that goal. - */ - for (cpp = localdirs; *cpp != NULL; cpp++) { - (void) snprintf(tmp, sizeof (tmp), "%s%s", luroot, *cpp); - (void) snprintf(fromdir, sizeof (fromdir), "%s%s", rootpath, - *cpp); - if (mkdir(tmp, 0755) != 0) { - zerror(zlogp, B_TRUE, "cannot create %s", tmp); - return (B_FALSE); - } - if (domount(zlogp, MNTTYPE_LOFS, "", fromdir, tmp) != 0) { - zerror(zlogp, B_TRUE, "cannot mount %s on %s", tmp, + if (mount_cmd == Z_MNT_SCRATCH) { + /* + * These are mounted read-write from the zone undergoing + * upgrade. We must be careful not to 'leak' things from the + * main system into the zone, and this accomplishes that goal. + */ + for (cpp = localdirs; *cpp != NULL; cpp++) { + (void) snprintf(tmp, sizeof (tmp), "%s%s", luroot, *cpp); - return (B_FALSE); + (void) snprintf(fromdir, sizeof (fromdir), "%s%s", + rootpath, *cpp); + if (mkdir(tmp, 0755) != 0) { + zerror(zlogp, B_TRUE, "cannot create %s", tmp); + return (B_FALSE); + } + if (domount(zlogp, MNTTYPE_LOFS, "", fromdir, tmp) + != 0) { + zerror(zlogp, B_TRUE, "cannot mount %s on %s", + tmp, *cpp); + return (B_FALSE); + } } } + if (mount_cmd == Z_MNT_UPDATE) + loopdirs = upd_loopdirs; + else + loopdirs = scr_loopdirs; + /* * These are things mounted read-only from the running system because * they contain binaries that must match system. @@ -1473,12 +1498,18 @@ build_mounted_post_var(zlog_t *zlogp, char *rootpath, const char *luroot) } } + if (mount_cmd == Z_MNT_UPDATE) + tmpdirs = upd_tmpdirs; + else + tmpdirs = scr_tmpdirs; + /* * These are things with tmpfs mounted inside. */ for (cpp = tmpdirs; *cpp != NULL; cpp++) { (void) snprintf(tmp, sizeof (tmp), "%s%s", luroot, *cpp); - if (mkdir(tmp, 0755) != 0 && errno != EEXIST) { + if (mount_cmd == Z_MNT_SCRATCH && mkdir(tmp, 0755) != 0 && + errno != EEXIST) { zerror(zlogp, B_TRUE, "cannot create %s", tmp); return (B_FALSE); } @@ -1595,7 +1626,7 @@ mount_filesystems_ipdent(zone_dochandle_t handle, zlog_t *zlogp, static int mount_filesystems_fsent(zone_dochandle_t handle, zlog_t *zlogp, - struct zone_fstab **fs_tabp, int *num_fsp, int mount_cmd) + struct zone_fstab **fs_tabp, int *num_fsp, zone_mnt_t mount_cmd) { struct zone_fstab *tmp_ptr, *fs_ptr, *fsp, fstab; int num_fs; @@ -1613,7 +1644,8 @@ mount_filesystems_fsent(zone_dochandle_t handle, zlog_t *zlogp, * root, since the pool will not be known. Ignore them in this * case. */ - if (mount_cmd && strcmp(fstab.zone_fs_type, MNTTYPE_ZFS) == 0) + if (ALT_MOUNT(mount_cmd) && + strcmp(fstab.zone_fs_type, MNTTYPE_ZFS) == 0) continue; num_fs++; @@ -1660,7 +1692,7 @@ mount_filesystems_fsent(zone_dochandle_t handle, zlog_t *zlogp, } static int -mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd) +mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) { char rootpath[MAXPATHLEN]; char zonepath[MAXPATHLEN]; @@ -1774,15 +1806,14 @@ mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd) * 3) Set up the rest of the scratch zone environment * (build_mounted_post_var()). */ - if (mount_cmd && - !build_mounted_pre_var(zlogp, + if (ALT_MOUNT(mount_cmd) && !build_mounted_pre_var(zlogp, rootpath, sizeof (rootpath), zonepath, luroot, sizeof (luroot))) goto bad; qsort(fs_ptr, num_fs, sizeof (*fs_ptr), fs_compare); for (i = 0; i < num_fs; i++) { - if (mount_cmd && + if (ALT_MOUNT(mount_cmd) && strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) { size_t slen = strlen(rootpath) - 2; @@ -1802,14 +1833,15 @@ mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd) if (mount_one(zlogp, &fs_ptr[i], rootpath) != 0) goto bad; } - if (mount_cmd && - !build_mounted_post_var(zlogp, rootpath, luroot)) + if (ALT_MOUNT(mount_cmd) && + !build_mounted_post_var(zlogp, mount_cmd, rootpath, luroot)) goto bad; /* * For Trusted Extensions cross-mount each lower level /export/home */ - if (!mount_cmd && tsol_mounts(zlogp, zone_name, rootpath) != 0) + if (mount_cmd == Z_MNT_BOOT && + tsol_mounts(zlogp, zone_name, rootpath) != 0) goto bad; free_fs_data(fs_ptr, num_fs); @@ -2907,7 +2939,7 @@ tcp_abort_connections(zlog_t *zlogp, zoneid_t zoneid) } static int -get_privset(zlog_t *zlogp, priv_set_t *privs, boolean_t mount_cmd) +get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) { int error = -1; zone_dochandle_t handle; @@ -2923,7 +2955,7 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, boolean_t mount_cmd) return (-1); } - if (mount_cmd) { + if (ALT_MOUNT(mount_cmd)) { zone_iptype_t iptype; const char *curr_iptype; @@ -4010,7 +4042,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) } zoneid_t -vplat_create(zlog_t *zlogp, boolean_t mount_cmd) +vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) { zoneid_t rval = -1; priv_set_t *privs; @@ -4060,7 +4092,8 @@ vplat_create(zlog_t *zlogp, boolean_t mount_cmd) if (get_privset(zlogp, privs, mount_cmd) != 0) goto error; - if (!mount_cmd && get_rctls(zlogp, &rctlbuf, &rctlbufsz) != 0) { + if (mount_cmd == Z_MNT_BOOT && + get_rctls(zlogp, &rctlbuf, &rctlbufsz) != 0) { zerror(zlogp, B_FALSE, "Unable to get list of rctls"); goto error; } @@ -4070,7 +4103,7 @@ vplat_create(zlog_t *zlogp, boolean_t mount_cmd) goto error; } - if (!mount_cmd && is_system_labeled()) { + if (mount_cmd == Z_MNT_BOOT && is_system_labeled()) { zcent = get_zone_label(zlogp, privs); if (zcent != NULL) { match = zcent->zc_match; @@ -4094,7 +4127,7 @@ vplat_create(zlog_t *zlogp, boolean_t mount_cmd) if (duplicate_reachable_path(zlogp, rootpath)) goto error; - if (mount_cmd) { + if (ALT_MOUNT(mount_cmd)) { assert(zone_isnative || zone_iscluster); root_to_lu(zlogp, rootpath, sizeof (rootpath), B_TRUE); @@ -4222,7 +4255,7 @@ vplat_create(zlog_t *zlogp, boolean_t mount_cmd) * The following actions are not performed when merely mounting a zone * for administrative use. */ - if (!mount_cmd) { + if (mount_cmd == Z_MNT_BOOT) { if (setup_zone_rm(zlogp, zone_name, zoneid) != Z_OK) { (void) zone_shutdown(zoneid); goto error; @@ -4325,11 +4358,11 @@ write_index_file(zoneid_t zoneid) } int -vplat_bringup(zlog_t *zlogp, boolean_t mount_cmd, zoneid_t zoneid) +vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) { char zonepath[MAXPATHLEN]; - if (!mount_cmd && validate_datasets(zlogp) != 0) { + if (mount_cmd == Z_MNT_BOOT && validate_datasets(zlogp) != 0) { lofs_discard_mnttab(); return (-1); } @@ -4356,7 +4389,7 @@ vplat_bringup(zlog_t *zlogp, boolean_t mount_cmd, zoneid_t zoneid) return (-1); } - if (!mount_cmd) { + if (mount_cmd == Z_MNT_BOOT) { zone_iptype_t iptype; if (get_iptype(zlogp, &iptype) < 0) { diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c index 7ba467160c..b7f9dfadf7 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.c +++ b/usr/src/cmd/zoneadmd/zoneadmd.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. */ @@ -446,7 +446,7 @@ mkzonedir(zlog_t *zlogp) * subcommand. */ static int -zone_ready(zlog_t *zlogp, boolean_t mount_cmd) +zone_ready(zlog_t *zlogp, zone_mnt_t mount_cmd) { int err; @@ -464,7 +464,7 @@ zone_ready(zlog_t *zlogp, boolean_t mount_cmd) } if (vplat_bringup(zlogp, mount_cmd, zone_id) != 0) { bringup_failure_recovery = B_TRUE; - (void) vplat_teardown(NULL, mount_cmd, B_FALSE); + (void) vplat_teardown(NULL, (mount_cmd != Z_MNT_BOOT), B_FALSE); if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) zerror(zlogp, B_FALSE, "destroying snapshot: %s", zonecfg_strerror(err)); @@ -1032,14 +1032,14 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case ZONE_STATE_INSTALLED: switch (cmd) { case Z_READY: - rval = zone_ready(zlogp, B_FALSE); + rval = zone_ready(zlogp, Z_MNT_BOOT); if (rval == 0) eventstream_write(Z_EVT_ZONE_READIED); break; case Z_BOOT: case Z_FORCEBOOT: eventstream_write(Z_EVT_ZONE_BOOTING); - if ((rval = zone_ready(zlogp, B_FALSE)) == 0) + if ((rval = zone_ready(zlogp, Z_MNT_BOOT)) == 0) rval = zone_bootup(zlogp, zargp->bootbuf); audit_put_record(zlogp, uc, rval, "boot"); if (rval != 0) { @@ -1089,7 +1089,9 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, break; } - rval = zone_ready(zlogp, B_TRUE); + rval = zone_ready(zlogp, + strcmp(zargp->bootbuf, "-U") == 0 ? + Z_MNT_UPDATE : Z_MNT_SCRATCH); if (rval != 0) break; @@ -1209,7 +1211,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case Z_READY: if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE)) != 0) break; - if ((rval = zone_ready(zlogp, B_FALSE)) == 0) + if ((rval = zone_ready(zlogp, Z_MNT_BOOT)) == 0) eventstream_write(Z_EVT_ZONE_READIED); else eventstream_write(Z_EVT_ZONE_HALTED); @@ -1238,7 +1240,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, boot_args[0] = '\0'; break; } - if ((rval = zone_ready(zlogp, B_FALSE)) != 0) { + if ((rval = zone_ready(zlogp, Z_MNT_BOOT)) != 0) { eventstream_write(Z_EVT_ZONE_BOOTFAILED); boot_args[0] = '\0'; break; diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h index d8612b73b9..f7e2114666 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.h +++ b/usr/src/cmd/zoneadmd/zoneadmd.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. */ @@ -111,10 +111,23 @@ extern int eventstream_init(); extern void eventstream_write(zone_evt_t evt); /* + * Zone mount styles. Boot is the standard mount we do when booting the zone, + * scratch is the standard scratch zone mount for upgrade and update is a + * variation on the scratch zone where we don't lofs mount the zone's /etc + * and /var back into the scratch zone so that we can then do an + * 'update on attach' within the scratch zone. + */ +typedef enum { + Z_MNT_BOOT = 0, + Z_MNT_SCRATCH, + Z_MNT_UPDATE +} zone_mnt_t; + +/* * Virtual platform interfaces. */ -extern zoneid_t vplat_create(zlog_t *, boolean_t); -extern int vplat_bringup(zlog_t *, boolean_t, zoneid_t); +extern zoneid_t vplat_create(zlog_t *, zone_mnt_t); +extern int vplat_bringup(zlog_t *, zone_mnt_t, zoneid_t); extern int vplat_teardown(zlog_t *, boolean_t, boolean_t); /* diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h index 2eaf2e218a..ec122addda 100644 --- a/usr/src/head/libzonecfg.h +++ b/usr/src/head/libzonecfg.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,6 +50,7 @@ extern "C" { #include <zone.h> #include <libbrand.h> #include <sys/uuid.h> +#include <libuutil.h> #define ZONE_ID_UNDEFINED -1 @@ -239,6 +240,13 @@ struct zone_devpermtab { char *zone_devperm_acl; }; +typedef struct { + uu_avl_node_t zpe_entry; + char *zpe_name; + char *zpe_vers; + uu_avl_t *zpe_patches_avl; +} zone_pkg_entry_t; + typedef enum zone_iptype { ZS_SHARED, ZS_EXCLUSIVE @@ -457,12 +465,8 @@ extern int zonecfg_getdsent(zone_dochandle_t, struct zone_dstab *); extern int zonecfg_enddsent(zone_dochandle_t); extern int zonecfg_getpsetent(zone_dochandle_t, struct zone_psettab *); extern int zonecfg_getmcapent(zone_dochandle_t, struct zone_mcaptab *); -extern int zonecfg_setpkgent(zone_dochandle_t); -extern int zonecfg_getpkgent(zone_dochandle_t, struct zone_pkgtab *); -extern int zonecfg_endpkgent(zone_dochandle_t); -extern int zonecfg_setpatchent(zone_dochandle_t); -extern int zonecfg_getpatchent(zone_dochandle_t, struct zone_patchtab *); -extern int zonecfg_endpatchent(zone_dochandle_t); +extern int zonecfg_getpkgdata(zone_dochandle_t, uu_avl_pool_t *, + uu_avl_t *); extern int zonecfg_setdevperment(zone_dochandle_t); extern int zonecfg_getdevperment(zone_dochandle_t, struct zone_devpermtab *); diff --git a/usr/src/lib/brand/native/zone/Makefile b/usr/src/lib/brand/native/zone/Makefile index 6912c2dd0f..1d1a92fe92 100644 --- a/usr/src/lib/brand/native/zone/Makefile +++ b/usr/src/lib/brand/native/zone/Makefile @@ -20,14 +20,14 @@ # # -# Copyright 2006 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" # BRAND= native -PROGS= postclone +PROGS= postclone attach_update XMLDOCS= config.xml platform.xml TEMPLATES= SUNWdefault.xml SUNWblank.xml diff --git a/usr/src/lib/brand/native/zone/attach_update.ksh b/usr/src/lib/brand/native/zone/attach_update.ksh new file mode 100755 index 0000000000..1cc7f87650 --- /dev/null +++ b/usr/src/lib/brand/native/zone/attach_update.ksh @@ -0,0 +1,626 @@ +#!/bin/ksh -p +# +# 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. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# This script runs within the scratch zone and updates the dependent files and +# pkg metadata within the zone to match the global zone. +# +# The algorithm for this is as follows: +# 1) Build the list of editable files and files to install based on the +# dependent pkgs. This is done in get_ed_cp_list(). +# Editable files are saved in gz_ed_list. +# Files to install are saved in gz_cp_list +# get_ed_cp_list() makes a pass through the global zone's contents file. +# 2) Build the list of files to remove in get_rm_list(). +# Files to remove are saved in rmlist. +# The zone's editable files are saved in ngz_ed_list. +# At the end we figure out which zone editable files are obsolete and those +# are added to the rmlist. +# get_rm_list() makes a pass through the non-global zone's contents file. +# 3) Remove the files listed in remove_files +# 4) Remove the old pkg metadata listed in remove_pkg +# 5) Copy files from the scratch zone (i.e. global zone) into the zone using +# the gz_cp_list file. Do this by running cpio. +# 6) Update the pkg metadata for the new pkgs using add_pkg. +# 7) Fix the editable file entries in the contents file using installf. +# +# We don't have to remove or add the pkg's in dependency order since we are +# not doing normal pkgrm or pkgadd operations. We are simply updating pkg +# metadata with those commands. The actual files will already be installed +# separately from the base files in the global zone using the same approach we +# use when we install a fresh zone. +# +# The script uses the following data files during the update process: +# pkg_rm - list of pkgs to remove - provided by zoneadm +# pkg_add - list of pkgs to add - provided by zoneadm +# inherited - list of ipd's - provided by zoneadm +# +# gz_ed_list - generated in get_ed_cp_list - GZ editable files +# gz_cp_list - generated in get_ed_cp_list - files to copy from GZ +# rmlist - generated in get_rm_list - list of file to remove +# ngz_ed_list - generated in get_rm_list - NGZ editable files +# These files are stored in the scratch zone /tmp so there won't be any +# name conflicts with existing tmp files. +# + +fatal() +{ + printf "${e_fatal}\n" "$@" >>$LOGFILE + printf "${e_fatal}\n" "$@" + printf "${v_loginfo}\n" + exit 1 +} + +verbose_log() +{ + printf "$@" >>$LOGFILE + printf "$@" +} + +# Clean up on interrupt +trap_cleanup() +{ + printf "${e_intr}\n" >>$LOGFILE + printf "${e_intr}\n" + exit 1 +} + +save_to_log() +{ + FILENAME=$1 + + echo "***** $FILENAME *****" >>$LOGFILE + cat /tmp/$FILENAME >>$LOGFILE + echo >>$LOGFILE +} + +# 1) Build the list of editable files and files to install based on the +# dependent pkgs. +# Processes the GZ contents file. +get_ed_cp_list() +{ + nawk ' + BEGIN { + # Get the list of pkgs to add and see if they are hollow pkgs. + while (getline p <"/tmp/pkg_add" > 0) { + pkgs[p] = 1; + + pkginfo="/var/sadm/pkg/" p "/pkginfo"; + while (getline pi <pkginfo > 0) { + if (pi ~ "SUNW_PKG_HOLLOW=true") { + hollow[p] = 1; + break; + } + } + close(pkginfo); + } + close("/tmp/pkg_add"); + + load_inherited(); + ndirs=0; + } + { + # Read entries in the contents file, figure out what kind of + # entry this is and where the pkg data is. + # tp indicates what type of entry this is: + # editable or volatile files are tp == 0 + # files are tp == 1 + # dirs are tp == 2 + # symlinks or hardlinks are tp == 1 + # fld is the field where the pkg names begin. + # nm is the file/dir entry name. + if ($2 == "e" || $2 == "v") { + fld=10; + nm=$1; + tp=0; + } else if ($2 == "f") { + fld=10; + nm=$1; + tp=1; + } else if ($2 == "d") { + fld=7; + nm=$1; + tp=2; + } else if ($2 == "s" || $2 == "l") { + fld=4; + split($1, a, "="); + nm=a[1]; + tp=1; + } else { + next; + } + + # Skip it if it is in an ipd. + if (is_inherited(nm)) + next; + + # Determine if this entry is part of a pkg to install + # and if it is in a hollow pkg. + installpkg = 0; + nhollow = 0; + for (i = fld; i <= NF; i++) { + pname = get_pkg_name($i) + + if (pkgs[pname] == 1) + installpkg = 1; + if (hollow[pname] == 1) + nhollow++; + } + + if (installpkg == 0) + next; + + # If this entry is only in hollow pkgs, skip it. + if (nhollow >= (NF - fld + 1)) + next; + + if (tp == 0) { + # editable or volatile file + printf("/a%s\n", nm) >>"/tmp/gz_ed_list"; + } else if (tp == 1) { + # regular file or link + printf("%s\n", nm) > "/tmp/gz_cp_list"; + } else { + # directory + dirs[ndirs++] = nm; + } + } + END { + for (i = ndirs - 1; i >= 0; i--) + printf("%s\n", dirs[i]) > "/tmp/gz_cp_list"; + } + + # Get the list of inherited directories. + # We are creating an array of regular expression matches here. + function load_inherited() { + nint=0 + while (getline p <"/tmp/inherited" > 0) { + inherited[nint] = "^" p "/"; + nint++; + inherited[nint] = "^" p "$"; + nint++; + } + close("/tmp/inherited"); + } + + # Check if this entry is in an inherited-pkg-dir. + function is_inherited(nm) { + for (i = 0; i < nint; i++) { + if (nm ~ inherited[i]) + return (1); + } + return (0); + } + + # Get the clean pkg name from the fld entry. + function get_pkg_name(fld) { + # Remove any pkg control prefix (e.g. *, !) + first = substr(fld, 1, 1) + if (match(first, /[A-Za-z]/)) { + pname = fld + } else { + pname = substr(fld, 2) + } + + # Then remove any class action script name + pos = index(pname, ":") + if (pos != 0) + pname = substr(pname, 1, pos - 1) + + return (pname) + } + + ' /var/sadm/install/contents || fatal "get_ed_cp_list" +} + +create_admin_file() +{ + cat <<-EOF > /tmp/admin.dflt || fatal "create_admin_file" + mail= + instance=overwrite + partial=nocheck + runlevel=nocheck + idepend=nocheck + rdepend=nocheck + space=nocheck + setuid=nocheck + conflict=nocheck + action=nocheck + basedir=default + EOF +} + +# 2) Build the list of files to remove. +# Similar structure to get_ed_cp_list() but we're processing the NGZ contents +# file. +get_rm_list() +{ + nawk ' + BEGIN { + while (getline p <"/tmp/pkg_rm" > 0) + pkgs[p] = 1; + close("/tmp/pkg_rm"); + load_inherited(); + } + { + # fld is the field where the pkg names begin. + # nm is the file/dir entry name. + # rm is set if we should remove the entry. + if ($2 == "e" || $2 == "v") { + fld=10; + nm=$1; + rm=0; + } else if ($2 == "f") { + fld=10; + nm=$1; + rm=1; + } else if ($2 == "d") { + fld=7; + nm=$1; + rm=1; + } else if ($2 == "s" || $2 == "l") { + fld=4; + split($1, a, "="); + nm=a[1]; + rm=1; + } else { + next; + } + + # Skip it if it is in an ipd. + if (is_inherited(nm)) + next; + + # Check if this entry is part of a pkg to remove. Files, + # including editable files, can be delivered by multiple pkgs. + # We should only add this entry to the rm list or ed list if + # all of the pkgs delivering this entry are being removed. + for (i = fld; i <= NF; i++) { + pname = get_pkg_name($i) + + # If this is in a pkg we are not removing, we are done. + if (pkgs[pname] == 0) + next; + } + + if (rm == 1) + printf("/a%s\n", nm) >>"/tmp/rmlist"; + else + printf("/a%s\n", nm) >>"/tmp/ngz_ed_list"; + + } + + # Get the list of inherited directories. + # We are creating an array of regular expression matches here. + function load_inherited() { + nint=0 + while (getline p <"/tmp/inherited" > 0) { + inherited[nint] = "^" p "/"; + nint++; + inherited[nint] = "^" p "$"; + nint++; + } + close("/tmp/inherited"); + } + + # Check if this entry is in an inherited-pkg-dir. + function is_inherited(nm) { + for (i = 0; i < nint; i++) { + if (nm ~ inherited[i]) + return (1); + } + return (0); + } + + # Get the clean pkg name from the fld entry. + function get_pkg_name(fld) { + # Remove any pkg control prefix (e.g. *, !) + first = substr(fld, 1, 1) + if (match(first, /[A-Za-z]/)) { + pname = fld + } else { + pname = substr(fld, 2) + } + + # Then remove any class action script name + pos = index(pname, ":") + if (pos != 0) + pname = substr(pname, 1, pos - 1) + + return (pname) + } + + ' /a/var/sadm/install/contents || fatal "get_rm_list" + + # Add the obsolete editable files to the rm list. + # comm assumes the files are sorted. Since the contents file is + # sorted and we wrote the files as we processed the contents file, + # these files are already sorted. + comm -13 /tmp/gz_ed_list /tmp/ngz_ed_list >>/tmp/rmlist || \ + fatal "get_rm_list" +} + +remove_files() +{ + for path in `cat /tmp/rmlist` + do + if [ "$path" != "" ] ; then + # Check for symlink first since -d follows links. + if [ -h $path ]; then + rm -f $path || fatal "remove_files" + elif [ -d $path ]; then + # ignore errs since dir might not be empty + rmdir $path >/dev/null 2>&1 + else + rm -f $path || fatal "remove_files" + fi + fi + done +} + +remove_pkg() +{ + PKG=$1 + NUM=$2 + CNT=$3 + + # pkgremove options: + # -a same as public pkgrm option + # -F private - used by upgrade to suppress actual removal of files + # delivered by the pkg + # -M same as public pkgrm option + # -n same as public pkgrm option + # -O inherited-filesystem={IPD} private - used to specify the zone's + # inherited-pkg-dir entries + # -R same as public pkgrm option + /usr/sadm/install/bin/pkgremove -R /a -M -F -a /tmp/admin.dflt -n \ + $IPDS $PKG >>$LOGFILE 2>&1 + errcode=$? + printf "${v_rmpkgs}" $NUM $CNT + # errcode 99 means the pkg doesn't exist. + if [ $errcode -ne 0 -a $errcode -ne 99 ]; then + ERR_PKGS=`echo $ERR_PKGS $PKG` + fi +} + +add_pkg() +{ + PKG=$1 + NUM=$2 + CNT=$3 + + echo "===== ${PKG} ====" >>$LOGFILE + # pkginstall options: + # -a same as public pkgrm option + # -C private - disable checksums since files are installed via a + # separate copy from the global zone + # -h private - enable hollow pkg support + # -N pkgadd private - error msgs use the name "pkgadd" instead + # of "pkginstall" + # -n same as public pkgrm option + # -O addzonename private - error msgs include zonename + # -O inherited-filesystem={IPD} private - used to specify the zone's + # inherited-pkg-dir entries + # -R same as public pkgrm option + # -S private - suppress copyright output + # -t private - suppress spooled pkg creation + # -z private - install zone pkg data from spooled pkg data + /usr/sadm/install/bin/pkginstall -S -A -C -N pkgadd -R /a \ + -a /etc/lu/zones_pkgadd_admin -h -n -t -z $IPDS -O addzonename \ + /var/sadm/pkg/${PKG}/save/pspool $PKG >>$LOGFILE 2>&1 + errcode=$? + printf "${v_addpkgs}" $NUM $CNT + if [ $errcode -ne 0 ]; then + ERR_PKGS=`echo $ERR_PKGS $PKG` + fi +} + +# installf the editable file so that the pkg metadata is correct. +finalize() +{ + nawk -v lf=$LOGFILE ' + BEGIN { + logfile = " >>" lf " 2>&1" + + while (getline e <"/tmp/ngz_ed_list" > 0) { + # remove /a/ prefix from the name + nm=substr(e, 3) + ed_path[nm] = 1; + } + close("/tmp/ngz_ed_list"); + } + { + if ($2 != "e" && $2 != "v") + next; + + # For the contents file format: + # editable and volatile entry pkg names start at field 10 + # $1 is filename, $2 is type (e or v), $3 is class name + # That is: + # installf -R /a -c class pkgname filename type + for (i = 10; i <= NF; i++) { + if (ed_path[$1] == 1) { + pname = get_pkg_name($i) + + pkg[pname] = 1; + printf("%s\n", $1); + basecmd = "/usr/sbin/installf -R /a -c " + cmd = basecmd $3 " " pname " " $1 " " $2 logfile + if (system(cmd) != 0) + printf("ERROR: %s\n", cmd); + } + } + } + END { + for (p in pkg) { + printf("Finalize %s\n", p); + cmd = "/usr/sbin/installf -R /a -f " p logfile + if (system(cmd) != 0) + printf("ERROR: %s\n", cmd); + } + } + + # Get the clean pkg name from the fld entry. + function get_pkg_name(fld) { + # Remove any pkg control prefix (e.g. *, !) + first = substr(fld, 1, 1) + if (match(first, /[A-Za-z]/)) { + pname = fld + } else { + pname = substr(fld, 2) + } + + # Then remove any class action script name + pos = index(pname, ":") + if (pos != 0) + pname = substr(pname, 1, pos - 1) + + return (pname) + } + + ' /a/var/sadm/install/contents >>$LOGFILE || fatal "finalize" +} + +PATH=/sbin:/usr/bin:/usr/sbin; export PATH + +SUNW_PKG_INSTALL_ZONENAME=$1 +export SUNW_PKG_INSTALL_ZONENAME + +# Setup i18n output +TEXTDOMAIN="SUNW_OST_OSCMD" +export TEXTDOMAIN + +v_gathering=$(gettext "Getting the list of files to remove") +v_rmfiles=$(gettext "Removing %d files") +v_rmpkgs=$(gettext "Remove %d of %d packages\r") +v_instfiles=$(gettext "Installing %d files") +v_addpkgs=$(gettext "Add %d of %d packages\r") +v_updating=$(gettext "Updating editable files") +v_loginfo=$(gettext "The file </var/sadm/system/logs/update_log> within the zone contains a log of the zone update.") +e_intr=$(gettext "update cancelled due to interrupt") +e_rmpkgs=$(gettext "Problems removing the following pkgs: %s") +e_instkgs=$(gettext "Installation of these packages generated warnings: %s") +e_fatal=$(gettext "ERROR: zone update fatal error at: %s") + +LOGFILE=/a/var/sadm/system/logs/update_log + +if [ -f $LOGFILE ]; then + tmpnm=$LOGFILE.`date +%y%m%d-%H:%M:%S` + mv $LOGFILE $tmpnm || fatal "backup log file" +fi + +trap trap_cleanup INT + +echo "`date`" >$LOGFILE +echo >>$LOGFILE + +# Save file lists to LOGFILE +save_to_log inherited +save_to_log pkg_rm +save_to_log pkg_add + +printf "${v_gathering}\n" + +# Make sure we have these files, even though they might be empty. +touch /tmp/gz_ed_list /tmp/ngz_ed_list /tmp/gz_cp_list || fatal "touch files" + +# Get the list of editable files for the dependent pkgs. We do this to make +# sure we delete obsolete editable files as part of the removal of the files +# within the zone. In the same pass through the contents file we get the +# list of files to copy into the zone. +get_ed_cp_list + +save_to_log gz_cp_list +save_to_log gz_ed_list + +get_rm_list +sort -r -o /tmp/rmlist /tmp/rmlist || fatal "sort rmlist" + +save_to_log ngz_ed_list +save_to_log rmlist + +CNT=`wc -l /tmp/rmlist | nawk '{print $1}'` +verbose_log "${v_rmfiles}\n" $CNT +remove_files + +IPDS="" +for i in `cat /tmp/inherited` +do + IPDS=`echo $IPDS -O inherited-filesystem=$i` +done + +create_admin_file + +echo "***** remove_pkg *****" >>$LOGFILE + +ERR_PKGS="" +CNT=`wc -l /tmp/pkg_rm | nawk '{print $1}'` +num=1 +for i in `cat /tmp/pkg_rm` +do + remove_pkg $i $num $CNT + num=`expr $num + 1` +done + +echo +if [ -n "$ERR_PKGS" ]; then + verbose_log "${e_rmpkgs}\n" "$ERR_PKGS" +fi + +echo >>$LOGFILE + +CNT=`wc -l /tmp/gz_cp_list | nawk '{print $1}'` +verbose_log "${v_instfiles}\n" $CNT +echo "***** cpio *****" >>$LOGFILE +cpio -pcdmu /a < /tmp/gz_cp_list >>$LOGFILE 2>&1 + +echo >>$LOGFILE +echo "***** add_pkg *****" >>$LOGFILE + +ERR_PKGS="" +CNT=`wc -l /tmp/pkg_add | nawk '{print $1}'` +num=1 +for i in `cat /tmp/pkg_add` +do + add_pkg $i $num $CNT + num=`expr $num + 1` +done + +echo +if [ -n "$ERR_PKGS" ]; then + verbose_log "${e_instkgs}\n" "$ERR_PKGS" +fi + +echo >>$LOGFILE +echo "***** finalize *****" >>$LOGFILE + +verbose_log "${v_updating}\n" +finalize + +verbose_log "${v_loginfo}\n" + +exit 0 diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index 6fac6b36f9..94644741dd 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.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. */ @@ -89,6 +89,7 @@ #define DTD_ELEM_MCAP (const xmlChar *) "mcap" #define DTD_ELEM_PACKAGE (const xmlChar *) "package" #define DTD_ELEM_PATCH (const xmlChar *) "patch" +#define DTD_ELEM_OBSOLETES (const xmlChar *) "obsoletes" #define DTD_ELEM_DEV_PERM (const xmlChar *) "dev-perm" #define DTD_ATTR_ACTION (const xmlChar *) "action" @@ -226,9 +227,15 @@ 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; @@ -6504,94 +6511,214 @@ zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr) return (err); } +/* + * Get the full tree of pkg/patch metadata in a set of nested AVL trees. + * pkgs_avl is an AVL tree of pkgs. Each pkg element contains a + * zpe_patches_avl member which holds an AVL tree of patches for that pkg. + * The patch elements have the same zpe_patches_avl member, each of which can + * hold an AVL tree of patches that are obsoleted by the patch. + * + * The zone xml data contains DTD_ELEM_PACKAGE elements, followed by + * DTD_ELEM_PATCH elements. The DTD_ELEM_PATCH patch element applies to the + * DTD_ELEM_PACKAGE that precedes it. The DTD_ELEM_PATCH element may have + * child DTD_ELEM_OBSOLETES nodes associated with it. The DTD_ELEM_PACKAGE + * really should have had the DTD_ELEM_PATCH elements as children but it + * was not defined that way initially so we are stuck with the DTD definition + * now. However, we can safely assume the ordering for compatibility. + */ int -zonecfg_setpkgent(zone_dochandle_t handle) -{ - return (zonecfg_setent(handle)); -} - -int -zonecfg_getpkgent(zone_dochandle_t handle, struct zone_pkgtab *tabptr) +zonecfg_getpkgdata(zone_dochandle_t handle, uu_avl_pool_t *pkg_pool, + uu_avl_t *pkgs_avl) { xmlNodePtr cur; - int err; + int res; + zone_pkg_entry_t *pkg; + char name[MAXNAMELEN]; + char version[ZONE_PKG_VERSMAX]; if (handle == NULL) return (Z_INVAL); - if ((cur = handle->zone_dh_cur) == NULL) - return (Z_NO_ENTRY); + if ((res = zonecfg_setent(handle)) != Z_OK) + return (res); - for (; cur != NULL; cur = cur->next) - if (!xmlStrcmp(cur->name, DTD_ELEM_PACKAGE)) - break; - if (cur == NULL) { - handle->zone_dh_cur = handle->zone_dh_top; - return (Z_NO_ENTRY); + if ((cur = handle->zone_dh_cur) == NULL) { + res = Z_NO_ENTRY; + goto done; } - if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_pkg_name, - sizeof (tabptr->zone_pkg_name))) != Z_OK) { - handle->zone_dh_cur = handle->zone_dh_top; - return (err); - } + for (; cur != NULL; cur = cur->next) { + if (xmlStrcmp(cur->name, DTD_ELEM_PACKAGE) == 0) { + uu_avl_index_t where; - if ((err = fetchprop(cur, DTD_ATTR_VERSION, tabptr->zone_pkg_version, - sizeof (tabptr->zone_pkg_version))) != Z_OK) { - handle->zone_dh_cur = handle->zone_dh_top; - return (err); - } + if ((res = fetchprop(cur, DTD_ATTR_NAME, name, + sizeof (name))) != Z_OK) + goto done; - handle->zone_dh_cur = cur->next; - return (Z_OK); -} + if ((res = fetchprop(cur, DTD_ATTR_VERSION, version, + sizeof (version))) != Z_OK) + goto done; -int -zonecfg_endpkgent(zone_dochandle_t handle) -{ - return (zonecfg_endent(handle)); -} + if ((pkg = (zone_pkg_entry_t *) + malloc(sizeof (zone_pkg_entry_t))) == NULL) { + res = Z_NOMEM; + goto done; + } -int -zonecfg_setpatchent(zone_dochandle_t handle) -{ - return (zonecfg_setent(handle)); -} + if ((pkg->zpe_name = strdup(name)) == NULL) { + free(pkg); + res = Z_NOMEM; + goto done; + } -int -zonecfg_getpatchent(zone_dochandle_t handle, struct zone_patchtab *tabptr) -{ - xmlNodePtr cur; - int err; + if ((pkg->zpe_vers = strdup(version)) == NULL) { + free(pkg->zpe_name); + free(pkg); + res = Z_NOMEM; + goto done; + } - if (handle == NULL) - return (Z_INVAL); + pkg->zpe_patches_avl = NULL; - if ((cur = handle->zone_dh_cur) == NULL) - return (Z_NO_ENTRY); + uu_avl_node_init(pkg, &pkg->zpe_entry, pkg_pool); + if (uu_avl_find(pkgs_avl, pkg, NULL, &where) != NULL) { + free(pkg->zpe_name); + free(pkg->zpe_vers); + free(pkg); + } else { + uu_avl_insert(pkgs_avl, pkg, where); + } - for (; cur != NULL; cur = cur->next) - if (!xmlStrcmp(cur->name, DTD_ELEM_PATCH)) - break; - if (cur == NULL) { - handle->zone_dh_cur = handle->zone_dh_top; - return (Z_NO_ENTRY); - } + } else if (xmlStrcmp(cur->name, DTD_ELEM_PATCH) == 0) { + zone_pkg_entry_t *patch; + uu_avl_index_t where; + char *p; + char *dashp = NULL; + xmlNodePtr child; - if ((err = fetchprop(cur, DTD_ATTR_ID, tabptr->zone_patch_id, - sizeof (tabptr->zone_patch_id))) != Z_OK) { - handle->zone_dh_cur = handle->zone_dh_top; - return (err); - } + if ((res = fetchprop(cur, DTD_ATTR_ID, name, + sizeof (name))) != Z_OK) + goto done; - handle->zone_dh_cur = cur->next; - return (Z_OK); -} + if ((patch = (zone_pkg_entry_t *) + malloc(sizeof (zone_pkg_entry_t))) == NULL) { + res = Z_NOMEM; + goto done; + } -int -zonecfg_endpatchent(zone_dochandle_t handle) -{ - return (zonecfg_endent(handle)); + if ((p = strchr(name, '-')) != NULL) { + dashp = p; + *p++ = '\0'; + } else { + p = ""; + } + + if ((patch->zpe_name = strdup(name)) == NULL) { + free(patch); + res = Z_NOMEM; + goto done; + } + + if ((patch->zpe_vers = strdup(p)) == NULL) { + free(patch->zpe_name); + free(patch); + res = Z_NOMEM; + goto done; + } + + if (dashp != NULL) + *dashp = '-'; + + patch->zpe_patches_avl = NULL; + + if (pkg->zpe_patches_avl == NULL) { + pkg->zpe_patches_avl = uu_avl_create(pkg_pool, + NULL, UU_DEFAULT); + if (pkg->zpe_patches_avl == NULL) { + free(patch->zpe_name); + free(patch->zpe_vers); + free(patch); + res = Z_NOMEM; + goto done; + } + } + + uu_avl_node_init(patch, &patch->zpe_entry, pkg_pool); + if (uu_avl_find(pkg->zpe_patches_avl, patch, NULL, + &where) != NULL) { + free(patch->zpe_name); + free(patch->zpe_vers); + free(patch); + } else { + uu_avl_insert(pkg->zpe_patches_avl, patch, + where); + } + + /* Add any patches this patch obsoletes. */ + for (child = cur->xmlChildrenNode; child != NULL; + child = child->next) { + zone_pkg_entry_t *obs; + + if (xmlStrcmp(child->name, DTD_ELEM_OBSOLETES) + != 0) + continue; + + if ((res = fetchprop(child, DTD_ATTR_ID, + name, sizeof (name))) != Z_OK) + goto done; + + if ((obs = (zone_pkg_entry_t *)malloc( + sizeof (zone_pkg_entry_t))) == NULL) { + res = Z_NOMEM; + goto done; + } + + if ((obs->zpe_name = strdup(name)) == NULL) { + free(obs); + res = Z_NOMEM; + goto done; + } + /* + * The version doesn't matter for obsoleted + * patches. + */ + obs->zpe_vers = NULL; + obs->zpe_patches_avl = NULL; + + /* + * If this is the first obsolete patch, add an + * AVL tree to the parent patch element. + */ + if (patch->zpe_patches_avl == NULL) { + patch->zpe_patches_avl = + uu_avl_create(pkg_pool, NULL, + UU_DEFAULT); + if (patch->zpe_patches_avl == NULL) { + free(obs->zpe_name); + free(obs); + res = Z_NOMEM; + goto done; + } + } + + /* Insert obsolete patch into the AVL tree. */ + uu_avl_node_init(obs, &obs->zpe_entry, + pkg_pool); + if (uu_avl_find(patch->zpe_patches_avl, obs, + NULL, &where) != NULL) { + free(obs->zpe_name); + free(obs); + } else { + uu_avl_insert(patch->zpe_patches_avl, + obs, where); + } + } + } + } + +done: + (void) zonecfg_endent(handle); + return (res); } int @@ -7065,13 +7192,43 @@ dir_pkg(char *pkg_name, char **pkg_list, int cnt) } /* + * 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_avl) +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; @@ -7085,15 +7242,17 @@ save_obs_patch(char *num, uu_avl_pool_t *patches_pool, } patch->patch_vers = NULL; + patch->obs_patches = NULL; + uu_avl_node_init(patch, &patch->patch_node, patches_pool); - if (uu_avl_find(obs_patches_avl, patch, NULL, &where) != NULL) { + if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL) { free(patch->patch_num); free(patch); return (Z_OK); } - uu_avl_insert(obs_patches_avl, patch, where); + uu_avl_insert(obs_patches, patch, where); return (Z_OK); } @@ -7134,11 +7293,11 @@ save_patch(patch_node_t *patch, uu_avl_t *patches_avl) * 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_avl) +obsolete_patch(patch_node_t *patch, uu_avl_t *obs_patches) { uu_avl_index_t where; - if (uu_avl_find(obs_patches_avl, patch, NULL, &where) != NULL) + if (uu_avl_find(obs_patches, patch, NULL, &where) != NULL) return (B_TRUE); return (B_FALSE); @@ -7172,12 +7331,13 @@ patch_node_compare(const void *l_arg, const void *r_arg, void *private) * 119255-06 Incompatibles: * * A backed out patch will have "backed out\n" as the status. We should - * skip these patches. We also also ignore any entries that seem to be - * corrupted. + * 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_avl) + uu_avl_t *obs_patches, uu_list_pool_t *list_pool) { char *p; char *lastp; @@ -7222,6 +7382,7 @@ parse_info(char *patchinfo, uu_avl_pool_t *patches_pool, uu_avl_t *patches_avl, free(patch); return (Z_NOMEM); } + patch->obs_patches = NULL; uu_avl_node_init(patch, &patch->patch_node, patches_pool); save_patch(patch, patches_avl); @@ -7251,7 +7412,17 @@ parse_info(char *patchinfo, uu_avl_pool_t *patches_pool, uu_avl_t *patches_avl, if ((pvers = strchr(p, '-')) != NULL) *pvers = '\0'; - if (save_obs_patch(p, patches_pool, obs_patches_avl) != Z_OK) + /* + * 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); @@ -7294,6 +7465,21 @@ add_patch(void *e, void *p) 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); + } + } + return (UU_WALK_NEXT); } @@ -7308,6 +7494,19 @@ patch_avl_delete(uu_avl_t *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); } @@ -7321,7 +7520,8 @@ patch_avl_delete(uu_avl_t *patches_avl) */ static int add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop, - uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches_avl) + uu_avl_pool_t *patches_pool, uu_avl_t *obs_patches, + uu_list_pool_t *list_pool) { int i; int res; @@ -7334,13 +7534,13 @@ add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop, for (i = 0; i < infop->zpi_patch_cnt; i++) { if ((res = parse_info(infop->zpi_patchinfo[i], patches_pool, - patches_avl, obs_patches_avl)) != Z_OK) { + patches_avl, obs_patches, list_pool)) != Z_OK) { patch_avl_delete(patches_avl); return (res); } } - args.obs_patches_avl = obs_patches_avl; + args.obs_patches_avl = obs_patches; args.handle = handle; args.res = Z_OK; @@ -7351,6 +7551,44 @@ add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop, } /* + * 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); +} + +/* * Add the pkg to the sw inventory on the handle. */ static int @@ -7462,6 +7700,114 @@ get_pkginfo(char *pkginfo, struct zone_pkginfo *infop) } /* + * 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 = 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_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) +{ + 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: @@ -7477,7 +7823,13 @@ get_pkginfo(char *pkginfo, struct zone_pkginfo *infop) * 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) @@ -7490,32 +7842,56 @@ zonecfg_sw_inventory(zone_dochandle_t handle) struct zone_pkginfo info; int pkg_cnt = 0; char **pkgs = NULL; - uu_avl_pool_t *patches_pool; - uu_avl_t *obs_patches_avl; + 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; + } 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) { - return (Z_NOMEM); + res = Z_NOMEM; + goto done; } - if ((obs_patches_avl = uu_avl_create(patches_pool, NULL, UU_DEFAULT)) + 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) { - uu_avl_pool_destroy(patches_pool); - return (Z_NOMEM); + res = Z_NOMEM; + goto done; } if ((res = get_ipd_pkgs(handle, &pkgs, &pkg_cnt)) != Z_OK) { - patch_avl_delete(obs_patches_avl); - uu_avl_pool_destroy(patches_pool); - return (res); + res = Z_NOMEM; + goto done; } if ((dirp = opendir(PKG_PATH)) == NULL) { - patch_avl_delete(obs_patches_avl); - uu_avl_pool_destroy(patches_pool); - free_ipd_pkgs(pkgs, pkg_cnt); - return (Z_OK); + res = Z_NOMEM; + goto done; } while ((dp = readdir(dirp)) != (struct dirent *)0) { @@ -7536,12 +7912,21 @@ zonecfg_sw_inventory(zone_dochandle_t handle) if (!info.zpi_this_zone && (info.zpi_all_zones || - dir_pkg(dp->d_name, pkgs, pkg_cnt))) { - if ((res = add_pkg(handle, dp->d_name, + 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 = 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_avl); + patches_pool, obs_patches, + list_pool); } } @@ -7553,8 +7938,15 @@ zonecfg_sw_inventory(zone_dochandle_t handle) (void) closedir(dirp); - patch_avl_delete(obs_patches_avl); - uu_avl_pool_destroy(patches_pool); +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) diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers index 384641b1a7..e36565f19d 100644 --- a/usr/src/lib/libzonecfg/common/mapfile-vers +++ b/usr/src/lib/libzonecfg/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" @@ -80,8 +80,6 @@ SUNWprivate_1.1 { zonecfg_endfsent; zonecfg_endipdent; zonecfg_endnwifent; - zonecfg_endpatchent; - zonecfg_endpkgent; zonecfg_endrctlent; zonecfg_find_mounts; zonecfg_find_scratch; @@ -112,8 +110,7 @@ SUNWprivate_1.1 { zonecfg_get_name; zonecfg_get_name_by_uuid; zonecfg_getnwifent; - zonecfg_getpatchent; - zonecfg_getpkgent; + zonecfg_getpkgdata; zonecfg_get_pool; zonecfg_get_privset; zonecfg_getpsetent; @@ -178,8 +175,6 @@ SUNWprivate_1.1 { zonecfg_set_limitpriv; zonecfg_set_name; zonecfg_setnwifent; - zonecfg_setpatchent; - zonecfg_setpkgent; zonecfg_set_pool; zonecfg_setrctlent; zonecfg_set_root; diff --git a/usr/src/pkgdefs/SUNWzoneu/prototype_com b/usr/src/pkgdefs/SUNWzoneu/prototype_com index 358607fd25..2020693704 100644 --- a/usr/src/pkgdefs/SUNWzoneu/prototype_com +++ b/usr/src/pkgdefs/SUNWzoneu/prototype_com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 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" @@ -52,6 +52,7 @@ d none usr/kernel/drv 755 root sys d none usr/lib 755 root bin d none usr/lib/brand 755 root bin d none usr/lib/brand/native 755 root sys +f none usr/lib/brand/native/attach_update 755 root bin f none usr/lib/brand/native/config.xml 444 root bin f none usr/lib/brand/native/platform.xml 444 root bin f none usr/lib/brand/native/postclone 755 root bin |