"Fossies" - the Fresh Open Source Software Archive

Member "apt-2.2.4/apt-pkg/policy.cc" (10 Jun 2021, 16839 Bytes) of package /linux/misc/apt-2.2.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "policy.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.2.3_vs_2.2.4.

    1    // -*- mode: cpp; mode: fold -*-
    2 // Description                              /*{{{*/
    3 /* ######################################################################
    4 
    5    Package Version Policy implementation
    6 
    7    This is just a really simple wrapper around pkgVersionMatch with
    8    some added goodies to manage the list of things..
    9 
   10    See man apt_preferences for what value means what.
   11 
   12    ##################################################################### */
   13                                     /*}}}*/
   14 // Include Files                            /*{{{*/
   15 #include <config.h>
   16 
   17 #include <apt-pkg/aptconfiguration.h>
   18 #include <apt-pkg/cachefilter.h>
   19 #include <apt-pkg/configuration.h>
   20 #include <apt-pkg/error.h>
   21 #include <apt-pkg/fileutl.h>
   22 #include <apt-pkg/pkgcache.h>
   23 #include <apt-pkg/policy.h>
   24 #include <apt-pkg/strutl.h>
   25 #include <apt-pkg/tagfile.h>
   26 #include <apt-pkg/version.h>
   27 #include <apt-pkg/versionmatch.h>
   28 
   29 #include <iostream>
   30 #include <random>
   31 #include <sstream>
   32 #include <string>
   33 #include <vector>
   34 #include <ctype.h>
   35 #include <stddef.h>
   36 #include <string.h>
   37 
   38 #include <apti18n.h>
   39                                     /*}}}*/
   40 
   41 using namespace std;
   42 
   43 constexpr short NEVER_PIN = std::numeric_limits<short>::min();
   44 
   45 struct pkgPolicy::Private
   46 {
   47    std::string machineID;
   48 };
   49 
   50 // Policy::Init - Startup and bind to a cache               /*{{{*/
   51 // ---------------------------------------------------------------------
   52 /* Set the defaults for operation. The default mode with no loaded policy
   53    file matches the V0 policy engine. */
   54 pkgPolicy::pkgPolicy(pkgCache *Owner) : VerPins(nullptr),
   55                     PFPriority(nullptr), Cache(Owner), d(new Private)
   56 {
   57    if (Owner == 0)
   58       return;
   59    PFPriority = new signed short[Owner->Head().PackageFileCount];
   60    VerPins = new Pin[Owner->Head().VersionCount];
   61 
   62    auto VersionCount = Owner->Head().VersionCount;
   63    for (decltype(VersionCount) I = 0; I != VersionCount; ++I)
   64       VerPins[I].Type = pkgVersionMatch::None;
   65 
   66    // The config file has a master override.
   67    string DefRel = _config->Find("APT::Default-Release");
   68    if (DefRel.empty() == false)
   69    {
   70       bool found = false;
   71       // FIXME: make ExpressionMatches static to use it here easily
   72       pkgVersionMatch vm("", pkgVersionMatch::None);
   73       for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); ++F)
   74       {
   75      if (vm.ExpressionMatches(DefRel, F.Archive()) ||
   76          vm.ExpressionMatches(DefRel, F.Codename()) ||
   77          vm.ExpressionMatches(DefRel, F.Version()) ||
   78          (DefRel.length() > 2 && DefRel[1] == '='))
   79         found = true;
   80       }
   81       if (found == false)
   82      _error->Error(_("The value '%s' is invalid for APT::Default-Release as such a release is not available in the sources"), DefRel.c_str());
   83       else
   84      CreatePin(pkgVersionMatch::Release,"",DefRel,990);
   85    }
   86    InitDefaults();
   87 
   88    d->machineID = APT::Configuration::getMachineID();
   89 }
   90                                     /*}}}*/
   91 // Policy::InitDefaults - Compute the default selections        /*{{{*/
   92 // ---------------------------------------------------------------------
   93 /* */
   94 bool pkgPolicy::InitDefaults()
   95 {
   96    // Initialize the priorities based on the status of the package file
   97    for (pkgCache::PkgFileIterator I = Cache->FileBegin(); I != Cache->FileEnd(); ++I)
   98    {
   99       PFPriority[I->ID] = 500;
  100       if (I.Flagged(pkgCache::Flag::NotSource))
  101      PFPriority[I->ID] = 100;
  102       else if (I.Flagged(pkgCache::Flag::ButAutomaticUpgrades))
  103      PFPriority[I->ID] = 100;
  104       else if (I.Flagged(pkgCache::Flag::NotAutomatic))
  105      PFPriority[I->ID] = 1;
  106    }
  107 
  108    // Apply the defaults..
  109    std::unique_ptr<bool[]> Fixed(new bool[Cache->HeaderP->PackageFileCount]);
  110    memset(Fixed.get(),0,sizeof(Fixed[0])*Cache->HeaderP->PackageFileCount);
  111    StatusOverride = false;
  112    for (vector<Pin>::const_iterator I = Defaults.begin(); I != Defaults.end(); ++I)
  113    {
  114       pkgVersionMatch Match(I->Data,I->Type);
  115       for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); ++F)
  116       {
  117      if ((Fixed[F->ID] == false || I->Priority == NEVER_PIN) && PFPriority[F->ID] != NEVER_PIN && Match.FileMatch(F) == true)
  118      {
  119         PFPriority[F->ID] = I->Priority;
  120 
  121         if (PFPriority[F->ID] >= 1000)
  122            StatusOverride = true;
  123 
  124         Fixed[F->ID] = true;
  125      }
  126       }
  127    }
  128 
  129    if (_config->FindB("Debug::pkgPolicy",false) == true)
  130       for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); ++F)
  131      std::clog << "Prio of " << F.FileName() << ' ' << PFPriority[F->ID] << std::endl;
  132 
  133    return true;
  134 }
  135                                     /*}}}*/
  136 // Policy::GetCandidateVer - Get the candidate install version      /*{{{*/
  137 // ---------------------------------------------------------------------
  138 /* Evaluate the package pins and the default list to deteremine what the
  139    best package is. */
  140 pkgCache::VerIterator pkgPolicy::GetCandidateVer(pkgCache::PkgIterator const &Pkg)
  141 {
  142    pkgCache::VerIterator cand;
  143    pkgCache::VerIterator cur = Pkg.CurrentVer();
  144    int candPriority = -1;
  145    pkgVersioningSystem *vs = Cache->VS;
  146 
  147    for (pkgCache::VerIterator ver = Pkg.VersionList(); ver.end() == false; ++ver) {
  148       int priority = GetPriority(ver, true);
  149 
  150       if (priority == 0 || priority <= candPriority)
  151      continue;
  152 
  153       // TODO: Maybe optimize to not compare versions
  154       if (!cur.end() && priority < 1000
  155       && (vs->CmpVersion(ver.VerStr(), cur.VerStr()) < 0))
  156      continue;
  157 
  158       candPriority = priority;
  159       cand = ver;
  160    }
  161 
  162    return cand;
  163 }
  164                                     /*}}}*/
  165 // Policy::CreatePin - Create an entry in the pin table..       /*{{{*/
  166 // ---------------------------------------------------------------------
  167 /* For performance we have 3 tables, the default table, the main cache
  168    table (hashed to the cache). A blank package name indicates the pin
  169    belongs to the default table. Order of insertion matters here, the
  170    earlier defaults override later ones. */
  171 void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name,
  172               string Data,signed short Priority)
  173 {
  174    if (Name.empty() == true)
  175    {
  176       Pin *P = &*Defaults.insert(Defaults.end(),Pin());
  177       P->Type = Type;
  178       P->Priority = Priority;
  179       P->Data = Data;
  180       return;
  181    }
  182 
  183    bool IsSourcePin = APT::String::Startswith(Name, "src:");
  184    if (IsSourcePin) {
  185       Name = Name.substr(sizeof("src:") - 1);
  186    }
  187 
  188    size_t found = Name.rfind(':');
  189    string Arch;
  190    if (found != string::npos) {
  191       Arch = Name.substr(found+1);
  192       Name.erase(found);
  193    }
  194 
  195    // Allow pinning by wildcards - beware of package names looking like wildcards!
  196    // TODO: Maybe we should always prefer specific pins over non-specific ones.
  197    if ((Name[0] == '/' && Name[Name.length() - 1] == '/') || Name.find_first_of("*[?") != string::npos)
  198    {
  199       pkgVersionMatch match(Data, Type);
  200       for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() != true; ++G)
  201      if (Name != G.Name() && match.ExpressionMatches(Name, G.Name()))
  202      {
  203         auto NameToPinFor = IsSourcePin ? string("src:").append(G.Name()) : string(G.Name());
  204         if (Arch.empty() == false)
  205            CreatePin(Type, NameToPinFor.append(":").append(Arch), Data, Priority);
  206         else
  207            CreatePin(Type, NameToPinFor, Data, Priority);
  208      }
  209       return;
  210    }
  211 
  212    // find the package (group) this pin applies to
  213    pkgCache::GrpIterator Grp = Cache->FindGrp(Name);
  214    bool matched = false;
  215    if (Grp.end() == false)
  216    {
  217       std::string MatchingArch;
  218       if (Arch.empty() == true)
  219      MatchingArch = Cache->NativeArch();
  220       else
  221      MatchingArch = Arch;
  222       APT::CacheFilter::PackageArchitectureMatchesSpecification pams(MatchingArch);
  223 
  224       if (IsSourcePin) {
  225      for (pkgCache::VerIterator Ver = Grp.VersionsInSource(); not Ver.end(); Ver = Ver.NextInSource())
  226      {
  227         if (pams(Ver.ParentPkg().Arch()) == false)
  228            continue;
  229 
  230         PkgPin P(Ver.ParentPkg().FullName());
  231         P.Type = Type;
  232         P.Priority = Priority;
  233         P.Data = Data;
  234         // Find matching version(s) and copy the pin into it
  235         pkgVersionMatch Match(P.Data,P.Type);
  236         if (Match.VersionMatches(Ver)) {
  237            Pin *VP = VerPins + Ver->ID;
  238            if (VP->Type == pkgVersionMatch::None) {
  239           *VP = P;
  240            matched = true;
  241            }
  242         }
  243      }
  244       } else {
  245      for (pkgCache::PkgIterator Pkg = Grp.PackageList(); Pkg.end() != true; Pkg = Grp.NextPkg(Pkg))
  246      {
  247         if (pams(Pkg.Arch()) == false)
  248            continue;
  249 
  250         PkgPin P(Pkg.FullName());
  251         P.Type = Type;
  252         P.Priority = Priority;
  253         P.Data = Data;
  254 
  255         // Find matching version(s) and copy the pin into it
  256         pkgVersionMatch Match(P.Data,P.Type);
  257         for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() != true; ++Ver)
  258         {
  259            if (Match.VersionMatches(Ver)) {
  260           Pin *VP = VerPins + Ver->ID;
  261           if (VP->Type == pkgVersionMatch::None) {
  262              *VP = P;
  263               matched = true;
  264           }
  265            }
  266         }
  267      }
  268       }
  269    }
  270 
  271    if (matched == false)
  272    {
  273       PkgPin *P = &*Unmatched.insert(Unmatched.end(),PkgPin(Name));
  274       if (Arch.empty() == false)
  275      P->Pkg.append(":").append(Arch);
  276       P->Type = Type;
  277       P->Priority = Priority;
  278       P->Data = Data;
  279       return;
  280    }
  281 }
  282                                     /*}}}*/
  283 // Policy::GetPriority - Get the priority of the package pin        /*{{{*/
  284 // ---------------------------------------------------------------------
  285 /* */
  286 // Returns true if this update is excluded by phasing.
  287 static inline bool ExcludePhased(std::string machineID, pkgCache::VerIterator const &Ver)
  288 {
  289    // The order and fallbacks for the always/never checks come from update-manager and exist
  290    // to preserve compatibility.
  291    if (_config->FindB("APT::Get::Always-Include-Phased-Updates",
  292               _config->FindB("Update-Manager::Always-Include-Phased-Updates", false)))
  293       return false;
  294 
  295    if (_config->FindB("APT::Get::Never-Include-Phased-Updates",
  296               _config->FindB("Update-Manager::Never-Include-Phased-Updates", false)))
  297       return true;
  298 
  299    if (machineID.empty()             // no machine-id
  300        || getenv("SOURCE_DATE_EPOCH") != nullptr // reproducible build - always include
  301        || APT::Configuration::isChroot())
  302       return false;
  303 
  304    std::string seedStr = std::string(Ver.SourcePkgName()) + "-" + Ver.SourceVerStr() + "-" + machineID;
  305    std::seed_seq seed(seedStr.begin(), seedStr.end());
  306    std::minstd_rand rand(seed);
  307    std::uniform_int_distribution<unsigned int> dist(0, 100);
  308 
  309    return dist(rand) > Ver.PhasedUpdatePercentage();
  310 }
  311 APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, bool ConsiderFiles)
  312 {
  313    if (Ver.PhasedUpdatePercentage() != 100)
  314    {
  315       if (ExcludePhased(d->machineID, Ver))
  316      return 1;
  317    }
  318    if (VerPins[Ver->ID].Type != pkgVersionMatch::None)
  319    {
  320       // If all sources are never pins, the never pin wins.
  321       if (VerPins[Ver->ID].Priority == NEVER_PIN)
  322      return NEVER_PIN;
  323       for (pkgCache::VerFileIterator file = Ver.FileList(); file.end() == false; file++)
  324      if (GetPriority(file.File()) != NEVER_PIN)
  325         return VerPins[Ver->ID].Priority;
  326    }
  327    if (!ConsiderFiles)
  328       return 0;
  329 
  330    // priorities are short ints, but we want to pick a value outside the valid range here
  331    auto priority = std::numeric_limits<signed int>::min();
  332    for (pkgCache::VerFileIterator file = Ver.FileList(); file.end() == false; file++)
  333    {
  334       /* If this is the status file, and the current version is not the
  335          version in the status file (ie it is not installed, or somesuch)
  336          then it is not a candidate for installation, ever. This weeds
  337          out bogus entries that may be due to config-file states, or
  338          other. */
  339       if (file.File().Flagged(pkgCache::Flag::NotSource) && Ver.ParentPkg().CurrentVer() != Ver)
  340      priority = std::max<decltype(priority)>(priority, -1);
  341       else
  342      priority = std::max<decltype(priority)>(priority, GetPriority(file.File()));
  343    }
  344 
  345    return priority == std::numeric_limits<decltype(priority)>::min() ? 0 : priority;
  346 }
  347 APT_PURE signed short pkgPolicy::GetPriority(pkgCache::PkgFileIterator const &File)
  348 {
  349    return PFPriority[File->ID];
  350 }
  351                                     /*}}}*/
  352 // SetPriority - Directly set priority                  /*{{{*/
  353 // ---------------------------------------------------------------------
  354 void pkgPolicy::SetPriority(pkgCache::VerIterator const &Ver, signed short Priority)
  355 {
  356    Pin pin;
  357    pin.Data = "pkgPolicy::SetPriority";
  358    pin.Priority = Priority;
  359    VerPins[Ver->ID] = pin;
  360 }
  361 void pkgPolicy::SetPriority(pkgCache::PkgFileIterator const &File, signed short Priority)
  362 {
  363    PFPriority[File->ID] = Priority;
  364 }
  365 
  366                                     /*}}}*/
  367 // ReadPinDir - Load the pin files from this dir into a Policy      /*{{{*/
  368 // ---------------------------------------------------------------------
  369 /* This will load each pin file in the given dir into a Policy. If the
  370    given dir is empty the dir set in Dir::Etc::PreferencesParts is used.
  371    Note also that this method will issue a warning if the dir does not
  372    exists but it will return true in this case! */
  373 bool ReadPinDir(pkgPolicy &Plcy,string Dir)
  374 {
  375    if (Dir.empty() == true)
  376       Dir = _config->FindDir("Dir::Etc::PreferencesParts", "/dev/null");
  377 
  378    if (DirectoryExists(Dir) == false)
  379    {
  380       if (APT::String::Endswith(Dir, "/dev/null") == false)
  381      _error->WarningE("DirectoryExists",_("Unable to read %s"),Dir.c_str());
  382       return true;
  383    }
  384 
  385    _error->PushToStack();
  386    vector<string> const List = GetListOfFilesInDir(Dir, "pref", true, true);
  387    bool const PendingErrors = _error->PendingError();
  388    _error->MergeWithStack();
  389    if (PendingErrors)
  390       return false;
  391 
  392    // Read the files
  393    bool good = true;
  394    for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I)
  395       good = ReadPinFile(Plcy, *I) && good;
  396    return good;
  397 }
  398                                     /*}}}*/
  399 // ReadPinFile - Load the pin file into a Policy            /*{{{*/
  400 // ---------------------------------------------------------------------
  401 /* I'd like to see the preferences file store more than just pin information
  402    but right now that is the only stuff I have to store. Later there will
  403    have to be some kind of combined super parser to get the data into all
  404    the right classes.. */
  405 bool ReadPinFile(pkgPolicy &Plcy,string File)
  406 {
  407    if (File.empty() == true)
  408       File = _config->FindFile("Dir::Etc::Preferences");
  409 
  410    if (RealFileExists(File) == false)
  411       return true;
  412 
  413    FileFd Fd;
  414    if (OpenConfigurationFileFd(File, Fd) == false)
  415       return false;
  416 
  417    pkgTagFile TF(&Fd, pkgTagFile::SUPPORT_COMMENTS);
  418    if (Fd.IsOpen() == false || Fd.Failed())
  419       return false;
  420 
  421    pkgTagSection Tags;
  422    while (TF.Step(Tags) == true)
  423    {
  424       // can happen when there are only comments in a record
  425       if (Tags.Count() == 0)
  426          continue;
  427 
  428       string Name = Tags.FindS("Package");
  429       if (Name.empty() == true)
  430      return _error->Error(_("Invalid record in the preferences file %s, no Package header"), File.c_str());
  431       if (Name == "*")
  432      Name = string();
  433       
  434       const char *Start;
  435       const char *End;
  436       if (Tags.Find("Pin",Start,End) == false)
  437      continue;
  438      
  439       const char *Word = Start;
  440       for (; Word != End && isspace(*Word) == 0; Word++);
  441 
  442       // Parse the type..
  443       pkgVersionMatch::MatchType Type;
  444       if (stringcasecmp(Start,Word,"version") == 0 && Name.empty() == false)
  445      Type = pkgVersionMatch::Version;
  446       else if (stringcasecmp(Start,Word,"release") == 0)
  447      Type = pkgVersionMatch::Release;
  448       else if (stringcasecmp(Start,Word,"origin") == 0)
  449      Type = pkgVersionMatch::Origin;
  450       else
  451       {
  452      _error->Warning(_("Did not understand pin type %s"),string(Start,Word).c_str());
  453      continue;
  454       }
  455       for (; Word != End && isspace(*Word) != 0; Word++);
  456 
  457       _error->PushToStack();
  458       std::string sPriority = Tags.FindS("Pin-Priority");
  459       int priority = sPriority == "never" ? NEVER_PIN : Tags.FindI("Pin-Priority", 0);
  460       bool const newError = _error->PendingError();
  461       _error->MergeWithStack();
  462 
  463       if (sPriority == "never" && not Name.empty())
  464      return _error->Error(_("%s: The special 'Pin-Priority: %s' can only be used for 'Package: *' records"), File.c_str(), "never");
  465 
  466       // Silently clamp the never pin to never pin + 1
  467       if (priority == NEVER_PIN && sPriority != "never")
  468      priority = NEVER_PIN + 1;
  469       if (priority < std::numeric_limits<short>::min() ||
  470           priority > std::numeric_limits<short>::max() ||
  471       newError) {
  472      return _error->Error(_("%s: Value %s is outside the range of valid pin priorities (%d to %d)"),
  473                   File.c_str(), Tags.FindS("Pin-Priority").c_str(),
  474                   std::numeric_limits<short>::min(),
  475                   std::numeric_limits<short>::max());
  476       }
  477       if (priority == 0)
  478       {
  479          return _error->Error(_("No priority (or zero) specified for pin"));
  480       }
  481 
  482       istringstream s(Name);
  483       string pkg;
  484       while(!s.eof())
  485       {
  486      s >> pkg;
  487          Plcy.CreatePin(Type, pkg, string(Word,End),priority);
  488       };
  489    }
  490 
  491    Plcy.InitDefaults();
  492    return true;
  493 }
  494                                     /*}}}*/
  495 
  496 pkgPolicy::~pkgPolicy()
  497 {
  498    delete[] PFPriority;
  499    delete[] VerPins;
  500    delete d;
  501 }