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