sw_support.c revision ff17c8bf86c3e567734be83f90267edee20f580f
/*
* 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
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* sw_support does install, detach and attach processing for svr4 pkgs.
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <wait.h>
#include <zone.h>
#include <locale.h>
#include <libintl.h>
#include <libzonecfg.h>
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <dirent.h>
#include <libxml/xmlmemory.h>
#include <fcntl.h>
#include <door.h>
#include <macros.h>
#include <libgen.h>
#include <fnmatch.h>
#include <strings.h>
#include <libzonecfg.h>
#define ZONE_SUBPROC_OK 0
#define ZONE_SUBPROC_USAGE 253
#define ZONE_SUBPROC_NOTCOMPLETE 254
#define ZONE_SUBPROC_FATAL 255
#define Z_ERR 1
#define Z_USAGE 2
#define Z_FATAL 3
#define SW_CMP_NONE 0x0
#define SW_CMP_SRC 0x01
#define SW_CMP_SILENT 0x02
#define DETACHED "SUNWdetached.xml"
#define ATTACH_FORCED "SUNWattached.xml"
#define CONTENTS_FILE "/var/sadm/install/contents"
#define SUNW_PKG_ALL_ZONES "SUNW_PKG_ALLZONES=true\n"
#define SUNW_PKG_THIS_ZONE "SUNW_PKG_THISZONE=true\n"
#define VERSION "VERSION="
#define PATCHLIST "PATCHLIST="
#define PATCHINFO "PATCH_INFO_"
#define PKGINFO_RD_LEN 128
#define MY_BRAND_NAME "native"
#define EXEC_PREFIX "exec "
/* 0755 is the default directory mode. */
struct zone_pkginfo {
int zpi_patch_cnt;
char *zpi_version;
char **zpi_patchinfo;
};
typedef struct {
char *patch_num;
char *patch_vers;
} patch_node_t;
typedef struct {
char *patch_num;
typedef struct {
int res;
static char *locale;
static char *zonename;
static char *zonepath;
/* used in attach_func() and signal handler */
static volatile boolean_t attach_interupted;
void
{
}
/* PRINTFLIKE1 */
void
{
}
static int
do_subproc(char *cmdbuf)
{
void (*saveint)(int);
void (*saveterm)(int);
void (*savequit)(int);
void (*savehup)(int);
/*
* interactive subprocess we try to launch here.
*/
}
if (child == -1)
return (-1);
;
}
static int
{
return (exit_code);
} else if (WIFSIGNALED(status)) {
char sigstr[SIG2STR_MAX];
sigstr);
} else {
cmd);
}
} else {
}
/*
* Assume a subprocess that died due to a signal or an unknown error
* should be considered an exit code of ZONE_SUBPROC_FATAL, as the
* user will likely need to do some manual cleanup.
*/
return (ZONE_SUBPROC_FATAL);
}
/*
* Maintain a space separated list of unique pkg names. PATH_MAX is used in
* the pkg code as the maximum size for a pkg name.
*/
static int
{
char *tmp;
/* space for str + 2 spaces + NULL */
return (Z_NOMEM);
return (Z_OK);
}
return (Z_OK);
/* space for str + 1 space + NULL */
return (Z_NOMEM);
return (Z_OK);
}
/*
* Process a list of pkgs from an entry in the contents file, adding each pkg
* name to the list of pkgs.
*
* It is possible for the pkg name to be preceeded by a special character
* which indicates some bookkeeping information for pkging. Check if the
* first char is not an Alpha char. If so, skip over it.
*/
static int
{
char *p;
char **tmpp;
int i;
/* skip over any special pkg bookkeeping char */
if (!isalpha(*p)) {
p++;
break;
}
/* Check if the pkg is already in the list */
for (i = 0; i < pkg_cnt; i++) {
break;
}
if (i < pkg_cnt)
continue;
/* The pkg is not in the list; add it. */
break;
}
break;
}
pkg_cnt++;
}
return (res);
}
/*
* Process an entry from the contents file (type "directory"). If the
* directory path is in the list of ipds and is not under a lofs mount within
* the ipd then add the associated list of pkgs to the pkg list. The input
* parameter "entry" will be broken up by the parser within this function so
* its value will be modified when this function exits.
*
* The entries we are looking for will look something like:
* /usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
*/
static int
char **pkg_warn)
{
char *f1;
char *f2;
char *lastp;
int i;
char *nlp;
return (Z_OK);
/* Check if this directory entry is in the list of ipds. */
char wildcard[MAXPATHLEN];
/*
* We want to match on the path and any other directory
* entries under this path. When we use FNM_PATHNAME then
* that means '/' will not be matched by a wildcard (*) so
* we omit FNM_PATHNAME on the call with the wildcard matching.
*/
/* It looks like we do want the pkgs for this path. */
break;
}
}
/* This entry did not match any of the ipds. */
return (Z_OK);
/*
* Check if there is a fs mounted under the ipd. If so, ignore this
* entry.
*/
char wildcard[MAXPATHLEN];
/* We should ignore this path. */
break;
}
}
/* If not null, then we matched an fs mount point so ignore entry. */
return (Z_OK);
/*
* We do want the pkgs for this entry. First, skip over the next 4
* fields in the entry so that we call add_pkg_list starting with the
* pkg names.
*/
;
/* If there are < 4 fields this entry is corrupt, just skip it. */
if (i < 4)
return (Z_OK);
/* strip newline from the line */
if (*nlp == '\n')
*nlp = '\0';
}
/*
* Read an entry from a pkginfo or contents file. Some of these lines can
* either be arbitrarily long or be continued by a backslash at the end of
* the line. This function coalesces lines that are longer than the read
* buffer, and lines that are continued, into one buffer which is returned.
* The caller must free this memory. NULL is returned when we hit EOF or
* if we run out of memory (errno is set to ENOMEM).
*/
static char *
{
char *start;
char *inp;
char *p;
int char_cnt = 0;
errno = 0;
return (NULL);
}
int len;
break;
}
else
break;
}
start = p;
}
}
return (start);
}
static void
{
int i;
for (i = 0; i < cnt; i++)
}
/*
* Get a list of the inherited pkg dirs or fs entries configured for the
* zone. The type parameter will be either ZONE_IPD or ZONE_FS.
*/
static int
{
int res;
struct zone_fstab fstab;
int cnt = 0;
int i;
} else {
}
return (res);
char **p;
break;
}
entries = p;
break;
}
cnt++;
}
(void) zonecfg_endipdent(handle);
else
(void) zonecfg_endfsent(handle);
/* Add a NULL terminating element. */
char **p;
} else {
entries = p;
}
}
for (i = 0; i < cnt; i++)
}
return (res);
}
return (Z_OK);
}
/*
* Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
* list of pkgs that deliver into those dirs.
*/
static int
{
int res;
char **ipds;
char **fss;
int pkg_cnt = 0;
int i;
return (res);
return (res);
}
/* We only have to process the contents file if we have ipds. */
char *buf;
break;
}
"WARNING: package operation in progress "
"on the following packages:\n %s\n"),
pkg_warn);
}
}
}
} else {
}
return (res);
}
/*
* Return true if pkg_name is in the list of pkgs that deliver into an
* inherited pkg directory for the zone.
*/
static boolean_t
{
int i;
for (i = 0; i < cnt; i++) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Keep track of obsoleted patches for this specific patch. We don't need to
* keep track of the patch version since once a patch is obsoleted, all prior
* versions are also obsolete and there won't be any new versions.
*/
static int
{
0)) == NULL)
return (Z_NOMEM);
}
== NULL)
return (Z_NOMEM);
return (Z_NOMEM);
}
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
{
return (Z_NOMEM);
return (Z_NOMEM);
}
return (Z_OK);
}
return (Z_OK);
}
/*
* Keep a list of patches for a pkg. If we see a newer version of a patch,
* we only keep track of the newer version.
*/
static boolean_t
{
/*
* Check if this is a newer version of a patch we already have.
* If it is an older version of a patch we already have, ignore it.
*/
char *endptr;
return (B_FALSE);
/*
* Remove the lower version patch from the tree so we can
* insert the new higher version one. We also discard the
* obsolete patch list from the old version since the new
* version will have its own, likely different, list.
*/
}
}
/*
* Now that the old one is gone, find the new location
* in the tree.
*/
}
return (B_TRUE);
}
/*
* Check if a patch is on the list of obsoleted patches. We don't need to
* check the patch version since once a patch is obsoleted, all prior versions
* are also obsolete and there won't be any new versions.
*/
static boolean_t
{
return (B_TRUE);
return (B_FALSE);
}
/* ARGSUSED */
static int
{
char *endptr;
return (1);
return (-1);
return (0);
}
/*
* Parse the patchinfo string for the patch.
*
* We are parsing entries of the form:
* PATCH_INFO_121454-02=Installed: Wed Dec 7 07:13:51 PST 2005 From: mum \
* Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
* 119255-06 Incompatibles:
*
* A backed out patch will have "backed out\n" as the status. We should
* skip these patches. We also ignore any entries that seem to be
* corrupted. Obsolete patches are saved in the obs_patches parameter
* AVL list.
*/
static int
{
char *p;
char *lastp;
char *ep;
char *pvers;
return (Z_OK);
/* Skip over "PATCH_INFO_" to get the patch id. */
return (Z_OK);
*ep++ = '\0';
/* Ignore all but installed patches. */
return (Z_OK);
/* remove newline */
if (*lastp == '\n')
*lastp = '\0';
return (Z_NOMEM);
*pvers++ = '\0';
else
pvers = "";
return (Z_NOMEM);
}
return (Z_NOMEM);
}
return (Z_OK);
}
/*
* Start with the first token. This will probably be "Installed:".
* If we can't tokenize this entry, just return.
*/
return (Z_OK);
do {
if (strcmp(p, "Installed:") == 0 ||
strcmp(p, "Requires:") == 0 ||
strcmp(p, "From:") == 0 ||
strcmp(p, "Incompatibles:") == 0) {
continue;
} else if (strcmp(p, "Obsoletes:") == 0) {
continue;
}
if (!add_info)
continue;
*pvers = '\0';
/*
* We save all of the obsolete patches in one big list in the
* obs_patches AVL tree so that we know not to output those as
* part of the sw dependencies. However, we also need to save
* the obsolete patch information for this sepcific patch so
* so that we can do the cross manifest patch checking
* correctly.
*/
return (Z_NOMEM);
return (Z_NOMEM);
return (Z_OK);
}
/*
* AVL walker callback used to add patch to XML manifest.
*
*/
static int
avl_add_patch(void *e, void *p)
{
patch = e;
args = p;
/* skip this patch if it has been obsoleted */
return (UU_WALK_NEXT);
else
patch->patch_vers);
!= Z_OK)
return (UU_WALK_DONE);
!= Z_OK)
return (UU_WALK_DONE);
}
}
return (UU_WALK_NEXT);
}
static void
{
if (patches_avl != NULL) {
patch_node_t *p;
free(p->patch_vers);
if (p->obs_patches != NULL) {
}
}
free(p);
}
}
}
/*
* Add the unique, highest version patches that are associated with this pkg
* to the sw inventory on the handle.
*/
static int
{
int i;
int res;
== NULL)
return (Z_NOMEM);
for (i = 0; i < infop->zpi_patch_cnt; i++) {
return (res);
}
}
}
/*
* Keep track of the pkgs we have already processed so that we can quickly
* skip those pkgs while recursively doing dependents.
*/
static boolean_t
{
/*
* 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 *)
return (B_FALSE);
return (B_FALSE);
}
/* Insert pkg into the AVL tree. */
return (B_FALSE);
}
return (B_TRUE);
}
static void
{
if (infop->zpi_patch_cnt > 0) {
int i;
for (i = 0; i < infop->zpi_patch_cnt; i++)
}
}
/*
* Read the pkginfo file and populate the structure with the data we need
* from this pkg for a sw inventory.
*/
static int
{
char *buf;
int err = 0;
infop->zpi_patch_cnt = 0;
return (errno);
int len;
if ((infop->zpi_version =
break;
}
/* remove trailing newline */
== 0) {
char **p;
== NULL) {
break;
}
infop->zpi_patchinfo = p;
break;
}
infop->zpi_patch_cnt++;
}
}
/* Clean up anything we did manage to allocate. */
}
return (err);
}
/*
* Add any dependent pkgs to the list. The pkg depend file lists pkg
* dependencies, one per line with an entry that looks like:
* P SUNWcar Core Architecture, (Root)
* See the depend(4) man page.
*/
static int
{
char depend[MAXPATHLEN];
char *buf;
return (Z_OK);
return (Z_OK);
char *deppkg;
char *delims = " \t";
char pkginfo[MAXPATHLEN];
struct zone_pkginfo info;
if (*buf != 'P') {
continue;
}
/* Skip past the leading 'P '. */
continue;
}
/* If the pkg is already in the manifest don't add it again. */
continue;
}
continue;
}
break;
}
== Z_OK) {
if (info.zpi_patch_cnt > 0)
}
free_pkginfo(&info);
break;
}
return (res);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
}
static void
{
zone_pkg_entry_t *p;
free(p);
}
}
}
/*
* Take a software inventory of the global zone. We need to get the set of
* packages and patches that are on the global zone that the specified
* non-global zone depends on. The packages we need in the inventory are:
*
* - skip the package if SUNW_PKG_THISZONE is 'true'
* otherwise,
* - add the package if
* a) SUNW_PKG_ALLZONES is 'true',
* or
* b) any file delivered by the package is in a file system that is inherited
* from the global zone.
* If the zone does not inherit any file systems (whole root)
* then (b) will be skipped.
*
* For each of the packages that is being added to the inventory, we will also
* add its dependent packages to the inventory.
*
* For each of the packages that is being added to the inventory, we will also
* add all of the associated, unique patches to the inventory.
*
* See the comment for zonecfg_getpkgdata() for compatability restrictions on
* how we must save the XML representation of the software inventory.
*/
static int
{
char pkginfo[MAXPATHLEN];
int res;
struct zone_pkginfo info;
int pkg_cnt = 0;
goto done;
}
goto done;
}
goto done;
}
UU_DEFAULT)) == NULL) {
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.
*/
== NULL) {
goto done;
}
goto done;
}
goto done;
}
continue;
continue;
break;
}
if (!info.zpi_this_zone &&
(info.zpi_all_zones ||
/*
* Add dependents first so any patches will get
* associated with the right pkg in the xml file.
*/
if (info.zpi_patch_cnt > 0)
}
}
free_pkginfo(&info);
break;
}
done:
if (patches_pool != NULL)
return (res);
}
/*
* Get the information required to support detaching a zone. This is
* called on the source system when detaching (the detaching parameter should
* be set to true) and on the destination system before attaching (the
* detaching parameter should be false).
*
* that the software on the global zone can support the zone when we attach.
* To do this we take a software inventory of the global zone. We also
* have to keep track of the device configuration so that we can properly
* recreate it on the destination.
*/
static int
{
int res;
return (res);
if (detaching)
return (res);
}
/* ARGSUSED */
static int
zfm_print(const char *p, void *r) {
return (0);
}
static int
{
opterr = 0;
optind = 0;
switch (arg) {
case '?':
if (optopt != '?') {
"invalid option: %c\n"), MY_BRAND_NAME,
optopt);
}
"options: none\n"), MY_BRAND_NAME);
case 'n':
break;
default:
return (ZONE_SUBPROC_USAGE);
}
}
/* Don't detach the zone if anything is still mounted there */
"mounted on subdirectories of %s.\n"), zonepath);
return (ZONE_SUBPROC_NOTCOMPLETE);
}
return (ZONE_SUBPROC_NOTCOMPLETE);
}
return (ZONE_SUBPROC_NOTCOMPLETE);
}
goto done;
}
!= Z_OK) {
goto done;
}
done:
}
/*
* Validate attaching a zone but don't actually do the work. The zone
* does not have to exist, so there is some complexity getting a new zone
* configuration set up so that we can perform the validation. This is
* handled within zonecfg_attach_manifest() which returns two handles; one
* for the the full configuration to validate (rem_handle) and the other
* (local_handle) containing only the zone configuration derived from the
* manifest.
*/
static int
dryrun_attach(char *manifest_path)
{
int fd;
int err;
int res;
char atbrand[MAXNAMELEN];
return (ZONE_SUBPROC_NOTCOMPLETE);
}
goto done;
}
goto done;
}
!= Z_OK) {
if (err == Z_INVALID_DOCUMENT) {
char buf[6];
"XML file\n"), manifest_path);
else
"to an earlier release of the operating "
"system\n"));
} else {
}
goto done;
}
/*
* Retrieve remote handle brand type and determine whether it is
* native or not.
*/
}
}
/* Get the detach information for the locally defined zone. */
} else {
/* sw_cmp prints error msgs as necessary */
}
done:
}
static int
{
return (Z_ERR);
}
return (Z_OK);
}
static int
{
return (Z_ERR);
}
return (Z_OK);
}
/*
* Attempt to generate the information we need to make the zone look like
* it was properly detached by using the pkg information contained within
* the zone itself.
*
* We will perform a dry-run detach within the zone to generate the xml file.
* To do this we need to be able to get a handle on the zone so we can see
* how it is configured. In order to get a handle, we need a copy of the
* zone configuration within the zone. Since the zone's configuration is
* not available within the zone itself, we need to temporarily copy it into
* the zone.
*
* The sequence of actions we are doing is this:
* [set zone state to installed]
* [mount zone]
* zlogin {zone} 'zoneadm -z {zone} detach -n' >{zonepath}/SUNWdetached.xml
* [unmount zone]
* [set zone state to configured]
*
* The successful result of this function is that we will have a
* SUNWdetached.xml file in the zonepath and we can use that to attach the zone.
*/
static boolean_t
{
int status;
/*
* The zone has to be installed to mount and zlogin. Temporarily set
* the state to 'installed'.
*/
return (B_FALSE);
/* Mount the zone so we can zlogin. */
goto cleanup;
/*
* We need to copy the zones xml configuration file into the
* zone so we can get a handle for the zone while running inside
* the zone.
*/
goto cleanup;
goto cleanup;
/* Now run the detach command within the mounted zone. */
"'/usr/sbin/zoneadm -z %s detach -n' >%s/SUNWdetached.xml",
goto cleanup;
goto cleanup;
/* Cleanup from the previous actions. */
if (mounted) {
} else {
!= ZONE_SUBPROC_OK)
}
if (unmount_func() != Z_OK)
}
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
{
int err;
int update_res;
int status;
struct zone_fstab fstab;
!= Z_OK) {
return (Z_FATAL);
}
/* We reset the state since the zone wasn't modified yet. */
!= Z_OK) {
}
return (Z_FATAL);
}
/*
* Move data files generated by sw_up_to_date() into the scratch
* zone's /tmp.
*/
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.
*/
zonepath);
goto fail;
}
goto fail;
}
goto fail;
}
}
(void) zonecfg_endipdent(handle);
goto fail;
}
/* run the updater inside the scratch zone */
update_res = Z_OK;
!= ZONE_SUBPROC_OK) {
update_res = Z_ERR;
}
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);
return (Z_ERR);
}
return (Z_OK);
fail:
/* We reset the state since the zone wasn't modified yet. */
!= Z_OK) {
}
return (Z_ERR);
}
/* ARGSUSED */
static void
sigcleanup(int sig)
{
}
static boolean_t
valid_num(char *n)
{
for (; isdigit(*n); n++)
;
if (*n != NULL)
return (B_FALSE);
return (B_TRUE);
}
/*
* Take an input field, which must look like a positive int, and return the
* numeric value of the field. Return -1 if the input field does not look
* like something we can convert.
*/
static int
{
char *ppoint;
long n;
*ppoint = '\0';
} else {
}
return (-1);
errno = 0;
if (errno != 0)
return (-1);
return ((int)n);
}
/*
* Step through two version strings that look like postive ints delimited by
* decimals and compare them. Example input can look like 2, 010.3, 75.02.09,
* etc. If the input does not look like this then we do a simple lexical
* comparison of the two strings. The string can be modified on exit of
* this function.
*/
static int
{
for (;;) {
/*
* If either field is not a postive int, just compare them
* lexically.
*/
return (1);
return (-1);
/* They're equal */
/* No more fields */
return (0);
/* Field 2 still has data so it is greater than field 1 */
return (-1);
/* Field 1 still has data so it is greater than field 2 */
return (1);
/* Both fields still have data, keep going. */
}
}
/*
* The result of the comparison is returned in the cmp parameter:
* 0 if both versions are equal.
* <0 if version1 is less than version 2.
* >0 if version1 is greater than version 2.
* The function returns B_TRUE if there was an ENOMEM error, B_FALSE otherwise.
*
* This function handles the various version strings we can get from the
* dependent pkg versions. They usually look like:
* "1.21,REV=2005.01.17.23.31"
* "2.6.0,REV=10.0.3.2004.12.16.18.02"
*
* We can't do a simple lexical comparison since:
* 2.6.0 would be greater than 2.20.0
* 12 would be greater than 110
*
* If the input strings do not look like decimal delimted version strings
* then we fall back to doing a simple lexical comparison.
*/
static boolean_t
{
int res;
/* We need to modify the input strings so we dup them. */
return (B_TRUE);
return (B_TRUE);
}
/* Strip off a revision delimited by a comma. */
*rev1++ = '\0';
*rev2++ = '\0';
/* If the primary versions are not equal, return the result */
if (res != 0) {
goto done;
}
/*
* All of the fields in the primary version strings are equal, check
* the rev, if it exists.
*/
/* No revs */
*cmp = 0;
goto done;
}
/* Field 2 has a rev so it is greater than field 1 */
*cmp = -1;
goto done;
}
/* Field 1 has a rev so it is greater than field 2 */
*cmp = 1;
goto done;
}
/* If no recognized REV data then just lexically compare them */
goto done;
}
/* Both fields have revs, check them. */
done:
return (B_FALSE);
}
/*
* Walk all of the patches on the pkg, looking to see if the specified patch
* has been obsoleted by one of those patches.
*/
static boolean_t
{
return (B_FALSE);
if (patch_walk == NULL)
return (B_FALSE);
continue;
/* Check the obsolete list on the patch. */
!= NULL) {
break;
}
}
return (res);
}
/*
* Build a list of unique patches from the input pkg_patches list.
* If the pkg parameter is not null then we will check the patches on that
* pkg to see if any of the pkg_patches have been obsoleted. We don't
* add those obsoleted patches to the unique list.
* Returns B_FALSE if an error occurs.
*/
static boolean_t
{
if (pkg_patches == NULL)
return (B_TRUE);
return (B_FALSE);
/* Skip adding it if we already have it. */
continue;
/* Likewise, skip adding it if it has been obsoleted. */
continue;
/* We need to add it so make a duplicate. */
if ((patch = (zone_pkg_entry_t *)
return (B_FALSE);
}
return (B_FALSE);
}
return (B_FALSE);
}
/* Insert patch into the unique patch AVL tree. */
}
return (B_TRUE);
}
/*
* Common code for sw_cmp which will check flags, update res and print the
* section header. Return true if we should be silent.
*/
static boolean_t
{
if (flag & SW_CMP_SILENT)
return (B_TRUE);
if (*do_header) {
/* LINTED E_SEC_PRINTF_VAR_FMT */
}
return (B_FALSE);
}
/*
* Compare the software on the local global zone and source system global
* zone. Used when we are trying to attach a zone during migration or
* when checking if a ZFS snapshot is still usable for a ZFS clone.
* l_handle is for the local system and s_handle is for the source system.
* These have a snapshot of the appropriate packages and patches in the global
* zone for the two machines.
* The functions called here can print any messages that are needed to
* inform the user about package or patch problems.
* The flag parameter controls how the messages are printed. If the
* SW_CMP_SILENT bit is set in the flag then no messages will be printed
* but we still compare the sw and return an error if there is a mismatch.
*/
static int
{
char *hdr;
int res;
int err;
/* Set res to cover any of these memory allocation errors. */
goto done;
goto done;
goto done;
goto done;
goto done;
B_TRUE);
goto done;
}
B_TRUE);
goto done;
}
/*
* Check the source host for pkgs (and versions) that are not on the
* local host.
*/
"are inconsistent with this system:\n");
goto done;
}
int cmp;
/*
* 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.
*/
pkg_pool)) {
goto done;
}
/* src pkg is not installed on dst */
break;
gettext("\t%s: not installed\n\t\t(%s)\n"),
continue;
}
/* Check pkg version */
goto done;
}
if (cmp != 0) {
break;
"\t%s: version mismatch\n\t\t(%s)\n\t\t(%s)\n"),
}
}
/*
* Now check the local host for pkgs that were not on the source host.
* We already handled version mismatches in the loop above.
*/
"not installed on the source system:\n");
goto done;
}
/*
* 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.
*/
pkg_pool)) {
goto done;
}
/* dst pkg is not installed on src */
break;
}
}
/*
* Check the source host for patches that are not on the local host.
*/
"are inconsistent with this system:\n");
goto done;
}
/* src patch is not installed on dst */
break;
gettext("\t%s-%s: not installed\n"),
continue;
}
/*
* Check patch version. We assume the patch versions are
* properly structured with a leading 0 if necessary (e.g. 01).
*/
break;
gettext("\t%s: version mismatch\n\t\t(%s) (%s)\n"),
}
}
/*
* Check the local host for patches that were not on the source host.
* We already handled version mismatches in the loop above.
*/
"not installed on the source system:\n");
goto done;
}
/* dst patch is not installed on src */
break;
}
}
done:
/* free avl structs */
return (res);
}
/*
* Compare the software on the local global zone and source system global
* We generate the data files needed by the update process in this case.
* l_handle is for the local system and s_handle is for the source system.
* These have a snapshot of the appropriate packages and patches in the global
* zone for the two machines.
*
* The algorithm we use to compare the pkgs is as follows:
* 1) pkg on src but not on dst
* remove src pkg (allowed in order to handle obsolete pkgs - note that
* this only applies to dependent pkgs, not generic pkgs installed into
* the zone by the zone admin)
* 2) pkg on dst but not on src
* add pkg
* 3) pkg on src with higher rev than on dst
* fail (downgrade)
* 4) pkg on dst with higher rev than on src
* remove src pkg & add new
* 5) pkg version is the same
* a) patch on src but not on dst
* fail (downgrade, unless obsoleted)
* b) patch on dst but not on src
* remove src pkg & add new
* c) patch on src with higher rev than on dst
* fail (downgrade, unless obsoleted)
* d) patch on dst with higher rev than on src
* remove src pkg & add new
*
* We run this algorithm in 2 passes, first looking at the pkgs from the src
* system and then looking at the pkgs from the dst system.
*
* As with the sw_cmp function, we return Z_OK if there is no work to be
* done (the attach can just happen) or Z_ERR if we have to update the pkgs
* within the zone. We can also return Z_FATAL if we had a real error during
* this process.
*/
static int
{
int err;
int cmp;
char fname[MAXPATHLEN];
B_FALSE);
goto fatal;
}
B_FALSE);
goto fatal;
}
goto fatal;
goto fatal;
goto fatal;
B_TRUE);
goto fatal;
}
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)
*/
goto fatal;
}
/* src pkg is not installed on dst */
"packages to remove"), B_FALSE);
goto fatal;
}
continue;
}
/* Check pkg version to determine how to proceed. */
goto fatal;
}
if (cmp > 0) {
/* src pkg has higher vers than dst pkg */
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.
*/
continue;
/*
* 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.
*/
"%s, the source had patches but this system does "
goto fatal;
}
if (patch_walk == NULL) {
goto fatal;
}
/*
* 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.
*/
continue;
"package %s, the source had patch %s-%s "
"which is not installed on this system\n"),
goto fatal;
}
/* Check if the src patch is newer than the dst patch */
> 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.
*/
continue;
"package %s, the source had patch %s-%s "
"but this system only has %s-%s\n"),
goto fatal;
}
/*
* If the src patch is the same rev or older than the
* dst patch we'll handle that in the second pass.
*/
}
}
/*
* 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
*/
goto fatal;
}
/* dst pkg was not installed on src */
"packages to add"), B_FALSE);
goto fatal;
}
continue;
}
/* Check pkg version to determine how to proceed. */
goto fatal;
}
if (cmp > 0) {
/* dst pkg has higher vers than src pkg */
"packages to remove"), B_FALSE);
goto fatal;
}
"packages to add"), B_FALSE);
goto fatal;
}
continue;
}
/*
* cmp < 0 was handled in the first loop. This would
* be a downgrade so we should have already failed.
*/
/* src and dst pkgs have the same version. */
/* If dst pkg has no patches, then we're done with this pkg. */
continue;
/*
* 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.
*/
"packages to remove"), B_FALSE);
goto fatal;
}
"packages to add"), B_FALSE);
goto fatal;
}
continue;
}
if (patch_walk == NULL) {
goto fatal;
}
/*
* 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.
*/
< 0) {
"of packages to remove"), B_FALSE);
goto fatal;
}
< 0) {
"of packages to add"), B_FALSE);
goto fatal;
}
continue;
}
/* Check if the dst patch is newer than the src patch */
> 0) {
/*
* We have a patch on the dst with higher rev
* than the patch on the src. Just replace the
* pkg.
*/
< 0) {
"of packages to remove"), B_FALSE);
goto fatal;
}
< 0) {
"of packages to add"), B_FALSE);
goto fatal;
}
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.
*/
>= 0);
}
}
B_FALSE);
goto fatal;
}
B_FALSE);
goto fatal;
}
/* free avl structs */
return (res);
/* free avl structs */
/* clean up data files left behind */
return (Z_FATAL);
}
/*
* During attach we go through and fix up the /dev entries for the zone
* we are attaching. In order to regenerate /dev with the correct devices,
* the old /dev will be removed, the zone readied (which generates a new
* /dev) then halted, then we use the info from the manifest to update
* the modes, owners, etc. on the new /dev.
*/
static int
{
int err;
int status;
struct zone_devpermtab devtab;
char devpath[MAXPATHLEN];
/* 6: "exec " and " " */
>= sizeof (devpath))
return (Z_TOO_BIG);
/*
* "exec" the command so that the returned status is that of
* RMCOMMAND and not the shell.
*/
devpath);
gettext("could not remove existing /dev\n"));
return (Z_ERR);
}
/* In order to ready the zone, it must be in the installed state */
return (Z_ERR);
}
/* We have to ready the zone to regen the dev tree */
/* attempt to restore zone to configured state */
return (Z_ERR);
}
/* attempt to restore zone to configured state */
return (Z_ERR);
}
/* attempt to restore zone to configured state */
gettext("unable to enumerate device entries\n"));
return (Z_ERR);
}
int err;
}
(void) zonecfg_enddevperment(handle);
return (Z_OK);
}
static int
{
char *manifest_path;
opterr = 0;
optind = 0;
switch (arg) {
case '?':
if (optopt != '?') {
"invalid option: %c\n"), MY_BRAND_NAME,
optopt);
}
"options: [-u]\n"), MY_BRAND_NAME);
"-u to update the zone to the current "
"system software.\n"));
case 'n':
break;
case 'u':
break;
default:
return (ZONE_SUBPROC_USAGE);
}
}
/* dry-run and update flags are mutually exclusive */
"exclusive\n"));
return (ZONE_SUBPROC_USAGE);
}
/*
* If the no-execute option was specified, we need to branch down
* a completely different path since there is no zone required to be
* configured for this option.
*/
if (!execute)
return (dryrun_attach(manifest_path));
return (ZONE_SUBPROC_NOTCOMPLETE);
}
return (ZONE_SUBPROC_NOTCOMPLETE);
}
goto done;
}
/*
* Zone was not detached. Try to fall back to getting
* the needed information from within the zone.
*/
if (!retried) {
"not properly detached.\n\tAttempting to "
"attach anyway.\n"));
if (gen_detach_info()) {
goto retry;
}
}
"information needed to attach this zone.\n"));
} else if (err == Z_INVALID_DOCUMENT) {
"earlier release of the operating system\n"));
} else {
}
goto done;
}
/* Get the detach information for the locally defined zone. */
goto done;
}
/*
* Ensure that the detached and locally defined zones are both of
* the same brand.
*/
goto done;
}
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];
}
goto done;
}
/* clean up data files left behind by sw_up_to_date() */
if (attach_interupted) {
goto done;
}
} else {
/* sw_cmp prints error msgs as necessary */
goto done;
goto done;
}
}
done:
}
static int
{
char cmdbuf[MAXPATHLEN];
int arg;
int status;
opterr = 0;
optind = 0;
switch (arg) {
case '?':
if (optopt != '?') {
"invalid option: %c\n"), MY_BRAND_NAME,
optopt);
}
"options: none\n"), MY_BRAND_NAME);
case 'x':
"invalid option: %c\n"), MY_BRAND_NAME,
arg);
return (ZONE_SUBPROC_USAGE);
}
/* Ignore option, handled in zoneadm. */
break;
default:
return (ZONE_SUBPROC_USAGE);
}
}
return (Z_ERR);
/*
* According to the Application Packaging Developer's Guide, a
* "checkinstall" script when included in a package is executed as
* the user "install", if such a user exists, or by the user
* "nobody". In order to support this dubious behavior, the path
* to the zone being constructed is opened up during the life of
* the command laying down the zone's root file system. Once this
* has completed, regardless of whether it was successful, the
* path to the zone is again restricted.
*/
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_ERR);
return (Z_OK);
}
/* ARGSUSED */
static int
{
int status;
/* Ignore any arguments. */
/*
* Trusted Extensions requires that cloned zones use the same sysid
* configuration, so it is not appropriate to perform any
* post-clone reconfiguration.
*/
if (is_system_labeled())
return (ZONE_SUBPROC_OK);
/* If the zone is already sys-unconfiged, then we're done. */
return (ZONE_SUBPROC_FATAL);
return (ZONE_SUBPROC_OK);
/*
* Mount the zone. The zone is still in the INCOMPLETE state, so we
* have to force mount it.
*/
return (ZONE_SUBPROC_FATAL);
/* sys-unconfig the zone */
} else {
!= ZONE_SUBPROC_OK)
}
if (unmount_func() != Z_OK)
}
/*
* Perform any necessary housekeeping tasks we need to do before we take
* a ZFS snapshot of the zone. What this really entails is that we are
* taking a sw inventory of the source zone, like we do when we detach,
* so that there is the XML manifest in the snapshot. We use that to
* validate the snapshot if it is the source of a clone at some later time.
*/
static int
{
int err;
opterr = 0;
optind = 0;
return (ZONE_SUBPROC_USAGE);
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_OK);
}
/*
* Perform any necessary housekeeping tasks we need to do after we take
* a ZFS snapshot of the zone. What this really entails is removing the
* sw inventory XML file from the zone. It is still in the snapshot where
* we want it, but we don't want it in the source zone itself.
*/
static int
{
int err;
opterr = 0;
optind = 0;
return (ZONE_SUBPROC_USAGE);
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_OK);
}
/*
* We are using an explicit snapshot from some earlier point in time so
* we need to validate it. This involves checking the sw inventory that
* we took when we made the snapshot to verify that the current sw config
* on the host is still valid to run a zone made from this snapshot.
*/
static int
{
int err;
char *snapshot_name;
char *snap_path;
opterr = 0;
optind = 0;
return (ZONE_SUBPROC_USAGE);
if (argc < 2)
return (ZONE_SUBPROC_USAGE);
snapshot_name = argv[0];
return (Z_ERR);
}
return (Z_ERR);
}
goto done;
}
"taken\n\tby a 'zoneadm clone' command. It can "
"not be used to clone zones.\n"), snapshot_name);
else
"out-dated\n\tIt can no longer be used to clone "
"zones on this system.\n"), snapshot_name);
goto done;
}
/* Get the detach information for the locally defined zone. */
B_TRUE);
goto done;
}
"It can no longer be used to clone zones on this "
"system.\n"), snapshot_name);
done:
}
static void
usage()
{
exit(253);
}
int
{
int err = ZONE_SUBPROC_FATAL;
locale = "C";
(void) textdomain(TEXT_DOMAIN);
if (argc < 4)
usage();
argc -= 4;
return (err);
}