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>
57 APT_PURE
static unsigned int
60 unsigned int size
= 0;
61 char **envp
= environ
;
64 size
+= strlen (*envp
++) + 1;
69 class pkgDPkgPMPrivate
72 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
73 term_out(NULL
), history_out(NULL
),
74 progress(NULL
), tt_is_valid(false), master(-1),
75 slave(NULL
), protect_slave_from_dying(-1),
83 bool stdin_is_dev_null
;
84 // the buffer we use for the dpkg status-fd reading
90 APT::Progress::PackageManager
*progress
;
97 int protect_slave_from_dying
;
101 sigset_t original_sigmask
;
108 // Maps the dpkg "processing" info to human readable names. Entry 0
109 // of each array is the key, entry 1 is the value.
110 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
111 std::make_pair("install", N_("Installing %s")),
112 std::make_pair("configure", N_("Configuring %s")),
113 std::make_pair("remove", N_("Removing %s")),
114 std::make_pair("purge", N_("Completely removing %s")),
115 std::make_pair("disappear", N_("Noting disappearance of %s")),
116 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
119 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
120 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
122 // Predicate to test whether an entry in the PackageProcessingOps
123 // array matches a string.
124 class MatchProcessingOp
129 MatchProcessingOp(const char *the_target
)
134 bool operator()(const std::pair
<const char *, const char *> &pair
) const
136 return strcmp(pair
.first
, target
) == 0;
141 /* helper function to ionice the given PID
143 there is no C header for ionice yet - just the syscall interface
144 so we use the binary from util-linux
149 if (!FileExists("/usr/bin/ionice"))
151 pid_t Process
= ExecFork();
155 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
157 Args
[0] = "/usr/bin/ionice";
161 execv(Args
[0], (char **)Args
);
163 return ExecWait(Process
, "ionice");
166 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
167 // ---------------------------------------------------------------------
168 /* This is helpful when a package is no longer installed but has residual
172 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
174 pkgCache::VerIterator Ver
;
175 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
176 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
177 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
179 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
186 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
187 // ---------------------------------------------------------------------
189 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
190 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
194 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
195 // ---------------------------------------------------------------------
197 pkgDPkgPM::~pkgDPkgPM()
202 // DPkgPM::Install - Install a package /*{{{*/
203 // ---------------------------------------------------------------------
204 /* Add an install operation to the sequence list */
205 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
207 if (File
.empty() == true || Pkg
.end() == true)
208 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
210 // If the filename string begins with DPkg::Chroot-Directory, return the
211 // substr that is within the chroot so dpkg can access it.
212 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
213 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
215 size_t len
= chrootdir
.length();
216 if (chrootdir
.at(len
- 1) == '/')
218 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
221 List
.push_back(Item(Item::Install
,Pkg
,File
));
226 // DPkgPM::Configure - Configure a package /*{{{*/
227 // ---------------------------------------------------------------------
228 /* Add a configure operation to the sequence list */
229 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
231 if (Pkg
.end() == true)
234 List
.push_back(Item(Item::Configure
, Pkg
));
236 // Use triggers for config calls if we configure "smart"
237 // as otherwise Pre-Depends will not be satisfied, see #526774
238 if (_config
->FindB("DPkg::TriggersPending", false) == true)
239 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
244 // DPkgPM::Remove - Remove a package /*{{{*/
245 // ---------------------------------------------------------------------
246 /* Add a remove operation to the sequence list */
247 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
249 if (Pkg
.end() == true)
253 List
.push_back(Item(Item::Purge
,Pkg
));
255 List
.push_back(Item(Item::Remove
,Pkg
));
259 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
260 // ---------------------------------------------------------------------
261 /* This is part of the helper script communication interface, it sends
262 very complete information down to the other end of the pipe.*/
263 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
265 return SendPkgsInfo(F
, 2);
267 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
269 // This version of APT supports only v3, so don't sent higher versions
271 fprintf(F
,"VERSION %u\n", Version
);
273 fprintf(F
,"VERSION 3\n");
275 /* Write out all of the configuration directives by walking the
276 configuration tree */
277 const Configuration::Item
*Top
= _config
->Tree(0);
280 if (Top
->Value
.empty() == false)
283 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
284 QuoteString(Top
->Value
,"\n").c_str());
293 while (Top
!= 0 && Top
->Next
== 0)
300 // Write out the package actions in order.
301 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
303 if(I
->Pkg
.end() == true)
306 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
308 fprintf(F
,"%s ",I
->Pkg
.Name());
310 // Current version which we are going to replace
311 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
312 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
313 CurVer
= FindNowVersion(I
->Pkg
);
315 if (CurVer
.end() == true)
320 fprintf(F
, "- - none ");
324 fprintf(F
, "%s ", CurVer
.VerStr());
326 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
329 // Show the compare operator between current and install version
330 if (S
.InstallVer
!= 0)
332 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
334 if (CurVer
.end() == false)
335 Comp
= InstVer
.CompareVer(CurVer
);
342 fprintf(F
, "%s ", InstVer
.VerStr());
344 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
351 fprintf(F
, "> - - none ");
354 // Show the filename/operation
355 if (I
->Op
== Item::Install
)
358 if (I
->File
[0] != '/')
359 fprintf(F
,"**ERROR**\n");
361 fprintf(F
,"%s\n",I
->File
.c_str());
363 else if (I
->Op
== Item::Configure
)
364 fprintf(F
,"**CONFIGURE**\n");
365 else if (I
->Op
== Item::Remove
||
366 I
->Op
== Item::Purge
)
367 fprintf(F
,"**REMOVE**\n");
375 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
376 // ---------------------------------------------------------------------
377 /* This looks for a list of scripts to run from the configuration file
378 each one is run and is fed on standard input a list of all .deb files
379 that are due to be installed. */
380 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
384 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
385 if (Opts
== 0 || Opts
->Child
== 0)
389 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
391 unsigned int Count
= 1;
392 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
394 if (Opts
->Value
.empty() == true)
397 if(_config
->FindB("Debug::RunScripts", false) == true)
398 std::clog
<< "Running external script with list of all .deb file: '"
399 << Opts
->Value
<< "'" << std::endl
;
401 // Determine the protocol version
402 string OptSec
= Opts
->Value
;
403 string::size_type Pos
;
404 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
405 Pos
= OptSec
.length();
406 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
408 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
409 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
412 std::set
<int> KeepFDs
;
413 MergeKeepFdsFromConfiguration(KeepFDs
);
415 if (pipe(Pipes
) != 0) {
416 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
419 if (InfoFD
!= (unsigned)Pipes
[0])
420 SetCloseExec(Pipes
[0],true);
422 KeepFDs
.insert(Pipes
[0]);
425 SetCloseExec(Pipes
[1],true);
427 // Purified Fork for running the script
428 pid_t Process
= ExecFork(KeepFDs
);
432 dup2(Pipes
[0], InfoFD
);
433 SetCloseExec(STDOUT_FILENO
,false);
434 SetCloseExec(STDIN_FILENO
,false);
435 SetCloseExec(STDERR_FILENO
,false);
438 strprintf(hookfd
, "%d", InfoFD
);
439 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
441 debSystem::DpkgChrootDirectory();
445 Args
[2] = Opts
->Value
.c_str();
447 execv(Args
[0],(char **)Args
);
451 FILE *F
= fdopen(Pipes
[1],"w");
453 result
= _error
->Errno("fdopen","Faild to open new FD");
457 // Feed it the filenames.
460 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
462 // Only deal with packages to be installed from .deb
463 if (I
->Op
!= Item::Install
)
467 if (I
->File
[0] != '/')
470 /* Feed the filename of each package that is pending install
472 fprintf(F
,"%s\n",I
->File
.c_str());
478 SendPkgsInfo(F
, Version
);
482 // Clean up the sub process
483 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
484 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
488 signal(SIGPIPE
, old_sigpipe
);
493 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
494 // ---------------------------------------------------------------------
497 void pkgDPkgPM::DoStdin(int master
)
499 unsigned char input_buf
[256] = {0,};
500 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
502 FileFd::Write(master
, input_buf
, len
);
504 d
->stdin_is_dev_null
= true;
507 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
508 // ---------------------------------------------------------------------
510 * read the terminal pty and write log
512 void pkgDPkgPM::DoTerminalPty(int master
)
514 unsigned char term_buf
[1024] = {0,0, };
516 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
517 if(len
== -1 && errno
== EIO
)
519 // this happens when the child is about to exit, we
520 // give it time to actually exit, otherwise we run
521 // into a race so we sleep for half a second.
522 struct timespec sleepfor
= { 0, 500000000 };
523 nanosleep(&sleepfor
, NULL
);
528 FileFd::Write(1, term_buf
, len
);
530 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
533 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
534 // ---------------------------------------------------------------------
537 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
539 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
541 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
543 /* dpkg sends strings like this:
544 'status: <pkg>: <pkg qstate>'
545 'status: <pkg>:<arch>: <pkg qstate>'
547 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
548 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
551 // we need to split on ": " (note the appended space) as the ':' is
552 // part of the pkgname:arch information that dpkg sends
554 // A dpkg error message may contain additional ":" (like
555 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
556 // so we need to ensure to not split too much
557 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
561 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
565 // build the (prefix, pkgname, action) tuple, position of this
566 // is different for "processing" or "status" messages
567 std::string prefix
= APT::String::Strip(list
[0]);
571 // "processing" has the form "processing: action: pkg or trigger"
572 // with action = ["install", "upgrade", "configure", "remove", "purge",
573 // "disappear", "trigproc"]
574 if (prefix
== "processing")
576 pkgname
= APT::String::Strip(list
[2]);
577 action
= APT::String::Strip(list
[1]);
578 // we don't care for the difference (as dpkg doesn't really either)
579 if (action
== "upgrade")
582 // "status" has the form: "status: pkg: state"
583 // with state in ["half-installed", "unpacked", "half-configured",
584 // "installed", "config-files", "not-installed"]
585 else if (prefix
== "status")
587 pkgname
= APT::String::Strip(list
[1]);
588 action
= APT::String::Strip(list
[2]);
591 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
596 /* handle the special cases first:
598 errors look like this:
599 '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
600 and conffile-prompt like this
601 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
603 if (prefix
== "status")
605 if(action
== "error")
607 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
610 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
613 else if(action
== "conffile-prompt")
615 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
621 // at this point we know that we should have a valid pkgname, so build all
624 // dpkg does not always send "pkgname:arch" so we add it here if needed
625 if (pkgname
.find(":") == std::string::npos
)
627 // find the package in the group that is touched by dpkg
628 // if there are multiple pkgs dpkg would send us a full pkgname:arch
629 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
630 if (Grp
.end() == false)
632 pkgCache::PkgIterator P
= Grp
.PackageList();
633 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
635 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
637 pkgname
= P
.FullName();
644 const char* const pkg
= pkgname
.c_str();
645 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
646 std::string arch
= "";
647 if (pkgname
.find(":") != string::npos
)
648 arch
= StringSplit(pkgname
, ":")[1];
649 std::string i18n_pkgname
= pkgname
;
650 if (arch
.size() != 0)
651 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
653 // 'processing' from dpkg looks like
654 // 'processing: action: pkg'
655 if(prefix
== "processing")
657 const std::pair
<const char *, const char *> * const iter
=
658 std::find_if(PackageProcessingOpsBegin
,
659 PackageProcessingOpsEnd
,
660 MatchProcessingOp(action
.c_str()));
661 if(iter
== PackageProcessingOpsEnd
)
664 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
668 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
669 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
671 // FIXME: this needs a muliarch testcase
672 // FIXME2: is "pkgname" here reliable with dpkg only sending us
674 if (action
== "disappear")
675 handleDisappearAction(pkgname
);
679 if (prefix
== "status")
681 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
682 if(PackageOpsDone
[pkg
] < states
.size())
684 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
685 if (next_action
&& Debug
== true)
686 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
687 << " action: " << action
<< " (expected: '" << next_action
<< "' "
688 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
690 // check if the package moved to the next dpkg state
691 if(next_action
&& (action
== next_action
))
693 // only read the translation if there is actually a next action
694 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
696 // we moved from one dpkg state to a new one, report that
697 ++PackageOpsDone
[pkg
];
701 strprintf(msg
, translation
, i18n_pkgname
.c_str());
702 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
708 // DPkgPM::handleDisappearAction /*{{{*/
709 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
711 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
712 if (unlikely(Pkg
.end() == true))
715 // record the package name for display and stuff later
716 disappearedPkgs
.insert(Pkg
.FullName(true));
718 // the disappeared package was auto-installed - nothing to do
719 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
721 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
722 if (unlikely(PkgVer
.end() == true))
724 /* search in the list of dependencies for (Pre)Depends,
725 check if this dependency has a Replaces on our package
726 and if so transfer the manual installed flag to it */
727 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
729 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
730 Dep
->Type
!= pkgCache::Dep::PreDepends
)
732 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
733 if (unlikely(Tar
.end() == true))
735 // the package is already marked as manual
736 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
738 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
739 if (TarVer
.end() == true)
741 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
743 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
745 if (Pkg
!= Rep
.TargetPkg())
747 // okay, they are strongly connected - transfer manual-bit
749 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
750 Cache
[Tar
].Flags
&= ~Flag::Auto
;
756 // DPkgPM::DoDpkgStatusFd /*{{{*/
757 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
759 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
760 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
763 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
765 // process line by line from the buffer
766 char *p
= d
->dpkgbuf
, *q
= nullptr;
767 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
770 ProcessDpkgStatusLine(p
);
771 p
= q
+ 1; // continue with next line
774 // check if we stripped the buffer clean
775 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
781 // otherwise move the unprocessed tail to the start and update pos
782 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
783 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
786 // DPkgPM::WriteHistoryTag /*{{{*/
787 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
789 size_t const length
= value
.length();
792 // poor mans rstrip(", ")
793 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
794 value
.erase(length
- 2, 2);
795 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
797 // DPkgPM::OpenLog /*{{{*/
798 bool pkgDPkgPM::OpenLog()
800 string
const logdir
= _config
->FindDir("Dir::Log");
801 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
802 // FIXME: use a better string after freeze
803 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
807 time_t const t
= time(NULL
);
809 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
810 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
813 string
const logfile_name
= flCombine(logdir
,
814 _config
->Find("Dir::Log::Terminal"));
815 if (!logfile_name
.empty())
817 d
->term_out
= fopen(logfile_name
.c_str(),"a");
818 if (d
->term_out
== NULL
)
819 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
820 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
821 SetCloseExec(fileno(d
->term_out
), true);
822 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
824 struct passwd
*pw
= getpwnam("root");
825 struct group
*gr
= getgrnam("adm");
826 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
827 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
829 if (chmod(logfile_name
.c_str(), 0640) != 0)
830 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
831 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
834 // write your history
835 string
const history_name
= flCombine(logdir
,
836 _config
->Find("Dir::Log::History"));
837 if (!history_name
.empty())
839 d
->history_out
= fopen(history_name
.c_str(),"a");
840 if (d
->history_out
== NULL
)
841 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
842 SetCloseExec(fileno(d
->history_out
), true);
843 chmod(history_name
.c_str(), 0644);
844 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
845 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
846 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
848 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
850 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
851 if (Cache
[I
].NewInstall() == true)
852 HISTORYINFO(install
, CANDIDATE_AUTO
)
853 else if (Cache
[I
].ReInstall() == true)
854 HISTORYINFO(reinstall
, CANDIDATE
)
855 else if (Cache
[I
].Upgrade() == true)
856 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
857 else if (Cache
[I
].Downgrade() == true)
858 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
859 else if (Cache
[I
].Delete() == true)
860 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
864 line
->append(I
.FullName(false)).append(" (");
865 switch (infostring
) {
866 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
868 line
->append(Cache
[I
].CandVersion
);
869 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
870 line
->append(", automatic");
872 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
873 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
877 if (_config
->Exists("Commandline::AsString") == true)
878 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
879 WriteHistoryTag("Install", install
);
880 WriteHistoryTag("Reinstall", reinstall
);
881 WriteHistoryTag("Upgrade", upgrade
);
882 WriteHistoryTag("Downgrade",downgrade
);
883 WriteHistoryTag("Remove",remove
);
884 WriteHistoryTag("Purge",purge
);
885 fflush(d
->history_out
);
891 // DPkg::CloseLog /*{{{*/
892 bool pkgDPkgPM::CloseLog()
895 time_t t
= time(NULL
);
897 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
898 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
902 fprintf(d
->term_out
, "Log ended: ");
903 fprintf(d
->term_out
, "%s", timestr
);
904 fprintf(d
->term_out
, "\n");
911 if (disappearedPkgs
.empty() == false)
914 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
915 d
!= disappearedPkgs
.end(); ++d
)
917 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
918 disappear
.append(*d
);
920 disappear
.append(", ");
922 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
924 WriteHistoryTag("Disappeared", disappear
);
926 if (d
->dpkg_error
.empty() == false)
927 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
928 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
929 fclose(d
->history_out
);
931 d
->history_out
= NULL
;
938 // This implements a racy version of pselect for those architectures
939 // that don't have a working implementation.
940 // FIXME: Probably can be removed on Lenny+1
941 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
942 fd_set
*exceptfds
, const struct timespec
*timeout
,
943 const sigset_t
*sigmask
)
949 tv
.tv_sec
= timeout
->tv_sec
;
950 tv
.tv_usec
= timeout
->tv_nsec
/1000;
952 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
953 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
954 sigprocmask(SIG_SETMASK
, &origmask
, 0);
959 // DPkgPM::BuildPackagesProgressMap /*{{{*/
960 void pkgDPkgPM::BuildPackagesProgressMap()
962 // map the dpkg states to the operations that are performed
963 // (this is sorted in the same way as Item::Ops)
964 static const struct DpkgState DpkgStatesOpMap
[][7] = {
967 {"half-installed", N_("Preparing %s")},
968 {"unpacked", N_("Unpacking %s") },
971 // Configure operation
973 {"unpacked",N_("Preparing to configure %s") },
974 {"half-configured", N_("Configuring %s") },
975 { "installed", N_("Installed %s")},
980 {"half-configured", N_("Preparing for removal of %s")},
981 {"half-installed", N_("Removing %s")},
982 {"config-files", N_("Removed %s")},
987 {"config-files", N_("Preparing to completely remove %s")},
988 {"not-installed", N_("Completely removed %s")},
993 // init the PackageOps map, go over the list of packages that
994 // that will be [installed|configured|removed|purged] and add
995 // them to the PackageOps map (the dpkg states it goes through)
996 // and the PackageOpsTranslations (human readable strings)
997 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
999 if((*I
).Pkg
.end() == true)
1002 string
const name
= (*I
).Pkg
.FullName();
1003 PackageOpsDone
[name
] = 0;
1004 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1006 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1010 /* one extra: We don't want the progress bar to reach 100%, especially not
1011 if we call dpkg --configure --pending and process a bunch of triggers
1012 while showing 100%. Also, spindown takes a while, so never reaching 100%
1013 is way more correct than reaching 100% while still doing stuff even if
1014 doing it this way is slightly bending the rules */
1018 bool pkgDPkgPM::Go(int StatusFd
)
1020 APT::Progress::PackageManager
*progress
= NULL
;
1022 progress
= APT::Progress::PackageManagerProgressFactory();
1024 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1026 return Go(progress
);
1029 void pkgDPkgPM::StartPtyMagic()
1031 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1034 if (d
->slave
!= NULL
)
1040 if (isatty(STDIN_FILENO
) == 0)
1041 d
->direct_stdin
= true;
1043 _error
->PushToStack();
1045 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1046 if (d
->master
== -1)
1047 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1048 else if (unlockpt(d
->master
) == -1)
1049 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1052 #ifdef HAVE_PTS_NAME_R
1053 char slave_name
[64]; // 64 is used by bionic
1054 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1056 char const * const slave_name
= ptsname(d
->master
);
1057 if (slave_name
== NULL
)
1059 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1062 d
->slave
= strdup(slave_name
);
1063 if (d
->slave
== NULL
)
1064 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1065 else if (grantpt(d
->master
) == -1)
1066 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1067 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1069 d
->tt_is_valid
= true;
1070 struct termios raw_tt
;
1071 // copy window size of stdout if its a 'good' terminal
1072 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1075 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1076 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1077 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1078 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1080 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1081 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1085 raw_tt
.c_lflag
&= ~ECHO
;
1086 raw_tt
.c_lflag
|= ISIG
;
1087 // block SIGTTOU during tcsetattr to prevent a hang if
1088 // the process is a member of the background process group
1089 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1090 sigemptyset(&d
->sigmask
);
1091 sigaddset(&d
->sigmask
, SIGTTOU
);
1092 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1093 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1094 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1095 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1098 if (d
->slave
!= NULL
)
1100 /* on linux, closing (and later reopening) all references to the slave
1101 makes the slave a death end, so we open it here to have one open all
1102 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1103 on kfreebsd we get an incorrect ("step like") output then while it has
1104 no problem with closing all references… so to avoid platform specific
1105 code here we combine both and be happy once more */
1106 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1111 if (_error
->PendingError() == true)
1113 if (d
->master
!= -1)
1118 if (d
->slave
!= NULL
)
1123 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1125 _error
->RevertToStack();
1127 void pkgDPkgPM::SetupSlavePtyMagic()
1129 if(d
->master
== -1 || d
->slave
== NULL
)
1132 if (close(d
->master
) == -1)
1133 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1136 _error
->FatalE("setsid", "Starting a new session for child failed!");
1138 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1140 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1141 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1142 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1145 unsigned short i
= 0;
1146 if (d
->direct_stdin
== true)
1149 if (dup2(slaveFd
, i
) == -1)
1150 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1152 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1153 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1159 void pkgDPkgPM::StopPtyMagic()
1161 if (d
->slave
!= NULL
)
1164 if (d
->protect_slave_from_dying
!= -1)
1166 close(d
->protect_slave_from_dying
);
1167 d
->protect_slave_from_dying
= -1;
1171 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1172 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1178 // DPkgPM::Go - Run the sequence /*{{{*/
1179 // ---------------------------------------------------------------------
1180 /* This globs the operations and calls dpkg
1182 * If it is called with a progress object apt will report the install
1183 * progress to this object. It maps the dpkg states a package goes
1184 * through to human readable (and i10n-able)
1185 * names and calculates a percentage for each step.
1187 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1189 pkgPackageManager::SigINTStop
= false;
1190 d
->progress
= progress
;
1192 // Generate the base argument list for dpkg
1193 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1194 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1195 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1196 [](std::string
const &s
) { return s
.c_str(); });
1197 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0,
1198 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1199 size_t const BaseArgs
= Args
.size();
1204 // FIXME: do we really need this limit when we have MaxArgBytes?
1205 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1207 // try to figure out the max environment size
1208 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1211 OSArgMax
-= EnvironmentSize() - 2*1024;
1212 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1213 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1215 if (RunScripts("DPkg::Pre-Invoke") == false)
1218 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1221 // support subpressing of triggers processing for special
1222 // cases like d-i that runs the triggers handling manually
1223 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1224 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1225 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1228 BuildPackagesProgressMap();
1230 d
->stdin_is_dev_null
= false;
1235 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1237 // start pty magic before the loop
1240 // Tell the progress that its starting and fork dpkg
1241 d
->progress
->Start(d
->master
);
1243 // this loop is runs once per dpkg operation
1244 vector
<Item
>::const_iterator I
= List
.begin();
1245 while (I
!= List
.end())
1247 // Do all actions with the same Op in one run
1248 vector
<Item
>::const_iterator J
= I
;
1249 if (TriggersPending
== true)
1250 for (; J
!= List
.end(); ++J
)
1254 if (J
->Op
!= Item::TriggersPending
)
1256 vector
<Item
>::const_iterator T
= J
+ 1;
1257 if (T
!= List
.end() && T
->Op
== I
->Op
)
1262 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1265 // keep track of allocated strings for multiarch package names
1266 std::vector
<char *> Packages
;
1268 // start with the baseset of arguments
1269 unsigned long Size
= StartSize
;
1270 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1272 // Now check if we are within the MaxArgs limit
1274 // this code below is problematic, because it may happen that
1275 // the argument list is split in a way that A depends on B
1276 // and they are in the same "--configure A B" run
1277 // - with the split they may now be configured in different
1278 // runs, using Immediate-Configure-All can help prevent this.
1279 if (J
- I
> (signed)MaxArgs
)
1282 unsigned long const size
= MaxArgs
+ 10;
1284 Packages
.reserve(size
);
1288 unsigned long const size
= (J
- I
) + 10;
1290 Packages
.reserve(size
);
1295 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1297 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1298 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1300 ADDARGC("--status-fd");
1301 char status_fd_buf
[20];
1302 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1303 ADDARG(status_fd_buf
);
1304 unsigned long const Op
= I
->Op
;
1309 ADDARGC("--force-depends");
1310 ADDARGC("--force-remove-essential");
1311 ADDARGC("--remove");
1315 ADDARGC("--force-depends");
1316 ADDARGC("--force-remove-essential");
1320 case Item::Configure
:
1321 ADDARGC("--configure");
1324 case Item::ConfigurePending
:
1325 ADDARGC("--configure");
1326 ADDARGC("--pending");
1329 case Item::TriggersPending
:
1330 ADDARGC("--triggers-only");
1331 ADDARGC("--pending");
1335 ADDARGC("--unpack");
1336 ADDARGC("--auto-deconfigure");
1340 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1341 I
->Op
!= Item::ConfigurePending
)
1343 ADDARGC("--no-triggers");
1347 // Write in the file or package names
1348 if (I
->Op
== Item::Install
)
1350 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1352 if (I
->File
[0] != '/')
1353 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1354 Args
.push_back(I
->File
.c_str());
1355 Size
+= I
->File
.length();
1360 string
const nativeArch
= _config
->Find("APT::Architecture");
1361 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1362 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1364 if((*I
).Pkg
.end() == true)
1366 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1368 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1369 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1370 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1371 strcmp(I
->Pkg
.Arch(), "none") == 0))
1373 char const * const name
= I
->Pkg
.Name();
1378 pkgCache::VerIterator PkgVer
;
1379 std::string name
= I
->Pkg
.Name();
1380 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1382 PkgVer
= I
->Pkg
.CurrentVer();
1383 if(PkgVer
.end() == true)
1384 PkgVer
= FindNowVersion(I
->Pkg
);
1387 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1388 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1389 ; // never arch-qualify a package without an arch
1390 else if (PkgVer
.end() == false)
1391 name
.append(":").append(PkgVer
.Arch());
1393 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1394 char * const fullname
= strdup(name
.c_str());
1395 Packages
.push_back(fullname
);
1399 // skip configure action if all sheduled packages disappeared
1400 if (oldSize
== Size
)
1407 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1409 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1410 a
!= Args
.end(); ++a
)
1413 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1414 p
!= Packages
.end(); ++p
)
1419 Args
.push_back(NULL
);
1425 /* Mask off sig int/quit. We do this because dpkg also does when
1426 it forks scripts. What happens is that when you hit ctrl-c it sends
1427 it to all processes in the group. Since dpkg ignores the signal
1428 it doesn't die but we do! So we must also ignore it */
1429 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1430 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1432 // Check here for any SIGINT
1433 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1437 // ignore SIGHUP as well (debian #463030)
1438 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1441 d
->progress
->StartDpkg();
1442 std::set
<int> KeepFDs
;
1443 KeepFDs
.insert(fd
[1]);
1444 MergeKeepFdsFromConfiguration(KeepFDs
);
1445 pid_t Child
= ExecFork(KeepFDs
);
1448 // This is the child
1449 SetupSlavePtyMagic();
1450 close(fd
[0]); // close the read end of the pipe
1452 debSystem::DpkgChrootDirectory();
1454 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1457 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1461 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1464 // Discard everything in stdin before forking dpkg
1465 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1468 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1470 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1474 execvp(Args
[0], (char**) &Args
[0]);
1475 cerr
<< "Could not exec dpkg!" << endl
;
1480 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1486 // we read from dpkg here
1487 int const _dpkgin
= fd
[0];
1488 close(fd
[1]); // close the write end of the pipe
1491 sigemptyset(&d
->sigmask
);
1492 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1494 /* free vectors (and therefore memory) as we don't need the included data anymore */
1495 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1496 p
!= Packages
.end(); ++p
)
1500 // the result of the waitpid call
1503 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1505 // FIXME: move this to a function or something, looks ugly here
1506 // error handling, waitpid returned -1
1509 RunScripts("DPkg::Post-Invoke");
1511 // Restore sig int/quit
1512 signal(SIGQUIT
,old_SIGQUIT
);
1513 signal(SIGINT
,old_SIGINT
);
1515 signal(SIGHUP
,old_SIGHUP
);
1516 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1519 // wait for input or output here
1521 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1522 FD_SET(STDIN_FILENO
, &rfds
);
1523 FD_SET(_dpkgin
, &rfds
);
1525 FD_SET(d
->master
, &rfds
);
1527 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1528 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1529 &tv
, &d
->original_sigmask
);
1530 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1531 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1532 NULL
, &tv
, &d
->original_sigmask
);
1533 d
->progress
->Pulse();
1534 if (select_ret
== 0)
1536 else if (select_ret
< 0 && errno
== EINTR
)
1538 else if (select_ret
< 0)
1540 perror("select() returned error");
1544 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1545 DoTerminalPty(d
->master
);
1546 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1548 if(FD_ISSET(_dpkgin
, &rfds
))
1549 DoDpkgStatusFd(_dpkgin
);
1553 // Restore sig int/quit
1554 signal(SIGQUIT
,old_SIGQUIT
);
1555 signal(SIGINT
,old_SIGINT
);
1557 signal(SIGHUP
,old_SIGHUP
);
1558 // Check for an error code.
1559 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1561 // if it was set to "keep-dpkg-runing" then we won't return
1562 // here but keep the loop going and just report it as a error
1564 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1566 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1567 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1568 else if (WIFEXITED(Status
) != 0)
1569 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1571 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1572 _error
->Error("%s", d
->dpkg_error
.c_str());
1578 // dpkg is done at this point
1579 d
->progress
->Stop();
1583 if (pkgPackageManager::SigINTStop
)
1584 _error
->Warning(_("Operation was interrupted before it could finish"));
1586 if (RunScripts("DPkg::Post-Invoke") == false)
1589 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1591 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1592 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1593 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1595 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1596 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1598 _error
->PushToStack();
1599 pkgCacheFile CacheFile
;
1600 CacheFile
.BuildCaches(NULL
, true);
1601 _error
->RevertToStack();
1606 Cache
.writeStateFile(NULL
);
1607 return d
->dpkg_error
.empty();
1610 void SigINT(int /*sig*/) {
1611 pkgPackageManager::SigINTStop
= true;
1614 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1615 // ---------------------------------------------------------------------
1617 void pkgDPkgPM::Reset()
1619 List
.erase(List
.begin(),List
.end());
1622 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1623 // ---------------------------------------------------------------------
1625 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1627 // If apport doesn't exist or isn't installed do nothing
1628 // This e.g. prevents messages in 'universes' without apport
1629 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1630 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1633 string pkgname
, reportfile
, pkgver
, arch
;
1634 string::size_type pos
;
1637 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1639 std::clog
<< "configured to not write apport reports" << std::endl
;
1643 // only report the first errors
1644 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1646 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1650 // check if its not a follow up error
1651 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1652 if(strstr(errormsg
, needle
) != NULL
) {
1653 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1657 // do not report disk-full failures
1658 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1659 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1663 // do not report out-of-memory failures
1664 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1665 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1666 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1670 // do not report bugs regarding inaccessible local files
1671 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1672 strstr(errormsg
, "cannot access archive") != NULL
) {
1673 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1677 // do not report errors encountered when decompressing packages
1678 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1679 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1683 // do not report dpkg I/O errors, this is a format string, so we compare
1684 // the prefix and the suffix of the error with the dpkg error message
1685 vector
<string
> io_errors
;
1686 io_errors
.push_back(string("failed to read"));
1687 io_errors
.push_back(string("failed to write"));
1688 io_errors
.push_back(string("failed to seek"));
1689 io_errors
.push_back(string("unexpected end of file or stream"));
1691 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1693 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1694 if (list
.size() > 1) {
1695 // we need to split %s, VectorizeString only allows char so we need
1696 // to kill the "s" manually
1697 if (list
[1].size() > 1) {
1698 list
[1].erase(0, 1);
1699 if(strstr(errormsg
, list
[0].c_str()) &&
1700 strstr(errormsg
, list
[1].c_str())) {
1701 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1708 // get the pkgname and reportfile
1709 pkgname
= flNotDir(pkgpath
);
1710 pos
= pkgname
.find('_');
1711 if(pos
!= string::npos
)
1712 pkgname
= pkgname
.substr(0, pos
);
1714 // find the package version and source package name
1715 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1716 if (Pkg
.end() == true)
1718 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1719 if (Ver
.end() == true)
1721 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1723 // if the file exists already, we check:
1724 // - if it was reported already (touched by apport).
1725 // If not, we do nothing, otherwise
1726 // we overwrite it. This is the same behaviour as apport
1727 // - if we have a report with the same pkgversion already
1729 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1730 if(FileExists(reportfile
))
1735 // check atime/mtime
1736 stat(reportfile
.c_str(), &buf
);
1737 if(buf
.st_mtime
> buf
.st_atime
)
1740 // check if the existing report is the same version
1741 report
= fopen(reportfile
.c_str(),"r");
1742 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1744 if(strstr(strbuf
,"Package:") == strbuf
)
1746 char pkgname
[255], version
[255];
1747 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1748 if(strcmp(pkgver
.c_str(), version
) == 0)
1758 // now write the report
1759 arch
= _config
->Find("APT::Architecture");
1760 report
= fopen(reportfile
.c_str(),"w");
1763 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1764 chmod(reportfile
.c_str(), 0);
1766 chmod(reportfile
.c_str(), 0600);
1767 fprintf(report
, "ProblemType: Package\n");
1768 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1769 time_t now
= time(NULL
);
1770 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1771 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1772 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1773 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1774 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1776 // ensure that the log is flushed
1778 fflush(d
->term_out
);
1780 // attach terminal log it if we have it
1781 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1782 if (!logfile_name
.empty())
1786 fprintf(report
, "DpkgTerminalLog:\n");
1787 log
= fopen(logfile_name
.c_str(),"r");
1791 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1792 fprintf(report
, " %s", buf
);
1793 fprintf(report
, " \n");
1798 // attach history log it if we have it
1799 string histfile_name
= _config
->FindFile("Dir::Log::History");
1800 if (!histfile_name
.empty())
1802 fprintf(report
, "DpkgHistoryLog:\n");
1803 FILE* log
= fopen(histfile_name
.c_str(),"r");
1807 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1808 fprintf(report
, " %s", buf
);
1813 // log the ordering, see dpkgpm.h and the "Ops" enum there
1814 const char *ops_str
[] = {
1822 fprintf(report
, "AptOrdering:\n");
1823 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1824 if ((*I
).Pkg
!= NULL
)
1825 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1827 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1829 // attach dmesg log (to learn about segfaults)
1830 if (FileExists("/bin/dmesg"))
1832 fprintf(report
, "Dmesg:\n");
1833 FILE *log
= popen("/bin/dmesg","r");
1837 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1838 fprintf(report
, " %s", buf
);
1843 // attach df -l log (to learn about filesystem status)
1844 if (FileExists("/bin/df"))
1847 fprintf(report
, "Df:\n");
1848 FILE *log
= popen("/bin/df -l","r");
1852 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1853 fprintf(report
, " %s", buf
);