]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
use FindB instead of FindI for Debug::pkgAutoRemove
[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/algorithms.h>
11 #include <apt-pkg/error.h>
12 #include <apt-pkg/cacheset.h>
13 #include <apt-pkg/depcache.h>
14 #include <apt-pkg/pkgcache.h>
15 #include <apt-pkg/cacheiterators.h>
16 #include <apt-pkg/prettyprinters.h>
17 #include <apt-pkg/packagemanager.h>
18 #include <apt-pkg/progress.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/edsp.h>
21 #include <apt-pkg/tagfile.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/string_view.h>
24 #include <apt-pkg/pkgsystem.h>
25
26 #include <sys/stat.h>
27 #include <ctype.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdio.h>
32
33 #include <array>
34 #include <limits>
35 #include <string>
36
37 #include <apti18n.h>
38 /*}}}*/
39
40 using std::string;
41
42 // we could use pkgCache::DepType and ::Priority, but these would be localized strings…
43 constexpr char const * const PrioMap[] = {
44 nullptr, "important", "required", "standard",
45 "optional", "extra"
46 };
47 constexpr char const * const DepMap[] = {
48 nullptr, "Depends", "Pre-Depends", "Suggests",
49 "Recommends" , "Conflicts", "Replaces",
50 "Obsoletes", "Breaks", "Enhances"
51 };
52
53 // WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
54 static bool WriteOkay_fn(FileFd &) { return true; }
55 template<typename... Tail> static bool WriteOkay_fn(FileFd &output, APT::StringView data, Tail... more_data)
56 {
57 return likely(output.Write(data.data(), data.length()) && WriteOkay_fn(output, more_data...));
58 }
59 template<typename... Tail> static bool WriteOkay_fn(FileFd &output, unsigned int data, Tail... more_data)
60 {
61 std::string number;
62 strprintf(number, "%d", data);
63 return likely(output.Write(number.data(), number.length()) && WriteOkay_fn(output, more_data...));
64 }
65 template<typename... Data> static bool WriteOkay(bool &Okay, FileFd &output, Data&&... data)
66 {
67 Okay = likely(Okay && WriteOkay_fn(output, std::forward<Data>(data)...));
68 return Okay;
69 }
70 template<typename... Data> static bool WriteOkay(FileFd &output, Data&&... data)
71 {
72 bool Okay = likely(output.Failed() == false);
73 return WriteOkay(Okay, output, std::forward<Data>(data)...);
74 }
75 /*}}}*/
76 // WriteScenarioVersion /*{{{*/
77 static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
78 pkgCache::VerIterator const &Ver)
79 {
80 fprintf(output, "Package: %s\n", Pkg.Name());
81 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
82 fprintf(output, "Architecture: %s\n", Ver.Arch());
83 fprintf(output, "Version: %s\n", Ver.VerStr());
84 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
85 if (Pkg.CurrentVer() == Ver)
86 fprintf(output, "Installed: yes\n");
87 if (Pkg->SelectedState == pkgCache::State::Hold ||
88 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
89 fprintf(output, "Hold: yes\n");
90 fprintf(output, "APT-ID: %d\n", Ver->ID);
91 if (PrioMap[Ver->Priority] != nullptr)
92 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
93 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
94 fprintf(output, "Essential: yes\n");
95 if (Ver->Section != 0)
96 fprintf(output, "Section: %s\n", Ver.Section());
97 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
98 fprintf(output, "Multi-Arch: allowed\n");
99 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
100 fprintf(output, "Multi-Arch: foreign\n");
101 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
102 fprintf(output, "Multi-Arch: same\n");
103 std::set<string> Releases;
104 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
105 pkgCache::PkgFileIterator File = I.File();
106 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
107 string Release = File.RelStr();
108 if (!Release.empty())
109 Releases.insert(Release);
110 }
111 }
112 if (!Releases.empty()) {
113 fprintf(output, "APT-Release:\n");
114 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
115 fprintf(output, " %s\n", R->c_str());
116 }
117 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
118 if (Cache.GetCandidateVersion(Pkg) == Ver)
119 fprintf(output, "APT-Candidate: yes\n");
120 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
121 fprintf(output, "APT-Automatic: yes\n");
122 }
123 static bool WriteScenarioVersion(FileFd &output, pkgCache::PkgIterator const &Pkg,
124 pkgCache::VerIterator const &Ver)
125 {
126 bool Okay = WriteOkay(output, "Package: ", Pkg.Name(),
127 "\nArchitecture: ", Ver.Arch(),
128 "\nVersion: ", Ver.VerStr());
129 WriteOkay(Okay, output, "\nAPT-ID: ", Ver->ID);
130 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
131 WriteOkay(Okay, output, "\nEssential: yes");
132 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
133 WriteOkay(Okay, output, "\nMulti-Arch: allowed");
134 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
135 WriteOkay(Okay, output, "\nMulti-Arch: foreign");
136 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
137 WriteOkay(Okay, output, "\nMulti-Arch: same");
138 return Okay;
139 }
140 /*}}}*/
141 // WriteScenarioDependency /*{{{*/
142 static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
143 {
144 std::array<std::string, _count(DepMap)> dependencies;
145 bool orGroup = false;
146 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
147 {
148 if (Dep.IsImplicit() == true)
149 continue;
150 if (orGroup == false)
151 dependencies[Dep->Type].append(", ");
152 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
153 if (Dep->Version != 0)
154 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
155 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
156 {
157 dependencies[Dep->Type].append(" | ");
158 orGroup = true;
159 }
160 else
161 orGroup = false;
162 }
163 for (size_t i = 1; i < dependencies.size(); ++i)
164 if (dependencies[i].empty() == false)
165 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
166 string provides;
167 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
168 {
169 if (Prv.IsMultiArchImplicit() == true)
170 continue;
171 if (provides.empty() == false)
172 provides.append(", ");
173 provides.append(Prv.Name());
174 if (Prv->ProvideVersion != 0)
175 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
176 }
177 if (provides.empty() == false)
178 fprintf(output, "Provides: %s\n", provides.c_str());
179 }
180 static bool WriteScenarioDependency(FileFd &output, pkgCache::VerIterator const &Ver, bool const OnlyCritical)
181 {
182 std::array<std::string, _count(DepMap)> dependencies;
183 bool orGroup = false;
184 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
185 {
186 if (Dep.IsImplicit() == true)
187 continue;
188 if (OnlyCritical && Dep.IsCritical() == false)
189 continue;
190 if (orGroup == false && dependencies[Dep->Type].empty() == false)
191 dependencies[Dep->Type].append(", ");
192 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
193 if (Dep->Version != 0)
194 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
195 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
196 {
197 dependencies[Dep->Type].append(" | ");
198 orGroup = true;
199 }
200 else
201 orGroup = false;
202 }
203 bool Okay = output.Failed() == false;
204 for (size_t i = 1; i < dependencies.size(); ++i)
205 if (dependencies[i].empty() == false)
206 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
207 string provides;
208 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
209 {
210 if (Prv.IsMultiArchImplicit() == true)
211 continue;
212 if (provides.empty() == false)
213 provides.append(", ");
214 provides.append(Prv.Name());
215 if (Prv->ProvideVersion != 0)
216 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
217 }
218 if (provides.empty() == false)
219 WriteOkay(Okay, output, "\nProvides: ", provides);
220 return WriteOkay(Okay, output, "\n");
221 }
222 /*}}}*/
223 // WriteScenarioLimitedDependency /*{{{*/
224 static void WriteScenarioLimitedDependency(FILE* output,
225 pkgCache::VerIterator const &Ver,
226 APT::PackageSet const &pkgset)
227 {
228 std::array<std::string, _count(DepMap)> dependencies;
229 bool orGroup = false;
230 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
231 {
232 if (Dep.IsImplicit() == true)
233 continue;
234 if (orGroup == false)
235 {
236 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
237 continue;
238 if (dependencies[Dep->Type].empty() == false)
239 dependencies[Dep->Type].append(", ");
240 }
241 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
242 {
243 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
244 continue;
245 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
246 orGroup = false;
247 continue;
248 }
249 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
250 if (Dep->Version != 0)
251 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
252 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
253 {
254 dependencies[Dep->Type].append(" | ");
255 orGroup = true;
256 }
257 else
258 orGroup = false;
259 }
260 for (size_t i = 1; i < dependencies.size(); ++i)
261 if (dependencies[i].empty() == false)
262 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str());
263 string provides;
264 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
265 {
266 if (Prv.IsMultiArchImplicit() == true)
267 continue;
268 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
269 continue;
270 if (provides.empty() == false)
271 provides.append(", ");
272 provides.append(Prv.Name());
273 if (Prv->ProvideVersion != 0)
274 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
275 }
276 if (provides.empty() == false)
277 fprintf(output, "Provides: %s\n", provides.c_str());
278 }
279 static bool WriteScenarioLimitedDependency(FileFd &output,
280 pkgCache::VerIterator const &Ver,
281 std::vector<bool> const &pkgset,
282 bool const OnlyCritical)
283 {
284 std::array<std::string, _count(DepMap)> dependencies;
285 bool orGroup = false;
286 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
287 {
288 if (Dep.IsImplicit() == true)
289 continue;
290 if (OnlyCritical && Dep.IsCritical() == false)
291 continue;
292 if (orGroup == false)
293 {
294 if (pkgset[Dep.TargetPkg()->ID] == false)
295 continue;
296 if (dependencies[Dep->Type].empty() == false)
297 dependencies[Dep->Type].append(", ");
298 }
299 else if (pkgset[Dep.TargetPkg()->ID] == false)
300 {
301 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
302 continue;
303 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
304 orGroup = false;
305 continue;
306 }
307 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
308 if (Dep->Version != 0)
309 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
310 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
311 {
312 dependencies[Dep->Type].append(" | ");
313 orGroup = true;
314 }
315 else
316 orGroup = false;
317 }
318 bool Okay = output.Failed() == false;
319 for (size_t i = 1; i < dependencies.size(); ++i)
320 if (dependencies[i].empty() == false)
321 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
322 string provides;
323 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
324 {
325 if (Prv.IsMultiArchImplicit() == true)
326 continue;
327 if (pkgset[Prv.ParentPkg()->ID] == false)
328 continue;
329 if (provides.empty() == false)
330 provides.append(", ");
331 provides.append(Prv.Name());
332 if (Prv->ProvideVersion != 0)
333 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
334 }
335 if (provides.empty() == false)
336 WriteOkay(Okay, output, "\nProvides: ", provides);
337 return WriteOkay(Okay, output, "\n");
338 }
339 /*}}}*/
340 static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
341 {
342 /* versions which aren't current and aren't available in
343 any "online" source file are bad, expect if they are the chosen
344 candidate: The exception is for build-dep implementation as it creates
345 such pseudo (package) versions and removes them later on again.
346 We filter out versions at all so packages in 'rc' state only available
347 in dpkg/status aren't passed to solvers as they can't be installed. */
348 if (Pkg->CurrentVer != 0)
349 return false;
350 if (Cache.GetCandidateVersion(Pkg) == Ver)
351 return false;
352 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
353 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
354 return false;
355 return true;
356 }
357 /*}}}*/
358 static bool WriteScenarioEDSPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
359 pkgCache::VerIterator const &Ver)
360 {
361 bool Okay = WriteOkay(output, "\nSource: ", Ver.SourcePkgName(),
362 "\nSource-Version: ", Ver.SourceVerStr());
363 if (PrioMap[Ver->Priority] != nullptr)
364 WriteOkay(Okay, output, "\nPriority: ", PrioMap[Ver->Priority]);
365 if (Ver->Section != 0)
366 WriteOkay(Okay, output, "\nSection: ", Ver.Section());
367 if (Pkg.CurrentVer() == Ver)
368 WriteOkay(Okay, output, "\nInstalled: yes");
369 if (Pkg->SelectedState == pkgCache::State::Hold ||
370 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
371 WriteOkay(Okay, output, "\nHold: yes");
372 std::set<string> Releases;
373 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
374 pkgCache::PkgFileIterator File = I.File();
375 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
376 string Release = File.RelStr();
377 if (!Release.empty())
378 Releases.insert(Release);
379 }
380 }
381 if (!Releases.empty()) {
382 WriteOkay(Okay, output, "\nAPT-Release:");
383 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
384 WriteOkay(Okay, output, "\n ", *R);
385 }
386 WriteOkay(Okay, output, "\nAPT-Pin: ", Cache.GetPolicy().GetPriority(Ver));
387 if (Cache.GetCandidateVersion(Pkg) == Ver)
388 WriteOkay(Okay, output, "\nAPT-Candidate: yes");
389 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
390 WriteOkay(Okay, output, "\nAPT-Automatic: yes");
391 return Okay;
392 }
393 /*}}}*/
394 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
395 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
396 {
397 if (Progress != NULL)
398 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
399 unsigned long p = 0;
400 std::vector<std::string> archs = APT::Configuration::getArchitectures();
401 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
402 {
403 std::string const arch = Pkg.Arch();
404 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
405 continue;
406 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
407 {
408 if (SkipUnavailableVersions(Cache, Pkg, Ver))
409 continue;
410 WriteScenarioVersion(Cache, output, Pkg, Ver);
411 WriteScenarioDependency(output, Ver);
412 fprintf(output, "\n");
413 if (Progress != NULL && p % 100 == 0)
414 Progress->Progress(p);
415 }
416 }
417 return true;
418 }
419 bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progress)
420 {
421 if (Progress != NULL)
422 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
423 unsigned long p = 0;
424 bool Okay = output.Failed() == false;
425 std::vector<std::string> archs = APT::Configuration::getArchitectures();
426 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
427 {
428 std::string const arch = Pkg.Arch();
429 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
430 continue;
431 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p)
432 {
433 if (SkipUnavailableVersions(Cache, Pkg, Ver))
434 continue;
435 Okay &= WriteScenarioVersion(output, Pkg, Ver);
436 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
437 Okay &= WriteScenarioDependency(output, Ver, false);
438 WriteOkay(Okay, output, "\n");
439 if (Progress != NULL && p % 100 == 0)
440 Progress->Progress(p);
441 }
442 }
443 return Okay;
444 }
445 /*}}}*/
446 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
447 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
448 APT::PackageSet const &pkgset,
449 OpProgress *Progress)
450 {
451 if (Progress != NULL)
452 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
453 unsigned long p = 0;
454 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
455 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
456 {
457 if (SkipUnavailableVersions(Cache, Pkg, Ver))
458 continue;
459 WriteScenarioVersion(Cache, output, Pkg, Ver);
460 WriteScenarioLimitedDependency(output, Ver, pkgset);
461 fprintf(output, "\n");
462 if (Progress != NULL && p % 100 == 0)
463 Progress->Progress(p);
464 }
465 if (Progress != NULL)
466 Progress->Done();
467 return true;
468 }
469 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FileFd &output,
470 std::vector<bool> const &pkgset,
471 OpProgress *Progress)
472 {
473 if (Progress != NULL)
474 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
475 unsigned long p = 0;
476 bool Okay = output.Failed() == false;
477 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
478 {
479 if (pkgset[Pkg->ID] == false)
480 continue;
481 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
482 {
483 if (SkipUnavailableVersions(Cache, Pkg, Ver))
484 continue;
485 Okay &= WriteScenarioVersion(output, Pkg, Ver);
486 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
487 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, false);
488 WriteOkay(Okay, output, "\n");
489 if (Progress != NULL && p % 100 == 0)
490 Progress->Progress(p);
491 }
492 }
493 if (Progress != NULL)
494 Progress->Done();
495 return Okay;
496 }
497 /*}}}*/
498 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
499 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
500 bool const DistUpgrade, bool const AutoRemove,
501 OpProgress *Progress)
502 {
503 if (Progress != NULL)
504 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
505 unsigned long p = 0;
506 string del, inst;
507 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
508 {
509 if (Progress != NULL && p % 100 == 0)
510 Progress->Progress(p);
511 string* req;
512 pkgDepCache::StateCache &P = Cache[Pkg];
513 if (P.Delete() == true)
514 req = &del;
515 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
516 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
517 req = &inst;
518 else
519 continue;
520 req->append(" ").append(Pkg.FullName());
521 }
522 fprintf(output, "Request: EDSP 0.5\n");
523
524 const char *arch = _config->Find("APT::Architecture").c_str();
525 std::vector<string> archs = APT::Configuration::getArchitectures();
526 fprintf(output, "Architecture: %s\n", arch);
527 fprintf(output, "Architectures:");
528 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
529 fprintf(output, " %s", a->c_str());
530 fprintf(output, "\n");
531
532 if (del.empty() == false)
533 fprintf(output, "Remove: %s\n", del.c_str()+1);
534 if (inst.empty() == false)
535 fprintf(output, "Install: %s\n", inst.c_str()+1);
536 if (Upgrade == true)
537 fprintf(output, "Upgrade: yes\n");
538 if (DistUpgrade == true)
539 fprintf(output, "Dist-Upgrade: yes\n");
540 if (AutoRemove == true)
541 fprintf(output, "Autoremove: yes\n");
542 auto const solver = _config->Find("APT::Solver", "internal");
543 fprintf(output, "Solver: %s\n", solver.c_str());
544 auto const solverconf = std::string("APT::Solver::") + solver + "::";
545 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
546 fprintf(output, "Strict-Pinning: no\n");
547 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
548 if (solverpref.empty() == false)
549 fprintf(output, "Preferences: %s\n", solverpref.c_str());
550 fprintf(output, "\n");
551 return true;
552 }
553 bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
554 unsigned int const flags,
555 OpProgress *Progress)
556 {
557 if (Progress != NULL)
558 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
559 unsigned long p = 0;
560 string del, inst;
561 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
562 {
563 if (Progress != NULL && p % 100 == 0)
564 Progress->Progress(p);
565 string* req;
566 pkgDepCache::StateCache &P = Cache[Pkg];
567 if (P.Delete() == true)
568 req = &del;
569 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
570 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
571 req = &inst;
572 else
573 continue;
574 req->append(" ").append(Pkg.FullName());
575 }
576 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
577
578 const char *arch = _config->Find("APT::Architecture").c_str();
579 std::vector<string> archs = APT::Configuration::getArchitectures();
580 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
581 "Architectures:");
582 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
583 WriteOkay(Okay, output, " ", *a);
584 WriteOkay(Okay, output, "\n");
585
586 if (del.empty() == false)
587 WriteOkay(Okay, output, "Remove:", del, "\n");
588 if (inst.empty() == false)
589 WriteOkay(Okay, output, "Install:", inst, "\n");
590 if (flags & Request::AUTOREMOVE)
591 WriteOkay(Okay, output, "Autoremove: yes\n");
592 if (flags & Request::UPGRADE_ALL)
593 {
594 WriteOkay(Okay, output, "Upgrade-All: yes\n");
595 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
596 WriteOkay(Okay, output, "Upgrade: yes\n");
597 else
598 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
599 }
600 if (flags & Request::FORBID_NEW_INSTALL)
601 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
602 if (flags & Request::FORBID_REMOVE)
603 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
604 auto const solver = _config->Find("APT::Solver", "internal");
605 WriteOkay(Okay, output, "Solver: ", solver, "\n");
606 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
607 WriteOkay(Okay, output, "Strict-Pinning: no\n");
608 string solverpref("APT::Solver::");
609 solverpref.append(solver).append("::Preferences");
610 if (_config->Exists(solverpref) == true)
611 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
612 return WriteOkay(Okay, output, "\n");
613 }
614 /*}}}*/
615 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
616 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
617 /* We build an map id to mmap offset here
618 In theory we could use the offset as ID, but then VersionCount
619 couldn't be used to create other versionmappings anymore and it
620 would be too easy for a (buggy) solver to segfault APT… */
621 unsigned long long const VersionCount = Cache.Head().VersionCount;
622 unsigned long VerIdx[VersionCount];
623 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
624 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
625 VerIdx[V->ID] = V.Index();
626 Cache[P].Marked = true;
627 Cache[P].Garbage = false;
628 }
629
630 FileFd in;
631 in.OpenDescriptor(input, FileFd::ReadOnly, true);
632 pkgTagFile response(&in, 100);
633 pkgTagSection section;
634
635 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
636 while (response.Step(section) == true) {
637 std::string type;
638 if (section.Exists("Install") == true)
639 type = "Install";
640 else if (section.Exists("Remove") == true)
641 type = "Remove";
642 else if (section.Exists("Progress") == true) {
643 if (Progress != NULL) {
644 string msg = section.FindS("Message");
645 if (msg.empty() == true)
646 msg = _("Prepare for receiving solution");
647 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
648 }
649 continue;
650 } else if (section.Exists("Error") == true) {
651 if (_error->PendingError()) {
652 if (Progress != nullptr)
653 Progress->Done();
654 Progress = nullptr;
655 _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
656 }
657 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
658 if (msg.empty() == true) {
659 msg = _("External solver failed without a proper error message");
660 _error->Error("%s", msg.c_str());
661 } else
662 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
663 if (Progress != nullptr)
664 Progress->Done();
665 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
666 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
667 std::cerr << msg << std::endl << std::endl;
668 return false;
669 } else if (section.Exists("Autoremove") == true)
670 type = "Autoremove";
671 else {
672 char const *Start, *End;
673 section.GetSection(Start, End);
674 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
675 continue;
676 }
677
678 size_t const id = section.FindULL(type.c_str(), VersionCount);
679 if (id == VersionCount) {
680 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
681 continue;
682 } else if (id > Cache.Head().VersionCount) {
683 _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());
684 continue;
685 }
686
687 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
688 auto const Pkg = Ver.ParentPkg();
689 if (type == "Autoremove") {
690 Cache[Pkg].Marked = false;
691 Cache[Pkg].Garbage = true;
692 } else if (seenOnce.emplace(Pkg->ID).second == false) {
693 _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());
694 } else if (type == "Install") {
695 if (Pkg.CurrentVer() == Ver) {
696 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
697 Ver.VerStr(), Pkg.FullName(false).c_str());
698 } else {
699 Cache.SetCandidateVersion(Ver);
700 Cache.MarkInstall(Pkg, false, 0, false);
701 }
702 } else if (type == "Remove") {
703 if (Pkg->CurrentVer == 0)
704 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
705 Ver.VerStr(), Pkg.FullName(false).c_str());
706 else if (Pkg.CurrentVer() != Ver)
707 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
708 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
709 else
710 Cache.MarkDelete(Ver.ParentPkg(), false);
711 }
712 }
713 return true;
714 }
715 /*}}}*/
716 // ReadLine - first line from the given file descriptor /*{{{*/
717 // ---------------------------------------------------------------------
718 /* Little helper method to read a complete line into a string. Similar to
719 fgets but we need to use the low-level read() here as otherwise the
720 listparser will be confused later on as mixing of fgets and read isn't
721 a supported action according to the manpages and results are undefined */
722 static bool ReadLine(int const input, std::string &line) {
723 char one;
724 ssize_t data = 0;
725 line.erase();
726 line.reserve(100);
727 while ((data = read(input, &one, sizeof(one))) != -1) {
728 if (data != 1)
729 continue;
730 if (one == '\n')
731 return true;
732 if (one == '\r')
733 continue;
734 if (line.empty() == true && isblank(one) != 0)
735 continue;
736 line += one;
737 }
738 return false;
739 }
740 /*}}}*/
741 // StringToBool - convert yes/no to bool /*{{{*/
742 // ---------------------------------------------------------------------
743 /* we are not as lazy as we are in the global StringToBool as we really
744 only accept yes/no here */
745 static bool localStringToBool(std::string answer, bool const defValue) {
746 std::transform(answer.begin(), answer.end(), answer.begin(), ::tolower);
747 if (answer == "yes")
748 return true;
749 else if (answer == "no")
750 return false;
751 else
752 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer.c_str());
753 return defValue;
754 }
755 /*}}}*/
756 static bool LineStartsWithAndStrip(std::string &line, APT::StringView const with)/*{{{*/
757 {
758 if (line.compare(0, with.size(), with.data()) != 0)
759 return false;
760 line = APT::String::Strip(line.substr(with.length()));
761 return true;
762 }
763 /*}}}*/
764 static bool ReadFlag(unsigned int &flags, std::string &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
765 {
766 if (LineStartsWithAndStrip(line, name) == false)
767 return false;
768 if (localStringToBool(line, false))
769 flags |= setflag;
770 else
771 flags &= ~setflag;
772 return true;
773 }
774 /*}}}*/
775 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
776 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
777 std::list<std::string> &remove, unsigned int &flags)
778 {
779 install.clear();
780 remove.clear();
781 flags = 0;
782 std::string line;
783 while (ReadLine(input, line) == true)
784 {
785 // Skip empty lines before request
786 if (line.empty() == true)
787 continue;
788 // The first Tag must be a request, so search for it
789 if (LineStartsWithAndStrip(line, "Request:"))
790 continue;
791
792 while (ReadLine(input, line) == true)
793 {
794 // empty lines are the end of the request
795 if (line.empty() == true)
796 return true;
797
798 std::list<std::string> *request = NULL;
799 if (LineStartsWithAndStrip(line, "Install:"))
800 request = &install;
801 else if (LineStartsWithAndStrip(line, "Remove:"))
802 request = &remove;
803 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
804 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
805 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
806 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
807 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
808 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
809 ;
810 else if (LineStartsWithAndStrip(line, "Architecture:"))
811 _config->Set("APT::Architecture", line);
812 else if (LineStartsWithAndStrip(line, "Architectures:"))
813 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
814 else if (LineStartsWithAndStrip(line, "Solver:"))
815 ; // purely informational line
816 else
817 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
818
819 if (request == NULL)
820 continue;
821 auto const pkgs = VectorizeString(line, ' ');
822 std::move(pkgs.begin(), pkgs.end(), std::back_inserter(*request));
823 }
824 }
825 return false;
826 }
827 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
828 std::list<std::string> &remove, bool &upgrade,
829 bool &distUpgrade, bool &autoRemove)
830 {
831 unsigned int flags;
832 auto const ret = ReadRequest(input, install, remove, flags);
833 autoRemove = (flags & Request::AUTOREMOVE);
834 if (flags & Request::UPGRADE_ALL)
835 {
836 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
837 {
838 upgrade = true;
839 distUpgrade = false;
840 } else {
841 upgrade = false;
842 distUpgrade = false;
843 }
844 }
845 else
846 {
847 upgrade = false;
848 distUpgrade = false;
849 }
850 return ret;
851 }
852 /*}}}*/
853 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
854 bool EDSP::ApplyRequest(std::list<std::string> const &install,
855 std::list<std::string> const &remove,
856 pkgDepCache &Cache)
857 {
858 for (std::list<std::string>::const_iterator i = install.begin();
859 i != install.end(); ++i) {
860 pkgCache::PkgIterator P = Cache.FindPkg(*i);
861 if (P.end() == true)
862 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
863 else
864 Cache.MarkInstall(P, false);
865 }
866
867 for (std::list<std::string>::const_iterator i = remove.begin();
868 i != remove.end(); ++i) {
869 pkgCache::PkgIterator P = Cache.FindPkg(*i);
870 if (P.end() == true)
871 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
872 else
873 Cache.MarkDelete(P);
874 }
875 return true;
876 }
877 /*}}}*/
878 // EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
879 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
880 {
881 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
882 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
883 {
884 if (Cache[Pkg].Delete() == true)
885 {
886 fprintf(output, "Remove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
887 if (Debug == true)
888 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
889 }
890 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
891 {
892 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
893 fprintf(output, "Install: %d\n", _system->GetVersionMapping(CandVer->ID));
894 if (Debug == true)
895 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
896 }
897 else if (Cache[Pkg].Garbage == true)
898 {
899 fprintf(output, "Autoremove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
900 if (Debug == true)
901 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
902 }
903 else
904 continue;
905 fprintf(output, "\n");
906 }
907
908 return true;
909 }
910 bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache::VerIterator const &Ver)
911 {
912 bool Okay = output.Failed() == false;
913 WriteOkay(Okay, output, Type, ": ", _system->GetVersionMapping(Ver->ID));
914 if (_config->FindB("Debug::EDSP::WriteSolution", false) == true)
915 WriteOkay(Okay, output, "\nPackage: ", Ver.ParentPkg().FullName(), "\nVersion: ", Ver.VerStr());
916 return WriteOkay(Okay, output, "\n\n");
917 }
918 /*}}}*/
919 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
920 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
921 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL), true).c_str());
922 fprintf(output, "Percentage: %d\n", percent);
923 fprintf(output, "Message: %s\n\n", message);
924 fflush(output);
925 return true;
926 }
927 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
928 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL), true), "\n",
929 "Percentage: ", percent, "\n",
930 "Message: ", message, "\n\n") && output.Flush();
931 }
932 /*}}}*/
933 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
934 static std::string formatMessage(std::string const &msg)
935 {
936 return SubstVar(SubstVar(APT::String::Strip(msg), "\n\n", "\n.\n"), "\n", "\n ");
937 }
938 bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
939 fprintf(output, "Error: %s\n", uuid);
940 fprintf(output, "Message: %s\n\n", formatMessage(message).c_str());
941 return true;
942 }
943 bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
944 return WriteOkay(output, "Error: ", uuid, "\n",
945 "Message: ", formatMessage(message),
946 "\n\n");
947 }
948 /*}}}*/
949 static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/
950 for (auto && dir : dirs) {
951 std::string const file = flCombine(dir, binary);
952 if (RealFileExists(file) == true)
953 return file;
954 }
955 return "";
956 }
957 /*}}}*/
958 static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/
959 auto const solverDirs = _config->FindVector(configdir);
960 auto const file = findExecutable(solverDirs, binary);
961 std::string dumper;
962 {
963 dumper = findExecutable(solverDirs, "apt-dump-solver");
964 if (dumper.empty())
965 dumper = findExecutable(solverDirs, "dump");
966 }
967
968 if (file.empty() == true)
969 {
970 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
971 return 0;
972 }
973 int external[4] = {-1, -1, -1, -1};
974 if (pipe(external) != 0 || pipe(external + 2) != 0)
975 {
976 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
977 return 0;
978 }
979 for (int i = 0; i < 4; ++i)
980 SetCloseExec(external[i], true);
981
982 pid_t Solver = ExecFork();
983 if (Solver == 0) {
984 dup2(external[0], STDIN_FILENO);
985 dup2(external[3], STDOUT_FILENO);
986 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
987 auto const dumpdir = flNotFile(dumpfile);
988 auto const runasuser = _config->Find(std::string("APT::") + type + "::" + binary + "::RunAsUser",
989 _config->Find(std::string("APT::") + type + "::RunAsUser",
990 _config->Find("APT::Sandbox::User")));
991 if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false)
992 {
993 _config->Set("APT::Sandbox::User", runasuser);
994 DropPrivileges();
995 char const * const calling[] = { file.c_str(), nullptr };
996 execv(calling[0], const_cast<char**>(calling));
997 }
998 else
999 {
1000 char const * const calling[] = { dumper.c_str(), "--user", runasuser.c_str(), dumpfile.c_str(), file.c_str(), nullptr };
1001 execv(calling[0], const_cast<char**>(calling));
1002 }
1003 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
1004 _exit(100);
1005 }
1006 close(external[0]);
1007 close(external[3]);
1008
1009 if (WaitFd(external[1], true, 5) == false)
1010 {
1011 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
1012 return 0;
1013 }
1014
1015 *solver_in = external[1];
1016 *solver_out = external[2];
1017 return Solver;
1018 }
1019 /*}}}*/
1020 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1021 pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
1022 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
1023 }
1024 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
1025 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
1026 return false;
1027 return true;
1028 }
1029 /*}}}*/
1030 static bool CreateDumpFile(char const * const id, char const * const type, FileFd &output)/*{{{*/
1031 {
1032 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
1033 if (dumpfile.empty())
1034 return false;
1035 auto const dumpdir = flNotFile(dumpfile);
1036 _error->PushToStack();
1037 bool errored_out = CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
1038 output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false;
1039 std::vector<std::string> downgrademsgs;
1040 while (_error->empty() == false)
1041 {
1042 std::string msg;
1043 _error->PopMessage(msg);
1044 downgrademsgs.emplace_back(std::move(msg));
1045 }
1046 _error->RevertToStack();
1047 for (auto && msg : downgrademsgs)
1048 _error->Warning("%s", msg.c_str());
1049 if (errored_out)
1050 return _error->WarningE(id, _("Could not open file '%s'"), dumpfile.c_str());
1051 return true;
1052 }
1053 /*}}}*/
1054 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1055 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1056 unsigned int const flags, OpProgress *Progress) {
1057 if (strcmp(solver, "internal") == 0)
1058 {
1059 FileFd output;
1060 bool Okay = CreateDumpFile("EDSP::Resolve", "solver", output);
1061 Okay &= EDSP::WriteRequest(Cache, output, flags, nullptr);
1062 return Okay && EDSP::WriteScenario(Cache, output, nullptr);
1063 }
1064 _error->PushToStack();
1065 int solver_in, solver_out;
1066 pid_t const solver_pid = ExecuteSolver(solver, &solver_in, &solver_out, true);
1067 if (solver_pid == 0)
1068 return false;
1069
1070 FileFd output;
1071 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1072 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
1073
1074 bool Okay = output.Failed() == false;
1075 if (Okay && Progress != NULL)
1076 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
1077 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
1078 if (Okay && Progress != NULL)
1079 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
1080 Okay &= EDSP::WriteScenario(Cache, output, Progress);
1081 output.Close();
1082
1083 if (Okay && Progress != NULL)
1084 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
1085 bool const ret = EDSP::ReadResponse(solver_out, Cache, Progress);
1086 _error->MergeWithStack();
1087 if (ExecWait(solver_pid, solver))
1088 return ret;
1089 return false;
1090 }
1091 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1092 bool const upgrade, bool const distUpgrade,
1093 bool const autoRemove, OpProgress *Progress) {
1094 unsigned int flags = 0;
1095 if (autoRemove)
1096 flags |= Request::AUTOREMOVE;
1097 if (upgrade)
1098 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1099 if (distUpgrade)
1100 flags |= Request::UPGRADE_ALL;
1101 return ResolveExternal(solver, Cache, flags, Progress);
1102 }
1103 /*}}}*/
1104
1105 bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM, /*{{{*/
1106 unsigned int const flags, OpProgress * const Progress)
1107 {
1108 if (strcmp(solver, "internal") == 0)
1109 {
1110 FileFd output;
1111 _error->PushToStack();
1112 bool Okay = CreateDumpFile("EIPP::OrderInstall", "planner", output);
1113 if (Okay == false && dynamic_cast<pkgSimulate*>(PM) != nullptr)
1114 {
1115 _error->RevertToStack();
1116 return false;
1117 }
1118 _error->MergeWithStack();
1119 Okay &= EIPP::WriteRequest(PM->Cache, output, flags, nullptr);
1120 return Okay && EIPP::WriteScenario(PM->Cache, output, nullptr);
1121 }
1122 _error->PushToStack();
1123 int solver_in, solver_out;
1124 pid_t const solver_pid = ExecuteExternal("planner", solver, "Dir::Bin::Planners", &solver_in, &solver_out);
1125 if (solver_pid == 0)
1126 return false;
1127
1128 FileFd output;
1129 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1130 return _error->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver, solver_in);
1131
1132 bool Okay = output.Failed() == false;
1133 if (Okay && Progress != NULL)
1134 Progress->OverallProgress(0, 100, 5, _("Execute external planner"));
1135 Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress);
1136 if (Okay && Progress != NULL)
1137 Progress->OverallProgress(5, 100, 20, _("Execute external planner"));
1138 Okay &= EIPP::WriteScenario(PM->Cache, output, Progress);
1139 output.Close();
1140
1141 if (Okay)
1142 {
1143 if (Progress != nullptr)
1144 Progress->OverallProgress(25, 100, 75, _("Execute external planner"));
1145
1146 // we don't tell the external planners about boring things
1147 for (auto Pkg = PM->Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1148 {
1149 if (Pkg->CurrentState == pkgCache::State::ConfigFiles && PM->Cache[Pkg].Purge() == true)
1150 PM->Remove(Pkg, true);
1151 }
1152 }
1153 bool const ret = EIPP::ReadResponse(solver_out, PM, Progress);
1154 _error->MergeWithStack();
1155 if (ExecWait(solver_pid, solver))
1156 return ret;
1157 return false;
1158 }
1159 /*}}}*/
1160 bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/
1161 unsigned int const flags,
1162 OpProgress * const Progress)
1163 {
1164 if (Progress != NULL)
1165 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to planner"));
1166 unsigned long p = 0;
1167 string del, inst, reinst;
1168 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
1169 {
1170 if (Progress != NULL && p % 100 == 0)
1171 Progress->Progress(p);
1172 string* req;
1173 pkgDepCache::StateCache &P = Cache[Pkg];
1174 if (P.Purge() == true && Pkg->CurrentState == pkgCache::State::ConfigFiles)
1175 continue;
1176 if (P.Delete() == true)
1177 req = &del;
1178 else if (P.NewInstall() == true || P.Upgrade() == true || P.Downgrade() == true)
1179 req = &inst;
1180 else if (P.ReInstall() == true)
1181 req = &reinst;
1182 else
1183 continue;
1184 req->append(" ").append(Pkg.FullName());
1185 }
1186 bool Okay = WriteOkay(output, "Request: EIPP 0.1\n");
1187
1188 const char *arch = _config->Find("APT::Architecture").c_str();
1189 std::vector<string> archs = APT::Configuration::getArchitectures();
1190 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
1191 "Architectures:");
1192 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
1193 WriteOkay(Okay, output, " ", *a);
1194 WriteOkay(Okay, output, "\n");
1195
1196 if (del.empty() == false)
1197 WriteOkay(Okay, output, "Remove:", del, "\n");
1198 if (inst.empty() == false)
1199 WriteOkay(Okay, output, "Install:", inst, "\n");
1200 if (reinst.empty() == false)
1201 WriteOkay(Okay, output, "ReInstall:", reinst, "\n");
1202 WriteOkay(Okay, output, "Planner: ", _config->Find("APT::Planner", "internal"), "\n");
1203 if ((flags & Request::IMMEDIATE_CONFIGURATION_ALL) != 0)
1204 WriteOkay(Okay, output, "Immediate-Configuration: yes\n");
1205 else if ((flags & Request::NO_IMMEDIATE_CONFIGURATION) != 0)
1206 WriteOkay(Okay, output, "Immediate-Configuration: no\n");
1207 else if ((flags & Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0)
1208 WriteOkay(Okay, output, "Allow-Temporary-Remove-of-Essentials: yes\n");
1209 return WriteOkay(Okay, output, "\n");
1210 }
1211 /*}}}*/
1212 static bool WriteScenarioEIPPVersion(pkgDepCache &, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
1213 pkgCache::VerIterator const &Ver)
1214 {
1215 bool Okay = true;
1216 if (Pkg.CurrentVer() == Ver)
1217 switch (Pkg->CurrentState)
1218 {
1219 case pkgCache::State::NotInstalled: WriteOkay(Okay, output, "\nStatus: not-installed"); break;
1220 case pkgCache::State::ConfigFiles: WriteOkay(Okay, output, "\nStatus: config-files"); break;
1221 case pkgCache::State::HalfInstalled: WriteOkay(Okay, output, "\nStatus: half-installed"); break;
1222 case pkgCache::State::UnPacked: WriteOkay(Okay, output, "\nStatus: unpacked"); break;
1223 case pkgCache::State::HalfConfigured: WriteOkay(Okay, output, "\nStatus: half-configured"); break;
1224 case pkgCache::State::TriggersAwaited: WriteOkay(Okay, output, "\nStatus: triggers-awaited"); break;
1225 case pkgCache::State::TriggersPending: WriteOkay(Okay, output, "\nStatus: triggers-pending"); break;
1226 case pkgCache::State::Installed: WriteOkay(Okay, output, "\nStatus: installed"); break;
1227 }
1228 return Okay;
1229 }
1230 /*}}}*/
1231 // EIPP::WriteScenario - to the given file descriptor /*{{{*/
1232 template<typename forVersion> void forAllInterestingVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, forVersion const &func)
1233 {
1234 if (Pkg->CurrentState == pkgCache::State::NotInstalled)
1235 {
1236 auto P = Cache[Pkg];
1237 if (P.Install() == false)
1238 return;
1239 func(Pkg, P.InstVerIter(Cache));
1240 }
1241 else
1242 {
1243 if (Pkg->CurrentVer != 0)
1244 func(Pkg, Pkg.CurrentVer());
1245 auto P = Cache[Pkg];
1246 auto const V = P.InstVerIter(Cache);
1247 if (P.Delete() == false && Pkg.CurrentVer() != V)
1248 func(Pkg, V);
1249 }
1250 }
1251
1252 bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress)
1253 {
1254 if (Progress != NULL)
1255 Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planner"));
1256 unsigned long p = 0;
1257 bool Okay = output.Failed() == false;
1258 std::vector<std::string> archs = APT::Configuration::getArchitectures();
1259 std::vector<bool> pkgset(Cache.Head().PackageCount, false);
1260 auto const MarkVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1261 pkgset[Pkg->ID] = true;
1262 for (auto D = Ver.DependsList(); D.end() == false; ++D)
1263 {
1264 if (D.IsCritical() == false)
1265 continue;
1266 auto const P = D.TargetPkg();
1267 for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
1268 {
1269 auto const V = Prv.OwnerVer();
1270 auto const PV = V.ParentPkg();
1271 if (V == PV.CurrentVer() || V == Cache[PV].InstVerIter(Cache))
1272 pkgset[PV->ID] = true;
1273 }
1274 pkgset[P->ID] = true;
1275 if (strcmp(P.Arch(), "any") == 0)
1276 {
1277 APT::StringView const pkgname(P.Name());
1278 auto const idxColon = pkgname.find(':');
1279 if (idxColon != APT::StringView::npos)
1280 {
1281 pkgCache::PkgIterator PA;
1282 if (pkgname.substr(idxColon + 1) == "any")
1283 {
1284 auto const GA = Cache.FindGrp(pkgname.substr(0, idxColon).to_string());
1285 for (auto PA = GA.PackageList(); PA.end() == false; PA = GA.NextPkg(PA))
1286 {
1287 pkgset[PA->ID] = true;
1288 }
1289 }
1290 else
1291 {
1292 auto const PA = Cache.FindPkg(pkgname.to_string());
1293 if (PA.end() == false)
1294 pkgset[PA->ID] = true;
1295 }
1296 }
1297 }
1298 else
1299 {
1300 auto const PA = Cache.FindPkg(P.FullName(false), "any");
1301 if (PA.end() == false)
1302 pkgset[PA->ID] = true;
1303 }
1304 }
1305 };
1306 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1307 forAllInterestingVersions(Cache, Pkg, MarkVersion);
1308 auto const WriteVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1309 Okay &= WriteScenarioVersion(output, Pkg, Ver);
1310 Okay &= WriteScenarioEIPPVersion(Cache, output, Pkg, Ver);
1311 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, true);
1312 WriteOkay(Okay, output, "\n");
1313 if (Progress != NULL && p % 100 == 0)
1314 Progress->Progress(p);
1315 };
1316 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
1317 {
1318 if (pkgset[Pkg->ID] == false || Pkg->VersionList == 0)
1319 continue;
1320 forAllInterestingVersions(Cache, Pkg, WriteVersion);
1321 }
1322 return Okay;
1323 }
1324 /*}}}*/
1325 // EIPP::ReadResponse - from the given file descriptor /*{{{*/
1326 bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgress *Progress) {
1327 /* We build an map id to mmap offset here
1328 In theory we could use the offset as ID, but then VersionCount
1329 couldn't be used to create other versionmappings anymore and it
1330 would be too easy for a (buggy) solver to segfault APT… */
1331 unsigned long long const VersionCount = PM->Cache.Head().VersionCount;
1332 unsigned long VerIdx[VersionCount];
1333 for (pkgCache::PkgIterator P = PM->Cache.PkgBegin(); P.end() == false; ++P) {
1334 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
1335 VerIdx[V->ID] = V.Index();
1336 }
1337
1338 FileFd in;
1339 in.OpenDescriptor(input, FileFd::ReadOnly);
1340 pkgTagFile response(&in, 100);
1341 pkgTagSection section;
1342
1343 while (response.Step(section) == true) {
1344 char const * type = nullptr;
1345 if (section.Exists("Progress") == true) {
1346 if (Progress != NULL) {
1347 string msg = section.FindS("Message");
1348 if (msg.empty() == true)
1349 msg = _("Prepare for receiving solution");
1350 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
1351 }
1352 continue;
1353 } else if (section.Exists("Error") == true) {
1354 if (_error->PendingError()) {
1355 if (Progress != nullptr)
1356 Progress->Done();
1357 Progress = nullptr;
1358 _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
1359 }
1360 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1361 if (msg.empty() == true) {
1362 msg = _("External planner failed without a proper error message");
1363 _error->Error("%s", msg.c_str());
1364 } else
1365 _error->Error("External planner failed with: %s", msg.substr(0,msg.find('\n')).c_str());
1366 if (Progress != nullptr)
1367 Progress->Done();
1368 std::cerr << "The planner encountered an error of type: " << section.FindS("Error") << std::endl;
1369 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
1370 std::cerr << msg << std::endl << std::endl;
1371 return false;
1372 } else if (section.Exists("Unpack") == true)
1373 type = "Unpack";
1374 else if (section.Exists("Configure") == true)
1375 type = "Configure";
1376 else if (section.Exists("Remove") == true)
1377 type = "Remove";
1378 else {
1379 char const *Start, *End;
1380 section.GetSection(Start, End);
1381 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
1382 continue;
1383 }
1384
1385 if (type == nullptr)
1386 continue;
1387 size_t const id = section.FindULL(type, VersionCount);
1388 if (id == VersionCount) {
1389 _error->Warning("Unable to parse %s request with id value '%s'!", type, section.FindS(type).c_str());
1390 continue;
1391 } else if (id > PM->Cache.Head().VersionCount) {
1392 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type).c_str(), type);
1393 continue;
1394 }
1395
1396 pkgCache::VerIterator Ver(PM->Cache.GetCache(), PM->Cache.GetCache().VerP + VerIdx[id]);
1397 auto const Pkg = Ver.ParentPkg();
1398 if (strcmp(type, "Unpack") == 0)
1399 PM->Install(Pkg, PM->FileNames[Pkg->ID]);
1400 else if (strcmp(type, "Configure") == 0)
1401 PM->Configure(Pkg);
1402 else if (strcmp(type, "Remove") == 0)
1403 PM->Remove(Pkg, PM->Cache[Pkg].Purge());
1404 }
1405 return in.Failed() == false;
1406 }
1407 /*}}}*/
1408 bool EIPP::ReadRequest(int const input, std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1409 unsigned int &flags)
1410 {
1411 actions.clear();
1412 flags = 0;
1413 std::string line;
1414 while (ReadLine(input, line) == true)
1415 {
1416 // Skip empty lines before request
1417 if (line.empty() == true)
1418 continue;
1419 // The first Tag must be a request, so search for it
1420 if (line.compare(0, 8, "Request:") != 0)
1421 continue;
1422
1423 while (ReadLine(input, line) == true)
1424 {
1425 // empty lines are the end of the request
1426 if (line.empty() == true)
1427 return true;
1428
1429 PKG_ACTION pkgact = PKG_ACTION::NOOP;
1430 if (LineStartsWithAndStrip(line, "Install:"))
1431 pkgact = PKG_ACTION::INSTALL;
1432 else if (LineStartsWithAndStrip(line, "ReInstall:"))
1433 pkgact = PKG_ACTION::REINSTALL;
1434 else if (LineStartsWithAndStrip(line, "Remove:"))
1435 pkgact = PKG_ACTION::REMOVE;
1436 else if (LineStartsWithAndStrip(line, "Architecture:"))
1437 _config->Set("APT::Architecture", line);
1438 else if (LineStartsWithAndStrip(line, "Architectures:"))
1439 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
1440 else if (LineStartsWithAndStrip(line, "Planner:"))
1441 ; // purely informational line
1442 else if (LineStartsWithAndStrip(line, "Immediate-Configuration:"))
1443 {
1444 if (localStringToBool(line, true))
1445 flags |= Request::IMMEDIATE_CONFIGURATION_ALL;
1446 else
1447 flags |= Request::NO_IMMEDIATE_CONFIGURATION;
1448 }
1449 else if (ReadFlag(flags, line, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS))
1450 ;
1451 else
1452 _error->Warning("Unknown line in EIPP Request stanza: %s", line.c_str());
1453
1454 if (pkgact == PKG_ACTION::NOOP)
1455 continue;
1456 for (auto && p: VectorizeString(line, ' '))
1457 actions.emplace_back(std::move(p), pkgact);
1458 }
1459 }
1460 return false;
1461 }
1462 /*}}}*/
1463 bool EIPP::ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1464 pkgDepCache &Cache)
1465 {
1466 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1467 {
1468 short versions = 0;
1469 for (auto Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
1470 {
1471 ++versions;
1472 if (Pkg.CurrentVer() == Ver)
1473 continue;
1474 Cache.SetCandidateVersion(Ver);
1475 }
1476 if (unlikely(versions > 2))
1477 _error->Warning("Package %s has %d versions, but should have at most 2!", Pkg.FullName().c_str(), versions);
1478 }
1479 for (auto && a: actions)
1480 {
1481 pkgCache::PkgIterator P = Cache.FindPkg(a.first);
1482 if (P.end() == true)
1483 {
1484 _error->Warning("Package %s is not known, so can't be acted on", a.first.c_str());
1485 continue;
1486 }
1487 switch (a.second)
1488 {
1489 case PKG_ACTION::NOOP:
1490 _error->Warning("Package %s has NOOP as action?!?", a.first.c_str());
1491 break;
1492 case PKG_ACTION::INSTALL:
1493 Cache.MarkInstall(P, false);
1494 break;
1495 case PKG_ACTION::REINSTALL:
1496 Cache.MarkInstall(P, false);
1497 Cache.SetReInstall(P, true);
1498 break;
1499 case PKG_ACTION::REMOVE:
1500 Cache.MarkDelete(P);
1501 break;
1502 }
1503 }
1504 return true;
1505 }
1506 /*}}}*/