- fix old FIXME by not pruning installed packages
[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 static Solver *prune_to_best_version_sortcmp_data;
26
27 /*-----------------------------------------------------------------*/
28
29 /*
30  * prep for prune_best_version_arch
31  *   sort by name
32  */
33
34 static int
35 prune_to_best_version_sortcmp(const void *ap, const void *bp)
36 {
37   Solver *solv = prune_to_best_version_sortcmp_data;
38   Pool *pool = solv->pool;
39   int r;
40   Id a = *(Id *)ap;
41   Id b = *(Id *)bp;
42   Solvable *sa, *sb;
43
44   sa = pool->solvables + a;
45   sb = pool->solvables + b;
46   r = sa->name - sb->name;
47   if (r)
48     {
49       const char *na, *nb;
50       /* different names. We use real strcmp here so that the result
51        * is not depending on some random solvable order */
52       na = id2str(pool, sa->name);
53       nb = id2str(pool, sb->name);
54       /* bring patterns to the front */
55       /* XXX: no longer needed? */
56       if (!strncmp(na, "pattern:", 8))
57         {
58           if (strncmp(nb, "pattern:", 8))
59             return -1;
60         }
61       else if (!strncmp(nb, "pattern:", 8))
62         {
63           if (strncmp(na, "pattern:", 8))
64             return 1;
65         }
66       return strcmp(na, nb);
67     }
68   /* the same name, bring installed solvables to the front */
69   if (solv->installed)
70     {
71       if (sa->repo == solv->installed)
72         {
73           if (sb->repo != solv->installed)
74             return -1;
75         }
76       else if (sb->repo == solv->installed)
77         return 1;       
78     }
79   /* sort by repository sub-prio (installed repo handled above) */
80   r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
81   if (r)
82     return r;
83   /* no idea about the order, sort by id */
84   return a - b;
85 }
86
87
88 /*
89  * prune to repository with highest priority.
90  * does not prune installed solvables.
91  */
92
93 static void
94 prune_to_highest_prio(Pool *pool, Queue *plist)
95 {
96   int i, j;
97   Solvable *s;
98   int bestprio = 0;
99
100   /* prune to highest priority */
101   for (i = 0; i < plist->count; i++)  /* find highest prio in queue */
102     {
103       s = pool->solvables + plist->elements[i];
104       if (pool->installed && s->repo == pool->installed)
105         continue;
106       if (i == 0 || s->repo->priority > bestprio)
107         bestprio = s->repo->priority;
108     }
109   for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
110     {
111       s = pool->solvables + plist->elements[i];
112       if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
113         plist->elements[j++] = plist->elements[i];
114     }
115   plist->count = j;
116 }
117
118
119 /*
120  * prune to recommended/suggested packages.
121  * does not prune installed packages (they are also somewhat recommended).
122  */
123
124 static void
125 prune_to_recommended(Solver *solv, Queue *plist)
126 {
127   Pool *pool = solv->pool;
128   int i, j, k, ninst;
129   Solvable *s;
130   Id p, pp, rec, *recp, sug, *sugp;
131
132   ninst = 0;
133   if (pool->installed)
134     {
135       for (i = 0; i < plist->count; i++)
136         {
137           p = plist->elements[i];
138           s = pool->solvables + p;
139           if (pool->installed && s->repo == pool->installed)
140             ninst++;
141         }
142     }
143   if (plist->count - ninst < 2)
144     return;
145
146   /* update our recommendsmap/suggestsmap */
147   if (solv->recommends_index < 0)
148     {
149       MAPZERO(&solv->recommendsmap);
150       MAPZERO(&solv->suggestsmap);
151       solv->recommends_index = 0;
152     }
153   while (solv->recommends_index < solv->decisionq.count)
154     {
155       p = solv->decisionq.elements[solv->recommends_index++];
156       if (p < 0)
157         continue;
158       s = pool->solvables + p;
159       if (s->recommends)
160         {
161           recp = s->repo->idarraydata + s->recommends;
162           while ((rec = *recp++) != 0)
163             FOR_PROVIDES(p, pp, rec)
164               MAPSET(&solv->recommendsmap, p);
165         }
166       if (s->suggests)
167         {
168           sugp = s->repo->idarraydata + s->suggests;
169           while ((sug = *sugp++) != 0)
170             FOR_PROVIDES(p, pp, sug)
171               MAPSET(&solv->suggestsmap, p);
172         }
173     }
174
175   /* prune to recommended/supplemented */
176   ninst = 0;
177   for (i = j = 0; i < plist->count; i++)
178     {
179       p = plist->elements[i];
180       s = pool->solvables + p;
181       if (pool->installed && s->repo == pool->installed)
182         {
183           ninst++;
184           if (j)
185             plist->elements[j++] = p;
186           continue;
187         }
188       if (!MAPTST(&solv->recommendsmap, p))
189         if (!solver_is_supplementing(solv, s))
190           continue;
191       if (!j && ninst)
192         {
193           for (k = 0; j < ninst; k++)
194             {
195               s = pool->solvables + plist->elements[k];
196               if (pool->installed && s->repo == pool->installed)
197                 plist->elements[j++] = plist->elements[k];
198             }
199         }
200       plist->elements[j++] = p;
201     }
202   if (j)
203     plist->count = j;
204
205   /* anything left to prune? */
206   if (plist->count - ninst < 2)
207     return;
208
209   /* prune to suggested/enhanced*/
210   ninst = 0;
211   for (i = j = 0; i < plist->count; i++)
212     {
213       p = plist->elements[i];
214       s = pool->solvables + p;
215       if (pool->installed && s->repo == pool->installed)
216         {
217           ninst++;
218           if (j)
219             plist->elements[j++] = p;
220           continue;
221         }
222       if (!MAPTST(&solv->suggestsmap, p))
223         if (!solver_is_enhancing(solv, s))
224           continue;
225       if (!j && ninst)
226         {
227           for (k = 0; j < ninst; k++)
228             {
229               s = pool->solvables + plist->elements[k];
230               if (pool->installed && s->repo == pool->installed)
231                 plist->elements[j++] = plist->elements[k];
232             }
233         }
234       plist->elements[j++] = p;
235     }
236   if (j)
237     plist->count = j;
238 }
239
240 void
241 prune_to_best_arch(Pool *pool, Queue *plist)
242 {
243   Id a, bestscore;
244   Solvable *s;
245   int i, j;
246
247   if (!pool->id2arch || plist->count < 2)
248     return;
249   bestscore = 0;
250   for (i = 0; i < plist->count; i++)
251     {
252       s = pool->solvables + plist->elements[i];
253       a = s->arch;
254       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
255       if (a && a != 1 && (!bestscore || a < bestscore))
256         bestscore = a;
257     }
258   for (i = j = 0; i < plist->count; i++)
259     {
260       s = pool->solvables + plist->elements[i];
261       a = s->arch;
262       if (a > pool->lastarch)
263         continue;
264       a = pool->id2arch[a];
265       /* a == 1 -> noarch */
266       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
267         continue;
268       plist->elements[j++] = plist->elements[i];
269     }
270   if (j)
271     plist->count = j;
272 }
273
274 /*
275  * prune_to_best_version
276  *
277  * sort list of packages (given through plist) by name and evr
278  * return result through plist
279  */
280 void
281 prune_to_best_version(Solver *solv, Queue *plist)
282 {
283   Pool *pool = solv->pool;
284   Id best;
285   int i, j;
286   Solvable *s;
287
288   if (plist->count < 2)         /* no need to prune for a single entry */
289     return;
290   POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
291
292   prune_to_best_version_sortcmp_data = solv;
293   /* sort by name first, prefer installed */
294   qsort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp);
295
296   /* delete obsoleted. hmm, looks expensive! */
297   /* FIXME maybe also check provides depending on noupdateprovide? */
298   /* FIXME do not prune cycles */
299   for (i = 0; i < plist->count; i++)
300     {
301       Id p, pp, obs, *obsp;
302       s = pool->solvables + plist->elements[i];
303       if (!s->obsoletes)
304         continue;
305       obsp = s->repo->idarraydata + s->obsoletes;
306       while ((obs = *obsp++) != 0)
307         {
308           FOR_PROVIDES(p, pp, obs)
309             {
310               if (pool->solvables[p].name == s->name)
311                 continue;
312               if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
313                 continue;
314               for (j = 0; j < plist->count; j++)
315                 {
316                   if (i == j)
317                     continue;
318                   if (plist->elements[j] == p)
319                     plist->elements[j] = 0;
320                 }
321             }
322         }
323     }
324   /* delete zeroed out queue entries */
325   for (i = j = 0; i < plist->count; i++)
326     if (plist->elements[i])
327       plist->elements[j++] = plist->elements[i];
328   plist->count = j;
329
330   /* now find best 'per name' */
331   best = 0;
332   for (i = j = 0; i < plist->count; i++)
333     {
334       s = pool->solvables + plist->elements[i];
335
336       POOL_DEBUG(SAT_DEBUG_POLICY, "- %s[%s]\n",
337                  solvable2str(pool, s),
338                  (solv->installed && s->repo == solv->installed) ? "installed" : "not installed");
339
340       if (!best)                       /* if no best yet, the current is best */
341         {
342           best = plist->elements[i];
343           continue;
344         }
345
346       /* name switch: re-init */
347       if (pool->solvables[best].name != s->name)   /* new name */
348         {
349           plist->elements[j++] = best; /* move old best to front */
350           best = plist->elements[i];   /* take current as new best */
351           continue;
352         }
353
354       if (pool->solvables[best].evr != s->evr)   /* compare evr */
355         {
356           if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_COMPARE) < 0)
357             best = plist->elements[i];
358         }
359     }
360
361   if (!best)
362     best = plist->elements[0];
363
364   plist->elements[j++] = best;
365   plist->count = j;
366 }
367
368
369 /* legacy, do not use anymore!
370  * (rates arch higher than version, but thats a policy)
371  */
372
373 void
374 prune_best_arch_name_version(Solver *solv, Pool *pool, Queue *plist)
375 {
376   if (solv && solv->bestSolvableCb)
377     { /* The application is responsible for */
378       return solv->bestSolvableCb(solv->pool, plist);
379     }
380
381   if (plist->count > 1)
382     prune_to_best_arch(pool, plist);
383   if (plist->count > 1)
384     prune_to_best_version(solv, plist);
385 }
386
387
388 void
389 policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
390 {
391   Pool *pool = solv->pool;
392   if (plist->count > 1 && mode != POLICY_MODE_SUGGEST)
393     prune_to_highest_prio(pool, plist);
394   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
395     prune_to_recommended(solv, plist);
396   prune_best_arch_name_version(solv, pool, plist);
397 }
398
399
400 int
401 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
402 {
403   Pool *pool = solv->pool;
404   Id a1 = s1->arch, a2 = s2->arch;
405
406   if (solv && solv->archCheckCb)
407     { /* The application is responsible for */
408       return solv->archCheckCb(solv->pool, s1, s2);
409     }
410
411   /* we allow changes to/from noarch */
412   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
413     return 0;
414   if (!pool->id2arch)
415     return 0;
416   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
417   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
418   if (((a1 ^ a2) & 0xffff0000) != 0)
419     return 1;
420   return 0;
421 }
422
423 int
424 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
425 {
426   Pool *pool = solv->pool;
427   Id vendormask1, vendormask2;
428
429   if (solv && solv->vendorCheckCb)
430    {   /* The application is responsible for */
431      return solv->vendorCheckCb(solv->pool, s1, s2);
432    }
433
434   if (s1->vendor == s2->vendor)
435     return 0;
436   vendormask1 = pool_vendor2mask(pool, s1->vendor);
437   if (!vendormask1)
438     return 0;
439   vendormask2 = pool_vendor2mask(pool, s2->vendor);
440   if ((vendormask1 & vendormask2) == 0)
441     return 0;
442   return 1;
443 }
444
445
446 /*
447  * find update candidates
448  * 
449  * s: solvable to be updated
450  * qs: [out] queue to hold Ids of candidates
451  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
452  * 
453  */
454 void
455 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
456 {
457   /* installed packages get a special upgrade allowed rule */
458   Pool *pool = solv->pool;
459   Id p, pp, n, p2, pp2;
460   Id obs, *obsp;
461   Solvable *ps;
462
463   queue_empty(qs);
464
465   if (solv && solv->updateCandidateCb)
466     { /* The application is responsible for */
467       return solv->updateCandidateCb(solv->pool, s, qs);
468     }
469
470   /*
471    * s = solvable ptr
472    * n = solvable Id
473    */
474   n = s - pool->solvables;
475
476   /*
477    * look for updates for s
478    */
479   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
480     {
481       if (p == n)               /* skip itself */
482         continue;
483
484       ps = pool->solvables + p;
485       if (s->name == ps->name)  /* name match */
486         {
487           if (!allow_all && !solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
488             continue;
489         }
490       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
491         {
492           obsp = ps->repo->idarraydata + ps->obsoletes;
493           while ((obs = *obsp++) != 0)  /* for all obsoletes */
494             {
495               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
496                 {
497                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
498                     continue;
499                   if (p2 == n)          /* match ! */
500                     break;
501                 }
502               if (p2)                   /* match! */
503                 break;
504             }
505           if (!obs)                     /* continue if no match */
506             continue;
507           /* here we have 'p' with a matching provides/obsoletes combination
508            * thus flagging p as a valid update candidate for s
509            */
510         }
511       else
512         continue;
513       if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
514         continue;
515       if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
516         continue;
517       queue_push(qs, p);
518     }
519   /* if we have found some valid candidates and noupdateprovide is not set, we're
520      done. otherwise we fallback to all obsoletes */
521   if (!solv->noupdateprovide && qs->count)
522     return;
523   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
524     {
525       Id *opp;
526       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
527         {
528           ps = pool->solvables + p;
529           if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
530             continue;
531           if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
532             continue;
533           queue_push(qs, p);
534         }
535     }
536 }
537