]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
7 // Include Files /*{{{*/
10 #include <apt-pkg/error.h>
11 #include <apt-pkg/cacheset.h>
12 #include <apt-pkg/depcache.h>
13 #include <apt-pkg/pkgcache.h>
14 #include <apt-pkg/cacheiterators.h>
15 #include <apt-pkg/progress.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/edsp.h>
18 #include <apt-pkg/tagfile.h>
19 #include <apt-pkg/strutl.h>
35 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
36 const char * const PrioMap
[] = {0, "important", "required", "standard",
38 const char * const DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
39 "Recommends" , "Conflicts", "Replaces",
40 "Obsoletes", "Breaks", "Enhances"};
43 // WriteScenarioVersion /*{{{*/
44 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
45 pkgCache::VerIterator
const &Ver
)
47 fprintf(output
, "Package: %s\n", Pkg
.Name());
48 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
49 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
50 fprintf(output
, "Version: %s\n", Ver
.VerStr());
51 fprintf(output
, "Source-Version: %s\n", Ver
.SourceVerStr());
52 if (Pkg
.CurrentVer() == Ver
)
53 fprintf(output
, "Installed: yes\n");
54 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
55 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
56 fprintf(output
, "Hold: yes\n");
57 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
58 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
59 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
60 fprintf(output
, "Essential: yes\n");
61 fprintf(output
, "Section: %s\n", Ver
.Section());
62 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
63 fprintf(output
, "Multi-Arch: allowed\n");
64 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
65 fprintf(output
, "Multi-Arch: foreign\n");
66 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
67 fprintf(output
, "Multi-Arch: same\n");
68 std::set
<string
> Releases
;
69 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
70 pkgCache::PkgFileIterator File
= I
.File();
71 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
72 string Release
= File
.RelStr();
74 Releases
.insert(Release
);
77 if (!Releases
.empty()) {
78 fprintf(output
, "APT-Release:\n");
79 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
80 fprintf(output
, " %s\n", R
->c_str());
82 fprintf(output
, "APT-Pin: %d\n", Cache
.GetPolicy().GetPriority(Ver
));
83 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
84 fprintf(output
, "APT-Candidate: yes\n");
85 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
86 fprintf(output
, "APT-Automatic: yes\n");
89 // WriteScenarioDependency /*{{{*/
90 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
92 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
94 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
96 if (Dep
.IsImplicit() == true)
99 dependencies
[Dep
->Type
].append(", ");
100 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
101 if (Dep
->Version
!= 0)
102 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
103 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
105 dependencies
[Dep
->Type
].append(" | ");
111 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
112 if (dependencies
[i
].empty() == false)
113 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
115 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
117 if (Prv
.IsMultiArchImplicit() == true)
119 provides
.append(", ").append(Prv
.Name());
120 if (Prv
->ProvideVersion
!= 0)
121 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
123 if (provides
.empty() == false)
124 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
127 // WriteScenarioLimitedDependency /*{{{*/
128 static void WriteScenarioLimitedDependency(FILE* output
,
129 pkgCache::VerIterator
const &Ver
,
130 APT::PackageSet
const &pkgset
)
132 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
133 bool orGroup
= false;
134 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
136 if (Dep
.IsImplicit() == true)
138 if (orGroup
== false)
140 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
142 dependencies
[Dep
->Type
].append(", ");
144 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
146 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
148 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
152 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
153 if (Dep
->Version
!= 0)
154 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
155 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
157 dependencies
[Dep
->Type
].append(" | ");
163 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
164 if (dependencies
[i
].empty() == false)
165 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
167 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
169 if (Prv
.IsMultiArchImplicit() == true)
171 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
173 provides
.append(", ").append(Prv
.Name());
175 if (provides
.empty() == false)
176 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
179 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
180 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
182 if (Progress
!= NULL
)
183 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
185 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
186 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
188 std::string
const arch
= Pkg
.Arch();
189 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
191 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
193 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
194 WriteScenarioDependency(output
, Ver
);
195 fprintf(output
, "\n");
196 if (Progress
!= NULL
&& p
% 100 == 0)
197 Progress
->Progress(p
);
203 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
204 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
205 APT::PackageSet
const &pkgset
,
206 OpProgress
*Progress
)
208 if (Progress
!= NULL
)
209 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
211 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
212 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
214 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
215 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
216 fprintf(output
, "\n");
217 if (Progress
!= NULL
&& p
% 100 == 0)
218 Progress
->Progress(p
);
220 if (Progress
!= NULL
)
225 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
226 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
227 bool const DistUpgrade
, bool const AutoRemove
,
228 OpProgress
*Progress
)
230 if (Progress
!= NULL
)
231 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
234 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
236 if (Progress
!= NULL
&& p
% 100 == 0)
237 Progress
->Progress(p
);
239 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
240 if (P
.Delete() == true)
242 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
243 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
247 req
->append(" ").append(Pkg
.FullName());
249 fprintf(output
, "Request: EDSP 0.5\n");
251 const char *arch
= _config
->Find("APT::Architecture").c_str();
252 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
253 fprintf(output
, "Architecture: %s\n", arch
);
254 fprintf(output
, "Architectures:");
255 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
256 fprintf(output
, " %s", a
->c_str());
257 fprintf(output
, "\n");
259 if (del
.empty() == false)
260 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
261 if (inst
.empty() == false)
262 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
264 fprintf(output
, "Upgrade: yes\n");
265 if (DistUpgrade
== true)
266 fprintf(output
, "Dist-Upgrade: yes\n");
267 if (AutoRemove
== true)
268 fprintf(output
, "Autoremove: yes\n");
269 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
270 fprintf(output
, "Strict-Pinning: no\n");
271 string
solverpref("APT::Solver::");
272 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
273 if (_config
->Exists(solverpref
) == true)
274 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
275 fprintf(output
, "\n");
280 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
281 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
282 /* We build an map id to mmap offset here
283 In theory we could use the offset as ID, but then VersionCount
284 couldn't be used to create other versionmappings anymore and it
285 would be too easy for a (buggy) solver to segfault APTā¦ */
286 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
287 unsigned long VerIdx
[VersionCount
];
288 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
289 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
290 VerIdx
[V
->ID
] = V
.Index();
291 Cache
[P
].Marked
= true;
292 Cache
[P
].Garbage
= false;
296 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
297 pkgTagFile
response(&in
, 100);
298 pkgTagSection section
;
300 while (response
.Step(section
) == true) {
302 if (section
.Exists("Install") == true)
304 else if (section
.Exists("Remove") == true)
306 else if (section
.Exists("Progress") == true) {
307 if (Progress
!= NULL
) {
308 string msg
= section
.FindS("Message");
309 if (msg
.empty() == true)
310 msg
= _("Prepare for receiving solution");
311 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
314 } else if (section
.Exists("Error") == true) {
315 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
316 if (msg
.empty() == true) {
317 msg
= _("External solver failed without a proper error message");
318 _error
->Error("%s", msg
.c_str());
320 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
321 if (Progress
!= NULL
)
323 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
324 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
325 std::cerr
<< msg
<< std::endl
<< std::endl
;
327 } else if (section
.Exists("Autoremove") == true)
332 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
333 if (id
== VersionCount
) {
334 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
336 } else if (id
> Cache
.Head().VersionCount
) {
337 _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());
341 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
342 Cache
.SetCandidateVersion(Ver
);
343 if (type
== "Install")
345 pkgCache::PkgIterator
const P
= Ver
.ParentPkg();
346 if (Cache
[P
].Mode
!= pkgDepCache::ModeInstall
)
347 Cache
.MarkInstall(P
, false, 0, false);
349 else if (type
== "Remove")
350 Cache
.MarkDelete(Ver
.ParentPkg(), false);
351 else if (type
== "Autoremove") {
352 Cache
[Ver
.ParentPkg()].Marked
= false;
353 Cache
[Ver
.ParentPkg()].Garbage
= true;
359 // ReadLine - first line from the given file descriptor /*{{{*/
360 // ---------------------------------------------------------------------
361 /* Little helper method to read a complete line into a string. Similar to
362 fgets but we need to use the low-level read() here as otherwise the
363 listparser will be confused later on as mixing of fgets and read isn't
364 a supported action according to the manpages and results are undefined */
365 static bool ReadLine(int const input
, std::string
&line
) {
370 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
377 if (line
.empty() == true && isblank(one
) != 0)
384 // StringToBool - convert yes/no to bool /*{{{*/
385 // ---------------------------------------------------------------------
386 /* we are not as lazy as we are in the global StringToBool as we really
387 only accept yes/no here - but we will ignore leading spaces */
388 static bool StringToBool(char const *answer
, bool const defValue
) {
389 for (; isspace(*answer
) != 0; ++answer
);
390 if (strncasecmp(answer
, "yes", 3) == 0)
392 else if (strncasecmp(answer
, "no", 2) == 0)
395 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
399 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
400 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
401 std::list
<std::string
> &remove
, bool &upgrade
,
402 bool &distUpgrade
, bool &autoRemove
)
410 while (ReadLine(input
, line
) == true)
412 // Skip empty lines before request
413 if (line
.empty() == true)
415 // The first Tag must be a request, so search for it
416 if (line
.compare(0, 8, "Request:") != 0)
419 while (ReadLine(input
, line
) == true)
421 // empty lines are the end of the request
422 if (line
.empty() == true)
425 std::list
<std::string
> *request
= NULL
;
426 if (line
.compare(0, 8, "Install:") == 0)
431 else if (line
.compare(0, 7, "Remove:") == 0)
436 else if (line
.compare(0, 8, "Upgrade:") == 0)
437 upgrade
= StringToBool(line
.c_str() + 9, false);
438 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
439 distUpgrade
= StringToBool(line
.c_str() + 14, false);
440 else if (line
.compare(0, 11, "Autoremove:") == 0)
441 autoRemove
= StringToBool(line
.c_str() + 12, false);
442 else if (line
.compare(0, 13, "Architecture:") == 0)
443 _config
->Set("APT::Architecture", line
.c_str() + 14);
444 else if (line
.compare(0, 14, "Architectures:") == 0)
446 std::string
const archs
= line
.c_str() + 15;
447 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
450 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
454 size_t end
= line
.length();
456 size_t begin
= line
.rfind(' ');
457 if (begin
== std::string::npos
)
459 request
->push_back(line
.substr(0, end
));
462 else if (begin
< end
)
463 request
->push_back(line
.substr(begin
+ 1, end
));
465 end
= line
.find_last_not_of(' ');
466 } while (end
!= std::string::npos
);
472 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
473 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
474 std::list
<std::string
> const &remove
,
477 for (std::list
<std::string
>::const_iterator i
= install
.begin();
478 i
!= install
.end(); ++i
) {
479 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
481 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
483 Cache
.MarkInstall(P
, false);
486 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
487 i
!= remove
.end(); ++i
) {
488 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
490 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
497 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
498 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
500 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
501 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
503 if (Cache
[Pkg
].Delete() == true)
505 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
507 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
509 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
511 pkgCache::VerIterator
const CandVer
= Cache
.GetCandidateVersion(Pkg
);
512 fprintf(output
, "Install: %d\n", CandVer
->ID
);
514 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), CandVer
.VerStr());
516 else if (Cache
[Pkg
].Garbage
== true)
518 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
520 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
524 fprintf(output
, "\n");
530 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
531 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
532 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
533 fprintf(output
, "Percentage: %d\n", percent
);
534 fprintf(output
, "Message: %s\n\n", message
);
539 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
540 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
541 fprintf(output
, "Error: %s\n", uuid
);
542 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
546 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
547 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
548 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
550 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
551 dir
!= solverDirs
.end(); ++dir
) {
552 file
= flCombine(*dir
, solver
);
553 if (RealFileExists(file
.c_str()) == true)
558 if (file
.empty() == true)
560 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
563 int external
[4] = {-1, -1, -1, -1};
564 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
566 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
569 for (int i
= 0; i
< 4; ++i
)
570 SetCloseExec(external
[i
], true);
572 pid_t Solver
= ExecFork();
574 dup2(external
[0], STDIN_FILENO
);
575 dup2(external
[3], STDOUT_FILENO
);
576 const char* calling
[2] = { file
.c_str(), 0 };
577 execv(calling
[0], (char**) calling
);
578 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
584 if (WaitFd(external
[1], true, 5) == false)
586 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
590 *solver_in
= external
[1];
591 *solver_out
= external
[2];
594 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
595 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
600 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
601 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
602 bool const upgrade
, bool const distUpgrade
,
603 bool const autoRemove
, OpProgress
*Progress
) {
604 int solver_in
, solver_out
;
605 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
609 FILE* output
= fdopen(solver_in
, "w");
611 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
613 if (Progress
!= NULL
)
614 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
615 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
616 if (Progress
!= NULL
)
617 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
618 EDSP::WriteScenario(Cache
, output
, Progress
);
621 if (Progress
!= NULL
)
622 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
623 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
626 return ExecWait(solver_pid
, solver
);