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