Untangle lib/fsm.c and lib/cpio.c
[platform/upstream/rpm.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/cpio.h"
21 #include "lib/fsm.h"            /* XXX CPIO_FOO/FSM_FOO constants */
22 #include "lib/rpmchroot.h"
23 #include "lib/rpmfi_internal.h" /* XXX replaced/states... */
24 #include "lib/rpmte_internal.h" /* XXX internal apis */
25 #include "lib/rpmdb_internal.h" /* rpmdbAdd/Remove */
26 #include "lib/rpmscript.h"
27
28 #include "debug.h"
29
30 typedef enum pkgStage_e {
31     PSM_UNKNOWN         =  0,
32     PSM_INIT            =  1,
33     PSM_PRE             =  2,
34     PSM_PROCESS         =  3,
35     PSM_POST            =  4,
36     PSM_UNDO            =  5,
37     PSM_FINI            =  6,
38
39     PSM_CREATE          = 17,
40     PSM_DESTROY         = 23,
41
42     PSM_SCRIPT          = 53,
43     PSM_TRIGGERS        = 54,
44     PSM_IMMED_TRIGGERS  = 55,
45
46     PSM_RPMDB_ADD       = 98,
47     PSM_RPMDB_REMOVE    = 99
48
49 } pkgStage;
50
51 struct rpmpsm_s {
52     rpmts ts;                   /*!< transaction set */
53     rpmte te;                   /*!< current transaction element */
54     rpmfi fi;                   /*!< transaction element file info */
55     const char * goalName;
56     char * failedFile;
57     rpmTagVal scriptTag;        /*!< Scriptlet data tag. */
58     int npkgs_installed;        /*!< No. of installed instances. */
59     int scriptArg;              /*!< Scriptlet package arg. */
60     rpmsenseFlags sense;        /*!< One of RPMSENSE_TRIGGER{PREIN,IN,UN,POSTUN}. */
61     int countCorrection;        /*!< 0 if installing, -1 if removing. */
62     rpmCallbackType what;       /*!< Callback type. */
63     rpm_loff_t amount;          /*!< Callback amount. */
64     rpm_loff_t total;           /*!< Callback total. */
65     pkgGoal goal;
66     pkgStage stage;             /*!< Current psm stage. */
67     pkgStage nstage;            /*!< Next psm stage. */
68
69     int nrefs;                  /*!< Reference count. */
70 };
71
72 static rpmpsm rpmpsmNew(rpmts ts, rpmte te);
73 static rpmpsm rpmpsmFree(rpmpsm psm);
74 static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage);
75
76 /**
77  * Macros to be defined from per-header tag values.
78  * @todo Should other macros be added from header when installing a package?
79  */
80 static struct tagMacro {
81     const char *macroname;      /*!< Macro name to define. */
82     rpmTag tag;                 /*!< Header tag to use for value. */
83 } const tagMacros[] = {
84     { "name",           RPMTAG_NAME },
85     { "version",        RPMTAG_VERSION },
86     { "release",        RPMTAG_RELEASE },
87     { "epoch",          RPMTAG_EPOCH },
88     { NULL, 0 }
89 };
90
91 /**
92  * Define per-header macros.
93  * @param h             header
94  * @return              0 always
95  */
96 static void rpmInstallLoadMacros(Header h)
97 {
98     const struct tagMacro * tagm;
99
100     for (tagm = tagMacros; tagm->macroname != NULL; tagm++) {
101         struct rpmtd_s td;
102         char *body;
103         if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT))
104             continue;
105
106         switch (rpmtdType(&td)) {
107         default:
108             body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
109             addMacro(NULL, tagm->macroname, NULL, body, -1);
110             free(body);
111             break;
112         case RPM_NULL_TYPE:
113             break;
114         }
115         rpmtdFreeData(&td);
116     }
117 }
118
119 /**
120  * Adjust file states in database for files shared with this package:
121  * currently either "replaced" or "wrong color".
122  * @param psm           package state machine data
123  * @return              0 always
124  */
125 static rpmRC markReplacedFiles(const rpmpsm psm)
126 {
127     const rpmts ts = psm->ts;
128     rpmfs fs = rpmteGetFileStates(psm->te);
129     sharedFileInfo replaced = rpmfsGetReplaced(fs);
130     sharedFileInfo sfi;
131     rpmdbMatchIterator mi;
132     Header h;
133     int * offsets;
134     unsigned int prev;
135     int num;
136
137     if (!replaced)
138         return RPMRC_OK;
139
140     num = prev = 0;
141     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
142         if (prev && prev == sfi->otherPkg)
143             continue;
144         prev = sfi->otherPkg;
145         num++;
146     }
147     if (num == 0)
148         return RPMRC_OK;
149
150     offsets = xmalloc(num * sizeof(*offsets));
151     offsets[0] = 0;
152     num = prev = 0;
153     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
154         if (prev && prev == sfi->otherPkg)
155             continue;
156         prev = sfi->otherPkg;
157         offsets[num++] = sfi->otherPkg;
158     }
159
160     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
161     rpmdbAppendIterator(mi, offsets, num);
162     rpmdbSetIteratorRewrite(mi, 1);
163
164     sfi = replaced;
165     while ((h = rpmdbNextIterator(mi)) != NULL) {
166         int modified;
167         struct rpmtd_s secStates;
168         modified = 0;
169
170         if (!headerGet(h, RPMTAG_FILESTATES, &secStates, HEADERGET_MINMEM))
171             continue;
172         
173         prev = rpmdbGetIteratorOffset(mi);
174         num = 0;
175         while (sfi && sfi->otherPkg == prev) {
176             int ix = rpmtdSetIndex(&secStates, sfi->otherFileNum);
177             assert(ix != -1);
178
179             char *state = rpmtdGetChar(&secStates);
180             if (state && *state != sfi->rstate) {
181                 *state = sfi->rstate;
182                 if (modified == 0) {
183                     /* Modified header will be rewritten. */
184                     modified = 1;
185                     rpmdbSetIteratorModified(mi, modified);
186                 }
187                 num++;
188             }
189             sfi=rpmfsNextReplaced(fs, sfi);
190         }
191         rpmtdFreeData(&secStates);
192     }
193     rpmdbFreeIterator(mi);
194     free(offsets);
195
196     return RPMRC_OK;
197 }
198
199 static int rpmlibDeps(Header h)
200 {
201     rpmds req = rpmdsInit(rpmdsNew(h, RPMTAG_REQUIRENAME, 0));
202     rpmds rpmlib = NULL;
203     rpmdsRpmlib(&rpmlib, NULL);
204     int rc = 1;
205     char *nvr = NULL;
206     while (rpmdsNext(req) >= 0) {
207         if (!(rpmdsFlags(req) & RPMSENSE_RPMLIB))
208             continue;
209         if (rpmdsSearch(rpmlib, req) < 0) {
210             if (!nvr) {
211                 nvr = headerGetAsString(h, RPMTAG_NEVRA);
212                 rpmlog(RPMLOG_ERR, _("Missing rpmlib features for %s:\n"), nvr);
213             }
214             rpmlog(RPMLOG_ERR, "\t%s\n", rpmdsDNEVR(req)+2);
215             rc = 0;
216         }
217     }
218     rpmdsFree(req);
219     rpmdsFree(rpmlib);
220     free(nvr);
221     return rc;
222 }
223
224 rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd,
225                 char ** specFilePtr, char ** cookie)
226 {
227     rpmfi fi = NULL;
228     char * specFile = NULL;
229     const char *rootdir = rpmtsRootDir(ts);
230     Header h = NULL;
231     rpmpsm psm = NULL;
232     rpmte te = NULL;
233     rpmRC rpmrc;
234     int specix = -1;
235     struct rpmtd_s filenames;
236
237     rpmtdReset(&filenames);
238     rpmrc = rpmReadPackageFile(ts, fd, NULL, &h);
239     switch (rpmrc) {
240     case RPMRC_NOTTRUSTED:
241     case RPMRC_NOKEY:
242     case RPMRC_OK:
243         break;
244     default:
245         goto exit;
246         break;
247     }
248     if (h == NULL)
249         goto exit;
250
251     rpmrc = RPMRC_FAIL; /* assume failure */
252
253     if (!headerIsSource(h)) {
254         rpmlog(RPMLOG_ERR, _("source package expected, binary found\n"));
255         goto exit;
256     }
257
258     /* src.rpm install can require specific rpmlib features, check them */
259     if (!rpmlibDeps(h))
260         goto exit;
261
262     if (headerGet(h, RPMTAG_BASENAMES, &filenames, HEADERGET_ALLOC)) {
263         struct rpmtd_s td;
264         const char *str;
265         const char *_cookie = headerGetString(h, RPMTAG_COOKIE);
266         if (cookie && _cookie) *cookie = xstrdup(_cookie);
267         
268         /* Try to find spec by file flags */
269         if (_cookie && headerGet(h, RPMTAG_FILEFLAGS, &td, HEADERGET_MINMEM)) {
270             rpmfileAttrs *flags;
271             while (specix < 0 && (flags = rpmtdNextUint32(&td))) {
272                 if (*flags & RPMFILE_SPECFILE)
273                     specix = rpmtdGetIndex(&td);
274             }
275         }
276         /* Still no spec? Look by filename. */
277         while (specix < 0 && (str = rpmtdNextString(&filenames))) {
278             if (rpmFileHasSuffix(str, ".spec")) 
279                 specix = rpmtdGetIndex(&filenames);
280         }
281     }
282
283     if (rootdir && rstreq(rootdir, "/"))
284         rootdir = NULL;
285
286     /* Macros need to be added before trying to create directories */
287     rpmInstallLoadMacros(h);
288
289     if (specix >= 0) {
290         const char *bn;
291
292         headerDel(h, RPMTAG_BASENAMES);
293         headerDel(h, RPMTAG_DIRNAMES);
294         headerDel(h, RPMTAG_DIRINDEXES);
295
296         rpmtdInit(&filenames);
297         for (int i = 0; (bn = rpmtdNextString(&filenames)); i++) {
298             int spec = (i == specix);
299             char *fn = rpmGenPath(rpmtsRootDir(ts),
300                                   spec ? "%{_specdir}" : "%{_sourcedir}", bn);
301             headerPutString(h, RPMTAG_OLDFILENAMES, fn);
302             if (spec) specFile = xstrdup(fn);
303             free(fn);
304         }
305         headerConvert(h, HEADERCONV_COMPRESSFILELIST);
306     } else {
307         rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n"));
308         goto exit;
309     };
310
311     if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) {
312         goto exit;
313     }
314
315     te = rpmtsElement(ts, 0);
316     if (te == NULL) {   /* XXX can't happen */
317         goto exit;
318     }
319     rpmteSetFd(te, fd);
320
321     rpmteSetHeader(te, h);
322     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
323     h = headerFree(h);
324
325     if (fi == NULL) {
326         goto exit;
327     }
328     fi->apath = filenames.data; /* Ick */
329     rpmteSetFI(te, fi);
330     fi = rpmfiFree(fi);
331
332     if (rpmMkdirs(rpmtsRootDir(ts), "%{_topdir}:%{_sourcedir}:%{_specdir}")) {
333         goto exit;
334     }
335
336     {
337         /* set all files to be installed */
338         rpmfs fs = rpmteGetFileStates(te);
339         int i;
340         unsigned int fc = rpmfiFC(fi);
341         for (i=0; i<fc; i++) rpmfsSetAction(fs, i, FA_CREATE);
342     }
343
344     psm = rpmpsmNew(ts, te);
345     psm->goal = PKG_INSTALL;
346
347         /* FIX: psm->fi->dnl should be owned. */
348     if (rpmpsmStage(psm, PSM_PROCESS) == RPMRC_OK)
349         rpmrc = RPMRC_OK;
350
351     (void) rpmpsmStage(psm, PSM_FINI);
352     rpmpsmFree(psm);
353
354 exit:
355     if (specFilePtr && specFile && rpmrc == RPMRC_OK)
356         *specFilePtr = specFile;
357     else
358         free(specFile);
359
360     headerFree(h);
361     rpmfiFree(fi);
362
363     /* XXX nuke the added package(s). */
364     rpmtsClean(ts);
365
366     return rpmrc;
367 }
368
369 static rpmTagVal triggertag(rpmsenseFlags sense) 
370 {
371     rpmTagVal tag = RPMTAG_NOT_FOUND;
372     switch (sense) {
373     case RPMSENSE_TRIGGERIN:
374         tag = RPMTAG_TRIGGERIN;
375         break;
376     case RPMSENSE_TRIGGERUN:
377         tag = RPMTAG_TRIGGERUN;
378         break;
379     case RPMSENSE_TRIGGERPOSTUN:
380         tag = RPMTAG_TRIGGERPOSTUN;
381         break;
382     case RPMSENSE_TRIGGERPREIN:
383         tag = RPMTAG_TRIGGERPREIN;
384         break;
385     default:
386         break;
387     }
388     return tag;
389 }
390
391 /**
392  * Run a scriptlet with args.
393  *
394  * Run a script with an interpreter. If the interpreter is not specified,
395  * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
396  * the header will be ignored, passing instead arg1 and arg2.
397  *
398  * @param psm           package state machine data
399  * @param prefixes      install prefixes
400  * @param script        scriptlet from header
401  * @param arg1          no. instances of package installed after scriptlet exec
402  *                      (-1 is no arg)
403  * @param arg2          ditto, but for the target package
404  * @return              0 on success
405  */
406 static rpmRC runScript(rpmpsm psm, ARGV_const_t prefixes, 
407                        rpmScript script, int arg1, int arg2)
408 {
409     rpmRC stoprc, rc = RPMRC_OK;
410     rpmTagVal stag = rpmScriptTag(script);
411     FD_t sfd = NULL;
412     int warn_only = (stag != RPMTAG_PREIN &&
413                      stag != RPMTAG_PREUN &&
414                      stag != RPMTAG_PRETRANS &&
415                      stag != RPMTAG_VERIFYSCRIPT);
416     int selinux = !(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_NOCONTEXTS);
417
418     sfd = rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_START, stag, 0);
419     if (sfd == NULL)
420         sfd = rpmtsScriptFd(psm->ts);
421
422     rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0);
423     rc = rpmScriptRun(script, arg1, arg2, sfd,
424                       prefixes, warn_only, selinux);
425     rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0);
426
427     /* Map warn-only errors to "notfound" for script stop callback */
428     stoprc = (rc != RPMRC_OK && warn_only) ? RPMRC_NOTFOUND : rc;
429     rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_STOP, stag, stoprc);
430
431     /* 
432      * Notify callback for all errors. "total" abused for warning/error,
433      * rc only reflects whether the condition prevented install/erase 
434      * (which is only happens with %prein and %preun scriptlets) or not.
435      */
436     if (rc != RPMRC_OK) {
437         if (warn_only) {
438             rc = RPMRC_OK;
439         }
440         rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, stag, rc);
441     }
442
443     return rc;
444 }
445
446 static rpmRC runInstScript(rpmpsm psm)
447 {
448     rpmRC rc = RPMRC_OK;
449     struct rpmtd_s pfx;
450     Header h = rpmteHeader(psm->te);
451     rpmScript script = rpmScriptFromTag(h, psm->scriptTag);
452
453     if (script) {
454         headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
455         rc = runScript(psm, pfx.data, script, psm->scriptArg, -1);
456         rpmtdFreeData(&pfx);
457     }
458
459     rpmScriptFree(script);
460     headerFree(h);
461
462     return rc;
463 }
464
465 /**
466  * Execute triggers.
467  * @todo Trigger on any provides, not just package NVR.
468  * @param psm           package state machine data
469  * @param sourceH       header of trigger source
470  * @param trigH         header of triggered package
471  * @param arg2
472  * @param triggersAlreadyRun
473  * @return
474  */
475 static rpmRC handleOneTrigger(const rpmpsm psm,
476                         Header sourceH, Header trigH,
477                         int arg2, unsigned char * triggersAlreadyRun)
478 {
479     const rpmts ts = psm->ts;
480     rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0));
481     struct rpmtd_s pfx;
482     const char * sourceName = headerGetString(sourceH, RPMTAG_NAME);
483     const char * triggerName = headerGetString(trigH, RPMTAG_NAME);
484     rpmRC rc = RPMRC_OK;
485     int i;
486
487     if (trigger == NULL)
488         return rc;
489
490     headerGet(trigH, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
491     (void) rpmdsSetNoPromote(trigger, 1);
492
493     while ((i = rpmdsNext(trigger)) >= 0) {
494         struct rpmtd_s tindexes;
495         uint32_t tix;
496
497         if (!(rpmdsFlags(trigger) & psm->sense))
498             continue;
499
500         if (!rstreq(rpmdsN(trigger), sourceName))
501             continue;
502
503         /* XXX Trigger on any provided dependency, not just the package NEVR */
504         if (!rpmdsAnyMatchesDep(sourceH, trigger, 1))
505             continue;
506
507         if (!headerGet(trigH, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))
508             continue;
509
510         if (rpmtdSetIndex(&tindexes, i) < 0) {
511             rpmtdFreeData(&tindexes);
512             continue;
513         }
514
515         tix = rpmtdGetNumber(&tindexes);
516         if (triggersAlreadyRun == NULL || triggersAlreadyRun[tix] == 0) {
517             int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName);
518
519             if (arg1 < 0) {
520                 /* XXX W2DO? fails as "execution of script failed" */
521                 rc = RPMRC_FAIL;
522             } else {
523                 rpmScript script = rpmScriptFromTriggerTag(trigH,
524                                                  triggertag(psm->sense), tix);
525                 arg1 += psm->countCorrection;
526                 rc = runScript(psm, pfx.data, script, arg1, arg2);
527
528                 if (triggersAlreadyRun != NULL)
529                     triggersAlreadyRun[tix] = 1;
530
531                 rpmScriptFree(script);
532             }
533         }
534
535         rpmtdFreeData(&tindexes);
536
537         /*
538          * Each target/source header pair can only result in a single
539          * script being run.
540          */
541         break;
542     }
543
544     rpmtdFreeData(&pfx);
545     rpmdsFree(trigger);
546
547     return rc;
548 }
549
550 /**
551  * Run trigger scripts in the database that are fired by this header.
552  * @param psm           package state machine data
553  * @return              0 on success
554  */
555 static rpmRC runTriggers(rpmpsm psm)
556 {
557     const rpmts ts = psm->ts;
558     int numPackage = -1;
559     const char * N = NULL;
560     int nerrors = 0;
561
562     if (psm->te)        /* XXX can't happen */
563         N = rpmteN(psm->te);
564     if (N)              /* XXX can't happen */
565         numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
566                                 + psm->countCorrection;
567     if (numPackage < 0)
568         return RPMRC_NOTFOUND;
569
570     {   Header triggeredH;
571         Header h = rpmteHeader(psm->te);
572         rpmdbMatchIterator mi;
573         int countCorrection = psm->countCorrection;
574
575         psm->countCorrection = 0;
576         mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0);
577         while((triggeredH = rpmdbNextIterator(mi)) != NULL)
578             nerrors += handleOneTrigger(psm, h, triggeredH, numPackage, NULL);
579         rpmdbFreeIterator(mi);
580         psm->countCorrection = countCorrection;
581         headerFree(h);
582     }
583
584     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
585 }
586
587 /**
588  * Run triggers from this header that are fired by headers in the database.
589  * @param psm           package state machine data
590  * @return              0 on success
591  */
592 static rpmRC runImmedTriggers(rpmpsm psm)
593 {
594     const rpmts ts = psm->ts;
595     unsigned char * triggersRun;
596     struct rpmtd_s tnames, tindexes;
597     Header h = rpmteHeader(psm->te);
598     int nerrors = 0;
599
600     if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) &&
601           headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) {
602         goto exit;
603     }
604
605     triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun));
606     {   Header sourceH = NULL;
607         const char *trigName;
608         rpm_count_t *triggerIndices = tindexes.data;
609
610         while ((trigName = rpmtdNextString(&tnames))) {
611             rpmdbMatchIterator mi;
612             int i = rpmtdGetIndex(&tnames);
613
614             if (triggersRun[triggerIndices[i]] != 0) continue;
615         
616             mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0);
617
618             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
619                 nerrors += handleOneTrigger(psm, sourceH, h,
620                                 rpmdbGetIteratorCount(mi),
621                                 triggersRun);
622             }
623
624             rpmdbFreeIterator(mi);
625         }
626     }
627     rpmtdFreeData(&tnames);
628     rpmtdFreeData(&tindexes);
629     free(triggersRun);
630
631 exit:
632     headerFree(h);
633     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
634 }
635
636 static rpmpsm rpmpsmFree(rpmpsm psm)
637 {
638     if (psm) {
639         rpmfiFree(psm->fi);
640         rpmtsFree(psm->ts),
641         /* XXX rpmte not refcounted yet */
642         memset(psm, 0, sizeof(*psm)); /* XXX trash and burn */
643         free(psm);
644     }
645     return NULL;
646 }
647
648 static rpmpsm rpmpsmNew(rpmts ts, rpmte te)
649 {
650     rpmpsm psm = xcalloc(1, sizeof(*psm));
651     psm->ts = rpmtsLink(ts);
652     psm->fi = rpmfiLink(rpmteFI(te));
653     psm->te = te; /* XXX rpmte not refcounted yet */
654     return psm;
655 }
656
657 void rpmpsmNotify(rpmpsm psm, int what, rpm_loff_t amount)
658 {
659     if (psm) {
660         int changed = 0;
661         if (amount > psm->amount) {
662             psm->amount = amount;
663             changed = 1;
664         }
665         if (what && what != psm->what) {
666             psm->what = what;
667             changed = 1;
668         }
669         if (changed) {
670            rpmtsNotify(psm->ts, psm->te, psm->what, psm->amount, psm->total);
671         }
672     }
673 }
674
675 /*
676  * --replacepkgs hack: find the header instance we're replacing and
677  * mark it as the db instance of the install element. In PSM_POST,
678  * if an install element already has a db instance, it's removed
679  * before proceeding with the adding the new header to the db.
680  */
681 static void markReplacedInstance(rpmts ts, rpmte te)
682 {
683     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(te), 0);
684     rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(te));
685     rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(te));
686     rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(te));
687     /* XXX shouldn't we also do this on colorless transactions? */
688     if (rpmtsColor(ts)) {
689         rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(te));
690         rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(te));
691     }
692
693     while (rpmdbNextIterator(mi) != NULL) {
694         rpmteSetDBInstance(te, rpmdbGetIteratorOffset(mi));
695         break;
696     }
697     rpmdbFreeIterator(mi);
698 }
699
700 static rpmRC rpmpsmNext(rpmpsm psm, pkgStage nstage)
701 {
702     psm->nstage = nstage;
703     return rpmpsmStage(psm, psm->nstage);
704 }
705
706 static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage)
707 {
708     const rpmts ts = psm->ts;
709     rpmfi fi = psm->fi;
710     rpmRC rc = RPMRC_OK;
711
712     switch (stage) {
713     case PSM_UNKNOWN:
714         break;
715     case PSM_INIT:
716         rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n",
717                 psm->goalName, rpmteNEVR(psm->te), rpmfiFC(fi));
718
719         /*
720          * When we run scripts, we pass an argument which is the number of
721          * versions of this package that will be installed when we are
722          * finished.
723          */
724         psm->npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(psm->te));
725         if (psm->npkgs_installed < 0) {
726             rc = RPMRC_FAIL;
727             break;
728         }
729
730         if (psm->goal == PKG_INSTALL) {
731             Header h = rpmteHeader(psm->te);
732             psm->scriptArg = psm->npkgs_installed + 1;
733
734             psm->amount = 0;
735             psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE);
736             /* fake up something for packages with no files */
737             if (psm->total == 0)
738                 psm->total = 100;
739
740             /* HACK: reinstall abuses te instance to remove old header */
741             if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG)
742                 markReplacedInstance(ts, psm->te);
743
744             if (rpmfiFC(fi) > 0) {
745                 struct rpmtd_s filenames;
746                 rpmTag ftag = RPMTAG_FILENAMES;
747         
748                 if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) {
749                     ftag = RPMTAG_ORIGFILENAMES;
750                 }
751                 headerGet(h, ftag, &filenames, HEADERGET_EXT);
752                 fi->apath = filenames.data; /* Ick.. */
753             }
754             headerFree(h);
755         }
756         if (psm->goal == PKG_ERASE) {
757             psm->scriptArg = psm->npkgs_installed - 1;
758
759             psm->amount = 0;
760             psm->total = rpmfiFC(fi) ? rpmfiFC(fi) : 100;
761         }
762         break;
763     case PSM_PRE:
764         if (psm->goal == PKG_INSTALL) {
765             psm->scriptTag = RPMTAG_PREIN;
766             psm->sense = RPMSENSE_TRIGGERPREIN;
767             psm->countCorrection = 0;   /* XXX is this correct?!? */
768
769             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
770                 /* Run triggers in other package(s) this package sets off. */
771                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
772                 if (rc) break;
773
774                 /* Run triggers in this package other package(s) set off. */
775                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
776                 if (rc) break;
777             }
778
779             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) {
780                 rc = rpmpsmNext(psm, PSM_SCRIPT);
781                 if (rc) break;
782             }
783         }
784
785         if (psm->goal == PKG_ERASE) {
786             psm->scriptTag = RPMTAG_PREUN;
787             psm->sense = RPMSENSE_TRIGGERUN;
788             psm->countCorrection = -1;
789
790             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
791                 /* Run triggers in this package other package(s) set off. */
792                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
793                 if (rc) break;
794
795                 /* Run triggers in other package(s) this package sets off. */
796                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
797                 if (rc) break;
798             }
799
800             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN))
801                 rc = rpmpsmNext(psm, PSM_SCRIPT);
802         }
803         break;
804     case PSM_PROCESS:
805         if (psm->goal == PKG_INSTALL) {
806             int fsmrc = 0;
807
808             rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0);
809             /* make sure first progress call gets made */
810             rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0);
811
812             if (rpmfiFC(fi) > 0 && !(rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)) {
813                 FD_t payload = rpmtePayload(psm->te);
814                 if (payload == NULL) {
815                     rc = RPMRC_FAIL;
816                     break;
817                 }
818
819                 fsmrc = rpmfsmRun(FSM_PKGINSTALL, psm->ts, psm->te, psm->fi,
820                                   payload, psm, NULL, &psm->failedFile);
821
822                 rpmswAdd(rpmtsOp(psm->ts, RPMTS_OP_UNCOMPRESS),
823                          fdOp(payload, FDSTAT_READ));
824                 rpmswAdd(rpmtsOp(psm->ts, RPMTS_OP_DIGEST),
825                          fdOp(payload, FDSTAT_DIGEST));
826
827                 Fclose(payload);
828             }
829
830             /* XXX make sure progress reaches 100% */
831             rpmpsmNotify(psm, 0, psm->total);
832             rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total);
833
834             if (fsmrc) {
835                 rpmlog(RPMLOG_ERR,
836                         _("unpacking of archive failed%s%s: %s\n"),
837                         (psm->failedFile != NULL ? _(" on file ") : ""),
838                         (psm->failedFile != NULL ? psm->failedFile : ""),
839                         rpmcpioStrerror(fsmrc));
840                 rc = RPMRC_FAIL;
841
842                 /* XXX notify callback on error. */
843                 rpmtsNotify(ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0);
844                 break;
845             }
846         }
847         if (psm->goal == PKG_ERASE) {
848             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
849
850             rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0);
851             /* make sure first progress call gets made */
852             rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0);
853
854             /* XXX should't we log errors from here? */
855             if (rpmfiFC(fi) > 0 && !(rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)) {
856                 rc = rpmfsmRun(FSM_PKGERASE, psm->ts, psm->te, psm->fi,
857                                   NULL, psm, NULL, &psm->failedFile);
858             }
859
860             /* XXX make sure progress reaches 100% */
861             rpmpsmNotify(psm, 0, psm->total);
862             rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total);
863         }
864         break;
865     case PSM_POST:
866         if (psm->goal == PKG_INSTALL) {
867             rpm_time_t installTime = (rpm_time_t) time(NULL);
868             rpmfs fs = rpmteGetFileStates(psm->te);
869             rpm_count_t fc = rpmfsFC(fs);
870             rpm_fstate_t * fileStates = rpmfsGetStates(fs);
871             Header h = rpmteHeader(psm->te);
872             rpm_color_t tscolor = rpmtsColor(ts);
873
874             if (fileStates != NULL && fc > 0) {
875                 headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc);
876             }
877
878             headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1);
879             headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1);
880             headerFree(h);
881
882             /*
883              * If this package has already been installed, remove it from
884              * the database before adding the new one.
885              */
886             if (rpmteDBInstance(psm->te)) {
887                 rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
888                 if (rc) break;
889             }
890
891             rc = rpmpsmNext(psm, PSM_RPMDB_ADD);
892             if (rc) break;
893
894             psm->scriptTag = RPMTAG_POSTIN;
895             psm->sense = RPMSENSE_TRIGGERIN;
896             psm->countCorrection = 0;
897
898             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) {
899                 rc = rpmpsmNext(psm, PSM_SCRIPT);
900                 if (rc) break;
901             }
902             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
903                 /* Run triggers in other package(s) this package sets off. */
904                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
905                 if (rc) break;
906
907                 /* Run triggers in this package other package(s) set off. */
908                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
909                 if (rc) break;
910             }
911
912             rc = markReplacedFiles(psm);
913
914         }
915         if (psm->goal == PKG_ERASE) {
916
917             psm->scriptTag = RPMTAG_POSTUN;
918             psm->sense = RPMSENSE_TRIGGERPOSTUN;
919             psm->countCorrection = -1;
920
921             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) {
922                 rc = rpmpsmNext(psm, PSM_SCRIPT);
923                 if (rc) break;
924             }
925
926             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
927                 /* Run triggers in other package(s) this package sets off. */
928                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
929                 if (rc) break;
930             }
931
932             rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
933         }
934         break;
935     case PSM_UNDO:
936         break;
937     case PSM_FINI:
938         if (rc) {
939             if (psm->failedFile)
940                 rpmlog(RPMLOG_ERR,
941                         _("%s failed on file %s: %s\n"),
942                         psm->goalName, psm->failedFile, rpmcpioStrerror(rc));
943             else
944                 rpmlog(RPMLOG_ERR, _("%s failed: %s\n"),
945                         psm->goalName, rpmcpioStrerror(rc));
946
947             /* XXX notify callback on error. */
948             rpmtsNotify(ts, psm->te, RPMCALLBACK_CPIO_ERROR, 0, 0);
949         }
950
951         psm->failedFile = _free(psm->failedFile);
952
953         fi->apath = _free(fi->apath);
954         break;
955
956     case PSM_CREATE:
957         break;
958     case PSM_DESTROY:
959         break;
960     case PSM_SCRIPT:    /* Run current package scriptlets. */
961         rc = runInstScript(psm);
962         break;
963     case PSM_TRIGGERS:
964         /* Run triggers in other package(s) this package sets off. */
965         rc = runTriggers(psm);
966         break;
967     case PSM_IMMED_TRIGGERS:
968         /* Run triggers in this package other package(s) set off. */
969         rc = runImmedTriggers(psm);
970         break;
971
972     case PSM_RPMDB_ADD: {
973         Header h = rpmteHeader(psm->te);
974
975         if (!headerIsEntry(h, RPMTAG_INSTALLTID)) {
976             rpm_tid_t tid = rpmtsGetTid(ts);
977             if (tid != 0 && tid != (rpm_tid_t)-1)
978                 headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
979         }
980         
981         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
982         rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL;
983         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
984
985         if (rc == RPMRC_OK)
986             rpmteSetDBInstance(psm->te, headerGetInstance(h));
987         headerFree(h);
988     }   break;
989
990     case PSM_RPMDB_REMOVE:
991         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
992         rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(psm->te)) == 0) ?
993                                                     RPMRC_OK : RPMRC_FAIL;
994         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
995         if (rc == RPMRC_OK)
996             rpmteSetDBInstance(psm->te, 0);
997         break;
998
999     default:
1000         break;
1001    }
1002
1003     return rc;
1004 }
1005
1006 static const char * pkgGoalString(pkgGoal goal)
1007 {
1008     switch(goal) {
1009     case PKG_INSTALL:   return "  install";
1010     case PKG_ERASE:     return "    erase";
1011     case PKG_VERIFY:    return "   verify";
1012     case PKG_PRETRANS:  return " pretrans";
1013     case PKG_POSTTRANS: return "posttrans";
1014     default:            return "unknown";
1015     }
1016 }
1017
1018 rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal)
1019 {
1020     rpmpsm psm = NULL;
1021     rpmRC rc = RPMRC_FAIL;
1022
1023     /* Psm can't fail in test mode, just return early */
1024     if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
1025         return RPMRC_OK;
1026
1027     psm = rpmpsmNew(ts, te);
1028     if (rpmChrootIn() == 0) {
1029         rpmtsOpX op;
1030         psm->goal = goal;
1031         psm->goalName = pkgGoalString(goal);
1032
1033         switch (goal) {
1034         case PKG_INSTALL:
1035         case PKG_ERASE:
1036             op = (goal == PKG_INSTALL) ? RPMTS_OP_INSTALL : RPMTS_OP_ERASE;
1037             rpmswEnter(rpmtsOp(psm->ts, op), 0);
1038
1039             rc = rpmpsmNext(psm, PSM_INIT);
1040             if (!rc) rc = rpmpsmNext(psm, PSM_PRE);
1041             if (!rc) rc = rpmpsmNext(psm, PSM_PROCESS);
1042             if (!rc) rc = rpmpsmNext(psm, PSM_POST);
1043             (void) rpmpsmNext(psm, PSM_FINI);
1044
1045             rpmswExit(rpmtsOp(psm->ts, op), 0);
1046             break;
1047         case PKG_PRETRANS:
1048         case PKG_POSTTRANS:
1049         case PKG_VERIFY:
1050             psm->scriptTag = goal;
1051             rc = rpmpsmStage(psm, PSM_SCRIPT);
1052             break;
1053         default:
1054             break;
1055         }
1056         /* XXX an error here would require a full abort */
1057         (void) rpmChrootOut();
1058     }
1059     rpmpsmFree(psm);
1060     return rc;
1061 }