Change all internal uses of rpmtsInitIterator() to use DBI tags
[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_PKGCOMMIT       = 10,
40
41     PSM_CREATE          = 17,
42     PSM_NOTIFY          = 22,
43     PSM_DESTROY         = 23,
44     PSM_COMMIT          = 25,
45
46     PSM_SCRIPT          = 53,
47     PSM_TRIGGERS        = 54,
48     PSM_IMMED_TRIGGERS  = 55,
49
50     PSM_RPMDB_ADD       = 98,
51     PSM_RPMDB_REMOVE    = 99
52
53 } pkgStage;
54
55 typedef struct rpmpsm_s {
56     rpmts ts;                   /*!< transaction set */
57     rpmte te;                   /*!< current transaction element */
58     rpmfi fi;                   /*!< transaction element file info */
59     const char * goalName;
60     char * failedFile;
61     rpmTagVal scriptTag;        /*!< Scriptlet data tag. */
62     int npkgs_installed;        /*!< No. of installed instances. */
63     int scriptArg;              /*!< Scriptlet package arg. */
64     rpmsenseFlags sense;        /*!< One of RPMSENSE_TRIGGER{PREIN,IN,UN,POSTUN}. */
65     int countCorrection;        /*!< 0 if installing, -1 if removing. */
66     rpmCallbackType what;       /*!< Callback type. */
67     rpm_loff_t amount;          /*!< Callback amount. */
68     rpm_loff_t total;           /*!< Callback total. */
69     pkgGoal goal;
70     pkgStage stage;             /*!< Current psm stage. */
71     pkgStage nstage;            /*!< Next psm stage. */
72
73     int nrefs;                  /*!< Reference count. */
74 } * rpmpsm;
75
76 static rpmpsm rpmpsmNew(rpmts ts, rpmte te);
77 static rpmpsm rpmpsmFree(rpmpsm psm);
78 static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage);
79
80 /**
81  * Macros to be defined from per-header tag values.
82  * @todo Should other macros be added from header when installing a package?
83  */
84 static struct tagMacro {
85     const char *macroname;      /*!< Macro name to define. */
86     rpmTag tag;                 /*!< Header tag to use for value. */
87 } const tagMacros[] = {
88     { "name",           RPMTAG_NAME },
89     { "version",        RPMTAG_VERSION },
90     { "release",        RPMTAG_RELEASE },
91     { "epoch",          RPMTAG_EPOCH },
92     { NULL, 0 }
93 };
94
95 /**
96  * Define per-header macros.
97  * @param h             header
98  * @return              0 always
99  */
100 static void rpmInstallLoadMacros(Header h)
101 {
102     const struct tagMacro * tagm;
103
104     for (tagm = tagMacros; tagm->macroname != NULL; tagm++) {
105         struct rpmtd_s td;
106         char *body;
107         if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT))
108             continue;
109
110         switch (rpmtdType(&td)) {
111         default:
112             body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
113             addMacro(NULL, tagm->macroname, NULL, body, -1);
114             free(body);
115             break;
116         case RPM_NULL_TYPE:
117             break;
118         }
119         rpmtdFreeData(&td);
120     }
121 }
122
123 /**
124  * Mark files in database shared with this package as "replaced".
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, xx;
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     xx = rpmdbAppendIterator(mi, offsets, num);
165     xx = 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 != RPMFILE_STATE_REPLACED) {
184                 *state = RPMFILE_STATE_REPLACED;
185                 if (modified == 0) {
186                     /* Modified header will be rewritten. */
187                     modified = 1;
188                     xx = rpmdbSetIteratorModified(mi, modified);
189                 }
190                 num++;
191             }
192             sfi=rpmfsNextReplaced(fs, sfi);
193         }
194         rpmtdFreeData(&secStates);
195     }
196     mi = 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, RPMDBG_M("InstallSourcePackage"), &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) {   /* XXX can't happen */
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     psm = rpmpsmFree(psm);
356
357 exit:
358     if (specFilePtr && specFile && rpmrc == RPMRC_OK)
359         *specFilePtr = specFile;
360     else
361         specFile = _free(specFile);
362
363     if (h != NULL) h = headerFree(h);
364     if (fi != NULL) fi = 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 rc = RPMRC_OK;
413     int warn_only = (script->tag != RPMTAG_PREIN &&
414                      script->tag != RPMTAG_PREUN &&
415                      script->tag != RPMTAG_VERIFYSCRIPT);
416     int selinux = !(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_NOCONTEXTS);
417
418     rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0);
419     rc = rpmScriptRun(script, arg1, arg2, rpmtsScriptFd(psm->ts),
420                       prefixes, warn_only, selinux);
421     rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0);
422
423     /* 
424      * Notify callback for all errors. "total" abused for warning/error,
425      * rc only reflects whether the condition prevented install/erase 
426      * (which is only happens with %prein and %preun scriptlets) or not.
427      */
428     if (rc != RPMRC_OK) {
429         if (warn_only) {
430             rc = RPMRC_OK;
431         }
432         rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, script->tag, rc);
433     }
434
435     return rc;
436 }
437
438 static rpmRC runInstScript(rpmpsm psm)
439 {
440     rpmRC rc = RPMRC_OK;
441     struct rpmtd_s pfx;
442     Header h = rpmteHeader(psm->te);
443     rpmScript script = rpmScriptFromTag(h, psm->scriptTag);
444
445     if (script) {
446         headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
447         rc = runScript(psm, pfx.data, script, psm->scriptArg, -1);
448         rpmtdFreeData(&pfx);
449     }
450
451     rpmScriptFree(script);
452     headerFree(h);
453
454     return rc;
455 }
456
457 /**
458  * Execute triggers.
459  * @todo Trigger on any provides, not just package NVR.
460  * @param psm           package state machine data
461  * @param sourceH       header of trigger source
462  * @param trigH         header of triggered package
463  * @param arg2
464  * @param triggersAlreadyRun
465  * @return
466  */
467 static rpmRC handleOneTrigger(const rpmpsm psm,
468                         Header sourceH, Header trigH,
469                         int arg2, unsigned char * triggersAlreadyRun)
470 {
471     const rpmts ts = psm->ts;
472     rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0));
473     struct rpmtd_s pfx;
474     const char * sourceName = headerGetString(sourceH, RPMTAG_NAME);
475     const char * triggerName = headerGetString(trigH, RPMTAG_NAME);
476     rpmRC rc = RPMRC_OK;
477     int i;
478
479     if (trigger == NULL)
480         return rc;
481
482     headerGet(trigH, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV);
483     (void) rpmdsSetNoPromote(trigger, 1);
484
485     while ((i = rpmdsNext(trigger)) >= 0) {
486         struct rpmtd_s tscripts, tprogs, tindexes, tflags;
487         headerGetFlags hgflags = HEADERGET_MINMEM;
488
489         if (!(rpmdsFlags(trigger) & psm->sense))
490             continue;
491
492         if (!rstreq(rpmdsN(trigger), sourceName))
493             continue;
494
495         /* XXX Trigger on any provided dependency, not just the package NEVR */
496         if (!rpmdsAnyMatchesDep(sourceH, trigger, 1))
497             continue;
498
499         /* XXX FIXME: this leaks memory if scripts or progs retrieve fails */
500         if (!(headerGet(trigH, RPMTAG_TRIGGERINDEX, &tindexes, hgflags) &&
501               headerGet(trigH, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags) &&
502               headerGet(trigH, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags))) {
503             continue;
504         } else {
505             int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName);
506             char ** triggerScripts = tscripts.data;
507             char ** triggerProgs = tprogs.data;
508             uint32_t * triggerIndices = tindexes.data;
509             uint32_t * triggerFlags = NULL;
510             uint32_t ix = triggerIndices[i];
511
512             headerGet(trigH, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags);
513             triggerFlags = tflags.data;
514
515             if (arg1 < 0) {
516                 /* XXX W2DO? fails as "execution of script failed" */
517                 rc = RPMRC_FAIL;
518             } else {
519                 arg1 += psm->countCorrection;
520
521                 if (triggersAlreadyRun == NULL || triggersAlreadyRun[ix] == 0) {
522                     /* XXX TODO add rpmScript API to handle this, ugh */
523                     char *macro = NULL;
524                     char *qformat = NULL;
525                     char *args[2] = { triggerProgs[ix], NULL };
526                     struct rpmScript_s script = {
527                         .tag = triggertag(psm->sense),
528                         .body = triggerScripts[ix],
529                         .flags = triggerFlags ? triggerFlags[ix] : 0,
530                         .args = args
531                     };
532
533                     if (script.body && (script.flags & RPMSCRIPT_EXPAND)) {
534                         macro = rpmExpand(script.body, NULL);
535                         script.body = macro;
536                     }
537                     if (script.body && (script.flags & RPMSCRIPT_QFORMAT)) {
538                         qformat = headerFormat(trigH, script.body, NULL);
539                         script.body = qformat;
540                     }
541
542                     rc = runScript(psm, pfx.data, &script, arg1, arg2);
543                     if (triggersAlreadyRun != NULL)
544                         triggersAlreadyRun[ix] = 1;
545                     free(macro);
546                     free(qformat);
547                 }
548             }
549         }
550
551         rpmtdFreeData(&tindexes);
552         rpmtdFreeData(&tscripts);
553         rpmtdFreeData(&tprogs);
554
555         /*
556          * Each target/source header pair can only result in a single
557          * script being run.
558          */
559         break;
560     }
561
562     rpmtdFreeData(&pfx);
563     trigger = rpmdsFree(trigger);
564
565     return rc;
566 }
567
568 /**
569  * Run trigger scripts in the database that are fired by this header.
570  * @param psm           package state machine data
571  * @return              0 on success
572  */
573 static rpmRC runTriggers(rpmpsm psm)
574 {
575     const rpmts ts = psm->ts;
576     int numPackage = -1;
577     const char * N = NULL;
578     int nerrors = 0;
579
580     if (psm->te)        /* XXX can't happen */
581         N = rpmteN(psm->te);
582     if (N)              /* XXX can't happen */
583         numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
584                                 + psm->countCorrection;
585     if (numPackage < 0)
586         return RPMRC_NOTFOUND;
587
588     {   Header triggeredH;
589         Header h = rpmteHeader(psm->te);
590         rpmdbMatchIterator mi;
591         int countCorrection = psm->countCorrection;
592
593         psm->countCorrection = 0;
594         mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0);
595         while((triggeredH = rpmdbNextIterator(mi)) != NULL)
596             nerrors += handleOneTrigger(psm, h, triggeredH, numPackage, NULL);
597         mi = rpmdbFreeIterator(mi);
598         psm->countCorrection = countCorrection;
599         headerFree(h);
600     }
601
602     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
603 }
604
605 /**
606  * Run triggers from this header that are fired by headers in the database.
607  * @param psm           package state machine data
608  * @return              0 on success
609  */
610 static rpmRC runImmedTriggers(rpmpsm psm)
611 {
612     const rpmts ts = psm->ts;
613     unsigned char * triggersRun;
614     struct rpmtd_s tnames, tindexes;
615     Header h = rpmteHeader(psm->te);
616     int nerrors = 0;
617
618     if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) &&
619           headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) {
620         goto exit;
621     }
622
623     triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun));
624     {   Header sourceH = NULL;
625         const char *trigName;
626         rpm_count_t *triggerIndices = tindexes.data;
627
628         while ((trigName = rpmtdNextString(&tnames))) {
629             rpmdbMatchIterator mi;
630             int i = rpmtdGetIndex(&tnames);
631
632             if (triggersRun[triggerIndices[i]] != 0) continue;
633         
634             mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0);
635
636             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
637                 nerrors += handleOneTrigger(psm, sourceH, h,
638                                 rpmdbGetIteratorCount(mi),
639                                 triggersRun);
640             }
641
642             mi = rpmdbFreeIterator(mi);
643         }
644     }
645     rpmtdFreeData(&tnames);
646     rpmtdFreeData(&tindexes);
647     free(triggersRun);
648
649 exit:
650     headerFree(h);
651     return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL;
652 }
653
654 static rpmpsm rpmpsmFree(rpmpsm psm)
655 {
656     if (psm == NULL)
657         return NULL;
658
659     psm->fi = rpmfiFree(psm->fi);
660 #ifdef  NOTYET
661     psm->te = rpmteFree(psm->te);
662 #else
663     psm->te = NULL;
664 #endif
665     psm->ts = rpmtsFree(psm->ts);
666
667     memset(psm, 0, sizeof(*psm));               /* XXX trash and burn */
668     psm = _free(psm);
669
670     return NULL;
671 }
672
673 static rpmpsm rpmpsmNew(rpmts ts, rpmte te)
674 {
675     rpmpsm psm = xcalloc(1, sizeof(*psm));
676
677     if (ts)     psm->ts = rpmtsLink(ts);
678     if (te) {
679 #ifdef  NOTYET
680         psm->te = rpmteLink(te, RPMDBG_M("rpmpsmNew")); 
681 #else
682         psm->te = te;
683 #endif
684         psm->fi = rpmfiLink(rpmteFI(te));
685     }
686
687     return psm;
688 }
689
690 static rpmRC rpmpsmNext(rpmpsm psm, pkgStage nstage)
691 {
692     psm->nstage = nstage;
693     return rpmpsmStage(psm, psm->nstage);
694 }
695
696 static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage)
697 {
698     const rpmts ts = psm->ts;
699     rpm_color_t tscolor = rpmtsColor(ts);
700     rpmfi fi = psm->fi;
701     rpmRC rc = RPMRC_OK;
702     int saveerrno;
703     int xx;
704
705     switch (stage) {
706     case PSM_UNKNOWN:
707         break;
708     case PSM_INIT:
709         rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n",
710                 psm->goalName, rpmteNEVR(psm->te), rpmfiFC(fi));
711
712         /*
713          * When we run scripts, we pass an argument which is the number of
714          * versions of this package that will be installed when we are
715          * finished.
716          */
717         psm->npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(psm->te));
718         if (psm->npkgs_installed < 0) {
719             rc = RPMRC_FAIL;
720             break;
721         }
722
723         if (psm->goal == PKG_INSTALL) {
724             rpmdbMatchIterator mi;
725             Header oh;
726
727             psm->scriptArg = psm->npkgs_installed + 1;
728
729             mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(psm->te), 0);
730             xx = rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP,
731                         rpmteE(psm->te));
732             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP,
733                         rpmteV(psm->te));
734             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP,
735                         rpmteR(psm->te));
736             if (tscolor) {
737                 xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP,
738                         rpmteA(psm->te));
739                 xx = rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP,
740                         rpmteO(psm->te));
741             }
742
743             while ((oh = rpmdbNextIterator(mi)) != NULL) {
744                 rpmteSetDBInstance(psm->te, rpmdbGetIteratorOffset(mi));
745                 oh = NULL;
746                 break;
747             }
748             mi = rpmdbFreeIterator(mi);
749             rc = RPMRC_OK;
750
751             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
752         
753             if (rpmfiFC(fi) > 0) {
754                 struct rpmtd_s filenames;
755                 rpmTag ftag = RPMTAG_FILENAMES;
756                 Header h = rpmteHeader(psm->te);
757         
758                 if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) {
759                     ftag = RPMTAG_ORIGFILENAMES;
760                 }
761                 headerGet(h, ftag, &filenames, HEADERGET_EXT);
762                 fi->apath = filenames.data; /* Ick.. */
763                 headerFree(h);
764             }
765         }
766         if (psm->goal == PKG_ERASE) {
767             psm->scriptArg = psm->npkgs_installed - 1;
768         }
769         break;
770     case PSM_PRE:
771         if (psm->goal == PKG_INSTALL) {
772             psm->scriptTag = RPMTAG_PREIN;
773             psm->sense = RPMSENSE_TRIGGERPREIN;
774             psm->countCorrection = 0;   /* XXX is this correct?!? */
775
776             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
777                 /* Run triggers in other package(s) this package sets off. */
778                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
779                 if (rc) break;
780
781                 /* Run triggers in this package other package(s) set off. */
782                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
783                 if (rc) break;
784             }
785
786             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) {
787                 rc = rpmpsmNext(psm, PSM_SCRIPT);
788                 if (rc) break;
789             }
790         }
791
792         if (psm->goal == PKG_ERASE) {
793             psm->scriptTag = RPMTAG_PREUN;
794             psm->sense = RPMSENSE_TRIGGERUN;
795             psm->countCorrection = -1;
796
797             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
798                 /* Run triggers in this package other package(s) set off. */
799                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
800                 if (rc) break;
801
802                 /* Run triggers in other package(s) this package sets off. */
803                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
804                 if (rc) break;
805             }
806
807             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN))
808                 rc = rpmpsmNext(psm, PSM_SCRIPT);
809         }
810         break;
811     case PSM_PROCESS:
812         if (psm->goal == PKG_INSTALL) {
813             FD_t payload = NULL;
814
815             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
816
817             /* XXX Synthesize callbacks for packages with no files. */
818             if (rpmfiFC(fi) <= 0) {
819                 void * ptr;
820                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_START, 0, 100);
821                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_PROGRESS, 100, 100);
822                 break;
823             }
824
825             payload = rpmtePayload(psm->te);
826             if (payload == NULL) {
827                 rc = RPMRC_FAIL;
828                 break;
829             }
830
831             rc = fsmSetup(rpmfiFSM(fi), FSM_PKGINSTALL, ts, psm->te, fi,
832                         payload, NULL, &psm->failedFile);
833             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS),
834                         fdOp(payload, FDSTAT_READ));
835             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST),
836                         fdOp(payload, FDSTAT_DIGEST));
837             xx = fsmTeardown(rpmfiFSM(fi));
838
839             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
840             xx = Fclose(payload);
841             errno = saveerrno; /* XXX FIXME: Fclose with libio destroys errno */
842
843             if (!rc)
844                 rc = rpmpsmNext(psm, PSM_COMMIT);
845
846             /* XXX make sure progress is closed out */
847             psm->what = RPMCALLBACK_INST_PROGRESS;
848             psm->amount = (fi->archiveSize ? fi->archiveSize : 100);
849             psm->total = psm->amount;
850             xx = rpmpsmNext(psm, PSM_NOTIFY);
851
852             if (rc) {
853                 rpmlog(RPMLOG_ERR,
854                         _("unpacking of archive failed%s%s: %s\n"),
855                         (psm->failedFile != NULL ? _(" on file ") : ""),
856                         (psm->failedFile != NULL ? psm->failedFile : ""),
857                         cpioStrerror(rc));
858                 rc = RPMRC_FAIL;
859
860                 /* XXX notify callback on error. */
861                 psm->what = RPMCALLBACK_UNPACK_ERROR;
862                 psm->amount = 0;
863                 psm->total = 0;
864                 xx = rpmpsmNext(psm, PSM_NOTIFY);
865
866                 break;
867             }
868         }
869         if (psm->goal == PKG_ERASE) {
870             int fc = rpmfiFC(fi);
871
872             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
873             if (rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY)       break;
874
875             /* XXX Synthesize callbacks for packages with no files. */
876             if (rpmfiFC(fi) <= 0) {
877                 void * ptr;
878                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_START, 0, 100);
879                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_STOP, 0, 100);
880                 break;
881             }
882
883             psm->what = RPMCALLBACK_UNINST_START;
884             psm->amount = fc;           /* XXX W2DO? looks wrong. */
885             psm->total = fc;
886             xx = rpmpsmNext(psm, PSM_NOTIFY);
887
888             rc = fsmSetup(rpmfiFSM(fi), FSM_PKGERASE, ts, psm->te, fi,
889                         NULL, NULL, &psm->failedFile);
890             xx = fsmTeardown(rpmfiFSM(fi));
891
892             psm->what = RPMCALLBACK_UNINST_STOP;
893             psm->amount = 0;            /* XXX W2DO? looks wrong. */
894             psm->total = fc;
895             xx = rpmpsmNext(psm, PSM_NOTIFY);
896
897         }
898         break;
899     case PSM_POST:
900         if (psm->goal == PKG_INSTALL) {
901             rpm_time_t installTime = (rpm_time_t) time(NULL);
902             rpmfs fs = rpmteGetFileStates(psm->te);
903             rpm_count_t fc = rpmfsFC(fs);
904             rpm_fstate_t * fileStates = rpmfsGetStates(fs);
905             Header h = rpmteHeader(psm->te);
906
907             if (fileStates != NULL && fc > 0) {
908                 headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc);
909             }
910
911             headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1);
912             headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1);
913             headerFree(h);
914
915             /*
916              * If this package has already been installed, remove it from
917              * the database before adding the new one.
918              */
919             if (rpmteDBInstance(psm->te) && 
920                         !(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY)) {
921                 rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
922                 if (rc) break;
923             }
924
925             rc = rpmpsmNext(psm, PSM_RPMDB_ADD);
926             if (rc) break;
927
928             psm->scriptTag = RPMTAG_POSTIN;
929             psm->sense = RPMSENSE_TRIGGERIN;
930             psm->countCorrection = 0;
931
932             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) {
933                 rc = rpmpsmNext(psm, PSM_SCRIPT);
934                 if (rc) break;
935             }
936             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
937                 /* Run triggers in other package(s) this package sets off. */
938                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
939                 if (rc) break;
940
941                 /* Run triggers in this package other package(s) set off. */
942                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
943                 if (rc) break;
944             }
945
946             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY))
947                 rc = markReplacedFiles(psm);
948
949         }
950         if (psm->goal == PKG_ERASE) {
951
952             psm->scriptTag = RPMTAG_POSTUN;
953             psm->sense = RPMSENSE_TRIGGERPOSTUN;
954             psm->countCorrection = -1;
955
956             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) {
957                 rc = rpmpsmNext(psm, PSM_SCRIPT);
958                 if (rc) break;
959             }
960
961             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
962                 /* Run triggers in other package(s) this package sets off. */
963                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
964                 if (rc) break;
965             }
966
967             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY))
968                 rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
969         }
970         break;
971     case PSM_UNDO:
972         break;
973     case PSM_FINI:
974         if (rc) {
975             if (psm->failedFile)
976                 rpmlog(RPMLOG_ERR,
977                         _("%s failed on file %s: %s\n"),
978                         psm->goalName, psm->failedFile, cpioStrerror(rc));
979             else
980                 rpmlog(RPMLOG_ERR, _("%s failed: %s\n"),
981                         psm->goalName, cpioStrerror(rc));
982
983             /* XXX notify callback on error. */
984             psm->what = RPMCALLBACK_CPIO_ERROR;
985             psm->amount = 0;
986             psm->total = 0;
987             xx = rpmpsmNext(psm, PSM_NOTIFY);
988         }
989
990         psm->failedFile = _free(psm->failedFile);
991
992         fi->apath = _free(fi->apath);
993         break;
994
995     case PSM_PKGCOMMIT:
996         break;
997
998     case PSM_CREATE:
999         break;
1000     case PSM_NOTIFY:
1001     {   void * ptr;
1002 /* FIX: psm->te may be NULL */
1003         ptr = rpmtsNotify(ts, psm->te, psm->what, psm->amount, psm->total);
1004     }   break;
1005     case PSM_DESTROY:
1006         break;
1007     case PSM_COMMIT:
1008         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_PKGCOMMIT)) break;
1009         if (rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY) break;
1010
1011         rc = fsmSetup(rpmfiFSM(fi), FSM_PKGCOMMIT, ts, psm->te, fi,
1012                         NULL, NULL, &psm->failedFile);
1013         xx = fsmTeardown(rpmfiFSM(fi));
1014         break;
1015
1016     case PSM_SCRIPT:    /* Run current package scriptlets. */
1017         rc = runInstScript(psm);
1018         break;
1019     case PSM_TRIGGERS:
1020         /* Run triggers in other package(s) this package sets off. */
1021         rc = runTriggers(psm);
1022         break;
1023     case PSM_IMMED_TRIGGERS:
1024         /* Run triggers in this package other package(s) set off. */
1025         rc = runImmedTriggers(psm);
1026         break;
1027
1028     case PSM_RPMDB_ADD: {
1029         Header h = rpmteHeader(psm->te);
1030
1031         if (!headerIsEntry(h, RPMTAG_INSTALLTID)) {
1032             rpm_tid_t tid = rpmtsGetTid(ts);
1033             if (tid != 0 && tid != (rpm_tid_t)-1)
1034                 headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
1035         }
1036         
1037         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
1038         rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL;
1039         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
1040
1041         if (rc == RPMRC_OK)
1042             rpmteSetDBInstance(psm->te, headerGetInstance(h));
1043         headerFree(h);
1044     }   break;
1045
1046     case PSM_RPMDB_REMOVE:
1047         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
1048         rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(psm->te)) == 0) ?
1049                                                     RPMRC_OK : RPMRC_FAIL;
1050         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
1051         if (rc == RPMRC_OK)
1052             rpmteSetDBInstance(psm->te, 0);
1053         break;
1054
1055     default:
1056         break;
1057    }
1058
1059     return rc;
1060 }
1061
1062 static const char * pkgGoalString(pkgGoal goal)
1063 {
1064     switch(goal) {
1065     case PKG_INSTALL:   return "  install";
1066     case PKG_ERASE:     return "    erase";
1067     case PKG_VERIFY:    return "   verify";
1068     case PKG_PRETRANS:  return " pretrans";
1069     case PKG_POSTTRANS: return "posttrans";
1070     default:            return "unknown";
1071     }
1072 }
1073
1074 rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal)
1075 {
1076     rpmpsm psm = NULL;
1077     rpmRC rc = RPMRC_FAIL;
1078
1079     /* Psm can't fail in test mode, just return early */
1080     if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
1081         return RPMRC_OK;
1082
1083     psm = rpmpsmNew(ts, te);
1084     if (rpmChrootIn() == 0) {
1085         rpmtsOpX op;
1086         psm->goal = goal;
1087         psm->goalName = pkgGoalString(goal);
1088
1089         switch (goal) {
1090         case PKG_INSTALL:
1091         case PKG_ERASE:
1092             op = (goal == PKG_INSTALL) ? RPMTS_OP_INSTALL : RPMTS_OP_ERASE;
1093             rpmswEnter(rpmtsOp(psm->ts, op), 0);
1094
1095             rc = rpmpsmNext(psm, PSM_INIT);
1096             if (!rc) rc = rpmpsmNext(psm, PSM_PRE);
1097             if (!rc) rc = rpmpsmNext(psm, PSM_PROCESS);
1098             if (!rc) rc = rpmpsmNext(psm, PSM_POST);
1099             (void) rpmpsmNext(psm, PSM_FINI);
1100
1101             rpmswExit(rpmtsOp(psm->ts, op), 0);
1102             break;
1103         case PKG_PRETRANS:
1104         case PKG_POSTTRANS:
1105         case PKG_VERIFY:
1106             psm->scriptTag = goal;
1107             rc = rpmpsmStage(psm, PSM_SCRIPT);
1108             break;
1109         default:
1110             break;
1111         }
1112         /* XXX an error here would require a full abort */
1113         (void) rpmChrootOut();
1114     }
1115     rpmpsmFree(psm);
1116     return rc;
1117 }