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/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/pkgrecords.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>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
58 APT_PURE
static unsigned int
61 unsigned int size
= 0;
62 char **envp
= environ
;
65 size
+= strlen (*envp
++) + 1;
70 class pkgDPkgPMPrivate
73 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
74 term_out(NULL
), history_out(NULL
),
75 progress(NULL
), tt_is_valid(false), master(-1),
76 slave(NULL
), protect_slave_from_dying(-1),
84 bool stdin_is_dev_null
;
85 // the buffer we use for the dpkg status-fd reading
91 APT::Progress::PackageManager
*progress
;
98 int protect_slave_from_dying
;
102 sigset_t original_sigmask
;
109 // Maps the dpkg "processing" info to human readable names. Entry 0
110 // of each array is the key, entry 1 is the value.
111 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
112 std::make_pair("install", N_("Installing %s")),
113 std::make_pair("configure", N_("Configuring %s")),
114 std::make_pair("remove", N_("Removing %s")),
115 std::make_pair("purge", N_("Completely removing %s")),
116 std::make_pair("disappear", N_("Noting disappearance of %s")),
117 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
120 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
121 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
123 // Predicate to test whether an entry in the PackageProcessingOps
124 // array matches a string.
125 class MatchProcessingOp
130 MatchProcessingOp(const char *the_target
)
135 bool operator()(const std::pair
<const char *, const char *> &pair
) const
137 return strcmp(pair
.first
, target
) == 0;
142 /* helper function to ionice the given PID
144 there is no C header for ionice yet - just the syscall interface
145 so we use the binary from util-linux
150 if (!FileExists("/usr/bin/ionice"))
152 pid_t Process
= ExecFork();
156 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
158 Args
[0] = "/usr/bin/ionice";
162 execv(Args
[0], (char **)Args
);
164 return ExecWait(Process
, "ionice");
167 static std::string
getDpkgExecutable()
169 string Tmp
= _config
->Find("Dir::Bin::dpkg","dpkg");
170 string
const dpkgChrootDir
= _config
->FindDir("DPkg::Chroot-Directory", "/");
171 size_t dpkgChrootLen
= dpkgChrootDir
.length();
172 if (dpkgChrootDir
!= "/" && Tmp
.find(dpkgChrootDir
) == 0)
174 if (dpkgChrootDir
[dpkgChrootLen
- 1] == '/')
176 Tmp
= Tmp
.substr(dpkgChrootLen
);
181 // dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/
182 static void dpkgChrootDirectory()
184 std::string
const chrootDir
= _config
->FindDir("DPkg::Chroot-Directory");
185 if (chrootDir
== "/")
187 std::cerr
<< "Chrooting into " << chrootDir
<< std::endl
;
188 if (chroot(chrootDir
.c_str()) != 0)
196 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
197 // ---------------------------------------------------------------------
198 /* This is helpful when a package is no longer installed but has residual
202 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
204 pkgCache::VerIterator Ver
;
205 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
206 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
207 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
209 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
216 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
217 // ---------------------------------------------------------------------
219 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
220 : pkgPackageManager(Cache
), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
222 d
= new pkgDPkgPMPrivate();
225 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
226 // ---------------------------------------------------------------------
228 pkgDPkgPM::~pkgDPkgPM()
233 // DPkgPM::Install - Install a package /*{{{*/
234 // ---------------------------------------------------------------------
235 /* Add an install operation to the sequence list */
236 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
238 if (File
.empty() == true || Pkg
.end() == true)
239 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
241 // If the filename string begins with DPkg::Chroot-Directory, return the
242 // substr that is within the chroot so dpkg can access it.
243 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
244 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
246 size_t len
= chrootdir
.length();
247 if (chrootdir
.at(len
- 1) == '/')
249 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
252 List
.push_back(Item(Item::Install
,Pkg
,File
));
257 // DPkgPM::Configure - Configure a package /*{{{*/
258 // ---------------------------------------------------------------------
259 /* Add a configure operation to the sequence list */
260 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
262 if (Pkg
.end() == true)
265 List
.push_back(Item(Item::Configure
, Pkg
));
267 // Use triggers for config calls if we configure "smart"
268 // as otherwise Pre-Depends will not be satisfied, see #526774
269 if (_config
->FindB("DPkg::TriggersPending", false) == true)
270 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
275 // DPkgPM::Remove - Remove a package /*{{{*/
276 // ---------------------------------------------------------------------
277 /* Add a remove operation to the sequence list */
278 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
280 if (Pkg
.end() == true)
284 List
.push_back(Item(Item::Purge
,Pkg
));
286 List
.push_back(Item(Item::Remove
,Pkg
));
290 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
291 // ---------------------------------------------------------------------
292 /* This is part of the helper script communication interface, it sends
293 very complete information down to the other end of the pipe.*/
294 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
296 return SendPkgsInfo(F
, 2);
298 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
300 // This version of APT supports only v3, so don't sent higher versions
302 fprintf(F
,"VERSION %u\n", Version
);
304 fprintf(F
,"VERSION 3\n");
306 /* Write out all of the configuration directives by walking the
307 configuration tree */
308 const Configuration::Item
*Top
= _config
->Tree(0);
311 if (Top
->Value
.empty() == false)
314 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
315 QuoteString(Top
->Value
,"\n").c_str());
324 while (Top
!= 0 && Top
->Next
== 0)
331 // Write out the package actions in order.
332 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
334 if(I
->Pkg
.end() == true)
337 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
339 fprintf(F
,"%s ",I
->Pkg
.Name());
341 // Current version which we are going to replace
342 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
343 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
344 CurVer
= FindNowVersion(I
->Pkg
);
346 if (CurVer
.end() == true)
351 fprintf(F
, "- - none ");
355 fprintf(F
, "%s ", CurVer
.VerStr());
357 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
360 // Show the compare operator between current and install version
361 if (S
.InstallVer
!= 0)
363 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
365 if (CurVer
.end() == false)
366 Comp
= InstVer
.CompareVer(CurVer
);
373 fprintf(F
, "%s ", InstVer
.VerStr());
375 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
382 fprintf(F
, "> - - none ");
385 // Show the filename/operation
386 if (I
->Op
== Item::Install
)
389 if (I
->File
[0] != '/')
390 fprintf(F
,"**ERROR**\n");
392 fprintf(F
,"%s\n",I
->File
.c_str());
394 else if (I
->Op
== Item::Configure
)
395 fprintf(F
,"**CONFIGURE**\n");
396 else if (I
->Op
== Item::Remove
||
397 I
->Op
== Item::Purge
)
398 fprintf(F
,"**REMOVE**\n");
406 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
407 // ---------------------------------------------------------------------
408 /* This looks for a list of scripts to run from the configuration file
409 each one is run and is fed on standard input a list of all .deb files
410 that are due to be installed. */
411 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
415 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
416 if (Opts
== 0 || Opts
->Child
== 0)
420 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
422 unsigned int Count
= 1;
423 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
425 if (Opts
->Value
.empty() == true)
428 if(_config
->FindB("Debug::RunScripts", false) == true)
429 std::clog
<< "Running external script with list of all .deb file: '"
430 << Opts
->Value
<< "'" << std::endl
;
432 // Determine the protocol version
433 string OptSec
= Opts
->Value
;
434 string::size_type Pos
;
435 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
436 Pos
= OptSec
.length();
437 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
439 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
440 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
443 std::set
<int> KeepFDs
;
444 MergeKeepFdsFromConfiguration(KeepFDs
);
446 if (pipe(Pipes
) != 0) {
447 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
450 if (InfoFD
!= (unsigned)Pipes
[0])
451 SetCloseExec(Pipes
[0],true);
453 KeepFDs
.insert(Pipes
[0]);
456 SetCloseExec(Pipes
[1],true);
458 // Purified Fork for running the script
459 pid_t Process
= ExecFork(KeepFDs
);
463 dup2(Pipes
[0], InfoFD
);
464 SetCloseExec(STDOUT_FILENO
,false);
465 SetCloseExec(STDIN_FILENO
,false);
466 SetCloseExec(STDERR_FILENO
,false);
469 strprintf(hookfd
, "%d", InfoFD
);
470 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
472 dpkgChrootDirectory();
476 Args
[2] = Opts
->Value
.c_str();
478 execv(Args
[0],(char **)Args
);
482 FILE *F
= fdopen(Pipes
[1],"w");
484 result
= _error
->Errno("fdopen","Faild to open new FD");
488 // Feed it the filenames.
491 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
493 // Only deal with packages to be installed from .deb
494 if (I
->Op
!= Item::Install
)
498 if (I
->File
[0] != '/')
501 /* Feed the filename of each package that is pending install
503 fprintf(F
,"%s\n",I
->File
.c_str());
509 SendPkgsInfo(F
, Version
);
513 // Clean up the sub process
514 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
515 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
519 signal(SIGPIPE
, old_sigpipe
);
524 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
525 // ---------------------------------------------------------------------
528 void pkgDPkgPM::DoStdin(int master
)
530 unsigned char input_buf
[256] = {0,};
531 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
533 FileFd::Write(master
, input_buf
, len
);
535 d
->stdin_is_dev_null
= true;
538 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
539 // ---------------------------------------------------------------------
541 * read the terminal pty and write log
543 void pkgDPkgPM::DoTerminalPty(int master
)
545 unsigned char term_buf
[1024] = {0,0, };
547 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
548 if(len
== -1 && errno
== EIO
)
550 // this happens when the child is about to exit, we
551 // give it time to actually exit, otherwise we run
552 // into a race so we sleep for half a second.
553 struct timespec sleepfor
= { 0, 500000000 };
554 nanosleep(&sleepfor
, NULL
);
559 FileFd::Write(1, term_buf
, len
);
561 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
564 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
565 // ---------------------------------------------------------------------
568 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
570 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
572 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
574 /* dpkg sends strings like this:
575 'status: <pkg>: <pkg qstate>'
576 'status: <pkg>:<arch>: <pkg qstate>'
578 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
579 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
582 // we need to split on ": " (note the appended space) as the ':' is
583 // part of the pkgname:arch information that dpkg sends
585 // A dpkg error message may contain additional ":" (like
586 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
587 // so we need to ensure to not split too much
588 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
592 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
596 // build the (prefix, pkgname, action) tuple, position of this
597 // is different for "processing" or "status" messages
598 std::string prefix
= APT::String::Strip(list
[0]);
602 // "processing" has the form "processing: action: pkg or trigger"
603 // with action = ["install", "upgrade", "configure", "remove", "purge",
604 // "disappear", "trigproc"]
605 if (prefix
== "processing")
607 pkgname
= APT::String::Strip(list
[2]);
608 action
= APT::String::Strip(list
[1]);
609 // we don't care for the difference (as dpkg doesn't really either)
610 if (action
== "upgrade")
613 // "status" has the form: "status: pkg: state"
614 // with state in ["half-installed", "unpacked", "half-configured",
615 // "installed", "config-files", "not-installed"]
616 else if (prefix
== "status")
618 pkgname
= APT::String::Strip(list
[1]);
619 action
= APT::String::Strip(list
[2]);
622 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
627 /* handle the special cases first:
629 errors look like this:
630 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
631 and conffile-prompt like this
632 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
634 if (prefix
== "status")
636 if(action
== "error")
638 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
641 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
644 else if(action
== "conffile-prompt")
646 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
652 // at this point we know that we should have a valid pkgname, so build all
655 // dpkg does not always send "pkgname:arch" so we add it here if needed
656 if (pkgname
.find(":") == std::string::npos
)
658 // find the package in the group that is touched by dpkg
659 // if there are multiple pkgs dpkg would send us a full pkgname:arch
660 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
661 if (Grp
.end() == false)
663 pkgCache::PkgIterator P
= Grp
.PackageList();
664 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
666 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
668 pkgname
= P
.FullName();
675 const char* const pkg
= pkgname
.c_str();
676 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
677 std::string arch
= "";
678 if (pkgname
.find(":") != string::npos
)
679 arch
= StringSplit(pkgname
, ":")[1];
680 std::string i18n_pkgname
= pkgname
;
681 if (arch
.size() != 0)
682 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
684 // 'processing' from dpkg looks like
685 // 'processing: action: pkg'
686 if(prefix
== "processing")
688 const std::pair
<const char *, const char *> * const iter
=
689 std::find_if(PackageProcessingOpsBegin
,
690 PackageProcessingOpsEnd
,
691 MatchProcessingOp(action
.c_str()));
692 if(iter
== PackageProcessingOpsEnd
)
695 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
699 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
700 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
702 // FIXME: this needs a muliarch testcase
703 // FIXME2: is "pkgname" here reliable with dpkg only sending us
705 if (action
== "disappear")
706 handleDisappearAction(pkgname
);
710 if (prefix
== "status")
712 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
713 if(PackageOpsDone
[pkg
] < states
.size())
715 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
716 if (next_action
&& Debug
== true)
717 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
718 << " action: " << action
<< " (expected: '" << next_action
<< "' "
719 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
721 // check if the package moved to the next dpkg state
722 if(next_action
&& (action
== next_action
))
724 // only read the translation if there is actually a next action
725 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
727 // we moved from one dpkg state to a new one, report that
728 ++PackageOpsDone
[pkg
];
732 strprintf(msg
, translation
, i18n_pkgname
.c_str());
733 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
739 // DPkgPM::handleDisappearAction /*{{{*/
740 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
742 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
743 if (unlikely(Pkg
.end() == true))
746 // record the package name for display and stuff later
747 disappearedPkgs
.insert(Pkg
.FullName(true));
749 // the disappeared package was auto-installed - nothing to do
750 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
752 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
753 if (unlikely(PkgVer
.end() == true))
755 /* search in the list of dependencies for (Pre)Depends,
756 check if this dependency has a Replaces on our package
757 and if so transfer the manual installed flag to it */
758 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
760 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
761 Dep
->Type
!= pkgCache::Dep::PreDepends
)
763 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
764 if (unlikely(Tar
.end() == true))
766 // the package is already marked as manual
767 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
769 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
770 if (TarVer
.end() == true)
772 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
774 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
776 if (Pkg
!= Rep
.TargetPkg())
778 // okay, they are strongly connected - transfer manual-bit
780 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
781 Cache
[Tar
].Flags
&= ~Flag::Auto
;
787 // DPkgPM::DoDpkgStatusFd /*{{{*/
788 // ---------------------------------------------------------------------
791 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
796 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
797 d
->dpkgbuf_pos
+= len
;
801 // process line by line if we have a buffer
803 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
806 ProcessDpkgStatusLine(p
);
807 p
=q
+1; // continue with next line
810 // now move the unprocessed bits (after the final \n that is now a 0x0)
811 // to the start and update d->dpkgbuf_pos
812 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
816 // we are interessted in the first char *after* 0x0
819 // move the unprocessed tail to the start and update pos
820 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
821 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
824 // DPkgPM::WriteHistoryTag /*{{{*/
825 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
827 size_t const length
= value
.length();
830 // poor mans rstrip(", ")
831 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
832 value
.erase(length
- 2, 2);
833 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
835 // DPkgPM::OpenLog /*{{{*/
836 bool pkgDPkgPM::OpenLog()
838 string
const logdir
= _config
->FindDir("Dir::Log");
839 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
840 // FIXME: use a better string after freeze
841 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
845 time_t const t
= time(NULL
);
846 struct tm
const * const tmp
= localtime(&t
);
847 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
850 string
const logfile_name
= flCombine(logdir
,
851 _config
->Find("Dir::Log::Terminal"));
852 if (!logfile_name
.empty())
854 d
->term_out
= fopen(logfile_name
.c_str(),"a");
855 if (d
->term_out
== NULL
)
856 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
857 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
858 SetCloseExec(fileno(d
->term_out
), true);
859 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
861 struct passwd
*pw
= getpwnam("root");
862 struct group
*gr
= getgrnam("adm");
863 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
864 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
866 if (chmod(logfile_name
.c_str(), 0640) != 0)
867 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
868 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
871 // write your history
872 string
const history_name
= flCombine(logdir
,
873 _config
->Find("Dir::Log::History"));
874 if (!history_name
.empty())
876 d
->history_out
= fopen(history_name
.c_str(),"a");
877 if (d
->history_out
== NULL
)
878 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
879 SetCloseExec(fileno(d
->history_out
), true);
880 chmod(history_name
.c_str(), 0644);
881 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
882 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
883 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
885 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
887 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
888 if (Cache
[I
].NewInstall() == true)
889 HISTORYINFO(install
, CANDIDATE_AUTO
)
890 else if (Cache
[I
].ReInstall() == true)
891 HISTORYINFO(reinstall
, CANDIDATE
)
892 else if (Cache
[I
].Upgrade() == true)
893 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
894 else if (Cache
[I
].Downgrade() == true)
895 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
896 else if (Cache
[I
].Delete() == true)
897 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
901 line
->append(I
.FullName(false)).append(" (");
902 switch (infostring
) {
903 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
905 line
->append(Cache
[I
].CandVersion
);
906 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
907 line
->append(", automatic");
909 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
910 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
914 if (_config
->Exists("Commandline::AsString") == true)
915 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
916 WriteHistoryTag("Install", install
);
917 WriteHistoryTag("Reinstall", reinstall
);
918 WriteHistoryTag("Upgrade", upgrade
);
919 WriteHistoryTag("Downgrade",downgrade
);
920 WriteHistoryTag("Remove",remove
);
921 WriteHistoryTag("Purge",purge
);
922 fflush(d
->history_out
);
928 // DPkg::CloseLog /*{{{*/
929 bool pkgDPkgPM::CloseLog()
932 time_t t
= time(NULL
);
933 struct tm
*tmp
= localtime(&t
);
934 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
938 fprintf(d
->term_out
, "Log ended: ");
939 fprintf(d
->term_out
, "%s", timestr
);
940 fprintf(d
->term_out
, "\n");
947 if (disappearedPkgs
.empty() == false)
950 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
951 d
!= disappearedPkgs
.end(); ++d
)
953 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
954 disappear
.append(*d
);
956 disappear
.append(", ");
958 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
960 WriteHistoryTag("Disappeared", disappear
);
962 if (d
->dpkg_error
.empty() == false)
963 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
964 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
965 fclose(d
->history_out
);
967 d
->history_out
= NULL
;
974 // This implements a racy version of pselect for those architectures
975 // that don't have a working implementation.
976 // FIXME: Probably can be removed on Lenny+1
977 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
978 fd_set
*exceptfds
, const struct timespec
*timeout
,
979 const sigset_t
*sigmask
)
985 tv
.tv_sec
= timeout
->tv_sec
;
986 tv
.tv_usec
= timeout
->tv_nsec
/1000;
988 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
989 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
990 sigprocmask(SIG_SETMASK
, &origmask
, 0);
995 // DPkgPM::BuildPackagesProgressMap /*{{{*/
996 void pkgDPkgPM::BuildPackagesProgressMap()
998 // map the dpkg states to the operations that are performed
999 // (this is sorted in the same way as Item::Ops)
1000 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1001 // Install operation
1003 {"half-installed", N_("Preparing %s")},
1004 {"unpacked", N_("Unpacking %s") },
1007 // Configure operation
1009 {"unpacked",N_("Preparing to configure %s") },
1010 {"half-configured", N_("Configuring %s") },
1011 { "installed", N_("Installed %s")},
1016 {"half-configured", N_("Preparing for removal of %s")},
1017 {"half-installed", N_("Removing %s")},
1018 {"config-files", N_("Removed %s")},
1023 {"config-files", N_("Preparing to completely remove %s")},
1024 {"not-installed", N_("Completely removed %s")},
1029 // init the PackageOps map, go over the list of packages that
1030 // that will be [installed|configured|removed|purged] and add
1031 // them to the PackageOps map (the dpkg states it goes through)
1032 // and the PackageOpsTranslations (human readable strings)
1033 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1035 if((*I
).Pkg
.end() == true)
1038 string
const name
= (*I
).Pkg
.FullName();
1039 PackageOpsDone
[name
] = 0;
1040 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1042 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1046 /* one extra: We don't want the progress bar to reach 100%, especially not
1047 if we call dpkg --configure --pending and process a bunch of triggers
1048 while showing 100%. Also, spindown takes a while, so never reaching 100%
1049 is way more correct than reaching 100% while still doing stuff even if
1050 doing it this way is slightly bending the rules */
1054 bool pkgDPkgPM::Go(int StatusFd
)
1056 APT::Progress::PackageManager
*progress
= NULL
;
1058 progress
= APT::Progress::PackageManagerProgressFactory();
1060 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1062 return Go(progress
);
1065 void pkgDPkgPM::StartPtyMagic()
1067 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1070 if (d
->slave
!= NULL
)
1076 if (isatty(STDIN_FILENO
) == 0)
1077 d
->direct_stdin
= true;
1079 _error
->PushToStack();
1081 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1082 if (d
->master
== -1)
1083 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1084 else if (unlockpt(d
->master
) == -1)
1085 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1088 char const * const slave_name
= ptsname(d
->master
);
1089 if (slave_name
== NULL
)
1090 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1093 d
->slave
= strdup(slave_name
);
1094 if (d
->slave
== NULL
)
1095 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1096 else if (grantpt(d
->master
) == -1)
1097 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1098 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1100 d
->tt_is_valid
= true;
1101 struct termios raw_tt
;
1102 // copy window size of stdout if its a 'good' terminal
1103 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1106 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1107 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1108 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1109 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1111 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1112 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1116 raw_tt
.c_lflag
&= ~ECHO
;
1117 raw_tt
.c_lflag
|= ISIG
;
1118 // block SIGTTOU during tcsetattr to prevent a hang if
1119 // the process is a member of the background process group
1120 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1121 sigemptyset(&d
->sigmask
);
1122 sigaddset(&d
->sigmask
, SIGTTOU
);
1123 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1124 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1125 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1126 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1129 if (d
->slave
!= NULL
)
1131 /* on linux, closing (and later reopening) all references to the slave
1132 makes the slave a death end, so we open it here to have one open all
1133 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1134 on kfreebsd we get an incorrect ("step like") output then while it has
1135 no problem with closing all references… so to avoid platform specific
1136 code here we combine both and be happy once more */
1137 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1142 if (_error
->PendingError() == true)
1144 if (d
->master
!= -1)
1149 if (d
->slave
!= NULL
)
1154 _error
->DumpErrors(std::cerr
);
1156 _error
->RevertToStack();
1158 void pkgDPkgPM::SetupSlavePtyMagic()
1160 if(d
->master
== -1 || d
->slave
== NULL
)
1163 if (close(d
->master
) == -1)
1164 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1167 _error
->FatalE("setsid", "Starting a new session for child failed!");
1169 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1171 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1172 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1173 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1176 unsigned short i
= 0;
1177 if (d
->direct_stdin
== true)
1180 if (dup2(slaveFd
, i
) == -1)
1181 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1183 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1184 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1190 void pkgDPkgPM::StopPtyMagic()
1192 if (d
->slave
!= NULL
)
1195 if (d
->protect_slave_from_dying
!= -1)
1197 close(d
->protect_slave_from_dying
);
1198 d
->protect_slave_from_dying
= -1;
1202 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1203 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1209 // DPkgPM::Go - Run the sequence /*{{{*/
1210 // ---------------------------------------------------------------------
1211 /* This globs the operations and calls dpkg
1213 * If it is called with a progress object apt will report the install
1214 * progress to this object. It maps the dpkg states a package goes
1215 * through to human readable (and i10n-able)
1216 * names and calculates a percentage for each step.
1218 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1220 pkgPackageManager::SigINTStop
= false;
1221 d
->progress
= progress
;
1223 // Generate the base argument list for dpkg
1224 unsigned long StartSize
= 0;
1225 std::vector
<const char *> Args
;
1226 std::string DpkgExecutable
= getDpkgExecutable();
1227 Args
.push_back(DpkgExecutable
.c_str());
1228 StartSize
+= DpkgExecutable
.length();
1230 // Stick in any custom dpkg options
1231 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1235 for (; Opts
!= 0; Opts
= Opts
->Next
)
1237 if (Opts
->Value
.empty() == true)
1239 Args
.push_back(Opts
->Value
.c_str());
1240 StartSize
+= Opts
->Value
.length();
1244 size_t const BaseArgs
= Args
.size();
1245 // we need to detect if we can qualify packages with the architecture or not
1246 Args
.push_back("--assert-multi-arch");
1247 Args
.push_back(NULL
);
1249 pid_t dpkgAssertMultiArch
= ExecFork();
1250 if (dpkgAssertMultiArch
== 0)
1252 dpkgChrootDirectory();
1253 // redirect everything to the ultimate sink as we only need the exit-status
1254 int const nullfd
= open("/dev/null", O_RDONLY
);
1255 dup2(nullfd
, STDIN_FILENO
);
1256 dup2(nullfd
, STDOUT_FILENO
);
1257 dup2(nullfd
, STDERR_FILENO
);
1258 execvp(Args
[0], (char**) &Args
[0]);
1259 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1266 // FIXME: do we really need this limit when we have MaxArgBytes?
1267 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1269 // try to figure out the max environment size
1270 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1273 OSArgMax
-= EnvironmentSize() - 2*1024;
1274 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1275 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1277 if (RunScripts("DPkg::Pre-Invoke") == false)
1280 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1283 // support subpressing of triggers processing for special
1284 // cases like d-i that runs the triggers handling manually
1285 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1286 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1287 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1290 BuildPackagesProgressMap();
1292 d
->stdin_is_dev_null
= false;
1297 bool dpkgMultiArch
= false;
1298 if (dpkgAssertMultiArch
> 0)
1301 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1305 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1308 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1309 dpkgMultiArch
= true;
1312 // start pty magic before the loop
1315 // Tell the progress that its starting and fork dpkg
1316 d
->progress
->Start(d
->master
);
1318 // this loop is runs once per dpkg operation
1319 vector
<Item
>::const_iterator I
= List
.begin();
1320 while (I
!= List
.end())
1322 // Do all actions with the same Op in one run
1323 vector
<Item
>::const_iterator J
= I
;
1324 if (TriggersPending
== true)
1325 for (; J
!= List
.end(); ++J
)
1329 if (J
->Op
!= Item::TriggersPending
)
1331 vector
<Item
>::const_iterator T
= J
+ 1;
1332 if (T
!= List
.end() && T
->Op
== I
->Op
)
1337 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1340 // keep track of allocated strings for multiarch package names
1341 std::vector
<char *> Packages
;
1343 // start with the baseset of arguments
1344 unsigned long Size
= StartSize
;
1345 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1347 // Now check if we are within the MaxArgs limit
1349 // this code below is problematic, because it may happen that
1350 // the argument list is split in a way that A depends on B
1351 // and they are in the same "--configure A B" run
1352 // - with the split they may now be configured in different
1353 // runs, using Immediate-Configure-All can help prevent this.
1354 if (J
- I
> (signed)MaxArgs
)
1357 unsigned long const size
= MaxArgs
+ 10;
1359 Packages
.reserve(size
);
1363 unsigned long const size
= (J
- I
) + 10;
1365 Packages
.reserve(size
);
1370 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1372 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1373 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1375 ADDARGC("--status-fd");
1376 char status_fd_buf
[20];
1377 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1378 ADDARG(status_fd_buf
);
1379 unsigned long const Op
= I
->Op
;
1384 ADDARGC("--force-depends");
1385 ADDARGC("--force-remove-essential");
1386 ADDARGC("--remove");
1390 ADDARGC("--force-depends");
1391 ADDARGC("--force-remove-essential");
1395 case Item::Configure
:
1396 ADDARGC("--configure");
1399 case Item::ConfigurePending
:
1400 ADDARGC("--configure");
1401 ADDARGC("--pending");
1404 case Item::TriggersPending
:
1405 ADDARGC("--triggers-only");
1406 ADDARGC("--pending");
1410 ADDARGC("--unpack");
1411 ADDARGC("--auto-deconfigure");
1415 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1416 I
->Op
!= Item::ConfigurePending
)
1418 ADDARGC("--no-triggers");
1422 // Write in the file or package names
1423 if (I
->Op
== Item::Install
)
1425 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1427 if (I
->File
[0] != '/')
1428 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1429 Args
.push_back(I
->File
.c_str());
1430 Size
+= I
->File
.length();
1435 string
const nativeArch
= _config
->Find("APT::Architecture");
1436 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1437 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1439 if((*I
).Pkg
.end() == true)
1441 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1443 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1444 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1445 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1446 strcmp(I
->Pkg
.Arch(), "none") == 0))
1448 char const * const name
= I
->Pkg
.Name();
1453 pkgCache::VerIterator PkgVer
;
1454 std::string name
= I
->Pkg
.Name();
1455 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1457 PkgVer
= I
->Pkg
.CurrentVer();
1458 if(PkgVer
.end() == true)
1459 PkgVer
= FindNowVersion(I
->Pkg
);
1462 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1463 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1464 ; // never arch-qualify a package without an arch
1465 else if (PkgVer
.end() == false)
1466 name
.append(":").append(PkgVer
.Arch());
1468 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1469 char * const fullname
= strdup(name
.c_str());
1470 Packages
.push_back(fullname
);
1474 // skip configure action if all sheduled packages disappeared
1475 if (oldSize
== Size
)
1482 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1484 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1485 a
!= Args
.end(); ++a
)
1490 Args
.push_back(NULL
);
1496 /* Mask off sig int/quit. We do this because dpkg also does when
1497 it forks scripts. What happens is that when you hit ctrl-c it sends
1498 it to all processes in the group. Since dpkg ignores the signal
1499 it doesn't die but we do! So we must also ignore it */
1500 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1501 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1503 // Check here for any SIGINT
1504 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1508 // ignore SIGHUP as well (debian #463030)
1509 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1512 d
->progress
->StartDpkg();
1513 std::set
<int> KeepFDs
;
1514 KeepFDs
.insert(fd
[1]);
1515 MergeKeepFdsFromConfiguration(KeepFDs
);
1516 pid_t Child
= ExecFork(KeepFDs
);
1519 // This is the child
1520 SetupSlavePtyMagic();
1521 close(fd
[0]); // close the read end of the pipe
1523 dpkgChrootDirectory();
1525 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1528 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1532 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1535 // Discard everything in stdin before forking dpkg
1536 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1539 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1541 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1545 /* No Job Control Stop Env is a magic dpkg var that prevents it
1546 from using sigstop */
1547 putenv((char *)"DPKG_NO_TSTP=yes");
1548 execvp(Args
[0], (char**) &Args
[0]);
1549 cerr
<< "Could not exec dpkg!" << endl
;
1554 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1560 // we read from dpkg here
1561 int const _dpkgin
= fd
[0];
1562 close(fd
[1]); // close the write end of the pipe
1565 sigemptyset(&d
->sigmask
);
1566 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1568 /* free vectors (and therefore memory) as we don't need the included data anymore */
1569 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1570 p
!= Packages
.end(); ++p
)
1574 // the result of the waitpid call
1577 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1579 // FIXME: move this to a function or something, looks ugly here
1580 // error handling, waitpid returned -1
1583 RunScripts("DPkg::Post-Invoke");
1585 // Restore sig int/quit
1586 signal(SIGQUIT
,old_SIGQUIT
);
1587 signal(SIGINT
,old_SIGINT
);
1589 signal(SIGHUP
,old_SIGHUP
);
1590 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1593 // wait for input or output here
1595 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1596 FD_SET(STDIN_FILENO
, &rfds
);
1597 FD_SET(_dpkgin
, &rfds
);
1599 FD_SET(d
->master
, &rfds
);
1601 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1602 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1603 &tv
, &d
->original_sigmask
);
1604 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1605 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1606 NULL
, &tv
, &d
->original_sigmask
);
1607 d
->progress
->Pulse();
1608 if (select_ret
== 0)
1610 else if (select_ret
< 0 && errno
== EINTR
)
1612 else if (select_ret
< 0)
1614 perror("select() returned error");
1618 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1619 DoTerminalPty(d
->master
);
1620 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1622 if(FD_ISSET(_dpkgin
, &rfds
))
1623 DoDpkgStatusFd(_dpkgin
);
1627 // Restore sig int/quit
1628 signal(SIGQUIT
,old_SIGQUIT
);
1629 signal(SIGINT
,old_SIGINT
);
1631 signal(SIGHUP
,old_SIGHUP
);
1632 // Check for an error code.
1633 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1635 // if it was set to "keep-dpkg-runing" then we won't return
1636 // here but keep the loop going and just report it as a error
1638 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1640 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1641 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1642 else if (WIFEXITED(Status
) != 0)
1643 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1645 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1646 _error
->Error("%s", d
->dpkg_error
.c_str());
1652 // dpkg is done at this point
1653 d
->progress
->Stop();
1657 if (pkgPackageManager::SigINTStop
)
1658 _error
->Warning(_("Operation was interrupted before it could finish"));
1660 if (RunScripts("DPkg::Post-Invoke") == false)
1663 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1665 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1666 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1667 unlink(oldpkgcache
.c_str()) == 0)
1669 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1670 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1672 _error
->PushToStack();
1673 pkgCacheFile CacheFile
;
1674 CacheFile
.BuildCaches(NULL
, true);
1675 _error
->RevertToStack();
1680 Cache
.writeStateFile(NULL
);
1681 return d
->dpkg_error
.empty();
1684 void SigINT(int /*sig*/) {
1685 pkgPackageManager::SigINTStop
= true;
1688 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1689 // ---------------------------------------------------------------------
1691 void pkgDPkgPM::Reset()
1693 List
.erase(List
.begin(),List
.end());
1696 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1697 // ---------------------------------------------------------------------
1699 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1701 // If apport doesn't exist or isn't installed do nothing
1702 // This e.g. prevents messages in 'universes' without apport
1703 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1704 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1707 string pkgname
, reportfile
, pkgver
, arch
;
1708 string::size_type pos
;
1711 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1713 std::clog
<< "configured to not write apport reports" << std::endl
;
1717 // only report the first errors
1718 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1720 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1724 // check if its not a follow up error
1725 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1726 if(strstr(errormsg
, needle
) != NULL
) {
1727 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1731 // do not report disk-full failures
1732 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1733 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1737 // do not report out-of-memory failures
1738 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1739 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1740 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1744 // do not report bugs regarding inaccessible local files
1745 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1746 strstr(errormsg
, "cannot access archive") != NULL
) {
1747 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1751 // do not report errors encountered when decompressing packages
1752 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1753 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1757 // do not report dpkg I/O errors, this is a format string, so we compare
1758 // the prefix and the suffix of the error with the dpkg error message
1759 vector
<string
> io_errors
;
1760 io_errors
.push_back(string("failed to read"));
1761 io_errors
.push_back(string("failed to write"));
1762 io_errors
.push_back(string("failed to seek"));
1763 io_errors
.push_back(string("unexpected end of file or stream"));
1765 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1767 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1768 if (list
.size() > 1) {
1769 // we need to split %s, VectorizeString only allows char so we need
1770 // to kill the "s" manually
1771 if (list
[1].size() > 1) {
1772 list
[1].erase(0, 1);
1773 if(strstr(errormsg
, list
[0].c_str()) &&
1774 strstr(errormsg
, list
[1].c_str())) {
1775 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1782 // get the pkgname and reportfile
1783 pkgname
= flNotDir(pkgpath
);
1784 pos
= pkgname
.find('_');
1785 if(pos
!= string::npos
)
1786 pkgname
= pkgname
.substr(0, pos
);
1788 // find the package versin and source package name
1789 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1790 if (Pkg
.end() == true)
1792 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1793 if (Ver
.end() == true)
1795 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1797 // if the file exists already, we check:
1798 // - if it was reported already (touched by apport).
1799 // If not, we do nothing, otherwise
1800 // we overwrite it. This is the same behaviour as apport
1801 // - if we have a report with the same pkgversion already
1803 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1804 if(FileExists(reportfile
))
1809 // check atime/mtime
1810 stat(reportfile
.c_str(), &buf
);
1811 if(buf
.st_mtime
> buf
.st_atime
)
1814 // check if the existing report is the same version
1815 report
= fopen(reportfile
.c_str(),"r");
1816 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1818 if(strstr(strbuf
,"Package:") == strbuf
)
1820 char pkgname
[255], version
[255];
1821 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1822 if(strcmp(pkgver
.c_str(), version
) == 0)
1832 // now write the report
1833 arch
= _config
->Find("APT::Architecture");
1834 report
= fopen(reportfile
.c_str(),"w");
1837 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1838 chmod(reportfile
.c_str(), 0);
1840 chmod(reportfile
.c_str(), 0600);
1841 fprintf(report
, "ProblemType: Package\n");
1842 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1843 time_t now
= time(NULL
);
1844 fprintf(report
, "Date: %s" , ctime(&now
));
1845 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1846 #if APT_PKG_ABI >= 413
1847 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1849 pkgRecords
Recs(Cache
);
1850 pkgRecords::Parser
&Parse
= Recs
.Lookup(Ver
.FileList());
1851 std::string srcpkgname
= Parse
.SourcePkg();
1852 if(srcpkgname
.empty())
1853 srcpkgname
= pkgname
;
1854 fprintf(report
, "SourcePackage: %s\n", srcpkgname
.c_str());
1856 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1858 // ensure that the log is flushed
1860 fflush(d
->term_out
);
1862 // attach terminal log it if we have it
1863 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1864 if (!logfile_name
.empty())
1868 fprintf(report
, "DpkgTerminalLog:\n");
1869 log
= fopen(logfile_name
.c_str(),"r");
1873 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1874 fprintf(report
, " %s", buf
);
1875 fprintf(report
, " \n");
1880 // attach history log it if we have it
1881 string histfile_name
= _config
->FindFile("Dir::Log::History");
1882 if (!histfile_name
.empty())
1884 fprintf(report
, "DpkgHistoryLog:\n");
1885 FILE* log
= fopen(histfile_name
.c_str(),"r");
1889 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1890 fprintf(report
, " %s", buf
);
1895 // log the ordering, see dpkgpm.h and the "Ops" enum there
1896 const char *ops_str
[] = {
1904 fprintf(report
, "AptOrdering:\n");
1905 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1906 if ((*I
).Pkg
!= NULL
)
1907 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1909 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1911 // attach dmesg log (to learn about segfaults)
1912 if (FileExists("/bin/dmesg"))
1914 fprintf(report
, "Dmesg:\n");
1915 FILE *log
= popen("/bin/dmesg","r");
1919 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1920 fprintf(report
, " %s", buf
);
1925 // attach df -l log (to learn about filesystem status)
1926 if (FileExists("/bin/df"))
1929 fprintf(report
, "Df:\n");
1930 FILE *log
= popen("/bin/df -l","r");
1934 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1935 fprintf(report
, " %s", buf
);