sw_cmp.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"
#include <stdio.h>
#include <unistd.h>
#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
{
}
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);
}
static void
{
zone_pkg_entry_t *p;
return;
free(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
{
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.
*/
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.
*/
int
char *zonepath)
{
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;
}
break;
}
/* 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;
}
break;
}
/*
* 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);
}