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>
64 extern char **environ
;
68 APT_PURE
static string
AptHistoryRequestingUser() /*{{{*/
70 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
72 for (const auto &Key
: EnvKeys
)
74 if (getenv(Key
) != nullptr)
76 int uid
= atoi(getenv(Key
));
79 struct passwd
*result
;
81 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
83 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
92 APT_PURE
static unsigned int EnvironmentSize() /*{{{*/
94 unsigned int size
= 0;
95 char **envp
= environ
;
98 size
+= strlen (*envp
++) + 1;
103 class pkgDPkgPMPrivate
/*{{{*/
106 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
107 term_out(NULL
), history_out(NULL
),
108 progress(NULL
), tt_is_valid(false), master(-1),
109 slave(NULL
), protect_slave_from_dying(-1),
117 bool stdin_is_dev_null
;
118 // the buffer we use for the dpkg status-fd reading
124 APT::Progress::PackageManager
*progress
;
131 int protect_slave_from_dying
;
135 sigset_t original_sigmask
;
142 // Maps the dpkg "processing" info to human readable names. Entry 0
143 // of each array is the key, entry 1 is the value.
144 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
145 std::make_pair("install", N_("Installing %s")),
146 std::make_pair("configure", N_("Configuring %s")),
147 std::make_pair("remove", N_("Removing %s")),
148 std::make_pair("purge", N_("Completely removing %s")),
149 std::make_pair("disappear", N_("Noting disappearance of %s")),
150 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
153 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
154 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
156 // Predicate to test whether an entry in the PackageProcessingOps
157 // array matches a string.
158 class MatchProcessingOp
163 explicit MatchProcessingOp(const char *the_target
)
168 bool operator()(const std::pair
<const char *, const char *> &pair
) const
170 return strcmp(pair
.first
, target
) == 0;
175 // ionice - helper function to ionice the given PID /*{{{*/
176 /* there is no C header for ionice yet - just the syscall interface
177 so we use the binary from util-linux */
178 static bool ionice(int PID
)
180 if (!FileExists("/usr/bin/ionice"))
182 pid_t Process
= ExecFork();
186 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
188 Args
[0] = "/usr/bin/ionice";
192 execv(Args
[0], (char **)Args
);
194 return ExecWait(Process
, "ionice");
197 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
198 // ---------------------------------------------------------------------
199 /* This is helpful when a package is no longer installed but has residual
203 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
205 pkgCache::VerIterator Ver
;
206 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
207 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
208 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
210 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
216 static pkgCache::VerIterator
FindToBeRemovedVersion(pkgCache::PkgIterator
const &Pkg
)/*{{{*/
218 auto const PV
= Pkg
.CurrentVer();
219 if (PV
.end() == false)
221 return FindNowVersion(Pkg
);
225 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
226 // ---------------------------------------------------------------------
228 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
229 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
233 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
234 // ---------------------------------------------------------------------
236 pkgDPkgPM::~pkgDPkgPM()
241 // DPkgPM::Install - Install a package /*{{{*/
242 // ---------------------------------------------------------------------
243 /* Add an install operation to the sequence list */
244 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
246 if (File
.empty() == true || Pkg
.end() == true)
247 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
249 // If the filename string begins with DPkg::Chroot-Directory, return the
250 // substr that is within the chroot so dpkg can access it.
251 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
252 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
254 size_t len
= chrootdir
.length();
255 if (chrootdir
.at(len
- 1) == '/')
257 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
260 List
.push_back(Item(Item::Install
,Pkg
,File
));
265 // DPkgPM::Configure - Configure a package /*{{{*/
266 // ---------------------------------------------------------------------
267 /* Add a configure operation to the sequence list */
268 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
270 if (Pkg
.end() == true)
273 List
.push_back(Item(Item::Configure
, Pkg
));
275 // Use triggers for config calls if we configure "smart"
276 // as otherwise Pre-Depends will not be satisfied, see #526774
277 if (_config
->FindB("DPkg::TriggersPending", false) == true)
278 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
283 // DPkgPM::Remove - Remove a package /*{{{*/
284 // ---------------------------------------------------------------------
285 /* Add a remove operation to the sequence list */
286 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
288 if (Pkg
.end() == true)
292 List
.push_back(Item(Item::Purge
,Pkg
));
294 List
.push_back(Item(Item::Remove
,Pkg
));
298 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
299 // ---------------------------------------------------------------------
300 /* This is part of the helper script communication interface, it sends
301 very complete information down to the other end of the pipe.*/
302 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
304 return SendPkgsInfo(F
, 2);
306 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
308 // This version of APT supports only v3, so don't sent higher versions
310 fprintf(F
,"VERSION %u\n", Version
);
312 fprintf(F
,"VERSION 3\n");
314 /* Write out all of the configuration directives by walking the
315 configuration tree */
316 const Configuration::Item
*Top
= _config
->Tree(0);
319 if (Top
->Value
.empty() == false)
322 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
323 QuoteString(Top
->Value
,"\n").c_str());
332 while (Top
!= 0 && Top
->Next
== 0)
339 // Write out the package actions in order.
340 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
342 if(I
->Pkg
.end() == true)
345 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
347 fprintf(F
,"%s ",I
->Pkg
.Name());
349 // Current version which we are going to replace
350 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
351 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
352 CurVer
= FindNowVersion(I
->Pkg
);
354 if (CurVer
.end() == true)
359 fprintf(F
, "- - none ");
363 fprintf(F
, "%s ", CurVer
.VerStr());
365 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
368 // Show the compare operator between current and install version
369 if (S
.InstallVer
!= 0)
371 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
373 if (CurVer
.end() == false)
374 Comp
= InstVer
.CompareVer(CurVer
);
381 fprintf(F
, "%s ", InstVer
.VerStr());
383 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
390 fprintf(F
, "> - - none ");
393 // Show the filename/operation
394 if (I
->Op
== Item::Install
)
397 if (I
->File
[0] != '/')
398 fprintf(F
,"**ERROR**\n");
400 fprintf(F
,"%s\n",I
->File
.c_str());
402 else if (I
->Op
== Item::Configure
)
403 fprintf(F
,"**CONFIGURE**\n");
404 else if (I
->Op
== Item::Remove
||
405 I
->Op
== Item::Purge
)
406 fprintf(F
,"**REMOVE**\n");
414 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
415 // ---------------------------------------------------------------------
416 /* This looks for a list of scripts to run from the configuration file
417 each one is run and is fed on standard input a list of all .deb files
418 that are due to be installed. */
419 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
423 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
424 if (Opts
== 0 || Opts
->Child
== 0)
428 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
429 sighandler_t old_sigint
= signal(SIGINT
, SIG_IGN
);
430 sighandler_t old_sigquit
= signal(SIGQUIT
, SIG_IGN
);
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
);
531 signal(SIGQUIT
, old_sigquit
);
536 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
537 // ---------------------------------------------------------------------
540 void pkgDPkgPM::DoStdin(int master
)
542 unsigned char input_buf
[256] = {0,};
543 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
545 FileFd::Write(master
, input_buf
, len
);
547 d
->stdin_is_dev_null
= true;
550 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
551 // ---------------------------------------------------------------------
553 * read the terminal pty and write log
555 void pkgDPkgPM::DoTerminalPty(int master
)
557 unsigned char term_buf
[1024] = {0,0, };
559 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
560 if(len
== -1 && errno
== EIO
)
562 // this happens when the child is about to exit, we
563 // give it time to actually exit, otherwise we run
564 // into a race so we sleep for half a second.
565 struct timespec sleepfor
= { 0, 500000000 };
566 nanosleep(&sleepfor
, NULL
);
571 FileFd::Write(1, term_buf
, len
);
573 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
576 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
577 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
579 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
581 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
583 /* dpkg sends strings like this:
584 'status: <pkg>: <pkg qstate>'
585 'status: <pkg>:<arch>: <pkg qstate>'
587 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
588 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
591 // we need to split on ": " (note the appended space) as the ':' is
592 // part of the pkgname:arch information that dpkg sends
594 // A dpkg error message may contain additional ":" (like
595 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
596 // so we need to ensure to not split too much
597 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
601 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
605 // build the (prefix, pkgname, action) tuple, position of this
606 // is different for "processing" or "status" messages
607 std::string prefix
= APT::String::Strip(list
[0]);
611 // "processing" has the form "processing: action: pkg or trigger"
612 // with action = ["install", "upgrade", "configure", "remove", "purge",
613 // "disappear", "trigproc"]
614 if (prefix
== "processing")
616 pkgname
= APT::String::Strip(list
[2]);
617 action
= APT::String::Strip(list
[1]);
618 // we don't care for the difference (as dpkg doesn't really either)
619 if (action
== "upgrade")
622 // "status" has the form: "status: pkg: state"
623 // with state in ["half-installed", "unpacked", "half-configured",
624 // "installed", "config-files", "not-installed"]
625 else if (prefix
== "status")
627 pkgname
= APT::String::Strip(list
[1]);
628 action
= APT::String::Strip(list
[2]);
631 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
636 /* handle the special cases first:
638 errors look like this:
639 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
640 and conffile-prompt like this
641 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
643 if (prefix
== "status")
645 if(action
== "error")
647 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
650 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
653 else if(action
== "conffile-prompt")
655 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
661 // at this point we know that we should have a valid pkgname, so build all
664 // dpkg does not always send "pkgname:arch" so we add it here if needed
665 if (pkgname
.find(":") == std::string::npos
)
667 // find the package in the group that is touched by dpkg
668 // if there are multiple pkgs dpkg would send us a full pkgname:arch
669 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
670 if (Grp
.end() == false)
671 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
672 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
674 auto fullname
= P
.FullName();
675 if (Cache
[P
].Delete() && PackageOps
[fullname
].size() <= PackageOpsDone
[fullname
])
677 pkgname
= std::move(fullname
);
682 std::string arch
= "";
683 if (pkgname
.find(":") != string::npos
)
684 arch
= StringSplit(pkgname
, ":")[1];
685 std::string i18n_pkgname
= pkgname
;
686 if (arch
.size() != 0)
687 strprintf(i18n_pkgname
, "%s (%s)", StringSplit(pkgname
, ":")[0].c_str(), arch
.c_str());
689 // 'processing' from dpkg looks like
690 // 'processing: action: pkg'
691 if(prefix
== "processing")
693 const std::pair
<const char *, const char *> * const iter
=
694 std::find_if(PackageProcessingOpsBegin
,
695 PackageProcessingOpsEnd
,
696 MatchProcessingOp(action
.c_str()));
697 if(iter
== PackageProcessingOpsEnd
)
700 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
704 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
705 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
707 // FIXME: this needs a muliarch testcase
708 // FIXME2: is "pkgname" here reliable with dpkg only sending us
710 if (action
== "disappear")
711 handleDisappearAction(pkgname
);
715 if (prefix
== "status")
717 std::vector
<struct DpkgState
> &states
= PackageOps
[pkgname
];
718 if (action
== "triggers-pending")
721 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
722 << " action: " << action
<< " (prefix 2 to "
723 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
725 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
726 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
729 else if(PackageOpsDone
[pkgname
] < states
.size())
731 char const * next_action
= states
[PackageOpsDone
[pkgname
]].state
;
735 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
736 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
739 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
740 << " pending trigger defused by unpack" << std::endl;
741 // unpacking a package defuses the pending trigger
742 PackageOpsDone[pkg] += 2;
744 next_action = states[PackageOpsDone[pkg]].state;
748 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
749 << " action: " << action
<< " (expected: '" << next_action
<< "' "
750 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
752 // check if the package moved to the next dpkg state
753 if(action
== next_action
)
755 // only read the translation if there is actually a next action
756 char const * const translation
= _(states
[PackageOpsDone
[pkgname
]].str
);
758 // we moved from one dpkg state to a new one, report that
759 ++PackageOpsDone
[pkgname
];
763 strprintf(msg
, translation
, i18n_pkgname
.c_str());
764 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
766 else if (action
== "unpacked" && strcmp(next_action
, "config-files") == 0)
768 // in a crossgrade what looked like a remove first is really an unpack over it
769 ++PackageOpsDone
[pkgname
];
772 auto const Pkg
= Cache
.FindPkg(pkgname
);
773 if (likely(Pkg
.end() == false))
775 auto const Grp
= Pkg
.Group();
776 if (likely(Grp
.end() == false))
778 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
779 if(Cache
[P
].Install())
781 auto && Ops
= PackageOps
[P
.FullName()];
782 auto const unpackOp
= std::find_if(Ops
.cbegin(), Ops
.cend(), [](DpkgState
const &s
) { return strcmp(s
.state
, "unpacked") == 0; });
783 if (unpackOp
!= Ops
.cend())
785 auto const skipped
= std::distance(Ops
.cbegin(), unpackOp
);
786 PackagesDone
+= skipped
;
787 PackageOpsDone
[P
.FullName()] += skipped
;
799 // DPkgPM::handleDisappearAction /*{{{*/
800 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
802 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
803 if (unlikely(Pkg
.end() == true))
806 // record the package name for display and stuff later
807 disappearedPkgs
.insert(Pkg
.FullName(true));
809 // the disappeared package was auto-installed - nothing to do
810 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
812 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
813 if (unlikely(PkgVer
.end() == true))
815 /* search in the list of dependencies for (Pre)Depends,
816 check if this dependency has a Replaces on our package
817 and if so transfer the manual installed flag to it */
818 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
820 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
821 Dep
->Type
!= pkgCache::Dep::PreDepends
)
823 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
824 if (unlikely(Tar
.end() == true))
826 // the package is already marked as manual
827 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
829 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
830 if (TarVer
.end() == true)
832 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
834 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
836 if (Pkg
!= Rep
.TargetPkg())
838 // okay, they are strongly connected - transfer manual-bit
840 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
841 Cache
[Tar
].Flags
&= ~Flag::Auto
;
847 // DPkgPM::DoDpkgStatusFd /*{{{*/
848 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
850 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
851 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
854 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
856 // process line by line from the buffer
857 char *p
= d
->dpkgbuf
, *q
= nullptr;
858 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
861 ProcessDpkgStatusLine(p
);
862 p
= q
+ 1; // continue with next line
865 // check if we stripped the buffer clean
866 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
872 // otherwise move the unprocessed tail to the start and update pos
873 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
874 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
877 // DPkgPM::WriteHistoryTag /*{{{*/
878 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
880 size_t const length
= value
.length();
883 // poor mans rstrip(", ")
884 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
885 value
.erase(length
- 2, 2);
886 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
888 // DPkgPM::OpenLog /*{{{*/
889 bool pkgDPkgPM::OpenLog()
891 string
const logdir
= _config
->FindDir("Dir::Log");
892 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
893 // FIXME: use a better string after freeze
894 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
898 time_t const t
= time(NULL
);
900 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
901 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
904 string
const logfile_name
= flCombine(logdir
,
905 _config
->Find("Dir::Log::Terminal"));
906 if (!logfile_name
.empty())
908 d
->term_out
= fopen(logfile_name
.c_str(),"a");
909 if (d
->term_out
== NULL
)
910 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
911 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
912 SetCloseExec(fileno(d
->term_out
), true);
913 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
915 struct passwd
*pw
= getpwnam("root");
916 struct group
*gr
= getgrnam("adm");
917 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
918 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
920 if (chmod(logfile_name
.c_str(), 0640) != 0)
921 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
922 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
925 // write your history
926 string
const history_name
= flCombine(logdir
,
927 _config
->Find("Dir::Log::History"));
928 if (!history_name
.empty())
930 d
->history_out
= fopen(history_name
.c_str(),"a");
931 if (d
->history_out
== NULL
)
932 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
933 SetCloseExec(fileno(d
->history_out
), true);
934 chmod(history_name
.c_str(), 0644);
935 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
936 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
937 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
939 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
941 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
942 if (Cache
[I
].NewInstall() == true)
943 HISTORYINFO(install
, CANDIDATE_AUTO
)
944 else if (Cache
[I
].ReInstall() == true)
945 HISTORYINFO(reinstall
, CANDIDATE
)
946 else if (Cache
[I
].Upgrade() == true)
947 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
948 else if (Cache
[I
].Downgrade() == true)
949 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
950 else if (Cache
[I
].Delete() == true)
951 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
955 line
->append(I
.FullName(false)).append(" (");
956 switch (infostring
) {
957 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
959 line
->append(Cache
[I
].CandVersion
);
960 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
961 line
->append(", automatic");
963 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
964 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
968 if (_config
->Exists("Commandline::AsString") == true)
969 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
970 std::string RequestingUser
= AptHistoryRequestingUser();
971 if (RequestingUser
!= "")
972 WriteHistoryTag("Requested-By", RequestingUser
);
973 WriteHistoryTag("Install", install
);
974 WriteHistoryTag("Reinstall", reinstall
);
975 WriteHistoryTag("Upgrade", upgrade
);
976 WriteHistoryTag("Downgrade",downgrade
);
977 WriteHistoryTag("Remove",remove
);
978 WriteHistoryTag("Purge",purge
);
979 fflush(d
->history_out
);
985 // DPkg::CloseLog /*{{{*/
986 bool pkgDPkgPM::CloseLog()
989 time_t t
= time(NULL
);
991 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
992 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
996 fprintf(d
->term_out
, "Log ended: ");
997 fprintf(d
->term_out
, "%s", timestr
);
998 fprintf(d
->term_out
, "\n");
1005 if (disappearedPkgs
.empty() == false)
1008 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
1009 d
!= disappearedPkgs
.end(); ++d
)
1011 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
1012 disappear
.append(*d
);
1013 if (P
.end() == true)
1014 disappear
.append(", ");
1016 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
1018 WriteHistoryTag("Disappeared", disappear
);
1020 if (d
->dpkg_error
.empty() == false)
1021 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
1022 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
1023 fclose(d
->history_out
);
1025 d
->history_out
= NULL
;
1031 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1032 void pkgDPkgPM::BuildPackagesProgressMap()
1034 // map the dpkg states to the operations that are performed
1035 // (this is sorted in the same way as Item::Ops)
1036 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1037 // Install operation
1039 {"half-installed", N_("Preparing %s")},
1040 {"unpacked", N_("Unpacking %s") },
1043 // Configure operation
1045 {"unpacked",N_("Preparing to configure %s") },
1046 {"half-configured", N_("Configuring %s") },
1047 { "installed", N_("Installed %s")},
1051 {"half-configured", N_("Preparing for removal of %s")},
1052 {"half-installed", N_("Removing %s")},
1053 {"config-files", N_("Removed %s")},
1057 {"config-files", N_("Preparing to completely remove %s")},
1058 {"not-installed", N_("Completely removed %s")},
1062 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1064 // init the PackageOps map, go over the list of packages that
1065 // that will be [installed|configured|removed|purged] and add
1066 // them to the PackageOps map (the dpkg states it goes through)
1067 // and the PackageOpsTranslations (human readable strings)
1068 for (auto &&I
: List
)
1070 if(I
.Pkg
.end() == true)
1073 string
const name
= I
.Pkg
.FullName();
1074 PackageOpsDone
[name
] = 0;
1075 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1076 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1078 // purging a package which is installed first passes through remove states
1079 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1080 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1081 PackagesTotal
+= DpkgOps
.size();
1083 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1084 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1085 if (state
.state
== nullptr)
1091 /* one extra: We don't want the progress bar to reach 100%, especially not
1092 if we call dpkg --configure --pending and process a bunch of triggers
1093 while showing 100%. Also, spindown takes a while, so never reaching 100%
1094 is way more correct than reaching 100% while still doing stuff even if
1095 doing it this way is slightly bending the rules */
1099 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1101 APT::Progress::PackageManager
*progress
= NULL
;
1103 progress
= APT::Progress::PackageManagerProgressFactory();
1105 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1107 return Go(progress
);
1110 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1112 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1115 if (d
->slave
!= NULL
)
1121 if (isatty(STDIN_FILENO
) == 0)
1122 d
->direct_stdin
= true;
1124 _error
->PushToStack();
1126 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1127 if (d
->master
== -1)
1128 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1129 else if (unlockpt(d
->master
) == -1)
1130 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1133 #ifdef HAVE_PTSNAME_R
1134 char slave_name
[64]; // 64 is used by bionic
1135 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1137 char const * const slave_name
= ptsname(d
->master
);
1138 if (slave_name
== NULL
)
1140 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1143 d
->slave
= strdup(slave_name
);
1144 if (d
->slave
== NULL
)
1145 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1146 else if (grantpt(d
->master
) == -1)
1147 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1148 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1150 d
->tt_is_valid
= true;
1151 struct termios raw_tt
;
1152 // copy window size of stdout if its a 'good' terminal
1153 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1156 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1157 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1158 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1159 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1161 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1162 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1166 raw_tt
.c_lflag
&= ~ECHO
;
1167 raw_tt
.c_lflag
|= ISIG
;
1168 // block SIGTTOU during tcsetattr to prevent a hang if
1169 // the process is a member of the background process group
1170 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1171 sigemptyset(&d
->sigmask
);
1172 sigaddset(&d
->sigmask
, SIGTTOU
);
1173 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1174 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1175 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1176 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1179 if (d
->slave
!= NULL
)
1181 /* on linux, closing (and later reopening) all references to the slave
1182 makes the slave a death end, so we open it here to have one open all
1183 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1184 on kfreebsd we get an incorrect ("step like") output then while it has
1185 no problem with closing all references… so to avoid platform specific
1186 code here we combine both and be happy once more */
1187 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1192 if (_error
->PendingError() == true)
1194 if (d
->master
!= -1)
1199 if (d
->slave
!= NULL
)
1204 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1206 _error
->RevertToStack();
1209 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1211 if(d
->master
== -1 || d
->slave
== NULL
)
1214 if (close(d
->master
) == -1)
1215 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1218 _error
->FatalE("setsid", "Starting a new session for child failed!");
1220 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1222 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1223 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1224 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1227 unsigned short i
= 0;
1228 if (d
->direct_stdin
== true)
1231 if (dup2(slaveFd
, i
) == -1)
1232 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1234 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1235 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1242 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1244 if (d
->slave
!= NULL
)
1247 if (d
->protect_slave_from_dying
!= -1)
1249 close(d
->protect_slave_from_dying
);
1250 d
->protect_slave_from_dying
= -1;
1254 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1255 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1261 static void cleanUpTmpDir(char * const tmpdir
) /*{{{*/
1263 if (tmpdir
== nullptr)
1265 DIR * const D
= opendir(tmpdir
);
1267 _error
->Errno("opendir", _("Unable to read %s"), tmpdir
);
1270 auto const dfd
= dirfd(D
);
1271 for (struct dirent
*Ent
= readdir(D
); Ent
!= nullptr; Ent
= readdir(D
))
1273 if (Ent
->d_name
[0] == '.')
1275 #ifdef _DIRENT_HAVE_D_TYPE
1276 if (unlikely(Ent
->d_type
!= DT_LNK
&& Ent
->d_type
!= DT_UNKNOWN
))
1279 if (unlikely(unlinkat(dfd
, Ent
->d_name
, 0) != 0))
1289 // DPkgPM::Go - Run the sequence /*{{{*/
1290 // ---------------------------------------------------------------------
1291 /* This globs the operations and calls dpkg
1293 * If it is called with a progress object apt will report the install
1294 * progress to this object. It maps the dpkg states a package goes
1295 * through to human readable (and i10n-able)
1296 * names and calculates a percentage for each step.
1298 static bool ItemIsEssential(pkgDPkgPM::Item
const &I
)
1300 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1301 if (cachegen
== "none" || cachegen
== "native")
1303 if (unlikely(I
.Pkg
.end()))
1305 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1307 bool pkgDPkgPM::ExpandPendingCalls(std::vector
<Item
> &List
, pkgDepCache
&Cache
)
1310 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyRemoved
;
1311 for (auto && I
: List
)
1312 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1313 alreadyRemoved
.insert(I
.Pkg
->ID
);
1314 std::remove_reference
<decltype(List
)>::type AppendList
;
1315 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1316 if (Cache
[Pkg
].Delete() && alreadyRemoved
.insert(Pkg
->ID
).second
== true)
1317 AppendList
.emplace_back(Cache
[Pkg
].Purge() ? Item::Purge
: Item::Remove
, Pkg
);
1318 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1321 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyConfigured
;
1322 for (auto && I
: List
)
1323 if (I
.Op
== Item::Configure
)
1324 alreadyConfigured
.insert(I
.Pkg
->ID
);
1325 std::remove_reference
<decltype(List
)>::type AppendList
;
1326 for (auto && I
: List
)
1327 if (I
.Op
== Item::Install
&& alreadyConfigured
.insert(I
.Pkg
->ID
).second
== true)
1328 AppendList
.emplace_back(Item::Configure
, I
.Pkg
);
1329 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1330 if (Pkg
.State() == pkgCache::PkgIterator::NeedsConfigure
&& alreadyConfigured
.insert(Pkg
->ID
).second
== true)
1331 AppendList
.emplace_back(Item::Configure
, Pkg
);
1332 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1336 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1338 // explicitely remove&configure everything for hookscripts and progress building
1339 // we need them only temporarily through, so keep the length and erase afterwards
1340 decltype(List
)::const_iterator::difference_type explicitIdx
=
1341 std::distance(List
.cbegin(), List
.cend());
1342 ExpandPendingCalls(List
, Cache
);
1344 auto const StripAlreadyDoneFromPending
= [&](APT::VersionVector
& Pending
) {
1345 Pending
.erase(std::remove_if(Pending
.begin(), Pending
.end(), [&](pkgCache::VerIterator
const &Ver
) {
1346 auto const PN
= Ver
.ParentPkg().FullName();
1347 return PackageOps
[PN
].size() <= PackageOpsDone
[PN
];
1351 pkgPackageManager::SigINTStop
= false;
1352 d
->progress
= progress
;
1354 // Generate the base argument list for dpkg
1355 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1356 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1357 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1358 [](std::string
const &s
) { return s
.c_str(); });
1359 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1360 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1361 size_t const BaseArgs
= Args
.size();
1366 // try to figure out the max environment size
1367 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1370 OSArgMax
-= EnvironmentSize() - 2*1024;
1371 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1372 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", true);
1374 if (RunScripts("DPkg::Pre-Invoke") == false)
1377 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1380 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1381 // store auto-bits as they are supposed to be after dpkg is run
1382 if (noopDPkgInvocation
== false)
1383 Cache
.writeStateFile(NULL
);
1385 bool dpkg_recursive_install
= _config
->FindB("dpkg::install::recursive", false);
1386 if (_config
->FindB("dpkg::install::recursive::force", false) == false)
1388 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1389 auto const dpkgpkg
= Cache
.FindPkg("dpkg");
1390 if (likely(dpkgpkg
.end() == false && dpkgpkg
->CurrentVer
!= 0))
1391 dpkg_recursive_install
= Cache
.VS().CmpVersion("1.18.5", dpkgpkg
.CurrentVer().VerStr()) <= 0;
1393 // no point in doing this dance for a handful of packages only
1394 unsigned int const dpkg_recursive_install_min
= _config
->FindB("dpkg::install::recursive::minimum", 5);
1395 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1396 bool const dpkg_recursive_install_numbered
= _config
->FindB("dpkg::install::recursive::numbered", true);
1399 BuildPackagesProgressMap();
1401 APT::StateChanges approvedStates
;
1402 if (_config
->FindB("dpkg::selection::remove::approved", true))
1404 for (auto && I
: List
)
1405 if (I
.Op
== Item::Purge
)
1406 approvedStates
.Purge(FindToBeRemovedVersion(I
.Pkg
));
1407 else if (I
.Op
== Item::Remove
)
1408 approvedStates
.Remove(FindToBeRemovedVersion(I
.Pkg
));
1411 // Skip removes if we install another architecture of this package soon (crossgrade)
1412 // We can't just skip them all the time as it could be an ordering requirement [of another package]
1413 if ((approvedStates
.Remove().empty() == false || approvedStates
.Purge().empty() == false) &&
1414 _config
->FindB("dpkg::remove::crossgrade::implicit", true) == true)
1416 std::unordered_set
<decltype(pkgCache::Package::ID
)> crossgraded
;
1417 std::vector
<std::pair
<Item
*, std::string
>> toCrossgrade
;
1418 auto const PlanedEnd
= std::next(List
.begin(), explicitIdx
);
1419 for (auto I
= List
.begin(); I
!= PlanedEnd
; ++I
)
1421 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1424 auto const Grp
= I
->Pkg
.Group();
1425 size_t installedInstances
= 0;
1426 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1427 if (Pkg
->CurrentVer
!= 0 || Cache
[Pkg
].Install())
1428 ++installedInstances
;
1429 if (installedInstances
== 2)
1431 auto const FirstInstall
= std::find_if_not(I
, List
.end(),
1432 [](Item
const &i
) { return i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
; });
1433 auto const LastInstall
= std::find_if_not(FirstInstall
, List
.end(),
1434 [](Item
const &i
) { return i
.Op
== Item::Install
; });
1435 auto const crosser
= std::find_if(FirstInstall
, LastInstall
,
1436 [&I
](Item
const &i
) { return i
.Pkg
->Group
== I
->Pkg
->Group
; });
1437 if (crosser
!= LastInstall
)
1439 crossgraded
.insert(I
->Pkg
->ID
);
1440 toCrossgrade
.emplace_back(&(*I
), crosser
->Pkg
.FullName());
1444 for (auto I
= PlanedEnd
; I
!= List
.end(); ++I
)
1446 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1449 auto const Grp
= I
->Pkg
.Group();
1450 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1452 if (Pkg
== I
->Pkg
|| Cache
[Pkg
].Install() == false)
1454 toCrossgrade
.emplace_back(&(*I
), Pkg
.FullName());
1458 for (auto C
: toCrossgrade
)
1460 // we never do purges on packages which are crossgraded, even if "requested"
1461 if (C
.first
->Op
== Item::Purge
)
1463 C
.first
->Op
= Item::Remove
; // crossgrades should never be purged
1464 auto && Purges
= approvedStates
.Purge();
1465 auto const Ver
= std::find_if(
1466 #if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
1467 Purges
.cbegin(), Purges
.cend(),
1469 Purges
.begin(), Purges
.end(),
1471 [&C
](pkgCache::VerIterator
const &V
) { return V
.ParentPkg() == C
.first
->Pkg
; });
1472 approvedStates
.Remove(*Ver
);
1474 auto && RemOp
= PackageOps
[C
.first
->Pkg
.FullName()];
1475 if (RemOp
.size() == 5)
1477 RemOp
.erase(std::next(RemOp
.begin(), 3), RemOp
.end());
1481 _error
->Warning("Unexpected amount of planned ops for package %s: %lu", C
.first
->Pkg
.FullName().c_str(), RemOp
.size());
1484 if (crossgraded
.empty() == false)
1486 auto const oldsize
= List
.size();
1487 List
.erase(std::remove_if(List
.begin(), PlanedEnd
,
1488 [&crossgraded
](Item
const &i
){
1489 return (i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
) &&
1490 crossgraded
.find(i
.Pkg
->ID
) != crossgraded
.end();
1492 explicitIdx
-= (oldsize
- List
.size());
1496 APT::StateChanges currentStates
;
1497 if (_config
->FindB("dpkg::selection::current::saveandrestore", true))
1499 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1500 if (Pkg
->CurrentVer
== 0)
1502 else if (Pkg
->SelectedState
== pkgCache::State::Purge
)
1503 currentStates
.Purge(FindToBeRemovedVersion(Pkg
));
1504 else if (Pkg
->SelectedState
== pkgCache::State::DeInstall
)
1505 currentStates
.Remove(FindToBeRemovedVersion(Pkg
));
1506 if (currentStates
.empty() == false)
1508 APT::StateChanges cleanStates
;
1509 for (auto && P
: currentStates
.Remove())
1510 cleanStates
.Install(P
);
1511 for (auto && P
: currentStates
.Purge())
1512 cleanStates
.Install(P
);
1513 if (cleanStates
.Save(false) == false)
1514 return _error
->Error("Couldn't clean the currently selected dpkg states");
1518 if (_config
->FindB("dpkg::selection::remove::approved", true))
1520 if (approvedStates
.Save(false) == false)
1522 _error
->Error("Couldn't record the approved state changes as dpkg selection states");
1523 if (currentStates
.Save(false) == false)
1524 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1528 List
.erase(std::next(List
.begin(), explicitIdx
), List
.end());
1530 std::vector
<bool> toBeRemoved(Cache
.Head().PackageCount
, false);
1531 for (auto && I
: approvedStates
.Remove())
1532 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1533 for (auto && I
: approvedStates
.Purge())
1534 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1536 for (auto && I
: List
)
1537 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1538 toBeRemoved
[I
.Pkg
->ID
] = false;
1540 bool const RemovePending
= std::find(toBeRemoved
.begin(), toBeRemoved
.end(), true) != toBeRemoved
.end();
1541 bool const PurgePending
= approvedStates
.Purge().empty() == false;
1542 if (RemovePending
!= false || PurgePending
!= false)
1543 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1545 List
.emplace_back(Item::RemovePending
, pkgCache::PkgIterator());
1547 List
.emplace_back(Item::PurgePending
, pkgCache::PkgIterator());
1549 // support subpressing of triggers processing for special
1550 // cases like d-i that runs the triggers handling manually
1551 if (_config
->FindB("DPkg::ConfigurePending", true))
1552 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1554 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1556 d
->stdin_is_dev_null
= false;
1561 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1563 // start pty magic before the loop
1566 // Tell the progress that its starting and fork dpkg
1567 d
->progress
->Start(d
->master
);
1569 // this loop is runs once per dpkg operation
1570 vector
<Item
>::const_iterator I
= List
.cbegin();
1571 while (I
!= List
.end())
1573 // Do all actions with the same Op in one run
1574 vector
<Item
>::const_iterator J
= I
;
1575 if (TriggersPending
== true)
1576 for (; J
!= List
.end(); ++J
)
1580 if (J
->Op
!= Item::TriggersPending
)
1582 vector
<Item
>::const_iterator T
= J
+ 1;
1583 if (T
!= List
.end() && T
->Op
== I
->Op
)
1587 else if (J
->Op
== Item::Remove
|| J
->Op
== Item::Purge
)
1588 J
= std::find_if(J
, List
.cend(), [](Item
const &I
) { return I
.Op
!= Item::Remove
&& I
.Op
!= Item::Purge
; });
1590 J
= std::find_if(J
, List
.cend(), [&J
](Item
const &I
) { return I
.Op
!= J
->Op
; });
1592 auto const size
= (J
- I
) + 10;
1594 // start with the baseset of arguments
1595 auto Size
= StartSize
;
1596 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1598 // keep track of allocated strings for multiarch package names
1599 std::vector
<char *> Packages(size
, nullptr);
1603 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1605 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1606 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1608 ADDARGC("--status-fd");
1609 char status_fd_buf
[20];
1610 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1611 ADDARG(status_fd_buf
);
1612 unsigned long const Op
= I
->Op
;
1614 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1615 (I
->Op
!= Item::ConfigurePending
|| std::next(I
) != List
.end()))
1617 ADDARGC("--no-triggers");
1624 ADDARGC("--force-depends");
1625 if (std::any_of(I
, J
, ItemIsEssential
))
1626 ADDARGC("--force-remove-essential");
1627 ADDARGC("--remove");
1630 case Item::Configure
:
1631 ADDARGC("--configure");
1634 case Item::ConfigurePending
:
1635 ADDARGC("--configure");
1636 ADDARGC("--pending");
1639 case Item::TriggersPending
:
1640 ADDARGC("--triggers-only");
1641 ADDARGC("--pending");
1644 case Item::RemovePending
:
1645 ADDARGC("--remove");
1646 ADDARGC("--pending");
1649 case Item::PurgePending
:
1651 ADDARGC("--pending");
1655 ADDARGC("--unpack");
1656 ADDARGC("--auto-deconfigure");
1660 char * tmpdir_to_free
= nullptr;
1662 // Write in the file or package names
1663 if (I
->Op
== Item::Install
)
1665 auto const installsToDo
= J
- I
;
1666 if (dpkg_recursive_install
== true && dpkg_recursive_install_min
< installsToDo
)
1669 strprintf(tmpdir
, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
1670 tmpdir_to_free
= strndup(tmpdir
.data(), tmpdir
.length());
1671 if (mkdtemp(tmpdir_to_free
) == nullptr)
1672 return _error
->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free
);
1675 for (auto c
= installsToDo
- 1; (c
= c
/10) != 0; ++p
);
1676 for (unsigned long n
= 0; I
!= J
; ++n
, ++I
)
1678 if (I
->File
[0] != '/')
1679 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1680 auto const file
= flNotDir(I
->File
);
1681 std::string linkpath
;
1682 if (dpkg_recursive_install_numbered
)
1683 strprintf(linkpath
, "%s/%.*lu-%s", tmpdir_to_free
, p
, n
, file
.c_str());
1685 strprintf(linkpath
, "%s/%s", tmpdir_to_free
, file
.c_str());
1686 if (symlink(I
->File
.c_str(), linkpath
.c_str()) != 0)
1687 return _error
->Errno("DPkg::Go", "Symlinking %s to %s failed!", I
->File
.c_str(), linkpath
.c_str());
1689 ADDARGC("--recursive");
1690 ADDARG(tmpdir_to_free
);
1694 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1696 if (I
->File
[0] != '/')
1697 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1698 Args
.push_back(I
->File
.c_str());
1699 Size
+= I
->File
.length();
1703 else if (I
->Op
== Item::RemovePending
)
1706 StripAlreadyDoneFromPending(approvedStates
.Remove());
1707 if (approvedStates
.Remove().empty())
1710 else if (I
->Op
== Item::PurgePending
)
1713 // explicit removes of packages without conffiles passthrough the purge states instantly, too.
1714 // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg
1715 StripAlreadyDoneFromPending(approvedStates
.Purge());
1716 if (approvedStates
.Purge().empty())
1718 std::remove_reference
<decltype(approvedStates
.Remove())>::type approvedRemoves
;
1719 std::swap(approvedRemoves
, approvedStates
.Remove());
1720 // we apply it again here as an explicit remove in the ordering will have cleared the purge state
1721 if (approvedStates
.Save(false) == false)
1723 _error
->Error("Couldn't record the approved purges as dpkg selection states");
1724 if (currentStates
.Save(false) == false)
1725 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1728 std::swap(approvedRemoves
, approvedStates
.Remove());
1732 string
const nativeArch
= _config
->Find("APT::Architecture");
1733 unsigned long const oldSize
= I
->Pkg
.end() == false ? Size
: 0;
1734 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1736 if((*I
).Pkg
.end() == true)
1738 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1740 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1741 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1742 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1743 strcmp(I
->Pkg
.Arch(), "none") == 0))
1745 char const * const name
= I
->Pkg
.Name();
1750 pkgCache::VerIterator PkgVer
;
1751 std::string name
= I
->Pkg
.Name();
1752 if (Op
== Item::Remove
)
1753 PkgVer
= I
->Pkg
.CurrentVer();
1754 else if (Op
== Item::Purge
)
1756 // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here
1757 PkgVer
= I
->Pkg
.CurrentVer();
1758 if (PkgVer
.end() == true)
1762 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1763 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1764 ; // never arch-qualify a package without an arch
1765 else if (PkgVer
.end() == false)
1766 name
.append(":").append(PkgVer
.Arch());
1768 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1769 char * const fullname
= strdup(name
.c_str());
1770 Packages
.push_back(fullname
);
1774 // skip configure action if all sheduled packages disappeared
1775 if (oldSize
== Size
)
1783 if (noopDPkgInvocation
== true)
1785 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1786 a
!= Args
.end(); ++a
)
1789 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1790 p
!= Packages
.end(); ++p
)
1795 cleanUpTmpDir(tmpdir_to_free
);
1798 Args
.push_back(NULL
);
1804 /* Mask off sig int/quit. We do this because dpkg also does when
1805 it forks scripts. What happens is that when you hit ctrl-c it sends
1806 it to all processes in the group. Since dpkg ignores the signal
1807 it doesn't die but we do! So we must also ignore it */
1808 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1809 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1811 // Check here for any SIGINT
1812 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1815 // ignore SIGHUP as well (debian #463030)
1816 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1819 d
->progress
->StartDpkg();
1820 std::set
<int> KeepFDs
;
1821 KeepFDs
.insert(fd
[1]);
1822 MergeKeepFdsFromConfiguration(KeepFDs
);
1823 pid_t Child
= ExecFork(KeepFDs
);
1826 // This is the child
1827 SetupSlavePtyMagic();
1828 close(fd
[0]); // close the read end of the pipe
1830 debSystem::DpkgChrootDirectory();
1832 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1835 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1839 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1842 // Discard everything in stdin before forking dpkg
1843 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1846 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1848 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1852 // if color support isn't enabled/disabled explicitly tell
1853 // dpkg to use the same state apt is using for its color support
1854 if (_config
->FindB("APT::Color", false) == true)
1855 setenv("DPKG_COLORS", "always", 0);
1857 setenv("DPKG_COLORS", "never", 0);
1859 execvp(Args
[0], (char**) &Args
[0]);
1860 cerr
<< "Could not exec dpkg!" << endl
;
1864 // we read from dpkg here
1865 int const _dpkgin
= fd
[0];
1866 close(fd
[1]); // close the write end of the pipe
1869 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1873 sigemptyset(&d
->sigmask
);
1874 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1876 /* free vectors (and therefore memory) as we don't need the included data anymore */
1877 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1878 p
!= Packages
.end(); ++p
)
1882 // the result of the waitpid call
1885 bool waitpid_failure
= false;
1886 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1888 // error handling, waitpid returned -1
1891 waitpid_failure
= true;
1895 // wait for input or output here
1897 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1898 FD_SET(STDIN_FILENO
, &rfds
);
1899 FD_SET(_dpkgin
, &rfds
);
1901 FD_SET(d
->master
, &rfds
);
1903 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1904 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1905 &tv
, &d
->original_sigmask
);
1906 d
->progress
->Pulse();
1907 if (select_ret
== 0)
1909 else if (select_ret
< 0 && errno
== EINTR
)
1911 else if (select_ret
< 0)
1913 perror("select() returned error");
1917 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1918 DoTerminalPty(d
->master
);
1919 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1921 if(FD_ISSET(_dpkgin
, &rfds
))
1922 DoDpkgStatusFd(_dpkgin
);
1926 // Restore sig int/quit
1927 signal(SIGQUIT
,old_SIGQUIT
);
1928 signal(SIGINT
,old_SIGINT
);
1929 signal(SIGHUP
,old_SIGHUP
);
1931 cleanUpTmpDir(tmpdir_to_free
);
1933 if (waitpid_failure
== true)
1935 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1936 _error
->Error("%s", d
->dpkg_error
.c_str());
1940 // Check for an error code.
1941 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1943 // if it was set to "keep-dpkg-running" then we won't return
1944 // here but keep the loop going and just report it as a error
1946 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1948 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1949 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1950 else if (WIFEXITED(Status
) != 0)
1951 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1953 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1954 _error
->Error("%s", d
->dpkg_error
.c_str());
1960 // dpkg is done at this point
1964 if (d
->dpkg_error
.empty() == false)
1966 // no point in reseting packages we already completed removal for
1967 StripAlreadyDoneFromPending(approvedStates
.Remove());
1968 StripAlreadyDoneFromPending(approvedStates
.Purge());
1969 APT::StateChanges undo
;
1970 auto && undoRem
= approvedStates
.Remove();
1971 std::move(undoRem
.begin(), undoRem
.end(), std::back_inserter(undo
.Install()));
1972 auto && undoPur
= approvedStates
.Purge();
1973 std::move(undoPur
.begin(), undoPur
.end(), std::back_inserter(undo
.Install()));
1974 approvedStates
.clear();
1975 if (undo
.Save(false) == false)
1976 _error
->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
1978 if (currentStates
.Save(false) == false)
1979 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1981 if (pkgPackageManager::SigINTStop
)
1982 _error
->Warning(_("Operation was interrupted before it could finish"));
1984 if (noopDPkgInvocation
== false)
1986 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1987 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1988 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1990 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1991 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1993 _error
->PushToStack();
1994 pkgCacheFile CacheFile
;
1995 CacheFile
.BuildCaches(NULL
, true);
1996 _error
->RevertToStack();
2001 // disappearing packages can forward their auto-bit
2002 if (disappearedPkgs
.empty() == false)
2003 Cache
.writeStateFile(NULL
);
2005 d
->progress
->Stop();
2007 if (RunScripts("DPkg::Post-Invoke") == false)
2010 return d
->dpkg_error
.empty();
2013 void SigINT(int /*sig*/) {
2014 pkgPackageManager::SigINTStop
= true;
2017 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
2018 // ---------------------------------------------------------------------
2020 void pkgDPkgPM::Reset()
2022 List
.erase(List
.begin(),List
.end());
2025 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
2026 // ---------------------------------------------------------------------
2028 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
2030 // If apport doesn't exist or isn't installed do nothing
2031 // This e.g. prevents messages in 'universes' without apport
2032 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
2033 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
2036 string pkgname
, reportfile
, pkgver
, arch
;
2037 string::size_type pos
;
2040 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
2042 std::clog
<< "configured to not write apport reports" << std::endl
;
2046 // only report the first errors
2047 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
2049 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
2053 // check if its not a follow up error
2054 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
2055 if(strstr(errormsg
, needle
) != NULL
) {
2056 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
2060 // do not report disk-full failures
2061 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
2062 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
2066 // do not report out-of-memory failures
2067 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
2068 strstr(errormsg
, "failed to allocate memory") != NULL
) {
2069 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
2073 // do not report bugs regarding inaccessible local files
2074 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
2075 strstr(errormsg
, "cannot access archive") != NULL
) {
2076 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2080 // do not report errors encountered when decompressing packages
2081 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
2082 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2086 // do not report dpkg I/O errors, this is a format string, so we compare
2087 // the prefix and the suffix of the error with the dpkg error message
2088 vector
<string
> io_errors
;
2089 io_errors
.push_back(string("failed to read"));
2090 io_errors
.push_back(string("failed to write"));
2091 io_errors
.push_back(string("failed to seek"));
2092 io_errors
.push_back(string("unexpected end of file or stream"));
2094 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
2096 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
2097 if (list
.size() > 1) {
2098 // we need to split %s, VectorizeString only allows char so we need
2099 // to kill the "s" manually
2100 if (list
[1].size() > 1) {
2101 list
[1].erase(0, 1);
2102 if(strstr(errormsg
, list
[0].c_str()) &&
2103 strstr(errormsg
, list
[1].c_str())) {
2104 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
2111 // get the pkgname and reportfile
2112 pkgname
= flNotDir(pkgpath
);
2113 pos
= pkgname
.find('_');
2114 if(pos
!= string::npos
)
2115 pkgname
= pkgname
.substr(0, pos
);
2117 // find the package version and source package name
2118 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
2119 if (Pkg
.end() == true)
2121 if (pos
== std::string::npos
|| _config
->FindB("dpkg::install::recursive::numbered", true) == false)
2123 auto const dash
= pkgname
.find_first_not_of("0123456789");
2124 if (dash
== std::string::npos
|| pkgname
[dash
] != '-')
2126 pkgname
.erase(0, dash
+ 1);
2127 Pkg
= Cache
.FindPkg(pkgname
);
2128 if (Pkg
.end() == true)
2131 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
2132 if (Ver
.end() == true)
2134 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
2136 // if the file exists already, we check:
2137 // - if it was reported already (touched by apport).
2138 // If not, we do nothing, otherwise
2139 // we overwrite it. This is the same behaviour as apport
2140 // - if we have a report with the same pkgversion already
2142 _config
->CndSet("Dir::Apport", "var/crash");
2143 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
2144 if(FileExists(reportfile
))
2149 // check atime/mtime
2150 stat(reportfile
.c_str(), &buf
);
2151 if(buf
.st_mtime
> buf
.st_atime
)
2154 // check if the existing report is the same version
2155 report
= fopen(reportfile
.c_str(),"r");
2156 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
2158 if(strstr(strbuf
,"Package:") == strbuf
)
2160 char pkgname
[255], version
[255];
2161 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
2162 if(strcmp(pkgver
.c_str(), version
) == 0)
2172 // now write the report
2173 arch
= _config
->Find("APT::Architecture");
2174 report
= fopen(reportfile
.c_str(),"w");
2177 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
2178 chmod(reportfile
.c_str(), 0);
2180 chmod(reportfile
.c_str(), 0600);
2181 fprintf(report
, "ProblemType: Package\n");
2182 fprintf(report
, "Architecture: %s\n", arch
.c_str());
2183 time_t now
= time(NULL
);
2184 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
2185 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
2186 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
2187 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
2188 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
2190 // ensure that the log is flushed
2192 fflush(d
->term_out
);
2194 // attach terminal log it if we have it
2195 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
2196 if (!logfile_name
.empty())
2200 fprintf(report
, "DpkgTerminalLog:\n");
2201 log
= fopen(logfile_name
.c_str(),"r");
2205 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2206 fprintf(report
, " %s", buf
);
2207 fprintf(report
, " \n");
2212 // attach history log it if we have it
2213 string histfile_name
= _config
->FindFile("Dir::Log::History");
2214 if (!histfile_name
.empty())
2216 fprintf(report
, "DpkgHistoryLog:\n");
2217 FILE* log
= fopen(histfile_name
.c_str(),"r");
2221 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2222 fprintf(report
, " %s", buf
);
2227 // log the ordering, see dpkgpm.h and the "Ops" enum there
2228 fprintf(report
, "AptOrdering:\n");
2229 for (auto && I
: List
)
2231 char const * opstr
= nullptr;
2234 case Item::Install
: opstr
= "Install"; break;
2235 case Item::Configure
: opstr
= "Configure"; break;
2236 case Item::Remove
: opstr
= "Remove"; break;
2237 case Item::Purge
: opstr
= "Purge"; break;
2238 case Item::ConfigurePending
: opstr
= "ConfigurePending"; break;
2239 case Item::TriggersPending
: opstr
= "TriggersPending"; break;
2240 case Item::RemovePending
: opstr
= "RemovePending"; break;
2241 case Item::PurgePending
: opstr
= "PurgePending"; break;
2243 auto const pkgname
= I
.Pkg
.end() ? "NULL" : I
.Pkg
.FullName();
2244 fprintf(report
, " %s: %s\n", pkgname
.c_str(), opstr
);
2247 // attach dmesg log (to learn about segfaults)
2248 if (FileExists("/bin/dmesg"))
2250 fprintf(report
, "Dmesg:\n");
2251 FILE *log
= popen("/bin/dmesg","r");
2255 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2256 fprintf(report
, " %s", buf
);
2261 // attach df -l log (to learn about filesystem status)
2262 if (FileExists("/bin/df"))
2265 fprintf(report
, "Df:\n");
2266 FILE *log
= popen("/bin/df -l","r");
2270 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2271 fprintf(report
, " %s", buf
);