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/cacheiterators.h>
23 #include <apt-pkg/macros.h>
24 #include <apt-pkg/pkgcache.h>
34 #include <sys/ioctl.h>
35 #include <sys/select.h>
60 APT_PURE
static string
AptHistoryRequestingUser() /*{{{*/
62 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
64 for (const auto &Key
: EnvKeys
)
66 if (getenv(Key
) != nullptr)
68 int uid
= atoi(getenv(Key
));
71 struct passwd
*result
;
73 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
75 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
84 APT_PURE
static unsigned int EnvironmentSize() /*{{{*/
86 unsigned int size
= 0;
87 char **envp
= environ
;
90 size
+= strlen (*envp
++) + 1;
95 class pkgDPkgPMPrivate
/*{{{*/
98 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
99 term_out(NULL
), history_out(NULL
),
100 progress(NULL
), tt_is_valid(false), master(-1),
101 slave(NULL
), protect_slave_from_dying(-1),
109 bool stdin_is_dev_null
;
110 // the buffer we use for the dpkg status-fd reading
116 APT::Progress::PackageManager
*progress
;
123 int protect_slave_from_dying
;
127 sigset_t original_sigmask
;
134 // Maps the dpkg "processing" info to human readable names. Entry 0
135 // of each array is the key, entry 1 is the value.
136 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
137 std::make_pair("install", N_("Installing %s")),
138 std::make_pair("configure", N_("Configuring %s")),
139 std::make_pair("remove", N_("Removing %s")),
140 std::make_pair("purge", N_("Completely removing %s")),
141 std::make_pair("disappear", N_("Noting disappearance of %s")),
142 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
145 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
146 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
148 // Predicate to test whether an entry in the PackageProcessingOps
149 // array matches a string.
150 class MatchProcessingOp
155 explicit MatchProcessingOp(const char *the_target
)
160 bool operator()(const std::pair
<const char *, const char *> &pair
) const
162 return strcmp(pair
.first
, target
) == 0;
167 // ionice - helper function to ionice the given PID /*{{{*/
168 /* there is no C header for ionice yet - just the syscall interface
169 so we use the binary from util-linux */
170 static bool ionice(int PID
)
172 if (!FileExists("/usr/bin/ionice"))
174 pid_t Process
= ExecFork();
178 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
180 Args
[0] = "/usr/bin/ionice";
184 execv(Args
[0], (char **)Args
);
186 return ExecWait(Process
, "ionice");
189 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
190 // ---------------------------------------------------------------------
191 /* This is helpful when a package is no longer installed but has residual
195 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
197 pkgCache::VerIterator Ver
;
198 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
199 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
200 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
202 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
209 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
210 // ---------------------------------------------------------------------
212 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
213 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
217 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
218 // ---------------------------------------------------------------------
220 pkgDPkgPM::~pkgDPkgPM()
225 // DPkgPM::Install - Install a package /*{{{*/
226 // ---------------------------------------------------------------------
227 /* Add an install operation to the sequence list */
228 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
230 if (File
.empty() == true || Pkg
.end() == true)
231 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
233 // If the filename string begins with DPkg::Chroot-Directory, return the
234 // substr that is within the chroot so dpkg can access it.
235 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
236 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
238 size_t len
= chrootdir
.length();
239 if (chrootdir
.at(len
- 1) == '/')
241 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
244 List
.push_back(Item(Item::Install
,Pkg
,File
));
249 // DPkgPM::Configure - Configure a package /*{{{*/
250 // ---------------------------------------------------------------------
251 /* Add a configure operation to the sequence list */
252 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
254 if (Pkg
.end() == true)
257 List
.push_back(Item(Item::Configure
, Pkg
));
259 // Use triggers for config calls if we configure "smart"
260 // as otherwise Pre-Depends will not be satisfied, see #526774
261 if (_config
->FindB("DPkg::TriggersPending", false) == true)
262 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
267 // DPkgPM::Remove - Remove a package /*{{{*/
268 // ---------------------------------------------------------------------
269 /* Add a remove operation to the sequence list */
270 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
272 if (Pkg
.end() == true)
276 List
.push_back(Item(Item::Purge
,Pkg
));
278 List
.push_back(Item(Item::Remove
,Pkg
));
282 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
283 // ---------------------------------------------------------------------
284 /* This is part of the helper script communication interface, it sends
285 very complete information down to the other end of the pipe.*/
286 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
288 return SendPkgsInfo(F
, 2);
290 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
292 // This version of APT supports only v3, so don't sent higher versions
294 fprintf(F
,"VERSION %u\n", Version
);
296 fprintf(F
,"VERSION 3\n");
298 /* Write out all of the configuration directives by walking the
299 configuration tree */
300 const Configuration::Item
*Top
= _config
->Tree(0);
303 if (Top
->Value
.empty() == false)
306 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
307 QuoteString(Top
->Value
,"\n").c_str());
316 while (Top
!= 0 && Top
->Next
== 0)
323 // Write out the package actions in order.
324 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
326 if(I
->Pkg
.end() == true)
329 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
331 fprintf(F
,"%s ",I
->Pkg
.Name());
333 // Current version which we are going to replace
334 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
335 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
336 CurVer
= FindNowVersion(I
->Pkg
);
338 if (CurVer
.end() == true)
343 fprintf(F
, "- - none ");
347 fprintf(F
, "%s ", CurVer
.VerStr());
349 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
352 // Show the compare operator between current and install version
353 if (S
.InstallVer
!= 0)
355 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
357 if (CurVer
.end() == false)
358 Comp
= InstVer
.CompareVer(CurVer
);
365 fprintf(F
, "%s ", InstVer
.VerStr());
367 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
374 fprintf(F
, "> - - none ");
377 // Show the filename/operation
378 if (I
->Op
== Item::Install
)
381 if (I
->File
[0] != '/')
382 fprintf(F
,"**ERROR**\n");
384 fprintf(F
,"%s\n",I
->File
.c_str());
386 else if (I
->Op
== Item::Configure
)
387 fprintf(F
,"**CONFIGURE**\n");
388 else if (I
->Op
== Item::Remove
||
389 I
->Op
== Item::Purge
)
390 fprintf(F
,"**REMOVE**\n");
398 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
399 // ---------------------------------------------------------------------
400 /* This looks for a list of scripts to run from the configuration file
401 each one is run and is fed on standard input a list of all .deb files
402 that are due to be installed. */
403 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
406 static bool interrupted
= false;
408 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
409 if (Opts
== 0 || Opts
->Child
== 0)
413 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
414 sighandler_t old_sigint
= signal(SIGINT
, [](int signum
){
418 unsigned int Count
= 1;
419 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
421 if (Opts
->Value
.empty() == true)
424 if(_config
->FindB("Debug::RunScripts", false) == true)
425 std::clog
<< "Running external script with list of all .deb file: '"
426 << Opts
->Value
<< "'" << std::endl
;
428 // Determine the protocol version
429 string OptSec
= Opts
->Value
;
430 string::size_type Pos
;
431 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
432 Pos
= OptSec
.length();
433 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
435 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
436 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
439 std::set
<int> KeepFDs
;
440 MergeKeepFdsFromConfiguration(KeepFDs
);
442 if (pipe(Pipes
) != 0) {
443 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
446 if (InfoFD
!= (unsigned)Pipes
[0])
447 SetCloseExec(Pipes
[0],true);
449 KeepFDs
.insert(Pipes
[0]);
452 SetCloseExec(Pipes
[1],true);
454 // Purified Fork for running the script
455 pid_t Process
= ExecFork(KeepFDs
);
459 dup2(Pipes
[0], InfoFD
);
460 SetCloseExec(STDOUT_FILENO
,false);
461 SetCloseExec(STDIN_FILENO
,false);
462 SetCloseExec(STDERR_FILENO
,false);
465 strprintf(hookfd
, "%d", InfoFD
);
466 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
468 debSystem::DpkgChrootDirectory();
472 Args
[2] = Opts
->Value
.c_str();
474 execv(Args
[0],(char **)Args
);
478 FILE *F
= fdopen(Pipes
[1],"w");
480 result
= _error
->Errno("fdopen","Failed to open new FD");
484 // Feed it the filenames.
487 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
489 // Only deal with packages to be installed from .deb
490 if (I
->Op
!= Item::Install
)
494 if (I
->File
[0] != '/')
497 /* Feed the filename of each package that is pending install
499 fprintf(F
,"%s\n",I
->File
.c_str());
505 SendPkgsInfo(F
, Version
);
509 // Clean up the sub process
510 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
511 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
515 signal(SIGINT
, old_sigint
);
516 signal(SIGPIPE
, old_sigpipe
);
519 result
= _error
->Error("Interrupted");
524 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
525 // ---------------------------------------------------------------------
528 void pkgDPkgPM::DoStdin(int master
)
530 unsigned char input_buf
[256] = {0,};
531 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
533 FileFd::Write(master
, input_buf
, len
);
535 d
->stdin_is_dev_null
= true;
538 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
539 // ---------------------------------------------------------------------
541 * read the terminal pty and write log
543 void pkgDPkgPM::DoTerminalPty(int master
)
545 unsigned char term_buf
[1024] = {0,0, };
547 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
548 if(len
== -1 && errno
== EIO
)
550 // this happens when the child is about to exit, we
551 // give it time to actually exit, otherwise we run
552 // into a race so we sleep for half a second.
553 struct timespec sleepfor
= { 0, 500000000 };
554 nanosleep(&sleepfor
, NULL
);
559 FileFd::Write(1, term_buf
, len
);
561 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
564 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
565 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
567 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
569 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
571 /* dpkg sends strings like this:
572 'status: <pkg>: <pkg qstate>'
573 'status: <pkg>:<arch>: <pkg qstate>'
575 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
576 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
579 // we need to split on ": " (note the appended space) as the ':' is
580 // part of the pkgname:arch information that dpkg sends
582 // A dpkg error message may contain additional ":" (like
583 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
584 // so we need to ensure to not split too much
585 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
589 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
593 // build the (prefix, pkgname, action) tuple, position of this
594 // is different for "processing" or "status" messages
595 std::string prefix
= APT::String::Strip(list
[0]);
599 // "processing" has the form "processing: action: pkg or trigger"
600 // with action = ["install", "upgrade", "configure", "remove", "purge",
601 // "disappear", "trigproc"]
602 if (prefix
== "processing")
604 pkgname
= APT::String::Strip(list
[2]);
605 action
= APT::String::Strip(list
[1]);
606 // we don't care for the difference (as dpkg doesn't really either)
607 if (action
== "upgrade")
610 // "status" has the form: "status: pkg: state"
611 // with state in ["half-installed", "unpacked", "half-configured",
612 // "installed", "config-files", "not-installed"]
613 else if (prefix
== "status")
615 pkgname
= APT::String::Strip(list
[1]);
616 action
= APT::String::Strip(list
[2]);
619 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
624 /* handle the special cases first:
626 errors look like this:
627 '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
628 and conffile-prompt like this
629 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
631 if (prefix
== "status")
633 if(action
== "error")
635 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
638 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
641 else if(action
== "conffile-prompt")
643 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
649 // at this point we know that we should have a valid pkgname, so build all
652 // dpkg does not always send "pkgname:arch" so we add it here if needed
653 if (pkgname
.find(":") == std::string::npos
)
655 // find the package in the group that is touched by dpkg
656 // if there are multiple pkgs dpkg would send us a full pkgname:arch
657 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
658 if (Grp
.end() == false)
660 pkgCache::PkgIterator P
= Grp
.PackageList();
661 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
663 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
665 pkgname
= P
.FullName();
672 const char* const pkg
= pkgname
.c_str();
673 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
674 std::string arch
= "";
675 if (pkgname
.find(":") != string::npos
)
676 arch
= StringSplit(pkgname
, ":")[1];
677 std::string i18n_pkgname
= pkgname
;
678 if (arch
.size() != 0)
679 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
681 // 'processing' from dpkg looks like
682 // 'processing: action: pkg'
683 if(prefix
== "processing")
685 const std::pair
<const char *, const char *> * const iter
=
686 std::find_if(PackageProcessingOpsBegin
,
687 PackageProcessingOpsEnd
,
688 MatchProcessingOp(action
.c_str()));
689 if(iter
== PackageProcessingOpsEnd
)
692 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
696 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
697 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
699 // FIXME: this needs a muliarch testcase
700 // FIXME2: is "pkgname" here reliable with dpkg only sending us
702 if (action
== "disappear")
703 handleDisappearAction(pkgname
);
707 if (prefix
== "status")
709 std::vector
<struct DpkgState
> &states
= PackageOps
[pkg
];
710 if (action
== "triggers-pending")
713 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
714 << " action: " << action
<< " (prefix 2 to "
715 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
717 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
718 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
721 else if(PackageOpsDone
[pkg
] < states
.size())
723 char const * next_action
= states
[PackageOpsDone
[pkg
]].state
;
727 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
728 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
731 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
732 << " pending trigger defused by unpack" << std::endl;
733 // unpacking a package defuses the pending trigger
734 PackageOpsDone[pkg] += 2;
736 next_action = states[PackageOpsDone[pkg]].state;
740 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
741 << " action: " << action
<< " (expected: '" << next_action
<< "' "
742 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
744 // check if the package moved to the next dpkg state
745 if(action
== next_action
)
747 // only read the translation if there is actually a next action
748 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
750 // we moved from one dpkg state to a new one, report that
751 ++PackageOpsDone
[pkg
];
755 strprintf(msg
, translation
, i18n_pkgname
.c_str());
756 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
763 // DPkgPM::handleDisappearAction /*{{{*/
764 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
766 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
767 if (unlikely(Pkg
.end() == true))
770 // record the package name for display and stuff later
771 disappearedPkgs
.insert(Pkg
.FullName(true));
773 // the disappeared package was auto-installed - nothing to do
774 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
776 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
777 if (unlikely(PkgVer
.end() == true))
779 /* search in the list of dependencies for (Pre)Depends,
780 check if this dependency has a Replaces on our package
781 and if so transfer the manual installed flag to it */
782 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
784 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
785 Dep
->Type
!= pkgCache::Dep::PreDepends
)
787 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
788 if (unlikely(Tar
.end() == true))
790 // the package is already marked as manual
791 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
793 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
794 if (TarVer
.end() == true)
796 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
798 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
800 if (Pkg
!= Rep
.TargetPkg())
802 // okay, they are strongly connected - transfer manual-bit
804 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
805 Cache
[Tar
].Flags
&= ~Flag::Auto
;
811 // DPkgPM::DoDpkgStatusFd /*{{{*/
812 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
814 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
815 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
818 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
820 // process line by line from the buffer
821 char *p
= d
->dpkgbuf
, *q
= nullptr;
822 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
825 ProcessDpkgStatusLine(p
);
826 p
= q
+ 1; // continue with next line
829 // check if we stripped the buffer clean
830 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
836 // otherwise move the unprocessed tail to the start and update pos
837 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
838 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
841 // DPkgPM::WriteHistoryTag /*{{{*/
842 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
844 size_t const length
= value
.length();
847 // poor mans rstrip(", ")
848 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
849 value
.erase(length
- 2, 2);
850 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
852 // DPkgPM::OpenLog /*{{{*/
853 bool pkgDPkgPM::OpenLog()
855 string
const logdir
= _config
->FindDir("Dir::Log");
856 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
857 // FIXME: use a better string after freeze
858 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
862 time_t const t
= time(NULL
);
864 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
865 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
868 string
const logfile_name
= flCombine(logdir
,
869 _config
->Find("Dir::Log::Terminal"));
870 if (!logfile_name
.empty())
872 d
->term_out
= fopen(logfile_name
.c_str(),"a");
873 if (d
->term_out
== NULL
)
874 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
875 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
876 SetCloseExec(fileno(d
->term_out
), true);
877 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
879 struct passwd
*pw
= getpwnam("root");
880 struct group
*gr
= getgrnam("adm");
881 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
882 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
884 if (chmod(logfile_name
.c_str(), 0640) != 0)
885 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
886 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
889 // write your history
890 string
const history_name
= flCombine(logdir
,
891 _config
->Find("Dir::Log::History"));
892 if (!history_name
.empty())
894 d
->history_out
= fopen(history_name
.c_str(),"a");
895 if (d
->history_out
== NULL
)
896 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
897 SetCloseExec(fileno(d
->history_out
), true);
898 chmod(history_name
.c_str(), 0644);
899 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
900 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
901 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
903 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
905 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
906 if (Cache
[I
].NewInstall() == true)
907 HISTORYINFO(install
, CANDIDATE_AUTO
)
908 else if (Cache
[I
].ReInstall() == true)
909 HISTORYINFO(reinstall
, CANDIDATE
)
910 else if (Cache
[I
].Upgrade() == true)
911 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
912 else if (Cache
[I
].Downgrade() == true)
913 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
914 else if (Cache
[I
].Delete() == true)
915 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
919 line
->append(I
.FullName(false)).append(" (");
920 switch (infostring
) {
921 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
923 line
->append(Cache
[I
].CandVersion
);
924 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
925 line
->append(", automatic");
927 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
928 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
932 if (_config
->Exists("Commandline::AsString") == true)
933 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
934 std::string RequestingUser
= AptHistoryRequestingUser();
935 if (RequestingUser
!= "")
936 WriteHistoryTag("Requested-By", RequestingUser
);
937 WriteHistoryTag("Install", install
);
938 WriteHistoryTag("Reinstall", reinstall
);
939 WriteHistoryTag("Upgrade", upgrade
);
940 WriteHistoryTag("Downgrade",downgrade
);
941 WriteHistoryTag("Remove",remove
);
942 WriteHistoryTag("Purge",purge
);
943 fflush(d
->history_out
);
949 // DPkg::CloseLog /*{{{*/
950 bool pkgDPkgPM::CloseLog()
953 time_t t
= time(NULL
);
955 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
956 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
960 fprintf(d
->term_out
, "Log ended: ");
961 fprintf(d
->term_out
, "%s", timestr
);
962 fprintf(d
->term_out
, "\n");
969 if (disappearedPkgs
.empty() == false)
972 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
973 d
!= disappearedPkgs
.end(); ++d
)
975 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
976 disappear
.append(*d
);
978 disappear
.append(", ");
980 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
982 WriteHistoryTag("Disappeared", disappear
);
984 if (d
->dpkg_error
.empty() == false)
985 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
986 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
987 fclose(d
->history_out
);
989 d
->history_out
= NULL
;
995 // DPkgPM::BuildPackagesProgressMap /*{{{*/
996 void pkgDPkgPM::BuildPackagesProgressMap()
998 // map the dpkg states to the operations that are performed
999 // (this is sorted in the same way as Item::Ops)
1000 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1001 // Install operation
1003 {"half-installed", N_("Preparing %s")},
1004 {"unpacked", N_("Unpacking %s") },
1007 // Configure operation
1009 {"unpacked",N_("Preparing to configure %s") },
1010 {"half-configured", N_("Configuring %s") },
1011 { "installed", N_("Installed %s")},
1015 {"half-configured", N_("Preparing for removal of %s")},
1016 {"half-installed", N_("Removing %s")},
1017 {"config-files", N_("Removed %s")},
1021 {"config-files", N_("Preparing to completely remove %s")},
1022 {"not-installed", N_("Completely removed %s")},
1026 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1028 // init the PackageOps map, go over the list of packages that
1029 // that will be [installed|configured|removed|purged] and add
1030 // them to the PackageOps map (the dpkg states it goes through)
1031 // and the PackageOpsTranslations (human readable strings)
1032 for (auto &&I
: List
)
1034 if(I
.Pkg
.end() == true)
1037 string
const name
= I
.Pkg
.FullName();
1038 PackageOpsDone
[name
] = 0;
1039 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1040 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1042 // purging a package which is installed first passes through remove states
1043 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1044 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1045 PackagesTotal
+= DpkgOps
.size();
1047 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1048 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1049 if (state
.state
== nullptr)
1055 /* one extra: We don't want the progress bar to reach 100%, especially not
1056 if we call dpkg --configure --pending and process a bunch of triggers
1057 while showing 100%. Also, spindown takes a while, so never reaching 100%
1058 is way more correct than reaching 100% while still doing stuff even if
1059 doing it this way is slightly bending the rules */
1063 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1065 APT::Progress::PackageManager
*progress
= NULL
;
1067 progress
= APT::Progress::PackageManagerProgressFactory();
1069 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1071 return Go(progress
);
1074 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1076 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1079 if (d
->slave
!= NULL
)
1085 if (isatty(STDIN_FILENO
) == 0)
1086 d
->direct_stdin
= true;
1088 _error
->PushToStack();
1090 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1091 if (d
->master
== -1)
1092 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1093 else if (unlockpt(d
->master
) == -1)
1094 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1097 #ifdef HAVE_PTSNAME_R
1098 char slave_name
[64]; // 64 is used by bionic
1099 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1101 char const * const slave_name
= ptsname(d
->master
);
1102 if (slave_name
== NULL
)
1104 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1107 d
->slave
= strdup(slave_name
);
1108 if (d
->slave
== NULL
)
1109 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1110 else if (grantpt(d
->master
) == -1)
1111 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1112 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1114 d
->tt_is_valid
= true;
1115 struct termios raw_tt
;
1116 // copy window size of stdout if its a 'good' terminal
1117 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1120 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1121 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1122 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1123 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1125 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1126 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1130 raw_tt
.c_lflag
&= ~ECHO
;
1131 raw_tt
.c_lflag
|= ISIG
;
1132 // block SIGTTOU during tcsetattr to prevent a hang if
1133 // the process is a member of the background process group
1134 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1135 sigemptyset(&d
->sigmask
);
1136 sigaddset(&d
->sigmask
, SIGTTOU
);
1137 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1138 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1139 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1140 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1143 if (d
->slave
!= NULL
)
1145 /* on linux, closing (and later reopening) all references to the slave
1146 makes the slave a death end, so we open it here to have one open all
1147 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1148 on kfreebsd we get an incorrect ("step like") output then while it has
1149 no problem with closing all references… so to avoid platform specific
1150 code here we combine both and be happy once more */
1151 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1156 if (_error
->PendingError() == true)
1158 if (d
->master
!= -1)
1163 if (d
->slave
!= NULL
)
1168 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1170 _error
->RevertToStack();
1173 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1175 if(d
->master
== -1 || d
->slave
== NULL
)
1178 if (close(d
->master
) == -1)
1179 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1182 _error
->FatalE("setsid", "Starting a new session for child failed!");
1184 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1186 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1187 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1188 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1191 unsigned short i
= 0;
1192 if (d
->direct_stdin
== true)
1195 if (dup2(slaveFd
, i
) == -1)
1196 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1198 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1199 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1206 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1208 if (d
->slave
!= NULL
)
1211 if (d
->protect_slave_from_dying
!= -1)
1213 close(d
->protect_slave_from_dying
);
1214 d
->protect_slave_from_dying
= -1;
1218 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1219 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1225 // DPkgPM::Go - Run the sequence /*{{{*/
1226 // ---------------------------------------------------------------------
1227 /* This globs the operations and calls dpkg
1229 * If it is called with a progress object apt will report the install
1230 * progress to this object. It maps the dpkg states a package goes
1231 * through to human readable (and i10n-able)
1232 * names and calculates a percentage for each step.
1234 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1236 auto const ItemIsEssential
= [](pkgDPkgPM::Item
const &I
) {
1237 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1238 if (cachegen
== "none" || cachegen
== "native")
1240 if (unlikely(I
.Pkg
.end()))
1242 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1245 pkgPackageManager::SigINTStop
= false;
1246 d
->progress
= progress
;
1248 // Generate the base argument list for dpkg
1249 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1250 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1251 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1252 [](std::string
const &s
) { return s
.c_str(); });
1253 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1254 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1255 size_t const BaseArgs
= Args
.size();
1260 // try to figure out the max environment size
1261 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1264 OSArgMax
-= EnvironmentSize() - 2*1024;
1265 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1266 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1268 if (RunScripts("DPkg::Pre-Invoke") == false)
1271 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1274 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1275 // store auto-bits as they are supposed to be after dpkg is run
1276 if (noopDPkgInvocation
== false)
1277 Cache
.writeStateFile(NULL
);
1279 decltype(List
)::const_iterator::difference_type
const notconfidx
=
1280 _config
->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits
<decltype(notconfidx
)>::max() :
1281 std::distance(List
.cbegin(), std::find_if_not(List
.crbegin(), List
.crend(), [](Item
const &i
) { return i
.Op
== Item::Configure
; }).base());
1283 // support subpressing of triggers processing for special
1284 // cases like d-i that runs the triggers handling manually
1285 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1286 bool const ConfigurePending
= _config
->FindB("DPkg::ConfigurePending", true);
1287 if (ConfigurePending
)
1288 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1291 BuildPackagesProgressMap();
1293 if (notconfidx
!= std::numeric_limits
<decltype(notconfidx
)>::max())
1295 if (ConfigurePending
)
1296 List
.erase(std::next(List
.begin(), notconfidx
), std::prev(List
.end()));
1298 List
.erase(std::next(List
.begin(), notconfidx
), List
.end());
1301 d
->stdin_is_dev_null
= false;
1306 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1308 // start pty magic before the loop
1311 // Tell the progress that its starting and fork dpkg
1312 d
->progress
->Start(d
->master
);
1314 // this loop is runs once per dpkg operation
1315 vector
<Item
>::const_iterator I
= List
.begin();
1316 while (I
!= List
.end())
1318 // Do all actions with the same Op in one run
1319 vector
<Item
>::const_iterator J
= I
;
1320 if (TriggersPending
== true)
1321 for (; J
!= List
.end(); ++J
)
1325 if (J
->Op
!= Item::TriggersPending
)
1327 vector
<Item
>::const_iterator T
= J
+ 1;
1328 if (T
!= List
.end() && T
->Op
== I
->Op
)
1333 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1336 auto const size
= (J
- I
) + 10;
1338 // start with the baseset of arguments
1339 auto Size
= StartSize
;
1340 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1342 // keep track of allocated strings for multiarch package names
1343 std::vector
<char *> Packages(size
, nullptr);
1347 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1349 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1350 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1352 ADDARGC("--status-fd");
1353 char status_fd_buf
[20];
1354 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1355 ADDARG(status_fd_buf
);
1356 unsigned long const Op
= I
->Op
;
1361 ADDARGC("--force-depends");
1362 if (std::any_of(I
, J
, ItemIsEssential
))
1363 ADDARGC("--force-remove-essential");
1364 ADDARGC("--remove");
1368 ADDARGC("--force-depends");
1369 if (std::any_of(I
, J
, ItemIsEssential
))
1370 ADDARGC("--force-remove-essential");
1374 case Item::Configure
:
1375 ADDARGC("--configure");
1378 case Item::ConfigurePending
:
1379 ADDARGC("--configure");
1380 ADDARGC("--pending");
1383 case Item::TriggersPending
:
1384 ADDARGC("--triggers-only");
1385 ADDARGC("--pending");
1389 ADDARGC("--unpack");
1390 ADDARGC("--auto-deconfigure");
1394 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1395 I
->Op
!= Item::ConfigurePending
)
1397 ADDARGC("--no-triggers");
1401 // Write in the file or package names
1402 if (I
->Op
== Item::Install
)
1404 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1406 if (I
->File
[0] != '/')
1407 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1408 Args
.push_back(I
->File
.c_str());
1409 Size
+= I
->File
.length();
1414 string
const nativeArch
= _config
->Find("APT::Architecture");
1415 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1416 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1418 if((*I
).Pkg
.end() == true)
1420 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1422 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1423 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1424 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1425 strcmp(I
->Pkg
.Arch(), "none") == 0))
1427 char const * const name
= I
->Pkg
.Name();
1432 pkgCache::VerIterator PkgVer
;
1433 std::string name
= I
->Pkg
.Name();
1434 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1436 PkgVer
= I
->Pkg
.CurrentVer();
1437 if(PkgVer
.end() == true)
1438 PkgVer
= FindNowVersion(I
->Pkg
);
1441 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1442 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1443 ; // never arch-qualify a package without an arch
1444 else if (PkgVer
.end() == false)
1445 name
.append(":").append(PkgVer
.Arch());
1447 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1448 char * const fullname
= strdup(name
.c_str());
1449 Packages
.push_back(fullname
);
1453 // skip configure action if all sheduled packages disappeared
1454 if (oldSize
== Size
)
1461 if (noopDPkgInvocation
== true)
1463 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1464 a
!= Args
.end(); ++a
)
1467 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1468 p
!= Packages
.end(); ++p
)
1475 Args
.push_back(NULL
);
1481 /* Mask off sig int/quit. We do this because dpkg also does when
1482 it forks scripts. What happens is that when you hit ctrl-c it sends
1483 it to all processes in the group. Since dpkg ignores the signal
1484 it doesn't die but we do! So we must also ignore it */
1485 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1486 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1488 // Check here for any SIGINT
1489 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1492 // ignore SIGHUP as well (debian #463030)
1493 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1496 d
->progress
->StartDpkg();
1497 std::set
<int> KeepFDs
;
1498 KeepFDs
.insert(fd
[1]);
1499 MergeKeepFdsFromConfiguration(KeepFDs
);
1500 pid_t Child
= ExecFork(KeepFDs
);
1503 // This is the child
1504 SetupSlavePtyMagic();
1505 close(fd
[0]); // close the read end of the pipe
1507 debSystem::DpkgChrootDirectory();
1509 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1512 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1516 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1519 // Discard everything in stdin before forking dpkg
1520 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1523 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1525 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1529 // if color support isn't enabled/disabled explicitly tell
1530 // dpkg to use the same state apt is using for its color support
1531 if (_config
->FindB("APT::Color", false) == true)
1532 setenv("DPKG_COLORS", "always", 0);
1534 setenv("DPKG_COLORS", "never", 0);
1536 execvp(Args
[0], (char**) &Args
[0]);
1537 cerr
<< "Could not exec dpkg!" << endl
;
1541 // we read from dpkg here
1542 int const _dpkgin
= fd
[0];
1543 close(fd
[1]); // close the write end of the pipe
1546 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1550 sigemptyset(&d
->sigmask
);
1551 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1553 /* free vectors (and therefore memory) as we don't need the included data anymore */
1554 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1555 p
!= Packages
.end(); ++p
)
1559 // the result of the waitpid call
1562 bool waitpid_failure
= false;
1563 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1565 // error handling, waitpid returned -1
1568 waitpid_failure
= true;
1572 // wait for input or output here
1574 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1575 FD_SET(STDIN_FILENO
, &rfds
);
1576 FD_SET(_dpkgin
, &rfds
);
1578 FD_SET(d
->master
, &rfds
);
1580 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1581 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1582 &tv
, &d
->original_sigmask
);
1583 d
->progress
->Pulse();
1584 if (select_ret
== 0)
1586 else if (select_ret
< 0 && errno
== EINTR
)
1588 else if (select_ret
< 0)
1590 perror("select() returned error");
1594 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1595 DoTerminalPty(d
->master
);
1596 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1598 if(FD_ISSET(_dpkgin
, &rfds
))
1599 DoDpkgStatusFd(_dpkgin
);
1603 // Restore sig int/quit
1604 signal(SIGQUIT
,old_SIGQUIT
);
1605 signal(SIGINT
,old_SIGINT
);
1606 signal(SIGHUP
,old_SIGHUP
);
1608 if (waitpid_failure
== true)
1610 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1611 _error
->Error("%s", d
->dpkg_error
.c_str());
1615 // Check for an error code.
1616 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1618 // if it was set to "keep-dpkg-running" then we won't return
1619 // here but keep the loop going and just report it as a error
1621 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1623 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1624 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1625 else if (WIFEXITED(Status
) != 0)
1626 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1628 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1629 _error
->Error("%s", d
->dpkg_error
.c_str());
1635 // dpkg is done at this point
1639 if (pkgPackageManager::SigINTStop
)
1640 _error
->Warning(_("Operation was interrupted before it could finish"));
1642 if (noopDPkgInvocation
== false)
1644 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1645 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1646 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1648 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1649 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1651 _error
->PushToStack();
1652 pkgCacheFile CacheFile
;
1653 CacheFile
.BuildCaches(NULL
, true);
1654 _error
->RevertToStack();
1659 // disappearing packages can forward their auto-bit
1660 if (disappearedPkgs
.empty() == false)
1661 Cache
.writeStateFile(NULL
);
1663 d
->progress
->Stop();
1665 if (RunScripts("DPkg::Post-Invoke") == false)
1668 return d
->dpkg_error
.empty();
1671 void SigINT(int /*sig*/) {
1672 pkgPackageManager::SigINTStop
= true;
1675 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1676 // ---------------------------------------------------------------------
1678 void pkgDPkgPM::Reset()
1680 List
.erase(List
.begin(),List
.end());
1683 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1684 // ---------------------------------------------------------------------
1686 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1688 // If apport doesn't exist or isn't installed do nothing
1689 // This e.g. prevents messages in 'universes' without apport
1690 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1691 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1694 string pkgname
, reportfile
, pkgver
, arch
;
1695 string::size_type pos
;
1698 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1700 std::clog
<< "configured to not write apport reports" << std::endl
;
1704 // only report the first errors
1705 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1707 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1711 // check if its not a follow up error
1712 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1713 if(strstr(errormsg
, needle
) != NULL
) {
1714 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1718 // do not report disk-full failures
1719 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1720 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1724 // do not report out-of-memory failures
1725 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1726 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1727 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1731 // do not report bugs regarding inaccessible local files
1732 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1733 strstr(errormsg
, "cannot access archive") != NULL
) {
1734 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1738 // do not report errors encountered when decompressing packages
1739 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1740 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1744 // do not report dpkg I/O errors, this is a format string, so we compare
1745 // the prefix and the suffix of the error with the dpkg error message
1746 vector
<string
> io_errors
;
1747 io_errors
.push_back(string("failed to read"));
1748 io_errors
.push_back(string("failed to write"));
1749 io_errors
.push_back(string("failed to seek"));
1750 io_errors
.push_back(string("unexpected end of file or stream"));
1752 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1754 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1755 if (list
.size() > 1) {
1756 // we need to split %s, VectorizeString only allows char so we need
1757 // to kill the "s" manually
1758 if (list
[1].size() > 1) {
1759 list
[1].erase(0, 1);
1760 if(strstr(errormsg
, list
[0].c_str()) &&
1761 strstr(errormsg
, list
[1].c_str())) {
1762 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1769 // get the pkgname and reportfile
1770 pkgname
= flNotDir(pkgpath
);
1771 pos
= pkgname
.find('_');
1772 if(pos
!= string::npos
)
1773 pkgname
= pkgname
.substr(0, pos
);
1775 // find the package version and source package name
1776 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1777 if (Pkg
.end() == true)
1779 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1780 if (Ver
.end() == true)
1782 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1784 // if the file exists already, we check:
1785 // - if it was reported already (touched by apport).
1786 // If not, we do nothing, otherwise
1787 // we overwrite it. This is the same behaviour as apport
1788 // - if we have a report with the same pkgversion already
1790 _config
->CndSet("Dir::Apport", "var/crash");
1791 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
1792 if(FileExists(reportfile
))
1797 // check atime/mtime
1798 stat(reportfile
.c_str(), &buf
);
1799 if(buf
.st_mtime
> buf
.st_atime
)
1802 // check if the existing report is the same version
1803 report
= fopen(reportfile
.c_str(),"r");
1804 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1806 if(strstr(strbuf
,"Package:") == strbuf
)
1808 char pkgname
[255], version
[255];
1809 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1810 if(strcmp(pkgver
.c_str(), version
) == 0)
1820 // now write the report
1821 arch
= _config
->Find("APT::Architecture");
1822 report
= fopen(reportfile
.c_str(),"w");
1825 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1826 chmod(reportfile
.c_str(), 0);
1828 chmod(reportfile
.c_str(), 0600);
1829 fprintf(report
, "ProblemType: Package\n");
1830 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1831 time_t now
= time(NULL
);
1832 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1833 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1834 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1835 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1836 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1838 // ensure that the log is flushed
1840 fflush(d
->term_out
);
1842 // attach terminal log it if we have it
1843 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1844 if (!logfile_name
.empty())
1848 fprintf(report
, "DpkgTerminalLog:\n");
1849 log
= fopen(logfile_name
.c_str(),"r");
1853 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1854 fprintf(report
, " %s", buf
);
1855 fprintf(report
, " \n");
1860 // attach history log it if we have it
1861 string histfile_name
= _config
->FindFile("Dir::Log::History");
1862 if (!histfile_name
.empty())
1864 fprintf(report
, "DpkgHistoryLog:\n");
1865 FILE* log
= fopen(histfile_name
.c_str(),"r");
1869 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1870 fprintf(report
, " %s", buf
);
1875 // log the ordering, see dpkgpm.h and the "Ops" enum there
1876 const char *ops_str
[] = {
1884 fprintf(report
, "AptOrdering:\n");
1885 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1886 if ((*I
).Pkg
!= NULL
)
1887 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1889 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1891 // attach dmesg log (to learn about segfaults)
1892 if (FileExists("/bin/dmesg"))
1894 fprintf(report
, "Dmesg:\n");
1895 FILE *log
= popen("/bin/dmesg","r");
1899 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1900 fprintf(report
, " %s", buf
);
1905 // attach df -l log (to learn about filesystem status)
1906 if (FileExists("/bin/df"))
1909 fprintf(report
, "Df:\n");
1910 FILE *log
= popen("/bin/df -l","r");
1914 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1915 fprintf(report
, " %s", buf
);