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
);
844 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
845 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
848 string
const logfile_name
= flCombine(logdir
,
849 _config
->Find("Dir::Log::Terminal"));
850 if (!logfile_name
.empty())
852 d
->term_out
= fopen(logfile_name
.c_str(),"a");
853 if (d
->term_out
== NULL
)
854 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
855 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
856 SetCloseExec(fileno(d
->term_out
), true);
857 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
859 struct passwd
*pw
= getpwnam("root");
860 struct group
*gr
= getgrnam("adm");
861 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
862 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
864 if (chmod(logfile_name
.c_str(), 0640) != 0)
865 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
866 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
869 // write your history
870 string
const history_name
= flCombine(logdir
,
871 _config
->Find("Dir::Log::History"));
872 if (!history_name
.empty())
874 d
->history_out
= fopen(history_name
.c_str(),"a");
875 if (d
->history_out
== NULL
)
876 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
877 SetCloseExec(fileno(d
->history_out
), true);
878 chmod(history_name
.c_str(), 0644);
879 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
880 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
881 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
883 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
885 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
886 if (Cache
[I
].NewInstall() == true)
887 HISTORYINFO(install
, CANDIDATE_AUTO
)
888 else if (Cache
[I
].ReInstall() == true)
889 HISTORYINFO(reinstall
, CANDIDATE
)
890 else if (Cache
[I
].Upgrade() == true)
891 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
892 else if (Cache
[I
].Downgrade() == true)
893 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
894 else if (Cache
[I
].Delete() == true)
895 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
899 line
->append(I
.FullName(false)).append(" (");
900 switch (infostring
) {
901 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
903 line
->append(Cache
[I
].CandVersion
);
904 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
905 line
->append(", automatic");
907 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
908 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
912 if (_config
->Exists("Commandline::AsString") == true)
913 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
914 WriteHistoryTag("Install", install
);
915 WriteHistoryTag("Reinstall", reinstall
);
916 WriteHistoryTag("Upgrade", upgrade
);
917 WriteHistoryTag("Downgrade",downgrade
);
918 WriteHistoryTag("Remove",remove
);
919 WriteHistoryTag("Purge",purge
);
920 fflush(d
->history_out
);
926 // DPkg::CloseLog /*{{{*/
927 bool pkgDPkgPM::CloseLog()
930 time_t t
= time(NULL
);
932 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
933 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
937 fprintf(d
->term_out
, "Log ended: ");
938 fprintf(d
->term_out
, "%s", timestr
);
939 fprintf(d
->term_out
, "\n");
946 if (disappearedPkgs
.empty() == false)
949 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
950 d
!= disappearedPkgs
.end(); ++d
)
952 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
953 disappear
.append(*d
);
955 disappear
.append(", ");
957 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
959 WriteHistoryTag("Disappeared", disappear
);
961 if (d
->dpkg_error
.empty() == false)
962 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
963 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
964 fclose(d
->history_out
);
966 d
->history_out
= NULL
;
973 // This implements a racy version of pselect for those architectures
974 // that don't have a working implementation.
975 // FIXME: Probably can be removed on Lenny+1
976 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
977 fd_set
*exceptfds
, const struct timespec
*timeout
,
978 const sigset_t
*sigmask
)
984 tv
.tv_sec
= timeout
->tv_sec
;
985 tv
.tv_usec
= timeout
->tv_nsec
/1000;
987 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
988 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
989 sigprocmask(SIG_SETMASK
, &origmask
, 0);
994 // DPkgPM::BuildPackagesProgressMap /*{{{*/
995 void pkgDPkgPM::BuildPackagesProgressMap()
997 // map the dpkg states to the operations that are performed
998 // (this is sorted in the same way as Item::Ops)
999 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1000 // Install operation
1002 {"half-installed", N_("Preparing %s")},
1003 {"unpacked", N_("Unpacking %s") },
1006 // Configure operation
1008 {"unpacked",N_("Preparing to configure %s") },
1009 {"half-configured", N_("Configuring %s") },
1010 { "installed", N_("Installed %s")},
1015 {"half-configured", N_("Preparing for removal of %s")},
1016 {"half-installed", N_("Removing %s")},
1017 {"config-files", N_("Removed %s")},
1022 {"config-files", N_("Preparing to completely remove %s")},
1023 {"not-installed", N_("Completely removed %s")},
1028 // init the PackageOps map, go over the list of packages that
1029 // that will be [installed|configured|removed|purged] and add
1030 // them to the PackageOps map (the dpkg states it goes through)
1031 // and the PackageOpsTranslations (human readable strings)
1032 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1034 if((*I
).Pkg
.end() == true)
1037 string
const name
= (*I
).Pkg
.FullName();
1038 PackageOpsDone
[name
] = 0;
1039 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1041 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1045 /* one extra: We don't want the progress bar to reach 100%, especially not
1046 if we call dpkg --configure --pending and process a bunch of triggers
1047 while showing 100%. Also, spindown takes a while, so never reaching 100%
1048 is way more correct than reaching 100% while still doing stuff even if
1049 doing it this way is slightly bending the rules */
1053 bool pkgDPkgPM::Go(int StatusFd
)
1055 APT::Progress::PackageManager
*progress
= NULL
;
1057 progress
= APT::Progress::PackageManagerProgressFactory();
1059 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1061 return Go(progress
);
1064 void pkgDPkgPM::StartPtyMagic()
1066 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1069 if (d
->slave
!= NULL
)
1075 if (isatty(STDIN_FILENO
) == 0)
1076 d
->direct_stdin
= true;
1078 _error
->PushToStack();
1080 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1081 if (d
->master
== -1)
1082 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1083 else if (unlockpt(d
->master
) == -1)
1084 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1087 #ifdef HAVE_PTS_NAME_R
1088 char slave_name
[64]; // 64 is used by bionic
1089 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1091 char const * const slave_name
= ptsname(d
->master
);
1092 if (slave_name
== NULL
)
1094 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1097 d
->slave
= strdup(slave_name
);
1098 if (d
->slave
== NULL
)
1099 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1100 else if (grantpt(d
->master
) == -1)
1101 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1102 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1104 d
->tt_is_valid
= true;
1105 struct termios raw_tt
;
1106 // copy window size of stdout if its a 'good' terminal
1107 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1110 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1111 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1112 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1113 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1115 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1116 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1120 raw_tt
.c_lflag
&= ~ECHO
;
1121 raw_tt
.c_lflag
|= ISIG
;
1122 // block SIGTTOU during tcsetattr to prevent a hang if
1123 // the process is a member of the background process group
1124 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1125 sigemptyset(&d
->sigmask
);
1126 sigaddset(&d
->sigmask
, SIGTTOU
);
1127 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1128 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1129 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1130 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1133 if (d
->slave
!= NULL
)
1135 /* on linux, closing (and later reopening) all references to the slave
1136 makes the slave a death end, so we open it here to have one open all
1137 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1138 on kfreebsd we get an incorrect ("step like") output then while it has
1139 no problem with closing all references… so to avoid platform specific
1140 code here we combine both and be happy once more */
1141 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1146 if (_error
->PendingError() == true)
1148 if (d
->master
!= -1)
1153 if (d
->slave
!= NULL
)
1158 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1160 _error
->RevertToStack();
1162 void pkgDPkgPM::SetupSlavePtyMagic()
1164 if(d
->master
== -1 || d
->slave
== NULL
)
1167 if (close(d
->master
) == -1)
1168 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1171 _error
->FatalE("setsid", "Starting a new session for child failed!");
1173 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1175 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1176 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1177 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1180 unsigned short i
= 0;
1181 if (d
->direct_stdin
== true)
1184 if (dup2(slaveFd
, i
) == -1)
1185 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1187 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1188 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1194 void pkgDPkgPM::StopPtyMagic()
1196 if (d
->slave
!= NULL
)
1199 if (d
->protect_slave_from_dying
!= -1)
1201 close(d
->protect_slave_from_dying
);
1202 d
->protect_slave_from_dying
= -1;
1206 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1207 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1213 // DPkgPM::Go - Run the sequence /*{{{*/
1214 // ---------------------------------------------------------------------
1215 /* This globs the operations and calls dpkg
1217 * If it is called with a progress object apt will report the install
1218 * progress to this object. It maps the dpkg states a package goes
1219 * through to human readable (and i10n-able)
1220 * names and calculates a percentage for each step.
1222 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1224 pkgPackageManager::SigINTStop
= false;
1225 d
->progress
= progress
;
1227 // Generate the base argument list for dpkg
1228 unsigned long StartSize
= 0;
1229 std::vector
<const char *> Args
;
1230 std::string DpkgExecutable
= getDpkgExecutable();
1231 Args
.push_back(DpkgExecutable
.c_str());
1232 StartSize
+= DpkgExecutable
.length();
1234 // Stick in any custom dpkg options
1235 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1239 for (; Opts
!= 0; Opts
= Opts
->Next
)
1241 if (Opts
->Value
.empty() == true)
1243 Args
.push_back(Opts
->Value
.c_str());
1244 StartSize
+= Opts
->Value
.length();
1248 size_t const BaseArgs
= Args
.size();
1249 // we need to detect if we can qualify packages with the architecture or not
1250 Args
.push_back("--assert-multi-arch");
1251 Args
.push_back(NULL
);
1253 pid_t dpkgAssertMultiArch
= ExecFork();
1254 if (dpkgAssertMultiArch
== 0)
1256 dpkgChrootDirectory();
1257 // redirect everything to the ultimate sink as we only need the exit-status
1258 int const nullfd
= open("/dev/null", O_RDONLY
);
1259 dup2(nullfd
, STDIN_FILENO
);
1260 dup2(nullfd
, STDOUT_FILENO
);
1261 dup2(nullfd
, STDERR_FILENO
);
1262 execvp(Args
[0], (char**) &Args
[0]);
1263 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1270 // FIXME: do we really need this limit when we have MaxArgBytes?
1271 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1273 // try to figure out the max environment size
1274 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1277 OSArgMax
-= EnvironmentSize() - 2*1024;
1278 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1279 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1281 if (RunScripts("DPkg::Pre-Invoke") == false)
1284 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1287 // support subpressing of triggers processing for special
1288 // cases like d-i that runs the triggers handling manually
1289 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1290 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1291 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1294 BuildPackagesProgressMap();
1296 d
->stdin_is_dev_null
= false;
1301 bool dpkgMultiArch
= false;
1302 if (dpkgAssertMultiArch
> 0)
1305 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1309 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1312 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1313 dpkgMultiArch
= true;
1316 // start pty magic before the loop
1319 // Tell the progress that its starting and fork dpkg
1320 d
->progress
->Start(d
->master
);
1322 // this loop is runs once per dpkg operation
1323 vector
<Item
>::const_iterator I
= List
.begin();
1324 while (I
!= List
.end())
1326 // Do all actions with the same Op in one run
1327 vector
<Item
>::const_iterator J
= I
;
1328 if (TriggersPending
== true)
1329 for (; J
!= List
.end(); ++J
)
1333 if (J
->Op
!= Item::TriggersPending
)
1335 vector
<Item
>::const_iterator T
= J
+ 1;
1336 if (T
!= List
.end() && T
->Op
== I
->Op
)
1341 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1344 // keep track of allocated strings for multiarch package names
1345 std::vector
<char *> Packages
;
1347 // start with the baseset of arguments
1348 unsigned long Size
= StartSize
;
1349 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1351 // Now check if we are within the MaxArgs limit
1353 // this code below is problematic, because it may happen that
1354 // the argument list is split in a way that A depends on B
1355 // and they are in the same "--configure A B" run
1356 // - with the split they may now be configured in different
1357 // runs, using Immediate-Configure-All can help prevent this.
1358 if (J
- I
> (signed)MaxArgs
)
1361 unsigned long const size
= MaxArgs
+ 10;
1363 Packages
.reserve(size
);
1367 unsigned long const size
= (J
- I
) + 10;
1369 Packages
.reserve(size
);
1374 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1376 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1377 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1379 ADDARGC("--status-fd");
1380 char status_fd_buf
[20];
1381 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1382 ADDARG(status_fd_buf
);
1383 unsigned long const Op
= I
->Op
;
1388 ADDARGC("--force-depends");
1389 ADDARGC("--force-remove-essential");
1390 ADDARGC("--remove");
1394 ADDARGC("--force-depends");
1395 ADDARGC("--force-remove-essential");
1399 case Item::Configure
:
1400 ADDARGC("--configure");
1403 case Item::ConfigurePending
:
1404 ADDARGC("--configure");
1405 ADDARGC("--pending");
1408 case Item::TriggersPending
:
1409 ADDARGC("--triggers-only");
1410 ADDARGC("--pending");
1414 ADDARGC("--unpack");
1415 ADDARGC("--auto-deconfigure");
1419 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1420 I
->Op
!= Item::ConfigurePending
)
1422 ADDARGC("--no-triggers");
1426 // Write in the file or package names
1427 if (I
->Op
== Item::Install
)
1429 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1431 if (I
->File
[0] != '/')
1432 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1433 Args
.push_back(I
->File
.c_str());
1434 Size
+= I
->File
.length();
1439 string
const nativeArch
= _config
->Find("APT::Architecture");
1440 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1441 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1443 if((*I
).Pkg
.end() == true)
1445 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1447 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1448 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1449 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1450 strcmp(I
->Pkg
.Arch(), "none") == 0))
1452 char const * const name
= I
->Pkg
.Name();
1457 pkgCache::VerIterator PkgVer
;
1458 std::string name
= I
->Pkg
.Name();
1459 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1461 PkgVer
= I
->Pkg
.CurrentVer();
1462 if(PkgVer
.end() == true)
1463 PkgVer
= FindNowVersion(I
->Pkg
);
1466 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1467 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1468 ; // never arch-qualify a package without an arch
1469 else if (PkgVer
.end() == false)
1470 name
.append(":").append(PkgVer
.Arch());
1472 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1473 char * const fullname
= strdup(name
.c_str());
1474 Packages
.push_back(fullname
);
1478 // skip configure action if all sheduled packages disappeared
1479 if (oldSize
== Size
)
1486 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1488 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1489 a
!= Args
.end(); ++a
)
1492 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1493 p
!= Packages
.end(); ++p
)
1498 Args
.push_back(NULL
);
1504 /* Mask off sig int/quit. We do this because dpkg also does when
1505 it forks scripts. What happens is that when you hit ctrl-c it sends
1506 it to all processes in the group. Since dpkg ignores the signal
1507 it doesn't die but we do! So we must also ignore it */
1508 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1509 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1511 // Check here for any SIGINT
1512 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1516 // ignore SIGHUP as well (debian #463030)
1517 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1520 d
->progress
->StartDpkg();
1521 std::set
<int> KeepFDs
;
1522 KeepFDs
.insert(fd
[1]);
1523 MergeKeepFdsFromConfiguration(KeepFDs
);
1524 pid_t Child
= ExecFork(KeepFDs
);
1527 // This is the child
1528 SetupSlavePtyMagic();
1529 close(fd
[0]); // close the read end of the pipe
1531 dpkgChrootDirectory();
1533 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1536 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1540 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1543 // Discard everything in stdin before forking dpkg
1544 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1547 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1549 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1553 execvp(Args
[0], (char**) &Args
[0]);
1554 cerr
<< "Could not exec dpkg!" << endl
;
1559 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1565 // we read from dpkg here
1566 int const _dpkgin
= fd
[0];
1567 close(fd
[1]); // close the write end of the pipe
1570 sigemptyset(&d
->sigmask
);
1571 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1573 /* free vectors (and therefore memory) as we don't need the included data anymore */
1574 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1575 p
!= Packages
.end(); ++p
)
1579 // the result of the waitpid call
1582 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1584 // FIXME: move this to a function or something, looks ugly here
1585 // error handling, waitpid returned -1
1588 RunScripts("DPkg::Post-Invoke");
1590 // Restore sig int/quit
1591 signal(SIGQUIT
,old_SIGQUIT
);
1592 signal(SIGINT
,old_SIGINT
);
1594 signal(SIGHUP
,old_SIGHUP
);
1595 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1598 // wait for input or output here
1600 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1601 FD_SET(STDIN_FILENO
, &rfds
);
1602 FD_SET(_dpkgin
, &rfds
);
1604 FD_SET(d
->master
, &rfds
);
1606 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1607 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1608 &tv
, &d
->original_sigmask
);
1609 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1610 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1611 NULL
, &tv
, &d
->original_sigmask
);
1612 d
->progress
->Pulse();
1613 if (select_ret
== 0)
1615 else if (select_ret
< 0 && errno
== EINTR
)
1617 else if (select_ret
< 0)
1619 perror("select() returned error");
1623 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1624 DoTerminalPty(d
->master
);
1625 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1627 if(FD_ISSET(_dpkgin
, &rfds
))
1628 DoDpkgStatusFd(_dpkgin
);
1632 // Restore sig int/quit
1633 signal(SIGQUIT
,old_SIGQUIT
);
1634 signal(SIGINT
,old_SIGINT
);
1636 signal(SIGHUP
,old_SIGHUP
);
1637 // Check for an error code.
1638 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1640 // if it was set to "keep-dpkg-runing" then we won't return
1641 // here but keep the loop going and just report it as a error
1643 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1645 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1646 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1647 else if (WIFEXITED(Status
) != 0)
1648 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1650 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1651 _error
->Error("%s", d
->dpkg_error
.c_str());
1657 // dpkg is done at this point
1658 d
->progress
->Stop();
1662 if (pkgPackageManager::SigINTStop
)
1663 _error
->Warning(_("Operation was interrupted before it could finish"));
1665 if (RunScripts("DPkg::Post-Invoke") == false)
1668 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1670 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1671 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1672 unlink(oldpkgcache
.c_str()) == 0)
1674 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1675 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1677 _error
->PushToStack();
1678 pkgCacheFile CacheFile
;
1679 CacheFile
.BuildCaches(NULL
, true);
1680 _error
->RevertToStack();
1685 Cache
.writeStateFile(NULL
);
1686 return d
->dpkg_error
.empty();
1689 void SigINT(int /*sig*/) {
1690 pkgPackageManager::SigINTStop
= true;
1693 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1694 // ---------------------------------------------------------------------
1696 void pkgDPkgPM::Reset()
1698 List
.erase(List
.begin(),List
.end());
1701 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1702 // ---------------------------------------------------------------------
1704 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1706 // If apport doesn't exist or isn't installed do nothing
1707 // This e.g. prevents messages in 'universes' without apport
1708 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1709 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1712 string pkgname
, reportfile
, pkgver
, arch
;
1713 string::size_type pos
;
1716 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1718 std::clog
<< "configured to not write apport reports" << std::endl
;
1722 // only report the first errors
1723 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1725 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1729 // check if its not a follow up error
1730 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1731 if(strstr(errormsg
, needle
) != NULL
) {
1732 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1736 // do not report disk-full failures
1737 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1738 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1742 // do not report out-of-memory failures
1743 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1744 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1745 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1749 // do not report bugs regarding inaccessible local files
1750 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1751 strstr(errormsg
, "cannot access archive") != NULL
) {
1752 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1756 // do not report errors encountered when decompressing packages
1757 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1758 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1762 // do not report dpkg I/O errors, this is a format string, so we compare
1763 // the prefix and the suffix of the error with the dpkg error message
1764 vector
<string
> io_errors
;
1765 io_errors
.push_back(string("failed to read"));
1766 io_errors
.push_back(string("failed to write"));
1767 io_errors
.push_back(string("failed to seek"));
1768 io_errors
.push_back(string("unexpected end of file or stream"));
1770 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1772 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1773 if (list
.size() > 1) {
1774 // we need to split %s, VectorizeString only allows char so we need
1775 // to kill the "s" manually
1776 if (list
[1].size() > 1) {
1777 list
[1].erase(0, 1);
1778 if(strstr(errormsg
, list
[0].c_str()) &&
1779 strstr(errormsg
, list
[1].c_str())) {
1780 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1787 // get the pkgname and reportfile
1788 pkgname
= flNotDir(pkgpath
);
1789 pos
= pkgname
.find('_');
1790 if(pos
!= string::npos
)
1791 pkgname
= pkgname
.substr(0, pos
);
1793 // find the package versin and source package name
1794 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1795 if (Pkg
.end() == true)
1797 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1798 if (Ver
.end() == true)
1800 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1802 // if the file exists already, we check:
1803 // - if it was reported already (touched by apport).
1804 // If not, we do nothing, otherwise
1805 // we overwrite it. This is the same behaviour as apport
1806 // - if we have a report with the same pkgversion already
1808 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1809 if(FileExists(reportfile
))
1814 // check atime/mtime
1815 stat(reportfile
.c_str(), &buf
);
1816 if(buf
.st_mtime
> buf
.st_atime
)
1819 // check if the existing report is the same version
1820 report
= fopen(reportfile
.c_str(),"r");
1821 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1823 if(strstr(strbuf
,"Package:") == strbuf
)
1825 char pkgname
[255], version
[255];
1826 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1827 if(strcmp(pkgver
.c_str(), version
) == 0)
1837 // now write the report
1838 arch
= _config
->Find("APT::Architecture");
1839 report
= fopen(reportfile
.c_str(),"w");
1842 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1843 chmod(reportfile
.c_str(), 0);
1845 chmod(reportfile
.c_str(), 0600);
1846 fprintf(report
, "ProblemType: Package\n");
1847 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1848 time_t now
= time(NULL
);
1849 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1850 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1851 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1852 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1853 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1855 // ensure that the log is flushed
1857 fflush(d
->term_out
);
1859 // attach terminal log it if we have it
1860 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1861 if (!logfile_name
.empty())
1865 fprintf(report
, "DpkgTerminalLog:\n");
1866 log
= fopen(logfile_name
.c_str(),"r");
1870 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1871 fprintf(report
, " %s", buf
);
1872 fprintf(report
, " \n");
1877 // attach history log it if we have it
1878 string histfile_name
= _config
->FindFile("Dir::Log::History");
1879 if (!histfile_name
.empty())
1881 fprintf(report
, "DpkgHistoryLog:\n");
1882 FILE* log
= fopen(histfile_name
.c_str(),"r");
1886 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1887 fprintf(report
, " %s", buf
);
1892 // log the ordering, see dpkgpm.h and the "Ops" enum there
1893 const char *ops_str
[] = {
1901 fprintf(report
, "AptOrdering:\n");
1902 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1903 if ((*I
).Pkg
!= NULL
)
1904 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1906 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1908 // attach dmesg log (to learn about segfaults)
1909 if (FileExists("/bin/dmesg"))
1911 fprintf(report
, "Dmesg:\n");
1912 FILE *log
= popen("/bin/dmesg","r");
1916 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1917 fprintf(report
, " %s", buf
);
1922 // attach df -l log (to learn about filesystem status)
1923 if (FileExists("/bin/df"))
1926 fprintf(report
, "Df:\n");
1927 FILE *log
= popen("/bin/df -l","r");
1931 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1932 fprintf(report
, " %s", buf
);