- fix package priority handling of installed packages in dup case [bnc#631306]
[platform/upstream/libsolv.git] / src / policy.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * Generic policy interface for SAT solver
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17
18 #include "solver.h"
19 #include "evr.h"
20 #include "policy.h"
21 #include "poolvendor.h"
22 #include "poolarch.h"
23
24
25 /*-----------------------------------------------------------------*/
26
27 /*
28  * prep for prune_best_version_arch
29  *   sort by name
30  */
31
32 static int
33 prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp)
34 {
35   Pool *pool = dp;
36   int r;
37   Id a = *(Id *)ap;
38   Id b = *(Id *)bp;
39   Solvable *sa, *sb;
40
41   sa = pool->solvables + a;
42   sb = pool->solvables + b;
43   r = sa->name - sb->name;
44   if (r)
45     {
46       const char *na, *nb;
47       /* different names. We use real strcmp here so that the result
48        * is not depending on some random solvable order */
49       na = id2str(pool, sa->name);
50       nb = id2str(pool, sb->name);
51       return strcmp(na, nb);
52     }
53   /* the same name, bring installed solvables to the front */
54   if (pool->installed)
55     {
56       if (sa->repo == pool->installed)
57         {
58           if (sb->repo != pool->installed)
59             return -1;
60         }
61       else if (sb->repo == pool->installed)
62         return 1;       
63     }
64   /* sort by repository sub-prio (installed repo handled above) */
65   r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
66   if (r)
67     return r;
68   /* no idea about the order, sort by id */
69   return a - b;
70 }
71
72
73 /*
74  * prune to repository with highest priority.
75  * does not prune installed solvables.
76  */
77
78 static void
79 prune_to_highest_prio(Pool *pool, Queue *plist)
80 {
81   int i, j;
82   Solvable *s;
83   int bestprio = 0, bestprioset = 0;
84
85   /* prune to highest priority */
86   for (i = 0; i < plist->count; i++)  /* find highest prio in queue */
87     {
88       s = pool->solvables + plist->elements[i];
89       if (pool->installed && s->repo == pool->installed)
90         continue;
91       if (!bestprioset || s->repo->priority > bestprio)
92         {
93           bestprio = s->repo->priority;
94           bestprioset = 1;
95         }
96     }
97   if (!bestprioset)
98     return;
99   for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
100     {
101       s = pool->solvables + plist->elements[i];
102       if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
103         plist->elements[j++] = plist->elements[i];
104     }
105   plist->count = j;
106 }
107
108
109 /*
110  * prune to recommended/suggested packages.
111  * does not prune installed packages (they are also somewhat recommended).
112  */
113
114 static void
115 prune_to_recommended(Solver *solv, Queue *plist)
116 {
117   Pool *pool = solv->pool;
118   int i, j, k, ninst;
119   Solvable *s;
120   Id p, pp, rec, *recp, sug, *sugp;
121
122   ninst = 0;
123   if (pool->installed)
124     {
125       for (i = 0; i < plist->count; i++)
126         {
127           p = plist->elements[i];
128           s = pool->solvables + p;
129           if (pool->installed && s->repo == pool->installed)
130             ninst++;
131         }
132     }
133   if (plist->count - ninst < 2)
134     return;
135
136   /* update our recommendsmap/suggestsmap */
137   if (solv->recommends_index < 0)
138     {
139       MAPZERO(&solv->recommendsmap);
140       MAPZERO(&solv->suggestsmap);
141       solv->recommends_index = 0;
142     }
143   while (solv->recommends_index < solv->decisionq.count)
144     {
145       p = solv->decisionq.elements[solv->recommends_index++];
146       if (p < 0)
147         continue;
148       s = pool->solvables + p;
149       if (s->recommends)
150         {
151           recp = s->repo->idarraydata + s->recommends;
152           while ((rec = *recp++) != 0)
153             FOR_PROVIDES(p, pp, rec)
154               MAPSET(&solv->recommendsmap, p);
155         }
156       if (s->suggests)
157         {
158           sugp = s->repo->idarraydata + s->suggests;
159           while ((sug = *sugp++) != 0)
160             FOR_PROVIDES(p, pp, sug)
161               MAPSET(&solv->suggestsmap, p);
162         }
163     }
164
165   /* prune to recommended/supplemented */
166   ninst = 0;
167   for (i = j = 0; i < plist->count; i++)
168     {
169       p = plist->elements[i];
170       s = pool->solvables + p;
171       if (pool->installed && s->repo == pool->installed)
172         {
173           ninst++;
174           if (j)
175             plist->elements[j++] = p;
176           continue;
177         }
178       if (!MAPTST(&solv->recommendsmap, p))
179         if (!solver_is_supplementing(solv, s))
180           continue;
181       if (!j && ninst)
182         {
183           for (k = 0; j < ninst; k++)
184             {
185               s = pool->solvables + plist->elements[k];
186               if (pool->installed && s->repo == pool->installed)
187                 plist->elements[j++] = plist->elements[k];
188             }
189         }
190       plist->elements[j++] = p;
191     }
192   if (j)
193     plist->count = j;
194
195   /* anything left to prune? */
196   if (plist->count - ninst < 2)
197     return;
198
199   /* prune to suggested/enhanced*/
200   ninst = 0;
201   for (i = j = 0; i < plist->count; i++)
202     {
203       p = plist->elements[i];
204       s = pool->solvables + p;
205       if (pool->installed && s->repo == pool->installed)
206         {
207           ninst++;
208           if (j)
209             plist->elements[j++] = p;
210           continue;
211         }
212       if (!MAPTST(&solv->suggestsmap, p))
213         if (!solver_is_enhancing(solv, s))
214           continue;
215       if (!j && ninst)
216         {
217           for (k = 0; j < ninst; k++)
218             {
219               s = pool->solvables + plist->elements[k];
220               if (pool->installed && s->repo == pool->installed)
221                 plist->elements[j++] = plist->elements[k];
222             }
223         }
224       plist->elements[j++] = p;
225     }
226   if (j)
227     plist->count = j;
228 }
229
230 void
231 prune_to_best_arch(const Pool *pool, Queue *plist)
232 {
233   Id a, bestscore;
234   Solvable *s;
235   int i, j;
236
237   if (!pool->id2arch || plist->count < 2)
238     return;
239   bestscore = 0;
240   for (i = 0; i < plist->count; i++)
241     {
242       s = pool->solvables + plist->elements[i];
243       a = s->arch;
244       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
245       if (a && a != 1 && (!bestscore || a < bestscore))
246         bestscore = a;
247     }
248   for (i = j = 0; i < plist->count; i++)
249     {
250       s = pool->solvables + plist->elements[i];
251       a = s->arch;
252       if (a > pool->lastarch)
253         continue;
254       a = pool->id2arch[a];
255       /* a == 1 -> noarch */
256       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
257         continue;
258       plist->elements[j++] = plist->elements[i];
259     }
260   if (j)
261     plist->count = j;
262 }
263
264 /*
265  * prune_to_best_version
266  *
267  * sort list of packages (given through plist) by name and evr
268  * return result through plist
269  */
270 void
271 prune_to_best_version(Pool *pool, Queue *plist)
272 {
273   Id best;
274   int i, j;
275   Solvable *s;
276
277   if (plist->count < 2)         /* no need to prune for a single entry */
278     return;
279   POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
280
281   /* sort by name first, prefer installed */
282   sat_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
283
284   /* delete obsoleted. hmm, looks expensive! */
285   /* FIXME maybe also check provides depending on noupdateprovide? */
286   /* FIXME do not prune cycles */
287   for (i = 0; i < plist->count; i++)
288     {
289       Id p, pp, obs, *obsp;
290       s = pool->solvables + plist->elements[i];
291       if (!s->obsoletes)
292         continue;
293       obsp = s->repo->idarraydata + s->obsoletes;
294       while ((obs = *obsp++) != 0)
295         {
296           FOR_PROVIDES(p, pp, obs)
297             {
298               Solvable *ps = pool->solvables + p;
299               if (ps->name == s->name)
300                 continue;
301               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
302                 continue;
303               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
304                 continue;
305               for (j = 0; j < plist->count; j++)
306                 {
307                   if (i == j)
308                     continue;
309                   if (plist->elements[j] == p)
310                     plist->elements[j] = 0;
311                 }
312             }
313         }
314     }
315   /* delete zeroed out queue entries */
316   for (i = j = 0; i < plist->count; i++)
317     if (plist->elements[i])
318       plist->elements[j++] = plist->elements[i];
319   plist->count = j;
320
321   /* now find best 'per name' */
322   best = 0;
323   for (i = j = 0; i < plist->count; i++)
324     {
325       s = pool->solvables + plist->elements[i];
326
327       POOL_DEBUG(SAT_DEBUG_POLICY, "- %s[%s]\n",
328                  solvable2str(pool, s),
329                  (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
330
331       if (!best)                       /* if no best yet, the current is best */
332         {
333           best = plist->elements[i];
334           continue;
335         }
336
337       /* name switch: re-init */
338       if (pool->solvables[best].name != s->name)   /* new name */
339         {
340           plist->elements[j++] = best; /* move old best to front */
341           best = plist->elements[i];   /* take current as new best */
342           continue;
343         }
344
345       if (pool->solvables[best].evr != s->evr)   /* compare evr */
346         {
347           if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_COMPARE) < 0)
348             best = plist->elements[i];
349         }
350     }
351
352   if (!best)
353     best = plist->elements[0];
354
355   plist->elements[j++] = best;
356   plist->count = j;
357 }
358
359
360 /* legacy, do not use anymore!
361  * (rates arch higher than version, but thats a policy)
362  */
363
364 void
365 prune_best_arch_name_version(const Solver *solv, Pool *pool, Queue *plist)
366 {
367   if (solv && solv->bestSolvableCb)
368     { /* The application is responsible for */
369       return solv->bestSolvableCb(solv->pool, plist);
370     }
371
372   if (plist->count > 1)
373     prune_to_best_arch(pool, plist);
374   if (plist->count > 1)
375     prune_to_best_version(pool, plist);
376 }
377
378
379 void
380 policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
381 {
382   Pool *pool = solv->pool;
383   if (plist->count > 1 && mode != POLICY_MODE_SUGGEST)
384     {
385       prune_to_highest_prio(pool, plist);
386       /* installed packages involed in a dup operation can only be kept
387        * if they are identical to a non-installed one */
388       if (plist->count > 1 && pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
389         {
390           int i, j, k;
391           for (i = j = 0; i < plist->count; i++)
392             {
393               Id p = plist->elements[i];
394               Solvable *s = pool->solvables + p;
395               if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
396                 {
397                   for (k = 0; k < plist->count; k++)
398                     {
399                       Solvable *s2 = pool->solvables + plist->elements[k];
400                       if (s2->repo != pool->installed && solvable_identical(s, s2))
401                         break;
402                     }
403                   if (k == plist->count)
404                     continue;   /* no identical package found, ignore installed package */
405                 }
406               plist->elements[j++] = p;
407             }
408           if (j)
409             plist->count = j;
410         }
411     }
412   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
413     prune_to_recommended(solv, plist);
414   prune_best_arch_name_version(solv, pool, plist);
415 }
416
417
418 int
419 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
420 {
421   Pool *pool = solv->pool;
422   Id a1 = s1->arch, a2 = s2->arch;
423
424   if (solv && solv->archCheckCb)
425     { /* The application is responsible for */
426       return solv->archCheckCb(solv->pool, s1, s2);
427     }
428
429   /* we allow changes to/from noarch */
430 #ifndef DEBIAN_SEMANTICS
431   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
432     return 0;
433 #else
434   if (a1 == a2 || a1 == ARCH_ALL || a2 == ARCH_ALL)
435     return 0;
436 #endif
437   if (!pool->id2arch)
438     return 0;
439   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
440   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
441   if (((a1 ^ a2) & 0xffff0000) != 0)
442     return 1;
443   return 0;
444 }
445
446 int
447 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
448 {
449   Pool *pool = solv->pool;
450   Id v1, v2;
451   Id vendormask1, vendormask2;
452
453   if (solv->vendorCheckCb)
454    {   /* The application is responsible for */
455      return solv->vendorCheckCb(pool, s1, s2);
456    }
457   /* treat a missing vendor as empty string */
458   v1 = s1->vendor ? s1->vendor : ID_EMPTY;
459   v2 = s2->vendor ? s2->vendor : ID_EMPTY;
460   if (v1 == v2)
461     return 0;
462   vendormask1 = pool_vendor2mask(pool, v1);
463   if (!vendormask1)
464     return 1;   /* can't match */
465   vendormask2 = pool_vendor2mask(pool, v2);
466   if ((vendormask1 & vendormask2) != 0)
467     return 0;
468   return 1;     /* no class matches */
469 }
470
471 /* check if it is illegal to replace installed
472  * package "is" with package "s" (which must obsolete "is")
473  */
474 int
475 policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
476 {
477   Pool *pool = solv->pool;
478   int ret = 0;
479   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !solv->allowdowngrade)
480     {
481       if (is->name == s->name && evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
482         ret |= POLICY_ILLEGAL_DOWNGRADE;
483     }
484   if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !solv->allowarchchange)
485     {
486       if (is->arch != s->arch && policy_illegal_archchange(solv, s, is))
487         ret |= POLICY_ILLEGAL_ARCHCHANGE;
488     }
489   if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !solv->allowvendorchange)
490     {
491       if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, s, is))
492         ret |= POLICY_ILLEGAL_VENDORCHANGE;
493     }
494   return ret;
495 }
496
497 /*-------------------------------------------------------------------
498  * 
499  * create reverse obsoletes map for installed solvables
500  *
501  * For each installed solvable find which packages with *different* names
502  * obsolete the solvable.
503  * This index is used in policy_findupdatepackages() below.
504  */
505 void
506 policy_create_obsolete_index(Solver *solv)
507 {
508   Pool *pool = solv->pool;
509   Solvable *s;
510   Repo *installed = solv->installed;
511   Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
512   int i, n, cnt;
513
514   if (!installed || installed->start == installed->end)
515     return;
516   cnt = installed->end - installed->start;
517   solv->obsoletes = obsoletes = sat_calloc(cnt, sizeof(Id));
518   for (i = 1; i < pool->nsolvables; i++)
519     {
520       s = pool->solvables + i;
521       if (!s->obsoletes)
522         continue;
523       if (!pool_installable(pool, s))
524         continue;
525       obsp = s->repo->idarraydata + s->obsoletes;
526       while ((obs = *obsp++) != 0)
527         {
528           FOR_PROVIDES(p, pp, obs)
529             {
530               Solvable *ps = pool->solvables + p;;
531               if (ps->repo != installed)
532                 continue;
533               if (ps->name == s->name)
534                 continue;
535               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
536                 continue;
537               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
538                 continue;
539               obsoletes[p - installed->start]++;
540             }
541         }
542     }
543   n = 0;
544   for (i = 0; i < cnt; i++)
545     if (obsoletes[i])
546       {
547         n += obsoletes[i] + 1;
548         obsoletes[i] = n;
549       }
550   solv->obsoletes_data = obsoletes_data = sat_calloc(n + 1, sizeof(Id));
551   POOL_DEBUG(SAT_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
552   for (i = pool->nsolvables - 1; i > 0; i--)
553     {
554       s = pool->solvables + i;
555       if (!s->obsoletes)
556         continue;
557       if (!pool_installable(pool, s))
558         continue;
559       obsp = s->repo->idarraydata + s->obsoletes;
560       while ((obs = *obsp++) != 0)
561         {
562           FOR_PROVIDES(p, pp, obs)
563             {
564               Solvable *ps = pool->solvables + p;;
565               if (ps->repo != installed)
566                 continue;
567               if (ps->name == s->name)
568                 continue;
569               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
570                 continue;
571               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
572                 continue;
573               if (obsoletes_data[obsoletes[p - installed->start]] != i)
574                 obsoletes_data[--obsoletes[p - installed->start]] = i;
575             }
576         }
577     }
578 }
579
580
581 /*
582  * find update candidates
583  * 
584  * s: solvable to be updated
585  * qs: [out] queue to hold Ids of candidates
586  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
587  * 
588  */
589 void
590 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
591 {
592   /* installed packages get a special upgrade allowed rule */
593   Pool *pool = solv->pool;
594   Id p, pp, n, p2, pp2;
595   Id obs, *obsp;
596   Solvable *ps;
597   int haveprovobs = 0;
598
599   queue_empty(qs);
600
601   if (solv && solv->updateCandidateCb)
602     { /* The application is responsible for */
603       return solv->updateCandidateCb(solv->pool, s, qs);
604     }
605
606   /*
607    * s = solvable ptr
608    * n = solvable Id
609    */
610   n = s - pool->solvables;
611
612   /*
613    * look for updates for s
614    */
615   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
616     {
617       if (p == n)               /* skip itself */
618         continue;
619
620       ps = pool->solvables + p;
621       if (s->name == ps->name)  /* name match */
622         {
623           if (!allow_all && !solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
624             continue;
625         }
626       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
627         {
628           obsp = ps->repo->idarraydata + ps->obsoletes;
629           while ((obs = *obsp++) != 0)  /* for all obsoletes */
630             {
631               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
632                 {
633                   Solvable *ps2 = pool->solvables + p2;
634                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
635                     continue;
636                   if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps2))
637                     continue;
638                   if (p2 == n)          /* match ! */
639                     break;
640                 }
641               if (p2)                   /* match! */
642                 break;
643             }
644           if (!obs)                     /* continue if no match */
645             continue;
646           /* here we have 'p' with a matching provides/obsoletes combination
647            * thus flagging p as a valid update candidate for s
648            */
649           haveprovobs = 1;
650         }
651       else
652         continue;
653       if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
654         continue;
655       if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
656         continue;
657       queue_push(qs, p);
658     }
659   /* if we have found some valid candidates and noupdateprovide is not set, we're
660      done. otherwise we fallback to all obsoletes */
661   if (!solv->noupdateprovide && haveprovobs)
662     return;
663   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
664     {
665       Id *opp;
666       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
667         {
668           ps = pool->solvables + p;
669           if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
670             continue;
671           if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
672             continue;
673           queue_push(qs, p);
674         }
675     }
676 }
677