Change all internal uses of rpmtsInitIterator() to use DBI tags
[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 #include "lib/misc.h"
19
20 #include "debug.h"
21
22 const char * const RPMVERSION = VERSION;
23
24 const char * const rpmNAME = PACKAGE;
25
26 const char * const rpmEVR = VERSION;
27
28 const int rpmFLAGS = RPMSENSE_EQUAL;
29
30 /* rpmlib provides */
31 static rpmds rpmlibP = NULL;
32
33 #undef HASHTYPE
34 #undef HTKEYTYPE
35 #undef HTDATATYPE
36
37 #define HASHTYPE depCache
38 #define HTKEYTYPE const char *
39 #define HTDATATYPE int
40 #include "lib/rpmhash.H"
41 #include "lib/rpmhash.C"
42 #undef HASHTYPE
43 #undef HTKEYTYPE
44 #undef HTDATATYPE
45
46 #define HASHTYPE intHash
47 #define HTKEYTYPE unsigned int
48 #include "rpmhash.C"
49 #undef HASHTYPE
50 #undef HASHKEYTYPE
51
52 /**
53  * Add removed package instance to ordered transaction set.
54  * @param ts            transaction set
55  * @param h             header
56  * @param depends       installed package of pair (or RPMAL_NOMATCH on erase)
57  * @return              0 on success
58  */
59 static int removePackage(rpmts ts, Header h, rpmte depends)
60 {
61     tsMembers tsmem = rpmtsMembers(ts);
62     rpmte p;
63     unsigned int dboffset = headerGetInstance(h);
64
65     /* Can't remove what's not installed */
66     if (dboffset == 0) return 1;
67
68     /* Filter out duplicate erasures. */
69     if (intHashHasEntry(tsmem->removedPackages, dboffset)) {
70         return 0;
71     }
72
73     intHashAddEntry(tsmem->removedPackages, dboffset);
74
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);
78     }
79
80     p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
81     rpmteSetDependsOn(p, depends);
82
83     tsmem->order[tsmem->orderCount] = p;
84     tsmem->orderCount++;
85
86     return 0;
87 }
88
89 /* Return rpmdb iterator with removals pruned out */
90 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, const char * key)
91 {
92     rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
93     tsMembers tsmem = rpmtsMembers(ts);
94     rpmdbPruneIterator(mi, tsmem->removedPackages);
95     return mi;
96 }
97
98 #define skipColor(_tscolor, _color, _ocolor) \
99         ((_tscolor) && (_color) && (_ocolor) && !((_color) & (_ocolor)))
100
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)
104 {
105     Header oh;
106     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
107
108     while((oh = rpmdbNextIterator(mi)) != NULL) {
109         /* Ignore colored packages not in our rainbow. */
110         if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
111             continue;
112
113         /* Skip packages that contain identical NEVR. */
114         if (rpmVersionCompare(h, oh) == 0)
115             continue;
116
117         removePackage(ts, oh, p);
118     }
119     mi = rpmdbFreeIterator(mi);
120 }
121
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)
125 {
126     rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
127     Header oh;
128
129     while (rpmdsNext(obsoletes) >= 0) {
130         const char * Name;
131         rpmdbMatchIterator mi = NULL;
132
133         if ((Name = rpmdsN(obsoletes)) == NULL)
134             continue;   /* XXX can't happen */
135
136         /* XXX avoid self-obsoleting packages. */
137         if (rstreq(rpmteN(p), Name))
138             continue;
139
140         mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name);
141
142         while((oh = rpmdbNextIterator(mi)) != NULL) {
143             /* Ignore colored packages not in our rainbow. */
144             if (skipColor(tscolor, hcolor, 
145                           headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
146                 continue;
147
148             /*
149              * Rpm prior to 3.0.3 does not have versioned obsoletes.
150              * If no obsoletes version info is available, match all names.
151              */
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);
158
159                 removePackage(ts, oh, p);
160             }
161         }
162         mi = rpmdbFreeIterator(mi);
163     }
164 }
165
166 /*
167  * Check for previously added versions and obsoletions.
168  * Return index where to place this element, or -1 to skip.
169  */
170 static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade)
171 {
172     int oc;
173     int obsolete = 0;
174     const char * arch = headerGetString(h, RPMTAG_ARCH);
175     const char * os = headerGetString(h, RPMTAG_OS);
176     rpmte p;
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);
182
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;
186
187         /* Only added binary packages need checking */
188         if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p))
189             continue;
190
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)) {
195                 obsolete = 1;
196                 oc = -1;
197                 break;
198             }
199         }
200
201         /* Replace already added obsoleted packages by obsoleting package */
202         thisds = rpmteDS(p, RPMTAG_NAME);
203         rpmdsInit(obsChk);
204         while (rpmdsNext(obsChk) >= 0) {
205             if (rpmdsCompare(obsChk, thisds)) {
206                 obsolete = 1;
207                 break;
208             }
209         }
210
211         if (obsolete)
212             break;
213
214         if (tscolor) {
215             const char * parch = rpmteA(p);
216             const char * pos = rpmteO(p);
217
218             if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
219                 continue;
220             if (!rstreq(arch, parch) || !rstreq(os, pos))
221                 continue;
222         }
223
224         /* 
225          * Always skip identical NEVR. 
226          * On upgrade, if newer NEVR was previously added, skip adding older.
227          */
228         if (rpmdsCompare(sameChk, thisds) ||
229                 (upgrade && rpmdsCompare(newChk, thisds))) {
230             oc = -1;
231             break;;
232         }
233
234         /* On upgrade, if older NEVR was previously added, replace with new */
235         if (upgrade && rpmdsCompare(oldChk, thisds) != 0) {
236             break;
237         }
238     }
239
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);
247         free(nevra);
248     }
249
250     rpmtsiFree(pi);
251     rpmdsFree(oldChk);
252     rpmdsFree(newChk);
253     rpmdsFree(sameChk);
254     rpmdsFree(obsChk);
255     return oc;
256 }
257
258
259 int rpmtsAddInstallElement(rpmts ts, Header h,
260                         fnpyKey key, int upgrade, rpmRelocation * relocs)
261 {
262     tsMembers tsmem = rpmtsMembers(ts);
263     rpm_color_t tscolor = rpmtsColor(ts);
264     rpmte p = NULL;
265     int isSource = headerIsSource(h);
266     int ec = 0;
267     int oc = tsmem->orderCount;
268
269     /* Check for supported payload format if it's a package */
270     if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
271         ec = 1;
272         goto exit;
273     }
274
275     /* Check binary packages for redundancies in the set */
276     if (!isSource) {
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 */
283         } else if (oc < 0) {
284             goto exit;
285         }
286     }
287
288     if (tsmem->addedPackages == NULL) {
289         tsmem->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
290     }
291
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));
296     }
297
298     p = rpmteNew(ts, h, TR_ADDED, key, relocs);
299
300     tsmem->order[oc] = p;
301     if (oc == tsmem->orderCount) {
302         tsmem->orderCount++;
303         rpmcliPackagesTotal++;
304     }
305     
306     rpmalAdd(tsmem->addedPackages, p);
307
308     /* If not upgrading or a source package, then we're done. */
309     if (!(upgrade & 0x1) || isSource)
310         goto exit;
311
312     /* Do lazy (readonly?) open of rpm database. */
313     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
314         if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
315             goto exit;
316     }
317
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));
321
322 exit:
323     return ec;
324 }
325
326 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
327 {
328     return removePackage(ts, h, NULL);
329 }
330
331 /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
332 static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
333 {
334     const char * Name = rpmdsN(dep);
335     const char * DNEVR = rpmdsDNEVR(dep);
336     int *cachedrc = NULL;
337     rpmdbMatchIterator mi = NULL;
338     Header h = NULL;
339     int rc = 0;
340
341     /* See if we already looked this up */
342     if (depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) {
343         rc = *cachedrc;
344         rpmdsNotify(dep, "(cached)", rc);
345         return rc;
346     }
347
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);
353             break;
354         }
355         rpmdbFreeIterator(mi);
356     }
357
358     /* Otherwise look in provides no matter what the dependency looks like */
359     if (h == NULL) {
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);
364                 break;
365             }
366         }
367         rpmdbFreeIterator(mi);
368     }
369     rc = (h != NULL) ? 0 : 1;
370
371     /* Cache the relatively expensive rpmdb lookup results */
372     depCacheAddEntry(dcache, xstrdup(DNEVR), rc);
373     return rc;
374 }
375
376 /**
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
381  */
382 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
383 {
384     tsMembers tsmem = rpmtsMembers(ts);
385     int rc;
386     int retrying = 0;
387     int adding = (rpmdsInstance(dep) == 0);
388     rpmsenseFlags dsflags = rpmdsFlags(dep);
389
390 retry:
391     rc = 0;     /* assume dependency is satisfied */
392
393     /*
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.
397      */
398     if (dsflags & RPMSENSE_RPMLIB) {
399         static int oneshot = -1;
400         if (oneshot) 
401             oneshot = rpmdsRpmlib(&rpmlibP, NULL);
402         
403         if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
404             rpmdsNotify(dep, "(rpmlib provides)", rc);
405             goto exit;
406         }
407         goto unsatisfied;
408     }
409
410     /* Dont look at pre-requisites of already installed packages */
411     if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
412         goto exit;
413
414     /* Pretrans dependencies can't be satisfied by added packages. */
415     if (!(dsflags & RPMSENSE_PRETRANS) &&
416                 rpmalSatisfiesDepend(tsmem->addedPackages, dep) != NULL) {
417         goto exit;
418     }
419
420     /* See if the rpmdb provides it */
421     if (rpmdbProvides(ts, dcache, dep) == 0)
422         goto exit;
423
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);
428         if (xx == 0)
429             goto exit;
430         if (xx == -1) {
431             retrying = 1;
432             goto retry;
433         }
434     }
435
436 unsatisfied:
437     rc = 1;     /* dependency is unsatisfied */
438     rpmdsNotify(dep, NULL, rc);
439
440 exit:
441     return rc;
442 }
443
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)
448 {
449     rpm_color_t dscolor;
450     /* require-problems are unsatisfied, others appear "satisfied" */
451     int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
452
453     ds = rpmdsInit(ds);
454     while (rpmdsNext(ds) >= 0) {
455         /* Filter out dependencies that came along for the ride. */
456         if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
457             continue;
458
459         /* Ignore colored dependencies not in our rainbow. */
460         dscolor = rpmdsColor(ds);
461         if (tscolor && dscolor && !(tscolor & dscolor))
462             continue;
463
464         if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
465             rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
466     }
467 }
468
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)
472 {
473     Header h;
474     rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep);
475
476     while ((h = rpmdbNextIterator(mi)) != NULL) {
477         char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
478         rpmds ds = rpmdsNew(h, depTag, 0);
479
480         checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
481
482         ds = rpmdsFree(ds);
483         pkgNEVRA = _free(pkgNEVRA);
484     }
485     rpmdbFreeIterator(mi);
486 }
487
488 int rpmtsCheck(rpmts ts)
489 {
490     rpm_color_t tscolor = rpmtsColor(ts);
491     rpmtsi pi = NULL; rpmte p;
492     int closeatexit = 0;
493     int rc = 0;
494     depCache dcache = NULL;
495     
496     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
497
498     /* Do lazy, readonly, open of rpm database. */
499     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
500         if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
501             goto exit;
502         closeatexit = 1;
503     }
504
505     /* XXX FIXME: figure some kind of heuristic for the cache size */
506     dcache = depCacheCreate(5001, hashFunctionString, strcmp,
507                                      (depCacheFreeKey)rfree, NULL);
508
509     /*
510      * Look at all of the added packages and make sure their dependencies
511      * are satisfied.
512      */
513     pi = rpmtsiInit(ts);
514     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
515         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
516
517         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
518                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
519
520         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
521                 NULL, tscolor);
522         checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
523                 NULL, tscolor);
524
525         /* Check provides against conflicts in installed packages. */
526         while (rpmdsNext(provides) >= 0) {
527             checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
528         }
529
530         /* Skip obsoletion checks for source packages (ie build) */
531         if (rpmteIsSource(p))
532             continue;
533
534         /* Check package name (not provides!) against installed obsoletes */
535         checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
536     }
537     pi = rpmtsiFree(pi);
538
539     /*
540      * Look at the removed packages and make sure they aren't critical.
541      */
542     pi = rpmtsiInit(ts);
543     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
544         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
545         rpmfi fi = rpmfiInit(rpmteFI(p), 0);
546
547         rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
548                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
549
550         /* Check provides and filenames against installed dependencies. */
551         while (rpmdsNext(provides) >= 0) {
552             checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
553         }
554
555         while (rpmfiNext(fi) >= 0) {
556             checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
557         }
558     }
559     pi = rpmtsiFree(pi);
560
561 exit:
562     depCacheFree(dcache);
563
564     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
565
566     if (closeatexit)
567         (void) rpmtsCloseDB(ts);
568     return rc;
569 }