]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
d604110ef6bc37e6c5a27e6d19a05b461f475c1b
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
)
31 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
32 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
34 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
35 WriteScenarioDependency(Cache
, output
, Pkg
, Ver
);
36 fprintf(output
, "\n");
41 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
42 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
43 APT::PackageSet
const &pkgset
)
45 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
)
46 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
48 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
49 WriteScenarioLimitedDependency(Cache
, output
, Pkg
, Ver
, pkgset
);
50 fprintf(output
, "\n");
55 // EDSP::WriteScenarioVersion /*{{{*/
56 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
57 pkgCache::VerIterator
const &Ver
)
59 fprintf(output
, "Package: %s\n", Pkg
.Name());
60 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
61 fprintf(output
, "Version: %s\n", Ver
.VerStr());
62 if (Pkg
.CurrentVer() == Ver
)
63 fprintf(output
, "Installed: yes\n");
64 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
65 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
66 fprintf(output
, "Hold: yes\n");
67 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
68 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
69 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
70 fprintf(output
, "Essential: yes\n");
71 fprintf(output
, "Section: %s\n", Ver
.Section());
72 if (Ver
->MultiArch
== pkgCache::Version::Allowed
|| Ver
->MultiArch
== pkgCache::Version::AllAllowed
)
73 fprintf(output
, "Multi-Arch: allowed\n");
74 else if (Ver
->MultiArch
== pkgCache::Version::Foreign
|| Ver
->MultiArch
== pkgCache::Version::AllForeign
)
75 fprintf(output
, "Multi-Arch: foreign\n");
76 else if (Ver
->MultiArch
== pkgCache::Version::Same
)
77 fprintf(output
, "Multi-Arch: same\n");
78 signed short Pin
= std::numeric_limits
<signed short>::min();
79 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
80 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
84 fprintf(output
, "APT-Pin: %d\n", Pin
);
85 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
86 fprintf(output
, "APT-Candidate: yes\n");
87 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
88 fprintf(output
, "APT-Automatic: yes\n");
91 // EDSP::WriteScenarioDependency /*{{{*/
92 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
93 pkgCache::VerIterator
const &Ver
)
95 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
97 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
99 // Ignore implicit dependencies for multiarch here
100 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
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 // Ignore implicit provides for multiarch here
122 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
124 provides
.append(", ").append(Prv
.Name());
126 if (provides
.empty() == false)
127 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
130 // EDSP::WriteScenarioLimitedDependency /*{{{*/
131 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
132 pkgCache::PkgIterator
const &Pkg
,
133 pkgCache::VerIterator
const &Ver
,
134 APT::PackageSet
const &pkgset
)
136 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
137 bool orGroup
= false;
138 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
140 // Ignore implicit dependencies for multiarch here
141 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
143 if (orGroup
== false)
145 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
147 dependencies
[Dep
->Type
].append(", ");
149 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
151 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
153 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
157 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
158 if (Dep
->Version
!= 0)
159 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
160 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
162 dependencies
[Dep
->Type
].append(" | ");
168 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
169 if (dependencies
[i
].empty() == false)
170 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
172 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
174 // Ignore implicit provides for multiarch here
175 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
177 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
179 provides
.append(", ").append(Prv
.Name());
181 if (provides
.empty() == false)
182 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
185 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
186 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
187 bool const DistUpgrade
, bool const AutoRemove
)
190 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
193 if (Cache
[Pkg
].Delete() == true)
195 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
199 req
->append(" ").append(Pkg
.FullName());
201 fprintf(output
, "Request: EDSP 0.4\n");
202 if (del
.empty() == false)
203 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
204 if (inst
.empty() == false)
205 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
207 fprintf(output
, "Upgrade: yes\n");
208 if (DistUpgrade
== true)
209 fprintf(output
, "Dist-Upgrade: yes\n");
210 if (AutoRemove
== true)
211 fprintf(output
, "Autoremove: yes\n");
212 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
213 fprintf(output
, "Strict-Pinning: no\n");
214 string
solverpref("APT::Solver::");
215 solverpref
.append(_config
->Find("APT::Solver::Name", "internal")).append("::Preferences");
216 if (_config
->Exists(solverpref
) == true)
217 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
218 fprintf(output
, "\n");
223 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
224 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
) {
225 /* We build an map id to mmap offset here
226 In theory we could use the offset as ID, but then VersionCount
227 couldn't be used to create other versionmappings anymore and it
228 would be too easy for a (buggy) solver to segfault APTā¦ */
229 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
230 unsigned long VerIdx
[VersionCount
];
231 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
232 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
233 VerIdx
[V
->ID
] = V
.Index();
234 Cache
[P
].Marked
= true;
235 Cache
[P
].Garbage
= false;
239 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
240 pkgTagFile
response(&in
, 100);
241 pkgTagSection section
;
243 while (response
.Step(section
) == true) {
245 if (section
.Exists("Install") == true)
247 else if (section
.Exists("Remove") == true)
249 else if (section
.Exists("Progress") == true) {
250 std::clog
<< TimeRFC1123(time(NULL
)) << " ";
251 ioprintf(std::clog
, "[ %3d%% ] ", section
.FindI("Percentage", 0));
252 std::clog
<< section
.FindS("Progress") << " - ";
253 string
const msg
= section
.FindS("Message");
254 if (msg
.empty() == true)
255 std::clog
<< "Solver is still working on the solution" << std::endl
;
257 std::clog
<< msg
<< std::endl
;
259 } else if (section
.Exists("Autoremove") == true)
264 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
265 if (id
== VersionCount
) {
266 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
268 } else if (id
> Cache
.Head().VersionCount
) {
269 _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());
273 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
274 Cache
.SetCandidateVersion(Ver
);
275 if (type
== "Install")
276 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
277 else if (type
== "Remove")
278 Cache
.MarkDelete(Ver
.ParentPkg(), false);
279 else if (type
== "Autoremove") {
280 Cache
[Ver
.ParentPkg()].Marked
= false;
281 Cache
[Ver
.ParentPkg()].Garbage
= true;
287 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
288 // ---------------------------------------------------------------------
289 /* Little helper method to read a complete line into a string. Similar to
290 fgets but we need to use the low-level read() here as otherwise the
291 listparser will be confused later on as mixing of fgets and read isn't
292 a supported action according to the manpages and results are undefined */
293 bool EDSP::ReadLine(int const input
, std::string
&line
) {
298 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
305 if (line
.empty() == true && isblank(one
) != 0)
312 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
313 // ---------------------------------------------------------------------
314 /* we are not as lazy as we are in the global StringToBool as we really
315 only accept yes/no here - but we will ignore leading spaces */
316 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
317 for (; isspace(*answer
) != 0; ++answer
);
318 if (strncasecmp(answer
, "yes", 3) == 0)
320 else if (strncasecmp(answer
, "no", 2) == 0)
323 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
327 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
328 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
329 std::list
<std::string
> &remove
, bool &upgrade
,
330 bool &distUpgrade
, bool &autoRemove
)
338 while (ReadLine(input
, line
) == true)
340 // Skip empty lines before request
341 if (line
.empty() == true)
343 // The first Tag must be a request, so search for it
344 if (line
.compare(0, 8, "Request:") != 0)
347 while (ReadLine(input
, line
) == true)
349 // empty lines are the end of the request
350 if (line
.empty() == true)
353 std::list
<std::string
> *request
= NULL
;
354 if (line
.compare(0, 8, "Install:") == 0)
359 else if (line
.compare(0, 7, "Remove:") == 0)
364 else if (line
.compare(0, 8, "Upgrade:") == 0)
365 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
366 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
367 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
368 else if (line
.compare(0, 11, "Autoremove:") == 0)
369 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
371 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
375 size_t end
= line
.length();
377 size_t begin
= line
.rfind(' ');
378 if (begin
== std::string::npos
)
380 request
->push_back(line
.substr(0, end
));
383 else if (begin
< end
)
384 request
->push_back(line
.substr(begin
+ 1, end
));
386 end
= line
.find_last_not_of(' ');
387 } while (end
!= std::string::npos
);
393 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
394 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
395 std::list
<std::string
> const &remove
,
398 for (std::list
<std::string
>::const_iterator i
= install
.begin();
399 i
!= install
.end(); ++i
) {
400 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
402 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
404 Cache
.MarkInstall(P
, false);
407 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
408 i
!= remove
.end(); ++i
) {
409 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
411 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
418 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
419 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
421 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
422 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
424 if (Cache
[Pkg
].Delete() == true)
426 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
428 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
430 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
432 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
434 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
436 else if (Cache
[Pkg
].Garbage
== true)
438 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
440 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
441 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
445 fprintf(output
, "\n");
451 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
452 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
453 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
454 fprintf(output
, "Percentage: %d\n", percent
);
455 fprintf(output
, "Message: %s\n\n", message
);
460 bool EDSP::WriteError(std::string
const &message
, FILE* output
) { return false; }
462 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
463 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
464 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
466 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
467 dir
!= solverDirs
.end(); ++dir
) {
468 file
= flCombine(*dir
, solver
);
469 if (RealFileExists(file
.c_str()) == true)
474 if (file
.empty() == true)
475 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
476 int external
[4] = {-1, -1, -1, -1};
477 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
478 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
479 for (int i
= 0; i
< 4; ++i
)
480 SetCloseExec(external
[i
], true);
482 pid_t Solver
= ExecFork();
484 dup2(external
[0], STDIN_FILENO
);
485 dup2(external
[3], STDOUT_FILENO
);
486 const char* calling
[2] = { file
.c_str(), 0 };
487 execv(calling
[0], (char**) calling
);
488 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
494 if (WaitFd(external
[1], true, 5) == false)
495 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
497 *solver_in
= external
[1];
498 *solver_out
= external
[2];
502 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
503 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
504 bool const upgrade
, bool const distUpgrade
,
505 bool const autoRemove
) {
506 int solver_in
, solver_out
;
507 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
510 FILE* output
= fdopen(solver_in
, "w");
512 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
513 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
);
514 EDSP::WriteScenario(Cache
, output
);
517 if (EDSP::ReadResponse(solver_out
, Cache
) == false)
518 return _error
->Error("Reading solver response failed");