/** \ingroup rpmcli * \file lib/rpminstall.c */ #include "system.h" #include "rpmcli.h" #include "manifest.h" #include "misc.h" /* XXX for rpmGlob() */ #include "debug.h" /*@access rpmTransactionSet @*/ /* XXX compared with NULL */ /*@access rpmProblemSet @*/ /* XXX compared with NULL */ /*@access Header @*/ /* XXX compared with NULL */ /*@access rpmdb @*/ /* XXX compared with NULL */ /*@access FD_t @*/ /* XXX compared with NULL */ /*@access IDTX @*/ /*@access IDT @*/ #include int fancyPercents = 0; /*@unchecked@*/ static int hashesPrinted = 0; /*@unchecked@*/ int packagesTotal = 0; /*@unchecked@*/ static int progressTotal = 0; /*@unchecked@*/ static int progressCurrent = 0; static int checkedTTY = 0; static int countWidth = 0; static int nameWidth = 29; static int hashesTotal = 50; static void checkTTY( void ) { struct winsize ws; if ( checkedTTY ) return; checkedTTY = 1; if ( ioctl( STDOUT_FILENO, TIOCGWINSZ, (char *)&ws ) < 0 ) { fancyPercents = 0; } else { int w = ws.ws_col; int i; if ( w <= 2 ) w = 80; if ( w < 39 ) { fancyPercents = 0; nameWidth = w - 2; hashesTotal = 1; return; } if ( fancyPercents ) { w -= 6; for ( i = packagesTotal; i > 0; i /= 10 ) ++countWidth; nameWidth -= countWidth + 2; } hashesTotal = w - 30; if ( hashesTotal > 100 ) { nameWidth += (hashesTotal - 100 ); hashesTotal = 100; } } } /** */ static void printHash(const unsigned long amount, const unsigned long total) /*@globals hashesPrinted, progressCurrent, fileSystem @*/ /*@modifies hashesPrinted, progressCurrent, fileSystem @*/ { checkTTY(); if ( hashesPrinted <= hashesTotal ) { int hashesNeeded = hashesTotal * (total ? (((float) amount) / total) : 1); while ( hashesNeeded > hashesPrinted ) { if ( fancyPercents ) { int i; for ( i = 0; i < hashesPrinted; ++i ) putchar( '#' ); for ( ; i < hashesTotal; ++i ) putchar( ' ' ); printf( "(%3d%%)", (int)(100 * (total ? (((float) amount) / total) : 1)) ); for ( i = 0; i < (hashesTotal + 6); ++i ) putchar( '\b' ); } else putchar( '#' ); fflush( stdout ); ++hashesPrinted; } fflush( stdout ); hashesPrinted = hashesNeeded; if ( hashesPrinted >= hashesTotal ) { if ( fancyPercents ) { int i; ++progressCurrent; for ( i = 1; i < hashesPrinted; ++i ) putchar( '#' ); printf( " [%3d%%]\n", (int)(100 * (progressTotal ? (((float) progressCurrent) / progressTotal) : 1))) ; } else putchar( '\n' ); } fflush( stdout ); } } void * rpmShowProgress(/*@null@*/ const void * arg, const rpmCallbackType what, const unsigned long amount, const unsigned long total, /*@null@*/ const void * pkgKey, /*@null@*/ void * data) /*@globals hashesPrinted, progressCurrent, progressTotal, fileSystem @*/ /*@modifies hashesPrinted, progressCurrent, progressTotal, fileSystem @*/ { /*@-castexpose@*/ Header h = (Header) arg; /*@=castexpose@*/ char * s; int flags = (int) ((long)data); void * rc = NULL; const char * filename = pkgKey; static FD_t fd = NULL; switch (what) { case RPMCALLBACK_INST_OPEN_FILE: if (filename == NULL || filename[0] == '\0') return NULL; fd = Fopen(filename, "r.ufdio"); if (fd) fd = fdLink(fd, "persist (showProgress)"); return fd; /*@notreached@*/ break; case RPMCALLBACK_INST_CLOSE_FILE: fd = fdFree(fd, "persist (showProgress)"); if (fd) { (void) Fclose(fd); fd = NULL; } break; case RPMCALLBACK_INST_START: hashesPrinted = 0; if (h == NULL || !(flags & INSTALL_LABEL)) break; if (flags & INSTALL_HASH) { s = headerSprintf(h, "%{NAME}", rpmTagTable, rpmHeaderFormats, NULL); if ( fancyPercents ) printf( "%*d: %-*.*s", countWidth, progressCurrent + 1, nameWidth, nameWidth, s ); else printf("%-*.*s", nameWidth, nameWidth, s); (void) fflush(stdout); s = _free(s); } else { s = headerSprintf(h, "%{NAME}-%{VERSION}-%{RELEASE}", rpmTagTable, rpmHeaderFormats, NULL); fprintf(stdout, "%s\n", s); (void) fflush(stdout); s = _free(s); } break; case RPMCALLBACK_TRANS_PROGRESS: case RPMCALLBACK_INST_PROGRESS: if (flags & INSTALL_PERCENT) fprintf(stdout, "%%%% %f\n", (double) (total ? ((((float) amount) / total) * 100) : 100.0)); else if (flags & INSTALL_HASH) printHash(amount, total); (void) fflush(stdout); break; case RPMCALLBACK_TRANS_START: hashesPrinted = 0; progressTotal = 1; progressCurrent = 0; if (!(flags & INSTALL_LABEL)) break; if (flags & INSTALL_HASH) { int width; checkTTY(); if ( fancyPercents ) width = countWidth + 2 + nameWidth; else width = nameWidth; printf( "%-*.*s", width, width, _("Preparing...") ); } else printf("%s\n", _("Preparing packages for installation...")); (void) fflush(stdout); break; case RPMCALLBACK_TRANS_STOP: if (flags & INSTALL_HASH) printHash(1, 1); /* Fixes "preparing..." progress bar */ progressTotal = packagesTotal; progressCurrent = 0; break; case RPMCALLBACK_UNINST_PROGRESS: case RPMCALLBACK_UNINST_START: case RPMCALLBACK_UNINST_STOP: case RPMCALLBACK_UNPACK_ERROR: case RPMCALLBACK_CPIO_ERROR: default: /* ignore */ break; } return rc; } typedef /*@only@*/ /*@null@*/ const char * str_t; struct rpmEIU { /*@only@*/ rpmTransactionSet ts; /*@only@*/ /*@null@*/ rpmdb db; Header h; FD_t fd; int numFailed; int numPkgs; /*@only@*/ str_t * pkgURL; /*@dependent@*/ /*@null@*/ str_t * fnp; /*@only@*/ char * pkgState; int prevx; int pkgx; int numRPMS; int numSRPMS; /*@only@*/ /*@null@*/ str_t * sourceURL; int isSource; int argc; /*@only@*/ /*@null@*/ str_t * argv; /*@temp@*/ rpmRelocation * relocations; rpmRC rpmrc; }; /** @todo Generalize --freshen policies. */ int rpmInstall(const char * rootdir, const char ** fileArgv, rpmtransFlags transFlags, rpmInstallInterfaceFlags interfaceFlags, rpmprobFilterFlags probFilter, rpmRelocation * relocations) { struct rpmEIU * eiu = alloca(sizeof(*eiu)); int notifyFlags = interfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 ); /*@only@*/ /*@null@*/ const char * fileURL = NULL; int stopInstall = 0; const char ** av = NULL; int ac = 0; int rc; int xx; int i; if (fileArgv == NULL) return 0; memset(eiu, 0, sizeof(*eiu)); eiu->numPkgs = 0; eiu->prevx = 0; eiu->pkgx = 0; if ((eiu->relocations = relocations) != NULL) { while (eiu->relocations->oldPath) eiu->relocations++; if (eiu->relocations->newPath == NULL) eiu->relocations = NULL; } /* Build fully globbed list of arguments in argv[argc]. */ /*@-branchstate@*/ /*@-temptrans@*/ for (eiu->fnp = fileArgv; *eiu->fnp != NULL; eiu->fnp++) { /*@=temptrans@*/ av = _free(av); ac = 0; rc = rpmGlob(*eiu->fnp, &ac, &av); if (rc || ac == 0) continue; eiu->argv = xrealloc(eiu->argv, (eiu->argc+ac+1) * sizeof(*eiu->argv)); memcpy(eiu->argv+eiu->argc, av, ac * sizeof(*av)); eiu->argc += ac; eiu->argv[eiu->argc] = NULL; } /*@=branchstate@*/ av = _free(av); ac = 0; restart: /* Allocate sufficient storage for next set of args. */ if (eiu->pkgx >= eiu->numPkgs) { eiu->numPkgs = eiu->pkgx + eiu->argc; eiu->pkgURL = xrealloc(eiu->pkgURL, (eiu->numPkgs + 1) * sizeof(*eiu->pkgURL)); memset(eiu->pkgURL + eiu->pkgx, 0, ((eiu->argc + 1) * sizeof(*eiu->pkgURL))); eiu->pkgState = xrealloc(eiu->pkgState, (eiu->numPkgs + 1) * sizeof(*eiu->pkgState)); memset(eiu->pkgState + eiu->pkgx, 0, ((eiu->argc + 1) * sizeof(*eiu->pkgState))); } /* Retrieve next set of args, cache on local storage. */ for (i = 0; i < eiu->argc; i++) { fileURL = _free(fileURL); fileURL = eiu->argv[i]; eiu->argv[i] = NULL; switch (urlIsURL(fileURL)) { case URL_IS_FTP: case URL_IS_HTTP: { const char *tfn; if (rpmIsVerbose()) fprintf(stdout, _("Retrieving %s\n"), fileURL); { char tfnbuf[64]; strcpy(tfnbuf, "rpm-xfer.XXXXXX"); (void) mktemp(tfnbuf); tfn = rpmGenPath(rootdir, "%{_tmppath}/", tfnbuf); } /* XXX undefined %{name}/%{version}/%{release} here */ /* XXX %{_tmpdir} does not exist */ rpmMessage(RPMMESS_DEBUG, _(" ... as %s\n"), tfn); rc = urlGetFile(fileURL, tfn); if (rc < 0) { rpmMessage(RPMMESS_ERROR, _("skipping %s - transfer failed - %s\n"), fileURL, ftpStrerror(rc)); eiu->numFailed++; eiu->pkgURL[eiu->pkgx] = NULL; tfn = _free(tfn); /*@switchbreak@*/ break; } eiu->pkgState[eiu->pkgx] = 1; eiu->pkgURL[eiu->pkgx] = tfn; eiu->pkgx++; } /*@switchbreak@*/ break; case URL_IS_PATH: default: eiu->pkgURL[eiu->pkgx] = fileURL; fileURL = NULL; eiu->pkgx++; /*@switchbreak@*/ break; } } fileURL = _free(fileURL); if (eiu->numFailed) goto exit; /* Continue processing file arguments, building transaction set. */ for (eiu->fnp = eiu->pkgURL+eiu->prevx; *eiu->fnp != NULL; eiu->fnp++, eiu->prevx++) { const char * fileName; rpmMessage(RPMMESS_DEBUG, "============== %s\n", *eiu->fnp); (void) urlPath(*eiu->fnp, &fileName); /* Try to read the header from a package file. */ eiu->fd = Fopen(*eiu->fnp, "r.ufdio"); if (eiu->fd == NULL || Ferror(eiu->fd)) { rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp, Fstrerror(eiu->fd)); if (eiu->fd) { xx = Fclose(eiu->fd); eiu->fd = NULL; } eiu->numFailed++; *eiu->fnp = NULL; continue; } /*@-mustmod@*/ /* LCL: segfault */ eiu->rpmrc = rpmReadPackageHeader(eiu->fd, &eiu->h, &eiu->isSource, NULL, NULL); /*@=mustmod@*/ xx = Fclose(eiu->fd); eiu->fd = NULL; if (eiu->rpmrc == RPMRC_FAIL || eiu->rpmrc == RPMRC_SHORTREAD) { eiu->numFailed++; *eiu->fnp = NULL; continue; } if (eiu->isSource && (eiu->rpmrc == RPMRC_OK || eiu->rpmrc == RPMRC_BADSIZE)) { if (!(geteuid() || rpmExpandNumeric("%{?_allow_root_build}"))) { rpmError(RPMMESS_ERROR, _("%s: current site policy disallows root to install source packages\n"), fileName); eiu->numFailed++; *eiu->fnp = NULL; continue; } rpmMessage(RPMMESS_DEBUG, "\tadded source package [%d]\n", eiu->numSRPMS); eiu->sourceURL = xrealloc(eiu->sourceURL, (eiu->numSRPMS + 2) * sizeof(*eiu->sourceURL)); eiu->sourceURL[eiu->numSRPMS] = *eiu->fnp; *eiu->fnp = NULL; eiu->numSRPMS++; eiu->sourceURL[eiu->numSRPMS] = NULL; continue; } if (eiu->rpmrc == RPMRC_OK || eiu->rpmrc == RPMRC_BADSIZE) { if (eiu->db == NULL) { int mode = (transFlags & RPMTRANS_FLAG_TEST) ? O_RDONLY : (O_RDWR | O_CREAT); if (rpmdbOpen(rootdir, &eiu->db, mode, 0644)) { const char *dn; dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL); rpmMessage(RPMMESS_ERROR, _("cannot open Packages database in %s\n"), dn); dn = _free(dn); eiu->numFailed++; *eiu->fnp = NULL; break; } /*@-onlytrans@*/ eiu->ts = rpmtransCreateSet(eiu->db, rootdir); /*@=onlytrans@*/ } if (eiu->relocations) { const char ** paths; int pft; int c; if (headerGetEntry(eiu->h, RPMTAG_PREFIXES, &pft, (void **) &paths, &c) && (c == 1)) { eiu->relocations->oldPath = xstrdup(paths[0]); paths = headerFreeData(paths, pft); } else { const char * name; xx = headerNVR(eiu->h, &name, NULL, NULL); rpmMessage(RPMMESS_ERROR, _("package %s is not relocateable\n"), name); eiu->numFailed++; goto exit; /*@notreached@*/ } } /* On --freshen, verify package is installed and newer */ if (interfaceFlags & INSTALL_FRESHEN) { rpmdbMatchIterator mi; const char * name; Header oldH; int count; xx = headerNVR(eiu->h, &name, NULL, NULL); /*@-onlytrans@*/ mi = rpmdbInitIterator(eiu->db, RPMTAG_NAME, name, 0); /*@=onlytrans@*/ count = rpmdbGetIteratorCount(mi); while ((oldH = rpmdbNextIterator(mi)) != NULL) { if (rpmVersionCompare(oldH, eiu->h) < 0) /*@innercontinue@*/ continue; /* same or newer package already installed */ count = 0; /*@innerbreak@*/ break; } mi = rpmdbFreeIterator(mi); if (count == 0) { eiu->h = headerFree(eiu->h); continue; } /* Package is newer than those currently installed. */ } rc = rpmtransAddPackage(eiu->ts, eiu->h, NULL, fileName, (interfaceFlags & INSTALL_UPGRADE) != 0, relocations); /* XXX reference held by transaction set */ eiu->h = headerFree(eiu->h); if (eiu->relocations) eiu->relocations->oldPath = _free(eiu->relocations->oldPath); switch(rc) { case 0: rpmMessage(RPMMESS_DEBUG, "\tadded binary package [%d]\n", eiu->numRPMS); /*@switchbreak@*/ break; case 1: rpmMessage(RPMMESS_ERROR, _("error reading from file %s\n"), *eiu->fnp); eiu->numFailed++; goto exit; /*@notreached@*/ /*@switchbreak@*/ break; case 2: rpmMessage(RPMMESS_ERROR, _("file %s requires a newer version of RPM\n"), *eiu->fnp); eiu->numFailed++; goto exit; /*@notreached@*/ /*@switchbreak@*/ break; } eiu->numRPMS++; continue; } if (eiu->rpmrc != RPMRC_BADMAGIC) { rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), *eiu->fnp); eiu->numFailed++; *eiu->fnp = NULL; break; } /* Try to read a package manifest. */ eiu->fd = Fopen(*eiu->fnp, "r.fpio"); if (eiu->fd == NULL || Ferror(eiu->fd)) { rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *eiu->fnp, Fstrerror(eiu->fd)); if (eiu->fd) { xx = Fclose(eiu->fd); eiu->fd = NULL; } eiu->numFailed++; *eiu->fnp = NULL; break; } /* Read list of packages from manifest. */ rc = rpmReadPackageManifest(eiu->fd, &eiu->argc, &eiu->argv); if (rc) rpmError(RPMERR_MANIFEST, _("%s: read manifest failed: %s\n"), *eiu->fnp, Fstrerror(eiu->fd)); xx = Fclose(eiu->fd); eiu->fd = NULL; /* If successful, restart the query loop. */ if (rc == 0) { eiu->prevx++; goto restart; } eiu->numFailed++; *eiu->fnp = NULL; break; } rpmMessage(RPMMESS_DEBUG, _("found %d source and %d binary packages\n"), eiu->numSRPMS, eiu->numRPMS); if (eiu->numFailed) goto exit; if (eiu->numRPMS && !(interfaceFlags & INSTALL_NODEPS)) { rpmDependencyConflict conflicts; int numConflicts; if (rpmdepCheck(eiu->ts, &conflicts, &numConflicts)) { eiu->numFailed = eiu->numPkgs; stopInstall = 1; } /*@-branchstate@*/ if (!stopInstall && conflicts) { rpmMessage(RPMMESS_ERROR, _("failed dependencies:\n")); printDepProblems(stderr, conflicts, numConflicts); conflicts = rpmdepFreeConflicts(conflicts, numConflicts); eiu->numFailed = eiu->numPkgs; stopInstall = 1; } /*@=branchstate@*/ } if (eiu->numRPMS && !stopInstall && !(interfaceFlags & INSTALL_NOORDER)) { if (rpmdepOrder(eiu->ts)) { eiu->numFailed = eiu->numPkgs; stopInstall = 1; } } if (eiu->numRPMS && !stopInstall) { rpmProblemSet probs = NULL; packagesTotal = eiu->numRPMS + eiu->numSRPMS; rpmMessage(RPMMESS_DEBUG, _("installing binary packages\n")); rc = rpmRunTransactions(eiu->ts, rpmShowProgress, (void *) ((long)notifyFlags), NULL, &probs, transFlags, probFilter); if (rc < 0) { eiu->numFailed += eiu->numRPMS; } else if (rc > 0) { eiu->numFailed += rc; rpmProblemSetPrint(stderr, probs); } if (probs != NULL) rpmProblemSetFree(probs); } if (eiu->numSRPMS && !stopInstall) { if (eiu->sourceURL != NULL) for (i = 0; i < eiu->numSRPMS; i++) { if (eiu->sourceURL[i] == NULL) continue; eiu->fd = Fopen(eiu->sourceURL[i], "r.ufdio"); if (eiu->fd == NULL || Ferror(eiu->fd)) { rpmMessage(RPMMESS_ERROR, _("cannot open file %s: %s\n"), eiu->sourceURL[i], Fstrerror(eiu->fd)); if (eiu->fd) { xx = Fclose(eiu->fd); eiu->fd = NULL; } continue; } if (!(transFlags & RPMTRANS_FLAG_TEST)) { eiu->rpmrc = rpmInstallSourcePackage(rootdir, eiu->fd, NULL, rpmShowProgress, (void *) ((long)notifyFlags), NULL); if (eiu->rpmrc != RPMRC_OK) eiu->numFailed++; } xx = Fclose(eiu->fd); eiu->fd = NULL; } } exit: eiu->ts = rpmtransFree(eiu->ts); if (eiu->pkgURL != NULL) for (i = 0; i < eiu->numPkgs; i++) { if (eiu->pkgURL[i] == NULL) continue; if (eiu->pkgState[i] == 1) (void) Unlink(eiu->pkgURL[i]); eiu->pkgURL[i] = _free(eiu->pkgURL[i]); } eiu->pkgState = _free(eiu->pkgState); eiu->pkgURL = _free(eiu->pkgURL); eiu->argv = _free(eiu->argv); if (eiu->db != NULL) { xx = rpmdbClose(eiu->db); eiu->db = NULL; } return eiu->numFailed; } int rpmErase(const char * rootdir, const char ** argv, rpmtransFlags transFlags, rpmEraseInterfaceFlags interfaceFlags) { rpmdb db; int mode; int count; const char ** arg; int numFailed = 0; rpmTransactionSet ts; rpmDependencyConflict conflicts; int numConflicts; int stopUninstall = 0; int numPackages = 0; rpmProblemSet probs; if (argv == NULL) return 0; if (transFlags & RPMTRANS_FLAG_TEST) mode = O_RDONLY; else mode = O_RDWR | O_EXCL; if (rpmdbOpen(rootdir, &db, mode, 0644)) { const char *dn; dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL); rpmMessage(RPMMESS_ERROR, _("cannot open %s/packages.rpm\n"), dn); dn = _free(dn); return -1; } ts = rpmtransCreateSet(db, rootdir); for (arg = argv; *arg; arg++) { rpmdbMatchIterator mi; /* XXX HACK to get rpmdbFindByLabel out of the API */ mi = rpmdbInitIterator(db, RPMDBI_LABEL, *arg, 0); count = rpmdbGetIteratorCount(mi); if (count <= 0) { rpmMessage(RPMMESS_ERROR, _("package %s is not installed\n"), *arg); numFailed++; } else if (!(count == 1 || (interfaceFlags & UNINSTALL_ALLMATCHES))) { rpmMessage(RPMMESS_ERROR, _("\"%s\" specifies multiple packages\n"), *arg); numFailed++; } else { Header h; /* XXX iterator owns the reference */ while ((h = rpmdbNextIterator(mi)) != NULL) { unsigned int recOffset = rpmdbGetIteratorOffset(mi); if (recOffset) { (void) rpmtransRemovePackage(ts, recOffset); numPackages++; } } } mi = rpmdbFreeIterator(mi); } if (!(interfaceFlags & UNINSTALL_NODEPS)) { if (rpmdepCheck(ts, &conflicts, &numConflicts)) { numFailed = numPackages; stopUninstall = 1; } /*@-branchstate@*/ if (!stopUninstall && conflicts) { rpmMessage(RPMMESS_ERROR, _("removing these packages would break " "dependencies:\n")); printDepProblems(stderr, conflicts, numConflicts); conflicts = rpmdepFreeConflicts(conflicts, numConflicts); numFailed += numPackages; stopUninstall = 1; } /*@=branchstate@*/ } #ifdef NOTYET if (!stopUninstall && !(interfaceFlags & INSTALL_NOORDER)) { if (rpmdepOrder(ts)) { numFailed += numPackages; stopUninstall = 1; } } #endif if (!stopUninstall) { transFlags |= RPMTRANS_FLAG_REVERSE; numFailed += rpmRunTransactions(ts, NULL, NULL, NULL, &probs, transFlags, 0); } ts = rpmtransFree(ts); (void) rpmdbClose(db); return numFailed; } int rpmInstallSource(const char * rootdir, const char * arg, const char ** specFile, char ** cookie) { FD_t fd; int rc; fd = Fopen(arg, "r.ufdio"); if (fd == NULL || Ferror(fd)) { rpmMessage(RPMMESS_ERROR, _("cannot open %s: %s\n"), arg, Fstrerror(fd)); if (fd) (void) Fclose(fd); return 1; } if (rpmIsVerbose()) fprintf(stdout, _("Installing %s\n"), arg); { /*@-mayaliasunique@*/ rpmRC rpmrc = rpmInstallSourcePackage(rootdir, fd, specFile, NULL, NULL, cookie); /*@=mayaliasunique@*/ rc = (rpmrc == RPMRC_OK ? 0 : 1); } if (rc != 0) { rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), arg); /*@-unqualifiedtrans@*/ if (specFile && *specFile) *specFile = _free(*specFile); if (cookie && *cookie) *cookie = _free(*cookie); /*@=unqualifiedtrans@*/ } (void) Fclose(fd); return rc; } /*@unchecked@*/ static int reverse = -1; /** */ static int IDTintcmp(const void * a, const void * b) /*@*/ { /*@-castexpose@*/ IDT ap = (IDT)a; IDT bp = (IDT)b; /*@=castexpose@*/ int rc = ((int)ap->val.u32 - (int)bp->val.u32); if (rc) return ( reverse * rc ); return ( strcmp(ap->n, bp->n) ); } IDTX IDTXfree(IDTX idtx) { if (idtx) { int i; if (idtx->idt) for (i = 0; i < idtx->nidt; i++) { IDT idt = idtx->idt + i; idt->h = headerFree(idt->h); idt->key = _free(idt->key); } idtx->idt = _free(idtx->idt); idtx = _free(idtx); } return NULL; } IDTX IDTXnew(void) { IDTX idtx = xcalloc(1, sizeof(*idtx)); idtx->delta = 10; idtx->size = sizeof(*((IDT)0)); return idtx; } IDTX IDTXgrow(IDTX idtx, int need) { if (need < 0) return NULL; if (idtx == NULL) idtx = IDTXnew(); if (need == 0) return idtx; if ((idtx->nidt + need) > idtx->alloced) { while (need > 0) { idtx->alloced += idtx->delta; need -= idtx->delta; } idtx->idt = xrealloc(idtx->idt, (idtx->alloced * idtx->size) ); } return idtx; } IDTX IDTXsort(IDTX idtx) { if (idtx != NULL && idtx->idt != NULL && idtx->nidt > 0) qsort(idtx->idt, idtx->nidt, idtx->size, IDTintcmp); return idtx; } IDTX IDTXload(rpmdb db, rpmTag tag) { IDTX idtx = NULL; rpmdbMatchIterator mi; HGE_t hge = (HGE_t) headerGetEntry; Header h; mi = rpmdbInitIterator(db, tag, NULL, 0); /*@-branchstate@*/ while ((h = rpmdbNextIterator(mi)) != NULL) { rpmTagType type = RPM_NULL_TYPE; int_32 count = 0; int_32 * tidp; tidp = NULL; if (!hge(h, tag, &type, (void **)&tidp, &count) || tidp == NULL) continue; if (type == RPM_INT32_TYPE && (*tidp == 0 || *tidp == -1)) continue; idtx = IDTXgrow(idtx, 1); if (idtx == NULL) continue; if (idtx->idt == NULL) continue; { IDT idt; /*@-nullderef@*/ idt = idtx->idt + idtx->nidt; /*@=nullderef@*/ idt->h = headerLink(h); (void) headerNVR(idt->h, &idt->n, &idt->v, &idt->r); idt->key = NULL; idt->instance = rpmdbGetIteratorOffset(mi); idt->val.u32 = *tidp; } idtx->nidt++; } /*@=branchstate@*/ mi = rpmdbFreeIterator(mi); return IDTXsort(idtx); } IDTX IDTXglob(const char * globstr, rpmTag tag) { IDTX idtx = NULL; HGE_t hge = (HGE_t) headerGetEntry; Header h; int_32 * tidp; FD_t fd; const char ** av = NULL; int ac = 0; int rc; int i; av = NULL; ac = 0; rc = rpmGlob(globstr, &ac, &av); if (rc == 0) for (i = 0; i < ac; i++) { rpmTagType type; int_32 count; int isSource; rpmRC rpmrc; fd = Fopen(av[i], "r.ufdio"); if (fd == NULL || Ferror(fd)) { rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), av[i], Fstrerror(fd)); if (fd) (void) Fclose(fd); continue; } /*@-mustmod@*/ /* LCL: segfault */ rpmrc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL); /*@=mustmod@*/ if (rpmrc != RPMRC_OK || isSource) { (void) Fclose(fd); continue; } tidp = NULL; /*@-branchstate@*/ if (hge(h, tag, &type, (void **) &tidp, &count) && tidp) { idtx = IDTXgrow(idtx, 1); if (idtx == NULL || idtx->idt == NULL) { h = headerFree(h); (void) Fclose(fd); continue; } { IDT idt; idt = idtx->idt + idtx->nidt; idt->h = headerLink(h); (void) headerNVR(idt->h, &idt->n, &idt->v, &idt->r); idt->key = xstrdup(av[i]); idt->instance = 0; idt->val.u32 = *tidp; } idtx->nidt++; } /*@=branchstate@*/ h = headerFree(h); (void) Fclose(fd); } for (i = 0; i < ac; i++) av[i] = _free(av[i]); av = _free(av); ac = 0; return IDTXsort(idtx); } int rpmRollback(struct rpmInstallArguments_s * ia, const char ** argv) { rpmdb db = NULL; rpmTransactionSet ts = NULL; rpmDependencyConflict conflicts = NULL; int numConflicts = 0; rpmProblemSet probs = NULL; IDTX itids = NULL; IDTX rtids = NULL; unsigned thistid = 0xffffffff; unsigned prevtid; time_t tid; IDT rp; int nrids = 0; IDT ip; int niids = 0; int packagesIn; int packagesOut; int rc; int i; int ifmask= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL|INSTALL_ERASE); if (argv != NULL && *argv != NULL) { rc = -1; goto exit; } rc = rpmdbOpen(ia->rootdir, &db, O_RDWR, 0644); if (rc != 0) goto exit; itids = IDTXload(db, RPMTAG_INSTALLTID); if (itids != NULL) { ip = itids->idt; niids = itids->nidt; } else { ip = NULL; niids = 0; } { const char * globstr = rpmExpand("%{_repackage_dir}/*.rpm", NULL); if (globstr == NULL || *globstr == '%') { globstr = _free(globstr); rc = -1; goto exit; } rtids = IDTXglob(globstr, RPMTAG_REMOVETID); if (rtids != NULL) { rp = rtids->idt; nrids = rtids->nidt; } else { rp = NULL; nrids = 0; } globstr = _free(globstr); } /* Run transactions until rollback goal is achieved. */ do { prevtid = thistid; rc = 0; packagesTotal = 0; packagesIn = 0; packagesOut = 0; ia->installInterfaceFlags &= ~ifmask; /* Find larger of the remaining install/erase transaction id's. */ thistid = 0; if (ip != NULL && ip->val.u32 > thistid) thistid = ip->val.u32; if (rp != NULL && rp->val.u32 > thistid) thistid = rp->val.u32; /* If we've achieved the rollback goal, then we're done. */ if (thistid == 0 || thistid < ia->rbtid) break; ts = rpmtransCreateSet(db, ia->rootdir); /* Install the previously erased packages for this transaction. */ while (rp != NULL && rp->val.u32 == thistid) { rpmMessage(RPMMESS_DEBUG, "\t+++ %s-%s-%s\t(from %s)\n", rp->n, rp->v, rp->r, basename(rp->key)); if (!(ia->installInterfaceFlags & ifmask)) ia->installInterfaceFlags |= INSTALL_INSTALL; rc = rpmtransAddPackage(ts, rp->h, NULL, rp->key, (ia->installInterfaceFlags & INSTALL_UPGRADE) != 0, ia->relocations); if (rc != 0) goto exit; packagesTotal++; packagesIn++; #ifdef NOTYET rp->h = headerFree(rp->h); #endif nrids--; if (nrids > 0) rp++; else rp = NULL; } /* * XXX OK, let's prevent disaster right here, as rollbacks will merrily * XXX erase everything in order to achieve the desired goal. */ if (packagesIn == 0) break; /* Erase the previously installed packages for this transaction. */ while (ip != NULL && ip->val.u32 == thistid) { rpmMessage(RPMMESS_DEBUG, "\t--- %s-%s-%s\t(from rpmdb instance #%u)\n", ip->n, ip->v, ip->r, ip->instance); if (!(ia->installInterfaceFlags & ifmask)) ia->installInterfaceFlags |= INSTALL_ERASE; rc = rpmtransRemovePackage(ts, ip->instance); if (rc != 0) goto exit; packagesTotal++; packagesOut++; #ifdef NOTYET ip->instance = 0; #endif niids--; if (niids > 0) ip++; else ip = NULL; } /* Anything to do? */ if (packagesTotal <= 0) break; tid = (time_t)thistid; rpmMessage(RPMMESS_DEBUG, _("rollback (+%d,-%d) packages to %s"), packagesIn, packagesOut, ctime(&tid)); conflicts = NULL; numConflicts = 0; rc = rpmdepCheck(ts, &conflicts, &numConflicts); if (rc != 0) { rpmMessage(RPMMESS_ERROR, _("failed dependencies:\n")); printDepProblems(stderr, conflicts, numConflicts); conflicts = rpmdepFreeConflicts(conflicts, numConflicts); goto exit; } rc = rpmdepOrder(ts); if (rc != 0) goto exit; probs = NULL; rc = rpmRunTransactions(ts, rpmShowProgress, (void *) ((long)ia->installInterfaceFlags), NULL, &probs, ia->transFlags, (ia->probFilter|RPMPROB_FILTER_OLDPACKAGE)); if (rc > 0) { rpmProblemSetPrint(stderr, probs); if (probs != NULL) rpmProblemSetFree(probs); probs = NULL; } if (rc) goto exit; ts = rpmtransFree(ts); /* Clean up after successful rollback. */ if (!rpmIsDebug()) for (i = 0; i < rtids->nidt; i++) { IDT rrp = rtids->idt + i; if (rrp->val.u32 != thistid) continue; (void) unlink(rrp->key); } } while (1); exit: ts = rpmtransFree(ts); if (db != NULL) (void) rpmdbClose(db); rtids = IDTXfree(rtids); itids = IDTXfree(itids); return rc; }