1 // -*- mode: cpp; mode: fold -*-
3 // $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $
4 /* ######################################################################
6 DPKG Package Manager - Provide an interface to dpkg
8 ##################################################################### */
13 #include <apt-pkg/cachefile.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/depcache.h>
16 #include <apt-pkg/dpkgpm.h>
17 #include <apt-pkg/debsystem.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/install-progress.h>
21 #include <apt-pkg/packagemanager.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
35 #include <sys/ioctl.h>
36 #include <sys/select.h>
59 APT_PURE
static string
60 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
87 unsigned int size
= 0;
88 char **envp
= environ
;
91 size
+= strlen (*envp
++) + 1;
96 class pkgDPkgPMPrivate
99 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
100 term_out(NULL
), history_out(NULL
),
101 progress(NULL
), tt_is_valid(false), master(-1),
102 slave(NULL
), protect_slave_from_dying(-1),
110 bool stdin_is_dev_null
;
111 // the buffer we use for the dpkg status-fd reading
117 APT::Progress::PackageManager
*progress
;
124 int protect_slave_from_dying
;
128 sigset_t original_sigmask
;
135 // Maps the dpkg "processing" info to human readable names. Entry 0
136 // of each array is the key, entry 1 is the value.
137 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
138 std::make_pair("install", N_("Installing %s")),
139 std::make_pair("configure", N_("Configuring %s")),
140 std::make_pair("remove", N_("Removing %s")),
141 std::make_pair("purge", N_("Completely removing %s")),
142 std::make_pair("disappear", N_("Noting disappearance of %s")),
143 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
146 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
147 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
149 // Predicate to test whether an entry in the PackageProcessingOps
150 // array matches a string.
151 class MatchProcessingOp
156 explicit MatchProcessingOp(const char *the_target
)
161 bool operator()(const std::pair
<const char *, const char *> &pair
) const
163 return strcmp(pair
.first
, target
) == 0;
168 /* helper function to ionice the given PID
170 there is no C header for ionice yet - just the syscall interface
171 so we use the binary from util-linux
176 if (!FileExists("/usr/bin/ionice"))
178 pid_t Process
= ExecFork();
182 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
184 Args
[0] = "/usr/bin/ionice";
188 execv(Args
[0], (char **)Args
);
190 return ExecWait(Process
, "ionice");
193 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This is helpful when a package is no longer installed but has residual
199 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
201 pkgCache::VerIterator Ver
;
202 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
203 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
204 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
206 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
213 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
214 // ---------------------------------------------------------------------
216 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
217 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
221 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
222 // ---------------------------------------------------------------------
224 pkgDPkgPM::~pkgDPkgPM()
229 // DPkgPM::Install - Install a package /*{{{*/
230 // ---------------------------------------------------------------------
231 /* Add an install operation to the sequence list */
232 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
234 if (File
.empty() == true || Pkg
.end() == true)
235 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
237 // If the filename string begins with DPkg::Chroot-Directory, return the
238 // substr that is within the chroot so dpkg can access it.
239 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
240 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
242 size_t len
= chrootdir
.length();
243 if (chrootdir
.at(len
- 1) == '/')
245 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
248 List
.push_back(Item(Item::Install
,Pkg
,File
));
253 // DPkgPM::Configure - Configure a package /*{{{*/
254 // ---------------------------------------------------------------------
255 /* Add a configure operation to the sequence list */
256 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
258 if (Pkg
.end() == true)
261 List
.push_back(Item(Item::Configure
, Pkg
));
263 // Use triggers for config calls if we configure "smart"
264 // as otherwise Pre-Depends will not be satisfied, see #526774
265 if (_config
->FindB("DPkg::TriggersPending", false) == true)
266 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
271 // DPkgPM::Remove - Remove a package /*{{{*/
272 // ---------------------------------------------------------------------
273 /* Add a remove operation to the sequence list */
274 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
276 if (Pkg
.end() == true)
280 List
.push_back(Item(Item::Purge
,Pkg
));
282 List
.push_back(Item(Item::Remove
,Pkg
));
286 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
287 // ---------------------------------------------------------------------
288 /* This is part of the helper script communication interface, it sends
289 very complete information down to the other end of the pipe.*/
290 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
292 return SendPkgsInfo(F
, 2);
294 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
296 // This version of APT supports only v3, so don't sent higher versions
298 fprintf(F
,"VERSION %u\n", Version
);
300 fprintf(F
,"VERSION 3\n");
302 /* Write out all of the configuration directives by walking the
303 configuration tree */
304 const Configuration::Item
*Top
= _config
->Tree(0);
307 if (Top
->Value
.empty() == false)
310 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
311 QuoteString(Top
->Value
,"\n").c_str());
320 while (Top
!= 0 && Top
->Next
== 0)
327 // Write out the package actions in order.
328 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
330 if(I
->Pkg
.end() == true)
333 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
335 fprintf(F
,"%s ",I
->Pkg
.Name());
337 // Current version which we are going to replace
338 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
339 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
340 CurVer
= FindNowVersion(I
->Pkg
);
342 if (CurVer
.end() == true)
347 fprintf(F
, "- - none ");
351 fprintf(F
, "%s ", CurVer
.VerStr());
353 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
356 // Show the compare operator between current and install version
357 if (S
.InstallVer
!= 0)
359 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
361 if (CurVer
.end() == false)
362 Comp
= InstVer
.CompareVer(CurVer
);
369 fprintf(F
, "%s ", InstVer
.VerStr());
371 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
378 fprintf(F
, "> - - none ");
381 // Show the filename/operation
382 if (I
->Op
== Item::Install
)
385 if (I
->File
[0] != '/')
386 fprintf(F
,"**ERROR**\n");
388 fprintf(F
,"%s\n",I
->File
.c_str());
390 else if (I
->Op
== Item::Configure
)
391 fprintf(F
,"**CONFIGURE**\n");
392 else if (I
->Op
== Item::Remove
||
393 I
->Op
== Item::Purge
)
394 fprintf(F
,"**REMOVE**\n");
402 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
403 // ---------------------------------------------------------------------
404 /* This looks for a list of scripts to run from the configuration file
405 each one is run and is fed on standard input a list of all .deb files
406 that are due to be installed. */
407 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
411 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
412 if (Opts
== 0 || Opts
->Child
== 0)
416 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
417 sighandler_t old_sigint
= signal(SIGINT
, SIG_IGN
);
418 sighandler_t old_sigquit
= signal(SIGQUIT
, SIG_IGN
);
420 unsigned int Count
= 1;
421 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
423 if (Opts
->Value
.empty() == true)
426 if(_config
->FindB("Debug::RunScripts", false) == true)
427 std::clog
<< "Running external script with list of all .deb file: '"
428 << Opts
->Value
<< "'" << std::endl
;
430 // Determine the protocol version
431 string OptSec
= Opts
->Value
;
432 string::size_type Pos
;
433 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
434 Pos
= OptSec
.length();
435 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
437 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
438 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
441 std::set
<int> KeepFDs
;
442 MergeKeepFdsFromConfiguration(KeepFDs
);
444 if (pipe(Pipes
) != 0) {
445 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
448 if (InfoFD
!= (unsigned)Pipes
[0])
449 SetCloseExec(Pipes
[0],true);
451 KeepFDs
.insert(Pipes
[0]);
454 SetCloseExec(Pipes
[1],true);
456 // Purified Fork for running the script
457 pid_t Process
= ExecFork(KeepFDs
);
461 dup2(Pipes
[0], InfoFD
);
462 SetCloseExec(STDOUT_FILENO
,false);
463 SetCloseExec(STDIN_FILENO
,false);
464 SetCloseExec(STDERR_FILENO
,false);
467 strprintf(hookfd
, "%d", InfoFD
);
468 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
470 debSystem::DpkgChrootDirectory();
474 Args
[2] = Opts
->Value
.c_str();
476 execv(Args
[0],(char **)Args
);
480 FILE *F
= fdopen(Pipes
[1],"w");
482 result
= _error
->Errno("fdopen","Faild to open new FD");
486 // Feed it the filenames.
489 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
491 // Only deal with packages to be installed from .deb
492 if (I
->Op
!= Item::Install
)
496 if (I
->File
[0] != '/')
499 /* Feed the filename of each package that is pending install
501 fprintf(F
,"%s\n",I
->File
.c_str());
507 SendPkgsInfo(F
, Version
);
511 // Clean up the sub process
512 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
513 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
517 signal(SIGINT
, old_sigint
);
518 signal(SIGPIPE
, old_sigpipe
);
519 signal(SIGQUIT
, old_sigquit
);
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 // ---------------------------------------------------------------------
568 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
570 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
572 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
574 /* dpkg sends strings like this:
575 'status: <pkg>: <pkg qstate>'
576 'status: <pkg>:<arch>: <pkg qstate>'
578 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
579 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
582 // we need to split on ": " (note the appended space) as the ':' is
583 // part of the pkgname:arch information that dpkg sends
585 // A dpkg error message may contain additional ":" (like
586 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
587 // so we need to ensure to not split too much
588 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
592 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
596 // build the (prefix, pkgname, action) tuple, position of this
597 // is different for "processing" or "status" messages
598 std::string prefix
= APT::String::Strip(list
[0]);
602 // "processing" has the form "processing: action: pkg or trigger"
603 // with action = ["install", "upgrade", "configure", "remove", "purge",
604 // "disappear", "trigproc"]
605 if (prefix
== "processing")
607 pkgname
= APT::String::Strip(list
[2]);
608 action
= APT::String::Strip(list
[1]);
609 // we don't care for the difference (as dpkg doesn't really either)
610 if (action
== "upgrade")
613 // "status" has the form: "status: pkg: state"
614 // with state in ["half-installed", "unpacked", "half-configured",
615 // "installed", "config-files", "not-installed"]
616 else if (prefix
== "status")
618 pkgname
= APT::String::Strip(list
[1]);
619 action
= APT::String::Strip(list
[2]);
622 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
627 /* handle the special cases first:
629 errors look like this:
630 '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
631 and conffile-prompt like this
632 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
634 if (prefix
== "status")
636 if(action
== "error")
638 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
641 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
644 else if(action
== "conffile-prompt")
646 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
652 // at this point we know that we should have a valid pkgname, so build all
655 // dpkg does not always send "pkgname:arch" so we add it here if needed
656 if (pkgname
.find(":") == std::string::npos
)
658 // find the package in the group that is touched by dpkg
659 // if there are multiple pkgs dpkg would send us a full pkgname:arch
660 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
661 if (Grp
.end() == false)
663 pkgCache::PkgIterator P
= Grp
.PackageList();
664 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
666 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
668 pkgname
= P
.FullName();
675 const char* const pkg
= pkgname
.c_str();
676 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
677 std::string arch
= "";
678 if (pkgname
.find(":") != string::npos
)
679 arch
= StringSplit(pkgname
, ":")[1];
680 std::string i18n_pkgname
= pkgname
;
681 if (arch
.size() != 0)
682 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
684 // 'processing' from dpkg looks like
685 // 'processing: action: pkg'
686 if(prefix
== "processing")
688 const std::pair
<const char *, const char *> * const iter
=
689 std::find_if(PackageProcessingOpsBegin
,
690 PackageProcessingOpsEnd
,
691 MatchProcessingOp(action
.c_str()));
692 if(iter
== PackageProcessingOpsEnd
)
695 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
699 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
700 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
702 // FIXME: this needs a muliarch testcase
703 // FIXME2: is "pkgname" here reliable with dpkg only sending us
705 if (action
== "disappear")
706 handleDisappearAction(pkgname
);
710 if (prefix
== "status")
712 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
713 if(PackageOpsDone
[pkg
] < states
.size())
715 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
716 if (next_action
&& Debug
== true)
717 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
718 << " action: " << action
<< " (expected: '" << next_action
<< "' "
719 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
721 // check if the package moved to the next dpkg state
722 if(next_action
&& (action
== next_action
))
724 // only read the translation if there is actually a next action
725 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
727 // we moved from one dpkg state to a new one, report that
728 ++PackageOpsDone
[pkg
];
732 strprintf(msg
, translation
, i18n_pkgname
.c_str());
733 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
739 // DPkgPM::handleDisappearAction /*{{{*/
740 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
742 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
743 if (unlikely(Pkg
.end() == true))
746 // record the package name for display and stuff later
747 disappearedPkgs
.insert(Pkg
.FullName(true));
749 // the disappeared package was auto-installed - nothing to do
750 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
752 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
753 if (unlikely(PkgVer
.end() == true))
755 /* search in the list of dependencies for (Pre)Depends,
756 check if this dependency has a Replaces on our package
757 and if so transfer the manual installed flag to it */
758 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
760 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
761 Dep
->Type
!= pkgCache::Dep::PreDepends
)
763 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
764 if (unlikely(Tar
.end() == true))
766 // the package is already marked as manual
767 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
769 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
770 if (TarVer
.end() == true)
772 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
774 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
776 if (Pkg
!= Rep
.TargetPkg())
778 // okay, they are strongly connected - transfer manual-bit
780 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
781 Cache
[Tar
].Flags
&= ~Flag::Auto
;
787 // DPkgPM::DoDpkgStatusFd /*{{{*/
788 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
790 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
791 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
794 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
796 // process line by line from the buffer
797 char *p
= d
->dpkgbuf
, *q
= nullptr;
798 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
801 ProcessDpkgStatusLine(p
);
802 p
= q
+ 1; // continue with next line
805 // check if we stripped the buffer clean
806 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
812 // otherwise move the unprocessed tail to the start and update pos
813 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
814 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
817 // DPkgPM::WriteHistoryTag /*{{{*/
818 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
820 size_t const length
= value
.length();
823 // poor mans rstrip(", ")
824 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
825 value
.erase(length
- 2, 2);
826 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
828 // DPkgPM::OpenLog /*{{{*/
829 bool pkgDPkgPM::OpenLog()
831 string
const logdir
= _config
->FindDir("Dir::Log");
832 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
833 // FIXME: use a better string after freeze
834 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
838 time_t const t
= time(NULL
);
840 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
841 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
844 string
const logfile_name
= flCombine(logdir
,
845 _config
->Find("Dir::Log::Terminal"));
846 if (!logfile_name
.empty())
848 d
->term_out
= fopen(logfile_name
.c_str(),"a");
849 if (d
->term_out
== NULL
)
850 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
851 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
852 SetCloseExec(fileno(d
->term_out
), true);
853 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
855 struct passwd
*pw
= getpwnam("root");
856 struct group
*gr
= getgrnam("adm");
857 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
858 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
860 if (chmod(logfile_name
.c_str(), 0640) != 0)
861 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
862 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
865 // write your history
866 string
const history_name
= flCombine(logdir
,
867 _config
->Find("Dir::Log::History"));
868 if (!history_name
.empty())
870 d
->history_out
= fopen(history_name
.c_str(),"a");
871 if (d
->history_out
== NULL
)
872 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
873 SetCloseExec(fileno(d
->history_out
), true);
874 chmod(history_name
.c_str(), 0644);
875 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
876 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
877 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
879 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
881 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
882 if (Cache
[I
].NewInstall() == true)
883 HISTORYINFO(install
, CANDIDATE_AUTO
)
884 else if (Cache
[I
].ReInstall() == true)
885 HISTORYINFO(reinstall
, CANDIDATE
)
886 else if (Cache
[I
].Upgrade() == true)
887 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
888 else if (Cache
[I
].Downgrade() == true)
889 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
890 else if (Cache
[I
].Delete() == true)
891 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
895 line
->append(I
.FullName(false)).append(" (");
896 switch (infostring
) {
897 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
899 line
->append(Cache
[I
].CandVersion
);
900 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
901 line
->append(", automatic");
903 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
904 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
908 if (_config
->Exists("Commandline::AsString") == true)
909 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
910 std::string RequestingUser
= AptHistoryRequestingUser();
911 if (RequestingUser
!= "")
912 WriteHistoryTag("Requested-By", RequestingUser
);
913 WriteHistoryTag("Install", install
);
914 WriteHistoryTag("Reinstall", reinstall
);
915 WriteHistoryTag("Upgrade", upgrade
);
916 WriteHistoryTag("Downgrade",downgrade
);
917 WriteHistoryTag("Remove",remove
);
918 WriteHistoryTag("Purge",purge
);
919 fflush(d
->history_out
);
925 // DPkg::CloseLog /*{{{*/
926 bool pkgDPkgPM::CloseLog()
929 time_t t
= time(NULL
);
931 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
932 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
936 fprintf(d
->term_out
, "Log ended: ");
937 fprintf(d
->term_out
, "%s", timestr
);
938 fprintf(d
->term_out
, "\n");
945 if (disappearedPkgs
.empty() == false)
948 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
949 d
!= disappearedPkgs
.end(); ++d
)
951 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
952 disappear
.append(*d
);
954 disappear
.append(", ");
956 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
958 WriteHistoryTag("Disappeared", disappear
);
960 if (d
->dpkg_error
.empty() == false)
961 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
962 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
963 fclose(d
->history_out
);
965 d
->history_out
= NULL
;
972 // This implements a racy version of pselect for those architectures
973 // that don't have a working implementation.
974 // FIXME: Probably can be removed on Lenny+1
975 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
976 fd_set
*exceptfds
, const struct timespec
*timeout
,
977 const sigset_t
*sigmask
)
983 tv
.tv_sec
= timeout
->tv_sec
;
984 tv
.tv_usec
= timeout
->tv_nsec
/1000;
986 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
987 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
988 sigprocmask(SIG_SETMASK
, &origmask
, 0);
993 // DPkgPM::BuildPackagesProgressMap /*{{{*/
994 void pkgDPkgPM::BuildPackagesProgressMap()
996 // map the dpkg states to the operations that are performed
997 // (this is sorted in the same way as Item::Ops)
998 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1001 {"half-installed", N_("Preparing %s")},
1002 {"unpacked", N_("Unpacking %s") },
1005 // Configure operation
1007 {"unpacked",N_("Preparing to configure %s") },
1008 {"half-configured", N_("Configuring %s") },
1009 { "installed", N_("Installed %s")},
1014 {"half-configured", N_("Preparing for removal of %s")},
1015 {"half-installed", N_("Removing %s")},
1016 {"config-files", N_("Removed %s")},
1021 {"config-files", N_("Preparing to completely remove %s")},
1022 {"not-installed", N_("Completely removed %s")},
1027 // init the PackageOps map, go over the list of packages that
1028 // that will be [installed|configured|removed|purged] and add
1029 // them to the PackageOps map (the dpkg states it goes through)
1030 // and the PackageOpsTranslations (human readable strings)
1031 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1033 if((*I
).Pkg
.end() == true)
1036 string
const name
= (*I
).Pkg
.FullName();
1037 PackageOpsDone
[name
] = 0;
1038 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1040 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1044 /* one extra: We don't want the progress bar to reach 100%, especially not
1045 if we call dpkg --configure --pending and process a bunch of triggers
1046 while showing 100%. Also, spindown takes a while, so never reaching 100%
1047 is way more correct than reaching 100% while still doing stuff even if
1048 doing it this way is slightly bending the rules */
1052 bool pkgDPkgPM::Go(int StatusFd
)
1054 APT::Progress::PackageManager
*progress
= NULL
;
1056 progress
= APT::Progress::PackageManagerProgressFactory();
1058 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1060 return Go(progress
);
1063 void pkgDPkgPM::StartPtyMagic()
1065 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1068 if (d
->slave
!= NULL
)
1074 if (isatty(STDIN_FILENO
) == 0)
1075 d
->direct_stdin
= true;
1077 _error
->PushToStack();
1079 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1080 if (d
->master
== -1)
1081 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1082 else if (unlockpt(d
->master
) == -1)
1083 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1086 #ifdef HAVE_PTS_NAME_R
1087 char slave_name
[64]; // 64 is used by bionic
1088 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1090 char const * const slave_name
= ptsname(d
->master
);
1091 if (slave_name
== NULL
)
1093 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1096 d
->slave
= strdup(slave_name
);
1097 if (d
->slave
== NULL
)
1098 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1099 else if (grantpt(d
->master
) == -1)
1100 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1101 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1103 d
->tt_is_valid
= true;
1104 struct termios raw_tt
;
1105 // copy window size of stdout if its a 'good' terminal
1106 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1109 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1110 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1111 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1112 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1114 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1115 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1119 raw_tt
.c_lflag
&= ~ECHO
;
1120 raw_tt
.c_lflag
|= ISIG
;
1121 // block SIGTTOU during tcsetattr to prevent a hang if
1122 // the process is a member of the background process group
1123 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1124 sigemptyset(&d
->sigmask
);
1125 sigaddset(&d
->sigmask
, SIGTTOU
);
1126 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1127 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1128 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1129 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1132 if (d
->slave
!= NULL
)
1134 /* on linux, closing (and later reopening) all references to the slave
1135 makes the slave a death end, so we open it here to have one open all
1136 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1137 on kfreebsd we get an incorrect ("step like") output then while it has
1138 no problem with closing all references… so to avoid platform specific
1139 code here we combine both and be happy once more */
1140 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1145 if (_error
->PendingError() == true)
1147 if (d
->master
!= -1)
1152 if (d
->slave
!= NULL
)
1157 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1159 _error
->RevertToStack();
1161 void pkgDPkgPM::SetupSlavePtyMagic()
1163 if(d
->master
== -1 || d
->slave
== NULL
)
1166 if (close(d
->master
) == -1)
1167 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1170 _error
->FatalE("setsid", "Starting a new session for child failed!");
1172 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1174 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1175 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1176 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1179 unsigned short i
= 0;
1180 if (d
->direct_stdin
== true)
1183 if (dup2(slaveFd
, i
) == -1)
1184 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1186 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1187 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1193 void pkgDPkgPM::StopPtyMagic()
1195 if (d
->slave
!= NULL
)
1198 if (d
->protect_slave_from_dying
!= -1)
1200 close(d
->protect_slave_from_dying
);
1201 d
->protect_slave_from_dying
= -1;
1205 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1206 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1212 // DPkgPM::Go - Run the sequence /*{{{*/
1213 // ---------------------------------------------------------------------
1214 /* This globs the operations and calls dpkg
1216 * If it is called with a progress object apt will report the install
1217 * progress to this object. It maps the dpkg states a package goes
1218 * through to human readable (and i10n-able)
1219 * names and calculates a percentage for each step.
1221 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1223 auto const ItemIsEssential
= [](pkgDPkgPM::Item
const &I
) {
1224 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1225 if (cachegen
== "none" || cachegen
== "native")
1227 if (unlikely(I
.Pkg
.end()))
1229 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1232 pkgPackageManager::SigINTStop
= false;
1233 d
->progress
= progress
;
1235 // Generate the base argument list for dpkg
1236 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1237 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1238 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1239 [](std::string
const &s
) { return s
.c_str(); });
1240 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1241 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1242 size_t const BaseArgs
= Args
.size();
1247 // FIXME: do we really need this limit when we have MaxArgBytes?
1248 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1250 // try to figure out the max environment size
1251 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1254 OSArgMax
-= EnvironmentSize() - 2*1024;
1255 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1256 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1258 if (RunScripts("DPkg::Pre-Invoke") == false)
1261 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1264 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1265 // store auto-bits as they are supposed to be after dpkg is run
1266 if (noopDPkgInvocation
== false)
1267 Cache
.writeStateFile(NULL
);
1269 // support subpressing of triggers processing for special
1270 // cases like d-i that runs the triggers handling manually
1271 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1272 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1273 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1276 BuildPackagesProgressMap();
1278 d
->stdin_is_dev_null
= false;
1283 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1285 // start pty magic before the loop
1288 // Tell the progress that its starting and fork dpkg
1289 d
->progress
->Start(d
->master
);
1291 // this loop is runs once per dpkg operation
1292 vector
<Item
>::const_iterator I
= List
.begin();
1293 while (I
!= List
.end())
1295 // Do all actions with the same Op in one run
1296 vector
<Item
>::const_iterator J
= I
;
1297 if (TriggersPending
== true)
1298 for (; J
!= List
.end(); ++J
)
1302 if (J
->Op
!= Item::TriggersPending
)
1304 vector
<Item
>::const_iterator T
= J
+ 1;
1305 if (T
!= List
.end() && T
->Op
== I
->Op
)
1310 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1313 // keep track of allocated strings for multiarch package names
1314 std::vector
<char *> Packages
;
1316 // start with the baseset of arguments
1317 unsigned long Size
= StartSize
;
1318 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1320 // Now check if we are within the MaxArgs limit
1322 // this code below is problematic, because it may happen that
1323 // the argument list is split in a way that A depends on B
1324 // and they are in the same "--configure A B" run
1325 // - with the split they may now be configured in different
1326 // runs, using Immediate-Configure-All can help prevent this.
1327 if (J
- I
> (signed)MaxArgs
)
1330 unsigned long const size
= MaxArgs
+ 10;
1332 Packages
.reserve(size
);
1336 unsigned long const size
= (J
- I
) + 10;
1338 Packages
.reserve(size
);
1343 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1345 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1346 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1348 ADDARGC("--status-fd");
1349 char status_fd_buf
[20];
1350 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1351 ADDARG(status_fd_buf
);
1352 unsigned long const Op
= I
->Op
;
1357 ADDARGC("--force-depends");
1358 if (std::any_of(I
, J
, ItemIsEssential
))
1359 ADDARGC("--force-remove-essential");
1360 ADDARGC("--remove");
1364 ADDARGC("--force-depends");
1365 if (std::any_of(I
, J
, ItemIsEssential
))
1366 ADDARGC("--force-remove-essential");
1370 case Item::Configure
:
1371 ADDARGC("--configure");
1374 case Item::ConfigurePending
:
1375 ADDARGC("--configure");
1376 ADDARGC("--pending");
1379 case Item::TriggersPending
:
1380 ADDARGC("--triggers-only");
1381 ADDARGC("--pending");
1385 ADDARGC("--unpack");
1386 ADDARGC("--auto-deconfigure");
1390 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1391 I
->Op
!= Item::ConfigurePending
)
1393 ADDARGC("--no-triggers");
1397 // Write in the file or package names
1398 if (I
->Op
== Item::Install
)
1400 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1402 if (I
->File
[0] != '/')
1403 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1404 Args
.push_back(I
->File
.c_str());
1405 Size
+= I
->File
.length();
1410 string
const nativeArch
= _config
->Find("APT::Architecture");
1411 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1412 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1414 if((*I
).Pkg
.end() == true)
1416 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1418 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1419 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1420 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1421 strcmp(I
->Pkg
.Arch(), "none") == 0))
1423 char const * const name
= I
->Pkg
.Name();
1428 pkgCache::VerIterator PkgVer
;
1429 std::string name
= I
->Pkg
.Name();
1430 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1432 PkgVer
= I
->Pkg
.CurrentVer();
1433 if(PkgVer
.end() == true)
1434 PkgVer
= FindNowVersion(I
->Pkg
);
1437 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1438 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1439 ; // never arch-qualify a package without an arch
1440 else if (PkgVer
.end() == false)
1441 name
.append(":").append(PkgVer
.Arch());
1443 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1444 char * const fullname
= strdup(name
.c_str());
1445 Packages
.push_back(fullname
);
1449 // skip configure action if all sheduled packages disappeared
1450 if (oldSize
== Size
)
1457 if (noopDPkgInvocation
== true)
1459 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1460 a
!= Args
.end(); ++a
)
1463 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1464 p
!= Packages
.end(); ++p
)
1469 Args
.push_back(NULL
);
1475 /* Mask off sig int/quit. We do this because dpkg also does when
1476 it forks scripts. What happens is that when you hit ctrl-c it sends
1477 it to all processes in the group. Since dpkg ignores the signal
1478 it doesn't die but we do! So we must also ignore it */
1479 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1480 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1482 // Check here for any SIGINT
1483 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1487 // ignore SIGHUP as well (debian #463030)
1488 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1491 d
->progress
->StartDpkg();
1492 std::set
<int> KeepFDs
;
1493 KeepFDs
.insert(fd
[1]);
1494 MergeKeepFdsFromConfiguration(KeepFDs
);
1495 pid_t Child
= ExecFork(KeepFDs
);
1498 // This is the child
1499 SetupSlavePtyMagic();
1500 close(fd
[0]); // close the read end of the pipe
1502 debSystem::DpkgChrootDirectory();
1504 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1507 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1511 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1514 // Discard everything in stdin before forking dpkg
1515 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1518 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1520 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1524 execvp(Args
[0], (char**) &Args
[0]);
1525 cerr
<< "Could not exec dpkg!" << endl
;
1530 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1536 // we read from dpkg here
1537 int const _dpkgin
= fd
[0];
1538 close(fd
[1]); // close the write end of the pipe
1541 sigemptyset(&d
->sigmask
);
1542 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1544 /* free vectors (and therefore memory) as we don't need the included data anymore */
1545 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1546 p
!= Packages
.end(); ++p
)
1550 // the result of the waitpid call
1553 bool waitpid_failure
= false;
1554 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1556 // error handling, waitpid returned -1
1559 waitpid_failure
= true;
1563 // wait for input or output here
1565 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1566 FD_SET(STDIN_FILENO
, &rfds
);
1567 FD_SET(_dpkgin
, &rfds
);
1569 FD_SET(d
->master
, &rfds
);
1571 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1572 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1573 &tv
, &d
->original_sigmask
);
1574 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1575 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1576 NULL
, &tv
, &d
->original_sigmask
);
1577 d
->progress
->Pulse();
1578 if (select_ret
== 0)
1580 else if (select_ret
< 0 && errno
== EINTR
)
1582 else if (select_ret
< 0)
1584 perror("select() returned error");
1588 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1589 DoTerminalPty(d
->master
);
1590 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1592 if(FD_ISSET(_dpkgin
, &rfds
))
1593 DoDpkgStatusFd(_dpkgin
);
1597 // Restore sig int/quit
1598 signal(SIGQUIT
,old_SIGQUIT
);
1599 signal(SIGINT
,old_SIGINT
);
1600 signal(SIGHUP
,old_SIGHUP
);
1602 if (waitpid_failure
== true)
1604 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1605 _error
->Error("%s", d
->dpkg_error
.c_str());
1609 // Check for an error code.
1610 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1612 // if it was set to "keep-dpkg-running" then we won't return
1613 // here but keep the loop going and just report it as a error
1615 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1617 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1618 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1619 else if (WIFEXITED(Status
) != 0)
1620 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1622 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1623 _error
->Error("%s", d
->dpkg_error
.c_str());
1629 // dpkg is done at this point
1633 if (pkgPackageManager::SigINTStop
)
1634 _error
->Warning(_("Operation was interrupted before it could finish"));
1636 if (noopDPkgInvocation
== false)
1638 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1639 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1640 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1642 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1643 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1645 _error
->PushToStack();
1646 pkgCacheFile CacheFile
;
1647 CacheFile
.BuildCaches(NULL
, true);
1648 _error
->RevertToStack();
1653 // disappearing packages can forward their auto-bit
1654 if (disappearedPkgs
.empty() == false)
1655 Cache
.writeStateFile(NULL
);
1657 d
->progress
->Stop();
1659 if (RunScripts("DPkg::Post-Invoke") == false)
1662 return d
->dpkg_error
.empty();
1665 void SigINT(int /*sig*/) {
1666 pkgPackageManager::SigINTStop
= true;
1669 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1670 // ---------------------------------------------------------------------
1672 void pkgDPkgPM::Reset()
1674 List
.erase(List
.begin(),List
.end());
1677 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1678 // ---------------------------------------------------------------------
1680 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1682 // If apport doesn't exist or isn't installed do nothing
1683 // This e.g. prevents messages in 'universes' without apport
1684 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1685 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1688 string pkgname
, reportfile
, pkgver
, arch
;
1689 string::size_type pos
;
1692 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1694 std::clog
<< "configured to not write apport reports" << std::endl
;
1698 // only report the first errors
1699 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1701 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1705 // check if its not a follow up error
1706 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1707 if(strstr(errormsg
, needle
) != NULL
) {
1708 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1712 // do not report disk-full failures
1713 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1714 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1718 // do not report out-of-memory failures
1719 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1720 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1721 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1725 // do not report bugs regarding inaccessible local files
1726 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1727 strstr(errormsg
, "cannot access archive") != NULL
) {
1728 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1732 // do not report errors encountered when decompressing packages
1733 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != 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 dpkg I/O errors, this is a format string, so we compare
1739 // the prefix and the suffix of the error with the dpkg error message
1740 vector
<string
> io_errors
;
1741 io_errors
.push_back(string("failed to read"));
1742 io_errors
.push_back(string("failed to write"));
1743 io_errors
.push_back(string("failed to seek"));
1744 io_errors
.push_back(string("unexpected end of file or stream"));
1746 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1748 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1749 if (list
.size() > 1) {
1750 // we need to split %s, VectorizeString only allows char so we need
1751 // to kill the "s" manually
1752 if (list
[1].size() > 1) {
1753 list
[1].erase(0, 1);
1754 if(strstr(errormsg
, list
[0].c_str()) &&
1755 strstr(errormsg
, list
[1].c_str())) {
1756 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1763 // get the pkgname and reportfile
1764 pkgname
= flNotDir(pkgpath
);
1765 pos
= pkgname
.find('_');
1766 if(pos
!= string::npos
)
1767 pkgname
= pkgname
.substr(0, pos
);
1769 // find the package version and source package name
1770 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1771 if (Pkg
.end() == true)
1773 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1774 if (Ver
.end() == true)
1776 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1778 // if the file exists already, we check:
1779 // - if it was reported already (touched by apport).
1780 // If not, we do nothing, otherwise
1781 // we overwrite it. This is the same behaviour as apport
1782 // - if we have a report with the same pkgversion already
1784 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1785 if(FileExists(reportfile
))
1790 // check atime/mtime
1791 stat(reportfile
.c_str(), &buf
);
1792 if(buf
.st_mtime
> buf
.st_atime
)
1795 // check if the existing report is the same version
1796 report
= fopen(reportfile
.c_str(),"r");
1797 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1799 if(strstr(strbuf
,"Package:") == strbuf
)
1801 char pkgname
[255], version
[255];
1802 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1803 if(strcmp(pkgver
.c_str(), version
) == 0)
1813 // now write the report
1814 arch
= _config
->Find("APT::Architecture");
1815 report
= fopen(reportfile
.c_str(),"w");
1818 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1819 chmod(reportfile
.c_str(), 0);
1821 chmod(reportfile
.c_str(), 0600);
1822 fprintf(report
, "ProblemType: Package\n");
1823 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1824 time_t now
= time(NULL
);
1825 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1826 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1827 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1828 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1829 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1831 // ensure that the log is flushed
1833 fflush(d
->term_out
);
1835 // attach terminal log it if we have it
1836 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1837 if (!logfile_name
.empty())
1841 fprintf(report
, "DpkgTerminalLog:\n");
1842 log
= fopen(logfile_name
.c_str(),"r");
1846 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1847 fprintf(report
, " %s", buf
);
1848 fprintf(report
, " \n");
1853 // attach history log it if we have it
1854 string histfile_name
= _config
->FindFile("Dir::Log::History");
1855 if (!histfile_name
.empty())
1857 fprintf(report
, "DpkgHistoryLog:\n");
1858 FILE* log
= fopen(histfile_name
.c_str(),"r");
1862 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1863 fprintf(report
, " %s", buf
);
1868 // log the ordering, see dpkgpm.h and the "Ops" enum there
1869 const char *ops_str
[] = {
1877 fprintf(report
, "AptOrdering:\n");
1878 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1879 if ((*I
).Pkg
!= NULL
)
1880 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1882 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1884 // attach dmesg log (to learn about segfaults)
1885 if (FileExists("/bin/dmesg"))
1887 fprintf(report
, "Dmesg:\n");
1888 FILE *log
= popen("/bin/dmesg","r");
1892 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1893 fprintf(report
, " %s", buf
);
1898 // attach df -l log (to learn about filesystem status)
1899 if (FileExists("/bin/df"))
1902 fprintf(report
, "Df:\n");
1903 FILE *log
= popen("/bin/df -l","r");
1907 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1908 fprintf(report
, " %s", buf
);