]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/dpkgpm.cc
bc34e50e03a4b8faddd458c0cfd2e84cb036f09b
[apt.git] / apt-pkg / deb / dpkgpm.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 DPKG Package Manager - Provide an interface to dpkg
6
7 ##################################################################### */
8 /*}}}*/
9 // Includes /*{{{*/
10 #include <config.h>
11
12 #include <apt-pkg/cachefile.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/depcache.h>
15 #include <apt-pkg/dpkgpm.h>
16 #include <apt-pkg/debsystem.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/statechanges.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
26 #include <apt-pkg/version.h>
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <grp.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41 #include <sys/types.h>
42 #include <dirent.h>
43 #include <termios.h>
44 #include <time.h>
45 #include <unistd.h>
46
47 #include <algorithm>
48 #include <array>
49 #include <cstring>
50 #include <iostream>
51 #include <map>
52 #include <set>
53 #include <string>
54 #include <utility>
55 #include <vector>
56 #include <sstream>
57 #include <numeric>
58
59 #include <apti18n.h>
60 /*}}}*/
61
62 using namespace std;
63
64 APT_PURE static string AptHistoryRequestingUser() /*{{{*/
65 {
66 const char* EnvKeys[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
67
68 for (const auto &Key: EnvKeys)
69 {
70 if (getenv(Key) != nullptr)
71 {
72 int uid = atoi(getenv(Key));
73 if (uid > 0) {
74 struct passwd pwd;
75 struct passwd *result;
76 char buf[255];
77 if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result != NULL) {
78 std::string res;
79 strprintf(res, "%s (%d)", pwd.pw_name, uid);
80 return res;
81 }
82 }
83 }
84 }
85 return "";
86 }
87 /*}}}*/
88 APT_PURE static unsigned int EnvironmentSize() /*{{{*/
89 {
90 unsigned int size = 0;
91 char **envp = environ;
92
93 while (*envp != NULL)
94 size += strlen (*envp++) + 1;
95
96 return size;
97 }
98 /*}}}*/
99 class pkgDPkgPMPrivate /*{{{*/
100 {
101 public:
102 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
103 term_out(NULL), history_out(NULL),
104 progress(NULL), tt_is_valid(false), master(-1),
105 slave(NULL), protect_slave_from_dying(-1),
106 direct_stdin(false)
107 {
108 dpkgbuf[0] = '\0';
109 }
110 ~pkgDPkgPMPrivate()
111 {
112 }
113 bool stdin_is_dev_null;
114 // the buffer we use for the dpkg status-fd reading
115 char dpkgbuf[1024];
116 size_t dpkgbuf_pos;
117 FILE *term_out;
118 FILE *history_out;
119 string dpkg_error;
120 APT::Progress::PackageManager *progress;
121
122 // pty stuff
123 struct termios tt;
124 bool tt_is_valid;
125 int master;
126 char * slave;
127 int protect_slave_from_dying;
128
129 // signals
130 sigset_t sigmask;
131 sigset_t original_sigmask;
132
133 bool direct_stdin;
134 };
135 /*}}}*/
136 namespace
137 {
138 // Maps the dpkg "processing" info to human readable names. Entry 0
139 // of each array is the key, entry 1 is the value.
140 const std::pair<const char *, const char *> PackageProcessingOps[] = {
141 std::make_pair("install", N_("Installing %s")),
142 std::make_pair("configure", N_("Configuring %s")),
143 std::make_pair("remove", N_("Removing %s")),
144 std::make_pair("purge", N_("Completely removing %s")),
145 std::make_pair("disappear", N_("Noting disappearance of %s")),
146 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
147 };
148
149 const std::pair<const char *, const char *> * const PackageProcessingOpsBegin = PackageProcessingOps;
150 const std::pair<const char *, const char *> * const PackageProcessingOpsEnd = PackageProcessingOps + sizeof(PackageProcessingOps) / sizeof(PackageProcessingOps[0]);
151
152 // Predicate to test whether an entry in the PackageProcessingOps
153 // array matches a string.
154 class MatchProcessingOp
155 {
156 const char *target;
157
158 public:
159 explicit MatchProcessingOp(const char *the_target)
160 : target(the_target)
161 {
162 }
163
164 bool operator()(const std::pair<const char *, const char *> &pair) const
165 {
166 return strcmp(pair.first, target) == 0;
167 }
168 };
169 }
170
171 // ionice - helper function to ionice the given PID /*{{{*/
172 /* there is no C header for ionice yet - just the syscall interface
173 so we use the binary from util-linux */
174 static bool ionice(int PID)
175 {
176 if (!FileExists("/usr/bin/ionice"))
177 return false;
178 pid_t Process = ExecFork();
179 if (Process == 0)
180 {
181 char buf[32];
182 snprintf(buf, sizeof(buf), "-p%d", PID);
183 const char *Args[4];
184 Args[0] = "/usr/bin/ionice";
185 Args[1] = "-c3";
186 Args[2] = buf;
187 Args[3] = 0;
188 execv(Args[0], (char **)Args);
189 }
190 return ExecWait(Process, "ionice");
191 }
192 /*}}}*/
193 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This is helpful when a package is no longer installed but has residual
196 * config files
197 */
198 static
199 pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
200 {
201 pkgCache::VerIterator Ver;
202 for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
203 for (pkgCache::VerFileIterator Vf = Ver.FileList(); Vf.end() == false; ++Vf)
204 for (pkgCache::PkgFileIterator F = Vf.File(); F.end() == false; ++F)
205 {
206 if (F.Archive() != 0 && strcmp(F.Archive(), "now") == 0)
207 return Ver;
208 }
209 return Ver;
210 }
211 /*}}}*/
212 static pkgCache::VerIterator FindToBeRemovedVersion(pkgCache::PkgIterator const &Pkg)/*{{{*/
213 {
214 auto const PV = Pkg.CurrentVer();
215 if (PV.end() == false)
216 return PV;
217 return FindNowVersion(Pkg);
218 }
219 /*}}}*/
220
221 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
222 // ---------------------------------------------------------------------
223 /* */
224 pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
225 : pkgPackageManager(Cache),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
226 {
227 }
228 /*}}}*/
229 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
230 // ---------------------------------------------------------------------
231 /* */
232 pkgDPkgPM::~pkgDPkgPM()
233 {
234 delete d;
235 }
236 /*}}}*/
237 // DPkgPM::Install - Install a package /*{{{*/
238 // ---------------------------------------------------------------------
239 /* Add an install operation to the sequence list */
240 bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
241 {
242 if (File.empty() == true || Pkg.end() == true)
243 return _error->Error("Internal Error, No file name for %s",Pkg.FullName().c_str());
244
245 // If the filename string begins with DPkg::Chroot-Directory, return the
246 // substr that is within the chroot so dpkg can access it.
247 string const chrootdir = _config->FindDir("DPkg::Chroot-Directory","/");
248 if (chrootdir != "/" && File.find(chrootdir) == 0)
249 {
250 size_t len = chrootdir.length();
251 if (chrootdir.at(len - 1) == '/')
252 len--;
253 List.push_back(Item(Item::Install,Pkg,File.substr(len)));
254 }
255 else
256 List.push_back(Item(Item::Install,Pkg,File));
257
258 return true;
259 }
260 /*}}}*/
261 // DPkgPM::Configure - Configure a package /*{{{*/
262 // ---------------------------------------------------------------------
263 /* Add a configure operation to the sequence list */
264 bool pkgDPkgPM::Configure(PkgIterator Pkg)
265 {
266 if (Pkg.end() == true)
267 return false;
268
269 List.push_back(Item(Item::Configure, Pkg));
270
271 // Use triggers for config calls if we configure "smart"
272 // as otherwise Pre-Depends will not be satisfied, see #526774
273 if (_config->FindB("DPkg::TriggersPending", false) == true)
274 List.push_back(Item(Item::TriggersPending, PkgIterator()));
275
276 return true;
277 }
278 /*}}}*/
279 // DPkgPM::Remove - Remove a package /*{{{*/
280 // ---------------------------------------------------------------------
281 /* Add a remove operation to the sequence list */
282 bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
283 {
284 if (Pkg.end() == true)
285 return false;
286
287 if (Purge == true)
288 List.push_back(Item(Item::Purge,Pkg));
289 else
290 List.push_back(Item(Item::Remove,Pkg));
291 return true;
292 }
293 /*}}}*/
294 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
295 // ---------------------------------------------------------------------
296 /* This is part of the helper script communication interface, it sends
297 very complete information down to the other end of the pipe.*/
298 bool pkgDPkgPM::SendV2Pkgs(FILE *F)
299 {
300 return SendPkgsInfo(F, 2);
301 }
302 bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
303 {
304 // This version of APT supports only v3, so don't sent higher versions
305 if (Version <= 3)
306 fprintf(F,"VERSION %u\n", Version);
307 else
308 fprintf(F,"VERSION 3\n");
309
310 /* Write out all of the configuration directives by walking the
311 configuration tree */
312 const Configuration::Item *Top = _config->Tree(0);
313 for (; Top != 0;)
314 {
315 if (Top->Value.empty() == false)
316 {
317 fprintf(F,"%s=%s\n",
318 QuoteString(Top->FullTag(),"=\"\n").c_str(),
319 QuoteString(Top->Value,"\n").c_str());
320 }
321
322 if (Top->Child != 0)
323 {
324 Top = Top->Child;
325 continue;
326 }
327
328 while (Top != 0 && Top->Next == 0)
329 Top = Top->Parent;
330 if (Top != 0)
331 Top = Top->Next;
332 }
333 fprintf(F,"\n");
334
335 // Write out the package actions in order.
336 for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
337 {
338 if(I->Pkg.end() == true)
339 continue;
340
341 pkgDepCache::StateCache &S = Cache[I->Pkg];
342
343 fprintf(F,"%s ",I->Pkg.Name());
344
345 // Current version which we are going to replace
346 pkgCache::VerIterator CurVer = I->Pkg.CurrentVer();
347 if (CurVer.end() == true && (I->Op == Item::Remove || I->Op == Item::Purge))
348 CurVer = FindNowVersion(I->Pkg);
349
350 if (CurVer.end() == true)
351 {
352 if (Version <= 2)
353 fprintf(F, "- ");
354 else
355 fprintf(F, "- - none ");
356 }
357 else
358 {
359 fprintf(F, "%s ", CurVer.VerStr());
360 if (Version >= 3)
361 fprintf(F, "%s %s ", CurVer.Arch(), CurVer.MultiArchType());
362 }
363
364 // Show the compare operator between current and install version
365 if (S.InstallVer != 0)
366 {
367 pkgCache::VerIterator const InstVer = S.InstVerIter(Cache);
368 int Comp = 2;
369 if (CurVer.end() == false)
370 Comp = InstVer.CompareVer(CurVer);
371 if (Comp < 0)
372 fprintf(F,"> ");
373 else if (Comp == 0)
374 fprintf(F,"= ");
375 else if (Comp > 0)
376 fprintf(F,"< ");
377 fprintf(F, "%s ", InstVer.VerStr());
378 if (Version >= 3)
379 fprintf(F, "%s %s ", InstVer.Arch(), InstVer.MultiArchType());
380 }
381 else
382 {
383 if (Version <= 2)
384 fprintf(F, "> - ");
385 else
386 fprintf(F, "> - - none ");
387 }
388
389 // Show the filename/operation
390 if (I->Op == Item::Install)
391 {
392 // No errors here..
393 if (I->File[0] != '/')
394 fprintf(F,"**ERROR**\n");
395 else
396 fprintf(F,"%s\n",I->File.c_str());
397 }
398 else if (I->Op == Item::Configure)
399 fprintf(F,"**CONFIGURE**\n");
400 else if (I->Op == Item::Remove ||
401 I->Op == Item::Purge)
402 fprintf(F,"**REMOVE**\n");
403
404 if (ferror(F) != 0)
405 return false;
406 }
407 return true;
408 }
409 /*}}}*/
410 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
411 // ---------------------------------------------------------------------
412 /* This looks for a list of scripts to run from the configuration file
413 each one is run and is fed on standard input a list of all .deb files
414 that are due to be installed. */
415 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
416 {
417 bool result = true;
418 static bool interrupted = false;
419
420 Configuration::Item const *Opts = _config->Tree(Cnf);
421 if (Opts == 0 || Opts->Child == 0)
422 return true;
423 Opts = Opts->Child;
424
425 sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN);
426 sighandler_t old_sigint = signal(SIGINT, [](int signum){
427 interrupted = true;
428 });
429
430 unsigned int Count = 1;
431 for (; Opts != 0; Opts = Opts->Next, Count++)
432 {
433 if (Opts->Value.empty() == true)
434 continue;
435
436 if(_config->FindB("Debug::RunScripts", false) == true)
437 std::clog << "Running external script with list of all .deb file: '"
438 << Opts->Value << "'" << std::endl;
439
440 // Determine the protocol version
441 string OptSec = Opts->Value;
442 string::size_type Pos;
443 if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0)
444 Pos = OptSec.length();
445 OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
446
447 unsigned int Version = _config->FindI(OptSec+"::Version",1);
448 unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO);
449
450 // Create the pipes
451 std::set<int> KeepFDs;
452 MergeKeepFdsFromConfiguration(KeepFDs);
453 int Pipes[2];
454 if (pipe(Pipes) != 0) {
455 result = _error->Errno("pipe","Failed to create IPC pipe to subprocess");
456 break;
457 }
458 if (InfoFD != (unsigned)Pipes[0])
459 SetCloseExec(Pipes[0],true);
460 else
461 KeepFDs.insert(Pipes[0]);
462
463
464 SetCloseExec(Pipes[1],true);
465
466 // Purified Fork for running the script
467 pid_t Process = ExecFork(KeepFDs);
468 if (Process == 0)
469 {
470 // Setup the FDs
471 dup2(Pipes[0], InfoFD);
472 SetCloseExec(STDOUT_FILENO,false);
473 SetCloseExec(STDIN_FILENO,false);
474 SetCloseExec(STDERR_FILENO,false);
475
476 string hookfd;
477 strprintf(hookfd, "%d", InfoFD);
478 setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
479
480 debSystem::DpkgChrootDirectory();
481 const char *Args[4];
482 Args[0] = "/bin/sh";
483 Args[1] = "-c";
484 Args[2] = Opts->Value.c_str();
485 Args[3] = 0;
486 execv(Args[0],(char **)Args);
487 _exit(100);
488 }
489 close(Pipes[0]);
490 FILE *F = fdopen(Pipes[1],"w");
491 if (F == 0) {
492 result = _error->Errno("fdopen","Failed to open new FD");
493 break;
494 }
495
496 // Feed it the filenames.
497 if (Version <= 1)
498 {
499 for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
500 {
501 // Only deal with packages to be installed from .deb
502 if (I->Op != Item::Install)
503 continue;
504
505 // No errors here..
506 if (I->File[0] != '/')
507 continue;
508
509 /* Feed the filename of each package that is pending install
510 into the pipe. */
511 fprintf(F,"%s\n",I->File.c_str());
512 if (ferror(F) != 0)
513 break;
514 }
515 }
516 else
517 SendPkgsInfo(F, Version);
518
519 fclose(F);
520
521 // Clean up the sub process
522 if (ExecWait(Process,Opts->Value.c_str()) == false) {
523 result = _error->Error("Failure running script %s",Opts->Value.c_str());
524 break;
525 }
526 }
527 signal(SIGINT, old_sigint);
528 signal(SIGPIPE, old_sigpipe);
529
530 if (interrupted)
531 result = _error->Error("Interrupted");
532
533 return result;
534 }
535 /*}}}*/
536 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
537 // ---------------------------------------------------------------------
538 /*
539 */
540 void pkgDPkgPM::DoStdin(int master)
541 {
542 unsigned char input_buf[256] = {0,};
543 ssize_t len = read(STDIN_FILENO, input_buf, sizeof(input_buf));
544 if (len)
545 FileFd::Write(master, input_buf, len);
546 else
547 d->stdin_is_dev_null = true;
548 }
549 /*}}}*/
550 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
551 // ---------------------------------------------------------------------
552 /*
553 * read the terminal pty and write log
554 */
555 void pkgDPkgPM::DoTerminalPty(int master)
556 {
557 unsigned char term_buf[1024] = {0,0, };
558
559 ssize_t len=read(master, term_buf, sizeof(term_buf));
560 if(len == -1 && errno == EIO)
561 {
562 // this happens when the child is about to exit, we
563 // give it time to actually exit, otherwise we run
564 // into a race so we sleep for half a second.
565 struct timespec sleepfor = { 0, 500000000 };
566 nanosleep(&sleepfor, NULL);
567 return;
568 }
569 if(len <= 0)
570 return;
571 FileFd::Write(1, term_buf, len);
572 if(d->term_out)
573 fwrite(term_buf, len, sizeof(char), d->term_out);
574 }
575 /*}}}*/
576 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
577 void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
578 {
579 bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false);
580 if (Debug == true)
581 std::clog << "got from dpkg '" << line << "'" << std::endl;
582
583 /* dpkg sends strings like this:
584 'status: <pkg>: <pkg qstate>'
585 'status: <pkg>:<arch>: <pkg qstate>'
586
587 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
588 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
589 */
590
591 // we need to split on ": " (note the appended space) as the ':' is
592 // part of the pkgname:arch information that dpkg sends
593 //
594 // A dpkg error message may contain additional ":" (like
595 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
596 // so we need to ensure to not split too much
597 std::vector<std::string> list = StringSplit(line, ": ", 4);
598 if(list.size() < 3)
599 {
600 if (Debug == true)
601 std::clog << "ignoring line: not enough ':'" << std::endl;
602 return;
603 }
604
605 // build the (prefix, pkgname, action) tuple, position of this
606 // is different for "processing" or "status" messages
607 std::string prefix = APT::String::Strip(list[0]);
608 std::string pkgname;
609 std::string action;
610
611 // "processing" has the form "processing: action: pkg or trigger"
612 // with action = ["install", "upgrade", "configure", "remove", "purge",
613 // "disappear", "trigproc"]
614 if (prefix == "processing")
615 {
616 pkgname = APT::String::Strip(list[2]);
617 action = APT::String::Strip(list[1]);
618 // we don't care for the difference (as dpkg doesn't really either)
619 if (action == "upgrade")
620 action = "install";
621 }
622 // "status" has the form: "status: pkg: state"
623 // with state in ["half-installed", "unpacked", "half-configured",
624 // "installed", "config-files", "not-installed"]
625 else if (prefix == "status")
626 {
627 pkgname = APT::String::Strip(list[1]);
628 action = APT::String::Strip(list[2]);
629 } else {
630 if (Debug == true)
631 std::clog << "unknown prefix '" << prefix << "'" << std::endl;
632 return;
633 }
634
635
636 /* handle the special cases first:
637
638 errors look like this:
639 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
640 and conffile-prompt like this
641 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
642 */
643 if (prefix == "status")
644 {
645 if(action == "error")
646 {
647 d->progress->Error(pkgname, PackagesDone, PackagesTotal,
648 list[3]);
649 pkgFailures++;
650 WriteApportReport(pkgname.c_str(), list[3].c_str());
651 return;
652 }
653 else if(action == "conffile-prompt")
654 {
655 d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal,
656 list[3]);
657 return;
658 }
659 }
660
661 // at this point we know that we should have a valid pkgname, so build all
662 // the info from it
663
664 // dpkg does not always send "pkgname:arch" so we add it here if needed
665 if (pkgname.find(":") == std::string::npos)
666 {
667 // find the package in the group that is touched by dpkg
668 // if there are multiple pkgs dpkg would send us a full pkgname:arch
669 pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
670 if (Grp.end() == false)
671 {
672 pkgCache::PkgIterator P = Grp.PackageList();
673 for (; P.end() != true; P = Grp.NextPkg(P))
674 {
675 if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
676 {
677 pkgname = P.FullName();
678 break;
679 }
680 }
681 }
682 }
683
684 const char* const pkg = pkgname.c_str();
685 std::string short_pkgname = StringSplit(pkgname, ":")[0];
686 std::string arch = "";
687 if (pkgname.find(":") != string::npos)
688 arch = StringSplit(pkgname, ":")[1];
689 std::string i18n_pkgname = pkgname;
690 if (arch.size() != 0)
691 strprintf(i18n_pkgname, "%s (%s)", short_pkgname.c_str(), arch.c_str());
692
693 // 'processing' from dpkg looks like
694 // 'processing: action: pkg'
695 if(prefix == "processing")
696 {
697 const std::pair<const char *, const char *> * const iter =
698 std::find_if(PackageProcessingOpsBegin,
699 PackageProcessingOpsEnd,
700 MatchProcessingOp(action.c_str()));
701 if(iter == PackageProcessingOpsEnd)
702 {
703 if (Debug == true)
704 std::clog << "ignoring unknown action: " << action << std::endl;
705 return;
706 }
707 std::string msg;
708 strprintf(msg, _(iter->second), i18n_pkgname.c_str());
709 d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
710
711 // FIXME: this needs a muliarch testcase
712 // FIXME2: is "pkgname" here reliable with dpkg only sending us
713 // short pkgnames?
714 if (action == "disappear")
715 handleDisappearAction(pkgname);
716 return;
717 }
718
719 if (prefix == "status")
720 {
721 std::vector<struct DpkgState> &states = PackageOps[pkg];
722 if (action == "triggers-pending")
723 {
724 if (Debug == true)
725 std::clog << "(parsed from dpkg) pkg: " << short_pkgname
726 << " action: " << action << " (prefix 2 to "
727 << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl;
728
729 states.insert(states.begin(), {"installed", N_("Installed %s")});
730 states.insert(states.begin(), {"half-configured", N_("Configuring %s")});
731 PackagesTotal += 2;
732 }
733 else if(PackageOpsDone[pkg] < states.size())
734 {
735 char const * next_action = states[PackageOpsDone[pkg]].state;
736 if (next_action)
737 {
738 /*
739 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
740 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
741 {
742 if (Debug == true)
743 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
744 << " pending trigger defused by unpack" << std::endl;
745 // unpacking a package defuses the pending trigger
746 PackageOpsDone[pkg] += 2;
747 PackagesDone += 2;
748 next_action = states[PackageOpsDone[pkg]].state;
749 }
750 */
751 if (Debug == true)
752 std::clog << "(parsed from dpkg) pkg: " << short_pkgname
753 << " action: " << action << " (expected: '" << next_action << "' "
754 << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl;
755
756 // check if the package moved to the next dpkg state
757 if(action == next_action)
758 {
759 // only read the translation if there is actually a next action
760 char const * const translation = _(states[PackageOpsDone[pkg]].str);
761
762 // we moved from one dpkg state to a new one, report that
763 ++PackageOpsDone[pkg];
764 ++PackagesDone;
765
766 std::string msg;
767 strprintf(msg, translation, i18n_pkgname.c_str());
768 d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
769 }
770 }
771 }
772 }
773 }
774 /*}}}*/
775 // DPkgPM::handleDisappearAction /*{{{*/
776 void pkgDPkgPM::handleDisappearAction(string const &pkgname)
777 {
778 pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
779 if (unlikely(Pkg.end() == true))
780 return;
781
782 // record the package name for display and stuff later
783 disappearedPkgs.insert(Pkg.FullName(true));
784
785 // the disappeared package was auto-installed - nothing to do
786 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
787 return;
788 pkgCache::VerIterator PkgVer = Cache[Pkg].InstVerIter(Cache);
789 if (unlikely(PkgVer.end() == true))
790 return;
791 /* search in the list of dependencies for (Pre)Depends,
792 check if this dependency has a Replaces on our package
793 and if so transfer the manual installed flag to it */
794 for (pkgCache::DepIterator Dep = PkgVer.DependsList(); Dep.end() != true; ++Dep)
795 {
796 if (Dep->Type != pkgCache::Dep::Depends &&
797 Dep->Type != pkgCache::Dep::PreDepends)
798 continue;
799 pkgCache::PkgIterator Tar = Dep.TargetPkg();
800 if (unlikely(Tar.end() == true))
801 continue;
802 // the package is already marked as manual
803 if ((Cache[Tar].Flags & pkgCache::Flag::Auto) != pkgCache::Flag::Auto)
804 continue;
805 pkgCache::VerIterator TarVer = Cache[Tar].InstVerIter(Cache);
806 if (TarVer.end() == true)
807 continue;
808 for (pkgCache::DepIterator Rep = TarVer.DependsList(); Rep.end() != true; ++Rep)
809 {
810 if (Rep->Type != pkgCache::Dep::Replaces)
811 continue;
812 if (Pkg != Rep.TargetPkg())
813 continue;
814 // okay, they are strongly connected - transfer manual-bit
815 if (Debug == true)
816 std::clog << "transfer manual-bit from disappeared »" << pkgname << "« to »" << Tar.FullName() << "«" << std::endl;
817 Cache[Tar].Flags &= ~Flag::Auto;
818 break;
819 }
820 }
821 }
822 /*}}}*/
823 // DPkgPM::DoDpkgStatusFd /*{{{*/
824 void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
825 {
826 ssize_t const len = read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos],
827 (sizeof(d->dpkgbuf)/sizeof(d->dpkgbuf[0])) - d->dpkgbuf_pos);
828 if(len <= 0)
829 return;
830 d->dpkgbuf_pos += (len / sizeof(d->dpkgbuf[0]));
831
832 // process line by line from the buffer
833 char *p = d->dpkgbuf, *q = nullptr;
834 while((q=(char*)memchr(p, '\n', (d->dpkgbuf + d->dpkgbuf_pos) - p)) != nullptr)
835 {
836 *q = '\0';
837 ProcessDpkgStatusLine(p);
838 p = q + 1; // continue with next line
839 }
840
841 // check if we stripped the buffer clean
842 if (p > (d->dpkgbuf + d->dpkgbuf_pos))
843 {
844 d->dpkgbuf_pos = 0;
845 return;
846 }
847
848 // otherwise move the unprocessed tail to the start and update pos
849 memmove(d->dpkgbuf, p, (p - d->dpkgbuf));
850 d->dpkgbuf_pos = (d->dpkgbuf + d->dpkgbuf_pos) - p;
851 }
852 /*}}}*/
853 // DPkgPM::WriteHistoryTag /*{{{*/
854 void pkgDPkgPM::WriteHistoryTag(string const &tag, string value)
855 {
856 size_t const length = value.length();
857 if (length == 0)
858 return;
859 // poor mans rstrip(", ")
860 if (value[length-2] == ',' && value[length-1] == ' ')
861 value.erase(length - 2, 2);
862 fprintf(d->history_out, "%s: %s\n", tag.c_str(), value.c_str());
863 } /*}}}*/
864 // DPkgPM::OpenLog /*{{{*/
865 bool pkgDPkgPM::OpenLog()
866 {
867 string const logdir = _config->FindDir("Dir::Log");
868 if(CreateAPTDirectoryIfNeeded(logdir, logdir) == false)
869 // FIXME: use a better string after freeze
870 return _error->Error(_("Directory '%s' missing"), logdir.c_str());
871
872 // get current time
873 char timestr[200];
874 time_t const t = time(NULL);
875 struct tm tm_buf;
876 struct tm const * const tmp = localtime_r(&t, &tm_buf);
877 strftime(timestr, sizeof(timestr), "%F %T", tmp);
878
879 // open terminal log
880 string const logfile_name = flCombine(logdir,
881 _config->Find("Dir::Log::Terminal"));
882 if (!logfile_name.empty())
883 {
884 d->term_out = fopen(logfile_name.c_str(),"a");
885 if (d->term_out == NULL)
886 return _error->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name.c_str());
887 setvbuf(d->term_out, NULL, _IONBF, 0);
888 SetCloseExec(fileno(d->term_out), true);
889 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
890 {
891 struct passwd *pw = getpwnam("root");
892 struct group *gr = getgrnam("adm");
893 if (pw != NULL && gr != NULL && chown(logfile_name.c_str(), pw->pw_uid, gr->gr_gid) != 0)
894 _error->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name.c_str());
895 }
896 if (chmod(logfile_name.c_str(), 0640) != 0)
897 _error->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name.c_str());
898 fprintf(d->term_out, "\nLog started: %s\n", timestr);
899 }
900
901 // write your history
902 string const history_name = flCombine(logdir,
903 _config->Find("Dir::Log::History"));
904 if (!history_name.empty())
905 {
906 d->history_out = fopen(history_name.c_str(),"a");
907 if (d->history_out == NULL)
908 return _error->WarningE("OpenLog", _("Could not open file '%s'"), history_name.c_str());
909 SetCloseExec(fileno(d->history_out), true);
910 chmod(history_name.c_str(), 0644);
911 fprintf(d->history_out, "\nStart-Date: %s\n", timestr);
912 string remove, purge, install, reinstall, upgrade, downgrade;
913 for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
914 {
915 enum { CANDIDATE, CANDIDATE_AUTO, CURRENT_CANDIDATE, CURRENT } infostring;
916 string *line = NULL;
917 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
918 if (Cache[I].NewInstall() == true)
919 HISTORYINFO(install, CANDIDATE_AUTO)
920 else if (Cache[I].ReInstall() == true)
921 HISTORYINFO(reinstall, CANDIDATE)
922 else if (Cache[I].Upgrade() == true)
923 HISTORYINFO(upgrade, CURRENT_CANDIDATE)
924 else if (Cache[I].Downgrade() == true)
925 HISTORYINFO(downgrade, CURRENT_CANDIDATE)
926 else if (Cache[I].Delete() == true)
927 HISTORYINFO((Cache[I].Purge() ? purge : remove), CURRENT)
928 else
929 continue;
930 #undef HISTORYINFO
931 line->append(I.FullName(false)).append(" (");
932 switch (infostring) {
933 case CANDIDATE: line->append(Cache[I].CandVersion); break;
934 case CANDIDATE_AUTO:
935 line->append(Cache[I].CandVersion);
936 if ((Cache[I].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
937 line->append(", automatic");
938 break;
939 case CURRENT_CANDIDATE: line->append(Cache[I].CurVersion).append(", ").append(Cache[I].CandVersion); break;
940 case CURRENT: line->append(Cache[I].CurVersion); break;
941 }
942 line->append("), ");
943 }
944 if (_config->Exists("Commandline::AsString") == true)
945 WriteHistoryTag("Commandline", _config->Find("Commandline::AsString"));
946 std::string RequestingUser = AptHistoryRequestingUser();
947 if (RequestingUser != "")
948 WriteHistoryTag("Requested-By", RequestingUser);
949 WriteHistoryTag("Install", install);
950 WriteHistoryTag("Reinstall", reinstall);
951 WriteHistoryTag("Upgrade", upgrade);
952 WriteHistoryTag("Downgrade",downgrade);
953 WriteHistoryTag("Remove",remove);
954 WriteHistoryTag("Purge",purge);
955 fflush(d->history_out);
956 }
957
958 return true;
959 }
960 /*}}}*/
961 // DPkg::CloseLog /*{{{*/
962 bool pkgDPkgPM::CloseLog()
963 {
964 char timestr[200];
965 time_t t = time(NULL);
966 struct tm tm_buf;
967 struct tm *tmp = localtime_r(&t, &tm_buf);
968 strftime(timestr, sizeof(timestr), "%F %T", tmp);
969
970 if(d->term_out)
971 {
972 fprintf(d->term_out, "Log ended: ");
973 fprintf(d->term_out, "%s", timestr);
974 fprintf(d->term_out, "\n");
975 fclose(d->term_out);
976 }
977 d->term_out = NULL;
978
979 if(d->history_out)
980 {
981 if (disappearedPkgs.empty() == false)
982 {
983 string disappear;
984 for (std::set<std::string>::const_iterator d = disappearedPkgs.begin();
985 d != disappearedPkgs.end(); ++d)
986 {
987 pkgCache::PkgIterator P = Cache.FindPkg(*d);
988 disappear.append(*d);
989 if (P.end() == true)
990 disappear.append(", ");
991 else
992 disappear.append(" (").append(Cache[P].CurVersion).append("), ");
993 }
994 WriteHistoryTag("Disappeared", disappear);
995 }
996 if (d->dpkg_error.empty() == false)
997 fprintf(d->history_out, "Error: %s\n", d->dpkg_error.c_str());
998 fprintf(d->history_out, "End-Date: %s\n", timestr);
999 fclose(d->history_out);
1000 }
1001 d->history_out = NULL;
1002
1003 return true;
1004 }
1005 /*}}}*/
1006
1007 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1008 void pkgDPkgPM::BuildPackagesProgressMap()
1009 {
1010 // map the dpkg states to the operations that are performed
1011 // (this is sorted in the same way as Item::Ops)
1012 static const std::array<std::array<DpkgState, 3>, 4> DpkgStatesOpMap = {{
1013 // Install operation
1014 {{
1015 {"half-installed", N_("Preparing %s")},
1016 {"unpacked", N_("Unpacking %s") },
1017 {nullptr, nullptr}
1018 }},
1019 // Configure operation
1020 {{
1021 {"unpacked",N_("Preparing to configure %s") },
1022 {"half-configured", N_("Configuring %s") },
1023 { "installed", N_("Installed %s")},
1024 }},
1025 // Remove operation
1026 {{
1027 {"half-configured", N_("Preparing for removal of %s")},
1028 {"half-installed", N_("Removing %s")},
1029 {"config-files", N_("Removed %s")},
1030 }},
1031 // Purge operation
1032 {{
1033 {"config-files", N_("Preparing to completely remove %s")},
1034 {"not-installed", N_("Completely removed %s")},
1035 {nullptr, nullptr}
1036 }},
1037 }};
1038 static_assert(Item::Purge == 3, "Enum item has unexpected index for mapping array");
1039
1040 // init the PackageOps map, go over the list of packages that
1041 // that will be [installed|configured|removed|purged] and add
1042 // them to the PackageOps map (the dpkg states it goes through)
1043 // and the PackageOpsTranslations (human readable strings)
1044 for (auto &&I : List)
1045 {
1046 if(I.Pkg.end() == true)
1047 continue;
1048
1049 string const name = I.Pkg.FullName();
1050 PackageOpsDone[name] = 0;
1051 auto AddToPackageOps = std::back_inserter(PackageOps[name]);
1052 if (I.Op == Item::Purge && I.Pkg->CurrentVer != 0)
1053 {
1054 // purging a package which is installed first passes through remove states
1055 auto const DpkgOps = DpkgStatesOpMap[Item::Remove];
1056 std::copy(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps);
1057 PackagesTotal += DpkgOps.size();
1058 }
1059 auto const DpkgOps = DpkgStatesOpMap[I.Op];
1060 std::copy_if(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps, [&](DpkgState const &state) {
1061 if (state.state == nullptr)
1062 return false;
1063 ++PackagesTotal;
1064 return true;
1065 });
1066 }
1067 /* one extra: We don't want the progress bar to reach 100%, especially not
1068 if we call dpkg --configure --pending and process a bunch of triggers
1069 while showing 100%. Also, spindown takes a while, so never reaching 100%
1070 is way more correct than reaching 100% while still doing stuff even if
1071 doing it this way is slightly bending the rules */
1072 ++PackagesTotal;
1073 }
1074 /*}}}*/
1075 bool pkgDPkgPM::Go(int StatusFd) /*{{{*/
1076 {
1077 APT::Progress::PackageManager *progress = NULL;
1078 if (StatusFd == -1)
1079 progress = APT::Progress::PackageManagerProgressFactory();
1080 else
1081 progress = new APT::Progress::PackageManagerProgressFd(StatusFd);
1082
1083 return Go(progress);
1084 }
1085 /*}}}*/
1086 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1087 {
1088 if (_config->FindB("Dpkg::Use-Pty", true) == false)
1089 {
1090 d->master = -1;
1091 if (d->slave != NULL)
1092 free(d->slave);
1093 d->slave = NULL;
1094 return;
1095 }
1096
1097 if (isatty(STDIN_FILENO) == 0)
1098 d->direct_stdin = true;
1099
1100 _error->PushToStack();
1101
1102 d->master = posix_openpt(O_RDWR | O_NOCTTY);
1103 if (d->master == -1)
1104 _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1105 else if (unlockpt(d->master) == -1)
1106 _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master);
1107 else
1108 {
1109 #ifdef HAVE_PTSNAME_R
1110 char slave_name[64]; // 64 is used by bionic
1111 if (ptsname_r(d->master, slave_name, sizeof(slave_name)) != 0)
1112 #else
1113 char const * const slave_name = ptsname(d->master);
1114 if (slave_name == NULL)
1115 #endif
1116 _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master);
1117 else
1118 {
1119 d->slave = strdup(slave_name);
1120 if (d->slave == NULL)
1121 _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master);
1122 else if (grantpt(d->master) == -1)
1123 _error->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name, d->master);
1124 else if (tcgetattr(STDIN_FILENO, &d->tt) == 0)
1125 {
1126 d->tt_is_valid = true;
1127 struct termios raw_tt;
1128 // copy window size of stdout if its a 'good' terminal
1129 if (tcgetattr(STDOUT_FILENO, &raw_tt) == 0)
1130 {
1131 struct winsize win;
1132 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0)
1133 _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1134 if (ioctl(d->master, TIOCSWINSZ, &win) < 0)
1135 _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master);
1136 }
1137 if (tcsetattr(d->master, TCSANOW, &d->tt) == -1)
1138 _error->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d->master);
1139
1140 raw_tt = d->tt;
1141 cfmakeraw(&raw_tt);
1142 raw_tt.c_lflag &= ~ECHO;
1143 raw_tt.c_lflag |= ISIG;
1144 // block SIGTTOU during tcsetattr to prevent a hang if
1145 // the process is a member of the background process group
1146 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1147 sigemptyset(&d->sigmask);
1148 sigaddset(&d->sigmask, SIGTTOU);
1149 sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask);
1150 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1)
1151 _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1152 sigprocmask(SIG_SETMASK, &d->original_sigmask, NULL);
1153
1154 }
1155 if (d->slave != NULL)
1156 {
1157 /* on linux, closing (and later reopening) all references to the slave
1158 makes the slave a death end, so we open it here to have one open all
1159 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1160 on kfreebsd we get an incorrect ("step like") output then while it has
1161 no problem with closing all references… so to avoid platform specific
1162 code here we combine both and be happy once more */
1163 d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY);
1164 }
1165 }
1166 }
1167
1168 if (_error->PendingError() == true)
1169 {
1170 if (d->master != -1)
1171 {
1172 close(d->master);
1173 d->master = -1;
1174 }
1175 if (d->slave != NULL)
1176 {
1177 free(d->slave);
1178 d->slave = NULL;
1179 }
1180 _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
1181 }
1182 _error->RevertToStack();
1183 }
1184 /*}}}*/
1185 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1186 {
1187 if(d->master == -1 || d->slave == NULL)
1188 return;
1189
1190 if (close(d->master) == -1)
1191 _error->FatalE("close", "Closing master %d in child failed!", d->master);
1192 d->master = -1;
1193 if (setsid() == -1)
1194 _error->FatalE("setsid", "Starting a new session for child failed!");
1195
1196 int const slaveFd = open(d->slave, O_RDWR | O_NOCTTY);
1197 if (slaveFd == -1)
1198 _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1199 else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0)
1200 _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd);
1201 else
1202 {
1203 unsigned short i = 0;
1204 if (d->direct_stdin == true)
1205 ++i;
1206 for (; i < 3; ++i)
1207 if (dup2(slaveFd, i) == -1)
1208 _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i);
1209
1210 if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSANOW, &d->tt) < 0)
1211 _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd);
1212 }
1213
1214 if (slaveFd != -1)
1215 close(slaveFd);
1216 }
1217 /*}}}*/
1218 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1219 {
1220 if (d->slave != NULL)
1221 free(d->slave);
1222 d->slave = NULL;
1223 if (d->protect_slave_from_dying != -1)
1224 {
1225 close(d->protect_slave_from_dying);
1226 d->protect_slave_from_dying = -1;
1227 }
1228 if(d->master >= 0)
1229 {
1230 if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSAFLUSH, &d->tt) == -1)
1231 _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1232 close(d->master);
1233 d->master = -1;
1234 }
1235 }
1236 /*}}}*/
1237 static void cleanUpTmpDir(char * const tmpdir) /*{{{*/
1238 {
1239 if (tmpdir == nullptr)
1240 return;
1241 DIR * const D = opendir(tmpdir);
1242 if (D == nullptr)
1243 _error->Errno("opendir", _("Unable to read %s"), tmpdir);
1244 else
1245 {
1246 auto const dfd = dirfd(D);
1247 for (struct dirent *Ent = readdir(D); Ent != nullptr; Ent = readdir(D))
1248 {
1249 if (Ent->d_name[0] == '.')
1250 continue;
1251 #ifdef _DIRENT_HAVE_D_TYPE
1252 if (unlikely(Ent->d_type != DT_LNK && Ent->d_type != DT_UNKNOWN))
1253 continue;
1254 #endif
1255 if (unlikely(unlinkat(dfd, Ent->d_name, 0) != 0))
1256 break;
1257 }
1258 closedir(D);
1259 rmdir(tmpdir);
1260 }
1261 free(tmpdir);
1262 }
1263 /*}}}*/
1264
1265 // DPkgPM::Go - Run the sequence /*{{{*/
1266 // ---------------------------------------------------------------------
1267 /* This globs the operations and calls dpkg
1268 *
1269 * If it is called with a progress object apt will report the install
1270 * progress to this object. It maps the dpkg states a package goes
1271 * through to human readable (and i10n-able)
1272 * names and calculates a percentage for each step.
1273 */
1274 bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
1275 {
1276 auto const ItemIsEssential = [](pkgDPkgPM::Item const &I) {
1277 static auto const cachegen = _config->Find("pkgCacheGen::Essential");
1278 if (cachegen == "none" || cachegen == "native")
1279 return true;
1280 if (unlikely(I.Pkg.end()))
1281 return true;
1282 return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0;
1283 };
1284
1285 pkgPackageManager::SigINTStop = false;
1286 d->progress = progress;
1287
1288 // Generate the base argument list for dpkg
1289 std::vector<std::string> const sArgs = debSystem::GetDpkgBaseCommand();
1290 std::vector<const char *> Args(sArgs.size(), NULL);
1291 std::transform(sArgs.begin(), sArgs.end(), Args.begin(),
1292 [](std::string const &s) { return s.c_str(); });
1293 unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0llu,
1294 [](unsigned long long const i, std::string const &s) { return i + s.length(); });
1295 size_t const BaseArgs = Args.size();
1296
1297 fd_set rfds;
1298 struct timespec tv;
1299
1300 // try to figure out the max environment size
1301 int OSArgMax = sysconf(_SC_ARG_MAX);
1302 if(OSArgMax < 0)
1303 OSArgMax = 32*1024;
1304 OSArgMax -= EnvironmentSize() - 2*1024;
1305 unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax);
1306 bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false);
1307
1308 if (RunScripts("DPkg::Pre-Invoke") == false)
1309 return false;
1310
1311 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1312 return false;
1313
1314 auto const noopDPkgInvocation = _config->FindB("Debug::pkgDPkgPM",false);
1315 // store auto-bits as they are supposed to be after dpkg is run
1316 if (noopDPkgInvocation == false)
1317 Cache.writeStateFile(NULL);
1318
1319 bool dpkg_recursive_install = _config->FindB("dpkg::install::recursive", false);
1320 if (_config->FindB("dpkg::install::recursive::force", false) == false)
1321 {
1322 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1323 auto const dpkgpkg = Cache.FindPkg("dpkg");
1324 if (likely(dpkgpkg.end() == false && dpkgpkg->CurrentVer != 0))
1325 dpkg_recursive_install = Cache.VS().CmpVersion("1.18.5", dpkgpkg.CurrentVer().VerStr()) <= 0;
1326 }
1327 // no point in doing this dance for a handful of packages only
1328 unsigned int const dpkg_recursive_install_min = _config->FindB("dpkg::install::recursive::minimum", 5);
1329 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1330 bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true);
1331
1332 decltype(List)::const_iterator::difference_type const notconfidx =
1333 _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits<decltype(notconfidx)>::max() :
1334 std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base());
1335
1336 // support subpressing of triggers processing for special
1337 // cases like d-i that runs the triggers handling manually
1338 bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
1339 bool const ConfigurePending = _config->FindB("DPkg::ConfigurePending", true);
1340 if (ConfigurePending)
1341 List.push_back(Item(Item::ConfigurePending, PkgIterator()));
1342
1343 // for the progress
1344 BuildPackagesProgressMap();
1345
1346 if (notconfidx != std::numeric_limits<decltype(notconfidx)>::max())
1347 {
1348 if (ConfigurePending)
1349 List.erase(std::next(List.begin(), notconfidx), std::prev(List.end()));
1350 else
1351 List.erase(std::next(List.begin(), notconfidx), List.end());
1352 }
1353
1354 APT::StateChanges currentStates;
1355 if (_config->FindB("dpkg::selection::current::saveandrestore", true))
1356 {
1357 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1358 if (Pkg->CurrentVer == 0)
1359 continue;
1360 else if (Pkg->SelectedState == pkgCache::State::Purge)
1361 currentStates.Purge(FindToBeRemovedVersion(Pkg));
1362 else if (Pkg->SelectedState == pkgCache::State::DeInstall)
1363 currentStates.Remove(FindToBeRemovedVersion(Pkg));
1364 if (currentStates.empty() == false)
1365 {
1366 APT::StateChanges cleanStates;
1367 for (auto && P: currentStates.Remove())
1368 cleanStates.Install(P);
1369 for (auto && P: currentStates.Purge())
1370 cleanStates.Install(P);
1371 if (cleanStates.Save(false) == false)
1372 return _error->Error("Couldn't clean the currently selected dpkg states");
1373 }
1374 }
1375 APT::StateChanges approvedStates;
1376 if (_config->FindB("dpkg::selection::remove::approved", true))
1377 {
1378 for (auto && I: List)
1379 if (I.Op == Item::Remove && Cache[I.Pkg].Delete())
1380 approvedStates.Remove(FindToBeRemovedVersion(I.Pkg));
1381 else if (I.Op == Item::Purge && Cache[I.Pkg].Purge())
1382 approvedStates.Purge(FindToBeRemovedVersion(I.Pkg));
1383 if (approvedStates.Save(false) == false)
1384 {
1385 _error->Error("Couldn't record the approved state changes as dpkg selection states");
1386 if (currentStates.Save(false) == false)
1387 _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1388 return false;
1389 }
1390
1391 {
1392 std::vector<bool> toBeRemoved(Cache.Head().PackageCount, false);
1393 std::vector<bool> toBePurged(Cache.Head().PackageCount, false);
1394 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1395 if (Cache[Pkg].Purge())
1396 toBePurged[Pkg->ID] = true;
1397 else if (Cache[Pkg].Delete())
1398 toBeRemoved[Pkg->ID] = true;
1399 for (auto && I: approvedStates.Remove())
1400 toBeRemoved[I.ParentPkg()->ID] = false;
1401 for (auto && I: approvedStates.Purge())
1402 toBePurged[I.ParentPkg()->ID] = false;
1403 if (std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end())
1404 {
1405 if (ConfigurePending)
1406 List.emplace(std::prev(List.end()), Item::RemovePending, pkgCache::PkgIterator());
1407 else
1408 List.emplace_back(Item::RemovePending, pkgCache::PkgIterator());
1409 }
1410 if (std::find(toBePurged.begin(), toBePurged.end(), true) != toBePurged.end())
1411 {
1412 if (ConfigurePending)
1413 List.emplace(std::prev(List.end()), Item::PurgePending, pkgCache::PkgIterator());
1414 else
1415 List.emplace_back(Item::PurgePending, pkgCache::PkgIterator());
1416 }
1417 }
1418 }
1419
1420 d->stdin_is_dev_null = false;
1421
1422 // create log
1423 OpenLog();
1424
1425 bool dpkgMultiArch = debSystem::SupportsMultiArch();
1426
1427 // start pty magic before the loop
1428 StartPtyMagic();
1429
1430 // Tell the progress that its starting and fork dpkg
1431 d->progress->Start(d->master);
1432
1433 // this loop is runs once per dpkg operation
1434 vector<Item>::const_iterator I = List.begin();
1435 while (I != List.end())
1436 {
1437 // Do all actions with the same Op in one run
1438 vector<Item>::const_iterator J = I;
1439 if (TriggersPending == true)
1440 for (; J != List.end(); ++J)
1441 {
1442 if (J->Op == I->Op)
1443 continue;
1444 if (J->Op != Item::TriggersPending)
1445 break;
1446 vector<Item>::const_iterator T = J + 1;
1447 if (T != List.end() && T->Op == I->Op)
1448 continue;
1449 break;
1450 }
1451 else
1452 for (; J != List.end() && J->Op == I->Op; ++J)
1453 /* nothing */;
1454
1455 auto const size = (J - I) + 10;
1456
1457 // start with the baseset of arguments
1458 auto Size = StartSize;
1459 Args.erase(Args.begin() + BaseArgs, Args.end());
1460 Args.reserve(size);
1461 // keep track of allocated strings for multiarch package names
1462 std::vector<char *> Packages(size, nullptr);
1463
1464 int fd[2];
1465 if (pipe(fd) != 0)
1466 return _error->Errno("pipe","Failed to create IPC pipe to dpkg");
1467
1468 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1469 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1470
1471 ADDARGC("--status-fd");
1472 char status_fd_buf[20];
1473 snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]);
1474 ADDARG(status_fd_buf);
1475 unsigned long const Op = I->Op;
1476
1477 switch (I->Op)
1478 {
1479 case Item::Remove:
1480 ADDARGC("--force-depends");
1481 if (std::any_of(I, J, ItemIsEssential))
1482 ADDARGC("--force-remove-essential");
1483 ADDARGC("--remove");
1484 break;
1485
1486 case Item::Purge:
1487 ADDARGC("--force-depends");
1488 if (std::any_of(I, J, ItemIsEssential))
1489 ADDARGC("--force-remove-essential");
1490 ADDARGC("--purge");
1491 break;
1492
1493 case Item::Configure:
1494 ADDARGC("--configure");
1495 break;
1496
1497 case Item::ConfigurePending:
1498 ADDARGC("--configure");
1499 ADDARGC("--pending");
1500 break;
1501
1502 case Item::TriggersPending:
1503 ADDARGC("--triggers-only");
1504 ADDARGC("--pending");
1505 break;
1506
1507 case Item::RemovePending:
1508 ADDARGC("--remove");
1509 ADDARGC("--pending");
1510 break;
1511
1512 case Item::PurgePending:
1513 ADDARGC("--purge");
1514 ADDARGC("--pending");
1515 break;
1516
1517 case Item::Install:
1518 ADDARGC("--unpack");
1519 ADDARGC("--auto-deconfigure");
1520 break;
1521 }
1522
1523 if (NoTriggers == true && I->Op != Item::TriggersPending &&
1524 I->Op != Item::ConfigurePending)
1525 {
1526 ADDARGC("--no-triggers");
1527 }
1528 char * tmpdir_to_free = nullptr;
1529
1530 // Write in the file or package names
1531 if (I->Op == Item::Install)
1532 {
1533 auto const installsToDo = J - I;
1534 if (dpkg_recursive_install == true && dpkg_recursive_install_min < installsToDo)
1535 {
1536 std::string tmpdir;
1537 strprintf(tmpdir, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
1538 tmpdir_to_free = strndup(tmpdir.data(), tmpdir.length());
1539 if (mkdtemp(tmpdir_to_free) == nullptr)
1540 return _error->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free);
1541
1542 char p = 1;
1543 for (auto c = installsToDo - 1; (c = c/10) != 0; ++p);
1544 for (unsigned long n = 0; I != J; ++n, ++I)
1545 {
1546 if (I->File[0] != '/')
1547 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
1548 auto const file = flNotDir(I->File);
1549 std::string linkpath;
1550 if (dpkg_recursive_install_numbered)
1551 strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str());
1552 else
1553 strprintf(linkpath, "%s/%s", tmpdir_to_free, file.c_str());
1554 if (symlink(I->File.c_str(), linkpath.c_str()) != 0)
1555 return _error->Errno("DPkg::Go", "Symlinking %s to %s failed!", I->File.c_str(), linkpath.c_str());
1556 }
1557 ADDARGC("--recursive");
1558 ADDARG(tmpdir_to_free);
1559 }
1560 else
1561 {
1562 for (;I != J && Size < MaxArgBytes; ++I)
1563 {
1564 if (I->File[0] != '/')
1565 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
1566 Args.push_back(I->File.c_str());
1567 Size += I->File.length();
1568 }
1569 }
1570 }
1571 else
1572 {
1573 string const nativeArch = _config->Find("APT::Architecture");
1574 unsigned long const oldSize = I->Op == Item::Configure ? Size : 0;
1575 for (;I != J && Size < MaxArgBytes; ++I)
1576 {
1577 if((*I).Pkg.end() == true)
1578 continue;
1579 if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.FullName(true)) != disappearedPkgs.end())
1580 continue;
1581 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1582 if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch ||
1583 strcmp(I->Pkg.Arch(), "all") == 0 ||
1584 strcmp(I->Pkg.Arch(), "none") == 0))
1585 {
1586 char const * const name = I->Pkg.Name();
1587 ADDARG(name);
1588 }
1589 else
1590 {
1591 pkgCache::VerIterator PkgVer;
1592 std::string name = I->Pkg.Name();
1593 if (Op == Item::Remove || Op == Item::Purge)
1594 PkgVer = FindToBeRemovedVersion(I->Pkg);
1595 else
1596 PkgVer = Cache[I->Pkg].InstVerIter(Cache);
1597 if (strcmp(I->Pkg.Arch(), "none") == 0)
1598 ; // never arch-qualify a package without an arch
1599 else if (PkgVer.end() == false)
1600 name.append(":").append(PkgVer.Arch());
1601 else
1602 _error->Warning("Can not find PkgVer for '%s'", name.c_str());
1603 char * const fullname = strdup(name.c_str());
1604 Packages.push_back(fullname);
1605 ADDARG(fullname);
1606 }
1607 }
1608 // skip configure action if all sheduled packages disappeared
1609 if (oldSize == Size)
1610 continue;
1611 }
1612 #undef ADDARGC
1613 #undef ADDARG
1614
1615 J = I;
1616
1617 if (noopDPkgInvocation == true)
1618 {
1619 for (std::vector<const char *>::const_iterator a = Args.begin();
1620 a != Args.end(); ++a)
1621 clog << *a << ' ';
1622 clog << endl;
1623 for (std::vector<char *>::const_iterator p = Packages.begin();
1624 p != Packages.end(); ++p)
1625 free(*p);
1626 Packages.clear();
1627 close(fd[0]);
1628 close(fd[1]);
1629 cleanUpTmpDir(tmpdir_to_free);
1630 continue;
1631 }
1632 Args.push_back(NULL);
1633
1634 cout << flush;
1635 clog << flush;
1636 cerr << flush;
1637
1638 /* Mask off sig int/quit. We do this because dpkg also does when
1639 it forks scripts. What happens is that when you hit ctrl-c it sends
1640 it to all processes in the group. Since dpkg ignores the signal
1641 it doesn't die but we do! So we must also ignore it */
1642 sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN);
1643 sighandler_t old_SIGINT = signal(SIGINT,SigINT);
1644
1645 // Check here for any SIGINT
1646 if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install))
1647 break;
1648
1649 // ignore SIGHUP as well (debian #463030)
1650 sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN);
1651
1652 // now run dpkg
1653 d->progress->StartDpkg();
1654 std::set<int> KeepFDs;
1655 KeepFDs.insert(fd[1]);
1656 MergeKeepFdsFromConfiguration(KeepFDs);
1657 pid_t Child = ExecFork(KeepFDs);
1658 if (Child == 0)
1659 {
1660 // This is the child
1661 SetupSlavePtyMagic();
1662 close(fd[0]); // close the read end of the pipe
1663
1664 debSystem::DpkgChrootDirectory();
1665
1666 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1667 _exit(100);
1668
1669 if (_config->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO))
1670 {
1671 int Flags;
1672 int dummy = 0;
1673 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
1674 _exit(100);
1675
1676 // Discard everything in stdin before forking dpkg
1677 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
1678 _exit(100);
1679
1680 while (read(STDIN_FILENO,&dummy,1) == 1);
1681
1682 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
1683 _exit(100);
1684 }
1685
1686 // if color support isn't enabled/disabled explicitly tell
1687 // dpkg to use the same state apt is using for its color support
1688 if (_config->FindB("APT::Color", false) == true)
1689 setenv("DPKG_COLORS", "always", 0);
1690 else
1691 setenv("DPKG_COLORS", "never", 0);
1692
1693 execvp(Args[0], (char**) &Args[0]);
1694 cerr << "Could not exec dpkg!" << endl;
1695 _exit(100);
1696 }
1697
1698 // we read from dpkg here
1699 int const _dpkgin = fd[0];
1700 close(fd[1]); // close the write end of the pipe
1701
1702 // apply ionice
1703 if (_config->FindB("DPkg::UseIoNice", false) == true)
1704 ionice(Child);
1705
1706 // setups fds
1707 sigemptyset(&d->sigmask);
1708 sigprocmask(SIG_BLOCK,&d->sigmask,&d->original_sigmask);
1709
1710 /* free vectors (and therefore memory) as we don't need the included data anymore */
1711 for (std::vector<char *>::const_iterator p = Packages.begin();
1712 p != Packages.end(); ++p)
1713 free(*p);
1714 Packages.clear();
1715
1716 // the result of the waitpid call
1717 int Status = 0;
1718 int res;
1719 bool waitpid_failure = false;
1720 while ((res=waitpid(Child,&Status, WNOHANG)) != Child) {
1721 if(res < 0) {
1722 // error handling, waitpid returned -1
1723 if (errno == EINTR)
1724 continue;
1725 waitpid_failure = true;
1726 break;
1727 }
1728
1729 // wait for input or output here
1730 FD_ZERO(&rfds);
1731 if (d->master >= 0 && d->direct_stdin == false && d->stdin_is_dev_null == false)
1732 FD_SET(STDIN_FILENO, &rfds);
1733 FD_SET(_dpkgin, &rfds);
1734 if(d->master >= 0)
1735 FD_SET(d->master, &rfds);
1736 tv.tv_sec = 0;
1737 tv.tv_nsec = d->progress->GetPulseInterval();
1738 auto const select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL,
1739 &tv, &d->original_sigmask);
1740 d->progress->Pulse();
1741 if (select_ret == 0)
1742 continue;
1743 else if (select_ret < 0 && errno == EINTR)
1744 continue;
1745 else if (select_ret < 0)
1746 {
1747 perror("select() returned error");
1748 continue;
1749 }
1750
1751 if(d->master >= 0 && FD_ISSET(d->master, &rfds))
1752 DoTerminalPty(d->master);
1753 if(d->master >= 0 && FD_ISSET(0, &rfds))
1754 DoStdin(d->master);
1755 if(FD_ISSET(_dpkgin, &rfds))
1756 DoDpkgStatusFd(_dpkgin);
1757 }
1758 close(_dpkgin);
1759
1760 // Restore sig int/quit
1761 signal(SIGQUIT,old_SIGQUIT);
1762 signal(SIGINT,old_SIGINT);
1763 signal(SIGHUP,old_SIGHUP);
1764
1765 cleanUpTmpDir(tmpdir_to_free);
1766
1767 if (waitpid_failure == true)
1768 {
1769 strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
1770 _error->Error("%s", d->dpkg_error.c_str());
1771 break;
1772 }
1773
1774 // Check for an error code.
1775 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
1776 {
1777 // if it was set to "keep-dpkg-running" then we won't return
1778 // here but keep the loop going and just report it as a error
1779 // for later
1780 bool const stopOnError = _config->FindB("Dpkg::StopOnError",true);
1781
1782 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
1783 strprintf(d->dpkg_error, "Sub-process %s received a segmentation fault.",Args[0]);
1784 else if (WIFEXITED(Status) != 0)
1785 strprintf(d->dpkg_error, "Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status));
1786 else
1787 strprintf(d->dpkg_error, "Sub-process %s exited unexpectedly",Args[0]);
1788 _error->Error("%s", d->dpkg_error.c_str());
1789
1790 if(stopOnError)
1791 break;
1792 }
1793 }
1794 // dpkg is done at this point
1795 StopPtyMagic();
1796 CloseLog();
1797
1798 if (d->dpkg_error.empty() == false)
1799 {
1800 APT::StateChanges undo;
1801 auto && undoRem = approvedStates.Remove();
1802 std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Remove()));
1803 auto && undoPur = approvedStates.Purge();
1804 std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Purge()));
1805 approvedStates.clear();
1806 if (undo.Save(false) == false)
1807 _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
1808 }
1809 if (currentStates.Save(false) == false)
1810 _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1811
1812 if (pkgPackageManager::SigINTStop)
1813 _error->Warning(_("Operation was interrupted before it could finish"));
1814
1815 if (noopDPkgInvocation == false)
1816 {
1817 std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache");
1818 if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true &&
1819 RemoveFile("pkgDPkgPM::Go", oldpkgcache))
1820 {
1821 std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
1822 if (srcpkgcache.empty() == false && RealFileExists(srcpkgcache) == true)
1823 {
1824 _error->PushToStack();
1825 pkgCacheFile CacheFile;
1826 CacheFile.BuildCaches(NULL, true);
1827 _error->RevertToStack();
1828 }
1829 }
1830 }
1831
1832 // disappearing packages can forward their auto-bit
1833 if (disappearedPkgs.empty() == false)
1834 Cache.writeStateFile(NULL);
1835
1836 d->progress->Stop();
1837
1838 if (RunScripts("DPkg::Post-Invoke") == false)
1839 return false;
1840
1841 return d->dpkg_error.empty();
1842 }
1843
1844 void SigINT(int /*sig*/) {
1845 pkgPackageManager::SigINTStop = true;
1846 }
1847 /*}}}*/
1848 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1849 // ---------------------------------------------------------------------
1850 /* */
1851 void pkgDPkgPM::Reset()
1852 {
1853 List.erase(List.begin(),List.end());
1854 }
1855 /*}}}*/
1856 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1857 // ---------------------------------------------------------------------
1858 /* */
1859 void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
1860 {
1861 // If apport doesn't exist or isn't installed do nothing
1862 // This e.g. prevents messages in 'universes' without apport
1863 pkgCache::PkgIterator apportPkg = Cache.FindPkg("apport");
1864 if (apportPkg.end() == true || apportPkg->CurrentVer == 0)
1865 return;
1866
1867 string pkgname, reportfile, pkgver, arch;
1868 string::size_type pos;
1869 FILE *report;
1870
1871 if (_config->FindB("Dpkg::ApportFailureReport", true) == false)
1872 {
1873 std::clog << "configured to not write apport reports" << std::endl;
1874 return;
1875 }
1876
1877 // only report the first errors
1878 if(pkgFailures > _config->FindI("APT::Apport::MaxReports", 3))
1879 {
1880 std::clog << _("No apport report written because MaxReports is reached already") << std::endl;
1881 return;
1882 }
1883
1884 // check if its not a follow up error
1885 const char *needle = dgettext("dpkg", "dependency problems - leaving unconfigured");
1886 if(strstr(errormsg, needle) != NULL) {
1887 std::clog << _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl;
1888 return;
1889 }
1890
1891 // do not report disk-full failures
1892 if(strstr(errormsg, strerror(ENOSPC)) != NULL) {
1893 std::clog << _("No apport report written because the error message indicates a disk full error") << std::endl;
1894 return;
1895 }
1896
1897 // do not report out-of-memory failures
1898 if(strstr(errormsg, strerror(ENOMEM)) != NULL ||
1899 strstr(errormsg, "failed to allocate memory") != NULL) {
1900 std::clog << _("No apport report written because the error message indicates a out of memory error") << std::endl;
1901 return;
1902 }
1903
1904 // do not report bugs regarding inaccessible local files
1905 if(strstr(errormsg, strerror(ENOENT)) != NULL ||
1906 strstr(errormsg, "cannot access archive") != NULL) {
1907 std::clog << _("No apport report written because the error message indicates an issue on the local system") << std::endl;
1908 return;
1909 }
1910
1911 // do not report errors encountered when decompressing packages
1912 if(strstr(errormsg, "--fsys-tarfile returned error exit status 2") != NULL) {
1913 std::clog << _("No apport report written because the error message indicates an issue on the local system") << std::endl;
1914 return;
1915 }
1916
1917 // do not report dpkg I/O errors, this is a format string, so we compare
1918 // the prefix and the suffix of the error with the dpkg error message
1919 vector<string> io_errors;
1920 io_errors.push_back(string("failed to read"));
1921 io_errors.push_back(string("failed to write"));
1922 io_errors.push_back(string("failed to seek"));
1923 io_errors.push_back(string("unexpected end of file or stream"));
1924
1925 for (vector<string>::iterator I = io_errors.begin(); I != io_errors.end(); ++I)
1926 {
1927 vector<string> list = VectorizeString(dgettext("dpkg", (*I).c_str()), '%');
1928 if (list.size() > 1) {
1929 // we need to split %s, VectorizeString only allows char so we need
1930 // to kill the "s" manually
1931 if (list[1].size() > 1) {
1932 list[1].erase(0, 1);
1933 if(strstr(errormsg, list[0].c_str()) &&
1934 strstr(errormsg, list[1].c_str())) {
1935 std::clog << _("No apport report written because the error message indicates a dpkg I/O error") << std::endl;
1936 return;
1937 }
1938 }
1939 }
1940 }
1941
1942 // get the pkgname and reportfile
1943 pkgname = flNotDir(pkgpath);
1944 pos = pkgname.find('_');
1945 if(pos != string::npos)
1946 pkgname = pkgname.substr(0, pos);
1947
1948 // find the package version and source package name
1949 pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
1950 if (Pkg.end() == true)
1951 {
1952 if (pos == std::string::npos || _config->FindB("dpkg::install::recursive::numbered", true) == false)
1953 return;
1954 auto const dash = pkgname.find_first_not_of("0123456789");
1955 if (dash == std::string::npos || pkgname[dash] != '-')
1956 return;
1957 pkgname.erase(0, dash + 1);
1958 Pkg = Cache.FindPkg(pkgname);
1959 if (Pkg.end() == true)
1960 return;
1961 }
1962 pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
1963 if (Ver.end() == true)
1964 return;
1965 pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr();
1966
1967 // if the file exists already, we check:
1968 // - if it was reported already (touched by apport).
1969 // If not, we do nothing, otherwise
1970 // we overwrite it. This is the same behaviour as apport
1971 // - if we have a report with the same pkgversion already
1972 // then we skip it
1973 _config->CndSet("Dir::Apport", "var/crash");
1974 reportfile = flCombine(_config->FindDir("Dir::Apport", "var/crash"), pkgname+".0.crash");
1975 if(FileExists(reportfile))
1976 {
1977 struct stat buf;
1978 char strbuf[255];
1979
1980 // check atime/mtime
1981 stat(reportfile.c_str(), &buf);
1982 if(buf.st_mtime > buf.st_atime)
1983 return;
1984
1985 // check if the existing report is the same version
1986 report = fopen(reportfile.c_str(),"r");
1987 while(fgets(strbuf, sizeof(strbuf), report) != NULL)
1988 {
1989 if(strstr(strbuf,"Package:") == strbuf)
1990 {
1991 char pkgname[255], version[255];
1992 if(sscanf(strbuf, "Package: %254s %254s", pkgname, version) == 2)
1993 if(strcmp(pkgver.c_str(), version) == 0)
1994 {
1995 fclose(report);
1996 return;
1997 }
1998 }
1999 }
2000 fclose(report);
2001 }
2002
2003 // now write the report
2004 arch = _config->Find("APT::Architecture");
2005 report = fopen(reportfile.c_str(),"w");
2006 if(report == NULL)
2007 return;
2008 if(_config->FindB("DPkgPM::InitialReportOnly",false) == true)
2009 chmod(reportfile.c_str(), 0);
2010 else
2011 chmod(reportfile.c_str(), 0600);
2012 fprintf(report, "ProblemType: Package\n");
2013 fprintf(report, "Architecture: %s\n", arch.c_str());
2014 time_t now = time(NULL);
2015 char ctime_buf[26]; // need at least 26 bytes according to ctime(3)
2016 fprintf(report, "Date: %s" , ctime_r(&now, ctime_buf));
2017 fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str());
2018 fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName());
2019 fprintf(report, "ErrorMessage:\n %s\n", errormsg);
2020
2021 // ensure that the log is flushed
2022 if(d->term_out)
2023 fflush(d->term_out);
2024
2025 // attach terminal log it if we have it
2026 string logfile_name = _config->FindFile("Dir::Log::Terminal");
2027 if (!logfile_name.empty())
2028 {
2029 FILE *log = NULL;
2030
2031 fprintf(report, "DpkgTerminalLog:\n");
2032 log = fopen(logfile_name.c_str(),"r");
2033 if(log != NULL)
2034 {
2035 char buf[1024];
2036 while( fgets(buf, sizeof(buf), log) != NULL)
2037 fprintf(report, " %s", buf);
2038 fprintf(report, " \n");
2039 fclose(log);
2040 }
2041 }
2042
2043 // attach history log it if we have it
2044 string histfile_name = _config->FindFile("Dir::Log::History");
2045 if (!histfile_name.empty())
2046 {
2047 fprintf(report, "DpkgHistoryLog:\n");
2048 FILE* log = fopen(histfile_name.c_str(),"r");
2049 if(log != NULL)
2050 {
2051 char buf[1024];
2052 while( fgets(buf, sizeof(buf), log) != NULL)
2053 fprintf(report, " %s", buf);
2054 fclose(log);
2055 }
2056 }
2057
2058 // log the ordering, see dpkgpm.h and the "Ops" enum there
2059 fprintf(report, "AptOrdering:\n");
2060 for (auto && I : List)
2061 {
2062 char const * opstr = nullptr;
2063 switch (I.Op)
2064 {
2065 case Item::Install: opstr = "Install"; break;
2066 case Item::Configure: opstr = "Configure"; break;
2067 case Item::Remove: opstr = "Remove"; break;
2068 case Item::Purge: opstr = "Purge"; break;
2069 case Item::ConfigurePending: opstr = "ConfigurePending"; break;
2070 case Item::TriggersPending: opstr = "TriggersPending"; break;
2071 case Item::RemovePending: opstr = "RemovePending"; break;
2072 case Item::PurgePending: opstr = "PurgePending"; break;
2073 }
2074 auto const pkgname = I.Pkg.end() ? "NULL" : I.Pkg.FullName();
2075 fprintf(report, " %s: %s\n", pkgname.c_str(), opstr);
2076 }
2077
2078 // attach dmesg log (to learn about segfaults)
2079 if (FileExists("/bin/dmesg"))
2080 {
2081 fprintf(report, "Dmesg:\n");
2082 FILE *log = popen("/bin/dmesg","r");
2083 if(log != NULL)
2084 {
2085 char buf[1024];
2086 while( fgets(buf, sizeof(buf), log) != NULL)
2087 fprintf(report, " %s", buf);
2088 pclose(log);
2089 }
2090 }
2091
2092 // attach df -l log (to learn about filesystem status)
2093 if (FileExists("/bin/df"))
2094 {
2095
2096 fprintf(report, "Df:\n");
2097 FILE *log = popen("/bin/df -l","r");
2098 if(log != NULL)
2099 {
2100 char buf[1024];
2101 while( fgets(buf, sizeof(buf), log) != NULL)
2102 fprintf(report, " %s", buf);
2103 pclose(log);
2104 }
2105 }
2106
2107 fclose(report);
2108
2109 }
2110 /*}}}*/