]> git.saurik.com Git - apt.git/blame - apt-pkg/edsp.cc
edsp: document unique package identifiers
[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 /*{{{*/
ea542140
DK
8#include <config.h>
9
6d38011b 10#include <apt-pkg/error.h>
472ff00e 11#include <apt-pkg/cacheset.h>
453b82a3
DK
12#include <apt-pkg/depcache.h>
13#include <apt-pkg/pkgcache.h>
14#include <apt-pkg/cacheiterators.h>
ac7f8f79
MV
15#include <apt-pkg/progress.h>
16#include <apt-pkg/fileutl.h>
17#include <apt-pkg/edsp.h>
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/strutl.h>
6d38011b 20
453b82a3
DK
21#include <ctype.h>
22#include <stddef.h>
23#include <string.h>
453b82a3 24#include <unistd.h>
6d38011b 25#include <stdio.h>
453b82a3 26#include <iostream>
453b82a3 27#include <limits>
472ff00e
DK
28#include <string>
29
ea542140 30#include <apti18n.h>
6d38011b
DK
31 /*}}}*/
32
472ff00e
DK
33using std::string;
34
d4f626ff 35// we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
c8a4ce6c 36const char * const PrioMap[] = {0, "important", "required", "standard",
d4f626ff 37 "optional", "extra"};
c8a4ce6c 38const char * const DepMap[] = {"", "Depends", "Pre-Depends", "Suggests",
d4f626ff
DK
39 "Recommends" , "Conflicts", "Replaces",
40 "Obsoletes", "Breaks", "Enhances"};
41
c8a4ce6c
DK
42
43// WriteScenarioVersion /*{{{*/
44static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
d4f626ff
DK
45 pkgCache::VerIterator const &Ver)
46{
47 fprintf(output, "Package: %s\n", Pkg.Name());
a221efc3 48 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
d4f626ff
DK
49 fprintf(output, "Architecture: %s\n", Ver.Arch());
50 fprintf(output, "Version: %s\n", Ver.VerStr());
eed4639e 51 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
d4f626ff
DK
52 if (Pkg.CurrentVer() == Ver)
53 fprintf(output, "Installed: yes\n");
cbc702ea
DK
54 if (Pkg->SelectedState == pkgCache::State::Hold ||
55 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
d4f626ff
DK
56 fprintf(output, "Hold: yes\n");
57 fprintf(output, "APT-ID: %d\n", Ver->ID);
58 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
59 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
60 fprintf(output, "Essential: yes\n");
61 fprintf(output, "Section: %s\n", Ver.Section());
894d672e 62 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
d4f626ff 63 fprintf(output, "Multi-Arch: allowed\n");
894d672e 64 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
d4f626ff 65 fprintf(output, "Multi-Arch: foreign\n");
894d672e 66 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
d4f626ff 67 fprintf(output, "Multi-Arch: same\n");
b5ea5d4a
SZ
68 std::set<string> Releases;
69 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
70 pkgCache::PkgFileIterator File = I.File();
b07aeb1a 71 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
b5ea5d4a
SZ
72 string Release = File.RelStr();
73 if (!Release.empty())
74 Releases.insert(Release);
75 }
76 }
77 if (!Releases.empty()) {
78 fprintf(output, "APT-Release:\n");
79 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
80 fprintf(output, " %s\n", R->c_str());
d4f626ff 81 }
3c7567a5 82 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
294a8020 83 if (Cache.GetCandidateVersion(Pkg) == Ver)
d4f626ff
DK
84 fprintf(output, "APT-Candidate: yes\n");
85 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
86 fprintf(output, "APT-Automatic: yes\n");
87}
88 /*}}}*/
c8a4ce6c
DK
89// WriteScenarioDependency /*{{{*/
90static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
d4f626ff
DK
91{
92 std::string dependencies[pkgCache::Dep::Enhances + 1];
93 bool orGroup = false;
94 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
95 {
8c7af4d4 96 if (Dep.IsImplicit() == true)
d4f626ff
DK
97 continue;
98 if (orGroup == false)
99 dependencies[Dep->Type].append(", ");
3addaba1 100 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
101 if (Dep->Version != 0)
102 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
103 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
104 {
105 dependencies[Dep->Type].append(" | ");
106 orGroup = true;
107 }
108 else
109 orGroup = false;
110 }
111 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
112 if (dependencies[i].empty() == false)
113 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
114 string provides;
115 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
116 {
d5648746 117 if (Prv.IsMultiArchImplicit() == true)
d4f626ff
DK
118 continue;
119 provides.append(", ").append(Prv.Name());
2c53226b
DK
120 if (Prv->ProvideVersion != 0)
121 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
d4f626ff
DK
122 }
123 if (provides.empty() == false)
124 fprintf(output, "Provides: %s\n", provides.c_str()+2);
125}
126 /*}}}*/
c8a4ce6c
DK
127// WriteScenarioLimitedDependency /*{{{*/
128static void WriteScenarioLimitedDependency(FILE* output,
d4f626ff
DK
129 pkgCache::VerIterator const &Ver,
130 APT::PackageSet const &pkgset)
131{
132 std::string dependencies[pkgCache::Dep::Enhances + 1];
133 bool orGroup = false;
134 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
135 {
8c7af4d4 136 if (Dep.IsImplicit() == true)
d4f626ff
DK
137 continue;
138 if (orGroup == false)
139 {
140 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
141 continue;
142 dependencies[Dep->Type].append(", ");
143 }
144 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
145 {
146 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
147 continue;
148 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
149 orGroup = false;
150 continue;
151 }
3addaba1 152 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
153 if (Dep->Version != 0)
154 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
155 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
156 {
157 dependencies[Dep->Type].append(" | ");
158 orGroup = true;
159 }
160 else
161 orGroup = false;
162 }
163 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
164 if (dependencies[i].empty() == false)
165 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
166 string provides;
167 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
168 {
d5648746 169 if (Prv.IsMultiArchImplicit() == true)
d4f626ff
DK
170 continue;
171 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
172 continue;
173 provides.append(", ").append(Prv.Name());
174 }
175 if (provides.empty() == false)
176 fprintf(output, "Provides: %s\n", provides.c_str()+2);
177}
178 /*}}}*/
33190fe3
DK
179static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
180{
181 /* versions which aren't current and aren't available in
182 any "online" source file are bad, expect if they are the choosen
183 candidate: The exception is for build-dep implementation as it creates
184 such pseudo (package) versions and removes them later on again.
185 We filter out versions at all so packages in 'rc' state only available
186 in dpkg/status aren't passed to solvers as they can't be installed. */
187 if (Pkg->CurrentVer != 0)
188 return false;
189 if (Cache.GetCandidateVersion(Pkg) == Ver)
190 return false;
191 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
192 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
193 return false;
194 return true;
195}
196 /*}}}*/
c8a4ce6c
DK
197// EDSP::WriteScenario - to the given file descriptor /*{{{*/
198bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
199{
200 if (Progress != NULL)
201 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
202 unsigned long p = 0;
203 std::vector<std::string> archs = APT::Configuration::getArchitectures();
204 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
205 {
206 std::string const arch = Pkg.Arch();
207 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
208 continue;
209 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
210 {
33190fe3
DK
211 if (SkipUnavailableVersions(Cache, Pkg, Ver))
212 continue;
c8a4ce6c
DK
213 WriteScenarioVersion(Cache, output, Pkg, Ver);
214 WriteScenarioDependency(output, Ver);
215 fprintf(output, "\n");
216 if (Progress != NULL && p % 100 == 0)
217 Progress->Progress(p);
218 }
219 }
220 return true;
221}
222 /*}}}*/
223// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
224bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
225 APT::PackageSet const &pkgset,
226 OpProgress *Progress)
227{
228 if (Progress != NULL)
229 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
230 unsigned long p = 0;
231 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
232 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
233 {
33190fe3
DK
234 if (SkipUnavailableVersions(Cache, Pkg, Ver))
235 continue;
c8a4ce6c
DK
236 WriteScenarioVersion(Cache, output, Pkg, Ver);
237 WriteScenarioLimitedDependency(output, Ver, pkgset);
238 fprintf(output, "\n");
239 if (Progress != NULL && p % 100 == 0)
240 Progress->Progress(p);
241 }
242 if (Progress != NULL)
243 Progress->Done();
244 return true;
245}
246 /*}}}*/
c3b85126 247// EDSP::WriteRequest - to the given file descriptor /*{{{*/
93794bc9 248bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
b57c0e35
DK
249 bool const DistUpgrade, bool const AutoRemove,
250 OpProgress *Progress)
6d38011b 251{
b57c0e35
DK
252 if (Progress != NULL)
253 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
254 unsigned long p = 0;
93794bc9 255 string del, inst;
b57c0e35 256 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
6d38011b 257 {
b57c0e35
DK
258 if (Progress != NULL && p % 100 == 0)
259 Progress->Progress(p);
6d38011b 260 string* req;
036eb012
DK
261 pkgDepCache::StateCache &P = Cache[Pkg];
262 if (P.Delete() == true)
6d38011b 263 req = &del;
036eb012
DK
264 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
265 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
6d38011b 266 req = &inst;
6d38011b
DK
267 else
268 continue;
6d5bd614 269 req->append(" ").append(Pkg.FullName());
6d38011b 270 }
25252738 271 fprintf(output, "Request: EDSP 0.5\n");
caa32793
SZ
272
273 const char *arch = _config->Find("APT::Architecture").c_str();
274 std::vector<string> archs = APT::Configuration::getArchitectures();
275 fprintf(output, "Architecture: %s\n", arch);
276 fprintf(output, "Architectures:");
277 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
278 fprintf(output, " %s", a->c_str());
279 fprintf(output, "\n");
280
6d38011b 281 if (del.empty() == false)
6d5bd614 282 fprintf(output, "Remove: %s\n", del.c_str()+1);
6d38011b 283 if (inst.empty() == false)
6d5bd614 284 fprintf(output, "Install: %s\n", inst.c_str()+1);
93794bc9
DK
285 if (Upgrade == true)
286 fprintf(output, "Upgrade: yes\n");
287 if (DistUpgrade == true)
288 fprintf(output, "Dist-Upgrade: yes\n");
289 if (AutoRemove == true)
290 fprintf(output, "Autoremove: yes\n");
eb1000f6
DK
291 auto const solver = _config->Find("APT::Solver", "internal");
292 fprintf(output, "Solver: %s\n", solver.c_str());
293 auto const solverconf = std::string("APT::Solver::") + solver + "::";
294 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
93794bc9 295 fprintf(output, "Strict-Pinning: no\n");
eb1000f6
DK
296 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
297 if (solverpref.empty() == false)
298 fprintf(output, "Preferences: %s\n", solverpref.c_str());
6d5bd614 299 fprintf(output, "\n");
e3674d91
DK
300 return true;
301}
302 /*}}}*/
2029276f 303// EDSP::ReadResponse - from the given file descriptor /*{{{*/
b57c0e35 304bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
2a33cb16
DK
305 /* We build an map id to mmap offset here
306 In theory we could use the offset as ID, but then VersionCount
307 couldn't be used to create other versionmappings anymore and it
308 would be too easy for a (buggy) solver to segfault APTā€¦ */
309 unsigned long long const VersionCount = Cache.Head().VersionCount;
310 unsigned long VerIdx[VersionCount];
76d4aab0 311 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
2a33cb16
DK
312 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
313 VerIdx[V->ID] = V.Index();
76d4aab0
DK
314 Cache[P].Marked = true;
315 Cache[P].Garbage = false;
316 }
2a33cb16 317
c80a49f5
DK
318 FileFd in;
319 in.OpenDescriptor(input, FileFd::ReadOnly);
288a76d2 320 pkgTagFile response(&in, 100);
c80a49f5
DK
321 pkgTagSection section;
322
2029276f
DK
323 while (response.Step(section) == true) {
324 std::string type;
325 if (section.Exists("Install") == true)
326 type = "Install";
327 else if (section.Exists("Remove") == true)
328 type = "Remove";
e876223c 329 else if (section.Exists("Progress") == true) {
b57c0e35 330 if (Progress != NULL) {
c6660a4b 331 string msg = section.FindS("Message");
b57c0e35 332 if (msg.empty() == true)
c6660a4b
DK
333 msg = _("Prepare for receiving solution");
334 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
b57c0e35 335 }
e876223c 336 continue;
ebfeeaed 337 } else if (section.Exists("Error") == true) {
27c69dd0
DK
338 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
339 if (msg.empty() == true) {
340 msg = _("External solver failed without a proper error message");
a1e68c33 341 _error->Error("%s", msg.c_str());
27c69dd0
DK
342 } else
343 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
344 if (Progress != NULL)
345 Progress->Done();
ebfeeaed
DK
346 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
347 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
27c69dd0
DK
348 std::cerr << msg << std::endl << std::endl;
349 return false;
76d4aab0
DK
350 } else if (section.Exists("Autoremove") == true)
351 type = "Autoremove";
352 else
2029276f 353 continue;
29099cb6 354
2a33cb16
DK
355 size_t const id = section.FindULL(type.c_str(), VersionCount);
356 if (id == VersionCount) {
69a78835
DK
357 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
358 continue;
2a33cb16
DK
359 } else if (id > Cache.Head().VersionCount) {
360 _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());
361 continue;
69a78835 362 }
2029276f 363
2a33cb16 364 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
69a78835
DK
365 Cache.SetCandidateVersion(Ver);
366 if (type == "Install")
9777639e
DK
367 {
368 pkgCache::PkgIterator const P = Ver.ParentPkg();
369 if (Cache[P].Mode != pkgDepCache::ModeInstall)
370 Cache.MarkInstall(P, false, 0, false);
371 }
69a78835
DK
372 else if (type == "Remove")
373 Cache.MarkDelete(Ver.ParentPkg(), false);
76d4aab0
DK
374 else if (type == "Autoremove") {
375 Cache[Ver.ParentPkg()].Marked = false;
376 Cache[Ver.ParentPkg()].Garbage = true;
377 }
2029276f
DK
378 }
379 return true;
380}
381 /*}}}*/
c8a4ce6c 382// ReadLine - first line from the given file descriptor /*{{{*/
6d5bd614
DK
383// ---------------------------------------------------------------------
384/* Little helper method to read a complete line into a string. Similar to
385 fgets but we need to use the low-level read() here as otherwise the
386 listparser will be confused later on as mixing of fgets and read isn't
2029276f 387 a supported action according to the manpages and results are undefined */
c8a4ce6c 388static bool ReadLine(int const input, std::string &line) {
6d5bd614
DK
389 char one;
390 ssize_t data = 0;
391 line.erase();
392 line.reserve(100);
393 while ((data = read(input, &one, sizeof(one))) != -1) {
394 if (data != 1)
395 continue;
396 if (one == '\n')
397 return true;
398 if (one == '\r')
399 continue;
400 if (line.empty() == true && isblank(one) != 0)
401 continue;
402 line += one;
403 }
404 return false;
405}
406 /*}}}*/
c8a4ce6c 407// StringToBool - convert yes/no to bool /*{{{*/
40795fca
DK
408// ---------------------------------------------------------------------
409/* we are not as lazy as we are in the global StringToBool as we really
410 only accept yes/no here - but we will ignore leading spaces */
c8a4ce6c 411static bool StringToBool(char const *answer, bool const defValue) {
40795fca
DK
412 for (; isspace(*answer) != 0; ++answer);
413 if (strncasecmp(answer, "yes", 3) == 0)
414 return true;
415 else if (strncasecmp(answer, "no", 2) == 0)
416 return false;
417 else
418 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
419 return defValue;
420}
421 /*}}}*/
6d5bd614
DK
422// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
423bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
40795fca
DK
424 std::list<std::string> &remove, bool &upgrade,
425 bool &distUpgrade, bool &autoRemove)
6d5bd614 426{
40795fca
DK
427 install.clear();
428 remove.clear();
429 upgrade = false;
430 distUpgrade = false;
431 autoRemove = false;
6d5bd614
DK
432 std::string line;
433 while (ReadLine(input, line) == true)
434 {
435 // Skip empty lines before request
436 if (line.empty() == true)
437 continue;
438 // The first Tag must be a request, so search for it
40795fca 439 if (line.compare(0, 8, "Request:") != 0)
6d5bd614
DK
440 continue;
441
442 while (ReadLine(input, line) == true)
443 {
444 // empty lines are the end of the request
445 if (line.empty() == true)
446 return true;
447
448 std::list<std::string> *request = NULL;
40795fca 449 if (line.compare(0, 8, "Install:") == 0)
6d5bd614 450 {
40795fca 451 line.erase(0, 8);
6d5bd614
DK
452 request = &install;
453 }
40795fca 454 else if (line.compare(0, 7, "Remove:") == 0)
6d5bd614 455 {
40795fca 456 line.erase(0, 7);
6d5bd614
DK
457 request = &remove;
458 }
40795fca 459 else if (line.compare(0, 8, "Upgrade:") == 0)
c8a4ce6c 460 upgrade = StringToBool(line.c_str() + 9, false);
40795fca 461 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
c8a4ce6c 462 distUpgrade = StringToBool(line.c_str() + 14, false);
40795fca 463 else if (line.compare(0, 11, "Autoremove:") == 0)
c8a4ce6c 464 autoRemove = StringToBool(line.c_str() + 12, false);
1f6cf9e7
DK
465 else if (line.compare(0, 13, "Architecture:") == 0)
466 _config->Set("APT::Architecture", line.c_str() + 14);
467 else if (line.compare(0, 14, "Architectures:") == 0)
468 {
469 std::string const archs = line.c_str() + 15;
470 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
471 }
eb1000f6
DK
472 else if (line.compare(0, 7, "Solver:") == 0)
473 ; // purely informational line
40795fca
DK
474 else
475 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
476
6d5bd614
DK
477 if (request == NULL)
478 continue;
479 size_t end = line.length();
480 do {
481 size_t begin = line.rfind(' ');
482 if (begin == std::string::npos)
483 {
40795fca 484 request->push_back(line.substr(0, end));
6d5bd614
DK
485 break;
486 }
487 else if (begin < end)
488 request->push_back(line.substr(begin + 1, end));
489 line.erase(begin);
490 end = line.find_last_not_of(' ');
491 } while (end != std::string::npos);
492 }
493 }
494 return false;
495}
496 /*}}}*/
497// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
c3b85126 498bool EDSP::ApplyRequest(std::list<std::string> const &install,
29099cb6
DK
499 std::list<std::string> const &remove,
500 pkgDepCache &Cache)
6d5bd614
DK
501{
502 for (std::list<std::string>::const_iterator i = install.begin();
d4f626ff
DK
503 i != install.end(); ++i) {
504 pkgCache::PkgIterator P = Cache.FindPkg(*i);
505 if (P.end() == true)
506 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
507 else
508 Cache.MarkInstall(P, false);
509 }
6d5bd614
DK
510
511 for (std::list<std::string>::const_iterator i = remove.begin();
d4f626ff
DK
512 i != remove.end(); ++i) {
513 pkgCache::PkgIterator P = Cache.FindPkg(*i);
514 if (P.end() == true)
515 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
516 else
517 Cache.MarkDelete(P);
518 }
6d5bd614
DK
519 return true;
520}
521 /*}}}*/
c3b85126
DK
522// EDSP::WriteSolution - to the given file descriptor /*{{{*/
523bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
e3674d91 524{
6d5bd614 525 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
e3674d91
DK
526 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
527 {
528 if (Cache[Pkg].Delete() == true)
d4f626ff
DK
529 {
530 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
531 if (Debug == true)
532 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
533 }
e3674d91 534 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
d4f626ff 535 {
294a8020
DK
536 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
537 fprintf(output, "Install: %d\n", CandVer->ID);
d4f626ff 538 if (Debug == true)
294a8020 539 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
d4f626ff 540 }
76d4aab0
DK
541 else if (Cache[Pkg].Garbage == true)
542 {
543 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
544 if (Debug == true)
545 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
76d4aab0 546 }
e3674d91
DK
547 else
548 continue;
e3674d91
DK
549 fprintf(output, "\n");
550 }
551
6d38011b
DK
552 return true;
553}
554 /*}}}*/
e876223c
DK
555// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
556bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
3d17b9ff
DK
557 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
558 fprintf(output, "Percentage: %d\n", percent);
559 fprintf(output, "Message: %s\n\n", message);
560 fflush(output);
e876223c
DK
561 return true;
562}
563 /*}}}*/
ebfeeaed
DK
564// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
565bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
566 fprintf(output, "Error: %s\n", uuid);
567 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
568 return true;
569}
570 /*}}}*/
ac5fbff8 571// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
5681b3fc 572pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
741b7da9
DK
573 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
574 std::string file;
575 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
576 dir != solverDirs.end(); ++dir) {
577 file = flCombine(*dir, solver);
578 if (RealFileExists(file.c_str()) == true)
579 break;
580 file.clear();
581 }
ac5fbff8 582
741b7da9 583 if (file.empty() == true)
5681b3fc
DK
584 {
585 _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
586 return 0;
587 }
741b7da9
DK
588 int external[4] = {-1, -1, -1, -1};
589 if (pipe(external) != 0 || pipe(external + 2) != 0)
5681b3fc
DK
590 {
591 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
592 return 0;
593 }
741b7da9
DK
594 for (int i = 0; i < 4; ++i)
595 SetCloseExec(external[i], true);
ac5fbff8 596
741b7da9
DK
597 pid_t Solver = ExecFork();
598 if (Solver == 0) {
599 dup2(external[0], STDIN_FILENO);
600 dup2(external[3], STDOUT_FILENO);
601 const char* calling[2] = { file.c_str(), 0 };
602 execv(calling[0], (char**) calling);
603 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
604 _exit(100);
605 }
606 close(external[0]);
607 close(external[3]);
ac5fbff8 608
741b7da9 609 if (WaitFd(external[1], true, 5) == false)
5681b3fc
DK
610 {
611 _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
612 return 0;
613 }
ac5fbff8 614
741b7da9
DK
615 *solver_in = external[1];
616 *solver_out = external[2];
5681b3fc
DK
617 return Solver;
618}
619bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
620 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
621 return false;
622 return true;
741b7da9
DK
623}
624 /*}}}*/
625// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
626bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
627 bool const upgrade, bool const distUpgrade,
b57c0e35 628 bool const autoRemove, OpProgress *Progress) {
741b7da9 629 int solver_in, solver_out;
5681b3fc
DK
630 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
631 if (solver_pid == 0)
741b7da9
DK
632 return false;
633
634 FILE* output = fdopen(solver_in, "w");
635 if (output == NULL)
636 return _error->Errno("Resolve", "fdopen on solver stdin failed");
b57c0e35
DK
637
638 if (Progress != NULL)
639 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
640 EDSP::WriteRequest(Cache, output, upgrade, distUpgrade, autoRemove, Progress);
641 if (Progress != NULL)
642 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
643 EDSP::WriteScenario(Cache, output, Progress);
741b7da9
DK
644 fclose(output);
645
b57c0e35
DK
646 if (Progress != NULL)
647 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
648 if (EDSP::ReadResponse(solver_out, Cache, Progress) == false)
27c69dd0 649 return false;
741b7da9 650
5681b3fc 651 return ExecWait(solver_pid, solver);
ac5fbff8
DK
652}
653 /*}}}*/