Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / lib / depends.c
1 /** \ingroup rpmts
2  * \file lib/depends.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmlib.h>         /* rpmVersionCompare, rpmlib provides */
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfi.h>
13
14 #include "lib/rpmts_internal.h"
15 #include "lib/rpmte_internal.h"
16 #include "lib/rpmds_internal.h"
17 #include "lib/misc.h"
18
19 #include "debug.h"
20
21 const char * const RPMVERSION = VERSION;
22
23 const char * const rpmNAME = PACKAGE;
24
25 const char * const rpmEVR = VERSION;
26
27 const int rpmFLAGS = RPMSENSE_EQUAL;
28
29 #undef HASHTYPE
30 #undef HTKEYTYPE
31 #undef HTDATATYPE
32
33 #define HASHTYPE depCache
34 #define HTKEYTYPE const char *
35 #define HTDATATYPE int
36 #include "lib/rpmhash.H"
37 #include "lib/rpmhash.C"
38 #undef HASHTYPE
39 #undef HTKEYTYPE
40 #undef HTDATATYPE
41
42 #define HASHTYPE removedHash
43 #define HTKEYTYPE unsigned int
44 #define HTDATATYPE struct rpmte_s *
45 #include "rpmhash.C"
46 #undef HASHTYPE
47 #undef HTKEYTYPE
48 #undef HTDATATYPE
49
50 /**
51  * Check for supported payload format in header.
52  * @param h             header to check
53  * @return              RPMRC_OK if supported, RPMRC_FAIL otherwise
54  */
55 static rpmRC headerCheckPayloadFormat(Header h) {
56     rpmRC rc = RPMRC_OK;
57     const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
58     /* 
59      * XXX Ugh, rpm 3.x packages don't have payload format tag. Instead
60      * of blindly allowing, should check somehow (HDRID existence or... ?)
61      */
62     if (!payloadfmt) return rc;
63
64     if (!rstreq(payloadfmt, "cpio")) {
65         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
66         if (payloadfmt && rstreq(payloadfmt, "drpm")) {
67             rpmlog(RPMLOG_ERR,
68                      _("%s is a Delta RPM and cannot be directly installed\n"),
69                      nevra);
70         } else {
71             rpmlog(RPMLOG_ERR, 
72                      _("Unsupported payload (%s) in package %s\n"),
73                      payloadfmt ? payloadfmt : "none", nevra);
74         } 
75         free(nevra);
76         rc = RPMRC_FAIL;
77     }
78     return rc;
79 }
80
81 /**
82  * Add removed package instance to ordered transaction set.
83  * @param ts            transaction set
84  * @param h             header
85  * @param depends       installed package of pair (or RPMAL_NOMATCH on erase)
86  * @return              0 on success
87  */
88 static int removePackage(rpmts ts, Header h, rpmte depends)
89 {
90     tsMembers tsmem = rpmtsMembers(ts);
91     rpmte p;
92     unsigned int dboffset = headerGetInstance(h);
93
94     /* Can't remove what's not installed */
95     if (dboffset == 0) return 1;
96
97     /* Filter out duplicate erasures. */
98     if (removedHashHasEntry(tsmem->removedPackages, dboffset)) {
99         return 0;
100     }
101
102     p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
103     if (p == NULL)
104         return 1;
105
106     removedHashAddEntry(tsmem->removedPackages, dboffset, p);
107
108     if (tsmem->orderCount >= tsmem->orderAlloced) {
109         tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
110         tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
111     }
112
113     rpmteSetDependsOn(p, depends);
114
115     tsmem->order[tsmem->orderCount] = p;
116     tsmem->orderCount++;
117
118     return 0;
119 }
120
121 /* Return rpmdb iterator with removals optionally pruned out */
122 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag,
123                                               const char * key, int prune)
124 {
125     rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
126     if (prune) {
127         tsMembers tsmem = rpmtsMembers(ts);
128         rpmdbPruneIterator(mi, tsmem->removedPackages);
129     }
130     return mi;
131 }
132
133 /**
134  * Decides whether to skip a package upgrade/obsoletion on TE color.
135  *
136  * @param tscolor       color of this transaction
137  * @param color         color of this transaction element
138  * @param ocolor        header color of the upgraded/obsoleted package
139  *
140  * @return              non-zero if the package should be skipped
141  */
142 static int skipColor(rpm_color_t tscolor, rpm_color_t color, rpm_color_t ocolor)
143 {
144     return tscolor && color && ocolor && !(color & ocolor);
145 }
146
147 static int rpmNameVersionCompare(Header first, Header second)
148 {
149     const char * one, * two;
150     int rc;
151
152     one = headerGetString(first, RPMTAG_NAME);
153     two = headerGetString(second, RPMTAG_NAME);
154     rc = strcmp(one, two);
155     if (rc)
156         return rc;
157     one = headerGetString(first, RPMTAG_ARCH);
158     two = headerGetString(second, RPMTAG_ARCH);
159     rc = strcmp(one, two);
160     if (rc)
161         return rc;
162     return rpmVersionCompare(first, second);
163 }
164
165 /* Add erase elements for older packages of same color (if any). */
166 static int addUpgradeErasures(rpmts ts, rpm_color_t tscolor,
167                                 rpmte p, rpm_color_t hcolor, Header h)
168 {
169     Header oh;
170     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
171     int rc = 0;
172
173     while((oh = rpmdbNextIterator(mi)) != NULL) {
174         /* Ignore colored packages not in our rainbow. */
175         if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
176             continue;
177
178         /* Skip packages that contain identical NEVRA. */
179         if (rpmNameVersionCompare(h, oh) == 0)
180             continue;
181
182         if (removePackage(ts, oh, p)) {
183             rc = 1;
184             break;
185         }
186     }
187     rpmdbFreeIterator(mi);
188     return rc;
189 }
190
191 /* Add erase elements for obsoleted packages of same color (if any). */
192 static int addObsoleteErasures(rpmts ts, rpm_color_t tscolor, rpmte p)
193 {
194     rpmstrPool tspool = rpmtsPool(ts);
195     rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
196     Header oh;
197     int rc = 0;
198
199     while (rpmdsNext(obsoletes) >= 0 && rc == 0) {
200         const char * Name;
201         rpmdbMatchIterator mi = NULL;
202
203         if ((Name = rpmdsN(obsoletes)) == NULL)
204             continue;   /* XXX can't happen */
205
206         mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name, 1);
207
208         while((oh = rpmdbNextIterator(mi)) != NULL) {
209             const char *oarch = headerGetString(oh, RPMTAG_ARCH);
210             int match;
211
212             /* avoid self-obsoleting packages */
213             if (rstreq(rpmteN(p), Name) && rstreq(rpmteA(p), oarch)) {
214                 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
215                 rpmlog(RPMLOG_DEBUG, "  Not obsoleting: %s\n", ohNEVRA);
216                 free(ohNEVRA);
217                 continue;
218             }
219
220             /*
221              * Rpm prior to 3.0.3 does not have versioned obsoletes.
222              * If no obsoletes version info is available, match all names.
223              */
224             match = (rpmdsEVR(obsoletes) == NULL);
225             if (!match)
226                 match = rpmdsMatches(tspool, oh, -1, obsoletes, 1,
227                                          _rpmds_nopromote);
228
229             if (match) {
230                 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
231                 rpmlog(RPMLOG_DEBUG, "  Obsoletes: %s\t\terases %s\n",
232                         rpmdsDNEVR(obsoletes)+2, ohNEVRA);
233                 free(ohNEVRA);
234
235                 if (removePackage(ts, oh, p)) {
236                     rc = 1;
237                     break;
238                 }
239             }
240         }
241         rpmdbFreeIterator(mi);
242     }
243     return rc;
244 }
245
246 /*
247  * Lookup obsoletions in the added set. In theory there could
248  * be more than one obsoleting package, but we only care whether this
249  * has been obsoleted by *something* or not.
250  */
251 static rpmte checkObsoleted(rpmal addedPackages, rpmds thisds)
252 {
253     rpmte p = NULL;
254     rpmte *matches = NULL;
255
256     matches = rpmalAllObsoletes(addedPackages, thisds);
257     if (matches) {
258         p = matches[0];
259         free(matches);
260     }
261     return p;
262 }
263
264 /*
265  * Filtered rpmal lookup: on colored transactions there can be more
266  * than one identical NEVR but different arch, this must be allowed.
267  * Only a single element needs to be considred as there can only ever
268  * be one previous element to be replaced.
269  */
270 static rpmte checkAdded(rpmal addedPackages, rpm_color_t tscolor,
271                         rpmte te, rpmds ds)
272 {
273     rpmte p = NULL;
274     rpmte *matches = NULL;
275
276     matches = rpmalAllSatisfiesDepend(addedPackages, ds);
277     if (matches) {
278         const char * arch = rpmteA(te);
279         const char * os = rpmteO(te);
280
281         for (rpmte *m = matches; m && *m; m++) {
282             if (tscolor) {
283                 const char * parch = rpmteA(*m);
284                 const char * pos = rpmteO(*m);
285
286                 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
287                     continue;
288                 if (!rstreq(arch, parch) || !rstreq(os, pos))
289                     continue;
290             }
291             p = *m;
292             break;
293         }
294         free(matches);
295     }
296     return p;
297 }
298
299 /*
300  * Check for previously added versions and obsoletions.
301  * Return index where to place this element, or -1 to skip.
302  * XXX OBSOLETENAME is a bit of a hack, but gives us what
303  * we want from rpmal: we're only interested in added package
304  * names here, not their provides.
305  */
306 static int findPos(rpmts ts, rpm_color_t tscolor, rpmte te, int upgrade)
307 {
308     tsMembers tsmem = rpmtsMembers(ts);
309     int oc = tsmem->orderCount;
310     int skip = 0;
311     const char * name = rpmteN(te);
312     const char * evr = rpmteEVR(te);
313     rpmte p;
314     rpmstrPool tspool = rpmtsPool(ts);
315     rpmds oldChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
316                                    name, evr, (RPMSENSE_LESS));
317     rpmds newChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
318                                    name, evr, (RPMSENSE_GREATER));
319     rpmds sameChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
320                                     name, evr, (RPMSENSE_EQUAL));
321     rpmds obsChk = rpmteDS(te, RPMTAG_OBSOLETENAME);
322
323     /* If obsoleting package has already been added, skip this. */
324     if ((p = checkObsoleted(tsmem->addedPackages, rpmteDS(te, RPMTAG_NAME)))) {
325         skip = 1;
326         goto exit;
327     }
328
329     /* If obsoleted package has already been added, replace with this. */
330     rpmdsInit(obsChk);
331     while (rpmdsNext(obsChk) >= 0) {
332         /* XXX Obsoletes are not colored */
333         if ((p = checkAdded(tsmem->addedPackages, 0, te, obsChk))) {
334             goto exit;
335         }
336     }
337
338     /* If same NEVR has already been added, skip this. */
339     if ((p = checkAdded(tsmem->addedPackages, tscolor, te, sameChk))) {
340         skip = 1;
341         goto exit;
342     }
343
344     /* On upgrades... */
345     if (upgrade) {
346         /* ...if newer NEVR has already been added, skip this. */
347         if ((p = checkAdded(tsmem->addedPackages, tscolor, te, newChk))) {
348             skip = 1;
349             goto exit;
350         }
351
352         /* ...if older NEVR has already been added, replace with this. */
353         if ((p = checkAdded(tsmem->addedPackages, tscolor, te, oldChk))) {
354             goto exit;
355         }
356     }
357
358 exit:
359     /* If we found a previous element we've something to say */
360     if (p != NULL && rpmIsVerbose()) {
361         const char *msg = skip ?
362                     _("package %s was already added, skipping %s\n") :
363                     _("package %s was already added, replacing with %s\n");
364         rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), rpmteNEVRA(te));
365     }
366
367     /* If replacing a previous element, find out where it is. Pooh. */
368     if (!skip && p != NULL) {
369         for (oc = 0; oc < tsmem->orderCount; oc++) {
370             if (p == tsmem->order[oc])
371                 break;
372         }
373     }
374
375     rpmdsFree(oldChk);
376     rpmdsFree(newChk);
377     rpmdsFree(sameChk);
378     return (skip) ? -1 : oc;
379 }
380
381 rpmal rpmtsCreateAl(rpmts ts, rpmElementTypes types)
382 {
383     rpmal al = NULL;
384     if (ts) {
385         rpmte p;
386         rpmtsi pi;
387         rpmstrPool tspool = rpmtsPool(ts);
388
389         al = rpmalCreate(tspool, (rpmtsNElements(ts) / 4) + 1, rpmtsFlags(ts),
390                                 rpmtsColor(ts), rpmtsPrefColor(ts));
391         pi = rpmtsiInit(ts);
392         while ((p = rpmtsiNext(pi, types)))
393             rpmalAdd(al, p);
394         rpmtsiFree(pi);
395     }
396     return al;
397 }
398
399 int rpmtsAddInstallElement(rpmts ts, Header h,
400                         fnpyKey key, int upgrade, rpmRelocation * relocs)
401 {
402     tsMembers tsmem = rpmtsMembers(ts);
403     rpm_color_t tscolor = rpmtsColor(ts);
404     rpmte p = NULL;
405     int isSource = headerIsSource(h);
406     int ec = 0;
407     int oc = tsmem->orderCount;
408
409     /* Check for supported payload format if it's a package */
410     if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
411         ec = 1;
412         goto exit;
413     }
414
415     /* Source packages are never "upgraded" */
416     if (isSource)
417         upgrade = 0;
418
419     /* Do lazy (readonly?) open of rpm database for upgrades. */
420     if (upgrade && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
421         if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
422             goto exit;
423     }
424
425     p = rpmteNew(ts, h, TR_ADDED, key, relocs);
426     if (p == NULL) {
427         ec = 1;
428         goto exit;
429     }
430
431     /* Check binary packages for redundancies in the set */
432     if (!isSource) {
433         oc = findPos(ts, tscolor, p, upgrade);
434         /* If we're replacing a previously added element, free the old one */
435         if (oc >= 0 && oc < tsmem->orderCount) {
436             rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
437             tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
438         /* If newer NEVR was already added, we're done */
439         } else if (oc < 0) {
440             p = rpmteFree(p);
441             goto exit;
442         }
443     }
444
445     if (oc >= tsmem->orderAlloced) {
446         tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
447         tsmem->order = xrealloc(tsmem->order,
448                         tsmem->orderAlloced * sizeof(*tsmem->order));
449     }
450
451
452     tsmem->order[oc] = p;
453     if (oc == tsmem->orderCount) {
454         tsmem->orderCount++;
455     }
456     
457     if (tsmem->addedPackages == NULL) {
458         tsmem->addedPackages = rpmalCreate(rpmtsPool(ts), 5, rpmtsFlags(ts),
459                                            tscolor, rpmtsPrefColor(ts));
460     }
461     rpmalAdd(tsmem->addedPackages, p);
462
463     /* Add erasure elements for old versions and obsoletions on upgrades */
464     /* XXX TODO: If either of these fails, we'd need to undo all additions */
465     if (upgrade) {
466         addUpgradeErasures(ts, tscolor, p, rpmteColor(p), h);
467         addObsoleteErasures(ts, tscolor, p);
468     }
469
470 exit:
471     return ec;
472 }
473
474 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
475 {
476     return removePackage(ts, h, NULL);
477 }
478
479 /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
480 static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
481 {
482     const char * Name = rpmdsN(dep);
483     const char * DNEVR = rpmdsDNEVR(dep);
484     rpmTagVal deptag = rpmdsTagN(dep);
485     int *cachedrc = NULL;
486     rpmdbMatchIterator mi = NULL;
487     Header h = NULL;
488     int rc = 0;
489     /* pretrans deps are provided by current packages, don't prune erasures */
490     int prune = (rpmdsFlags(dep) & RPMSENSE_PRETRANS) ? 0 : 1;
491     unsigned int keyhash = 0;
492
493     /* See if we already looked this up */
494     if (prune) {
495         keyhash = depCacheKeyHash(dcache, DNEVR);
496         if (depCacheGetHEntry(dcache, DNEVR, keyhash, &cachedrc, NULL, NULL)) {
497             rc = *cachedrc;
498             rpmdsNotify(dep, "(cached)", rc);
499             return rc;
500         }
501     }
502
503     /*
504      * See if a filename dependency is a real file in some package,
505      * taking file state into account: replaced, wrong colored and
506      * not installed files can not satisfy a dependency.
507      */
508     if (deptag != RPMTAG_OBSOLETENAME && Name[0] == '/') {
509         mi = rpmtsPrunedIterator(ts, RPMDBI_INSTFILENAMES, Name, prune);
510         while ((h = rpmdbNextIterator(mi)) != NULL) {
511             rpmdsNotify(dep, "(db files)", rc);
512             break;
513         }
514         rpmdbFreeIterator(mi);
515     }
516
517     /* Otherwise look in provides no matter what the dependency looks like */
518     if (h == NULL) {
519         rpmstrPool tspool = rpmtsPool(ts);
520         /* Obsoletes use just name alone, everything else uses provides */
521         rpmTagVal dbtag = RPMDBI_PROVIDENAME;
522         int selfevr = 0;
523         if (deptag == RPMTAG_OBSOLETENAME) {
524             dbtag = RPMDBI_NAME;
525             selfevr = 1;
526         }
527
528         mi = rpmtsPrunedIterator(ts, dbtag, Name, prune);
529         while ((h = rpmdbNextIterator(mi)) != NULL) {
530             /* Provide-indexes can't be used with nevr-only matching */
531             int prix = (selfevr) ? -1 : rpmdbGetIteratorFileNum(mi);
532             int match = rpmdsMatches(tspool, h, prix, dep, selfevr,
533                                         _rpmds_nopromote);
534             if (match) {
535                 rpmdsNotify(dep, "(db provides)", rc);
536                 break;
537             }
538         }
539         rpmdbFreeIterator(mi);
540     }
541     rc = (h != NULL) ? 0 : 1;
542
543     /* Cache the relatively expensive rpmdb lookup results */
544     /* Caching the oddball non-pruned case would mess up other results */
545     if (prune)
546         depCacheAddHEntry(dcache, xstrdup(DNEVR), keyhash, rc);
547     return rc;
548 }
549
550 /**
551  * Check dep for an unsatisfied dependency.
552  * @param ts            transaction set
553  * @param dep           dependency
554  * @return              0 if satisfied, 1 if not satisfied
555  */
556 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
557 {
558     tsMembers tsmem = rpmtsMembers(ts);
559     int rc;
560     int retrying = 0;
561     int adding = (rpmdsInstance(dep) == 0);
562     rpmsenseFlags dsflags = rpmdsFlags(dep);
563
564 retry:
565     rc = 0;     /* assume dependency is satisfied */
566
567     /*
568      * New features in rpm packaging implicitly add versioned dependencies
569      * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
570      * Check those dependencies now.
571      */
572     if (dsflags & RPMSENSE_RPMLIB) {
573         if (tsmem->rpmlib == NULL)
574             rpmdsRpmlibPool(rpmtsPool(ts), &(tsmem->rpmlib), NULL);
575         
576         if (tsmem->rpmlib != NULL && rpmdsSearch(tsmem->rpmlib, dep) >= 0) {
577             rpmdsNotify(dep, "(rpmlib provides)", rc);
578             goto exit;
579         }
580         goto unsatisfied;
581     }
582
583     /* Dont look at pre-requisites of already installed packages */
584     if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
585         goto exit;
586
587     /* Pretrans dependencies can't be satisfied by added packages. */
588     if (!(dsflags & RPMSENSE_PRETRANS)) {
589         rpmte match = rpmalSatisfiesDepend(tsmem->addedPackages, dep);
590
591         /*
592          * Handle definitive matches within the added package set.
593          * Self-obsoletes and -conflicts fall through here as we need to 
594          * check for possible other matches in the rpmdb.
595          */
596         if (match) {
597             rpmTagVal dtag = rpmdsTagN(dep);
598             /* Requires match, look no further */
599             if (dtag == RPMTAG_REQUIRENAME)
600                 goto exit;
601
602             /* Conflicts/obsoletes match on another package, look no further */
603             if (rpmteDS(match, dtag) != dep)
604                 goto exit;
605         }
606     }
607
608     /* See if the rpmdb provides it */
609     if (rpmdbProvides(ts, dcache, dep) == 0)
610         goto exit;
611
612     /* Search for an unsatisfied dependency. */
613     if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS)) {
614         int xx = rpmtsSolve(ts, dep);
615         if (xx == 0)
616             goto exit;
617         if (xx == -1) {
618             retrying = 1;
619             goto retry;
620         }
621     }
622
623 unsatisfied:
624     if (dsflags & RPMSENSE_MISSINGOK) {
625         /* note the result, but missingok deps are never unsatisfied */
626         rpmdsNotify(dep, "(missingok)", 1);
627     } else {
628         /* dependency is unsatisfied */
629         rc = 1;
630         rpmdsNotify(dep, NULL, rc);
631     }
632
633 exit:
634     return rc;
635 }
636
637 /* Check a dependency set for problems */
638 static void checkDS(rpmts ts, depCache dcache, rpmte te,
639                 const char * pkgNEVRA, rpmds ds,
640                 const char * depName, rpm_color_t tscolor)
641 {
642     rpm_color_t dscolor;
643     /* require-problems are unsatisfied, others appear "satisfied" */
644     int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
645
646     ds = rpmdsInit(ds);
647     while (rpmdsNext(ds) >= 0) {
648         /* Filter out dependencies that came along for the ride. */
649         if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
650             continue;
651
652         /* Ignore colored dependencies not in our rainbow. */
653         dscolor = rpmdsColor(ds);
654         if (tscolor && dscolor && !(tscolor & dscolor))
655             continue;
656
657         if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
658             rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
659     }
660 }
661
662 /* Check a given dependency type against installed packages */
663 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
664                           rpmTag depTag, const char *dep)
665 {
666     Header h;
667     rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep, 1);
668     rpmstrPool pool = rpmtsPool(ts);
669
670     while ((h = rpmdbNextIterator(mi)) != NULL) {
671         char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
672         rpmds ds = rpmdsNewPool(pool, h, depTag, 0);
673
674         checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
675
676         rpmdsFree(ds);
677         free(pkgNEVRA);
678     }
679     rpmdbFreeIterator(mi);
680 }
681
682 int rpmtsCheck(rpmts ts)
683 {
684     rpm_color_t tscolor = rpmtsColor(ts);
685     rpmtsi pi = NULL; rpmte p;
686     int closeatexit = 0;
687     int rc = 0;
688     depCache dcache = NULL;
689     
690     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
691
692     /* Do lazy, readonly, open of rpm database. */
693     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
694         if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
695             goto exit;
696         closeatexit = 1;
697     }
698
699     /* XXX FIXME: figure some kind of heuristic for the cache size */
700     dcache = depCacheCreate(5001, rstrhash, strcmp,
701                                      (depCacheFreeKey)rfree, NULL);
702
703     /*
704      * Look at all of the added packages and make sure their dependencies
705      * are satisfied.
706      */
707     pi = rpmtsiInit(ts);
708     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
709         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
710
711         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
712                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
713
714         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
715                 NULL, tscolor);
716         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
717                 NULL, tscolor);
718         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_OBSOLETENAME),
719                 NULL, tscolor);
720
721         /* Check provides against conflicts in installed packages. */
722         while (rpmdsNext(provides) >= 0) {
723             checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
724         }
725
726         /* Skip obsoletion checks for source packages (ie build) */
727         if (rpmteIsSource(p))
728             continue;
729
730         /* Check package name (not provides!) against installed obsoletes */
731         checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
732     }
733     rpmtsiFree(pi);
734
735     /*
736      * Look at the removed packages and make sure they aren't critical.
737      */
738     pi = rpmtsiInit(ts);
739     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
740         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
741         rpmfi fi = rpmfiInit(rpmteFI(p), 0);
742
743         rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
744                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
745
746         /* Check provides and filenames against installed dependencies. */
747         while (rpmdsNext(provides) >= 0) {
748             checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
749         }
750
751         while (rpmfiNext(fi) >= 0) {
752             if (RPMFILE_IS_INSTALLED(rpmfiFState(fi)))
753                 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
754         }
755     }
756     rpmtsiFree(pi);
757
758 exit:
759     depCacheFree(dcache);
760
761     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
762
763     if (closeatexit)
764         (void) rpmtsCloseDB(ts);
765     return rc;
766 }