"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.

    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 }