]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
8414c6a23058a17c6272fb07e99b2c6a86a6f43d
[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 #include <iostream>
27 #include <limits>
28 #include <string>
29
30 #include <apti18n.h>
31 /*}}}*/
32
33 using std::string;
34
35 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
36 const char * const PrioMap[] = {0, "important", "required", "standard",
37 "optional", "extra"};
38 const char * const DepMap[] = {"", "Depends", "Pre-Depends", "Suggests",
39 "Recommends" , "Conflicts", "Replaces",
40 "Obsoletes", "Breaks", "Enhances"};
41
42
43 // WriteScenarioVersion /*{{{*/
44 static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
45 pkgCache::VerIterator const &Ver)
46 {
47 fprintf(output, "Package: %s\n", Pkg.Name());
48 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
49 fprintf(output, "Architecture: %s\n", Ver.Arch());
50 fprintf(output, "Version: %s\n", Ver.VerStr());
51 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
52 if (Pkg.CurrentVer() == Ver)
53 fprintf(output, "Installed: yes\n");
54 if (Pkg->SelectedState == pkgCache::State::Hold ||
55 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
56 fprintf(output, "Hold: yes\n");
57 fprintf(output, "APT-ID: %d\n", Ver->ID);
58 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
59 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
60 fprintf(output, "Essential: yes\n");
61 fprintf(output, "Section: %s\n", Ver.Section());
62 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
63 fprintf(output, "Multi-Arch: allowed\n");
64 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
65 fprintf(output, "Multi-Arch: foreign\n");
66 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
67 fprintf(output, "Multi-Arch: same\n");
68 std::set<string> Releases;
69 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
70 pkgCache::PkgFileIterator File = I.File();
71 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
72 string Release = File.RelStr();
73 if (!Release.empty())
74 Releases.insert(Release);
75 }
76 }
77 if (!Releases.empty()) {
78 fprintf(output, "APT-Release:\n");
79 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
80 fprintf(output, " %s\n", R->c_str());
81 }
82 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
83 if (Cache.GetCandidateVersion(Pkg) == Ver)
84 fprintf(output, "APT-Candidate: yes\n");
85 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
86 fprintf(output, "APT-Automatic: yes\n");
87 }
88 /*}}}*/
89 // WriteScenarioDependency /*{{{*/
90 static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
91 {
92 std::string dependencies[pkgCache::Dep::Enhances + 1];
93 bool orGroup = false;
94 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
95 {
96 if (Dep.IsImplicit() == true)
97 continue;
98 if (orGroup == false)
99 dependencies[Dep->Type].append(", ");
100 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
101 if (Dep->Version != 0)
102 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
103 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
104 {
105 dependencies[Dep->Type].append(" | ");
106 orGroup = true;
107 }
108 else
109 orGroup = false;
110 }
111 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
112 if (dependencies[i].empty() == false)
113 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
114 string provides;
115 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
116 {
117 if (Prv.IsMultiArchImplicit() == true)
118 continue;
119 provides.append(", ").append(Prv.Name());
120 if (Prv->ProvideVersion != 0)
121 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
122 }
123 if (provides.empty() == false)
124 fprintf(output, "Provides: %s\n", provides.c_str()+2);
125 }
126 /*}}}*/
127 // WriteScenarioLimitedDependency /*{{{*/
128 static void WriteScenarioLimitedDependency(FILE* output,
129 pkgCache::VerIterator const &Ver,
130 APT::PackageSet const &pkgset)
131 {
132 std::string dependencies[pkgCache::Dep::Enhances + 1];
133 bool orGroup = false;
134 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
135 {
136 if (Dep.IsImplicit() == true)
137 continue;
138 if (orGroup == false)
139 {
140 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
141 continue;
142 dependencies[Dep->Type].append(", ");
143 }
144 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
145 {
146 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
147 continue;
148 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
149 orGroup = false;
150 continue;
151 }
152 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
153 if (Dep->Version != 0)
154 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
155 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
156 {
157 dependencies[Dep->Type].append(" | ");
158 orGroup = true;
159 }
160 else
161 orGroup = false;
162 }
163 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
164 if (dependencies[i].empty() == false)
165 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
166 string provides;
167 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
168 {
169 if (Prv.IsMultiArchImplicit() == true)
170 continue;
171 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
172 continue;
173 provides.append(", ").append(Prv.Name());
174 }
175 if (provides.empty() == false)
176 fprintf(output, "Provides: %s\n", provides.c_str()+2);
177 }
178 /*}}}*/
179 static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
180 {
181 /* versions which aren't current and aren't available in
182 any "online" source file are bad, expect if they are the choosen
183 candidate: The exception is for build-dep implementation as it creates
184 such pseudo (package) versions and removes them later on again.
185 We filter out versions at all so packages in 'rc' state only available
186 in dpkg/status aren't passed to solvers as they can't be installed. */
187 if (Pkg->CurrentVer != 0)
188 return false;
189 if (Cache.GetCandidateVersion(Pkg) == Ver)
190 return false;
191 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
192 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
193 return false;
194 return true;
195 }
196 /*}}}*/
197 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
198 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
199 {
200 if (Progress != NULL)
201 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
202 unsigned long p = 0;
203 std::vector<std::string> archs = APT::Configuration::getArchitectures();
204 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
205 {
206 std::string const arch = Pkg.Arch();
207 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
208 continue;
209 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
210 {
211 if (SkipUnavailableVersions(Cache, Pkg, Ver))
212 continue;
213 WriteScenarioVersion(Cache, output, Pkg, Ver);
214 WriteScenarioDependency(output, Ver);
215 fprintf(output, "\n");
216 if (Progress != NULL && p % 100 == 0)
217 Progress->Progress(p);
218 }
219 }
220 return true;
221 }
222 /*}}}*/
223 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
224 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
225 APT::PackageSet const &pkgset,
226 OpProgress *Progress)
227 {
228 if (Progress != NULL)
229 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
230 unsigned long p = 0;
231 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
232 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
233 {
234 if (SkipUnavailableVersions(Cache, Pkg, Ver))
235 continue;
236 WriteScenarioVersion(Cache, output, Pkg, Ver);
237 WriteScenarioLimitedDependency(output, Ver, pkgset);
238 fprintf(output, "\n");
239 if (Progress != NULL && p % 100 == 0)
240 Progress->Progress(p);
241 }
242 if (Progress != NULL)
243 Progress->Done();
244 return true;
245 }
246 /*}}}*/
247 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
248 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
249 bool const DistUpgrade, bool const AutoRemove,
250 OpProgress *Progress)
251 {
252 if (Progress != NULL)
253 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
254 unsigned long p = 0;
255 string del, inst;
256 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
257 {
258 if (Progress != NULL && p % 100 == 0)
259 Progress->Progress(p);
260 string* req;
261 pkgDepCache::StateCache &P = Cache[Pkg];
262 if (P.Delete() == true)
263 req = &del;
264 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
265 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
266 req = &inst;
267 else
268 continue;
269 req->append(" ").append(Pkg.FullName());
270 }
271 fprintf(output, "Request: EDSP 0.5\n");
272
273 const char *arch = _config->Find("APT::Architecture").c_str();
274 std::vector<string> archs = APT::Configuration::getArchitectures();
275 fprintf(output, "Architecture: %s\n", arch);
276 fprintf(output, "Architectures:");
277 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
278 fprintf(output, " %s", a->c_str());
279 fprintf(output, "\n");
280
281 if (del.empty() == false)
282 fprintf(output, "Remove: %s\n", del.c_str()+1);
283 if (inst.empty() == false)
284 fprintf(output, "Install: %s\n", inst.c_str()+1);
285 if (Upgrade == true)
286 fprintf(output, "Upgrade: yes\n");
287 if (DistUpgrade == true)
288 fprintf(output, "Dist-Upgrade: yes\n");
289 if (AutoRemove == true)
290 fprintf(output, "Autoremove: yes\n");
291 auto const solver = _config->Find("APT::Solver", "internal");
292 fprintf(output, "Solver: %s\n", solver.c_str());
293 auto const solverconf = std::string("APT::Solver::") + solver + "::";
294 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
295 fprintf(output, "Strict-Pinning: no\n");
296 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
297 if (solverpref.empty() == false)
298 fprintf(output, "Preferences: %s\n", solverpref.c_str());
299 fprintf(output, "\n");
300 return true;
301 }
302 /*}}}*/
303 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
304 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
305 /* We build an map id to mmap offset here
306 In theory we could use the offset as ID, but then VersionCount
307 couldn't be used to create other versionmappings anymore and it
308 would be too easy for a (buggy) solver to segfault APTā€¦ */
309 unsigned long long const VersionCount = Cache.Head().VersionCount;
310 unsigned long VerIdx[VersionCount];
311 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
312 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
313 VerIdx[V->ID] = V.Index();
314 Cache[P].Marked = true;
315 Cache[P].Garbage = false;
316 }
317
318 FileFd in;
319 in.OpenDescriptor(input, FileFd::ReadOnly);
320 pkgTagFile response(&in, 100);
321 pkgTagSection section;
322
323 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
324 while (response.Step(section) == true) {
325 std::string type;
326 if (section.Exists("Install") == true)
327 type = "Install";
328 else if (section.Exists("Remove") == true)
329 type = "Remove";
330 else if (section.Exists("Progress") == true) {
331 if (Progress != NULL) {
332 string msg = section.FindS("Message");
333 if (msg.empty() == true)
334 msg = _("Prepare for receiving solution");
335 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
336 }
337 continue;
338 } else if (section.Exists("Error") == true) {
339 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
340 if (msg.empty() == true) {
341 msg = _("External solver failed without a proper error message");
342 _error->Error("%s", msg.c_str());
343 } else
344 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
345 if (Progress != NULL)
346 Progress->Done();
347 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
348 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
349 std::cerr << msg << std::endl << std::endl;
350 return false;
351 } else if (section.Exists("Autoremove") == true)
352 type = "Autoremove";
353 else
354 continue;
355
356 size_t const id = section.FindULL(type.c_str(), VersionCount);
357 if (id == VersionCount) {
358 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
359 continue;
360 } else if (id > Cache.Head().VersionCount) {
361 _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());
362 continue;
363 }
364
365 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
366 auto const Pkg = Ver.ParentPkg();
367 if (type == "Autoremove") {
368 Cache[Pkg].Marked = false;
369 Cache[Pkg].Garbage = true;
370 } else if (seenOnce.emplace(Pkg->ID).second == false) {
371 _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());
372 } else if (type == "Install") {
373 if (Pkg.CurrentVer() == Ver) {
374 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
375 Ver.VerStr(), Pkg.FullName(false).c_str());
376 } else {
377 Cache.SetCandidateVersion(Ver);
378 Cache.MarkInstall(Pkg, false, 0, false);
379 }
380 } else if (type == "Remove") {
381 if (Pkg->CurrentVer == 0)
382 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
383 Ver.VerStr(), Pkg.FullName(false).c_str());
384 else if (Pkg.CurrentVer() != Ver)
385 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
386 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
387 else
388 Cache.MarkDelete(Ver.ParentPkg(), false);
389 }
390 }
391 return true;
392 }
393 /*}}}*/
394 // ReadLine - first line from the given file descriptor /*{{{*/
395 // ---------------------------------------------------------------------
396 /* Little helper method to read a complete line into a string. Similar to
397 fgets but we need to use the low-level read() here as otherwise the
398 listparser will be confused later on as mixing of fgets and read isn't
399 a supported action according to the manpages and results are undefined */
400 static bool ReadLine(int const input, std::string &line) {
401 char one;
402 ssize_t data = 0;
403 line.erase();
404 line.reserve(100);
405 while ((data = read(input, &one, sizeof(one))) != -1) {
406 if (data != 1)
407 continue;
408 if (one == '\n')
409 return true;
410 if (one == '\r')
411 continue;
412 if (line.empty() == true && isblank(one) != 0)
413 continue;
414 line += one;
415 }
416 return false;
417 }
418 /*}}}*/
419 // StringToBool - convert yes/no to bool /*{{{*/
420 // ---------------------------------------------------------------------
421 /* we are not as lazy as we are in the global StringToBool as we really
422 only accept yes/no here - but we will ignore leading spaces */
423 static bool StringToBool(char const *answer, bool const defValue) {
424 for (; isspace(*answer) != 0; ++answer);
425 if (strncasecmp(answer, "yes", 3) == 0)
426 return true;
427 else if (strncasecmp(answer, "no", 2) == 0)
428 return false;
429 else
430 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
431 return defValue;
432 }
433 /*}}}*/
434 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
435 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
436 std::list<std::string> &remove, bool &upgrade,
437 bool &distUpgrade, bool &autoRemove)
438 {
439 install.clear();
440 remove.clear();
441 upgrade = false;
442 distUpgrade = false;
443 autoRemove = false;
444 std::string line;
445 while (ReadLine(input, line) == true)
446 {
447 // Skip empty lines before request
448 if (line.empty() == true)
449 continue;
450 // The first Tag must be a request, so search for it
451 if (line.compare(0, 8, "Request:") != 0)
452 continue;
453
454 while (ReadLine(input, line) == true)
455 {
456 // empty lines are the end of the request
457 if (line.empty() == true)
458 return true;
459
460 std::list<std::string> *request = NULL;
461 if (line.compare(0, 8, "Install:") == 0)
462 {
463 line.erase(0, 8);
464 request = &install;
465 }
466 else if (line.compare(0, 7, "Remove:") == 0)
467 {
468 line.erase(0, 7);
469 request = &remove;
470 }
471 else if (line.compare(0, 8, "Upgrade:") == 0)
472 upgrade = StringToBool(line.c_str() + 9, false);
473 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
474 distUpgrade = StringToBool(line.c_str() + 14, false);
475 else if (line.compare(0, 11, "Autoremove:") == 0)
476 autoRemove = StringToBool(line.c_str() + 12, false);
477 else if (line.compare(0, 13, "Architecture:") == 0)
478 _config->Set("APT::Architecture", line.c_str() + 14);
479 else if (line.compare(0, 14, "Architectures:") == 0)
480 {
481 std::string const archs = line.c_str() + 15;
482 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
483 }
484 else if (line.compare(0, 7, "Solver:") == 0)
485 ; // purely informational line
486 else
487 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
488
489 if (request == NULL)
490 continue;
491 size_t end = line.length();
492 do {
493 size_t begin = line.rfind(' ');
494 if (begin == std::string::npos)
495 {
496 request->push_back(line.substr(0, end));
497 break;
498 }
499 else if (begin < end)
500 request->push_back(line.substr(begin + 1, end));
501 line.erase(begin);
502 end = line.find_last_not_of(' ');
503 } while (end != std::string::npos);
504 }
505 }
506 return false;
507 }
508 /*}}}*/
509 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
510 bool EDSP::ApplyRequest(std::list<std::string> const &install,
511 std::list<std::string> const &remove,
512 pkgDepCache &Cache)
513 {
514 for (std::list<std::string>::const_iterator i = install.begin();
515 i != install.end(); ++i) {
516 pkgCache::PkgIterator P = Cache.FindPkg(*i);
517 if (P.end() == true)
518 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
519 else
520 Cache.MarkInstall(P, false);
521 }
522
523 for (std::list<std::string>::const_iterator i = remove.begin();
524 i != remove.end(); ++i) {
525 pkgCache::PkgIterator P = Cache.FindPkg(*i);
526 if (P.end() == true)
527 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
528 else
529 Cache.MarkDelete(P);
530 }
531 return true;
532 }
533 /*}}}*/
534 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
535 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
536 {
537 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
538 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
539 {
540 if (Cache[Pkg].Delete() == true)
541 {
542 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
543 if (Debug == true)
544 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
545 }
546 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
547 {
548 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
549 fprintf(output, "Install: %d\n", CandVer->ID);
550 if (Debug == true)
551 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
552 }
553 else if (Cache[Pkg].Garbage == true)
554 {
555 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
556 if (Debug == true)
557 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
558 }
559 else
560 continue;
561 fprintf(output, "\n");
562 }
563
564 return true;
565 }
566 /*}}}*/
567 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
568 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
569 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
570 fprintf(output, "Percentage: %d\n", percent);
571 fprintf(output, "Message: %s\n\n", message);
572 fflush(output);
573 return true;
574 }
575 /*}}}*/
576 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
577 bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
578 fprintf(output, "Error: %s\n", uuid);
579 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
580 return true;
581 }
582 /*}}}*/
583 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
584 pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
585 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
586 std::string file;
587 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
588 dir != solverDirs.end(); ++dir) {
589 file = flCombine(*dir, solver);
590 if (RealFileExists(file.c_str()) == true)
591 break;
592 file.clear();
593 }
594
595 if (file.empty() == true)
596 {
597 _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
598 return 0;
599 }
600 int external[4] = {-1, -1, -1, -1};
601 if (pipe(external) != 0 || pipe(external + 2) != 0)
602 {
603 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
604 return 0;
605 }
606 for (int i = 0; i < 4; ++i)
607 SetCloseExec(external[i], true);
608
609 pid_t Solver = ExecFork();
610 if (Solver == 0) {
611 dup2(external[0], STDIN_FILENO);
612 dup2(external[3], STDOUT_FILENO);
613 const char* calling[2] = { file.c_str(), 0 };
614 execv(calling[0], (char**) calling);
615 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
616 _exit(100);
617 }
618 close(external[0]);
619 close(external[3]);
620
621 if (WaitFd(external[1], true, 5) == false)
622 {
623 _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
624 return 0;
625 }
626
627 *solver_in = external[1];
628 *solver_out = external[2];
629 return Solver;
630 }
631 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
632 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
633 return false;
634 return true;
635 }
636 /*}}}*/
637 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
638 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
639 bool const upgrade, bool const distUpgrade,
640 bool const autoRemove, OpProgress *Progress) {
641 int solver_in, solver_out;
642 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
643 if (solver_pid == 0)
644 return false;
645
646 FILE* output = fdopen(solver_in, "w");
647 if (output == NULL)
648 return _error->Errno("Resolve", "fdopen on solver stdin failed");
649
650 if (Progress != NULL)
651 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
652 EDSP::WriteRequest(Cache, output, upgrade, distUpgrade, autoRemove, Progress);
653 if (Progress != NULL)
654 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
655 EDSP::WriteScenario(Cache, output, Progress);
656 fclose(output);
657
658 if (Progress != NULL)
659 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
660 if (EDSP::ReadResponse(solver_out, Cache, Progress) == false)
661 return false;
662
663 return ExecWait(solver_pid, solver);
664 }
665 /*}}}*/