diff options
Diffstat (limited to 'lib/psm.c')
-rw-r--r-- | lib/psm.c | 1095 |
1 files changed, 1095 insertions, 0 deletions
diff --git a/lib/psm.c b/lib/psm.c new file mode 100644 index 0000000..1c761bf --- /dev/null +++ b/lib/psm.c @@ -0,0 +1,1095 @@ +/** \ingroup rpmts payload + * \file lib/psm.c + * Package state machine to handle a package from a transaction set. + */ + +#include "system.h" + +#include <errno.h> + +#include <rpm/rpmlib.h> /* rpmvercmp and others */ +#include <rpm/rpmmacro.h> +#include <rpm/rpmds.h> +#include <rpm/rpmts.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmdb.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmstring.h> +#include <rpm/argv.h> + +#include "lib/cpio.h" +#include "lib/fsm.h" /* XXX CPIO_FOO/FSM_FOO constants */ +#include "lib/rpmchroot.h" +#include "lib/rpmfi_internal.h" /* XXX replaced/states... */ +#include "lib/rpmte_internal.h" /* XXX internal apis */ +#include "lib/rpmdb_internal.h" /* rpmdbAdd/Remove */ +#include "lib/rpmscript.h" + +#include "debug.h" + +typedef enum pkgStage_e { + PSM_UNKNOWN = 0, + PSM_INIT = 1, + PSM_PRE = 2, + PSM_PROCESS = 3, + PSM_POST = 4, + PSM_UNDO = 5, + PSM_FINI = 6, + + PSM_CREATE = 17, + PSM_NOTIFY = 22, + PSM_DESTROY = 23, + + PSM_SCRIPT = 53, + PSM_TRIGGERS = 54, + PSM_IMMED_TRIGGERS = 55, + + PSM_RPMDB_ADD = 98, + PSM_RPMDB_REMOVE = 99 + +} pkgStage; + +typedef struct rpmpsm_s { + rpmts ts; /*!< transaction set */ + rpmte te; /*!< current transaction element */ + rpmfi fi; /*!< transaction element file info */ + const char * goalName; + char * failedFile; + rpmTagVal scriptTag; /*!< Scriptlet data tag. */ + int npkgs_installed; /*!< No. of installed instances. */ + int scriptArg; /*!< Scriptlet package arg. */ + rpmsenseFlags sense; /*!< One of RPMSENSE_TRIGGER{PREIN,IN,UN,POSTUN}. */ + int countCorrection; /*!< 0 if installing, -1 if removing. */ + rpmCallbackType what; /*!< Callback type. */ + rpm_loff_t amount; /*!< Callback amount. */ + rpm_loff_t total; /*!< Callback total. */ + pkgGoal goal; + pkgStage stage; /*!< Current psm stage. */ + pkgStage nstage; /*!< Next psm stage. */ + + int nrefs; /*!< Reference count. */ +} * rpmpsm; + +static rpmpsm rpmpsmNew(rpmts ts, rpmte te); +static rpmpsm rpmpsmFree(rpmpsm psm); +static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage); + +/** + * Macros to be defined from per-header tag values. + * @todo Should other macros be added from header when installing a package? + */ +static struct tagMacro { + const char *macroname; /*!< Macro name to define. */ + rpmTag tag; /*!< Header tag to use for value. */ +} const tagMacros[] = { + { "name", RPMTAG_NAME }, + { "version", RPMTAG_VERSION }, + { "release", RPMTAG_RELEASE }, + { "epoch", RPMTAG_EPOCH }, + { NULL, 0 } +}; + +/** + * Define per-header macros. + * @param h header + * @return 0 always + */ +static void rpmInstallLoadMacros(Header h) +{ + const struct tagMacro * tagm; + + for (tagm = tagMacros; tagm->macroname != NULL; tagm++) { + struct rpmtd_s td; + char *body; + if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT)) + continue; + + switch (rpmtdType(&td)) { + default: + body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL); + addMacro(NULL, tagm->macroname, NULL, body, -1); + free(body); + break; + case RPM_NULL_TYPE: + break; + } + rpmtdFreeData(&td); + } +} + +/** + * Mark files in database shared with this package as "replaced". + * @param psm package state machine data + * @return 0 always + */ +static rpmRC markReplacedFiles(const rpmpsm psm) +{ + const rpmts ts = psm->ts; + rpmfs fs = rpmteGetFileStates(psm->te); + sharedFileInfo replaced = rpmfsGetReplaced(fs); + sharedFileInfo sfi; + rpmdbMatchIterator mi; + Header h; + int * offsets; + unsigned int prev; + int num, xx; + + if (!replaced) + return RPMRC_OK; + + num = prev = 0; + for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) { + if (prev && prev == sfi->otherPkg) + continue; + prev = sfi->otherPkg; + num++; + } + if (num == 0) + return RPMRC_OK; + + offsets = xmalloc(num * sizeof(*offsets)); + offsets[0] = 0; + num = prev = 0; + for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) { + if (prev && prev == sfi->otherPkg) + continue; + prev = sfi->otherPkg; + offsets[num++] = sfi->otherPkg; + } + + mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); + xx = rpmdbAppendIterator(mi, offsets, num); + xx = rpmdbSetIteratorRewrite(mi, 1); + + sfi = replaced; + while ((h = rpmdbNextIterator(mi)) != NULL) { + int modified; + struct rpmtd_s secStates; + modified = 0; + + if (!headerGet(h, RPMTAG_FILESTATES, &secStates, HEADERGET_MINMEM)) + continue; + + prev = rpmdbGetIteratorOffset(mi); + num = 0; + while (sfi && sfi->otherPkg == prev) { + int ix = rpmtdSetIndex(&secStates, sfi->otherFileNum); + assert(ix != -1); + + char *state = rpmtdGetChar(&secStates); + if (state && *state != RPMFILE_STATE_REPLACED) { + *state = RPMFILE_STATE_REPLACED; + if (modified == 0) { + /* Modified header will be rewritten. */ + modified = 1; + xx = rpmdbSetIteratorModified(mi, modified); + } + num++; + } + sfi=rpmfsNextReplaced(fs, sfi); + } + rpmtdFreeData(&secStates); + } + mi = rpmdbFreeIterator(mi); + free(offsets); + + return RPMRC_OK; +} + +static int rpmlibDeps(Header h) +{ + rpmds req = rpmdsInit(rpmdsNew(h, RPMTAG_REQUIRENAME, 0)); + rpmds rpmlib = NULL; + rpmdsRpmlib(&rpmlib, NULL); + int rc = 1; + char *nvr = NULL; + while (rpmdsNext(req) >= 0) { + if (!(rpmdsFlags(req) & RPMSENSE_RPMLIB)) + continue; + if (rpmdsSearch(rpmlib, req) < 0) { + if (!nvr) { + nvr = headerGetAsString(h, RPMTAG_NEVRA); + rpmlog(RPMLOG_ERR, _("Missing rpmlib features for %s:\n"), nvr); + } + rpmlog(RPMLOG_ERR, "\t%s\n", rpmdsDNEVR(req)+2); + rc = 0; + } + } + rpmdsFree(req); + rpmdsFree(rpmlib); + free(nvr); + return rc; +} + +rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd, + char ** specFilePtr, char ** cookie) +{ + rpmfi fi = NULL; + char * specFile = NULL; + const char *rootdir = rpmtsRootDir(ts); + Header h = NULL; + rpmpsm psm = NULL; + rpmte te = NULL; + rpmRC rpmrc; + int specix = -1; + struct rpmtd_s filenames; + + rpmtdReset(&filenames); + rpmrc = rpmReadPackageFile(ts, fd, RPMDBG_M("InstallSourcePackage"), &h); + switch (rpmrc) { + case RPMRC_NOTTRUSTED: + case RPMRC_NOKEY: + case RPMRC_OK: + break; + default: + goto exit; + break; + } + if (h == NULL) + goto exit; + + rpmrc = RPMRC_FAIL; /* assume failure */ + + if (!headerIsSource(h)) { + rpmlog(RPMLOG_ERR, _("source package expected, binary found\n")); + goto exit; + } + + /* src.rpm install can require specific rpmlib features, check them */ + if (!rpmlibDeps(h)) + goto exit; + + if (headerGet(h, RPMTAG_BASENAMES, &filenames, HEADERGET_ALLOC)) { + struct rpmtd_s td; + const char *str; + const char *_cookie = headerGetString(h, RPMTAG_COOKIE); + if (cookie && _cookie) *cookie = xstrdup(_cookie); + + /* Try to find spec by file flags */ + if (_cookie && headerGet(h, RPMTAG_FILEFLAGS, &td, HEADERGET_MINMEM)) { + rpmfileAttrs *flags; + while (specix < 0 && (flags = rpmtdNextUint32(&td))) { + if (*flags & RPMFILE_SPECFILE) + specix = rpmtdGetIndex(&td); + } + } + /* Still no spec? Look by filename. */ + while (specix < 0 && (str = rpmtdNextString(&filenames))) { + if (rpmFileHasSuffix(str, ".spec")) + specix = rpmtdGetIndex(&filenames); + } + } + + if (rootdir && rstreq(rootdir, "/")) + rootdir = NULL; + + /* Macros need to be added before trying to create directories */ + rpmInstallLoadMacros(h); + + if (specix >= 0) { + const char *bn; + + headerDel(h, RPMTAG_BASENAMES); + headerDel(h, RPMTAG_DIRNAMES); + headerDel(h, RPMTAG_DIRINDEXES); + + rpmtdInit(&filenames); + for (int i = 0; (bn = rpmtdNextString(&filenames)); i++) { + int spec = (i == specix); + char *fn = rpmGenPath(rpmtsRootDir(ts), + spec ? "%{_specdir}" : "%{_sourcedir}", bn); + headerPutString(h, RPMTAG_OLDFILENAMES, fn); + if (spec) specFile = xstrdup(fn); + free(fn); + } + headerConvert(h, HEADERCONV_COMPRESSFILELIST); + } else { + rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n")); + goto exit; + }; + + if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) { + goto exit; + } + + te = rpmtsElement(ts, 0); + if (te == NULL) { /* XXX can't happen */ + goto exit; + } + rpmteSetFd(te, fd); + + rpmteSetHeader(te, h); + fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER); + h = headerFree(h); + + if (fi == NULL) { /* XXX can't happen */ + goto exit; + } + fi->apath = filenames.data; /* Ick */ + rpmteSetFI(te, fi); + fi = rpmfiFree(fi); + + if (rpmMkdirs(rpmtsRootDir(ts), "%{_topdir}:%{_sourcedir}:%{_specdir}")) { + goto exit; + } + + { + /* set all files to be installed */ + rpmfs fs = rpmteGetFileStates(te); + int i; + unsigned int fc = rpmfiFC(fi); + for (i=0; i<fc; i++) rpmfsSetAction(fs, i, FA_CREATE); + } + + psm = rpmpsmNew(ts, te); + psm->goal = PKG_INSTALL; + + /* FIX: psm->fi->dnl should be owned. */ + if (rpmpsmStage(psm, PSM_PROCESS) == RPMRC_OK) + rpmrc = RPMRC_OK; + + (void) rpmpsmStage(psm, PSM_FINI); + psm = rpmpsmFree(psm); + +exit: + if (specFilePtr && specFile && rpmrc == RPMRC_OK) + *specFilePtr = specFile; + else + specFile = _free(specFile); + + if (h != NULL) h = headerFree(h); + if (fi != NULL) fi = rpmfiFree(fi); + + /* XXX nuke the added package(s). */ + rpmtsClean(ts); + + return rpmrc; +} + +static rpmTagVal triggertag(rpmsenseFlags sense) +{ + rpmTagVal tag = RPMTAG_NOT_FOUND; + switch (sense) { + case RPMSENSE_TRIGGERIN: + tag = RPMTAG_TRIGGERIN; + break; + case RPMSENSE_TRIGGERUN: + tag = RPMTAG_TRIGGERUN; + break; + case RPMSENSE_TRIGGERPOSTUN: + tag = RPMTAG_TRIGGERPOSTUN; + break; + case RPMSENSE_TRIGGERPREIN: + tag = RPMTAG_TRIGGERPREIN; + break; + default: + break; + } + return tag; +} + +/** + * Run a scriptlet with args. + * + * Run a script with an interpreter. If the interpreter is not specified, + * /bin/sh will be used. If the interpreter is /bin/sh, then the args from + * the header will be ignored, passing instead arg1 and arg2. + * + * @param psm package state machine data + * @param prefixes install prefixes + * @param script scriptlet from header + * @param arg1 no. instances of package installed after scriptlet exec + * (-1 is no arg) + * @param arg2 ditto, but for the target package + * @return 0 on success + */ +static rpmRC runScript(rpmpsm psm, ARGV_const_t prefixes, + rpmScript script, int arg1, int arg2) +{ + rpmRC rc = RPMRC_OK; + int warn_only = (script->tag != RPMTAG_PREIN && + script->tag != RPMTAG_PREUN && + script->tag != RPMTAG_VERIFYSCRIPT); + int selinux = !(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_NOCONTEXTS); + + rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0); + rc = rpmScriptRun(script, arg1, arg2, rpmtsScriptFd(psm->ts), + prefixes, warn_only, selinux); + rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0); + + /* + * Notify callback for all errors. "total" abused for warning/error, + * rc only reflects whether the condition prevented install/erase + * (which is only happens with %prein and %preun scriptlets) or not. + */ + if (rc != RPMRC_OK) { + if (warn_only) { + rc = RPMRC_OK; + } + rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, script->tag, rc); + } + + return rc; +} + +static rpmRC runInstScript(rpmpsm psm) +{ + rpmRC rc = RPMRC_OK; + struct rpmtd_s pfx; + Header h = rpmteHeader(psm->te); + rpmScript script = rpmScriptFromTag(h, psm->scriptTag); + + if (script) { + headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV); + rc = runScript(psm, pfx.data, script, psm->scriptArg, -1); + rpmtdFreeData(&pfx); + } + + rpmScriptFree(script); + headerFree(h); + + return rc; +} + +/** + * Execute triggers. + * @todo Trigger on any provides, not just package NVR. + * @param psm package state machine data + * @param sourceH header of trigger source + * @param trigH header of triggered package + * @param arg2 + * @param triggersAlreadyRun + * @return + */ +static rpmRC handleOneTrigger(const rpmpsm psm, + Header sourceH, Header trigH, + int arg2, unsigned char * triggersAlreadyRun) +{ + const rpmts ts = psm->ts; + rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0)); + struct rpmtd_s pfx; + const char * sourceName = headerGetString(sourceH, RPMTAG_NAME); + const char * triggerName = headerGetString(trigH, RPMTAG_NAME); + rpmRC rc = RPMRC_OK; + int i; + + if (trigger == NULL) + return rc; + + headerGet(trigH, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV); + (void) rpmdsSetNoPromote(trigger, 1); + + while ((i = rpmdsNext(trigger)) >= 0) { + struct rpmtd_s tscripts, tprogs, tindexes, tflags; + headerGetFlags hgflags = HEADERGET_MINMEM; + + if (!(rpmdsFlags(trigger) & psm->sense)) + continue; + + if (!rstreq(rpmdsN(trigger), sourceName)) + continue; + + /* XXX Trigger on any provided dependency, not just the package NEVR */ + if (!rpmdsAnyMatchesDep(sourceH, trigger, 1)) + continue; + + /* XXX FIXME: this leaks memory if scripts or progs retrieve fails */ + if (!(headerGet(trigH, RPMTAG_TRIGGERINDEX, &tindexes, hgflags) && + headerGet(trigH, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags) && + headerGet(trigH, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags))) { + continue; + } else { + int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName); + char ** triggerScripts = tscripts.data; + char ** triggerProgs = tprogs.data; + uint32_t * triggerIndices = tindexes.data; + uint32_t * triggerFlags = NULL; + uint32_t ix = triggerIndices[i]; + + headerGet(trigH, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags); + triggerFlags = tflags.data; + + if (arg1 < 0) { + /* XXX W2DO? fails as "execution of script failed" */ + rc = RPMRC_FAIL; + } else { + arg1 += psm->countCorrection; + + if (triggersAlreadyRun == NULL || triggersAlreadyRun[ix] == 0) { + /* XXX TODO add rpmScript API to handle this, ugh */ + char *macro = NULL; + char *qformat = NULL; + char *args[2] = { triggerProgs[ix], NULL }; + struct rpmScript_s script = { + .tag = triggertag(psm->sense), + .body = triggerScripts[ix], + .flags = triggerFlags ? triggerFlags[ix] : 0, + .args = args + }; + + if (script.body && (script.flags & RPMSCRIPT_EXPAND)) { + macro = rpmExpand(script.body, NULL); + script.body = macro; + } + if (script.body && (script.flags & RPMSCRIPT_QFORMAT)) { + qformat = headerFormat(trigH, script.body, NULL); + script.body = qformat; + } + + rc = runScript(psm, pfx.data, &script, arg1, arg2); + if (triggersAlreadyRun != NULL) + triggersAlreadyRun[ix] = 1; + free(macro); + free(qformat); + } + } + } + + rpmtdFreeData(&tindexes); + rpmtdFreeData(&tscripts); + rpmtdFreeData(&tprogs); + + /* + * Each target/source header pair can only result in a single + * script being run. + */ + break; + } + + rpmtdFreeData(&pfx); + trigger = rpmdsFree(trigger); + + return rc; +} + +/** + * Run trigger scripts in the database that are fired by this header. + * @param psm package state machine data + * @return 0 on success + */ +static rpmRC runTriggers(rpmpsm psm) +{ + const rpmts ts = psm->ts; + int numPackage = -1; + const char * N = NULL; + int nerrors = 0; + + if (psm->te) /* XXX can't happen */ + N = rpmteN(psm->te); + if (N) /* XXX can't happen */ + numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N) + + psm->countCorrection; + if (numPackage < 0) + return RPMRC_NOTFOUND; + + { Header triggeredH; + Header h = rpmteHeader(psm->te); + rpmdbMatchIterator mi; + int countCorrection = psm->countCorrection; + + psm->countCorrection = 0; + mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0); + while((triggeredH = rpmdbNextIterator(mi)) != NULL) + nerrors += handleOneTrigger(psm, h, triggeredH, numPackage, NULL); + mi = rpmdbFreeIterator(mi); + psm->countCorrection = countCorrection; + headerFree(h); + } + + return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL; +} + +/** + * Run triggers from this header that are fired by headers in the database. + * @param psm package state machine data + * @return 0 on success + */ +static rpmRC runImmedTriggers(rpmpsm psm) +{ + const rpmts ts = psm->ts; + unsigned char * triggersRun; + struct rpmtd_s tnames, tindexes; + Header h = rpmteHeader(psm->te); + int nerrors = 0; + + if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) && + headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) { + goto exit; + } + + triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun)); + { Header sourceH = NULL; + const char *trigName; + rpm_count_t *triggerIndices = tindexes.data; + + while ((trigName = rpmtdNextString(&tnames))) { + rpmdbMatchIterator mi; + int i = rpmtdGetIndex(&tnames); + + if (triggersRun[triggerIndices[i]] != 0) continue; + + mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0); + + while((sourceH = rpmdbNextIterator(mi)) != NULL) { + nerrors += handleOneTrigger(psm, sourceH, h, + rpmdbGetIteratorCount(mi), + triggersRun); + } + + mi = rpmdbFreeIterator(mi); + } + } + rpmtdFreeData(&tnames); + rpmtdFreeData(&tindexes); + free(triggersRun); + +exit: + headerFree(h); + return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL; +} + +static rpmpsm rpmpsmFree(rpmpsm psm) +{ + if (psm == NULL) + return NULL; + + psm->fi = rpmfiFree(psm->fi); +#ifdef NOTYET + psm->te = rpmteFree(psm->te); +#else + psm->te = NULL; +#endif + psm->ts = rpmtsFree(psm->ts); + + memset(psm, 0, sizeof(*psm)); /* XXX trash and burn */ + psm = _free(psm); + + return NULL; +} + +static rpmpsm rpmpsmNew(rpmts ts, rpmte te) +{ + rpmpsm psm = xcalloc(1, sizeof(*psm)); + + if (ts) psm->ts = rpmtsLink(ts); + if (te) { +#ifdef NOTYET + psm->te = rpmteLink(te, RPMDBG_M("rpmpsmNew")); +#else + psm->te = te; +#endif + psm->fi = rpmfiLink(rpmteFI(te)); + } + + return psm; +} + +static rpmRC rpmpsmNext(rpmpsm psm, pkgStage nstage) +{ + psm->nstage = nstage; + return rpmpsmStage(psm, psm->nstage); +} + +static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage) +{ + const rpmts ts = psm->ts; + rpm_color_t tscolor = rpmtsColor(ts); + rpmfi fi = psm->fi; + rpmRC rc = RPMRC_OK; + int saveerrno; + int xx; + + switch (stage) { + case PSM_UNKNOWN: + break; + case PSM_INIT: + rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", + psm->goalName, rpmteNEVR(psm->te), rpmfiFC(fi)); + + /* + * When we run scripts, we pass an argument which is the number of + * versions of this package that will be installed when we are + * finished. + */ + psm->npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(psm->te)); + if (psm->npkgs_installed < 0) { + rc = RPMRC_FAIL; + break; + } + + if (psm->goal == PKG_INSTALL) { + rpmdbMatchIterator mi; + Header oh; + + psm->scriptArg = psm->npkgs_installed + 1; + + mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(psm->te), 0); + xx = rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, + rpmteE(psm->te)); + xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, + rpmteV(psm->te)); + xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, + rpmteR(psm->te)); + if (tscolor) { + xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, + rpmteA(psm->te)); + xx = rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, + rpmteO(psm->te)); + } + + while ((oh = rpmdbNextIterator(mi)) != NULL) { + rpmteSetDBInstance(psm->te, rpmdbGetIteratorOffset(mi)); + oh = NULL; + break; + } + mi = rpmdbFreeIterator(mi); + rc = RPMRC_OK; + + if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB) break; + + if (rpmfiFC(fi) > 0) { + struct rpmtd_s filenames; + rpmTag ftag = RPMTAG_FILENAMES; + Header h = rpmteHeader(psm->te); + + if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { + ftag = RPMTAG_ORIGFILENAMES; + } + headerGet(h, ftag, &filenames, HEADERGET_EXT); + fi->apath = filenames.data; /* Ick.. */ + headerFree(h); + } + } + if (psm->goal == PKG_ERASE) { + psm->scriptArg = psm->npkgs_installed - 1; + } + break; + case PSM_PRE: + if (psm->goal == PKG_INSTALL) { + psm->scriptTag = RPMTAG_PREIN; + psm->sense = RPMSENSE_TRIGGERPREIN; + psm->countCorrection = 0; /* XXX is this correct?!? */ + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = rpmpsmNext(psm, PSM_TRIGGERS); + if (rc) break; + + /* Run triggers in this package other package(s) set off. */ + rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); + if (rc) break; + } + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) { + rc = rpmpsmNext(psm, PSM_SCRIPT); + if (rc) break; + } + } + + if (psm->goal == PKG_ERASE) { + psm->scriptTag = RPMTAG_PREUN; + psm->sense = RPMSENSE_TRIGGERUN; + psm->countCorrection = -1; + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) { + /* Run triggers in this package other package(s) set off. */ + rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); + if (rc) break; + + /* Run triggers in other package(s) this package sets off. */ + rc = rpmpsmNext(psm, PSM_TRIGGERS); + if (rc) break; + } + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN)) + rc = rpmpsmNext(psm, PSM_SCRIPT); + } + break; + case PSM_PROCESS: + if (psm->goal == PKG_INSTALL) { + FD_t payload = NULL; + + if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB) break; + + /* XXX Synthesize callbacks for packages with no files. */ + if (rpmfiFC(fi) <= 0) { + void * ptr; + ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_START, 0, 100); + ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_PROGRESS, 100, 100); + break; + } + + payload = rpmtePayload(psm->te); + if (payload == NULL) { + rc = RPMRC_FAIL; + break; + } + + rc = fsmSetup(rpmfiFSM(fi), FSM_PKGINSTALL, ts, psm->te, fi, + payload, NULL, &psm->failedFile); + (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), + fdOp(payload, FDSTAT_READ)); + (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), + fdOp(payload, FDSTAT_DIGEST)); + xx = fsmTeardown(rpmfiFSM(fi)); + + saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */ + xx = Fclose(payload); + errno = saveerrno; /* XXX FIXME: Fclose with libio destroys errno */ + + /* XXX make sure progress is closed out */ + psm->what = RPMCALLBACK_INST_PROGRESS; + psm->amount = (fi->archiveSize ? fi->archiveSize : 100); + psm->total = psm->amount; + xx = rpmpsmNext(psm, PSM_NOTIFY); + + if (rc) { + rpmlog(RPMLOG_ERR, + _("unpacking of archive failed%s%s: %s\n"), + (psm->failedFile != NULL ? _(" on file ") : ""), + (psm->failedFile != NULL ? psm->failedFile : ""), + cpioStrerror(rc)); + rc = RPMRC_FAIL; + + /* XXX notify callback on error. */ + psm->what = RPMCALLBACK_UNPACK_ERROR; + psm->amount = 0; + psm->total = 0; + xx = rpmpsmNext(psm, PSM_NOTIFY); + + break; + } + } + if (psm->goal == PKG_ERASE) { + int fc = rpmfiFC(fi); + + if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB) break; + + /* XXX Synthesize callbacks for packages with no files. */ + if (rpmfiFC(fi) <= 0) { + void * ptr; + ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_START, 0, 100); + ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_STOP, 0, 100); + break; + } + + psm->what = RPMCALLBACK_UNINST_START; + psm->amount = fc; /* XXX W2DO? looks wrong. */ + psm->total = fc; + xx = rpmpsmNext(psm, PSM_NOTIFY); + + rc = fsmSetup(rpmfiFSM(fi), FSM_PKGERASE, ts, psm->te, fi, + NULL, NULL, &psm->failedFile); + xx = fsmTeardown(rpmfiFSM(fi)); + + psm->what = RPMCALLBACK_UNINST_STOP; + psm->amount = 0; /* XXX W2DO? looks wrong. */ + psm->total = fc; + xx = rpmpsmNext(psm, PSM_NOTIFY); + + } + break; + case PSM_POST: + if (psm->goal == PKG_INSTALL) { + rpm_time_t installTime = (rpm_time_t) time(NULL); + rpmfs fs = rpmteGetFileStates(psm->te); + rpm_count_t fc = rpmfsFC(fs); + rpm_fstate_t * fileStates = rpmfsGetStates(fs); + Header h = rpmteHeader(psm->te); + + if (fileStates != NULL && fc > 0) { + headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc); + } + + headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1); + headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1); + headerFree(h); + + /* + * If this package has already been installed, remove it from + * the database before adding the new one. + */ + if (rpmteDBInstance(psm->te)) { + rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE); + if (rc) break; + } + + rc = rpmpsmNext(psm, PSM_RPMDB_ADD); + if (rc) break; + + psm->scriptTag = RPMTAG_POSTIN; + psm->sense = RPMSENSE_TRIGGERIN; + psm->countCorrection = 0; + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) { + rc = rpmpsmNext(psm, PSM_SCRIPT); + if (rc) break; + } + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = rpmpsmNext(psm, PSM_TRIGGERS); + if (rc) break; + + /* Run triggers in this package other package(s) set off. */ + rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); + if (rc) break; + } + + rc = markReplacedFiles(psm); + + } + if (psm->goal == PKG_ERASE) { + + psm->scriptTag = RPMTAG_POSTUN; + psm->sense = RPMSENSE_TRIGGERPOSTUN; + psm->countCorrection = -1; + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) { + rc = rpmpsmNext(psm, PSM_SCRIPT); + if (rc) break; + } + + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = rpmpsmNext(psm, PSM_TRIGGERS); + if (rc) break; + } + + rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE); + } + break; + case PSM_UNDO: + break; + case PSM_FINI: + if (rc) { + if (psm->failedFile) + rpmlog(RPMLOG_ERR, + _("%s failed on file %s: %s\n"), + psm->goalName, psm->failedFile, cpioStrerror(rc)); + else + rpmlog(RPMLOG_ERR, _("%s failed: %s\n"), + psm->goalName, cpioStrerror(rc)); + + /* XXX notify callback on error. */ + psm->what = RPMCALLBACK_CPIO_ERROR; + psm->amount = 0; + psm->total = 0; + xx = rpmpsmNext(psm, PSM_NOTIFY); + } + + psm->failedFile = _free(psm->failedFile); + + fi->apath = _free(fi->apath); + break; + + case PSM_CREATE: + break; + case PSM_NOTIFY: + { void * ptr; +/* FIX: psm->te may be NULL */ + ptr = rpmtsNotify(ts, psm->te, psm->what, psm->amount, psm->total); + } break; + case PSM_DESTROY: + break; + case PSM_SCRIPT: /* Run current package scriptlets. */ + rc = runInstScript(psm); + break; + case PSM_TRIGGERS: + /* Run triggers in other package(s) this package sets off. */ + rc = runTriggers(psm); + break; + case PSM_IMMED_TRIGGERS: + /* Run triggers in this package other package(s) set off. */ + rc = runImmedTriggers(psm); + break; + + case PSM_RPMDB_ADD: { + Header h = rpmteHeader(psm->te); + + if (!headerIsEntry(h, RPMTAG_INSTALLTID)) { + rpm_tid_t tid = rpmtsGetTid(ts); + if (tid != 0 && tid != (rpm_tid_t)-1) + headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1); + } + + (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0); + rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL; + (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0); + + if (rc == RPMRC_OK) + rpmteSetDBInstance(psm->te, headerGetInstance(h)); + headerFree(h); + } break; + + case PSM_RPMDB_REMOVE: + (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); + rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(psm->te)) == 0) ? + RPMRC_OK : RPMRC_FAIL; + (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); + if (rc == RPMRC_OK) + rpmteSetDBInstance(psm->te, 0); + break; + + default: + break; + } + + return rc; +} + +static const char * pkgGoalString(pkgGoal goal) +{ + switch(goal) { + case PKG_INSTALL: return " install"; + case PKG_ERASE: return " erase"; + case PKG_VERIFY: return " verify"; + case PKG_PRETRANS: return " pretrans"; + case PKG_POSTTRANS: return "posttrans"; + default: return "unknown"; + } +} + +rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal) +{ + rpmpsm psm = NULL; + rpmRC rc = RPMRC_FAIL; + + /* Psm can't fail in test mode, just return early */ + if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) + return RPMRC_OK; + + psm = rpmpsmNew(ts, te); + if (rpmChrootIn() == 0) { + rpmtsOpX op; + psm->goal = goal; + psm->goalName = pkgGoalString(goal); + + switch (goal) { + case PKG_INSTALL: + case PKG_ERASE: + op = (goal == PKG_INSTALL) ? RPMTS_OP_INSTALL : RPMTS_OP_ERASE; + rpmswEnter(rpmtsOp(psm->ts, op), 0); + + rc = rpmpsmNext(psm, PSM_INIT); + if (!rc) rc = rpmpsmNext(psm, PSM_PRE); + if (!rc) rc = rpmpsmNext(psm, PSM_PROCESS); + if (!rc) rc = rpmpsmNext(psm, PSM_POST); + (void) rpmpsmNext(psm, PSM_FINI); + + rpmswExit(rpmtsOp(psm->ts, op), 0); + break; + case PKG_PRETRANS: + case PKG_POSTTRANS: + case PKG_VERIFY: + psm->scriptTag = goal; + rc = rpmpsmStage(psm, PSM_SCRIPT); + break; + default: + break; + } + /* XXX an error here would require a full abort */ + (void) rpmChrootOut(); + } + rpmpsmFree(psm); + return rc; +} |