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