sw_support.c revision 16ab8c7b0738497a3fa85cb1afebcfd7844517d8
/*
* 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 TMP_MANIFEST "SUNWupdate.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. */
/* These are the file status indicators for the contents file */
struct zone_pkginfo {
int zpi_patch_cnt;
char *zpi_version;
char *zpi_patchlist;
char **zpi_patchinfo;
};
typedef struct {
char *patch_num;
char *patch_vers;
} patch_node_t;
typedef struct {
char *patch_num;
typedef struct {
int res;
typedef struct {
char *patch_num;
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)) {
break;
p++;
}
/* 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 metadata problems "
"with 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) {
int len;
if ((infop->zpi_patchlist =
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 void
{
}
static int
{
opterr = 0;
optind = 0;
switch (arg) {
case '?':
if (optopt != '?') {
"invalid option: %c\n"), MY_BRAND_NAME,
optopt);
}
detach_usage();
case 'n':
break;
default:
return (ZONE_SUBPROC_USAGE);
}
}
detach_usage();
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);
}
/*
* Setup a configured zone so we can mount it and zlogin to perform various
* tasks. Also put a copy of the zone configuration into the zone so we can
* get a handle on it.
*
* The sequence of actions we are doing is this:
* [set zone state to installed]
* [mount zone]
*
* The unmount_configured_zone() function should be used to clean up from
* this function.
*/
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. */
return (B_FALSE);
/*
* 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.
*/
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* Cleanup a configured zone mounted using mount_configured_zone().
* tasks.
*
* The sequence of actions we are doing is this:
* [unmount zone]
* [set zone state to configured]
*/
static boolean_t
{
/* Cleanup from the previous actions. */
if (mounted) {
int status;
} else {
!= ZONE_SUBPROC_OK)
}
if (unmount_func() != Z_OK)
}
return (res);
}
/*
* 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.
* The successful result of this function is that we will have the "manifest"
* xml file in the zonepath and we can use that to attach the zone.
*/
static boolean_t
gen_detach_info(char *manifest)
{
int status;
if (!mount_configured_zone(&mounted))
goto cleanup;
/* Now run the detach command within the mounted zone. */
goto cleanup;
goto cleanup;
if (!unmount_configured_zone(mounted))
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;
}
if (update_res == Z_OK) {
!= ZONE_SUBPROC_OK) {
"zone\n"));
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_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 */
continue;
}
/*
* 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 "
if (patch_walk == NULL) {
goto fatal;
}
!= NULL) {
}
continue;
}
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"),
continue;
}
/* 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"),
continue;
}
/*
* If the src patch is the same rev or older than the
* dst patch we'll handle that in the second pass.
*/
}
}
if (failed)
goto fatal;
/*
* 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);
}
/*
* Check the pkg to see if the patch backout data exists for the specified
* patch in the "bo" parameter. Mark the patch if we cannot back it out.
* Only return false if we hit a fatal error.
*/
static boolean_t
{
char backout_path[MAXPATHLEN];
char backout_data[MAXPATHLEN];
return (B_FALSE);
}
backout_path) >= sizeof (backout_data)) {
return (B_FALSE);
}
return (B_TRUE);
backout_path) >= sizeof (backout_data)) {
return (B_FALSE);
}
return (B_TRUE);
backout_path) >= sizeof (backout_data)) {
return (B_FALSE);
}
return (B_TRUE);
backout_path) >= sizeof (backout_data)) {
return (B_FALSE);
}
return (B_TRUE);
/* There is no backout data for the patch. */
return (B_TRUE);
}
/*
* Parse pkg patch_list and check if any of the backout patches are in the pkg.
* For each backout, collect the data about backing it out. Only return false
* if we hit a fatal error.
*/
static boolean_t
{
char *tmp;
char *patch;
char *lastp;
if (patch_list == NULL)
return (B_TRUE);
!= NULL) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Walk all of the pkgs installed to determine if one of the patches to backout
* is installed on that pkg. To figure out what patches are installed we have
* to look at each pkg, since patch data is stored in individual pkginfo files.
*
* Once that is done, the patches to backout will each be flagged so we
* can verify that the backout list is valid.
*
* We're doing this from the global zone, but this is just a sanity check and
* nothing is being modified. The actual backout happens in the scratch zone.
*/
static boolean_t
{
char pkg_dir[MAXPATHLEN];
if (backout_list == NULL)
return (B_TRUE);
return (B_FALSE);
return (B_FALSE);
char pkgpath[MAXPATHLEN];
char pkginfo[MAXPATHLEN];
struct zone_pkginfo info;
continue;
continue;
>= sizeof (pkginfo))
continue;
continue;
break;
}
free_pkginfo(&info);
if (!res)
break;
}
/* If we had a fatal error in the loop above, we're done. */
if (!res)
return (B_FALSE);
/*
* Walk the list of patches to see if any are not installed or cannot
* be backed out.
*/
return (B_FALSE);
}
if (do_header) {
"patches cannot be removed:\n"));
}
gettext("\t%s is not installed\n"),
else
"installed without creating its backout "
}
}
return (res);
}
/*
* Run patchrm in the scratch zone to backout the specified patches.
* Return B_TRUE if the backout suceeded, otherwise, return B_FALSE.
*/
static boolean_t
{
int status;
int len;
char *patchlog = " 2>&1 | "
char *tmp;
if (backout_list == NULL)
return (B_TRUE);
if (!mount_configured_zone(&mounted))
goto cleanup;
goto cleanup;
}
/* Walk the list of patches to build the backout command. */
goto cleanup;
}
/* LINTED E_SEC_PRINTF_VAR_FMT */
goto cleanup;
}
}
(void) printf("\n");
/* Now run the patchrm command within the mounted zone. */
"See the /var/sadm/system/logs/backout_log file in the "
"zone for details.\n"));
goto cleanup;
}
if (!unmount_configured_zone(mounted))
return (res);
}
/* ARGSUSED */
static int
{
}
static boolean_t
{
return (B_FALSE);
== NULL)
return (B_FALSE);
return (B_TRUE);
}
static void
{
if (backout_list != NULL) {
bo_patch_node_t *p;
free(p);
}
if (backout_pool != NULL)
}
static void
{
"[-b patchid]* [-u]\n"), MY_BRAND_NAME);
"options to backout the patch from the\n\tzone.\n"));
"the current system software.\n"));
}
static int
{
int res = ZONE_SUBPROC_NOTCOMPLETE;
char *manifest_path;
char tmp_path[MAXPATHLEN];
opterr = 0;
optind = 0;
switch (arg) {
case '?':
if (optopt != '?') {
"invalid option: %c\n"), MY_BRAND_NAME,
optopt);
}
attach_usage();
goto done;
case 'b':
if (backout_pool == NULL &&
goto done;
sizeof (bo_patch_node_t))) == NULL)
goto done;
== NULL) {
where);
} else {
}
break;
case 'n':
break;
case 'u':
break;
default:
goto done;
}
}
attach_usage();
goto done;
}
/* dry-run and update flags are mutually exclusive */
"exclusive\n"));
goto done;
}
/*
* If there are any patches to backout, check if each patch was
* installed with its backout data.
*/
goto done;
/*
* 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) {
goto done;
}
goto done;
}
goto done;
}
goto done;
}
/* Get the detach information for the locally defined zone. */
goto done;
}
/*
* If the zone was previously detached, then we can read its manifest
* to check that the detached and locally defined zones are both of
* the same brand.
*/
!= 0)) {
"brand\n"));
goto done;
}
"'%s' zone to a '%s' configuration.\n"), atbrand,
brand);
goto done;
}
goto done;
}
}
/* Now backout the specified patches. */
if (!backout_patches(backout_list)) {
goto done;
}
/*
* Now that any patches have been backed out, regenerate the zone
* detach data.
*
* Even if no patches were backed out, we want to regenerate the
* detach data using the latest software on this target system so
* that we take advantage of any bug fixes on this release.
*/
TMP_MANIFEST) >= sizeof (tmp_path) ||
"information needed to attach this zone.\n"));
goto done;
}
goto done;
}
/*
* We set the result to be fatal here. If we make it through the sw
* check and possible update cleanly, then we set the result to OK.
* Otherwise, we goto done and the result remains fatal.
*/
/*
* 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];
}
/*
* Clean up data files left behind by sw_up_to_date().
* Don't worry if the files don't exist; they might have been
* cleaned up by the attach_update() function.
*/
goto done;
} else {
/* sw_cmp prints error msgs as necessary */
goto done;
goto done;
}
goto done;
}
done:
return (res);
}
static void
{
}
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);
}
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 (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);
}