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