]>
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", "PreDepends", "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 fprintf(output
, "Hold: yes\n");
66 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
67 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
68 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
69 fprintf(output
, "Essential: yes\n");
70 fprintf(output
, "Section: %s\n", Ver
.Section());
71 if (Ver
->MultiArch
== pkgCache::Version::Allowed
|| Ver
->MultiArch
== pkgCache::Version::AllAllowed
)
72 fprintf(output
, "Multi-Arch: allowed\n");
73 else if (Ver
->MultiArch
== pkgCache::Version::Foreign
|| Ver
->MultiArch
== pkgCache::Version::AllForeign
)
74 fprintf(output
, "Multi-Arch: foreign\n");
75 else if (Ver
->MultiArch
== pkgCache::Version::Same
)
76 fprintf(output
, "Multi-Arch: same\n");
77 signed short Pin
= std::numeric_limits
<signed short>::min();
78 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
79 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
83 fprintf(output
, "APT-Pin: %d\n", Pin
);
84 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
85 fprintf(output
, "APT-Candidate: yes\n");
86 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
87 fprintf(output
, "APT-Automatic: yes\n");
90 // EDSP::WriteScenarioDependency /*{{{*/
91 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
92 pkgCache::VerIterator
const &Ver
)
94 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
96 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
98 // Ignore implicit dependencies for multiarch here
99 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
101 if (orGroup
== false)
102 dependencies
[Dep
->Type
].append(", ");
103 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
104 if (Dep
->Version
!= 0)
105 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
106 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
108 dependencies
[Dep
->Type
].append(" | ");
114 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
115 if (dependencies
[i
].empty() == false)
116 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
118 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
120 // Ignore implicit provides for multiarch here
121 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
123 provides
.append(", ").append(Prv
.Name());
125 if (provides
.empty() == false)
126 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
129 // EDSP::WriteScenarioLimitedDependency /*{{{*/
130 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
131 pkgCache::PkgIterator
const &Pkg
,
132 pkgCache::VerIterator
const &Ver
,
133 APT::PackageSet
const &pkgset
)
135 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
136 bool orGroup
= false;
137 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
139 // Ignore implicit dependencies for multiarch here
140 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
142 if (orGroup
== false)
144 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
146 dependencies
[Dep
->Type
].append(", ");
148 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
150 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
152 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
156 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
157 if (Dep
->Version
!= 0)
158 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
159 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
161 dependencies
[Dep
->Type
].append(" | ");
167 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
168 if (dependencies
[i
].empty() == false)
169 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
171 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
173 // Ignore implicit provides for multiarch here
174 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
176 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
178 provides
.append(", ").append(Prv
.Name());
180 if (provides
.empty() == false)
181 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
184 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
185 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
186 bool const DistUpgrade
, bool const AutoRemove
)
189 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
192 if (Cache
[Pkg
].Delete() == true)
194 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
198 req
->append(" ").append(Pkg
.FullName());
200 fprintf(output
, "Request: EDSP 0.2\n");
201 if (del
.empty() == false)
202 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
203 if (inst
.empty() == false)
204 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
206 fprintf(output
, "Upgrade: yes\n");
207 if (DistUpgrade
== true)
208 fprintf(output
, "Dist-Upgrade: yes\n");
209 if (AutoRemove
== true)
210 fprintf(output
, "Autoremove: yes\n");
211 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
212 fprintf(output
, "Strict-Pinning: no\n");
213 string
solverpref("APT::Solver::");
214 solverpref
.append(_config
->Find("APT::Solver::Name", "internal")).append("::Preferences");
215 if (_config
->Exists(solverpref
) == true)
216 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
217 fprintf(output
, "\n");
222 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
223 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
) {
224 /* We build an map id to mmap offset here
225 In theory we could use the offset as ID, but then VersionCount
226 couldn't be used to create other versionmappings anymore and it
227 would be too easy for a (buggy) solver to segfault APTā¦ */
228 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
229 unsigned long VerIdx
[VersionCount
];
230 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
231 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
232 VerIdx
[V
->ID
] = V
.Index();
233 Cache
[P
].Marked
= true;
234 Cache
[P
].Garbage
= false;
238 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
239 pkgTagFile
response(&in
, 100);
240 pkgTagSection section
;
242 while (response
.Step(section
) == true) {
244 if (section
.Exists("Install") == true)
246 else if (section
.Exists("Remove") == true)
248 else if (section
.Exists("Progress") == true) {
249 std::clog
<< TimeRFC1123(time(NULL
)) << " ";
250 ioprintf(std::clog
, "[ %3d%% ] ", section
.FindI("Percentage", 0));
251 std::clog
<< section
.FindS("Progress") << " - ";
252 string
const msg
= section
.FindS("Message");
253 if (msg
.empty() == true)
254 std::clog
<< "Solver is still working on the solution" << std::endl
;
256 std::clog
<< msg
<< std::endl
;
258 } else if (section
.Exists("Autoremove") == true)
263 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
264 if (id
== VersionCount
) {
265 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
267 } else if (id
> Cache
.Head().VersionCount
) {
268 _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());
272 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
273 Cache
.SetCandidateVersion(Ver
);
274 if (type
== "Install")
275 Cache
.MarkInstall(Ver
.ParentPkg(), false, false);
276 else if (type
== "Remove")
277 Cache
.MarkDelete(Ver
.ParentPkg(), false);
278 else if (type
== "Autoremove") {
279 Cache
[Ver
.ParentPkg()].Marked
= false;
280 Cache
[Ver
.ParentPkg()].Garbage
= true;
286 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
287 // ---------------------------------------------------------------------
288 /* Little helper method to read a complete line into a string. Similar to
289 fgets but we need to use the low-level read() here as otherwise the
290 listparser will be confused later on as mixing of fgets and read isn't
291 a supported action according to the manpages and results are undefined */
292 bool EDSP::ReadLine(int const input
, std::string
&line
) {
297 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
304 if (line
.empty() == true && isblank(one
) != 0)
311 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
312 // ---------------------------------------------------------------------
313 /* we are not as lazy as we are in the global StringToBool as we really
314 only accept yes/no here - but we will ignore leading spaces */
315 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
316 for (; isspace(*answer
) != 0; ++answer
);
317 if (strncasecmp(answer
, "yes", 3) == 0)
319 else if (strncasecmp(answer
, "no", 2) == 0)
322 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
326 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
327 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
328 std::list
<std::string
> &remove
, bool &upgrade
,
329 bool &distUpgrade
, bool &autoRemove
)
337 while (ReadLine(input
, line
) == true)
339 // Skip empty lines before request
340 if (line
.empty() == true)
342 // The first Tag must be a request, so search for it
343 if (line
.compare(0, 8, "Request:") != 0)
346 while (ReadLine(input
, line
) == true)
348 // empty lines are the end of the request
349 if (line
.empty() == true)
352 std::list
<std::string
> *request
= NULL
;
353 if (line
.compare(0, 8, "Install:") == 0)
358 else if (line
.compare(0, 7, "Remove:") == 0)
363 else if (line
.compare(0, 8, "Upgrade:") == 0)
364 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
365 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
366 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
367 else if (line
.compare(0, 11, "Autoremove:") == 0)
368 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
370 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
374 size_t end
= line
.length();
376 size_t begin
= line
.rfind(' ');
377 if (begin
== std::string::npos
)
379 request
->push_back(line
.substr(0, end
));
382 else if (begin
< end
)
383 request
->push_back(line
.substr(begin
+ 1, end
));
385 end
= line
.find_last_not_of(' ');
386 } while (end
!= std::string::npos
);
392 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
393 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
394 std::list
<std::string
> const &remove
,
397 for (std::list
<std::string
>::const_iterator i
= install
.begin();
398 i
!= install
.end(); ++i
) {
399 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
401 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
403 Cache
.MarkInstall(P
, false);
406 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
407 i
!= remove
.end(); ++i
) {
408 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
410 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
417 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
418 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
420 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
421 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
423 if (Cache
[Pkg
].Delete() == true)
425 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
427 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
429 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
431 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
433 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
435 else if (Cache
[Pkg
].Garbage
== true)
437 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
439 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
440 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
444 fprintf(output
, "\n");
450 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
451 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
452 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
453 fprintf(output
, "Percentage: %d\n", percent
);
454 fprintf(output
, "Message: %s\n\n", message
);
459 bool EDSP::WriteError(std::string
const &message
, FILE* output
) { return false; }
461 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
462 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
463 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
465 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
466 dir
!= solverDirs
.end(); ++dir
) {
467 file
= flCombine(*dir
, solver
);
468 if (RealFileExists(file
.c_str()) == true)
473 if (file
.empty() == true)
474 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
475 int external
[4] = {-1, -1, -1, -1};
476 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
477 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
478 for (int i
= 0; i
< 4; ++i
)
479 SetCloseExec(external
[i
], true);
481 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];