]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
b125d4bfb3b3b9e68d934225821644d7f0d5d4fa
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/version.h>
15 #include <apt-pkg/policy.h>
16 #include <apt-pkg/tagfile.h>
17 #include <apt-pkg/fileutl.h>
18 #include <apt-pkg/progress.h>
30 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
31 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
33 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
34 "Recommends" , "Conflicts", "Replaces",
35 "Obsoletes", "Breaks", "Enhances"};
37 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
38 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
41 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
43 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
44 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
46 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
47 WriteScenarioDependency(Cache
, output
, Pkg
, Ver
);
48 fprintf(output
, "\n");
49 if (Progress
!= NULL
&& p
% 100 == 0)
50 Progress
->Progress(p
);
55 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
56 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
57 APT::PackageSet
const &pkgset
,
61 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
63 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
64 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
66 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
67 WriteScenarioLimitedDependency(Cache
, output
, Pkg
, Ver
, pkgset
);
68 fprintf(output
, "\n");
69 if (Progress
!= NULL
&& p
% 100 == 0)
70 Progress
->Progress(p
);
77 // EDSP::WriteScenarioVersion /*{{{*/
78 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
79 pkgCache::VerIterator
const &Ver
)
81 fprintf(output
, "Package: %s\n", Pkg
.Name());
82 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
83 fprintf(output
, "Version: %s\n", Ver
.VerStr());
84 if (Pkg
.CurrentVer() == Ver
)
85 fprintf(output
, "Installed: yes\n");
86 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
87 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
88 fprintf(output
, "Hold: yes\n");
89 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
90 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
91 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
92 fprintf(output
, "Essential: yes\n");
93 fprintf(output
, "Section: %s\n", Ver
.Section());
94 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
95 fprintf(output
, "Multi-Arch: allowed\n");
96 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
97 fprintf(output
, "Multi-Arch: foreign\n");
98 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
99 fprintf(output
, "Multi-Arch: same\n");
100 signed short Pin
= std::numeric_limits
<signed short>::min();
101 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
102 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
106 fprintf(output
, "APT-Pin: %d\n", Pin
);
107 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
108 fprintf(output
, "APT-Candidate: yes\n");
109 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
110 fprintf(output
, "APT-Automatic: yes\n");
113 // EDSP::WriteScenarioDependency /*{{{*/
114 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
115 pkgCache::VerIterator
const &Ver
)
117 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
118 bool orGroup
= false;
119 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
121 if (Dep
.IsMultiArchImplicit() == true)
123 if (orGroup
== false)
124 dependencies
[Dep
->Type
].append(", ");
125 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
126 if (Dep
->Version
!= 0)
127 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
128 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
130 dependencies
[Dep
->Type
].append(" | ");
136 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
137 if (dependencies
[i
].empty() == false)
138 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
140 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
142 if (Prv
.IsMultiArchImplicit() == true)
144 provides
.append(", ").append(Prv
.Name());
146 if (provides
.empty() == false)
147 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
150 // EDSP::WriteScenarioLimitedDependency /*{{{*/
151 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
152 pkgCache::PkgIterator
const &Pkg
,
153 pkgCache::VerIterator
const &Ver
,
154 APT::PackageSet
const &pkgset
)
156 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
157 bool orGroup
= false;
158 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
160 if (Dep
.IsMultiArchImplicit() == true)
162 if (orGroup
== false)
164 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
166 dependencies
[Dep
->Type
].append(", ");
168 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
170 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
172 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
176 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
177 if (Dep
->Version
!= 0)
178 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
179 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
181 dependencies
[Dep
->Type
].append(" | ");
187 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
188 if (dependencies
[i
].empty() == false)
189 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
191 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
193 if (Prv
.IsMultiArchImplicit() == true)
195 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
197 provides
.append(", ").append(Prv
.Name());
199 if (provides
.empty() == false)
200 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
203 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
204 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
205 bool const DistUpgrade
, bool const AutoRemove
,
206 OpProgress
*Progress
)
208 if (Progress
!= NULL
)
209 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
212 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
214 if (Progress
!= NULL
&& p
% 100 == 0)
215 Progress
->Progress(p
);
217 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
218 if (P
.Delete() == true)
220 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
221 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
225 req
->append(" ").append(Pkg
.FullName());
227 fprintf(output
, "Request: EDSP 0.4\n");
228 if (del
.empty() == false)
229 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
230 if (inst
.empty() == false)
231 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
233 fprintf(output
, "Upgrade: yes\n");
234 if (DistUpgrade
== true)
235 fprintf(output
, "Dist-Upgrade: yes\n");
236 if (AutoRemove
== true)
237 fprintf(output
, "Autoremove: yes\n");
238 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
239 fprintf(output
, "Strict-Pinning: no\n");
240 string
solverpref("APT::Solver::");
241 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
242 if (_config
->Exists(solverpref
) == true)
243 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
244 fprintf(output
, "\n");
249 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
250 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
251 /* We build an map id to mmap offset here
252 In theory we could use the offset as ID, but then VersionCount
253 couldn't be used to create other versionmappings anymore and it
254 would be too easy for a (buggy) solver to segfault APT⦠*/
255 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
256 unsigned long VerIdx
[VersionCount
];
257 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
258 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
259 VerIdx
[V
->ID
] = V
.Index();
260 Cache
[P
].Marked
= true;
261 Cache
[P
].Garbage
= false;
265 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
266 pkgTagFile
response(&in
, 100);
267 pkgTagSection section
;
269 while (response
.Step(section
) == true) {
271 if (section
.Exists("Install") == true)
273 else if (section
.Exists("Remove") == true)
275 else if (section
.Exists("Progress") == true) {
276 if (Progress
!= NULL
) {
277 string msg
= section
.FindS("Message");
278 if (msg
.empty() == true)
279 msg
= _("Prepare for receiving solution");
280 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
283 } else if (section
.Exists("Error") == true) {
284 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
285 if (msg
.empty() == true) {
286 msg
= _("External solver failed without a proper error message");
287 _error
->Error("%s", msg
.c_str());
289 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
290 if (Progress
!= NULL
)
292 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
293 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
294 std::cerr
<< msg
<< std::endl
<< std::endl
;
296 } else if (section
.Exists("Autoremove") == true)
301 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
302 if (id
== VersionCount
) {
303 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
305 } else if (id
> Cache
.Head().VersionCount
) {
306 _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());
310 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
311 Cache
.SetCandidateVersion(Ver
);
312 if (type
== "Install")
313 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
314 else if (type
== "Remove")
315 Cache
.MarkDelete(Ver
.ParentPkg(), false);
316 else if (type
== "Autoremove") {
317 Cache
[Ver
.ParentPkg()].Marked
= false;
318 Cache
[Ver
.ParentPkg()].Garbage
= true;
324 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
325 // ---------------------------------------------------------------------
326 /* Little helper method to read a complete line into a string. Similar to
327 fgets but we need to use the low-level read() here as otherwise the
328 listparser will be confused later on as mixing of fgets and read isn't
329 a supported action according to the manpages and results are undefined */
330 bool EDSP::ReadLine(int const input
, std::string
&line
) {
335 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
342 if (line
.empty() == true && isblank(one
) != 0)
349 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
350 // ---------------------------------------------------------------------
351 /* we are not as lazy as we are in the global StringToBool as we really
352 only accept yes/no here - but we will ignore leading spaces */
353 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
354 for (; isspace(*answer
) != 0; ++answer
);
355 if (strncasecmp(answer
, "yes", 3) == 0)
357 else if (strncasecmp(answer
, "no", 2) == 0)
360 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
364 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
365 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
366 std::list
<std::string
> &remove
, bool &upgrade
,
367 bool &distUpgrade
, bool &autoRemove
)
375 while (ReadLine(input
, line
) == true)
377 // Skip empty lines before request
378 if (line
.empty() == true)
380 // The first Tag must be a request, so search for it
381 if (line
.compare(0, 8, "Request:") != 0)
384 while (ReadLine(input
, line
) == true)
386 // empty lines are the end of the request
387 if (line
.empty() == true)
390 std::list
<std::string
> *request
= NULL
;
391 if (line
.compare(0, 8, "Install:") == 0)
396 else if (line
.compare(0, 7, "Remove:") == 0)
401 else if (line
.compare(0, 8, "Upgrade:") == 0)
402 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
403 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
404 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
405 else if (line
.compare(0, 11, "Autoremove:") == 0)
406 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
408 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
412 size_t end
= line
.length();
414 size_t begin
= line
.rfind(' ');
415 if (begin
== std::string::npos
)
417 request
->push_back(line
.substr(0, end
));
420 else if (begin
< end
)
421 request
->push_back(line
.substr(begin
+ 1, end
));
423 end
= line
.find_last_not_of(' ');
424 } while (end
!= std::string::npos
);
430 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
431 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
432 std::list
<std::string
> const &remove
,
435 for (std::list
<std::string
>::const_iterator i
= install
.begin();
436 i
!= install
.end(); ++i
) {
437 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
439 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
441 Cache
.MarkInstall(P
, false);
444 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
445 i
!= remove
.end(); ++i
) {
446 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
448 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
455 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
456 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
458 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
459 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
461 if (Cache
[Pkg
].Delete() == true)
463 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
465 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
467 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
469 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
471 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
473 else if (Cache
[Pkg
].Garbage
== true)
475 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
478 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
479 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
484 fprintf(output
, "\n");
490 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
491 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
492 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
493 fprintf(output
, "Percentage: %d\n", percent
);
494 fprintf(output
, "Message: %s\n\n", message
);
499 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
500 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
501 fprintf(output
, "Error: %s\n", uuid
);
502 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
506 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
507 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
508 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
510 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
511 dir
!= solverDirs
.end(); ++dir
) {
512 file
= flCombine(*dir
, solver
);
513 if (RealFileExists(file
.c_str()) == true)
518 if (file
.empty() == true)
519 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
520 int external
[4] = {-1, -1, -1, -1};
521 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
522 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
523 for (int i
= 0; i
< 4; ++i
)
524 SetCloseExec(external
[i
], true);
526 pid_t Solver
= ExecFork();
528 dup2(external
[0], STDIN_FILENO
);
529 dup2(external
[3], STDOUT_FILENO
);
530 const char* calling
[2] = { file
.c_str(), 0 };
531 execv(calling
[0], (char**) calling
);
532 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
538 if (WaitFd(external
[1], true, 5) == false)
539 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
541 *solver_in
= external
[1];
542 *solver_out
= external
[2];
546 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
547 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
548 bool const upgrade
, bool const distUpgrade
,
549 bool const autoRemove
, OpProgress
*Progress
) {
550 int solver_in
, solver_out
;
551 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
554 FILE* output
= fdopen(solver_in
, "w");
556 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
558 if (Progress
!= NULL
)
559 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
560 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
561 if (Progress
!= NULL
)
562 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
563 EDSP::WriteScenario(Cache
, output
, Progress
);
566 if (Progress
!= NULL
)
567 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
568 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)