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
)
420 static bool interrupted
= false;
422 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
423 if (Opts
== 0 || Opts
->Child
== 0)
427 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
428 sighandler_t old_sigint
= signal(SIGINT
, [](int signum
){
432 unsigned int Count
= 1;
433 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
435 if (Opts
->Value
.empty() == true)
438 if(_config
->FindB("Debug::RunScripts", false) == true)
439 std::clog
<< "Running external script with list of all .deb file: '"
440 << Opts
->Value
<< "'" << std::endl
;
442 // Determine the protocol version
443 string OptSec
= Opts
->Value
;
444 string::size_type Pos
;
445 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
446 Pos
= OptSec
.length();
447 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
449 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
450 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
453 std::set
<int> KeepFDs
;
454 MergeKeepFdsFromConfiguration(KeepFDs
);
456 if (pipe(Pipes
) != 0) {
457 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
460 if (InfoFD
!= (unsigned)Pipes
[0])
461 SetCloseExec(Pipes
[0],true);
463 KeepFDs
.insert(Pipes
[0]);
466 SetCloseExec(Pipes
[1],true);
468 // Purified Fork for running the script
469 pid_t Process
= ExecFork(KeepFDs
);
473 dup2(Pipes
[0], InfoFD
);
474 SetCloseExec(STDOUT_FILENO
,false);
475 SetCloseExec(STDIN_FILENO
,false);
476 SetCloseExec(STDERR_FILENO
,false);
479 strprintf(hookfd
, "%d", InfoFD
);
480 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
482 debSystem::DpkgChrootDirectory();
486 Args
[2] = Opts
->Value
.c_str();
488 execv(Args
[0],(char **)Args
);
492 FILE *F
= fdopen(Pipes
[1],"w");
494 result
= _error
->Errno("fdopen","Failed to open new FD");
498 // Feed it the filenames.
501 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
503 // Only deal with packages to be installed from .deb
504 if (I
->Op
!= Item::Install
)
508 if (I
->File
[0] != '/')
511 /* Feed the filename of each package that is pending install
513 fprintf(F
,"%s\n",I
->File
.c_str());
519 SendPkgsInfo(F
, Version
);
523 // Clean up the sub process
524 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
525 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
529 signal(SIGINT
, old_sigint
);
530 signal(SIGPIPE
, old_sigpipe
);
533 result
= _error
->Error("Interrupted");
538 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
539 // ---------------------------------------------------------------------
542 void pkgDPkgPM::DoStdin(int master
)
544 unsigned char input_buf
[256] = {0,};
545 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
547 FileFd::Write(master
, input_buf
, len
);
549 d
->stdin_is_dev_null
= true;
552 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
553 // ---------------------------------------------------------------------
555 * read the terminal pty and write log
557 void pkgDPkgPM::DoTerminalPty(int master
)
559 unsigned char term_buf
[1024] = {0,0, };
561 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
562 if(len
== -1 && errno
== EIO
)
564 // this happens when the child is about to exit, we
565 // give it time to actually exit, otherwise we run
566 // into a race so we sleep for half a second.
567 struct timespec sleepfor
= { 0, 500000000 };
568 nanosleep(&sleepfor
, NULL
);
573 FileFd::Write(1, term_buf
, len
);
575 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
578 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
579 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
581 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
583 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
585 /* dpkg sends strings like this:
586 'status: <pkg>: <pkg qstate>'
587 'status: <pkg>:<arch>: <pkg qstate>'
589 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
590 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
593 // we need to split on ": " (note the appended space) as the ':' is
594 // part of the pkgname:arch information that dpkg sends
596 // A dpkg error message may contain additional ":" (like
597 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
598 // so we need to ensure to not split too much
599 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
603 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
607 // build the (prefix, pkgname, action) tuple, position of this
608 // is different for "processing" or "status" messages
609 std::string prefix
= APT::String::Strip(list
[0]);
613 // "processing" has the form "processing: action: pkg or trigger"
614 // with action = ["install", "upgrade", "configure", "remove", "purge",
615 // "disappear", "trigproc"]
616 if (prefix
== "processing")
618 pkgname
= APT::String::Strip(list
[2]);
619 action
= APT::String::Strip(list
[1]);
620 // we don't care for the difference (as dpkg doesn't really either)
621 if (action
== "upgrade")
624 // "status" has the form: "status: pkg: state"
625 // with state in ["half-installed", "unpacked", "half-configured",
626 // "installed", "config-files", "not-installed"]
627 else if (prefix
== "status")
629 pkgname
= APT::String::Strip(list
[1]);
630 action
= APT::String::Strip(list
[2]);
633 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
638 /* handle the special cases first:
640 errors look like this:
641 '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
642 and conffile-prompt like this
643 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
645 if (prefix
== "status")
647 if(action
== "error")
649 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
652 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
655 else if(action
== "conffile-prompt")
657 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
663 // at this point we know that we should have a valid pkgname, so build all
666 // dpkg does not always send "pkgname:arch" so we add it here if needed
667 if (pkgname
.find(":") == std::string::npos
)
669 // find the package in the group that is touched by dpkg
670 // if there are multiple pkgs dpkg would send us a full pkgname:arch
671 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
672 if (Grp
.end() == false)
673 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
674 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
676 auto fullname
= P
.FullName();
677 if (Cache
[P
].Delete() && PackageOps
[fullname
].size() <= PackageOpsDone
[fullname
])
679 pkgname
= std::move(fullname
);
684 std::string arch
= "";
685 if (pkgname
.find(":") != string::npos
)
686 arch
= StringSplit(pkgname
, ":")[1];
687 std::string i18n_pkgname
= pkgname
;
688 if (arch
.size() != 0)
689 strprintf(i18n_pkgname
, "%s (%s)", StringSplit(pkgname
, ":")[0].c_str(), arch
.c_str());
691 // 'processing' from dpkg looks like
692 // 'processing: action: pkg'
693 if(prefix
== "processing")
695 const std::pair
<const char *, const char *> * const iter
=
696 std::find_if(PackageProcessingOpsBegin
,
697 PackageProcessingOpsEnd
,
698 MatchProcessingOp(action
.c_str()));
699 if(iter
== PackageProcessingOpsEnd
)
702 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
706 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
707 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
709 // FIXME: this needs a muliarch testcase
710 // FIXME2: is "pkgname" here reliable with dpkg only sending us
712 if (action
== "disappear")
713 handleDisappearAction(pkgname
);
717 if (prefix
== "status")
719 std::vector
<struct DpkgState
> &states
= PackageOps
[pkgname
];
720 if (action
== "triggers-pending")
723 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
724 << " action: " << action
<< " (prefix 2 to "
725 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
727 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
728 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
731 else if(PackageOpsDone
[pkgname
] < states
.size())
733 char const * next_action
= states
[PackageOpsDone
[pkgname
]].state
;
737 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
738 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
741 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
742 << " pending trigger defused by unpack" << std::endl;
743 // unpacking a package defuses the pending trigger
744 PackageOpsDone[pkg] += 2;
746 next_action = states[PackageOpsDone[pkg]].state;
750 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
751 << " action: " << action
<< " (expected: '" << next_action
<< "' "
752 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
754 // check if the package moved to the next dpkg state
755 if(action
== next_action
)
757 // only read the translation if there is actually a next action
758 char const * const translation
= _(states
[PackageOpsDone
[pkgname
]].str
);
760 // we moved from one dpkg state to a new one, report that
761 ++PackageOpsDone
[pkgname
];
765 strprintf(msg
, translation
, i18n_pkgname
.c_str());
766 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
768 else if (action
== "unpacked" && strcmp(next_action
, "config-files") == 0)
770 // in a crossgrade what looked like a remove first is really an unpack over it
771 ++PackageOpsDone
[pkgname
];
774 auto const Pkg
= Cache
.FindPkg(pkgname
);
775 if (likely(Pkg
.end() == false))
777 auto const Grp
= Pkg
.Group();
778 if (likely(Grp
.end() == false))
780 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
781 if(Cache
[P
].Install())
783 auto && Ops
= PackageOps
[P
.FullName()];
784 auto const unpackOp
= std::find_if(Ops
.cbegin(), Ops
.cend(), [](DpkgState
const &s
) { return strcmp(s
.state
, "unpacked") == 0; });
785 if (unpackOp
!= Ops
.cend())
787 auto const skipped
= std::distance(Ops
.cbegin(), unpackOp
);
788 PackagesDone
+= skipped
;
789 PackageOpsDone
[P
.FullName()] += skipped
;
801 // DPkgPM::handleDisappearAction /*{{{*/
802 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
804 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
805 if (unlikely(Pkg
.end() == true))
808 // record the package name for display and stuff later
809 disappearedPkgs
.insert(Pkg
.FullName(true));
811 // the disappeared package was auto-installed - nothing to do
812 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
814 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
815 if (unlikely(PkgVer
.end() == true))
817 /* search in the list of dependencies for (Pre)Depends,
818 check if this dependency has a Replaces on our package
819 and if so transfer the manual installed flag to it */
820 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
822 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
823 Dep
->Type
!= pkgCache::Dep::PreDepends
)
825 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
826 if (unlikely(Tar
.end() == true))
828 // the package is already marked as manual
829 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
831 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
832 if (TarVer
.end() == true)
834 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
836 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
838 if (Pkg
!= Rep
.TargetPkg())
840 // okay, they are strongly connected - transfer manual-bit
842 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
843 Cache
[Tar
].Flags
&= ~Flag::Auto
;
849 // DPkgPM::DoDpkgStatusFd /*{{{*/
850 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
852 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
853 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
856 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
858 // process line by line from the buffer
859 char *p
= d
->dpkgbuf
, *q
= nullptr;
860 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
863 ProcessDpkgStatusLine(p
);
864 p
= q
+ 1; // continue with next line
867 // check if we stripped the buffer clean
868 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
874 // otherwise move the unprocessed tail to the start and update pos
875 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
876 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
879 // DPkgPM::WriteHistoryTag /*{{{*/
880 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
882 size_t const length
= value
.length();
885 // poor mans rstrip(", ")
886 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
887 value
.erase(length
- 2, 2);
888 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
890 // DPkgPM::OpenLog /*{{{*/
891 bool pkgDPkgPM::OpenLog()
893 string
const logdir
= _config
->FindDir("Dir::Log");
894 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
895 // FIXME: use a better string after freeze
896 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
900 time_t const t
= time(NULL
);
902 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
903 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
906 string
const logfile_name
= flCombine(logdir
,
907 _config
->Find("Dir::Log::Terminal"));
908 if (!logfile_name
.empty())
910 d
->term_out
= fopen(logfile_name
.c_str(),"a");
911 if (d
->term_out
== NULL
)
912 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
913 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
914 SetCloseExec(fileno(d
->term_out
), true);
915 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
917 struct passwd
*pw
= getpwnam("root");
918 struct group
*gr
= getgrnam("adm");
919 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
920 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
922 if (chmod(logfile_name
.c_str(), 0640) != 0)
923 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
924 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
927 // write your history
928 string
const history_name
= flCombine(logdir
,
929 _config
->Find("Dir::Log::History"));
930 if (!history_name
.empty())
932 d
->history_out
= fopen(history_name
.c_str(),"a");
933 if (d
->history_out
== NULL
)
934 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
935 SetCloseExec(fileno(d
->history_out
), true);
936 chmod(history_name
.c_str(), 0644);
937 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
938 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
939 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
941 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
943 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
944 if (Cache
[I
].NewInstall() == true)
945 HISTORYINFO(install
, CANDIDATE_AUTO
)
946 else if (Cache
[I
].ReInstall() == true)
947 HISTORYINFO(reinstall
, CANDIDATE
)
948 else if (Cache
[I
].Upgrade() == true)
949 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
950 else if (Cache
[I
].Downgrade() == true)
951 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
952 else if (Cache
[I
].Delete() == true)
953 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
957 line
->append(I
.FullName(false)).append(" (");
958 switch (infostring
) {
959 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
961 line
->append(Cache
[I
].CandVersion
);
962 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
963 line
->append(", automatic");
965 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
966 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
970 if (_config
->Exists("Commandline::AsString") == true)
971 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
972 std::string RequestingUser
= AptHistoryRequestingUser();
973 if (RequestingUser
!= "")
974 WriteHistoryTag("Requested-By", RequestingUser
);
975 WriteHistoryTag("Install", install
);
976 WriteHistoryTag("Reinstall", reinstall
);
977 WriteHistoryTag("Upgrade", upgrade
);
978 WriteHistoryTag("Downgrade",downgrade
);
979 WriteHistoryTag("Remove",remove
);
980 WriteHistoryTag("Purge",purge
);
981 fflush(d
->history_out
);
987 // DPkg::CloseLog /*{{{*/
988 bool pkgDPkgPM::CloseLog()
991 time_t t
= time(NULL
);
993 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
994 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
998 fprintf(d
->term_out
, "Log ended: ");
999 fprintf(d
->term_out
, "%s", timestr
);
1000 fprintf(d
->term_out
, "\n");
1001 fclose(d
->term_out
);
1007 if (disappearedPkgs
.empty() == false)
1010 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
1011 d
!= disappearedPkgs
.end(); ++d
)
1013 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
1014 disappear
.append(*d
);
1015 if (P
.end() == true)
1016 disappear
.append(", ");
1018 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
1020 WriteHistoryTag("Disappeared", disappear
);
1022 if (d
->dpkg_error
.empty() == false)
1023 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
1024 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
1025 fclose(d
->history_out
);
1027 d
->history_out
= NULL
;
1033 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1034 void pkgDPkgPM::BuildPackagesProgressMap()
1036 // map the dpkg states to the operations that are performed
1037 // (this is sorted in the same way as Item::Ops)
1038 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1039 // Install operation
1041 {"half-installed", N_("Preparing %s")},
1042 {"unpacked", N_("Unpacking %s") },
1045 // Configure operation
1047 {"unpacked",N_("Preparing to configure %s") },
1048 {"half-configured", N_("Configuring %s") },
1049 { "installed", N_("Installed %s")},
1053 {"half-configured", N_("Preparing for removal of %s")},
1054 {"half-installed", N_("Removing %s")},
1055 {"config-files", N_("Removed %s")},
1059 {"config-files", N_("Preparing to completely remove %s")},
1060 {"not-installed", N_("Completely removed %s")},
1064 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1066 // init the PackageOps map, go over the list of packages that
1067 // that will be [installed|configured|removed|purged] and add
1068 // them to the PackageOps map (the dpkg states it goes through)
1069 // and the PackageOpsTranslations (human readable strings)
1070 for (auto &&I
: List
)
1072 if(I
.Pkg
.end() == true)
1075 string
const name
= I
.Pkg
.FullName();
1076 PackageOpsDone
[name
] = 0;
1077 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1078 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1080 // purging a package which is installed first passes through remove states
1081 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1082 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1083 PackagesTotal
+= DpkgOps
.size();
1085 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1086 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1087 if (state
.state
== nullptr)
1093 /* one extra: We don't want the progress bar to reach 100%, especially not
1094 if we call dpkg --configure --pending and process a bunch of triggers
1095 while showing 100%. Also, spindown takes a while, so never reaching 100%
1096 is way more correct than reaching 100% while still doing stuff even if
1097 doing it this way is slightly bending the rules */
1101 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1103 APT::Progress::PackageManager
*progress
= NULL
;
1105 progress
= APT::Progress::PackageManagerProgressFactory();
1107 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1109 return Go(progress
);
1112 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1114 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1117 if (d
->slave
!= NULL
)
1123 if (isatty(STDIN_FILENO
) == 0)
1124 d
->direct_stdin
= true;
1126 _error
->PushToStack();
1128 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1129 if (d
->master
== -1)
1130 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1131 else if (unlockpt(d
->master
) == -1)
1132 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1135 #ifdef HAVE_PTSNAME_R
1136 char slave_name
[64]; // 64 is used by bionic
1137 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1139 char const * const slave_name
= ptsname(d
->master
);
1140 if (slave_name
== NULL
)
1142 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1145 d
->slave
= strdup(slave_name
);
1146 if (d
->slave
== NULL
)
1147 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1148 else if (grantpt(d
->master
) == -1)
1149 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1150 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1152 d
->tt_is_valid
= true;
1153 struct termios raw_tt
;
1154 // copy window size of stdout if its a 'good' terminal
1155 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1158 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1159 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1160 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1161 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1163 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1164 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1168 raw_tt
.c_lflag
&= ~ECHO
;
1169 raw_tt
.c_lflag
|= ISIG
;
1170 // block SIGTTOU during tcsetattr to prevent a hang if
1171 // the process is a member of the background process group
1172 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1173 sigemptyset(&d
->sigmask
);
1174 sigaddset(&d
->sigmask
, SIGTTOU
);
1175 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1176 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1177 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1178 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1181 if (d
->slave
!= NULL
)
1183 /* on linux, closing (and later reopening) all references to the slave
1184 makes the slave a death end, so we open it here to have one open all
1185 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1186 on kfreebsd we get an incorrect ("step like") output then while it has
1187 no problem with closing all references… so to avoid platform specific
1188 code here we combine both and be happy once more */
1189 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1194 if (_error
->PendingError() == true)
1196 if (d
->master
!= -1)
1201 if (d
->slave
!= NULL
)
1206 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1208 _error
->RevertToStack();
1211 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1213 if(d
->master
== -1 || d
->slave
== NULL
)
1216 if (close(d
->master
) == -1)
1217 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1220 _error
->FatalE("setsid", "Starting a new session for child failed!");
1222 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1224 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1225 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1226 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1229 unsigned short i
= 0;
1230 if (d
->direct_stdin
== true)
1233 if (dup2(slaveFd
, i
) == -1)
1234 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1236 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1237 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1244 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1246 if (d
->slave
!= NULL
)
1249 if (d
->protect_slave_from_dying
!= -1)
1251 close(d
->protect_slave_from_dying
);
1252 d
->protect_slave_from_dying
= -1;
1256 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1257 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1263 static void cleanUpTmpDir(char * const tmpdir
) /*{{{*/
1265 if (tmpdir
== nullptr)
1267 DIR * const D
= opendir(tmpdir
);
1269 _error
->Errno("opendir", _("Unable to read %s"), tmpdir
);
1272 auto const dfd
= dirfd(D
);
1273 for (struct dirent
*Ent
= readdir(D
); Ent
!= nullptr; Ent
= readdir(D
))
1275 if (Ent
->d_name
[0] == '.')
1277 #ifdef _DIRENT_HAVE_D_TYPE
1278 if (unlikely(Ent
->d_type
!= DT_LNK
&& Ent
->d_type
!= DT_UNKNOWN
))
1281 if (unlikely(unlinkat(dfd
, Ent
->d_name
, 0) != 0))
1291 // DPkgPM::Go - Run the sequence /*{{{*/
1292 // ---------------------------------------------------------------------
1293 /* This globs the operations and calls dpkg
1295 * If it is called with a progress object apt will report the install
1296 * progress to this object. It maps the dpkg states a package goes
1297 * through to human readable (and i10n-able)
1298 * names and calculates a percentage for each step.
1300 static bool ItemIsEssential(pkgDPkgPM::Item
const &I
)
1302 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1303 if (cachegen
== "none" || cachegen
== "native")
1305 if (unlikely(I
.Pkg
.end()))
1307 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1309 bool pkgDPkgPM::ExpandPendingCalls(std::vector
<Item
> &List
, pkgDepCache
&Cache
)
1312 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyRemoved
;
1313 for (auto && I
: List
)
1314 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1315 alreadyRemoved
.insert(I
.Pkg
->ID
);
1316 std::remove_reference
<decltype(List
)>::type AppendList
;
1317 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1318 if (Cache
[Pkg
].Delete() && alreadyRemoved
.insert(Pkg
->ID
).second
== true)
1319 AppendList
.emplace_back(Cache
[Pkg
].Purge() ? Item::Purge
: Item::Remove
, Pkg
);
1320 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1323 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyConfigured
;
1324 for (auto && I
: List
)
1325 if (I
.Op
== Item::Configure
)
1326 alreadyConfigured
.insert(I
.Pkg
->ID
);
1327 std::remove_reference
<decltype(List
)>::type AppendList
;
1328 for (auto && I
: List
)
1329 if (I
.Op
== Item::Install
&& alreadyConfigured
.insert(I
.Pkg
->ID
).second
== true)
1330 AppendList
.emplace_back(Item::Configure
, I
.Pkg
);
1331 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1332 if (Pkg
.State() == pkgCache::PkgIterator::NeedsConfigure
&& alreadyConfigured
.insert(Pkg
->ID
).second
== true)
1333 AppendList
.emplace_back(Item::Configure
, Pkg
);
1334 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1338 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1340 // we remove the last configures (and after that removes) from the list here
1341 // as they will be covered by the pending calls, so explicit calls are busy work
1342 decltype(List
)::const_iterator::difference_type explicitIdx
=
1343 std::distance(List
.cbegin(),
1344 _config
->FindB("Dpkg::ExplicitLastConfigure", false) ? List
.cend() :
1346 std::find_if_not(List
.crbegin(), List
.crend(), [](Item
const &i
) { return i
.Op
== Item::Configure
; }),
1347 List
.crend(), [](Item
const &i
) { return i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
; }).base());
1349 // explicitely remove&configure everything for hookscripts and progress building
1350 ExpandPendingCalls(List
, Cache
);
1352 auto const StripAlreadyDoneFromPending
= [&](APT::VersionVector
& Pending
) {
1353 Pending
.erase(std::remove_if(Pending
.begin(), Pending
.end(), [&](pkgCache::VerIterator
const &Ver
) {
1354 auto const PN
= Ver
.ParentPkg().FullName();
1355 return PackageOps
[PN
].size() <= PackageOpsDone
[PN
];
1359 pkgPackageManager::SigINTStop
= false;
1360 d
->progress
= progress
;
1362 // Generate the base argument list for dpkg
1363 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1364 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1365 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1366 [](std::string
const &s
) { return s
.c_str(); });
1367 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1368 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1369 size_t const BaseArgs
= Args
.size();
1374 // try to figure out the max environment size
1375 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1378 OSArgMax
-= EnvironmentSize() - 2*1024;
1379 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1380 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", true);
1382 if (RunScripts("DPkg::Pre-Invoke") == false)
1385 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1388 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1389 // store auto-bits as they are supposed to be after dpkg is run
1390 if (noopDPkgInvocation
== false)
1391 Cache
.writeStateFile(NULL
);
1393 bool dpkg_recursive_install
= _config
->FindB("dpkg::install::recursive", false);
1394 if (_config
->FindB("dpkg::install::recursive::force", false) == false)
1396 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1397 auto const dpkgpkg
= Cache
.FindPkg("dpkg");
1398 if (likely(dpkgpkg
.end() == false && dpkgpkg
->CurrentVer
!= 0))
1399 dpkg_recursive_install
= Cache
.VS().CmpVersion("1.18.5", dpkgpkg
.CurrentVer().VerStr()) <= 0;
1401 // no point in doing this dance for a handful of packages only
1402 unsigned int const dpkg_recursive_install_min
= _config
->FindB("dpkg::install::recursive::minimum", 5);
1403 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1404 bool const dpkg_recursive_install_numbered
= _config
->FindB("dpkg::install::recursive::numbered", true);
1407 BuildPackagesProgressMap();
1409 APT::StateChanges approvedStates
;
1410 if (_config
->FindB("dpkg::selection::remove::approved", true))
1412 for (auto && I
: List
)
1413 if (I
.Op
== Item::Purge
)
1414 approvedStates
.Purge(FindToBeRemovedVersion(I
.Pkg
));
1415 else if (I
.Op
== Item::Remove
)
1416 approvedStates
.Remove(FindToBeRemovedVersion(I
.Pkg
));
1419 // Skip removes if we install another architecture of this package soon (crossgrade)
1420 // We can't just skip them all the time as it could be an ordering requirement [of another package]
1421 if ((approvedStates
.Remove().empty() == false || approvedStates
.Purge().empty() == false) &&
1422 _config
->FindB("dpkg::remove::crossgrade::implicit", true) == true)
1424 std::unordered_set
<decltype(pkgCache::Package::ID
)> crossgraded
;
1425 std::vector
<std::pair
<Item
*, std::string
>> toCrossgrade
;
1426 auto const PlanedEnd
= std::next(List
.begin(), explicitIdx
);
1427 for (auto I
= List
.begin(); I
!= PlanedEnd
; ++I
)
1429 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1432 auto const Grp
= I
->Pkg
.Group();
1433 size_t installedInstances
= 0;
1434 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1435 if (Pkg
->CurrentVer
!= 0 || Cache
[Pkg
].Install())
1436 ++installedInstances
;
1437 if (installedInstances
== 2)
1439 auto const FirstInstall
= std::find_if_not(I
, List
.end(),
1440 [](Item
const &i
) { return i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
; });
1441 auto const LastInstall
= std::find_if_not(FirstInstall
, List
.end(),
1442 [](Item
const &i
) { return i
.Op
== Item::Install
; });
1443 auto const crosser
= std::find_if(FirstInstall
, LastInstall
,
1444 [&I
](Item
const &i
) { return i
.Pkg
->Group
== I
->Pkg
->Group
; });
1445 if (crosser
!= LastInstall
)
1447 crossgraded
.insert(I
->Pkg
->ID
);
1448 toCrossgrade
.emplace_back(&(*I
), crosser
->Pkg
.FullName());
1452 for (auto I
= PlanedEnd
; I
!= List
.end(); ++I
)
1454 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1457 auto const Grp
= I
->Pkg
.Group();
1458 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1460 if (Pkg
== I
->Pkg
|| Cache
[Pkg
].Install() == false)
1462 toCrossgrade
.emplace_back(&(*I
), Pkg
.FullName());
1466 for (auto C
: toCrossgrade
)
1468 // we never do purges on packages which are crossgraded, even if "requested"
1469 if (C
.first
->Op
== Item::Purge
)
1471 C
.first
->Op
= Item::Remove
; // crossgrades should never be purged
1472 auto && Purges
= approvedStates
.Purge();
1473 auto const Ver
= std::find_if(
1474 #if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
1475 Purges
.cbegin(), Purges
.cend(),
1477 Purges
.begin(), Purges
.end(),
1479 [&C
](pkgCache::VerIterator
const &V
) { return V
.ParentPkg() == C
.first
->Pkg
; });
1480 approvedStates
.Remove(*Ver
);
1482 auto && RemOp
= PackageOps
[C
.first
->Pkg
.FullName()];
1483 if (RemOp
.size() == 5)
1485 RemOp
.erase(std::next(RemOp
.begin(), 3), RemOp
.end());
1489 _error
->Warning("Unexpected amount of planned ops for package %s: %lu", C
.first
->Pkg
.FullName().c_str(), RemOp
.size());
1492 if (crossgraded
.empty() == false)
1494 auto const oldsize
= List
.size();
1495 List
.erase(std::remove_if(List
.begin(), PlanedEnd
,
1496 [&crossgraded
](Item
const &i
){
1497 return (i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
) &&
1498 crossgraded
.find(i
.Pkg
->ID
) != crossgraded
.end();
1500 explicitIdx
-= (oldsize
- List
.size());
1504 APT::StateChanges currentStates
;
1505 if (_config
->FindB("dpkg::selection::current::saveandrestore", true))
1507 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1508 if (Pkg
->CurrentVer
== 0)
1510 else if (Pkg
->SelectedState
== pkgCache::State::Purge
)
1511 currentStates
.Purge(FindToBeRemovedVersion(Pkg
));
1512 else if (Pkg
->SelectedState
== pkgCache::State::DeInstall
)
1513 currentStates
.Remove(FindToBeRemovedVersion(Pkg
));
1514 if (currentStates
.empty() == false)
1516 APT::StateChanges cleanStates
;
1517 for (auto && P
: currentStates
.Remove())
1518 cleanStates
.Install(P
);
1519 for (auto && P
: currentStates
.Purge())
1520 cleanStates
.Install(P
);
1521 if (cleanStates
.Save(false) == false)
1522 return _error
->Error("Couldn't clean the currently selected dpkg states");
1526 if (_config
->FindB("dpkg::selection::remove::approved", true))
1528 if (approvedStates
.Save(false) == false)
1530 _error
->Error("Couldn't record the approved state changes as dpkg selection states");
1531 if (currentStates
.Save(false) == false)
1532 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1536 List
.erase(std::next(List
.begin(), explicitIdx
), List
.end());
1538 std::vector
<bool> toBeRemoved(Cache
.Head().PackageCount
, false);
1539 for (auto && I
: approvedStates
.Remove())
1540 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1541 for (auto && I
: approvedStates
.Purge())
1542 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1544 for (auto && I
: List
)
1545 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1546 toBeRemoved
[I
.Pkg
->ID
] = false;
1548 if (std::find(toBeRemoved
.begin(), toBeRemoved
.end(), true) != toBeRemoved
.end())
1549 List
.emplace_back(Item::RemovePending
, pkgCache::PkgIterator());
1550 if (approvedStates
.Purge().empty() == false)
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
)
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 StripAlreadyDoneFromPending(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 StripAlreadyDoneFromPending(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 StripAlreadyDoneFromPending(approvedStates
.Remove());
1972 StripAlreadyDoneFromPending(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!");
1982 if (currentStates
.Save(false) == false)
1983 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1985 if (pkgPackageManager::SigINTStop
)
1986 _error
->Warning(_("Operation was interrupted before it could finish"));
1988 if (noopDPkgInvocation
== false)
1990 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1991 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1992 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1994 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1995 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1997 _error
->PushToStack();
1998 pkgCacheFile CacheFile
;
1999 CacheFile
.BuildCaches(NULL
, true);
2000 _error
->RevertToStack();
2005 // disappearing packages can forward their auto-bit
2006 if (disappearedPkgs
.empty() == false)
2007 Cache
.writeStateFile(NULL
);
2009 d
->progress
->Stop();
2011 if (RunScripts("DPkg::Post-Invoke") == false)
2014 return d
->dpkg_error
.empty();
2017 void SigINT(int /*sig*/) {
2018 pkgPackageManager::SigINTStop
= true;
2021 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
2022 // ---------------------------------------------------------------------
2024 void pkgDPkgPM::Reset()
2026 List
.erase(List
.begin(),List
.end());
2029 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
2030 // ---------------------------------------------------------------------
2032 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
2034 // If apport doesn't exist or isn't installed do nothing
2035 // This e.g. prevents messages in 'universes' without apport
2036 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
2037 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
2040 string pkgname
, reportfile
, pkgver
, arch
;
2041 string::size_type pos
;
2044 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
2046 std::clog
<< "configured to not write apport reports" << std::endl
;
2050 // only report the first errors
2051 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
2053 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
2057 // check if its not a follow up error
2058 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
2059 if(strstr(errormsg
, needle
) != NULL
) {
2060 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
2064 // do not report disk-full failures
2065 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
2066 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
2070 // do not report out-of-memory failures
2071 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
2072 strstr(errormsg
, "failed to allocate memory") != NULL
) {
2073 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
2077 // do not report bugs regarding inaccessible local files
2078 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
2079 strstr(errormsg
, "cannot access archive") != NULL
) {
2080 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2084 // do not report errors encountered when decompressing packages
2085 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
2086 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2090 // do not report dpkg I/O errors, this is a format string, so we compare
2091 // the prefix and the suffix of the error with the dpkg error message
2092 vector
<string
> io_errors
;
2093 io_errors
.push_back(string("failed to read"));
2094 io_errors
.push_back(string("failed to write"));
2095 io_errors
.push_back(string("failed to seek"));
2096 io_errors
.push_back(string("unexpected end of file or stream"));
2098 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
2100 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
2101 if (list
.size() > 1) {
2102 // we need to split %s, VectorizeString only allows char so we need
2103 // to kill the "s" manually
2104 if (list
[1].size() > 1) {
2105 list
[1].erase(0, 1);
2106 if(strstr(errormsg
, list
[0].c_str()) &&
2107 strstr(errormsg
, list
[1].c_str())) {
2108 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
2115 // get the pkgname and reportfile
2116 pkgname
= flNotDir(pkgpath
);
2117 pos
= pkgname
.find('_');
2118 if(pos
!= string::npos
)
2119 pkgname
= pkgname
.substr(0, pos
);
2121 // find the package version and source package name
2122 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
2123 if (Pkg
.end() == true)
2125 if (pos
== std::string::npos
|| _config
->FindB("dpkg::install::recursive::numbered", true) == false)
2127 auto const dash
= pkgname
.find_first_not_of("0123456789");
2128 if (dash
== std::string::npos
|| pkgname
[dash
] != '-')
2130 pkgname
.erase(0, dash
+ 1);
2131 Pkg
= Cache
.FindPkg(pkgname
);
2132 if (Pkg
.end() == true)
2135 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
2136 if (Ver
.end() == true)
2138 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
2140 // if the file exists already, we check:
2141 // - if it was reported already (touched by apport).
2142 // If not, we do nothing, otherwise
2143 // we overwrite it. This is the same behaviour as apport
2144 // - if we have a report with the same pkgversion already
2146 _config
->CndSet("Dir::Apport", "var/crash");
2147 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
2148 if(FileExists(reportfile
))
2153 // check atime/mtime
2154 stat(reportfile
.c_str(), &buf
);
2155 if(buf
.st_mtime
> buf
.st_atime
)
2158 // check if the existing report is the same version
2159 report
= fopen(reportfile
.c_str(),"r");
2160 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
2162 if(strstr(strbuf
,"Package:") == strbuf
)
2164 char pkgname
[255], version
[255];
2165 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
2166 if(strcmp(pkgver
.c_str(), version
) == 0)
2176 // now write the report
2177 arch
= _config
->Find("APT::Architecture");
2178 report
= fopen(reportfile
.c_str(),"w");
2181 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
2182 chmod(reportfile
.c_str(), 0);
2184 chmod(reportfile
.c_str(), 0600);
2185 fprintf(report
, "ProblemType: Package\n");
2186 fprintf(report
, "Architecture: %s\n", arch
.c_str());
2187 time_t now
= time(NULL
);
2188 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
2189 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
2190 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
2191 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
2192 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
2194 // ensure that the log is flushed
2196 fflush(d
->term_out
);
2198 // attach terminal log it if we have it
2199 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
2200 if (!logfile_name
.empty())
2204 fprintf(report
, "DpkgTerminalLog:\n");
2205 log
= fopen(logfile_name
.c_str(),"r");
2209 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2210 fprintf(report
, " %s", buf
);
2211 fprintf(report
, " \n");
2216 // attach history log it if we have it
2217 string histfile_name
= _config
->FindFile("Dir::Log::History");
2218 if (!histfile_name
.empty())
2220 fprintf(report
, "DpkgHistoryLog:\n");
2221 FILE* log
= fopen(histfile_name
.c_str(),"r");
2225 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2226 fprintf(report
, " %s", buf
);
2231 // log the ordering, see dpkgpm.h and the "Ops" enum there
2232 fprintf(report
, "AptOrdering:\n");
2233 for (auto && I
: List
)
2235 char const * opstr
= nullptr;
2238 case Item::Install
: opstr
= "Install"; break;
2239 case Item::Configure
: opstr
= "Configure"; break;
2240 case Item::Remove
: opstr
= "Remove"; break;
2241 case Item::Purge
: opstr
= "Purge"; break;
2242 case Item::ConfigurePending
: opstr
= "ConfigurePending"; break;
2243 case Item::TriggersPending
: opstr
= "TriggersPending"; break;
2244 case Item::RemovePending
: opstr
= "RemovePending"; break;
2245 case Item::PurgePending
: opstr
= "PurgePending"; break;
2247 auto const pkgname
= I
.Pkg
.end() ? "NULL" : I
.Pkg
.FullName();
2248 fprintf(report
, " %s: %s\n", pkgname
.c_str(), opstr
);
2251 // attach dmesg log (to learn about segfaults)
2252 if (FileExists("/bin/dmesg"))
2254 fprintf(report
, "Dmesg:\n");
2255 FILE *log
= popen("/bin/dmesg","r");
2259 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2260 fprintf(report
, " %s", buf
);
2265 // attach df -l log (to learn about filesystem status)
2266 if (FileExists("/bin/df"))
2269 fprintf(report
, "Df:\n");
2270 FILE *log
= popen("/bin/df -l","r");
2274 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2275 fprintf(report
, " %s", buf
);