Doxygen annotations for config files.
[tools/librpm-tizen.git] / lib / psm.c
1 /** \ingroup rpmtrans 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 <rpmlib.h>
9 #include <rpmmacro.h>
10 #include <rpmurl.h>
11
12 #include "psm.h"
13 #include "rpmlead.h"            /* writeLead proto */
14 #include "signature.h"          /* signature constants */
15 #include "misc.h"
16 #include "debug.h"
17
18 /*@access Header @*/            /* compared with NULL */
19 /*@access rpmTransactionSet @*/ /* compared with NULL */
20 /*@access rpmdbMatchIterator @*/ /* compared with NULL */
21 /*@access TFI_t @*/             /* compared with NULL */
22 /*@access FSM_t @*/             /* compared with NULL */
23 /*@access PSM_t @*/             /* compared with NULL */
24 /*@access FD_t @*/              /* compared with NULL */
25
26 extern int _fsm_debug;
27
28 /**
29  * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
30  * @param this          memory to free
31  * @retval              NULL always
32  */
33 static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
34     if (this)   free((void *)this);
35     return NULL;
36 }
37
38 int rpmVersionCompare(Header first, Header second)
39 {
40     const char * one, * two;
41     int_32 * epochOne, * epochTwo;
42     int rc;
43
44     if (!headerGetEntry(first, RPMTAG_EPOCH, NULL, (void **) &epochOne, NULL))
45         epochOne = NULL;
46     if (!headerGetEntry(second, RPMTAG_EPOCH, NULL, (void **) &epochTwo,
47                         NULL))
48         epochTwo = NULL;
49
50     if (epochOne && !epochTwo)
51         return 1;
52     else if (!epochOne && epochTwo)
53         return -1;
54     else if (epochOne && epochTwo) {
55         if (*epochOne < *epochTwo)
56             return -1;
57         else if (*epochOne > *epochTwo)
58             return 1;
59     }
60
61     headerGetEntry(first, RPMTAG_VERSION, NULL, (void **) &one, NULL);
62     headerGetEntry(second, RPMTAG_VERSION, NULL, (void **) &two, NULL);
63
64     rc = rpmvercmp(one, two);
65     if (rc)
66         return rc;
67
68     headerGetEntry(first, RPMTAG_RELEASE, NULL, (void **) &one, NULL);
69     headerGetEntry(second, RPMTAG_RELEASE, NULL, (void **) &two, NULL);
70
71     return rpmvercmp(one, two);
72 }
73
74 void loadFi(Header h, TFI_t fi)
75 {
76     HGE_t hge;
77     HFD_t hfd;
78     uint_32 * uip;
79     int len;
80     int rc;
81     int i;
82     
83     if (fi->fsm == NULL)
84         fi->fsm = newFSM();
85
86     /* XXX avoid gcc noise on pointer (4th arg) cast(s) */
87     hge = (fi->type == TR_ADDED)
88         ? (HGE_t) headerGetEntryMinMemory : (HGE_t) headerGetEntry;
89     fi->hge = hge;
90
91     fi->hfd = hfd = headerFreeData;
92
93     if (h && fi->h == NULL)     fi->h = headerLink(h);
94
95     /* Duplicate name-version-release so that headers can be free'd. */
96     hge(fi->h, RPMTAG_NAME, NULL, (void **) &fi->name, NULL);
97     fi->name = xstrdup(fi->name);
98     hge(fi->h, RPMTAG_VERSION, NULL, (void **) &fi->version, NULL);
99     fi->version = xstrdup(fi->version);
100     hge(fi->h, RPMTAG_RELEASE, NULL, (void **) &fi->release, NULL);
101     fi->release = xstrdup(fi->release);
102
103     /* -1 means not found */
104     rc = hge(fi->h, RPMTAG_EPOCH, NULL, (void **) &uip, NULL);
105     fi->epoch = (rc ? *uip : -1);
106     /* 0 means unknown */
107     rc = hge(fi->h, RPMTAG_ARCHIVESIZE, NULL, (void **) &uip, NULL);
108     fi->archiveSize = (rc ? *uip : 0);
109
110     if (!hge(fi->h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc)) {
111         fi->dc = 0;
112         fi->fc = 0;
113         return;
114     }
115
116     hge(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
117     hge(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
118     hge(fi->h, RPMTAG_FILEMODES, NULL, (void **) &fi->fmodes, NULL);
119     hge(fi->h, RPMTAG_FILEFLAGS, NULL, (void **) &fi->fflags, NULL);
120     hge(fi->h, RPMTAG_FILESIZES, NULL, (void **) &fi->fsizes, NULL);
121     hge(fi->h, RPMTAG_FILESTATES, NULL, (void **) &fi->fstates, NULL);
122
123     fi->action = FA_UNKNOWN;
124     fi->flags = 0;
125
126     /* actions is initialized earlier for added packages */
127     if (fi->actions == NULL)
128         fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
129
130     switch (fi->type) {
131     case TR_ADDED:
132         fi->mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
133         hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
134         hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
135         hge(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fi->flangs, NULL);
136         hge(fi->h, RPMTAG_FILEMTIMES, NULL, (void **) &fi->fmtimes, NULL);
137
138         /* 0 makes for noops */
139         fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
140
141         break;
142     case TR_REMOVED:
143         fi->mapflags = CPIO_MAP_ABSOLUTE | CPIO_MAP_ADDDOT | CPIO_MAP_PATH | CPIO_MAP_MODE;
144         hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
145         hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
146         fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
147                                 fi->fsizes, fi->fc * sizeof(*fi->fsizes));
148         fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
149                                 fi->fflags, fi->fc * sizeof(*fi->fflags));
150         fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
151                                 fi->fmodes, fi->fc * sizeof(*fi->fmodes));
152         /* XXX there's a tedious segfault here for some version(s) of rpm */
153         if (fi->fstates)
154             fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
155                                 fi->fstates, fi->fc * sizeof(*fi->fstates));
156         else
157             fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
158         fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
159                                 fi->dil, fi->fc * sizeof(*fi->dil));
160         headerFree(fi->h);
161         fi->h = NULL;
162         break;
163     }
164
165     fi->dnlmax = -1;
166     for (i = 0; i < fi->dc; i++) {
167         if ((len = strlen(fi->dnl[i])) > fi->dnlmax)
168             fi->dnlmax = len;
169     }
170
171     fi->bnlmax = -1;
172     for (i = 0; i < fi->fc; i++) {
173         if ((len = strlen(fi->bnl[i])) > fi->bnlmax)
174             fi->bnlmax = len;
175     }
176
177     fi->dperms = 0755;
178     fi->fperms = 0644;
179
180     return;
181 }
182
183 void freeFi(TFI_t fi)
184 {
185     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
186
187     fi->name = _free(fi->name);
188     fi->version = _free(fi->version);
189     fi->release = _free(fi->release);
190     fi->actions = _free(fi->actions);
191     fi->replacedSizes = _free(fi->replacedSizes);
192     fi->replaced = _free(fi->replaced);
193
194     fi->bnl = hfd(fi->bnl, -1);
195     fi->dnl = hfd(fi->dnl, -1);
196     fi->obnl = hfd(fi->obnl, -1);
197     fi->odnl = hfd(fi->odnl, -1);
198     fi->flinks = hfd(fi->flinks, -1);
199     fi->fmd5s = hfd(fi->fmd5s, -1);
200     fi->fuser = hfd(fi->fuser, -1);
201     fi->fgroup = hfd(fi->fgroup, -1);
202     fi->flangs = hfd(fi->flangs, -1);
203
204     fi->apath = _free(fi->apath);
205     fi->fuids = _free(fi->fuids);
206     fi->fgids = _free(fi->fgids);
207     fi->fmapflags = _free(fi->fmapflags);
208
209     fi->fsm = freeFSM(fi->fsm);
210
211     switch (fi->type) {
212     case TR_ADDED:
213             break;
214     case TR_REMOVED:
215         fi->fsizes = hfd(fi->fsizes, -1);
216         fi->fflags = hfd(fi->fflags, -1);
217         fi->fmodes = hfd(fi->fmodes, -1);
218         fi->fstates = hfd(fi->fstates, -1);
219         fi->dil = hfd(fi->dil, -1);
220         break;
221     }
222     if (fi->h) {
223         headerFree(fi->h); fi->h = NULL;
224     }
225 }
226
227 /*@observer@*/ const char *const fiTypeString(TFI_t fi) {
228     switch(fi->type) {
229     case TR_ADDED:      return " install";
230     case TR_REMOVED:    return "   erase";
231     default:            return "???";
232     }
233     /*@noteached@*/
234 }
235
236 /**
237  * Macros to be defined from per-header tag values.
238  * @todo Should other macros be added from header when installing a package?
239  */
240 static struct tagMacro {
241         const char *    macroname;      /*!< Macro name to define. */
242         int             tag;            /*!< Header tag to use for value. */
243 } tagMacros[] = {
244         { "name",       RPMTAG_NAME },
245         { "version",    RPMTAG_VERSION },
246         { "release",    RPMTAG_RELEASE },
247 #if 0
248         { "epoch",      RPMTAG_EPOCH },
249 #endif
250         { NULL, 0 }
251 };
252
253 /**
254  * Define per-header macros.
255  * @param h             header
256  * @return              0 always
257  */
258 static int rpmInstallLoadMacros(TFI_t fi, Header h)
259 {
260     HGE_t hge = (HGE_t)fi->hge;
261     struct tagMacro *tagm;
262     union {
263         const char * ptr;
264         int_32 * i32p;
265     } body;
266     char numbuf[32];
267     int_32 type;
268
269     for (tagm = tagMacros; tagm->macroname != NULL; tagm++) {
270         if (!hge(h, tagm->tag, &type, (void **) &body, NULL))
271             continue;
272         switch (type) {
273         case RPM_INT32_TYPE:
274             sprintf(numbuf, "%d", *body.i32p);
275             addMacro(NULL, tagm->macroname, NULL, numbuf, -1);
276             break;
277         case RPM_STRING_TYPE:
278             addMacro(NULL, tagm->macroname, NULL, body.ptr, -1);
279             break;
280         }
281     }
282     return 0;
283 }
284
285 /**
286  * Copy file data from h to newH.
287  * @param h             header from
288  * @param newH          header to
289  * @param actions       array of file dispositions
290  * @return              0 on success, 1 on failure
291  */
292 static int mergeFiles(TFI_t fi, Header h, Header newH)
293 {
294     HGE_t hge = (HGE_t)fi->hge;
295     HFD_t hfd = fi->hfd;
296     fileAction * actions = fi->actions;
297     int i, j, k, fc;
298     int_32 type = 0;
299     int_32 count = 0;
300     int_32 dirNamesCount, dirCount;
301     void * data, * newdata;
302     int_32 * dirIndexes, * newDirIndexes;
303     uint_32 * fileSizes, fileSize;
304     const char ** dirNames;
305     const char ** newDirNames;
306     static int_32 mergeTags[] = {
307         RPMTAG_FILESIZES,
308         RPMTAG_FILESTATES,
309         RPMTAG_FILEMODES,
310         RPMTAG_FILERDEVS,
311         RPMTAG_FILEMTIMES,
312         RPMTAG_FILEMD5S,
313         RPMTAG_FILELINKTOS,
314         RPMTAG_FILEFLAGS,
315         RPMTAG_FILEUSERNAME,
316         RPMTAG_FILEGROUPNAME,
317         RPMTAG_FILEVERIFYFLAGS,
318         RPMTAG_FILEDEVICES,
319         RPMTAG_FILEINODES,
320         RPMTAG_FILELANGS,
321         RPMTAG_BASENAMES,
322         0,
323     };
324     static int_32 requireTags[] = {
325         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
326         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
327         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS
328     };
329
330     hge(h, RPMTAG_SIZE, NULL, (void **) &fileSizes, NULL);
331     fileSize = *fileSizes;
332     hge(newH, RPMTAG_FILESIZES, NULL, (void **) &fileSizes, &count);
333     for (i = 0, fc = 0; i < count; i++)
334         if (actions[i] != FA_SKIPMULTILIB) {
335             fc++;
336             fileSize += fileSizes[i];
337         }
338     headerModifyEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE, &fileSize, 1);
339
340     for (i = 0; mergeTags[i]; i++) {
341         if (!hge(newH, mergeTags[i], &type, (void **) &data, &count))
342             continue;
343         switch (type) {
344         case RPM_CHAR_TYPE:
345         case RPM_INT8_TYPE:
346             newdata = xmalloc(fc * sizeof(int_8));
347             for (j = 0, k = 0; j < count; j++)
348                 if (actions[j] != FA_SKIPMULTILIB)
349                         ((int_8 *) newdata)[k++] = ((int_8 *) data)[j];
350             headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
351             free (newdata);
352             break;
353         case RPM_INT16_TYPE:
354             newdata = xmalloc(fc * sizeof(int_16));
355             for (j = 0, k = 0; j < count; j++)
356                 if (actions[j] != FA_SKIPMULTILIB)
357                     ((int_16 *) newdata)[k++] = ((int_16 *) data)[j];
358             headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
359             free (newdata);
360             break;
361         case RPM_INT32_TYPE:
362             newdata = xmalloc(fc * sizeof(int_32));
363             for (j = 0, k = 0; j < count; j++)
364                 if (actions[j] != FA_SKIPMULTILIB)
365                     ((int_32 *) newdata)[k++] = ((int_32 *) data)[j];
366             headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
367             free (newdata);
368             break;
369         case RPM_STRING_ARRAY_TYPE:
370             newdata = xmalloc(fc * sizeof(char *));
371             for (j = 0, k = 0; j < count; j++)
372                 if (actions[j] != FA_SKIPMULTILIB)
373                     ((char **) newdata)[k++] = ((char **) data)[j];
374             headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
375             free (newdata);
376             break;
377         default:
378             rpmError(RPMERR_DATATYPE, _("Data type %d not supported\n"),
379                         (int) type);
380             return 1;
381             /*@notreached@*/ break;
382         }
383         data = hfd(data, type);
384     }
385     hge(newH, RPMTAG_DIRINDEXES, NULL, (void **) &newDirIndexes, &count);
386     hge(newH, RPMTAG_DIRNAMES, NULL, (void **) &newDirNames, NULL);
387     hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
388     hge(h, RPMTAG_DIRNAMES, NULL, (void **) &data, &dirNamesCount);
389
390     dirNames = xcalloc(dirNamesCount + fc, sizeof(char *));
391     for (i = 0; i < dirNamesCount; i++)
392         dirNames[i] = ((char **) data)[i];
393     dirCount = dirNamesCount;
394     newdata = xmalloc(fc * sizeof(int_32));
395     for (i = 0, k = 0; i < count; i++) {
396         if (actions[i] == FA_SKIPMULTILIB)
397             continue;
398         for (j = 0; j < dirCount; j++)
399             if (!strcmp(dirNames[j], newDirNames[newDirIndexes[i]]))
400                 break;
401         if (j == dirCount)
402             dirNames[dirCount++] = newDirNames[newDirIndexes[i]];
403         ((int_32 *) newdata)[k++] = j;
404     }
405     headerAddOrAppendEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, newdata, fc);
406     if (dirCount > dirNamesCount)
407         headerAddOrAppendEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
408                                dirNames + dirNamesCount,
409                                dirCount - dirNamesCount);
410     data = hfd(data, -1);
411     newDirNames = hfd(newDirNames, -1);
412     free (newdata);
413     free (dirNames);
414
415     for (i = 0; i < 9; i += 3) {
416         const char **Names, **EVR, **newNames, **newEVR;
417         int nnt, nvt, rnt;
418         uint_32 *Flags, *newFlags;
419         int Count = 0, newCount = 0;
420
421         if (!hge(newH, requireTags[i], &nnt, (void **) &newNames, &newCount))
422             continue;
423
424         hge(newH, requireTags[i+1], &nvt, (void **) &newEVR, NULL);
425         hge(newH, requireTags[i+2], NULL, (void **) &newFlags, NULL);
426         if (hge(h, requireTags[i], &rnt, (void **) &Names, &Count))
427         {
428             hge(h, requireTags[i+1], NULL, (void **) &EVR, NULL);
429             hge(h, requireTags[i+2], NULL, (void **) &Flags, NULL);
430             for (j = 0; j < newCount; j++)
431                 for (k = 0; k < Count; k++)
432                     if (!strcmp (newNames[j], Names[k])
433                         && !strcmp (newEVR[j], EVR[k])
434                         && (newFlags[j] & RPMSENSE_SENSEMASK) ==
435                            (Flags[k] & RPMSENSE_SENSEMASK))
436                     {
437                         newNames[j] = NULL;
438                         break;
439                     }
440         }
441         for (j = 0, k = 0; j < newCount; j++) {
442             if (!newNames[j] || !isDependsMULTILIB(newFlags[j]))
443                 continue;
444             if (j != k) {
445                 newNames[k] = newNames[j];
446                 newEVR[k] = newEVR[j];
447                 newFlags[k] = newFlags[j];
448             }
449             k++;
450         }
451         if (k) {
452             headerAddOrAppendEntry(h, requireTags[i],
453                                        RPM_STRING_ARRAY_TYPE, newNames, k);
454             headerAddOrAppendEntry(h, requireTags[i+1],
455                                        RPM_STRING_ARRAY_TYPE, newEVR, k);
456             headerAddOrAppendEntry(h, requireTags[i+2], RPM_INT32_TYPE,
457                                        newFlags, k);
458         }
459         newNames = hfd(newNames, nnt);
460         newEVR = hfd(newEVR, nvt);
461         Names = hfd(Names, rnt);
462     }
463     return 0;
464 }
465
466 /**
467  * Mark files in database shared with this package as "replaced".
468  * @param psm           package state machine data
469  * @return              0 always
470  */
471 static int markReplacedFiles(PSM_t psm)
472 {
473     const rpmTransactionSet ts = psm->ts;
474     TFI_t fi = psm->fi;
475     HGE_t hge = (HGE_t)fi->hge;
476     const struct sharedFileInfo * replaced = fi->replaced;
477     const struct sharedFileInfo * sfi;
478     rpmdbMatchIterator mi;
479     Header h;
480     unsigned int * offsets;
481     unsigned int prev;
482     int num;
483
484     if (!(fi->fc > 0 && fi->replaced))
485         return 0;
486
487     num = prev = 0;
488     for (sfi = replaced; sfi->otherPkg; sfi++) {
489         if (prev && prev == sfi->otherPkg)
490             continue;
491         prev = sfi->otherPkg;
492         num++;
493     }
494     if (num == 0)
495         return 0;
496
497     offsets = alloca(num * sizeof(*offsets));
498     num = prev = 0;
499     for (sfi = replaced; sfi->otherPkg; sfi++) {
500         if (prev && prev == sfi->otherPkg)
501             continue;
502         prev = sfi->otherPkg;
503         offsets[num++] = sfi->otherPkg;
504     }
505
506     mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES, NULL, 0);
507     rpmdbAppendIterator(mi, offsets, num);
508
509     sfi = replaced;
510     while ((h = rpmdbNextIterator(mi)) != NULL) {
511         char * secStates;
512         int modified;
513         int count;
514
515         modified = 0;
516
517         if (!hge(h, RPMTAG_FILESTATES, NULL, (void **)&secStates, &count))
518             continue;
519         
520         prev = rpmdbGetIteratorOffset(mi);
521         num = 0;
522         while (sfi->otherPkg && sfi->otherPkg == prev) {
523             assert(sfi->otherFileNum < count);
524             if (secStates[sfi->otherFileNum] != RPMFILE_STATE_REPLACED) {
525                 secStates[sfi->otherFileNum] = RPMFILE_STATE_REPLACED;
526                 if (modified == 0) {
527                     /* Modified header will be rewritten. */
528                     modified = 1;
529                     rpmdbSetIteratorModified(mi, modified);
530                 }
531                 num++;
532             }
533             sfi++;
534         }
535     }
536     rpmdbFreeIterator(mi);
537
538     return 0;
539 }
540
541 /**
542  */
543 static rpmRC chkdir (const char * dpath, const char * dname)
544 {
545     struct stat st;
546     int rc;
547
548     if ((rc = Stat(dpath, &st)) < 0) {
549         int ut = urlPath(dpath, NULL);
550         switch (ut) {
551         case URL_IS_PATH:
552         case URL_IS_UNKNOWN:
553             if (errno != ENOENT)
554                 break;
555             /*@fallthrough@*/
556         case URL_IS_FTP:
557         case URL_IS_HTTP:
558             /* XXX this will only create last component of directory path */
559             rc = Mkdir(dpath, 0755);
560             break;
561         case URL_IS_DASH:
562             break;
563         }
564         if (rc < 0) {
565             rpmError(RPMERR_CREATE, _("cannot create %s %s\n"),
566                         dname, dpath);
567             return RPMRC_FAIL;
568         }
569     }
570     if ((rc = Access(dpath, W_OK))) {
571         rpmError(RPMERR_CREATE, _("cannot write to %s\n"), dpath);
572         return RPMRC_FAIL;
573     }
574     return RPMRC_OK;
575 }
576
577 rpmRC rpmInstallSourcePackage(const char * rootDir, FD_t fd,
578                         const char ** specFilePtr,
579                         rpmCallbackFunction notify, rpmCallbackData notifyData,
580                         char ** cookie)
581 {
582     rpmdb rpmdb = NULL;
583     rpmTransactionSet ts = rpmtransCreateSet(rpmdb, rootDir);
584     TFI_t fi = xcalloc(sizeof(*fi), 1);
585     const char * _sourcedir = NULL;
586     const char * _specdir = NULL;
587     const char * specFile = NULL;
588     HGE_t hge;
589     HFD_t hfd;
590     Header h;
591     struct psm_s psmbuf;
592     PSM_t psm = &psmbuf;
593     int isSource;
594     rpmRC rc;
595     int i;
596
597     ts->notify = notify;
598     ts->notifyData = notifyData;
599
600     rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
601     if (rc)
602         goto exit;
603
604     if (!isSource) {
605         rpmError(RPMERR_NOTSRPM, _("source package expected, binary found\n"));
606         rc = RPMRC_FAIL;
607         goto exit;
608     }
609
610     (void) rpmtransAddPackage(ts, h, fd, NULL, 0, NULL);
611
612     fi->type = TR_ADDED;
613     fi->ap = ts->addedPackages.list;
614     loadFi(h, fi);
615     hge = fi->hge;
616     hfd = fi->hfd;
617     headerFree(h);      /* XXX reference held by transaction set */
618     h = NULL;
619
620     rpmInstallLoadMacros(fi, fi->h);
621
622     memset(psm, 0, sizeof(*psm));
623     psm->ts = ts;
624     psm->fi = fi;
625
626     if (cookie) {
627         *cookie = NULL;
628         if (hge(h, RPMTAG_COOKIE, NULL, (void **) cookie, NULL))
629             *cookie = xstrdup(*cookie);
630     }
631
632     /* XXX FIXME: can't do endian neutral MD5 verification yet. */
633     fi->fmd5s = hfd(fi->fmd5s, -1);
634
635     /* XXX FIXME: don't do per-file mapping, force global flags. */
636     fi->fmapflags = hfd(fi->fmapflags, -1);
637     fi->mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
638
639     fi->uid = getuid();
640     fi->gid = getgid();
641     fi->astriplen = 0;
642     fi->striplen = 0;
643
644     fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
645     fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
646     for (i = 0; i < fi->fc; i++) {
647         fi->fuids[i] = fi->uid;
648         fi->fgids[i] = fi->gid;
649     }
650
651     for (i = 0; i < fi->fc; i++) {
652         fi->actions[i] = FA_CREATE;
653     }
654
655     rpmBuildFileList(fi->h, &fi->apath, NULL);
656
657     i = fi->fc;
658     if (headerIsEntry(fi->h, RPMTAG_COOKIE))
659         for (i = 0; i < fi->fc; i++)
660                 if (fi->fflags[i] & RPMFILE_SPECFILE) break;
661
662     if (i == fi->fc) {
663         /* Find the spec file by name. */
664         for (i = 0; i < fi->fc; i++) {
665             const char * t = fi->apath[i];
666             t += strlen(fi->apath[i]) - 5;
667             if (!strcmp(t, ".spec")) break;
668         }
669     }
670
671     _sourcedir = rpmGenPath(ts->rootDir, "%{_sourcedir}", "");
672     rc = chkdir(_sourcedir, "sourcedir");
673     if (rc) {
674         rc = RPMRC_FAIL;
675         goto exit;
676     }
677
678     _specdir = rpmGenPath(ts->rootDir, "%{_specdir}", "");
679     rc = chkdir(_specdir, "specdir");
680     if (rc) {
681         rc = RPMRC_FAIL;
682         goto exit;
683     }
684
685     /* Build dnl/dil with {_sourcedir, _specdir} as values. */
686     if (i < fi->fc) {
687         int speclen = strlen(_specdir) + 2;
688         int sourcelen = strlen(_sourcedir) + 2;
689         char * t;
690
691         fi->dnl = hfd(fi->dnl, -1);
692
693         fi->dc = 2;
694         fi->dnl = xmalloc(fi->dc * sizeof(*fi->dnl) + fi->fc * sizeof(*fi->dil) +
695                         speclen + sourcelen);
696         fi->dil = (int *)(fi->dnl + fi->dc);
697         memset(fi->dil, 0, fi->fc * sizeof(*fi->dil));
698         fi->dil[i] = 1;
699         fi->dnl[0] = t = (char *)(fi->dil + fi->fc);
700         fi->dnl[1] = t = stpcpy( stpcpy(t, _sourcedir), "/") + 1;
701         (void) stpcpy( stpcpy(t, _specdir), "/");
702
703         t = xmalloc(speclen + strlen(fi->bnl[i]) + 1);
704         (void) stpcpy( stpcpy( stpcpy(t, _specdir), "/"), fi->bnl[i]);
705         specFile = t;
706     } else {
707         rpmError(RPMERR_NOSPEC, _("source package contains no .spec file\n"));
708         rc = RPMRC_FAIL;
709         goto exit;
710     }
711
712     psm->goal = PSM_PKGINSTALL;
713
714     rc = psmStage(psm, PSM_PROCESS);
715
716     (void) psmStage(psm, PSM_FINI);
717
718     if (rc) rc = RPMRC_FAIL;
719
720 exit:
721     if (rc == RPMRC_OK && specFile && specFilePtr)
722         *specFilePtr = specFile;
723     else
724         specFile = _free(specFile);
725
726     _specdir = _free(_specdir);
727     _sourcedir = _free(_sourcedir);
728
729     if (h)
730         headerFree(h);
731
732     if (fi) {
733         freeFi(fi);
734         free(fi);
735     }
736     if (ts)
737         rpmtransFree(ts);
738
739     return rc;
740 }
741
742 static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
743
744 /**
745  * Return scriptlet name from tag.
746  * @param tag           scriptlet tag
747  * @return              name of scriptlet
748  */
749 static /*@observer@*/ const char * const tag2sln(int tag)
750 {
751     switch (tag) {
752     case RPMTAG_PREIN:          return "%pre";
753     case RPMTAG_POSTIN:         return "%post";
754     case RPMTAG_PREUN:          return "%preun";
755     case RPMTAG_POSTUN:         return "%postun";
756     case RPMTAG_VERIFYSCRIPT:   return "%verify";
757     }
758     return "%unknownscript";
759 }
760
761 /**
762  * Run scriptlet with args.
763  *
764  * Run a script with an interpreter. If the interpreter is not specified,
765  * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
766  * the header will be ignored, passing instead arg1 and arg2.
767  * 
768  * @param psm           package state machine data
769  * @param h             header
770  * @param sln           name of scriptlet section
771  * @param progArgc      no. of args from header
772  * @param progArgv      args from header, progArgv[0] is the interpreter to use
773  * @param script        scriptlet from header
774  * @param arg1          no. instances of package installed after scriptlet exec
775  *                      (-1 is no arg)
776  * @param arg2          ditto, but for the target package
777  * @return              0 on success, 1 on error
778  */
779 static int runScript(PSM_t psm, Header h,
780                 const char * sln,
781                 int progArgc, const char ** progArgv, 
782                 const char * script, int arg1, int arg2)
783 {
784     const rpmTransactionSet ts = psm->ts;
785     TFI_t fi = psm->fi;
786     HGE_t hge = fi->hge;
787     HFD_t hfd = fi->hfd;
788     const char ** argv = NULL;
789     int argc = 0;
790     const char ** prefixes = NULL;
791     int numPrefixes;
792     int_32 ipt;
793     const char * oldPrefix;
794     int maxPrefixLength;
795     int len;
796     char * prefixBuf = NULL;
797     pid_t child;
798     int status = 0;
799     const char * fn = NULL;
800     int i;
801     int freePrefixes = 0;
802     FD_t out;
803     rpmRC rc = RPMRC_OK;
804     const char *n, *v, *r;
805
806     if (!progArgv && !script)
807         return 0;
808
809     if (!progArgv) {
810         argv = alloca(5 * sizeof(char *));
811         argv[0] = "/bin/sh";
812         argc = 1;
813     } else {
814         argv = alloca((progArgc + 4) * sizeof(char *));
815         memcpy(argv, progArgv, progArgc * sizeof(char *));
816         argc = progArgc;
817     }
818
819     headerNVR(h, &n, &v, &r);
820     if (hge(h, RPMTAG_INSTPREFIXES, &ipt, (void **) &prefixes, &numPrefixes)) {
821         freePrefixes = 1;
822     } else if (hge(h, RPMTAG_INSTALLPREFIX, NULL, (void **) &oldPrefix, NULL)) {
823         prefixes = &oldPrefix;
824         numPrefixes = 1;
825     } else {
826         numPrefixes = 0;
827     }
828
829     maxPrefixLength = 0;
830     for (i = 0; i < numPrefixes; i++) {
831         len = strlen(prefixes[i]);
832         if (len > maxPrefixLength) maxPrefixLength = len;
833     }
834     prefixBuf = alloca(maxPrefixLength + 50);
835
836     if (script) {
837         FD_t fd;
838         if (makeTempFile((!ts->chrootDone ? ts->rootDir : "/"), &fn, &fd)) {
839             if (freePrefixes) free(prefixes);
840             return 1;
841         }
842
843         if (rpmIsDebug() &&
844             (!strcmp(argv[0], "/bin/sh") || !strcmp(argv[0], "/bin/bash")))
845             (void)Fwrite("set -x\n", sizeof(char), 7, fd);
846
847         (void)Fwrite(script, sizeof(script[0]), strlen(script), fd);
848         Fclose(fd);
849
850         {   const char * sn = fn;
851             if (!ts->chrootDone &&
852                 !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
853             {
854                 sn += strlen(ts->rootDir)-1;
855             }
856             argv[argc++] = sn;
857         }
858
859         if (arg1 >= 0) {
860             char *av = alloca(20);
861             sprintf(av, "%d", arg1);
862             argv[argc++] = av;
863         }
864         if (arg2 >= 0) {
865             char *av = alloca(20);
866             sprintf(av, "%d", arg2);
867             argv[argc++] = av;
868         }
869     }
870
871     argv[argc] = NULL;
872
873     if (ts->scriptFd != NULL) {
874         if (rpmIsVerbose()) {
875             out = fdDup(Fileno(ts->scriptFd));
876         } else {
877             out = Fopen("/dev/null", "w.fdio");
878             if (Ferror(out)) {
879                 out = fdDup(Fileno(ts->scriptFd));
880             }
881         }
882     } else {
883         out = fdDup(STDOUT_FILENO);
884         out = fdLink(out, "runScript persist");
885     }
886     
887     if (!(child = fork())) {
888         const char * rootDir;
889         int pipes[2];
890
891         pipes[0] = pipes[1] = 0;
892         /* make stdin inaccessible */
893         pipe(pipes);
894         close(pipes[1]);
895         dup2(pipes[0], STDIN_FILENO);
896         close(pipes[0]);
897
898         if (ts->scriptFd != NULL) {
899             if (Fileno(ts->scriptFd) != STDERR_FILENO)
900                 dup2(Fileno(ts->scriptFd), STDERR_FILENO);
901             if (Fileno(out) != STDOUT_FILENO)
902                 dup2(Fileno(out), STDOUT_FILENO);
903             /* make sure we don't close stdin/stderr/stdout by mistake! */
904             if (Fileno(out) > STDERR_FILENO && Fileno(out) != Fileno(ts->scriptFd)) {
905                 Fclose (out);
906             }
907             if (Fileno(ts->scriptFd) > STDERR_FILENO) {
908                 Fclose (ts->scriptFd);
909             }
910         }
911
912         {   const char *ipath = rpmExpand("PATH=%{_install_script_path}", NULL);
913             const char *path = SCRIPT_PATH;
914
915             if (ipath && ipath[5] != '%')
916                 path = ipath;
917             doputenv(path);
918             if (ipath)  free((void *)ipath);
919         }
920
921         for (i = 0; i < numPrefixes; i++) {
922             sprintf(prefixBuf, "RPM_INSTALL_PREFIX%d=%s", i, prefixes[i]);
923             doputenv(prefixBuf);
924
925             /* backwards compatibility */
926             if (i == 0) {
927                 sprintf(prefixBuf, "RPM_INSTALL_PREFIX=%s", prefixes[i]);
928                 doputenv(prefixBuf);
929             }
930         }
931
932         rootDir = ts->rootDir;
933         switch(urlIsURL(rootDir)) {
934         case URL_IS_PATH:
935             rootDir += sizeof("file://") - 1;
936             rootDir = strchr(rootDir, '/');
937             /*@fallthrough@*/
938         case URL_IS_UNKNOWN:
939             if (!ts->chrootDone && !(rootDir[0] == '/' && rootDir[1] == '\0')) {
940                 /*@-unrecog@*/ chroot(rootDir); /*@=unrecog@*/
941             }
942             chdir("/");
943             execv(argv[0], (char *const *)argv);
944             break;
945         default:
946             break;
947         }
948
949         _exit(-1);
950         /*@notreached@*/
951     }
952
953     if (waitpid(child, &status, 0) < 0) {
954         rpmError(RPMERR_SCRIPT,
955      _("execution of %s scriptlet from %s-%s-%s failed, waitpid returned %s\n"),
956                  sln, n, v, r, strerror (errno));
957         /* XXX what to do here? */
958         rc = RPMRC_OK;
959     } else {
960         if (!WIFEXITED(status) || WEXITSTATUS(status)) {
961             rpmError(RPMERR_SCRIPT,
962      _("execution of %s scriptlet from %s-%s-%s failed, exit status %d\n"),
963                      sln, n, v, r, WEXITSTATUS(status));
964             rc = RPMRC_FAIL;
965         }
966     }
967
968     if (freePrefixes) prefixes = hfd(prefixes, ipt);
969
970     Fclose(out);        /* XXX dup'd STDOUT_FILENO */
971     
972     if (script) {
973         if (!rpmIsDebug()) unlink(fn);
974         free((void *)fn);
975     }
976
977     return rc;
978 }
979
980 /**
981  * Retrieve and run scriptlet from header.
982  * @param psm           package state machine data
983  * @return              rpmRC return code
984  */
985 static rpmRC runInstScript(PSM_t psm)
986 {
987     TFI_t fi = psm->fi;
988     HGE_t hge = fi->hge;
989     HFD_t hfd = fi->hfd;
990     void ** programArgv;
991     int programArgc;
992     const char ** argv;
993     int_32 ptt, stt;
994     const char * script;
995     rpmRC rc = RPMRC_OK;
996
997     /*
998      * headerGetEntry() sets the data pointer to NULL if the entry does
999      * not exist.
1000      */
1001     hge(fi->h, psm->progTag, &ptt, (void **) &programArgv, &programArgc);
1002     hge(fi->h, psm->scriptTag, &stt, (void **) &script, NULL);
1003
1004     if (programArgv && ptt == RPM_STRING_TYPE) {
1005         argv = alloca(sizeof(char *));
1006         *argv = (const char *) programArgv;
1007     } else {
1008         argv = (const char **) programArgv;
1009     }
1010
1011     rc = runScript(psm, fi->h, tag2sln(psm->scriptTag), programArgc, argv,
1012                 script, psm->scriptArg, -1);
1013
1014     programArgv = hfd(programArgv, ptt);
1015     script = hfd(script, stt);
1016     return rc;
1017 }
1018
1019 /**
1020  * @param psm           package state machine data
1021  * @param sourceH
1022  * @param triggeredH
1023  * @param arg2
1024  * @param triggersAlreadyRun
1025  * @return
1026  */
1027 static int handleOneTrigger(PSM_t psm, Header sourceH, Header triggeredH,
1028                         int arg2, char * triggersAlreadyRun)
1029 {
1030     const rpmTransactionSet ts = psm->ts;
1031     TFI_t fi = psm->fi;
1032     HGE_t hge = fi->hge;
1033     HFD_t hfd = fi->hfd;
1034     const char ** triggerNames;
1035     const char ** triggerEVR;
1036     const char ** triggerScripts;
1037     const char ** triggerProgs;
1038     int_32 * triggerFlags;
1039     int_32 * triggerIndices;
1040     int_32 tnt, tvt, tft;
1041     const char * triggerPackageName;
1042     const char * sourceName;
1043     int numTriggers;
1044     rpmRC rc = RPMRC_OK;
1045     int i;
1046     int skip;
1047
1048     if (!hge(triggeredH, RPMTAG_TRIGGERNAME, &tnt, 
1049                         (void **) &triggerNames, &numTriggers))
1050         return 0;
1051
1052     headerNVR(sourceH, &sourceName, NULL, NULL);
1053
1054     hge(triggeredH, RPMTAG_TRIGGERFLAGS, &tft, (void **) &triggerFlags, NULL);
1055     hge(triggeredH, RPMTAG_TRIGGERVERSION, &tvt, (void **) &triggerEVR, NULL);
1056
1057     for (i = 0; i < numTriggers; i++) {
1058         int_32 tit, tst, tpt;
1059
1060         if (!(triggerFlags[i] & psm->sense)) continue;
1061         if (strcmp(triggerNames[i], sourceName)) continue;
1062
1063         /*
1064          * For some reason, the TRIGGERVERSION stuff includes the name of
1065          * the package which the trigger is based on. We need to skip
1066          * over that here. I suspect that we'll change our minds on this
1067          * and remove that, so I'm going to just 'do the right thing'.
1068          */
1069         skip = strlen(triggerNames[i]);
1070         if (!strncmp(triggerEVR[i], triggerNames[i], skip) &&
1071             (triggerEVR[i][skip] == '-'))
1072             skip++;
1073         else
1074             skip = 0;
1075
1076         if (!headerMatchesDepFlags(sourceH, triggerNames[i],
1077                 triggerEVR[i] + skip, triggerFlags[i]))
1078             continue;
1079
1080         hge(triggeredH, RPMTAG_TRIGGERINDEX, &tit,
1081                        (void **) &triggerIndices, NULL);
1082         hge(triggeredH, RPMTAG_TRIGGERSCRIPTS, &tst,
1083                        (void **) &triggerScripts, NULL);
1084         hge(triggeredH, RPMTAG_TRIGGERSCRIPTPROG, &tpt,
1085                        (void **) &triggerProgs, NULL);
1086
1087         headerNVR(triggeredH, &triggerPackageName, NULL, NULL);
1088
1089         {   int arg1;
1090             int index;
1091
1092             arg1 = rpmdbCountPackages(ts->rpmdb, triggerPackageName);
1093             if (arg1 < 0) {
1094                 /* XXX W2DO? same as "execution of script failed" */
1095                 rc = RPMRC_FAIL;
1096             } else {
1097                 arg1 += psm->countCorrection;
1098                 index = triggerIndices[i];
1099                 if (!triggersAlreadyRun || !triggersAlreadyRun[index]) {
1100                     rc = runScript(psm, triggeredH, "%trigger", 1,
1101                             triggerProgs + index, triggerScripts[index], 
1102                             arg1, arg2);
1103                     if (triggersAlreadyRun) triggersAlreadyRun[index] = 1;
1104                 }
1105             }
1106         }
1107
1108         triggerIndices = hfd(triggerIndices, tit);
1109         triggerScripts = hfd(triggerScripts, tst);
1110         triggerProgs = hfd(triggerProgs, tpt);
1111
1112         /*
1113          * Each target/source header pair can only result in a single
1114          * script being run.
1115          */
1116         break;
1117     }
1118
1119     triggerNames = hfd(triggerNames, tnt);
1120     triggerFlags = hfd(triggerFlags, tft);
1121     triggerEVR = hfd(triggerEVR, tvt);
1122
1123     return rc;
1124 }
1125
1126 /**
1127  * Run trigger scripts in the database that are fired by this header.
1128  * @param psm           package state machine data
1129  * @return              0 on success, 1 on error
1130  */
1131 static int runTriggers(PSM_t psm)
1132 {
1133     const rpmTransactionSet ts = psm->ts;
1134     TFI_t fi = psm->fi;
1135     int numPackage;
1136     rpmRC rc = RPMRC_OK;
1137
1138     numPackage = rpmdbCountPackages(ts->rpmdb, fi->name) + psm->countCorrection;
1139     if (numPackage < 0)
1140         return 1;
1141
1142     {   Header triggeredH;
1143         rpmdbMatchIterator mi;
1144         int countCorrection = psm->countCorrection;
1145
1146         psm->countCorrection = 0;
1147         mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_TRIGGERNAME, fi->name, 0);
1148         while((triggeredH = rpmdbNextIterator(mi)) != NULL) {
1149             rc |= handleOneTrigger(psm, fi->h, triggeredH, numPackage, NULL);
1150         }
1151
1152         rpmdbFreeIterator(mi);
1153         psm->countCorrection = countCorrection;
1154     }
1155
1156     return rc;
1157 }
1158
1159 /**
1160  * Run triggers from this header that are fired by headers in the database.
1161  * @param psm           package state machine data
1162  * @return              0 on success, 1 on error
1163  */
1164 static int runImmedTriggers(PSM_t psm)
1165 {
1166     const rpmTransactionSet ts = psm->ts;
1167     TFI_t fi = psm->fi;
1168     HGE_t hge = fi->hge;
1169     HFD_t hfd = fi->hfd;
1170     const char ** triggerNames;
1171     int numTriggers;
1172     int_32 * triggerIndices;
1173     int_32 tnt, tit;
1174     int numTriggerIndices;
1175     char * triggersRun;
1176     rpmRC rc = RPMRC_OK;
1177
1178     if (!hge(fi->h, RPMTAG_TRIGGERNAME, &tnt,
1179                         (void **) &triggerNames, &numTriggers))
1180         return 0;
1181
1182     hge(fi->h, RPMTAG_TRIGGERINDEX, &tit, (void **) &triggerIndices, 
1183                    &numTriggerIndices);
1184     triggersRun = alloca(sizeof(*triggersRun) * numTriggerIndices);
1185     memset(triggersRun, 0, sizeof(*triggersRun) * numTriggerIndices);
1186
1187     {   Header sourceH = NULL;
1188         int i;
1189
1190         for (i = 0; i < numTriggers; i++) {
1191             rpmdbMatchIterator mi;
1192
1193             if (triggersRun[triggerIndices[i]]) continue;
1194         
1195             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, triggerNames[i], 0);
1196
1197             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
1198                 rc |= handleOneTrigger(psm, sourceH, fi->h, 
1199                                 rpmdbGetIteratorCount(mi),
1200                                 triggersRun);
1201             }
1202
1203             rpmdbFreeIterator(mi);
1204         }
1205     }
1206     triggerIndices = hfd(triggerIndices, tit);
1207     triggerNames = hfd(triggerNames, tnt);
1208     return rc;
1209 }
1210
1211 /*@observer@*/ static const char *const pkgStageString(pkgStage a) {
1212     switch(a) {
1213     case PSM_UNKNOWN:           return "unknown";
1214
1215     case PSM_PKGINSTALL:        return "  install";
1216     case PSM_PKGERASE:          return "    erase";
1217     case PSM_PKGCOMMIT:         return "   commit";
1218     case PSM_PKGSAVE:           return "repackage";
1219
1220     case PSM_INIT:              return "init";
1221     case PSM_PRE:               return "pre";
1222     case PSM_PROCESS:           return "process";
1223     case PSM_POST:              return "post";
1224     case PSM_UNDO:              return "undo";
1225     case PSM_FINI:              return "fini";
1226
1227     case PSM_CREATE:            return "create";
1228     case PSM_NOTIFY:            return "notify";
1229     case PSM_DESTROY:           return "destroy";
1230     case PSM_COMMIT:            return "commit";
1231
1232     case PSM_CHROOT_IN:         return "chrootin";
1233     case PSM_CHROOT_OUT:        return "chrootout";
1234     case PSM_SCRIPT:            return "script";
1235     case PSM_TRIGGERS:          return "triggers";
1236     case PSM_IMMED_TRIGGERS:    return "immedtriggers";
1237
1238     case PSM_RPMIO_FLAGS:       return "rpmioflags";
1239
1240     case PSM_RPMDB_LOAD:        return "rpmdbload";
1241     case PSM_RPMDB_ADD:         return "rpmdbadd";
1242     case PSM_RPMDB_REMOVE:      return "rpmdbremove";
1243
1244     default:                    return "???";
1245     }
1246     /*@noteached@*/
1247 }
1248
1249 /**
1250  * @todo Packages w/o files never get a callback, hence don't get displayed
1251  * on install with -v.
1252  */
1253 int psmStage(PSM_t psm, pkgStage stage)
1254 {
1255     const rpmTransactionSet ts = psm->ts;
1256     TFI_t fi = psm->fi;
1257     HGE_t hge = fi->hge;
1258     HFD_t hfd = fi->hfd;
1259     rpmRC rc = psm->rc;
1260     int saveerrno;
1261
1262     switch (stage) {
1263     case PSM_UNKNOWN:
1264         break;
1265     case PSM_INIT:
1266         rpmMessage(RPMMESS_DEBUG, _("%s: %s-%s-%s has %d files, test = %d\n"),
1267                 psm->stepName, fi->name, fi->version, fi->release,
1268                 fi->fc, (ts->transFlags & RPMTRANS_FLAG_TEST));
1269
1270         /*
1271          * When we run scripts, we pass an argument which is the number of 
1272          * versions of this package that will be installed when we are
1273          * finished.
1274          */
1275         psm->npkgs_installed = rpmdbCountPackages(ts->rpmdb, fi->name);
1276         if (psm->npkgs_installed < 0) {
1277             rc = RPMRC_FAIL;
1278             break;
1279         }
1280
1281         if (psm->goal == PSM_PKGINSTALL) {
1282             psm->scriptArg = psm->npkgs_installed + 1;
1283
1284 assert(psm->mi == NULL);
1285             psm->mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, fi->name, 0);
1286             rpmdbSetIteratorVersion(psm->mi, fi->version);
1287             rpmdbSetIteratorRelease(psm->mi, fi->release);
1288             while ((psm->oh = rpmdbNextIterator(psm->mi))) {
1289                 fi->record = rpmdbGetIteratorOffset(psm->mi);
1290                 psm->oh = (ts->transFlags & RPMTRANS_FLAG_MULTILIB)
1291                         ? headerCopy(psm->oh) : NULL;
1292                 break;
1293             }
1294             rpmdbFreeIterator(psm->mi);
1295             psm->mi = NULL;
1296             rc = RPMRC_OK;
1297
1298             if (fi->fc > 0 && fi->fstates == NULL) {
1299                 fi->fstates = xmalloc(sizeof(*fi->fstates) * fi->fc);
1300                 memset(fi->fstates, RPMFILE_STATE_NORMAL, fi->fc);
1301             }
1302
1303             if (fi->fc <= 0)                            break;
1304             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
1305         
1306             /*
1307              * Old format relocateable packages need the entire default
1308              * prefix stripped to form the cpio list, while all other packages
1309              * need the leading / stripped.
1310              */
1311             {   const char * p;
1312                 rc = hge(fi->h, RPMTAG_DEFAULTPREFIX, NULL, (void **) &p, NULL);
1313                 fi->striplen = (rc ? strlen(p) + 1 : 1); 
1314             }
1315             fi->mapflags =
1316                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1317         
1318             if (headerIsEntry(fi->h, RPMTAG_ORIGBASENAMES))
1319                 buildOrigFileList(fi->h, &fi->apath, NULL);
1320             else
1321                 rpmBuildFileList(fi->h, &fi->apath, NULL);
1322         
1323             if (fi->fuser == NULL)
1324                 hge(fi->h, RPMTAG_FILEUSERNAME, NULL,
1325                                 (void **) &fi->fuser, NULL);
1326             if (fi->fgroup == NULL)
1327                 hge(fi->h, RPMTAG_FILEGROUPNAME, NULL,
1328                                 (void **) &fi->fgroup, NULL);
1329             if (fi->fuids == NULL)
1330                 fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
1331             if (fi->fgids == NULL)
1332                 fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
1333             rc = RPMRC_OK;
1334         }
1335         if (psm->goal == PSM_PKGERASE || psm->goal == PSM_PKGSAVE) {
1336             psm->scriptArg = psm->npkgs_installed - 1;
1337         
1338             /* Retrieve installed header. */
1339             rc = psmStage(psm, PSM_RPMDB_LOAD);
1340         }
1341         if (psm->goal == PSM_PKGSAVE) {
1342             /* Open output package for writing. */
1343             {   const char * bfmt = rpmGetPath("%{_repackage_name_fmt}", NULL);
1344                 const char * pkgbn =
1345                         headerSprintf(fi->h, bfmt, rpmTagTable, rpmHeaderFormats, NULL);
1346
1347                 bfmt = _free(bfmt);
1348                 psm->pkgURL = rpmGenPath("%{?_repackage_root:%{_repackage_root}}",
1349                                          "%{?_repackage_dir:%{_repackage_dir}}",
1350                                         pkgbn);
1351                 pkgbn = _free(pkgbn);
1352                 (void) urlPath(psm->pkgURL, &psm->pkgfn);
1353                 psm->fd = Fopen(psm->pkgfn, "w.ufdio");
1354                 if (psm->fd == NULL || Ferror(psm->fd)) {
1355                     rc = RPMRC_FAIL;
1356                     break;
1357                 }
1358             }
1359         }
1360         break;
1361     case PSM_PRE:
1362         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
1363
1364         /* Change root directory if requested and not already done. */
1365         rc = psmStage(psm, PSM_CHROOT_IN);
1366
1367         if (psm->goal == PSM_PKGINSTALL) {
1368             psm->scriptTag = RPMTAG_PREIN;
1369             psm->progTag = RPMTAG_PREINPROG;
1370
1371             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
1372                 /* XXX FIXME: implement %triggerprein. */
1373             }
1374
1375             if (!(ts->transFlags & RPMTRANS_FLAG_NOPRE)) {
1376                 rc = psmStage(psm, PSM_SCRIPT);
1377                 if (rc) {
1378                     rpmError(RPMERR_SCRIPT,
1379                         _("%s: %s scriptlet failed (%d), skipping %s-%s-%s\n"),
1380                         psm->stepName, tag2sln(psm->scriptTag), rc,
1381                         fi->name, fi->version, fi->release);
1382                     break;
1383                 }
1384             }
1385         }
1386
1387         if (psm->goal == PSM_PKGERASE) {
1388             psm->scriptTag = RPMTAG_PREUN;
1389             psm->progTag = RPMTAG_PREUNPROG;
1390             psm->sense = RPMSENSE_TRIGGERUN;
1391             psm->countCorrection = -1;
1392
1393             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERUN)) {
1394                 /* Run triggers in other package(s) this package sets off. */
1395                 rc = psmStage(psm, PSM_TRIGGERS);
1396                 if (rc) break;
1397
1398                 /* Run triggers in this package other package(s) set off. */
1399                 rc = psmStage(psm, PSM_IMMED_TRIGGERS);
1400                 if (rc) break;
1401             }
1402
1403             if (!(ts->transFlags & RPMTRANS_FLAG_NOPREUN))
1404                 rc = psmStage(psm, PSM_SCRIPT);
1405         }
1406         if (psm->goal == PSM_PKGSAVE) {
1407             /* Regenerate original header. */
1408             {   void * uh = NULL;
1409                 int_32 uht, uhc;
1410
1411                 if (headerGetEntry(fi->h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc)) {
1412                     psm->oh = headerCopyLoad(uh);
1413                     uh = hfd(uh, uht);
1414                 } else {
1415                     psm->oh = headerLink(fi->h);
1416                 }
1417             }
1418
1419             /* Retrieve type of payload compression. */
1420             rc = psmStage(psm, PSM_RPMIO_FLAGS);
1421
1422             /* Write the lead section into the package. */
1423             {   int archnum = -1;
1424                 int osnum = -1;
1425                 struct rpmlead lead;
1426
1427 #ifndef DYING
1428                 rpmGetArchInfo(NULL, &archnum);
1429                 rpmGetOsInfo(NULL, &osnum);
1430 #endif
1431
1432                 memset(&lead, 0, sizeof(lead));
1433                 /* XXX Set package version conditioned on noDirTokens. */
1434                 lead.major = 4;
1435                 lead.minor = 0;
1436                 lead.type = RPMLEAD_BINARY;
1437                 lead.archnum = archnum;
1438                 lead.osnum = osnum;
1439                 lead.signature_type = RPMSIGTYPE_HEADERSIG;
1440
1441                 {   char buf[256];
1442                     sprintf(buf, "%s-%s-%s", fi->name, fi->version, fi->release);
1443                     strncpy(lead.name, buf, sizeof(lead.name));
1444                 }
1445
1446                 rc = writeLead(psm->fd, &lead);
1447                 if (rc) {
1448                     rpmError(RPMERR_NOSPACE, _("Unable to write package: %s\n"),
1449                          Fstrerror(psm->fd));
1450                     rc = RPMRC_FAIL;
1451                     break;
1452                 }
1453             }
1454
1455             /* Write the signature section into the package. */
1456             {   Header sig = headerRegenSigHeader(fi->h);
1457                 rc = rpmWriteSignature(psm->fd, sig);
1458                 headerFree(sig);
1459                 if (rc) break;
1460             }
1461
1462             /* Write the metadata section into the package. */
1463             rc = headerWrite(psm->fd, psm->oh, HEADER_MAGIC_YES);
1464             if (rc) break;
1465         }
1466         break;
1467     case PSM_PROCESS:
1468         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
1469
1470         if (psm->goal == PSM_PKGINSTALL) {
1471             struct availablePackage * alp = fi->ap;
1472             int i;
1473
1474             if (fi->fc <= 0)                            break;
1475             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
1476
1477             for (i = 0; i < fi->fc; i++) {
1478                 uid_t uid;
1479                 gid_t gid;
1480
1481                 uid = fi->uid;
1482                 gid = fi->gid;
1483                 if (fi->fuser && unameToUid(fi->fuser[i], &uid)) {
1484                     rpmMessage(RPMMESS_WARNING,
1485                         _("user %s does not exist - using root\n"),
1486                         fi->fuser[i]);
1487                     uid = 0;
1488                     /* XXX this diddles header memory. */
1489                     fi->fmodes[i] &= ~S_ISUID;  /* turn off the suid bit */
1490                 }
1491
1492                 if (fi->fgroup && gnameToGid(fi->fgroup[i], &gid)) {
1493                     rpmMessage(RPMMESS_WARNING,
1494                         _("group %s does not exist - using root\n"),
1495                         fi->fgroup[i]);
1496                     gid = 0;
1497                     /* XXX this diddles header memory. */
1498                     fi->fmodes[i] &= ~S_ISGID;  /* turn off the sgid bit */
1499                 }
1500                 if (fi->fuids) fi->fuids[i] = uid;
1501                 if (fi->fgids) fi->fgids[i] = gid;
1502             }
1503
1504             /* Retrieve type of payload compression. */
1505             rc = psmStage(psm, PSM_RPMIO_FLAGS);
1506
1507             psm->cfd = Fdopen(fdDup(Fileno(alp->fd)), psm->rpmio_flags);
1508
1509             rc = fsmSetup(fi->fsm, FSM_PKGINSTALL, ts, fi,
1510                         psm->cfd, NULL, &psm->failedFile);
1511             (void) fsmTeardown(fi->fsm);
1512
1513             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
1514             Fclose(psm->cfd);
1515             psm->cfd = NULL;
1516             errno = saveerrno; /* XXX FIXME: Fclose with libio destroys errno */
1517
1518             if (!rc)
1519                 rc = psmStage(psm, PSM_COMMIT);
1520
1521             if (rc) {
1522                 rpmError(RPMERR_CPIO,
1523                         _("unpacking of archive failed%s%s: %s\n"),
1524                         (psm->failedFile != NULL ? _(" on file ") : ""),
1525                         (psm->failedFile != NULL ? psm->failedFile : ""),
1526                         cpioStrerror(rc));
1527                 rc = RPMRC_FAIL;
1528                 break;
1529             }
1530             psm->what = RPMCALLBACK_INST_PROGRESS;
1531             psm->amount = (fi->archiveSize ? fi->archiveSize : 100);
1532             psm->total = psm->amount;
1533             (void) psmStage(psm, PSM_NOTIFY);
1534         }
1535         if (psm->goal == PSM_PKGERASE) {
1536
1537             if (fi->fc <= 0)                            break;
1538             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
1539             if (ts->transFlags & RPMTRANS_FLAG_APPLYONLY)       break;
1540
1541             psm->what = RPMCALLBACK_UNINST_START;
1542             psm->amount = fi->fc;       /* XXX W2DO? looks wrong. */
1543             psm->total = fi->fc;
1544             (void) psmStage(psm, PSM_NOTIFY);
1545
1546             rc = fsmSetup(fi->fsm, FSM_PKGERASE, ts, fi,
1547                         NULL, NULL, &psm->failedFile);
1548             (void) fsmTeardown(fi->fsm);
1549
1550             psm->what = RPMCALLBACK_UNINST_STOP;
1551             psm->amount = 0;            /* XXX W2DO? looks wrong. */
1552             psm->total = fi->fc;
1553             (void) psmStage(psm, PSM_NOTIFY);
1554
1555         }
1556         if (psm->goal == PSM_PKGSAVE) {
1557             fileAction * actions = fi->actions;
1558             fileAction action = fi->action;
1559
1560             fi->action = FA_COPYOUT;
1561             fi->actions = NULL;
1562
1563             Fflush(psm->fd);
1564             psm->cfd = Fdopen(fdDup(Fileno(psm->fd)), psm->rpmio_flags);
1565
1566             /* XXX failedFile? */
1567             rc = fsmSetup(fi->fsm, FSM_PKGBUILD, ts, fi, psm->cfd, NULL, NULL);
1568             (void) fsmTeardown(fi->fsm);
1569
1570             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
1571             Fclose(psm->cfd);
1572             psm->cfd = NULL;
1573             errno = saveerrno;
1574
1575             fi->action = action;
1576             fi->actions = actions;
1577         }
1578         break;
1579     case PSM_POST:
1580         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
1581
1582         if (psm->goal == PSM_PKGINSTALL) {
1583             int_32 installTime = time(NULL);
1584
1585             if (fi->fc > 0 && fi->fstates)
1586                 headerAddEntry(fi->h, RPMTAG_FILESTATES, RPM_CHAR_TYPE,
1587                                 fi->fstates, fi->fc);
1588
1589             headerAddEntry(fi->h, RPMTAG_INSTALLTIME, RPM_INT32_TYPE,
1590                                 &installTime, 1);
1591
1592             if (ts->transFlags & RPMTRANS_FLAG_MULTILIB) {
1593                 uint_32 multiLib, * newMultiLib, * p;
1594
1595                 if (hge(fi->h, RPMTAG_MULTILIBS, NULL, (void **) &newMultiLib, NULL) &&
1596                     hge(psm->oh, RPMTAG_MULTILIBS, NULL, (void **) &p, NULL)) {
1597                     multiLib = *p;
1598                     multiLib |= *newMultiLib;
1599                     headerModifyEntry(psm->oh, RPMTAG_MULTILIBS, RPM_INT32_TYPE,
1600                                       &multiLib, 1);
1601                 }
1602                 rc = mergeFiles(fi, psm->oh, fi->h);
1603                 if (rc) break;
1604             }
1605
1606
1607             /*
1608              * If this package has already been installed, remove it from
1609              * the database before adding the new one.
1610              */
1611             if (fi->record && !(ts->transFlags & RPMTRANS_FLAG_APPLYONLY)) {
1612                 rc = psmStage(psm, PSM_RPMDB_REMOVE);
1613                 if (rc) break;
1614             }
1615
1616             rc = psmStage(psm, PSM_RPMDB_ADD);
1617             if (rc) break;
1618
1619             psm->scriptTag = RPMTAG_POSTIN;
1620             psm->progTag = RPMTAG_POSTINPROG;
1621             psm->sense = RPMSENSE_TRIGGERIN;
1622             psm->countCorrection = 0;
1623
1624             if (!(ts->transFlags & RPMTRANS_FLAG_NOPOST)) {
1625                 rc = psmStage(psm, PSM_SCRIPT);
1626                 if (rc) break;
1627             }
1628             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERIN)) {
1629                 /* Run triggers in other package(s) this package sets off. */
1630                 rc = psmStage(psm, PSM_TRIGGERS);
1631                 if (rc) break;
1632
1633                 /* Run triggers in this package other package(s) set off. */
1634                 rc = psmStage(psm, PSM_IMMED_TRIGGERS);
1635                 if (rc) break;
1636             }
1637
1638             if (!(ts->transFlags & RPMTRANS_FLAG_APPLYONLY))
1639                 rc = markReplacedFiles(psm);
1640
1641         }
1642         if (psm->goal == PSM_PKGERASE) {
1643
1644             psm->scriptTag = RPMTAG_POSTUN;
1645             psm->progTag = RPMTAG_POSTUNPROG;
1646             psm->sense = RPMSENSE_TRIGGERPOSTUN;
1647             psm->countCorrection = -1;
1648
1649             if (!(ts->transFlags & RPMTRANS_FLAG_NOPOSTUN)) {
1650                 rc = psmStage(psm, PSM_SCRIPT);
1651                 /* XXX WTFO? postun failures don't cause erasure failure. */
1652             }
1653
1654             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
1655                 /* Run triggers in other package(s) this package sets off. */
1656                 rc = psmStage(psm, PSM_TRIGGERS);
1657                 if (rc) break;
1658             }
1659
1660             if (!(ts->transFlags & RPMTRANS_FLAG_APPLYONLY))
1661                 rc = psmStage(psm, PSM_RPMDB_REMOVE);
1662         }
1663         if (psm->goal == PSM_PKGSAVE) {
1664         }
1665
1666         /* Restore root directory if changed. */
1667         (void) psmStage(psm, PSM_CHROOT_OUT);
1668         break;
1669     case PSM_UNDO:
1670         break;
1671     case PSM_FINI:
1672         /* Restore root directory if changed. */
1673         (void) psmStage(psm, PSM_CHROOT_OUT);
1674
1675         if (psm->fd) {
1676             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
1677             Fclose(psm->fd);
1678             psm->fd = NULL;
1679             errno = saveerrno;
1680         }
1681
1682         if (psm->goal == PSM_PKGSAVE) {
1683             if (!rc)
1684                 rpmMessage(RPMMESS_VERBOSE, _("Wrote: %s\n"), psm->pkgURL);
1685         }
1686
1687         if (fi->h && (psm->goal == PSM_PKGERASE || psm->goal == PSM_PKGSAVE)) {
1688             headerFree(fi->h);
1689             fi->h = NULL;
1690         }
1691         if (psm->oh) {
1692             headerFree(psm->oh);
1693             psm->oh = NULL;
1694         }
1695         psm->pkgURL = _free(psm->pkgURL);
1696         psm->rpmio_flags = _free(psm->rpmio_flags);
1697         psm->failedFile = _free(psm->failedFile);
1698
1699         fi->fgids = _free(fi->fgids);
1700         fi->fuids = _free(fi->fuids);
1701         fi->fgroup = hfd(fi->fgroup, -1);
1702         fi->fuser = hfd(fi->fuser, -1);
1703         fi->apath = _free(fi->apath);
1704         fi->fstates = _free(fi->fstates);
1705
1706         break;
1707
1708     case PSM_PKGINSTALL:
1709     case PSM_PKGERASE:
1710     case PSM_PKGSAVE:
1711         psm->goal = stage;
1712         psm->rc = RPMRC_OK;
1713         psm->stepName = pkgStageString(stage);
1714
1715         rc = psmStage(psm, PSM_INIT);
1716         if (!rc) rc = psmStage(psm, PSM_PRE);
1717         if (!rc) rc = psmStage(psm, PSM_PROCESS);
1718         if (!rc) rc = psmStage(psm, PSM_POST);
1719         (void) psmStage(psm, PSM_FINI);
1720         break;
1721     case PSM_PKGCOMMIT:
1722         break;
1723
1724     case PSM_CREATE:
1725         break;
1726     case PSM_NOTIFY:
1727         if (ts && ts->notify)
1728             (void) ts->notify(fi->h, psm->what, psm->amount, psm->total,
1729                 (fi->ap ? fi->ap->key : NULL), ts->notifyData);
1730         break;
1731     case PSM_DESTROY:
1732         break;
1733     case PSM_COMMIT:
1734         if (!(ts->transFlags & RPMTRANS_FLAG_PKGCOMMIT)) break;
1735         if (ts->transFlags & RPMTRANS_FLAG_APPLYONLY) break;
1736
1737         rc = fsmSetup(fi->fsm, FSM_PKGCOMMIT, ts, fi,
1738                         NULL, NULL, &psm->failedFile);
1739         (void) fsmTeardown(fi->fsm);
1740         break;
1741
1742     case PSM_CHROOT_IN:
1743         /* Change root directory if requested and not already done. */
1744         if (ts->rootDir && !ts->chrootDone && !psm->chrootDone) {
1745             static int _loaded = 0;
1746
1747             /*
1748              * This loads all of the name services libraries, in case we
1749              * don't have access to them in the chroot().
1750              */
1751             if (!_loaded) {
1752                 (void)getpwnam("root");
1753                 endpwent();
1754                 _loaded++;
1755             }
1756
1757             chdir("/");
1758             /*@-unrecog@*/
1759             rc = chroot(ts->rootDir);
1760             /*@=unrecog@*/
1761             psm->chrootDone = ts->chrootDone = 1;
1762         }
1763         break;
1764     case PSM_CHROOT_OUT:
1765         /* Restore root directory if changed. */
1766         if (psm->chrootDone) {
1767             /*@-unrecog@*/
1768             rc = chroot(".");
1769             /*@=unrecog@*/
1770             psm->chrootDone = ts->chrootDone = 0;
1771             chdir(ts->currDir);
1772         }
1773         break;
1774     case PSM_SCRIPT:
1775         rpmMessage(RPMMESS_DEBUG, _("%s: running %s script(s) (if any)\n"),
1776                 psm->stepName, tag2sln(psm->scriptTag));
1777         rc = runInstScript(psm);
1778         break;
1779     case PSM_TRIGGERS:
1780         /* Run triggers in other package(s) this package sets off. */
1781         rc = runTriggers(psm);
1782         break;
1783     case PSM_IMMED_TRIGGERS:
1784         /* Run triggers in this package other package(s) set off. */
1785         rc = runImmedTriggers(psm);
1786         break;
1787
1788     case PSM_RPMIO_FLAGS:
1789     {   const char * payload_compressor = NULL;
1790         char * t;
1791
1792         if (!hge(fi->h, RPMTAG_PAYLOADCOMPRESSOR, NULL,
1793                             (void **) &payload_compressor, NULL))
1794             payload_compressor = "gzip";
1795         psm->rpmio_flags = t = xmalloc(sizeof("w9.gzdio"));
1796         *t = '\0';
1797         t = stpcpy(t, ((psm->goal == PSM_PKGSAVE) ? "w9" : "r"));
1798         if (!strcmp(payload_compressor, "gzip"))
1799             t = stpcpy(t, ".gzdio");
1800         if (!strcmp(payload_compressor, "bzip2"))
1801             t = stpcpy(t, ".bzdio");
1802         rc = RPMRC_OK;
1803     }   break;
1804
1805     case PSM_RPMDB_LOAD:
1806 assert(psm->mi == NULL);
1807         psm->mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
1808                                 &fi->record, sizeof(fi->record));
1809
1810         fi->h = rpmdbNextIterator(psm->mi);
1811         if (fi->h)
1812             fi->h = headerLink(fi->h);
1813         rpmdbFreeIterator(psm->mi);
1814         psm->mi = NULL;
1815         rc = (fi->h ? RPMRC_OK : RPMRC_FAIL);
1816         break;
1817     case PSM_RPMDB_ADD:
1818         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
1819         rc = rpmdbAdd(ts->rpmdb, ts->id, fi->h);
1820         break;
1821     case PSM_RPMDB_REMOVE:
1822         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
1823         rc = rpmdbRemove(ts->rpmdb, ts->id, fi->record);
1824         break;
1825
1826     default:
1827         break;
1828     }
1829
1830     return rc;
1831 }