]> git.saurik.com Git - apt.git/blame - apt-pkg/edsp.cc
handle Dir::Bin::Solvers as a list of directories and find the
[apt.git] / apt-pkg / edsp.cc
CommitLineData
6d38011b
DK
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
6 /*}}}*/
7// Include Files /*{{{*/
c3b85126 8#include <apt-pkg/edsp.h>
6d38011b
DK
9#include <apt-pkg/error.h>
10#include <apt-pkg/configuration.h>
11#include <apt-pkg/version.h>
12#include <apt-pkg/policy.h>
2029276f 13#include <apt-pkg/tagfile.h>
6d38011b
DK
14
15#include <apti18n.h>
16#include <limits>
17
18#include <stdio.h>
19 /*}}}*/
20
d4f626ff
DK
21// we could use pkgCache::DepType and ::Priority, but these would be localized strings…
22const char * const EDSP::PrioMap[] = {0, "important", "required", "standard",
23 "optional", "extra"};
24const char * const EDSP::DepMap[] = {"", "Depends", "PreDepends", "Suggests",
25 "Recommends" , "Conflicts", "Replaces",
26 "Obsoletes", "Breaks", "Enhances"};
27
c3b85126
DK
28// EDSP::WriteScenario - to the given file descriptor /*{{{*/
29bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output)
6d38011b 30{
6d38011b 31 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
6d38011b
DK
32 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
33 {
d4f626ff
DK
34 WriteScenarioVersion(Cache, output, Pkg, Ver);
35 WriteScenarioDependency(Cache, output, Pkg, Ver);
6d38011b
DK
36 fprintf(output, "\n");
37 }
6d38011b
DK
38 return true;
39}
40 /*}}}*/
d4f626ff
DK
41// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
42bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
43 APT::PackageSet const &pkgset)
44{
45 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
46 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
47 {
48 WriteScenarioVersion(Cache, output, Pkg, Ver);
49 WriteScenarioLimitedDependency(Cache, output, Pkg, Ver, pkgset);
50 fprintf(output, "\n");
51 }
52 return true;
53}
54 /*}}}*/
55// EDSP::WriteScenarioVersion /*{{{*/
56void EDSP::WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
57 pkgCache::VerIterator const &Ver)
58{
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());
80 if (Pin < p)
81 Pin = p;
82 }
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");
88}
89 /*}}}*/
90// EDSP::WriteScenarioDependency /*{{{*/
91void EDSP::WriteScenarioDependency(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
92 pkgCache::VerIterator const &Ver)
93{
94 std::string dependencies[pkgCache::Dep::Enhances + 1];
95 bool orGroup = false;
96 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
97 {
98 // Ignore implicit dependencies for multiarch here
99 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
100 continue;
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)
107 {
108 dependencies[Dep->Type].append(" | ");
109 orGroup = true;
110 }
111 else
112 orGroup = false;
113 }
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);
117 string provides;
118 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
119 {
120 // Ignore implicit provides for multiarch here
121 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
122 continue;
123 provides.append(", ").append(Prv.Name());
124 }
125 if (provides.empty() == false)
126 fprintf(output, "Provides: %s\n", provides.c_str()+2);
127}
128 /*}}}*/
129// EDSP::WriteScenarioLimitedDependency /*{{{*/
130void EDSP::WriteScenarioLimitedDependency(pkgDepCache &Cache, FILE* output,
131 pkgCache::PkgIterator const &Pkg,
132 pkgCache::VerIterator const &Ver,
133 APT::PackageSet const &pkgset)
134{
135 std::string dependencies[pkgCache::Dep::Enhances + 1];
136 bool orGroup = false;
137 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
138 {
139 // Ignore implicit dependencies for multiarch here
140 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
141 continue;
142 if (orGroup == false)
143 {
144 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
145 continue;
146 dependencies[Dep->Type].append(", ");
147 }
148 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
149 {
150 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
151 continue;
152 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
153 orGroup = false;
154 continue;
155 }
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)
160 {
161 dependencies[Dep->Type].append(" | ");
162 orGroup = true;
163 }
164 else
165 orGroup = false;
166 }
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);
170 string provides;
171 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
172 {
173 // Ignore implicit provides for multiarch here
174 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
175 continue;
176 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
177 continue;
178 provides.append(", ").append(Prv.Name());
179 }
180 if (provides.empty() == false)
181 fprintf(output, "Provides: %s\n", provides.c_str()+2);
182}
183 /*}}}*/
c3b85126 184// EDSP::WriteRequest - to the given file descriptor /*{{{*/
93794bc9
DK
185bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
186 bool const DistUpgrade, bool const AutoRemove)
6d38011b 187{
93794bc9 188 string del, inst;
6d38011b
DK
189 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
190 {
191 string* req;
192 if (Cache[Pkg].Delete() == true)
193 req = &del;
93794bc9 194 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
6d38011b 195 req = &inst;
6d38011b
DK
196 else
197 continue;
6d5bd614 198 req->append(" ").append(Pkg.FullName());
6d38011b 199 }
93794bc9 200 fprintf(output, "Request: EDSP 0.2\n");
6d38011b 201 if (del.empty() == false)
6d5bd614 202 fprintf(output, "Remove: %s\n", del.c_str()+1);
6d38011b 203 if (inst.empty() == false)
6d5bd614 204 fprintf(output, "Install: %s\n", inst.c_str()+1);
93794bc9
DK
205 if (Upgrade == true)
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");
6d5bd614 215 if (_config->Exists(solverpref) == true)
93794bc9 216 fprintf(output, "Preferences: %s\n", _config->Find(solverpref,"").c_str());
6d5bd614 217 fprintf(output, "\n");
6d38011b 218
e3674d91
DK
219 return true;
220}
221 /*}}}*/
2029276f
DK
222// EDSP::ReadResponse - from the given file descriptor /*{{{*/
223bool EDSP::ReadResponse(int const input, pkgDepCache &Cache) {
2a33cb16
DK
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
c80a49f5
DK
234 FileFd in;
235 in.OpenDescriptor(input, FileFd::ReadOnly);
288a76d2 236 pkgTagFile response(&in, 100);
c80a49f5
DK
237 pkgTagSection section;
238
2029276f
DK
239 while (response.Step(section) == true) {
240 std::string type;
241 if (section.Exists("Install") == true)
242 type = "Install";
243 else if (section.Exists("Remove") == true)
244 type = "Remove";
e876223c 245 else if (section.Exists("Progress") == true) {
288a76d2 246 std::clog << TimeRFC1123(time(NULL)) << " ";
e876223c
DK
247 ioprintf(std::clog, "[ %3d%% ] ", section.FindI("Percentage", 0));
248 std::clog << section.FindS("Progress") << " - ";
249 string const msg = section.FindS("Message");
250 if (msg.empty() == true)
251 std::clog << "Solver is still working on the solution" << std::endl;
252 else
253 std::clog << msg << std::endl;
254 continue;
255 } else
2029276f 256 continue;
29099cb6 257
2a33cb16
DK
258 size_t const id = section.FindULL(type.c_str(), VersionCount);
259 if (id == VersionCount) {
69a78835
DK
260 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
261 continue;
2a33cb16
DK
262 } else if (id > Cache.Head().VersionCount) {
263 _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());
264 continue;
69a78835 265 }
2029276f 266
2a33cb16 267 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
69a78835
DK
268 Cache.SetCandidateVersion(Ver);
269 if (type == "Install")
270 Cache.MarkInstall(Ver.ParentPkg(), false, false);
271 else if (type == "Remove")
272 Cache.MarkDelete(Ver.ParentPkg(), false);
2029276f
DK
273 }
274 return true;
275}
276 /*}}}*/
6d5bd614
DK
277// EDSP::ReadLine - first line from the given file descriptor /*{{{*/
278// ---------------------------------------------------------------------
279/* Little helper method to read a complete line into a string. Similar to
280 fgets but we need to use the low-level read() here as otherwise the
281 listparser will be confused later on as mixing of fgets and read isn't
2029276f 282 a supported action according to the manpages and results are undefined */
6d5bd614
DK
283bool EDSP::ReadLine(int const input, std::string &line) {
284 char one;
285 ssize_t data = 0;
286 line.erase();
287 line.reserve(100);
288 while ((data = read(input, &one, sizeof(one))) != -1) {
289 if (data != 1)
290 continue;
291 if (one == '\n')
292 return true;
293 if (one == '\r')
294 continue;
295 if (line.empty() == true && isblank(one) != 0)
296 continue;
297 line += one;
298 }
299 return false;
300}
301 /*}}}*/
40795fca
DK
302// EDSP::StringToBool - convert yes/no to bool /*{{{*/
303// ---------------------------------------------------------------------
304/* we are not as lazy as we are in the global StringToBool as we really
305 only accept yes/no here - but we will ignore leading spaces */
306bool EDSP::StringToBool(char const *answer, bool const defValue) {
307 for (; isspace(*answer) != 0; ++answer);
308 if (strncasecmp(answer, "yes", 3) == 0)
309 return true;
310 else if (strncasecmp(answer, "no", 2) == 0)
311 return false;
312 else
313 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
314 return defValue;
315}
316 /*}}}*/
6d5bd614
DK
317// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
318bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
40795fca
DK
319 std::list<std::string> &remove, bool &upgrade,
320 bool &distUpgrade, bool &autoRemove)
6d5bd614 321{
40795fca
DK
322 install.clear();
323 remove.clear();
324 upgrade = false;
325 distUpgrade = false;
326 autoRemove = false;
6d5bd614
DK
327 std::string line;
328 while (ReadLine(input, line) == true)
329 {
330 // Skip empty lines before request
331 if (line.empty() == true)
332 continue;
333 // The first Tag must be a request, so search for it
40795fca 334 if (line.compare(0, 8, "Request:") != 0)
6d5bd614
DK
335 continue;
336
337 while (ReadLine(input, line) == true)
338 {
339 // empty lines are the end of the request
340 if (line.empty() == true)
341 return true;
342
343 std::list<std::string> *request = NULL;
40795fca 344 if (line.compare(0, 8, "Install:") == 0)
6d5bd614 345 {
40795fca 346 line.erase(0, 8);
6d5bd614
DK
347 request = &install;
348 }
40795fca 349 else if (line.compare(0, 7, "Remove:") == 0)
6d5bd614 350 {
40795fca 351 line.erase(0, 7);
6d5bd614
DK
352 request = &remove;
353 }
40795fca
DK
354 else if (line.compare(0, 8, "Upgrade:") == 0)
355 upgrade = EDSP::StringToBool(line.c_str() + 9, false);
356 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
357 distUpgrade = EDSP::StringToBool(line.c_str() + 14, false);
358 else if (line.compare(0, 11, "Autoremove:") == 0)
359 autoRemove = EDSP::StringToBool(line.c_str() + 12, false);
360 else
361 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
362
6d5bd614
DK
363 if (request == NULL)
364 continue;
365 size_t end = line.length();
366 do {
367 size_t begin = line.rfind(' ');
368 if (begin == std::string::npos)
369 {
40795fca 370 request->push_back(line.substr(0, end));
6d5bd614
DK
371 break;
372 }
373 else if (begin < end)
374 request->push_back(line.substr(begin + 1, end));
375 line.erase(begin);
376 end = line.find_last_not_of(' ');
377 } while (end != std::string::npos);
378 }
379 }
380 return false;
381}
382 /*}}}*/
383// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
c3b85126 384bool EDSP::ApplyRequest(std::list<std::string> const &install,
29099cb6
DK
385 std::list<std::string> const &remove,
386 pkgDepCache &Cache)
6d5bd614
DK
387{
388 for (std::list<std::string>::const_iterator i = install.begin();
d4f626ff
DK
389 i != install.end(); ++i) {
390 pkgCache::PkgIterator P = Cache.FindPkg(*i);
391 if (P.end() == true)
392 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
393 else
394 Cache.MarkInstall(P, false);
395 }
6d5bd614
DK
396
397 for (std::list<std::string>::const_iterator i = remove.begin();
d4f626ff
DK
398 i != remove.end(); ++i) {
399 pkgCache::PkgIterator P = Cache.FindPkg(*i);
400 if (P.end() == true)
401 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
402 else
403 Cache.MarkDelete(P);
404 }
6d5bd614
DK
405 return true;
406}
407 /*}}}*/
c3b85126
DK
408// EDSP::WriteSolution - to the given file descriptor /*{{{*/
409bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
e3674d91 410{
6d5bd614 411 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
e3674d91
DK
412 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
413 {
414 if (Cache[Pkg].Delete() == true)
d4f626ff
DK
415 {
416 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
417 if (Debug == true)
418 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
419 }
e3674d91 420 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
d4f626ff 421 {
e3674d91 422 fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID);
d4f626ff
DK
423 if (Debug == true)
424 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr());
425 }
e3674d91
DK
426 else
427 continue;
e3674d91
DK
428 fprintf(output, "\n");
429 }
430
6d38011b
DK
431 return true;
432}
433 /*}}}*/
e876223c
DK
434// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
435bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
436 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
437 fprintf(output, "Percentage: %d\n", percent);
438 fprintf(output, "Message: %s\n\n", message);
439 fflush(output);
440 return true;
441}
442 /*}}}*/
c3b85126 443bool EDSP::WriteError(std::string const &message, FILE* output) { return false; }