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