]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
225d86de0a873e66d8f2ab631d5a91f250ac5937
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 signed short Pin
= std::numeric_limits
<signed short>::min();
69 std::set
<string
> Releases
;
70 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
71 pkgCache::PkgFileIterator File
= I
.File();
72 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
75 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
76 string Release
= File
.RelStr();
78 Releases
.insert(Release
);
81 if (!Releases
.empty()) {
82 fprintf(output
, "APT-Release:\n");
83 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
84 fprintf(output
, " %s\n", R
->c_str());
86 fprintf(output
, "APT-Pin: %d\n", Pin
);
87 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
88 fprintf(output
, "APT-Candidate: yes\n");
89 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
90 fprintf(output
, "APT-Automatic: yes\n");
93 // WriteScenarioDependency /*{{{*/
94 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
96 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
98 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
100 if (Dep
.IsImplicit() == true)
102 if (orGroup
== false)
103 dependencies
[Dep
->Type
].append(", ");
104 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
105 if (Dep
->Version
!= 0)
106 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
107 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
109 dependencies
[Dep
->Type
].append(" | ");
115 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
116 if (dependencies
[i
].empty() == false)
117 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
119 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
121 if (Prv
.IsMultiArchImplicit() == true)
123 provides
.append(", ").append(Prv
.Name());
125 if (provides
.empty() == false)
126 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
129 // WriteScenarioLimitedDependency /*{{{*/
130 static void WriteScenarioLimitedDependency(FILE* output
,
131 pkgCache::VerIterator
const &Ver
,
132 APT::PackageSet
const &pkgset
)
134 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
135 bool orGroup
= false;
136 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
138 if (Dep
.IsImplicit() == true)
140 if (orGroup
== false)
142 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
144 dependencies
[Dep
->Type
].append(", ");
146 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
148 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
150 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
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 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
175 provides
.append(", ").append(Prv
.Name());
177 if (provides
.empty() == false)
178 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
181 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
182 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
184 if (Progress
!= NULL
)
185 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
187 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
188 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
190 std::string
const arch
= Pkg
.Arch();
191 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
193 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
195 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
196 WriteScenarioDependency(output
, Ver
);
197 fprintf(output
, "\n");
198 if (Progress
!= NULL
&& p
% 100 == 0)
199 Progress
->Progress(p
);
205 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
206 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
207 APT::PackageSet
const &pkgset
,
208 OpProgress
*Progress
)
210 if (Progress
!= NULL
)
211 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
213 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
214 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
216 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
217 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
218 fprintf(output
, "\n");
219 if (Progress
!= NULL
&& p
% 100 == 0)
220 Progress
->Progress(p
);
222 if (Progress
!= NULL
)
227 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
228 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
229 bool const DistUpgrade
, bool const AutoRemove
,
230 OpProgress
*Progress
)
232 if (Progress
!= NULL
)
233 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
236 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
238 if (Progress
!= NULL
&& p
% 100 == 0)
239 Progress
->Progress(p
);
241 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
242 if (P
.Delete() == true)
244 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
245 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
249 req
->append(" ").append(Pkg
.FullName());
251 fprintf(output
, "Request: EDSP 0.5\n");
253 const char *arch
= _config
->Find("APT::Architecture").c_str();
254 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
255 fprintf(output
, "Architecture: %s\n", arch
);
256 fprintf(output
, "Architectures:");
257 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
258 fprintf(output
, " %s", a
->c_str());
259 fprintf(output
, "\n");
261 if (del
.empty() == false)
262 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
263 if (inst
.empty() == false)
264 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
266 fprintf(output
, "Upgrade: yes\n");
267 if (DistUpgrade
== true)
268 fprintf(output
, "Dist-Upgrade: yes\n");
269 if (AutoRemove
== true)
270 fprintf(output
, "Autoremove: yes\n");
271 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
272 fprintf(output
, "Strict-Pinning: no\n");
273 string
solverpref("APT::Solver::");
274 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
275 if (_config
->Exists(solverpref
) == true)
276 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
277 fprintf(output
, "\n");
282 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
283 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
284 /* We build an map id to mmap offset here
285 In theory we could use the offset as ID, but then VersionCount
286 couldn't be used to create other versionmappings anymore and it
287 would be too easy for a (buggy) solver to segfault APTā¦ */
288 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
289 unsigned long VerIdx
[VersionCount
];
290 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
291 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
292 VerIdx
[V
->ID
] = V
.Index();
293 Cache
[P
].Marked
= true;
294 Cache
[P
].Garbage
= false;
298 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
299 pkgTagFile
response(&in
, 100);
300 pkgTagSection section
;
302 while (response
.Step(section
) == true) {
304 if (section
.Exists("Install") == true)
306 else if (section
.Exists("Remove") == true)
308 else if (section
.Exists("Progress") == true) {
309 if (Progress
!= NULL
) {
310 string msg
= section
.FindS("Message");
311 if (msg
.empty() == true)
312 msg
= _("Prepare for receiving solution");
313 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
316 } else if (section
.Exists("Error") == true) {
317 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
318 if (msg
.empty() == true) {
319 msg
= _("External solver failed without a proper error message");
320 _error
->Error("%s", msg
.c_str());
322 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
323 if (Progress
!= NULL
)
325 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
326 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
327 std::cerr
<< msg
<< std::endl
<< std::endl
;
329 } else if (section
.Exists("Autoremove") == true)
334 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
335 if (id
== VersionCount
) {
336 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
338 } else if (id
> Cache
.Head().VersionCount
) {
339 _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());
343 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
344 Cache
.SetCandidateVersion(Ver
);
345 if (type
== "Install")
346 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
347 else if (type
== "Remove")
348 Cache
.MarkDelete(Ver
.ParentPkg(), false);
349 else if (type
== "Autoremove") {
350 Cache
[Ver
.ParentPkg()].Marked
= false;
351 Cache
[Ver
.ParentPkg()].Garbage
= true;
357 // ReadLine - first line from the given file descriptor /*{{{*/
358 // ---------------------------------------------------------------------
359 /* Little helper method to read a complete line into a string. Similar to
360 fgets but we need to use the low-level read() here as otherwise the
361 listparser will be confused later on as mixing of fgets and read isn't
362 a supported action according to the manpages and results are undefined */
363 static bool ReadLine(int const input
, std::string
&line
) {
368 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
375 if (line
.empty() == true && isblank(one
) != 0)
382 // StringToBool - convert yes/no to bool /*{{{*/
383 // ---------------------------------------------------------------------
384 /* we are not as lazy as we are in the global StringToBool as we really
385 only accept yes/no here - but we will ignore leading spaces */
386 static bool StringToBool(char const *answer
, bool const defValue
) {
387 for (; isspace(*answer
) != 0; ++answer
);
388 if (strncasecmp(answer
, "yes", 3) == 0)
390 else if (strncasecmp(answer
, "no", 2) == 0)
393 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
397 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
398 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
399 std::list
<std::string
> &remove
, bool &upgrade
,
400 bool &distUpgrade
, bool &autoRemove
)
408 while (ReadLine(input
, line
) == true)
410 // Skip empty lines before request
411 if (line
.empty() == true)
413 // The first Tag must be a request, so search for it
414 if (line
.compare(0, 8, "Request:") != 0)
417 while (ReadLine(input
, line
) == true)
419 // empty lines are the end of the request
420 if (line
.empty() == true)
423 std::list
<std::string
> *request
= NULL
;
424 if (line
.compare(0, 8, "Install:") == 0)
429 else if (line
.compare(0, 7, "Remove:") == 0)
434 else if (line
.compare(0, 8, "Upgrade:") == 0)
435 upgrade
= StringToBool(line
.c_str() + 9, false);
436 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
437 distUpgrade
= StringToBool(line
.c_str() + 14, false);
438 else if (line
.compare(0, 11, "Autoremove:") == 0)
439 autoRemove
= StringToBool(line
.c_str() + 12, false);
440 else if (line
.compare(0, 13, "Architecture:") == 0)
441 _config
->Set("APT::Architecture", line
.c_str() + 14);
442 else if (line
.compare(0, 14, "Architectures:") == 0)
444 std::string
const archs
= line
.c_str() + 15;
445 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
448 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
452 size_t end
= line
.length();
454 size_t begin
= line
.rfind(' ');
455 if (begin
== std::string::npos
)
457 request
->push_back(line
.substr(0, end
));
460 else if (begin
< end
)
461 request
->push_back(line
.substr(begin
+ 1, end
));
463 end
= line
.find_last_not_of(' ');
464 } while (end
!= std::string::npos
);
470 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
471 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
472 std::list
<std::string
> const &remove
,
475 for (std::list
<std::string
>::const_iterator i
= install
.begin();
476 i
!= install
.end(); ++i
) {
477 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
479 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
481 Cache
.MarkInstall(P
, false);
484 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
485 i
!= remove
.end(); ++i
) {
486 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
488 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
495 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
496 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
498 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
499 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
501 if (Cache
[Pkg
].Delete() == true)
503 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
505 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
507 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
509 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
511 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
513 else if (Cache
[Pkg
].Garbage
== true)
515 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
517 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
521 fprintf(output
, "\n");
527 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
528 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
529 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
530 fprintf(output
, "Percentage: %d\n", percent
);
531 fprintf(output
, "Message: %s\n\n", message
);
536 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
537 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
538 fprintf(output
, "Error: %s\n", uuid
);
539 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
543 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
544 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
545 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
547 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
548 dir
!= solverDirs
.end(); ++dir
) {
549 file
= flCombine(*dir
, solver
);
550 if (RealFileExists(file
.c_str()) == true)
555 if (file
.empty() == true)
557 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
560 int external
[4] = {-1, -1, -1, -1};
561 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
563 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
566 for (int i
= 0; i
< 4; ++i
)
567 SetCloseExec(external
[i
], true);
569 pid_t Solver
= ExecFork();
571 dup2(external
[0], STDIN_FILENO
);
572 dup2(external
[3], STDOUT_FILENO
);
573 const char* calling
[2] = { file
.c_str(), 0 };
574 execv(calling
[0], (char**) calling
);
575 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
581 if (WaitFd(external
[1], true, 5) == false)
583 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
587 *solver_in
= external
[1];
588 *solver_out
= external
[2];
591 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
592 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
597 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
598 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
599 bool const upgrade
, bool const distUpgrade
,
600 bool const autoRemove
, OpProgress
*Progress
) {
601 int solver_in
, solver_out
;
602 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
606 FILE* output
= fdopen(solver_in
, "w");
608 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
610 if (Progress
!= NULL
)
611 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
612 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
613 if (Progress
!= NULL
)
614 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
615 EDSP::WriteScenario(Cache
, output
, Progress
);
618 if (Progress
!= NULL
)
619 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
620 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
623 return ExecWait(solver_pid
, solver
);