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     {
00385       prune_to_highest_prio(pool, plist);
00386       /* installed packages involed in a dup operation can only be kept
00387        * if they are identical to a non-installed one */
00388       if (plist->count > 1 && pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
00389         {
00390           int i, j, k;
00391           for (i = j = 0; i < plist->count; i++)
00392             {
00393               Id p = plist->elements[i];
00394               Solvable *s = pool->solvables + p;
00395               if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
00396                 {
00397                   for (k = 0; k < plist->count; k++)
00398                     {
00399                       Solvable *s2 = pool->solvables + plist->elements[k];
00400                       if (s2->repo != pool->installed && solvable_identical(s, s2))
00401                         break;
00402                     }
00403                   if (k == plist->count)
00404                     continue;   /* no identical package found, ignore installed package */
00405                 }
00406               plist->elements[j++] = p;
00407             }
00408           if (j)
00409             plist->count = j;
00410         }
00411     }
00412   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
00413     prune_to_recommended(solv, plist);
00414   prune_best_arch_name_version(solv, pool, plist);
00415 }
00416 
00417 
00418 int
00419 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
00420 {
00421   Pool *pool = solv->pool;
00422   Id a1 = s1->arch, a2 = s2->arch;
00423 
00424   if (solv && solv->archCheckCb)
00425     { /* The application is responsible for */
00426       return solv->archCheckCb(solv->pool, s1, s2);
00427     }
00428 
00429   /* we allow changes to/from noarch */
00430 #ifndef DEBIAN_SEMANTICS
00431   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
00432     return 0;
00433 #else
00434   if (a1 == a2 || a1 == ARCH_ALL || a2 == ARCH_ALL)
00435     return 0;
00436 #endif
00437   if (!pool->id2arch)
00438     return 0;
00439   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
00440   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
00441   if (((a1 ^ a2) & 0xffff0000) != 0)
00442     return 1;
00443   return 0;
00444 }
00445 
00446 int
00447 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
00448 {
00449   Pool *pool = solv->pool;
00450   Id v1, v2;
00451   Id vendormask1, vendormask2;
00452 
00453   if (solv->vendorCheckCb)
00454    {   /* The application is responsible for */
00455      return solv->vendorCheckCb(pool, s1, s2);
00456    }
00457   /* treat a missing vendor as empty string */
00458   v1 = s1->vendor ? s1->vendor : ID_EMPTY;
00459   v2 = s2->vendor ? s2->vendor : ID_EMPTY;
00460   if (v1 == v2)
00461     return 0;
00462   vendormask1 = pool_vendor2mask(pool, v1);
00463   if (!vendormask1)
00464     return 1;   /* can't match */
00465   vendormask2 = pool_vendor2mask(pool, v2);
00466   if ((vendormask1 & vendormask2) != 0)
00467     return 0;
00468   return 1;     /* no class matches */
00469 }
00470 
00471 /* check if it is illegal to replace installed
00472  * package "is" with package "s" (which must obsolete "is")
00473  */
00474 int
00475 policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
00476 {
00477   Pool *pool = solv->pool;
00478   int ret = 0;
00479   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !solv->allowdowngrade)
00480     {
00481       if (is->name == s->name && evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
00482         ret |= POLICY_ILLEGAL_DOWNGRADE;
00483     }
00484   if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !solv->allowarchchange)
00485     {
00486       if (is->arch != s->arch && policy_illegal_archchange(solv, s, is))
00487         ret |= POLICY_ILLEGAL_ARCHCHANGE;
00488     }
00489   if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !solv->allowvendorchange)
00490     {
00491       if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, s, is))
00492         ret |= POLICY_ILLEGAL_VENDORCHANGE;
00493     }
00494   return ret;
00495 }
00496 
00497 /*-------------------------------------------------------------------
00498  * 
00499  * create reverse obsoletes map for installed solvables
00500  *
00501  * For each installed solvable find which packages with *different* names
00502  * obsolete the solvable.
00503  * This index is used in policy_findupdatepackages() below.
00504  */
00505 void
00506 policy_create_obsolete_index(Solver *solv)
00507 {
00508   Pool *pool = solv->pool;
00509   Solvable *s;
00510   Repo *installed = solv->installed;
00511   Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
00512   int i, n, cnt;
00513 
00514   if (!installed || installed->start == installed->end)
00515     return;
00516   cnt = installed->end - installed->start;
00517   solv->obsoletes = obsoletes = sat_calloc(cnt, sizeof(Id));
00518   for (i = 1; i < pool->nsolvables; i++)
00519     {
00520       s = pool->solvables + i;
00521       if (!s->obsoletes)
00522         continue;
00523       if (!pool_installable(pool, s))
00524         continue;
00525       obsp = s->repo->idarraydata + s->obsoletes;
00526       while ((obs = *obsp++) != 0)
00527         {
00528           FOR_PROVIDES(p, pp, obs)
00529             {
00530               Solvable *ps = pool->solvables + p;;
00531               if (ps->repo != installed)
00532                 continue;
00533               if (ps->name == s->name)
00534                 continue;
00535               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
00536                 continue;
00537               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
00538                 continue;
00539               obsoletes[p - installed->start]++;
00540             }
00541         }
00542     }
00543   n = 0;
00544   for (i = 0; i < cnt; i++)
00545     if (obsoletes[i])
00546       {
00547         n += obsoletes[i] + 1;
00548         obsoletes[i] = n;
00549       }
00550   solv->obsoletes_data = obsoletes_data = sat_calloc(n + 1, sizeof(Id));
00551   POOL_DEBUG(SAT_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
00552   for (i = pool->nsolvables - 1; i > 0; i--)
00553     {
00554       s = pool->solvables + i;
00555       if (!s->obsoletes)
00556         continue;
00557       if (!pool_installable(pool, s))
00558         continue;
00559       obsp = s->repo->idarraydata + s->obsoletes;
00560       while ((obs = *obsp++) != 0)
00561         {
00562           FOR_PROVIDES(p, pp, obs)
00563             {
00564               Solvable *ps = pool->solvables + p;;
00565               if (ps->repo != installed)
00566                 continue;
00567               if (ps->name == s->name)
00568                 continue;
00569               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
00570                 continue;
00571               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
00572                 continue;
00573               if (obsoletes_data[obsoletes[p - installed->start]] != i)
00574                 obsoletes_data[--obsoletes[p - installed->start]] = i;
00575             }
00576         }
00577     }
00578 }
00579 
00580 
00581 /*
00582  * find update candidates
00583  * 
00584  * s: solvable to be updated
00585  * qs: [out] queue to hold Ids of candidates
00586  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
00587  * 
00588  */
00589 void
00590 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
00591 {
00592   /* installed packages get a special upgrade allowed rule */
00593   Pool *pool = solv->pool;
00594   Id p, pp, n, p2, pp2;
00595   Id obs, *obsp;
00596   Solvable *ps;
00597   int haveprovobs = 0;
00598 
00599   queue_empty(qs);
00600 
00601   if (solv && solv->updateCandidateCb)
00602     { /* The application is responsible for */
00603       return solv->updateCandidateCb(solv->pool, s, qs);
00604     }
00605 
00606   /*
00607    * s = solvable ptr
00608    * n = solvable Id
00609    */
00610   n = s - pool->solvables;
00611 
00612   /*
00613    * look for updates for s
00614    */
00615   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
00616     {
00617       if (p == n)               /* skip itself */
00618         continue;
00619 
00620       ps = pool->solvables + p;
00621       if (s->name == ps->name)  /* name match */
00622         {
00623           if (!allow_all && !solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
00624             continue;
00625         }
00626       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
00627         {
00628           obsp = ps->repo->idarraydata + ps->obsoletes;
00629           while ((obs = *obsp++) != 0)  /* for all obsoletes */
00630             {
00631               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
00632                 {
00633                   Solvable *ps2 = pool->solvables + p2;
00634                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
00635                     continue;
00636                   if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps2))
00637                     continue;
00638                   if (p2 == n)          /* match ! */
00639                     break;
00640                 }
00641               if (p2)                   /* match! */
00642                 break;
00643             }
00644           if (!obs)                     /* continue if no match */
00645             continue;
00646           /* here we have 'p' with a matching provides/obsoletes combination
00647            * thus flagging p as a valid update candidate for s
00648            */
00649           haveprovobs = 1;
00650         }
00651       else
00652         continue;
00653       if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
00654         continue;
00655       if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
00656         continue;
00657       queue_push(qs, p);
00658     }
00659   /* if we have found some valid candidates and noupdateprovide is not set, we're
00660      done. otherwise we fallback to all obsoletes */
00661   if (!solv->noupdateprovide && haveprovobs)
00662     return;
00663   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
00664     {
00665       Id *opp;
00666       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
00667         {
00668           ps = pool->solvables + p;
00669           if (!allow_all && !solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
00670             continue;
00671           if (!allow_all && !solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
00672             continue;
00673           queue_push(qs, p);
00674         }
00675     }
00676 }
00677 
Generated on Mon Dec 12 11:44:12 2011 for satsolver by  doxygen 1.6.3