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>
58 APT_PURE
static string
59 AptHistoryRequestingUser()
61 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
63 for (const auto &Key
: EnvKeys
)
65 if (getenv(Key
) != nullptr)
67 int uid
= atoi(getenv(Key
));
70 struct passwd
*result
;
72 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
74 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
83 APT_PURE
static unsigned int
86 unsigned int size
= 0;
87 char **envp
= environ
;
90 size
+= strlen (*envp
++) + 1;
95 class pkgDPkgPMPrivate
98 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
99 term_out(NULL
), history_out(NULL
),
100 progress(NULL
), tt_is_valid(false), master(-1),
101 slave(NULL
), protect_slave_from_dying(-1),
109 bool stdin_is_dev_null
;
110 // the buffer we use for the dpkg status-fd reading
116 APT::Progress::PackageManager
*progress
;
123 int protect_slave_from_dying
;
127 sigset_t original_sigmask
;
134 // Maps the dpkg "processing" info to human readable names. Entry 0
135 // of each array is the key, entry 1 is the value.
136 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
137 std::make_pair("install", N_("Installing %s")),
138 std::make_pair("configure", N_("Configuring %s")),
139 std::make_pair("remove", N_("Removing %s")),
140 std::make_pair("purge", N_("Completely removing %s")),
141 std::make_pair("disappear", N_("Noting disappearance of %s")),
142 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
145 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
146 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
148 // Predicate to test whether an entry in the PackageProcessingOps
149 // array matches a string.
150 class MatchProcessingOp
155 explicit MatchProcessingOp(const char *the_target
)
160 bool operator()(const std::pair
<const char *, const char *> &pair
) const
162 return strcmp(pair
.first
, target
) == 0;
167 /* helper function to ionice the given PID
169 there is no C header for ionice yet - just the syscall interface
170 so we use the binary from util-linux
175 if (!FileExists("/usr/bin/ionice"))
177 pid_t Process
= ExecFork();
181 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
183 Args
[0] = "/usr/bin/ionice";
187 execv(Args
[0], (char **)Args
);
189 return ExecWait(Process
, "ionice");
192 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
193 // ---------------------------------------------------------------------
194 /* This is helpful when a package is no longer installed but has residual
198 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
200 pkgCache::VerIterator Ver
;
201 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
202 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
203 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
205 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
212 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
213 // ---------------------------------------------------------------------
215 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
216 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
220 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
221 // ---------------------------------------------------------------------
223 pkgDPkgPM::~pkgDPkgPM()
228 // DPkgPM::Install - Install a package /*{{{*/
229 // ---------------------------------------------------------------------
230 /* Add an install operation to the sequence list */
231 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
233 if (File
.empty() == true || Pkg
.end() == true)
234 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
236 // If the filename string begins with DPkg::Chroot-Directory, return the
237 // substr that is within the chroot so dpkg can access it.
238 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
239 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
241 size_t len
= chrootdir
.length();
242 if (chrootdir
.at(len
- 1) == '/')
244 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
247 List
.push_back(Item(Item::Install
,Pkg
,File
));
252 // DPkgPM::Configure - Configure a package /*{{{*/
253 // ---------------------------------------------------------------------
254 /* Add a configure operation to the sequence list */
255 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
257 if (Pkg
.end() == true)
260 List
.push_back(Item(Item::Configure
, Pkg
));
262 // Use triggers for config calls if we configure "smart"
263 // as otherwise Pre-Depends will not be satisfied, see #526774
264 if (_config
->FindB("DPkg::TriggersPending", false) == true)
265 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
270 // DPkgPM::Remove - Remove a package /*{{{*/
271 // ---------------------------------------------------------------------
272 /* Add a remove operation to the sequence list */
273 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
275 if (Pkg
.end() == true)
279 List
.push_back(Item(Item::Purge
,Pkg
));
281 List
.push_back(Item(Item::Remove
,Pkg
));
285 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
286 // ---------------------------------------------------------------------
287 /* This is part of the helper script communication interface, it sends
288 very complete information down to the other end of the pipe.*/
289 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
291 return SendPkgsInfo(F
, 2);
293 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
295 // This version of APT supports only v3, so don't sent higher versions
297 fprintf(F
,"VERSION %u\n", Version
);
299 fprintf(F
,"VERSION 3\n");
301 /* Write out all of the configuration directives by walking the
302 configuration tree */
303 const Configuration::Item
*Top
= _config
->Tree(0);
306 if (Top
->Value
.empty() == false)
309 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
310 QuoteString(Top
->Value
,"\n").c_str());
319 while (Top
!= 0 && Top
->Next
== 0)
326 // Write out the package actions in order.
327 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
329 if(I
->Pkg
.end() == true)
332 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
334 fprintf(F
,"%s ",I
->Pkg
.Name());
336 // Current version which we are going to replace
337 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
338 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
339 CurVer
= FindNowVersion(I
->Pkg
);
341 if (CurVer
.end() == true)
346 fprintf(F
, "- - none ");
350 fprintf(F
, "%s ", CurVer
.VerStr());
352 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
355 // Show the compare operator between current and install version
356 if (S
.InstallVer
!= 0)
358 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
360 if (CurVer
.end() == false)
361 Comp
= InstVer
.CompareVer(CurVer
);
368 fprintf(F
, "%s ", InstVer
.VerStr());
370 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
377 fprintf(F
, "> - - none ");
380 // Show the filename/operation
381 if (I
->Op
== Item::Install
)
384 if (I
->File
[0] != '/')
385 fprintf(F
,"**ERROR**\n");
387 fprintf(F
,"%s\n",I
->File
.c_str());
389 else if (I
->Op
== Item::Configure
)
390 fprintf(F
,"**CONFIGURE**\n");
391 else if (I
->Op
== Item::Remove
||
392 I
->Op
== Item::Purge
)
393 fprintf(F
,"**REMOVE**\n");
401 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
402 // ---------------------------------------------------------------------
403 /* This looks for a list of scripts to run from the configuration file
404 each one is run and is fed on standard input a list of all .deb files
405 that are due to be installed. */
406 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
410 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
411 if (Opts
== 0 || Opts
->Child
== 0)
415 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
417 unsigned int Count
= 1;
418 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
420 if (Opts
->Value
.empty() == true)
423 if(_config
->FindB("Debug::RunScripts", false) == true)
424 std::clog
<< "Running external script with list of all .deb file: '"
425 << Opts
->Value
<< "'" << std::endl
;
427 // Determine the protocol version
428 string OptSec
= Opts
->Value
;
429 string::size_type Pos
;
430 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
431 Pos
= OptSec
.length();
432 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
434 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
435 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
438 std::set
<int> KeepFDs
;
439 MergeKeepFdsFromConfiguration(KeepFDs
);
441 if (pipe(Pipes
) != 0) {
442 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
445 if (InfoFD
!= (unsigned)Pipes
[0])
446 SetCloseExec(Pipes
[0],true);
448 KeepFDs
.insert(Pipes
[0]);
451 SetCloseExec(Pipes
[1],true);
453 // Purified Fork for running the script
454 pid_t Process
= ExecFork(KeepFDs
);
458 dup2(Pipes
[0], InfoFD
);
459 SetCloseExec(STDOUT_FILENO
,false);
460 SetCloseExec(STDIN_FILENO
,false);
461 SetCloseExec(STDERR_FILENO
,false);
464 strprintf(hookfd
, "%d", InfoFD
);
465 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
467 debSystem::DpkgChrootDirectory();
471 Args
[2] = Opts
->Value
.c_str();
473 execv(Args
[0],(char **)Args
);
477 FILE *F
= fdopen(Pipes
[1],"w");
479 result
= _error
->Errno("fdopen","Faild to open new FD");
483 // Feed it the filenames.
486 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
488 // Only deal with packages to be installed from .deb
489 if (I
->Op
!= Item::Install
)
493 if (I
->File
[0] != '/')
496 /* Feed the filename of each package that is pending install
498 fprintf(F
,"%s\n",I
->File
.c_str());
504 SendPkgsInfo(F
, Version
);
508 // Clean up the sub process
509 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
510 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
514 signal(SIGPIPE
, old_sigpipe
);
519 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
520 // ---------------------------------------------------------------------
523 void pkgDPkgPM::DoStdin(int master
)
525 unsigned char input_buf
[256] = {0,};
526 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
528 FileFd::Write(master
, input_buf
, len
);
530 d
->stdin_is_dev_null
= true;
533 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
534 // ---------------------------------------------------------------------
536 * read the terminal pty and write log
538 void pkgDPkgPM::DoTerminalPty(int master
)
540 unsigned char term_buf
[1024] = {0,0, };
542 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
543 if(len
== -1 && errno
== EIO
)
545 // this happens when the child is about to exit, we
546 // give it time to actually exit, otherwise we run
547 // into a race so we sleep for half a second.
548 struct timespec sleepfor
= { 0, 500000000 };
549 nanosleep(&sleepfor
, NULL
);
554 FileFd::Write(1, term_buf
, len
);
556 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
559 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
560 // ---------------------------------------------------------------------
563 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
565 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
567 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
569 /* dpkg sends strings like this:
570 'status: <pkg>: <pkg qstate>'
571 'status: <pkg>:<arch>: <pkg qstate>'
573 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
574 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
577 // we need to split on ": " (note the appended space) as the ':' is
578 // part of the pkgname:arch information that dpkg sends
580 // A dpkg error message may contain additional ":" (like
581 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
582 // so we need to ensure to not split too much
583 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
587 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
591 // build the (prefix, pkgname, action) tuple, position of this
592 // is different for "processing" or "status" messages
593 std::string prefix
= APT::String::Strip(list
[0]);
597 // "processing" has the form "processing: action: pkg or trigger"
598 // with action = ["install", "upgrade", "configure", "remove", "purge",
599 // "disappear", "trigproc"]
600 if (prefix
== "processing")
602 pkgname
= APT::String::Strip(list
[2]);
603 action
= APT::String::Strip(list
[1]);
604 // we don't care for the difference (as dpkg doesn't really either)
605 if (action
== "upgrade")
608 // "status" has the form: "status: pkg: state"
609 // with state in ["half-installed", "unpacked", "half-configured",
610 // "installed", "config-files", "not-installed"]
611 else if (prefix
== "status")
613 pkgname
= APT::String::Strip(list
[1]);
614 action
= APT::String::Strip(list
[2]);
617 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
622 /* handle the special cases first:
624 errors look like this:
625 '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
626 and conffile-prompt like this
627 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
629 if (prefix
== "status")
631 if(action
== "error")
633 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
636 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
639 else if(action
== "conffile-prompt")
641 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
647 // at this point we know that we should have a valid pkgname, so build all
650 // dpkg does not always send "pkgname:arch" so we add it here if needed
651 if (pkgname
.find(":") == std::string::npos
)
653 // find the package in the group that is touched by dpkg
654 // if there are multiple pkgs dpkg would send us a full pkgname:arch
655 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
656 if (Grp
.end() == false)
658 pkgCache::PkgIterator P
= Grp
.PackageList();
659 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
661 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
663 pkgname
= P
.FullName();
670 const char* const pkg
= pkgname
.c_str();
671 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
672 std::string arch
= "";
673 if (pkgname
.find(":") != string::npos
)
674 arch
= StringSplit(pkgname
, ":")[1];
675 std::string i18n_pkgname
= pkgname
;
676 if (arch
.size() != 0)
677 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
679 // 'processing' from dpkg looks like
680 // 'processing: action: pkg'
681 if(prefix
== "processing")
683 const std::pair
<const char *, const char *> * const iter
=
684 std::find_if(PackageProcessingOpsBegin
,
685 PackageProcessingOpsEnd
,
686 MatchProcessingOp(action
.c_str()));
687 if(iter
== PackageProcessingOpsEnd
)
690 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
694 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
695 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
697 // FIXME: this needs a muliarch testcase
698 // FIXME2: is "pkgname" here reliable with dpkg only sending us
700 if (action
== "disappear")
701 handleDisappearAction(pkgname
);
705 if (prefix
== "status")
707 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
708 if(PackageOpsDone
[pkg
] < states
.size())
710 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
711 if (next_action
&& Debug
== true)
712 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
713 << " action: " << action
<< " (expected: '" << next_action
<< "' "
714 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
716 // check if the package moved to the next dpkg state
717 if(next_action
&& (action
== next_action
))
719 // only read the translation if there is actually a next action
720 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
722 // we moved from one dpkg state to a new one, report that
723 ++PackageOpsDone
[pkg
];
727 strprintf(msg
, translation
, i18n_pkgname
.c_str());
728 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
734 // DPkgPM::handleDisappearAction /*{{{*/
735 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
737 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
738 if (unlikely(Pkg
.end() == true))
741 // record the package name for display and stuff later
742 disappearedPkgs
.insert(Pkg
.FullName(true));
744 // the disappeared package was auto-installed - nothing to do
745 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
747 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
748 if (unlikely(PkgVer
.end() == true))
750 /* search in the list of dependencies for (Pre)Depends,
751 check if this dependency has a Replaces on our package
752 and if so transfer the manual installed flag to it */
753 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
755 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
756 Dep
->Type
!= pkgCache::Dep::PreDepends
)
758 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
759 if (unlikely(Tar
.end() == true))
761 // the package is already marked as manual
762 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
764 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
765 if (TarVer
.end() == true)
767 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
769 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
771 if (Pkg
!= Rep
.TargetPkg())
773 // okay, they are strongly connected - transfer manual-bit
775 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
776 Cache
[Tar
].Flags
&= ~Flag::Auto
;
782 // DPkgPM::DoDpkgStatusFd /*{{{*/
783 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
785 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
786 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
789 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
791 // process line by line from the buffer
792 char *p
= d
->dpkgbuf
, *q
= nullptr;
793 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
796 ProcessDpkgStatusLine(p
);
797 p
= q
+ 1; // continue with next line
800 // check if we stripped the buffer clean
801 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
807 // otherwise move the unprocessed tail to the start and update pos
808 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
809 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
812 // DPkgPM::WriteHistoryTag /*{{{*/
813 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
815 size_t const length
= value
.length();
818 // poor mans rstrip(", ")
819 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
820 value
.erase(length
- 2, 2);
821 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
823 // DPkgPM::OpenLog /*{{{*/
824 bool pkgDPkgPM::OpenLog()
826 string
const logdir
= _config
->FindDir("Dir::Log");
827 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
828 // FIXME: use a better string after freeze
829 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
833 time_t const t
= time(NULL
);
835 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
836 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
839 string
const logfile_name
= flCombine(logdir
,
840 _config
->Find("Dir::Log::Terminal"));
841 if (!logfile_name
.empty())
843 d
->term_out
= fopen(logfile_name
.c_str(),"a");
844 if (d
->term_out
== NULL
)
845 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
846 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
847 SetCloseExec(fileno(d
->term_out
), true);
848 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
850 struct passwd
*pw
= getpwnam("root");
851 struct group
*gr
= getgrnam("adm");
852 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
853 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
855 if (chmod(logfile_name
.c_str(), 0640) != 0)
856 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
857 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
860 // write your history
861 string
const history_name
= flCombine(logdir
,
862 _config
->Find("Dir::Log::History"));
863 if (!history_name
.empty())
865 d
->history_out
= fopen(history_name
.c_str(),"a");
866 if (d
->history_out
== NULL
)
867 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
868 SetCloseExec(fileno(d
->history_out
), true);
869 chmod(history_name
.c_str(), 0644);
870 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
871 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
872 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
874 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
876 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
877 if (Cache
[I
].NewInstall() == true)
878 HISTORYINFO(install
, CANDIDATE_AUTO
)
879 else if (Cache
[I
].ReInstall() == true)
880 HISTORYINFO(reinstall
, CANDIDATE
)
881 else if (Cache
[I
].Upgrade() == true)
882 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
883 else if (Cache
[I
].Downgrade() == true)
884 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
885 else if (Cache
[I
].Delete() == true)
886 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
890 line
->append(I
.FullName(false)).append(" (");
891 switch (infostring
) {
892 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
894 line
->append(Cache
[I
].CandVersion
);
895 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
896 line
->append(", automatic");
898 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
899 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
903 if (_config
->Exists("Commandline::AsString") == true)
904 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
905 std::string RequestingUser
= AptHistoryRequestingUser();
906 if (RequestingUser
!= "")
907 WriteHistoryTag("Requested-By", RequestingUser
);
908 WriteHistoryTag("Install", install
);
909 WriteHistoryTag("Reinstall", reinstall
);
910 WriteHistoryTag("Upgrade", upgrade
);
911 WriteHistoryTag("Downgrade",downgrade
);
912 WriteHistoryTag("Remove",remove
);
913 WriteHistoryTag("Purge",purge
);
914 fflush(d
->history_out
);
920 // DPkg::CloseLog /*{{{*/
921 bool pkgDPkgPM::CloseLog()
924 time_t t
= time(NULL
);
926 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
927 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
931 fprintf(d
->term_out
, "Log ended: ");
932 fprintf(d
->term_out
, "%s", timestr
);
933 fprintf(d
->term_out
, "\n");
940 if (disappearedPkgs
.empty() == false)
943 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
944 d
!= disappearedPkgs
.end(); ++d
)
946 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
947 disappear
.append(*d
);
949 disappear
.append(", ");
951 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
953 WriteHistoryTag("Disappeared", disappear
);
955 if (d
->dpkg_error
.empty() == false)
956 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
957 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
958 fclose(d
->history_out
);
960 d
->history_out
= NULL
;
967 // This implements a racy version of pselect for those architectures
968 // that don't have a working implementation.
969 // FIXME: Probably can be removed on Lenny+1
970 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
971 fd_set
*exceptfds
, const struct timespec
*timeout
,
972 const sigset_t
*sigmask
)
978 tv
.tv_sec
= timeout
->tv_sec
;
979 tv
.tv_usec
= timeout
->tv_nsec
/1000;
981 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
982 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
983 sigprocmask(SIG_SETMASK
, &origmask
, 0);
988 // DPkgPM::BuildPackagesProgressMap /*{{{*/
989 void pkgDPkgPM::BuildPackagesProgressMap()
991 // map the dpkg states to the operations that are performed
992 // (this is sorted in the same way as Item::Ops)
993 static const struct DpkgState DpkgStatesOpMap
[][7] = {
996 {"half-installed", N_("Preparing %s")},
997 {"unpacked", N_("Unpacking %s") },
1000 // Configure operation
1002 {"unpacked",N_("Preparing to configure %s") },
1003 {"half-configured", N_("Configuring %s") },
1004 { "installed", N_("Installed %s")},
1009 {"half-configured", N_("Preparing for removal of %s")},
1010 {"half-installed", N_("Removing %s")},
1011 {"config-files", N_("Removed %s")},
1016 {"config-files", N_("Preparing to completely remove %s")},
1017 {"not-installed", N_("Completely removed %s")},
1022 // init the PackageOps map, go over the list of packages that
1023 // that will be [installed|configured|removed|purged] and add
1024 // them to the PackageOps map (the dpkg states it goes through)
1025 // and the PackageOpsTranslations (human readable strings)
1026 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1028 if((*I
).Pkg
.end() == true)
1031 string
const name
= (*I
).Pkg
.FullName();
1032 PackageOpsDone
[name
] = 0;
1033 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1035 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1039 /* one extra: We don't want the progress bar to reach 100%, especially not
1040 if we call dpkg --configure --pending and process a bunch of triggers
1041 while showing 100%. Also, spindown takes a while, so never reaching 100%
1042 is way more correct than reaching 100% while still doing stuff even if
1043 doing it this way is slightly bending the rules */
1047 bool pkgDPkgPM::Go(int StatusFd
)
1049 APT::Progress::PackageManager
*progress
= NULL
;
1051 progress
= APT::Progress::PackageManagerProgressFactory();
1053 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1055 return Go(progress
);
1058 void pkgDPkgPM::StartPtyMagic()
1060 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1063 if (d
->slave
!= NULL
)
1069 if (isatty(STDIN_FILENO
) == 0)
1070 d
->direct_stdin
= true;
1072 _error
->PushToStack();
1074 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1075 if (d
->master
== -1)
1076 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1077 else if (unlockpt(d
->master
) == -1)
1078 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1081 #ifdef HAVE_PTS_NAME_R
1082 char slave_name
[64]; // 64 is used by bionic
1083 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1085 char const * const slave_name
= ptsname(d
->master
);
1086 if (slave_name
== NULL
)
1088 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1091 d
->slave
= strdup(slave_name
);
1092 if (d
->slave
== NULL
)
1093 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1094 else if (grantpt(d
->master
) == -1)
1095 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1096 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1098 d
->tt_is_valid
= true;
1099 struct termios raw_tt
;
1100 // copy window size of stdout if its a 'good' terminal
1101 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1104 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1105 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1106 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1107 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1109 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1110 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1114 raw_tt
.c_lflag
&= ~ECHO
;
1115 raw_tt
.c_lflag
|= ISIG
;
1116 // block SIGTTOU during tcsetattr to prevent a hang if
1117 // the process is a member of the background process group
1118 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1119 sigemptyset(&d
->sigmask
);
1120 sigaddset(&d
->sigmask
, SIGTTOU
);
1121 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1122 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1123 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1124 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1127 if (d
->slave
!= NULL
)
1129 /* on linux, closing (and later reopening) all references to the slave
1130 makes the slave a death end, so we open it here to have one open all
1131 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1132 on kfreebsd we get an incorrect ("step like") output then while it has
1133 no problem with closing all references… so to avoid platform specific
1134 code here we combine both and be happy once more */
1135 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1140 if (_error
->PendingError() == true)
1142 if (d
->master
!= -1)
1147 if (d
->slave
!= NULL
)
1152 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1154 _error
->RevertToStack();
1156 void pkgDPkgPM::SetupSlavePtyMagic()
1158 if(d
->master
== -1 || d
->slave
== NULL
)
1161 if (close(d
->master
) == -1)
1162 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1165 _error
->FatalE("setsid", "Starting a new session for child failed!");
1167 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1169 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1170 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1171 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1174 unsigned short i
= 0;
1175 if (d
->direct_stdin
== true)
1178 if (dup2(slaveFd
, i
) == -1)
1179 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1181 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1182 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1188 void pkgDPkgPM::StopPtyMagic()
1190 if (d
->slave
!= NULL
)
1193 if (d
->protect_slave_from_dying
!= -1)
1195 close(d
->protect_slave_from_dying
);
1196 d
->protect_slave_from_dying
= -1;
1200 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1201 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1207 // DPkgPM::Go - Run the sequence /*{{{*/
1208 // ---------------------------------------------------------------------
1209 /* This globs the operations and calls dpkg
1211 * If it is called with a progress object apt will report the install
1212 * progress to this object. It maps the dpkg states a package goes
1213 * through to human readable (and i10n-able)
1214 * names and calculates a percentage for each step.
1216 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1218 pkgPackageManager::SigINTStop
= false;
1219 d
->progress
= progress
;
1221 // Generate the base argument list for dpkg
1222 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1223 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1224 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1225 [](std::string
const &s
) { return s
.c_str(); });
1226 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1227 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1228 size_t const BaseArgs
= Args
.size();
1233 // FIXME: do we really need this limit when we have MaxArgBytes?
1234 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1236 // try to figure out the max environment size
1237 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1240 OSArgMax
-= EnvironmentSize() - 2*1024;
1241 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1242 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1244 if (RunScripts("DPkg::Pre-Invoke") == false)
1247 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1250 // support subpressing of triggers processing for special
1251 // cases like d-i that runs the triggers handling manually
1252 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1253 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1254 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1257 BuildPackagesProgressMap();
1259 d
->stdin_is_dev_null
= false;
1264 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1266 // start pty magic before the loop
1269 // Tell the progress that its starting and fork dpkg
1270 d
->progress
->Start(d
->master
);
1272 // this loop is runs once per dpkg operation
1273 vector
<Item
>::const_iterator I
= List
.begin();
1274 while (I
!= List
.end())
1276 // Do all actions with the same Op in one run
1277 vector
<Item
>::const_iterator J
= I
;
1278 if (TriggersPending
== true)
1279 for (; J
!= List
.end(); ++J
)
1283 if (J
->Op
!= Item::TriggersPending
)
1285 vector
<Item
>::const_iterator T
= J
+ 1;
1286 if (T
!= List
.end() && T
->Op
== I
->Op
)
1291 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1294 // keep track of allocated strings for multiarch package names
1295 std::vector
<char *> Packages
;
1297 // start with the baseset of arguments
1298 unsigned long Size
= StartSize
;
1299 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1301 // Now check if we are within the MaxArgs limit
1303 // this code below is problematic, because it may happen that
1304 // the argument list is split in a way that A depends on B
1305 // and they are in the same "--configure A B" run
1306 // - with the split they may now be configured in different
1307 // runs, using Immediate-Configure-All can help prevent this.
1308 if (J
- I
> (signed)MaxArgs
)
1311 unsigned long const size
= MaxArgs
+ 10;
1313 Packages
.reserve(size
);
1317 unsigned long const size
= (J
- I
) + 10;
1319 Packages
.reserve(size
);
1324 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1326 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1327 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1329 ADDARGC("--status-fd");
1330 char status_fd_buf
[20];
1331 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1332 ADDARG(status_fd_buf
);
1333 unsigned long const Op
= I
->Op
;
1338 ADDARGC("--force-depends");
1339 ADDARGC("--force-remove-essential");
1340 ADDARGC("--remove");
1344 ADDARGC("--force-depends");
1345 ADDARGC("--force-remove-essential");
1349 case Item::Configure
:
1350 ADDARGC("--configure");
1353 case Item::ConfigurePending
:
1354 ADDARGC("--configure");
1355 ADDARGC("--pending");
1358 case Item::TriggersPending
:
1359 ADDARGC("--triggers-only");
1360 ADDARGC("--pending");
1364 ADDARGC("--unpack");
1365 ADDARGC("--auto-deconfigure");
1369 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1370 I
->Op
!= Item::ConfigurePending
)
1372 ADDARGC("--no-triggers");
1376 // Write in the file or package names
1377 if (I
->Op
== Item::Install
)
1379 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1381 if (I
->File
[0] != '/')
1382 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1383 Args
.push_back(I
->File
.c_str());
1384 Size
+= I
->File
.length();
1389 string
const nativeArch
= _config
->Find("APT::Architecture");
1390 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1391 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1393 if((*I
).Pkg
.end() == true)
1395 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1397 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1398 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1399 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1400 strcmp(I
->Pkg
.Arch(), "none") == 0))
1402 char const * const name
= I
->Pkg
.Name();
1407 pkgCache::VerIterator PkgVer
;
1408 std::string name
= I
->Pkg
.Name();
1409 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1411 PkgVer
= I
->Pkg
.CurrentVer();
1412 if(PkgVer
.end() == true)
1413 PkgVer
= FindNowVersion(I
->Pkg
);
1416 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1417 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1418 ; // never arch-qualify a package without an arch
1419 else if (PkgVer
.end() == false)
1420 name
.append(":").append(PkgVer
.Arch());
1422 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1423 char * const fullname
= strdup(name
.c_str());
1424 Packages
.push_back(fullname
);
1428 // skip configure action if all sheduled packages disappeared
1429 if (oldSize
== Size
)
1436 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1438 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1439 a
!= Args
.end(); ++a
)
1442 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1443 p
!= Packages
.end(); ++p
)
1448 Args
.push_back(NULL
);
1454 /* Mask off sig int/quit. We do this because dpkg also does when
1455 it forks scripts. What happens is that when you hit ctrl-c it sends
1456 it to all processes in the group. Since dpkg ignores the signal
1457 it doesn't die but we do! So we must also ignore it */
1458 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1459 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1461 // Check here for any SIGINT
1462 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1466 // ignore SIGHUP as well (debian #463030)
1467 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1470 d
->progress
->StartDpkg();
1471 std::set
<int> KeepFDs
;
1472 KeepFDs
.insert(fd
[1]);
1473 MergeKeepFdsFromConfiguration(KeepFDs
);
1474 pid_t Child
= ExecFork(KeepFDs
);
1477 // This is the child
1478 SetupSlavePtyMagic();
1479 close(fd
[0]); // close the read end of the pipe
1481 debSystem::DpkgChrootDirectory();
1483 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1486 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1490 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1493 // Discard everything in stdin before forking dpkg
1494 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1497 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1499 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1503 execvp(Args
[0], (char**) &Args
[0]);
1504 cerr
<< "Could not exec dpkg!" << endl
;
1509 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1515 // we read from dpkg here
1516 int const _dpkgin
= fd
[0];
1517 close(fd
[1]); // close the write end of the pipe
1520 sigemptyset(&d
->sigmask
);
1521 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1523 /* free vectors (and therefore memory) as we don't need the included data anymore */
1524 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1525 p
!= Packages
.end(); ++p
)
1529 // the result of the waitpid call
1532 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1534 // FIXME: move this to a function or something, looks ugly here
1535 // error handling, waitpid returned -1
1538 RunScripts("DPkg::Post-Invoke");
1540 // Restore sig int/quit
1541 signal(SIGQUIT
,old_SIGQUIT
);
1542 signal(SIGINT
,old_SIGINT
);
1544 signal(SIGHUP
,old_SIGHUP
);
1545 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1548 // wait for input or output here
1550 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1551 FD_SET(STDIN_FILENO
, &rfds
);
1552 FD_SET(_dpkgin
, &rfds
);
1554 FD_SET(d
->master
, &rfds
);
1556 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1557 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1558 &tv
, &d
->original_sigmask
);
1559 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1560 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1561 NULL
, &tv
, &d
->original_sigmask
);
1562 d
->progress
->Pulse();
1563 if (select_ret
== 0)
1565 else if (select_ret
< 0 && errno
== EINTR
)
1567 else if (select_ret
< 0)
1569 perror("select() returned error");
1573 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1574 DoTerminalPty(d
->master
);
1575 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1577 if(FD_ISSET(_dpkgin
, &rfds
))
1578 DoDpkgStatusFd(_dpkgin
);
1582 // Restore sig int/quit
1583 signal(SIGQUIT
,old_SIGQUIT
);
1584 signal(SIGINT
,old_SIGINT
);
1586 signal(SIGHUP
,old_SIGHUP
);
1587 // Check for an error code.
1588 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1590 // if it was set to "keep-dpkg-runing" then we won't return
1591 // here but keep the loop going and just report it as a error
1593 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1595 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1596 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1597 else if (WIFEXITED(Status
) != 0)
1598 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1600 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1601 _error
->Error("%s", d
->dpkg_error
.c_str());
1607 // dpkg is done at this point
1608 d
->progress
->Stop();
1612 if (pkgPackageManager::SigINTStop
)
1613 _error
->Warning(_("Operation was interrupted before it could finish"));
1615 if (RunScripts("DPkg::Post-Invoke") == false)
1618 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1620 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1621 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1622 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1624 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1625 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1627 _error
->PushToStack();
1628 pkgCacheFile CacheFile
;
1629 CacheFile
.BuildCaches(NULL
, true);
1630 _error
->RevertToStack();
1635 Cache
.writeStateFile(NULL
);
1636 return d
->dpkg_error
.empty();
1639 void SigINT(int /*sig*/) {
1640 pkgPackageManager::SigINTStop
= true;
1643 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1644 // ---------------------------------------------------------------------
1646 void pkgDPkgPM::Reset()
1648 List
.erase(List
.begin(),List
.end());
1651 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1652 // ---------------------------------------------------------------------
1654 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1656 // If apport doesn't exist or isn't installed do nothing
1657 // This e.g. prevents messages in 'universes' without apport
1658 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1659 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1662 string pkgname
, reportfile
, pkgver
, arch
;
1663 string::size_type pos
;
1666 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1668 std::clog
<< "configured to not write apport reports" << std::endl
;
1672 // only report the first errors
1673 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1675 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1679 // check if its not a follow up error
1680 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1681 if(strstr(errormsg
, needle
) != NULL
) {
1682 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1686 // do not report disk-full failures
1687 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1688 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1692 // do not report out-of-memory failures
1693 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1694 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1695 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1699 // do not report bugs regarding inaccessible local files
1700 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1701 strstr(errormsg
, "cannot access archive") != NULL
) {
1702 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1706 // do not report errors encountered when decompressing packages
1707 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1708 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1712 // do not report dpkg I/O errors, this is a format string, so we compare
1713 // the prefix and the suffix of the error with the dpkg error message
1714 vector
<string
> io_errors
;
1715 io_errors
.push_back(string("failed to read"));
1716 io_errors
.push_back(string("failed to write"));
1717 io_errors
.push_back(string("failed to seek"));
1718 io_errors
.push_back(string("unexpected end of file or stream"));
1720 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1722 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1723 if (list
.size() > 1) {
1724 // we need to split %s, VectorizeString only allows char so we need
1725 // to kill the "s" manually
1726 if (list
[1].size() > 1) {
1727 list
[1].erase(0, 1);
1728 if(strstr(errormsg
, list
[0].c_str()) &&
1729 strstr(errormsg
, list
[1].c_str())) {
1730 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1737 // get the pkgname and reportfile
1738 pkgname
= flNotDir(pkgpath
);
1739 pos
= pkgname
.find('_');
1740 if(pos
!= string::npos
)
1741 pkgname
= pkgname
.substr(0, pos
);
1743 // find the package version and source package name
1744 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1745 if (Pkg
.end() == true)
1747 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1748 if (Ver
.end() == true)
1750 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1752 // if the file exists already, we check:
1753 // - if it was reported already (touched by apport).
1754 // If not, we do nothing, otherwise
1755 // we overwrite it. This is the same behaviour as apport
1756 // - if we have a report with the same pkgversion already
1758 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1759 if(FileExists(reportfile
))
1764 // check atime/mtime
1765 stat(reportfile
.c_str(), &buf
);
1766 if(buf
.st_mtime
> buf
.st_atime
)
1769 // check if the existing report is the same version
1770 report
= fopen(reportfile
.c_str(),"r");
1771 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1773 if(strstr(strbuf
,"Package:") == strbuf
)
1775 char pkgname
[255], version
[255];
1776 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1777 if(strcmp(pkgver
.c_str(), version
) == 0)
1787 // now write the report
1788 arch
= _config
->Find("APT::Architecture");
1789 report
= fopen(reportfile
.c_str(),"w");
1792 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1793 chmod(reportfile
.c_str(), 0);
1795 chmod(reportfile
.c_str(), 0600);
1796 fprintf(report
, "ProblemType: Package\n");
1797 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1798 time_t now
= time(NULL
);
1799 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1800 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1801 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1802 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1803 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1805 // ensure that the log is flushed
1807 fflush(d
->term_out
);
1809 // attach terminal log it if we have it
1810 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1811 if (!logfile_name
.empty())
1815 fprintf(report
, "DpkgTerminalLog:\n");
1816 log
= fopen(logfile_name
.c_str(),"r");
1820 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1821 fprintf(report
, " %s", buf
);
1822 fprintf(report
, " \n");
1827 // attach history log it if we have it
1828 string histfile_name
= _config
->FindFile("Dir::Log::History");
1829 if (!histfile_name
.empty())
1831 fprintf(report
, "DpkgHistoryLog:\n");
1832 FILE* log
= fopen(histfile_name
.c_str(),"r");
1836 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1837 fprintf(report
, " %s", buf
);
1842 // log the ordering, see dpkgpm.h and the "Ops" enum there
1843 const char *ops_str
[] = {
1851 fprintf(report
, "AptOrdering:\n");
1852 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1853 if ((*I
).Pkg
!= NULL
)
1854 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1856 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1858 // attach dmesg log (to learn about segfaults)
1859 if (FileExists("/bin/dmesg"))
1861 fprintf(report
, "Dmesg:\n");
1862 FILE *log
= popen("/bin/dmesg","r");
1866 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1867 fprintf(report
, " %s", buf
);
1872 // attach df -l log (to learn about filesystem status)
1873 if (FileExists("/bin/df"))
1876 fprintf(report
, "Df:\n");
1877 FILE *log
= popen("/bin/df -l","r");
1881 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1882 fprintf(report
, " %s", buf
);