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