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 // ---------------------------------------------------------------------
760 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
765 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
766 d
->dpkgbuf_pos
+= len
;
770 // process line by line if we have a buffer
772 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
775 ProcessDpkgStatusLine(p
);
776 p
=q
+1; // continue with next line
779 // now move the unprocessed bits (after the final \n that is now a 0x0)
780 // to the start and update d->dpkgbuf_pos
781 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
785 // we are interessted in the first char *after* 0x0
788 // move the unprocessed tail to the start and update pos
789 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
790 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
793 // DPkgPM::WriteHistoryTag /*{{{*/
794 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
796 size_t const length
= value
.length();
799 // poor mans rstrip(", ")
800 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
801 value
.erase(length
- 2, 2);
802 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
804 // DPkgPM::OpenLog /*{{{*/
805 bool pkgDPkgPM::OpenLog()
807 string
const logdir
= _config
->FindDir("Dir::Log");
808 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
809 // FIXME: use a better string after freeze
810 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
814 time_t const t
= time(NULL
);
816 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
817 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
820 string
const logfile_name
= flCombine(logdir
,
821 _config
->Find("Dir::Log::Terminal"));
822 if (!logfile_name
.empty())
824 d
->term_out
= fopen(logfile_name
.c_str(),"a");
825 if (d
->term_out
== NULL
)
826 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
827 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
828 SetCloseExec(fileno(d
->term_out
), true);
829 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
831 struct passwd
*pw
= getpwnam("root");
832 struct group
*gr
= getgrnam("adm");
833 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
834 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
836 if (chmod(logfile_name
.c_str(), 0640) != 0)
837 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
838 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
841 // write your history
842 string
const history_name
= flCombine(logdir
,
843 _config
->Find("Dir::Log::History"));
844 if (!history_name
.empty())
846 d
->history_out
= fopen(history_name
.c_str(),"a");
847 if (d
->history_out
== NULL
)
848 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
849 SetCloseExec(fileno(d
->history_out
), true);
850 chmod(history_name
.c_str(), 0644);
851 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
852 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
853 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
855 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
857 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
858 if (Cache
[I
].NewInstall() == true)
859 HISTORYINFO(install
, CANDIDATE_AUTO
)
860 else if (Cache
[I
].ReInstall() == true)
861 HISTORYINFO(reinstall
, CANDIDATE
)
862 else if (Cache
[I
].Upgrade() == true)
863 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
864 else if (Cache
[I
].Downgrade() == true)
865 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
866 else if (Cache
[I
].Delete() == true)
867 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
871 line
->append(I
.FullName(false)).append(" (");
872 switch (infostring
) {
873 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
875 line
->append(Cache
[I
].CandVersion
);
876 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
877 line
->append(", automatic");
879 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
880 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
884 if (_config
->Exists("Commandline::AsString") == true)
885 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
886 WriteHistoryTag("Install", install
);
887 WriteHistoryTag("Reinstall", reinstall
);
888 WriteHistoryTag("Upgrade", upgrade
);
889 WriteHistoryTag("Downgrade",downgrade
);
890 WriteHistoryTag("Remove",remove
);
891 WriteHistoryTag("Purge",purge
);
892 fflush(d
->history_out
);
898 // DPkg::CloseLog /*{{{*/
899 bool pkgDPkgPM::CloseLog()
902 time_t t
= time(NULL
);
904 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
905 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
909 fprintf(d
->term_out
, "Log ended: ");
910 fprintf(d
->term_out
, "%s", timestr
);
911 fprintf(d
->term_out
, "\n");
918 if (disappearedPkgs
.empty() == false)
921 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
922 d
!= disappearedPkgs
.end(); ++d
)
924 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
925 disappear
.append(*d
);
927 disappear
.append(", ");
929 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
931 WriteHistoryTag("Disappeared", disappear
);
933 if (d
->dpkg_error
.empty() == false)
934 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
935 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
936 fclose(d
->history_out
);
938 d
->history_out
= NULL
;
945 // This implements a racy version of pselect for those architectures
946 // that don't have a working implementation.
947 // FIXME: Probably can be removed on Lenny+1
948 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
949 fd_set
*exceptfds
, const struct timespec
*timeout
,
950 const sigset_t
*sigmask
)
956 tv
.tv_sec
= timeout
->tv_sec
;
957 tv
.tv_usec
= timeout
->tv_nsec
/1000;
959 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
960 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
961 sigprocmask(SIG_SETMASK
, &origmask
, 0);
966 // DPkgPM::BuildPackagesProgressMap /*{{{*/
967 void pkgDPkgPM::BuildPackagesProgressMap()
969 // map the dpkg states to the operations that are performed
970 // (this is sorted in the same way as Item::Ops)
971 static const struct DpkgState DpkgStatesOpMap
[][7] = {
974 {"half-installed", N_("Preparing %s")},
975 {"unpacked", N_("Unpacking %s") },
978 // Configure operation
980 {"unpacked",N_("Preparing to configure %s") },
981 {"half-configured", N_("Configuring %s") },
982 { "installed", N_("Installed %s")},
987 {"half-configured", N_("Preparing for removal of %s")},
988 {"half-installed", N_("Removing %s")},
989 {"config-files", N_("Removed %s")},
994 {"config-files", N_("Preparing to completely remove %s")},
995 {"not-installed", N_("Completely removed %s")},
1000 // init the PackageOps map, go over the list of packages that
1001 // that will be [installed|configured|removed|purged] and add
1002 // them to the PackageOps map (the dpkg states it goes through)
1003 // and the PackageOpsTranslations (human readable strings)
1004 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1006 if((*I
).Pkg
.end() == true)
1009 string
const name
= (*I
).Pkg
.FullName();
1010 PackageOpsDone
[name
] = 0;
1011 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1013 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1017 /* one extra: We don't want the progress bar to reach 100%, especially not
1018 if we call dpkg --configure --pending and process a bunch of triggers
1019 while showing 100%. Also, spindown takes a while, so never reaching 100%
1020 is way more correct than reaching 100% while still doing stuff even if
1021 doing it this way is slightly bending the rules */
1025 bool pkgDPkgPM::Go(int StatusFd
)
1027 APT::Progress::PackageManager
*progress
= NULL
;
1029 progress
= APT::Progress::PackageManagerProgressFactory();
1031 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1033 return Go(progress
);
1036 void pkgDPkgPM::StartPtyMagic()
1038 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1041 if (d
->slave
!= NULL
)
1047 if (isatty(STDIN_FILENO
) == 0)
1048 d
->direct_stdin
= true;
1050 _error
->PushToStack();
1052 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1053 if (d
->master
== -1)
1054 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1055 else if (unlockpt(d
->master
) == -1)
1056 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1059 #ifdef HAVE_PTS_NAME_R
1060 char slave_name
[64]; // 64 is used by bionic
1061 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1063 char const * const slave_name
= ptsname(d
->master
);
1064 if (slave_name
== NULL
)
1066 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1069 d
->slave
= strdup(slave_name
);
1070 if (d
->slave
== NULL
)
1071 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1072 else if (grantpt(d
->master
) == -1)
1073 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1074 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1076 d
->tt_is_valid
= true;
1077 struct termios raw_tt
;
1078 // copy window size of stdout if its a 'good' terminal
1079 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1082 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1083 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1084 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1085 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1087 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1088 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1092 raw_tt
.c_lflag
&= ~ECHO
;
1093 raw_tt
.c_lflag
|= ISIG
;
1094 // block SIGTTOU during tcsetattr to prevent a hang if
1095 // the process is a member of the background process group
1096 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1097 sigemptyset(&d
->sigmask
);
1098 sigaddset(&d
->sigmask
, SIGTTOU
);
1099 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1100 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1101 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1102 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1105 if (d
->slave
!= NULL
)
1107 /* on linux, closing (and later reopening) all references to the slave
1108 makes the slave a death end, so we open it here to have one open all
1109 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1110 on kfreebsd we get an incorrect ("step like") output then while it has
1111 no problem with closing all references… so to avoid platform specific
1112 code here we combine both and be happy once more */
1113 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1118 if (_error
->PendingError() == true)
1120 if (d
->master
!= -1)
1125 if (d
->slave
!= NULL
)
1130 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1132 _error
->RevertToStack();
1134 void pkgDPkgPM::SetupSlavePtyMagic()
1136 if(d
->master
== -1 || d
->slave
== NULL
)
1139 if (close(d
->master
) == -1)
1140 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1143 _error
->FatalE("setsid", "Starting a new session for child failed!");
1145 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1147 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1148 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1149 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1152 unsigned short i
= 0;
1153 if (d
->direct_stdin
== true)
1156 if (dup2(slaveFd
, i
) == -1)
1157 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1159 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1160 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1166 void pkgDPkgPM::StopPtyMagic()
1168 if (d
->slave
!= NULL
)
1171 if (d
->protect_slave_from_dying
!= -1)
1173 close(d
->protect_slave_from_dying
);
1174 d
->protect_slave_from_dying
= -1;
1178 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1179 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1185 // DPkgPM::Go - Run the sequence /*{{{*/
1186 // ---------------------------------------------------------------------
1187 /* This globs the operations and calls dpkg
1189 * If it is called with a progress object apt will report the install
1190 * progress to this object. It maps the dpkg states a package goes
1191 * through to human readable (and i10n-able)
1192 * names and calculates a percentage for each step.
1194 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1196 pkgPackageManager::SigINTStop
= false;
1197 d
->progress
= progress
;
1199 // Generate the base argument list for dpkg
1200 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1201 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1202 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1203 [](std::string
const &s
) { return s
.c_str(); });
1204 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0,
1205 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1206 size_t const BaseArgs
= Args
.size();
1211 // FIXME: do we really need this limit when we have MaxArgBytes?
1212 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1214 // try to figure out the max environment size
1215 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1218 OSArgMax
-= EnvironmentSize() - 2*1024;
1219 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1220 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1222 if (RunScripts("DPkg::Pre-Invoke") == false)
1225 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1228 // support subpressing of triggers processing for special
1229 // cases like d-i that runs the triggers handling manually
1230 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1231 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1232 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1235 BuildPackagesProgressMap();
1237 d
->stdin_is_dev_null
= false;
1242 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1244 // start pty magic before the loop
1247 // Tell the progress that its starting and fork dpkg
1248 d
->progress
->Start(d
->master
);
1250 // this loop is runs once per dpkg operation
1251 vector
<Item
>::const_iterator I
= List
.begin();
1252 while (I
!= List
.end())
1254 // Do all actions with the same Op in one run
1255 vector
<Item
>::const_iterator J
= I
;
1256 if (TriggersPending
== true)
1257 for (; J
!= List
.end(); ++J
)
1261 if (J
->Op
!= Item::TriggersPending
)
1263 vector
<Item
>::const_iterator T
= J
+ 1;
1264 if (T
!= List
.end() && T
->Op
== I
->Op
)
1269 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1272 // keep track of allocated strings for multiarch package names
1273 std::vector
<char *> Packages
;
1275 // start with the baseset of arguments
1276 unsigned long Size
= StartSize
;
1277 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1279 // Now check if we are within the MaxArgs limit
1281 // this code below is problematic, because it may happen that
1282 // the argument list is split in a way that A depends on B
1283 // and they are in the same "--configure A B" run
1284 // - with the split they may now be configured in different
1285 // runs, using Immediate-Configure-All can help prevent this.
1286 if (J
- I
> (signed)MaxArgs
)
1289 unsigned long const size
= MaxArgs
+ 10;
1291 Packages
.reserve(size
);
1295 unsigned long const size
= (J
- I
) + 10;
1297 Packages
.reserve(size
);
1302 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1304 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1305 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1307 ADDARGC("--status-fd");
1308 char status_fd_buf
[20];
1309 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1310 ADDARG(status_fd_buf
);
1311 unsigned long const Op
= I
->Op
;
1316 ADDARGC("--force-depends");
1317 ADDARGC("--force-remove-essential");
1318 ADDARGC("--remove");
1322 ADDARGC("--force-depends");
1323 ADDARGC("--force-remove-essential");
1327 case Item::Configure
:
1328 ADDARGC("--configure");
1331 case Item::ConfigurePending
:
1332 ADDARGC("--configure");
1333 ADDARGC("--pending");
1336 case Item::TriggersPending
:
1337 ADDARGC("--triggers-only");
1338 ADDARGC("--pending");
1342 ADDARGC("--unpack");
1343 ADDARGC("--auto-deconfigure");
1347 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1348 I
->Op
!= Item::ConfigurePending
)
1350 ADDARGC("--no-triggers");
1354 // Write in the file or package names
1355 if (I
->Op
== Item::Install
)
1357 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1359 if (I
->File
[0] != '/')
1360 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1361 Args
.push_back(I
->File
.c_str());
1362 Size
+= I
->File
.length();
1367 string
const nativeArch
= _config
->Find("APT::Architecture");
1368 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1369 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1371 if((*I
).Pkg
.end() == true)
1373 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1375 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1376 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1377 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1378 strcmp(I
->Pkg
.Arch(), "none") == 0))
1380 char const * const name
= I
->Pkg
.Name();
1385 pkgCache::VerIterator PkgVer
;
1386 std::string name
= I
->Pkg
.Name();
1387 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1389 PkgVer
= I
->Pkg
.CurrentVer();
1390 if(PkgVer
.end() == true)
1391 PkgVer
= FindNowVersion(I
->Pkg
);
1394 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1395 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1396 ; // never arch-qualify a package without an arch
1397 else if (PkgVer
.end() == false)
1398 name
.append(":").append(PkgVer
.Arch());
1400 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1401 char * const fullname
= strdup(name
.c_str());
1402 Packages
.push_back(fullname
);
1406 // skip configure action if all sheduled packages disappeared
1407 if (oldSize
== Size
)
1414 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1416 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1417 a
!= Args
.end(); ++a
)
1420 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1421 p
!= Packages
.end(); ++p
)
1426 Args
.push_back(NULL
);
1432 /* Mask off sig int/quit. We do this because dpkg also does when
1433 it forks scripts. What happens is that when you hit ctrl-c it sends
1434 it to all processes in the group. Since dpkg ignores the signal
1435 it doesn't die but we do! So we must also ignore it */
1436 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1437 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1439 // Check here for any SIGINT
1440 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1444 // ignore SIGHUP as well (debian #463030)
1445 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1448 d
->progress
->StartDpkg();
1449 std::set
<int> KeepFDs
;
1450 KeepFDs
.insert(fd
[1]);
1451 MergeKeepFdsFromConfiguration(KeepFDs
);
1452 pid_t Child
= ExecFork(KeepFDs
);
1455 // This is the child
1456 SetupSlavePtyMagic();
1457 close(fd
[0]); // close the read end of the pipe
1459 debSystem::DpkgChrootDirectory();
1461 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1464 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1468 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1471 // Discard everything in stdin before forking dpkg
1472 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1475 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1477 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1481 execvp(Args
[0], (char**) &Args
[0]);
1482 cerr
<< "Could not exec dpkg!" << endl
;
1487 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1493 // we read from dpkg here
1494 int const _dpkgin
= fd
[0];
1495 close(fd
[1]); // close the write end of the pipe
1498 sigemptyset(&d
->sigmask
);
1499 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1501 /* free vectors (and therefore memory) as we don't need the included data anymore */
1502 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1503 p
!= Packages
.end(); ++p
)
1507 // the result of the waitpid call
1510 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1512 // FIXME: move this to a function or something, looks ugly here
1513 // error handling, waitpid returned -1
1516 RunScripts("DPkg::Post-Invoke");
1518 // Restore sig int/quit
1519 signal(SIGQUIT
,old_SIGQUIT
);
1520 signal(SIGINT
,old_SIGINT
);
1522 signal(SIGHUP
,old_SIGHUP
);
1523 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1526 // wait for input or output here
1528 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1529 FD_SET(STDIN_FILENO
, &rfds
);
1530 FD_SET(_dpkgin
, &rfds
);
1532 FD_SET(d
->master
, &rfds
);
1534 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1535 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1536 &tv
, &d
->original_sigmask
);
1537 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1538 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1539 NULL
, &tv
, &d
->original_sigmask
);
1540 d
->progress
->Pulse();
1541 if (select_ret
== 0)
1543 else if (select_ret
< 0 && errno
== EINTR
)
1545 else if (select_ret
< 0)
1547 perror("select() returned error");
1551 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1552 DoTerminalPty(d
->master
);
1553 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1555 if(FD_ISSET(_dpkgin
, &rfds
))
1556 DoDpkgStatusFd(_dpkgin
);
1560 // Restore sig int/quit
1561 signal(SIGQUIT
,old_SIGQUIT
);
1562 signal(SIGINT
,old_SIGINT
);
1564 signal(SIGHUP
,old_SIGHUP
);
1565 // Check for an error code.
1566 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1568 // if it was set to "keep-dpkg-runing" then we won't return
1569 // here but keep the loop going and just report it as a error
1571 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1573 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1574 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1575 else if (WIFEXITED(Status
) != 0)
1576 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1578 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1579 _error
->Error("%s", d
->dpkg_error
.c_str());
1585 // dpkg is done at this point
1586 d
->progress
->Stop();
1590 if (pkgPackageManager::SigINTStop
)
1591 _error
->Warning(_("Operation was interrupted before it could finish"));
1593 if (RunScripts("DPkg::Post-Invoke") == false)
1596 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1598 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1599 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1600 unlink(oldpkgcache
.c_str()) == 0)
1602 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1603 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1605 _error
->PushToStack();
1606 pkgCacheFile CacheFile
;
1607 CacheFile
.BuildCaches(NULL
, true);
1608 _error
->RevertToStack();
1613 Cache
.writeStateFile(NULL
);
1614 return d
->dpkg_error
.empty();
1617 void SigINT(int /*sig*/) {
1618 pkgPackageManager::SigINTStop
= true;
1621 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1622 // ---------------------------------------------------------------------
1624 void pkgDPkgPM::Reset()
1626 List
.erase(List
.begin(),List
.end());
1629 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1630 // ---------------------------------------------------------------------
1632 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1634 // If apport doesn't exist or isn't installed do nothing
1635 // This e.g. prevents messages in 'universes' without apport
1636 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1637 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1640 string pkgname
, reportfile
, pkgver
, arch
;
1641 string::size_type pos
;
1644 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1646 std::clog
<< "configured to not write apport reports" << std::endl
;
1650 // only report the first errors
1651 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1653 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1657 // check if its not a follow up error
1658 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1659 if(strstr(errormsg
, needle
) != NULL
) {
1660 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1664 // do not report disk-full failures
1665 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1666 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1670 // do not report out-of-memory failures
1671 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1672 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1673 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1677 // do not report bugs regarding inaccessible local files
1678 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1679 strstr(errormsg
, "cannot access archive") != NULL
) {
1680 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1684 // do not report errors encountered when decompressing packages
1685 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1686 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1690 // do not report dpkg I/O errors, this is a format string, so we compare
1691 // the prefix and the suffix of the error with the dpkg error message
1692 vector
<string
> io_errors
;
1693 io_errors
.push_back(string("failed to read"));
1694 io_errors
.push_back(string("failed to write"));
1695 io_errors
.push_back(string("failed to seek"));
1696 io_errors
.push_back(string("unexpected end of file or stream"));
1698 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1700 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1701 if (list
.size() > 1) {
1702 // we need to split %s, VectorizeString only allows char so we need
1703 // to kill the "s" manually
1704 if (list
[1].size() > 1) {
1705 list
[1].erase(0, 1);
1706 if(strstr(errormsg
, list
[0].c_str()) &&
1707 strstr(errormsg
, list
[1].c_str())) {
1708 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1715 // get the pkgname and reportfile
1716 pkgname
= flNotDir(pkgpath
);
1717 pos
= pkgname
.find('_');
1718 if(pos
!= string::npos
)
1719 pkgname
= pkgname
.substr(0, pos
);
1721 // find the package versin and source package name
1722 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1723 if (Pkg
.end() == true)
1725 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1726 if (Ver
.end() == true)
1728 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1730 // if the file exists already, we check:
1731 // - if it was reported already (touched by apport).
1732 // If not, we do nothing, otherwise
1733 // we overwrite it. This is the same behaviour as apport
1734 // - if we have a report with the same pkgversion already
1736 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1737 if(FileExists(reportfile
))
1742 // check atime/mtime
1743 stat(reportfile
.c_str(), &buf
);
1744 if(buf
.st_mtime
> buf
.st_atime
)
1747 // check if the existing report is the same version
1748 report
= fopen(reportfile
.c_str(),"r");
1749 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1751 if(strstr(strbuf
,"Package:") == strbuf
)
1753 char pkgname
[255], version
[255];
1754 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1755 if(strcmp(pkgver
.c_str(), version
) == 0)
1765 // now write the report
1766 arch
= _config
->Find("APT::Architecture");
1767 report
= fopen(reportfile
.c_str(),"w");
1770 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1771 chmod(reportfile
.c_str(), 0);
1773 chmod(reportfile
.c_str(), 0600);
1774 fprintf(report
, "ProblemType: Package\n");
1775 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1776 time_t now
= time(NULL
);
1777 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1778 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1779 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1780 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1781 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1783 // ensure that the log is flushed
1785 fflush(d
->term_out
);
1787 // attach terminal log it if we have it
1788 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1789 if (!logfile_name
.empty())
1793 fprintf(report
, "DpkgTerminalLog:\n");
1794 log
= fopen(logfile_name
.c_str(),"r");
1798 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1799 fprintf(report
, " %s", buf
);
1800 fprintf(report
, " \n");
1805 // attach history log it if we have it
1806 string histfile_name
= _config
->FindFile("Dir::Log::History");
1807 if (!histfile_name
.empty())
1809 fprintf(report
, "DpkgHistoryLog:\n");
1810 FILE* log
= fopen(histfile_name
.c_str(),"r");
1814 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1815 fprintf(report
, " %s", buf
);
1820 // log the ordering, see dpkgpm.h and the "Ops" enum there
1821 const char *ops_str
[] = {
1829 fprintf(report
, "AptOrdering:\n");
1830 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1831 if ((*I
).Pkg
!= NULL
)
1832 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1834 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1836 // attach dmesg log (to learn about segfaults)
1837 if (FileExists("/bin/dmesg"))
1839 fprintf(report
, "Dmesg:\n");
1840 FILE *log
= popen("/bin/dmesg","r");
1844 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1845 fprintf(report
, " %s", buf
);
1850 // attach df -l log (to learn about filesystem status)
1851 if (FileExists("/bin/df"))
1854 fprintf(report
, "Df:\n");
1855 FILE *log
= popen("/bin/df -l","r");
1859 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1860 fprintf(report
, " %s", buf
);