policy.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2007, Novell Inc.
00003  *
00004  * This program is licensed under the BSD license, read LICENSE.BSD
00005  * for further information
00006  */
00007 
00008 /*
00009  * Generic policy interface for SAT solver
00010  *
00011  */
00012 
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <unistd.h>
00016 #include <string.h>
00017 
00018 #include "solver.h"
00019 #include "evr.h"
00020 #include "policy.h"
00021 #include "poolvendor.h"
00022 #include "poolarch.h"
00023 
00024 
00025 /*-----------------------------------------------------------------*/
00026 
00027 /*
00028  * prep for prune_best_version_arch
00029  *   sort by name
00030  */
00031 
00032 static int
00033 prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp)
00034 {
00035   Pool *pool = dp;
00036   int r;
00037   Id a = *(Id *)ap;
00038   Id b = *(Id *)bp;
00039   Solvable *sa, *sb;
00040 
00041   sa = pool->solvables + a;
00042   sb = pool->solvables + b;
00043   r = sa->name - sb->name;
00044   if (r)
00045     {
00046       const char *na, *nb;
00047       /* different names. We use real strcmp here so that the result
00048        * is not depending on some random solvable order */
00049       na = id2str(pool, sa->name);
00050       nb = id2str(pool, sb->name);
00051       return strcmp(na, nb);
00052     }
00053   /* the same name, bring installed solvables to the front */
00054   if (pool->installed)
00055     {
00056       if (sa->repo == pool->installed)
00057         {
00058           if (sb->repo != pool->installed)
00059             return -1;
00060         }
00061       else if (sb->repo == pool->installed)
00062         return 1;       
00063     }
00064   /* sort by repository sub-prio (installed repo handled above) */
00065   r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
00066   if (r)
00067     return r;
00068   /* no idea about the order, sort by id */
00069   return a - b;
00070 }
00071 
00072 
00073 /*
00074  * prune to repository with highest priority.
00075  * does not prune installed solvables.
00076  */
00077 
00078 static void
00079 prune_to_highest_prio(Pool *pool, Queue *plist)
00080 {
00081   int i, j;
00082   Solvable *s;
00083   int bestprio = 0, bestprioset = 0;
00084 
00085   /* prune to highest priority */
00086   for (i = 0; i < plist->count; i++)  /* find highest prio in queue */
00087     {
00088       s = pool->solvables + plist->elements[i];
00089       if (pool->installed && s->repo == pool->installed)
00090         continue;
00091       if (!bestprioset || s->repo->priority > bestprio)
00092         {
00093           bestprio = s->repo->priority;
00094           bestprioset = 1;
00095         }
00096     }
00097   if (!bestprioset)
00098     return;
00099   for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
00100     {
00101       s = pool->solvables + plist->elements[i];
00102       if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
00103         plist->elements[j++] = plist->elements[i];
00104     }
00105   plist->count = j;
00106 }
00107 
00108 
00109 /*
00110  * prune to recommended/suggested packages.
00111  * does not prune installed packages (they are also somewhat recommended).
00112  */
00113 
00114 static void
00115 prune_to_recommended(Solver *solv, Queue *plist)
00116 {
00117   Pool *pool = solv->pool;
00118   int i, j, k, ninst;
00119   Solvable *s;
00120   Id p, pp, rec, *recp, sug, *sugp;
00121 
00122   ninst = 0;
00123   if (pool->installed)
00124     {
00125       for (i = 0; i < plist->count; i++)
00126         {
00127           p = plist->elements[i];
00128           s = pool->solvables + p;
00129           if (pool->installed && s->repo == pool->installed)
00130             ninst++;
00131         }
00132     }
00133   if (plist->count - ninst < 2)
00134     return;
00135 
00136   /* update our recommendsmap/suggestsmap */
00137   if (solv->recommends_index < 0)
00138     {
00139       MAPZERO(&solv->recommendsmap);
00140       MAPZERO(&solv->suggestsmap);
00141       solv->recommends_index = 0;
00142     }
00143   while (solv->recommends_index < solv->decisionq.count)
00144     {
00145       p = solv->decisionq.elements[solv->recommends_index++];
00146       if (p < 0)
00147         continue;
00148       s = pool->solvables + p;
00149       if (s->recommends)
00150         {
00151           recp = s->repo->idarraydata + s->recommends;
00152           while ((rec = *recp++) != 0)
00153             FOR_PROVIDES(p, pp, rec)
00154               MAPSET(&solv->recommendsmap, p);
00155         }
00156       if (s->suggests)
00157         {
00158           sugp = s->repo->idarraydata + s->suggests;
00159           while ((sug = *sugp++) != 0)
00160             FOR_PROVIDES(p, pp, sug)
00161               MAPSET(&solv->suggestsmap, p);
00162         }
00163     }
00164 
00165   /* prune to recommended/supplemented */
00166   ninst = 0;
00167   for (i = j = 0; i < plist->count; i++)
00168     {
00169       p = plist->elements[i];
00170       s = pool->solvables + p;
00171       if (pool->installed && s->repo == pool->installed)
00172         {
00173           ninst++;
00174           if (j)
00175             plist->elements[j++] = p;
00176           continue;
00177         }
00178       if (!MAPTST(&solv->recommendsmap, p))
00179         if (!solver_is_supplementing(solv, s))
00180           continue;
00181       if (!j && ninst)
00182         {
00183           for (k = 0; j < ninst; k++)
00184             {
00185               s = pool->solvables + plist->elements[k];
00186               if (pool->installed && s->repo == pool->installed)
00187                 plist->elements[j++] = plist->elements[k];
00188             }
00189         }
00190       plist->elements[j++] = p;
00191     }
00192   if (j)
00193     plist->count = j;
00194 
00195   /* anything left to prune? */
00196   if (plist->count - ninst < 2)
00197     return;
00198 
00199   /* prune to suggested/enhanced*/
00200   ninst = 0;
00201   for (i = j = 0; i < plist->count; i++)
00202     {
00203       p = plist->elements[i];
00204       s = pool->solvables + p;
00205       if (pool->installed && s->repo == pool->installed)
00206         {
00207           ninst++;
00208           if (j)
00209             plist->elements[j++] = p;
00210           continue;
00211         }
00212       if (!MAPTST(&solv->suggestsmap, p))
00213         if (!solver_is_enhancing(solv, s))
00214           continue;
00215       if (!j && ninst)
00216         {
00217           for (k = 0; j < ninst; k++)
00218             {
00219               s = pool->solvables + plist->elements[k];
00220               if (pool->installed && s->repo == pool->installed)
00221                 plist->elements[j++] = plist->elements[k];
00222             }
00223         }
00224       plist->elements[j++] = p;
00225     }
00226   if (j)
00227     plist->count = j;
00228 }
00229 
00230 void
00231 prune_to_best_arch(const Pool *pool, Queue *plist)
00232 {
00233   Id a, bestscore;
00234   Solvable *s;
00235   int i, j;
00236 
00237   if (!pool->id2arch || plist->count < 2)
00238     return;
00239   bestscore = 0;
00240   for (i = 0; i < plist->count; i++)
00241     {
00242       s = pool->solvables + plist->elements[i];
00243       a = s->arch;
00244       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
00245       if (a && a != 1 && (!bestscore || a < bestscore))
00246         bestscore = a;
00247     }
00248   for (i = j = 0; i < plist->count; i++)
00249     {
00250       s = pool->solvables + plist->elements[i];
00251       a = s->arch;
00252       if (a > pool->lastarch)
00253         continue;
00254       a = pool->id2arch[a];
00255       /* a == 1 -> noarch */
00256       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
00257         continue;
00258       plist->elements[j++] = plist->elements[i];
00259     }
00260   if (j)
00261     plist->count = j;
00262 }
00263 
00264 /*
00265  * prune_to_best_version
00266  *
00267  * sort list of packages (given through plist) by name and evr
00268  * return result through plist
00269  */
00270 void
00271 prune_to_best_version(Pool *pool, Queue *plist)
00272 {
00273   Id best;
00274   int i, j;
00275   Solvable *s;
00276 
00277   if (plist->count < 2)         /* no need to prune for a single entry */
00278     return;
00279   POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
00280 
00281   /* sort by name first, prefer installed */
00282   sat_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
00283 
00284   /* delete obsoleted. hmm, looks expensive! */
00285   /* FIXME maybe also check provides depending on noupdateprovide? */
00286   /* FIXME do not prune cycles */
00287   for (i = 0; i < plist->count; i++)
00288     {
00289       Id p, pp, obs, *obsp;
00290       s = pool->solvables + plist->elements[i];
00291       if (!s->obsoletes)
00292         continue;
00293       obsp = s->repo->idarraydata + s->obsoletes;
00294       while ((obs = *obsp++) != 0)
00295         {
00296           FOR_PROVIDES(p, pp, obs)
00297             {
00298               Solvable *ps = pool->solvables + p;
00299               if (ps->name == s->name)
00300                 continue;
00301               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
00302                 continue;
00303               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
00304                 continue;
00305               for (j = 0; j < plist->count; j++)
00306                 {
00307                   if (i == j)
00308                     continue;
00309                   if (plist->elements[j] == p)
00310                     plist->elements[j] = 0;
00311                 }
00312             }
00313         }
00314     }
00315   /* delete zeroed out queue entries */
00316   for (i = j = 0; i < plist->count; i++)
00317     if (plist->elements[i])
00318       plist->elements[j++] = plist->elements[i];
00319   plist->count = j;
00320 
00321   /* now find best 'per name' */
00322   best = 0;
00323   for (i = j = 0; i < plist->count; i++)
00324     {
00325       s = pool->solvables + plist->elements[i];
00326 
00327       POOL_DEBUG(SAT_DEBUG_POLICY, "- %s[%s]\n",
00328                  solvable2str(pool, s),
00329                  (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
00330 
00331       if (!best)                       /* if no best yet, the current is best */
00332         {
00333           best = plist->elements[i];
00334           continue;
00335         }
00336 
00337       /* name switch: re-init */
00338       if (pool->solvables[best].name != s->name)   /* new name */
00339         {
00340           plist->elements[j++] = best; /* move old best to front */
00341           best = plist->elements[i];   /* take current as new best */
00342           continue;
00343         }
00344 
00345       if (pool->solvables[best].evr != s->evr)   /* compare evr */
00346         {
00347           if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_COMPARE) < 0)
00348             best = plist->elements[i];
00349         }
00350     }
00351 
00352   if (!best)
00353     best = plist->elements[0];
00354 
00355   plist->elements[j++] = best;
00356   plist->count = j;
00357 }
00358 
00359 
00360 /* legacy, do not use anymore!
00361  * (rates arch higher than version, but thats a policy)
00362  */
00363 
00364 void
00365 prune_best_arch_name_version(const Solver *solv, Pool *pool, Queue *plist)
00366 {
00367   if (solv && solv->bestSolvableCb)
00368     { /* The application is responsible for */
00369       return solv->bestSolvableCb(solv->pool, plist);
00370     }
00371 
00372   if (plist->count > 1)
00373     prune_to_best_arch(pool, plist);
00374   if (plist->count > 1)
00375     prune_to_best_version(pool, plist);
00376 }
00377 
00378 
00379 void
00380 policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
00381 {
00382   Pool *pool = solv->pool;
00383   if (plist->count > 1 && mode != POLICY_MODE_SUGGEST)
00384     prune_to_highest_prio(pool, plist);
00385   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
00386     prune_to_recommended(solv, plist);
00387   prune_best_arch_name_version(solv, pool, plist);
00388 }
00389 
00390 
00391 int
00392 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
00393 {
00394   Pool *pool = solv->pool;
00395   Id a1 = s1->arch, a2 = s2->arch;
00396 
00397   if (solv && solv->archCheckCb)
00398     { /* The application is responsible for */
00399       return solv->archCheckCb(solv->pool, s1, s2);
00400     }
00401 
00402   /* we allow changes to/from noarch */
00403 #ifndef DEBIAN_SEMANTICS
00404   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
00405     return 0;
00406 #else
00407   if (a1 == a2 || a1 == ARCH_ALL || a2 == ARCH_ALL)
00408     return 0;
00409 #endif
00410   if (!pool->id2arch)
00411     return 0;
00412   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
00413   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
00414   if (((a1 ^ a2) & 0xffff0000) != 0)
00415     return 1;
00416   return 0;
00417 }
00418 
00419 int
00420 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
00421 {
00422   Pool *pool = solv->pool;
00423   Id v1, v2;
00424   Id vendormask1, vendormask2;
00425 
00426   if (solv->vendorCheckCb)
00427    {   /* The application is responsible for */
00428      return solv->vendorCheckCb(pool, s1, s2);
00429    }
00430   /* treat a missing vendor as empty string */
00431   v1 = s1->vendor ? s1->vendor : ID_EMPTY;
00432   v2 = s2->vendor ? s2->vendor : ID_EMPTY;
00433   if (v1 == v2)
00434     return 0;
00435   vendormask1 = pool_vendor2mask(pool, v1);
00436   if (!vendormask1)
00437     return 1;   /* can't match */
00438   vendormask2 = pool_vendor2mask(pool, v2);
00439   if ((vendormask1 & vendormask2) != 0)
00440     return 0;
00441   return 1;     /* no class matches */
00442 }
00443 
00444 
00445 /*-------------------------------------------------------------------
00446  * 
00447  * create reverse obsoletes map for installed solvables
00448  *
00449  * For each installed solvable find which packages with *different* names
00450  * obsolete the solvable.
00451  * This index is used in policy_findupdatepackages() below.
00452  */
00453 void
00454 policy_create_obsolete_index(Solver *solv)
00455 {
00456   Pool *pool = solv->pool;
00457   Solvable *s;
00458   Repo *installed = solv->installed;
00459   Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
00460   int i, n, cnt;
00461 
00462   if (!installed || installed->start == installed->end)
00463     return;
00464   cnt = installed->end - installed->start;
00465   solv->obsoletes = obsoletes = sat_calloc(cnt, sizeof(Id));
00466   for (i = 1; i < pool->nsolvables; i++)
00467     {
00468       s = pool->solvables + i;
00469       if (!s->obsoletes)
00470         continue;
00471       if (!pool_installable(pool, s))
00472         continue;
00473       obsp = s->repo->idarraydata + s->obsoletes;
00474       while ((obs = *obsp++) != 0)
00475         {
00476           FOR_PROVIDES(p, pp, obs)
00477             {
00478               Solvable *ps = pool->solvables + p;;
00479               if (ps->repo != installed)
00480                 continue;
00481               if (ps->name == s->name)
00482                 continue;
00483               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
00484                 continue;
00485               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
00486                 continue;
00487               obsoletes[p - installed->start]++;
00488             }
00489         }
00490     }
00491   n = 0;
00492   for (i = 0; i < cnt; i++)
00493     if (obsoletes[i])
00494       {
00495         n += obsoletes[i] + 1;
00496         obsoletes[i] = n;
00497       }
00498   solv->obsoletes_data = obsoletes_data = sat_calloc(n + 1, sizeof(Id));
00499   POOL_DEBUG(SAT_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
00500   for (i = pool->nsolvables - 1; i > 0; i--)
00501     {
00502       s = pool->solvables + i;
00503       if (!s->obsoletes)
00504         continue;
00505       if (!pool_installable(pool, s))
00506         continue;
00507       obsp = s->repo->idarraydata + s->obsoletes;
00508       while ((obs = *obsp++) != 0)
00509         {
00510           FOR_PROVIDES(p, pp, obs)
00511             {
00512               Solvable *ps = pool->solvables + p;;
00513               if (ps->repo != installed)
00514                 continue;
00515               if (ps->name == s->name)
00516                 continue;
00517               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
00518                 continue;
00519               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
00520                 continue;
00521               if (obsoletes_data[obsoletes[p - installed->start]] != i)
00522                 obsoletes_data[--obsoletes[p - installed->start]] = i;
00523             }
00524         }
00525     }
00526 }
00527 
00528 
00529 /*
00530  * find update candidates
00531  * 
00532  * s: solvable to be updated
00533  * qs: [out] queue to hold Ids of candidates
00534  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
00535  * 
00536  */
00537 void
00538 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
00539 {
00540   /* installed packages get a special upgrade allowed rule */
00541   Pool *pool = solv->pool;
00542   Id p, pp, n, p2, pp2;
00543   Id obs, *obsp;
00544   Solvable *ps;
00545   int haveprovobs = 0;
00546 
00547   queue_empty(qs);
00548 
00549   if (solv && solv->updateCandidateCb)
00550     { /* The application is responsible for */
00551       return solv->updateCandidateCb(solv->pool, s, qs);
00552     }
00553 
00554   /*
00555    * s = solvable ptr
00556    * n = solvable Id
00557    */
00558   n = s - pool->solvables;
00559 
00560   /*
00561    * look for updates for s
00562    */
00563   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
00564     {
00565       if (p == n)               /* skip itself */
00566         continue;
00567 
00568       ps = pool->solvables + p;
00569       if (s->name == ps->name)  /* name match */
00570         {
00571           if (!allow_all && !solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
00572             continue;
00573         }
00574       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
00575         {
00576           obsp = ps->repo->idarraydata + ps->obsoletes;
00577           while ((obs = *obsp++) != 0)  /* for all obsoletes */
00578             {
00579               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
00580                 {
00581                   Solvable *ps2 = pool->solvables + p2;
00582                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
00583                     continue;
00584                   if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps2))
00585                     continue;
00586                   if (p2 == n)          /* match ! */
00587                     break;
00588                 }
00589               if (p2)                   /* match! */
00590                 break;
00591             }
00592           if (!obs)                     /* continue if no match */
00593             continue;
00594           /* here we have 'p' with a matching provides/obsoletes combination
00595            * thus flagging p as a valid update candidate for s
00596            */
00597           haveprovobs = 1;
00598         }
00599       else
00600         continue;
00601       if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
00602         continue;
00603       if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
00604         continue;
00605       queue_push(qs, p);
00606     }
00607   /* if we have found some valid candidates and noupdateprovide is not set, we're
00608      done. otherwise we fallback to all obsoletes */
00609   if (!solv->noupdateprovide && haveprovobs)
00610     return;
00611   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
00612     {
00613       Id *opp;
00614       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
00615         {
00616           ps = pool->solvables + p;
00617           if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
00618             continue;
00619           if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
00620             continue;
00621           queue_push(qs, p);
00622         }
00623     }
00624 }
00625 

doxygen