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