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