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/strutl.h>
22 #include <apt-pkg/cacheiterators.h>
23 #include <apt-pkg/macros.h>
24 #include <apt-pkg/pkgcache.h>
34 #include <sys/ioctl.h>
35 #include <sys/select.h>
56 APT_PURE
static unsigned int
59 unsigned int size
= 0;
60 char **envp
= environ
;
63 size
+= strlen (*envp
++) + 1;
68 class pkgDPkgPMPrivate
71 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
72 term_out(NULL
), history_out(NULL
),
73 progress(NULL
), tt_is_valid(false), master(-1),
74 slave(NULL
), protect_slave_from_dying(-1),
82 bool stdin_is_dev_null
;
83 // the buffer we use for the dpkg status-fd reading
89 APT::Progress::PackageManager
*progress
;
96 int protect_slave_from_dying
;
100 sigset_t original_sigmask
;
107 // Maps the dpkg "processing" info to human readable names. Entry 0
108 // of each array is the key, entry 1 is the value.
109 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
110 std::make_pair("install", N_("Installing %s")),
111 std::make_pair("configure", N_("Configuring %s")),
112 std::make_pair("remove", N_("Removing %s")),
113 std::make_pair("purge", N_("Completely removing %s")),
114 std::make_pair("disappear", N_("Noting disappearance of %s")),
115 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
118 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
119 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
121 // Predicate to test whether an entry in the PackageProcessingOps
122 // array matches a string.
123 class MatchProcessingOp
128 MatchProcessingOp(const char *the_target
)
133 bool operator()(const std::pair
<const char *, const char *> &pair
) const
135 return strcmp(pair
.first
, target
) == 0;
140 /* helper function to ionice the given PID
142 there is no C header for ionice yet - just the syscall interface
143 so we use the binary from util-linux
148 if (!FileExists("/usr/bin/ionice"))
150 pid_t Process
= ExecFork();
154 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
156 Args
[0] = "/usr/bin/ionice";
160 execv(Args
[0], (char **)Args
);
162 return ExecWait(Process
, "ionice");
165 static std::string
getDpkgExecutable()
167 string Tmp
= _config
->Find("Dir::Bin::dpkg","dpkg");
168 string
const dpkgChrootDir
= _config
->FindDir("DPkg::Chroot-Directory", "/");
169 size_t dpkgChrootLen
= dpkgChrootDir
.length();
170 if (dpkgChrootDir
!= "/" && Tmp
.find(dpkgChrootDir
) == 0)
172 if (dpkgChrootDir
[dpkgChrootLen
- 1] == '/')
174 Tmp
= Tmp
.substr(dpkgChrootLen
);
179 // dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/
180 static void dpkgChrootDirectory()
182 std::string
const chrootDir
= _config
->FindDir("DPkg::Chroot-Directory");
183 if (chrootDir
== "/")
185 std::cerr
<< "Chrooting into " << chrootDir
<< std::endl
;
186 if (chroot(chrootDir
.c_str()) != 0)
194 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
195 // ---------------------------------------------------------------------
196 /* This is helpful when a package is no longer installed but has residual
200 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
202 pkgCache::VerIterator Ver
;
203 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
204 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
205 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
207 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
214 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
215 // ---------------------------------------------------------------------
217 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
218 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
222 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
223 // ---------------------------------------------------------------------
225 pkgDPkgPM::~pkgDPkgPM()
230 // DPkgPM::Install - Install a package /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Add an install operation to the sequence list */
233 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
235 if (File
.empty() == true || Pkg
.end() == true)
236 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
238 // If the filename string begins with DPkg::Chroot-Directory, return the
239 // substr that is within the chroot so dpkg can access it.
240 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
241 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
243 size_t len
= chrootdir
.length();
244 if (chrootdir
.at(len
- 1) == '/')
246 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
249 List
.push_back(Item(Item::Install
,Pkg
,File
));
254 // DPkgPM::Configure - Configure a package /*{{{*/
255 // ---------------------------------------------------------------------
256 /* Add a configure operation to the sequence list */
257 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
259 if (Pkg
.end() == true)
262 List
.push_back(Item(Item::Configure
, Pkg
));
264 // Use triggers for config calls if we configure "smart"
265 // as otherwise Pre-Depends will not be satisfied, see #526774
266 if (_config
->FindB("DPkg::TriggersPending", false) == true)
267 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
272 // DPkgPM::Remove - Remove a package /*{{{*/
273 // ---------------------------------------------------------------------
274 /* Add a remove operation to the sequence list */
275 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
277 if (Pkg
.end() == true)
281 List
.push_back(Item(Item::Purge
,Pkg
));
283 List
.push_back(Item(Item::Remove
,Pkg
));
287 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This is part of the helper script communication interface, it sends
290 very complete information down to the other end of the pipe.*/
291 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
293 return SendPkgsInfo(F
, 2);
295 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
297 // This version of APT supports only v3, so don't sent higher versions
299 fprintf(F
,"VERSION %u\n", Version
);
301 fprintf(F
,"VERSION 3\n");
303 /* Write out all of the configuration directives by walking the
304 configuration tree */
305 const Configuration::Item
*Top
= _config
->Tree(0);
308 if (Top
->Value
.empty() == false)
311 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
312 QuoteString(Top
->Value
,"\n").c_str());
321 while (Top
!= 0 && Top
->Next
== 0)
328 // Write out the package actions in order.
329 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
331 if(I
->Pkg
.end() == true)
334 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
336 fprintf(F
,"%s ",I
->Pkg
.Name());
338 // Current version which we are going to replace
339 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
340 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
341 CurVer
= FindNowVersion(I
->Pkg
);
343 if (CurVer
.end() == true)
348 fprintf(F
, "- - none ");
352 fprintf(F
, "%s ", CurVer
.VerStr());
354 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
357 // Show the compare operator between current and install version
358 if (S
.InstallVer
!= 0)
360 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
362 if (CurVer
.end() == false)
363 Comp
= InstVer
.CompareVer(CurVer
);
370 fprintf(F
, "%s ", InstVer
.VerStr());
372 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
379 fprintf(F
, "> - - none ");
382 // Show the filename/operation
383 if (I
->Op
== Item::Install
)
386 if (I
->File
[0] != '/')
387 fprintf(F
,"**ERROR**\n");
389 fprintf(F
,"%s\n",I
->File
.c_str());
391 else if (I
->Op
== Item::Configure
)
392 fprintf(F
,"**CONFIGURE**\n");
393 else if (I
->Op
== Item::Remove
||
394 I
->Op
== Item::Purge
)
395 fprintf(F
,"**REMOVE**\n");
403 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
404 // ---------------------------------------------------------------------
405 /* This looks for a list of scripts to run from the configuration file
406 each one is run and is fed on standard input a list of all .deb files
407 that are due to be installed. */
408 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
412 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
413 if (Opts
== 0 || Opts
->Child
== 0)
417 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
419 unsigned int Count
= 1;
420 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
422 if (Opts
->Value
.empty() == true)
425 if(_config
->FindB("Debug::RunScripts", false) == true)
426 std::clog
<< "Running external script with list of all .deb file: '"
427 << Opts
->Value
<< "'" << std::endl
;
429 // Determine the protocol version
430 string OptSec
= Opts
->Value
;
431 string::size_type Pos
;
432 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
433 Pos
= OptSec
.length();
434 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
436 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
437 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
440 std::set
<int> KeepFDs
;
441 MergeKeepFdsFromConfiguration(KeepFDs
);
443 if (pipe(Pipes
) != 0) {
444 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
447 if (InfoFD
!= (unsigned)Pipes
[0])
448 SetCloseExec(Pipes
[0],true);
450 KeepFDs
.insert(Pipes
[0]);
453 SetCloseExec(Pipes
[1],true);
455 // Purified Fork for running the script
456 pid_t Process
= ExecFork(KeepFDs
);
460 dup2(Pipes
[0], InfoFD
);
461 SetCloseExec(STDOUT_FILENO
,false);
462 SetCloseExec(STDIN_FILENO
,false);
463 SetCloseExec(STDERR_FILENO
,false);
466 strprintf(hookfd
, "%d", InfoFD
);
467 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
469 dpkgChrootDirectory();
473 Args
[2] = Opts
->Value
.c_str();
475 execv(Args
[0],(char **)Args
);
479 FILE *F
= fdopen(Pipes
[1],"w");
481 result
= _error
->Errno("fdopen","Faild to open new FD");
485 // Feed it the filenames.
488 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
490 // Only deal with packages to be installed from .deb
491 if (I
->Op
!= Item::Install
)
495 if (I
->File
[0] != '/')
498 /* Feed the filename of each package that is pending install
500 fprintf(F
,"%s\n",I
->File
.c_str());
506 SendPkgsInfo(F
, Version
);
510 // Clean up the sub process
511 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
512 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
516 signal(SIGPIPE
, old_sigpipe
);
521 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
522 // ---------------------------------------------------------------------
525 void pkgDPkgPM::DoStdin(int master
)
527 unsigned char input_buf
[256] = {0,};
528 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
530 FileFd::Write(master
, input_buf
, len
);
532 d
->stdin_is_dev_null
= true;
535 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
536 // ---------------------------------------------------------------------
538 * read the terminal pty and write log
540 void pkgDPkgPM::DoTerminalPty(int master
)
542 unsigned char term_buf
[1024] = {0,0, };
544 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
545 if(len
== -1 && errno
== EIO
)
547 // this happens when the child is about to exit, we
548 // give it time to actually exit, otherwise we run
549 // into a race so we sleep for half a second.
550 struct timespec sleepfor
= { 0, 500000000 };
551 nanosleep(&sleepfor
, NULL
);
556 FileFd::Write(1, term_buf
, len
);
558 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
561 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
562 // ---------------------------------------------------------------------
565 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
567 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
569 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
571 /* dpkg sends strings like this:
572 'status: <pkg>: <pkg qstate>'
573 'status: <pkg>:<arch>: <pkg qstate>'
575 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
576 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
579 // we need to split on ": " (note the appended space) as the ':' is
580 // part of the pkgname:arch information that dpkg sends
582 // A dpkg error message may contain additional ":" (like
583 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
584 // so we need to ensure to not split too much
585 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
589 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
593 // build the (prefix, pkgname, action) tuple, position of this
594 // is different for "processing" or "status" messages
595 std::string prefix
= APT::String::Strip(list
[0]);
599 // "processing" has the form "processing: action: pkg or trigger"
600 // with action = ["install", "upgrade", "configure", "remove", "purge",
601 // "disappear", "trigproc"]
602 if (prefix
== "processing")
604 pkgname
= APT::String::Strip(list
[2]);
605 action
= APT::String::Strip(list
[1]);
606 // we don't care for the difference (as dpkg doesn't really either)
607 if (action
== "upgrade")
610 // "status" has the form: "status: pkg: state"
611 // with state in ["half-installed", "unpacked", "half-configured",
612 // "installed", "config-files", "not-installed"]
613 else if (prefix
== "status")
615 pkgname
= APT::String::Strip(list
[1]);
616 action
= APT::String::Strip(list
[2]);
619 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
624 /* handle the special cases first:
626 errors look like this:
627 '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
628 and conffile-prompt like this
629 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
631 if (prefix
== "status")
633 if(action
== "error")
635 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
638 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
641 else if(action
== "conffile-prompt")
643 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
649 // at this point we know that we should have a valid pkgname, so build all
652 // dpkg does not always send "pkgname:arch" so we add it here if needed
653 if (pkgname
.find(":") == std::string::npos
)
655 // find the package in the group that is touched by dpkg
656 // if there are multiple pkgs dpkg would send us a full pkgname:arch
657 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
658 if (Grp
.end() == false)
660 pkgCache::PkgIterator P
= Grp
.PackageList();
661 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
663 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
665 pkgname
= P
.FullName();
672 const char* const pkg
= pkgname
.c_str();
673 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
674 std::string arch
= "";
675 if (pkgname
.find(":") != string::npos
)
676 arch
= StringSplit(pkgname
, ":")[1];
677 std::string i18n_pkgname
= pkgname
;
678 if (arch
.size() != 0)
679 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
681 // 'processing' from dpkg looks like
682 // 'processing: action: pkg'
683 if(prefix
== "processing")
685 const std::pair
<const char *, const char *> * const iter
=
686 std::find_if(PackageProcessingOpsBegin
,
687 PackageProcessingOpsEnd
,
688 MatchProcessingOp(action
.c_str()));
689 if(iter
== PackageProcessingOpsEnd
)
692 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
696 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
697 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
699 // FIXME: this needs a muliarch testcase
700 // FIXME2: is "pkgname" here reliable with dpkg only sending us
702 if (action
== "disappear")
703 handleDisappearAction(pkgname
);
707 if (prefix
== "status")
709 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
710 if(PackageOpsDone
[pkg
] < states
.size())
712 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
713 if (next_action
&& Debug
== true)
714 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
715 << " action: " << action
<< " (expected: '" << next_action
<< "' "
716 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
718 // check if the package moved to the next dpkg state
719 if(next_action
&& (action
== next_action
))
721 // only read the translation if there is actually a next action
722 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
724 // we moved from one dpkg state to a new one, report that
725 ++PackageOpsDone
[pkg
];
729 strprintf(msg
, translation
, i18n_pkgname
.c_str());
730 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
736 // DPkgPM::handleDisappearAction /*{{{*/
737 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
739 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
740 if (unlikely(Pkg
.end() == true))
743 // record the package name for display and stuff later
744 disappearedPkgs
.insert(Pkg
.FullName(true));
746 // the disappeared package was auto-installed - nothing to do
747 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
749 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
750 if (unlikely(PkgVer
.end() == true))
752 /* search in the list of dependencies for (Pre)Depends,
753 check if this dependency has a Replaces on our package
754 and if so transfer the manual installed flag to it */
755 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
757 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
758 Dep
->Type
!= pkgCache::Dep::PreDepends
)
760 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
761 if (unlikely(Tar
.end() == true))
763 // the package is already marked as manual
764 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
766 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
767 if (TarVer
.end() == true)
769 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
771 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
773 if (Pkg
!= Rep
.TargetPkg())
775 // okay, they are strongly connected - transfer manual-bit
777 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
778 Cache
[Tar
].Flags
&= ~Flag::Auto
;
784 // DPkgPM::DoDpkgStatusFd /*{{{*/
785 // ---------------------------------------------------------------------
788 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
793 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
794 d
->dpkgbuf_pos
+= len
;
798 // process line by line if we have a buffer
800 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
803 ProcessDpkgStatusLine(p
);
804 p
=q
+1; // continue with next line
807 // now move the unprocessed bits (after the final \n that is now a 0x0)
808 // to the start and update d->dpkgbuf_pos
809 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
813 // we are interessted in the first char *after* 0x0
816 // move the unprocessed tail to the start and update pos
817 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
818 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
821 // DPkgPM::WriteHistoryTag /*{{{*/
822 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
824 size_t const length
= value
.length();
827 // poor mans rstrip(", ")
828 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
829 value
.erase(length
- 2, 2);
830 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
832 // DPkgPM::OpenLog /*{{{*/
833 bool pkgDPkgPM::OpenLog()
835 string
const logdir
= _config
->FindDir("Dir::Log");
836 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
837 // FIXME: use a better string after freeze
838 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
842 time_t const t
= time(NULL
);
843 struct tm
const * const tmp
= localtime(&t
);
844 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
847 string
const logfile_name
= flCombine(logdir
,
848 _config
->Find("Dir::Log::Terminal"));
849 if (!logfile_name
.empty())
851 d
->term_out
= fopen(logfile_name
.c_str(),"a");
852 if (d
->term_out
== NULL
)
853 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
854 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
855 SetCloseExec(fileno(d
->term_out
), true);
856 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
858 struct passwd
*pw
= getpwnam("root");
859 struct group
*gr
= getgrnam("adm");
860 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
861 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
863 if (chmod(logfile_name
.c_str(), 0640) != 0)
864 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
865 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
868 // write your history
869 string
const history_name
= flCombine(logdir
,
870 _config
->Find("Dir::Log::History"));
871 if (!history_name
.empty())
873 d
->history_out
= fopen(history_name
.c_str(),"a");
874 if (d
->history_out
== NULL
)
875 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
876 SetCloseExec(fileno(d
->history_out
), true);
877 chmod(history_name
.c_str(), 0644);
878 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
879 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
880 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
882 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
884 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
885 if (Cache
[I
].NewInstall() == true)
886 HISTORYINFO(install
, CANDIDATE_AUTO
)
887 else if (Cache
[I
].ReInstall() == true)
888 HISTORYINFO(reinstall
, CANDIDATE
)
889 else if (Cache
[I
].Upgrade() == true)
890 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
891 else if (Cache
[I
].Downgrade() == true)
892 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
893 else if (Cache
[I
].Delete() == true)
894 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
898 line
->append(I
.FullName(false)).append(" (");
899 switch (infostring
) {
900 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
902 line
->append(Cache
[I
].CandVersion
);
903 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
904 line
->append(", automatic");
906 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
907 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
911 if (_config
->Exists("Commandline::AsString") == true)
912 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
913 WriteHistoryTag("Install", install
);
914 WriteHistoryTag("Reinstall", reinstall
);
915 WriteHistoryTag("Upgrade", upgrade
);
916 WriteHistoryTag("Downgrade",downgrade
);
917 WriteHistoryTag("Remove",remove
);
918 WriteHistoryTag("Purge",purge
);
919 fflush(d
->history_out
);
925 // DPkg::CloseLog /*{{{*/
926 bool pkgDPkgPM::CloseLog()
929 time_t t
= time(NULL
);
930 struct tm
*tmp
= localtime(&t
);
931 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
935 fprintf(d
->term_out
, "Log ended: ");
936 fprintf(d
->term_out
, "%s", timestr
);
937 fprintf(d
->term_out
, "\n");
944 if (disappearedPkgs
.empty() == false)
947 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
948 d
!= disappearedPkgs
.end(); ++d
)
950 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
951 disappear
.append(*d
);
953 disappear
.append(", ");
955 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
957 WriteHistoryTag("Disappeared", disappear
);
959 if (d
->dpkg_error
.empty() == false)
960 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
961 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
962 fclose(d
->history_out
);
964 d
->history_out
= NULL
;
971 // This implements a racy version of pselect for those architectures
972 // that don't have a working implementation.
973 // FIXME: Probably can be removed on Lenny+1
974 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
975 fd_set
*exceptfds
, const struct timespec
*timeout
,
976 const sigset_t
*sigmask
)
982 tv
.tv_sec
= timeout
->tv_sec
;
983 tv
.tv_usec
= timeout
->tv_nsec
/1000;
985 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
986 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
987 sigprocmask(SIG_SETMASK
, &origmask
, 0);
992 // DPkgPM::BuildPackagesProgressMap /*{{{*/
993 void pkgDPkgPM::BuildPackagesProgressMap()
995 // map the dpkg states to the operations that are performed
996 // (this is sorted in the same way as Item::Ops)
997 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1000 {"half-installed", N_("Preparing %s")},
1001 {"unpacked", N_("Unpacking %s") },
1004 // Configure operation
1006 {"unpacked",N_("Preparing to configure %s") },
1007 {"half-configured", N_("Configuring %s") },
1008 { "installed", N_("Installed %s")},
1013 {"half-configured", N_("Preparing for removal of %s")},
1014 {"half-installed", N_("Removing %s")},
1015 {"config-files", N_("Removed %s")},
1020 {"config-files", N_("Preparing to completely remove %s")},
1021 {"not-installed", N_("Completely removed %s")},
1026 // init the PackageOps map, go over the list of packages that
1027 // that will be [installed|configured|removed|purged] and add
1028 // them to the PackageOps map (the dpkg states it goes through)
1029 // and the PackageOpsTranslations (human readable strings)
1030 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1032 if((*I
).Pkg
.end() == true)
1035 string
const name
= (*I
).Pkg
.FullName();
1036 PackageOpsDone
[name
] = 0;
1037 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1039 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1043 /* one extra: We don't want the progress bar to reach 100%, especially not
1044 if we call dpkg --configure --pending and process a bunch of triggers
1045 while showing 100%. Also, spindown takes a while, so never reaching 100%
1046 is way more correct than reaching 100% while still doing stuff even if
1047 doing it this way is slightly bending the rules */
1051 bool pkgDPkgPM::Go(int StatusFd
)
1053 APT::Progress::PackageManager
*progress
= NULL
;
1055 progress
= APT::Progress::PackageManagerProgressFactory();
1057 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1059 return Go(progress
);
1062 void pkgDPkgPM::StartPtyMagic()
1064 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1067 if (d
->slave
!= NULL
)
1073 if (isatty(STDIN_FILENO
) == 0)
1074 d
->direct_stdin
= true;
1076 _error
->PushToStack();
1078 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1079 if (d
->master
== -1)
1080 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1081 else if (unlockpt(d
->master
) == -1)
1082 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1085 char const * const slave_name
= ptsname(d
->master
);
1086 if (slave_name
== NULL
)
1087 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1090 d
->slave
= strdup(slave_name
);
1091 if (d
->slave
== NULL
)
1092 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1093 else if (grantpt(d
->master
) == -1)
1094 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1095 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1097 d
->tt_is_valid
= true;
1098 struct termios raw_tt
;
1099 // copy window size of stdout if its a 'good' terminal
1100 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1103 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1104 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1105 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1106 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1108 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1109 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1113 raw_tt
.c_lflag
&= ~ECHO
;
1114 raw_tt
.c_lflag
|= ISIG
;
1115 // block SIGTTOU during tcsetattr to prevent a hang if
1116 // the process is a member of the background process group
1117 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1118 sigemptyset(&d
->sigmask
);
1119 sigaddset(&d
->sigmask
, SIGTTOU
);
1120 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1121 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1122 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1123 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1126 if (d
->slave
!= NULL
)
1128 /* on linux, closing (and later reopening) all references to the slave
1129 makes the slave a death end, so we open it here to have one open all
1130 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1131 on kfreebsd we get an incorrect ("step like") output then while it has
1132 no problem with closing all references… so to avoid platform specific
1133 code here we combine both and be happy once more */
1134 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1139 if (_error
->PendingError() == true)
1141 if (d
->master
!= -1)
1146 if (d
->slave
!= NULL
)
1151 _error
->DumpErrors(std::cerr
);
1153 _error
->RevertToStack();
1155 void pkgDPkgPM::SetupSlavePtyMagic()
1157 if(d
->master
== -1 || d
->slave
== NULL
)
1160 if (close(d
->master
) == -1)
1161 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1164 _error
->FatalE("setsid", "Starting a new session for child failed!");
1166 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1168 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1169 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1170 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1173 unsigned short i
= 0;
1174 if (d
->direct_stdin
== true)
1177 if (dup2(slaveFd
, i
) == -1)
1178 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1180 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1181 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1187 void pkgDPkgPM::StopPtyMagic()
1189 if (d
->slave
!= NULL
)
1192 if (d
->protect_slave_from_dying
!= -1)
1194 close(d
->protect_slave_from_dying
);
1195 d
->protect_slave_from_dying
= -1;
1199 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1200 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1206 // DPkgPM::Go - Run the sequence /*{{{*/
1207 // ---------------------------------------------------------------------
1208 /* This globs the operations and calls dpkg
1210 * If it is called with a progress object apt will report the install
1211 * progress to this object. It maps the dpkg states a package goes
1212 * through to human readable (and i10n-able)
1213 * names and calculates a percentage for each step.
1215 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1217 pkgPackageManager::SigINTStop
= false;
1218 d
->progress
= progress
;
1220 // Generate the base argument list for dpkg
1221 unsigned long StartSize
= 0;
1222 std::vector
<const char *> Args
;
1223 std::string DpkgExecutable
= getDpkgExecutable();
1224 Args
.push_back(DpkgExecutable
.c_str());
1225 StartSize
+= DpkgExecutable
.length();
1227 // Stick in any custom dpkg options
1228 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1232 for (; Opts
!= 0; Opts
= Opts
->Next
)
1234 if (Opts
->Value
.empty() == true)
1236 Args
.push_back(Opts
->Value
.c_str());
1237 StartSize
+= Opts
->Value
.length();
1241 size_t const BaseArgs
= Args
.size();
1242 // we need to detect if we can qualify packages with the architecture or not
1243 Args
.push_back("--assert-multi-arch");
1244 Args
.push_back(NULL
);
1246 pid_t dpkgAssertMultiArch
= ExecFork();
1247 if (dpkgAssertMultiArch
== 0)
1249 dpkgChrootDirectory();
1250 // redirect everything to the ultimate sink as we only need the exit-status
1251 int const nullfd
= open("/dev/null", O_RDONLY
);
1252 dup2(nullfd
, STDIN_FILENO
);
1253 dup2(nullfd
, STDOUT_FILENO
);
1254 dup2(nullfd
, STDERR_FILENO
);
1255 execvp(Args
[0], (char**) &Args
[0]);
1256 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1263 // FIXME: do we really need this limit when we have MaxArgBytes?
1264 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1266 // try to figure out the max environment size
1267 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1270 OSArgMax
-= EnvironmentSize() - 2*1024;
1271 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1272 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1274 if (RunScripts("DPkg::Pre-Invoke") == false)
1277 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1280 // support subpressing of triggers processing for special
1281 // cases like d-i that runs the triggers handling manually
1282 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1283 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1284 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1287 BuildPackagesProgressMap();
1289 d
->stdin_is_dev_null
= false;
1294 bool dpkgMultiArch
= false;
1295 if (dpkgAssertMultiArch
> 0)
1298 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1302 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1305 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1306 dpkgMultiArch
= true;
1309 // start pty magic before the loop
1312 // Tell the progress that its starting and fork dpkg
1313 d
->progress
->Start(d
->master
);
1315 // this loop is runs once per dpkg operation
1316 vector
<Item
>::const_iterator I
= List
.begin();
1317 while (I
!= List
.end())
1319 // Do all actions with the same Op in one run
1320 vector
<Item
>::const_iterator J
= I
;
1321 if (TriggersPending
== true)
1322 for (; J
!= List
.end(); ++J
)
1326 if (J
->Op
!= Item::TriggersPending
)
1328 vector
<Item
>::const_iterator T
= J
+ 1;
1329 if (T
!= List
.end() && T
->Op
== I
->Op
)
1334 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1337 // keep track of allocated strings for multiarch package names
1338 std::vector
<char *> Packages
;
1340 // start with the baseset of arguments
1341 unsigned long Size
= StartSize
;
1342 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1344 // Now check if we are within the MaxArgs limit
1346 // this code below is problematic, because it may happen that
1347 // the argument list is split in a way that A depends on B
1348 // and they are in the same "--configure A B" run
1349 // - with the split they may now be configured in different
1350 // runs, using Immediate-Configure-All can help prevent this.
1351 if (J
- I
> (signed)MaxArgs
)
1354 unsigned long const size
= MaxArgs
+ 10;
1356 Packages
.reserve(size
);
1360 unsigned long const size
= (J
- I
) + 10;
1362 Packages
.reserve(size
);
1367 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1369 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1370 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1372 ADDARGC("--status-fd");
1373 char status_fd_buf
[20];
1374 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1375 ADDARG(status_fd_buf
);
1376 unsigned long const Op
= I
->Op
;
1381 ADDARGC("--force-depends");
1382 ADDARGC("--force-remove-essential");
1383 ADDARGC("--remove");
1387 ADDARGC("--force-depends");
1388 ADDARGC("--force-remove-essential");
1392 case Item::Configure
:
1393 ADDARGC("--configure");
1396 case Item::ConfigurePending
:
1397 ADDARGC("--configure");
1398 ADDARGC("--pending");
1401 case Item::TriggersPending
:
1402 ADDARGC("--triggers-only");
1403 ADDARGC("--pending");
1407 ADDARGC("--unpack");
1408 ADDARGC("--auto-deconfigure");
1412 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1413 I
->Op
!= Item::ConfigurePending
)
1415 ADDARGC("--no-triggers");
1419 // Write in the file or package names
1420 if (I
->Op
== Item::Install
)
1422 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1424 if (I
->File
[0] != '/')
1425 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1426 Args
.push_back(I
->File
.c_str());
1427 Size
+= I
->File
.length();
1432 string
const nativeArch
= _config
->Find("APT::Architecture");
1433 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1434 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1436 if((*I
).Pkg
.end() == true)
1438 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1440 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1441 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1442 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1443 strcmp(I
->Pkg
.Arch(), "none") == 0))
1445 char const * const name
= I
->Pkg
.Name();
1450 pkgCache::VerIterator PkgVer
;
1451 std::string name
= I
->Pkg
.Name();
1452 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1454 PkgVer
= I
->Pkg
.CurrentVer();
1455 if(PkgVer
.end() == true)
1456 PkgVer
= FindNowVersion(I
->Pkg
);
1459 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1460 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1461 ; // never arch-qualify a package without an arch
1462 else if (PkgVer
.end() == false)
1463 name
.append(":").append(PkgVer
.Arch());
1465 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1466 char * const fullname
= strdup(name
.c_str());
1467 Packages
.push_back(fullname
);
1471 // skip configure action if all sheduled packages disappeared
1472 if (oldSize
== Size
)
1479 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1481 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1482 a
!= Args
.end(); ++a
)
1485 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1486 p
!= Packages
.end(); ++p
)
1491 Args
.push_back(NULL
);
1497 /* Mask off sig int/quit. We do this because dpkg also does when
1498 it forks scripts. What happens is that when you hit ctrl-c it sends
1499 it to all processes in the group. Since dpkg ignores the signal
1500 it doesn't die but we do! So we must also ignore it */
1501 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1502 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1504 // Check here for any SIGINT
1505 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1509 // ignore SIGHUP as well (debian #463030)
1510 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1513 d
->progress
->StartDpkg();
1514 std::set
<int> KeepFDs
;
1515 KeepFDs
.insert(fd
[1]);
1516 MergeKeepFdsFromConfiguration(KeepFDs
);
1517 pid_t Child
= ExecFork(KeepFDs
);
1520 // This is the child
1521 SetupSlavePtyMagic();
1522 close(fd
[0]); // close the read end of the pipe
1524 dpkgChrootDirectory();
1526 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1529 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1533 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1536 // Discard everything in stdin before forking dpkg
1537 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1540 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1542 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1546 execvp(Args
[0], (char**) &Args
[0]);
1547 cerr
<< "Could not exec dpkg!" << endl
;
1552 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1558 // we read from dpkg here
1559 int const _dpkgin
= fd
[0];
1560 close(fd
[1]); // close the write end of the pipe
1563 sigemptyset(&d
->sigmask
);
1564 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1566 /* free vectors (and therefore memory) as we don't need the included data anymore */
1567 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1568 p
!= Packages
.end(); ++p
)
1572 // the result of the waitpid call
1575 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1577 // FIXME: move this to a function or something, looks ugly here
1578 // error handling, waitpid returned -1
1581 RunScripts("DPkg::Post-Invoke");
1583 // Restore sig int/quit
1584 signal(SIGQUIT
,old_SIGQUIT
);
1585 signal(SIGINT
,old_SIGINT
);
1587 signal(SIGHUP
,old_SIGHUP
);
1588 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1591 // wait for input or output here
1593 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1594 FD_SET(STDIN_FILENO
, &rfds
);
1595 FD_SET(_dpkgin
, &rfds
);
1597 FD_SET(d
->master
, &rfds
);
1599 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1600 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1601 &tv
, &d
->original_sigmask
);
1602 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1603 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1604 NULL
, &tv
, &d
->original_sigmask
);
1605 d
->progress
->Pulse();
1606 if (select_ret
== 0)
1608 else if (select_ret
< 0 && errno
== EINTR
)
1610 else if (select_ret
< 0)
1612 perror("select() returned error");
1616 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1617 DoTerminalPty(d
->master
);
1618 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1620 if(FD_ISSET(_dpkgin
, &rfds
))
1621 DoDpkgStatusFd(_dpkgin
);
1625 // Restore sig int/quit
1626 signal(SIGQUIT
,old_SIGQUIT
);
1627 signal(SIGINT
,old_SIGINT
);
1629 signal(SIGHUP
,old_SIGHUP
);
1630 // Check for an error code.
1631 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1633 // if it was set to "keep-dpkg-runing" then we won't return
1634 // here but keep the loop going and just report it as a error
1636 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1638 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1639 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1640 else if (WIFEXITED(Status
) != 0)
1641 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1643 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1644 _error
->Error("%s", d
->dpkg_error
.c_str());
1650 // dpkg is done at this point
1651 d
->progress
->Stop();
1655 if (pkgPackageManager::SigINTStop
)
1656 _error
->Warning(_("Operation was interrupted before it could finish"));
1658 if (RunScripts("DPkg::Post-Invoke") == false)
1661 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1663 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1664 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1665 unlink(oldpkgcache
.c_str()) == 0)
1667 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1668 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1670 _error
->PushToStack();
1671 pkgCacheFile CacheFile
;
1672 CacheFile
.BuildCaches(NULL
, true);
1673 _error
->RevertToStack();
1678 Cache
.writeStateFile(NULL
);
1679 return d
->dpkg_error
.empty();
1682 void SigINT(int /*sig*/) {
1683 pkgPackageManager::SigINTStop
= true;
1686 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1687 // ---------------------------------------------------------------------
1689 void pkgDPkgPM::Reset()
1691 List
.erase(List
.begin(),List
.end());
1694 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1695 // ---------------------------------------------------------------------
1697 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1699 // If apport doesn't exist or isn't installed do nothing
1700 // This e.g. prevents messages in 'universes' without apport
1701 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1702 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1705 string pkgname
, reportfile
, pkgver
, arch
;
1706 string::size_type pos
;
1709 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1711 std::clog
<< "configured to not write apport reports" << std::endl
;
1715 // only report the first errors
1716 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1718 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1722 // check if its not a follow up error
1723 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1724 if(strstr(errormsg
, needle
) != NULL
) {
1725 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1729 // do not report disk-full failures
1730 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1731 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1735 // do not report out-of-memory failures
1736 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1737 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1738 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1742 // do not report bugs regarding inaccessible local files
1743 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1744 strstr(errormsg
, "cannot access archive") != NULL
) {
1745 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1749 // do not report errors encountered when decompressing packages
1750 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1751 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1755 // do not report dpkg I/O errors, this is a format string, so we compare
1756 // the prefix and the suffix of the error with the dpkg error message
1757 vector
<string
> io_errors
;
1758 io_errors
.push_back(string("failed to read"));
1759 io_errors
.push_back(string("failed to write"));
1760 io_errors
.push_back(string("failed to seek"));
1761 io_errors
.push_back(string("unexpected end of file or stream"));
1763 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1765 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1766 if (list
.size() > 1) {
1767 // we need to split %s, VectorizeString only allows char so we need
1768 // to kill the "s" manually
1769 if (list
[1].size() > 1) {
1770 list
[1].erase(0, 1);
1771 if(strstr(errormsg
, list
[0].c_str()) &&
1772 strstr(errormsg
, list
[1].c_str())) {
1773 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1780 // get the pkgname and reportfile
1781 pkgname
= flNotDir(pkgpath
);
1782 pos
= pkgname
.find('_');
1783 if(pos
!= string::npos
)
1784 pkgname
= pkgname
.substr(0, pos
);
1786 // find the package versin and source package name
1787 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1788 if (Pkg
.end() == true)
1790 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1791 if (Ver
.end() == true)
1793 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1795 // if the file exists already, we check:
1796 // - if it was reported already (touched by apport).
1797 // If not, we do nothing, otherwise
1798 // we overwrite it. This is the same behaviour as apport
1799 // - if we have a report with the same pkgversion already
1801 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1802 if(FileExists(reportfile
))
1807 // check atime/mtime
1808 stat(reportfile
.c_str(), &buf
);
1809 if(buf
.st_mtime
> buf
.st_atime
)
1812 // check if the existing report is the same version
1813 report
= fopen(reportfile
.c_str(),"r");
1814 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1816 if(strstr(strbuf
,"Package:") == strbuf
)
1818 char pkgname
[255], version
[255];
1819 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1820 if(strcmp(pkgver
.c_str(), version
) == 0)
1830 // now write the report
1831 arch
= _config
->Find("APT::Architecture");
1832 report
= fopen(reportfile
.c_str(),"w");
1835 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1836 chmod(reportfile
.c_str(), 0);
1838 chmod(reportfile
.c_str(), 0600);
1839 fprintf(report
, "ProblemType: Package\n");
1840 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1841 time_t now
= time(NULL
);
1842 fprintf(report
, "Date: %s" , ctime(&now
));
1843 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1844 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1845 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1847 // ensure that the log is flushed
1849 fflush(d
->term_out
);
1851 // attach terminal log it if we have it
1852 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1853 if (!logfile_name
.empty())
1857 fprintf(report
, "DpkgTerminalLog:\n");
1858 log
= fopen(logfile_name
.c_str(),"r");
1862 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1863 fprintf(report
, " %s", buf
);
1864 fprintf(report
, " \n");
1869 // attach history log it if we have it
1870 string histfile_name
= _config
->FindFile("Dir::Log::History");
1871 if (!histfile_name
.empty())
1873 fprintf(report
, "DpkgHistoryLog:\n");
1874 FILE* log
= fopen(histfile_name
.c_str(),"r");
1878 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1879 fprintf(report
, " %s", buf
);
1884 // log the ordering, see dpkgpm.h and the "Ops" enum there
1885 const char *ops_str
[] = {
1893 fprintf(report
, "AptOrdering:\n");
1894 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1895 if ((*I
).Pkg
!= NULL
)
1896 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1898 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1900 // attach dmesg log (to learn about segfaults)
1901 if (FileExists("/bin/dmesg"))
1903 fprintf(report
, "Dmesg:\n");
1904 FILE *log
= popen("/bin/dmesg","r");
1908 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1909 fprintf(report
, " %s", buf
);
1914 // attach df -l log (to learn about filesystem status)
1915 if (FileExists("/bin/df"))
1918 fprintf(report
, "Df:\n");
1919 FILE *log
= popen("/bin/df -l","r");
1923 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1924 fprintf(report
, " %s", buf
);