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