initial autorollback feature.

CVS patchset: 7149
CVS date: 2004/03/05 18:22:25
This commit is contained in:
joden 2004-03-05 18:22:25 +00:00
parent 3341692cc2
commit d7db215d7f
7 changed files with 1053 additions and 6 deletions

103
lib/psm.c
View File

@ -38,6 +38,11 @@ int _psm_debug = _PSM_DEBUG;
/*@unchecked@*/
int _psm_threads = 0;
/* Give access to the rpmte global tracking the last instance added
* to the database.
*/
extern unsigned int myinstall_instance;
/*@access FD_t @*/ /* XXX void ptr args */
/*@access rpmpsm @*/
@ -1048,6 +1053,7 @@ static rpmRC runTriggers(rpmpsm psm)
if (psm->te) /* XXX can't happen */
N = rpmteN(psm->te);
/* ADJUST */
if (N) /* XXX can't happen */
numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
+ psm->countCorrection;
@ -1300,6 +1306,48 @@ rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage)
break;
}
/* If we have a score then autorollback is enabled. If autorollback is
* enabled, and this is an autorollback transaction, then we may need to
* adjust the pkgs installed count.
*
* If all this is true, this adjustment should only be made if the PSM goal
* is an install. No need to make this adjustment on the erase
* component of the upgrade, or even more absurd to do this when doing a
* PKGSAVE.
*/
if(rpmtsGetScore(ts) != NULL &&
rpmtsGetType(ts) == RPMTRANS_TYPE_AUTOROLLBACK &&
(psm->goal & ~(PSM_PKGSAVE|PSM_PKGERASE))) {
/* Get the score, if its not NULL, get the appropriate
* score entry.
*/
rpmtsScore score = rpmtsGetScore(ts);
if(score != NULL) {
/* OK, we got a real score so lets get the appropriate
* score entry.
*/
rpmtsScoreEntry se;
se = rpmtsScoreGetEntry(score, rpmteN(psm->te));
/* IF the header for the install element has been installed,
* but the header for the erase element has not been erased,
* then decrement the instance count. This is because in an
* autorollback, if the header was added in the initial transaction
* then in the case of an upgrade the instance count will be
* 2 instead of one when re-installing the old package, and 3 when
* erasing the new package.
*
* Another wrinkle is we only want to make this adjustement
* if the thing we are rollback was an upgrade of package. A pure
* install or erase does not need the adjustment
*/
if(se && se->installed &&
!se->erased &&
(se->te_types & (TR_ADDED|TR_REMOVED)))
psm->npkgs_installed--;
}
}
if (psm->goal == PSM_PKGINSTALL) {
int fc = rpmfiFC(fi);
@ -1956,6 +2004,36 @@ assert(psm->mi == NULL);
else
rc = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), fi->h,
NULL, NULL);
/* Set the database instance so consumers (i.e. rpmtsRun())
* can add this to a rollback transaction.
*/
rpmteSetDBInstance(psm->te, myinstall_instance);
/*
* If the score exists and this is not a rollback or autorollback
* then lets check off installed for this package.
*/
if(rpmtsGetScore(ts) != NULL &&
rpmtsGetType(ts) != RPMTRANS_TYPE_ROLLBACK &&
rpmtsGetType(ts) != RPMTRANS_TYPE_AUTOROLLBACK) {
/* Get the score, if its not NULL, get the appropriate
* score entry.
*/
rpmtsScore score = rpmtsGetScore(ts);
if(score != NULL) {
/* OK, we got a real score so lets get the appropriate
* score entry.
*/
rpmMessage(RPMMESS_DEBUG,
_("Attempting to mark %s as installed in score board(0x%x).\n"),
rpmteN(psm->te), score);
rpmtsScoreEntry se;
se = rpmtsScoreGetEntry(score, rpmteN(psm->te));
if(se) se->installed = 1;
}
}
(void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
break;
case PSM_RPMDB_REMOVE:
@ -1963,6 +2041,31 @@ assert(psm->mi == NULL);
(void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
rc = rpmdbRemove(rpmtsGetRdb(ts), rpmtsGetTid(ts), fi->record,
NULL, NULL);
/*
* If the score exists and this is not a rollback or autorollback
* then lets check off erased for this package.
*/
if(rpmtsGetScore(ts) != NULL &&
rpmtsGetType(ts) != RPMTRANS_TYPE_ROLLBACK &&
rpmtsGetType(ts) != RPMTRANS_TYPE_AUTOROLLBACK) {
/* Get the score, if its not NULL, get the appropriate
* score entry.
*/
rpmtsScore score = rpmtsGetScore(ts);
if(score != NULL) { /* XXX: Can't happen */
/* OK, we got a real score so lets get the appropriate
* score entry.
*/
rpmMessage(RPMMESS_DEBUG,
_("Attempting to mark %s as erased in score board(0x%x).\n"),
rpmteN(psm->te), score);
rpmtsScoreEntry se;
se = rpmtsScoreGetEntry(score, rpmteN(psm->te));
if(se) se->erased = 1;
}
}
(void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
break;

View File

@ -9,6 +9,7 @@
#include "rpmdb.h"
#include "rpmds.h"
#include "rpmte.h" /* XXX: rpmts.h needs this for rpmtsScoreEntries */
#define _RPMTS_INTERNAL /* ts->goal, ts->dbmode, ts->suggests */
#include "rpmts.h"
@ -1120,6 +1121,11 @@ int rpmRollback(rpmts ts, struct rpmInstallArguments_s * ia, const char ** argv)
(void) rpmtsSetFlags(ts, transFlags);
/* Make the transaction a rollback transaction. In a rollback
* a best effort is what we want
*/
rpmtsSetType(ts, RPMTRANS_TYPE_ROLLBACK);
itids = IDTXload(ts, RPMTAG_INSTALLTID);
if (itids != NULL) {
ip = itids->idt;

View File

@ -104,6 +104,11 @@ static void addTE(rpmts ts, rpmte p, Header h,
if ((p->version = strrchr(p->name, '-')) != NULL)
*p->version++ = '\0';
/* Set db_instance to 0 as it has not been installed
* necessarily yet.
*/
p->db_instance = 0;
arch = NULL;
xx = hge(h, RPMTAG_ARCH, NULL, (void **)&arch, NULL);
if (arch != NULL) {
@ -225,6 +230,19 @@ rpmte rpmteNew(const rpmts ts, Header h,
return p;
}
/* Get the DB Instance value */
unsigned int rpmteDBInstance(rpmte te)
{
return (te != NULL ? te->db_instance : 0);
}
/* Set the DB Instance value */
void rpmteSetDBInstance(rpmte te, unsigned int instance)
{
if(te != NULL)
te->db_instance = instance;
}
rpmElementType rpmteType(rpmte te)
{
return (te != NULL ? te->type : -1);

View File

@ -85,6 +85,7 @@ struct rpmte_s {
int depth; /*!< Max. depth in dependency tree. */
int npreds; /*!< No. of predecessors. */
int tree; /*!< Tree index. */
unsigned int db_instance; /*!< Database Instance after add */
/*@owned@*/
tsortInfo tsi; /*!< Dependency ordering chains. */
@ -253,6 +254,21 @@ uint_32 rpmteColor(rpmte te)
uint_32 rpmteSetColor(rpmte te, uint_32 color)
/*@modifies te @*/;
/**
* Retrieve last instance installed to the database.
* @param te transaction element
* @return last install instance.
*/
unsigned int rpmteDBInstance(rpmte te);
/**
* Set last instance installed to the database.
* @param te transaction element
* @param instance Database instance of last install element.
* @return last install instance.
*/
void rpmteSetDBInstance(rpmte te, unsigned int instance);
/**
* Retrieve size in bytes of package file.
* @todo Signature header is estimated at 256b.

View File

@ -833,6 +833,9 @@ rpmts rpmtsFree(rpmts ts)
if (_rpmts_stats)
rpmtsPrintStats(ts);
/* Free up the memory used by the rpmtsScore */
rpmtsScoreFree(ts->score);
(void) rpmtsUnlink(ts, "tsCreate");
/*@-refcounttrans -usereleased @*/
@ -860,6 +863,36 @@ rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
return ovsflags;
}
/*
* This allows us to mark transactions as being of a certain type.
* The three types are:
*
* RPM_TRANS_NORMAL
* RPM_TRANS_ROLLBACK
* RPM_TRANS_AUTOROLLBACK
*
* ROLLBACK and AUTOROLLBACK transactions should always be ran as
* a best effort. In particular this important to the autorollback
* feature to avoid rolling back a rollback (otherwise known as
* dueling rollbacks (-;). AUTOROLLBACK's additionally need instance
* counts passed to scriptlets to be altered.
*/
void rpmtsSetType(rpmts ts, rpmtsType type)
{
if(ts != NULL) {
ts->type = type;
}
}
/* Let them know what type of transaction we are */
rpmtsType rpmtsGetType(rpmts ts)
{
if(ts != NULL)
return ts->type;
else
return 0;
}
int rpmtsUnorderedSuccessors(rpmts ts, int first)
{
int unorderedSuccessors = 0;
@ -1419,6 +1452,7 @@ rpmts rpmtsCreate(void)
ts = xcalloc(1, sizeof(*ts));
memset(&ts->ops, 0, sizeof(ts->ops));
(void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
ts->type = RPMTRANS_TYPE_NORMAL;
ts->goal = TSM_UNKNOWN;
ts->filesystemCount = 0;
ts->filesystems = NULL;
@ -1469,7 +1503,187 @@ rpmts rpmtsCreate(void)
memset(ts->pksignid, 0, sizeof(ts->pksignid));
ts->dig = NULL;
/*
We only use the score in an autorollback. So set this to
NULL by default.
*/
ts->score = NULL;
ts->nrefs = 0;
return rpmtsLink(ts, "tsCreate");
}
/**********************
* Transaction Scores *
**********************/
rpmRC rpmtsScoreInit(rpmts runningTS, rpmts rollbackTS)
{
rpmtsScore score;
rpmtsi pi;
rpmte p;
int i;
int tranElements; /* Number of transaction elements in runningTS */
int found = 0;
rpmRC rc = RPMRC_OK; /* Assume success */
rpmtsScoreEntry se;
rpmMessage(RPMMESS_DEBUG, _("Creating transaction score board(0x%x, 0x%x)\n"),
runningTS, rollbackTS);
/* Allocate space for score board */
score = xcalloc(1, sizeof(*score));
rpmMessage(RPMMESS_DEBUG, _("\tScore board address: 0x%x\n"), score);
/*
* Determine the maximum size needed for the entry list.
* XXX: Today, I just get the count of rpmts elements, and allocate
* an array that big. Yes this is guaranteed to waste memory.
* Future updates will hopefully make this more efficient,
* but for now it will work.
*/
tranElements = rpmtsNElements(runningTS);
rpmMessage(RPMMESS_DEBUG, _("\tAllocating space for %d entries\n"), tranElements);
score->scores = xcalloc(tranElements, sizeof(score->scores));
/* Initialize score entry count */
score->entries = 0;
score->nrefs = 0;
/*
* Increment through transaction elements and make sure for every
* N there is an rpmtsScoreEntry.
*/
pi = rpmtsiInit(runningTS);
while ((p = rpmtsiNext(pi, TR_ADDED|TR_REMOVED)) != NULL) {
found = 0;
/* Try to find the entry in the score list */
for(i = 0; i < score->entries; i++) {
se = score->scores[i];
if(strcmp(rpmteN(p), se->N) == 0) {
found = 1;
break;
}
}
/* If we did not find the entry then allocate space for it */
if(!found) {
rpmMessage(RPMMESS_DEBUG, _("\tAdding entry for %s to score board.\n"),
rpmteN(p));
se = xcalloc(1, sizeof(*(*(score->scores))));
rpmMessage(RPMMESS_DEBUG, _("\t\tEntry address: 0x%x\n"), se);
se->N = xstrdup(rpmteN(p));
se->te_types = rpmteType(p);
se->installed = 0;
se->erased = 0;
score->scores[score->entries] = se;
score->entries++;
} else {
/* We found this one, so just add the element type to the one
* already there.
*/
rpmMessage(RPMMESS_DEBUG, _("\tUpdating entry for %s in score board.\n"),
rpmteN(p));
score->scores[i]->te_types |= rpmteType(p);
}
}
pi = rpmtsiFree(pi);
/*
* Attach the score to the running transaction and the autorollback
* transaction.
*/
runningTS->score = score;
score->nrefs++;
rollbackTS->score = score;
score->nrefs++;
return rc;
}
rpmRC rpmtsScoreFree(rpmtsScore score)
{
int i;
rpmtsScoreEntry se = NULL;
rpmMessage(RPMMESS_DEBUG, _("May free Score board(0x%x)\n"), score);
/* If score is not initialized, then just return.
* This is likely the case if autorollbacks are not enabled.
*/
if(score == NULL) return RPMRC_OK;
/* Decrement the reference count */
score->nrefs--;
/* Do we have any more references? If so
* just return.
*/
if(score->nrefs > 0) return RPMRC_OK;
rpmMessage(RPMMESS_DEBUG, _("\tRefcount is zero...will free\n"), score);
/* No more references, lets clean up */
/* First deallocate the score entries */
for(i = 0; i < score->entries; i++) {
/* Get the score for the ith entry */
se = score->scores[i];
/* Deallocate the score entries name */
_free(se->N);
/* Deallocate the score entry itself */
_free(se);
}
/* Next deallocate the score entry table */
_free(score->scores);
/* Finally deallocate the score itself */
_free(score);
return RPMRC_OK;
}
/*
* XXX: Do not get the score and then store it aside for later use.
* we will delete it out from under you. There is not rpmtsScoreLink()
* as this may be a very temporary fix for autorollbacks.
*/
rpmtsScore rpmtsGetScore(rpmts ts)
{
if(ts == NULL) return NULL;
return ts->score;
}
/*
* XXX: Do not get the score entry and then store it aside for later use.
* we will delete it out from under you. There is not an
* rpmtsScoreEntryLink() as this may be a very temporary fix
* for autorollbacks.
* XXX: The scores are not sorted. This should be fixed at earliest
* opportunity (i.e. when we have the whole autorollback working).
*/
rpmtsScoreEntry rpmtsScoreGetEntry(rpmtsScore score, const char *N)
{
int i;
rpmtsScoreEntry se;
rpmtsScoreEntry ret = NULL; /* Assume we don't find it */
rpmMessage(RPMMESS_DEBUG, _("Looking in score board(0x%x) for %s\n"), score, N);
/* Try to find the entry in the score list */
for(i = 0; i < score->entries; i++) {
se = score->scores[i];
if(strcmp(N, se->N) == 0) {
rpmMessage(RPMMESS_DEBUG, _("\tFound entry at address: 0x%x\n"), se);
ret = se;
break;
}
}
return ret;
}

View File

@ -1,10 +1,6 @@
#ifndef H_RPMTS
#define H_RPMTS
/** \ingroup rpmts
* \file lib/rpmts.h
* Structures and prototypes used for an "rpmts" transaction set.
*/
#include "rpmps.h"
#include "rpmsw.h"
@ -39,6 +35,15 @@ typedef enum rpmVSFlags_e {
/* bit(s) 16-31 unused */
} rpmVSFlags;
/**
* Transaction Types
*/
typedef enum rpmtsType_e {
RPMTRANS_TYPE_NORMAL = 0,
RPMTRANS_TYPE_ROLLBACK = (1 << 0),
RPMTRANS_TYPE_AUTOROLLBACK = (1 << 1)
} rpmtsType;
#define _RPMVSF_NODIGESTS \
( RPMVSF_NOSHA1HEADER | \
RPMVSF_NOMD5HEADER | \
@ -92,6 +97,84 @@ typedef enum rpmtsOpX_e {
#include "rpmhash.h" /* XXX hashTable */
#include "rpmal.h" /* XXX availablePackage/relocateFileList ,*/
/**********************
* Transaction Scores *
**********************
*
* In order to allow instance counts to be adjusted properly when an
* autorollback transaction is ran, we keep a list that is indexed
* by rpm name of whether the rpm has been installed or erased. This listed
* is only updated:
*
* iif autorollbacks are enabled.
* iif this is not a rollback or autorollback transaction.
*
* When creating an autorollback transaction, its rpmts points to the same
* rpmtsScore object as the running transaction. So when the autorollback
* transaction runs it can see where each package was in the running transaction
* at the point the running transaction failed, and thus on a per package
* basis make adjustments to the instance counts.
*
* XXX: Jeff, I am not convinced that this does not need to be in its own file
* (i.e. rpmtsScore.{h,c}), but I first wanted to get it working.
*/
struct rpmtsScoreEntry_s {
char * N; /*!<Name of package */
rpmElementType te_types; /*!<te types this entry represents */
int installed; /*!<Was the new header installed */
int erased; /*!<Was the old header removed */
};
typedef struct rpmtsScoreEntry_s * rpmtsScoreEntry;
struct rpmtsScore_s {
int entries; /*!< Number of scores */
rpmtsScoreEntry * scores; /*!< Array of score entries */
int nrefs; /*!< Reference count. */
};
typedef struct rpmtsScore_s * rpmtsScore;
/** \ingroup rpmts
* initialize rpmtsScore for running transaction and autorollback
* transaction.
* @param runningTS Running Transaction.
* @param rollbackTS Rollback Transaction.
* @return RPMRC_OK
*/
rpmRC rpmtsScoreInit(rpmts runningTS, rpmts rollbackTS);
/** \ingroup rpmts
* Free rpmtsScore provided no more references exist against it.
* @param score rpmtsScore to free
* @return RPMRC_OK
*/
rpmRC rpmtsScoreFree(rpmtsScore score);
/** \ingroup rpmts
* Get rpmtsScore from transaction.
* @param ts RPM Transaction.
* @return rpmtsScore or NULL.
*/
rpmtsScore rpmtsGetScore(rpmts ts);
/** \ingroup rpmts
* Get rpmtsScoreEntry from rpmtsScore.
* @param score RPM Transaction Score.
* @return rpmtsScoreEntry or NULL.
*/
rpmtsScoreEntry rpmtsScoreGetEntry(rpmtsScore score, const char *N);
/** \ingroup rpmts
* \file lib/rpmts.h
* Structures and prototypes used for an "rpmts" transaction set.
*/
/**************************
* END Transaction Scores *
**************************/
/*@unchecked@*/
/*@-exportlocal@*/
extern int _cacheDependsRC;
@ -135,6 +218,7 @@ typedef enum tsStage_e {
struct rpmts_s {
rpmtransFlags transFlags; /*!< Bit(s) to control operation. */
tsmStage goal; /*!< Transaction goal (i.e. mode) */
rpmtsType type; /*!< default, rollback, autorollback */
/*@refcounted@*/ /*@null@*/
rpmdb sdb; /*!< Solve database handle. */
@ -233,9 +317,9 @@ struct rpmts_s {
/*@null@*/
Spec spec; /*!< Spec file control structure. */
rpmtsScore score; /*!< Transaction Score (autorollback). */
/*@refs@*/
int nrefs; /*!< Reference count. */
};
#endif /* _RPMTS_INTERNAL */
@ -451,6 +535,26 @@ int rpmtsSetSolveCallback(rpmts ts,
const void * solveData)
/*@modifies ts @*/;
/**
* Set transaction type. Allowed types are:
*
* RPMTRANS_TYPE_NORMAL
* RPMTRANS_TYPE_ROLLBACK
* RPMTRANS_TYPE_AUTOROLLBACK
*
* @param ts transaction set
* @param rollback rpmtsType
* @return void
*/
void rpmtsSetType(rpmts ts, rpmtsType type);
/**
* Return the type of a transaction.
* @param ts transaction set
* @return 0 it is not, 1 it is.
*/
rpmtsType rpmtsGetType(rpmts ts);
/**
* Return current transaction set problems.
* @param ts transaction set
@ -959,4 +1063,5 @@ uint_32 hGetColor(Header h)
}
#endif
#endif /* H_RPMTS */

View File

@ -32,6 +32,13 @@
#include "debug.h"
/*
* This is needed for the IDTX definitions. I think probably those need
* to be moved into a different source file (idtx.{c,h}), but that is up
* Jeff Johnson.
*/
#include "rpmcli.h"
/*@access Header @*/ /* XXX ts->notify arg1 is void ptr */
/*@access rpmps @*/ /* XXX need rpmProblemSetOK() */
/*@access dbiIndexSet @*/
@ -47,6 +54,35 @@
/*@access rpmtsi @*/
/*@access rpmts @*/
/* Internal function to rollback a transaction
* This is not declared in the header because we not
* want others calling this directly (or at all).
*/
rpmRC _rpmtsRollback(rpmts rollbackTransaction);
/* Internal function to add an element to a rollback transaction
* This is not declared in the header because we not want others
* calling this directly (or at all).
*/
rpmRC _rpmtsAddRollbackElement(rpmts rollbackTransaction, rpmts runningTransaction, rpmte te);
/* You might want to move this into the header Jeff (or even
* to a different file altogether (i.e. the prototype and the
* the function).
*/
rpmRC getRepackageHeaderFromTE(rpmte te, rpmts ts, Header *hdrp, char **fn);
/* XXX: This is a hack. I needed a to setup a notify callback
* for the rollback transaction, but I did not want to create
* a header for rpminstall.c.
*/
extern void * rpmShowProgress(/*@null@*/ const void * arg,
const rpmCallbackType what,
const unsigned long amount,
const unsigned long total,
/*@null@*/ fnpyKey key,
/*@null@*/ void * data);
/**
*/
static int archOkay(/*@null@*/ const char * pkgArch)
@ -953,13 +989,28 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
rpmtsi qi; rpmte q;
int numAdded;
int numRemoved;
rpmts rollbackTransaction = NULL;
int rollbackOnFailure = 0;
void * lock;
int xx;
/* XXX programmer error segfault avoidance. */
if (rpmtsNElements(ts) <= 0)
return -1;
/* See if we need to rollback on failure */
rollbackOnFailure = rpmExpandNumeric(
"%{?_rollback_transaction_on_failure}");
if(rpmtsGetType(ts) & (RPMTRANS_TYPE_ROLLBACK
| RPMTRANS_TYPE_AUTOROLLBACK)) {
rollbackOnFailure = 0;
}
/* If we are in test mode, there is no need to rollback on
* failure (-;
*/
if(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) rollbackOnFailure = 0;
lock = rpmtsAcquireLock(ts);
if (lock == NULL)
return -1; /* XXX W2DO? */
@ -1345,6 +1396,56 @@ rpmMessage(RPMMESS_DEBUG, _("computing file dispositions\n"));
return ts->orderCount;
}
/* ===============================================
* If we were requested to rollback this transaction
* if an error occurs, then we need to create a
* a rollback transaction.
*/
if(rollbackOnFailure) {
rpmtransFlags xx;
rpmVSFlags ovsflags;
rpmVSFlags vsflags;
rpmMessage(RPMMESS_DEBUG,
_("Creating auto-rollback transaction\n"));
rollbackTransaction = rpmtsCreate();
/* Set the verify signature flags:
* - can't verify digests on repackaged packages. Other than
* they are wrong, this will cause segfaults down stream.
* - signatures are out too.
* - header check are out.
*/
vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
vsflags |= _RPMVSF_NODIGESTS;
vsflags |= _RPMVSF_NOSIGNATURES;
vsflags |= RPMVSF_NOHDRCHK;
vsflags |= RPMVSF_NEEDPAYLOAD; /* XXX no legacy signatures */
ovsflags = rpmtsSetVSFlags(ts, vsflags);
/*
* If we run this thing its imperitive that it be known that it
* is an autorollback transaction. This will affect the instance
* counts passed to the scriptlets in the psm.
*/
rpmtsSetType(rollbackTransaction, RPMTRANS_TYPE_AUTOROLLBACK);
/* Set transaction flags to be the same as the running transaction */
xx = rpmtsSetFlags(rollbackTransaction, rpmtsFlags(ts));
/* Set root dir to be the same as the running transaction */
rpmtsSetRootDir(rollbackTransaction, rpmtsRootDir(ts));
/* Setup the notify of the call back to be the same as the running
* transaction
*/
xx = rpmtsSetNotifyCallback(rollbackTransaction, ts->notify, ts->notifyData);
/* Create rpmtsScore for running transaction and rollback transaction */
xx = rpmtsScoreInit(ts, rollbackTransaction);
}
/* ===============================================
* Save removed files before erasing.
*/
@ -1454,6 +1555,23 @@ assert(psm != NULL);
/*@=noeffectuncon@*/
p->fd = NULL;
ourrc++;
/* If we should rollback this transaction
on failure, lets do it. */
if(rollbackOnFailure) {
rpmMessage(RPMMESS_ERROR,
_("Add failed. Could not read package header.\n"));
/* Clean up the current transaction */
p->h = headerFree(p->h);
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Run the rollback transaction */
xx = _rpmtsRollback(rollbackTransaction);
return -1;
}
/*@innerbreak@*/ break;
case RPMRC_NOTTRUSTED:
case RPMRC_NOKEY:
@ -1501,11 +1619,67 @@ assert(psm != NULL);
if (rpmpsmStage(psm, PSM_PKGINSTALL)) {
ourrc++;
lastFailKey = pkgKey;
/* If we should rollback this transaction
on failure, lets do it. */
if(rollbackOnFailure) {
rpmMessage(RPMMESS_ERROR,
_("Add failed in rpmpsmStage().\n"));
/* Clean up the current transaction */
p->h = headerFree(p->h);
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Run the rollback transaction */
xx = _rpmtsRollback(rollbackTransaction);
return -1;
}
}
/* If we should rollback on failure lets add
* this element to the rollback transaction
* as an erase element as it has installed succesfully.
*/
if(rollbackOnFailure) {
int rc;
rc = _rpmtsAddRollbackElement(rollbackTransaction, ts, p);
if(rc != RPMRC_OK) {
/* Clean up the current transaction */
p->h = headerFree(p->h);
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Clean up rollback transaction */
rpmtsFree(rollbackTransaction);
return -1;
}
}
/*@=nullstate@*/
} else {
ourrc++;
lastFailKey = pkgKey;
/* If we should rollback this transaction
* on failure, lets do it.
*/
if(rollbackOnFailure) {
rpmMessage(RPMMESS_ERROR, _("Add failed. Could not get file list.\n"));
/* Clean up the current transaction */
p->h = headerFree(p->h);
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Run the rollback transaction */
xx = _rpmtsRollback(rollbackTransaction);
return -1;
}
}
if (gotfd) {
@ -1535,8 +1709,48 @@ assert(psm != NULL);
* If install failed, then we shouldn't erase.
*/
if (rpmteDependsOnKey(p) != lastFailKey) {
if (rpmpsmStage(psm, PSM_PKGERASE))
if (rpmpsmStage(psm, PSM_PKGERASE)) {
ourrc++;
/* If we should rollback this transaction
* on failure, lets do it.
*/
if(rollbackOnFailure) {
rpmMessage(RPMMESS_ERROR,
_("Erase failed failed in rpmpsmStage().\n"));
/* Clean up the current transaction */
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Run the rollback transaction */
xx = _rpmtsRollback(rollbackTransaction);
return -1;
}
}
/* If we should rollback on failure lets add
* this element to the rollback transaction
* as an install element as it has erased succesfully.
*/
if(rollbackOnFailure) {
int rc;
rc = _rpmtsAddRollbackElement(rollbackTransaction, ts, p);
if(rc != RPMRC_OK) {
/* Clean up the current transaction */
xx = rpmdbSync(rpmtsGetRdb(ts));
psm = rpmpsmFree(psm);
p->fi = rpmfiFree(p->fi);
pi = rpmtsiFree(pi);
/* Clean up rollback transaction */
rpmtsFree(rollbackTransaction);
return -1;
}
}
}
(void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ERASE), 0);
@ -1557,6 +1771,11 @@ assert(psm != NULL);
/*@=branchstate@*/
pi = rpmtsiFree(pi);
/* If we created a rollback transaction lets get rid of it */
if(rollbackOnFailure && rollbackTransaction != NULL) {
rpmtsFree(rollbackTransaction);
}
rpmtsFreeLock(lock);
/*@-nullstate@*/ /* FIX: ts->flList may be NULL */
@ -1566,3 +1785,369 @@ assert(psm != NULL);
return 0;
/*@=nullstate@*/
}
/**
* Get the repackaged header and filename from the repackage directory.
* @todo Find a suitable home for this function.
* @todo This function creates an IDTX everytime it is called. Needs to
* be made more efficient (only create on per running transaction).
* @param te transaction element
* @param rpmts rpm transaction
* @return hdrp Repackaged header
* @return fn Repackaged package's path (transaction key)
* @return RPMRC_NOTFOUND or RPMRC_OK
*/
rpmRC getRepackageHeaderFromTE(rpmte te, rpmts ts, Header *hdrp, char **fn)
{
int_32 tid;
const char * name;
const char * rpname = NULL;
const char * _repackage_dir = NULL;
const char * globStr = "-*.rpm";
char * rp = NULL; /* Rollback package name */
IDTX rtids = NULL;
IDT rpIDT;
int nrids = 0;
int nb; /* Number of bytes */
Header h = NULL;
int rc = RPMRC_NOTFOUND; /* Assume we do not find it*/
rpmMessage(RPMMESS_DEBUG,
_("Getting repackaged header from transaction element\n"));
/* Set header pointer to null if its not already */
if(hdrp)
*hdrp = NULL;
if(fn)
*fn = NULL;
/* Get the TID of the current transaction */
tid = rpmtsGetTid(ts);
/* Need the repackage dir if the user want to
* rollback on a failure.
*/
_repackage_dir = rpmExpand("%{?_repackage_dir}", NULL);
if(_repackage_dir == NULL) goto exit;
/* Build the glob string to find the possible repackaged
* packages for this package.
*/
name = rpmteN(te);
nb = strlen(_repackage_dir) + strlen(name) + strlen(globStr) + 2;
rp = memset((char *) malloc(nb), 0, nb);
snprintf(rp, nb, "%s/%s%s.rpm", _repackage_dir, name, globStr);
/* Get the index of possible repackaged packages */
rpmMessage(RPMMESS_DEBUG, _("\tLooking for %s...\n"), rp);
rtids = IDTXglob(ts, rp, RPMTAG_REMOVETID);
rp = _free(rp);
if (rtids != NULL) {
rpmMessage(RPMMESS_DEBUG, _("\tMatches found.\n"));
rpIDT = rtids->idt;
nrids = rtids->nidt;
} else {
rpmMessage(RPMMESS_DEBUG, _("\tNo matches found.\n"));
goto exit;
}
/* Now walk through index until we find the package (or we have
* exhausted the index.
*/
do {
/* If index is null we have exhausted the list and need to
* get out of here...the repackaged package was not found.
*/
if(rpIDT == NULL) {
rpmMessage(RPMMESS_DEBUG, _("\tRepackaged package not found!.\n"));
break;
}
/* Is this the same tid. If not decrement the list and continue */
if(rpIDT->val.u32 != tid) {
nrids--;
if(nrids > 0)
rpIDT++;
else
rpIDT = NULL;
continue;
}
/* OK, the tid matches. Now lets see if the name is the same.
* If I could not get the name from the package, I will go onto
* the next one. Perhaps I should return an error at this
* point, but if this was not the correct one, at least the correct one
* would be found.
* XXX: Should I be matching name and arch?
*/
rpmMessage(RPMMESS_DEBUG, _("\tREMOVETID matched INSTALLTID.\n"));
if(headerGetEntry(rpIDT->h, RPMTAG_NAME, NULL, (void **) &rpname, NULL)) {
rpmMessage(RPMMESS_DEBUG, _("\t\tName: %s.\n"), rpname);
if(!strcmp(name,rpname)) {
/* It matched we have a canidate */
h = headerLink(rpIDT->h);
nb = strlen(rpIDT->key) + 1;
rp = memset((char *) malloc(nb), 0, nb);
rp = strncat(rp, rpIDT->key, nb);
rc = RPMRC_OK;
break;
}
}
/* Decrement list */
nrids--;
if(nrids > 0)
rpIDT++;
else
rpIDT = NULL;
} while(1);
exit:
if(rc != RPMRC_NOTFOUND && h != NULL && hdrp != NULL) {
rpmMessage(RPMMESS_DEBUG, _("\tRepackaged Package was %s...\n"), rp);
*hdrp = headerLink(h);
*fn = rp;
}
if(h != NULL) {
h = headerFree(h);
}
rtids = IDTXfree(rtids);
return rc;
}
/**
* This is not a generalized function to be called from outside
* librpm. It is called internally by rpmtsRun() to add elements
* to its rollback transaction.
* @param rollbackTransaction rollback transaction
* @param runningTransaction running transaction (the one you want to rollback)
* @param te Transaction element.
* @return RPMRC_OK, or RPMRC_FAIL
*/
rpmRC _rpmtsAddRollbackElement(rpmts rollbackTransaction, rpmts runningTransaction, rpmte te)
{
Header h = NULL;
Header rph = NULL;
char * rpn;
unsigned int db_instance = 0;
rpmtsi pi;
rpmte p;
int rc = RPMRC_FAIL; /* Assume Failure */
switch(rpmteType(te)) {
case TR_ADDED:
rpmMessage(RPMMESS_DEBUG,
_("Adding install element to auto-rollback transaction.\n"));
/* Get the header for this package from the database
* First get the database instance (the key).
*/
db_instance = rpmteDBInstance(te);
if(db_instance <= 0) {
/* Could not get the db instance: WTD! */
rpmMessage(RPMMESS_FATALERROR,
_("Could not get install element database instance!\n"));
break;
}
/* Now suck the header out of the database */
rpmdbMatchIterator mi = rpmtsInitIterator(rollbackTransaction,
RPMDBI_PACKAGES, &db_instance, sizeof(db_instance));
h = rpmdbNextIterator(mi);
if(h != NULL) h = headerLink(h);
mi = rpmdbFreeIterator(mi);
if(h == NULL) {
/* Header was not there??? */
rpmMessage(RPMMESS_FATALERROR,
_("Could not get header for auto-rollback transaction!\n"));
break;
}
/* Now see if there is a repackaged package for this */
rc = getRepackageHeaderFromTE(te, runningTransaction, &rph, &rpn);
switch(rc) {
case RPMRC_OK:
/* Add the install element, as we had a repackaged package */
rpmMessage(RPMMESS_DEBUG,
_("\tAdded repackaged package header: %s.\n"), rpn);
rc = rpmtsAddInstallElement(rollbackTransaction, headerLink(rph),
(fnpyKey) rpn, 1, te->relocs);
break;
case RPMRC_NOTFOUND:
/* Add the header as an erase element, we did not
* have a repackaged package
*/
rpmMessage(RPMMESS_DEBUG, _("\tAdded erase element.\n"));
rc = rpmtsAddEraseElement(rollbackTransaction, h, db_instance);
break;
default:
/* Not sure what to do on failure...just give up */
rpmMessage(RPMMESS_FATALERROR,
_("Could not get repackaged header for auto-rollback transaction!\n"));
break;
}
break;
case TR_REMOVED:
rpmMessage(RPMMESS_DEBUG,
_("Add erase element to auto-rollback transaction.\n"));
/* See if this element has already been added as an upgrade.
* If so we want to do nothing.
*/
pi = rpmtsiInit(rollbackTransaction);
while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
if(rpmteType(p) == TR_ADDED) continue;
if(!strcmp(rpmteN(p), rpmteN(te))) {
rpmMessage(RPMMESS_DEBUG, _("\tFound existing upgrade element.\n"));
rpmMessage(RPMMESS_DEBUG, _("\tNot adding erase element for %s.\n"),
rpmteN(te));
rc = RPMRC_OK;
pi = rpmtsiFree(pi);
break;
}
}
pi = rpmtsiFree(pi);
/* Get the repackage header from the current transaction
* element.
*/
rc = getRepackageHeaderFromTE(te, runningTransaction, &rph, &rpn);
switch(rc) {
case RPMRC_OK:
/* Add the install element */
rpmMessage(RPMMESS_DEBUG,
_("\tAdded repackaged package %s.\n"), rpn);
rc = rpmtsAddInstallElement(rollbackTransaction, rph,
(fnpyKey) rpn, 1, te->relocs);
if(rc != RPMRC_OK)
rpmMessage(RPMMESS_FATALERROR,
_("Could not add erase element to auto-rollback transaction.\n"));
break;
case RPMRC_NOTFOUND:
/* Just did not have a repackaged package */
rpmMessage(RPMMESS_DEBUG,
_("\tNo repackaged package...nothing to do.\n"));
rc = RPMRC_OK;
break;
default:
rpmMessage(RPMMESS_FATALERROR,
_("Failure reading repackaged package!\n"));
break;
}
break;
default:
break;
}
/* XXX: I want to free this, but if I do then the consumers of
* are hosed. Just leaving you a little note Jeff, so you
* know that this does introduce a memory leak. I wanted
* keep the patch as simple as possible so I am not fixxing
* the leak.
* if(rpn != NULL)
* free(rpn);
*/
/* Clean up */
if(h != NULL)
h = headerFree(h);
if(rph != NULL)
rph = headerFree(rph);
return rc;
}
/**
* This is not a generalized function to be called from outside
* librpm. It is called internally by rpmtsRun() to rollback
* a failed transaction.
* @param rollbackTransaction rollback transaction
* @return RPMRC_OK, or RPMRC_FAIL
*/
rpmRC _rpmtsRollback(rpmts rollbackTransaction)
{
int rc = 0;
int numAdded = 0;
int numRemoved = 0;
int_32 tid;
rpmtsi tsi;
rpmte te;
rpmps ps;
/*
* Gather information about this rollback transaction for reporting.
* 1) Get tid
*/
tid = rpmtsGetTid(rollbackTransaction);
/*
* 2) Get number of install elments and erase elements
*/
tsi = rpmtsiInit(rollbackTransaction);
while((te = rpmtsiNext(tsi, 0)) != NULL) {
switch (rpmteType(te)) {
case TR_ADDED:
numAdded++;
break;
case TR_REMOVED:
numRemoved++;
break;
default:
break;
}
}
tsi = rpmtsiFree(tsi);
rpmMessage(RPMMESS_NORMAL, _("Transaction failed...rolling back\n"));
rpmMessage(RPMMESS_NORMAL,
_("Rollback packages (+%d/-%d) to %-24.24s (0x%08x):\n"),
numAdded, numRemoved, ctime(&tid), tid);
/* Check the transaction to see if it is doable */
rc = rpmtsCheck(rollbackTransaction);
ps = rpmtsProblems(rollbackTransaction);
if (rc != 0 && rpmpsNumProblems(ps) > 0) {
rpmMessage(RPMMESS_ERROR, _("Failed dependencies:\n"));
rpmpsPrint(NULL, ps);
ps = rpmpsFree(ps);
return -1;
}
ps = rpmpsFree(ps);
/* Order the transaction */
rc = rpmtsOrder(rollbackTransaction);
if (rc != 0) {
rpmMessage(RPMMESS_ERROR,
_("Could not order auto-rollback transaction!\n"));
return -1;
}
/* Run the transaction and print any problems
* We want to stay with the original transactions flags except
* that we want to add what is essentially a force.
* This handles two things in particular:
*
* 1. We we want to upgrade over a newer package.
* 2. If a header for the old package is there we
* we want to replace it. No questions asked.
*/
rc = rpmtsRun(rollbackTransaction, NULL,
RPMPROB_FILTER_REPLACEPKG
| RPMPROB_FILTER_REPLACEOLDFILES
| RPMPROB_FILTER_REPLACENEWFILES
| RPMPROB_FILTER_OLDPACKAGE
);
ps = rpmtsProblems(rollbackTransaction);
if (rc > 0 && rpmpsNumProblems(ps) > 0)
rpmpsPrint(stderr, ps);
ps = rpmpsFree(ps);
rollbackTransaction = rpmtsFree(rollbackTransaction);
return rc;
}