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"
20 const char * const rpmNAME = PACKAGE;
22 const char * const rpmEVR = VERSION;
24 const int rpmFLAGS = RPMSENSE_EQUAL;
27 static rpmds rpmlibP = NULL;
30 * Compare removed package instances (qsort/bsearch).
31 * @param a 1st instance address
32 * @param b 2nd instance address
33 * @return result of comparison
35 static int intcmp(const void * a, const void * b)
39 int rc = (*aptr - *bptr);
44 * Add removed package instance to ordered transaction set.
45 * @param ts transaction set
47 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
48 * @return 0 on success
50 static int removePackage(rpmts ts, Header h, rpmte depends)
53 unsigned int dboffset = headerGetInstance(h);
55 /* Filter out duplicate erasures. */
56 if (ts->numRemovedPackages > 0 && ts->removedPackages != NULL) {
57 if (bsearch(&dboffset, ts->removedPackages, ts->numRemovedPackages,
58 sizeof(*ts->removedPackages), intcmp) != NULL)
62 if (ts->numRemovedPackages == ts->allocedRemovedPackages) {
63 ts->allocedRemovedPackages += ts->delta;
64 ts->removedPackages = xrealloc(ts->removedPackages,
65 sizeof(ts->removedPackages) * ts->allocedRemovedPackages);
68 if (ts->removedPackages != NULL) { /* XXX can't happen. */
69 ts->removedPackages[ts->numRemovedPackages] = dboffset;
70 ts->numRemovedPackages++;
71 if (ts->numRemovedPackages > 1)
72 qsort(ts->removedPackages, ts->numRemovedPackages,
73 sizeof(*ts->removedPackages), intcmp);
76 if (ts->orderCount >= ts->orderAlloced) {
77 ts->orderAlloced += (ts->orderCount - ts->orderAlloced) + ts->delta;
78 ts->order = xrealloc(ts->order, sizeof(*ts->order) * ts->orderAlloced);
81 p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL, -1);
82 rpmteSetDependsOn(p, depends);
84 ts->order[ts->orderCount] = p;
90 int rpmtsAddInstallElement(rpmts ts, Header h,
91 fnpyKey key, int upgrade, rpmRelocation * relocs)
93 rpm_color_t tscolor = rpmtsColor(ts);
96 rpmdbMatchIterator mi;
100 rpmtsi pi = NULL; rpmte p;
101 rpmds oldChk = NULL, newChk = NULL, sameChk = NULL;
108 /* Check for supported payload format if it's a package */
109 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
114 if (ts->addedPackages == NULL) {
115 ts->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
118 /* XXX Always add source headers. */
119 isSource = headerIsSource(h);
126 * Check for previously added versions with the same name and arch/os.
127 * FIXME: only catches previously added, older packages.
129 oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS));
130 newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER));
131 sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL));
132 /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */
133 for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
135 const char *pkgNEVR, *addNEVR;
138 /* XXX Only added packages need be checked for dupes. */
139 if (rpmteType(p) == TR_REMOVED)
142 /* XXX Never check source headers. */
143 if (rpmteIsSource(p))
147 const char * arch = headerGetString(h, RPMTAG_ARCH);
148 const char * os = headerGetString(h, RPMTAG_OS);
149 const char * parch = rpmteA(p);
150 const char * pos = rpmteO(p);
152 if (arch == NULL || parch == NULL)
154 if (os == NULL || pos == NULL)
156 if (!rstreq(arch, parch) || !rstreq(os, pos))
160 /* OK, binary rpm's with same arch and os. Check NEVR. */
161 if ((this = rpmteDS(p, RPMTAG_NAME)) == NULL)
162 continue; /* XXX can't happen */
165 * Always skip identical NEVR.
166 * On upgrade, if newer NEVR was previously added,
167 * then skip adding older.
169 if (rpmdsCompare(sameChk, this)) {
171 addNEVR = rpmdsDNEVR(sameChk);
172 } else if (upgrade && rpmdsCompare(newChk, this)) {
174 addNEVR = rpmdsDNEVR(newChk);
178 pkgNEVR = rpmdsDNEVR(this);
180 rpmlog(RPMLOG_WARNING,
181 _("package %s was already added, skipping %s\n"),
182 (pkgNEVR ? pkgNEVR + 2 : "?pkgNEVR?"),
183 (addNEVR ? addNEVR + 2 : "?addNEVR?"));
189 * On upgrade, if older NEVR was previously added,
190 * then replace old with new.
192 rc = rpmdsCompare(oldChk, this);
193 if (upgrade && rc != 0) {
194 pkgNEVR = rpmdsDNEVR(this);
195 addNEVR = rpmdsDNEVR(newChk);
197 rpmlog(RPMLOG_WARNING,
198 _("package %s was already added, replacing with %s\n"),
199 (pkgNEVR ? pkgNEVR + 2 : "?pkgNEVR?"),
200 (addNEVR ? addNEVR + 2 : "?addNEVR?"));
202 rpmalDel(ts->addedPackages, p);
208 /* If newer NEVR was already added, exit now. */
213 if (oc >= ts->orderAlloced) {
214 ts->orderAlloced += (oc - ts->orderAlloced) + ts->delta;
215 ts->order = xrealloc(ts->order, ts->orderAlloced * sizeof(*ts->order));
218 p = rpmteNew(ts, h, TR_ADDED, key, relocs, -1);
220 if (duplicate && oc < ts->orderCount) {
221 ts->order[oc] = rpmteFree(ts->order[oc]);
227 rpmcliPackagesTotal++;
230 rpmalAdd(ts->addedPackages, p);
233 ts->numAddedPackages++;
236 /* XXX rpmgi hack: Save header in transaction element if requested. */
238 (void) rpmteSetHeader(p, h);
240 /* If not upgrading, then we're done. */
241 if (!(upgrade & 0x1))
244 /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
248 /* Do lazy (readonly?) open of rpm database. */
249 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
250 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
254 /* On upgrade, erase older packages of same color (if any). */
255 hcolor = headerGetNumber(h, RPMTAG_HEADERCOLOR);
256 mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
257 while((oh = rpmdbNextIterator(mi)) != NULL) {
259 /* Ignore colored packages not in our rainbow. */
260 ohcolor = headerGetNumber(oh, RPMTAG_HEADERCOLOR);
261 if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor))
264 /* Skip packages that contain identical NEVR. */
265 if (rpmVersionCompare(h, oh) == 0)
268 xx = removePackage(ts, oh, p);
270 mi = rpmdbFreeIterator(mi);
272 obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME), RPMDBG_M("Obsoletes"));
273 obsoletes = rpmdsInit(obsoletes);
274 if (obsoletes != NULL)
275 while (rpmdsNext(obsoletes) >= 0) {
278 if ((Name = rpmdsN(obsoletes)) == NULL)
279 continue; /* XXX can't happen */
281 /* XXX avoid self-obsoleting packages. */
282 if (rstreq(rpmteN(p), Name))
286 mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, Name, 0);
288 mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0);
290 xx = rpmdbPruneIterator(mi,
291 ts->removedPackages, ts->numRemovedPackages, 1);
293 while((oh = rpmdbNextIterator(mi)) != NULL) {
294 /* Ignore colored packages not in our rainbow. */
295 ohcolor = headerGetNumber(oh, RPMTAG_HEADERCOLOR);
296 /* XXX provides *are* colored, effectively limiting Obsoletes:
297 to matching only colored Provides: based on pkg coloring. */
298 if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor))
302 * Rpm prior to 3.0.3 does not have versioned obsoletes.
303 * If no obsoletes version info is available, match all names.
305 if (rpmdsEVR(obsoletes) == NULL
306 || rpmdsAnyMatchesDep(oh, obsoletes, _rpmds_nopromote)) {
307 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
308 #ifdef DYING /* XXX see http://bugzilla.redhat.com #134497 */
309 if (rpmVersionCompare(h, oh))
311 xx = removePackage(ts, oh, p);
312 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
313 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
314 ohNEVRA = _free(ohNEVRA);
317 mi = rpmdbFreeIterator(mi);
319 obsoletes = rpmdsFree(obsoletes);
324 oldChk = rpmdsFree(oldChk);
325 newChk = rpmdsFree(newChk);
326 sameChk = rpmdsFree(sameChk);
331 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
333 return removePackage(ts, h, NULL);
337 * Check dep for an unsatisfied dependency.
338 * @param ts transaction set
339 * @param dep dependency
340 * @param adding dependency is from added package set?
341 * @return 0 if satisfied, 1 if not satisfied, 2 if error
343 static int unsatisfiedDepend(rpmts ts, rpmds dep, int adding)
345 rpmdbMatchIterator mi;
352 if ((Name = rpmdsN(dep)) == NULL)
353 return 0; /* XXX can't happen */
356 rc = 0; /* assume dependency is satisfied */
359 * New features in rpm packaging implicitly add versioned dependencies
360 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
361 * Check those dependencies now.
363 if (rpmdsFlags(dep) & RPMSENSE_RPMLIB) {
364 static int oneshot = -1;
366 oneshot = rpmdsRpmlib(&rpmlibP, NULL);
368 if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
369 rpmdsNotify(dep, _("(rpmlib provides)"), rc);
375 /* Search added packages for the dependency. */
376 if (rpmalSatisfiesDepend(ts->addedPackages, dep) != NULL) {
380 /* XXX only the installer does not have the database open here. */
381 if (rpmtsGetRdb(ts) != NULL) {
382 if (Name[0] == '/') {
383 /* depFlags better be 0! */
385 mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, Name, 0);
387 (void) rpmdbPruneIterator(mi,
388 ts->removedPackages, ts->numRemovedPackages, 1);
390 while ((h = rpmdbNextIterator(mi)) != NULL) {
391 rpmdsNotify(dep, _("(db files)"), rc);
392 mi = rpmdbFreeIterator(mi);
395 mi = rpmdbFreeIterator(mi);
398 mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, Name, 0);
399 (void) rpmdbPruneIterator(mi,
400 ts->removedPackages, ts->numRemovedPackages, 1);
401 while ((h = rpmdbNextIterator(mi)) != NULL) {
402 if (rpmdsAnyMatchesDep(h, dep, _rpmds_nopromote)) {
403 rpmdsNotify(dep, _("(db provides)"), rc);
404 mi = rpmdbFreeIterator(mi);
408 mi = rpmdbFreeIterator(mi);
412 * Search for an unsatisfied dependency.
414 if (adding && !retrying && !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
415 if (ts->solve != NULL) {
416 xx = (*ts->solve) (ts, dep, ts->solveData);
421 rpmalMakeIndex(ts->addedPackages);
428 rc = 1; /* dependency is unsatisfied */
429 rpmdsNotify(dep, NULL, rc);
436 * Check added requires/conflicts against against installed+added packages.
437 * @param ts transaction set
438 * @param pkgNEVRA package name-version-release.arch
439 * @param requires Requires: dependencies (or NULL)
440 * @param conflicts Conflicts: dependencies (or NULL)
441 * @param depName dependency name to filter (or NULL)
442 * @param tscolor color bits for transaction set (0 disables)
443 * @param adding dependency is from added package set?
444 * @return 0 no problems found
446 static int checkPackageDeps(rpmts ts, const char * pkgNEVRA,
447 rpmds requires, rpmds conflicts,
448 const char * depName, rpm_color_t tscolor, int adding)
455 requires = rpmdsInit(requires);
456 if (requires != NULL)
457 while (!ourrc && rpmdsNext(requires) >= 0) {
459 if ((Name = rpmdsN(requires)) == NULL)
460 continue; /* XXX can't happen */
462 /* Filter out requires that came along for the ride. */
463 if (depName != NULL && !rstreq(depName, Name))
466 /* Ignore colored requires not in our rainbow. */
467 dscolor = rpmdsColor(requires);
468 if (tscolor && dscolor && !(tscolor & dscolor))
471 rc = unsatisfiedDepend(ts, requires, adding);
474 case 0: /* requirements are satisfied. */
476 case 1: /* requirements are not satisfied. */
477 rpmdsProblem(ts->probs, pkgNEVRA, requires, NULL, adding);
479 case 2: /* something went wrong! */
486 conflicts = rpmdsInit(conflicts);
487 if (conflicts != NULL)
488 while (!ourrc && rpmdsNext(conflicts) >= 0) {
490 if ((Name = rpmdsN(conflicts)) == NULL)
491 continue; /* XXX can't happen */
493 /* Filter out conflicts that came along for the ride. */
494 if (depName != NULL && !rstreq(depName, Name))
497 /* Ignore colored conflicts not in our rainbow. */
498 dscolor = rpmdsColor(conflicts);
499 if (tscolor && dscolor && !(tscolor & dscolor))
502 rc = unsatisfiedDepend(ts, conflicts, adding);
504 /* 1 == unsatisfied, 0 == satsisfied */
506 case 0: /* conflicts exist. */
507 rpmdsProblem(ts->probs, pkgNEVRA, conflicts, NULL, adding);
509 case 1: /* conflicts don't exist. */
511 case 2: /* something went wrong! */
522 * Check dependency against installed packages.
523 * Adding: check name/provides dep against each conflict match,
524 * Erasing: check name/provides/filename dep against each requiredby match.
525 * @param ts transaction set
526 * @param dep dependency name
527 * @param mi rpm database iterator
528 * @param adding dependency is from added package set?
529 * @return 0 no problems found
531 static int checkPackageSet(rpmts ts, const char * dep,
532 rpmdbMatchIterator mi, int adding)
537 (void) rpmdbPruneIterator(mi,
538 ts->removedPackages, ts->numRemovedPackages, 1);
539 while ((h = rpmdbNextIterator(mi)) != NULL) {
541 rpmds requires, conflicts;
544 pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
545 requires = rpmdsNew(h, RPMTAG_REQUIRENAME, 0);
546 (void) rpmdsSetNoPromote(requires, _rpmds_nopromote);
547 conflicts = rpmdsNew(h, RPMTAG_CONFLICTNAME, 0);
548 (void) rpmdsSetNoPromote(conflicts, _rpmds_nopromote);
549 rc = checkPackageDeps(ts, pkgNEVRA, requires, conflicts, dep, 0, adding);
550 conflicts = rpmdsFree(conflicts);
551 requires = rpmdsFree(requires);
552 pkgNEVRA = _free(pkgNEVRA);
559 mi = rpmdbFreeIterator(mi);
565 * Check to-be-erased dependencies against installed requires.
566 * @param ts transaction set
567 * @param dep requires name
568 * @return 0 no problems found
570 static int checkDependentPackages(rpmts ts, const char * dep)
572 rpmdbMatchIterator mi;
573 mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, dep, 0);
574 return checkPackageSet(ts, dep, mi, 0);
578 * Check to-be-added dependencies against installed conflicts.
579 * @param ts transaction set
580 * @param dep conflicts name
581 * @return 0 no problems found
583 static int checkDependentConflicts(rpmts ts, const char * dep)
587 if (rpmtsGetRdb(ts) != NULL) { /* XXX is this necessary? */
588 rpmdbMatchIterator mi;
589 mi = rpmtsInitIterator(ts, RPMTAG_CONFLICTNAME, dep, 0);
590 rc = checkPackageSet(ts, dep, mi, 1);
596 int rpmtsCheck(rpmts ts)
598 rpm_color_t tscolor = rpmtsColor(ts);
599 rpmdbMatchIterator mi = NULL;
600 rpmtsi pi = NULL; rpmte p;
605 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
607 /* Do lazy, readonly, open of rpm database. */
608 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
609 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
614 ts->probs = rpmpsFree(ts->probs);
615 ts->probs = rpmpsCreate();
617 rpmalMakeIndex(ts->addedPackages);
620 * Look at all of the added packages and make sure their dependencies
624 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
627 /* FIX: rpmts{A,O} can return null. */
628 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
629 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
630 rc = checkPackageDeps(ts, rpmteNEVRA(p),
631 rpmteDS(p, RPMTAG_REQUIRENAME),
632 rpmteDS(p, RPMTAG_CONFLICTNAME),
639 provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
640 while (rpmdsNext(provides) >= 0) {
643 if ((Name = rpmdsN(provides)) == NULL)
644 continue; /* XXX can't happen */
646 /* Adding: check provides key against conflicts matches. */
647 if (!checkDependentConflicts(ts, Name))
658 * Look at the removed packages and make sure they aren't critical.
661 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
662 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
665 /* FIX: rpmts{A,O} can return null. */
666 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
667 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
670 while (rpmdsNext(provides) >= 0) {
673 if ((Name = rpmdsN(provides)) == NULL)
674 continue; /* XXX can't happen */
676 /* Erasing: check provides against requiredby matches. */
677 if (!checkDependentPackages(ts, Name))
687 fi = rpmfiInit(fi, 0);
688 while (rpmfiNext(fi) >= 0) {
689 const char * fn = rpmfiFN(fi);
691 /* Erasing: check filename against requiredby matches. */
692 if (!checkDependentPackages(ts, fn))
705 mi = rpmdbFreeIterator(mi);
708 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
711 xx = rpmtsCloseDB(ts);