]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
6fd97845bcf9f6fb568b0f36c9129294ce95d5ad
[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 APT::PackageSet 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.find(Dep.TargetPkg()) == pkgset.end())
314 continue;
315 if (dependencies[Dep->Type].empty() == false)
316 dependencies[Dep->Type].append(", ");
317 }
318 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
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.find(Prv.ParentPkg()) == pkgset.end())
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 APT::PackageSet 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 (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end() && likely(Okay); ++Pkg, ++p)
460 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
461 {
462 if (SkipUnavailableVersions(Cache, Pkg, Ver))
463 continue;
464 Okay &= WriteScenarioVersion(Cache, output, Pkg, Ver);
465 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset);
466 WriteOkay(Okay, output, "\n");
467 if (Progress != NULL && p % 100 == 0)
468 Progress->Progress(p);
469 }
470 if (Progress != NULL)
471 Progress->Done();
472 return Okay;
473 }
474 /*}}}*/
475 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
476 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
477 bool const DistUpgrade, bool const AutoRemove,
478 OpProgress *Progress)
479 {
480 if (Progress != NULL)
481 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
482 unsigned long p = 0;
483 string del, inst;
484 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
485 {
486 if (Progress != NULL && p % 100 == 0)
487 Progress->Progress(p);
488 string* req;
489 pkgDepCache::StateCache &P = Cache[Pkg];
490 if (P.Delete() == true)
491 req = &del;
492 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
493 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
494 req = &inst;
495 else
496 continue;
497 req->append(" ").append(Pkg.FullName());
498 }
499 fprintf(output, "Request: EDSP 0.5\n");
500
501 const char *arch = _config->Find("APT::Architecture").c_str();
502 std::vector<string> archs = APT::Configuration::getArchitectures();
503 fprintf(output, "Architecture: %s\n", arch);
504 fprintf(output, "Architectures:");
505 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
506 fprintf(output, " %s", a->c_str());
507 fprintf(output, "\n");
508
509 if (del.empty() == false)
510 fprintf(output, "Remove: %s\n", del.c_str()+1);
511 if (inst.empty() == false)
512 fprintf(output, "Install: %s\n", inst.c_str()+1);
513 if (Upgrade == true)
514 fprintf(output, "Upgrade: yes\n");
515 if (DistUpgrade == true)
516 fprintf(output, "Dist-Upgrade: yes\n");
517 if (AutoRemove == true)
518 fprintf(output, "Autoremove: yes\n");
519 auto const solver = _config->Find("APT::Solver", "internal");
520 fprintf(output, "Solver: %s\n", solver.c_str());
521 auto const solverconf = std::string("APT::Solver::") + solver + "::";
522 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
523 fprintf(output, "Strict-Pinning: no\n");
524 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
525 if (solverpref.empty() == false)
526 fprintf(output, "Preferences: %s\n", solverpref.c_str());
527 fprintf(output, "\n");
528 return true;
529 }
530 bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
531 unsigned int const flags,
532 OpProgress *Progress)
533 {
534 if (Progress != NULL)
535 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
536 unsigned long p = 0;
537 string del, inst;
538 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
539 {
540 if (Progress != NULL && p % 100 == 0)
541 Progress->Progress(p);
542 string* req;
543 pkgDepCache::StateCache &P = Cache[Pkg];
544 if (P.Delete() == true)
545 req = &del;
546 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
547 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
548 req = &inst;
549 else
550 continue;
551 req->append(" ").append(Pkg.FullName());
552 }
553 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
554
555 const char *arch = _config->Find("APT::Architecture").c_str();
556 std::vector<string> archs = APT::Configuration::getArchitectures();
557 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
558 "Architectures:");
559 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
560 WriteOkay(Okay, output, " ", *a);
561 WriteOkay(Okay, output, "\n");
562
563 if (del.empty() == false)
564 WriteOkay(Okay, output, "Remove:", del, "\n");
565 if (inst.empty() == false)
566 WriteOkay(Okay, output, "Install:", inst, "\n");
567 if (flags & Request::AUTOREMOVE)
568 WriteOkay(Okay, output, "Autoremove: yes\n");
569 if (flags & Request::UPGRADE_ALL)
570 {
571 WriteOkay(Okay, output, "Upgrade-All: yes\n");
572 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
573 WriteOkay(Okay, output, "Upgrade: yes\n");
574 else
575 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
576 }
577 if (flags & Request::FORBID_NEW_INSTALL)
578 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
579 if (flags & Request::FORBID_REMOVE)
580 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
581 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
582 WriteOkay(Okay, output, "Strict-Pinning: no\n");
583 string solverpref("APT::Solver::");
584 solverpref.append(_config->Find("APT::Solver", "internal")).append("::Preferences");
585 if (_config->Exists(solverpref) == true)
586 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
587 return WriteOkay(Okay, output, "\n");
588 }
589 /*}}}*/
590 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
591 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
592 /* We build an map id to mmap offset here
593 In theory we could use the offset as ID, but then VersionCount
594 couldn't be used to create other versionmappings anymore and it
595 would be too easy for a (buggy) solver to segfault APTā€¦ */
596 unsigned long long const VersionCount = Cache.Head().VersionCount;
597 unsigned long VerIdx[VersionCount];
598 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
599 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
600 VerIdx[V->ID] = V.Index();
601 Cache[P].Marked = true;
602 Cache[P].Garbage = false;
603 }
604
605 FileFd in;
606 in.OpenDescriptor(input, FileFd::ReadOnly);
607 pkgTagFile response(&in, 100);
608 pkgTagSection section;
609
610 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
611 while (response.Step(section) == true) {
612 std::string type;
613 if (section.Exists("Install") == true)
614 type = "Install";
615 else if (section.Exists("Remove") == true)
616 type = "Remove";
617 else if (section.Exists("Progress") == true) {
618 if (Progress != NULL) {
619 string msg = section.FindS("Message");
620 if (msg.empty() == true)
621 msg = _("Prepare for receiving solution");
622 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
623 }
624 continue;
625 } else if (section.Exists("Error") == true) {
626 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
627 if (msg.empty() == true) {
628 msg = _("External solver failed without a proper error message");
629 _error->Error("%s", msg.c_str());
630 } else
631 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
632 if (Progress != NULL)
633 Progress->Done();
634 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
635 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
636 std::cerr << msg << std::endl << std::endl;
637 return false;
638 } else if (section.Exists("Autoremove") == true)
639 type = "Autoremove";
640 else
641 continue;
642
643 size_t const id = section.FindULL(type.c_str(), VersionCount);
644 if (id == VersionCount) {
645 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
646 continue;
647 } else if (id > Cache.Head().VersionCount) {
648 _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());
649 continue;
650 }
651
652 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
653 auto const Pkg = Ver.ParentPkg();
654 if (type == "Autoremove") {
655 Cache[Pkg].Marked = false;
656 Cache[Pkg].Garbage = true;
657 } else if (seenOnce.emplace(Pkg->ID).second == false) {
658 _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());
659 } else if (type == "Install") {
660 if (Pkg.CurrentVer() == Ver) {
661 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
662 Ver.VerStr(), Pkg.FullName(false).c_str());
663 } else {
664 Cache.SetCandidateVersion(Ver);
665 Cache.MarkInstall(Pkg, false, 0, false);
666 }
667 } else if (type == "Remove") {
668 if (Pkg->CurrentVer == 0)
669 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
670 Ver.VerStr(), Pkg.FullName(false).c_str());
671 else if (Pkg.CurrentVer() != Ver)
672 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
673 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
674 else
675 Cache.MarkDelete(Ver.ParentPkg(), false);
676 }
677 }
678 return true;
679 }
680 /*}}}*/
681 // ReadLine - first line from the given file descriptor /*{{{*/
682 // ---------------------------------------------------------------------
683 /* Little helper method to read a complete line into a string. Similar to
684 fgets but we need to use the low-level read() here as otherwise the
685 listparser will be confused later on as mixing of fgets and read isn't
686 a supported action according to the manpages and results are undefined */
687 static bool ReadLine(int const input, std::string &line) {
688 char one;
689 ssize_t data = 0;
690 line.erase();
691 line.reserve(100);
692 while ((data = read(input, &one, sizeof(one))) != -1) {
693 if (data != 1)
694 continue;
695 if (one == '\n')
696 return true;
697 if (one == '\r')
698 continue;
699 if (line.empty() == true && isblank(one) != 0)
700 continue;
701 line += one;
702 }
703 return false;
704 }
705 /*}}}*/
706 // StringToBool - convert yes/no to bool /*{{{*/
707 // ---------------------------------------------------------------------
708 /* we are not as lazy as we are in the global StringToBool as we really
709 only accept yes/no here - but we will ignore leading spaces */
710 static bool StringToBool(char const *answer, bool const defValue) {
711 for (; isspace(*answer) != 0; ++answer);
712 if (strncasecmp(answer, "yes", 3) == 0)
713 return true;
714 else if (strncasecmp(answer, "no", 2) == 0)
715 return false;
716 else
717 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
718 return defValue;
719 }
720 /*}}}*/
721 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
722 static bool ReadFlag(unsigned int &flags, std::string const &line, APT::StringView const name, unsigned int const setflag)
723 {
724 if (line.compare(0, name.length(), name.data()) != 0)
725 return false;
726 auto const l = line.c_str() + name.length() + 1;
727 if (StringToBool(l, false))
728 flags |= setflag;
729 else
730 flags &= ~setflag;
731 return true;
732 }
733 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
734 std::list<std::string> &remove, unsigned int &flags)
735 {
736 install.clear();
737 remove.clear();
738 flags = 0;
739 std::string line;
740 while (ReadLine(input, line) == true)
741 {
742 // Skip empty lines before request
743 if (line.empty() == true)
744 continue;
745 // The first Tag must be a request, so search for it
746 if (line.compare(0, 8, "Request:") != 0)
747 continue;
748
749 while (ReadLine(input, line) == true)
750 {
751 // empty lines are the end of the request
752 if (line.empty() == true)
753 return true;
754
755 std::list<std::string> *request = NULL;
756 if (line.compare(0, 8, "Install:") == 0)
757 {
758 line.erase(0, 8);
759 request = &install;
760 }
761 else if (line.compare(0, 7, "Remove:") == 0)
762 {
763 line.erase(0, 7);
764 request = &remove;
765 }
766 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
767 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
768 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
769 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
770 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
771 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
772 ;
773 else if (line.compare(0, 13, "Architecture:") == 0)
774 _config->Set("APT::Architecture", line.c_str() + 14);
775 else if (line.compare(0, 14, "Architectures:") == 0)
776 {
777 std::string const archs = line.c_str() + 15;
778 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
779 }
780 else if (line.compare(0, 7, "Solver:") == 0)
781 ; // purely informational line
782 else
783 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
784
785 if (request == NULL)
786 continue;
787 size_t end = line.length();
788 do {
789 size_t begin = line.rfind(' ');
790 if (begin == std::string::npos)
791 {
792 request->push_back(line.substr(0, end));
793 break;
794 }
795 else if (begin < end)
796 request->push_back(line.substr(begin + 1, end));
797 line.erase(begin);
798 end = line.find_last_not_of(' ');
799 } while (end != std::string::npos);
800 }
801 }
802 return false;
803 }
804 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
805 std::list<std::string> &remove, bool &upgrade,
806 bool &distUpgrade, bool &autoRemove)
807 {
808 unsigned int flags;
809 auto const ret = ReadRequest(input, install, remove, flags);
810 autoRemove = (flags & Request::AUTOREMOVE);
811 if (flags & Request::UPGRADE_ALL)
812 {
813 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
814 {
815 upgrade = true;
816 distUpgrade = false;
817 } else {
818 upgrade = false;
819 distUpgrade = false;
820 }
821 }
822 else
823 {
824 upgrade = false;
825 distUpgrade = false;
826 }
827 return ret;
828 }
829 /*}}}*/
830 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
831 bool EDSP::ApplyRequest(std::list<std::string> const &install,
832 std::list<std::string> const &remove,
833 pkgDepCache &Cache)
834 {
835 for (std::list<std::string>::const_iterator i = install.begin();
836 i != install.end(); ++i) {
837 pkgCache::PkgIterator P = Cache.FindPkg(*i);
838 if (P.end() == true)
839 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
840 else
841 Cache.MarkInstall(P, false);
842 }
843
844 for (std::list<std::string>::const_iterator i = remove.begin();
845 i != remove.end(); ++i) {
846 pkgCache::PkgIterator P = Cache.FindPkg(*i);
847 if (P.end() == true)
848 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
849 else
850 Cache.MarkDelete(P);
851 }
852 return true;
853 }
854 /*}}}*/
855 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
856 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
857 {
858 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
859 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
860 {
861 if (Cache[Pkg].Delete() == true)
862 {
863 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
864 if (Debug == true)
865 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
866 }
867 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
868 {
869 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
870 fprintf(output, "Install: %d\n", CandVer->ID);
871 if (Debug == true)
872 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
873 }
874 else if (Cache[Pkg].Garbage == true)
875 {
876 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
877 if (Debug == true)
878 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
879 }
880 else
881 continue;
882 fprintf(output, "\n");
883 }
884
885 return true;
886 }
887 bool EDSP::WriteSolution(pkgDepCache &Cache, FileFd &output)
888 {
889 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
890 bool Okay = output.Failed() == false;
891 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
892 {
893 std::string action;
894 if (Cache[Pkg].Delete() == true)
895 WriteOkay(Okay, output, "Remove: ", Pkg.CurrentVer()->ID, "\n");
896 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
897 WriteOkay(Okay, output, "Install: ", Cache.GetCandidateVersion(Pkg)->ID, "\n");
898 else if (Cache[Pkg].Garbage == true)
899 WriteOkay(Okay, output, "Autoremove: ", Pkg.CurrentVer()->ID, "\n");
900 else
901 continue;
902
903 if (Debug)
904 {
905 WriteOkay(Okay, output, "Package: ", Pkg.FullName(), "\nVersion: ");
906 if (Cache[Pkg].Delete() == true || Cache[Pkg].Garbage == true)
907 WriteOkay(Okay, output, Pkg.CurrentVer().VerStr(), "\n\n");
908 else
909 WriteOkay(Okay, output, Cache.GetCandidateVersion(Pkg).VerStr(), "\n\n");
910 }
911 else
912 WriteOkay(Okay, output, "\n");
913 }
914 return Okay;
915 }
916 /*}}}*/
917 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
918 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
919 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
920 fprintf(output, "Percentage: %d\n", percent);
921 fprintf(output, "Message: %s\n\n", message);
922 fflush(output);
923 return true;
924 }
925 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
926 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
927 "Percentage: ", percent, "\n",
928 "Message: ", message, "\n\n") && output.Flush();
929 }
930 /*}}}*/
931 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
932 bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
933 fprintf(output, "Error: %s\n", uuid);
934 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
935 return true;
936 }
937 bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
938 return WriteOkay(output, "Error: ", uuid, "\n",
939 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
940 "\n\n");
941 }
942 /*}}}*/
943 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
944 pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
945 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
946 std::string file;
947 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
948 dir != solverDirs.end(); ++dir) {
949 file = flCombine(*dir, solver);
950 if (RealFileExists(file.c_str()) == true)
951 break;
952 file.clear();
953 }
954
955 if (file.empty() == true)
956 {
957 _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
958 return 0;
959 }
960 int external[4] = {-1, -1, -1, -1};
961 if (pipe(external) != 0 || pipe(external + 2) != 0)
962 {
963 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
964 return 0;
965 }
966 for (int i = 0; i < 4; ++i)
967 SetCloseExec(external[i], true);
968
969 pid_t Solver = ExecFork();
970 if (Solver == 0) {
971 dup2(external[0], STDIN_FILENO);
972 dup2(external[3], STDOUT_FILENO);
973 const char* calling[2] = { file.c_str(), 0 };
974 execv(calling[0], (char**) calling);
975 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
976 _exit(100);
977 }
978 close(external[0]);
979 close(external[3]);
980
981 if (WaitFd(external[1], true, 5) == false)
982 {
983 _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
984 return 0;
985 }
986
987 *solver_in = external[1];
988 *solver_out = external[2];
989 return Solver;
990 }
991 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
992 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
993 return false;
994 return true;
995 }
996 /*}}}*/
997 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
998 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
999 unsigned int const flags, OpProgress *Progress) {
1000 int solver_in, solver_out;
1001 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1002 if (solver_pid == 0)
1003 return false;
1004
1005 FileFd output;
1006 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1007 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
1008
1009 bool Okay = output.Failed() == false;
1010 if (Progress != NULL)
1011 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
1012 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
1013 if (Progress != NULL)
1014 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
1015 Okay &= EDSP::WriteScenario(Cache, output, Progress);
1016 output.Close();
1017
1018 if (Progress != NULL)
1019 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
1020 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
1021 return false;
1022
1023 return ExecWait(solver_pid, solver);
1024 }
1025 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1026 bool const upgrade, bool const distUpgrade,
1027 bool const autoRemove, OpProgress *Progress) {
1028 unsigned int flags = 0;
1029 if (autoRemove)
1030 flags |= Request::AUTOREMOVE;
1031 if (upgrade)
1032 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1033 if (distUpgrade)
1034 flags |= Request::UPGRADE_ALL;
1035 return ResolveExternal(solver, Cache, flags, Progress);
1036 }
1037 /*}}}*/