1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
5 DPKG Package Manager - Provide an interface to dpkg
7 ##################################################################### */
12 #include <apt-pkg/cachefile.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/depcache.h>
15 #include <apt-pkg/dpkgpm.h>
16 #include <apt-pkg/debsystem.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/statechanges.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
26 #include <apt-pkg/version.h>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
41 #include <sys/types.h>
54 #include <type_traits>
56 #include <unordered_set>
66 APT_PURE
static string
AptHistoryRequestingUser() /*{{{*/
68 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
70 for (const auto &Key
: EnvKeys
)
72 if (getenv(Key
) != nullptr)
74 int uid
= atoi(getenv(Key
));
77 struct passwd
*result
;
79 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
81 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
90 APT_PURE
static unsigned int EnvironmentSize() /*{{{*/
92 unsigned int size
= 0;
93 char **envp
= environ
;
96 size
+= strlen (*envp
++) + 1;
101 class pkgDPkgPMPrivate
/*{{{*/
104 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
105 term_out(NULL
), history_out(NULL
),
106 progress(NULL
), tt_is_valid(false), master(-1),
107 slave(NULL
), protect_slave_from_dying(-1),
115 bool stdin_is_dev_null
;
116 // the buffer we use for the dpkg status-fd reading
122 APT::Progress::PackageManager
*progress
;
129 int protect_slave_from_dying
;
133 sigset_t original_sigmask
;
140 // Maps the dpkg "processing" info to human readable names. Entry 0
141 // of each array is the key, entry 1 is the value.
142 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
143 std::make_pair("install", N_("Installing %s")),
144 std::make_pair("configure", N_("Configuring %s")),
145 std::make_pair("remove", N_("Removing %s")),
146 std::make_pair("purge", N_("Completely removing %s")),
147 std::make_pair("disappear", N_("Noting disappearance of %s")),
148 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
151 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
152 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
154 // Predicate to test whether an entry in the PackageProcessingOps
155 // array matches a string.
156 class MatchProcessingOp
161 explicit MatchProcessingOp(const char *the_target
)
166 bool operator()(const std::pair
<const char *, const char *> &pair
) const
168 return strcmp(pair
.first
, target
) == 0;
173 // ionice - helper function to ionice the given PID /*{{{*/
174 /* there is no C header for ionice yet - just the syscall interface
175 so we use the binary from util-linux */
176 static bool ionice(int PID
)
178 if (!FileExists("/usr/bin/ionice"))
180 pid_t Process
= ExecFork();
184 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
186 Args
[0] = "/usr/bin/ionice";
190 execv(Args
[0], (char **)Args
);
192 return ExecWait(Process
, "ionice");
195 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
196 // ---------------------------------------------------------------------
197 /* This is helpful when a package is no longer installed but has residual
201 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
203 pkgCache::VerIterator Ver
;
204 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
205 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
206 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
208 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
214 static pkgCache::VerIterator
FindToBeRemovedVersion(pkgCache::PkgIterator
const &Pkg
)/*{{{*/
216 auto const PV
= Pkg
.CurrentVer();
217 if (PV
.end() == false)
219 return FindNowVersion(Pkg
);
223 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
224 // ---------------------------------------------------------------------
226 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
227 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
231 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
232 // ---------------------------------------------------------------------
234 pkgDPkgPM::~pkgDPkgPM()
239 // DPkgPM::Install - Install a package /*{{{*/
240 // ---------------------------------------------------------------------
241 /* Add an install operation to the sequence list */
242 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
244 if (File
.empty() == true || Pkg
.end() == true)
245 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
247 // If the filename string begins with DPkg::Chroot-Directory, return the
248 // substr that is within the chroot so dpkg can access it.
249 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
250 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
252 size_t len
= chrootdir
.length();
253 if (chrootdir
.at(len
- 1) == '/')
255 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
258 List
.push_back(Item(Item::Install
,Pkg
,File
));
263 // DPkgPM::Configure - Configure a package /*{{{*/
264 // ---------------------------------------------------------------------
265 /* Add a configure operation to the sequence list */
266 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
268 if (Pkg
.end() == true)
271 List
.push_back(Item(Item::Configure
, Pkg
));
273 // Use triggers for config calls if we configure "smart"
274 // as otherwise Pre-Depends will not be satisfied, see #526774
275 if (_config
->FindB("DPkg::TriggersPending", false) == true)
276 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
281 // DPkgPM::Remove - Remove a package /*{{{*/
282 // ---------------------------------------------------------------------
283 /* Add a remove operation to the sequence list */
284 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
286 if (Pkg
.end() == true)
290 List
.push_back(Item(Item::Purge
,Pkg
));
292 List
.push_back(Item(Item::Remove
,Pkg
));
296 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This is part of the helper script communication interface, it sends
299 very complete information down to the other end of the pipe.*/
300 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
302 return SendPkgsInfo(F
, 2);
304 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
306 // This version of APT supports only v3, so don't sent higher versions
308 fprintf(F
,"VERSION %u\n", Version
);
310 fprintf(F
,"VERSION 3\n");
312 /* Write out all of the configuration directives by walking the
313 configuration tree */
314 const Configuration::Item
*Top
= _config
->Tree(0);
317 if (Top
->Value
.empty() == false)
320 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
321 QuoteString(Top
->Value
,"\n").c_str());
330 while (Top
!= 0 && Top
->Next
== 0)
337 // Write out the package actions in order.
338 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
340 if(I
->Pkg
.end() == true)
343 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
345 fprintf(F
,"%s ",I
->Pkg
.Name());
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
);
352 if (CurVer
.end() == true)
357 fprintf(F
, "- - none ");
361 fprintf(F
, "%s ", CurVer
.VerStr());
363 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
366 // Show the compare operator between current and install version
367 if (S
.InstallVer
!= 0)
369 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
371 if (CurVer
.end() == false)
372 Comp
= InstVer
.CompareVer(CurVer
);
379 fprintf(F
, "%s ", InstVer
.VerStr());
381 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
388 fprintf(F
, "> - - none ");
391 // Show the filename/operation
392 if (I
->Op
== Item::Install
)
395 if (I
->File
[0] != '/')
396 fprintf(F
,"**ERROR**\n");
398 fprintf(F
,"%s\n",I
->File
.c_str());
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");
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
)
421 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
422 if (Opts
== 0 || Opts
->Child
== 0)
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
);
430 unsigned int Count
= 1;
431 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
433 if (Opts
->Value
.empty() == true)
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
;
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
);
447 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
448 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
451 std::set
<int> KeepFDs
;
452 MergeKeepFdsFromConfiguration(KeepFDs
);
454 if (pipe(Pipes
) != 0) {
455 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
458 if (InfoFD
!= (unsigned)Pipes
[0])
459 SetCloseExec(Pipes
[0],true);
461 KeepFDs
.insert(Pipes
[0]);
464 SetCloseExec(Pipes
[1],true);
466 // Purified Fork for running the script
467 pid_t Process
= ExecFork(KeepFDs
);
471 dup2(Pipes
[0], InfoFD
);
472 SetCloseExec(STDOUT_FILENO
,false);
473 SetCloseExec(STDIN_FILENO
,false);
474 SetCloseExec(STDERR_FILENO
,false);
477 strprintf(hookfd
, "%d", InfoFD
);
478 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
480 debSystem::DpkgChrootDirectory();
484 Args
[2] = Opts
->Value
.c_str();
486 execv(Args
[0],(char **)Args
);
490 FILE *F
= fdopen(Pipes
[1],"w");
492 result
= _error
->Errno("fdopen","Failed to open new FD");
496 // Feed it the filenames.
499 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
501 // Only deal with packages to be installed from .deb
502 if (I
->Op
!= Item::Install
)
506 if (I
->File
[0] != '/')
509 /* Feed the filename of each package that is pending install
511 fprintf(F
,"%s\n",I
->File
.c_str());
517 SendPkgsInfo(F
, Version
);
521 // Clean up the sub process
522 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
523 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
527 signal(SIGINT
, old_sigint
);
528 signal(SIGPIPE
, old_sigpipe
);
529 signal(SIGQUIT
, old_sigquit
);
534 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
535 // ---------------------------------------------------------------------
538 void pkgDPkgPM::DoStdin(int master
)
540 unsigned char input_buf
[256] = {0,};
541 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
543 FileFd::Write(master
, input_buf
, len
);
545 d
->stdin_is_dev_null
= true;
548 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
549 // ---------------------------------------------------------------------
551 * read the terminal pty and write log
553 void pkgDPkgPM::DoTerminalPty(int master
)
555 unsigned char term_buf
[1024] = {0,0, };
557 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
558 if(len
== -1 && errno
== EIO
)
560 // this happens when the child is about to exit, we
561 // give it time to actually exit, otherwise we run
562 // into a race so we sleep for half a second.
563 struct timespec sleepfor
= { 0, 500000000 };
564 nanosleep(&sleepfor
, NULL
);
569 FileFd::Write(1, term_buf
, len
);
571 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
574 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
575 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
577 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
579 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
581 /* dpkg sends strings like this:
582 'status: <pkg>: <pkg qstate>'
583 'status: <pkg>:<arch>: <pkg qstate>'
585 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
586 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
589 // we need to split on ": " (note the appended space) as the ':' is
590 // part of the pkgname:arch information that dpkg sends
592 // A dpkg error message may contain additional ":" (like
593 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
594 // so we need to ensure to not split too much
595 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
599 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
603 // build the (prefix, pkgname, action) tuple, position of this
604 // is different for "processing" or "status" messages
605 std::string prefix
= APT::String::Strip(list
[0]);
609 // "processing" has the form "processing: action: pkg or trigger"
610 // with action = ["install", "upgrade", "configure", "remove", "purge",
611 // "disappear", "trigproc"]
612 if (prefix
== "processing")
614 pkgname
= APT::String::Strip(list
[2]);
615 action
= APT::String::Strip(list
[1]);
616 // we don't care for the difference (as dpkg doesn't really either)
617 if (action
== "upgrade")
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")
625 pkgname
= APT::String::Strip(list
[1]);
626 action
= APT::String::Strip(list
[2]);
629 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
634 /* handle the special cases first:
636 errors look like this:
637 '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
638 and conffile-prompt like this
639 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
641 if (prefix
== "status")
643 if(action
== "error")
645 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
648 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
651 else if(action
== "conffile-prompt")
653 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
659 // at this point we know that we should have a valid pkgname, so build all
662 // dpkg does not always send "pkgname:arch" so we add it here if needed
663 if (pkgname
.find(":") == std::string::npos
)
665 // find the package in the group that is touched by dpkg
666 // if there are multiple pkgs dpkg would send us a full pkgname:arch
667 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
668 if (Grp
.end() == false)
669 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
670 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
672 auto fullname
= P
.FullName();
673 if (Cache
[P
].Delete() && PackageOps
[fullname
].size() <= PackageOpsDone
[fullname
])
675 pkgname
= std::move(fullname
);
680 std::string arch
= "";
681 if (pkgname
.find(":") != string::npos
)
682 arch
= StringSplit(pkgname
, ":")[1];
683 std::string i18n_pkgname
= pkgname
;
684 if (arch
.size() != 0)
685 strprintf(i18n_pkgname
, "%s (%s)", StringSplit(pkgname
, ":")[0].c_str(), arch
.c_str());
687 // 'processing' from dpkg looks like
688 // 'processing: action: pkg'
689 if(prefix
== "processing")
691 const std::pair
<const char *, const char *> * const iter
=
692 std::find_if(PackageProcessingOpsBegin
,
693 PackageProcessingOpsEnd
,
694 MatchProcessingOp(action
.c_str()));
695 if(iter
== PackageProcessingOpsEnd
)
698 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
702 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
703 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
705 // FIXME: this needs a muliarch testcase
706 // FIXME2: is "pkgname" here reliable with dpkg only sending us
708 if (action
== "disappear")
709 handleDisappearAction(pkgname
);
713 if (prefix
== "status")
715 std::vector
<struct DpkgState
> &states
= PackageOps
[pkgname
];
716 if (action
== "triggers-pending")
719 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
720 << " action: " << action
<< " (prefix 2 to "
721 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
723 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
724 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
727 else if(PackageOpsDone
[pkgname
] < states
.size())
729 char const * next_action
= states
[PackageOpsDone
[pkgname
]].state
;
733 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
734 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
737 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
738 << " pending trigger defused by unpack" << std::endl;
739 // unpacking a package defuses the pending trigger
740 PackageOpsDone[pkg] += 2;
742 next_action = states[PackageOpsDone[pkg]].state;
746 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
747 << " action: " << action
<< " (expected: '" << next_action
<< "' "
748 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
750 // check if the package moved to the next dpkg state
751 if(action
== next_action
)
753 // only read the translation if there is actually a next action
754 char const * const translation
= _(states
[PackageOpsDone
[pkgname
]].str
);
756 // we moved from one dpkg state to a new one, report that
757 ++PackageOpsDone
[pkgname
];
761 strprintf(msg
, translation
, i18n_pkgname
.c_str());
762 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
764 else if (action
== "unpacked" && strcmp(next_action
, "config-files") == 0)
766 // in a crossgrade what looked like a remove first is really an unpack over it
767 ++PackageOpsDone
[pkgname
];
770 auto const Pkg
= Cache
.FindPkg(pkgname
);
771 if (likely(Pkg
.end() == false))
773 auto const Grp
= Pkg
.Group();
774 if (likely(Grp
.end() == false))
776 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
777 if(Cache
[P
].Install())
779 auto && Ops
= PackageOps
[P
.FullName()];
780 auto const unpackOp
= std::find_if(Ops
.cbegin(), Ops
.cend(), [](DpkgState
const &s
) { return strcmp(s
.state
, "unpacked") == 0; });
781 if (unpackOp
!= Ops
.cend())
783 auto const skipped
= std::distance(Ops
.cbegin(), unpackOp
);
784 PackagesDone
+= skipped
;
785 PackageOpsDone
[P
.FullName()] += skipped
;
797 // DPkgPM::handleDisappearAction /*{{{*/
798 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
800 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
801 if (unlikely(Pkg
.end() == true))
804 // record the package name for display and stuff later
805 disappearedPkgs
.insert(Pkg
.FullName(true));
807 // the disappeared package was auto-installed - nothing to do
808 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
810 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
811 if (unlikely(PkgVer
.end() == true))
813 /* search in the list of dependencies for (Pre)Depends,
814 check if this dependency has a Replaces on our package
815 and if so transfer the manual installed flag to it */
816 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
818 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
819 Dep
->Type
!= pkgCache::Dep::PreDepends
)
821 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
822 if (unlikely(Tar
.end() == true))
824 // the package is already marked as manual
825 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
827 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
828 if (TarVer
.end() == true)
830 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
832 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
834 if (Pkg
!= Rep
.TargetPkg())
836 // okay, they are strongly connected - transfer manual-bit
838 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
839 Cache
[Tar
].Flags
&= ~Flag::Auto
;
845 // DPkgPM::DoDpkgStatusFd /*{{{*/
846 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
848 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
849 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
852 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
854 // process line by line from the buffer
855 char *p
= d
->dpkgbuf
, *q
= nullptr;
856 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
859 ProcessDpkgStatusLine(p
);
860 p
= q
+ 1; // continue with next line
863 // check if we stripped the buffer clean
864 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
870 // otherwise move the unprocessed tail to the start and update pos
871 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
872 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
875 // DPkgPM::WriteHistoryTag /*{{{*/
876 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
878 size_t const length
= value
.length();
881 // poor mans rstrip(", ")
882 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
883 value
.erase(length
- 2, 2);
884 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
886 // DPkgPM::OpenLog /*{{{*/
887 bool pkgDPkgPM::OpenLog()
889 string
const logdir
= _config
->FindDir("Dir::Log");
890 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
891 // FIXME: use a better string after freeze
892 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
896 time_t const t
= time(NULL
);
898 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
899 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
902 string
const logfile_name
= flCombine(logdir
,
903 _config
->Find("Dir::Log::Terminal"));
904 if (!logfile_name
.empty())
906 d
->term_out
= fopen(logfile_name
.c_str(),"a");
907 if (d
->term_out
== NULL
)
908 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
909 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
910 SetCloseExec(fileno(d
->term_out
), true);
911 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
913 struct passwd
*pw
= getpwnam("root");
914 struct group
*gr
= getgrnam("adm");
915 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
916 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
918 if (chmod(logfile_name
.c_str(), 0640) != 0)
919 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
920 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
923 // write your history
924 string
const history_name
= flCombine(logdir
,
925 _config
->Find("Dir::Log::History"));
926 if (!history_name
.empty())
928 d
->history_out
= fopen(history_name
.c_str(),"a");
929 if (d
->history_out
== NULL
)
930 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
931 SetCloseExec(fileno(d
->history_out
), true);
932 chmod(history_name
.c_str(), 0644);
933 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
934 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
935 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
937 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
939 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
940 if (Cache
[I
].NewInstall() == true)
941 HISTORYINFO(install
, CANDIDATE_AUTO
)
942 else if (Cache
[I
].ReInstall() == true)
943 HISTORYINFO(reinstall
, CANDIDATE
)
944 else if (Cache
[I
].Upgrade() == true)
945 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
946 else if (Cache
[I
].Downgrade() == true)
947 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
948 else if (Cache
[I
].Delete() == true)
949 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
953 line
->append(I
.FullName(false)).append(" (");
954 switch (infostring
) {
955 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
957 line
->append(Cache
[I
].CandVersion
);
958 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
959 line
->append(", automatic");
961 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
962 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
966 if (_config
->Exists("Commandline::AsString") == true)
967 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
968 std::string RequestingUser
= AptHistoryRequestingUser();
969 if (RequestingUser
!= "")
970 WriteHistoryTag("Requested-By", RequestingUser
);
971 WriteHistoryTag("Install", install
);
972 WriteHistoryTag("Reinstall", reinstall
);
973 WriteHistoryTag("Upgrade", upgrade
);
974 WriteHistoryTag("Downgrade",downgrade
);
975 WriteHistoryTag("Remove",remove
);
976 WriteHistoryTag("Purge",purge
);
977 fflush(d
->history_out
);
983 // DPkg::CloseLog /*{{{*/
984 bool pkgDPkgPM::CloseLog()
987 time_t t
= time(NULL
);
989 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
990 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
994 fprintf(d
->term_out
, "Log ended: ");
995 fprintf(d
->term_out
, "%s", timestr
);
996 fprintf(d
->term_out
, "\n");
1003 if (disappearedPkgs
.empty() == false)
1006 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
1007 d
!= disappearedPkgs
.end(); ++d
)
1009 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
1010 disappear
.append(*d
);
1011 if (P
.end() == true)
1012 disappear
.append(", ");
1014 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
1016 WriteHistoryTag("Disappeared", disappear
);
1018 if (d
->dpkg_error
.empty() == false)
1019 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
1020 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
1021 fclose(d
->history_out
);
1023 d
->history_out
= NULL
;
1029 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1030 void pkgDPkgPM::BuildPackagesProgressMap()
1032 // map the dpkg states to the operations that are performed
1033 // (this is sorted in the same way as Item::Ops)
1034 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1035 // Install operation
1037 {"half-installed", N_("Preparing %s")},
1038 {"unpacked", N_("Unpacking %s") },
1041 // Configure operation
1043 {"unpacked",N_("Preparing to configure %s") },
1044 {"half-configured", N_("Configuring %s") },
1045 { "installed", N_("Installed %s")},
1049 {"half-configured", N_("Preparing for removal of %s")},
1050 {"half-installed", N_("Removing %s")},
1051 {"config-files", N_("Removed %s")},
1055 {"config-files", N_("Preparing to completely remove %s")},
1056 {"not-installed", N_("Completely removed %s")},
1060 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1062 // init the PackageOps map, go over the list of packages that
1063 // that will be [installed|configured|removed|purged] and add
1064 // them to the PackageOps map (the dpkg states it goes through)
1065 // and the PackageOpsTranslations (human readable strings)
1066 for (auto &&I
: List
)
1068 if(I
.Pkg
.end() == true)
1071 string
const name
= I
.Pkg
.FullName();
1072 PackageOpsDone
[name
] = 0;
1073 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1074 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1076 // purging a package which is installed first passes through remove states
1077 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1078 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1079 PackagesTotal
+= DpkgOps
.size();
1081 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1082 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1083 if (state
.state
== nullptr)
1089 /* one extra: We don't want the progress bar to reach 100%, especially not
1090 if we call dpkg --configure --pending and process a bunch of triggers
1091 while showing 100%. Also, spindown takes a while, so never reaching 100%
1092 is way more correct than reaching 100% while still doing stuff even if
1093 doing it this way is slightly bending the rules */
1097 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1099 APT::Progress::PackageManager
*progress
= NULL
;
1101 progress
= APT::Progress::PackageManagerProgressFactory();
1103 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1105 return Go(progress
);
1108 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1110 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1113 if (d
->slave
!= NULL
)
1119 if (isatty(STDIN_FILENO
) == 0)
1120 d
->direct_stdin
= true;
1122 _error
->PushToStack();
1124 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1125 if (d
->master
== -1)
1126 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1127 else if (unlockpt(d
->master
) == -1)
1128 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1131 #ifdef HAVE_PTSNAME_R
1132 char slave_name
[64]; // 64 is used by bionic
1133 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1135 char const * const slave_name
= ptsname(d
->master
);
1136 if (slave_name
== NULL
)
1138 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1141 d
->slave
= strdup(slave_name
);
1142 if (d
->slave
== NULL
)
1143 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1144 else if (grantpt(d
->master
) == -1)
1145 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1146 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1148 d
->tt_is_valid
= true;
1149 struct termios raw_tt
;
1150 // copy window size of stdout if its a 'good' terminal
1151 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1154 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1155 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1156 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1157 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1159 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1160 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1164 raw_tt
.c_lflag
&= ~ECHO
;
1165 raw_tt
.c_lflag
|= ISIG
;
1166 // block SIGTTOU during tcsetattr to prevent a hang if
1167 // the process is a member of the background process group
1168 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1169 sigemptyset(&d
->sigmask
);
1170 sigaddset(&d
->sigmask
, SIGTTOU
);
1171 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1172 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1173 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1174 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1177 if (d
->slave
!= NULL
)
1179 /* on linux, closing (and later reopening) all references to the slave
1180 makes the slave a death end, so we open it here to have one open all
1181 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1182 on kfreebsd we get an incorrect ("step like") output then while it has
1183 no problem with closing all references… so to avoid platform specific
1184 code here we combine both and be happy once more */
1185 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1190 if (_error
->PendingError() == true)
1192 if (d
->master
!= -1)
1197 if (d
->slave
!= NULL
)
1202 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1204 _error
->RevertToStack();
1207 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1209 if(d
->master
== -1 || d
->slave
== NULL
)
1212 if (close(d
->master
) == -1)
1213 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1216 _error
->FatalE("setsid", "Starting a new session for child failed!");
1218 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1220 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1221 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1222 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1225 unsigned short i
= 0;
1226 if (d
->direct_stdin
== true)
1229 if (dup2(slaveFd
, i
) == -1)
1230 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1232 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1233 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1240 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1242 if (d
->slave
!= NULL
)
1245 if (d
->protect_slave_from_dying
!= -1)
1247 close(d
->protect_slave_from_dying
);
1248 d
->protect_slave_from_dying
= -1;
1252 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1253 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1259 static void cleanUpTmpDir(char * const tmpdir
) /*{{{*/
1261 if (tmpdir
== nullptr)
1263 DIR * const D
= opendir(tmpdir
);
1265 _error
->Errno("opendir", _("Unable to read %s"), tmpdir
);
1268 auto const dfd
= dirfd(D
);
1269 for (struct dirent
*Ent
= readdir(D
); Ent
!= nullptr; Ent
= readdir(D
))
1271 if (Ent
->d_name
[0] == '.')
1273 #ifdef _DIRENT_HAVE_D_TYPE
1274 if (unlikely(Ent
->d_type
!= DT_LNK
&& Ent
->d_type
!= DT_UNKNOWN
))
1277 if (unlikely(unlinkat(dfd
, Ent
->d_name
, 0) != 0))
1287 // DPkgPM::Go - Run the sequence /*{{{*/
1288 // ---------------------------------------------------------------------
1289 /* This globs the operations and calls dpkg
1291 * If it is called with a progress object apt will report the install
1292 * progress to this object. It maps the dpkg states a package goes
1293 * through to human readable (and i10n-able)
1294 * names and calculates a percentage for each step.
1296 static bool ItemIsEssential(pkgDPkgPM::Item
const &I
)
1298 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1299 if (cachegen
== "none" || cachegen
== "native")
1301 if (unlikely(I
.Pkg
.end()))
1303 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1305 bool pkgDPkgPM::ExpandPendingCalls(std::vector
<Item
> &List
, pkgDepCache
&Cache
)
1308 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyRemoved
;
1309 for (auto && I
: List
)
1310 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1311 alreadyRemoved
.insert(I
.Pkg
->ID
);
1312 std::remove_reference
<decltype(List
)>::type AppendList
;
1313 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1314 if (Cache
[Pkg
].Delete() && alreadyRemoved
.insert(Pkg
->ID
).second
== true)
1315 AppendList
.emplace_back(Cache
[Pkg
].Purge() ? Item::Purge
: Item::Remove
, Pkg
);
1316 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1319 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyConfigured
;
1320 for (auto && I
: List
)
1321 if (I
.Op
== Item::Configure
)
1322 alreadyConfigured
.insert(I
.Pkg
->ID
);
1323 std::remove_reference
<decltype(List
)>::type AppendList
;
1324 for (auto && I
: List
)
1325 if (I
.Op
== Item::Install
&& alreadyConfigured
.insert(I
.Pkg
->ID
).second
== true)
1326 AppendList
.emplace_back(Item::Configure
, I
.Pkg
);
1327 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1328 if (Pkg
.State() == pkgCache::PkgIterator::NeedsConfigure
&& alreadyConfigured
.insert(Pkg
->ID
).second
== true)
1329 AppendList
.emplace_back(Item::Configure
, Pkg
);
1330 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1334 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1336 // explicitely remove&configure everything for hookscripts and progress building
1337 // we need them only temporarily through, so keep the length and erase afterwards
1338 decltype(List
)::const_iterator::difference_type explicitIdx
=
1339 std::distance(List
.cbegin(), List
.cend());
1340 ExpandPendingCalls(List
, Cache
);
1342 /* if dpkg told us that it has already done everything to the package we wanted it to do,
1343 we shouldn't ask it for "more" later. That can e.g. happen if packages without conffiles
1344 are purged as they will have pass through the purge states on remove already */
1345 auto const StripAlreadyDoneFrom
= [&](APT::VersionVector
& Pending
) {
1346 Pending
.erase(std::remove_if(Pending
.begin(), Pending
.end(), [&](pkgCache::VerIterator
const &Ver
) {
1347 auto const PN
= Ver
.ParentPkg().FullName();
1348 auto const POD
= PackageOpsDone
.find(PN
);
1349 if (POD
== PackageOpsDone
.end())
1351 return PackageOps
[PN
].size() <= POD
->second
;
1355 pkgPackageManager::SigINTStop
= false;
1356 d
->progress
= progress
;
1358 // Generate the base argument list for dpkg
1359 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1360 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1361 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1362 [](std::string
const &s
) { return s
.c_str(); });
1363 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1364 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1365 size_t const BaseArgs
= Args
.size();
1370 // try to figure out the max environment size
1371 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1374 OSArgMax
-= EnvironmentSize() - 2*1024;
1375 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1376 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", true);
1378 if (RunScripts("DPkg::Pre-Invoke") == false)
1381 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1384 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1385 // store auto-bits as they are supposed to be after dpkg is run
1386 if (noopDPkgInvocation
== false)
1387 Cache
.writeStateFile(NULL
);
1389 bool dpkg_recursive_install
= _config
->FindB("dpkg::install::recursive", false);
1390 if (_config
->FindB("dpkg::install::recursive::force", false) == false)
1392 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1393 auto const dpkgpkg
= Cache
.FindPkg("dpkg");
1394 if (likely(dpkgpkg
.end() == false && dpkgpkg
->CurrentVer
!= 0))
1395 dpkg_recursive_install
= Cache
.VS().CmpVersion("1.18.5", dpkgpkg
.CurrentVer().VerStr()) <= 0;
1397 // no point in doing this dance for a handful of packages only
1398 unsigned int const dpkg_recursive_install_min
= _config
->FindB("dpkg::install::recursive::minimum", 5);
1399 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1400 bool const dpkg_recursive_install_numbered
= _config
->FindB("dpkg::install::recursive::numbered", true);
1403 BuildPackagesProgressMap();
1405 APT::StateChanges approvedStates
;
1406 if (_config
->FindB("dpkg::selection::remove::approved", true))
1408 for (auto && I
: List
)
1409 if (I
.Op
== Item::Purge
)
1410 approvedStates
.Purge(FindToBeRemovedVersion(I
.Pkg
));
1411 else if (I
.Op
== Item::Remove
)
1412 approvedStates
.Remove(FindToBeRemovedVersion(I
.Pkg
));
1415 // Skip removes if we install another architecture of this package soon (crossgrade)
1416 // We can't just skip them all the time as it could be an ordering requirement [of another package]
1417 if ((approvedStates
.Remove().empty() == false || approvedStates
.Purge().empty() == false) &&
1418 _config
->FindB("dpkg::remove::crossgrade::implicit", true) == true)
1420 std::unordered_set
<decltype(pkgCache::Package::ID
)> crossgraded
;
1421 std::vector
<std::pair
<Item
*, std::string
>> toCrossgrade
;
1422 auto const PlanedEnd
= std::next(List
.begin(), explicitIdx
);
1423 for (auto I
= List
.begin(); I
!= PlanedEnd
; ++I
)
1425 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1428 auto const Grp
= I
->Pkg
.Group();
1429 size_t installedInstances
= 0;
1430 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1431 if (Pkg
->CurrentVer
!= 0 || Cache
[Pkg
].Install())
1432 ++installedInstances
;
1433 if (installedInstances
== 2)
1435 auto const FirstInstall
= std::find_if_not(I
, List
.end(),
1436 [](Item
const &i
) { return i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
; });
1437 auto const LastInstall
= std::find_if_not(FirstInstall
, List
.end(),
1438 [](Item
const &i
) { return i
.Op
== Item::Install
; });
1439 auto const crosser
= std::find_if(FirstInstall
, LastInstall
,
1440 [&I
](Item
const &i
) { return i
.Pkg
->Group
== I
->Pkg
->Group
; });
1441 if (crosser
!= LastInstall
)
1443 crossgraded
.insert(I
->Pkg
->ID
);
1444 toCrossgrade
.emplace_back(&(*I
), crosser
->Pkg
.FullName());
1448 for (auto I
= PlanedEnd
; I
!= List
.end(); ++I
)
1450 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1453 auto const Grp
= I
->Pkg
.Group();
1454 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1456 if (Pkg
== I
->Pkg
|| Cache
[Pkg
].Install() == false)
1458 toCrossgrade
.emplace_back(&(*I
), Pkg
.FullName());
1462 for (auto C
: toCrossgrade
)
1464 // we never do purges on packages which are crossgraded, even if "requested"
1465 if (C
.first
->Op
== Item::Purge
)
1467 C
.first
->Op
= Item::Remove
; // crossgrades should never be purged
1468 auto && Purges
= approvedStates
.Purge();
1469 auto const Ver
= std::find_if(
1470 #if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
1471 Purges
.cbegin(), Purges
.cend(),
1473 Purges
.begin(), Purges
.end(),
1475 [&C
](pkgCache::VerIterator
const &V
) { return V
.ParentPkg() == C
.first
->Pkg
; });
1476 approvedStates
.Remove(*Ver
);
1478 auto && RemOp
= PackageOps
[C
.first
->Pkg
.FullName()];
1479 if (RemOp
.size() == 5)
1481 RemOp
.erase(std::next(RemOp
.begin(), 3), RemOp
.end());
1485 _error
->Warning("Unexpected amount of planned ops for package %s: %lu", C
.first
->Pkg
.FullName().c_str(), RemOp
.size());
1488 if (crossgraded
.empty() == false)
1490 auto const oldsize
= List
.size();
1491 List
.erase(std::remove_if(List
.begin(), PlanedEnd
,
1492 [&crossgraded
](Item
const &i
){
1493 return (i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
) &&
1494 crossgraded
.find(i
.Pkg
->ID
) != crossgraded
.end();
1496 explicitIdx
-= (oldsize
- List
.size());
1500 APT::StateChanges currentStates
;
1501 if (_config
->FindB("dpkg::selection::current::saveandrestore", true))
1503 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1504 if (Pkg
->CurrentVer
== 0)
1506 else if (Pkg
->SelectedState
== pkgCache::State::Purge
)
1507 currentStates
.Purge(FindToBeRemovedVersion(Pkg
));
1508 else if (Pkg
->SelectedState
== pkgCache::State::DeInstall
)
1509 currentStates
.Remove(FindToBeRemovedVersion(Pkg
));
1510 if (currentStates
.empty() == false)
1512 APT::StateChanges cleanStates
;
1513 for (auto && P
: currentStates
.Remove())
1514 cleanStates
.Install(P
);
1515 for (auto && P
: currentStates
.Purge())
1516 cleanStates
.Install(P
);
1517 if (cleanStates
.Save(false) == false)
1518 return _error
->Error("Couldn't clean the currently selected dpkg states");
1522 if (_config
->FindB("dpkg::selection::remove::approved", true))
1524 if (approvedStates
.Save(false) == false)
1526 _error
->Error("Couldn't record the approved state changes as dpkg selection states");
1527 if (currentStates
.Save(false) == false)
1528 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1532 List
.erase(std::next(List
.begin(), explicitIdx
), List
.end());
1534 std::vector
<bool> toBeRemoved(Cache
.Head().PackageCount
, false);
1535 for (auto && I
: approvedStates
.Remove())
1536 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1537 for (auto && I
: approvedStates
.Purge())
1538 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1540 for (auto && I
: List
)
1541 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1542 toBeRemoved
[I
.Pkg
->ID
] = false;
1544 bool const RemovePending
= std::find(toBeRemoved
.begin(), toBeRemoved
.end(), true) != toBeRemoved
.end();
1545 bool const PurgePending
= approvedStates
.Purge().empty() == false;
1546 if (RemovePending
!= false || PurgePending
!= false)
1547 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1549 List
.emplace_back(Item::RemovePending
, pkgCache::PkgIterator());
1551 List
.emplace_back(Item::PurgePending
, pkgCache::PkgIterator());
1553 // support subpressing of triggers processing for special
1554 // cases like d-i that runs the triggers handling manually
1555 if (_config
->FindB("DPkg::ConfigurePending", true))
1556 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1558 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1560 d
->stdin_is_dev_null
= false;
1565 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1567 // start pty magic before the loop
1570 // Tell the progress that its starting and fork dpkg
1571 d
->progress
->Start(d
->master
);
1573 // this loop is runs once per dpkg operation
1574 vector
<Item
>::const_iterator I
= List
.cbegin();
1575 while (I
!= List
.end())
1577 // Do all actions with the same Op in one run
1578 vector
<Item
>::const_iterator J
= I
;
1579 if (TriggersPending
== true)
1580 for (; J
!= List
.end(); ++J
)
1584 if (J
->Op
!= Item::TriggersPending
)
1586 vector
<Item
>::const_iterator T
= J
+ 1;
1587 if (T
!= List
.end() && T
->Op
== I
->Op
)
1591 else if (J
->Op
== Item::Remove
|| J
->Op
== Item::Purge
)
1592 J
= std::find_if(J
, List
.cend(), [](Item
const &I
) { return I
.Op
!= Item::Remove
&& I
.Op
!= Item::Purge
; });
1594 J
= std::find_if(J
, List
.cend(), [&J
](Item
const &I
) { return I
.Op
!= J
->Op
; });
1596 auto const size
= (J
- I
) + 10;
1598 // start with the baseset of arguments
1599 auto Size
= StartSize
;
1600 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1602 // keep track of allocated strings for multiarch package names
1603 std::vector
<char *> Packages(size
, nullptr);
1607 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1609 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1610 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1612 ADDARGC("--status-fd");
1613 char status_fd_buf
[20];
1614 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1615 ADDARG(status_fd_buf
);
1616 unsigned long const Op
= I
->Op
;
1618 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1619 (I
->Op
!= Item::ConfigurePending
|| std::next(I
) != List
.end()))
1621 ADDARGC("--no-triggers");
1628 ADDARGC("--force-depends");
1629 if (std::any_of(I
, J
, ItemIsEssential
))
1630 ADDARGC("--force-remove-essential");
1631 ADDARGC("--remove");
1634 case Item::Configure
:
1635 ADDARGC("--configure");
1638 case Item::ConfigurePending
:
1639 ADDARGC("--configure");
1640 ADDARGC("--pending");
1643 case Item::TriggersPending
:
1644 ADDARGC("--triggers-only");
1645 ADDARGC("--pending");
1648 case Item::RemovePending
:
1649 ADDARGC("--remove");
1650 ADDARGC("--pending");
1653 case Item::PurgePending
:
1655 ADDARGC("--pending");
1659 ADDARGC("--unpack");
1660 ADDARGC("--auto-deconfigure");
1664 char * tmpdir_to_free
= nullptr;
1666 // Write in the file or package names
1667 if (I
->Op
== Item::Install
)
1669 auto const installsToDo
= J
- I
;
1670 if (dpkg_recursive_install
== true && dpkg_recursive_install_min
< installsToDo
)
1673 strprintf(tmpdir
, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
1674 tmpdir_to_free
= strndup(tmpdir
.data(), tmpdir
.length());
1675 if (mkdtemp(tmpdir_to_free
) == nullptr)
1676 return _error
->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free
);
1679 for (auto c
= installsToDo
- 1; (c
= c
/10) != 0; ++p
);
1680 for (unsigned long n
= 0; I
!= J
; ++n
, ++I
)
1682 if (I
->File
[0] != '/')
1683 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1684 auto const file
= flNotDir(I
->File
);
1685 std::string linkpath
;
1686 if (dpkg_recursive_install_numbered
)
1687 strprintf(linkpath
, "%s/%.*lu-%s", tmpdir_to_free
, p
, n
, file
.c_str());
1689 strprintf(linkpath
, "%s/%s", tmpdir_to_free
, file
.c_str());
1690 if (symlink(I
->File
.c_str(), linkpath
.c_str()) != 0)
1691 return _error
->Errno("DPkg::Go", "Symlinking %s to %s failed!", I
->File
.c_str(), linkpath
.c_str());
1693 ADDARGC("--recursive");
1694 ADDARG(tmpdir_to_free
);
1698 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1700 if (I
->File
[0] != '/')
1701 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1702 Args
.push_back(I
->File
.c_str());
1703 Size
+= I
->File
.length();
1707 else if (I
->Op
== Item::RemovePending
)
1710 StripAlreadyDoneFrom(approvedStates
.Remove());
1711 if (approvedStates
.Remove().empty())
1714 else if (I
->Op
== Item::PurgePending
)
1717 // explicit removes of packages without conffiles passthrough the purge states instantly, too.
1718 // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg
1719 StripAlreadyDoneFrom(approvedStates
.Purge());
1720 if (approvedStates
.Purge().empty())
1722 std::remove_reference
<decltype(approvedStates
.Remove())>::type approvedRemoves
;
1723 std::swap(approvedRemoves
, approvedStates
.Remove());
1724 // we apply it again here as an explicit remove in the ordering will have cleared the purge state
1725 if (approvedStates
.Save(false) == false)
1727 _error
->Error("Couldn't record the approved purges as dpkg selection states");
1728 if (currentStates
.Save(false) == false)
1729 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1732 std::swap(approvedRemoves
, approvedStates
.Remove());
1736 string
const nativeArch
= _config
->Find("APT::Architecture");
1737 unsigned long const oldSize
= I
->Pkg
.end() == false ? Size
: 0;
1738 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1740 if((*I
).Pkg
.end() == true)
1742 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1744 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1745 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1746 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1747 strcmp(I
->Pkg
.Arch(), "none") == 0))
1749 char const * const name
= I
->Pkg
.Name();
1754 pkgCache::VerIterator PkgVer
;
1755 std::string name
= I
->Pkg
.Name();
1756 if (Op
== Item::Remove
)
1757 PkgVer
= I
->Pkg
.CurrentVer();
1758 else if (Op
== Item::Purge
)
1760 // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here
1761 PkgVer
= I
->Pkg
.CurrentVer();
1762 if (PkgVer
.end() == true)
1766 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1767 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1768 ; // never arch-qualify a package without an arch
1769 else if (PkgVer
.end() == false)
1770 name
.append(":").append(PkgVer
.Arch());
1772 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1773 char * const fullname
= strdup(name
.c_str());
1774 Packages
.push_back(fullname
);
1778 // skip configure action if all sheduled packages disappeared
1779 if (oldSize
== Size
)
1787 if (noopDPkgInvocation
== true)
1789 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1790 a
!= Args
.end(); ++a
)
1793 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1794 p
!= Packages
.end(); ++p
)
1799 cleanUpTmpDir(tmpdir_to_free
);
1802 Args
.push_back(NULL
);
1808 /* Mask off sig int/quit. We do this because dpkg also does when
1809 it forks scripts. What happens is that when you hit ctrl-c it sends
1810 it to all processes in the group. Since dpkg ignores the signal
1811 it doesn't die but we do! So we must also ignore it */
1812 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1813 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1815 // Check here for any SIGINT
1816 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1819 // ignore SIGHUP as well (debian #463030)
1820 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1823 d
->progress
->StartDpkg();
1824 std::set
<int> KeepFDs
;
1825 KeepFDs
.insert(fd
[1]);
1826 MergeKeepFdsFromConfiguration(KeepFDs
);
1827 pid_t Child
= ExecFork(KeepFDs
);
1830 // This is the child
1831 SetupSlavePtyMagic();
1832 close(fd
[0]); // close the read end of the pipe
1834 debSystem::DpkgChrootDirectory();
1836 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1839 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1843 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1846 // Discard everything in stdin before forking dpkg
1847 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1850 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1852 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1856 // if color support isn't enabled/disabled explicitly tell
1857 // dpkg to use the same state apt is using for its color support
1858 if (_config
->FindB("APT::Color", false) == true)
1859 setenv("DPKG_COLORS", "always", 0);
1861 setenv("DPKG_COLORS", "never", 0);
1863 execvp(Args
[0], (char**) &Args
[0]);
1864 cerr
<< "Could not exec dpkg!" << endl
;
1868 // we read from dpkg here
1869 int const _dpkgin
= fd
[0];
1870 close(fd
[1]); // close the write end of the pipe
1873 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1877 sigemptyset(&d
->sigmask
);
1878 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1880 /* free vectors (and therefore memory) as we don't need the included data anymore */
1881 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1882 p
!= Packages
.end(); ++p
)
1886 // the result of the waitpid call
1889 bool waitpid_failure
= false;
1890 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1892 // error handling, waitpid returned -1
1895 waitpid_failure
= true;
1899 // wait for input or output here
1901 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1902 FD_SET(STDIN_FILENO
, &rfds
);
1903 FD_SET(_dpkgin
, &rfds
);
1905 FD_SET(d
->master
, &rfds
);
1907 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1908 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1909 &tv
, &d
->original_sigmask
);
1910 d
->progress
->Pulse();
1911 if (select_ret
== 0)
1913 else if (select_ret
< 0 && errno
== EINTR
)
1915 else if (select_ret
< 0)
1917 perror("select() returned error");
1921 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1922 DoTerminalPty(d
->master
);
1923 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1925 if(FD_ISSET(_dpkgin
, &rfds
))
1926 DoDpkgStatusFd(_dpkgin
);
1930 // Restore sig int/quit
1931 signal(SIGQUIT
,old_SIGQUIT
);
1932 signal(SIGINT
,old_SIGINT
);
1933 signal(SIGHUP
,old_SIGHUP
);
1935 cleanUpTmpDir(tmpdir_to_free
);
1937 if (waitpid_failure
== true)
1939 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1940 _error
->Error("%s", d
->dpkg_error
.c_str());
1944 // Check for an error code.
1945 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1947 // if it was set to "keep-dpkg-running" then we won't return
1948 // here but keep the loop going and just report it as a error
1950 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1952 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1953 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1954 else if (WIFEXITED(Status
) != 0)
1955 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1957 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1958 _error
->Error("%s", d
->dpkg_error
.c_str());
1964 // dpkg is done at this point
1968 if (d
->dpkg_error
.empty() == false)
1970 // no point in reseting packages we already completed removal for
1971 StripAlreadyDoneFrom(approvedStates
.Remove());
1972 StripAlreadyDoneFrom(approvedStates
.Purge());
1973 APT::StateChanges undo
;
1974 auto && undoRem
= approvedStates
.Remove();
1975 std::move(undoRem
.begin(), undoRem
.end(), std::back_inserter(undo
.Install()));
1976 auto && undoPur
= approvedStates
.Purge();
1977 std::move(undoPur
.begin(), undoPur
.end(), std::back_inserter(undo
.Install()));
1978 approvedStates
.clear();
1979 if (undo
.Save(false) == false)
1980 _error
->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
1983 StripAlreadyDoneFrom(currentStates
.Remove());
1984 StripAlreadyDoneFrom(currentStates
.Purge());
1985 if (currentStates
.Save(false) == false)
1986 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1988 if (pkgPackageManager::SigINTStop
)
1989 _error
->Warning(_("Operation was interrupted before it could finish"));
1991 if (noopDPkgInvocation
== false)
1993 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1994 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1995 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1997 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1998 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
2000 _error
->PushToStack();
2001 pkgCacheFile CacheFile
;
2002 CacheFile
.BuildCaches(NULL
, true);
2003 _error
->RevertToStack();
2008 // disappearing packages can forward their auto-bit
2009 if (disappearedPkgs
.empty() == false)
2010 Cache
.writeStateFile(NULL
);
2012 d
->progress
->Stop();
2014 if (RunScripts("DPkg::Post-Invoke") == false)
2017 return d
->dpkg_error
.empty();
2020 void SigINT(int /*sig*/) {
2021 pkgPackageManager::SigINTStop
= true;
2024 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
2025 // ---------------------------------------------------------------------
2027 void pkgDPkgPM::Reset()
2029 List
.erase(List
.begin(),List
.end());
2032 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
2033 // ---------------------------------------------------------------------
2035 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
2037 // If apport doesn't exist or isn't installed do nothing
2038 // This e.g. prevents messages in 'universes' without apport
2039 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
2040 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
2043 string pkgname
, reportfile
, pkgver
, arch
;
2044 string::size_type pos
;
2047 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
2049 std::clog
<< "configured to not write apport reports" << std::endl
;
2053 // only report the first errors
2054 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
2056 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
2060 // check if its not a follow up error
2061 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
2062 if(strstr(errormsg
, needle
) != NULL
) {
2063 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
2067 // do not report disk-full failures
2068 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
2069 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
2073 // do not report out-of-memory failures
2074 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
2075 strstr(errormsg
, "failed to allocate memory") != NULL
) {
2076 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
2080 // do not report bugs regarding inaccessible local files
2081 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
2082 strstr(errormsg
, "cannot access archive") != NULL
) {
2083 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2087 // do not report errors encountered when decompressing packages
2088 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
2089 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2093 // do not report dpkg I/O errors, this is a format string, so we compare
2094 // the prefix and the suffix of the error with the dpkg error message
2095 vector
<string
> io_errors
;
2096 io_errors
.push_back(string("failed to read"));
2097 io_errors
.push_back(string("failed to write"));
2098 io_errors
.push_back(string("failed to seek"));
2099 io_errors
.push_back(string("unexpected end of file or stream"));
2101 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
2103 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
2104 if (list
.size() > 1) {
2105 // we need to split %s, VectorizeString only allows char so we need
2106 // to kill the "s" manually
2107 if (list
[1].size() > 1) {
2108 list
[1].erase(0, 1);
2109 if(strstr(errormsg
, list
[0].c_str()) &&
2110 strstr(errormsg
, list
[1].c_str())) {
2111 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
2118 // get the pkgname and reportfile
2119 pkgname
= flNotDir(pkgpath
);
2120 pos
= pkgname
.find('_');
2121 if(pos
!= string::npos
)
2122 pkgname
= pkgname
.substr(0, pos
);
2124 // find the package version and source package name
2125 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
2126 if (Pkg
.end() == true)
2128 if (pos
== std::string::npos
|| _config
->FindB("dpkg::install::recursive::numbered", true) == false)
2130 auto const dash
= pkgname
.find_first_not_of("0123456789");
2131 if (dash
== std::string::npos
|| pkgname
[dash
] != '-')
2133 pkgname
.erase(0, dash
+ 1);
2134 Pkg
= Cache
.FindPkg(pkgname
);
2135 if (Pkg
.end() == true)
2138 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
2139 if (Ver
.end() == true)
2141 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
2143 // if the file exists already, we check:
2144 // - if it was reported already (touched by apport).
2145 // If not, we do nothing, otherwise
2146 // we overwrite it. This is the same behaviour as apport
2147 // - if we have a report with the same pkgversion already
2149 _config
->CndSet("Dir::Apport", "var/crash");
2150 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
2151 if(FileExists(reportfile
))
2156 // check atime/mtime
2157 stat(reportfile
.c_str(), &buf
);
2158 if(buf
.st_mtime
> buf
.st_atime
)
2161 // check if the existing report is the same version
2162 report
= fopen(reportfile
.c_str(),"r");
2163 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
2165 if(strstr(strbuf
,"Package:") == strbuf
)
2167 char pkgname
[255], version
[255];
2168 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
2169 if(strcmp(pkgver
.c_str(), version
) == 0)
2179 // now write the report
2180 arch
= _config
->Find("APT::Architecture");
2181 report
= fopen(reportfile
.c_str(),"w");
2184 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
2185 chmod(reportfile
.c_str(), 0);
2187 chmod(reportfile
.c_str(), 0600);
2188 fprintf(report
, "ProblemType: Package\n");
2189 fprintf(report
, "Architecture: %s\n", arch
.c_str());
2190 time_t now
= time(NULL
);
2191 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
2192 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
2193 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
2194 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
2195 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
2197 // ensure that the log is flushed
2199 fflush(d
->term_out
);
2201 // attach terminal log it if we have it
2202 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
2203 if (!logfile_name
.empty())
2207 fprintf(report
, "DpkgTerminalLog:\n");
2208 log
= fopen(logfile_name
.c_str(),"r");
2212 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2213 fprintf(report
, " %s", buf
);
2214 fprintf(report
, " \n");
2219 // attach history log it if we have it
2220 string histfile_name
= _config
->FindFile("Dir::Log::History");
2221 if (!histfile_name
.empty())
2223 fprintf(report
, "DpkgHistoryLog:\n");
2224 FILE* log
= fopen(histfile_name
.c_str(),"r");
2228 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2229 fprintf(report
, " %s", buf
);
2234 // log the ordering, see dpkgpm.h and the "Ops" enum there
2235 fprintf(report
, "AptOrdering:\n");
2236 for (auto && I
: List
)
2238 char const * opstr
= nullptr;
2241 case Item::Install
: opstr
= "Install"; break;
2242 case Item::Configure
: opstr
= "Configure"; break;
2243 case Item::Remove
: opstr
= "Remove"; break;
2244 case Item::Purge
: opstr
= "Purge"; break;
2245 case Item::ConfigurePending
: opstr
= "ConfigurePending"; break;
2246 case Item::TriggersPending
: opstr
= "TriggersPending"; break;
2247 case Item::RemovePending
: opstr
= "RemovePending"; break;
2248 case Item::PurgePending
: opstr
= "PurgePending"; break;
2250 auto const pkgname
= I
.Pkg
.end() ? "NULL" : I
.Pkg
.FullName();
2251 fprintf(report
, " %s: %s\n", pkgname
.c_str(), opstr
);
2254 // attach dmesg log (to learn about segfaults)
2255 if (FileExists("/bin/dmesg"))
2257 fprintf(report
, "Dmesg:\n");
2258 FILE *log
= popen("/bin/dmesg","r");
2262 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2263 fprintf(report
, " %s", buf
);
2268 // attach df -l log (to learn about filesystem status)
2269 if (FileExists("/bin/df"))
2272 fprintf(report
, "Df:\n");
2273 FILE *log
= popen("/bin/df -l","r");
2277 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2278 fprintf(report
, " %s", buf
);