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