]>
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 /*{{{*/
8 #include <apt-pkg/edsp.h>
9 #include <apt-pkg/error.h>
10 #include <apt-pkg/configuration.h>
11 #include <apt-pkg/version.h>
12 #include <apt-pkg/policy.h>
13 #include <apt-pkg/tagfile.h>
21 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
22 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
24 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
25 "Recommends" , "Conflicts", "Replaces",
26 "Obsoletes", "Breaks", "Enhances"};
28 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
29 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
32 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
34 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
35 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
37 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
38 WriteScenarioDependency(Cache
, output
, Pkg
, Ver
);
39 fprintf(output
, "\n");
40 if (Progress
!= NULL
&& p
% 100 == 0)
41 Progress
->Progress(p
);
46 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
47 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
48 APT::PackageSet
const &pkgset
,
52 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
54 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
55 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
57 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
58 WriteScenarioLimitedDependency(Cache
, output
, Pkg
, Ver
, pkgset
);
59 fprintf(output
, "\n");
60 if (Progress
!= NULL
&& p
% 100 == 0)
61 Progress
->Progress(p
);
68 // EDSP::WriteScenarioVersion /*{{{*/
69 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
70 pkgCache::VerIterator
const &Ver
)
72 fprintf(output
, "Package: %s\n", Pkg
.Name());
73 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
74 fprintf(output
, "Version: %s\n", Ver
.VerStr());
75 if (Pkg
.CurrentVer() == Ver
)
76 fprintf(output
, "Installed: yes\n");
77 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
78 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
79 fprintf(output
, "Hold: yes\n");
80 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
81 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
82 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
83 fprintf(output
, "Essential: yes\n");
84 fprintf(output
, "Section: %s\n", Ver
.Section());
85 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
86 fprintf(output
, "Multi-Arch: allowed\n");
87 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
88 fprintf(output
, "Multi-Arch: foreign\n");
89 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
90 fprintf(output
, "Multi-Arch: same\n");
91 signed short Pin
= std::numeric_limits
<signed short>::min();
92 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
93 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
97 fprintf(output
, "APT-Pin: %d\n", Pin
);
98 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
99 fprintf(output
, "APT-Candidate: yes\n");
100 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
101 fprintf(output
, "APT-Automatic: yes\n");
104 // EDSP::WriteScenarioDependency /*{{{*/
105 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
106 pkgCache::VerIterator
const &Ver
)
108 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
109 bool orGroup
= false;
110 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
112 // Ignore implicit dependencies for multiarch here
113 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
115 if (orGroup
== false)
116 dependencies
[Dep
->Type
].append(", ");
117 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
118 if (Dep
->Version
!= 0)
119 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
120 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
122 dependencies
[Dep
->Type
].append(" | ");
128 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
129 if (dependencies
[i
].empty() == false)
130 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
132 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
134 // Ignore implicit provides for multiarch here
135 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
137 provides
.append(", ").append(Prv
.Name());
139 if (provides
.empty() == false)
140 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
143 // EDSP::WriteScenarioLimitedDependency /*{{{*/
144 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
145 pkgCache::PkgIterator
const &Pkg
,
146 pkgCache::VerIterator
const &Ver
,
147 APT::PackageSet
const &pkgset
)
149 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
150 bool orGroup
= false;
151 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
153 // Ignore implicit dependencies for multiarch here
154 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
156 if (orGroup
== false)
158 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
160 dependencies
[Dep
->Type
].append(", ");
162 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
164 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
166 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
170 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
171 if (Dep
->Version
!= 0)
172 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
173 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
175 dependencies
[Dep
->Type
].append(" | ");
181 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
182 if (dependencies
[i
].empty() == false)
183 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
185 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
187 // Ignore implicit provides for multiarch here
188 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
190 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
192 provides
.append(", ").append(Prv
.Name());
194 if (provides
.empty() == false)
195 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
198 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
199 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
200 bool const DistUpgrade
, bool const AutoRemove
,
201 OpProgress
*Progress
)
203 if (Progress
!= NULL
)
204 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
207 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
209 if (Progress
!= NULL
&& p
% 100 == 0)
210 Progress
->Progress(p
);
212 if (Cache
[Pkg
].Delete() == true)
214 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
218 req
->append(" ").append(Pkg
.FullName());
220 fprintf(output
, "Request: EDSP 0.4\n");
221 if (del
.empty() == false)
222 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
223 if (inst
.empty() == false)
224 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
226 fprintf(output
, "Upgrade: yes\n");
227 if (DistUpgrade
== true)
228 fprintf(output
, "Dist-Upgrade: yes\n");
229 if (AutoRemove
== true)
230 fprintf(output
, "Autoremove: yes\n");
231 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
232 fprintf(output
, "Strict-Pinning: no\n");
233 string
solverpref("APT::Solver::");
234 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
235 if (_config
->Exists(solverpref
) == true)
236 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
237 fprintf(output
, "\n");
242 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
243 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
244 /* We build an map id to mmap offset here
245 In theory we could use the offset as ID, but then VersionCount
246 couldn't be used to create other versionmappings anymore and it
247 would be too easy for a (buggy) solver to segfault APTā¦ */
248 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
249 unsigned long VerIdx
[VersionCount
];
250 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
251 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
252 VerIdx
[V
->ID
] = V
.Index();
253 Cache
[P
].Marked
= true;
254 Cache
[P
].Garbage
= false;
258 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
259 pkgTagFile
response(&in
, 100);
260 pkgTagSection section
;
262 while (response
.Step(section
) == true) {
264 if (section
.Exists("Install") == true)
266 else if (section
.Exists("Remove") == true)
268 else if (section
.Exists("Progress") == true) {
269 if (Progress
!= NULL
) {
270 string msg
= section
.FindS("Message");
271 if (msg
.empty() == true)
272 msg
= _("Prepare for receiving solution");
273 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
276 } else if (section
.Exists("Error") == true) {
277 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
278 if (msg
.empty() == true) {
279 msg
= _("External solver failed without a proper error message");
280 _error
->Error(msg
.c_str());
282 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
283 if (Progress
!= NULL
)
285 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
286 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
287 std::cerr
<< msg
<< std::endl
<< std::endl
;
289 } else if (section
.Exists("Autoremove") == true)
294 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
295 if (id
== VersionCount
) {
296 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
298 } else if (id
> Cache
.Head().VersionCount
) {
299 _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());
303 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
304 Cache
.SetCandidateVersion(Ver
);
305 if (type
== "Install")
306 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
307 else if (type
== "Remove")
308 Cache
.MarkDelete(Ver
.ParentPkg(), false);
309 else if (type
== "Autoremove") {
310 Cache
[Ver
.ParentPkg()].Marked
= false;
311 Cache
[Ver
.ParentPkg()].Garbage
= true;
317 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
318 // ---------------------------------------------------------------------
319 /* Little helper method to read a complete line into a string. Similar to
320 fgets but we need to use the low-level read() here as otherwise the
321 listparser will be confused later on as mixing of fgets and read isn't
322 a supported action according to the manpages and results are undefined */
323 bool EDSP::ReadLine(int const input
, std::string
&line
) {
328 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
335 if (line
.empty() == true && isblank(one
) != 0)
342 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
343 // ---------------------------------------------------------------------
344 /* we are not as lazy as we are in the global StringToBool as we really
345 only accept yes/no here - but we will ignore leading spaces */
346 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
347 for (; isspace(*answer
) != 0; ++answer
);
348 if (strncasecmp(answer
, "yes", 3) == 0)
350 else if (strncasecmp(answer
, "no", 2) == 0)
353 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
357 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
358 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
359 std::list
<std::string
> &remove
, bool &upgrade
,
360 bool &distUpgrade
, bool &autoRemove
)
368 while (ReadLine(input
, line
) == true)
370 // Skip empty lines before request
371 if (line
.empty() == true)
373 // The first Tag must be a request, so search for it
374 if (line
.compare(0, 8, "Request:") != 0)
377 while (ReadLine(input
, line
) == true)
379 // empty lines are the end of the request
380 if (line
.empty() == true)
383 std::list
<std::string
> *request
= NULL
;
384 if (line
.compare(0, 8, "Install:") == 0)
389 else if (line
.compare(0, 7, "Remove:") == 0)
394 else if (line
.compare(0, 8, "Upgrade:") == 0)
395 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
396 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
397 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
398 else if (line
.compare(0, 11, "Autoremove:") == 0)
399 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
401 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
405 size_t end
= line
.length();
407 size_t begin
= line
.rfind(' ');
408 if (begin
== std::string::npos
)
410 request
->push_back(line
.substr(0, end
));
413 else if (begin
< end
)
414 request
->push_back(line
.substr(begin
+ 1, end
));
416 end
= line
.find_last_not_of(' ');
417 } while (end
!= std::string::npos
);
423 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
424 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
425 std::list
<std::string
> const &remove
,
428 for (std::list
<std::string
>::const_iterator i
= install
.begin();
429 i
!= install
.end(); ++i
) {
430 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
432 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
434 Cache
.MarkInstall(P
, false);
437 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
438 i
!= remove
.end(); ++i
) {
439 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
441 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
448 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
449 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
451 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
452 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
454 if (Cache
[Pkg
].Delete() == true)
456 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
458 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
460 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
462 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
464 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
466 else if (Cache
[Pkg
].Garbage
== true)
468 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
470 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
471 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
475 fprintf(output
, "\n");
481 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
482 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
483 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
484 fprintf(output
, "Percentage: %d\n", percent
);
485 fprintf(output
, "Message: %s\n\n", message
);
490 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
491 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
492 fprintf(output
, "Error: %s\n", uuid
);
493 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
497 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
498 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
499 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
501 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
502 dir
!= solverDirs
.end(); ++dir
) {
503 file
= flCombine(*dir
, solver
);
504 if (RealFileExists(file
.c_str()) == true)
509 if (file
.empty() == true)
510 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
511 int external
[4] = {-1, -1, -1, -1};
512 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
513 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
514 for (int i
= 0; i
< 4; ++i
)
515 SetCloseExec(external
[i
], true);
517 pid_t Solver
= ExecFork();
519 dup2(external
[0], STDIN_FILENO
);
520 dup2(external
[3], STDOUT_FILENO
);
521 const char* calling
[2] = { file
.c_str(), 0 };
522 execv(calling
[0], (char**) calling
);
523 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
529 if (WaitFd(external
[1], true, 5) == false)
530 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
532 *solver_in
= external
[1];
533 *solver_out
= external
[2];
537 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
538 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
539 bool const upgrade
, bool const distUpgrade
,
540 bool const autoRemove
, OpProgress
*Progress
) {
541 int solver_in
, solver_out
;
542 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
545 FILE* output
= fdopen(solver_in
, "w");
547 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
549 if (Progress
!= NULL
)
550 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
551 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
552 if (Progress
!= NULL
)
553 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
554 EDSP::WriteScenario(Cache
, output
, Progress
);
557 if (Progress
!= NULL
)
558 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
559 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)