tell the resolver a package is set on hold if it was set by the user
[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 (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());
81 if (Pin < p)
82 Pin = p;
83 }
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");
89 }
90 /*}}}*/
91 // EDSP::WriteScenarioDependency /*{{{*/
92 void EDSP::WriteScenarioDependency(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
93 pkgCache::VerIterator const &Ver)
94 {
95 std::string dependencies[pkgCache::Dep::Enhances + 1];
96 bool orGroup = false;
97 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
98 {
99 // Ignore implicit dependencies for multiarch here
100 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
101 continue;
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)
108 {
109 dependencies[Dep->Type].append(" | ");
110 orGroup = true;
111 }
112 else
113 orGroup = false;
114 }
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);
118 string provides;
119 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
120 {
121 // Ignore implicit provides for multiarch here
122 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
123 continue;
124 provides.append(", ").append(Prv.Name());
125 }
126 if (provides.empty() == false)
127 fprintf(output, "Provides: %s\n", provides.c_str()+2);
128 }
129 /*}}}*/
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)
135 {
136 std::string dependencies[pkgCache::Dep::Enhances + 1];
137 bool orGroup = false;
138 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
139 {
140 // Ignore implicit dependencies for multiarch here
141 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
142 continue;
143 if (orGroup == false)
144 {
145 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
146 continue;
147 dependencies[Dep->Type].append(", ");
148 }
149 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
150 {
151 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
152 continue;
153 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
154 orGroup = false;
155 continue;
156 }
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)
161 {
162 dependencies[Dep->Type].append(" | ");
163 orGroup = true;
164 }
165 else
166 orGroup = false;
167 }
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);
171 string provides;
172 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
173 {
174 // Ignore implicit provides for multiarch here
175 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
176 continue;
177 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
178 continue;
179 provides.append(", ").append(Prv.Name());
180 }
181 if (provides.empty() == false)
182 fprintf(output, "Provides: %s\n", provides.c_str()+2);
183 }
184 /*}}}*/
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)
188 {
189 string del, inst;
190 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
191 {
192 string* req;
193 if (Cache[Pkg].Delete() == true)
194 req = &del;
195 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
196 req = &inst;
197 else
198 continue;
199 req->append(" ").append(Pkg.FullName());
200 }
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);
206 if (Upgrade == true)
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");
219
220 return true;
221 }
222 /*}}}*/
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;
236 }
237
238 FileFd in;
239 in.OpenDescriptor(input, FileFd::ReadOnly);
240 pkgTagFile response(&in, 100);
241 pkgTagSection section;
242
243 while (response.Step(section) == true) {
244 std::string type;
245 if (section.Exists("Install") == true)
246 type = "Install";
247 else if (section.Exists("Remove") == true)
248 type = "Remove";
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;
256 else
257 std::clog << msg << std::endl;
258 continue;
259 } else if (section.Exists("Autoremove") == true)
260 type = "Autoremove";
261 else
262 continue;
263
264 size_t const id = section.FindULL(type.c_str(), VersionCount);
265 if (id == VersionCount) {
266 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
267 continue;
268 } else if (id > Cache.Head().VersionCount) {
269 _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());
270 continue;
271 }
272
273 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
274 Cache.SetCandidateVersion(Ver);
275 if (type == "Install")
276 Cache.MarkInstall(Ver.ParentPkg(), false, false);
277 else if (type == "Remove")
278 Cache.MarkDelete(Ver.ParentPkg(), false);
279 else if (type == "Autoremove") {
280 Cache[Ver.ParentPkg()].Marked = false;
281 Cache[Ver.ParentPkg()].Garbage = true;
282 }
283 }
284 return true;
285 }
286 /*}}}*/
287 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
288 // ---------------------------------------------------------------------
289 /* Little helper method to read a complete line into a string. Similar to
290 fgets but we need to use the low-level read() here as otherwise the
291 listparser will be confused later on as mixing of fgets and read isn't
292 a supported action according to the manpages and results are undefined */
293 bool EDSP::ReadLine(int const input, std::string &line) {
294 char one;
295 ssize_t data = 0;
296 line.erase();
297 line.reserve(100);
298 while ((data = read(input, &one, sizeof(one))) != -1) {
299 if (data != 1)
300 continue;
301 if (one == '\n')
302 return true;
303 if (one == '\r')
304 continue;
305 if (line.empty() == true && isblank(one) != 0)
306 continue;
307 line += one;
308 }
309 return false;
310 }
311 /*}}}*/
312 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
313 // ---------------------------------------------------------------------
314 /* we are not as lazy as we are in the global StringToBool as we really
315 only accept yes/no here - but we will ignore leading spaces */
316 bool EDSP::StringToBool(char const *answer, bool const defValue) {
317 for (; isspace(*answer) != 0; ++answer);
318 if (strncasecmp(answer, "yes", 3) == 0)
319 return true;
320 else if (strncasecmp(answer, "no", 2) == 0)
321 return false;
322 else
323 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
324 return defValue;
325 }
326 /*}}}*/
327 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
328 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
329 std::list<std::string> &remove, bool &upgrade,
330 bool &distUpgrade, bool &autoRemove)
331 {
332 install.clear();
333 remove.clear();
334 upgrade = false;
335 distUpgrade = false;
336 autoRemove = false;
337 std::string line;
338 while (ReadLine(input, line) == true)
339 {
340 // Skip empty lines before request
341 if (line.empty() == true)
342 continue;
343 // The first Tag must be a request, so search for it
344 if (line.compare(0, 8, "Request:") != 0)
345 continue;
346
347 while (ReadLine(input, line) == true)
348 {
349 // empty lines are the end of the request
350 if (line.empty() == true)
351 return true;
352
353 std::list<std::string> *request = NULL;
354 if (line.compare(0, 8, "Install:") == 0)
355 {
356 line.erase(0, 8);
357 request = &install;
358 }
359 else if (line.compare(0, 7, "Remove:") == 0)
360 {
361 line.erase(0, 7);
362 request = &remove;
363 }
364 else if (line.compare(0, 8, "Upgrade:") == 0)
365 upgrade = EDSP::StringToBool(line.c_str() + 9, false);
366 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
367 distUpgrade = EDSP::StringToBool(line.c_str() + 14, false);
368 else if (line.compare(0, 11, "Autoremove:") == 0)
369 autoRemove = EDSP::StringToBool(line.c_str() + 12, false);
370 else
371 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
372
373 if (request == NULL)
374 continue;
375 size_t end = line.length();
376 do {
377 size_t begin = line.rfind(' ');
378 if (begin == std::string::npos)
379 {
380 request->push_back(line.substr(0, end));
381 break;
382 }
383 else if (begin < end)
384 request->push_back(line.substr(begin + 1, end));
385 line.erase(begin);
386 end = line.find_last_not_of(' ');
387 } while (end != std::string::npos);
388 }
389 }
390 return false;
391 }
392 /*}}}*/
393 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
394 bool EDSP::ApplyRequest(std::list<std::string> const &install,
395 std::list<std::string> const &remove,
396 pkgDepCache &Cache)
397 {
398 for (std::list<std::string>::const_iterator i = install.begin();
399 i != install.end(); ++i) {
400 pkgCache::PkgIterator P = Cache.FindPkg(*i);
401 if (P.end() == true)
402 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
403 else
404 Cache.MarkInstall(P, false);
405 }
406
407 for (std::list<std::string>::const_iterator i = remove.begin();
408 i != remove.end(); ++i) {
409 pkgCache::PkgIterator P = Cache.FindPkg(*i);
410 if (P.end() == true)
411 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
412 else
413 Cache.MarkDelete(P);
414 }
415 return true;
416 }
417 /*}}}*/
418 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
419 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
420 {
421 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
422 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
423 {
424 if (Cache[Pkg].Delete() == true)
425 {
426 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
427 if (Debug == true)
428 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
429 }
430 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
431 {
432 fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID);
433 if (Debug == true)
434 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr());
435 }
436 else if (Cache[Pkg].Garbage == true)
437 {
438 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
439 if (Debug == true)
440 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
441 fprintf(stderr, "Autoremove: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
442 }
443 else
444 continue;
445 fprintf(output, "\n");
446 }
447
448 return true;
449 }
450 /*}}}*/
451 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
452 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
453 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
454 fprintf(output, "Percentage: %d\n", percent);
455 fprintf(output, "Message: %s\n\n", message);
456 fflush(output);
457 return true;
458 }
459 /*}}}*/
460 bool EDSP::WriteError(std::string const &message, FILE* output) { return false; }
461
462 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
463 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
464 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
465 std::string file;
466 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
467 dir != solverDirs.end(); ++dir) {
468 file = flCombine(*dir, solver);
469 if (RealFileExists(file.c_str()) == true)
470 break;
471 file.clear();
472 }
473
474 if (file.empty() == true)
475 return _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
476 int external[4] = {-1, -1, -1, -1};
477 if (pipe(external) != 0 || pipe(external + 2) != 0)
478 return _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
479 for (int i = 0; i < 4; ++i)
480 SetCloseExec(external[i], true);
481
482 pid_t Solver = ExecFork();
483 if (Solver == 0) {
484 dup2(external[0], STDIN_FILENO);
485 dup2(external[3], STDOUT_FILENO);
486 const char* calling[2] = { file.c_str(), 0 };
487 execv(calling[0], (char**) calling);
488 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
489 _exit(100);
490 }
491 close(external[0]);
492 close(external[3]);
493
494 if (WaitFd(external[1], true, 5) == false)
495 return _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
496
497 *solver_in = external[1];
498 *solver_out = external[2];
499 return true;
500 }
501 /*}}}*/
502 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
503 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
504 bool const upgrade, bool const distUpgrade,
505 bool const autoRemove) {
506 int solver_in, solver_out;
507 if (EDSP::ExecuteSolver(solver, &solver_in, &solver_out) == false)
508 return false;
509
510 FILE* output = fdopen(solver_in, "w");
511 if (output == NULL)
512 return _error->Errno("Resolve", "fdopen on solver stdin failed");
513 EDSP::WriteRequest(Cache, output, upgrade, distUpgrade, autoRemove);
514 EDSP::WriteScenario(Cache, output);
515 fclose(output);
516
517 if (EDSP::ReadResponse(solver_out, Cache) == false)
518 return _error->Error("Reading solver response failed");
519
520 return true;
521 }
522 /*}}}*/