Only check the type of dependency requested in checkInstDeps()
[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/rpmcli.h>         /* XXX rpmcliPackagesTotal */
8
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>
15
16 #include "lib/rpmts_internal.h"
17 #include "lib/rpmte_internal.h"
18
19 #include "debug.h"
20
21 const char * const rpmNAME = PACKAGE;
22
23 const char * const rpmEVR = VERSION;
24
25 const int rpmFLAGS = RPMSENSE_EQUAL;
26
27 /* rpmlib provides */
28 static rpmds rpmlibP = NULL;
29
30 #undef HASHTYPE
31 #undef HTKEYTYPE
32 #undef HTDATATYPE
33
34 #define HASHTYPE depCache
35 #define HTKEYTYPE const char *
36 #define HTDATATYPE int
37 #include "lib/rpmhash.H"
38 #include "lib/rpmhash.C"
39
40 /**
41  * Compare removed package instances (qsort/bsearch).
42  * @param a             1st instance address
43  * @param b             2nd instance address
44  * @return              result of comparison
45  */
46 static int intcmp(const void * a, const void * b)
47 {
48     const int * aptr = a;
49     const int * bptr = b;
50     int rc = (*aptr - *bptr);
51     return rc;
52 }
53
54 /**
55  * Add removed package instance to ordered transaction set.
56  * @param ts            transaction set
57  * @param h             header
58  * @param depends       installed package of pair (or RPMAL_NOMATCH on erase)
59  * @return              0 on success
60  */
61 static int removePackage(tsMembers tsmem, Header h, rpmte depends)
62 {
63     rpmte p;
64     unsigned int dboffset = headerGetInstance(h);
65
66     /* Can't remove what's not installed */
67     if (dboffset == 0) return 1;
68
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)
74             return 0;
75     }
76
77     if (tsmem->numRemovedPackages == tsmem->allocedRemovedPackages) {
78         tsmem->allocedRemovedPackages += tsmem->delta;
79         tsmem->removedPackages = xrealloc(tsmem->removedPackages,
80                 sizeof(tsmem->removedPackages) * tsmem->allocedRemovedPackages);
81     }
82
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);
89     }
90
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);
94     }
95
96     p = rpmteNew(NULL, h, TR_REMOVED, NULL, NULL, -1);
97     rpmteSetDependsOn(p, depends);
98
99     tsmem->order[tsmem->orderCount] = p;
100     tsmem->orderCount++;
101
102     return 0;
103 }
104
105 /* Return rpmdb iterator with removals pruned out */
106 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmTag tag, const char * key)
107 {
108     rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
109     tsMembers tsmem = rpmtsMembers(ts);
110     rpmdbPruneIterator(mi, tsmem->removedPackages, tsmem->numRemovedPackages,1);
111     return mi;
112 }
113
114 #define skipColor(_tscolor, _color, _ocolor) \
115         ((_tscolor) && (_color) && (_ocolor) && !((_color) & (_ocolor)))
116
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)
120 {
121     Header oh;
122     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
123
124     while((oh = rpmdbNextIterator(mi)) != NULL) {
125         /* Ignore colored packages not in our rainbow. */
126         if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
127             continue;
128
129         /* Skip packages that contain identical NEVR. */
130         if (rpmVersionCompare(h, oh) == 0)
131             continue;
132
133         removePackage(tsmem, oh, p);
134     }
135     mi = rpmdbFreeIterator(mi);
136 }
137
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)
141 {
142     rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
143     Header oh;
144
145     while (rpmdsNext(obsoletes) >= 0) {
146         const char * Name;
147         rpmdbMatchIterator mi = NULL;
148
149         if ((Name = rpmdsN(obsoletes)) == NULL)
150             continue;   /* XXX can't happen */
151
152         /* XXX avoid self-obsoleting packages. */
153         if (rstreq(rpmteN(p), Name))
154             continue;
155
156         mi = rpmtsPrunedIterator(ts, RPMTAG_NAME, Name);
157
158         while((oh = rpmdbNextIterator(mi)) != NULL) {
159             /* Ignore colored packages not in our rainbow. */
160             if (skipColor(tscolor, hcolor, 
161                           headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
162                 continue;
163
164             /*
165              * Rpm prior to 3.0.3 does not have versioned obsoletes.
166              * If no obsoletes version info is available, match all names.
167              */
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);
174
175                 removePackage(tsmem, oh, p);
176             }
177         }
178         mi = rpmdbFreeIterator(mi);
179     }
180 }
181
182 /*
183  * Check for previously added versions and obsoletions.
184  * Return index where to place this element, or -1 to skip.
185  */
186 static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade)
187 {
188     int oc;
189     int obsolete = 0;
190     const char * arch = headerGetString(h, RPMTAG_ARCH);
191     const char * os = headerGetString(h, RPMTAG_OS);
192     rpmte p;
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);
198
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;
202
203         /* Only added binary packages need checking */
204         if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p))
205             continue;
206
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)) {
211                 obsolete = 1;
212                 oc = -1;
213                 break;
214             }
215         }
216
217         /* Replace already added obsoleted packages by obsoleting package */
218         this = rpmteDS(p, RPMTAG_NAME);
219         rpmdsInit(obsChk);
220         while (rpmdsNext(obsChk) >= 0) {
221             if (rpmdsCompare(obsChk, this)) {
222                 obsolete = 1;
223                 break;
224             }
225         }
226
227         if (obsolete)
228             break;
229
230         if (tscolor) {
231             const char * parch = rpmteA(p);
232             const char * pos = rpmteO(p);
233
234             if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
235                 continue;
236             if (!rstreq(arch, parch) || !rstreq(os, pos))
237                 continue;
238         }
239
240         /* 
241          * Always skip identical NEVR. 
242          * On upgrade, if newer NEVR was previously added, skip adding older.
243          */
244         if (rpmdsCompare(sameChk, this) ||
245                 (upgrade && rpmdsCompare(newChk, this))) {
246             oc = -1;
247             break;;
248         }
249
250         /* On upgrade, if older NEVR was previously added, replace with new */
251         if (upgrade && rpmdsCompare(oldChk, this) != 0) {
252             break;
253         }
254     }
255
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);
263         free(nevra);
264     }
265
266     rpmtsiFree(pi);
267     rpmdsFree(oldChk);
268     rpmdsFree(newChk);
269     rpmdsFree(sameChk);
270     rpmdsFree(obsChk);
271     return oc;
272 }
273
274
275 int rpmtsAddInstallElement(rpmts ts, Header h,
276                         fnpyKey key, int upgrade, rpmRelocation * relocs)
277 {
278     tsMembers tsmem = rpmtsMembers(ts);
279     rpm_color_t tscolor = rpmtsColor(ts);
280     rpmte p = NULL;
281     int isSource = headerIsSource(h);
282     int ec = 0;
283     int oc = tsmem->orderCount;
284
285     /* Check for supported payload format if it's a package */
286     if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
287         ec = 1;
288         goto exit;
289     }
290
291     /* Check binary packages for redundancies in the set */
292     if (!isSource) {
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 */
299         } else if (oc < 0) {
300             goto exit;
301         }
302     }
303
304     if (tsmem->addedPackages == NULL) {
305         tsmem->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
306     }
307
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));
312     }
313
314     p = rpmteNew(NULL, h, TR_ADDED, key, relocs, -1);
315
316     tsmem->order[oc] = p;
317     if (oc == tsmem->orderCount) {
318         tsmem->orderCount++;
319         tsmem->numAddedPackages++;
320         rpmcliPackagesTotal++;
321     }
322     
323     rpmalAdd(tsmem->addedPackages, p);
324
325     /* If not upgrading or a source package, then we're done. */
326     if (!(upgrade & 0x1) || isSource)
327         goto exit;
328
329     /* Do lazy (readonly?) open of rpm database. */
330     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
331         if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
332             goto exit;
333     }
334
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));
338
339 exit:
340     return ec;
341 }
342
343 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
344 {
345     return removePackage(rpmtsMembers(ts), h, NULL);
346 }
347
348 /**
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
353  */
354 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
355 {
356     tsMembers tsmem = rpmtsMembers(ts);
357     rpmdbMatchIterator mi;
358     const char * Name = rpmdsN(dep);
359     const char * DNEVR = rpmdsDNEVR(dep);
360     Header h;
361     int rc;
362     int xx;
363     int retrying = 0;
364     int *cachedrc = NULL;
365     int cacheThis = 0;
366     int adding = (rpmdsInstance(dep) == 0);
367     rpmsenseFlags dsflags = rpmdsFlags(dep);
368
369 retry:
370     rc = 0;     /* assume dependency is satisfied */
371
372     /*
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.
376      */
377     if (dsflags & RPMSENSE_RPMLIB) {
378         static int oneshot = -1;
379         if (oneshot) 
380             oneshot = rpmdsRpmlib(&rpmlibP, NULL);
381         
382         if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
383             rpmdsNotify(dep, "(rpmlib provides)", rc);
384             goto exit;
385         }
386         goto unsatisfied;
387     }
388
389     /* Dont look at pre-requisites of already installed packages */
390     if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
391         goto exit;
392
393     /* Pretrans dependencies can't be satisfied by added packages. */
394     if (!(dsflags & RPMSENSE_PRETRANS) &&
395                 rpmalSatisfiesDepend(tsmem->addedPackages, dep) != NULL) {
396         goto exit;
397     }
398
399     /* See if we already looked this up */
400     if (depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) {
401         rc = *cachedrc;
402         rpmdsNotify(dep, "(cached)", rc);
403         return rc;
404     }
405     /* Only bother caching the expensive rpmdb lookups */
406     cacheThis = 1;
407
408     if (Name[0] == '/') {
409         mi = rpmtsPrunedIterator(ts, RPMTAG_BASENAMES, Name);
410
411         while ((h = rpmdbNextIterator(mi)) != NULL) {
412             rpmdsNotify(dep, "(db files)", rc);
413             mi = rpmdbFreeIterator(mi);
414             goto exit;
415         }
416         mi = rpmdbFreeIterator(mi);
417     }
418
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);
424             goto exit;
425         }
426     }
427     mi = rpmdbFreeIterator(mi);
428
429     /*
430      * Search for an unsatisfied dependency.
431      */
432     if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS) &&
433                 !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
434         xx = rpmtsSolve(ts, dep);
435         if (xx == 0)
436             goto exit;
437         if (xx == -1) {
438             retrying = 1;
439             goto retry;
440         }
441     }
442
443 unsatisfied:
444     rc = 1;     /* dependency is unsatisfied */
445     rpmdsNotify(dep, NULL, rc);
446
447 exit:
448     if (cacheThis) {
449         char *key = xstrdup(DNEVR);
450         depCacheAddEntry(dcache, key, rc);
451     }
452         
453     return rc;
454 }
455
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)
460 {
461     rpm_color_t dscolor;
462     /* require-problems are unsatisfied, others appear "satisfied" */
463     int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
464
465     ds = rpmdsInit(ds);
466     while (rpmdsNext(ds) >= 0) {
467         /* Filter out dependencies that came along for the ride. */
468         if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
469             continue;
470
471         /* Ignore colored dependencies not in our rainbow. */
472         dscolor = rpmdsColor(ds);
473         if (tscolor && dscolor && !(tscolor & dscolor))
474             continue;
475
476         if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
477             rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
478     }
479 }
480
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)
484 {
485     Header h;
486     rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep);
487
488     while ((h = rpmdbNextIterator(mi)) != NULL) {
489         char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
490         rpmds ds = rpmdsNew(h, depTag, 0);
491
492         checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
493
494         ds = rpmdsFree(ds);
495         pkgNEVRA = _free(pkgNEVRA);
496     }
497     rpmdbFreeIterator(mi);
498 }
499
500 int rpmtsCheck(rpmts ts)
501 {
502     rpm_color_t tscolor = rpmtsColor(ts);
503     rpmtsi pi = NULL; rpmte p;
504     int closeatexit = 0;
505     int rc;
506     depCache dcache = NULL;
507     
508     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
509
510     /* Do lazy, readonly, open of rpm database. */
511     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
512         if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
513             goto exit;
514         closeatexit = 1;
515     }
516
517     /* XXX FIXME: figure some kind of heuristic for the cache size */
518     dcache = depCacheCreate(5001, hashFunctionString, strcmp,
519                                      (depCacheFreeKey)rfree, NULL);
520
521     /*
522      * Look at all of the added packages and make sure their dependencies
523      * are satisfied.
524      */
525     pi = rpmtsiInit(ts);
526     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
527         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
528
529         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
530                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
531
532         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
533                 NULL, tscolor);
534         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
535                 NULL, tscolor);
536
537         /* Check provides against conflicts in installed packages. */
538         while (rpmdsNext(provides) >= 0) {
539             checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
540         }
541
542         /* Check package name (not provides!) against installed obsoletes */
543         checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
544     }
545     pi = rpmtsiFree(pi);
546
547     /*
548      * Look at the removed packages and make sure they aren't critical.
549      */
550     pi = rpmtsiInit(ts);
551     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
552         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
553         rpmfi fi = rpmfiInit(rpmteFI(p), 0);
554
555         rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
556                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
557
558         /* Check provides and filenames against installed dependencies. */
559         while (rpmdsNext(provides) >= 0) {
560             checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
561         }
562
563         while (rpmfiNext(fi) >= 0) {
564             checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
565         }
566     }
567     pi = rpmtsiFree(pi);
568
569 exit:
570     depCacheFree(dcache);
571
572     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
573
574     if (closeatexit)
575         (void) rpmtsCloseDB(ts);
576     return rc;
577 }