apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

policy.cc
Go to the documentation of this 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 
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 
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)
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
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 
89 }
90  /*}}}*/
91 // Policy::InitDefaults - Compute the default selections /*{{{*/
92 // ---------------------------------------------------------------------
93 /* */
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. */
141 {
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. */
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;
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
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 }
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 
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..
444  if (stringcasecmp(Start,Word,"version") == 0 && Name.empty() == false)
446  else if (stringcasecmp(Start,Word,"release") == 0)
448  else if (stringcasecmp(Start,Word,"origin") == 0)
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 
497 {
498  delete[] PFPriority;
499  delete[] VerPins;
500  delete d;
501 }
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
Definition: fileutl.h:39
bool IsOpen()
Definition: fileutl.h:150
bool Failed()
Definition: fileutl.h:151
const char * Type
Definition: metaindex.h:38
pkgPolicy(pkgCache *Owner)
Definition: policy.cc:54
virtual pkgCache::VerIterator GetCandidateVer(pkgCache::PkgIterator const &Pkg) APT_OVERRIDE
Definition: policy.cc:140
virtual signed short GetPriority(pkgCache::VerIterator const &Ver, bool ConsiderFiles=true) APT_OVERRIDE
Definition: policy.cc:311
pkgCache * Cache
Definition: policy.h:65
std::vector< Pin > Defaults
Definition: policy.h:63
void CreatePin(pkgVersionMatch::MatchType Type, std::string Pkg, std::string Data, signed short Priority)
Definition: policy.cc:171
virtual ~pkgPolicy()
Definition: policy.cc:496
Pin * VerPins
Definition: policy.h:61
void SetPriority(pkgCache::VerIterator const &Ver, signed short Priority)
Definition: policy.cc:354
bool InitDefaults()
Definition: policy.cc:94
bool StatusOverride
Definition: policy.h:66
std::vector< PkgPin > Unmatched
Definition: policy.h:64
signed short * PFPriority
Definition: policy.h:62
Private *const d
Definition: policy.h:86
bool Step(pkgTagSection &Section)
Definition: tagfile.cc:204
@ SUPPORT_COMMENTS
Definition: tagfile.h:190
std::string FindS(APT::StringView sv) const
Definition: tagfile.h:70
unsigned int Count() const
amount of Tags in the current section
Definition: tagfile.cc:942
APT_HIDDEN bool Find(Key key, const char *&Start, const char *&End) const
Definition: tagfile.cc:690
APT_HIDDEN signed int FindI(Key key, signed long Default=0) const
Definition: tagfile.cc:776
bool FileMatch(pkgCache::PkgFileIterator File)
bool VersionMatches(pkgCache::VerIterator Ver)
static bool ExpressionMatches(const char *pattern, const char *string)
Configuration * _config
bool DirectoryExists(string const &Path)
Definition: fileutl.cc:348
bool OpenConfigurationFileFd(std::string const &File, FileFd &Fd)
Definition: fileutl.cc:3419
bool RealFileExists(string File)
Definition: fileutl.cc:337
std::vector< string > GetListOfFilesInDir(string const &Dir, string const &Ext, bool const &SortList, bool const &AllowNoExt)
Definition: fileutl.cc:421
#define APT_PURE
Definition: macros.h:56
std::string const getMachineID()
bool Endswith(const std::string &s, const std::string &end)
Definition: strutl.cc:77
bool Startswith(const std::string &s, const std::string &start)
Definition: strutl.cc:84
pkgCache - Structure definitions for the cache file
bool ReadPinDir(pkgPolicy &Plcy, string Dir)
Definition: policy.cc:373
static bool ExcludePhased(std::string machineID, pkgCache::VerIterator const &Ver)
Definition: policy.cc:287
bool ReadPinFile(pkgPolicy &Plcy, string File)
Definition: policy.cc:405
constexpr short NEVER_PIN
Definition: policy.cc:43
@ ButAutomaticUpgrades
Definition: pkgcache.h:203
signed short Priority
Definition: policy.h:51
std::string Data
Definition: policy.h:50
pkgVersionMatch::MatchType Type
Definition: policy.h:49
std::string Pkg
Definition: policy.h:57
std::string machineID
Definition: policy.cc:47
int stringcasecmp(const char *A, const char *AEnd, const char *B, const char *BEnd)
Definition: strutl.cc:685