/*@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 @*/
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;
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);
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:
(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;
if (_rpmts_stats)
rpmtsPrintStats(ts);
+ /* Free up the memory used by the rpmtsScore */
+ rpmtsScoreFree(ts->score);
+
(void) rpmtsUnlink(ts, "tsCreate");
/*@-refcounttrans -usereleased @*/
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;
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;
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;
+}
#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"
/* 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 | \
#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;
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. */
/*@null@*/
Spec spec; /*!< Spec file control structure. */
+ rpmtsScore score; /*!< Transaction Score (autorollback). */
/*@refs@*/
int nrefs; /*!< Reference count. */
-
};
#endif /* _RPMTS_INTERNAL */
/*@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
* @return current problem set (or NULL)
}
#endif
+
#endif /* H_RPMTS */
#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 @*/
/*@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)
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? */
}
/* ===============================================
+ * 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.
*/
if (rpmtsFlags(ts) & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
/*@=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:
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) {
* 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);
/*@=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 */
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;
+}