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"
22 const char * const rpmNAME = PACKAGE;
24 const char * const rpmEVR = VERSION;
26 const int rpmFLAGS = RPMSENSE_EQUAL;
29 static rpmds rpmlibP = NULL;
35 #define HASHTYPE depCache
36 #define HTKEYTYPE const char *
37 #define HTDATATYPE int
38 #include "lib/rpmhash.H"
39 #include "lib/rpmhash.C"
44 #define HASHTYPE intHash
45 #define HTKEYTYPE unsigned int
51 * Add removed package instance to ordered transaction set.
52 * @param ts transaction set
54 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
55 * @return 0 on success
57 static int removePackage(rpmts ts, Header h, rpmte depends)
59 tsMembers tsmem = rpmtsMembers(ts);
61 unsigned int dboffset = headerGetInstance(h);
63 /* Can't remove what's not installed */
64 if (dboffset == 0) return 1;
66 /* Filter out duplicate erasures. */
67 if (intHashHasEntry(tsmem->removedPackages, dboffset)) {
71 intHashAddEntry(tsmem->removedPackages, dboffset);
73 if (tsmem->orderCount >= tsmem->orderAlloced) {
74 tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
75 tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
78 p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
79 rpmteSetDependsOn(p, depends);
81 tsmem->order[tsmem->orderCount] = p;
87 /* Return rpmdb iterator with removals pruned out */
88 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmTag tag, const char * key)
90 rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
91 tsMembers tsmem = rpmtsMembers(ts);
92 rpmdbPruneIterator(mi, tsmem->removedPackages);
96 #define skipColor(_tscolor, _color, _ocolor) \
97 ((_tscolor) && (_color) && (_ocolor) && !((_color) & (_ocolor)))
99 /* Add erase elements for older packages of same color (if any). */
100 static void addUpgradeErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
101 rpmte p, rpm_color_t hcolor, Header h)
104 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
106 while((oh = rpmdbNextIterator(mi)) != NULL) {
107 /* Ignore colored packages not in our rainbow. */
108 if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
111 /* Skip packages that contain identical NEVR. */
112 if (rpmVersionCompare(h, oh) == 0)
115 removePackage(ts, oh, p);
117 mi = rpmdbFreeIterator(mi);
120 /* Add erase elements for obsoleted packages of same color (if any). */
121 static void addObsoleteErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
122 rpmte p, rpm_color_t hcolor)
124 rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
127 while (rpmdsNext(obsoletes) >= 0) {
129 rpmdbMatchIterator mi = NULL;
131 if ((Name = rpmdsN(obsoletes)) == NULL)
132 continue; /* XXX can't happen */
134 /* XXX avoid self-obsoleting packages. */
135 if (rstreq(rpmteN(p), Name))
138 mi = rpmtsPrunedIterator(ts, RPMTAG_NAME, Name);
140 while((oh = rpmdbNextIterator(mi)) != NULL) {
141 /* Ignore colored packages not in our rainbow. */
142 if (skipColor(tscolor, hcolor,
143 headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
147 * Rpm prior to 3.0.3 does not have versioned obsoletes.
148 * If no obsoletes version info is available, match all names.
150 if (rpmdsEVR(obsoletes) == NULL
151 || rpmdsAnyMatchesDep(oh, obsoletes, _rpmds_nopromote)) {
152 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
153 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
154 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
155 ohNEVRA = _free(ohNEVRA);
157 removePackage(ts, oh, p);
160 mi = rpmdbFreeIterator(mi);
165 * Check for previously added versions and obsoletions.
166 * Return index where to place this element, or -1 to skip.
168 static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade)
172 const char * arch = headerGetString(h, RPMTAG_ARCH);
173 const char * os = headerGetString(h, RPMTAG_OS);
175 rpmds oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS));
176 rpmds newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER));
177 rpmds sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL));
178 rpmds obsChk = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0);
179 rpmtsi pi = rpmtsiInit(ts);
181 /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */
182 for (oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
183 rpmds this, obsoletes;
185 /* Only added binary packages need checking */
186 if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p))
189 /* Skip packages obsoleted by already added packages */
190 obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
191 while (rpmdsNext(obsoletes) >= 0) {
192 if (rpmdsCompare(obsoletes, sameChk)) {
199 /* Replace already added obsoleted packages by obsoleting package */
200 this = rpmteDS(p, RPMTAG_NAME);
202 while (rpmdsNext(obsChk) >= 0) {
203 if (rpmdsCompare(obsChk, this)) {
213 const char * parch = rpmteA(p);
214 const char * pos = rpmteO(p);
216 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
218 if (!rstreq(arch, parch) || !rstreq(os, pos))
223 * Always skip identical NEVR.
224 * On upgrade, if newer NEVR was previously added, skip adding older.
226 if (rpmdsCompare(sameChk, this) ||
227 (upgrade && rpmdsCompare(newChk, this))) {
232 /* On upgrade, if older NEVR was previously added, replace with new */
233 if (upgrade && rpmdsCompare(oldChk, this) != 0) {
238 /* If we broke out of the loop early we've something to say */
239 if (p != NULL && rpmIsVerbose()) {
240 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
241 const char *msg = (oc < 0) ?
242 _("package %s was already added, skipping %s\n") :
243 _("package %s was already added, replacing with %s\n");
244 rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), nevra);
257 int rpmtsAddInstallElement(rpmts ts, Header h,
258 fnpyKey key, int upgrade, rpmRelocation * relocs)
260 tsMembers tsmem = rpmtsMembers(ts);
261 rpm_color_t tscolor = rpmtsColor(ts);
263 int isSource = headerIsSource(h);
265 int oc = tsmem->orderCount;
267 /* Check for supported payload format if it's a package */
268 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
273 /* Check binary packages for redundancies in the set */
275 oc = findPos(ts, tscolor, h, upgrade);
276 /* If we're replacing a previously added element, free the old one */
277 if (oc >= 0 && oc < tsmem->orderCount) {
278 rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
279 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
280 /* If newer NEVR was already added, we're done */
286 if (tsmem->addedPackages == NULL) {
287 tsmem->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
290 if (oc >= tsmem->orderAlloced) {
291 tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
292 tsmem->order = xrealloc(tsmem->order,
293 tsmem->orderAlloced * sizeof(*tsmem->order));
296 p = rpmteNew(ts, h, TR_ADDED, key, relocs);
298 tsmem->order[oc] = p;
299 if (oc == tsmem->orderCount) {
301 tsmem->numAddedPackages++;
302 rpmcliPackagesTotal++;
305 rpmalAdd(tsmem->addedPackages, p);
307 /* If not upgrading or a source package, then we're done. */
308 if (!(upgrade & 0x1) || isSource)
311 /* Do lazy (readonly?) open of rpm database. */
312 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
313 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
317 /* Add erasure elements for old versions and obsoletions */
318 addUpgradeErasures(ts, tsmem, tscolor, p, rpmteColor(p), h);
319 addObsoleteErasures(ts, tsmem, tscolor, p, rpmteColor(p));
325 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
327 return removePackage(ts, h, NULL);
331 * Check dep for an unsatisfied dependency.
332 * @param ts transaction set
333 * @param dep dependency
334 * @return 0 if satisfied, 1 if not satisfied
336 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
338 tsMembers tsmem = rpmtsMembers(ts);
339 rpmdbMatchIterator mi;
340 const char * Name = rpmdsN(dep);
341 const char * DNEVR = rpmdsDNEVR(dep);
346 int *cachedrc = NULL;
348 int adding = (rpmdsInstance(dep) == 0);
349 rpmsenseFlags dsflags = rpmdsFlags(dep);
352 rc = 0; /* assume dependency is satisfied */
355 * New features in rpm packaging implicitly add versioned dependencies
356 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
357 * Check those dependencies now.
359 if (dsflags & RPMSENSE_RPMLIB) {
360 static int oneshot = -1;
362 oneshot = rpmdsRpmlib(&rpmlibP, NULL);
364 if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
365 rpmdsNotify(dep, "(rpmlib provides)", rc);
371 /* Dont look at pre-requisites of already installed packages */
372 if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
375 /* Pretrans dependencies can't be satisfied by added packages. */
376 if (!(dsflags & RPMSENSE_PRETRANS) &&
377 rpmalSatisfiesDepend(tsmem->addedPackages, dep) != NULL) {
381 /* See if we already looked this up */
382 if (depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) {
384 rpmdsNotify(dep, "(cached)", rc);
387 /* Only bother caching the expensive rpmdb lookups */
390 if (Name[0] == '/') {
391 mi = rpmtsPrunedIterator(ts, RPMTAG_BASENAMES, Name);
393 while ((h = rpmdbNextIterator(mi)) != NULL) {
394 rpmdsNotify(dep, "(db files)", rc);
395 mi = rpmdbFreeIterator(mi);
398 mi = rpmdbFreeIterator(mi);
401 mi = rpmtsPrunedIterator(ts, RPMTAG_PROVIDENAME, Name);
402 while ((h = rpmdbNextIterator(mi)) != NULL) {
403 if (rpmdsAnyMatchesDep(h, dep, _rpmds_nopromote)) {
404 rpmdsNotify(dep, "(db provides)", rc);
405 mi = rpmdbFreeIterator(mi);
409 mi = rpmdbFreeIterator(mi);
412 * Search for an unsatisfied dependency.
414 if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS) &&
415 !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
416 xx = rpmtsSolve(ts, dep);
426 rc = 1; /* dependency is unsatisfied */
427 rpmdsNotify(dep, NULL, rc);
431 char *key = xstrdup(DNEVR);
432 depCacheAddEntry(dcache, key, rc);
438 /* Check a dependency set for problems */
439 static void checkDS(rpmts ts, depCache dcache, rpmte te,
440 const char * pkgNEVRA, rpmds ds,
441 const char * depName, rpm_color_t tscolor)
444 /* require-problems are unsatisfied, others appear "satisfied" */
445 int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
448 while (rpmdsNext(ds) >= 0) {
449 /* Filter out dependencies that came along for the ride. */
450 if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
453 /* Ignore colored dependencies not in our rainbow. */
454 dscolor = rpmdsColor(ds);
455 if (tscolor && dscolor && !(tscolor & dscolor))
458 if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
459 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
463 /* Check a given dependency type against installed packages */
464 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
465 rpmTag depTag, const char *dep)
468 rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep);
470 while ((h = rpmdbNextIterator(mi)) != NULL) {
471 char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
472 rpmds ds = rpmdsNew(h, depTag, 0);
474 checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
477 pkgNEVRA = _free(pkgNEVRA);
479 rpmdbFreeIterator(mi);
482 int rpmtsCheck(rpmts ts)
484 rpm_color_t tscolor = rpmtsColor(ts);
485 rpmtsi pi = NULL; rpmte p;
488 depCache dcache = NULL;
490 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
492 /* Do lazy, readonly, open of rpm database. */
493 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
494 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
499 /* XXX FIXME: figure some kind of heuristic for the cache size */
500 dcache = depCacheCreate(5001, hashFunctionString, strcmp,
501 (depCacheFreeKey)rfree, NULL);
504 * Look at all of the added packages and make sure their dependencies
508 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
509 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
511 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
512 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
514 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
516 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
519 /* Check provides against conflicts in installed packages. */
520 while (rpmdsNext(provides) >= 0) {
521 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
524 /* Check package name (not provides!) against installed obsoletes */
525 checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
530 * Look at the removed packages and make sure they aren't critical.
533 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
534 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
535 rpmfi fi = rpmfiInit(rpmteFI(p), 0);
537 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
538 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
540 /* Check provides and filenames against installed dependencies. */
541 while (rpmdsNext(provides) >= 0) {
542 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
545 while (rpmfiNext(fi) >= 0) {
546 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
552 depCacheFree(dcache);
554 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
557 (void) rpmtsCloseDB(ts);