]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/edsp.cc
refactor EDSP classes for better internal reuse
[apt.git] / apt-pkg / edsp.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
6 /*}}}*/
7// Include Files /*{{{*/
8#include <config.h>
9
10#include <apt-pkg/error.h>
11#include <apt-pkg/cacheset.h>
12#include <apt-pkg/depcache.h>
13#include <apt-pkg/pkgcache.h>
14#include <apt-pkg/cacheiterators.h>
15#include <apt-pkg/progress.h>
16#include <apt-pkg/fileutl.h>
17#include <apt-pkg/edsp.h>
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/strutl.h>
20#include <apt-pkg/pkgsystem.h>
21
22#include <ctype.h>
23#include <stddef.h>
24#include <string.h>
25#include <unistd.h>
26#include <stdio.h>
27
28#include <array>
29#include <limits>
30#include <string>
31
32#include <apti18n.h>
33 /*}}}*/
34
35using std::string;
36
37// we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
38constexpr char const * const PrioMap[] = {
39 nullptr, "important", "required", "standard",
40 "optional", "extra"
41};
42constexpr char const * const DepMap[] = {
43 nullptr, "Depends", "Pre-Depends", "Suggests",
44 "Recommends" , "Conflicts", "Replaces",
45 "Obsoletes", "Breaks", "Enhances"
46};
47
48// WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
49static bool WriteOkay_fn(FileFd &) { return true; }
50template<typename... Tail> static bool WriteOkay_fn(FileFd &output, APT::StringView data, Tail... more_data)
51{
52 return likely(output.Write(data.data(), data.length()) && WriteOkay_fn(output, more_data...));
53}
54template<typename... Tail> static bool WriteOkay_fn(FileFd &output, unsigned int data, Tail... more_data)
55{
56 std::string number;
57 strprintf(number, "%d", data);
58 return likely(output.Write(number.data(), number.length()) && WriteOkay_fn(output, more_data...));
59}
60template<typename... Data> static bool WriteOkay(bool &Okay, FileFd &output, Data&&... data)
61{
62 Okay = likely(Okay && WriteOkay_fn(output, std::forward<Data>(data)...));
63 return Okay;
64}
65template<typename... Data> static bool WriteOkay(FileFd &output, Data&&... data)
66{
67 bool Okay = likely(output.Failed() == false);
68 return WriteOkay(Okay, output, std::forward<Data>(data)...);
69}
70 /*}}}*/
71// WriteScenarioVersion /*{{{*/
72static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
73 pkgCache::VerIterator const &Ver)
74{
75 fprintf(output, "Package: %s\n", Pkg.Name());
76 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
77 fprintf(output, "Architecture: %s\n", Ver.Arch());
78 fprintf(output, "Version: %s\n", Ver.VerStr());
79 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
80 if (Pkg.CurrentVer() == Ver)
81 fprintf(output, "Installed: yes\n");
82 if (Pkg->SelectedState == pkgCache::State::Hold ||
83 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
84 fprintf(output, "Hold: yes\n");
85 fprintf(output, "APT-ID: %d\n", Ver->ID);
86 if (PrioMap[Ver->Priority] != nullptr)
87 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
88 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
89 fprintf(output, "Essential: yes\n");
90 if (Ver->Section != 0)
91 fprintf(output, "Section: %s\n", Ver.Section());
92 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
93 fprintf(output, "Multi-Arch: allowed\n");
94 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
95 fprintf(output, "Multi-Arch: foreign\n");
96 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
97 fprintf(output, "Multi-Arch: same\n");
98 std::set<string> Releases;
99 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
100 pkgCache::PkgFileIterator File = I.File();
101 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
102 string Release = File.RelStr();
103 if (!Release.empty())
104 Releases.insert(Release);
105 }
106 }
107 if (!Releases.empty()) {
108 fprintf(output, "APT-Release:\n");
109 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
110 fprintf(output, " %s\n", R->c_str());
111 }
112 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
113 if (Cache.GetCandidateVersion(Pkg) == Ver)
114 fprintf(output, "APT-Candidate: yes\n");
115 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
116 fprintf(output, "APT-Automatic: yes\n");
117}
118static bool WriteScenarioVersion(FileFd &output, pkgCache::PkgIterator const &Pkg,
119 pkgCache::VerIterator const &Ver)
120{
121 bool Okay = WriteOkay(output, "Package: ", Pkg.Name(),
122 "\nArchitecture: ", Ver.Arch(),
123 "\nVersion: ", Ver.VerStr());
124 WriteOkay(Okay, output, "\nAPT-ID: ", Ver->ID);
125 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
126 WriteOkay(Okay, output, "\nEssential: yes");
127 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
128 WriteOkay(Okay, output, "\nMulti-Arch: allowed");
129 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
130 WriteOkay(Okay, output, "\nMulti-Arch: foreign");
131 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
132 WriteOkay(Okay, output, "\nMulti-Arch: same");
133 return Okay;
134}
135 /*}}}*/
136// WriteScenarioDependency /*{{{*/
137static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
138{
139 std::array<std::string, _count(DepMap)> dependencies;
140 bool orGroup = false;
141 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
142 {
143 if (Dep.IsImplicit() == true)
144 continue;
145 if (orGroup == false)
146 dependencies[Dep->Type].append(", ");
147 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
148 if (Dep->Version != 0)
149 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
150 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
151 {
152 dependencies[Dep->Type].append(" | ");
153 orGroup = true;
154 }
155 else
156 orGroup = false;
157 }
158 for (size_t i = 1; i < dependencies.size(); ++i)
159 if (dependencies[i].empty() == false)
160 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
161 string provides;
162 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
163 {
164 if (Prv.IsMultiArchImplicit() == true)
165 continue;
166 if (provides.empty() == false)
167 provides.append(", ");
168 provides.append(Prv.Name());
169 if (Prv->ProvideVersion != 0)
170 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
171 }
172 if (provides.empty() == false)
173 fprintf(output, "Provides: %s\n", provides.c_str());
174}
175static bool WriteScenarioDependency(FileFd &output, pkgCache::VerIterator const &Ver, bool const OnlyCritical)
176{
177 std::array<std::string, _count(DepMap)> dependencies;
178 bool orGroup = false;
179 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
180 {
181 if (Dep.IsImplicit() == true)
182 continue;
183 if (OnlyCritical && Dep.IsCritical() == false)
184 continue;
185 if (orGroup == false && dependencies[Dep->Type].empty() == false)
186 dependencies[Dep->Type].append(", ");
187 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
188 if (Dep->Version != 0)
189 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
190 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
191 {
192 dependencies[Dep->Type].append(" | ");
193 orGroup = true;
194 }
195 else
196 orGroup = false;
197 }
198 bool Okay = output.Failed() == false;
199 for (size_t i = 1; i < dependencies.size(); ++i)
200 if (dependencies[i].empty() == false)
201 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
202 string provides;
203 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
204 {
205 if (Prv.IsMultiArchImplicit() == true)
206 continue;
207 if (provides.empty() == false)
208 provides.append(", ");
209 provides.append(Prv.Name());
210 if (Prv->ProvideVersion != 0)
211 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
212 }
213 if (provides.empty() == false)
214 WriteOkay(Okay, output, "\nProvides: ", provides);
215 return WriteOkay(Okay, output, "\n");
216}
217 /*}}}*/
218// WriteScenarioLimitedDependency /*{{{*/
219static void WriteScenarioLimitedDependency(FILE* output,
220 pkgCache::VerIterator const &Ver,
221 APT::PackageSet const &pkgset)
222{
223 std::array<std::string, _count(DepMap)> dependencies;
224 bool orGroup = false;
225 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
226 {
227 if (Dep.IsImplicit() == true)
228 continue;
229 if (orGroup == false)
230 {
231 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
232 continue;
233 if (dependencies[Dep->Type].empty() == false)
234 dependencies[Dep->Type].append(", ");
235 }
236 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
237 {
238 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
239 continue;
240 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
241 orGroup = false;
242 continue;
243 }
244 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
245 if (Dep->Version != 0)
246 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
247 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
248 {
249 dependencies[Dep->Type].append(" | ");
250 orGroup = true;
251 }
252 else
253 orGroup = false;
254 }
255 for (size_t i = 1; i < dependencies.size(); ++i)
256 if (dependencies[i].empty() == false)
257 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str());
258 string provides;
259 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
260 {
261 if (Prv.IsMultiArchImplicit() == true)
262 continue;
263 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
264 continue;
265 if (provides.empty() == false)
266 provides.append(", ");
267 provides.append(Prv.Name());
268 if (Prv->ProvideVersion != 0)
269 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
270 }
271 if (provides.empty() == false)
272 fprintf(output, "Provides: %s\n", provides.c_str());
273}
274static bool WriteScenarioLimitedDependency(FileFd &output,
275 pkgCache::VerIterator const &Ver,
276 std::vector<bool> const &pkgset,
277 bool const OnlyCritical)
278{
279 std::array<std::string, _count(DepMap)> dependencies;
280 bool orGroup = false;
281 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
282 {
283 if (Dep.IsImplicit() == true)
284 continue;
285 if (OnlyCritical && Dep.IsCritical() == false)
286 continue;
287 if (orGroup == false)
288 {
289 if (pkgset[Dep.TargetPkg()->ID] == false)
290 continue;
291 if (dependencies[Dep->Type].empty() == false)
292 dependencies[Dep->Type].append(", ");
293 }
294 else if (pkgset[Dep.TargetPkg()->ID] == false)
295 {
296 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
297 continue;
298 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
299 orGroup = false;
300 continue;
301 }
302 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
303 if (Dep->Version != 0)
304 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
305 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
306 {
307 dependencies[Dep->Type].append(" | ");
308 orGroup = true;
309 }
310 else
311 orGroup = false;
312 }
313 bool Okay = output.Failed() == false;
314 for (size_t i = 1; i < dependencies.size(); ++i)
315 if (dependencies[i].empty() == false)
316 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
317 string provides;
318 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
319 {
320 if (Prv.IsMultiArchImplicit() == true)
321 continue;
322 if (pkgset[Prv.ParentPkg()->ID] == false)
323 continue;
324 if (provides.empty() == false)
325 provides.append(", ");
326 provides.append(Prv.Name());
327 if (Prv->ProvideVersion != 0)
328 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
329 }
330 if (provides.empty() == false)
331 WriteOkay(Okay, output, "\nProvides: ", provides);
332 return WriteOkay(Okay, output, "\n");
333}
334 /*}}}*/
335static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
336{
337 /* versions which aren't current and aren't available in
338 any "online" source file are bad, expect if they are the choosen
339 candidate: The exception is for build-dep implementation as it creates
340 such pseudo (package) versions and removes them later on again.
341 We filter out versions at all so packages in 'rc' state only available
342 in dpkg/status aren't passed to solvers as they can't be installed. */
343 if (Pkg->CurrentVer != 0)
344 return false;
345 if (Cache.GetCandidateVersion(Pkg) == Ver)
346 return false;
347 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
348 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
349 return false;
350 return true;
351}
352 /*}}}*/
353static bool WriteScenarioEDSPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
354 pkgCache::VerIterator const &Ver)
355{
356 bool Okay = WriteOkay(output, "\nSource: ", Ver.SourcePkgName(),
357 "\nSource-Version: ", Ver.SourceVerStr());
358 if (PrioMap[Ver->Priority] != nullptr)
359 WriteOkay(Okay, output, "\nPriority: ", PrioMap[Ver->Priority]);
360 if (Ver->Section != 0)
361 WriteOkay(Okay, output, "\nSection: ", Ver.Section());
362 if (Pkg.CurrentVer() == Ver)
363 WriteOkay(Okay, output, "\nInstalled: yes");
364 if (Pkg->SelectedState == pkgCache::State::Hold ||
365 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
366 WriteOkay(Okay, output, "\nHold: yes");
367 std::set<string> Releases;
368 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
369 pkgCache::PkgFileIterator File = I.File();
370 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
371 string Release = File.RelStr();
372 if (!Release.empty())
373 Releases.insert(Release);
374 }
375 }
376 if (!Releases.empty()) {
377 WriteOkay(Okay, output, "\nAPT-Release:");
378 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
379 WriteOkay(Okay, output, "\n ", *R);
380 }
381 WriteOkay(Okay, output, "\nAPT-Pin: ", Cache.GetPolicy().GetPriority(Ver));
382 if (Cache.GetCandidateVersion(Pkg) == Ver)
383 WriteOkay(Okay, output, "\nAPT-Candidate: yes");
384 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
385 WriteOkay(Okay, output, "\nAPT-Automatic: yes");
386 return Okay;
387}
388 /*}}}*/
389// EDSP::WriteScenario - to the given file descriptor /*{{{*/
390bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
391{
392 if (Progress != NULL)
393 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
394 unsigned long p = 0;
395 std::vector<std::string> archs = APT::Configuration::getArchitectures();
396 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
397 {
398 std::string const arch = Pkg.Arch();
399 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
400 continue;
401 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
402 {
403 if (SkipUnavailableVersions(Cache, Pkg, Ver))
404 continue;
405 WriteScenarioVersion(Cache, output, Pkg, Ver);
406 WriteScenarioDependency(output, Ver);
407 fprintf(output, "\n");
408 if (Progress != NULL && p % 100 == 0)
409 Progress->Progress(p);
410 }
411 }
412 return true;
413}
414bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progress)
415{
416 if (Progress != NULL)
417 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
418 unsigned long p = 0;
419 bool Okay = output.Failed() == false;
420 std::vector<std::string> archs = APT::Configuration::getArchitectures();
421 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
422 {
423 std::string const arch = Pkg.Arch();
424 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
425 continue;
426 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p)
427 {
428 if (SkipUnavailableVersions(Cache, Pkg, Ver))
429 continue;
430 Okay &= WriteScenarioVersion(output, Pkg, Ver);
431 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
432 Okay &= WriteScenarioDependency(output, Ver, false);
433 WriteOkay(Okay, output, "\n");
434 if (Progress != NULL && p % 100 == 0)
435 Progress->Progress(p);
436 }
437 }
438 return true;
439}
440 /*}}}*/
441// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
442bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
443 APT::PackageSet const &pkgset,
444 OpProgress *Progress)
445{
446 if (Progress != NULL)
447 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
448 unsigned long p = 0;
449 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
450 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
451 {
452 if (SkipUnavailableVersions(Cache, Pkg, Ver))
453 continue;
454 WriteScenarioVersion(Cache, output, Pkg, Ver);
455 WriteScenarioLimitedDependency(output, Ver, pkgset);
456 fprintf(output, "\n");
457 if (Progress != NULL && p % 100 == 0)
458 Progress->Progress(p);
459 }
460 if (Progress != NULL)
461 Progress->Done();
462 return true;
463}
464bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FileFd &output,
465 std::vector<bool> const &pkgset,
466 OpProgress *Progress)
467{
468 if (Progress != NULL)
469 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
470 unsigned long p = 0;
471 bool Okay = output.Failed() == false;
472 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
473 {
474 if (pkgset[Pkg->ID] == false)
475 continue;
476 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
477 {
478 if (SkipUnavailableVersions(Cache, Pkg, Ver))
479 continue;
480 Okay &= WriteScenarioVersion(output, Pkg, Ver);
481 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
482 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, false);
483 WriteOkay(Okay, output, "\n");
484 if (Progress != NULL && p % 100 == 0)
485 Progress->Progress(p);
486 }
487 }
488 if (Progress != NULL)
489 Progress->Done();
490 return Okay;
491}
492 /*}}}*/
493// EDSP::WriteRequest - to the given file descriptor /*{{{*/
494bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
495 bool const DistUpgrade, bool const AutoRemove,
496 OpProgress *Progress)
497{
498 if (Progress != NULL)
499 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
500 unsigned long p = 0;
501 string del, inst;
502 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
503 {
504 if (Progress != NULL && p % 100 == 0)
505 Progress->Progress(p);
506 string* req;
507 pkgDepCache::StateCache &P = Cache[Pkg];
508 if (P.Delete() == true)
509 req = &del;
510 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
511 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
512 req = &inst;
513 else
514 continue;
515 req->append(" ").append(Pkg.FullName());
516 }
517 fprintf(output, "Request: EDSP 0.5\n");
518
519 const char *arch = _config->Find("APT::Architecture").c_str();
520 std::vector<string> archs = APT::Configuration::getArchitectures();
521 fprintf(output, "Architecture: %s\n", arch);
522 fprintf(output, "Architectures:");
523 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
524 fprintf(output, " %s", a->c_str());
525 fprintf(output, "\n");
526
527 if (del.empty() == false)
528 fprintf(output, "Remove: %s\n", del.c_str()+1);
529 if (inst.empty() == false)
530 fprintf(output, "Install: %s\n", inst.c_str()+1);
531 if (Upgrade == true)
532 fprintf(output, "Upgrade: yes\n");
533 if (DistUpgrade == true)
534 fprintf(output, "Dist-Upgrade: yes\n");
535 if (AutoRemove == true)
536 fprintf(output, "Autoremove: yes\n");
537 auto const solver = _config->Find("APT::Solver", "internal");
538 fprintf(output, "Solver: %s\n", solver.c_str());
539 auto const solverconf = std::string("APT::Solver::") + solver + "::";
540 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
541 fprintf(output, "Strict-Pinning: no\n");
542 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
543 if (solverpref.empty() == false)
544 fprintf(output, "Preferences: %s\n", solverpref.c_str());
545 fprintf(output, "\n");
546 return true;
547}
548bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
549 unsigned int const flags,
550 OpProgress *Progress)
551{
552 if (Progress != NULL)
553 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
554 unsigned long p = 0;
555 string del, inst;
556 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
557 {
558 if (Progress != NULL && p % 100 == 0)
559 Progress->Progress(p);
560 string* req;
561 pkgDepCache::StateCache &P = Cache[Pkg];
562 if (P.Delete() == true)
563 req = &del;
564 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
565 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
566 req = &inst;
567 else
568 continue;
569 req->append(" ").append(Pkg.FullName());
570 }
571 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
572
573 const char *arch = _config->Find("APT::Architecture").c_str();
574 std::vector<string> archs = APT::Configuration::getArchitectures();
575 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
576 "Architectures:");
577 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
578 WriteOkay(Okay, output, " ", *a);
579 WriteOkay(Okay, output, "\n");
580
581 if (del.empty() == false)
582 WriteOkay(Okay, output, "Remove:", del, "\n");
583 if (inst.empty() == false)
584 WriteOkay(Okay, output, "Install:", inst, "\n");
585 if (flags & Request::AUTOREMOVE)
586 WriteOkay(Okay, output, "Autoremove: yes\n");
587 if (flags & Request::UPGRADE_ALL)
588 {
589 WriteOkay(Okay, output, "Upgrade-All: yes\n");
590 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
591 WriteOkay(Okay, output, "Upgrade: yes\n");
592 else
593 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
594 }
595 if (flags & Request::FORBID_NEW_INSTALL)
596 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
597 if (flags & Request::FORBID_REMOVE)
598 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
599 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
600 WriteOkay(Okay, output, "Strict-Pinning: no\n");
601 string solverpref("APT::Solver::");
602 solverpref.append(_config->Find("APT::Solver", "internal")).append("::Preferences");
603 if (_config->Exists(solverpref) == true)
604 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
605 return WriteOkay(Okay, output, "\n");
606}
607 /*}}}*/
608// EDSP::ReadResponse - from the given file descriptor /*{{{*/
609bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
610 /* We build an map id to mmap offset here
611 In theory we could use the offset as ID, but then VersionCount
612 couldn't be used to create other versionmappings anymore and it
613 would be too easy for a (buggy) solver to segfault APTā€¦ */
614 unsigned long long const VersionCount = Cache.Head().VersionCount;
615 unsigned long VerIdx[VersionCount];
616 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
617 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
618 VerIdx[V->ID] = V.Index();
619 Cache[P].Marked = true;
620 Cache[P].Garbage = false;
621 }
622
623 FileFd in;
624 in.OpenDescriptor(input, FileFd::ReadOnly);
625 pkgTagFile response(&in, 100);
626 pkgTagSection section;
627
628 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
629 while (response.Step(section) == true) {
630 std::string type;
631 if (section.Exists("Install") == true)
632 type = "Install";
633 else if (section.Exists("Remove") == true)
634 type = "Remove";
635 else if (section.Exists("Progress") == true) {
636 if (Progress != NULL) {
637 string msg = section.FindS("Message");
638 if (msg.empty() == true)
639 msg = _("Prepare for receiving solution");
640 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
641 }
642 continue;
643 } else if (section.Exists("Error") == true) {
644 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
645 if (msg.empty() == true) {
646 msg = _("External solver failed without a proper error message");
647 _error->Error("%s", msg.c_str());
648 } else
649 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
650 if (Progress != NULL)
651 Progress->Done();
652 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
653 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
654 std::cerr << msg << std::endl << std::endl;
655 return false;
656 } else if (section.Exists("Autoremove") == true)
657 type = "Autoremove";
658 else
659 continue;
660
661 size_t const id = section.FindULL(type.c_str(), VersionCount);
662 if (id == VersionCount) {
663 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
664 continue;
665 } else if (id > Cache.Head().VersionCount) {
666 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type.c_str()).c_str(), type.c_str());
667 continue;
668 }
669
670 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
671 auto const Pkg = Ver.ParentPkg();
672 if (type == "Autoremove") {
673 Cache[Pkg].Marked = false;
674 Cache[Pkg].Garbage = true;
675 } else if (seenOnce.emplace(Pkg->ID).second == false) {
676 _error->Warning("Ignoring %s stanza received for package %s which already had a previous stanza effecting it!", type.c_str(), Pkg.FullName(false).c_str());
677 } else if (type == "Install") {
678 if (Pkg.CurrentVer() == Ver) {
679 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
680 Ver.VerStr(), Pkg.FullName(false).c_str());
681 } else {
682 Cache.SetCandidateVersion(Ver);
683 Cache.MarkInstall(Pkg, false, 0, false);
684 }
685 } else if (type == "Remove") {
686 if (Pkg->CurrentVer == 0)
687 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
688 Ver.VerStr(), Pkg.FullName(false).c_str());
689 else if (Pkg.CurrentVer() != Ver)
690 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
691 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
692 else
693 Cache.MarkDelete(Ver.ParentPkg(), false);
694 }
695 }
696 return true;
697}
698 /*}}}*/
699// ReadLine - first line from the given file descriptor /*{{{*/
700// ---------------------------------------------------------------------
701/* Little helper method to read a complete line into a string. Similar to
702 fgets but we need to use the low-level read() here as otherwise the
703 listparser will be confused later on as mixing of fgets and read isn't
704 a supported action according to the manpages and results are undefined */
705static bool ReadLine(int const input, std::string &line) {
706 char one;
707 ssize_t data = 0;
708 line.erase();
709 line.reserve(100);
710 while ((data = read(input, &one, sizeof(one))) != -1) {
711 if (data != 1)
712 continue;
713 if (one == '\n')
714 return true;
715 if (one == '\r')
716 continue;
717 if (line.empty() == true && isblank(one) != 0)
718 continue;
719 line += one;
720 }
721 return false;
722}
723 /*}}}*/
724// StringToBool - convert yes/no to bool /*{{{*/
725// ---------------------------------------------------------------------
726/* we are not as lazy as we are in the global StringToBool as we really
727 only accept yes/no here - but we will ignore leading spaces */
728static bool StringToBool(char const *answer, bool const defValue) {
729 for (; isspace(*answer) != 0; ++answer);
730 if (strncasecmp(answer, "yes", 3) == 0)
731 return true;
732 else if (strncasecmp(answer, "no", 2) == 0)
733 return false;
734 else
735 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
736 return defValue;
737}
738 /*}}}*/
739static bool ReadFlag(unsigned int &flags, std::string const &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
740{
741 if (line.compare(0, name.length(), name.data()) != 0)
742 return false;
743 auto const l = line.c_str() + name.length() + 1;
744 if (StringToBool(l, false))
745 flags |= setflag;
746 else
747 flags &= ~setflag;
748 return true;
749}
750 /*}}}*/
751// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
752bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
753 std::list<std::string> &remove, unsigned int &flags)
754{
755 install.clear();
756 remove.clear();
757 flags = 0;
758 std::string line;
759 while (ReadLine(input, line) == true)
760 {
761 // Skip empty lines before request
762 if (line.empty() == true)
763 continue;
764 // The first Tag must be a request, so search for it
765 if (line.compare(0, 8, "Request:") != 0)
766 continue;
767
768 while (ReadLine(input, line) == true)
769 {
770 // empty lines are the end of the request
771 if (line.empty() == true)
772 return true;
773
774 std::list<std::string> *request = NULL;
775 if (line.compare(0, 8, "Install:") == 0)
776 {
777 line.erase(0, 8);
778 request = &install;
779 }
780 else if (line.compare(0, 7, "Remove:") == 0)
781 {
782 line.erase(0, 7);
783 request = &remove;
784 }
785 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
786 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
787 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
788 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
789 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
790 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
791 ;
792 else if (line.compare(0, 13, "Architecture:") == 0)
793 _config->Set("APT::Architecture", line.c_str() + 14);
794 else if (line.compare(0, 14, "Architectures:") == 0)
795 {
796 std::string const archs = line.c_str() + 15;
797 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
798 }
799 else if (line.compare(0, 7, "Solver:") == 0)
800 ; // purely informational line
801 else
802 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
803
804 if (request == NULL)
805 continue;
806 size_t end = line.length();
807 do {
808 size_t begin = line.rfind(' ');
809 if (begin == std::string::npos)
810 {
811 request->push_back(line.substr(0, end));
812 break;
813 }
814 else if (begin < end)
815 request->push_back(line.substr(begin + 1, end));
816 line.erase(begin);
817 end = line.find_last_not_of(' ');
818 } while (end != std::string::npos);
819 }
820 }
821 return false;
822}
823bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
824 std::list<std::string> &remove, bool &upgrade,
825 bool &distUpgrade, bool &autoRemove)
826{
827 unsigned int flags;
828 auto const ret = ReadRequest(input, install, remove, flags);
829 autoRemove = (flags & Request::AUTOREMOVE);
830 if (flags & Request::UPGRADE_ALL)
831 {
832 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
833 {
834 upgrade = true;
835 distUpgrade = false;
836 } else {
837 upgrade = false;
838 distUpgrade = false;
839 }
840 }
841 else
842 {
843 upgrade = false;
844 distUpgrade = false;
845 }
846 return ret;
847}
848 /*}}}*/
849// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
850bool EDSP::ApplyRequest(std::list<std::string> const &install,
851 std::list<std::string> const &remove,
852 pkgDepCache &Cache)
853{
854 for (std::list<std::string>::const_iterator i = install.begin();
855 i != install.end(); ++i) {
856 pkgCache::PkgIterator P = Cache.FindPkg(*i);
857 if (P.end() == true)
858 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
859 else
860 Cache.MarkInstall(P, false);
861 }
862
863 for (std::list<std::string>::const_iterator i = remove.begin();
864 i != remove.end(); ++i) {
865 pkgCache::PkgIterator P = Cache.FindPkg(*i);
866 if (P.end() == true)
867 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
868 else
869 Cache.MarkDelete(P);
870 }
871 return true;
872}
873 /*}}}*/
874// EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
875bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
876{
877 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
878 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
879 {
880 if (Cache[Pkg].Delete() == true)
881 {
882 fprintf(output, "Remove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
883 if (Debug == true)
884 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
885 }
886 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
887 {
888 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
889 fprintf(output, "Install: %d\n", _system->GetVersionMapping(CandVer->ID));
890 if (Debug == true)
891 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
892 }
893 else if (Cache[Pkg].Garbage == true)
894 {
895 fprintf(output, "Autoremove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
896 if (Debug == true)
897 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
898 }
899 else
900 continue;
901 fprintf(output, "\n");
902 }
903
904 return true;
905}
906bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache::VerIterator const &Ver)
907{
908 bool Okay = output.Failed() == false;
909 WriteOkay(Okay, output, Type, ": ", _system->GetVersionMapping(Ver->ID));
910 if (_config->FindB("Debug::EDSP::WriteSolution", false) == true)
911 WriteOkay(Okay, output, "\nPackage: ", Ver.ParentPkg().FullName(), "\nVersion: ", Ver.VerStr());
912 return WriteOkay(Okay, output, "\n\n");
913}
914 /*}}}*/
915// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
916bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
917 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
918 fprintf(output, "Percentage: %d\n", percent);
919 fprintf(output, "Message: %s\n\n", message);
920 fflush(output);
921 return true;
922}
923bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
924 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
925 "Percentage: ", percent, "\n",
926 "Message: ", message, "\n\n") && output.Flush();
927}
928 /*}}}*/
929// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
930bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
931 fprintf(output, "Error: %s\n", uuid);
932 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
933 return true;
934}
935bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
936 return WriteOkay(output, "Error: ", uuid, "\n",
937 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
938 "\n\n");
939}
940 /*}}}*/
941static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/
942 std::vector<std::string> const solverDirs = _config->FindVector(configdir);
943 std::string file;
944 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
945 dir != solverDirs.end(); ++dir) {
946 file = flCombine(*dir, binary);
947 if (RealFileExists(file.c_str()) == true)
948 break;
949 file.clear();
950 }
951
952 if (file.empty() == true)
953 {
954 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
955 return 0;
956 }
957 int external[4] = {-1, -1, -1, -1};
958 if (pipe(external) != 0 || pipe(external + 2) != 0)
959 {
960 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
961 return 0;
962 }
963 for (int i = 0; i < 4; ++i)
964 SetCloseExec(external[i], true);
965
966 pid_t Solver = ExecFork();
967 if (Solver == 0) {
968 dup2(external[0], STDIN_FILENO);
969 dup2(external[3], STDOUT_FILENO);
970 const char* calling[2] = { file.c_str(), 0 };
971 execv(calling[0], (char**) calling);
972 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
973 _exit(100);
974 }
975 close(external[0]);
976 close(external[3]);
977
978 if (WaitFd(external[1], true, 5) == false)
979 {
980 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
981 return 0;
982 }
983
984 *solver_in = external[1];
985 *solver_out = external[2];
986 return Solver;
987}
988 /*}}}*/
989// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
990pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
991 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
992}
993bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
994 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
995 return false;
996 return true;
997}
998 /*}}}*/
999// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1000bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1001 unsigned int const flags, OpProgress *Progress) {
1002 int solver_in, solver_out;
1003 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1004 if (solver_pid == 0)
1005 return false;
1006
1007 FileFd output;
1008 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1009 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
1010
1011 bool Okay = output.Failed() == false;
1012 if (Progress != NULL)
1013 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
1014 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
1015 if (Progress != NULL)
1016 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
1017 Okay &= EDSP::WriteScenario(Cache, output, Progress);
1018 output.Close();
1019
1020 if (Progress != NULL)
1021 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
1022 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
1023 return false;
1024
1025 return ExecWait(solver_pid, solver);
1026}
1027bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1028 bool const upgrade, bool const distUpgrade,
1029 bool const autoRemove, OpProgress *Progress) {
1030 unsigned int flags = 0;
1031 if (autoRemove)
1032 flags |= Request::AUTOREMOVE;
1033 if (upgrade)
1034 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1035 if (distUpgrade)
1036 flags |= Request::UPGRADE_ALL;
1037 return ResolveExternal(solver, Cache, flags, Progress);
1038}
1039 /*}}}*/