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