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 // we don't care for the difference
147 std::make_pair("upgrade", N_("Installing %s")),
148 std::make_pair("configure", N_("Configuring %s")),
149 std::make_pair("remove", N_("Removing %s")),
150 std::make_pair("purge", N_("Completely removing %s")),
151 std::make_pair("disappear", N_("Noting disappearance of %s")),
152 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
155 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
156 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
158 // Predicate to test whether an entry in the PackageProcessingOps
159 // array matches a string.
160 class MatchProcessingOp
165 explicit MatchProcessingOp(const char *the_target
)
170 bool operator()(const std::pair
<const char *, const char *> &pair
) const
172 return strcmp(pair
.first
, target
) == 0;
177 // ionice - helper function to ionice the given PID /*{{{*/
178 /* there is no C header for ionice yet - just the syscall interface
179 so we use the binary from util-linux */
180 static bool ionice(int PID
)
182 if (!FileExists("/usr/bin/ionice"))
184 pid_t Process
= ExecFork();
188 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
190 Args
[0] = "/usr/bin/ionice";
194 execv(Args
[0], (char **)Args
);
196 return ExecWait(Process
, "ionice");
199 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
200 // ---------------------------------------------------------------------
201 /* This is helpful when a package is no longer installed but has residual
205 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
207 pkgCache::VerIterator Ver
;
208 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
209 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
210 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
212 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
218 static pkgCache::VerIterator
FindToBeRemovedVersion(pkgCache::PkgIterator
const &Pkg
)/*{{{*/
220 auto const PV
= Pkg
.CurrentVer();
221 if (PV
.end() == false)
223 return FindNowVersion(Pkg
);
227 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
228 // ---------------------------------------------------------------------
230 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
231 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
235 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
236 // ---------------------------------------------------------------------
238 pkgDPkgPM::~pkgDPkgPM()
243 // DPkgPM::Install - Install a package /*{{{*/
244 // ---------------------------------------------------------------------
245 /* Add an install operation to the sequence list */
246 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
248 if (File
.empty() == true || Pkg
.end() == true)
249 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
251 // If the filename string begins with DPkg::Chroot-Directory, return the
252 // substr that is within the chroot so dpkg can access it.
253 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
254 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
256 size_t len
= chrootdir
.length();
257 if (chrootdir
.at(len
- 1) == '/')
259 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
262 List
.push_back(Item(Item::Install
,Pkg
,File
));
267 // DPkgPM::Configure - Configure a package /*{{{*/
268 // ---------------------------------------------------------------------
269 /* Add a configure operation to the sequence list */
270 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
272 if (Pkg
.end() == true)
275 List
.push_back(Item(Item::Configure
, Pkg
));
277 // Use triggers for config calls if we configure "smart"
278 // as otherwise Pre-Depends will not be satisfied, see #526774
279 if (_config
->FindB("DPkg::TriggersPending", false) == true)
280 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
285 // DPkgPM::Remove - Remove a package /*{{{*/
286 // ---------------------------------------------------------------------
287 /* Add a remove operation to the sequence list */
288 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
290 if (Pkg
.end() == true)
294 List
.push_back(Item(Item::Purge
,Pkg
));
296 List
.push_back(Item(Item::Remove
,Pkg
));
300 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
301 // ---------------------------------------------------------------------
302 /* This is part of the helper script communication interface, it sends
303 very complete information down to the other end of the pipe.*/
304 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
306 return SendPkgsInfo(F
, 2);
308 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
310 // This version of APT supports only v3, so don't sent higher versions
312 fprintf(F
,"VERSION %u\n", Version
);
314 fprintf(F
,"VERSION 3\n");
316 /* Write out all of the configuration directives by walking the
317 configuration tree */
318 const Configuration::Item
*Top
= _config
->Tree(0);
321 if (Top
->Value
.empty() == false)
324 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
325 QuoteString(Top
->Value
,"\n").c_str());
334 while (Top
!= 0 && Top
->Next
== 0)
341 // Write out the package actions in order.
342 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
344 if(I
->Pkg
.end() == true)
347 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
349 fprintf(F
,"%s ",I
->Pkg
.Name());
351 // Current version which we are going to replace
352 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
353 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
354 CurVer
= FindNowVersion(I
->Pkg
);
356 if (CurVer
.end() == true)
361 fprintf(F
, "- - none ");
365 fprintf(F
, "%s ", CurVer
.VerStr());
367 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
370 // Show the compare operator between current and install version
371 if (S
.InstallVer
!= 0)
373 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
375 if (CurVer
.end() == false)
376 Comp
= InstVer
.CompareVer(CurVer
);
383 fprintf(F
, "%s ", InstVer
.VerStr());
385 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
392 fprintf(F
, "> - - none ");
395 // Show the filename/operation
396 if (I
->Op
== Item::Install
)
399 if (I
->File
[0] != '/')
400 fprintf(F
,"**ERROR**\n");
402 fprintf(F
,"%s\n",I
->File
.c_str());
404 else if (I
->Op
== Item::Configure
)
405 fprintf(F
,"**CONFIGURE**\n");
406 else if (I
->Op
== Item::Remove
||
407 I
->Op
== Item::Purge
)
408 fprintf(F
,"**REMOVE**\n");
416 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
417 // ---------------------------------------------------------------------
418 /* This looks for a list of scripts to run from the configuration file
419 each one is run and is fed on standard input a list of all .deb files
420 that are due to be installed. */
421 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
425 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
426 if (Opts
== 0 || Opts
->Child
== 0)
430 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
431 sighandler_t old_sigint
= signal(SIGINT
, SIG_IGN
);
432 sighandler_t old_sigquit
= signal(SIGQUIT
, SIG_IGN
);
434 unsigned int Count
= 1;
435 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
437 if (Opts
->Value
.empty() == true)
440 if(_config
->FindB("Debug::RunScripts", false) == true)
441 std::clog
<< "Running external script with list of all .deb file: '"
442 << Opts
->Value
<< "'" << std::endl
;
444 // Determine the protocol version
445 string OptSec
= Opts
->Value
;
446 string::size_type Pos
;
447 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
448 Pos
= OptSec
.length();
449 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
451 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
452 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
455 std::set
<int> KeepFDs
;
456 MergeKeepFdsFromConfiguration(KeepFDs
);
458 if (pipe(Pipes
) != 0) {
459 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
462 if (InfoFD
!= (unsigned)Pipes
[0])
463 SetCloseExec(Pipes
[0],true);
465 KeepFDs
.insert(Pipes
[0]);
468 SetCloseExec(Pipes
[1],true);
470 // Purified Fork for running the script
471 pid_t Process
= ExecFork(KeepFDs
);
475 dup2(Pipes
[0], InfoFD
);
476 SetCloseExec(STDOUT_FILENO
,false);
477 SetCloseExec(STDIN_FILENO
,false);
478 SetCloseExec(STDERR_FILENO
,false);
481 strprintf(hookfd
, "%d", InfoFD
);
482 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
484 debSystem::DpkgChrootDirectory();
488 Args
[2] = Opts
->Value
.c_str();
490 execv(Args
[0],(char **)Args
);
494 FILE *F
= fdopen(Pipes
[1],"w");
496 result
= _error
->Errno("fdopen","Failed to open new FD");
500 // Feed it the filenames.
503 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
505 // Only deal with packages to be installed from .deb
506 if (I
->Op
!= Item::Install
)
510 if (I
->File
[0] != '/')
513 /* Feed the filename of each package that is pending install
515 fprintf(F
,"%s\n",I
->File
.c_str());
521 SendPkgsInfo(F
, Version
);
525 // Clean up the sub process
526 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
527 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
531 signal(SIGINT
, old_sigint
);
532 signal(SIGPIPE
, old_sigpipe
);
533 signal(SIGQUIT
, old_sigquit
);
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]);
621 // "status" has the form: "status: pkg: state"
622 // with state in ["half-installed", "unpacked", "half-configured",
623 // "installed", "config-files", "not-installed"]
624 else if (prefix
== "status")
626 pkgname
= APT::String::Strip(list
[1]);
627 action
= APT::String::Strip(list
[2]);
629 /* handle the special cases first:
631 errors look like this:
632 '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
633 and conffile-prompt like this
634 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
636 if(action
== "error")
638 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
, list
[3]);
640 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
643 else if(action
== "conffile-prompt")
645 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
, list
[3]);
650 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
654 // At this point we have a pkgname, but it might not be arch-qualified !
655 if (pkgname
.find(":") == std::string::npos
)
657 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
658 /* No arch means that dpkg believes there can only be one package
659 this can refer to so lets see what could be candidates here: */
660 std::vector
<pkgCache::PkgIterator
> candset
;
661 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
663 if (PackageOps
.find(P
.FullName()) != PackageOps
.end())
664 candset
.push_back(P
);
665 // packages can disappear without them having any interaction itself
666 // so we have to consider these as candidates, too
667 else if (P
->CurrentVer
!= 0 && action
== "disappear")
668 candset
.push_back(P
);
670 if (unlikely(candset
.empty()))
673 std::clog
<< "unable to figure out which package is dpkg refering to with '" << pkgname
<< "'! (1)" << std::endl
;
676 else if (candset
.size() == 1) // we are lucky
677 pkgname
= candset
.cbegin()->FullName();
680 /* here be dragons^Wassumptions about dpkg:
681 - an M-A:same version is always arch-qualified
682 - a package from a foreign arch is (in newer versions) */
683 size_t installedInstances
= 0, wannabeInstances
= 0;
684 for (auto const &P
: candset
)
686 if (P
->CurrentVer
!= 0)
688 ++installedInstances
;
689 if (Cache
[P
].Delete() == false)
692 else if (Cache
[P
].Install())
695 // the package becomes M-A:same, so we are still talking about current
696 if (installedInstances
== 1 && wannabeInstances
>= 2)
698 for (auto const &P
: candset
)
700 if (P
->CurrentVer
== 0)
702 pkgname
= P
.FullName();
706 // the package was M-A:same, it isn't now, so we can only talk about that
707 else if (installedInstances
>= 2 && wannabeInstances
== 1)
709 for (auto const &P
: candset
)
711 auto const IV
= Cache
[P
].InstVerIter(Cache
);
714 pkgname
= P
.FullName();
718 // that is a crossgrade
719 else if (installedInstances
== 1 && wannabeInstances
== 1 && candset
.size() == 2)
721 auto const PkgHasCurrentVersion
= [](pkgCache::PkgIterator
const &P
) { return P
->CurrentVer
!= 0; };
722 auto const P
= std::find_if(candset
.begin(), candset
.end(), PkgHasCurrentVersion
);
723 if (unlikely(P
== candset
.end()))
726 std::clog
<< "situation for '" << pkgname
<< "' looked like a crossgrade, but no current version?!" << std::endl
;
729 auto fullname
= P
->FullName();
730 if (PackageOps
[fullname
].size() != PackageOpsDone
[fullname
])
731 pkgname
= std::move(fullname
);
733 pkgname
= std::find_if_not(candset
.begin(), candset
.end(), PkgHasCurrentVersion
)->FullName();
735 // we are desperate: so "just" take the native one, but that might change mid-air,
736 // so we have to ask dpkg what it believes native is at the moment… all the time
739 std::vector
<std::string
> sArgs
= debSystem::GetDpkgBaseCommand();
740 sArgs
.push_back("--print-architecture");
742 pid_t
const dpkgNativeArch
= debSystem::ExecDpkg(sArgs
, nullptr, &outputFd
, true);
743 if (unlikely(dpkgNativeArch
== -1))
746 std::clog
<< "calling dpkg failed to ask it for its current native architecture to expand '" << pkgname
<< "'!" << std::endl
;
749 FILE *dpkg
= fdopen(outputFd
, "r");
754 if (getline(&buf
, &bufsize
, dpkg
) != -1)
755 pkgname
+= ':' + bufsize
;
759 ExecWait(dpkgNativeArch
, "dpkg --print-architecture", true);
760 if (pkgname
.find(':') != std::string::npos
)
763 std::clog
<< "unable to figure out which package is dpkg refering to with '" << pkgname
<< "'! (2)" << std::endl
;
770 std::string arch
= "";
771 if (pkgname
.find(":") != string::npos
)
772 arch
= StringSplit(pkgname
, ":")[1];
773 std::string i18n_pkgname
= pkgname
;
774 if (arch
.size() != 0)
775 strprintf(i18n_pkgname
, "%s (%s)", StringSplit(pkgname
, ":")[0].c_str(), arch
.c_str());
777 // 'processing' from dpkg looks like
778 // 'processing: action: pkg'
779 if(prefix
== "processing")
781 auto const iter
= std::find_if(PackageProcessingOpsBegin
, PackageProcessingOpsEnd
, MatchProcessingOp(action
.c_str()));
782 if(iter
== PackageProcessingOpsEnd
)
785 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
789 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
790 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
792 // FIXME: this needs a muliarch testcase
793 // FIXME2: is "pkgname" here reliable with dpkg only sending us
795 if (action
== "disappear")
796 handleDisappearAction(pkgname
);
797 else if (action
== "upgrade")
798 handleCrossUpgradeAction(pkgname
);
802 if (prefix
== "status")
804 std::vector
<struct DpkgState
> &states
= PackageOps
[pkgname
];
805 if(PackageOpsDone
[pkgname
] < states
.size())
807 char const * next_action
= states
[PackageOpsDone
[pkgname
]].state
;
811 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
812 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
815 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
816 << " pending trigger defused by unpack" << std::endl;
817 // unpacking a package defuses the pending trigger
818 PackageOpsDone[pkg] += 2;
820 next_action = states[PackageOpsDone[pkg]].state;
824 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
825 << " action: " << action
<< " (expected: '" << next_action
<< "' "
826 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
828 // check if the package moved to the next dpkg state
829 if(action
== next_action
)
831 // only read the translation if there is actually a next action
832 char const * const translation
= _(states
[PackageOpsDone
[pkgname
]].str
);
834 // we moved from one dpkg state to a new one, report that
835 ++PackageOpsDone
[pkgname
];
839 strprintf(msg
, translation
, i18n_pkgname
.c_str());
840 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
844 else if (action
== "triggers-pending")
847 std::clog
<< "(parsed from dpkg) pkg: " << pkgname
848 << " action: " << action
<< " (prefix 2 to "
849 << PackageOpsDone
[pkgname
] << " of " << states
.size() << ")" << endl
;
851 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
852 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
858 // DPkgPM::handleDisappearAction /*{{{*/
859 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
861 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
862 if (unlikely(Pkg
.end() == true))
865 // a disappeared package has no further actions
866 auto const ROps
= PackageOps
[Pkg
.FullName()].size();
867 auto && ROpsDone
= PackageOpsDone
[Pkg
.FullName()];
868 PackagesDone
+= ROps
- ROpsDone
;
871 // record the package name for display and stuff later
872 disappearedPkgs
.insert(Pkg
.FullName(true));
874 // the disappeared package was auto-installed - nothing to do
875 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
877 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
878 if (unlikely(PkgVer
.end() == true))
880 /* search in the list of dependencies for (Pre)Depends,
881 check if this dependency has a Replaces on our package
882 and if so transfer the manual installed flag to it */
883 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
885 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
886 Dep
->Type
!= pkgCache::Dep::PreDepends
)
888 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
889 if (unlikely(Tar
.end() == true))
891 // the package is already marked as manual
892 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
894 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
895 if (TarVer
.end() == true)
897 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
899 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
901 if (Pkg
!= Rep
.TargetPkg())
903 // okay, they are strongly connected - transfer manual-bit
905 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
906 Cache
[Tar
].Flags
&= ~Flag::Auto
;
912 void pkgDPkgPM::handleCrossUpgradeAction(string
const &pkgname
) /*{{{*/
914 // in a crossgrade what looked like a remove first is really an unpack over it
915 auto const Pkg
= Cache
.FindPkg(pkgname
);
916 if (likely(Pkg
.end() == false) && Cache
[Pkg
].Delete())
918 auto const Grp
= Pkg
.Group();
919 if (likely(Grp
.end() == false))
921 for (auto P
= Grp
.PackageList(); P
.end() != true; P
= Grp
.NextPkg(P
))
922 if(Cache
[P
].Install())
924 auto && Ops
= PackageOps
[P
.FullName()];
925 auto const unpackOp
= std::find_if(Ops
.cbegin(), Ops
.cend(), [](DpkgState
const &s
) { return strcmp(s
.state
, "unpacked") == 0; });
926 if (unpackOp
!= Ops
.cend())
928 // skip ahead in the crossgraded packages
929 auto const skipped
= std::distance(Ops
.cbegin(), unpackOp
);
930 PackagesDone
+= skipped
;
931 PackageOpsDone
[P
.FullName()] += skipped
;
932 // finish the crossremoved package
933 auto const ROps
= PackageOps
[Pkg
.FullName()].size();
934 auto && ROpsDone
= PackageOpsDone
[Pkg
.FullName()];
935 PackagesDone
+= ROps
- ROpsDone
;
944 // DPkgPM::DoDpkgStatusFd /*{{{*/
945 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
947 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
948 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
951 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
953 // process line by line from the buffer
954 char *p
= d
->dpkgbuf
, *q
= nullptr;
955 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
958 ProcessDpkgStatusLine(p
);
959 p
= q
+ 1; // continue with next line
962 // check if we stripped the buffer clean
963 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
969 // otherwise move the unprocessed tail to the start and update pos
970 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
971 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
974 // DPkgPM::WriteHistoryTag /*{{{*/
975 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
977 size_t const length
= value
.length();
980 // poor mans rstrip(", ")
981 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
982 value
.erase(length
- 2, 2);
983 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
985 // DPkgPM::OpenLog /*{{{*/
986 bool pkgDPkgPM::OpenLog()
988 string
const logdir
= _config
->FindDir("Dir::Log");
989 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
990 // FIXME: use a better string after freeze
991 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
995 time_t const t
= time(NULL
);
997 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
998 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
1000 // open terminal log
1001 string
const logfile_name
= flCombine(logdir
,
1002 _config
->Find("Dir::Log::Terminal"));
1003 if (!logfile_name
.empty())
1005 d
->term_out
= fopen(logfile_name
.c_str(),"a");
1006 if (d
->term_out
== NULL
)
1007 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
1008 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
1009 SetCloseExec(fileno(d
->term_out
), true);
1010 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
1012 struct passwd
*pw
= getpwnam("root");
1013 struct group
*gr
= getgrnam("adm");
1014 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
1015 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
1017 if (chmod(logfile_name
.c_str(), 0640) != 0)
1018 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
1019 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
1022 // write your history
1023 string
const history_name
= flCombine(logdir
,
1024 _config
->Find("Dir::Log::History"));
1025 if (!history_name
.empty())
1027 d
->history_out
= fopen(history_name
.c_str(),"a");
1028 if (d
->history_out
== NULL
)
1029 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
1030 SetCloseExec(fileno(d
->history_out
), true);
1031 chmod(history_name
.c_str(), 0644);
1032 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
1033 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
1034 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
1036 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
1037 string
*line
= NULL
;
1038 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
1039 if (Cache
[I
].NewInstall() == true)
1040 HISTORYINFO(install
, CANDIDATE_AUTO
)
1041 else if (Cache
[I
].ReInstall() == true)
1042 HISTORYINFO(reinstall
, CANDIDATE
)
1043 else if (Cache
[I
].Upgrade() == true)
1044 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
1045 else if (Cache
[I
].Downgrade() == true)
1046 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
1047 else if (Cache
[I
].Delete() == true)
1048 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
1052 line
->append(I
.FullName(false)).append(" (");
1053 switch (infostring
) {
1054 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
1055 case CANDIDATE_AUTO
:
1056 line
->append(Cache
[I
].CandVersion
);
1057 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
1058 line
->append(", automatic");
1060 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
1061 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
1063 line
->append("), ");
1065 if (_config
->Exists("Commandline::AsString") == true)
1066 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
1067 std::string RequestingUser
= AptHistoryRequestingUser();
1068 if (RequestingUser
!= "")
1069 WriteHistoryTag("Requested-By", RequestingUser
);
1070 WriteHistoryTag("Install", install
);
1071 WriteHistoryTag("Reinstall", reinstall
);
1072 WriteHistoryTag("Upgrade", upgrade
);
1073 WriteHistoryTag("Downgrade",downgrade
);
1074 WriteHistoryTag("Remove",remove
);
1075 WriteHistoryTag("Purge",purge
);
1076 fflush(d
->history_out
);
1082 // DPkg::CloseLog /*{{{*/
1083 bool pkgDPkgPM::CloseLog()
1086 time_t t
= time(NULL
);
1088 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
1089 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
1093 fprintf(d
->term_out
, "Log ended: ");
1094 fprintf(d
->term_out
, "%s", timestr
);
1095 fprintf(d
->term_out
, "\n");
1096 fclose(d
->term_out
);
1102 if (disappearedPkgs
.empty() == false)
1105 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
1106 d
!= disappearedPkgs
.end(); ++d
)
1108 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
1109 disappear
.append(*d
);
1110 if (P
.end() == true)
1111 disappear
.append(", ");
1113 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
1115 WriteHistoryTag("Disappeared", disappear
);
1117 if (d
->dpkg_error
.empty() == false)
1118 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
1119 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
1120 fclose(d
->history_out
);
1122 d
->history_out
= NULL
;
1128 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1129 void pkgDPkgPM::BuildPackagesProgressMap()
1131 // map the dpkg states to the operations that are performed
1132 // (this is sorted in the same way as Item::Ops)
1133 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1134 // Install operation
1136 {"half-installed", N_("Preparing %s")},
1137 {"unpacked", N_("Unpacking %s") },
1140 // Configure operation
1142 {"unpacked",N_("Preparing to configure %s") },
1143 {"half-configured", N_("Configuring %s") },
1144 { "installed", N_("Installed %s")},
1148 {"half-configured", N_("Preparing for removal of %s")},
1149 {"half-installed", N_("Removing %s")},
1150 {"config-files", N_("Removed %s")},
1154 {"config-files", N_("Preparing to completely remove %s")},
1155 {"not-installed", N_("Completely removed %s")},
1159 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1161 // init the PackageOps map, go over the list of packages that
1162 // that will be [installed|configured|removed|purged] and add
1163 // them to the PackageOps map (the dpkg states it goes through)
1164 // and the PackageOpsTranslations (human readable strings)
1165 for (auto &&I
: List
)
1167 if(I
.Pkg
.end() == true)
1170 string
const name
= I
.Pkg
.FullName();
1171 PackageOpsDone
[name
] = 0;
1172 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1173 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1175 // purging a package which is installed first passes through remove states
1176 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1177 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1178 PackagesTotal
+= DpkgOps
.size();
1180 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1181 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1182 if (state
.state
== nullptr)
1187 if ((I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
) && I
.Pkg
->CurrentVer
!= 0)
1189 if (I
.Pkg
->CurrentState
== pkgCache::State::UnPacked
||
1190 I
.Pkg
->CurrentState
== pkgCache::State::HalfInstalled
)
1192 if (likely(strcmp(PackageOps
[name
][0].state
, "half-configured") == 0))
1194 ++PackageOpsDone
[name
];
1200 /* one extra: We don't want the progress bar to reach 100%, especially not
1201 if we call dpkg --configure --pending and process a bunch of triggers
1202 while showing 100%. Also, spindown takes a while, so never reaching 100%
1203 is way more correct than reaching 100% while still doing stuff even if
1204 doing it this way is slightly bending the rules */
1208 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1210 APT::Progress::PackageManager
*progress
= NULL
;
1212 progress
= APT::Progress::PackageManagerProgressFactory();
1214 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1216 return Go(progress
);
1219 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1221 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1224 if (d
->slave
!= NULL
)
1230 if (isatty(STDIN_FILENO
) == 0)
1231 d
->direct_stdin
= true;
1233 _error
->PushToStack();
1235 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1236 if (d
->master
== -1)
1237 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1238 else if (unlockpt(d
->master
) == -1)
1239 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1242 #ifdef HAVE_PTSNAME_R
1243 char slave_name
[64]; // 64 is used by bionic
1244 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1246 char const * const slave_name
= ptsname(d
->master
);
1247 if (slave_name
== NULL
)
1249 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1252 d
->slave
= strdup(slave_name
);
1253 if (d
->slave
== NULL
)
1254 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1255 else if (grantpt(d
->master
) == -1)
1256 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1257 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1259 d
->tt_is_valid
= true;
1260 struct termios raw_tt
;
1261 // copy window size of stdout if its a 'good' terminal
1262 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1265 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1266 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1267 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1268 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1270 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1271 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1275 raw_tt
.c_lflag
&= ~ECHO
;
1276 raw_tt
.c_lflag
|= ISIG
;
1277 // block SIGTTOU during tcsetattr to prevent a hang if
1278 // the process is a member of the background process group
1279 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1280 sigemptyset(&d
->sigmask
);
1281 sigaddset(&d
->sigmask
, SIGTTOU
);
1282 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1283 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1284 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1285 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1288 if (d
->slave
!= NULL
)
1290 /* on linux, closing (and later reopening) all references to the slave
1291 makes the slave a death end, so we open it here to have one open all
1292 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1293 on kfreebsd we get an incorrect ("step like") output then while it has
1294 no problem with closing all references… so to avoid platform specific
1295 code here we combine both and be happy once more */
1296 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1301 if (_error
->PendingError() == true)
1303 if (d
->master
!= -1)
1308 if (d
->slave
!= NULL
)
1313 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1315 _error
->RevertToStack();
1318 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1320 if(d
->master
== -1 || d
->slave
== NULL
)
1323 if (close(d
->master
) == -1)
1324 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1327 _error
->FatalE("setsid", "Starting a new session for child failed!");
1329 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1331 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1332 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1333 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1336 unsigned short i
= 0;
1337 if (d
->direct_stdin
== true)
1340 if (dup2(slaveFd
, i
) == -1)
1341 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1343 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1344 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1351 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1353 if (d
->slave
!= NULL
)
1356 if (d
->protect_slave_from_dying
!= -1)
1358 close(d
->protect_slave_from_dying
);
1359 d
->protect_slave_from_dying
= -1;
1363 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1364 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1370 static void cleanUpTmpDir(char * const tmpdir
) /*{{{*/
1372 if (tmpdir
== nullptr)
1374 DIR * const D
= opendir(tmpdir
);
1376 _error
->Errno("opendir", _("Unable to read %s"), tmpdir
);
1379 auto const dfd
= dirfd(D
);
1380 for (struct dirent
*Ent
= readdir(D
); Ent
!= nullptr; Ent
= readdir(D
))
1382 if (Ent
->d_name
[0] == '.')
1384 #ifdef _DIRENT_HAVE_D_TYPE
1385 if (unlikely(Ent
->d_type
!= DT_LNK
&& Ent
->d_type
!= DT_UNKNOWN
))
1388 char path
[strlen(tmpdir
) + 1 + strlen(Ent
->d_name
) + 1];
1389 sprintf(path
, "%s/%s", tmpdir
, Ent
->d_name
);
1390 if (unlikely(unlink(path
) != 0))
1400 // DPkgPM::Go - Run the sequence /*{{{*/
1401 // ---------------------------------------------------------------------
1402 /* This globs the operations and calls dpkg
1404 * If it is called with a progress object apt will report the install
1405 * progress to this object. It maps the dpkg states a package goes
1406 * through to human readable (and i10n-able)
1407 * names and calculates a percentage for each step.
1409 static bool ItemIsEssential(pkgDPkgPM::Item
const &I
)
1411 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1412 if (cachegen
== "none" || cachegen
== "native")
1414 if (unlikely(I
.Pkg
.end()))
1416 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1418 bool pkgDPkgPM::ExpandPendingCalls(std::vector
<Item
> &List
, pkgDepCache
&Cache
)
1421 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyRemoved
;
1422 for (auto && I
: List
)
1423 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1424 alreadyRemoved
.insert(I
.Pkg
->ID
);
1425 std::remove_reference
<decltype(List
)>::type AppendList
;
1426 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1427 if (Cache
[Pkg
].Delete() && alreadyRemoved
.insert(Pkg
->ID
).second
== true)
1428 AppendList
.emplace_back(Cache
[Pkg
].Purge() ? Item::Purge
: Item::Remove
, Pkg
);
1429 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1432 std::unordered_set
<decltype(pkgCache::Package::ID
)> alreadyConfigured
;
1433 for (auto && I
: List
)
1434 if (I
.Op
== Item::Configure
)
1435 alreadyConfigured
.insert(I
.Pkg
->ID
);
1436 std::remove_reference
<decltype(List
)>::type AppendList
;
1437 for (auto && I
: List
)
1438 if (I
.Op
== Item::Install
&& alreadyConfigured
.insert(I
.Pkg
->ID
).second
== true)
1439 AppendList
.emplace_back(Item::Configure
, I
.Pkg
);
1440 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1441 if (Pkg
.State() == pkgCache::PkgIterator::NeedsConfigure
&&
1442 Cache
[Pkg
].Delete() == false && alreadyConfigured
.insert(Pkg
->ID
).second
== true)
1443 AppendList
.emplace_back(Item::Configure
, Pkg
);
1444 std::move(AppendList
.begin(), AppendList
.end(), std::back_inserter(List
));
1448 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1450 // explicitely remove&configure everything for hookscripts and progress building
1451 // we need them only temporarily through, so keep the length and erase afterwards
1452 decltype(List
)::const_iterator::difference_type explicitIdx
=
1453 std::distance(List
.cbegin(), List
.cend());
1454 ExpandPendingCalls(List
, Cache
);
1456 /* if dpkg told us that it has already done everything to the package we wanted it to do,
1457 we shouldn't ask it for "more" later. That can e.g. happen if packages without conffiles
1458 are purged as they will have pass through the purge states on remove already */
1459 auto const StripAlreadyDoneFrom
= [&](APT::VersionVector
& Pending
) {
1460 Pending
.erase(std::remove_if(Pending
.begin(), Pending
.end(), [&](pkgCache::VerIterator
const &Ver
) {
1461 auto const PN
= Ver
.ParentPkg().FullName();
1462 auto const POD
= PackageOpsDone
.find(PN
);
1463 if (POD
== PackageOpsDone
.end())
1465 return PackageOps
[PN
].size() <= POD
->second
;
1469 pkgPackageManager::SigINTStop
= false;
1470 d
->progress
= progress
;
1472 // Generate the base argument list for dpkg
1473 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1474 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1475 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1476 [](std::string
const &s
) { return s
.c_str(); });
1477 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1478 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1479 size_t const BaseArgs
= Args
.size();
1484 // try to figure out the max environment size
1485 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1488 OSArgMax
-= EnvironmentSize() - 2*1024;
1489 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1490 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", true);
1492 if (RunScripts("DPkg::Pre-Invoke") == false)
1495 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1498 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1499 // store auto-bits as they are supposed to be after dpkg is run
1500 if (noopDPkgInvocation
== false)
1501 Cache
.writeStateFile(NULL
);
1503 bool dpkg_recursive_install
= _config
->FindB("dpkg::install::recursive", false);
1504 if (_config
->FindB("dpkg::install::recursive::force", false) == false)
1506 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1507 auto const dpkgpkg
= Cache
.FindPkg("dpkg");
1508 if (likely(dpkgpkg
.end() == false && dpkgpkg
->CurrentVer
!= 0))
1509 dpkg_recursive_install
= Cache
.VS().CmpVersion("1.18.5", dpkgpkg
.CurrentVer().VerStr()) <= 0;
1511 // no point in doing this dance for a handful of packages only
1512 unsigned int const dpkg_recursive_install_min
= _config
->FindB("dpkg::install::recursive::minimum", 5);
1513 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1514 bool const dpkg_recursive_install_numbered
= _config
->FindB("dpkg::install::recursive::numbered", true);
1517 BuildPackagesProgressMap();
1519 APT::StateChanges approvedStates
;
1520 if (_config
->FindB("dpkg::selection::remove::approved", true))
1522 for (auto && I
: List
)
1523 if (I
.Op
== Item::Purge
)
1524 approvedStates
.Purge(FindToBeRemovedVersion(I
.Pkg
));
1525 else if (I
.Op
== Item::Remove
)
1526 approvedStates
.Remove(FindToBeRemovedVersion(I
.Pkg
));
1529 // Skip removes if we install another architecture of this package soon (crossgrade)
1530 // We can't just skip them all the time as it could be an ordering requirement [of another package]
1531 if ((approvedStates
.Remove().empty() == false || approvedStates
.Purge().empty() == false) &&
1532 _config
->FindB("dpkg::remove::crossgrade::implicit", true) == true)
1534 std::unordered_set
<decltype(pkgCache::Package::ID
)> crossgraded
;
1535 std::vector
<std::pair
<Item
*, std::string
>> toCrossgrade
;
1536 auto const PlanedEnd
= std::next(List
.begin(), explicitIdx
);
1537 for (auto I
= List
.begin(); I
!= PlanedEnd
; ++I
)
1539 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1542 auto const Grp
= I
->Pkg
.Group();
1543 size_t installedInstances
= 0, wannabeInstances
= 0;
1544 bool multiArchInstances
= false;
1545 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1547 if (Pkg
->CurrentVer
!= 0)
1549 ++installedInstances
;
1550 if (Cache
[Pkg
].Delete() == false)
1553 else if (PackageOps
.find(Pkg
.FullName()) != PackageOps
.end())
1555 if (multiArchInstances
== false)
1557 auto const V
= Cache
[Pkg
].InstVerIter(Cache
);
1558 if (V
.end() == false && (Pkg
->CurrentVer
== 0 || V
!= Pkg
.CurrentVer()))
1559 multiArchInstances
= ((V
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
);
1562 /* theoretically the installed check would be enough as some wannabe will
1563 be first and hence be the crossgrade we were looking for, but #844300
1564 prevents this so we keep these situations explicit removes.
1565 It is also the reason why neither of them can be a M-A:same package */
1566 if (installedInstances
== 1 && wannabeInstances
== 1 && multiArchInstances
== false)
1568 auto const FirstInstall
= std::find_if_not(I
, List
.end(),
1569 [](Item
const &i
) { return i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
; });
1570 auto const LastInstall
= std::find_if_not(FirstInstall
, List
.end(),
1571 [](Item
const &i
) { return i
.Op
== Item::Install
; });
1572 auto const crosser
= std::find_if(FirstInstall
, LastInstall
,
1573 [&I
](Item
const &i
) { return i
.Pkg
->Group
== I
->Pkg
->Group
; });
1574 if (crosser
!= LastInstall
)
1576 crossgraded
.insert(I
->Pkg
->ID
);
1577 toCrossgrade
.emplace_back(&(*I
), crosser
->Pkg
.FullName());
1581 for (auto I
= PlanedEnd
; I
!= List
.end(); ++I
)
1583 if (I
->Op
!= Item::Remove
&& I
->Op
!= Item::Purge
)
1586 auto const Grp
= I
->Pkg
.Group();
1587 for (auto Pkg
= Grp
.PackageList(); Pkg
.end() == false; Pkg
= Grp
.NextPkg(Pkg
))
1589 if (Pkg
== I
->Pkg
|| Cache
[Pkg
].Install() == false)
1591 toCrossgrade
.emplace_back(&(*I
), Pkg
.FullName());
1595 for (auto C
: toCrossgrade
)
1597 // we never do purges on packages which are crossgraded, even if "requested"
1598 if (C
.first
->Op
== Item::Purge
)
1600 C
.first
->Op
= Item::Remove
; // crossgrades should never be purged
1601 auto && Purges
= approvedStates
.Purge();
1602 auto const Ver
= std::find_if(
1603 #if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
1604 Purges
.cbegin(), Purges
.cend(),
1606 Purges
.begin(), Purges
.end(),
1608 [&C
](pkgCache::VerIterator
const &V
) { return V
.ParentPkg() == C
.first
->Pkg
; });
1609 approvedStates
.Remove(*Ver
);
1611 auto && RemOp
= PackageOps
[C
.first
->Pkg
.FullName()];
1612 if (RemOp
.size() == 5)
1614 RemOp
.erase(std::next(RemOp
.begin(), 3), RemOp
.end());
1618 _error
->Warning("Unexpected amount of planned ops for package %s: %lu", C
.first
->Pkg
.FullName().c_str(), RemOp
.size());
1621 if (crossgraded
.empty() == false)
1623 auto const oldsize
= List
.size();
1624 List
.erase(std::remove_if(List
.begin(), PlanedEnd
,
1625 [&crossgraded
](Item
const &i
){
1626 return (i
.Op
== Item::Remove
|| i
.Op
== Item::Purge
) &&
1627 crossgraded
.find(i
.Pkg
->ID
) != crossgraded
.end();
1629 explicitIdx
-= (oldsize
- List
.size());
1633 APT::StateChanges currentStates
;
1634 if (_config
->FindB("dpkg::selection::current::saveandrestore", true))
1636 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1637 if (Pkg
->CurrentVer
== 0)
1639 else if (Pkg
->SelectedState
== pkgCache::State::Purge
)
1640 currentStates
.Purge(FindToBeRemovedVersion(Pkg
));
1641 else if (Pkg
->SelectedState
== pkgCache::State::DeInstall
)
1642 currentStates
.Remove(FindToBeRemovedVersion(Pkg
));
1643 if (currentStates
.empty() == false)
1645 APT::StateChanges cleanStates
;
1646 for (auto && P
: currentStates
.Remove())
1647 cleanStates
.Install(P
);
1648 for (auto && P
: currentStates
.Purge())
1649 cleanStates
.Install(P
);
1650 if (cleanStates
.Save(false) == false)
1651 return _error
->Error("Couldn't clean the currently selected dpkg states");
1655 if (_config
->FindB("dpkg::selection::remove::approved", true))
1657 if (approvedStates
.Save(false) == false)
1659 _error
->Error("Couldn't record the approved state changes as dpkg selection states");
1660 if (currentStates
.Save(false) == false)
1661 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1665 List
.erase(std::next(List
.begin(), explicitIdx
), List
.end());
1667 std::vector
<bool> toBeRemoved(Cache
.Head().PackageCount
, false);
1668 for (auto && I
: approvedStates
.Remove())
1669 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1670 for (auto && I
: approvedStates
.Purge())
1671 toBeRemoved
[I
.ParentPkg()->ID
] = true;
1673 for (auto && I
: List
)
1674 if (I
.Op
== Item::Remove
|| I
.Op
== Item::Purge
)
1675 toBeRemoved
[I
.Pkg
->ID
] = false;
1677 bool const RemovePending
= std::find(toBeRemoved
.begin(), toBeRemoved
.end(), true) != toBeRemoved
.end();
1678 bool const PurgePending
= approvedStates
.Purge().empty() == false;
1679 if (RemovePending
!= false || PurgePending
!= false)
1680 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1682 List
.emplace_back(Item::RemovePending
, pkgCache::PkgIterator());
1684 List
.emplace_back(Item::PurgePending
, pkgCache::PkgIterator());
1686 // support subpressing of triggers processing for special
1687 // cases like d-i that runs the triggers handling manually
1688 if (_config
->FindB("DPkg::ConfigurePending", true))
1689 List
.emplace_back(Item::ConfigurePending
, pkgCache::PkgIterator());
1691 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1693 d
->stdin_is_dev_null
= false;
1698 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1700 // start pty magic before the loop
1703 // Tell the progress that its starting and fork dpkg
1704 d
->progress
->Start(d
->master
);
1706 // this loop is runs once per dpkg operation
1707 vector
<Item
>::const_iterator I
= List
.cbegin();
1708 while (I
!= List
.end())
1710 // Do all actions with the same Op in one run
1711 vector
<Item
>::const_iterator J
= I
;
1712 if (TriggersPending
== true)
1713 for (; J
!= List
.end(); ++J
)
1717 if (J
->Op
!= Item::TriggersPending
)
1719 vector
<Item
>::const_iterator T
= J
+ 1;
1720 if (T
!= List
.end() && T
->Op
== I
->Op
)
1724 else if (J
->Op
== Item::Remove
|| J
->Op
== Item::Purge
)
1725 J
= std::find_if(J
, List
.cend(), [](Item
const &I
) { return I
.Op
!= Item::Remove
&& I
.Op
!= Item::Purge
; });
1727 J
= std::find_if(J
, List
.cend(), [&J
](Item
const &I
) { return I
.Op
!= J
->Op
; });
1729 auto const size
= (J
- I
) + 10;
1731 // start with the baseset of arguments
1732 auto Size
= StartSize
;
1733 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1735 // keep track of allocated strings for multiarch package names
1736 std::vector
<char *> Packages(size
, nullptr);
1740 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1742 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1743 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1745 ADDARGC("--status-fd");
1746 char status_fd_buf
[20];
1747 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1748 ADDARG(status_fd_buf
);
1749 unsigned long const Op
= I
->Op
;
1751 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1752 (I
->Op
!= Item::ConfigurePending
|| std::next(I
) != List
.end()))
1754 ADDARGC("--no-triggers");
1761 ADDARGC("--force-depends");
1762 ADDARGC("--force-remove-reinstreq");
1763 if (std::any_of(I
, J
, ItemIsEssential
))
1764 ADDARGC("--force-remove-essential");
1765 ADDARGC("--remove");
1768 case Item::Configure
:
1769 ADDARGC("--configure");
1772 case Item::ConfigurePending
:
1773 ADDARGC("--configure");
1774 ADDARGC("--pending");
1777 case Item::TriggersPending
:
1778 ADDARGC("--triggers-only");
1779 ADDARGC("--pending");
1782 case Item::RemovePending
:
1783 ADDARGC("--remove");
1784 ADDARGC("--pending");
1787 case Item::PurgePending
:
1789 ADDARGC("--pending");
1793 ADDARGC("--unpack");
1794 ADDARGC("--auto-deconfigure");
1798 char * tmpdir_to_free
= nullptr;
1800 // Write in the file or package names
1801 if (I
->Op
== Item::Install
)
1803 auto const installsToDo
= J
- I
;
1804 if (dpkg_recursive_install
== true && dpkg_recursive_install_min
< installsToDo
)
1807 strprintf(tmpdir
, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
1808 tmpdir_to_free
= strndup(tmpdir
.data(), tmpdir
.length());
1809 if (mkdtemp(tmpdir_to_free
) == nullptr)
1810 return _error
->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free
);
1813 for (auto c
= installsToDo
- 1; (c
= c
/10) != 0; ++p
);
1814 for (unsigned long n
= 0; I
!= J
; ++n
, ++I
)
1816 if (I
->File
[0] != '/')
1817 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1818 auto file
= flNotDir(I
->File
);
1819 if (flExtension(file
) != "deb")
1820 file
.append(".deb");
1821 std::string linkpath
;
1822 if (dpkg_recursive_install_numbered
)
1823 strprintf(linkpath
, "%s/%.*lu-%s", tmpdir_to_free
, p
, n
, file
.c_str());
1825 strprintf(linkpath
, "%s/%s", tmpdir_to_free
, file
.c_str());
1826 if (symlink(I
->File
.c_str(), linkpath
.c_str()) != 0)
1827 return _error
->Errno("DPkg::Go", "Symlinking %s to %s failed!", I
->File
.c_str(), linkpath
.c_str());
1829 ADDARGC("--recursive");
1830 ADDARG(tmpdir_to_free
);
1834 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1836 if (I
->File
[0] != '/')
1837 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1838 Args
.push_back(I
->File
.c_str());
1839 Size
+= I
->File
.length();
1843 else if (I
->Op
== Item::RemovePending
)
1846 StripAlreadyDoneFrom(approvedStates
.Remove());
1847 if (approvedStates
.Remove().empty())
1850 else if (I
->Op
== Item::PurgePending
)
1853 // explicit removes of packages without conffiles passthrough the purge states instantly, too.
1854 // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg
1855 StripAlreadyDoneFrom(approvedStates
.Purge());
1856 if (approvedStates
.Purge().empty())
1858 std::remove_reference
<decltype(approvedStates
.Remove())>::type approvedRemoves
;
1859 std::swap(approvedRemoves
, approvedStates
.Remove());
1860 // we apply it again here as an explicit remove in the ordering will have cleared the purge state
1861 if (approvedStates
.Save(false) == false)
1863 _error
->Error("Couldn't record the approved purges as dpkg selection states");
1864 if (currentStates
.Save(false) == false)
1865 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1868 std::swap(approvedRemoves
, approvedStates
.Remove());
1872 string
const nativeArch
= _config
->Find("APT::Architecture");
1873 unsigned long const oldSize
= I
->Pkg
.end() == false ? Size
: 0;
1874 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1876 if((*I
).Pkg
.end() == true)
1878 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1880 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1881 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1882 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1883 strcmp(I
->Pkg
.Arch(), "none") == 0))
1885 char const * const name
= I
->Pkg
.Name();
1890 pkgCache::VerIterator PkgVer
;
1891 std::string name
= I
->Pkg
.Name();
1892 if (Op
== Item::Remove
)
1893 PkgVer
= I
->Pkg
.CurrentVer();
1894 else if (Op
== Item::Purge
)
1896 // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here
1897 PkgVer
= I
->Pkg
.CurrentVer();
1898 if (PkgVer
.end() == true)
1902 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1903 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1904 ; // never arch-qualify a package without an arch
1905 else if (PkgVer
.end() == false)
1906 name
.append(":").append(PkgVer
.Arch());
1908 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1909 char * const fullname
= strdup(name
.c_str());
1910 Packages
.push_back(fullname
);
1914 // skip configure action if all sheduled packages disappeared
1915 if (oldSize
== Size
)
1923 if (noopDPkgInvocation
== true)
1925 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1926 a
!= Args
.end(); ++a
)
1929 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1930 p
!= Packages
.end(); ++p
)
1935 cleanUpTmpDir(tmpdir_to_free
);
1938 Args
.push_back(NULL
);
1944 /* Mask off sig int/quit. We do this because dpkg also does when
1945 it forks scripts. What happens is that when you hit ctrl-c it sends
1946 it to all processes in the group. Since dpkg ignores the signal
1947 it doesn't die but we do! So we must also ignore it */
1948 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1949 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1951 // Check here for any SIGINT
1952 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1955 // ignore SIGHUP as well (debian #463030)
1956 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1959 d
->progress
->StartDpkg();
1960 std::set
<int> KeepFDs
;
1961 KeepFDs
.insert(fd
[1]);
1962 MergeKeepFdsFromConfiguration(KeepFDs
);
1963 pid_t Child
= ExecFork(KeepFDs
);
1966 // This is the child
1967 SetupSlavePtyMagic();
1968 close(fd
[0]); // close the read end of the pipe
1970 debSystem::DpkgChrootDirectory();
1972 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1975 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1979 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1982 // Discard everything in stdin before forking dpkg
1983 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1986 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1988 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1992 // if color support isn't enabled/disabled explicitly tell
1993 // dpkg to use the same state apt is using for its color support
1994 if (_config
->FindB("APT::Color", false) == true)
1995 setenv("DPKG_COLORS", "always", 0);
1997 setenv("DPKG_COLORS", "never", 0);
1999 execvp(Args
[0], (char**) &Args
[0]);
2000 cerr
<< "Could not exec dpkg!" << endl
;
2004 // we read from dpkg here
2005 int const _dpkgin
= fd
[0];
2006 close(fd
[1]); // close the write end of the pipe
2009 if (_config
->FindB("DPkg::UseIoNice", false) == true)
2013 sigemptyset(&d
->sigmask
);
2014 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
2016 /* free vectors (and therefore memory) as we don't need the included data anymore */
2017 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
2018 p
!= Packages
.end(); ++p
)
2022 // the result of the waitpid call
2025 bool waitpid_failure
= false;
2026 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
2028 // error handling, waitpid returned -1
2031 waitpid_failure
= true;
2035 // wait for input or output here
2037 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
2038 FD_SET(STDIN_FILENO
, &rfds
);
2039 FD_SET(_dpkgin
, &rfds
);
2041 FD_SET(d
->master
, &rfds
);
2043 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
2044 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
2045 &tv
, &d
->original_sigmask
);
2046 d
->progress
->Pulse();
2047 if (select_ret
== 0)
2049 else if (select_ret
< 0 && errno
== EINTR
)
2051 else if (select_ret
< 0)
2053 perror("select() returned error");
2057 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
2058 DoTerminalPty(d
->master
);
2059 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
2061 if(FD_ISSET(_dpkgin
, &rfds
))
2062 DoDpkgStatusFd(_dpkgin
);
2066 // Restore sig int/quit
2067 signal(SIGQUIT
,old_SIGQUIT
);
2068 signal(SIGINT
,old_SIGINT
);
2069 signal(SIGHUP
,old_SIGHUP
);
2071 cleanUpTmpDir(tmpdir_to_free
);
2073 if (waitpid_failure
== true)
2075 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
2076 _error
->Error("%s", d
->dpkg_error
.c_str());
2080 // Check for an error code.
2081 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
2083 // if it was set to "keep-dpkg-running" then we won't return
2084 // here but keep the loop going and just report it as a error
2086 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
2088 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
2089 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
2090 else if (WIFEXITED(Status
) != 0)
2091 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
2093 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
2094 _error
->Error("%s", d
->dpkg_error
.c_str());
2100 // dpkg is done at this point
2104 if (d
->dpkg_error
.empty() == false)
2106 // no point in reseting packages we already completed removal for
2107 StripAlreadyDoneFrom(approvedStates
.Remove());
2108 StripAlreadyDoneFrom(approvedStates
.Purge());
2109 APT::StateChanges undo
;
2110 auto && undoRem
= approvedStates
.Remove();
2111 std::move(undoRem
.begin(), undoRem
.end(), std::back_inserter(undo
.Install()));
2112 auto && undoPur
= approvedStates
.Purge();
2113 std::move(undoPur
.begin(), undoPur
.end(), std::back_inserter(undo
.Install()));
2114 approvedStates
.clear();
2115 if (undo
.Save(false) == false)
2116 _error
->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
2119 StripAlreadyDoneFrom(currentStates
.Remove());
2120 StripAlreadyDoneFrom(currentStates
.Purge());
2121 if (currentStates
.Save(false) == false)
2122 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
2124 if (pkgPackageManager::SigINTStop
)
2125 _error
->Warning(_("Operation was interrupted before it could finish"));
2127 if (noopDPkgInvocation
== false)
2129 if (d
->dpkg_error
.empty() && (PackagesDone
+ 1) != PackagesTotal
)
2131 std::string pkglist
;
2132 for (auto const &PO
: PackageOps
)
2133 if (PO
.second
.size() != PackageOpsDone
[PO
.first
])
2135 if (pkglist
.empty() == false)
2136 pkglist
.append(" ");
2137 pkglist
.append(PO
.first
);
2139 /* who cares about correct progress? As we depend on it for skipping actions
2140 our parsing should be correct. People will no doubt be confused if they see
2141 this message, but the dpkg warning about unknown packages isn't much better
2142 from a user POV and combined we might have a chance to figure out what is wrong */
2143 _error
->Warning("APT had planned for dpkg to do more than it reported back (%u vs %u).\n"
2144 "Affected packages: %s", PackagesDone
, PackagesTotal
, pkglist
.c_str());
2147 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
2148 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
2149 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
2151 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
2152 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
2154 _error
->PushToStack();
2155 pkgCacheFile CacheFile
;
2156 CacheFile
.BuildCaches(NULL
, true);
2157 _error
->RevertToStack();
2162 // disappearing packages can forward their auto-bit
2163 if (disappearedPkgs
.empty() == false)
2164 Cache
.writeStateFile(NULL
);
2166 d
->progress
->Stop();
2168 if (RunScripts("DPkg::Post-Invoke") == false)
2171 return d
->dpkg_error
.empty();
2174 void SigINT(int /*sig*/) {
2175 pkgPackageManager::SigINTStop
= true;
2178 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
2179 // ---------------------------------------------------------------------
2181 void pkgDPkgPM::Reset()
2183 List
.erase(List
.begin(),List
.end());
2186 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
2187 // ---------------------------------------------------------------------
2189 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
2191 // If apport doesn't exist or isn't installed do nothing
2192 // This e.g. prevents messages in 'universes' without apport
2193 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
2194 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
2197 string pkgname
, reportfile
, pkgver
, arch
;
2198 string::size_type pos
;
2201 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
2203 std::clog
<< "configured to not write apport reports" << std::endl
;
2207 // only report the first errors
2208 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
2210 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
2214 // check if its not a follow up error
2215 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
2216 if(strstr(errormsg
, needle
) != NULL
) {
2217 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
2221 // do not report disk-full failures
2222 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
2223 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
2227 // do not report out-of-memory failures
2228 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
2229 strstr(errormsg
, "failed to allocate memory") != NULL
) {
2230 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
2234 // do not report bugs regarding inaccessible local files
2235 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
2236 strstr(errormsg
, "cannot access archive") != NULL
) {
2237 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2241 // do not report errors encountered when decompressing packages
2242 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
2243 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
2247 // do not report dpkg I/O errors, this is a format string, so we compare
2248 // the prefix and the suffix of the error with the dpkg error message
2249 vector
<string
> io_errors
;
2250 io_errors
.push_back(string("failed to read"));
2251 io_errors
.push_back(string("failed to write"));
2252 io_errors
.push_back(string("failed to seek"));
2253 io_errors
.push_back(string("unexpected end of file or stream"));
2255 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
2257 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
2258 if (list
.size() > 1) {
2259 // we need to split %s, VectorizeString only allows char so we need
2260 // to kill the "s" manually
2261 if (list
[1].size() > 1) {
2262 list
[1].erase(0, 1);
2263 if(strstr(errormsg
, list
[0].c_str()) &&
2264 strstr(errormsg
, list
[1].c_str())) {
2265 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
2272 // get the pkgname and reportfile
2273 pkgname
= flNotDir(pkgpath
);
2274 pos
= pkgname
.find('_');
2275 if(pos
!= string::npos
)
2276 pkgname
= pkgname
.substr(0, pos
);
2278 // find the package version and source package name
2279 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
2280 if (Pkg
.end() == true)
2282 if (pos
== std::string::npos
|| _config
->FindB("dpkg::install::recursive::numbered", true) == false)
2284 auto const dash
= pkgname
.find_first_not_of("0123456789");
2285 if (dash
== std::string::npos
|| pkgname
[dash
] != '-')
2287 pkgname
.erase(0, dash
+ 1);
2288 Pkg
= Cache
.FindPkg(pkgname
);
2289 if (Pkg
.end() == true)
2292 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
2293 if (Ver
.end() == true)
2295 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
2297 // if the file exists already, we check:
2298 // - if it was reported already (touched by apport).
2299 // If not, we do nothing, otherwise
2300 // we overwrite it. This is the same behaviour as apport
2301 // - if we have a report with the same pkgversion already
2303 _config
->CndSet("Dir::Apport", "var/crash");
2304 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
2305 if(FileExists(reportfile
))
2310 // check atime/mtime
2311 stat(reportfile
.c_str(), &buf
);
2312 if(buf
.st_mtime
> buf
.st_atime
)
2315 // check if the existing report is the same version
2316 report
= fopen(reportfile
.c_str(),"r");
2317 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
2319 if(strstr(strbuf
,"Package:") == strbuf
)
2321 char pkgname
[255], version
[255];
2322 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
2323 if(strcmp(pkgver
.c_str(), version
) == 0)
2333 // now write the report
2334 arch
= _config
->Find("APT::Architecture");
2335 report
= fopen(reportfile
.c_str(),"w");
2338 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
2339 chmod(reportfile
.c_str(), 0);
2341 chmod(reportfile
.c_str(), 0600);
2342 fprintf(report
, "ProblemType: Package\n");
2343 fprintf(report
, "Architecture: %s\n", arch
.c_str());
2344 time_t now
= time(NULL
);
2345 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
2346 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
2347 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
2348 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
2349 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
2351 // ensure that the log is flushed
2353 fflush(d
->term_out
);
2355 // attach terminal log it if we have it
2356 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
2357 if (!logfile_name
.empty())
2361 fprintf(report
, "DpkgTerminalLog:\n");
2362 log
= fopen(logfile_name
.c_str(),"r");
2366 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2367 fprintf(report
, " %s", buf
);
2368 fprintf(report
, " \n");
2373 // attach history log it if we have it
2374 string histfile_name
= _config
->FindFile("Dir::Log::History");
2375 if (!histfile_name
.empty())
2377 fprintf(report
, "DpkgHistoryLog:\n");
2378 FILE* log
= fopen(histfile_name
.c_str(),"r");
2382 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2383 fprintf(report
, " %s", buf
);
2388 // log the ordering, see dpkgpm.h and the "Ops" enum there
2389 fprintf(report
, "AptOrdering:\n");
2390 for (auto && I
: List
)
2392 char const * opstr
= nullptr;
2395 case Item::Install
: opstr
= "Install"; break;
2396 case Item::Configure
: opstr
= "Configure"; break;
2397 case Item::Remove
: opstr
= "Remove"; break;
2398 case Item::Purge
: opstr
= "Purge"; break;
2399 case Item::ConfigurePending
: opstr
= "ConfigurePending"; break;
2400 case Item::TriggersPending
: opstr
= "TriggersPending"; break;
2401 case Item::RemovePending
: opstr
= "RemovePending"; break;
2402 case Item::PurgePending
: opstr
= "PurgePending"; break;
2404 auto const pkgname
= I
.Pkg
.end() ? "NULL" : I
.Pkg
.FullName();
2405 fprintf(report
, " %s: %s\n", pkgname
.c_str(), opstr
);
2408 // attach dmesg log (to learn about segfaults)
2409 if (FileExists("/bin/dmesg"))
2411 fprintf(report
, "Dmesg:\n");
2412 FILE *log
= popen("/bin/dmesg","r");
2416 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2417 fprintf(report
, " %s", buf
);
2422 // attach df -l log (to learn about filesystem status)
2423 if (FileExists("/bin/df"))
2426 fprintf(report
, "Df:\n");
2427 FILE *log
= popen("/bin/df -l","r");
2431 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2432 fprintf(report
, " %s", buf
);