]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
7ece92d2e4ef433096fbc3c0dccac9b97869501d
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("Error") == true) {
260 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
261 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
262 std::cerr
<< SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n") << std::endl
<< std::endl
;
264 } else if (section
.Exists("Autoremove") == true)
269 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
270 if (id
== VersionCount
) {
271 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
273 } else if (id
> Cache
.Head().VersionCount
) {
274 _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());
278 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
279 Cache
.SetCandidateVersion(Ver
);
280 if (type
== "Install")
281 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
282 else if (type
== "Remove")
283 Cache
.MarkDelete(Ver
.ParentPkg(), false);
284 else if (type
== "Autoremove") {
285 Cache
[Ver
.ParentPkg()].Marked
= false;
286 Cache
[Ver
.ParentPkg()].Garbage
= true;
292 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
293 // ---------------------------------------------------------------------
294 /* Little helper method to read a complete line into a string. Similar to
295 fgets but we need to use the low-level read() here as otherwise the
296 listparser will be confused later on as mixing of fgets and read isn't
297 a supported action according to the manpages and results are undefined */
298 bool EDSP::ReadLine(int const input
, std::string
&line
) {
303 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
310 if (line
.empty() == true && isblank(one
) != 0)
317 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
318 // ---------------------------------------------------------------------
319 /* we are not as lazy as we are in the global StringToBool as we really
320 only accept yes/no here - but we will ignore leading spaces */
321 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
322 for (; isspace(*answer
) != 0; ++answer
);
323 if (strncasecmp(answer
, "yes", 3) == 0)
325 else if (strncasecmp(answer
, "no", 2) == 0)
328 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
332 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
333 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
334 std::list
<std::string
> &remove
, bool &upgrade
,
335 bool &distUpgrade
, bool &autoRemove
)
343 while (ReadLine(input
, line
) == true)
345 // Skip empty lines before request
346 if (line
.empty() == true)
348 // The first Tag must be a request, so search for it
349 if (line
.compare(0, 8, "Request:") != 0)
352 while (ReadLine(input
, line
) == true)
354 // empty lines are the end of the request
355 if (line
.empty() == true)
358 std::list
<std::string
> *request
= NULL
;
359 if (line
.compare(0, 8, "Install:") == 0)
364 else if (line
.compare(0, 7, "Remove:") == 0)
369 else if (line
.compare(0, 8, "Upgrade:") == 0)
370 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
371 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
372 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
373 else if (line
.compare(0, 11, "Autoremove:") == 0)
374 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
376 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
380 size_t end
= line
.length();
382 size_t begin
= line
.rfind(' ');
383 if (begin
== std::string::npos
)
385 request
->push_back(line
.substr(0, end
));
388 else if (begin
< end
)
389 request
->push_back(line
.substr(begin
+ 1, end
));
391 end
= line
.find_last_not_of(' ');
392 } while (end
!= std::string::npos
);
398 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
399 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
400 std::list
<std::string
> const &remove
,
403 for (std::list
<std::string
>::const_iterator i
= install
.begin();
404 i
!= install
.end(); ++i
) {
405 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
407 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
409 Cache
.MarkInstall(P
, false);
412 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
413 i
!= remove
.end(); ++i
) {
414 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
416 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
423 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
424 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
426 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
427 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
429 if (Cache
[Pkg
].Delete() == true)
431 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
433 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
435 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
437 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
439 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
441 else if (Cache
[Pkg
].Garbage
== true)
443 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
445 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
446 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
450 fprintf(output
, "\n");
456 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
457 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
458 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
459 fprintf(output
, "Percentage: %d\n", percent
);
460 fprintf(output
, "Message: %s\n\n", message
);
465 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
466 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
467 fprintf(output
, "Error: %s\n", uuid
);
468 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
472 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
473 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
474 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
476 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
477 dir
!= solverDirs
.end(); ++dir
) {
478 file
= flCombine(*dir
, solver
);
479 if (RealFileExists(file
.c_str()) == true)
484 if (file
.empty() == true)
485 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
486 int external
[4] = {-1, -1, -1, -1};
487 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
488 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
489 for (int i
= 0; i
< 4; ++i
)
490 SetCloseExec(external
[i
], true);
492 pid_t Solver
= ExecFork();
494 dup2(external
[0], STDIN_FILENO
);
495 dup2(external
[3], STDOUT_FILENO
);
496 const char* calling
[2] = { file
.c_str(), 0 };
497 execv(calling
[0], (char**) calling
);
498 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
504 if (WaitFd(external
[1], true, 5) == false)
505 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
507 *solver_in
= external
[1];
508 *solver_out
= external
[2];
512 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
513 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
514 bool const upgrade
, bool const distUpgrade
,
515 bool const autoRemove
) {
516 int solver_in
, solver_out
;
517 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
520 FILE* output
= fdopen(solver_in
, "w");
522 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
523 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
);
524 EDSP::WriteScenario(Cache
, output
);
527 if (EDSP::ReadResponse(solver_out
, Cache
) == false)
528 return _error
->Error("Reading solver response failed");