apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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