move the mapping generation to the top as the response reading is
[ntk/apt.git] / apt-pkg / edsp.cc
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 /*{{{*/
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>
14
15 #include <apti18n.h>
16 #include <limits>
17
18 #include <stdio.h>
19 /*}}}*/
20
21 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
22 const char * const EDSP::PrioMap[] = {0, "important", "required", "standard",
23 "optional", "extra"};
24 const char * const EDSP::DepMap[] = {"", "Depends", "PreDepends", "Suggests",
25 "Recommends" , "Conflicts", "Replaces",
26 "Obsoletes", "Breaks", "Enhances"};
27
28 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
29 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output)
30 {
31 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
32 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
33 {
34 WriteScenarioVersion(Cache, output, Pkg, Ver);
35 WriteScenarioDependency(Cache, output, Pkg, Ver);
36 fprintf(output, "\n");
37 }
38 return true;
39 }
40 /*}}}*/
41 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
42 bool 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 /*{{{*/
56 void 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 /*{{{*/
91 void 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 /*{{{*/
130 void 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 /*}}}*/
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)
187 {
188 string del, inst;
189 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
190 {
191 string* req;
192 if (Cache[Pkg].Delete() == true)
193 req = &del;
194 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
195 req = &inst;
196 else
197 continue;
198 req->append(" ").append(Pkg.FullName());
199 }
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);
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");
215 if (_config->Exists(solverpref) == true)
216 fprintf(output, "Preferences: %s\n", _config->Find(solverpref,"").c_str());
217 fprintf(output, "\n");
218
219 return true;
220 }
221 /*}}}*/
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
234 FileFd in;
235 in.OpenDescriptor(input, FileFd::ReadOnly);
236 pkgTagFile response(&in);
237 pkgTagSection section;
238
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";
245 else if (section.Exists("Progress") == true) {
246 ioprintf(std::clog, "[ %3d%% ] ", section.FindI("Percentage", 0));
247 std::clog << section.FindS("Progress") << " - ";
248 string const msg = section.FindS("Message");
249 if (msg.empty() == true)
250 std::clog << "Solver is still working on the solution" << std::endl;
251 else
252 std::clog << msg << std::endl;
253 continue;
254 } else
255 continue;
256
257 size_t const id = section.FindULL(type.c_str(), VersionCount);
258 if (id == VersionCount) {
259 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
260 continue;
261 } else if (id > Cache.Head().VersionCount) {
262 _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());
263 continue;
264 }
265
266 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
267 Cache.SetCandidateVersion(Ver);
268 if (type == "Install")
269 Cache.MarkInstall(Ver.ParentPkg(), false, false);
270 else if (type == "Remove")
271 Cache.MarkDelete(Ver.ParentPkg(), false);
272 }
273 return true;
274 }
275 /*}}}*/
276 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
277 // ---------------------------------------------------------------------
278 /* Little helper method to read a complete line into a string. Similar to
279 fgets but we need to use the low-level read() here as otherwise the
280 listparser will be confused later on as mixing of fgets and read isn't
281 a supported action according to the manpages and results are undefined */
282 bool EDSP::ReadLine(int const input, std::string &line) {
283 char one;
284 ssize_t data = 0;
285 line.erase();
286 line.reserve(100);
287 while ((data = read(input, &one, sizeof(one))) != -1) {
288 if (data != 1)
289 continue;
290 if (one == '\n')
291 return true;
292 if (one == '\r')
293 continue;
294 if (line.empty() == true && isblank(one) != 0)
295 continue;
296 line += one;
297 }
298 return false;
299 }
300 /*}}}*/
301 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
302 // ---------------------------------------------------------------------
303 /* we are not as lazy as we are in the global StringToBool as we really
304 only accept yes/no here - but we will ignore leading spaces */
305 bool EDSP::StringToBool(char const *answer, bool const defValue) {
306 for (; isspace(*answer) != 0; ++answer);
307 if (strncasecmp(answer, "yes", 3) == 0)
308 return true;
309 else if (strncasecmp(answer, "no", 2) == 0)
310 return false;
311 else
312 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
313 return defValue;
314 }
315 /*}}}*/
316 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
317 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
318 std::list<std::string> &remove, bool &upgrade,
319 bool &distUpgrade, bool &autoRemove)
320 {
321 install.clear();
322 remove.clear();
323 upgrade = false;
324 distUpgrade = false;
325 autoRemove = false;
326 std::string line;
327 while (ReadLine(input, line) == true)
328 {
329 // Skip empty lines before request
330 if (line.empty() == true)
331 continue;
332 // The first Tag must be a request, so search for it
333 if (line.compare(0, 8, "Request:") != 0)
334 continue;
335
336 while (ReadLine(input, line) == true)
337 {
338 // empty lines are the end of the request
339 if (line.empty() == true)
340 return true;
341
342 std::list<std::string> *request = NULL;
343 if (line.compare(0, 8, "Install:") == 0)
344 {
345 line.erase(0, 8);
346 request = &install;
347 }
348 else if (line.compare(0, 7, "Remove:") == 0)
349 {
350 line.erase(0, 7);
351 request = &remove;
352 }
353 else if (line.compare(0, 8, "Upgrade:") == 0)
354 upgrade = EDSP::StringToBool(line.c_str() + 9, false);
355 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
356 distUpgrade = EDSP::StringToBool(line.c_str() + 14, false);
357 else if (line.compare(0, 11, "Autoremove:") == 0)
358 autoRemove = EDSP::StringToBool(line.c_str() + 12, false);
359 else
360 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
361
362 if (request == NULL)
363 continue;
364 size_t end = line.length();
365 do {
366 size_t begin = line.rfind(' ');
367 if (begin == std::string::npos)
368 {
369 request->push_back(line.substr(0, end));
370 break;
371 }
372 else if (begin < end)
373 request->push_back(line.substr(begin + 1, end));
374 line.erase(begin);
375 end = line.find_last_not_of(' ');
376 } while (end != std::string::npos);
377 }
378 }
379 return false;
380 }
381 /*}}}*/
382 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
383 bool EDSP::ApplyRequest(std::list<std::string> const &install,
384 std::list<std::string> const &remove,
385 pkgDepCache &Cache)
386 {
387 for (std::list<std::string>::const_iterator i = install.begin();
388 i != install.end(); ++i) {
389 pkgCache::PkgIterator P = Cache.FindPkg(*i);
390 if (P.end() == true)
391 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
392 else
393 Cache.MarkInstall(P, false);
394 }
395
396 for (std::list<std::string>::const_iterator i = remove.begin();
397 i != remove.end(); ++i) {
398 pkgCache::PkgIterator P = Cache.FindPkg(*i);
399 if (P.end() == true)
400 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
401 else
402 Cache.MarkDelete(P);
403 }
404 return true;
405 }
406 /*}}}*/
407 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
408 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
409 {
410 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
411 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
412 {
413 if (Cache[Pkg].Delete() == true)
414 {
415 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
416 if (Debug == true)
417 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
418 }
419 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
420 {
421 fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID);
422 if (Debug == true)
423 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr());
424 }
425 else
426 continue;
427 fprintf(output, "\n");
428 }
429
430 return true;
431 }
432 /*}}}*/
433 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
434 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
435 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
436 fprintf(output, "Percentage: %d\n", percent);
437 fprintf(output, "Message: %s\n\n", message);
438 fflush(output);
439 return true;
440 }
441 /*}}}*/
442 bool EDSP::WriteError(std::string const &message, FILE* output) { return false; }