af38ca50d437278ef15a4babd445abe44ac3686c
[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 #if defined(HAVE_MKSTEMP)
373                 (void) close(mkstemp(tfnbuf));
374 #else
375                 (void) mktemp(tfnbuf);
376 #endif
377                 tfn = rpmGenPath(rootDir, "%{_tmppath}/", tfnbuf);
378             }
379
380             /* XXX undefined %{name}/%{version}/%{release} here */
381             /* XXX %{_tmpdir} does not exist */
382             rpmlog(RPMLOG_DEBUG, _(" ... as %s\n"), tfn);
383             rc = urlGetFile(fileURL, tfn);
384             if (rc < 0) {
385                 rpmlog(RPMLOG_ERR,
386                         _("skipping %s - transfer failed - %s\n"),
387                         fileURL, ftpStrerror(rc));
388                 eiu->numFailed++;
389                 eiu->pkgURL[eiu->pkgx] = NULL;
390                 tfn = _free(tfn);
391                 break;
392             }
393             eiu->pkgState[eiu->pkgx] = 1;
394             eiu->pkgURL[eiu->pkgx] = tfn;
395             eiu->pkgx++;
396         }   break;
397         case URL_IS_PATH:
398         case URL_IS_DASH:       /* WRONG WRONG WRONG */
399         case URL_IS_HKP:        /* WRONG WRONG WRONG */
400         default:
401             eiu->pkgURL[eiu->pkgx] = fileURL;
402             fileURL = NULL;
403             eiu->pkgx++;
404             break;
405         }
406     }
407     fileURL = _free(fileURL);
408
409     if (eiu->numFailed) goto exit;
410
411     /* Continue processing file arguments, building transaction set. */
412     for (eiu->fnp = eiu->pkgURL+eiu->prevx;
413          *eiu->fnp != NULL;
414          eiu->fnp++, eiu->prevx++)
415     {
416         const char * fileName;
417
418         rpmlog(RPMLOG_DEBUG, "============== %s\n", *eiu->fnp);
419         (void) urlPath(*eiu->fnp, &fileName);
420
421         /* Try to read the header from a package file. */
422         eiu->fd = Fopen(*eiu->fnp, "r.ufdio");
423         if (eiu->fd == NULL || Ferror(eiu->fd)) {
424             rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp,
425                         Fstrerror(eiu->fd));
426             if (eiu->fd != NULL) {
427                 xx = Fclose(eiu->fd);
428                 eiu->fd = NULL;
429             }
430             eiu->numFailed++; *eiu->fnp = NULL;
431             continue;
432         }
433
434         /* Read the header, verifying signatures (if present). */
435         tvsflags = rpmtsSetVSFlags(ts, vsflags);
436         eiu->rpmrc = rpmReadPackageFile(ts, eiu->fd, *eiu->fnp, &eiu->h);
437         tvsflags = rpmtsSetVSFlags(ts, tvsflags);
438         xx = Fclose(eiu->fd);
439         eiu->fd = NULL;
440
441         switch (eiu->rpmrc) {
442         case RPMRC_FAIL:
443             rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), *eiu->fnp);
444             eiu->numFailed++; *eiu->fnp = NULL;
445             continue;
446             break;
447         case RPMRC_NOTFOUND:
448             goto maybe_manifest;
449             break;
450         case RPMRC_NOTTRUSTED:
451         case RPMRC_NOKEY:
452         case RPMRC_OK:
453         default:
454             break;
455         }
456
457         eiu->isSource = headerIsEntry(eiu->h, RPMTAG_SOURCEPACKAGE);
458
459         if (eiu->isSource) {
460             rpmlog(RPMLOG_DEBUG, _("\tadded source package [%d]\n"),
461                 eiu->numSRPMS);
462             eiu->sourceURL = xrealloc(eiu->sourceURL,
463                                 (eiu->numSRPMS + 2) * sizeof(*eiu->sourceURL));
464             eiu->sourceURL[eiu->numSRPMS] = *eiu->fnp;
465             *eiu->fnp = NULL;
466             eiu->numSRPMS++;
467             eiu->sourceURL[eiu->numSRPMS] = NULL;
468             continue;
469         }
470
471         if (eiu->relocations) {
472             const char ** paths;
473             int pft;
474             int c;
475
476             if (headerGetEntry(eiu->h, RPMTAG_PREFIXES, &pft,
477                                        (void **) &paths, &c) && (c == 1))
478             {
479                 eiu->relocations->oldPath = xstrdup(paths[0]);
480                 paths = headerFreeData(paths, pft);
481             } else {
482                 const char * name;
483                 xx = headerNVR(eiu->h, &name, NULL, NULL);
484                 rpmlog(RPMLOG_ERR,
485                                _("package %s is not relocatable\n"), name);
486                 eiu->numFailed++;
487                 goto exit;
488             }
489         }
490
491         /* On --freshen, verify package is installed and newer */
492         if (ia->installInterfaceFlags & INSTALL_FRESHEN) {
493             rpmdbMatchIterator mi;
494             const char * name;
495             Header oldH;
496             int count;
497
498             xx = headerNVR(eiu->h, &name, NULL, NULL);
499             mi = rpmtsInitIterator(ts, RPMTAG_NAME, name, 0);
500             count = rpmdbGetIteratorCount(mi);
501             while ((oldH = rpmdbNextIterator(mi)) != NULL) {
502                 if (rpmVersionCompare(oldH, eiu->h) < 0)
503                     continue;
504                 /* same or newer package already installed */
505                 count = 0;
506                 break;
507             }
508             mi = rpmdbFreeIterator(mi);
509             if (count == 0) {
510                 eiu->h = headerFree(eiu->h);
511                 continue;
512             }
513             /* Package is newer than those currently installed. */
514         }
515
516         rc = rpmtsAddInstallElement(ts, eiu->h, (fnpyKey)fileName,
517                         (ia->installInterfaceFlags & INSTALL_UPGRADE) != 0,
518                         relocations);
519
520         /* XXX reference held by transaction set */
521         eiu->h = headerFree(eiu->h);
522         if (eiu->relocations)
523             eiu->relocations->oldPath = _free(eiu->relocations->oldPath);
524
525         switch(rc) {
526         case 0:
527             rpmlog(RPMLOG_DEBUG, _("\tadded binary package [%d]\n"),
528                         eiu->numRPMS);
529             break;
530         case 1:
531             rpmlog(RPMLOG_ERR,
532                             _("error reading from file %s\n"), *eiu->fnp);
533             eiu->numFailed++;
534             goto exit;
535             break;
536         case 2:
537             rpmlog(RPMLOG_ERR,
538                             _("file %s requires a newer version of RPM\n"),
539                             *eiu->fnp);
540             eiu->numFailed++;
541             goto exit;
542             break;
543         default:
544             eiu->numFailed++;
545             goto exit;
546             break;
547         }
548
549         eiu->numRPMS++;
550         continue;
551
552 maybe_manifest:
553         /* Try to read a package manifest. */
554         eiu->fd = Fopen(*eiu->fnp, "r.fpio");
555         if (eiu->fd == NULL || Ferror(eiu->fd)) {
556             rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp,
557                         Fstrerror(eiu->fd));
558             if (eiu->fd != NULL) {
559                 xx = Fclose(eiu->fd);
560                 eiu->fd = NULL;
561             }
562             eiu->numFailed++; *eiu->fnp = NULL;
563             break;
564         }
565
566         /* Read list of packages from manifest. */
567 /* FIX: *eiu->argv can be NULL */
568         rc = rpmReadPackageManifest(eiu->fd, &eiu->argc, &eiu->argv);
569         if (rc != RPMRC_OK)
570             rpmlog(RPMERR_MANIFEST, _("%s: not an rpm package (or package manifest): %s\n"),
571                         *eiu->fnp, Fstrerror(eiu->fd));
572         xx = Fclose(eiu->fd);
573         eiu->fd = NULL;
574
575         /* If successful, restart the query loop. */
576         if (rc == RPMRC_OK) {
577             eiu->prevx++;
578             goto restart;
579         }
580
581         eiu->numFailed++; *eiu->fnp = NULL;
582         break;
583     }
584
585     rpmlog(RPMLOG_DEBUG, _("found %d source and %d binary packages\n"),
586                 eiu->numSRPMS, eiu->numRPMS);
587
588     if (eiu->numFailed) goto exit;
589
590     if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NODEPS)) {
591
592         if (rpmtsCheck(ts)) {
593             eiu->numFailed = eiu->numPkgs;
594             stopInstall = 1;
595         }
596
597         ps = rpmtsProblems(ts);
598         if (!stopInstall && rpmpsNumProblems(ps) > 0) {
599             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
600             rpmpsPrint(NULL, ps);
601             eiu->numFailed = eiu->numPkgs;
602             stopInstall = 1;
603
604             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST))
605                 rpmtsPrintSuggests(ts);
606
607         }
608         ps = rpmpsFree(ps);
609     }
610
611     if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
612         if (rpmtsOrder(ts)) {
613             eiu->numFailed = eiu->numPkgs;
614             stopInstall = 1;
615         }
616     }
617
618     if (eiu->numRPMS && !stopInstall) {
619
620         rpmcliPackagesTotal += eiu->numSRPMS;
621
622         rpmlog(RPMLOG_DEBUG, _("installing binary packages\n"));
623
624         /* Drop added/available package indices and dependency sets. */
625         rpmtsClean(ts);
626
627         rc = rpmtsRun(ts, NULL, probFilter);
628         ps = rpmtsProblems(ts);
629
630         if (rc < 0) {
631             eiu->numFailed += eiu->numRPMS;
632         } else if (rc > 0) {
633             eiu->numFailed += rc;
634             if (rpmpsNumProblems(ps) > 0)
635                 rpmpsPrint(stderr, ps);
636         }
637         ps = rpmpsFree(ps);
638     }
639
640     if (eiu->numSRPMS && !stopInstall) {
641         if (eiu->sourceURL != NULL)
642         for (i = 0; i < eiu->numSRPMS; i++) {
643             rpmdbCheckSignals();
644             if (eiu->sourceURL[i] == NULL) continue;
645             eiu->fd = Fopen(eiu->sourceURL[i], "r.ufdio");
646             if (eiu->fd == NULL || Ferror(eiu->fd)) {
647                 rpmlog(RPMLOG_ERR, _("cannot open file %s: %s\n"),
648                            eiu->sourceURL[i], Fstrerror(eiu->fd));
649                 if (eiu->fd != NULL) {
650                     xx = Fclose(eiu->fd);
651                     eiu->fd = NULL;
652                 }
653                 continue;
654             }
655
656             if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
657                 eiu->rpmrc = rpmInstallSourcePackage(ts, eiu->fd, NULL, NULL);
658                 if (eiu->rpmrc != RPMRC_OK) eiu->numFailed++;
659             }
660
661             xx = Fclose(eiu->fd);
662             eiu->fd = NULL;
663         }
664     }
665
666 exit:
667     if (eiu->pkgURL != NULL)
668     for (i = 0; i < eiu->numPkgs; i++) {
669         if (eiu->pkgURL[i] == NULL) continue;
670         if (eiu->pkgState[i] == 1)
671             (void) Unlink(eiu->pkgURL[i]);
672         eiu->pkgURL[i] = _free(eiu->pkgURL[i]);
673     }
674     eiu->pkgState = _free(eiu->pkgState);
675     eiu->pkgURL = _free(eiu->pkgURL);
676     eiu->argv = _free(eiu->argv);
677
678     rpmtsEmpty(ts);
679
680     return eiu->numFailed;
681 }
682
683 int rpmErase(rpmts ts, struct rpmInstallArguments_s * ia,
684                 const char ** argv)
685 {
686     int count;
687     const char ** arg;
688     int numFailed = 0;
689     int stopUninstall = 0;
690     int numPackages = 0;
691     rpmVSFlags vsflags, ovsflags;
692     rpmps ps;
693
694     if (argv == NULL) return 0;
695
696     vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
697     if (ia->qva_flags & VERIFY_DIGEST)
698         vsflags |= _RPMVSF_NODIGESTS;
699     if (ia->qva_flags & VERIFY_SIGNATURE)
700         vsflags |= _RPMVSF_NOSIGNATURES;
701     if (ia->qva_flags & VERIFY_HDRCHK)
702         vsflags |= RPMVSF_NOHDRCHK;
703     ovsflags = rpmtsSetVSFlags(ts, vsflags);
704
705     if (rpmExpandNumeric("%{?_repackage_all_erasures}"))
706         ia->transFlags |= RPMTRANS_FLAG_REPACKAGE;
707
708     /* XXX suggest mechanism only meaningful when installing */
709     ia->transFlags |= RPMTRANS_FLAG_NOSUGGEST;
710
711     (void) rpmtsSetFlags(ts, ia->transFlags);
712
713 #ifdef  NOTYET  /* XXX no callbacks on erase yet */
714     {   int notifyFlags, xx;
715         notifyFlags = ia->eraseInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
716         xx = rpmtsSetNotifyCallback(ts,
717                         rpmShowProgress, (void *) ((long)notifyFlags));
718     }
719 #endif
720
721     for (arg = argv; *arg; arg++) {
722         rpmdbMatchIterator mi;
723
724         /* XXX HACK to get rpmdbFindByLabel out of the API */
725         mi = rpmtsInitIterator(ts, RPMDBI_LABEL, *arg, 0);
726         if (mi == NULL) {
727             rpmlog(RPMLOG_ERR, _("package %s is not installed\n"), *arg);
728             numFailed++;
729         } else {
730             Header h;   /* XXX iterator owns the reference */
731             count = 0;
732             while ((h = rpmdbNextIterator(mi)) != NULL) {
733                 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
734
735                 if (!(count++ == 0 || (ia->eraseInterfaceFlags & UNINSTALL_ALLMATCHES))) {
736                     rpmlog(RPMLOG_ERR, _("\"%s\" specifies multiple packages\n"),
737                         *arg);
738                     numFailed++;
739                     break;
740                 }
741                 if (recOffset) {
742                     (void) rpmtsAddEraseElement(ts, h, recOffset);
743                     numPackages++;
744                 }
745             }
746         }
747         mi = rpmdbFreeIterator(mi);
748     }
749
750     if (numFailed) goto exit;
751
752     if (!(ia->eraseInterfaceFlags & UNINSTALL_NODEPS)) {
753
754         if (rpmtsCheck(ts)) {
755             numFailed = numPackages;
756             stopUninstall = 1;
757         }
758
759         ps = rpmtsProblems(ts);
760         if (!stopUninstall && rpmpsNumProblems(ps) > 0) {
761             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
762             rpmpsPrint(NULL, ps);
763             numFailed += numPackages;
764             stopUninstall = 1;
765         }
766         ps = rpmpsFree(ps);
767     }
768
769     if (!stopUninstall && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
770         if (rpmtsOrder(ts)) {
771             numFailed += numPackages;
772             stopUninstall = 1;
773         }
774     }
775
776     if (numPackages && !stopUninstall) {
777         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_REVERSE));
778
779         /* Drop added/available package indices and dependency sets. */
780         rpmtsClean(ts);
781
782         numPackages = rpmtsRun(ts, NULL, ia->probFilter & (RPMPROB_FILTER_DISKSPACE|RPMPROB_FILTER_DISKNODES));
783         ps = rpmtsProblems(ts);
784         if (rpmpsNumProblems(ps) > 0)
785             rpmpsPrint(NULL, ps);
786         numFailed += numPackages;
787         stopUninstall = 1;
788         ps = rpmpsFree(ps);
789     }
790
791 exit:
792     rpmtsEmpty(ts);
793
794     return numFailed;
795 }
796
797 int rpmInstallSource(rpmts ts, const char * arg,
798                 const char ** specFilePtr, const char ** cookie)
799 {
800     FD_t fd;
801     int rc;
802
803
804     fd = Fopen(arg, "r.ufdio");
805     if (fd == NULL || Ferror(fd)) {
806         rpmlog(RPMLOG_ERR, _("cannot open %s: %s\n"), arg, Fstrerror(fd));
807         if (fd != NULL) (void) Fclose(fd);
808         return 1;
809     }
810
811     if (rpmIsVerbose())
812         fprintf(stdout, _("Installing %s\n"), arg);
813
814     {
815         rpmVSFlags ovsflags =
816                 rpmtsSetVSFlags(ts, (rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD));
817         rpmRC rpmrc = rpmInstallSourcePackage(ts, fd, specFilePtr, cookie);
818         rc = (rpmrc == RPMRC_OK ? 0 : 1);
819         ovsflags = rpmtsSetVSFlags(ts, ovsflags);
820     }
821     if (rc != 0) {
822         rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), arg);
823         if (specFilePtr && *specFilePtr)
824             *specFilePtr = _free(*specFilePtr);
825         if (cookie && *cookie)
826             *cookie = _free(*cookie);
827     }
828
829     (void) Fclose(fd);
830
831     return rc;
832 }
833
834 /** @todo Transaction handling, more, needs work. */
835 int rpmRollback(rpmts ts, struct rpmInstallArguments_s * ia, const char ** argv)
836 {
837     int ifmask= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL|INSTALL_ERASE);
838     unsigned thistid = 0xffffffff;
839     unsigned prevtid;
840     time_t tid;
841     IDTX itids = NULL;
842     IDTX rtids = NULL;
843     IDT rp;
844     int nrids = 0;
845     IDT ip;
846     int niids = 0;
847     int rc = 0;
848     int vsflags, ovsflags;
849     int numAdded;
850     int numRemoved;
851     rpmps ps;
852     int _unsafe_rollbacks = 0;
853     rpmtransFlags transFlags = ia->transFlags;
854
855     if (argv != NULL && *argv != NULL) {
856         rc = -1;
857         goto exit;
858     }
859
860     _unsafe_rollbacks = rpmExpandNumeric("%{?_unsafe_rollbacks}");
861
862     vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
863     if (ia->qva_flags & VERIFY_DIGEST)
864         vsflags |= _RPMVSF_NODIGESTS;
865     if (ia->qva_flags & VERIFY_SIGNATURE)
866         vsflags |= _RPMVSF_NOSIGNATURES;
867     if (ia->qva_flags & VERIFY_HDRCHK)
868         vsflags |= RPMVSF_NOHDRCHK;
869     vsflags |= RPMVSF_NEEDPAYLOAD;      /* XXX no legacy signatures */
870     ovsflags = rpmtsSetVSFlags(ts, vsflags);
871
872     (void) rpmtsSetFlags(ts, transFlags);
873
874     /*  Make the transaction a rollback transaction.  In a rollback
875      *  a best effort is what we want 
876      */
877     rpmtsSetType(ts, RPMTRANS_TYPE_ROLLBACK);
878
879     itids = IDTXload(ts, RPMTAG_INSTALLTID);
880     if (itids != NULL) {
881         ip = itids->idt;
882         niids = itids->nidt;
883     } else {
884         ip = NULL;
885         niids = 0;
886     }
887
888     {   const char * globstr = rpmExpand("%{_repackage_dir}/*.rpm", NULL);
889         if (globstr == NULL || *globstr == '%') {
890             globstr = _free(globstr);
891             rc = -1;
892             goto exit;
893         }
894         rtids = IDTXglob(ts, globstr, RPMTAG_REMOVETID);
895
896         if (rtids != NULL) {
897             rp = rtids->idt;
898             nrids = rtids->nidt;
899         } else {
900             rp = NULL;
901             nrids = 0;
902         }
903         globstr = _free(globstr);
904     }
905
906     {   int notifyFlags, xx;
907         notifyFlags = ia->installInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
908         xx = rpmtsSetNotifyCallback(ts,
909                         rpmShowProgress, (void *) ((long)notifyFlags));
910     }
911
912     /* Run transactions until rollback goal is achieved. */
913     do {
914         prevtid = thistid;
915         rc = 0;
916         rpmcliPackagesTotal = 0;
917         numAdded = 0;
918         numRemoved = 0;
919         ia->installInterfaceFlags &= ~ifmask;
920
921         /* Find larger of the remaining install/erase transaction id's. */
922         thistid = 0;
923         if (ip != NULL && ip->val.u32 > thistid)
924             thistid = ip->val.u32;
925         if (rp != NULL && rp->val.u32 > thistid)
926             thistid = rp->val.u32;
927
928         /* If we've achieved the rollback goal, then we're done. */
929         if (thistid == 0 || thistid < ia->rbtid)
930             break;
931
932         /* If we've reached the (configured) rollback goal, then we're done. */
933         if (_unsafe_rollbacks && thistid <= _unsafe_rollbacks)
934             break;
935
936         rpmtsEmpty(ts);
937         (void) rpmtsSetFlags(ts, transFlags);
938
939         /* Install the previously erased packages for this transaction. */
940         while (rp != NULL && rp->val.u32 == thistid) {
941
942             rpmlog(RPMLOG_DEBUG, "\t+++ install %s\n",
943                         (rp->key ? rp->key : "???"));
944
945             rc = rpmtsAddInstallElement(ts, rp->h, (fnpyKey)rp->key,
946                                0, ia->relocations);
947             if (rc != 0)
948                 goto exit;
949
950             numAdded++;
951             rpmcliPackagesTotal++;
952             if (!(ia->installInterfaceFlags & ifmask))
953                 ia->installInterfaceFlags |= INSTALL_UPGRADE;
954
955 #ifdef  NOTYET
956             rp->h = headerFree(rp->h);
957 #endif
958             nrids--;
959             if (nrids > 0)
960                 rp++;
961             else
962                 rp = NULL;
963         }
964
965         /* Erase the previously installed packages for this transaction. */
966         while (ip != NULL && ip->val.u32 == thistid) {
967
968             rpmlog(RPMLOG_DEBUG,
969                         "\t--- erase h#%u\n", ip->instance);
970
971             rc = rpmtsAddEraseElement(ts, ip->h, ip->instance);
972             if (rc != 0)
973                 goto exit;
974
975             numRemoved++;
976
977             if (_unsafe_rollbacks)
978                 rpmcliPackagesTotal++;
979
980             if (!(ia->installInterfaceFlags & ifmask)) {
981                 ia->installInterfaceFlags |= INSTALL_ERASE;
982                 (void) rpmtsSetFlags(ts, (transFlags | RPMTRANS_FLAG_REVERSE));
983             }
984
985 #ifdef  NOTYET
986             ip->instance = 0;
987 #endif
988             niids--;
989             if (niids > 0)
990                 ip++;
991             else
992                 ip = NULL;
993         }
994
995         /* Anything to do? */
996         if (rpmcliPackagesTotal <= 0)
997             break;
998
999         tid = (time_t)thistid;
1000         rpmlog(RPMLOG_NOTICE,
1001                 _("Rollback packages (+%d/-%d) to %-24.24s (0x%08x):\n"),
1002                         numAdded, numRemoved, ctime(&tid), tid);
1003
1004         rc = rpmtsCheck(ts);
1005         ps = rpmtsProblems(ts);
1006         if (rc != 0 && rpmpsNumProblems(ps) > 0) {
1007             rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
1008             rpmpsPrint(NULL, ps);
1009             ps = rpmpsFree(ps);
1010             goto exit;
1011         }
1012         ps = rpmpsFree(ps);
1013
1014         rc = rpmtsOrder(ts);
1015         if (rc != 0)
1016             goto exit;
1017
1018         /* Drop added/available package indices and dependency sets. */
1019         rpmtsClean(ts);
1020
1021         rc = rpmtsRun(ts, NULL, (ia->probFilter|RPMPROB_FILTER_OLDPACKAGE));
1022         ps = rpmtsProblems(ts);
1023         if (rc > 0 && rpmpsNumProblems(ps) > 0)
1024             rpmpsPrint(stderr, ps);
1025         ps = rpmpsFree(ps);
1026         if (rc)
1027             goto exit;
1028
1029         /* Clean up after successful rollback. */
1030         if (rtids && !rpmIsDebug()) {
1031             int i;
1032             rpmlog(RPMLOG_NOTICE, _("Cleaning up repackaged packages:\n"));
1033             if (rtids->idt)
1034             for (i = 0; i < rtids->nidt; i++) {
1035                 IDT rrp = rtids->idt + i;
1036                 if (rrp->val.u32 != thistid)
1037                     continue;
1038                 if (rrp->key) { /* XXX can't happen */
1039                     rpmlog(RPMLOG_NOTICE, _("\tRemoving %s:\n"), rrp->key);
1040                     (void) unlink(rrp->key);    /* XXX: Should check rc??? */
1041                 }
1042             }
1043         }
1044
1045
1046     } while (1);
1047
1048 exit:
1049     rtids = IDTXfree(rtids);
1050     itids = IDTXfree(itids);
1051
1052     rpmtsEmpty(ts);
1053     (void) rpmtsSetFlags(ts, transFlags);
1054
1055     return rc;
1056 }