Blame lib/rpmts.c

2ff057
/** \ingroup rpmdep
2ff057
 * \file lib/rpmts.c
2ff057
 * Routine(s) to handle a "rpmts" transaction sets.
2ff057
 */
2ff057
#include "system.h"
2ff057
2ff057
#include <inttypes.h>
2ff057
#include <libgen.h>
2ff057
2ff057
#include <rpm/rpmtypes.h>
2ff057
#include <rpm/rpmlib.h>			/* rpmReadPackage etc */
2ff057
#include <rpm/rpmmacro.h>
2ff057
#include <rpm/rpmfileutil.h>		/* rpmtsOpenDB() needs rpmGetPath */
2ff057
#include <rpm/rpmstring.h>
2ff057
#include <rpm/rpmkeyring.h>
2ff057
#include <rpm/rpmbase64.h>
2ff057
2ff057
#include <rpm/rpmdb.h>
2ff057
#include <rpm/rpmds.h>
2ff057
#include <rpm/rpmfi.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include <rpm/rpmsq.h>
2ff057
#include <rpm/rpmte.h>
2ff057
2ff057
#include "rpmio/digest.h"
2ff057
#include "lib/rpmal.h"
2ff057
#include "lib/rpmchroot.h"
2ff057
#include "lib/rpmplugins.h"
2ff057
#include "lib/rpmts_internal.h"
2ff057
#include "lib/rpmte_internal.h"
2ff057
#include "lib/misc.h"
2ff057
#include "lib/rpmtriggers.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
/**
2ff057
 * Iterator across transaction elements, forward on install, backward on erase.
2ff057
 */
2ff057
struct rpmtsi_s {
2ff057
    rpmts ts;		/*!< transaction set. */
2ff057
    int oc;		/*!< iterator index. */
2ff057
};
2ff057
2ff057
struct rpmtxn_s {
2ff057
    rpmlock lock;	/* transaction lock */
2ff057
    rpmtxnFlags flags;	/* transaction flags */
2ff057
    rpmts ts;		/* parent transaction set reference */
2ff057
};
2ff057
2ff057
static void loadKeyring(rpmts ts);
2ff057
2ff057
int _rpmts_stats = 0;
2ff057
2ff057
static rpmts rpmtsUnlink(rpmts ts)
2ff057
{
2ff057
    if (ts)
2ff057
	ts->nrefs--;
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
rpmts rpmtsLink(rpmts ts)
2ff057
{
2ff057
    if (ts)
2ff057
	ts->nrefs++;
2ff057
    return ts;
2ff057
}
2ff057
2ff057
int rpmtsCloseDB(rpmts ts)
2ff057
{
2ff057
    int rc = 0;
2ff057
2ff057
    if (ts->rdb != NULL) {
2ff057
	(void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
2ff057
			rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
2ff057
	(void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
2ff057
			rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
2ff057
	(void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
2ff057
			rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
2ff057
	rc = rpmdbClose(ts->rdb);
2ff057
	ts->rdb = NULL;
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsOpenDB(rpmts ts, int dbmode)
2ff057
{
2ff057
    int rc = 0;
2ff057
2ff057
    if (ts->rdb != NULL && ts->dbmode == dbmode)
2ff057
	return 0;
2ff057
2ff057
    (void) rpmtsCloseDB(ts);
2ff057
2ff057
    /* XXX there's a potential db lock race here. */
2ff057
2ff057
    ts->dbmode = dbmode;
2ff057
    rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
2ff057
    if (rc) {
2ff057
	char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
2ff057
	rpmlog(RPMLOG_ERR, _("cannot open Packages database in %s\n"), dn);
2ff057
	free(dn);
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsInitDB(rpmts ts, int dbmode)
2ff057
{
2ff057
    rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE);
2ff057
    int rc = -1;
2ff057
    if (txn)
2ff057
	    rc = rpmdbInit(ts->rootDir, dbmode);
2ff057
    rpmtxnEnd(txn);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsGetDBMode(rpmts ts)
2ff057
{
2ff057
    assert(ts != NULL);
2ff057
    return (ts->dbmode);
2ff057
}
2ff057
2ff057
int rpmtsSetDBMode(rpmts ts, int dbmode)
2ff057
{
2ff057
    int rc = 1;
2ff057
    /* mode setting only permitted on non-open db */
2ff057
    if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
2ff057
    	ts->dbmode = dbmode;
2ff057
	rc = 0;
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
2ff057
int rpmtsRebuildDB(rpmts ts)
2ff057
{
2ff057
    int rc = -1;
2ff057
    rpmtxn txn = NULL;
2ff057
2ff057
    /* Cannot do this on a populated transaction set */
2ff057
    if (rpmtsNElements(ts) > 0)
2ff057
	return -1;
2ff057
2ff057
    txn = rpmtxnBegin(ts, RPMTXN_WRITE);
2ff057
    if (txn) {
2ff057
	if (!(ts->vsflags & RPMVSF_NOHDRCHK))
2ff057
	    rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
2ff057
	else
2ff057
	    rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
2ff057
	rpmtxnEnd(txn);
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsVerifyDB(rpmts ts)
2ff057
{
2ff057
    int rc = -1;
2ff057
    rpmtxn txn = rpmtxnBegin(ts, RPMTXN_READ);
2ff057
    if (txn) {
2ff057
	rc = rpmdbVerify(ts->rootDir);
2ff057
	rpmtxnEnd(txn);
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
/* keyp might no be defined. */
2ff057
rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmDbiTagVal rpmtag,
2ff057
			const void * keyp, size_t keylen)
2ff057
{
2ff057
    rpmdbMatchIterator mi = NULL;
2ff057
    char *tmp = NULL;
2ff057
2ff057
    if (ts == NULL)
2ff057
	return NULL;
2ff057
2ff057
    if (ts && ts->keyring == NULL)
2ff057
	loadKeyring(ts);
2ff057
2ff057
    if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
2ff057
	return NULL;
2ff057
2ff057
    /* Parse out "N(EVR)" tokens from a label key if present */
2ff057
    if (rpmtag == RPMDBI_LABEL && keyp != NULL && strchr(keyp, '(')) {
2ff057
	const char *se, *s = keyp;
2ff057
	char *t;
2ff057
	size_t slen = strlen(s);
2ff057
	int level = 0;
2ff057
	int c;
2ff057
2ff057
	tmp = xmalloc(slen+1);
2ff057
	keyp = t = tmp;
2ff057
	while ((c = *s++) != '\0') {
2ff057
	    switch (c) {
2ff057
	    default:
2ff057
		*t++ = c;
2ff057
		break;
2ff057
	    case '(':
2ff057
		/* XXX Fail if nested parens. */
2ff057
		if (level++ != 0) {
2ff057
		    rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), (const char*)keyp);
2ff057
		    goto exit;
2ff057
		}
2ff057
		/* Parse explicit epoch. */
2ff057
		for (se = s; *se && risdigit(*se); se++)
2ff057
		    {};
2ff057
		if (*se == ':') {
2ff057
		    /* XXX skip explicit epoch's (for now) */
2ff057
		    *t++ = '-';
2ff057
		    s = se + 1;
2ff057
		} else {
2ff057
		    /* No Epoch: found. Convert '(' to '-' and chug. */
2ff057
		    *t++ = '-';
2ff057
		}
2ff057
		break;
2ff057
	    case ')':
2ff057
		/* XXX Fail if nested parens. */
2ff057
		if (--level != 0) {
2ff057
		    rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), (const char*)keyp);
2ff057
		    goto exit;
2ff057
		}
2ff057
		/* Don't copy trailing ')' */
2ff057
		break;
2ff057
	    }
2ff057
	}
2ff057
	if (level) {
2ff057
	    rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), (const char*)keyp);
2ff057
	    goto exit;
2ff057
	}
2ff057
	*t = '\0';
2ff057
    }
2ff057
2ff057
    mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
2ff057
2ff057
    /* Verify header signature/digest during retrieve (if not disabled). */
2ff057
    if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
2ff057
	(void) rpmdbSetHdrChk(mi, ts, headerCheck);
2ff057
2ff057
exit:
2ff057
    free(tmp);
2ff057
2ff057
    return mi;
2ff057
}
2ff057
2ff057
rpmKeyring rpmtsGetKeyring(rpmts ts, int autoload)
2ff057
{
2ff057
    rpmKeyring keyring = NULL;
2ff057
    if (ts) {
2ff057
	if (ts->keyring == NULL && autoload) {
2ff057
	    loadKeyring(ts);
2ff057
	}
2ff057
	keyring = rpmKeyringLink(ts->keyring);
2ff057
    }
2ff057
    return keyring;
2ff057
}
2ff057
2ff057
int rpmtsSetKeyring(rpmts ts, rpmKeyring keyring)
2ff057
{
2ff057
    if (ts == NULL)
2ff057
	return -1;
2ff057
2ff057
    rpmKeyringFree(ts->keyring);
2ff057
    ts->keyring = rpmKeyringLink(keyring);
2ff057
    return 0;
2ff057
}
2ff057
2ff057
static int loadKeyringFromFiles(rpmts ts)
2ff057
{
2ff057
    ARGV_t files = NULL;
2ff057
    /* XXX TODO: deal with chroot path issues */
2ff057
    char *pkpath = rpmGetPath(ts->rootDir, "%{_keyringpath}/*.key", NULL);
2ff057
    int nkeys = 0;
2ff057
2ff057
    rpmlog(RPMLOG_DEBUG, "loading keyring from pubkeys in %s\n", pkpath);
2ff057
    if (rpmGlob(pkpath, NULL, &files)) {
2ff057
	rpmlog(RPMLOG_DEBUG, "couldn't find any keys in %s\n", pkpath);
2ff057
	goto exit;
2ff057
    }
2ff057
2ff057
    for (char **f = files; *f; f++) {
2ff057
	int subkeysCount, i;
2ff057
	rpmPubkey *subkeys;
2ff057
	rpmPubkey key = rpmPubkeyRead(*f);
2ff057
2ff057
	if (!key) {
2ff057
	    rpmlog(RPMLOG_ERR, _("%s: reading of public key failed.\n"), *f);
2ff057
	    continue;
2ff057
	}
2ff057
	if (rpmKeyringAddKey(ts->keyring, key) == 0) {
2ff057
	    nkeys++;
2ff057
	    rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", *f);
2ff057
	}
2ff057
	subkeys = rpmGetSubkeys(key, &subkeysCount);
2ff057
	rpmPubkeyFree(key);
2ff057
2ff057
	for (i = 0; i < subkeysCount; i++) {
2ff057
	    rpmPubkey subkey = subkeys[i];
2ff057
2ff057
	    if (rpmKeyringAddKey(ts->keyring, subkey) == 0) {
2ff057
		rpmlog(RPMLOG_DEBUG,
2ff057
		    "added subkey %d of main key %s to keyring\n",
2ff057
		    i, *f);
2ff057
2ff057
		nkeys++;
2ff057
	    }
2ff057
	    rpmPubkeyFree(subkey);
2ff057
	}
2ff057
	free(subkeys);
2ff057
    }
2ff057
exit:
2ff057
    free(pkpath);
2ff057
    argvFree(files);
2ff057
    return nkeys;
2ff057
}
2ff057
2ff057
static int loadKeyringFromDB(rpmts ts)
2ff057
{
2ff057
    Header h;
2ff057
    rpmdbMatchIterator mi;
2ff057
    int nkeys = 0;
2ff057
2ff057
    rpmlog(RPMLOG_DEBUG, "loading keyring from rpmdb\n");
2ff057
    mi = rpmtsInitIterator(ts, RPMDBI_NAME, "gpg-pubkey", 0);
2ff057
    while ((h = rpmdbNextIterator(mi)) != NULL) {
2ff057
	struct rpmtd_s pubkeys;
2ff057
	const char *key;
2ff057
2ff057
	if (!headerGet(h, RPMTAG_PUBKEYS, &pubkeys, HEADERGET_MINMEM))
2ff057
	   continue;
2ff057
2ff057
	while ((key = rpmtdNextString(&pubkeys))) {
2ff057
	    uint8_t *pkt;
2ff057
	    size_t pktlen;
2ff057
2ff057
	    if (rpmBase64Decode(key, (void **) &pkt, &pktlen) == 0) {
2ff057
		rpmPubkey key = rpmPubkeyNew(pkt, pktlen);
2ff057
		int subkeysCount, i;
2ff057
		rpmPubkey *subkeys = rpmGetSubkeys(key, &subkeysCount);
2ff057
2ff057
		if (rpmKeyringAddKey(ts->keyring, key) == 0) {
2ff057
		    char *nvr = headerGetAsString(h, RPMTAG_NVR);
2ff057
		    rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", nvr);
2ff057
		    free(nvr);
2ff057
		    nkeys++;
2ff057
		}
2ff057
		rpmPubkeyFree(key);
2ff057
2ff057
		for (i = 0; i < subkeysCount; i++) {
2ff057
		    rpmPubkey subkey = subkeys[i];
2ff057
2ff057
		    if (rpmKeyringAddKey(ts->keyring, subkey) == 0) {
2ff057
			char *nvr = headerGetAsString(h, RPMTAG_NVR);
2ff057
			rpmlog(RPMLOG_DEBUG,
2ff057
			    "added subkey %d of main key %s to keyring\n",
2ff057
			    i, nvr);
2ff057
2ff057
			free(nvr);
2ff057
			nkeys++;
2ff057
		    }
2ff057
		    rpmPubkeyFree(subkey);
2ff057
		}
2ff057
		free(subkeys);
2ff057
		free(pkt);
2ff057
	    }
2ff057
	}
2ff057
	rpmtdFreeData(&pubkeys);
2ff057
    }
2ff057
    rpmdbFreeIterator(mi);
2ff057
2ff057
    return nkeys;
2ff057
}
2ff057
2ff057
static void loadKeyring(rpmts ts)
2ff057
{
2ff057
    /* Never load the keyring if signature checking is disabled */
2ff057
    if ((rpmtsVSFlags(ts) & RPMVSF_MASK_NOSIGNATURES) !=
2ff057
	RPMVSF_MASK_NOSIGNATURES) {
2ff057
	ts->keyring = rpmKeyringNew();
2ff057
	if (loadKeyringFromFiles(ts) == 0) {
2ff057
	    if (loadKeyringFromDB(ts) > 0) {
2ff057
		/* XXX make this a warning someday... */
2ff057
		rpmlog(RPMLOG_DEBUG, "Using legacy gpg-pubkey(s) from rpmdb\n");
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
}
2ff057
2ff057
/* Build pubkey header. */
2ff057
static int makePubkeyHeader(rpmts ts, rpmPubkey key, rpmPubkey *subkeys,
2ff057
			    int subkeysCount, Header * hdrp)
2ff057
{
2ff057
    Header h = headerNew();
2ff057
    const char * afmt = "%{pubkeys:armor}";
2ff057
    const char * group = "Public Keys";
2ff057
    const char * license = "pubkey";
2ff057
    const char * buildhost = "localhost";
2ff057
    const char * userid;
2ff057
    rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
2ff057
    uint32_t zero = 0;
2ff057
    uint32_t keytime = 0;
2ff057
    pgpDig dig = NULL;
2ff057
    pgpDigParams pubp = NULL;
2ff057
    char * d = NULL;
2ff057
    char * enc = NULL;
2ff057
    char * n = NULL;
2ff057
    char * u = NULL;
2ff057
    char * v = NULL;
2ff057
    char * r = NULL;
2ff057
    char * evr = NULL;
2ff057
    int rc = -1;
2ff057
    int i;
2ff057
2ff057
    if ((enc = rpmPubkeyBase64(key)) == NULL)
2ff057
	goto exit;
2ff057
    if ((dig = rpmPubkeyDig(key)) == NULL)
2ff057
	goto exit;
2ff057
    if ((pubp = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY)) == NULL)
2ff057
	goto exit;
2ff057
2ff057
    /* Build header elements. */
2ff057
    v = pgpHexStr(pubp->signid, sizeof(pubp->signid)); 
2ff057
    userid = pubp->userid ? pubp->userid : "none";
2ff057
    keytime = pubp->time;
2ff057
2ff057
    rasprintf(&n, "gpg(%s)", v+8);
2ff057
    rasprintf(&u, "gpg(%s)", userid);
2ff057
    rasprintf(&r, "%x", keytime);
2ff057
    rasprintf(&evr, "%d:%s-%s", pubp->version, v, r);
2ff057
2ff057
    headerPutString(h, RPMTAG_PUBKEYS, enc);
2ff057
2ff057
    if ((d = headerFormat(h, afmt, NULL)) == NULL)
2ff057
	goto exit;
2ff057
2ff057
    headerPutString(h, RPMTAG_NAME, "gpg-pubkey");
2ff057
    headerPutString(h, RPMTAG_VERSION, v+8);
2ff057
    headerPutString(h, RPMTAG_RELEASE, r);
2ff057
    headerPutString(h, RPMTAG_DESCRIPTION, d);
2ff057
    headerPutString(h, RPMTAG_GROUP, group);
2ff057
    headerPutString(h, RPMTAG_LICENSE, license);
2ff057
    headerPutString(h, RPMTAG_SUMMARY, u);
2ff057
    headerPutString(h, RPMTAG_PACKAGER, userid);
2ff057
2ff057
    headerPutUint32(h, RPMTAG_SIZE, &zero, 1);
2ff057
2ff057
    headerPutString(h, RPMTAG_PROVIDENAME, u);
2ff057
    headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
2ff057
    headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
2ff057
	
2ff057
    headerPutString(h, RPMTAG_PROVIDENAME, n);
2ff057
    headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
2ff057
    headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
2ff057
2ff057
    headerPutString(h, RPMTAG_RPMVERSION, RPMVERSION);
2ff057
    headerPutString(h, RPMTAG_BUILDHOST, buildhost);
2ff057
    headerPutUint32(h, RPMTAG_BUILDTIME, &keytime, 1);
2ff057
    headerPutString(h, RPMTAG_SOURCERPM, "(none)");
2ff057
2ff057
    for (i = 0; i < subkeysCount; i++) {
2ff057
	char *v, *r, *n, *evr;
2ff057
	pgpDigParams pgpkey;
2ff057
2ff057
	pgpkey = rpmPubkeyPgpDigParams(subkeys[i]);
2ff057
	v = pgpHexStr(pgpkey->signid, sizeof(pgpkey->signid));
2ff057
2ff057
	rasprintf(&n, "gpg(%s)", v+8);
2ff057
	rasprintf(&r, "%x", pgpkey->time);
2ff057
	rasprintf(&evr, "%d:%s-%s", pubp->version, v, r);
2ff057
2ff057
	headerPutString(h, RPMTAG_PROVIDENAME, n);
2ff057
	headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
2ff057
	headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
2ff057
2ff057
	free(v);
2ff057
	free(r);
2ff057
	free(n);
2ff057
	free(evr);
2ff057
    }
2ff057
2ff057
    /* Reload the lot to immutable region and stomp sha1 digest on it */
2ff057
    h = headerReload(h, RPMTAG_HEADERIMMUTABLE);
2ff057
    if (h != NULL) {
2ff057
	char *sha1 = NULL;
2ff057
	unsigned int blen = 0;
2ff057
	void *blob = headerExport(h, &blen);
2ff057
2ff057
	/* XXX FIXME: bah, this code is repeated in way too many places */
2ff057
	DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
2ff057
	rpmDigestUpdate(ctx, rpm_header_magic, sizeof(rpm_header_magic));
2ff057
	rpmDigestUpdate(ctx, blob, blen);
2ff057
	rpmDigestFinal(ctx, (void **)&sha1, NULL, 1);
2ff057
2ff057
	if (sha1) {
2ff057
	    headerPutString(h, RPMTAG_SHA1HEADER, sha1);
2ff057
	    *hdrp = headerLink(h);
2ff057
	    rc = 0;
2ff057
	}
2ff057
	free(sha1);
2ff057
	free(blob);
2ff057
    }
2ff057
2ff057
exit:
2ff057
    headerFree(h);
2ff057
    pgpFreeDig(dig);
2ff057
    free(n);
2ff057
    free(u);
2ff057
    free(v);
2ff057
    free(r);
2ff057
    free(evr);
2ff057
    free(enc);
2ff057
    free(d);
2ff057
2ff057
    return rc;
2ff057
}
2ff057
2ff057
rpmRC rpmtsImportHeader(rpmtxn txn, Header h, rpmFlags flags)
2ff057
{
2ff057
    rpmRC rc = RPMRC_FAIL;
2ff057
2ff057
    if (txn && h && rpmtsOpenDB(txn->ts, (O_RDWR|O_CREAT)) == 0) {
2ff057
	if (rpmdbAdd(rpmtsGetRdb(txn->ts), h) == 0) {
2ff057
	    rc = RPMRC_OK;
2ff057
	}
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
2ff057
{
2ff057
    Header h = NULL;
2ff057
    rpmRC rc = RPMRC_FAIL;		/* assume failure */
2ff057
    rpmPubkey pubkey = NULL;
2ff057
    rpmPubkey *subkeys = NULL;
2ff057
    int subkeysCount = 0;
2ff057
    rpmVSFlags oflags = rpmtsVSFlags(ts);
2ff057
    rpmKeyring keyring;
2ff057
    rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE);
2ff057
    int krc, i;
2ff057
2ff057
    if (txn == NULL)
2ff057
	return rc;
2ff057
2ff057
    /* XXX keyring wont load if sigcheck disabled, force it temporarily */
2ff057
    rpmtsSetVSFlags(ts, (oflags & ~RPMVSF_MASK_NOSIGNATURES));
2ff057
    keyring = rpmtsGetKeyring(ts, 1);
2ff057
    rpmtsSetVSFlags(ts, oflags);
2ff057
2ff057
    if ((pubkey = rpmPubkeyNew(pkt, pktlen)) == NULL)
2ff057
	goto exit;
2ff057
2ff057
    if ((subkeys = rpmGetSubkeys(pubkey, &subkeysCount)) == NULL)
2ff057
	goto exit;
2ff057
2ff057
    krc = rpmKeyringAddKey(keyring, pubkey);
2ff057
    if (krc < 0)
2ff057
	goto exit;
2ff057
2ff057
    /* If we dont already have the key, make a persistent record of it */
2ff057
    if (krc == 0) {
2ff057
	rpm_tid_t tid = rpmtsGetTid(ts);
2ff057
2ff057
	if (makePubkeyHeader(ts, pubkey, subkeys, subkeysCount, &h) != 0)
2ff057
	    goto exit;
2ff057
2ff057
	headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1);
2ff057
	headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
2ff057
2ff057
	/* Add header to database. */
2ff057
	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
2ff057
	    rc = rpmtsImportHeader(txn, h, 0);
2ff057
	}
2ff057
    }
2ff057
    rc = RPMRC_OK;
2ff057
2ff057
exit:
2ff057
    /* Clean up. */
2ff057
    headerFree(h);
2ff057
    rpmPubkeyFree(pubkey);
2ff057
    for (i = 0; i < subkeysCount; i++)
2ff057
	rpmPubkeyFree(subkeys[i]);
2ff057
    free(subkeys);
2ff057
2ff057
    rpmKeyringFree(keyring);
2ff057
    rpmtxnEnd(txn);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsSetSolveCallback(rpmts ts,
2ff057
		int (*solve) (rpmts ts, rpmds key, const void * data),
2ff057
		const void * solveData)
2ff057
{
2ff057
    int rc = 0;
2ff057
2ff057
    if (ts) {
2ff057
	ts->solve = solve;
2ff057
	ts->solveData = solveData;
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmtsSolve(rpmts ts, rpmds key)
2ff057
{
2ff057
    int rc = 1; /* assume not found */
2ff057
    if (ts && ts->solve) {
2ff057
	rc = (*ts->solve)(ts, key, ts->solveData);
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
rpmps rpmtsProblems(rpmts ts)
2ff057
{
2ff057
    rpmps ps = rpmpsCreate();
2ff057
    rpmtsi pi = rpmtsiInit(ts);
2ff057
    rpmte p;
2ff057
2ff057
    while ((p = rpmtsiNext(pi, 0)) != NULL) {
2ff057
	rpmps teprobs = rpmteProblems(p);
2ff057
	rpmpsMerge(ps, teprobs);
2ff057
	rpmpsFree(teprobs);
2ff057
    }
2ff057
    rpmtsiFree(pi);
2ff057
2ff057
    /* Return NULL on no problems instead of an empty set */
2ff057
    if (rpmpsNumProblems(ps) == 0) {
2ff057
	ps = rpmpsFree(ps);
2ff057
    }
2ff057
2ff057
    return ps;
2ff057
}
2ff057
2ff057
void rpmtsCleanProblems(rpmts ts)
2ff057
{
2ff057
    rpmte p;
2ff057
    rpmtsi pi = rpmtsiInit(ts);
2ff057
    while ((p = rpmtsiNext(pi, 0)) != NULL)
2ff057
	rpmteCleanProblems(p);
2ff057
    rpmtsiFree(pi);
2ff057
}
2ff057
2ff057
void rpmtsClean(rpmts ts)
2ff057
{
2ff057
    rpmtsi pi; rpmte p;
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
2ff057
    if (ts == NULL)
2ff057
	return;
2ff057
2ff057
    /* Clean up after dependency checks. */
2ff057
    pi = rpmtsiInit(ts);
2ff057
    while ((p = rpmtsiNext(pi, 0)) != NULL)
2ff057
	rpmteCleanDS(p);
2ff057
    rpmtsiFree(pi);
2ff057
2ff057
    tsmem->addedPackages = rpmalFree(tsmem->addedPackages);
2ff057
    tsmem->rpmlib = rpmdsFree(tsmem->rpmlib);
2ff057
2ff057
    rpmtsCleanProblems(ts);
2ff057
}
2ff057
2ff057
/* hash comparison function */
2ff057
static int uintCmp(unsigned int a, unsigned int b)
2ff057
{
2ff057
    return (a != b);
2ff057
}
2ff057
2ff057
/* "hash"function*/ 
2ff057
static unsigned int uintId(unsigned int a)
2ff057
{
2ff057
    return a;
2ff057
}
2ff057
2ff057
void rpmtsEmpty(rpmts ts)
2ff057
{
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    if (ts == NULL)
2ff057
	return;
2ff057
2ff057
    rpmtsClean(ts);
2ff057
2ff057
    for (int oc = 0; oc < tsmem->orderCount; oc++) {
2ff057
	tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
2ff057
    }
2ff057
2ff057
    tsmem->orderCount = 0;
2ff057
    /* The pool cannot be emptied, there might be references to its contents */
2ff057
    tsmem->pool = rpmstrPoolFree(tsmem->pool);
2ff057
    packageHashEmpty(tsmem->removedPackages);
2ff057
    return;
2ff057
}
2ff057
2ff057
static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
2ff057
{
2ff057
    static const unsigned int scale = (1000 * 1000);
2ff057
    if (op != NULL && op->count > 0)
2ff057
	fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
2ff057
		name, op->count,
2ff057
		(unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
2ff057
		op->usecs/scale, op->usecs%scale);
2ff057
}
2ff057
2ff057
static void rpmtsPrintStats(rpmts ts)
2ff057
{
2ff057
    (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
2ff057
2ff057
    rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
2ff057
    rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
2ff057
    rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
2ff057
    rpmtsPrintStat("verify:      ", rpmtsOp(ts, RPMTS_OP_VERIFY));
2ff057
    rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
2ff057
    rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
2ff057
    rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
2ff057
    rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
2ff057
    rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
2ff057
    rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
2ff057
    rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
2ff057
    rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
2ff057
    rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
2ff057
    rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
2ff057
    rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
2ff057
    rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
2ff057
    rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
2ff057
}
2ff057
2ff057
rpmts rpmtsFree(rpmts ts)
2ff057
{
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    if (ts == NULL)
2ff057
	return NULL;
2ff057
2ff057
    if (ts->nrefs > 1)
2ff057
	return rpmtsUnlink(ts);
2ff057
2ff057
    rpmtsEmpty(ts);
2ff057
2ff057
    (void) rpmtsCloseDB(ts);
2ff057
2ff057
    tsmem->removedPackages = packageHashFree(tsmem->removedPackages);
2ff057
    tsmem->installedPackages = packageHashFree(tsmem->installedPackages);
2ff057
    tsmem->order = _free(tsmem->order);
2ff057
    ts->members = _free(ts->members);
2ff057
2ff057
    ts->dsi = _free(ts->dsi);
2ff057
2ff057
    if (ts->scriptFd != NULL) {
2ff057
	ts->scriptFd = fdFree(ts->scriptFd);
2ff057
	ts->scriptFd = NULL;
2ff057
    }
2ff057
    ts->rootDir = _free(ts->rootDir);
2ff057
    ts->lockPath = _free(ts->lockPath);
2ff057
    ts->lock = rpmlockFree(ts->lock);
2ff057
2ff057
    ts->keyring = rpmKeyringFree(ts->keyring);
2ff057
    ts->netsharedPaths = argvFree(ts->netsharedPaths);
2ff057
    ts->installLangs = argvFree(ts->installLangs);
2ff057
2ff057
    ts->plugins = rpmpluginsFree(ts->plugins);
2ff057
2ff057
    rpmtriggersFree(ts->trigs2run);
2ff057
2ff057
    if (_rpmts_stats)
2ff057
	rpmtsPrintStats(ts);
2ff057
2ff057
    (void) rpmtsUnlink(ts);
2ff057
2ff057
    ts = _free(ts);
2ff057
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
rpmVSFlags rpmtsVSFlags(rpmts ts)
2ff057
{
2ff057
    rpmVSFlags vsflags = 0;
2ff057
    if (ts != NULL)
2ff057
	vsflags = ts->vsflags;
2ff057
    return vsflags;
2ff057
}
2ff057
2ff057
rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
2ff057
{
2ff057
    rpmVSFlags ovsflags = 0;
2ff057
    if (ts != NULL) {
2ff057
	ovsflags = ts->vsflags;
2ff057
	ts->vsflags = vsflags;
2ff057
    }
2ff057
    return ovsflags;
2ff057
}
2ff057
2ff057
rpmVSFlags rpmtsVfyFlags(rpmts ts)
2ff057
{
2ff057
    rpmVSFlags vfyflags = 0;
2ff057
    if (ts != NULL)
2ff057
	vfyflags = ts->vfyflags;
2ff057
    return vfyflags;
2ff057
}
2ff057
2ff057
rpmVSFlags rpmtsSetVfyFlags(rpmts ts, rpmVSFlags vfyflags)
2ff057
{
2ff057
    rpmVSFlags ovfyflags = 0;
2ff057
    if (ts != NULL) {
2ff057
	ovfyflags = ts->vfyflags;
2ff057
	ts->vfyflags = vfyflags;
2ff057
    }
2ff057
    return ovfyflags;
2ff057
}
2ff057
2ff057
int rpmtsVfyLevel(rpmts ts)
2ff057
{
2ff057
    int vfylevel = 0;
2ff057
    if (ts != NULL)
2ff057
	vfylevel = ts->vfylevel;
2ff057
    return vfylevel;
2ff057
}
2ff057
2ff057
int rpmtsSetVfyLevel(rpmts ts, int vfylevel)
2ff057
{
2ff057
    int ovfylevel = 0;
2ff057
    if (ts != NULL) {
2ff057
	ovfylevel = ts->vfylevel;
2ff057
	ts->vfylevel = vfylevel;
2ff057
    }
2ff057
    return ovfylevel;
2ff057
}
2ff057
2ff057
const char * rpmtsRootDir(rpmts ts)
2ff057
{
2ff057
    return ts ? ts->rootDir : NULL;
2ff057
}
2ff057
2ff057
int rpmtsSetRootDir(rpmts ts, const char * rootDir)
2ff057
{
2ff057
    if (ts == NULL || (rootDir && rootDir[0] != '/')) {
2ff057
	return -1;
2ff057
    }
2ff057
2ff057
    ts->rootDir = _free(ts->rootDir);
2ff057
    /* Ensure clean path with a trailing slash */
2ff057
    ts->rootDir = rootDir ? rpmGetPath(rootDir, NULL) : xstrdup("/");
2ff057
    if (!rstreq(ts->rootDir, "/")) {
2ff057
	rstrcat(&ts->rootDir, "/");
2ff057
    }
2ff057
    return 0;
2ff057
}
2ff057
2ff057
FD_t rpmtsScriptFd(rpmts ts)
2ff057
{
2ff057
    FD_t scriptFd = NULL;
2ff057
    if (ts != NULL) {
2ff057
	scriptFd = ts->scriptFd;
2ff057
    }
2ff057
    return scriptFd;
2ff057
}
2ff057
2ff057
void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
2ff057
{
2ff057
2ff057
    if (ts != NULL) {
2ff057
	if (ts->scriptFd != NULL) {
2ff057
	    ts->scriptFd = fdFree(ts->scriptFd);
2ff057
	    ts->scriptFd = NULL;
2ff057
	}
2ff057
	if (scriptFd != NULL)
2ff057
	    ts->scriptFd = fdLink(scriptFd);
2ff057
    }
2ff057
}
2ff057
2ff057
rpm_tid_t rpmtsGetTid(rpmts ts)
2ff057
{
2ff057
    rpm_tid_t tid = (rpm_tid_t)-1;  /* XXX -1 is time(2) error return. */
2ff057
    if (ts != NULL) {
2ff057
	tid = ts->tid;
2ff057
    }
2ff057
    return tid;
2ff057
}
2ff057
2ff057
rpm_tid_t rpmtsSetTid(rpmts ts, rpm_tid_t tid)
2ff057
{
2ff057
    rpm_tid_t otid = (rpm_tid_t)-1; /* XXX -1 is time(2) error return. */
2ff057
    if (ts != NULL) {
2ff057
	otid = ts->tid;
2ff057
	ts->tid = tid;
2ff057
    }
2ff057
    return otid;
2ff057
}
2ff057
2ff057
rpmdb rpmtsGetRdb(rpmts ts)
2ff057
{
2ff057
    rpmdb rdb = NULL;
2ff057
    if (ts != NULL) {
2ff057
	rdb = ts->rdb;
2ff057
    }
2ff057
    return rdb;
2ff057
}
2ff057
2ff057
void * rpmtsNotify(rpmts ts, rpmte te,
2ff057
		rpmCallbackType what, rpm_loff_t amount, rpm_loff_t total)
2ff057
{
2ff057
    void * ptr = NULL;
2ff057
    if (ts && ts->notify) {
2ff057
	Header h = NULL;
2ff057
	fnpyKey cbkey = NULL;
2ff057
	if (te) {
2ff057
	    h = rpmteHeader(te);
2ff057
	    cbkey = rpmteKey(te);
2ff057
	}
2ff057
	ptr = ts->notify(h, what, amount, total, cbkey, ts->notifyData);
2ff057
2ff057
	if (h) {
2ff057
	    headerFree(h); /* undo rpmteHeader() ref */
2ff057
	}
2ff057
    }
2ff057
    return ptr;
2ff057
}
2ff057
2ff057
int rpmtsNElements(rpmts ts)
2ff057
{
2ff057
    int nelements = 0;
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    if (tsmem != NULL && tsmem->order != NULL) {
2ff057
	nelements = tsmem->orderCount;
2ff057
    }
2ff057
    return nelements;
2ff057
}
2ff057
2ff057
rpmte rpmtsElement(rpmts ts, int ix)
2ff057
{
2ff057
    rpmte te = NULL;
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    if (tsmem != NULL && tsmem->order != NULL) {
2ff057
	if (ix >= 0 && ix < tsmem->orderCount)
2ff057
	    te = tsmem->order[ix];
2ff057
    }
2ff057
    return te;
2ff057
}
2ff057
2ff057
rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
2ff057
{
2ff057
    return (ts != NULL ? ts->ignoreSet : 0);
2ff057
}
2ff057
2ff057
rpmtransFlags rpmtsFlags(rpmts ts)
2ff057
{
2ff057
    return (ts != NULL ? ts->transFlags : 0);
2ff057
}
2ff057
2ff057
rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
2ff057
{
2ff057
    rpmtransFlags otransFlags = 0;
2ff057
    if (ts != NULL) {
2ff057
	otransFlags = ts->transFlags;
2ff057
	ts->transFlags = transFlags;
2ff057
    }
2ff057
    return otransFlags;
2ff057
}
2ff057
2ff057
rpm_color_t rpmtsColor(rpmts ts)
2ff057
{
2ff057
    return (ts != NULL ? ts->color : 0);
2ff057
}
2ff057
2ff057
rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color)
2ff057
{
2ff057
    rpm_color_t ocolor = 0;
2ff057
    if (ts != NULL) {
2ff057
	ocolor = ts->color;
2ff057
	ts->color = color;
2ff057
    }
2ff057
    return ocolor;
2ff057
}
2ff057
2ff057
rpm_color_t rpmtsPrefColor(rpmts ts)
2ff057
{
2ff057
    return (ts != NULL ? ts->prefcolor : 0);
2ff057
}
2ff057
2ff057
rpm_color_t rpmtsSetPrefColor(rpmts ts, rpm_color_t color)
2ff057
{
2ff057
    rpm_color_t ocolor = 0;
2ff057
    if (ts != NULL) {
2ff057
	ocolor = ts->prefcolor;
2ff057
	ts->prefcolor = color;
2ff057
    }
2ff057
    return ocolor;
2ff057
}
2ff057
2ff057
rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
2ff057
{
2ff057
    rpmop op = NULL;
2ff057
2ff057
    if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
2ff057
	op = ts->ops + opx;
2ff057
    return op;
2ff057
}
2ff057
2ff057
rpmPlugins rpmtsPlugins(rpmts ts)
2ff057
{
2ff057
    rpmPlugins plugins = NULL;
2ff057
2ff057
    if (ts != NULL) {
2ff057
	if (ts->plugins == NULL)
2ff057
	    ts->plugins = rpmpluginsNew(ts);
2ff057
	plugins = ts->plugins;
2ff057
    }
2ff057
    return plugins;
2ff057
}
2ff057
2ff057
int rpmtsSetNotifyCallback(rpmts ts,
2ff057
		rpmCallbackFunction notify, rpmCallbackData notifyData)
2ff057
{
2ff057
    if (ts != NULL) {
2ff057
	ts->notify = notify;
2ff057
	ts->notifyData = notifyData;
2ff057
    }
2ff057
    return 0;
2ff057
}
2ff057
2ff057
tsMembers rpmtsMembers(rpmts ts)
2ff057
{
2ff057
    return (ts != NULL) ? ts->members : NULL;
2ff057
}
2ff057
2ff057
rpmstrPool rpmtsPool(rpmts ts)
2ff057
{
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    rpmstrPool tspool = NULL;
2ff057
2ff057
    if (tsmem) {
2ff057
	if (tsmem->pool == NULL)
2ff057
	    tsmem->pool = rpmstrPoolCreate();
2ff057
	tspool = tsmem->pool;
2ff057
    }
2ff057
    return tspool;
2ff057
}
2ff057
2ff057
static int vfylevel_init(void)
2ff057
{
2ff057
    int vfylevel = -1;
2ff057
    char *val = rpmExpand("%{?_pkgverify_level}", NULL);
2ff057
2ff057
    if (rstreq(val, "all"))
2ff057
	vfylevel = RPMSIG_SIGNATURE_TYPE|RPMSIG_DIGEST_TYPE;
2ff057
    else if (rstreq(val, "signature"))
2ff057
	vfylevel = RPMSIG_SIGNATURE_TYPE;
2ff057
    else if (rstreq(val, "digest"))
2ff057
	vfylevel = RPMSIG_DIGEST_TYPE;
2ff057
    else if (rstreq(val, "none"))
2ff057
	vfylevel = 0;
2ff057
    else if (!rstreq(val, ""))
2ff057
	rpmlog(RPMLOG_WARNING, _("invalid package verify level %s\n"), val);
2ff057
2ff057
    free(val);
2ff057
    return vfylevel;
2ff057
}
2ff057
2ff057
rpmts rpmtsCreate(void)
2ff057
{
2ff057
    rpmts ts;
2ff057
    tsMembers tsmem;
2ff057
2ff057
    ts = xcalloc(1, sizeof(*ts));
2ff057
    memset(&ts->ops, 0, sizeof(ts->ops));
2ff057
    (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
2ff057
    ts->dsi = NULL;
2ff057
2ff057
    ts->solve = NULL;
2ff057
    ts->solveData = NULL;
2ff057
2ff057
    ts->rdb = NULL;
2ff057
    ts->dbmode = O_RDONLY;
2ff057
2ff057
    ts->scriptFd = NULL;
2ff057
    ts->tid = (rpm_tid_t) time(NULL);
2ff057
2ff057
    ts->color = rpmExpandNumeric("%{?_transaction_color}");
2ff057
    ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
2ff057
2ff057
    ts->netsharedPaths = NULL;
2ff057
    ts->installLangs = NULL;
2ff057
    {	char *tmp = rpmExpand("%{_netsharedpath}", NULL);
2ff057
	if (tmp && *tmp != '%') {
2ff057
	    argvSplit(&ts->netsharedPaths, tmp, ":");
2ff057
	}
2ff057
	free(tmp);
2ff057
2ff057
	tmp = rpmExpand("%{_install_langs}", NULL);
2ff057
	if (tmp && *tmp != '%') {
2ff057
	    ARGV_t langs = NULL;
2ff057
	    argvSplit(&langs, tmp, ":");	
2ff057
	    /* If we'll be installing all languages anyway, don't bother */
2ff057
	    for (ARGV_t l = langs; *l; l++) {
2ff057
		if (rstreq(*l, "all")) {
2ff057
		    langs = argvFree(langs);
2ff057
		    break;
2ff057
		}
2ff057
	    }
2ff057
	    ts->installLangs = langs;
2ff057
	}
2ff057
	free(tmp);
2ff057
    }
2ff057
2ff057
    tsmem = xcalloc(1, sizeof(*ts->members));
2ff057
    tsmem->pool = NULL;
2ff057
    tsmem->delta = 5;
2ff057
    tsmem->addedPackages = NULL;
2ff057
    tsmem->removedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL);
2ff057
    tsmem->installedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL);
2ff057
    tsmem->orderAlloced = 0;
2ff057
    tsmem->orderCount = 0;
2ff057
    tsmem->order = NULL;
2ff057
    ts->members = tsmem;
2ff057
2ff057
    ts->rootDir = NULL;
2ff057
    ts->keyring = NULL;
2ff057
    ts->vfyflags = rpmExpandNumeric("%{?_pkgverify_flags}");
2ff057
    ts->vfylevel = vfylevel_init();
2ff057
2ff057
    ts->nrefs = 0;
2ff057
2ff057
    ts->plugins = NULL;
2ff057
2ff057
    ts->trigs2run = rpmtriggersCreate(10);
2ff057
2ff057
    ts->min_writes = rpmExpandNumeric("%{_minimize_writes}");
2ff057
2ff057
    return rpmtsLink(ts);
2ff057
}
2ff057
2ff057
rpmtsi rpmtsiFree(rpmtsi tsi)
2ff057
{
2ff057
    /* XXX watchout: a funky recursion segfaults here iff nrefs is wrong. */
2ff057
    if (tsi) {
2ff057
	tsi->ts = rpmtsFree(tsi->ts);
2ff057
	_free(tsi);
2ff057
    }
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
rpmtsi rpmtsiInit(rpmts ts)
2ff057
{
2ff057
    rpmtsi tsi = NULL;
2ff057
2ff057
    tsi = xcalloc(1, sizeof(*tsi));
2ff057
    tsi->ts = rpmtsLink(ts);
2ff057
    tsi->oc = 0;
2ff057
    return tsi;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Return next transaction element.
2ff057
 * @param tsi		transaction element iterator
2ff057
 * @return		transaction element, NULL on termination
2ff057
 */
2ff057
static
2ff057
rpmte rpmtsiNextElement(rpmtsi tsi)
2ff057
{
2ff057
    rpmte te = NULL;
2ff057
    int oc = -1;
2ff057
2ff057
    if (tsi == NULL || tsi->ts == NULL || rpmtsNElements(tsi->ts) <= 0)
2ff057
	return te;
2ff057
2ff057
    if (tsi->oc < rpmtsNElements(tsi->ts))	oc = tsi->oc++;
2ff057
    if (oc != -1)
2ff057
	te = rpmtsElement(tsi->ts, oc);
2ff057
    return te;
2ff057
}
2ff057
2ff057
rpmte rpmtsiNext(rpmtsi tsi, rpmElementTypes types)
2ff057
{
2ff057
    rpmte te;
2ff057
2ff057
    while ((te = rpmtsiNextElement(tsi)) != NULL) {
2ff057
	if (types == 0 || (rpmteType(te) & types) != 0)
2ff057
	    break;
2ff057
    }
2ff057
    return te;
2ff057
}
2ff057
2ff057
#define RPMLOCK_PATH LOCALSTATEDIR "/rpm/.rpm.lock"
2ff057
rpmtxn rpmtxnBegin(rpmts ts, rpmtxnFlags flags)
2ff057
{
2ff057
    static const char * const rpmlock_path_default = "%{?_rpmlock_path}";
2ff057
    rpmtxn txn = NULL;
2ff057
2ff057
    if (ts == NULL)
2ff057
	return NULL;
2ff057
2ff057
    if (ts->lockPath == NULL) {
2ff057
	const char *rootDir = rpmtsRootDir(ts);
2ff057
	char *t;
2ff057
2ff057
	if (!rootDir || rpmChrootDone())
2ff057
	    rootDir = "/";
2ff057
2ff057
	t = rpmGenPath(rootDir, rpmlock_path_default, NULL);
2ff057
	if (t == NULL || *t == '\0' || *t == '%') {
2ff057
	    free(t);
2ff057
	    t = xstrdup(RPMLOCK_PATH);
2ff057
	}
2ff057
	ts->lockPath = xstrdup(t);
2ff057
	(void) rpmioMkpath(dirname(t), 0755, getuid(), getgid());
2ff057
	free(t);
2ff057
    }
2ff057
2ff057
    if (ts->lock == NULL)
2ff057
	ts->lock = rpmlockNew(ts->lockPath, _("transaction"));
2ff057
2ff057
    if (rpmlockAcquire(ts->lock)) {
2ff057
	txn = xcalloc(1, sizeof(*txn));
2ff057
	txn->lock = ts->lock;
2ff057
	txn->flags = flags;
2ff057
	txn->ts = rpmtsLink(ts);
2ff057
	if (txn->flags & RPMTXN_WRITE)
2ff057
	    rpmsqBlock(SIG_BLOCK);
2ff057
    }
2ff057
    
2ff057
    return txn;
2ff057
}
2ff057
2ff057
rpmtxn rpmtxnEnd(rpmtxn txn)
2ff057
{
2ff057
    if (txn) {
2ff057
	rpmlockRelease(txn->lock);
2ff057
	if (txn->flags & RPMTXN_WRITE)
2ff057
	    rpmsqBlock(SIG_UNBLOCK);
2ff057
	rpmtsFree(txn->ts);
2ff057
	free(txn);
2ff057
    }
2ff057
    return NULL;
2ff057
}