]>
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/edsp.h>
11 #include <apt-pkg/error.h>
12 #include <apt-pkg/cacheset.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/tagfile.h>
15 #include <apt-pkg/fileutl.h>
16 #include <apt-pkg/progress.h>
17 #include <apt-pkg/depcache.h>
18 #include <apt-pkg/pkgcache.h>
19 #include <apt-pkg/cacheiterators.h>
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/pkgrecords.h>
40 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
41 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
43 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
44 "Recommends" , "Conflicts", "Replaces",
45 "Obsoletes", "Breaks", "Enhances"};
47 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
48 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
51 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
53 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
54 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
56 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
57 WriteScenarioDependency(output
, Ver
);
58 fprintf(output
, "\n");
59 if (Progress
!= NULL
&& p
% 100 == 0)
60 Progress
->Progress(p
);
65 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
66 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
67 APT::PackageSet
const &pkgset
,
71 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
73 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
74 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
76 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
77 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
78 fprintf(output
, "\n");
79 if (Progress
!= NULL
&& p
% 100 == 0)
80 Progress
->Progress(p
);
87 // EDSP::WriteScenarioVersion /*{{{*/
88 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
89 pkgCache::VerIterator
const &Ver
)
91 pkgRecords
Recs(Cache
);
92 pkgRecords::Parser
&rec
= Recs
.Lookup(Ver
.FileList());
93 string srcpkg
= rec
.SourcePkg().empty() ? Pkg
.Name() : rec
.SourcePkg();
95 fprintf(output
, "Package: %s\n", Pkg
.Name());
96 fprintf(output
, "Source: %s\n", srcpkg
.c_str());
97 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
98 fprintf(output
, "Version: %s\n", Ver
.VerStr());
99 if (Pkg
.CurrentVer() == Ver
)
100 fprintf(output
, "Installed: yes\n");
101 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
102 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
103 fprintf(output
, "Hold: yes\n");
104 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
105 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
106 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
107 fprintf(output
, "Essential: yes\n");
108 fprintf(output
, "Section: %s\n", Ver
.Section());
109 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
110 fprintf(output
, "Multi-Arch: allowed\n");
111 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
112 fprintf(output
, "Multi-Arch: foreign\n");
113 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
114 fprintf(output
, "Multi-Arch: same\n");
115 signed short Pin
= std::numeric_limits
<signed short>::min();
116 std::set
<string
> Releases
;
117 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
118 pkgCache::PkgFileIterator File
= I
.File();
119 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
122 if ((File
->Flags
& pkgCache::Flag::NotSource
) != pkgCache::Flag::NotSource
) {
123 string Release
= File
.RelStr();
124 if (!Release
.empty())
125 Releases
.insert(Release
);
128 if (!Releases
.empty()) {
129 fprintf(output
, "APT-Release:\n");
130 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
131 fprintf(output
, " %s\n", R
->c_str());
133 fprintf(output
, "APT-Pin: %d\n", Pin
);
134 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
135 fprintf(output
, "APT-Candidate: yes\n");
136 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
137 fprintf(output
, "APT-Automatic: yes\n");
140 // EDSP::WriteScenarioDependency /*{{{*/
141 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
143 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
144 bool orGroup
= false;
145 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
147 if (Dep
.IsMultiArchImplicit() == true)
149 if (orGroup
== false)
150 dependencies
[Dep
->Type
].append(", ");
151 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
152 if (Dep
->Version
!= 0)
153 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
154 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
156 dependencies
[Dep
->Type
].append(" | ");
162 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
163 if (dependencies
[i
].empty() == false)
164 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
166 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
168 if (Prv
.IsMultiArchImplicit() == true)
170 provides
.append(", ").append(Prv
.Name());
172 if (provides
.empty() == false)
173 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
176 // EDSP::WriteScenarioLimitedDependency /*{{{*/
177 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
178 pkgCache::VerIterator
const &Ver
,
179 APT::PackageSet
const &pkgset
)
181 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
182 bool orGroup
= false;
183 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
185 if (Dep
.IsMultiArchImplicit() == true)
187 if (orGroup
== false)
189 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
191 dependencies
[Dep
->Type
].append(", ");
193 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
195 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
197 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
201 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
202 if (Dep
->Version
!= 0)
203 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
204 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
206 dependencies
[Dep
->Type
].append(" | ");
212 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
213 if (dependencies
[i
].empty() == false)
214 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
216 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
218 if (Prv
.IsMultiArchImplicit() == true)
220 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
222 provides
.append(", ").append(Prv
.Name());
224 if (provides
.empty() == false)
225 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
228 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
229 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
230 bool const DistUpgrade
, bool const AutoRemove
,
231 OpProgress
*Progress
)
233 if (Progress
!= NULL
)
234 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
237 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
239 if (Progress
!= NULL
&& p
% 100 == 0)
240 Progress
->Progress(p
);
242 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
243 if (P
.Delete() == true)
245 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
246 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
250 req
->append(" ").append(Pkg
.FullName());
252 fprintf(output
, "Request: EDSP 0.5\n");
254 const char *arch
= _config
->Find("APT::Architecture").c_str();
255 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
256 fprintf(output
, "Architecture: %s\n", arch
);
257 fprintf(output
, "Architectures:");
258 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
259 fprintf(output
, " %s", a
->c_str());
260 fprintf(output
, "\n");
262 if (del
.empty() == false)
263 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
264 if (inst
.empty() == false)
265 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
267 fprintf(output
, "Upgrade: yes\n");
268 if (DistUpgrade
== true)
269 fprintf(output
, "Dist-Upgrade: yes\n");
270 if (AutoRemove
== true)
271 fprintf(output
, "Autoremove: yes\n");
272 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
273 fprintf(output
, "Strict-Pinning: no\n");
274 string
solverpref("APT::Solver::");
275 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
276 if (_config
->Exists(solverpref
) == true)
277 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
278 fprintf(output
, "\n");
283 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
284 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
285 /* We build an map id to mmap offset here
286 In theory we could use the offset as ID, but then VersionCount
287 couldn't be used to create other versionmappings anymore and it
288 would be too easy for a (buggy) solver to segfault APTā¦ */
289 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
290 unsigned long VerIdx
[VersionCount
];
291 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
292 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
293 VerIdx
[V
->ID
] = V
.Index();
294 Cache
[P
].Marked
= true;
295 Cache
[P
].Garbage
= false;
299 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
300 pkgTagFile
response(&in
, 100);
301 pkgTagSection section
;
303 while (response
.Step(section
) == true) {
305 if (section
.Exists("Install") == true)
307 else if (section
.Exists("Remove") == true)
309 else if (section
.Exists("Progress") == true) {
310 if (Progress
!= NULL
) {
311 string msg
= section
.FindS("Message");
312 if (msg
.empty() == true)
313 msg
= _("Prepare for receiving solution");
314 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
317 } else if (section
.Exists("Error") == true) {
318 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
319 if (msg
.empty() == true) {
320 msg
= _("External solver failed without a proper error message");
321 _error
->Error("%s", msg
.c_str());
323 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
324 if (Progress
!= NULL
)
326 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
327 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
328 std::cerr
<< msg
<< std::endl
<< std::endl
;
330 } else if (section
.Exists("Autoremove") == true)
335 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
336 if (id
== VersionCount
) {
337 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
339 } else if (id
> Cache
.Head().VersionCount
) {
340 _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());
344 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
345 Cache
.SetCandidateVersion(Ver
);
346 if (type
== "Install")
347 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
348 else if (type
== "Remove")
349 Cache
.MarkDelete(Ver
.ParentPkg(), false);
350 else if (type
== "Autoremove") {
351 Cache
[Ver
.ParentPkg()].Marked
= false;
352 Cache
[Ver
.ParentPkg()].Garbage
= true;
358 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
359 // ---------------------------------------------------------------------
360 /* Little helper method to read a complete line into a string. Similar to
361 fgets but we need to use the low-level read() here as otherwise the
362 listparser will be confused later on as mixing of fgets and read isn't
363 a supported action according to the manpages and results are undefined */
364 bool EDSP::ReadLine(int const input
, std::string
&line
) {
369 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
376 if (line
.empty() == true && isblank(one
) != 0)
383 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
384 // ---------------------------------------------------------------------
385 /* we are not as lazy as we are in the global StringToBool as we really
386 only accept yes/no here - but we will ignore leading spaces */
387 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
388 for (; isspace(*answer
) != 0; ++answer
);
389 if (strncasecmp(answer
, "yes", 3) == 0)
391 else if (strncasecmp(answer
, "no", 2) == 0)
394 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
398 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
399 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
400 std::list
<std::string
> &remove
, bool &upgrade
,
401 bool &distUpgrade
, bool &autoRemove
)
409 while (ReadLine(input
, line
) == true)
411 // Skip empty lines before request
412 if (line
.empty() == true)
414 // The first Tag must be a request, so search for it
415 if (line
.compare(0, 8, "Request:") != 0)
418 while (ReadLine(input
, line
) == true)
420 // empty lines are the end of the request
421 if (line
.empty() == true)
424 std::list
<std::string
> *request
= NULL
;
425 if (line
.compare(0, 8, "Install:") == 0)
430 else if (line
.compare(0, 7, "Remove:") == 0)
435 else if (line
.compare(0, 8, "Upgrade:") == 0)
436 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
437 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
438 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
439 else if (line
.compare(0, 11, "Autoremove:") == 0)
440 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
441 else if (line
.compare(0, 13, "Architecture:") == 0)
442 _config
->Set("APT::Architecture", line
.c_str() + 14);
443 else if (line
.compare(0, 14, "Architectures:") == 0)
445 std::string
const archs
= line
.c_str() + 15;
446 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
449 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
453 size_t end
= line
.length();
455 size_t begin
= line
.rfind(' ');
456 if (begin
== std::string::npos
)
458 request
->push_back(line
.substr(0, end
));
461 else if (begin
< end
)
462 request
->push_back(line
.substr(begin
+ 1, end
));
464 end
= line
.find_last_not_of(' ');
465 } while (end
!= std::string::npos
);
471 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
472 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
473 std::list
<std::string
> const &remove
,
476 for (std::list
<std::string
>::const_iterator i
= install
.begin();
477 i
!= install
.end(); ++i
) {
478 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
480 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
482 Cache
.MarkInstall(P
, false);
485 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
486 i
!= remove
.end(); ++i
) {
487 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
489 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
496 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
497 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
499 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
500 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
502 if (Cache
[Pkg
].Delete() == true)
504 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
506 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
508 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
510 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
512 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
514 else if (Cache
[Pkg
].Garbage
== true)
516 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
518 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
522 fprintf(output
, "\n");
528 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
529 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
530 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
531 fprintf(output
, "Percentage: %d\n", percent
);
532 fprintf(output
, "Message: %s\n\n", message
);
537 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
538 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
539 fprintf(output
, "Error: %s\n", uuid
);
540 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
544 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
545 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
546 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
548 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
549 dir
!= solverDirs
.end(); ++dir
) {
550 file
= flCombine(*dir
, solver
);
551 if (RealFileExists(file
.c_str()) == true)
556 if (file
.empty() == true)
558 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
561 int external
[4] = {-1, -1, -1, -1};
562 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
564 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
567 for (int i
= 0; i
< 4; ++i
)
568 SetCloseExec(external
[i
], true);
570 pid_t Solver
= ExecFork();
572 dup2(external
[0], STDIN_FILENO
);
573 dup2(external
[3], STDOUT_FILENO
);
574 const char* calling
[2] = { file
.c_str(), 0 };
575 execv(calling
[0], (char**) calling
);
576 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
582 if (WaitFd(external
[1], true, 5) == false)
584 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
588 *solver_in
= external
[1];
589 *solver_out
= external
[2];
592 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
593 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
598 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
599 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
600 bool const upgrade
, bool const distUpgrade
,
601 bool const autoRemove
, OpProgress
*Progress
) {
602 int solver_in
, solver_out
;
603 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
607 FILE* output
= fdopen(solver_in
, "w");
609 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
611 if (Progress
!= NULL
)
612 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
613 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
614 if (Progress
!= NULL
)
615 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
616 EDSP::WriteScenario(Cache
, output
, Progress
);
619 if (Progress
!= NULL
)
620 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
621 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
624 return ExecWait(solver_pid
, solver
);