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