7 #include <rpm/rpmcli.h> /* XXX rpmcliPackagesTotal */
9 #include <rpm/rpmlib.h> /* rpmVersionCompare, rpmlib provides */
10 #include <rpm/rpmtag.h>
11 #include <rpm/rpmlog.h>
12 #include <rpm/rpmdb.h>
13 #include <rpm/rpmds.h>
14 #include <rpm/rpmfi.h>
16 #include "lib/rpmts_internal.h"
17 #include "lib/rpmte_internal.h"
21 const char * const rpmNAME = PACKAGE;
23 const char * const rpmEVR = VERSION;
25 const int rpmFLAGS = RPMSENSE_EQUAL;
28 static rpmds rpmlibP = NULL;
34 #define HASHTYPE depCache
35 #define HTKEYTYPE const char *
36 #define HTDATATYPE int
37 #include "lib/rpmhash.H"
38 #include "lib/rpmhash.C"
41 * Compare removed package instances (qsort/bsearch).
42 * @param a 1st instance address
43 * @param b 2nd instance address
44 * @return result of comparison
46 static int intcmp(const void * a, const void * b)
50 int rc = (*aptr - *bptr);
55 * Add removed package instance to ordered transaction set.
56 * @param ts transaction set
58 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
59 * @return 0 on success
61 static int removePackage(tsMembers tsmem, Header h, rpmte depends)
64 unsigned int dboffset = headerGetInstance(h);
66 /* Can't remove what's not installed */
67 if (dboffset == 0) return 1;
69 /* Filter out duplicate erasures. */
70 if (tsmem->numRemovedPackages > 0 && tsmem->removedPackages != NULL) {
71 if (bsearch(&dboffset,
72 tsmem->removedPackages, tsmem->numRemovedPackages,
73 sizeof(*tsmem->removedPackages), intcmp) != NULL)
77 if (tsmem->numRemovedPackages == tsmem->allocedRemovedPackages) {
78 tsmem->allocedRemovedPackages += tsmem->delta;
79 tsmem->removedPackages = xrealloc(tsmem->removedPackages,
80 sizeof(tsmem->removedPackages) * tsmem->allocedRemovedPackages);
83 if (tsmem->removedPackages != NULL) { /* XXX can't happen. */
84 tsmem->removedPackages[tsmem->numRemovedPackages] = dboffset;
85 tsmem->numRemovedPackages++;
86 if (tsmem->numRemovedPackages > 1)
87 qsort(tsmem->removedPackages, tsmem->numRemovedPackages,
88 sizeof(*tsmem->removedPackages), intcmp);
91 if (tsmem->orderCount >= tsmem->orderAlloced) {
92 tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
93 tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
96 p = rpmteNew(NULL, h, TR_REMOVED, NULL, NULL, -1);
97 rpmteSetDependsOn(p, depends);
99 tsmem->order[tsmem->orderCount] = p;
105 /* Return rpmdb iterator with removals pruned out */
106 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmTag tag, const char * key)
108 rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
109 tsMembers tsmem = rpmtsMembers(ts);
110 rpmdbPruneIterator(mi, tsmem->removedPackages, tsmem->numRemovedPackages,1);
114 #define skipColor(_tscolor, _color, _ocolor) \
115 ((_tscolor) && (_color) && (_ocolor) && !((_color) & (_ocolor)))
117 /* Add erase elements for older packages of same color (if any). */
118 static void addUpgradeErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
119 rpmte p, rpm_color_t hcolor, Header h)
122 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
124 while((oh = rpmdbNextIterator(mi)) != NULL) {
125 /* Ignore colored packages not in our rainbow. */
126 if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
129 /* Skip packages that contain identical NEVR. */
130 if (rpmVersionCompare(h, oh) == 0)
133 removePackage(tsmem, oh, p);
135 mi = rpmdbFreeIterator(mi);
138 /* Add erase elements for obsoleted packages of same color (if any). */
139 static void addObsoleteErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
140 rpmte p, rpm_color_t hcolor)
142 rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
145 while (rpmdsNext(obsoletes) >= 0) {
147 rpmdbMatchIterator mi = NULL;
149 if ((Name = rpmdsN(obsoletes)) == NULL)
150 continue; /* XXX can't happen */
152 /* XXX avoid self-obsoleting packages. */
153 if (rstreq(rpmteN(p), Name))
156 mi = rpmtsPrunedIterator(ts, RPMTAG_NAME, Name);
158 while((oh = rpmdbNextIterator(mi)) != NULL) {
159 /* Ignore colored packages not in our rainbow. */
160 if (skipColor(tscolor, hcolor,
161 headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
165 * Rpm prior to 3.0.3 does not have versioned obsoletes.
166 * If no obsoletes version info is available, match all names.
168 if (rpmdsEVR(obsoletes) == NULL
169 || rpmdsAnyMatchesDep(oh, obsoletes, _rpmds_nopromote)) {
170 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
171 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
172 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
173 ohNEVRA = _free(ohNEVRA);
175 removePackage(tsmem, oh, p);
178 mi = rpmdbFreeIterator(mi);
183 * Check for previously added versions and obsoletions.
184 * Return index where to place this element, or -1 to skip.
186 static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade)
190 const char * arch = headerGetString(h, RPMTAG_ARCH);
191 const char * os = headerGetString(h, RPMTAG_OS);
193 rpmds oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS));
194 rpmds newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER));
195 rpmds sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL));
196 rpmds obsChk = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0);
197 rpmtsi pi = rpmtsiInit(ts);
199 /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */
200 for (oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
201 rpmds this, obsoletes;
203 /* Only added binary packages need checking */
204 if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p))
207 /* Skip packages obsoleted by already added packages */
208 obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
209 while (rpmdsNext(obsoletes) >= 0) {
210 if (rpmdsCompare(obsoletes, sameChk)) {
217 /* Replace already added obsoleted packages by obsoleting package */
218 this = rpmteDS(p, RPMTAG_NAME);
220 while (rpmdsNext(obsChk) >= 0) {
221 if (rpmdsCompare(obsChk, this)) {
231 const char * parch = rpmteA(p);
232 const char * pos = rpmteO(p);
234 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
236 if (!rstreq(arch, parch) || !rstreq(os, pos))
241 * Always skip identical NEVR.
242 * On upgrade, if newer NEVR was previously added, skip adding older.
244 if (rpmdsCompare(sameChk, this) ||
245 (upgrade && rpmdsCompare(newChk, this))) {
250 /* On upgrade, if older NEVR was previously added, replace with new */
251 if (upgrade && rpmdsCompare(oldChk, this) != 0) {
256 /* If we broke out of the loop early we've something to say */
257 if (p != NULL && rpmIsVerbose()) {
258 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
259 const char *msg = (oc < 0) ?
260 _("package %s was already added, skipping %s\n") :
261 _("package %s was already added, replacing with %s\n");
262 rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), nevra);
275 int rpmtsAddInstallElement(rpmts ts, Header h,
276 fnpyKey key, int upgrade, rpmRelocation * relocs)
278 tsMembers tsmem = rpmtsMembers(ts);
279 rpm_color_t tscolor = rpmtsColor(ts);
281 int isSource = headerIsSource(h);
283 int oc = tsmem->orderCount;
285 /* Check for supported payload format if it's a package */
286 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
291 /* Check binary packages for redundancies in the set */
293 oc = findPos(ts, tscolor, h, upgrade);
294 /* If we're replacing a previously added element, free the old one */
295 if (oc >= 0 && oc < tsmem->orderCount) {
296 rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
297 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
298 /* If newer NEVR was already added, we're done */
304 if (tsmem->addedPackages == NULL) {
305 tsmem->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
308 if (oc >= tsmem->orderAlloced) {
309 tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
310 tsmem->order = xrealloc(tsmem->order,
311 tsmem->orderAlloced * sizeof(*tsmem->order));
314 p = rpmteNew(NULL, h, TR_ADDED, key, relocs, -1);
316 tsmem->order[oc] = p;
317 if (oc == tsmem->orderCount) {
319 tsmem->numAddedPackages++;
320 rpmcliPackagesTotal++;
323 rpmalAdd(tsmem->addedPackages, p);
325 /* If not upgrading or a source package, then we're done. */
326 if (!(upgrade & 0x1) || isSource)
329 /* Do lazy (readonly?) open of rpm database. */
330 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
331 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
335 /* Add erasure elements for old versions and obsoletions */
336 addUpgradeErasures(ts, tsmem, tscolor, p, rpmteColor(p), h);
337 addObsoleteErasures(ts, tsmem, tscolor, p, rpmteColor(p));
343 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
345 return removePackage(rpmtsMembers(ts), h, NULL);
349 * Check dep for an unsatisfied dependency.
350 * @param ts transaction set
351 * @param dep dependency
352 * @return 0 if satisfied, 1 if not satisfied
354 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
356 tsMembers tsmem = rpmtsMembers(ts);
357 rpmdbMatchIterator mi;
358 const char * Name = rpmdsN(dep);
359 const char * DNEVR = rpmdsDNEVR(dep);
364 int *cachedrc = NULL;
366 int adding = (rpmdsInstance(dep) == 0);
367 rpmsenseFlags dsflags = rpmdsFlags(dep);
370 rc = 0; /* assume dependency is satisfied */
373 * New features in rpm packaging implicitly add versioned dependencies
374 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
375 * Check those dependencies now.
377 if (dsflags & RPMSENSE_RPMLIB) {
378 static int oneshot = -1;
380 oneshot = rpmdsRpmlib(&rpmlibP, NULL);
382 if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
383 rpmdsNotify(dep, "(rpmlib provides)", rc);
389 /* Dont look at pre-requisites of already installed packages */
390 if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
393 /* Pretrans dependencies can't be satisfied by added packages. */
394 if (!(dsflags & RPMSENSE_PRETRANS) &&
395 rpmalSatisfiesDepend(tsmem->addedPackages, dep) != NULL) {
399 /* See if we already looked this up */
400 if (depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) {
402 rpmdsNotify(dep, "(cached)", rc);
405 /* Only bother caching the expensive rpmdb lookups */
408 if (Name[0] == '/') {
409 mi = rpmtsPrunedIterator(ts, RPMTAG_BASENAMES, Name);
411 while ((h = rpmdbNextIterator(mi)) != NULL) {
412 rpmdsNotify(dep, "(db files)", rc);
413 mi = rpmdbFreeIterator(mi);
416 mi = rpmdbFreeIterator(mi);
419 mi = rpmtsPrunedIterator(ts, RPMTAG_PROVIDENAME, Name);
420 while ((h = rpmdbNextIterator(mi)) != NULL) {
421 if (rpmdsAnyMatchesDep(h, dep, _rpmds_nopromote)) {
422 rpmdsNotify(dep, "(db provides)", rc);
423 mi = rpmdbFreeIterator(mi);
427 mi = rpmdbFreeIterator(mi);
430 * Search for an unsatisfied dependency.
432 if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS) &&
433 !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
434 xx = rpmtsSolve(ts, dep);
444 rc = 1; /* dependency is unsatisfied */
445 rpmdsNotify(dep, NULL, rc);
449 char *key = xstrdup(DNEVR);
450 depCacheAddEntry(dcache, key, rc);
456 /* Check a dependency set for problems */
457 static void checkDS(rpmts ts, depCache dcache, rpmte te,
458 const char * pkgNEVRA, rpmds ds,
459 const char * depName, rpm_color_t tscolor)
462 /* require-problems are unsatisfied, others appear "satisfied" */
463 int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
466 while (rpmdsNext(ds) >= 0) {
467 /* Filter out dependencies that came along for the ride. */
468 if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
471 /* Ignore colored dependencies not in our rainbow. */
472 dscolor = rpmdsColor(ds);
473 if (tscolor && dscolor && !(tscolor & dscolor))
476 if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
477 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
481 /* Check a given dependency type against installed packages */
482 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
483 rpmTag depTag, const char *dep)
486 rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep);
488 while ((h = rpmdbNextIterator(mi)) != NULL) {
489 char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
490 rpmds ds = rpmdsNew(h, depTag, 0);
492 checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
495 pkgNEVRA = _free(pkgNEVRA);
497 rpmdbFreeIterator(mi);
500 int rpmtsCheck(rpmts ts)
502 rpm_color_t tscolor = rpmtsColor(ts);
503 rpmtsi pi = NULL; rpmte p;
506 depCache dcache = NULL;
508 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
510 /* Do lazy, readonly, open of rpm database. */
511 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
512 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
517 /* XXX FIXME: figure some kind of heuristic for the cache size */
518 dcache = depCacheCreate(5001, hashFunctionString, strcmp,
519 (depCacheFreeKey)rfree, NULL);
522 * Look at all of the added packages and make sure their dependencies
526 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
527 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
529 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
530 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
532 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
534 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
537 /* Check provides against conflicts in installed packages. */
538 while (rpmdsNext(provides) >= 0) {
539 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
542 /* Check package name (not provides!) against installed obsoletes */
543 checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
548 * Look at the removed packages and make sure they aren't critical.
551 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
552 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
553 rpmfi fi = rpmfiInit(rpmteFI(p), 0);
555 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
556 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
558 /* Check provides and filenames against installed dependencies. */
559 while (rpmdsNext(provides) >= 0) {
560 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
563 while (rpmfiNext(fi) >= 0) {
564 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
570 depCacheFree(dcache);
572 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
575 (void) rpmtsCloseDB(ts);