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