csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
/** \ingroup rpmts payload
2ff057
 * \file lib/psm.c
2ff057
 * Package state machine to handle a package from a transaction set.
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <errno.h>
2ff057
2ff057
#include <rpm/rpmlib.h>		/* rpmvercmp and others */
2ff057
#include <rpm/rpmmacro.h>
2ff057
#include <rpm/rpmds.h>
2ff057
#include <rpm/rpmts.h>
2ff057
#include <rpm/rpmfileutil.h>
2ff057
#include <rpm/rpmdb.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include <rpm/rpmstring.h>
2ff057
#include <rpm/argv.h>
2ff057
2ff057
#include "lib/fsm.h"		/* XXX CPIO_FOO/FSM_FOO constants */
2ff057
#include "lib/rpmchroot.h"
2ff057
#include "lib/rpmfi_internal.h" /* XXX replaced/states... */
2ff057
#include "lib/rpmte_internal.h"	/* XXX internal apis */
2ff057
#include "lib/rpmdb_internal.h" /* rpmdbAdd/Remove */
2ff057
#include "lib/rpmts_internal.h" /* rpmtsPlugins() etc */
2ff057
#include "lib/rpmds_internal.h" /* rpmdsFilterTi() */
2ff057
#include "lib/rpmscript.h"
2ff057
#include "lib/misc.h"
2ff057
#include "lib/rpmtriggers.h"
2ff057
2ff057
#include "lib/rpmplugins.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
struct rpmpsm_s {
2ff057
    rpmts ts;			/*!< transaction set */
2ff057
    rpmte te;			/*!< current transaction element */
2ff057
    rpmfiles files;		/*!< transaction element file info */
2ff057
    int scriptArg;		/*!< Scriptlet package arg. */
2ff057
    int countCorrection;	/*!< 0 if installing, -1 if removing. */
2ff057
    rpmCallbackType what;	/*!< Callback type. */
2ff057
    rpm_loff_t amount;		/*!< Callback amount. */
2ff057
    rpm_loff_t total;		/*!< Callback total. */
2ff057
2ff057
    int nrefs;			/*!< Reference count. */
2ff057
};
2ff057
2ff057
static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal);
2ff057
static rpmRC rpmpsmUnpack(rpmpsm psm);
2ff057
static rpmpsm rpmpsmFree(rpmpsm psm);
2ff057
static const char * pkgGoalString(pkgGoal goal);
2ff057
2ff057
/**
2ff057
 * Adjust file states in database for files shared with this package:
2ff057
 * currently either "replaced" or "wrong color".
2ff057
 * @param psm		package state machine data
2ff057
 * @return		0 always
2ff057
 */
2ff057
static rpmRC markReplacedFiles(const rpmpsm psm)
2ff057
{
2ff057
    const rpmts ts = psm->ts;
2ff057
    rpmfs fs = rpmteGetFileStates(psm->te);
2ff057
    sharedFileInfo replaced = rpmfsGetReplaced(fs);
2ff057
    sharedFileInfo sfi;
2ff057
    rpmdbMatchIterator mi;
2ff057
    Header h;
2ff057
    unsigned int * offsets;
2ff057
    unsigned int prev;
2ff057
    unsigned int num;
2ff057
2ff057
    if (!replaced)
2ff057
	return RPMRC_OK;
2ff057
2ff057
    num = prev = 0;
2ff057
    for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
2ff057
	if (prev && prev == sfi->otherPkg)
2ff057
	    continue;
2ff057
	prev = sfi->otherPkg;
2ff057
	num++;
2ff057
    }
2ff057
    if (num == 0)
2ff057
	return RPMRC_OK;
2ff057
2ff057
    offsets = xmalloc(num * sizeof(*offsets));
2ff057
    offsets[0] = 0;
2ff057
    num = prev = 0;
2ff057
    for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
2ff057
	if (prev && prev == sfi->otherPkg)
2ff057
	    continue;
2ff057
	prev = sfi->otherPkg;
2ff057
	offsets[num++] = sfi->otherPkg;
2ff057
    }
2ff057
2ff057
    mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
2ff057
    rpmdbAppendIterator(mi, offsets, num);
2ff057
    rpmdbSetIteratorRewrite(mi, 1);
2ff057
2ff057
    sfi = replaced;
2ff057
    while ((h = rpmdbNextIterator(mi)) != NULL) {
2ff057
	int modified;
2ff057
	struct rpmtd_s secStates;
2ff057
	modified = 0;
2ff057
2ff057
	if (!headerGet(h, RPMTAG_FILESTATES, &secStates, HEADERGET_MINMEM))
2ff057
	    continue;
2ff057
	
2ff057
	prev = rpmdbGetIteratorOffset(mi);
2ff057
	num = 0;
2ff057
	while (sfi && sfi->otherPkg == prev) {
2ff057
	    int ix = rpmtdSetIndex(&secStates, sfi->otherFileNum);
2ff057
	    assert(ix != -1);
2ff057
2ff057
	    char *state = rpmtdGetChar(&secStates);
2ff057
	    if (state && *state != sfi->rstate) {
2ff057
		*state = sfi->rstate;
2ff057
		if (modified == 0) {
2ff057
		    /* Modified header will be rewritten. */
2ff057
		    modified = 1;
2ff057
		    rpmdbSetIteratorModified(mi, modified);
2ff057
		}
2ff057
		num++;
2ff057
	    }
2ff057
	    sfi=rpmfsNextReplaced(fs, sfi);
2ff057
	}
2ff057
	rpmtdFreeData(&secStates);
2ff057
    }
2ff057
    rpmdbFreeIterator(mi);
2ff057
    free(offsets);
2ff057
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int rpmlibDeps(Header h)
2ff057
{
2ff057
    rpmds req = rpmdsInit(rpmdsNew(h, RPMTAG_REQUIRENAME, 0));
2ff057
    rpmds rpmlib = NULL;
2ff057
    rpmdsRpmlib(&rpmlib, NULL);
2ff057
    int rc = 1;
2ff057
    char *nvr = NULL;
2ff057
    while (rpmdsNext(req) >= 0) {
2ff057
	if (!(rpmdsFlags(req) & RPMSENSE_RPMLIB))
2ff057
	    continue;
2ff057
	if (rpmdsSearch(rpmlib, req) < 0) {
2ff057
	    if (!nvr) {
2ff057
		nvr = headerGetAsString(h, RPMTAG_NEVRA);
2ff057
		rpmlog(RPMLOG_ERR, _("Missing rpmlib features for %s:\n"), nvr);
2ff057
	    }
2ff057
	    rpmlog(RPMLOG_ERR, "\t%s\n", rpmdsDNEVR(req)+2);
2ff057
	    rc = 0;
2ff057
	}
2ff057
    }
2ff057
    rpmdsFree(req);
2ff057
    rpmdsFree(rpmlib);
2ff057
    free(nvr);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd,
2ff057
		char ** specFilePtr, char ** cookie)
2ff057
{
2ff057
    Header h = NULL;
2ff057
    rpmpsm psm = NULL;
2ff057
    rpmte te = NULL;
2ff057
    rpmRC rpmrc;
2ff057
    int specix = -1;
2ff057
2ff057
    rpmrc = rpmReadPackageFile(ts, fd, NULL, &h);
2ff057
    switch (rpmrc) {
2ff057
    case RPMRC_NOTTRUSTED:
2ff057
    case RPMRC_NOKEY:
2ff057
    case RPMRC_OK:
2ff057
	break;
2ff057
    default:
2ff057
	goto exit;
2ff057
	break;
2ff057
    }
2ff057
    if (h == NULL)
2ff057
	goto exit;
2ff057
2ff057
    rpmrc = RPMRC_FAIL; /* assume failure */
2ff057
2ff057
    if (!headerIsSource(h)) {
2ff057
	rpmlog(RPMLOG_ERR, _("source package expected, binary found\n"));
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    /* src.rpm install can require specific rpmlib features, check them */
2ff057
    if (!rpmlibDeps(h))
2ff057
	goto exit;
2ff057
2ff057
    specix = headerFindSpec(h);
2ff057
2ff057
    if (specix < 0) {
2ff057
	rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n"));
2ff057
	goto exit;
2ff057
    };
2ff057
2ff057
    if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) {
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    te = rpmtsElement(ts, 0);
2ff057
    if (te == NULL) {	/* XXX can't happen */
2ff057
	goto exit;
2ff057
    }
2ff057
    rpmteSetFd(te, fd);
2ff057
2ff057
    rpmteSetHeader(te, h);
2ff057
2ff057
    {
2ff057
	/* set all files to be installed */
2ff057
	rpmfs fs = rpmteGetFileStates(te);
2ff057
	int fc = rpmfsFC(fs);
2ff057
	for (int i = 0; i < fc; i++)
2ff057
	    rpmfsSetAction(fs, i, FA_CREATE);
2ff057
    }
2ff057
2ff057
    psm = rpmpsmNew(ts, te, PKG_INSTALL);
2ff057
2ff057
    if (rpmpsmUnpack(psm) == RPMRC_OK)
2ff057
	rpmrc = RPMRC_OK;
2ff057
2ff057
    rpmpsmFree(psm);
2ff057
2ff057
exit:
2ff057
    if (rpmrc == RPMRC_OK && specix >= 0) {
2ff057
	if (cookie)
2ff057
	    *cookie = headerGetAsString(h, RPMTAG_COOKIE);
2ff057
	if (specFilePtr) {
2ff057
	    rpmfiles files = rpmteFiles(te);
2ff057
	    *specFilePtr = rpmfilesFN(files, specix);
2ff057
	    rpmfilesFree(files);
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* XXX nuke the added package(s). */
2ff057
    headerFree(h);
2ff057
    rpmtsEmpty(ts);
2ff057
2ff057
    return rpmrc;
2ff057
}
2ff057
2ff057
static rpmRC runInstScript(rpmpsm psm, rpmTagVal scriptTag)
2ff057
{
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
    struct rpmtd_s pfx;
2ff057
    Header h = rpmteHeader(psm->te);
2ff057
    rpmScript script = rpmScriptFromTag(h, scriptTag);
2ff057
2ff057
    if (script) {
2ff057
	headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
2ff057
	rc = runScript(psm->ts, psm->te, h, pfx.data, script, psm->scriptArg, -1);
2ff057
	rpmtdFreeData(&pfx;;
2ff057
    }
2ff057
2ff057
    rpmScriptFree(script);
2ff057
    headerFree(h);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Execute triggers.
2ff057
 * @todo Trigger on any provides, not just package NVR.
2ff057
 * @param ts		transaction set
2ff057
 * @param te		transaction element
2ff057
 * @param sense		trigger type
2ff057
 * @param sourceH	header of trigger source
2ff057
 * @param trigH		header of triggered package
2ff057
 * @param arg2
2ff057
 * @param triggersAlreadyRun
2ff057
 * @return
2ff057
 */
2ff057
static rpmRC handleOneTrigger(rpmts ts, rpmte te, rpmsenseFlags sense,
2ff057
			Header sourceH, Header trigH, int countCorrection,
2ff057
			int arg2, unsigned char * triggersAlreadyRun)
2ff057
{
2ff057
    rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0));
2ff057
    struct rpmtd_s pfx;
2ff057
    const char * sourceName = headerGetString(sourceH, RPMTAG_NAME);
2ff057
    const char * triggerName = headerGetString(trigH, RPMTAG_NAME);
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
    int i;
2ff057
2ff057
    if (trigger == NULL)
2ff057
	return rc;
2ff057
2ff057
    headerGet(trigH, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
2ff057
    (void) rpmdsSetNoPromote(trigger, 1);
2ff057
2ff057
    while ((i = rpmdsNext(trigger)) >= 0) {
2ff057
	uint32_t tix;
2ff057
2ff057
	if (!(rpmdsFlags(trigger) & sense))
2ff057
	    continue;
2ff057
2ff057
 	if (!rstreq(rpmdsN(trigger), sourceName))
2ff057
	    continue;
2ff057
2ff057
	/* XXX Trigger on any provided dependency, not just the package NEVR */
2ff057
	if (!rpmdsAnyMatchesDep(sourceH, trigger, 1))
2ff057
	    continue;
2ff057
2ff057
	tix = rpmdsTi(trigger);
2ff057
	if (triggersAlreadyRun == NULL || triggersAlreadyRun[tix] == 0) {
2ff057
	    int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName);
2ff057
2ff057
	    if (arg1 < 0) {
2ff057
		/* XXX W2DO? fails as "execution of script failed" */
2ff057
		rc = RPMRC_FAIL;
2ff057
	    } else {
2ff057
		rpmScript script = rpmScriptFromTriggerTag(trigH,
2ff057
			     triggertag(sense), RPMSCRIPT_NORMALTRIGGER, tix);
2ff057
		arg1 += countCorrection;
2ff057
		rc = runScript(ts, te, trigH, pfx.data, script, arg1, arg2);
2ff057
		if (triggersAlreadyRun != NULL)
2ff057
		    triggersAlreadyRun[tix] = 1;
2ff057
2ff057
		rpmScriptFree(script);
2ff057
	    }
2ff057
	}
2ff057
2ff057
	/*
2ff057
	 * Each target/source header pair can only result in a single
2ff057
	 * script being run.
2ff057
	 */
2ff057
	break;
2ff057
    }
2ff057
2ff057
    rpmtdFreeData(&pfx;;
2ff057
    rpmdsFree(trigger);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Run trigger scripts in the database that are fired by this header.
2ff057
 * @param psm		package state machine data
2ff057
 * @param sense		trigger type
2ff057
 * @return		0 on success
2ff057
 */
2ff057
static rpmRC runTriggers(rpmpsm psm, rpmsenseFlags sense)
2ff057
{
2ff057
    const rpmts ts = psm->ts;
2ff057
    int numPackage = -1;
2ff057
    const char * N = NULL;
2ff057
    int nerrors = 0;
2ff057
2ff057
    if (psm->te) 	/* XXX can't happen */
2ff057
	N = rpmteN(psm->te);
2ff057
    if (N) 		/* XXX can't happen */
2ff057
	numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
2ff057
				+ psm->countCorrection;
2ff057
    if (numPackage < 0)
2ff057
	return RPMRC_NOTFOUND;
2ff057
2ff057
    {	Header triggeredH;
2ff057
	Header h = rpmteHeader(psm->te);
2ff057
	rpmdbMatchIterator mi;
2ff057
2ff057
	mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0);
2ff057
	while ((triggeredH = rpmdbNextIterator(mi)) != NULL) {
2ff057
	    nerrors += handleOneTrigger(ts, NULL, sense, h, triggeredH,
2ff057
					0, numPackage, NULL);
2ff057
	}
2ff057
	rpmdbFreeIterator(mi);
2ff057
	headerFree(h);
2ff057
    }
2ff057
2ff057
    return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Run triggers from this header that are fired by headers in the database.
2ff057
 * @param psm		package state machine data
2ff057
 * @param sense		trigger type
2ff057
 * @return		0 on success
2ff057
 */
2ff057
static rpmRC runImmedTriggers(rpmpsm psm, rpmsenseFlags sense)
2ff057
{
2ff057
    const rpmts ts = psm->ts;
2ff057
    unsigned char * triggersRun;
2ff057
    struct rpmtd_s tnames, tindexes;
2ff057
    Header h = rpmteHeader(psm->te);
2ff057
    int nerrors = 0;
2ff057
2ff057
    if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) &&
2ff057
	  headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) {
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun));
2ff057
    {	Header sourceH = NULL;
2ff057
	const char *trigName;
2ff057
    	rpm_count_t *triggerIndices = tindexes.data;
2ff057
2ff057
	while ((trigName = rpmtdNextString(&tnames))) {
2ff057
	    rpmdbMatchIterator mi;
2ff057
	    int i = rpmtdGetIndex(&tnames);
2ff057
2ff057
	    if (triggersRun[triggerIndices[i]] != 0) continue;
2ff057
	
2ff057
	    mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0);
2ff057
2ff057
	    while ((sourceH = rpmdbNextIterator(mi)) != NULL) {
2ff057
		nerrors += handleOneTrigger(psm->ts, psm->te,
2ff057
				sense, sourceH, h,
2ff057
				psm->countCorrection,
2ff057
				rpmdbGetIteratorCount(mi),
2ff057
				triggersRun);
2ff057
	    }
2ff057
2ff057
	    rpmdbFreeIterator(mi);
2ff057
	}
2ff057
    }
2ff057
    rpmtdFreeData(&tnames);
2ff057
    rpmtdFreeData(&tindexes);
2ff057
    free(triggersRun);
2ff057
2ff057
exit:
2ff057
    headerFree(h);
2ff057
    return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
2ff057
}
2ff057
2ff057
static rpmpsm rpmpsmFree(rpmpsm psm)
2ff057
{
2ff057
    if (psm) {
2ff057
	rpmfilesFree(psm->files);
2ff057
	rpmtsFree(psm->ts),
2ff057
	/* XXX rpmte not refcounted yet */
2ff057
	memset(psm, 0, sizeof(*psm)); /* XXX trash and burn */
2ff057
    	free(psm);
2ff057
    }
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal)
2ff057
{
2ff057
    rpmpsm psm = xcalloc(1, sizeof(*psm));
2ff057
    psm->ts = rpmtsLink(ts);
2ff057
    psm->files = rpmteFiles(te);
2ff057
    psm->te = te; /* XXX rpmte not refcounted yet */
2ff057
    if (!rpmteIsSource(te)) {
2ff057
	/*
2ff057
	 * When we run scripts, we pass an argument which is the number of
2ff057
	 * versions of this package that will be installed when we are
2ff057
	 * finished.
2ff057
	 */
2ff057
	int npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(te));
2ff057
	switch (goal) {
2ff057
	case PKG_INSTALL:
2ff057
	case PKG_PRETRANS:
2ff057
	    psm->scriptArg = npkgs_installed + 1;
2ff057
	    psm->countCorrection = 0;
2ff057
	    break;
2ff057
	case PKG_ERASE:
2ff057
	    psm->scriptArg = npkgs_installed - 1;
2ff057
	    psm->countCorrection = -1;
2ff057
	    break;
2ff057
	case PKG_VERIFY:
2ff057
	case PKG_POSTTRANS:
2ff057
	    psm->scriptArg = npkgs_installed;
2ff057
	    psm->countCorrection = 0;
2ff057
	    break;
2ff057
	default:
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (goal == PKG_INSTALL) {
2ff057
	Header h = rpmteHeader(te);
2ff057
	psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE);
2ff057
	headerFree(h);
2ff057
    } else if (goal == PKG_ERASE) {
2ff057
	psm->total = rpmfilesFC(psm->files);
2ff057
    }
2ff057
    /* Fake up something for packages with no files */
2ff057
    if (psm->total == 0)
2ff057
	psm->total = 100;
2ff057
2ff057
    if (goal == PKG_INSTALL || goal == PKG_ERASE) {
2ff057
	rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", pkgGoalString(goal),
2ff057
	    rpmteNEVRA(psm->te), rpmfilesFC(psm->files));
2ff057
    }
2ff057
2ff057
    return psm;
2ff057
}
2ff057
2ff057
void rpmpsmNotify(rpmpsm psm, int what, rpm_loff_t amount)
2ff057
{
2ff057
    if (psm) {
2ff057
	int changed = 0;
2ff057
	if (amount > psm->total)
2ff057
	    amount = psm->total;
2ff057
	if (amount > psm->amount) {
2ff057
	    psm->amount = amount;
2ff057
	    changed = 1;
2ff057
	}
2ff057
	if (what && what != psm->what) {
2ff057
	    psm->what = what;
2ff057
	    changed = 1;
2ff057
	}
2ff057
	if (changed) {
2ff057
	   rpmtsNotify(psm->ts, psm->te, psm->what, psm->amount, psm->total);
2ff057
	}
2ff057
    }
2ff057
}
2ff057
2ff057
/*
2ff057
 * --replacepkgs hack: find the header instance we're replacing and
2ff057
 * mark it as the db instance of the install element. In PSM_POST,
2ff057
 * if an install element already has a db instance, it's removed
2ff057
 * before proceeding with the adding the new header to the db.
2ff057
 */
2ff057
static void markReplacedInstance(rpmts ts, rpmte te)
2ff057
{
2ff057
    rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(te), 0);
2ff057
    rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(te));
2ff057
    rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(te));
2ff057
    rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(te));
2ff057
    /* XXX shouldn't we also do this on colorless transactions? */
2ff057
    if (rpmtsColor(ts)) {
2ff057
	rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(te));
2ff057
	rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(te));
2ff057
    }
2ff057
2ff057
    while (rpmdbNextIterator(mi) != NULL) {
2ff057
	rpmteSetDBInstance(te, rpmdbGetIteratorOffset(mi));
2ff057
	break;
2ff057
    }
2ff057
    rpmdbFreeIterator(mi);
2ff057
}
2ff057
2ff057
static rpmRC dbAdd(rpmts ts, rpmte te)
2ff057
{
2ff057
    Header h = rpmteHeader(te);
2ff057
    rpm_time_t installTime = (rpm_time_t) time(NULL);
2ff057
    rpmfs fs = rpmteGetFileStates(te);
2ff057
    rpm_count_t fc = rpmfsFC(fs);
2ff057
    rpm_fstate_t * fileStates = rpmfsGetStates(fs);
2ff057
    rpm_color_t tscolor = rpmtsColor(ts);
2ff057
    rpm_tid_t tid = rpmtsGetTid(ts);
2ff057
    rpmRC rc;
2ff057
2ff057
    if (fileStates != NULL && fc > 0) {
2ff057
	headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc);
2ff057
    }
2ff057
2ff057
    headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
2ff057
    headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1);
2ff057
    headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1);
2ff057
2ff057
    (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
2ff057
    rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL;
2ff057
    (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
2ff057
2ff057
    if (rc == RPMRC_OK) {
2ff057
	rpmteSetDBInstance(te, headerGetInstance(h));
2ff057
	packageHashAddEntry(ts->members->installedPackages,
2ff057
			    headerGetInstance(h), te);
2ff057
    }
2ff057
    headerFree(h);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static rpmRC dbRemove(rpmts ts, rpmte te)
2ff057
{
2ff057
    rpmRC rc;
2ff057
2ff057
    (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
2ff057
    rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(te)) == 0) ?
2ff057
						RPMRC_OK : RPMRC_FAIL;
2ff057
    (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
2ff057
2ff057
    if (rc == RPMRC_OK)
2ff057
	rpmteSetDBInstance(te, 0);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static rpmRC rpmpsmUnpack(rpmpsm psm)
2ff057
{
2ff057
    char *failedFile = NULL;
2ff057
    int fsmrc = 0;
2ff057
    int saved_errno = 0;
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0);
2ff057
    /* make sure first progress call gets made */
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0);
2ff057
2ff057
    if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) {
2ff057
	if (rpmfilesFC(psm->files) > 0) {
2ff057
	    fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->files,
2ff057
				   psm, &failedFile);
2ff057
	    saved_errno = errno;
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* XXX make sure progress reaches 100% */
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, psm->total);
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total);
2ff057
2ff057
    if (fsmrc) {
2ff057
	char *emsg;
2ff057
	errno = saved_errno;
2ff057
	emsg = rpmfileStrerror(fsmrc);
2ff057
	rpmlog(RPMLOG_ERR,
2ff057
		_("unpacking of archive failed%s%s: %s\n"),
2ff057
		(failedFile != NULL ? _(" on file ") : ""),
2ff057
		(failedFile != NULL ? failedFile : ""),
2ff057
		emsg);
2ff057
	free(emsg);
2ff057
	rc = RPMRC_FAIL;
2ff057
2ff057
	/* XXX notify callback on error. */
2ff057
	rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0);
2ff057
    }
2ff057
    free(failedFile);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static rpmRC rpmpsmRemove(rpmpsm psm)
2ff057
{
2ff057
    char *failedFile = NULL;
2ff057
    int fsmrc = 0;
2ff057
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0);
2ff057
    /* make sure first progress call gets made */
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0);
2ff057
2ff057
    /* XXX should't we log errors from here? */
2ff057
    if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) {
2ff057
	if (rpmfilesFC(psm->files) > 0) {
2ff057
	    fsmrc = rpmPackageFilesRemove(psm->ts, psm->te, psm->files,
2ff057
					  psm, &failedFile);
2ff057
	}
2ff057
    }
2ff057
    /* XXX make sure progress reaches 100% */
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, psm->total);
2ff057
    rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total);
2ff057
2ff057
    free(failedFile);
2ff057
    return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL;
2ff057
}
2ff057
2ff057
static rpmRC rpmPackageInstall(rpmts ts, rpmpsm psm)
2ff057
{
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
    int once = 1;
2ff057
2ff057
    rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0);
2ff057
    while (once--) {
2ff057
	/* HACK: replacepkgs abuses te instance to remove old header */
2ff057
	if (rpmtsFilterFlags(psm->ts) & RPMPROB_FILTER_REPLACEPKG)
2ff057
	    markReplacedInstance(ts, psm->te);
2ff057
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
2ff057
	    /* Run triggers in other package(s) this package sets off. */
2ff057
	    rc = runTriggers(psm, RPMSENSE_TRIGGERPREIN);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedTriggers(psm, RPMSENSE_TRIGGERPREIN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) {
2ff057
	    rc = runInstScript(psm, RPMTAG_PREIN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	rc = rpmpsmUnpack(psm);
2ff057
	if (rc) break;
2ff057
2ff057
	/*
2ff057
	 * If this package has already been installed, remove it from
2ff057
	 * the database before adding the new one.
2ff057
	 */
2ff057
	if (rpmteDBInstance(psm->te)) {
2ff057
	    rc = dbRemove(ts, psm->te);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	rc = dbAdd(ts, psm->te);
2ff057
	if (rc) break;
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
2ff057
	    /* Run upper file triggers i. e. with higher priorities */
2ff057
	    /* Run file triggers in other package(s) this package sets off. */
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
2ff057
				RPMSCRIPT_FILETRIGGER, 1);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run file triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
2ff057
				    RPMSCRIPT_FILETRIGGER, 1);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) {
2ff057
	    rc = runInstScript(psm, RPMTAG_POSTIN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
2ff057
	    /* Run triggers in other package(s) this package sets off. */
2ff057
	    rc = runTriggers(psm, RPMSENSE_TRIGGERIN);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedTriggers(psm, RPMSENSE_TRIGGERIN);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run lower file triggers i. e. with lower priorities */
2ff057
	    /* Run file triggers in other package(s) this package sets off. */
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
2ff057
				RPMSCRIPT_FILETRIGGER, 2);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run file triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
2ff057
				    RPMSCRIPT_FILETRIGGER, 2);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	rc = markReplacedFiles(psm);
2ff057
    }
2ff057
2ff057
    rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static rpmRC rpmPackageErase(rpmts ts, rpmpsm psm)
2ff057
{
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
    int once = 1;
2ff057
2ff057
    rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0);
2ff057
    while (once--) {
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
2ff057
	    /* Run file triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
2ff057
				    RPMSCRIPT_FILETRIGGER, 1);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run file triggers in other package(s) this package sets off. */
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
2ff057
				RPMSCRIPT_FILETRIGGER, 1);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedTriggers(psm, RPMSENSE_TRIGGERUN);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run triggers in other package(s) this package sets off. */
2ff057
	    rc = runTriggers(psm, RPMSENSE_TRIGGERUN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN)) {
2ff057
	    rc = runInstScript(psm, RPMTAG_PREUN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
2ff057
	    /* Run file triggers in this package other package(s) set off. */
2ff057
	    rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
2ff057
				    RPMSCRIPT_FILETRIGGER, 2);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run file triggers in other package(s) this package sets off. */
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
2ff057
				RPMSCRIPT_FILETRIGGER, 2);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	rc = rpmpsmRemove(psm);
2ff057
	if (rc) break;
2ff057
2ff057
	/* Run file triggers in other package(s) this package sets off. */
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN,
2ff057
				RPMSCRIPT_FILETRIGGER, 1);
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) {
2ff057
	    rc = runInstScript(psm, RPMTAG_POSTUN);
2ff057
	    if (rc) break;
2ff057
	}
2ff057
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
2ff057
	    /* Run triggers in other package(s) this package sets off. */
2ff057
	    rc = runTriggers(psm, RPMSENSE_TRIGGERPOSTUN);
2ff057
	    if (rc) break;
2ff057
2ff057
	    /* Run file triggers in other package(s) this package sets off. */
2ff057
	    rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN,
2ff057
				RPMSCRIPT_FILETRIGGER, 2);
2ff057
	}
2ff057
	if (rc) break;
2ff057
2ff057
	if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) {
2ff057
	    /* Prepare post transaction uninstall triggers */
2ff057
	    rpmtriggersPrepPostUnTransFileTrigs(psm->ts, psm->te);
2ff057
	}
2ff057
2ff057
	rc = dbRemove(ts, psm->te);
2ff057
    }
2ff057
2ff057
    rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
static const char * pkgGoalString(pkgGoal goal)
2ff057
{
2ff057
    switch (goal) {
2ff057
    case PKG_INSTALL:	return "  install";
2ff057
    case PKG_ERASE:	return "    erase";
2ff057
    case PKG_VERIFY:	return "   verify";
2ff057
    case PKG_PRETRANS:	return " pretrans";
2ff057
    case PKG_POSTTRANS:	return "posttrans";
2ff057
    default:		return "unknown";
2ff057
    }
2ff057
}
2ff057
2ff057
rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal)
2ff057
{
2ff057
    rpmpsm psm = NULL;
2ff057
    rpmRC rc = RPMRC_FAIL;
2ff057
2ff057
    /* Psm can't fail in test mode, just return early */
2ff057
    if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
2ff057
	return RPMRC_OK;
2ff057
2ff057
    psm = rpmpsmNew(ts, te, goal);
2ff057
    if (rpmChrootIn() == 0) {
2ff057
	/* Run pre transaction element hook for all plugins */
2ff057
	rc = rpmpluginsCallPsmPre(rpmtsPlugins(ts), te);
2ff057
2ff057
	if (!rc) {
2ff057
	    switch (goal) {
2ff057
	    case PKG_INSTALL:
2ff057
		rc = rpmPackageInstall(ts, psm);
2ff057
		break;
2ff057
	    case PKG_ERASE:
2ff057
		rc = rpmPackageErase(ts, psm);
2ff057
		break;
2ff057
	    case PKG_PRETRANS:
2ff057
	    case PKG_POSTTRANS:
2ff057
	    case PKG_VERIFY:
2ff057
		rc = runInstScript(psm, goal);
2ff057
		break;
2ff057
	    case PKG_TRANSFILETRIGGERIN:
2ff057
		rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERIN,
2ff057
					    RPMSCRIPT_TRANSFILETRIGGER, 0);
2ff057
		break;
2ff057
	    case PKG_TRANSFILETRIGGERUN:
2ff057
		rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERUN,
2ff057
					    RPMSCRIPT_TRANSFILETRIGGER, 0);
2ff057
		break;
2ff057
	    default:
2ff057
		break;
2ff057
	    }
2ff057
	}
2ff057
	/* Run post transaction element hook for all plugins */
2ff057
	rpmpluginsCallPsmPost(rpmtsPlugins(ts), te, rc);
2ff057
2ff057
	/* XXX an error here would require a full abort */
2ff057
	(void) rpmChrootOut();
2ff057
    }
2ff057
    rpmpsmFree(psm);
2ff057
    return rc;
2ff057
}