Sanitize python object -> tag number exception handling
[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 <rpm/rpmlib.h>         /* rpmvercmp and others */
9 #include <rpm/rpmmacro.h>
10 #include <rpm/rpmurl.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmts.h>
13 #include <rpm/rpmfileutil.h>    /* rpmMkTemp() */
14 #include <rpm/rpmdb.h>          /* XXX for db_chrootDone */
15 #include <rpm/rpmlog.h>
16 #include <rpm/rpmstring.h>
17 #include <rpm/argv.h>
18
19 #include "rpmio/rpmlua.h"
20 #include "lib/cpio.h"
21 #include "lib/fsm.h"            /* XXX CPIO_FOO/FSM_FOO constants */
22 #include "lib/psm.h"
23 #include "lib/rpmfi_internal.h" /* XXX replaced/states... */
24 #include "lib/rpmte_internal.h" /* XXX internal apis */
25 #include "lib/rpmlead.h"                /* writeLead proto */
26 #include "lib/signature.h"              /* signature constants */
27
28 #include "debug.h"
29
30 #define _PSM_DEBUG      0
31 int _psm_debug = _PSM_DEBUG;
32 int _psm_threads = 0;
33
34 /**
35  */
36 struct rpmpsm_s {
37     struct rpmsqElem sq;        /*!< Scriptlet/signal queue element. */
38
39     rpmts ts;                   /*!< transaction set */
40     rpmte te;                   /*!< current transaction element */
41     rpmfi fi;                   /*!< transaction element file info */
42     const char * stepName;
43     char * rpmio_flags;
44     char * failedFile;
45     int scriptTag;              /*!< Scriptlet data tag. */
46     int progTag;                /*!< Scriptlet interpreter tag. */
47     int npkgs_installed;        /*!< No. of installed instances. */
48     int scriptArg;              /*!< Scriptlet package arg. */
49     rpmsenseFlags sense;        /*!< One of RPMSENSE_TRIGGER{PREIN,IN,UN,POSTUN}. */
50     int countCorrection;        /*!< 0 if installing, -1 if removing. */
51     int chrootDone;             /*!< Was chroot(2) done by pkgStage? */
52     rpmCallbackType what;       /*!< Callback type. */
53     rpm_loff_t amount;          /*!< Callback amount. */
54     rpm_loff_t total;           /*!< Callback total. */
55     pkgStage goal;
56     pkgStage stage;             /*!< Current psm stage. */
57     pkgStage nstage;            /*!< Next psm stage. */
58
59     int nrefs;                  /*!< Reference count. */
60 };
61
62 int rpmVersionCompare(Header first, Header second)
63 {
64     /* Missing epoch becomes zero here, which is what we want */
65     uint32_t epochOne = headerGetNumber(first, RPMTAG_EPOCH);
66     uint32_t epochTwo = headerGetNumber(second, RPMTAG_EPOCH);
67     int rc;
68
69     if (epochOne < epochTwo)
70         return -1;
71     else if (epochOne > epochTwo)
72         return 1;
73
74     rc = rpmvercmp(headerGetString(first, RPMTAG_VERSION),
75                    headerGetString(second, RPMTAG_VERSION));
76     if (rc)
77         return rc;
78
79     return rpmvercmp(headerGetString(first, RPMTAG_RELEASE),
80                      headerGetString(second, RPMTAG_RELEASE));
81 }
82
83 /**
84  * Macros to be defined from per-header tag values.
85  * @todo Should other macros be added from header when installing a package?
86  */
87 static struct tagMacro {
88     const char *macroname;      /*!< Macro name to define. */
89     rpmTag tag;                 /*!< Header tag to use for value. */
90 } const tagMacros[] = {
91     { "name",           RPMTAG_NAME },
92     { "version",        RPMTAG_VERSION },
93     { "release",        RPMTAG_RELEASE },
94     { "epoch",          RPMTAG_EPOCH },
95     { NULL, 0 }
96 };
97
98 /**
99  * Define per-header macros.
100  * @param h             header
101  * @return              0 always
102  */
103 static void rpmInstallLoadMacros(Header h)
104 {
105     const struct tagMacro * tagm;
106
107     for (tagm = tagMacros; tagm->macroname != NULL; tagm++) {
108         struct rpmtd_s td;
109         char *body;
110         if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT))
111             continue;
112
113         switch (rpmtdType(&td)) {
114         default:
115             body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
116             addMacro(NULL, tagm->macroname, NULL, body, -1);
117             free(body);
118             break;
119         case RPM_NULL_TYPE:
120             break;
121         }
122         rpmtdFreeData(&td);
123     }
124 }
125
126 /**
127  * Mark files in database shared with this package as "replaced".
128  * @param psm           package state machine data
129  * @return              0 always
130  */
131 static rpmRC markReplacedFiles(const rpmpsm psm)
132 {
133     const rpmts ts = psm->ts;
134     rpmfs fs = rpmteGetFileStates(psm->te);
135     sharedFileInfo replaced = rpmfsGetReplaced(fs);
136     sharedFileInfo sfi;
137     rpmdbMatchIterator mi;
138     Header h;
139     int * offsets;
140     unsigned int prev;
141     int num, xx;
142
143     if (!replaced)
144         return RPMRC_OK;
145
146     num = prev = 0;
147     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
148         if (prev && prev == sfi->otherPkg)
149             continue;
150         prev = sfi->otherPkg;
151         num++;
152     }
153     if (num == 0)
154         return RPMRC_OK;
155
156     offsets = xmalloc(num * sizeof(*offsets));
157     offsets[0] = 0;
158     num = prev = 0;
159     for (sfi = replaced; sfi; sfi=rpmfsNextReplaced(fs, sfi)) {
160         if (prev && prev == sfi->otherPkg)
161             continue;
162         prev = sfi->otherPkg;
163         offsets[num++] = sfi->otherPkg;
164     }
165
166     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
167     xx = rpmdbAppendIterator(mi, offsets, num);
168     xx = rpmdbSetIteratorRewrite(mi, 1);
169
170     sfi = replaced;
171     while ((h = rpmdbNextIterator(mi)) != NULL) {
172         int modified;
173         struct rpmtd_s secStates;
174         modified = 0;
175
176         if (!headerGet(h, RPMTAG_FILESTATES, &secStates, HEADERGET_MINMEM))
177             continue;
178         
179         prev = rpmdbGetIteratorOffset(mi);
180         num = 0;
181         while (sfi && sfi->otherPkg == prev) {
182             int ix = rpmtdSetIndex(&secStates, sfi->otherFileNum);
183             assert(ix != -1);
184
185             char *state = rpmtdGetChar(&secStates);
186             if (state && *state != RPMFILE_STATE_REPLACED) {
187                 *state = RPMFILE_STATE_REPLACED;
188                 if (modified == 0) {
189                     /* Modified header will be rewritten. */
190                     modified = 1;
191                     xx = rpmdbSetIteratorModified(mi, modified);
192                 }
193                 num++;
194             }
195             sfi=rpmfsNextReplaced(fs, sfi);
196         }
197         rpmtdFreeData(&secStates);
198     }
199     mi = rpmdbFreeIterator(mi);
200     free(offsets);
201
202     return RPMRC_OK;
203 }
204
205 static int rpmlibDeps(Header h)
206 {
207     rpmds req = rpmdsInit(rpmdsNew(h, RPMTAG_REQUIRENAME, 0));
208     rpmds rpmlib = NULL;
209     rpmdsRpmlib(&rpmlib, NULL);
210     int rc = 1;
211     char *nvr = NULL;
212     while (rpmdsNext(req) >= 0) {
213         if (!(rpmdsFlags(req) & RPMSENSE_RPMLIB))
214             continue;
215         if (rpmdsSearch(rpmlib, req) < 0) {
216             if (!nvr) {
217                 nvr = headerGetAsString(h, RPMTAG_NEVRA);
218                 rpmlog(RPMLOG_ERR, _("Missing rpmlib features for %s:\n"), nvr);
219             }
220             rpmlog(RPMLOG_ERR, "\t%s\n", rpmdsDNEVR(req)+2);
221             rc = 0;
222         }
223     }
224     rpmdsFree(req);
225     rpmdsFree(rpmlib);
226     free(nvr);
227     return rc;
228 }
229
230 rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd,
231                 char ** specFilePtr, char ** cookie)
232 {
233     rpmfi fi = NULL;
234     char * specFile = NULL;
235     const char *rootdir = rpmtsRootDir(ts);
236     Header h = NULL;
237     rpmpsm psm = NULL;
238     rpmte te = NULL;
239     rpmRC rpmrc;
240     int specix = -1;
241     struct rpmtd_s filenames;
242
243     rpmtdReset(&filenames);
244     rpmrc = rpmReadPackageFile(ts, fd, RPMDBG_M("InstallSourcePackage"), &h);
245     switch (rpmrc) {
246     case RPMRC_NOTTRUSTED:
247     case RPMRC_NOKEY:
248     case RPMRC_OK:
249         break;
250     default:
251         goto exit;
252         break;
253     }
254     if (h == NULL)
255         goto exit;
256
257     rpmrc = RPMRC_FAIL; /* assume failure */
258
259     if (!headerIsSource(h)) {
260         rpmlog(RPMLOG_ERR, _("source package expected, binary found\n"));
261         goto exit;
262     }
263
264     /* src.rpm install can require specific rpmlib features, check them */
265     if (!rpmlibDeps(h))
266         goto exit;
267
268     if (headerGet(h, RPMTAG_BASENAMES, &filenames, HEADERGET_ALLOC)) {
269         struct rpmtd_s td;
270         const char *str;
271         const char *_cookie = headerGetString(h, RPMTAG_COOKIE);
272         if (cookie && _cookie) *cookie = xstrdup(_cookie);
273         
274         /* Try to find spec by file flags */
275         if (_cookie && headerGet(h, RPMTAG_FILEFLAGS, &td, HEADERGET_MINMEM)) {
276             rpmfileAttrs *flags;
277             while (specix < 0 && (flags = rpmtdNextUint32(&td))) {
278                 if (*flags & RPMFILE_SPECFILE)
279                     specix = rpmtdGetIndex(&td);
280             }
281         }
282         /* Still no spec? Look by filename. */
283         while (specix < 0 && (str = rpmtdNextString(&filenames))) {
284             if (rpmFileHasSuffix(str, ".spec")) 
285                 specix = rpmtdGetIndex(&filenames);
286         }
287     }
288
289     if (rootdir && rstreq(rootdir, "/"))
290         rootdir = NULL;
291
292     /* Macros need to be added before trying to create directories */
293     rpmInstallLoadMacros(h);
294
295     if (specix >= 0) {
296         const char *bn;
297
298         headerDel(h, RPMTAG_BASENAMES);
299         headerDel(h, RPMTAG_DIRNAMES);
300         headerDel(h, RPMTAG_DIRINDEXES);
301
302         rpmtdInit(&filenames);
303         for (int i = 0; (bn = rpmtdNextString(&filenames)); i++) {
304             int spec = (i == specix);
305             char *fn = rpmGenPath(rpmtsRootDir(ts),
306                                   spec ? "%{_specdir}" : "%{_sourcedir}", bn);
307             headerPutString(h, RPMTAG_OLDFILENAMES, fn);
308             if (spec) specFile = xstrdup(fn);
309             free(fn);
310         }
311         headerConvert(h, HEADERCONV_COMPRESSFILELIST);
312     } else {
313         rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n"));
314         goto exit;
315     };
316
317     if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) {
318         goto exit;
319     }
320
321     te = rpmtsElement(ts, 0);
322     if (te == NULL) {   /* XXX can't happen */
323         goto exit;
324     }
325     rpmteSetFd(te, fd);
326
327     rpmteSetHeader(te, h);
328     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
329     h = headerFree(h);
330
331     if (fi == NULL) {   /* XXX can't happen */
332         goto exit;
333     }
334     fi->apath = filenames.data; /* Ick */
335     rpmteSetFI(te, fi);
336     fi = rpmfiFree(fi);
337
338     if (rpmMkdirs(rpmtsRootDir(ts), "%{_topdir}:%{_sourcedir}:%{_specdir}")) {
339         goto exit;
340     }
341
342     {
343         /* set all files to be installed */
344         rpmfs fs = rpmteGetFileStates(te);
345         int i;
346         unsigned int fc = rpmfiFC(fi);
347         for (i=0; i<fc; i++) rpmfsSetAction(fs, i, FA_CREATE);
348     }
349
350     psm = rpmpsmNew(ts, te);
351     psm->goal = PSM_PKGINSTALL;
352
353         /* FIX: psm->fi->dnl should be owned. */
354     if (rpmpsmStage(psm, PSM_PROCESS) == RPMRC_OK)
355         rpmrc = RPMRC_OK;
356
357     (void) rpmpsmStage(psm, PSM_FINI);
358     psm = rpmpsmFree(psm);
359
360 exit:
361     if (specFilePtr && specFile && rpmrc == RPMRC_OK)
362         *specFilePtr = specFile;
363     else
364         specFile = _free(specFile);
365
366     if (h != NULL) h = headerFree(h);
367     if (fi != NULL) fi = rpmfiFree(fi);
368
369     /* XXX nuke the added package(s). */
370     rpmtsClean(ts);
371
372     return rpmrc;
373 }
374
375 static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
376
377 /**
378  * Return scriptlet name from tag.
379  * @param tag           scriptlet tag
380  * @return              name of scriptlet
381  */
382 static const char * tag2sln(rpmTag tag)
383 {
384     switch ((rpm_tag_t) tag) {
385     case RPMTAG_PRETRANS:       return "%pretrans";
386     case RPMTAG_TRIGGERPREIN:   return "%triggerprein";
387     case RPMTAG_PREIN:          return "%pre";
388     case RPMTAG_POSTIN:         return "%post";
389     case RPMTAG_TRIGGERIN:      return "%triggerin";
390     case RPMTAG_TRIGGERUN:      return "%triggerun";
391     case RPMTAG_PREUN:          return "%preun";
392     case RPMTAG_POSTUN:         return "%postun";
393     case RPMTAG_POSTTRANS:      return "%posttrans";
394     case RPMTAG_TRIGGERPOSTUN:  return "%triggerpostun";
395     case RPMTAG_VERIFYSCRIPT:   return "%verify";
396     default: break;
397     }
398     return "%unknownscript";
399 }
400
401 static rpmTag triggertag(rpmsenseFlags sense) 
402 {
403     rpmTag tag = RPMTAG_NOT_FOUND;
404     switch (sense) {
405     case RPMSENSE_TRIGGERIN:
406         tag = RPMTAG_TRIGGERIN;
407         break;
408     case RPMSENSE_TRIGGERUN:
409         tag = RPMTAG_TRIGGERUN;
410         break;
411     case RPMSENSE_TRIGGERPOSTUN:
412         tag = RPMTAG_TRIGGERPOSTUN;
413         break;
414     case RPMSENSE_TRIGGERPREIN:
415         tag = RPMTAG_TRIGGERPREIN;
416         break;
417     default:
418         break;
419     }
420     return tag;
421 }
422
423 /**
424  * Wait for child process to be reaped.
425  * @param psm           package state machine data
426  * @return              
427  */
428 static pid_t psmWait(rpmpsm psm)
429 {
430     const rpmts ts = psm->ts;
431     rpmtime_t msecs;
432
433     (void) rpmsqWait(&psm->sq);
434     msecs = psm->sq.op.usecs/1000;
435     (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), &psm->sq.op);
436
437     rpmlog(RPMLOG_DEBUG,
438         "%s: waitpid(%d) rc %d status %x secs %u.%03u\n",
439         psm->stepName, (unsigned)psm->sq.child,
440         (unsigned)psm->sq.reaped, psm->sq.status,
441         (unsigned)msecs/1000, (unsigned)msecs%1000);
442
443     return psm->sq.reaped;
444 }
445
446 /**
447  * Run internal Lua script.
448  */
449 static rpmRC runLuaScript(rpmpsm psm, Header h, rpmTag stag, ARGV_t argv,
450                    const char *script, int arg1, int arg2)
451 {
452     rpmRC rc = RPMRC_FAIL;
453     int warn_only = 0;
454     const rpmts ts = psm->ts;
455 #ifdef WITH_LUA
456     char *sname = NULL;
457     int rootFd = -1;
458     int xx;
459     rpmlua lua = NULL; /* Global state. */
460     rpmluav var;
461
462     rasprintf(&sname, "%s(%s)", tag2sln(stag), rpmteNEVRA(psm->te));
463
464     rpmlog(RPMLOG_DEBUG, "%s: %s running <lua> scriptlet.\n",
465            psm->stepName, sname);
466     if (!rpmtsChrootDone(ts)) {
467         const char *rootDir = rpmtsRootDir(ts);
468         xx = chdir("/");
469         rootFd = open(".", O_RDONLY, 0);
470         if (rootFd >= 0) {
471             if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
472                 xx = chroot(rootDir);
473             xx = rpmtsSetChrootDone(ts, 1);
474         }
475     }
476
477     /* Create arg variable */
478     rpmluaPushTable(lua, "arg");
479     var = rpmluavNew();
480     rpmluavSetListMode(var, 1);
481     if (argv) {
482         char **p;
483         for (p = argv; *p; p++) {
484             rpmluavSetValue(var, RPMLUAV_STRING, *p);
485             rpmluaSetVar(lua, var);
486         }
487     }
488     if (arg1 >= 0) {
489         rpmluavSetValueNum(var, arg1);
490         rpmluaSetVar(lua, var);
491     }
492     if (arg2 >= 0) {
493         rpmluavSetValueNum(var, arg2);
494         rpmluaSetVar(lua, var);
495     }
496     var = rpmluavFree(var);
497     rpmluaPop(lua);
498
499     if (rpmluaRunScript(lua, script, sname) == 0) {
500         rc = RPMRC_OK;
501     } else if ((stag != RPMTAG_PREIN && stag != RPMTAG_PREUN)) {
502         warn_only = 1;
503     }
504
505     rpmluaDelVar(lua, "arg");
506
507     if (rootFd >= 0) {
508         const char *rootDir = rpmtsRootDir(ts);
509         xx = fchdir(rootFd);
510         xx = close(rootFd);
511         if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
512             xx = chroot(".");
513         xx = rpmtsSetChrootDone(ts, 0);
514     }
515     free(sname);
516 #else
517     rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
518 #endif
519
520     if (rc != RPMRC_OK) {
521         if (warn_only) {
522             rc = RPMRC_OK;
523         }
524         (void) rpmtsNotify(ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, stag, rc);
525     }
526     return rc;
527 }
528
529 static void doScriptExec(rpmts ts, ARGV_const_t argv, rpmtd prefixes,
530                         FD_t scriptFd, FD_t out)
531 {
532     const char * rootDir;
533     int pipes[2];
534     int flag;
535     int fdno;
536     int xx;
537     int open_max;
538
539     (void) signal(SIGPIPE, SIG_DFL);
540     pipes[0] = pipes[1] = 0;
541     /* make stdin inaccessible */
542     xx = pipe(pipes);
543     xx = close(pipes[1]);
544     xx = dup2(pipes[0], STDIN_FILENO);
545     xx = close(pipes[0]);
546
547     /* XXX Force FD_CLOEXEC on all inherited fdno's. */
548     open_max = sysconf(_SC_OPEN_MAX);
549     if (open_max == -1) {
550         open_max = 1024;
551     }
552     for (fdno = 3; fdno < open_max; fdno++) {
553         flag = fcntl(fdno, F_GETFD);
554         if (flag == -1 || (flag & FD_CLOEXEC))
555             continue;
556         xx = fcntl(fdno, F_SETFD, FD_CLOEXEC);
557         /* XXX W2DO? debug msg for inheirited fdno w/o FD_CLOEXEC */
558     }
559
560     if (scriptFd != NULL) {
561         int sfdno = Fileno(scriptFd);
562         int ofdno = Fileno(out);
563         if (sfdno != STDERR_FILENO)
564             xx = dup2(sfdno, STDERR_FILENO);
565         if (ofdno != STDOUT_FILENO)
566             xx = dup2(ofdno, STDOUT_FILENO);
567         /* make sure we don't close stdin/stderr/stdout by mistake! */
568         if (ofdno > STDERR_FILENO && ofdno != sfdno)
569             xx = Fclose (out);
570         if (sfdno > STDERR_FILENO && ofdno != sfdno)
571             xx = Fclose (scriptFd);
572     }
573
574     {   char *ipath = rpmExpand("%{_install_script_path}", NULL);
575         const char *path = SCRIPT_PATH;
576
577         if (ipath && ipath[5] != '%')
578             path = ipath;
579
580         xx = setenv("PATH", path, 1);
581         ipath = _free(ipath);
582     }
583
584     if (rpmtdCount(prefixes) > 0) {
585         const char *pfx;
586         /* backwards compatibility */
587         if ((pfx = rpmtdGetString(prefixes))) {
588             xx = setenv("RPM_INSTALL_PREFIX", pfx, 1);
589         }
590
591         while ((pfx = rpmtdNextString(prefixes))) {
592             char *name = NULL;
593             rasprintf(&name, "RPM_INSTALL_PREFIX%d", rpmtdGetIndex(prefixes));
594             xx = setenv(name, pfx, 1);
595             free(name);
596         }
597     }
598
599     rootDir = rpmtsRootDir(ts);
600     if (rootDir  != NULL) {     /* XXX can't happen */
601         if (!rpmtsChrootDone(ts) &&
602             !(rootDir[0] == '/' && rootDir[1] == '\0'))
603         {
604             xx = chroot(rootDir);
605         }
606         xx = chdir("/");
607
608         /* XXX Don't mtrace into children. */
609         unsetenv("MALLOC_CHECK_");
610
611         /* Permit libselinux to do the scriptlet exec. */
612         if (rpmtsSELinuxEnabled(ts) == 1) {     
613             xx = rpm_execcon(0, argv[0], argv, environ);
614         }
615
616         if (xx == 0) {
617             xx = execv(argv[0], argv);
618         }
619     }
620     _exit(127); /* exit 127 for compatibility with bash(1) */
621 }
622
623 /**
624  * Run scriptlet with args.
625  *
626  * Run a script with an interpreter. If the interpreter is not specified,
627  * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
628  * the header will be ignored, passing instead arg1 and arg2.
629  *
630  * @param psm           package state machine data
631  * @param h             header
632  * @param stag          scriptlet section tag
633  * @param argvp         ARGV_t pointer to args from header, *argvp[0] is the 
634  *                      interpreter to use. Pointer as we might need to 
635  *                      modify via argvAdd()
636  * @param script        scriptlet from header
637  * @param arg1          no. instances of package installed after scriptlet exec
638  *                      (-1 is no arg)
639  * @param arg2          ditto, but for the target package
640  * @return              0 on success
641  */
642 static rpmRC runScript(rpmpsm psm, Header h, rpmTag stag, ARGV_t * argvp,
643                 const char * script, int arg1, int arg2)
644 {
645     const rpmts ts = psm->ts;
646     char * fn = NULL;
647     int xx;
648     FD_t scriptFd;
649     FD_t out = NULL;
650     rpmRC rc = RPMRC_FAIL; /* assume failure */
651     int warn_only = 0;
652     char *sname = NULL; 
653     struct rpmtd_s prefixes;
654
655     assert(argvp != NULL);
656     if (*argvp == NULL && script == NULL)
657         return RPMRC_OK;
658
659     if (*argvp && *argvp[0] && rstreq(*argvp[0], "<lua>")) {
660         return runLuaScript(psm, h, stag, *argvp, script, arg1, arg2);
661     }
662     rasprintf(&sname, "%s(%s)", tag2sln(stag), rpmteNEVRA(psm->te));
663
664     psm->sq.reaper = 1;
665
666     rpmlog(RPMLOG_DEBUG, "%s: %s scriptlet start\n", psm->stepName, sname);
667
668     if (argvCount(*argvp) == 0) {
669         argvAdd(argvp, "/bin/sh");
670     }
671
672     /* Try new style prefixes first, then old. Otherwise there are none.. */
673     if (!headerGet(h, RPMTAG_INSTPREFIXES, &prefixes, HEADERGET_DEFAULT)) {
674         headerGet(h, RPMTAG_INSTALLPREFIX, &prefixes, HEADERGET_DEFAULT);
675     }
676
677     if (script) {
678         const char * rootDir = rpmtsRootDir(ts);
679         FD_t fd;
680
681         fd = rpmMkTempFile((!rpmtsChrootDone(ts) ? rootDir : "/"), &fn);
682         if (fd == NULL || Ferror(fd)) {
683             rpmlog(RPMLOG_ERR, _("Couldn't create temporary file for %s: %s\n"),
684                    sname, strerror(errno));
685             goto exit;
686         }
687
688         if (rpmIsDebug() &&
689             (rstreq(*argvp[0], "/bin/sh") || rstreq(*argvp[0], "/bin/bash")))
690         {
691             static const char set_x[] = "set -x\n";
692             xx = Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd);
693         }
694
695         xx = Fwrite(script, sizeof(script[0]), strlen(script), fd);
696         xx = Fclose(fd);
697
698         {   const char * sn = fn;
699             if (!rpmtsChrootDone(ts) && rootDir != NULL &&
700                 !(rootDir[0] == '/' && rootDir[1] == '\0'))
701             {
702                 sn += strlen(rootDir)-1;
703             }
704             argvAdd(argvp, sn);
705         }
706
707         if (arg1 >= 0) {
708             argvAddNum(argvp, arg1);
709         }
710         if (arg2 >= 0) {
711             argvAddNum(argvp, arg2);
712         }
713     }
714
715     scriptFd = rpmtsScriptFd(ts);
716     if (scriptFd != NULL) {
717         if (rpmIsVerbose()) {
718             out = fdDup(Fileno(scriptFd));
719         } else {
720             out = Fopen("/dev/null", "w.fdio");
721             if (Ferror(out)) {
722                 out = fdDup(Fileno(scriptFd));
723             }
724         }
725     } else {
726         out = fdDup(STDOUT_FILENO);
727     }
728     if (out == NULL) { 
729         rpmlog(RPMLOG_ERR, _("Couldn't duplicate file descriptor: %s: %s\n"),
730                sname, strerror(errno));
731         goto exit;
732     }
733
734     xx = rpmsqFork(&psm->sq);
735     if (psm->sq.child == 0) {
736         rpmlog(RPMLOG_DEBUG, "%s: %s\texecv(%s) pid %d\n",
737                psm->stepName, sname, *argvp[0], (unsigned)getpid());
738         doScriptExec(ts, *argvp, &prefixes, scriptFd, out);
739     }
740
741     if (psm->sq.child == (pid_t)-1) {
742         rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"), sname, strerror(errno));
743         goto exit;
744     }
745
746     (void) psmWait(psm);
747
748     if (psm->sq.reaped < 0) {
749         rpmlog(RPMLOG_ERR, _("%s scriptlet failed, waitpid(%d) rc %d: %s\n"),
750                  sname, psm->sq.child, psm->sq.reaped, strerror(errno));
751     } else if (!WIFEXITED(psm->sq.status) || WEXITSTATUS(psm->sq.status)) {
752         if (WIFSIGNALED(psm->sq.status)) {
753             rpmlog(RPMLOG_ERR, _("%s scriptlet failed, signal %d\n"),
754                    sname, WTERMSIG(psm->sq.status));
755         } else {
756             /* filter out "regular" error exits from non-pre scriptlets */
757             if ((stag != RPMTAG_PREIN && stag != RPMTAG_PREUN)) {
758                 warn_only = 1;
759             }
760             rpmlog(warn_only ? RPMLOG_WARNING : RPMLOG_ERR, 
761                    _("%s scriptlet failed, exit status %d\n"),
762                    sname, WEXITSTATUS(psm->sq.status));
763         }
764     } else {
765         /* if we get this far we're clear */
766         rc = RPMRC_OK;
767     }
768
769 exit:
770     rpmtdFreeData(&prefixes);
771
772     /* notify callback for all errors, "total" abused for warning/error */
773     if (rc != RPMRC_OK) {
774         if (warn_only) {
775             rc = RPMRC_OK;
776         }
777         (void) rpmtsNotify(ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, stag, rc);
778     }
779
780     if (out)
781         xx = Fclose(out);       /* XXX dup'd STDOUT_FILENO */
782
783     if (script) {
784         if (!rpmIsDebug())
785             xx = unlink(fn);
786         fn = _free(fn);
787     }
788     free(sname);
789
790     return rc;
791 }
792
793 /**
794  * Retrieve and run scriptlet from header.
795  * @param psm           package state machine data
796  * @return              rpmRC return code
797  */
798 static rpmRC runInstScript(rpmpsm psm)
799 {
800     rpmRC rc = RPMRC_OK;
801     ARGV_t argv;
802     struct rpmtd_s script, prog;
803     const char *str;
804     Header h = rpmteHeader(psm->te);
805
806     if (h == NULL)      /* XXX can't happen */
807         return RPMRC_FAIL;
808
809     headerGet(h, psm->scriptTag, &script, HEADERGET_DEFAULT);
810     headerGet(h, psm->progTag, &prog, HEADERGET_DEFAULT);
811     if (rpmtdCount(&script) == 0 && rpmtdCount(&prog) == 0)
812         goto exit;
813
814     argv = argvNew();
815     while ((str = rpmtdNextString(&prog))) {
816         argvAdd(&argv, str);
817     }
818
819     rc = runScript(psm, h, psm->scriptTag, &argv,
820                    rpmtdGetString(&script), psm->scriptArg, -1);
821     argvFree(argv);
822
823 exit:
824     rpmtdFreeData(&script);
825     rpmtdFreeData(&prog);
826     headerFree(h);
827     return rc;
828 }
829
830 /**
831  * Execute triggers.
832  * @todo Trigger on any provides, not just package NVR.
833  * @param psm           package state machine data
834  * @param sourceH       header of trigger source
835  * @param trigH         header of triggered package
836  * @param arg2
837  * @param triggersAlreadyRun
838  * @return
839  */
840 static rpmRC handleOneTrigger(const rpmpsm psm,
841                         Header sourceH, Header trigH,
842                         int arg2, unsigned char * triggersAlreadyRun)
843 {
844     const rpmts ts = psm->ts;
845     rpmds trigger = NULL;
846     const char * sourceName = headerGetString(sourceH, RPMTAG_NAME);
847     const char * triggerName = headerGetString(trigH, RPMTAG_NAME);
848     rpmRC rc = RPMRC_OK;
849     int i;
850
851     trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0));
852     if (trigger == NULL)
853         return rc;
854
855     (void) rpmdsSetNoPromote(trigger, 1);
856
857     while ((i = rpmdsNext(trigger)) >= 0) {
858         const char * Name;
859         rpmsenseFlags Flags = rpmdsFlags(trigger);
860         struct rpmtd_s tscripts, tprogs, tindexes;
861         headerGetFlags hgflags = HEADERGET_MINMEM;
862
863         if ((Name = rpmdsN(trigger)) == NULL)
864             continue;   /* XXX can't happen */
865
866         if (!rstreq(Name, sourceName))
867             continue;
868         if (!(Flags & psm->sense))
869             continue;
870
871         /*
872          * XXX Trigger on any provided dependency, not just the package NEVR.
873          */
874         if (!rpmdsAnyMatchesDep(sourceH, trigger, 1))
875             continue;
876
877         if (!(headerGet(trigH, RPMTAG_TRIGGERINDEX, &tindexes, hgflags) &&
878               headerGet(trigH, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags) &&
879               headerGet(trigH, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags))) {
880             continue;
881         }
882
883         {   int arg1;
884             int index;
885             const char ** triggerScripts = tscripts.data;
886             const char ** triggerProgs = tprogs.data;
887             uint32_t * triggerIndices = tindexes.data;
888
889             arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName);
890             if (arg1 < 0) {
891                 /* XXX W2DO? fails as "execution of script failed" */
892                 rc = RPMRC_FAIL;
893             } else {
894                 arg1 += psm->countCorrection;
895                 index = triggerIndices[i];
896                 if (triggersAlreadyRun == NULL ||
897                     triggersAlreadyRun[index] == 0)
898                 {
899                     ARGV_t argv = argvNew();
900                     argvAdd(&argv, *(triggerProgs + index));
901                     rc = runScript(psm, trigH, triggertag(psm->sense), 
902                             &argv, triggerScripts[index],
903                             arg1, arg2);
904                     if (triggersAlreadyRun != NULL)
905                         triggersAlreadyRun[index] = 1;
906                     argvFree(argv);
907                 }
908             }
909         }
910
911         rpmtdFreeData(&tindexes);
912         rpmtdFreeData(&tscripts);
913         rpmtdFreeData(&tprogs);
914
915         /*
916          * Each target/source header pair can only result in a single
917          * script being run.
918          */
919         break;
920     }
921
922     trigger = rpmdsFree(trigger);
923
924     return rc;
925 }
926
927 /**
928  * Run trigger scripts in the database that are fired by this header.
929  * @param psm           package state machine data
930  * @return              0 on success
931  */
932 static rpmRC runTriggers(rpmpsm psm)
933 {
934     const rpmts ts = psm->ts;
935     int numPackage = -1;
936     rpmRC rc = RPMRC_OK;
937     const char * N = NULL;
938
939     if (psm->te)        /* XXX can't happen */
940         N = rpmteN(psm->te);
941     if (N)              /* XXX can't happen */
942         numPackage = rpmdbCountPackages(rpmtsGetRdb(ts), N)
943                                 + psm->countCorrection;
944     if (numPackage < 0)
945         return RPMRC_NOTFOUND;
946
947     {   Header triggeredH;
948         Header h = rpmteHeader(psm->te);
949         rpmdbMatchIterator mi;
950         int countCorrection = psm->countCorrection;
951
952         psm->countCorrection = 0;
953         mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, N, 0);
954         while((triggeredH = rpmdbNextIterator(mi)) != NULL)
955             rc |= handleOneTrigger(psm, h, triggeredH, numPackage, NULL);
956         mi = rpmdbFreeIterator(mi);
957         psm->countCorrection = countCorrection;
958         headerFree(h);
959     }
960
961     return rc;
962 }
963
964 /**
965  * Run triggers from this header that are fired by headers in the database.
966  * @param psm           package state machine data
967  * @return              0 on success
968  */
969 static rpmRC runImmedTriggers(rpmpsm psm)
970 {
971     const rpmts ts = psm->ts;
972     unsigned char * triggersRun;
973     rpmRC rc = RPMRC_OK;
974     struct rpmtd_s tnames, tindexes;
975     Header h = rpmteHeader(psm->te);
976
977     if (!(headerGet(h, RPMTAG_TRIGGERNAME, &tnames, HEADERGET_MINMEM) &&
978           headerGet(h, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM))) {
979         goto exit;
980     }
981
982     triggersRun = xcalloc(rpmtdCount(&tindexes), sizeof(*triggersRun));
983     {   Header sourceH = NULL;
984         const char *trigName;
985         rpm_count_t *triggerIndices = tindexes.data;
986
987         while ((trigName = rpmtdNextString(&tnames))) {
988             rpmdbMatchIterator mi;
989             int i = rpmtdGetIndex(&tnames);
990
991             if (triggersRun[triggerIndices[i]] != 0) continue;
992         
993             mi = rpmtsInitIterator(ts, RPMTAG_NAME, trigName, 0);
994
995             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
996                 rc |= handleOneTrigger(psm, sourceH, h,
997                                 rpmdbGetIteratorCount(mi),
998                                 triggersRun);
999             }
1000
1001             mi = rpmdbFreeIterator(mi);
1002         }
1003     }
1004     rpmtdFreeData(&tnames);
1005     rpmtdFreeData(&tindexes);
1006     free(triggersRun);
1007
1008 exit:
1009     headerFree(h);
1010     return rc;
1011 }
1012
1013 static const char * pkgStageString(pkgStage a)
1014 {
1015     switch(a) {
1016     case PSM_UNKNOWN:           return "unknown";
1017
1018     case PSM_PKGINSTALL:        return "  install";
1019     case PSM_PKGERASE:          return "    erase";
1020     case PSM_PKGCOMMIT:         return "   commit";
1021
1022     case PSM_INIT:              return "init";
1023     case PSM_PRE:               return "pre";
1024     case PSM_PROCESS:           return "process";
1025     case PSM_POST:              return "post";
1026     case PSM_UNDO:              return "undo";
1027     case PSM_FINI:              return "fini";
1028
1029     case PSM_CREATE:            return "create";
1030     case PSM_NOTIFY:            return "notify";
1031     case PSM_DESTROY:           return "destroy";
1032     case PSM_COMMIT:            return "commit";
1033
1034     case PSM_CHROOT_IN:         return "chrootin";
1035     case PSM_CHROOT_OUT:        return "chrootout";
1036     case PSM_SCRIPT:            return "script";
1037     case PSM_TRIGGERS:          return "triggers";
1038     case PSM_IMMED_TRIGGERS:    return "immedtriggers";
1039
1040     case PSM_RPMIO_FLAGS:       return "rpmioflags";
1041
1042     case PSM_RPMDB_ADD:         return "rpmdbadd";
1043     case PSM_RPMDB_REMOVE:      return "rpmdbremove";
1044
1045     default:                    return "???";
1046     }
1047 }
1048
1049 rpmpsm rpmpsmUnlink(rpmpsm psm, const char * msg)
1050 {
1051     if (psm == NULL) return NULL;
1052 if (_psm_debug && msg != NULL)
1053 fprintf(stderr, "--> psm %p -- %d: %s\n", psm, psm->nrefs, msg);
1054     psm->nrefs--;
1055     return NULL;
1056 }
1057
1058 rpmpsm rpmpsmLink(rpmpsm psm, const char * msg)
1059 {
1060     if (psm == NULL) return NULL;
1061     psm->nrefs++;
1062
1063 if (_psm_debug && msg != NULL)
1064 fprintf(stderr, "--> psm %p ++ %d %s\n", psm, psm->nrefs, msg);
1065
1066     return psm;
1067 }
1068
1069 rpmpsm rpmpsmFree(rpmpsm psm)
1070 {
1071     if (psm == NULL)
1072         return NULL;
1073
1074     if (psm->nrefs > 1)
1075         return rpmpsmUnlink(psm, RPMDBG_M("rpmpsmFree"));
1076
1077     psm->fi = rpmfiFree(psm->fi);
1078 #ifdef  NOTYET
1079     psm->te = rpmteFree(psm->te);
1080 #else
1081     psm->te = NULL;
1082 #endif
1083     psm->ts = rpmtsFree(psm->ts);
1084
1085     (void) rpmpsmUnlink(psm, RPMDBG_M("rpmpsmFree"));
1086
1087     free(psm->rpmio_flags);
1088     memset(psm, 0, sizeof(*psm));               /* XXX trash and burn */
1089     psm = _free(psm);
1090
1091     return NULL;
1092 }
1093
1094 rpmRC rpmpsmScriptStage(rpmpsm psm, rpmTag scriptTag, rpmTag progTag)
1095 {
1096     assert(psm != NULL);
1097     psm->scriptTag = scriptTag;
1098     psm->progTag = progTag;
1099     if (scriptTag == RPMTAG_VERIFYSCRIPT) {
1100         psm->stepName = "verify";
1101     }
1102     return rpmpsmStage(psm, PSM_SCRIPT);
1103 }
1104
1105 rpmpsm rpmpsmNew(rpmts ts, rpmte te)
1106 {
1107     rpmpsm psm = xcalloc(1, sizeof(*psm));
1108
1109     if (ts)     psm->ts = rpmtsLink(ts, RPMDBG_M("rpmpsmNew"));
1110     if (te) {
1111 #ifdef  NOTYET
1112         psm->te = rpmteLink(te, RPMDBG_M("rpmpsmNew")); 
1113 #else
1114         psm->te = te;
1115 #endif
1116         psm->fi = rpmfiLink(rpmteFI(te), RPMDBG_M("rpmpsmNew"));
1117     }
1118
1119     return rpmpsmLink(psm, RPMDBG_M("rpmpsmNew"));
1120 }
1121
1122 static void * rpmpsmThread(void * arg)
1123 {
1124     rpmpsm psm = arg;
1125     return ((void *) rpmpsmStage(psm, psm->nstage));
1126 }
1127
1128 static int rpmpsmNext(rpmpsm psm, pkgStage nstage)
1129 {
1130     psm->nstage = nstage;
1131     if (_psm_threads)
1132         return rpmsqJoin( rpmsqThread(rpmpsmThread, psm) );
1133     return rpmpsmStage(psm, psm->nstage);
1134 }
1135
1136 rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage)
1137 {
1138     const rpmts ts = psm->ts;
1139     rpm_color_t tscolor = rpmtsColor(ts);
1140     rpmfi fi = psm->fi;
1141     rpmRC rc = RPMRC_OK;
1142     int saveerrno;
1143     int xx;
1144
1145     switch (stage) {
1146     case PSM_UNKNOWN:
1147         break;
1148     case PSM_INIT:
1149         rpmlog(RPMLOG_DEBUG, "%s: %s has %d files, test = %d\n",
1150                 psm->stepName, rpmteNEVR(psm->te),
1151                 rpmfiFC(fi), (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST));
1152
1153         /*
1154          * When we run scripts, we pass an argument which is the number of
1155          * versions of this package that will be installed when we are
1156          * finished.
1157          */
1158         psm->npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(psm->te));
1159         if (psm->npkgs_installed < 0) {
1160             rc = RPMRC_FAIL;
1161             break;
1162         }
1163
1164         if (psm->goal == PSM_PKGINSTALL) {
1165             rpmdbMatchIterator mi;
1166             Header oh;
1167             int fc = rpmfiFC(fi);
1168
1169             psm->scriptArg = psm->npkgs_installed + 1;
1170
1171             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(psm->te), 0);
1172             xx = rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP,
1173                         rpmteE(psm->te));
1174             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP,
1175                         rpmteV(psm->te));
1176             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP,
1177                         rpmteR(psm->te));
1178             if (tscolor) {
1179                 xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP,
1180                         rpmteA(psm->te));
1181                 xx = rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP,
1182                         rpmteO(psm->te));
1183             }
1184
1185             while ((oh = rpmdbNextIterator(mi)) != NULL) {
1186                 rpmteSetDBInstance(psm->te, rpmdbGetIteratorOffset(mi));
1187                 oh = NULL;
1188                 break;
1189             }
1190             mi = rpmdbFreeIterator(mi);
1191             rc = RPMRC_OK;
1192
1193             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
1194             if (fc <= 0)                                break;
1195         
1196             /*
1197              * Old format relocatable packages need the entire default
1198              * prefix stripped to form the cpio list, while all other packages
1199              * need the leading / stripped.
1200              */
1201             {   struct rpmtd_s filenames;
1202                 rpmTag ftag = RPMTAG_FILENAMES;
1203                 Header h = rpmteHeader(psm->te);
1204                 const char *p = headerGetString(h, RPMTAG_DEFAULTPREFIX);
1205
1206                 fi->striplen = p ? strlen(p) + 1 : 1;
1207         
1208                 if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) {
1209                     ftag = RPMTAG_ORIGFILENAMES;
1210                 }
1211                 headerGet(h, ftag, &filenames, HEADERGET_EXT);
1212                 fi->apath = filenames.data; /* Ick.. */
1213                 headerFree(h);
1214             }
1215         
1216             rc = RPMRC_OK;
1217         }
1218         if (psm->goal == PSM_PKGERASE) {
1219             psm->scriptArg = psm->npkgs_installed - 1;
1220         }
1221         break;
1222     case PSM_PRE:
1223         if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)        break;
1224
1225 /* XXX insure that trigger index is opened before entering chroot. */
1226 #ifdef  NOTYET
1227  { static int oneshot = 0;
1228    dbiIndex dbi;
1229    if (!oneshot) {
1230      dbi = dbiOpen(rpmtsGetRdb(ts), RPMTAG_TRIGGERNAME, 0);
1231      oneshot++;
1232    }
1233  }
1234 #endif
1235
1236         /* Change root directory if requested and not already done. */
1237         rc = rpmpsmNext(psm, PSM_CHROOT_IN);
1238         if (rc) break;
1239
1240         if (psm->goal == PSM_PKGINSTALL) {
1241             psm->scriptTag = RPMTAG_PREIN;
1242             psm->progTag = RPMTAG_PREINPROG;
1243             psm->sense = RPMSENSE_TRIGGERPREIN;
1244             psm->countCorrection = 0;   /* XXX is this correct?!? */
1245
1246             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
1247                 /* Run triggers in other package(s) this package sets off. */
1248                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
1249                 if (rc) break;
1250
1251                 /* Run triggers in this package other package(s) set off. */
1252                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
1253                 if (rc) break;
1254             }
1255
1256             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) {
1257                 rc = rpmpsmNext(psm, PSM_SCRIPT);
1258                 if (rc != RPMRC_OK) {
1259                     rpmlog(RPMLOG_ERR,
1260                         _("%s: %s scriptlet failed (%d), skipping %s\n"),
1261                         psm->stepName, tag2sln(psm->scriptTag), rc,
1262                         rpmteNEVR(psm->te));
1263                     break;
1264                 }
1265             }
1266         }
1267
1268         if (psm->goal == PSM_PKGERASE) {
1269             psm->scriptTag = RPMTAG_PREUN;
1270             psm->progTag = RPMTAG_PREUNPROG;
1271             psm->sense = RPMSENSE_TRIGGERUN;
1272             psm->countCorrection = -1;
1273
1274             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) {
1275                 /* Run triggers in this package other package(s) set off. */
1276                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
1277                 if (rc) break;
1278
1279                 /* Run triggers in other package(s) this package sets off. */
1280                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
1281                 if (rc) break;
1282             }
1283
1284             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN))
1285                 rc = rpmpsmNext(psm, PSM_SCRIPT);
1286         }
1287         break;
1288     case PSM_PROCESS:
1289         if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)        break;
1290
1291         if (psm->goal == PSM_PKGINSTALL) {
1292             FD_t payload = NULL;
1293
1294             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
1295
1296             /* XXX Synthesize callbacks for packages with no files. */
1297             if (rpmfiFC(fi) <= 0) {
1298                 void * ptr;
1299                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_START, 0, 100);
1300                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_INST_PROGRESS, 100, 100);
1301                 break;
1302             }
1303
1304             /* Retrieve type of payload compression. */
1305             rc = rpmpsmNext(psm, PSM_RPMIO_FLAGS);
1306
1307             if (rpmteFd(psm->te) == NULL) {     /* XXX can't happen */
1308                 rc = RPMRC_FAIL;
1309                 break;
1310             }
1311
1312             payload = Fdopen(fdDup(Fileno(rpmteFd(psm->te))), psm->rpmio_flags);
1313             if (payload == NULL) {      /* XXX can't happen */
1314                 rc = RPMRC_FAIL;
1315                 break;
1316             }
1317
1318             rc = fsmSetup(rpmfiFSM(fi), FSM_PKGINSTALL, ts, psm->te, fi,
1319                         payload, NULL, &psm->failedFile);
1320             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS),
1321                         fdOp(payload, FDSTAT_READ));
1322             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST),
1323                         fdOp(payload, FDSTAT_DIGEST));
1324             xx = fsmTeardown(rpmfiFSM(fi));
1325
1326             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
1327             xx = Fclose(payload);
1328             errno = saveerrno; /* XXX FIXME: Fclose with libio destroys errno */
1329
1330             if (!rc)
1331                 rc = rpmpsmNext(psm, PSM_COMMIT);
1332
1333             /* XXX make sure progress is closed out */
1334             psm->what = RPMCALLBACK_INST_PROGRESS;
1335             psm->amount = (fi->archiveSize ? fi->archiveSize : 100);
1336             psm->total = psm->amount;
1337             xx = rpmpsmNext(psm, PSM_NOTIFY);
1338
1339             if (rc) {
1340                 rpmlog(RPMLOG_ERR,
1341                         _("unpacking of archive failed%s%s: %s\n"),
1342                         (psm->failedFile != NULL ? _(" on file ") : ""),
1343                         (psm->failedFile != NULL ? psm->failedFile : ""),
1344                         cpioStrerror(rc));
1345                 rc = RPMRC_FAIL;
1346
1347                 /* XXX notify callback on error. */
1348                 psm->what = RPMCALLBACK_UNPACK_ERROR;
1349                 psm->amount = 0;
1350                 psm->total = 0;
1351                 xx = rpmpsmNext(psm, PSM_NOTIFY);
1352
1353                 break;
1354             }
1355         }
1356         if (psm->goal == PSM_PKGERASE) {
1357             int fc = rpmfiFC(fi);
1358
1359             if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)  break;
1360             if (rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY)       break;
1361
1362             /* XXX Synthesize callbacks for packages with no files. */
1363             if (rpmfiFC(fi) <= 0) {
1364                 void * ptr;
1365                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_START, 0, 100);
1366                 ptr = rpmtsNotify(ts, psm->te, RPMCALLBACK_UNINST_STOP, 0, 100);
1367                 break;
1368             }
1369
1370             psm->what = RPMCALLBACK_UNINST_START;
1371             psm->amount = fc;           /* XXX W2DO? looks wrong. */
1372             psm->total = fc;
1373             xx = rpmpsmNext(psm, PSM_NOTIFY);
1374
1375             rc = fsmSetup(rpmfiFSM(fi), FSM_PKGERASE, ts, psm->te, fi,
1376                         NULL, NULL, &psm->failedFile);
1377             xx = fsmTeardown(rpmfiFSM(fi));
1378
1379             psm->what = RPMCALLBACK_UNINST_STOP;
1380             psm->amount = 0;            /* XXX W2DO? looks wrong. */
1381             psm->total = fc;
1382             xx = rpmpsmNext(psm, PSM_NOTIFY);
1383
1384         }
1385         break;
1386     case PSM_POST:
1387         if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)        break;
1388
1389         if (psm->goal == PSM_PKGINSTALL) {
1390             rpm_time_t installTime = (rpm_time_t) time(NULL);
1391             rpmfs fs = rpmteGetFileStates(psm->te);
1392             rpm_count_t fc = rpmfsFC(fs);
1393             rpm_fstate_t * fileStates = rpmfsGetStates(fs);
1394             Header h = rpmteHeader(psm->te);
1395
1396             if (fileStates != NULL && fc > 0) {
1397                 headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc);
1398             }
1399
1400             headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1);
1401             headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1);
1402             headerFree(h);
1403
1404             /*
1405              * If this package has already been installed, remove it from
1406              * the database before adding the new one.
1407              */
1408             if (rpmteDBInstance(psm->te) && 
1409                         !(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY)) {
1410                 rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
1411                 if (rc) break;
1412             }
1413
1414             rc = rpmpsmNext(psm, PSM_RPMDB_ADD);
1415             if (rc) break;
1416
1417             psm->scriptTag = RPMTAG_POSTIN;
1418             psm->progTag = RPMTAG_POSTINPROG;
1419             psm->sense = RPMSENSE_TRIGGERIN;
1420             psm->countCorrection = 0;
1421
1422             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) {
1423                 rc = rpmpsmNext(psm, PSM_SCRIPT);
1424                 if (rc) break;
1425             }
1426             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) {
1427                 /* Run triggers in other package(s) this package sets off. */
1428                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
1429                 if (rc) break;
1430
1431                 /* Run triggers in this package other package(s) set off. */
1432                 rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS);
1433                 if (rc) break;
1434             }
1435
1436             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY))
1437                 rc = markReplacedFiles(psm);
1438
1439         }
1440         if (psm->goal == PSM_PKGERASE) {
1441
1442             psm->scriptTag = RPMTAG_POSTUN;
1443             psm->progTag = RPMTAG_POSTUNPROG;
1444             psm->sense = RPMSENSE_TRIGGERPOSTUN;
1445             psm->countCorrection = -1;
1446
1447             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) {
1448                 rc = rpmpsmNext(psm, PSM_SCRIPT);
1449                 if (rc) break;
1450             }
1451
1452             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
1453                 /* Run triggers in other package(s) this package sets off. */
1454                 rc = rpmpsmNext(psm, PSM_TRIGGERS);
1455                 if (rc) break;
1456             }
1457
1458             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY))
1459                 rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE);
1460         }
1461
1462         /* Restore root directory if changed. */
1463         xx = rpmpsmNext(psm, PSM_CHROOT_OUT);
1464         break;
1465     case PSM_UNDO:
1466         break;
1467     case PSM_FINI:
1468         /* Restore root directory if changed. */
1469         xx = rpmpsmNext(psm, PSM_CHROOT_OUT);
1470
1471         if (rc) {
1472             if (psm->failedFile)
1473                 rpmlog(RPMLOG_ERR,
1474                         _("%s failed on file %s: %s\n"),
1475                         psm->stepName, psm->failedFile, cpioStrerror(rc));
1476             else
1477                 rpmlog(RPMLOG_ERR, _("%s failed: %s\n"),
1478                         psm->stepName, cpioStrerror(rc));
1479
1480             /* XXX notify callback on error. */
1481             psm->what = RPMCALLBACK_CPIO_ERROR;
1482             psm->amount = 0;
1483             psm->total = 0;
1484             xx = rpmpsmNext(psm, PSM_NOTIFY);
1485         }
1486
1487         if (psm->goal == PSM_PKGERASE) {
1488             if (psm->te != NULL) 
1489                 rpmteSetHeader(psm->te, NULL);
1490         }
1491         psm->failedFile = _free(psm->failedFile);
1492
1493         fi->apath = _free(fi->apath);
1494         break;
1495
1496     case PSM_PKGINSTALL:
1497     case PSM_PKGERASE:
1498         psm->goal = stage;
1499         psm->stepName = pkgStageString(stage);
1500
1501         rc = rpmpsmNext(psm, PSM_INIT);
1502         if (!rc) rc = rpmpsmNext(psm, PSM_PRE);
1503         if (!rc) rc = rpmpsmNext(psm, PSM_PROCESS);
1504         if (!rc) rc = rpmpsmNext(psm, PSM_POST);
1505         xx = rpmpsmNext(psm, PSM_FINI);
1506         break;
1507     case PSM_PKGCOMMIT:
1508         break;
1509
1510     case PSM_CREATE:
1511         break;
1512     case PSM_NOTIFY:
1513     {   void * ptr;
1514 /* FIX: psm->te may be NULL */
1515         ptr = rpmtsNotify(ts, psm->te, psm->what, psm->amount, psm->total);
1516     }   break;
1517     case PSM_DESTROY:
1518         break;
1519     case PSM_COMMIT:
1520         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_PKGCOMMIT)) break;
1521         if (rpmtsFlags(ts) & RPMTRANS_FLAG_APPLYONLY) break;
1522
1523         rc = fsmSetup(rpmfiFSM(fi), FSM_PKGCOMMIT, ts, psm->te, fi,
1524                         NULL, NULL, &psm->failedFile);
1525         xx = fsmTeardown(rpmfiFSM(fi));
1526         break;
1527
1528     case PSM_CHROOT_IN:
1529     {   const char * rootDir = rpmtsRootDir(ts);
1530         /* Change root directory if requested and not already done. */
1531         if (rootDir != NULL && !(rootDir[0] == '/' && rootDir[1] == '\0')
1532          && !rpmtsChrootDone(ts) && !psm->chrootDone)
1533         {
1534             xx = chdir("/");
1535             if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
1536                 if (chroot(rootDir) == -1) {
1537                     rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
1538                     return -1;
1539                 }
1540             psm->chrootDone = 1;
1541             (void) rpmtsSetChrootDone(ts, 1);
1542         }
1543     }   break;
1544     case PSM_CHROOT_OUT:
1545         /* Restore root directory if changed. */
1546         if (psm->chrootDone) {
1547             const char * rootDir = rpmtsRootDir(ts);
1548             const char * currDir = rpmtsCurrDir(ts);
1549             if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
1550                 rc = chroot(".");
1551             psm->chrootDone = 0;
1552             (void) rpmtsSetChrootDone(ts, 0);
1553             if (currDir != NULL)        /* XXX can't happen */
1554                 xx = chdir(currDir);
1555         }
1556         break;
1557     case PSM_SCRIPT:    /* Run current package scriptlets. */
1558         rc = runInstScript(psm);
1559         break;
1560     case PSM_TRIGGERS:
1561         /* Run triggers in other package(s) this package sets off. */
1562         rc = runTriggers(psm);
1563         break;
1564     case PSM_IMMED_TRIGGERS:
1565         /* Run triggers in this package other package(s) set off. */
1566         rc = runImmedTriggers(psm);
1567         break;
1568
1569     case PSM_RPMIO_FLAGS:
1570     {   Header h = rpmteHeader(psm->te);
1571         const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
1572         psm->rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
1573         headerFree(h);
1574         rc = RPMRC_OK;
1575     }   break;
1576
1577     case PSM_RPMDB_ADD: {
1578         Header h;
1579         if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)        break;
1580         h = rpmteHeader(psm->te);
1581         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
1582         if (!(rpmtsVSFlags(ts) & RPMVSF_NOHDRCHK))
1583             rc = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h,
1584                                 ts, headerCheck);
1585         else
1586             rc = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h,
1587                                 NULL, NULL);
1588         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0);
1589
1590         if (rc == RPMRC_OK)
1591             rpmteSetDBInstance(psm->te, headerGetInstance(h));
1592         headerFree(h);
1593     }   break;
1594
1595     case PSM_RPMDB_REMOVE:
1596         if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)        break;
1597         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
1598         rc = rpmdbRemove(rpmtsGetRdb(ts), rpmtsGetTid(ts),
1599                                 rpmteDBInstance(psm->te), NULL, NULL);
1600         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0);
1601         if (rc == RPMRC_OK)
1602             rpmteSetDBInstance(psm->te, 0);
1603         break;
1604
1605     default:
1606         break;
1607    }
1608
1609     return rc;
1610 }