Add librpm-tizen.spec file & Debian packaging.
[tools/librpm-tizen.git] / lib / psm.c
1 /** \ingroup rpmts payload
2  * \file lib/psm.c
3  * Package state machine to handle a package from a transaction set.
4  */
5
6 #include "system.h"
7
8 #include <errno.h>
9
10 #include <rpm/rpmlib.h>         /* rpmvercmp and others */
11 #include <rpm/rpmmacro.h>
12 #include <rpm/rpmds.h>
13 #include <rpm/rpmts.h>
14 #include <rpm/rpmfileutil.h>
15 #include <rpm/rpmdb.h>
16 #include <rpm/rpmlog.h>
17 #include <rpm/rpmstring.h>
18 #include <rpm/argv.h>
19
20 #include "lib/fsm.h"            /* XXX CPIO_FOO/FSM_FOO constants */
21 #include "lib/rpmchroot.h"
22 #include "lib/rpmfi_internal.h" /* XXX replaced/states... */
23 #include "lib/rpmte_internal.h" /* XXX internal apis */
24 #include "lib/rpmdb_internal.h" /* rpmdbAdd/Remove */
25 #include "lib/rpmts_internal.h" /* rpmtsPlugins() etc */
26 #include "lib/rpmds_internal.h" /* rpmdsFilterTi() */
27 #include "lib/rpmscript.h"
28 #include "lib/misc.h"
29 #include "lib/rpmtriggers.h"
30
31 #include "lib/rpmplugins.h"
32
33 #include "debug.h"
34
35 struct rpmpsm_s {
36     rpmts ts;                   /*!< transaction set */
37     rpmte te;                   /*!< current transaction element */
38     rpmfiles files;             /*!< transaction element file info */
39     int scriptArg;              /*!< Scriptlet package arg. */
40     int countCorrection;        /*!< 0 if installing, -1 if removing. */
41     rpmCallbackType what;       /*!< Callback type. */
42     rpm_loff_t amount;          /*!< Callback amount. */
43     rpm_loff_t total;           /*!< Callback total. */
44
45     int nrefs;                  /*!< Reference count. */
46 };
47
48 static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal);
49 static rpmRC rpmpsmUnpack(rpmpsm psm);
50 static rpmpsm rpmpsmFree(rpmpsm psm);
51 static const char * pkgGoalString(pkgGoal goal);
52
53 /**
54  * Adjust file states in database for files shared with this package:
55  * currently either "replaced" or "wrong color".
56  * @param psm           package state machine data
57  * @return              0 always
58  */
59 static rpmRC markReplacedFiles(const rpmpsm psm)
60 {
61     const rpmts ts = psm->ts;
62     rpmfs fs = rpmteGetFileStates(psm->te);
63     sharedFileInfo replaced = rpmfsGetReplaced(fs);
64     sharedFileInfo sfi;
65     rpmdbMatchIterator mi;
66     Header h;
67     unsigned int * offsets;
68     unsigned int prev;
69     unsigned int num;
70
71     if (!replaced)
72         return RPMRC_OK;
73
74     num = prev = 0;
75     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
76         if (prev && prev == sfi->otherPkg)
77             continue;
78         prev = sfi->otherPkg;
79         num++;
80     }
81     if (num == 0)
82         return RPMRC_OK;
83
84     offsets = xmalloc(num * sizeof(*offsets));
85     offsets[0] = 0;
86     num = prev = 0;
87     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
88         if (prev && prev == sfi->otherPkg)
89             continue;
90         prev = sfi->otherPkg;
91         offsets[num++] = sfi->otherPkg;
92     }
93
94     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
95     rpmdbAppendIterator(mi, offsets, num);
96     rpmdbSetIteratorRewrite(mi, 1);
97
98     sfi = replaced;
99     while ((h = rpmdbNextIterator(mi)) != NULL) {
100         int modified;
101         struct rpmtd_s secStates;
102         modified = 0;
103
104         if (!headerGet(h, RPMTAG_FILESTATES, &secStates, HEADERGET_MINMEM))
105             continue;
106         
107         prev = rpmdbGetIteratorOffset(mi);
108         num = 0;
109         while (sfi && sfi->otherPkg == prev) {
110             int ix = rpmtdSetIndex(&secStates, sfi->otherFileNum);
111             assert(ix != -1);
112
113             char *state = rpmtdGetChar(&secStates);
114             if (state && *state != sfi->rstate) {
115                 *state = sfi->rstate;
116                 if (modified == 0) {
117                     /* Modified header will be rewritten. */
118                     modified = 1;
119                     rpmdbSetIteratorModified(mi, modified);
120                 }
121                 num++;
122             }
123             sfi=rpmfsNextReplaced(fs, sfi);
124         }
125         rpmtdFreeData(&secStates);
126     }
127     rpmdbFreeIterator(mi);
128     free(offsets);
129
130     return RPMRC_OK;
131 }
132
133 static int rpmlibDeps(Header h)
134 {
135     rpmds req = rpmdsInit(rpmdsNew(h, RPMTAG_REQUIRENAME, 0));
136     rpmds rpmlib = NULL;
137     rpmdsRpmlib(&rpmlib, NULL);
138     int rc = 1;
139     char *nvr = NULL;
140     while (rpmdsNext(req) >= 0) {
141         if (!(rpmdsFlags(req) & RPMSENSE_RPMLIB))
142             continue;
143         if (rpmdsSearch(rpmlib, req) < 0) {
144             if (!nvr) {
145                 nvr = headerGetAsString(h, RPMTAG_NEVRA);
146                 rpmlog(RPMLOG_ERR, _("Missing rpmlib features for %s:\n"), nvr);
147             }
148             rpmlog(RPMLOG_ERR, "\t%s\n", rpmdsDNEVR(req)+2);
149             rc = 0;
150         }
151     }
152     rpmdsFree(req);
153     rpmdsFree(rpmlib);
154     free(nvr);
155     return rc;
156 }
157
158 rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd,
159                 char ** specFilePtr, char ** cookie)
160 {
161     Header h = NULL;
162     rpmpsm psm = NULL;
163     rpmte te = NULL;
164     rpmRC rpmrc;
165     int specix = -1;
166
167     rpmrc = rpmReadPackageFile(ts, fd, NULL, &h);
168     switch (rpmrc) {
169     case RPMRC_NOTTRUSTED:
170     case RPMRC_NOKEY:
171     case RPMRC_OK:
172         break;
173     default:
174         goto exit;
175         break;
176     }
177     if (h == NULL)
178         goto exit;
179
180     rpmrc = RPMRC_FAIL; /* assume failure */
181
182     if (!headerIsSource(h)) {
183         rpmlog(RPMLOG_ERR, _("source package expected, binary found\n"));
184         goto exit;
185     }
186
187     /* src.rpm install can require specific rpmlib features, check them */
188     if (!rpmlibDeps(h))
189         goto exit;
190
191     specix = headerFindSpec(h);
192
193     if (specix < 0) {
194         rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n"));
195         goto exit;
196     };
197
198     if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) {
199         goto exit;
200     }
201
202     te = rpmtsElement(ts, 0);
203     if (te == NULL) {   /* XXX can't happen */
204         goto exit;
205     }
206     rpmteSetFd(te, fd);
207
208     rpmteSetHeader(te, h);
209
210     {
211         /* set all files to be installed */
212         rpmfs fs = rpmteGetFileStates(te);
213         int fc = rpmfsFC(fs);
214         for (int i = 0; i < fc; i++)
215             rpmfsSetAction(fs, i, FA_CREATE);
216     }
217
218     psm = rpmpsmNew(ts, te, PKG_INSTALL);
219
220     if (rpmpsmUnpack(psm) == RPMRC_OK)
221         rpmrc = RPMRC_OK;
222
223     rpmpsmFree(psm);
224
225 exit:
226     if (rpmrc == RPMRC_OK && specix >= 0) {
227         if (cookie)
228             *cookie = headerGetAsString(h, RPMTAG_COOKIE);
229         if (specFilePtr) {
230             rpmfiles files = rpmteFiles(te);
231             *specFilePtr = rpmfilesFN(files, specix);
232             rpmfilesFree(files);
233         }
234     }
235
236     /* XXX nuke the added package(s). */
237     headerFree(h);
238     rpmtsEmpty(ts);
239
240     return rpmrc;
241 }
242
243 static rpmRC runInstScript(rpmpsm psm, rpmTagVal scriptTag)
244 {
245     rpmRC rc = RPMRC_OK;
246     struct rpmtd_s pfx;
247     Header h = rpmteHeader(psm->te);
248     rpmScript script = rpmScriptFromTag(h, scriptTag);
249
250     if (script) {
251         headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
252         rc = runScript(psm->ts, psm->te, h, pfx.data, script, psm->scriptArg, -1);
253         rpmtdFreeData(&pfx);
254     }
255
256     rpmScriptFree(script);
257     headerFree(h);
258
259     return rc;
260 }
261
262 /**
263  * Execute triggers.
264  * @todo Trigger on any provides, not just package NVR.
265  * @param ts            transaction set
266  * @param te            transaction element
267  * @param sense         trigger type
268  * @param sourceH       header of trigger source
269  * @param trigH         header of triggered package
270  * @param arg2
271  * @param triggersAlreadyRun
272  * @return
273  */
274 static rpmRC handleOneTrigger(rpmts ts, rpmte te, rpmsenseFlags sense,
275                         Header sourceH, Header trigH, int countCorrection,
276                         int arg2, unsigned char * triggersAlreadyRun)
277 {
278     rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0));
279     struct rpmtd_s pfx;
280     const char * sourceName = headerGetString(sourceH, RPMTAG_NAME);
281     const char * triggerName = headerGetString(trigH, RPMTAG_NAME);
282     rpmRC rc = RPMRC_OK;
283     int i;
284
285     if (trigger == NULL)
286         return rc;
287
288     headerGet(trigH, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
289     (void) rpmdsSetNoPromote(trigger, 1);
290
291     while ((i = rpmdsNext(trigger)) >= 0) {
292         uint32_t tix;
293
294         if (!(rpmdsFlags(trigger) & sense))
295             continue;
296
297         if (!rstreq(rpmdsN(trigger), sourceName))
298             continue;
299
300         /* XXX Trigger on any provided dependency, not just the package NEVR */
301         if (!rpmdsAnyMatchesDep(sourceH, trigger, 1))
302             continue;
303
304         tix = rpmdsTi(trigger);
305         if (triggersAlreadyRun == NULL || triggersAlreadyRun[tix] == 0) {
306             int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName);
307
308             if (arg1 < 0) {
309                 /* XXX W2DO? fails as "execution of script failed" */
310                 rc = RPMRC_FAIL;
311             } else {
312                 rpmScript script = rpmScriptFromTriggerTag(trigH,
313                              triggertag(sense), RPMSCRIPT_NORMALTRIGGER, tix);
314                 arg1 += countCorrection;
315                 rc = runScript(ts, te, trigH, pfx.data, script, arg1, arg2);
316                 if (triggersAlreadyRun != NULL)
317                     triggersAlreadyRun[tix] = 1;
318
319                 rpmScriptFree(script);
320             }
321         }
322
323         /*
324          * Each target/source header pair can only result in a single
325          * script being run.
326          */
327         break;
328     }
329
330     rpmtdFreeData(&pfx);
331     rpmdsFree(trigger);
332
333     return rc;
334 }
335
336 /**
337  * Run trigger scripts in the database that are fired by this header.
338  * @param psm           package state machine data
339  * @param sense         trigger type
340  * @return              0 on success
341  */
342 static rpmRC runTriggers(rpmpsm psm, rpmsenseFlags sense)
343 {
344     const rpmts ts = psm->ts;
345     int numPackage = -1;
346     const char * N = NULL;
347     int nerrors = 0;
348
349     if (psm->te)        /* XXX can't happen */
350         N = rpmteN(psm->te);
351     if (N)              /* XXX can't happen */
352         numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
353                                 + psm->countCorrection;
354     if (numPackage < 0)
355         return RPMRC_NOTFOUND;
356
357     {   Header triggeredH;
358         Header h = rpmteHeader(psm->te);
359         rpmdbMatchIterator mi;
360
361         mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0);
362         while ((triggeredH = rpmdbNextIterator(mi)) != NULL) {
363             nerrors += handleOneTrigger(ts, NULL, sense, h, triggeredH,
364                                         0, numPackage, NULL);
365         }
366         rpmdbFreeIterator(mi);
367         headerFree(h);
368     }
369
370     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
371 }
372
373 /**
374  * Run triggers from this header that are fired by headers in the database.
375  * @param psm           package state machine data
376  * @param sense         trigger type
377  * @return              0 on success
378  */
379 static rpmRC runImmedTriggers(rpmpsm psm, rpmsenseFlags sense)
380 {
381     const rpmts ts = psm->ts;
382     unsigned char * triggersRun;
383     struct rpmtd_s tnames, tindexes;
384     Header h = rpmteHeader(psm->te);
385     int nerrors = 0;
386
387     if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) &&
388           headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) {
389         goto exit;
390     }
391
392     triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun));
393     {   Header sourceH = NULL;
394         const char *trigName;
395         rpm_count_t *triggerIndices = tindexes.data;
396
397         while ((trigName = rpmtdNextString(&tnames))) {
398             rpmdbMatchIterator mi;
399             int i = rpmtdGetIndex(&tnames);
400
401             if (triggersRun[triggerIndices[i]] != 0) continue;
402         
403             mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0);
404
405             while ((sourceH = rpmdbNextIterator(mi)) != NULL) {
406                 nerrors += handleOneTrigger(psm->ts, psm->te,
407                                 sense, sourceH, h,
408                                 psm->countCorrection,
409                                 rpmdbGetIteratorCount(mi),
410                                 triggersRun);
411             }
412
413             rpmdbFreeIterator(mi);
414         }
415     }
416     rpmtdFreeData(&tnames);
417     rpmtdFreeData(&tindexes);
418     free(triggersRun);
419
420 exit:
421     headerFree(h);
422     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
423 }
424
425 static rpmpsm rpmpsmFree(rpmpsm psm)
426 {
427     if (psm) {
428         rpmfilesFree(psm->files);
429         rpmtsFree(psm->ts),
430         /* XXX rpmte not refcounted yet */
431         memset(psm, 0, sizeof(*psm)); /* XXX trash and burn */
432         free(psm);
433     }
434     return NULL;
435 }
436
437 static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal)
438 {
439     rpmpsm psm = xcalloc(1, sizeof(*psm));
440     psm->ts = rpmtsLink(ts);
441     psm->files = rpmteFiles(te);
442     psm->te = te; /* XXX rpmte not refcounted yet */
443     if (!rpmteIsSource(te)) {
444         /*
445          * When we run scripts, we pass an argument which is the number of
446          * versions of this package that will be installed when we are
447          * finished.
448          */
449         int npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(te));
450         switch (goal) {
451         case PKG_INSTALL:
452         case PKG_PRETRANS:
453             psm->scriptArg = npkgs_installed + 1;
454             psm->countCorrection = 0;
455             break;
456         case PKG_ERASE:
457             psm->scriptArg = npkgs_installed - 1;
458             psm->countCorrection = -1;
459             break;
460         case PKG_VERIFY:
461         case PKG_POSTTRANS:
462             psm->scriptArg = npkgs_installed;
463             psm->countCorrection = 0;
464             break;
465         default:
466             break;
467         }
468     }
469
470     if (goal == PKG_INSTALL) {
471         Header h = rpmteHeader(te);
472         psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE);
473         headerFree(h);
474     } else if (goal == PKG_ERASE) {
475         psm->total = rpmfilesFC(psm->files);
476     }
477     /* Fake up something for packages with no files */
478     if (psm->total == 0)
479         psm->total = 100;
480
481     if (goal == PKG_INSTALL || goal == PKG_ERASE) {
482         rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", pkgGoalString(goal),
483             rpmteNEVRA(psm->te), rpmfilesFC(psm->files));
484     }
485
486     return psm;
487 }
488
489 void rpmpsmNotify(rpmpsm psm, int what, rpm_loff_t amount)
490 {
491     if (psm) {
492         int changed = 0;
493         if (amount > psm->amount) {
494             psm->amount = amount;
495             changed = 1;
496         }
497         if (what && what != psm->what) {
498             psm->what = what;
499             changed = 1;
500         }
501         if (changed) {
502            rpmtsNotify(psm->ts, psm->te, psm->what, psm->amount, psm->total);
503         }
504     }
505 }
506
507 /*
508  * --replacepkgs hack: find the header instance we're replacing and
509  * mark it as the db instance of the install element. In PSM_POST,
510  * if an install element already has a db instance, it's removed
511  * before proceeding with the adding the new header to the db.
512  */
513 static void markReplacedInstance(rpmts ts, rpmte te)
514 {
515     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(te), 0);
516     rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(te));
517     rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(te));
518     rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(te));
519     /* XXX shouldn't we also do this on colorless transactions? */
520     if (rpmtsColor(ts)) {
521         rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(te));
522         rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(te));
523     }
524
525     while (rpmdbNextIterator(mi) != NULL) {
526         rpmteSetDBInstance(te, rpmdbGetIteratorOffset(mi));
527         break;
528     }
529     rpmdbFreeIterator(mi);
530 }
531
532 static rpmRC dbAdd(rpmts ts, rpmte te)
533 {
534     Header h = rpmteHeader(te);
535     rpm_time_t installTime = (rpm_time_t) time(NULL);
536     rpmfs fs = rpmteGetFileStates(te);
537     rpm_count_t fc = rpmfsFC(fs);
538     rpm_fstate_t * fileStates = rpmfsGetStates(fs);
539     rpm_color_t tscolor = rpmtsColor(ts);
540     rpm_tid_t tid = rpmtsGetTid(ts);
541     rpmRC rc;
542
543     if (fileStates != NULL && fc > 0) {
544         headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc);
545     }
546
547     headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
548     headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1);
549     headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1);
550
551     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
552     rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL;
553     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
554
555     if (rc == RPMRC_OK) {
556         rpmteSetDBInstance(te, headerGetInstance(h));
557         packageHashAddEntry(ts->members->installedPackages,
558                             headerGetInstance(h), te);
559     }
560     headerFree(h);
561     return rc;
562 }
563
564 static rpmRC dbRemove(rpmts ts, rpmte te)
565 {
566     rpmRC rc;
567
568     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
569     rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(te)) == 0) ?
570                                                 RPMRC_OK : RPMRC_FAIL;
571     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
572
573     if (rc == RPMRC_OK)
574         rpmteSetDBInstance(te, 0);
575     return rc;
576 }
577
578 static rpmRC rpmpsmUnpack(rpmpsm psm)
579 {
580     char *failedFile = NULL;
581     int fsmrc = 0;
582     int saved_errno = 0;
583     rpmRC rc = RPMRC_OK;
584
585     rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0);
586     /* make sure first progress call gets made */
587     rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0);
588
589     if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) {
590         if (rpmfilesFC(psm->files) > 0) {
591             fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->files,
592                                    psm, &failedFile);
593             saved_errno = errno;
594         }
595     }
596
597     /* XXX make sure progress reaches 100% */
598     rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, psm->total);
599     rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total);
600
601     if (fsmrc) {
602         char *emsg;
603         errno = saved_errno;
604         emsg = rpmfileStrerror(fsmrc);
605         rpmlog(RPMLOG_ERR,
606                 _("unpacking of archive failed%s%s: %s\n"),
607                 (failedFile != NULL ? _(" on file ") : ""),
608                 (failedFile != NULL ? failedFile : ""),
609                 emsg);
610         free(emsg);
611         rc = RPMRC_FAIL;
612
613         /* XXX notify callback on error. */
614         rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0);
615     }
616     free(failedFile);
617     return rc;
618 }
619
620 static rpmRC rpmpsmRemove(rpmpsm psm)
621 {
622     char *failedFile = NULL;
623     int fsmrc = 0;
624
625     rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0);
626     /* make sure first progress call gets made */
627     rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0);
628
629     /* XXX should't we log errors from here? */
630     if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) {
631         if (rpmfilesFC(psm->files) > 0) {
632             fsmrc = rpmPackageFilesRemove(psm->ts, psm->te, psm->files,
633                                           psm, &failedFile);
634         }
635     }
636     /* XXX make sure progress reaches 100% */
637     rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, psm->total);
638     rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total);
639
640     free(failedFile);
641     return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL;
642 }
643
644 static rpmRC rpmPackageInstall(rpmts ts, rpmpsm psm)
645 {
646     rpmRC rc = RPMRC_OK;
647     int once = 1;
648
649     rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0);
650     while (once--) {
651         /* HACK: replacepkgs abuses te instance to remove old header */
652         if (rpmtsFilterFlags(psm->ts) & RPMPROB_FILTER_REPLACEPKG)
653             markReplacedInstance(ts, psm->te);
654
655
656         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
657             /* Run triggers in other package(s) this package sets off. */
658             rc = runTriggers(psm, RPMSENSE_TRIGGERPREIN);
659             if (rc) break;
660
661             /* Run triggers in this package other package(s) set off. */
662             rc = runImmedTriggers(psm, RPMSENSE_TRIGGERPREIN);
663             if (rc) break;
664         }
665
666         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) {
667             rc = runInstScript(psm, RPMTAG_PREIN);
668             if (rc) break;
669         }
670
671         rc = rpmpsmUnpack(psm);
672         if (rc) break;
673
674         /*
675          * If this package has already been installed, remove it from
676          * the database before adding the new one.
677          */
678         if (rpmteDBInstance(psm->te)) {
679             rc = dbRemove(ts, psm->te);
680             if (rc) break;
681         }
682
683         rc = dbAdd(ts, psm->te);
684         if (rc) break;
685
686         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
687             /* Run upper file triggers i. e. with higher priorities */
688             /* Run file triggers in other package(s) this package sets off. */
689             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
690                                 RPMSCRIPT_FILETRIGGER, 1);
691             if (rc) break;
692
693             /* Run file triggers in this package other package(s) set off. */
694             rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
695                                     RPMSCRIPT_FILETRIGGER, 1);
696             if (rc) break;
697         }
698
699         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) {
700             rc = runInstScript(psm, RPMTAG_POSTIN);
701             if (rc) break;
702         }
703
704         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
705             /* Run triggers in other package(s) this package sets off. */
706             rc = runTriggers(psm, RPMSENSE_TRIGGERIN);
707             if (rc) break;
708
709             /* Run triggers in this package other package(s) set off. */
710             rc = runImmedTriggers(psm, RPMSENSE_TRIGGERIN);
711             if (rc) break;
712
713             /* Run lower file triggers i. e. with lower priorities */
714             /* Run file triggers in other package(s) this package sets off. */
715             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
716                                 RPMSCRIPT_FILETRIGGER, 2);
717             if (rc) break;
718
719             /* Run file triggers in this package other package(s) set off. */
720             rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN,
721                                     RPMSCRIPT_FILETRIGGER, 2);
722             if (rc) break;
723         }
724
725         rc = markReplacedFiles(psm);
726     }
727
728     rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0);
729
730     return rc;
731 }
732
733 static rpmRC rpmPackageErase(rpmts ts, rpmpsm psm)
734 {
735     rpmRC rc = RPMRC_OK;
736     int once = 1;
737
738     rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0);
739     while (once--) {
740
741         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
742             /* Run file triggers in this package other package(s) set off. */
743             rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
744                                     RPMSCRIPT_FILETRIGGER, 1);
745             if (rc) break;
746
747             /* Run file triggers in other package(s) this package sets off. */
748             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
749                                 RPMSCRIPT_FILETRIGGER, 1);
750             if (rc) break;
751
752             /* Run triggers in this package other package(s) set off. */
753             rc = runImmedTriggers(psm, RPMSENSE_TRIGGERUN);
754             if (rc) break;
755
756             /* Run triggers in other package(s) this package sets off. */
757             rc = runTriggers(psm, RPMSENSE_TRIGGERUN);
758             if (rc) break;
759         }
760
761         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN)) {
762             rc = runInstScript(psm, RPMTAG_PREUN);
763             if (rc) break;
764         }
765
766         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
767             /* Run file triggers in this package other package(s) set off. */
768             rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
769                                     RPMSCRIPT_FILETRIGGER, 2);
770             if (rc) break;
771
772             /* Run file triggers in other package(s) this package sets off. */
773             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN,
774                                 RPMSCRIPT_FILETRIGGER, 2);
775             if (rc) break;
776         }
777
778         rc = rpmpsmRemove(psm);
779         if (rc) break;
780
781         /* Run file triggers in other package(s) this package sets off. */
782         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
783             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN,
784                                 RPMSCRIPT_FILETRIGGER, 1);
785         }
786
787         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) {
788             rc = runInstScript(psm, RPMTAG_POSTUN);
789             if (rc) break;
790         }
791
792         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
793             /* Run triggers in other package(s) this package sets off. */
794             rc = runTriggers(psm, RPMSENSE_TRIGGERPOSTUN);
795             if (rc) break;
796
797             /* Run file triggers in other package(s) this package sets off. */
798             rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN,
799                                 RPMSCRIPT_FILETRIGGER, 2);
800         }
801         if (rc) break;
802
803         if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) {
804             /* Prepare post transaction uninstall triggers */
805             rpmtriggersPrepPostUnTransFileTrigs(psm->ts, psm->te);
806         }
807
808         rc = dbRemove(ts, psm->te);
809     }
810
811     rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0);
812
813     return rc;
814 }
815
816 static const char * pkgGoalString(pkgGoal goal)
817 {
818     switch (goal) {
819     case PKG_INSTALL:   return "  install";
820     case PKG_ERASE:     return "    erase";
821     case PKG_VERIFY:    return "   verify";
822     case PKG_PRETRANS:  return " pretrans";
823     case PKG_POSTTRANS: return "posttrans";
824     default:            return "unknown";
825     }
826 }
827
828 rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal)
829 {
830     rpmpsm psm = NULL;
831     rpmRC rc = RPMRC_FAIL;
832
833     /* Psm can't fail in test mode, just return early */
834     if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
835         return RPMRC_OK;
836
837     psm = rpmpsmNew(ts, te, goal);
838     if (rpmChrootIn() == 0) {
839         /* Run pre transaction element hook for all plugins */
840         rc = rpmpluginsCallPsmPre(rpmtsPlugins(ts), te);
841
842         if (!rc) {
843             switch (goal) {
844             case PKG_INSTALL:
845                 rc = rpmPackageInstall(ts, psm);
846                 break;
847             case PKG_ERASE:
848                 rc = rpmPackageErase(ts, psm);
849                 break;
850             case PKG_PRETRANS:
851             case PKG_POSTTRANS:
852             case PKG_VERIFY:
853                 rc = runInstScript(psm, goal);
854                 break;
855             case PKG_TRANSFILETRIGGERIN:
856                 rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERIN,
857                                             RPMSCRIPT_TRANSFILETRIGGER, 0);
858                 break;
859             case PKG_TRANSFILETRIGGERUN:
860                 rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERUN,
861                                             RPMSCRIPT_TRANSFILETRIGGER, 0);
862                 break;
863             default:
864                 break;
865             }
866         }
867         /* Run post transaction element hook for all plugins */
868         rpmpluginsCallPsmPost(rpmtsPlugins(ts), te, rc);
869
870         /* XXX an error here would require a full abort */
871         (void) rpmChrootOut();
872     }
873     rpmpsmFree(psm);
874     return rc;
875 }