Add rpmtsPrintSuggests() method for now, use instead of ts internals mucking
[platform/upstream/rpm.git] / lib / rpminstall.c
1 /** \ingroup rpmcli
2  * \file lib/rpminstall.c
3  */
4
5 #include "system.h"
6
7 #include "rpmcli.h"
8
9 #include "rpmdb.h"
10 #include "rpmds.h"
11
12 #include "rpmts.h"
13
14 #include "manifest.h"
15 #include "rpmerr.h"
16 #include "debug.h"
17
18 int rpmcliPackagesTotal = 0;
19 int rpmcliHashesCurrent = 0;
20 int rpmcliHashesTotal = 0;
21 int rpmcliProgressCurrent = 0;
22 int rpmcliProgressTotal = 0;
23
24 /**
25  * Print a CLI progress bar.
26  * @todo Unsnarl isatty(STDOUT_FILENO) from the control flow.
27  * @param amount        current
28  * @param total         final
29  */
30 static void printHash(const unsigned long amount, const unsigned long total)
31 {
32     int hashesNeeded;
33
34     rpmcliHashesTotal = (isatty (STDOUT_FILENO) ? 44 : 50);
35
36     if (rpmcliHashesCurrent != rpmcliHashesTotal) {
37         float pct = (total ? (((float) amount) / total) : 1.0);
38         hashesNeeded = (rpmcliHashesTotal * pct) + 0.5;
39         while (hashesNeeded > rpmcliHashesCurrent) {
40             if (isatty (STDOUT_FILENO)) {
41                 int i;
42                 for (i = 0; i < rpmcliHashesCurrent; i++)
43                     (void) putchar ('#');
44                 for (; i < rpmcliHashesTotal; i++)
45                     (void) putchar (' ');
46                 fprintf(stdout, "(%3d%%)", (int)((100 * pct) + 0.5));
47                 for (i = 0; i < (rpmcliHashesTotal + 6); i++)
48                     (void) putchar ('\b');
49             } else
50                 fprintf(stdout, "#");
51
52             rpmcliHashesCurrent++;
53         }
54         (void) fflush(stdout);
55
56         if (rpmcliHashesCurrent == rpmcliHashesTotal) {
57             int i;
58             rpmcliProgressCurrent++;
59             if (isatty(STDOUT_FILENO)) {
60                 for (i = 1; i < rpmcliHashesCurrent; i++)
61                     (void) putchar ('#');
62                 pct = (rpmcliProgressTotal
63                     ? (((float) rpmcliProgressCurrent) / rpmcliProgressTotal)
64                     : 1);
65                 fprintf(stdout, " [%3d%%]", (int)((100 * pct) + 0.5));
66             }
67             fprintf(stdout, "\n");
68         }
69         (void) fflush(stdout);
70     }
71 }
72
73 void * rpmShowProgress(const void * arg,
74                         const rpmCallbackType what,
75                         const unsigned long amount,
76                         const unsigned long total,
77                         fnpyKey key,
78                         void * data)
79 {
80     Header h = (Header) arg;
81     char * s;
82     int flags = (int) ((long)data);
83     void * rc = NULL;
84     const char * filename = (const char *)key;
85     static FD_t fd = NULL;
86     int xx;
87
88     switch (what) {
89     case RPMCALLBACK_INST_OPEN_FILE:
90         if (filename == NULL || filename[0] == '\0')
91             return NULL;
92         fd = Fopen(filename, "r.ufdio");
93         /* FIX: still necessary? */
94         if (fd == NULL || Ferror(fd)) {
95             rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), filename,
96                         Fstrerror(fd));
97             if (fd != NULL) {
98                 xx = Fclose(fd);
99                 fd = NULL;
100             }
101         } else
102             fd = fdLink(fd, "persist (showProgress)");
103         return (void *)fd;
104         break;
105
106     case RPMCALLBACK_INST_CLOSE_FILE:
107         /* FIX: still necessary? */
108         fd = fdFree(fd, "persist (showProgress)");
109         if (fd != NULL) {
110             xx = Fclose(fd);
111             fd = NULL;
112         }
113         break;
114
115     case RPMCALLBACK_INST_START:
116         rpmcliHashesCurrent = 0;
117         if (h == NULL || !(flags & INSTALL_LABEL))
118             break;
119         /* @todo Remove headerSprintf() on a progress callback. */
120         if (flags & INSTALL_HASH) {
121             s = headerSprintf(h, "%{NAME}",
122                                 rpmTagTable, rpmHeaderFormats, NULL);
123             if (isatty (STDOUT_FILENO))
124                 fprintf(stdout, "%4d:%-23.23s", rpmcliProgressCurrent + 1, s);
125             else
126                 fprintf(stdout, "%-28.28s", s);
127             (void) fflush(stdout);
128             s = _free(s);
129         } else {
130             s = headerSprintf(h, "%{NAME}-%{VERSION}-%{RELEASE}",
131                                   rpmTagTable, rpmHeaderFormats, NULL);
132             fprintf(stdout, "%s\n", s);
133             (void) fflush(stdout);
134             s = _free(s);
135         }
136         break;
137
138     case RPMCALLBACK_TRANS_PROGRESS:
139     case RPMCALLBACK_INST_PROGRESS:
140         if (flags & INSTALL_PERCENT)
141             fprintf(stdout, "%%%% %f\n", (double) (total
142                                 ? ((((float) amount) / total) * 100)
143                                 : 100.0));
144         else if (flags & INSTALL_HASH)
145             printHash(amount, total);
146         (void) fflush(stdout);
147         break;
148
149     case RPMCALLBACK_TRANS_START:
150         rpmcliHashesCurrent = 0;
151         rpmcliProgressTotal = 1;
152         rpmcliProgressCurrent = 0;
153         if (!(flags & INSTALL_LABEL))
154             break;
155         if (flags & INSTALL_HASH)
156             fprintf(stdout, "%-28s", _("Preparing..."));
157         else
158             fprintf(stdout, "%s\n", _("Preparing packages for installation..."));
159         (void) fflush(stdout);
160         break;
161
162     case RPMCALLBACK_TRANS_STOP:
163         if (flags & INSTALL_HASH)
164             printHash(1, 1);    /* Fixes "preparing..." progress bar */
165         rpmcliProgressTotal = rpmcliPackagesTotal;
166         rpmcliProgressCurrent = 0;
167         break;
168
169     case RPMCALLBACK_REPACKAGE_START:
170         rpmcliHashesCurrent = 0;
171         rpmcliProgressTotal = total;
172         rpmcliProgressCurrent = 0;
173         if (!(flags & INSTALL_LABEL))
174             break;
175         if (flags & INSTALL_HASH)
176             fprintf(stdout, "%-28s\n", _("Repackaging..."));
177         else
178             fprintf(stdout, "%s\n", _("Repackaging erased files..."));
179         (void) fflush(stdout);
180         break;
181
182     case RPMCALLBACK_REPACKAGE_PROGRESS:
183         if (amount && (flags & INSTALL_HASH))
184             printHash(1, 1);    /* Fixes "preparing..." progress bar */
185         break;
186
187     case RPMCALLBACK_REPACKAGE_STOP:
188         rpmcliProgressTotal = total;
189         rpmcliProgressCurrent = total;
190         if (flags & INSTALL_HASH)
191             printHash(1, 1);    /* Fixes "preparing..." progress bar */
192         rpmcliProgressTotal = rpmcliPackagesTotal;
193         rpmcliProgressCurrent = 0;
194         if (!(flags & INSTALL_LABEL))
195             break;
196         if (flags & INSTALL_HASH)
197             fprintf(stdout, "%-28s\n", _("Upgrading..."));
198         else
199             fprintf(stdout, "%s\n", _("Upgrading packages..."));
200         (void) fflush(stdout);
201         break;
202
203     case RPMCALLBACK_UNINST_PROGRESS:
204         break;
205     case RPMCALLBACK_UNINST_START:
206         break;
207     case RPMCALLBACK_UNINST_STOP:
208         break;
209     case RPMCALLBACK_UNPACK_ERROR:
210         break;
211     case RPMCALLBACK_CPIO_ERROR:
212         break;
213     case RPMCALLBACK_UNKNOWN:
214     default:
215         break;
216     }
217
218     return rc;
219 }       
220
221 typedef const char * str_t;
222
223 struct rpmEIU {
224     Header h;
225     FD_t fd;
226     int numFailed;
227     int numPkgs;
228     str_t * pkgURL;
229     str_t * fnp;
230     char * pkgState;
231     int prevx;
232     int pkgx;
233     int numRPMS;
234     int numSRPMS;
235     str_t * sourceURL;
236     int isSource;
237     int argc;
238     str_t * argv;
239     rpmRelocation * relocations;
240     rpmRC rpmrc;
241 };
242
243 /** @todo Generalize --freshen policies. */
244 int rpmInstall(rpmts ts,
245                 struct rpmInstallArguments_s * ia,
246                 const char ** fileArgv)
247 {
248     struct rpmEIU * eiu = memset(alloca(sizeof(*eiu)), 0, sizeof(*eiu));
249     rpmps ps;
250     rpmprobFilterFlags probFilter;
251     rpmRelocation * relocations;
252 const char * fileURL = NULL;
253     int stopInstall = 0;
254     const char ** av = NULL;
255     rpmVSFlags vsflags, ovsflags, tvsflags;
256     int ac = 0;
257     int rc;
258     int xx;
259     int i;
260
261     if (fileArgv == NULL) goto exit;
262
263     rpmcliPackagesTotal = 0;
264
265     if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
266         ia->transFlags |= RPMTRANS_FLAG_REPACKAGE;
267
268     (void) rpmtsSetFlags(ts, ia->transFlags);
269
270     probFilter = ia->probFilter;
271     relocations = ia->relocations;
272
273     if (ia->installInterfaceFlags & INSTALL_UPGRADE)
274         vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
275     else
276         vsflags = rpmExpandNumeric("%{?_vsflags_install}");
277     if (ia->qva_flags & VERIFY_DIGEST)
278         vsflags |= _RPMVSF_NODIGESTS;
279     if (ia->qva_flags & VERIFY_SIGNATURE)
280         vsflags |= _RPMVSF_NOSIGNATURES;
281     if (ia->qva_flags & VERIFY_HDRCHK)
282         vsflags |= RPMVSF_NOHDRCHK;
283     ovsflags = rpmtsSetVSFlags(ts, (vsflags | RPMVSF_NEEDPAYLOAD));
284
285     {   int notifyFlags;
286         notifyFlags = ia->installInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
287         xx = rpmtsSetNotifyCallback(ts,
288                         rpmShowProgress, (void *) ((long)notifyFlags));
289     }
290
291     if ((eiu->relocations = relocations) != NULL) {
292         while (eiu->relocations->oldPath)
293             eiu->relocations++;
294         if (eiu->relocations->newPath == NULL)
295             eiu->relocations = NULL;
296     }
297
298     /* Build fully globbed list of arguments in argv[argc]. */
299     for (eiu->fnp = fileArgv; *eiu->fnp != NULL; eiu->fnp++) {
300         av = _free(av); ac = 0;
301         rc = rpmGlob(*eiu->fnp, &ac, &av);
302         if (rc || ac == 0) {
303             rpmlog(RPMERR_OPEN, _("File not found by glob: %s\n"), *eiu->fnp);
304             continue;
305         }
306
307         eiu->argv = xrealloc(eiu->argv, (eiu->argc+ac+1) * sizeof(*eiu->argv));
308         memcpy(eiu->argv+eiu->argc, av, ac * sizeof(*av));
309         eiu->argc += ac;
310         eiu->argv[eiu->argc] = NULL;
311     }
312     av = _free(av);     ac = 0;
313
314 restart:
315     /* Allocate sufficient storage for next set of args. */
316     if (eiu->pkgx >= eiu->numPkgs) {
317         eiu->numPkgs = eiu->pkgx + eiu->argc;
318         eiu->pkgURL = xrealloc(eiu->pkgURL,
319                         (eiu->numPkgs + 1) * sizeof(*eiu->pkgURL));
320         memset(eiu->pkgURL + eiu->pkgx, 0,
321                         ((eiu->argc + 1) * sizeof(*eiu->pkgURL)));
322         eiu->pkgState = xrealloc(eiu->pkgState,
323                         (eiu->numPkgs + 1) * sizeof(*eiu->pkgState));
324         memset(eiu->pkgState + eiu->pkgx, 0,
325                         ((eiu->argc + 1) * sizeof(*eiu->pkgState)));
326     }
327
328     /* Retrieve next set of args, cache on local storage. */
329     for (i = 0; i < eiu->argc; i++) {
330         fileURL = _free(fileURL);
331         fileURL = eiu->argv[i];
332         eiu->argv[i] = NULL;
333
334 #ifdef  NOTYET
335 if (fileURL[0] == '=') {
336     rpmds this = rpmdsSingle(RPMTAG_REQUIRENAME, fileURL+1, NULL, 0);
337
338     xx = rpmtsSolve(ts, this, NULL);
339     if (ts->suggests && ts->nsuggests > 0) {
340         fileURL = _free(fileURL);
341         fileURL = ts->suggests[0];
342         ts->suggests[0] = NULL;
343         while (ts->nsuggests-- > 0) {
344             if (ts->suggests[ts->nsuggests] == NULL)
345                 continue;
346             ts->suggests[ts->nsuggests] = _free(ts->suggests[ts->nsuggests]);
347         }
348         ts->suggests = _free(ts->suggests);
349         rpmlog(RPMLOG_DEBUG, _("Adding goal: %s\n"), fileURL);
350         eiu->pkgURL[eiu->pkgx] = fileURL;
351         fileURL = NULL;
352         eiu->pkgx++;
353     }
354     this = rpmdsFree(this);
355 } else
356 #endif
357
358         switch (urlIsURL(fileURL)) {
359         case URL_IS_HTTPS:
360         case URL_IS_HTTP:
361         case URL_IS_FTP:
362         {   const char *tfn;
363
364             if (rpmIsVerbose())
365                 fprintf(stdout, _("Retrieving %s\n"), fileURL);
366
367             {   char tfnbuf[64];
368                 const char * rootDir = rpmtsRootDir(ts);
369                 if (!(rootDir && * rootDir))
370                     rootDir = "";
371                 strcpy(tfnbuf, "rpm-xfer.XXXXXX");
372                 (void) mktemp(tfnbuf);
373                 tfn = rpmGenPath(rootDir, "%{_tmppath}/", tfnbuf);
374             }
375
376             /* XXX undefined %{name}/%{version}/%{release} here */
377             /* XXX %{_tmpdir} does not exist */
378             rpmlog(RPMLOG_DEBUG, _(" ... as %s\n"), tfn);
379             rc = urlGetFile(fileURL, tfn);
380             if (rc < 0) {
381                 rpmlog(RPMLOG_ERR,
382                         _("skipping %s - transfer failed - %s\n"),
383                         fileURL, ftpStrerror(rc));
384                 eiu->numFailed++;
385                 eiu->pkgURL[eiu->pkgx] = NULL;
386                 tfn = _free(tfn);
387                 break;
388             }
389             eiu->pkgState[eiu->pkgx] = 1;
390             eiu->pkgURL[eiu->pkgx] = tfn;
391             eiu->pkgx++;
392         }   break;
393         case URL_IS_PATH:
394         case URL_IS_DASH:       /* WRONG WRONG WRONG */
395         case URL_IS_HKP:        /* WRONG WRONG WRONG */
396         default:
397             eiu->pkgURL[eiu->pkgx] = fileURL;
398             fileURL = NULL;
399             eiu->pkgx++;
400             break;
401         }
402     }
403     fileURL = _free(fileURL);
404
405     if (eiu->numFailed) goto exit;
406
407     /* Continue processing file arguments, building transaction set. */
408     for (eiu->fnp = eiu->pkgURL+eiu->prevx;
409          *eiu->fnp != NULL;
410          eiu->fnp++, eiu->prevx++)
411     {
412         const char * fileName;
413
414         rpmlog(RPMLOG_DEBUG, "============== %s\n", *eiu->fnp);
415         (void) urlPath(*eiu->fnp, &fileName);
416
417         /* Try to read the header from a package file. */
418         eiu->fd = Fopen(*eiu->fnp, "r.ufdio");
419         if (eiu->fd == NULL || Ferror(eiu->fd)) {
420             rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp,
421                         Fstrerror(eiu->fd));
422             if (eiu->fd != NULL) {
423                 xx = Fclose(eiu->fd);
424                 eiu->fd = NULL;
425             }
426             eiu->numFailed++; *eiu->fnp = NULL;
427             continue;
428         }
429
430         /* Read the header, verifying signatures (if present). */
431         tvsflags = rpmtsSetVSFlags(ts, vsflags);
432         eiu->rpmrc = rpmReadPackageFile(ts, eiu->fd, *eiu->fnp, &eiu->h);
433         tvsflags = rpmtsSetVSFlags(ts, tvsflags);
434         xx = Fclose(eiu->fd);
435         eiu->fd = NULL;
436
437         switch (eiu->rpmrc) {
438         case RPMRC_FAIL:
439             rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), *eiu->fnp);
440             eiu->numFailed++; *eiu->fnp = NULL;
441             continue;
442             break;
443         case RPMRC_NOTFOUND:
444             goto maybe_manifest;
445             break;
446         case RPMRC_NOTTRUSTED:
447         case RPMRC_NOKEY:
448         case RPMRC_OK:
449         default:
450             break;
451         }
452
453         eiu->isSource = headerIsEntry(eiu->h, RPMTAG_SOURCEPACKAGE);
454
455         if (eiu->isSource) {
456             rpmlog(RPMLOG_DEBUG, _("\tadded source package [%d]\n"),
457                 eiu->numSRPMS);
458             eiu->sourceURL = xrealloc(eiu->sourceURL,
459                                 (eiu->numSRPMS + 2) * sizeof(*eiu->sourceURL));
460             eiu->sourceURL[eiu->numSRPMS] = *eiu->fnp;
461             *eiu->fnp = NULL;
462             eiu->numSRPMS++;
463             eiu->sourceURL[eiu->numSRPMS] = NULL;
464             continue;
465         }
466
467         if (eiu->relocations) {
468             const char ** paths;
469             int pft;
470             int c;
471
472             if (headerGetEntry(eiu->h, RPMTAG_PREFIXES, &pft,
473                                        (void **) &paths, &c) && (c == 1))
474             {
475                 eiu->relocations->oldPath = xstrdup(paths[0]);
476                 paths = headerFreeData(paths, pft);
477             } else {
478                 const char * name;
479                 xx = headerNVR(eiu->h, &name, NULL, NULL);
480                 rpmlog(RPMLOG_ERR,
481                                _("package %s is not relocatable\n"), name);
482                 eiu->numFailed++;
483                 goto exit;
484             }
485         }
486
487         /* On --freshen, verify package is installed and newer */
488         if (ia->installInterfaceFlags & INSTALL_FRESHEN) {
489             rpmdbMatchIterator mi;
490             const char * name;
491             Header oldH;
492             int count;
493
494             xx = headerNVR(eiu->h, &name, NULL, NULL);
495             mi = rpmtsInitIterator(ts, RPMTAG_NAME, name, 0);
496             count = rpmdbGetIteratorCount(mi);
497             while ((oldH = rpmdbNextIterator(mi)) != NULL) {
498                 if (rpmVersionCompare(oldH, eiu->h) < 0)
499                     continue;
500                 /* same or newer package already installed */
501                 count = 0;
502                 break;
503             }
504             mi = rpmdbFreeIterator(mi);
505             if (count == 0) {
506                 eiu->h = headerFree(eiu->h);
507                 continue;
508             }
509             /* Package is newer than those currently installed. */
510         }
511
512         rc = rpmtsAddInstallElement(ts, eiu->h, (fnpyKey)fileName,
513                         (ia->installInterfaceFlags & INSTALL_UPGRADE) != 0,
514                         relocations);
515
516         /* XXX reference held by transaction set */
517         eiu->h = headerFree(eiu->h);
518         if (eiu->relocations)
519             eiu->relocations->oldPath = _free(eiu->relocations->oldPath);
520
521         switch(rc) {
522         case 0:
523             rpmlog(RPMLOG_DEBUG, _("\tadded binary package [%d]\n"),
524                         eiu->numRPMS);
525             break;
526         case 1:
527             rpmlog(RPMLOG_ERR,
528                             _("error reading from file %s\n"), *eiu->fnp);
529             eiu->numFailed++;
530             goto exit;
531             break;
532         case 2:
533             rpmlog(RPMLOG_ERR,
534                             _("file %s requires a newer version of RPM\n"),
535                             *eiu->fnp);
536             eiu->numFailed++;
537             goto exit;
538             break;
539         default:
540             eiu->numFailed++;
541             goto exit;
542             break;
543         }
544
545         eiu->numRPMS++;
546         continue;
547
548 maybe_manifest:
549         /* Try to read a package manifest. */
550         eiu->fd = Fopen(*eiu->fnp, "r.fpio");
551         if (eiu->fd == NULL || Ferror(eiu->fd)) {
552             rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp,
553                         Fstrerror(eiu->fd));
554             if (eiu->fd != NULL) {
555                 xx = Fclose(eiu->fd);
556                 eiu->fd = NULL;
557             }
558             eiu->numFailed++; *eiu->fnp = NULL;
559             break;
560         }
561
562         /* Read list of packages from manifest. */
563 /* FIX: *eiu->argv can be NULL */
564         rc = rpmReadPackageManifest(eiu->fd, &eiu->argc, &eiu->argv);
565         if (rc != RPMRC_OK)
566             rpmlog(RPMERR_MANIFEST, _("%s: not an rpm package (or package manifest): %s\n"),
567                         *eiu->fnp, Fstrerror(eiu->fd));
568         xx = Fclose(eiu->fd);
569         eiu->fd = NULL;
570
571         /* If successful, restart the query loop. */
572         if (rc == RPMRC_OK) {
573             eiu->prevx++;
574             goto restart;
575         }
576
577         eiu->numFailed++; *eiu->fnp = NULL;
578         break;
579     }
580
581     rpmlog(RPMLOG_DEBUG, _("found %d source and %d binary packages\n"),
582                 eiu->numSRPMS, eiu->numRPMS);
583
584     if (eiu->numFailed) goto exit;
585
586     if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NODEPS)) {
587
588         if (rpmtsCheck(ts)) {
589             eiu->numFailed = eiu->numPkgs;
590             stopInstall = 1;
591         }
592
593         ps = rpmtsProblems(ts);
594         if (!stopInstall && rpmpsNumProblems(ps) > 0) {
595             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
596             rpmpsPrint(NULL, ps);
597             eiu->numFailed = eiu->numPkgs;
598             stopInstall = 1;
599
600             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST))
601                 rpmtsPrintSuggests(ts);
602
603         }
604         ps = rpmpsFree(ps);
605     }
606
607     if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
608         if (rpmtsOrder(ts)) {
609             eiu->numFailed = eiu->numPkgs;
610             stopInstall = 1;
611         }
612     }
613
614     if (eiu->numRPMS && !stopInstall) {
615
616         rpmcliPackagesTotal += eiu->numSRPMS;
617
618         rpmlog(RPMLOG_DEBUG, _("installing binary packages\n"));
619
620         /* Drop added/available package indices and dependency sets. */
621         rpmtsClean(ts);
622
623         rc = rpmtsRun(ts, NULL, probFilter);
624         ps = rpmtsProblems(ts);
625
626         if (rc < 0) {
627             eiu->numFailed += eiu->numRPMS;
628         } else if (rc > 0) {
629             eiu->numFailed += rc;
630             if (rpmpsNumProblems(ps) > 0)
631                 rpmpsPrint(stderr, ps);
632         }
633         ps = rpmpsFree(ps);
634     }
635
636     if (eiu->numSRPMS && !stopInstall) {
637         if (eiu->sourceURL != NULL)
638         for (i = 0; i < eiu->numSRPMS; i++) {
639             rpmdbCheckSignals();
640             if (eiu->sourceURL[i] == NULL) continue;
641             eiu->fd = Fopen(eiu->sourceURL[i], "r.ufdio");
642             if (eiu->fd == NULL || Ferror(eiu->fd)) {
643                 rpmlog(RPMLOG_ERR, _("cannot open file %s: %s\n"),
644                            eiu->sourceURL[i], Fstrerror(eiu->fd));
645                 if (eiu->fd != NULL) {
646                     xx = Fclose(eiu->fd);
647                     eiu->fd = NULL;
648                 }
649                 continue;
650             }
651
652             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
653                 eiu->rpmrc = rpmInstallSourcePackage(ts, eiu->fd, NULL, NULL);
654                 if (eiu->rpmrc != RPMRC_OK) eiu->numFailed++;
655             }
656
657             xx = Fclose(eiu->fd);
658             eiu->fd = NULL;
659         }
660     }
661
662 exit:
663     if (eiu->pkgURL != NULL)
664     for (i = 0; i < eiu->numPkgs; i++) {
665         if (eiu->pkgURL[i] == NULL) continue;
666         if (eiu->pkgState[i] == 1)
667             (void) Unlink(eiu->pkgURL[i]);
668         eiu->pkgURL[i] = _free(eiu->pkgURL[i]);
669     }
670     eiu->pkgState = _free(eiu->pkgState);
671     eiu->pkgURL = _free(eiu->pkgURL);
672     eiu->argv = _free(eiu->argv);
673
674     rpmtsEmpty(ts);
675
676     return eiu->numFailed;
677 }
678
679 int rpmErase(rpmts ts, struct rpmInstallArguments_s * ia,
680                 const char ** argv)
681 {
682     int count;
683     const char ** arg;
684     int numFailed = 0;
685     int stopUninstall = 0;
686     int numPackages = 0;
687     rpmVSFlags vsflags, ovsflags;
688     rpmps ps;
689
690     if (argv == NULL) return 0;
691
692     vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
693     if (ia->qva_flags & VERIFY_DIGEST)
694         vsflags |= _RPMVSF_NODIGESTS;
695     if (ia->qva_flags & VERIFY_SIGNATURE)
696         vsflags |= _RPMVSF_NOSIGNATURES;
697     if (ia->qva_flags & VERIFY_HDRCHK)
698         vsflags |= RPMVSF_NOHDRCHK;
699     ovsflags = rpmtsSetVSFlags(ts, vsflags);
700
701     if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
702         ia->transFlags |= RPMTRANS_FLAG_REPACKAGE;
703
704     /* XXX suggest mechanism only meaningful when installing */
705     ia->transFlags |= RPMTRANS_FLAG_NOSUGGEST;
706
707     (void) rpmtsSetFlags(ts, ia->transFlags);
708
709 #ifdef  NOTYET  /* XXX no callbacks on erase yet */
710     {   int notifyFlags, xx;
711         notifyFlags = ia->eraseInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
712         xx = rpmtsSetNotifyCallback(ts,
713                         rpmShowProgress, (void *) ((long)notifyFlags));
714     }
715 #endif
716
717     for (arg = argv; *arg; arg++) {
718         rpmdbMatchIterator mi;
719
720         /* XXX HACK to get rpmdbFindByLabel out of the API */
721         mi = rpmtsInitIterator(ts, RPMDBI_LABEL, *arg, 0);
722         if (mi == NULL) {
723             rpmlog(RPMLOG_ERR, _("package %s is not installed\n"), *arg);
724             numFailed++;
725         } else {
726             Header h;   /* XXX iterator owns the reference */
727             count = 0;
728             while ((h = rpmdbNextIterator(mi)) != NULL) {
729                 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
730
731                 if (!(count++ == 0 || (ia->eraseInterfaceFlags & UNINSTALL_ALLMATCHES))) {
732                     rpmlog(RPMLOG_ERR, _("\"%s\" specifies multiple packages\n"),
733                         *arg);
734                     numFailed++;
735                     break;
736                 }
737                 if (recOffset) {
738                     (void) rpmtsAddEraseElement(ts, h, recOffset);
739                     numPackages++;
740                 }
741             }
742         }
743         mi = rpmdbFreeIterator(mi);
744     }
745
746     if (numFailed) goto exit;
747
748     if (!(ia->eraseInterfaceFlags & UNINSTALL_NODEPS)) {
749
750         if (rpmtsCheck(ts)) {
751             numFailed = numPackages;
752             stopUninstall = 1;
753         }
754
755         ps = rpmtsProblems(ts);
756         if (!stopUninstall && rpmpsNumProblems(ps) > 0) {
757             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
758             rpmpsPrint(NULL, ps);
759             numFailed += numPackages;
760             stopUninstall = 1;
761         }
762         ps = rpmpsFree(ps);
763     }
764
765     if (!stopUninstall && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
766         if (rpmtsOrder(ts)) {
767             numFailed += numPackages;
768             stopUninstall = 1;
769         }
770     }
771
772     if (numPackages && !stopUninstall) {
773         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_REVERSE));
774
775         /* Drop added/available package indices and dependency sets. */
776         rpmtsClean(ts);
777
778         numPackages = rpmtsRun(ts, NULL, ia->probFilter & (RPMPROB_FILTER_DISKSPACE|RPMPROB_FILTER_DISKNODES));
779         ps = rpmtsProblems(ts);
780         if (rpmpsNumProblems(ps) > 0)
781             rpmpsPrint(NULL, ps);
782         numFailed += numPackages;
783         stopUninstall = 1;
784         ps = rpmpsFree(ps);
785     }
786
787 exit:
788     rpmtsEmpty(ts);
789
790     return numFailed;
791 }
792
793 int rpmInstallSource(rpmts ts, const char * arg,
794                 const char ** specFilePtr, const char ** cookie)
795 {
796     FD_t fd;
797     int rc;
798
799
800     fd = Fopen(arg, "r.ufdio");
801     if (fd == NULL || Ferror(fd)) {
802         rpmlog(RPMLOG_ERR, _("cannot open %s: %s\n"), arg, Fstrerror(fd));
803         if (fd != NULL) (void) Fclose(fd);
804         return 1;
805     }
806
807     if (rpmIsVerbose())
808         fprintf(stdout, _("Installing %s\n"), arg);
809
810     {
811         rpmVSFlags ovsflags =
812                 rpmtsSetVSFlags(ts, (rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD));
813         rpmRC rpmrc = rpmInstallSourcePackage(ts, fd, specFilePtr, cookie);
814         rc = (rpmrc == RPMRC_OK ? 0 : 1);
815         ovsflags = rpmtsSetVSFlags(ts, ovsflags);
816     }
817     if (rc != 0) {
818         rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), arg);
819         if (specFilePtr && *specFilePtr)
820             *specFilePtr = _free(*specFilePtr);
821         if (cookie && *cookie)
822             *cookie = _free(*cookie);
823     }
824
825     (void) Fclose(fd);
826
827     return rc;
828 }
829
830 /** @todo Transaction handling, more, needs work. */
831 int rpmRollback(rpmts ts, struct rpmInstallArguments_s * ia, const char ** argv)
832 {
833     int ifmask= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL|INSTALL_ERASE);
834     unsigned thistid = 0xffffffff;
835     unsigned prevtid;
836     time_t tid;
837     IDTX itids = NULL;
838     IDTX rtids = NULL;
839     IDT rp;
840     int nrids = 0;
841     IDT ip;
842     int niids = 0;
843     int rc = 0;
844     int vsflags, ovsflags;
845     int numAdded;
846     int numRemoved;
847     rpmps ps;
848     int _unsafe_rollbacks = 0;
849     rpmtransFlags transFlags = ia->transFlags;
850
851     if (argv != NULL && *argv != NULL) {
852         rc = -1;
853         goto exit;
854     }
855
856     _unsafe_rollbacks = rpmExpandNumeric("%{?_unsafe_rollbacks}");
857
858     vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
859     if (ia->qva_flags & VERIFY_DIGEST)
860         vsflags |= _RPMVSF_NODIGESTS;
861     if (ia->qva_flags & VERIFY_SIGNATURE)
862         vsflags |= _RPMVSF_NOSIGNATURES;
863     if (ia->qva_flags & VERIFY_HDRCHK)
864         vsflags |= RPMVSF_NOHDRCHK;
865     vsflags |= RPMVSF_NEEDPAYLOAD;      /* XXX no legacy signatures */
866     ovsflags = rpmtsSetVSFlags(ts, vsflags);
867
868     (void) rpmtsSetFlags(ts, transFlags);
869
870     /*  Make the transaction a rollback transaction.  In a rollback
871      *  a best effort is what we want 
872      */
873     rpmtsSetType(ts, RPMTRANS_TYPE_ROLLBACK);
874
875     itids = IDTXload(ts, RPMTAG_INSTALLTID);
876     if (itids != NULL) {
877         ip = itids->idt;
878         niids = itids->nidt;
879     } else {
880         ip = NULL;
881         niids = 0;
882     }
883
884     {   const char * globstr = rpmExpand("%{_repackage_dir}/*.rpm", NULL);
885         if (globstr == NULL || *globstr == '%') {
886             globstr = _free(globstr);
887             rc = -1;
888             goto exit;
889         }
890         rtids = IDTXglob(ts, globstr, RPMTAG_REMOVETID);
891
892         if (rtids != NULL) {
893             rp = rtids->idt;
894             nrids = rtids->nidt;
895         } else {
896             rp = NULL;
897             nrids = 0;
898         }
899         globstr = _free(globstr);
900     }
901
902     {   int notifyFlags, xx;
903         notifyFlags = ia->installInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
904         xx = rpmtsSetNotifyCallback(ts,
905                         rpmShowProgress, (void *) ((long)notifyFlags));
906     }
907
908     /* Run transactions until rollback goal is achieved. */
909     do {
910         prevtid = thistid;
911         rc = 0;
912         rpmcliPackagesTotal = 0;
913         numAdded = 0;
914         numRemoved = 0;
915         ia->installInterfaceFlags &= ~ifmask;
916
917         /* Find larger of the remaining install/erase transaction id's. */
918         thistid = 0;
919         if (ip != NULL && ip->val.u32 > thistid)
920             thistid = ip->val.u32;
921         if (rp != NULL && rp->val.u32 > thistid)
922             thistid = rp->val.u32;
923
924         /* If we've achieved the rollback goal, then we're done. */
925         if (thistid == 0 || thistid < ia->rbtid)
926             break;
927
928         /* If we've reached the (configured) rollback goal, then we're done. */
929         if (_unsafe_rollbacks && thistid <= _unsafe_rollbacks)
930             break;
931
932         rpmtsEmpty(ts);
933         (void) rpmtsSetFlags(ts, transFlags);
934
935         /* Install the previously erased packages for this transaction. */
936         while (rp != NULL && rp->val.u32 == thistid) {
937
938             rpmlog(RPMLOG_DEBUG, "\t+++ install %s\n",
939                         (rp->key ? rp->key : "???"));
940
941             rc = rpmtsAddInstallElement(ts, rp->h, (fnpyKey)rp->key,
942                                0, ia->relocations);
943             if (rc != 0)
944                 goto exit;
945
946             numAdded++;
947             rpmcliPackagesTotal++;
948             if (!(ia->installInterfaceFlags & ifmask))
949                 ia->installInterfaceFlags |= INSTALL_UPGRADE;
950
951 #ifdef  NOTYET
952             rp->h = headerFree(rp->h);
953 #endif
954             nrids--;
955             if (nrids > 0)
956                 rp++;
957             else
958                 rp = NULL;
959         }
960
961         /* Erase the previously installed packages for this transaction. */
962         while (ip != NULL && ip->val.u32 == thistid) {
963
964             rpmlog(RPMLOG_DEBUG,
965                         "\t--- erase h#%u\n", ip->instance);
966
967             rc = rpmtsAddEraseElement(ts, ip->h, ip->instance);
968             if (rc != 0)
969                 goto exit;
970
971             numRemoved++;
972
973             if (_unsafe_rollbacks)
974                 rpmcliPackagesTotal++;
975
976             if (!(ia->installInterfaceFlags & ifmask)) {
977                 ia->installInterfaceFlags |= INSTALL_ERASE;
978                 (void) rpmtsSetFlags(ts, (transFlags | RPMTRANS_FLAG_REVERSE));
979             }
980
981 #ifdef  NOTYET
982             ip->instance = 0;
983 #endif
984             niids--;
985             if (niids > 0)
986                 ip++;
987             else
988                 ip = NULL;
989         }
990
991         /* Anything to do? */
992         if (rpmcliPackagesTotal <= 0)
993             break;
994
995         tid = (time_t)thistid;
996         rpmlog(RPMLOG_NOTICE,
997                 _("Rollback packages (+%d/-%d) to %-24.24s (0x%08x):\n"),
998                         numAdded, numRemoved, ctime(&tid), tid);
999
1000         rc = rpmtsCheck(ts);
1001         ps = rpmtsProblems(ts);
1002         if (rc != 0 && rpmpsNumProblems(ps) > 0) {
1003             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
1004             rpmpsPrint(NULL, ps);
1005             ps = rpmpsFree(ps);
1006             goto exit;
1007         }
1008         ps = rpmpsFree(ps);
1009
1010         rc = rpmtsOrder(ts);
1011         if (rc != 0)
1012             goto exit;
1013
1014         /* Drop added/available package indices and dependency sets. */
1015         rpmtsClean(ts);
1016
1017         rc = rpmtsRun(ts, NULL, (ia->probFilter|RPMPROB_FILTER_OLDPACKAGE));
1018         ps = rpmtsProblems(ts);
1019         if (rc > 0 && rpmpsNumProblems(ps) > 0)
1020             rpmpsPrint(stderr, ps);
1021         ps = rpmpsFree(ps);
1022         if (rc)
1023             goto exit;
1024
1025         /* Clean up after successful rollback. */
1026         if (rtids && !rpmIsDebug()) {
1027             int i;
1028             rpmlog(RPMLOG_NOTICE, _("Cleaning up repackaged packages:\n"));
1029             if (rtids->idt)
1030             for (i = 0; i < rtids->nidt; i++) {
1031                 IDT rrp = rtids->idt + i;
1032                 if (rrp->val.u32 != thistid)
1033                     continue;
1034                 if (rrp->key) { /* XXX can't happen */
1035                     rpmlog(RPMLOG_NOTICE, _("\tRemoving %s:\n"), rrp->key);
1036                     (void) unlink(rrp->key);    /* XXX: Should check rc??? */
1037                 }
1038             }
1039         }
1040
1041
1042     } while (1);
1043
1044 exit:
1045     rtids = IDTXfree(rtids);
1046     itids = IDTXfree(itids);
1047
1048     rpmtsEmpty(ts);
1049     (void) rpmtsSetFlags(ts, transFlags);
1050
1051     return rc;
1052 }