]>
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 if (Pkg
.CurrentVer() == Ver
)
52 fprintf(output
, "Installed: yes\n");
53 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
54 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
55 fprintf(output
, "Hold: yes\n");
56 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
57 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
58 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
59 fprintf(output
, "Essential: yes\n");
60 fprintf(output
, "Section: %s\n", Ver
.Section());
61 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
62 fprintf(output
, "Multi-Arch: allowed\n");
63 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
64 fprintf(output
, "Multi-Arch: foreign\n");
65 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
66 fprintf(output
, "Multi-Arch: same\n");
67 signed short Pin
= std::numeric_limits
<signed short>::min();
68 std::set
<string
> Releases
;
69 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
70 pkgCache::PkgFileIterator File
= I
.File();
71 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
74 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
75 string Release
= File
.RelStr();
77 Releases
.insert(Release
);
80 if (!Releases
.empty()) {
81 fprintf(output
, "APT-Release:\n");
82 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
83 fprintf(output
, " %s\n", R
->c_str());
85 fprintf(output
, "APT-Pin: %d\n", Pin
);
86 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
87 fprintf(output
, "APT-Candidate: yes\n");
88 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
89 fprintf(output
, "APT-Automatic: yes\n");
92 // WriteScenarioDependency /*{{{*/
93 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
95 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
97 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
99 if (Dep
.IsImplicit() == true)
101 if (orGroup
== false)
102 dependencies
[Dep
->Type
].append(", ");
103 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
104 if (Dep
->Version
!= 0)
105 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
106 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
108 dependencies
[Dep
->Type
].append(" | ");
114 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
115 if (dependencies
[i
].empty() == false)
116 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
118 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
120 if (Prv
.IsMultiArchImplicit() == true)
122 provides
.append(", ").append(Prv
.Name());
124 if (provides
.empty() == false)
125 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
128 // WriteScenarioLimitedDependency /*{{{*/
129 static void WriteScenarioLimitedDependency(FILE* output
,
130 pkgCache::VerIterator
const &Ver
,
131 APT::PackageSet
const &pkgset
)
133 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
134 bool orGroup
= false;
135 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
137 if (Dep
.IsImplicit() == true)
139 if (orGroup
== false)
141 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
143 dependencies
[Dep
->Type
].append(", ");
145 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
147 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
149 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
153 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
154 if (Dep
->Version
!= 0)
155 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
156 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
158 dependencies
[Dep
->Type
].append(" | ");
164 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
165 if (dependencies
[i
].empty() == false)
166 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
168 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
170 if (Prv
.IsMultiArchImplicit() == true)
172 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
174 provides
.append(", ").append(Prv
.Name());
176 if (provides
.empty() == false)
177 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
180 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
181 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
183 if (Progress
!= NULL
)
184 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
186 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
187 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
189 std::string
const arch
= Pkg
.Arch();
190 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
192 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
194 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
195 WriteScenarioDependency(output
, Ver
);
196 fprintf(output
, "\n");
197 if (Progress
!= NULL
&& p
% 100 == 0)
198 Progress
->Progress(p
);
204 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
205 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
206 APT::PackageSet
const &pkgset
,
207 OpProgress
*Progress
)
209 if (Progress
!= NULL
)
210 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
212 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
213 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
215 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
216 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
217 fprintf(output
, "\n");
218 if (Progress
!= NULL
&& p
% 100 == 0)
219 Progress
->Progress(p
);
221 if (Progress
!= NULL
)
226 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
227 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
228 bool const DistUpgrade
, bool const AutoRemove
,
229 OpProgress
*Progress
)
231 if (Progress
!= NULL
)
232 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
235 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
237 if (Progress
!= NULL
&& p
% 100 == 0)
238 Progress
->Progress(p
);
240 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
241 if (P
.Delete() == true)
243 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
244 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
248 req
->append(" ").append(Pkg
.FullName());
250 fprintf(output
, "Request: EDSP 0.5\n");
252 const char *arch
= _config
->Find("APT::Architecture").c_str();
253 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
254 fprintf(output
, "Architecture: %s\n", arch
);
255 fprintf(output
, "Architectures:");
256 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
257 fprintf(output
, " %s", a
->c_str());
258 fprintf(output
, "\n");
260 if (del
.empty() == false)
261 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
262 if (inst
.empty() == false)
263 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
265 fprintf(output
, "Upgrade: yes\n");
266 if (DistUpgrade
== true)
267 fprintf(output
, "Dist-Upgrade: yes\n");
268 if (AutoRemove
== true)
269 fprintf(output
, "Autoremove: yes\n");
270 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
271 fprintf(output
, "Strict-Pinning: no\n");
272 string
solverpref("APT::Solver::");
273 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
274 if (_config
->Exists(solverpref
) == true)
275 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
276 fprintf(output
, "\n");
281 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
282 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
283 /* We build an map id to mmap offset here
284 In theory we could use the offset as ID, but then VersionCount
285 couldn't be used to create other versionmappings anymore and it
286 would be too easy for a (buggy) solver to segfault APTā¦ */
287 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
288 unsigned long VerIdx
[VersionCount
];
289 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
290 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
291 VerIdx
[V
->ID
] = V
.Index();
292 Cache
[P
].Marked
= true;
293 Cache
[P
].Garbage
= false;
297 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
298 pkgTagFile
response(&in
, 100);
299 pkgTagSection section
;
301 while (response
.Step(section
) == true) {
303 if (section
.Exists("Install") == true)
305 else if (section
.Exists("Remove") == true)
307 else if (section
.Exists("Progress") == true) {
308 if (Progress
!= NULL
) {
309 string msg
= section
.FindS("Message");
310 if (msg
.empty() == true)
311 msg
= _("Prepare for receiving solution");
312 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
315 } else if (section
.Exists("Error") == true) {
316 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
317 if (msg
.empty() == true) {
318 msg
= _("External solver failed without a proper error message");
319 _error
->Error("%s", msg
.c_str());
321 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
322 if (Progress
!= NULL
)
324 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
325 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
326 std::cerr
<< msg
<< std::endl
<< std::endl
;
328 } else if (section
.Exists("Autoremove") == true)
333 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
334 if (id
== VersionCount
) {
335 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
337 } else if (id
> Cache
.Head().VersionCount
) {
338 _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());
342 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
343 Cache
.SetCandidateVersion(Ver
);
344 if (type
== "Install")
345 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
346 else if (type
== "Remove")
347 Cache
.MarkDelete(Ver
.ParentPkg(), false);
348 else if (type
== "Autoremove") {
349 Cache
[Ver
.ParentPkg()].Marked
= false;
350 Cache
[Ver
.ParentPkg()].Garbage
= true;
356 // ReadLine - first line from the given file descriptor /*{{{*/
357 // ---------------------------------------------------------------------
358 /* Little helper method to read a complete line into a string. Similar to
359 fgets but we need to use the low-level read() here as otherwise the
360 listparser will be confused later on as mixing of fgets and read isn't
361 a supported action according to the manpages and results are undefined */
362 static bool ReadLine(int const input
, std::string
&line
) {
367 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
374 if (line
.empty() == true && isblank(one
) != 0)
381 // StringToBool - convert yes/no to bool /*{{{*/
382 // ---------------------------------------------------------------------
383 /* we are not as lazy as we are in the global StringToBool as we really
384 only accept yes/no here - but we will ignore leading spaces */
385 static bool StringToBool(char const *answer
, bool const defValue
) {
386 for (; isspace(*answer
) != 0; ++answer
);
387 if (strncasecmp(answer
, "yes", 3) == 0)
389 else if (strncasecmp(answer
, "no", 2) == 0)
392 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
396 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
397 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
398 std::list
<std::string
> &remove
, bool &upgrade
,
399 bool &distUpgrade
, bool &autoRemove
)
407 while (ReadLine(input
, line
) == true)
409 // Skip empty lines before request
410 if (line
.empty() == true)
412 // The first Tag must be a request, so search for it
413 if (line
.compare(0, 8, "Request:") != 0)
416 while (ReadLine(input
, line
) == true)
418 // empty lines are the end of the request
419 if (line
.empty() == true)
422 std::list
<std::string
> *request
= NULL
;
423 if (line
.compare(0, 8, "Install:") == 0)
428 else if (line
.compare(0, 7, "Remove:") == 0)
433 else if (line
.compare(0, 8, "Upgrade:") == 0)
434 upgrade
= StringToBool(line
.c_str() + 9, false);
435 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
436 distUpgrade
= StringToBool(line
.c_str() + 14, false);
437 else if (line
.compare(0, 11, "Autoremove:") == 0)
438 autoRemove
= StringToBool(line
.c_str() + 12, false);
439 else if (line
.compare(0, 13, "Architecture:") == 0)
440 _config
->Set("APT::Architecture", line
.c_str() + 14);
441 else if (line
.compare(0, 14, "Architectures:") == 0)
443 std::string
const archs
= line
.c_str() + 15;
444 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
447 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
451 size_t end
= line
.length();
453 size_t begin
= line
.rfind(' ');
454 if (begin
== std::string::npos
)
456 request
->push_back(line
.substr(0, end
));
459 else if (begin
< end
)
460 request
->push_back(line
.substr(begin
+ 1, end
));
462 end
= line
.find_last_not_of(' ');
463 } while (end
!= std::string::npos
);
469 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
470 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
471 std::list
<std::string
> const &remove
,
474 for (std::list
<std::string
>::const_iterator i
= install
.begin();
475 i
!= install
.end(); ++i
) {
476 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
478 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
480 Cache
.MarkInstall(P
, false);
483 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
484 i
!= remove
.end(); ++i
) {
485 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
487 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
494 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
495 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
497 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
498 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
500 if (Cache
[Pkg
].Delete() == true)
502 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
504 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
506 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
508 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
510 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
512 else if (Cache
[Pkg
].Garbage
== true)
514 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
516 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
520 fprintf(output
, "\n");
526 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
527 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
528 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
529 fprintf(output
, "Percentage: %d\n", percent
);
530 fprintf(output
, "Message: %s\n\n", message
);
535 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
536 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
537 fprintf(output
, "Error: %s\n", uuid
);
538 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
542 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
543 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
544 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
546 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
547 dir
!= solverDirs
.end(); ++dir
) {
548 file
= flCombine(*dir
, solver
);
549 if (RealFileExists(file
.c_str()) == true)
554 if (file
.empty() == true)
556 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
559 int external
[4] = {-1, -1, -1, -1};
560 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
562 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
565 for (int i
= 0; i
< 4; ++i
)
566 SetCloseExec(external
[i
], true);
568 pid_t Solver
= ExecFork();
570 dup2(external
[0], STDIN_FILENO
);
571 dup2(external
[3], STDOUT_FILENO
);
572 const char* calling
[2] = { file
.c_str(), 0 };
573 execv(calling
[0], (char**) calling
);
574 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
580 if (WaitFd(external
[1], true, 5) == false)
582 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
586 *solver_in
= external
[1];
587 *solver_out
= external
[2];
590 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
591 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
596 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
597 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
598 bool const upgrade
, bool const distUpgrade
,
599 bool const autoRemove
, OpProgress
*Progress
) {
600 int solver_in
, solver_out
;
601 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
605 FILE* output
= fdopen(solver_in
, "w");
607 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
609 if (Progress
!= NULL
)
610 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
611 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
612 if (Progress
!= NULL
)
613 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
614 EDSP::WriteScenario(Cache
, output
, Progress
);
617 if (Progress
!= NULL
)
618 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
619 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
622 return ExecWait(solver_pid
, solver
);