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
);
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","Faild 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(SIGPIPE
, old_sigpipe
);
520 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
521 // ---------------------------------------------------------------------
524 void pkgDPkgPM::DoStdin(int master
)
526 unsigned char input_buf
[256] = {0,};
527 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
529 FileFd::Write(master
, input_buf
, len
);
531 d
->stdin_is_dev_null
= true;
534 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
535 // ---------------------------------------------------------------------
537 * read the terminal pty and write log
539 void pkgDPkgPM::DoTerminalPty(int master
)
541 unsigned char term_buf
[1024] = {0,0, };
543 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
544 if(len
== -1 && errno
== EIO
)
546 // this happens when the child is about to exit, we
547 // give it time to actually exit, otherwise we run
548 // into a race so we sleep for half a second.
549 struct timespec sleepfor
= { 0, 500000000 };
550 nanosleep(&sleepfor
, NULL
);
555 FileFd::Write(1, term_buf
, len
);
557 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
560 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
561 // ---------------------------------------------------------------------
564 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
566 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
568 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
570 /* dpkg sends strings like this:
571 'status: <pkg>: <pkg qstate>'
572 'status: <pkg>:<arch>: <pkg qstate>'
574 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
575 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
578 // we need to split on ": " (note the appended space) as the ':' is
579 // part of the pkgname:arch information that dpkg sends
581 // A dpkg error message may contain additional ":" (like
582 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
583 // so we need to ensure to not split too much
584 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
588 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
592 // build the (prefix, pkgname, action) tuple, position of this
593 // is different for "processing" or "status" messages
594 std::string prefix
= APT::String::Strip(list
[0]);
598 // "processing" has the form "processing: action: pkg or trigger"
599 // with action = ["install", "upgrade", "configure", "remove", "purge",
600 // "disappear", "trigproc"]
601 if (prefix
== "processing")
603 pkgname
= APT::String::Strip(list
[2]);
604 action
= APT::String::Strip(list
[1]);
605 // we don't care for the difference (as dpkg doesn't really either)
606 if (action
== "upgrade")
609 // "status" has the form: "status: pkg: state"
610 // with state in ["half-installed", "unpacked", "half-configured",
611 // "installed", "config-files", "not-installed"]
612 else if (prefix
== "status")
614 pkgname
= APT::String::Strip(list
[1]);
615 action
= APT::String::Strip(list
[2]);
618 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
623 /* handle the special cases first:
625 errors look like this:
626 '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
627 and conffile-prompt like this
628 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
630 if (prefix
== "status")
632 if(action
== "error")
634 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
637 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
640 else if(action
== "conffile-prompt")
642 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
648 // at this point we know that we should have a valid pkgname, so build all
651 // dpkg does not always send "pkgname:arch" so we add it here if needed
652 if (pkgname
.find(":") == std::string::npos
)
654 // find the package in the group that is touched by dpkg
655 // if there are multiple pkgs dpkg would send us a full pkgname:arch
656 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
657 if (Grp
.end() == false)
659 pkgCache::PkgIterator P
= Grp
.PackageList();
660 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
662 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
664 pkgname
= P
.FullName();
671 const char* const pkg
= pkgname
.c_str();
672 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
673 std::string arch
= "";
674 if (pkgname
.find(":") != string::npos
)
675 arch
= StringSplit(pkgname
, ":")[1];
676 std::string i18n_pkgname
= pkgname
;
677 if (arch
.size() != 0)
678 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
680 // 'processing' from dpkg looks like
681 // 'processing: action: pkg'
682 if(prefix
== "processing")
684 const std::pair
<const char *, const char *> * const iter
=
685 std::find_if(PackageProcessingOpsBegin
,
686 PackageProcessingOpsEnd
,
687 MatchProcessingOp(action
.c_str()));
688 if(iter
== PackageProcessingOpsEnd
)
691 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
695 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
696 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
698 // FIXME: this needs a muliarch testcase
699 // FIXME2: is "pkgname" here reliable with dpkg only sending us
701 if (action
== "disappear")
702 handleDisappearAction(pkgname
);
706 if (prefix
== "status")
708 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
709 if(PackageOpsDone
[pkg
] < states
.size())
711 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
712 if (next_action
&& Debug
== true)
713 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
714 << " action: " << action
<< " (expected: '" << next_action
<< "' "
715 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
717 // check if the package moved to the next dpkg state
718 if(next_action
&& (action
== next_action
))
720 // only read the translation if there is actually a next action
721 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
723 // we moved from one dpkg state to a new one, report that
724 ++PackageOpsDone
[pkg
];
728 strprintf(msg
, translation
, i18n_pkgname
.c_str());
729 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
735 // DPkgPM::handleDisappearAction /*{{{*/
736 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
738 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
739 if (unlikely(Pkg
.end() == true))
742 // record the package name for display and stuff later
743 disappearedPkgs
.insert(Pkg
.FullName(true));
745 // the disappeared package was auto-installed - nothing to do
746 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
748 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
749 if (unlikely(PkgVer
.end() == true))
751 /* search in the list of dependencies for (Pre)Depends,
752 check if this dependency has a Replaces on our package
753 and if so transfer the manual installed flag to it */
754 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
756 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
757 Dep
->Type
!= pkgCache::Dep::PreDepends
)
759 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
760 if (unlikely(Tar
.end() == true))
762 // the package is already marked as manual
763 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
765 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
766 if (TarVer
.end() == true)
768 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
770 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
772 if (Pkg
!= Rep
.TargetPkg())
774 // okay, they are strongly connected - transfer manual-bit
776 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
777 Cache
[Tar
].Flags
&= ~Flag::Auto
;
783 // DPkgPM::DoDpkgStatusFd /*{{{*/
784 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
786 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
787 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
790 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
792 // process line by line from the buffer
793 char *p
= d
->dpkgbuf
, *q
= nullptr;
794 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
797 ProcessDpkgStatusLine(p
);
798 p
= q
+ 1; // continue with next line
801 // check if we stripped the buffer clean
802 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
808 // otherwise move the unprocessed tail to the start and update pos
809 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
810 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
813 // DPkgPM::WriteHistoryTag /*{{{*/
814 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
816 size_t const length
= value
.length();
819 // poor mans rstrip(", ")
820 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
821 value
.erase(length
- 2, 2);
822 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
824 // DPkgPM::OpenLog /*{{{*/
825 bool pkgDPkgPM::OpenLog()
827 string
const logdir
= _config
->FindDir("Dir::Log");
828 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
829 // FIXME: use a better string after freeze
830 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
834 time_t const t
= time(NULL
);
836 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
837 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
840 string
const logfile_name
= flCombine(logdir
,
841 _config
->Find("Dir::Log::Terminal"));
842 if (!logfile_name
.empty())
844 d
->term_out
= fopen(logfile_name
.c_str(),"a");
845 if (d
->term_out
== NULL
)
846 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
847 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
848 SetCloseExec(fileno(d
->term_out
), true);
849 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
851 struct passwd
*pw
= getpwnam("root");
852 struct group
*gr
= getgrnam("adm");
853 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
854 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
856 if (chmod(logfile_name
.c_str(), 0640) != 0)
857 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
858 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
861 // write your history
862 string
const history_name
= flCombine(logdir
,
863 _config
->Find("Dir::Log::History"));
864 if (!history_name
.empty())
866 d
->history_out
= fopen(history_name
.c_str(),"a");
867 if (d
->history_out
== NULL
)
868 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
869 SetCloseExec(fileno(d
->history_out
), true);
870 chmod(history_name
.c_str(), 0644);
871 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
872 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
873 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
875 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
877 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
878 if (Cache
[I
].NewInstall() == true)
879 HISTORYINFO(install
, CANDIDATE_AUTO
)
880 else if (Cache
[I
].ReInstall() == true)
881 HISTORYINFO(reinstall
, CANDIDATE
)
882 else if (Cache
[I
].Upgrade() == true)
883 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
884 else if (Cache
[I
].Downgrade() == true)
885 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
886 else if (Cache
[I
].Delete() == true)
887 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
891 line
->append(I
.FullName(false)).append(" (");
892 switch (infostring
) {
893 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
895 line
->append(Cache
[I
].CandVersion
);
896 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
897 line
->append(", automatic");
899 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
900 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
904 if (_config
->Exists("Commandline::AsString") == true)
905 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
906 std::string RequestingUser
= AptHistoryRequestingUser();
907 if (RequestingUser
!= "")
908 WriteHistoryTag("Requested-By", RequestingUser
);
909 WriteHistoryTag("Install", install
);
910 WriteHistoryTag("Reinstall", reinstall
);
911 WriteHistoryTag("Upgrade", upgrade
);
912 WriteHistoryTag("Downgrade",downgrade
);
913 WriteHistoryTag("Remove",remove
);
914 WriteHistoryTag("Purge",purge
);
915 fflush(d
->history_out
);
921 // DPkg::CloseLog /*{{{*/
922 bool pkgDPkgPM::CloseLog()
925 time_t t
= time(NULL
);
927 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
928 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
932 fprintf(d
->term_out
, "Log ended: ");
933 fprintf(d
->term_out
, "%s", timestr
);
934 fprintf(d
->term_out
, "\n");
941 if (disappearedPkgs
.empty() == false)
944 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
945 d
!= disappearedPkgs
.end(); ++d
)
947 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
948 disappear
.append(*d
);
950 disappear
.append(", ");
952 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
954 WriteHistoryTag("Disappeared", disappear
);
956 if (d
->dpkg_error
.empty() == false)
957 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
958 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
959 fclose(d
->history_out
);
961 d
->history_out
= NULL
;
968 // This implements a racy version of pselect for those architectures
969 // that don't have a working implementation.
970 // FIXME: Probably can be removed on Lenny+1
971 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
972 fd_set
*exceptfds
, const struct timespec
*timeout
,
973 const sigset_t
*sigmask
)
979 tv
.tv_sec
= timeout
->tv_sec
;
980 tv
.tv_usec
= timeout
->tv_nsec
/1000;
982 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
983 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
984 sigprocmask(SIG_SETMASK
, &origmask
, 0);
989 // DPkgPM::BuildPackagesProgressMap /*{{{*/
990 void pkgDPkgPM::BuildPackagesProgressMap()
992 // map the dpkg states to the operations that are performed
993 // (this is sorted in the same way as Item::Ops)
994 static const struct DpkgState DpkgStatesOpMap
[][7] = {
997 {"half-installed", N_("Preparing %s")},
998 {"unpacked", N_("Unpacking %s") },
1001 // Configure operation
1003 {"unpacked",N_("Preparing to configure %s") },
1004 {"half-configured", N_("Configuring %s") },
1005 { "installed", N_("Installed %s")},
1010 {"half-configured", N_("Preparing for removal of %s")},
1011 {"half-installed", N_("Removing %s")},
1012 {"config-files", N_("Removed %s")},
1017 {"config-files", N_("Preparing to completely remove %s")},
1018 {"not-installed", N_("Completely removed %s")},
1023 // init the PackageOps map, go over the list of packages that
1024 // that will be [installed|configured|removed|purged] and add
1025 // them to the PackageOps map (the dpkg states it goes through)
1026 // and the PackageOpsTranslations (human readable strings)
1027 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1029 if((*I
).Pkg
.end() == true)
1032 string
const name
= (*I
).Pkg
.FullName();
1033 PackageOpsDone
[name
] = 0;
1034 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1036 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1040 /* one extra: We don't want the progress bar to reach 100%, especially not
1041 if we call dpkg --configure --pending and process a bunch of triggers
1042 while showing 100%. Also, spindown takes a while, so never reaching 100%
1043 is way more correct than reaching 100% while still doing stuff even if
1044 doing it this way is slightly bending the rules */
1048 bool pkgDPkgPM::Go(int StatusFd
)
1050 APT::Progress::PackageManager
*progress
= NULL
;
1052 progress
= APT::Progress::PackageManagerProgressFactory();
1054 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1056 return Go(progress
);
1059 void pkgDPkgPM::StartPtyMagic()
1061 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1064 if (d
->slave
!= NULL
)
1070 if (isatty(STDIN_FILENO
) == 0)
1071 d
->direct_stdin
= true;
1073 _error
->PushToStack();
1075 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1076 if (d
->master
== -1)
1077 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1078 else if (unlockpt(d
->master
) == -1)
1079 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1082 #ifdef HAVE_PTS_NAME_R
1083 char slave_name
[64]; // 64 is used by bionic
1084 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1086 char const * const slave_name
= ptsname(d
->master
);
1087 if (slave_name
== NULL
)
1089 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1092 d
->slave
= strdup(slave_name
);
1093 if (d
->slave
== NULL
)
1094 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1095 else if (grantpt(d
->master
) == -1)
1096 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1097 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1099 d
->tt_is_valid
= true;
1100 struct termios raw_tt
;
1101 // copy window size of stdout if its a 'good' terminal
1102 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1105 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1106 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1107 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1108 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1110 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1111 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1115 raw_tt
.c_lflag
&= ~ECHO
;
1116 raw_tt
.c_lflag
|= ISIG
;
1117 // block SIGTTOU during tcsetattr to prevent a hang if
1118 // the process is a member of the background process group
1119 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1120 sigemptyset(&d
->sigmask
);
1121 sigaddset(&d
->sigmask
, SIGTTOU
);
1122 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1123 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1124 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1125 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1128 if (d
->slave
!= NULL
)
1130 /* on linux, closing (and later reopening) all references to the slave
1131 makes the slave a death end, so we open it here to have one open all
1132 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1133 on kfreebsd we get an incorrect ("step like") output then while it has
1134 no problem with closing all references… so to avoid platform specific
1135 code here we combine both and be happy once more */
1136 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1141 if (_error
->PendingError() == true)
1143 if (d
->master
!= -1)
1148 if (d
->slave
!= NULL
)
1153 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1155 _error
->RevertToStack();
1157 void pkgDPkgPM::SetupSlavePtyMagic()
1159 if(d
->master
== -1 || d
->slave
== NULL
)
1162 if (close(d
->master
) == -1)
1163 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1166 _error
->FatalE("setsid", "Starting a new session for child failed!");
1168 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1170 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1171 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1172 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1175 unsigned short i
= 0;
1176 if (d
->direct_stdin
== true)
1179 if (dup2(slaveFd
, i
) == -1)
1180 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1182 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1183 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1189 void pkgDPkgPM::StopPtyMagic()
1191 if (d
->slave
!= NULL
)
1194 if (d
->protect_slave_from_dying
!= -1)
1196 close(d
->protect_slave_from_dying
);
1197 d
->protect_slave_from_dying
= -1;
1201 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1202 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1208 // DPkgPM::Go - Run the sequence /*{{{*/
1209 // ---------------------------------------------------------------------
1210 /* This globs the operations and calls dpkg
1212 * If it is called with a progress object apt will report the install
1213 * progress to this object. It maps the dpkg states a package goes
1214 * through to human readable (and i10n-able)
1215 * names and calculates a percentage for each step.
1217 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1219 auto const ItemIsEssential
= [](pkgDPkgPM::Item
const &I
) {
1220 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1221 if (cachegen
== "none" || cachegen
== "native")
1223 if (unlikely(I
.Pkg
.end()))
1225 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1228 pkgPackageManager::SigINTStop
= false;
1229 d
->progress
= progress
;
1231 // Generate the base argument list for dpkg
1232 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1233 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1234 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1235 [](std::string
const &s
) { return s
.c_str(); });
1236 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1237 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1238 size_t const BaseArgs
= Args
.size();
1243 // FIXME: do we really need this limit when we have MaxArgBytes?
1244 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1246 // try to figure out the max environment size
1247 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1250 OSArgMax
-= EnvironmentSize() - 2*1024;
1251 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1252 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1254 if (RunScripts("DPkg::Pre-Invoke") == false)
1257 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1260 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1261 // store auto-bits as they are supposed to be after dpkg is run
1262 if (noopDPkgInvocation
== false)
1263 Cache
.writeStateFile(NULL
);
1265 // support subpressing of triggers processing for special
1266 // cases like d-i that runs the triggers handling manually
1267 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1268 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1269 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1272 BuildPackagesProgressMap();
1274 d
->stdin_is_dev_null
= false;
1279 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1281 // start pty magic before the loop
1284 // Tell the progress that its starting and fork dpkg
1285 d
->progress
->Start(d
->master
);
1287 // this loop is runs once per dpkg operation
1288 vector
<Item
>::const_iterator I
= List
.begin();
1289 while (I
!= List
.end())
1291 // Do all actions with the same Op in one run
1292 vector
<Item
>::const_iterator J
= I
;
1293 if (TriggersPending
== true)
1294 for (; J
!= List
.end(); ++J
)
1298 if (J
->Op
!= Item::TriggersPending
)
1300 vector
<Item
>::const_iterator T
= J
+ 1;
1301 if (T
!= List
.end() && T
->Op
== I
->Op
)
1306 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1309 // keep track of allocated strings for multiarch package names
1310 std::vector
<char *> Packages
;
1312 // start with the baseset of arguments
1313 unsigned long Size
= StartSize
;
1314 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1316 // Now check if we are within the MaxArgs limit
1318 // this code below is problematic, because it may happen that
1319 // the argument list is split in a way that A depends on B
1320 // and they are in the same "--configure A B" run
1321 // - with the split they may now be configured in different
1322 // runs, using Immediate-Configure-All can help prevent this.
1323 if (J
- I
> (signed)MaxArgs
)
1326 unsigned long const size
= MaxArgs
+ 10;
1328 Packages
.reserve(size
);
1332 unsigned long const size
= (J
- I
) + 10;
1334 Packages
.reserve(size
);
1339 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1341 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1342 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1344 ADDARGC("--status-fd");
1345 char status_fd_buf
[20];
1346 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1347 ADDARG(status_fd_buf
);
1348 unsigned long const Op
= I
->Op
;
1353 ADDARGC("--force-depends");
1354 if (std::any_of(I
, J
, ItemIsEssential
))
1355 ADDARGC("--force-remove-essential");
1356 ADDARGC("--remove");
1360 ADDARGC("--force-depends");
1361 if (std::any_of(I
, J
, ItemIsEssential
))
1362 ADDARGC("--force-remove-essential");
1366 case Item::Configure
:
1367 ADDARGC("--configure");
1370 case Item::ConfigurePending
:
1371 ADDARGC("--configure");
1372 ADDARGC("--pending");
1375 case Item::TriggersPending
:
1376 ADDARGC("--triggers-only");
1377 ADDARGC("--pending");
1381 ADDARGC("--unpack");
1382 ADDARGC("--auto-deconfigure");
1386 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1387 I
->Op
!= Item::ConfigurePending
)
1389 ADDARGC("--no-triggers");
1393 // Write in the file or package names
1394 if (I
->Op
== Item::Install
)
1396 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1398 if (I
->File
[0] != '/')
1399 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1400 Args
.push_back(I
->File
.c_str());
1401 Size
+= I
->File
.length();
1406 string
const nativeArch
= _config
->Find("APT::Architecture");
1407 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1408 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1410 if((*I
).Pkg
.end() == true)
1412 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1414 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1415 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1416 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1417 strcmp(I
->Pkg
.Arch(), "none") == 0))
1419 char const * const name
= I
->Pkg
.Name();
1424 pkgCache::VerIterator PkgVer
;
1425 std::string name
= I
->Pkg
.Name();
1426 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1428 PkgVer
= I
->Pkg
.CurrentVer();
1429 if(PkgVer
.end() == true)
1430 PkgVer
= FindNowVersion(I
->Pkg
);
1433 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1434 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1435 ; // never arch-qualify a package without an arch
1436 else if (PkgVer
.end() == false)
1437 name
.append(":").append(PkgVer
.Arch());
1439 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1440 char * const fullname
= strdup(name
.c_str());
1441 Packages
.push_back(fullname
);
1445 // skip configure action if all sheduled packages disappeared
1446 if (oldSize
== Size
)
1453 if (noopDPkgInvocation
== true)
1455 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1456 a
!= Args
.end(); ++a
)
1459 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1460 p
!= Packages
.end(); ++p
)
1465 Args
.push_back(NULL
);
1471 /* Mask off sig int/quit. We do this because dpkg also does when
1472 it forks scripts. What happens is that when you hit ctrl-c it sends
1473 it to all processes in the group. Since dpkg ignores the signal
1474 it doesn't die but we do! So we must also ignore it */
1475 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1476 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1478 // Check here for any SIGINT
1479 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1483 // ignore SIGHUP as well (debian #463030)
1484 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1487 d
->progress
->StartDpkg();
1488 std::set
<int> KeepFDs
;
1489 KeepFDs
.insert(fd
[1]);
1490 MergeKeepFdsFromConfiguration(KeepFDs
);
1491 pid_t Child
= ExecFork(KeepFDs
);
1494 // This is the child
1495 SetupSlavePtyMagic();
1496 close(fd
[0]); // close the read end of the pipe
1498 debSystem::DpkgChrootDirectory();
1500 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1503 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1507 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1510 // Discard everything in stdin before forking dpkg
1511 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1514 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1516 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1520 execvp(Args
[0], (char**) &Args
[0]);
1521 cerr
<< "Could not exec dpkg!" << endl
;
1526 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1532 // we read from dpkg here
1533 int const _dpkgin
= fd
[0];
1534 close(fd
[1]); // close the write end of the pipe
1537 sigemptyset(&d
->sigmask
);
1538 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1540 /* free vectors (and therefore memory) as we don't need the included data anymore */
1541 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1542 p
!= Packages
.end(); ++p
)
1546 // the result of the waitpid call
1549 bool waitpid_failure
= false;
1550 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1552 // error handling, waitpid returned -1
1555 waitpid_failure
= true;
1559 // wait for input or output here
1561 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1562 FD_SET(STDIN_FILENO
, &rfds
);
1563 FD_SET(_dpkgin
, &rfds
);
1565 FD_SET(d
->master
, &rfds
);
1567 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1568 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1569 &tv
, &d
->original_sigmask
);
1570 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1571 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1572 NULL
, &tv
, &d
->original_sigmask
);
1573 d
->progress
->Pulse();
1574 if (select_ret
== 0)
1576 else if (select_ret
< 0 && errno
== EINTR
)
1578 else if (select_ret
< 0)
1580 perror("select() returned error");
1584 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1585 DoTerminalPty(d
->master
);
1586 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1588 if(FD_ISSET(_dpkgin
, &rfds
))
1589 DoDpkgStatusFd(_dpkgin
);
1593 // Restore sig int/quit
1594 signal(SIGQUIT
,old_SIGQUIT
);
1595 signal(SIGINT
,old_SIGINT
);
1596 signal(SIGHUP
,old_SIGHUP
);
1598 if (waitpid_failure
== true)
1600 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1601 _error
->Error("%s", d
->dpkg_error
.c_str());
1605 // Check for an error code.
1606 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1608 // if it was set to "keep-dpkg-running" then we won't return
1609 // here but keep the loop going and just report it as a error
1611 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1613 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1614 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1615 else if (WIFEXITED(Status
) != 0)
1616 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1618 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1619 _error
->Error("%s", d
->dpkg_error
.c_str());
1625 // dpkg is done at this point
1629 if (pkgPackageManager::SigINTStop
)
1630 _error
->Warning(_("Operation was interrupted before it could finish"));
1632 if (noopDPkgInvocation
== false)
1634 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1635 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1636 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1638 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1639 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1641 _error
->PushToStack();
1642 pkgCacheFile CacheFile
;
1643 CacheFile
.BuildCaches(NULL
, true);
1644 _error
->RevertToStack();
1649 // disappearing packages can forward their auto-bit
1650 if (disappearedPkgs
.empty() == false)
1651 Cache
.writeStateFile(NULL
);
1653 d
->progress
->Stop();
1655 if (RunScripts("DPkg::Post-Invoke") == false)
1658 return d
->dpkg_error
.empty();
1661 void SigINT(int /*sig*/) {
1662 pkgPackageManager::SigINTStop
= true;
1665 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1666 // ---------------------------------------------------------------------
1668 void pkgDPkgPM::Reset()
1670 List
.erase(List
.begin(),List
.end());
1673 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1674 // ---------------------------------------------------------------------
1676 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1678 // If apport doesn't exist or isn't installed do nothing
1679 // This e.g. prevents messages in 'universes' without apport
1680 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1681 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1684 string pkgname
, reportfile
, pkgver
, arch
;
1685 string::size_type pos
;
1688 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1690 std::clog
<< "configured to not write apport reports" << std::endl
;
1694 // only report the first errors
1695 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1697 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1701 // check if its not a follow up error
1702 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1703 if(strstr(errormsg
, needle
) != NULL
) {
1704 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1708 // do not report disk-full failures
1709 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1710 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1714 // do not report out-of-memory failures
1715 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1716 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1717 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1721 // do not report bugs regarding inaccessible local files
1722 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1723 strstr(errormsg
, "cannot access archive") != NULL
) {
1724 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1728 // do not report errors encountered when decompressing packages
1729 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1730 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1734 // do not report dpkg I/O errors, this is a format string, so we compare
1735 // the prefix and the suffix of the error with the dpkg error message
1736 vector
<string
> io_errors
;
1737 io_errors
.push_back(string("failed to read"));
1738 io_errors
.push_back(string("failed to write"));
1739 io_errors
.push_back(string("failed to seek"));
1740 io_errors
.push_back(string("unexpected end of file or stream"));
1742 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1744 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1745 if (list
.size() > 1) {
1746 // we need to split %s, VectorizeString only allows char so we need
1747 // to kill the "s" manually
1748 if (list
[1].size() > 1) {
1749 list
[1].erase(0, 1);
1750 if(strstr(errormsg
, list
[0].c_str()) &&
1751 strstr(errormsg
, list
[1].c_str())) {
1752 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1759 // get the pkgname and reportfile
1760 pkgname
= flNotDir(pkgpath
);
1761 pos
= pkgname
.find('_');
1762 if(pos
!= string::npos
)
1763 pkgname
= pkgname
.substr(0, pos
);
1765 // find the package version and source package name
1766 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1767 if (Pkg
.end() == true)
1769 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1770 if (Ver
.end() == true)
1772 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1774 // if the file exists already, we check:
1775 // - if it was reported already (touched by apport).
1776 // If not, we do nothing, otherwise
1777 // we overwrite it. This is the same behaviour as apport
1778 // - if we have a report with the same pkgversion already
1780 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1781 if(FileExists(reportfile
))
1786 // check atime/mtime
1787 stat(reportfile
.c_str(), &buf
);
1788 if(buf
.st_mtime
> buf
.st_atime
)
1791 // check if the existing report is the same version
1792 report
= fopen(reportfile
.c_str(),"r");
1793 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1795 if(strstr(strbuf
,"Package:") == strbuf
)
1797 char pkgname
[255], version
[255];
1798 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1799 if(strcmp(pkgver
.c_str(), version
) == 0)
1809 // now write the report
1810 arch
= _config
->Find("APT::Architecture");
1811 report
= fopen(reportfile
.c_str(),"w");
1814 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1815 chmod(reportfile
.c_str(), 0);
1817 chmod(reportfile
.c_str(), 0600);
1818 fprintf(report
, "ProblemType: Package\n");
1819 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1820 time_t now
= time(NULL
);
1821 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1822 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1823 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1824 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1825 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1827 // ensure that the log is flushed
1829 fflush(d
->term_out
);
1831 // attach terminal log it if we have it
1832 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1833 if (!logfile_name
.empty())
1837 fprintf(report
, "DpkgTerminalLog:\n");
1838 log
= fopen(logfile_name
.c_str(),"r");
1842 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1843 fprintf(report
, " %s", buf
);
1844 fprintf(report
, " \n");
1849 // attach history log it if we have it
1850 string histfile_name
= _config
->FindFile("Dir::Log::History");
1851 if (!histfile_name
.empty())
1853 fprintf(report
, "DpkgHistoryLog:\n");
1854 FILE* log
= fopen(histfile_name
.c_str(),"r");
1858 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1859 fprintf(report
, " %s", buf
);
1864 // log the ordering, see dpkgpm.h and the "Ops" enum there
1865 const char *ops_str
[] = {
1873 fprintf(report
, "AptOrdering:\n");
1874 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1875 if ((*I
).Pkg
!= NULL
)
1876 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1878 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1880 // attach dmesg log (to learn about segfaults)
1881 if (FileExists("/bin/dmesg"))
1883 fprintf(report
, "Dmesg:\n");
1884 FILE *log
= popen("/bin/dmesg","r");
1888 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1889 fprintf(report
, " %s", buf
);
1894 // attach df -l log (to learn about filesystem status)
1895 if (FileExists("/bin/df"))
1898 fprintf(report
, "Df:\n");
1899 FILE *log
= popen("/bin/df -l","r");
1903 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1904 fprintf(report
, " %s", buf
);