]>
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>
41 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
42 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
44 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
45 "Recommends" , "Conflicts", "Replaces",
46 "Obsoletes", "Breaks", "Enhances"};
48 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
49 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
52 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
54 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
55 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
57 std::string
const arch
= Pkg
.Arch();
58 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
60 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
62 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
63 WriteScenarioDependency(output
, Ver
);
64 fprintf(output
, "\n");
65 if (Progress
!= NULL
&& p
% 100 == 0)
66 Progress
->Progress(p
);
72 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
73 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
74 APT::PackageSet
const &pkgset
,
78 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
80 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
81 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
83 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
84 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
85 fprintf(output
, "\n");
86 if (Progress
!= NULL
&& p
% 100 == 0)
87 Progress
->Progress(p
);
94 // EDSP::WriteScenarioVersion /*{{{*/
95 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
96 pkgCache::VerIterator
const &Ver
)
98 fprintf(output
, "Package: %s\n", Pkg
.Name());
99 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
100 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
101 fprintf(output
, "Version: %s\n", Ver
.VerStr());
102 if (Pkg
.CurrentVer() == Ver
)
103 fprintf(output
, "Installed: yes\n");
104 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
105 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
106 fprintf(output
, "Hold: yes\n");
107 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
108 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
109 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
110 fprintf(output
, "Essential: yes\n");
111 fprintf(output
, "Section: %s\n", Ver
.Section());
112 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
113 fprintf(output
, "Multi-Arch: allowed\n");
114 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
115 fprintf(output
, "Multi-Arch: foreign\n");
116 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
117 fprintf(output
, "Multi-Arch: same\n");
118 signed short Pin
= std::numeric_limits
<signed short>::min();
119 std::set
<string
> Releases
;
120 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
121 pkgCache::PkgFileIterator File
= I
.File();
122 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
125 if ((File
->Flags
& pkgCache::Flag::NotSource
) != pkgCache::Flag::NotSource
) {
126 string Release
= File
.RelStr();
127 if (!Release
.empty())
128 Releases
.insert(Release
);
131 if (!Releases
.empty()) {
132 fprintf(output
, "APT-Release:\n");
133 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
134 fprintf(output
, " %s\n", R
->c_str());
136 fprintf(output
, "APT-Pin: %d\n", Pin
);
137 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
138 fprintf(output
, "APT-Candidate: yes\n");
139 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
140 fprintf(output
, "APT-Automatic: yes\n");
143 // EDSP::WriteScenarioDependency /*{{{*/
144 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
146 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
147 bool orGroup
= false;
148 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
150 if (Dep
.IsMultiArchImplicit() == true)
152 if (orGroup
== false)
153 dependencies
[Dep
->Type
].append(", ");
154 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
155 if (Dep
->Version
!= 0)
156 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
157 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
159 dependencies
[Dep
->Type
].append(" | ");
165 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
166 if (dependencies
[i
].empty() == false)
167 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
169 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
171 if (Prv
.IsMultiArchImplicit() == true)
173 provides
.append(", ").append(Prv
.Name());
175 if (provides
.empty() == false)
176 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
179 // EDSP::WriteScenarioLimitedDependency /*{{{*/
180 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
181 pkgCache::VerIterator
const &Ver
,
182 APT::PackageSet
const &pkgset
)
184 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
185 bool orGroup
= false;
186 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
188 if (Dep
.IsMultiArchImplicit() == true)
190 if (orGroup
== false)
192 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
194 dependencies
[Dep
->Type
].append(", ");
196 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
198 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
200 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
204 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
205 if (Dep
->Version
!= 0)
206 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
207 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
209 dependencies
[Dep
->Type
].append(" | ");
215 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
216 if (dependencies
[i
].empty() == false)
217 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
219 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
221 if (Prv
.IsMultiArchImplicit() == true)
223 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
225 provides
.append(", ").append(Prv
.Name());
227 if (provides
.empty() == false)
228 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
231 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
232 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
233 bool const DistUpgrade
, bool const AutoRemove
,
234 OpProgress
*Progress
)
236 if (Progress
!= NULL
)
237 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
240 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
242 if (Progress
!= NULL
&& p
% 100 == 0)
243 Progress
->Progress(p
);
245 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
246 if (P
.Delete() == true)
248 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
249 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
253 req
->append(" ").append(Pkg
.FullName());
255 fprintf(output
, "Request: EDSP 0.5\n");
257 const char *arch
= _config
->Find("APT::Architecture").c_str();
258 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
259 fprintf(output
, "Architecture: %s\n", arch
);
260 fprintf(output
, "Architectures:");
261 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
262 fprintf(output
, " %s", a
->c_str());
263 fprintf(output
, "\n");
265 if (del
.empty() == false)
266 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
267 if (inst
.empty() == false)
268 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
270 fprintf(output
, "Upgrade: yes\n");
271 if (DistUpgrade
== true)
272 fprintf(output
, "Dist-Upgrade: yes\n");
273 if (AutoRemove
== true)
274 fprintf(output
, "Autoremove: yes\n");
275 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
276 fprintf(output
, "Strict-Pinning: no\n");
277 string
solverpref("APT::Solver::");
278 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
279 if (_config
->Exists(solverpref
) == true)
280 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
281 fprintf(output
, "\n");
286 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
287 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
288 /* We build an map id to mmap offset here
289 In theory we could use the offset as ID, but then VersionCount
290 couldn't be used to create other versionmappings anymore and it
291 would be too easy for a (buggy) solver to segfault APTā¦ */
292 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
293 unsigned long VerIdx
[VersionCount
];
294 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
295 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
296 VerIdx
[V
->ID
] = V
.Index();
297 Cache
[P
].Marked
= true;
298 Cache
[P
].Garbage
= false;
302 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
303 pkgTagFile
response(&in
, 100);
304 pkgTagSection section
;
306 while (response
.Step(section
) == true) {
308 if (section
.Exists("Install") == true)
310 else if (section
.Exists("Remove") == true)
312 else if (section
.Exists("Progress") == true) {
313 if (Progress
!= NULL
) {
314 string msg
= section
.FindS("Message");
315 if (msg
.empty() == true)
316 msg
= _("Prepare for receiving solution");
317 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
320 } else if (section
.Exists("Error") == true) {
321 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
322 if (msg
.empty() == true) {
323 msg
= _("External solver failed without a proper error message");
324 _error
->Error("%s", msg
.c_str());
326 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
327 if (Progress
!= NULL
)
329 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
330 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
331 std::cerr
<< msg
<< std::endl
<< std::endl
;
333 } else if (section
.Exists("Autoremove") == true)
338 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
339 if (id
== VersionCount
) {
340 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
342 } else if (id
> Cache
.Head().VersionCount
) {
343 _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());
347 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
348 Cache
.SetCandidateVersion(Ver
);
349 if (type
== "Install")
350 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
351 else if (type
== "Remove")
352 Cache
.MarkDelete(Ver
.ParentPkg(), false);
353 else if (type
== "Autoremove") {
354 Cache
[Ver
.ParentPkg()].Marked
= false;
355 Cache
[Ver
.ParentPkg()].Garbage
= true;
361 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
362 // ---------------------------------------------------------------------
363 /* Little helper method to read a complete line into a string. Similar to
364 fgets but we need to use the low-level read() here as otherwise the
365 listparser will be confused later on as mixing of fgets and read isn't
366 a supported action according to the manpages and results are undefined */
367 bool EDSP::ReadLine(int const input
, std::string
&line
) {
372 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
379 if (line
.empty() == true && isblank(one
) != 0)
386 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
387 // ---------------------------------------------------------------------
388 /* we are not as lazy as we are in the global StringToBool as we really
389 only accept yes/no here - but we will ignore leading spaces */
390 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
391 for (; isspace(*answer
) != 0; ++answer
);
392 if (strncasecmp(answer
, "yes", 3) == 0)
394 else if (strncasecmp(answer
, "no", 2) == 0)
397 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
401 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
402 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
403 std::list
<std::string
> &remove
, bool &upgrade
,
404 bool &distUpgrade
, bool &autoRemove
)
412 while (ReadLine(input
, line
) == true)
414 // Skip empty lines before request
415 if (line
.empty() == true)
417 // The first Tag must be a request, so search for it
418 if (line
.compare(0, 8, "Request:") != 0)
421 while (ReadLine(input
, line
) == true)
423 // empty lines are the end of the request
424 if (line
.empty() == true)
427 std::list
<std::string
> *request
= NULL
;
428 if (line
.compare(0, 8, "Install:") == 0)
433 else if (line
.compare(0, 7, "Remove:") == 0)
438 else if (line
.compare(0, 8, "Upgrade:") == 0)
439 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
440 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
441 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
442 else if (line
.compare(0, 11, "Autoremove:") == 0)
443 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
444 else if (line
.compare(0, 13, "Architecture:") == 0)
445 _config
->Set("APT::Architecture", line
.c_str() + 14);
446 else if (line
.compare(0, 14, "Architectures:") == 0)
448 std::string
const archs
= line
.c_str() + 15;
449 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
452 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
456 size_t end
= line
.length();
458 size_t begin
= line
.rfind(' ');
459 if (begin
== std::string::npos
)
461 request
->push_back(line
.substr(0, end
));
464 else if (begin
< end
)
465 request
->push_back(line
.substr(begin
+ 1, end
));
467 end
= line
.find_last_not_of(' ');
468 } while (end
!= std::string::npos
);
474 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
475 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
476 std::list
<std::string
> const &remove
,
479 for (std::list
<std::string
>::const_iterator i
= install
.begin();
480 i
!= install
.end(); ++i
) {
481 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
483 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
485 Cache
.MarkInstall(P
, false);
488 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
489 i
!= remove
.end(); ++i
) {
490 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
492 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
499 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
500 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
502 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
503 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
505 if (Cache
[Pkg
].Delete() == true)
507 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
509 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
511 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
513 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
515 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
517 else if (Cache
[Pkg
].Garbage
== true)
519 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
521 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
525 fprintf(output
, "\n");
531 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
532 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
533 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
534 fprintf(output
, "Percentage: %d\n", percent
);
535 fprintf(output
, "Message: %s\n\n", message
);
540 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
541 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
542 fprintf(output
, "Error: %s\n", uuid
);
543 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
547 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
548 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
549 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
551 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
552 dir
!= solverDirs
.end(); ++dir
) {
553 file
= flCombine(*dir
, solver
);
554 if (RealFileExists(file
.c_str()) == true)
559 if (file
.empty() == true)
561 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
564 int external
[4] = {-1, -1, -1, -1};
565 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
567 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
570 for (int i
= 0; i
< 4; ++i
)
571 SetCloseExec(external
[i
], true);
573 pid_t Solver
= ExecFork();
575 dup2(external
[0], STDIN_FILENO
);
576 dup2(external
[3], STDOUT_FILENO
);
577 const char* calling
[2] = { file
.c_str(), 0 };
578 execv(calling
[0], (char**) calling
);
579 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
585 if (WaitFd(external
[1], true, 5) == false)
587 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
591 *solver_in
= external
[1];
592 *solver_out
= external
[2];
595 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
596 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
601 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
602 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
603 bool const upgrade
, bool const distUpgrade
,
604 bool const autoRemove
, OpProgress
*Progress
) {
605 int solver_in
, solver_out
;
606 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
610 FILE* output
= fdopen(solver_in
, "w");
612 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
614 if (Progress
!= NULL
)
615 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
616 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
617 if (Progress
!= NULL
)
618 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
619 EDSP::WriteScenario(Cache
, output
, Progress
);
622 if (Progress
!= NULL
)
623 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
624 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
627 return ExecWait(solver_pid
, solver
);