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