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