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