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 RPMVERSION = VERSION;
24 const char * const rpmNAME = PACKAGE;
26 const char * const rpmEVR = VERSION;
28 const int rpmFLAGS = RPMSENSE_EQUAL;
31 static rpmds rpmlibP = NULL;
37 #define HASHTYPE depCache
38 #define HTKEYTYPE const char *
39 #define HTDATATYPE int
40 #include "lib/rpmhash.H"
41 #include "lib/rpmhash.C"
46 #define HASHTYPE intHash
47 #define HTKEYTYPE unsigned int
53 * Add removed package instance to ordered transaction set.
54 * @param ts transaction set
56 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
57 * @return 0 on success
59 static int removePackage(rpmts ts, Header h, rpmte depends)
61 tsMembers tsmem = rpmtsMembers(ts);
63 unsigned int dboffset = headerGetInstance(h);
65 /* Can't remove what's not installed */
66 if (dboffset == 0) return 1;
68 /* Filter out duplicate erasures. */
69 if (intHashHasEntry(tsmem->removedPackages, dboffset)) {
73 intHashAddEntry(tsmem->removedPackages, dboffset);
75 if (tsmem->orderCount >= tsmem->orderAlloced) {
76 tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
77 tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
80 p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
81 rpmteSetDependsOn(p, depends);
83 tsmem->order[tsmem->orderCount] = p;
89 /* Return rpmdb iterator with removals pruned out */
90 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, const char * key)
92 rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
93 tsMembers tsmem = rpmtsMembers(ts);
94 rpmdbPruneIterator(mi, tsmem->removedPackages);
98 #define skipColor(_tscolor, _color, _ocolor) \
99 ((_tscolor) && (_color) && (_ocolor) && !((_color) & (_ocolor)))
101 /* Add erase elements for older packages of same color (if any). */
102 static void addUpgradeErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
103 rpmte p, rpm_color_t hcolor, Header h)
106 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
108 while((oh = rpmdbNextIterator(mi)) != NULL) {
109 /* Ignore colored packages not in our rainbow. */
110 if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
113 /* Skip packages that contain identical NEVR. */
114 if (rpmVersionCompare(h, oh) == 0)
117 removePackage(ts, oh, p);
119 mi = rpmdbFreeIterator(mi);
122 /* Add erase elements for obsoleted packages of same color (if any). */
123 static void addObsoleteErasures(rpmts ts, tsMembers tsmem, rpm_color_t tscolor,
124 rpmte p, rpm_color_t hcolor)
126 rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
129 while (rpmdsNext(obsoletes) >= 0) {
131 rpmdbMatchIterator mi = NULL;
133 if ((Name = rpmdsN(obsoletes)) == NULL)
134 continue; /* XXX can't happen */
136 /* XXX avoid self-obsoleting packages. */
137 if (rstreq(rpmteN(p), Name))
140 mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name);
142 while((oh = rpmdbNextIterator(mi)) != NULL) {
143 /* Ignore colored packages not in our rainbow. */
144 if (skipColor(tscolor, hcolor,
145 headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
149 * Rpm prior to 3.0.3 does not have versioned obsoletes.
150 * If no obsoletes version info is available, match all names.
152 if (rpmdsEVR(obsoletes) == NULL
153 || rpmdsAnyMatchesDep(oh, obsoletes, _rpmds_nopromote)) {
154 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
155 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
156 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
157 ohNEVRA = _free(ohNEVRA);
159 removePackage(ts, oh, p);
162 mi = rpmdbFreeIterator(mi);
167 * Check for previously added versions and obsoletions.
168 * Return index where to place this element, or -1 to skip.
170 static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade)
174 const char * arch = headerGetString(h, RPMTAG_ARCH);
175 const char * os = headerGetString(h, RPMTAG_OS);
177 rpmds oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS));
178 rpmds newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER));
179 rpmds sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL));
180 rpmds obsChk = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0);
181 rpmtsi pi = rpmtsiInit(ts);
183 /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */
184 for (oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
185 rpmds thisds, obsoletes;
187 /* Only added binary packages need checking */
188 if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p))
191 /* Skip packages obsoleted by already added packages */
192 obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
193 while (rpmdsNext(obsoletes) >= 0) {
194 if (rpmdsCompare(obsoletes, sameChk)) {
201 /* Replace already added obsoleted packages by obsoleting package */
202 thisds = rpmteDS(p, RPMTAG_NAME);
204 while (rpmdsNext(obsChk) >= 0) {
205 if (rpmdsCompare(obsChk, thisds)) {
215 const char * parch = rpmteA(p);
216 const char * pos = rpmteO(p);
218 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
220 if (!rstreq(arch, parch) || !rstreq(os, pos))
225 * Always skip identical NEVR.
226 * On upgrade, if newer NEVR was previously added, skip adding older.
228 if (rpmdsCompare(sameChk, thisds) ||
229 (upgrade && rpmdsCompare(newChk, thisds))) {
234 /* On upgrade, if older NEVR was previously added, replace with new */
235 if (upgrade && rpmdsCompare(oldChk, thisds) != 0) {
240 /* If we broke out of the loop early we've something to say */
241 if (p != NULL && rpmIsVerbose()) {
242 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
243 const char *msg = (oc < 0) ?
244 _("package %s was already added, skipping %s\n") :
245 _("package %s was already added, replacing with %s\n");
246 rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), nevra);
259 int rpmtsAddInstallElement(rpmts ts, Header h,
260 fnpyKey key, int upgrade, rpmRelocation * relocs)
262 tsMembers tsmem = rpmtsMembers(ts);
263 rpm_color_t tscolor = rpmtsColor(ts);
265 int isSource = headerIsSource(h);
267 int oc = tsmem->orderCount;
269 /* Check for supported payload format if it's a package */
270 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
275 /* Check binary packages for redundancies in the set */
277 oc = findPos(ts, tscolor, h, upgrade);
278 /* If we're replacing a previously added element, free the old one */
279 if (oc >= 0 && oc < tsmem->orderCount) {
280 rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
281 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
282 /* If newer NEVR was already added, we're done */
288 if (tsmem->addedPackages == NULL) {
289 tsmem->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
292 if (oc >= tsmem->orderAlloced) {
293 tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
294 tsmem->order = xrealloc(tsmem->order,
295 tsmem->orderAlloced * sizeof(*tsmem->order));
298 p = rpmteNew(ts, h, TR_ADDED, key, relocs);
300 tsmem->order[oc] = p;
301 if (oc == tsmem->orderCount) {
303 rpmcliPackagesTotal++;
306 rpmalAdd(tsmem->addedPackages, p);
308 /* If not upgrading or a source package, then we're done. */
309 if (!(upgrade & 0x1) || isSource)
312 /* Do lazy (readonly?) open of rpm database. */
313 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
314 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
318 /* Add erasure elements for old versions and obsoletions */
319 addUpgradeErasures(ts, tsmem, tscolor, p, rpmteColor(p), h);
320 addObsoleteErasures(ts, tsmem, tscolor, p, rpmteColor(p));
326 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
328 return removePackage(ts, h, NULL);
331 /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
332 static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
334 const char * Name = rpmdsN(dep);
335 const char * DNEVR = rpmdsDNEVR(dep);
336 int *cachedrc = NULL;
337 rpmdbMatchIterator mi = NULL;
341 /* See if we already looked this up */
342 if (depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) {
344 rpmdsNotify(dep, "(cached)", rc);
348 /* See if a filename dependency is a real file in some package */
349 if (Name[0] == '/') {
350 mi = rpmtsPrunedIterator(ts, RPMDBI_BASENAMES, Name);
351 while ((h = rpmdbNextIterator(mi)) != NULL) {
352 rpmdsNotify(dep, "(db files)", rc);
355 rpmdbFreeIterator(mi);
358 /* Otherwise look in provides no matter what the dependency looks like */
360 mi = rpmtsPrunedIterator(ts, RPMDBI_PROVIDENAME, Name);
361 while ((h = rpmdbNextIterator(mi)) != NULL) {
362 if (rpmdsAnyMatchesDep(h, dep, _rpmds_nopromote)) {
363 rpmdsNotify(dep, "(db provides)", rc);
367 rpmdbFreeIterator(mi);
369 rc = (h != NULL) ? 0 : 1;
371 /* Cache the relatively expensive rpmdb lookup results */
372 depCacheAddEntry(dcache, xstrdup(DNEVR), rc);
377 * Check dep for an unsatisfied dependency.
378 * @param ts transaction set
379 * @param dep dependency
380 * @return 0 if satisfied, 1 if not satisfied
382 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
384 tsMembers tsmem = rpmtsMembers(ts);
387 int adding = (rpmdsInstance(dep) == 0);
388 rpmsenseFlags dsflags = rpmdsFlags(dep);
391 rc = 0; /* assume dependency is satisfied */
394 * New features in rpm packaging implicitly add versioned dependencies
395 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
396 * Check those dependencies now.
398 if (dsflags & RPMSENSE_RPMLIB) {
399 static int oneshot = -1;
401 oneshot = rpmdsRpmlib(&rpmlibP, NULL);
403 if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
404 rpmdsNotify(dep, "(rpmlib provides)", rc);
410 /* Dont look at pre-requisites of already installed packages */
411 if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
414 /* Pretrans dependencies can't be satisfied by added packages. */
415 if (!(dsflags & RPMSENSE_PRETRANS) &&
416 rpmalSatisfiesDepend(tsmem->addedPackages, dep) != NULL) {
420 /* See if the rpmdb provides it */
421 if (rpmdbProvides(ts, dcache, dep) == 0)
424 /* Search for an unsatisfied dependency. */
425 if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS) &&
426 !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
427 int xx = rpmtsSolve(ts, dep);
437 rc = 1; /* dependency is unsatisfied */
438 rpmdsNotify(dep, NULL, rc);
444 /* Check a dependency set for problems */
445 static void checkDS(rpmts ts, depCache dcache, rpmte te,
446 const char * pkgNEVRA, rpmds ds,
447 const char * depName, rpm_color_t tscolor)
450 /* require-problems are unsatisfied, others appear "satisfied" */
451 int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
454 while (rpmdsNext(ds) >= 0) {
455 /* Filter out dependencies that came along for the ride. */
456 if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
459 /* Ignore colored dependencies not in our rainbow. */
460 dscolor = rpmdsColor(ds);
461 if (tscolor && dscolor && !(tscolor & dscolor))
464 if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
465 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
469 /* Check a given dependency type against installed packages */
470 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
471 rpmTag depTag, const char *dep)
474 rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep);
476 while ((h = rpmdbNextIterator(mi)) != NULL) {
477 char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
478 rpmds ds = rpmdsNew(h, depTag, 0);
480 checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
483 pkgNEVRA = _free(pkgNEVRA);
485 rpmdbFreeIterator(mi);
488 int rpmtsCheck(rpmts ts)
490 rpm_color_t tscolor = rpmtsColor(ts);
491 rpmtsi pi = NULL; rpmte p;
494 depCache dcache = NULL;
496 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
498 /* Do lazy, readonly, open of rpm database. */
499 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
500 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
505 /* XXX FIXME: figure some kind of heuristic for the cache size */
506 dcache = depCacheCreate(5001, hashFunctionString, strcmp,
507 (depCacheFreeKey)rfree, NULL);
510 * Look at all of the added packages and make sure their dependencies
514 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
515 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
517 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
518 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
520 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
522 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
525 /* Check provides against conflicts in installed packages. */
526 while (rpmdsNext(provides) >= 0) {
527 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
530 /* Skip obsoletion checks for source packages (ie build) */
531 if (rpmteIsSource(p))
534 /* Check package name (not provides!) against installed obsoletes */
535 checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
540 * Look at the removed packages and make sure they aren't critical.
543 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
544 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
545 rpmfi fi = rpmfiInit(rpmteFI(p), 0);
547 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
548 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
550 /* Check provides and filenames against installed dependencies. */
551 while (rpmdsNext(provides) >= 0) {
552 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
555 while (rpmfiNext(fi) >= 0) {
556 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
562 depCacheFree(dcache);
564 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
567 (void) rpmtsCloseDB(ts);