]>
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 /*{{{*/
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 if (Cache
[Pkg
].Delete() == true)
219 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
223 req
->append(" ").append(Pkg
.FullName());
225 fprintf(output
, "Request: EDSP 0.4\n");
226 if (del
.empty() == false)
227 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
228 if (inst
.empty() == false)
229 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
231 fprintf(output
, "Upgrade: yes\n");
232 if (DistUpgrade
== true)
233 fprintf(output
, "Dist-Upgrade: yes\n");
234 if (AutoRemove
== true)
235 fprintf(output
, "Autoremove: yes\n");
236 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
237 fprintf(output
, "Strict-Pinning: no\n");
238 string
solverpref("APT::Solver::");
239 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
240 if (_config
->Exists(solverpref
) == true)
241 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
242 fprintf(output
, "\n");
247 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
248 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
249 /* We build an map id to mmap offset here
250 In theory we could use the offset as ID, but then VersionCount
251 couldn't be used to create other versionmappings anymore and it
252 would be too easy for a (buggy) solver to segfault APTā¦ */
253 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
254 unsigned long VerIdx
[VersionCount
];
255 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
256 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
257 VerIdx
[V
->ID
] = V
.Index();
258 Cache
[P
].Marked
= true;
259 Cache
[P
].Garbage
= false;
263 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
264 pkgTagFile
response(&in
, 100);
265 pkgTagSection section
;
267 while (response
.Step(section
) == true) {
269 if (section
.Exists("Install") == true)
271 else if (section
.Exists("Remove") == true)
273 else if (section
.Exists("Progress") == true) {
274 if (Progress
!= NULL
) {
275 string msg
= section
.FindS("Message");
276 if (msg
.empty() == true)
277 msg
= _("Prepare for receiving solution");
278 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
281 } else if (section
.Exists("Error") == true) {
282 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
283 if (msg
.empty() == true) {
284 msg
= _("External solver failed without a proper error message");
285 _error
->Error("%s", msg
.c_str());
287 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
288 if (Progress
!= NULL
)
290 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
291 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
292 std::cerr
<< msg
<< std::endl
<< std::endl
;
294 } else if (section
.Exists("Autoremove") == true)
299 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
300 if (id
== VersionCount
) {
301 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
303 } else if (id
> Cache
.Head().VersionCount
) {
304 _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());
308 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
309 Cache
.SetCandidateVersion(Ver
);
310 if (type
== "Install")
311 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
312 else if (type
== "Remove")
313 Cache
.MarkDelete(Ver
.ParentPkg(), false);
314 else if (type
== "Autoremove") {
315 Cache
[Ver
.ParentPkg()].Marked
= false;
316 Cache
[Ver
.ParentPkg()].Garbage
= true;
322 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
323 // ---------------------------------------------------------------------
324 /* Little helper method to read a complete line into a string. Similar to
325 fgets but we need to use the low-level read() here as otherwise the
326 listparser will be confused later on as mixing of fgets and read isn't
327 a supported action according to the manpages and results are undefined */
328 bool EDSP::ReadLine(int const input
, std::string
&line
) {
333 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
340 if (line
.empty() == true && isblank(one
) != 0)
347 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
348 // ---------------------------------------------------------------------
349 /* we are not as lazy as we are in the global StringToBool as we really
350 only accept yes/no here - but we will ignore leading spaces */
351 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
352 for (; isspace(*answer
) != 0; ++answer
);
353 if (strncasecmp(answer
, "yes", 3) == 0)
355 else if (strncasecmp(answer
, "no", 2) == 0)
358 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
362 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
363 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
364 std::list
<std::string
> &remove
, bool &upgrade
,
365 bool &distUpgrade
, bool &autoRemove
)
373 while (ReadLine(input
, line
) == true)
375 // Skip empty lines before request
376 if (line
.empty() == true)
378 // The first Tag must be a request, so search for it
379 if (line
.compare(0, 8, "Request:") != 0)
382 while (ReadLine(input
, line
) == true)
384 // empty lines are the end of the request
385 if (line
.empty() == true)
388 std::list
<std::string
> *request
= NULL
;
389 if (line
.compare(0, 8, "Install:") == 0)
394 else if (line
.compare(0, 7, "Remove:") == 0)
399 else if (line
.compare(0, 8, "Upgrade:") == 0)
400 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
401 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
402 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
403 else if (line
.compare(0, 11, "Autoremove:") == 0)
404 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
406 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
410 size_t end
= line
.length();
412 size_t begin
= line
.rfind(' ');
413 if (begin
== std::string::npos
)
415 request
->push_back(line
.substr(0, end
));
418 else if (begin
< end
)
419 request
->push_back(line
.substr(begin
+ 1, end
));
421 end
= line
.find_last_not_of(' ');
422 } while (end
!= std::string::npos
);
428 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
429 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
430 std::list
<std::string
> const &remove
,
433 for (std::list
<std::string
>::const_iterator i
= install
.begin();
434 i
!= install
.end(); ++i
) {
435 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
437 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
439 Cache
.MarkInstall(P
, false);
442 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
443 i
!= remove
.end(); ++i
) {
444 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
446 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
453 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
454 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
456 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
457 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
459 if (Cache
[Pkg
].Delete() == true)
461 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
463 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
465 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
467 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
469 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
471 else if (Cache
[Pkg
].Garbage
== true)
473 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
475 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
476 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
480 fprintf(output
, "\n");
486 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
487 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
488 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
489 fprintf(output
, "Percentage: %d\n", percent
);
490 fprintf(output
, "Message: %s\n\n", message
);
495 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
496 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
497 fprintf(output
, "Error: %s\n", uuid
);
498 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
502 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
503 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
504 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
506 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
507 dir
!= solverDirs
.end(); ++dir
) {
508 file
= flCombine(*dir
, solver
);
509 if (RealFileExists(file
.c_str()) == true)
514 if (file
.empty() == true)
515 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
516 int external
[4] = {-1, -1, -1, -1};
517 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
518 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
519 for (int i
= 0; i
< 4; ++i
)
520 SetCloseExec(external
[i
], true);
522 pid_t Solver
= ExecFork();
524 dup2(external
[0], STDIN_FILENO
);
525 dup2(external
[3], STDOUT_FILENO
);
526 const char* calling
[2] = { file
.c_str(), 0 };
527 execv(calling
[0], (char**) calling
);
528 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
534 if (WaitFd(external
[1], true, 5) == false)
535 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
537 *solver_in
= external
[1];
538 *solver_out
= external
[2];
542 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
543 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
544 bool const upgrade
, bool const distUpgrade
,
545 bool const autoRemove
, OpProgress
*Progress
) {
546 int solver_in
, solver_out
;
547 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
550 FILE* output
= fdopen(solver_in
, "w");
552 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
554 if (Progress
!= NULL
)
555 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
556 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
557 if (Progress
!= NULL
)
558 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
559 EDSP::WriteScenario(Cache
, output
, Progress
);
562 if (Progress
!= NULL
)
563 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
564 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)