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