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 #ifdef HAVE_PTS_NAME_R
1086 char slave_name
[64]; // 64 is used by bionic
1087 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1089 char const * const slave_name
= ptsname(d
->master
);
1090 if (slave_name
== NULL
)
1092 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1095 d
->slave
= strdup(slave_name
);
1096 if (d
->slave
== NULL
)
1097 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1098 else if (grantpt(d
->master
) == -1)
1099 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1100 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1102 d
->tt_is_valid
= true;
1103 struct termios raw_tt
;
1104 // copy window size of stdout if its a 'good' terminal
1105 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1108 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1109 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1110 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1111 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1113 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1114 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1118 raw_tt
.c_lflag
&= ~ECHO
;
1119 raw_tt
.c_lflag
|= ISIG
;
1120 // block SIGTTOU during tcsetattr to prevent a hang if
1121 // the process is a member of the background process group
1122 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1123 sigemptyset(&d
->sigmask
);
1124 sigaddset(&d
->sigmask
, SIGTTOU
);
1125 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1126 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1127 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1128 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1131 if (d
->slave
!= NULL
)
1133 /* on linux, closing (and later reopening) all references to the slave
1134 makes the slave a death end, so we open it here to have one open all
1135 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1136 on kfreebsd we get an incorrect ("step like") output then while it has
1137 no problem with closing all references… so to avoid platform specific
1138 code here we combine both and be happy once more */
1139 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1144 if (_error
->PendingError() == true)
1146 if (d
->master
!= -1)
1151 if (d
->slave
!= NULL
)
1156 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1158 _error
->RevertToStack();
1160 void pkgDPkgPM::SetupSlavePtyMagic()
1162 if(d
->master
== -1 || d
->slave
== NULL
)
1165 if (close(d
->master
) == -1)
1166 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1169 _error
->FatalE("setsid", "Starting a new session for child failed!");
1171 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1173 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1174 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1175 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1178 unsigned short i
= 0;
1179 if (d
->direct_stdin
== true)
1182 if (dup2(slaveFd
, i
) == -1)
1183 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1185 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1186 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1192 void pkgDPkgPM::StopPtyMagic()
1194 if (d
->slave
!= NULL
)
1197 if (d
->protect_slave_from_dying
!= -1)
1199 close(d
->protect_slave_from_dying
);
1200 d
->protect_slave_from_dying
= -1;
1204 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1205 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1211 // DPkgPM::Go - Run the sequence /*{{{*/
1212 // ---------------------------------------------------------------------
1213 /* This globs the operations and calls dpkg
1215 * If it is called with a progress object apt will report the install
1216 * progress to this object. It maps the dpkg states a package goes
1217 * through to human readable (and i10n-able)
1218 * names and calculates a percentage for each step.
1220 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1222 pkgPackageManager::SigINTStop
= false;
1223 d
->progress
= progress
;
1225 // Generate the base argument list for dpkg
1226 unsigned long StartSize
= 0;
1227 std::vector
<const char *> Args
;
1228 std::string DpkgExecutable
= getDpkgExecutable();
1229 Args
.push_back(DpkgExecutable
.c_str());
1230 StartSize
+= DpkgExecutable
.length();
1232 // Stick in any custom dpkg options
1233 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1237 for (; Opts
!= 0; Opts
= Opts
->Next
)
1239 if (Opts
->Value
.empty() == true)
1241 Args
.push_back(Opts
->Value
.c_str());
1242 StartSize
+= Opts
->Value
.length();
1246 size_t const BaseArgs
= Args
.size();
1247 // we need to detect if we can qualify packages with the architecture or not
1248 Args
.push_back("--assert-multi-arch");
1249 Args
.push_back(NULL
);
1251 pid_t dpkgAssertMultiArch
= ExecFork();
1252 if (dpkgAssertMultiArch
== 0)
1254 dpkgChrootDirectory();
1255 // redirect everything to the ultimate sink as we only need the exit-status
1256 int const nullfd
= open("/dev/null", O_RDONLY
);
1257 dup2(nullfd
, STDIN_FILENO
);
1258 dup2(nullfd
, STDOUT_FILENO
);
1259 dup2(nullfd
, STDERR_FILENO
);
1260 execvp(Args
[0], (char**) &Args
[0]);
1261 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1268 // FIXME: do we really need this limit when we have MaxArgBytes?
1269 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1271 // try to figure out the max environment size
1272 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1275 OSArgMax
-= EnvironmentSize() - 2*1024;
1276 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1277 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1279 if (RunScripts("DPkg::Pre-Invoke") == false)
1282 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1285 // support subpressing of triggers processing for special
1286 // cases like d-i that runs the triggers handling manually
1287 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1288 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1289 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1292 BuildPackagesProgressMap();
1294 d
->stdin_is_dev_null
= false;
1299 bool dpkgMultiArch
= false;
1300 if (dpkgAssertMultiArch
> 0)
1303 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1307 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1310 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1311 dpkgMultiArch
= true;
1314 // start pty magic before the loop
1317 // Tell the progress that its starting and fork dpkg
1318 d
->progress
->Start(d
->master
);
1320 // this loop is runs once per dpkg operation
1321 vector
<Item
>::const_iterator I
= List
.begin();
1322 while (I
!= List
.end())
1324 // Do all actions with the same Op in one run
1325 vector
<Item
>::const_iterator J
= I
;
1326 if (TriggersPending
== true)
1327 for (; J
!= List
.end(); ++J
)
1331 if (J
->Op
!= Item::TriggersPending
)
1333 vector
<Item
>::const_iterator T
= J
+ 1;
1334 if (T
!= List
.end() && T
->Op
== I
->Op
)
1339 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1342 // keep track of allocated strings for multiarch package names
1343 std::vector
<char *> Packages
;
1345 // start with the baseset of arguments
1346 unsigned long Size
= StartSize
;
1347 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1349 // Now check if we are within the MaxArgs limit
1351 // this code below is problematic, because it may happen that
1352 // the argument list is split in a way that A depends on B
1353 // and they are in the same "--configure A B" run
1354 // - with the split they may now be configured in different
1355 // runs, using Immediate-Configure-All can help prevent this.
1356 if (J
- I
> (signed)MaxArgs
)
1359 unsigned long const size
= MaxArgs
+ 10;
1361 Packages
.reserve(size
);
1365 unsigned long const size
= (J
- I
) + 10;
1367 Packages
.reserve(size
);
1372 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1374 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1375 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1377 ADDARGC("--status-fd");
1378 char status_fd_buf
[20];
1379 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1380 ADDARG(status_fd_buf
);
1381 unsigned long const Op
= I
->Op
;
1386 ADDARGC("--force-depends");
1387 ADDARGC("--force-remove-essential");
1388 ADDARGC("--remove");
1392 ADDARGC("--force-depends");
1393 ADDARGC("--force-remove-essential");
1397 case Item::Configure
:
1398 ADDARGC("--configure");
1401 case Item::ConfigurePending
:
1402 ADDARGC("--configure");
1403 ADDARGC("--pending");
1406 case Item::TriggersPending
:
1407 ADDARGC("--triggers-only");
1408 ADDARGC("--pending");
1412 ADDARGC("--unpack");
1413 ADDARGC("--auto-deconfigure");
1417 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1418 I
->Op
!= Item::ConfigurePending
)
1420 ADDARGC("--no-triggers");
1424 // Write in the file or package names
1425 if (I
->Op
== Item::Install
)
1427 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1429 if (I
->File
[0] != '/')
1430 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1431 Args
.push_back(I
->File
.c_str());
1432 Size
+= I
->File
.length();
1437 string
const nativeArch
= _config
->Find("APT::Architecture");
1438 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1439 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1441 if((*I
).Pkg
.end() == true)
1443 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1445 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1446 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1447 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1448 strcmp(I
->Pkg
.Arch(), "none") == 0))
1450 char const * const name
= I
->Pkg
.Name();
1455 pkgCache::VerIterator PkgVer
;
1456 std::string name
= I
->Pkg
.Name();
1457 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1459 PkgVer
= I
->Pkg
.CurrentVer();
1460 if(PkgVer
.end() == true)
1461 PkgVer
= FindNowVersion(I
->Pkg
);
1464 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1465 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1466 ; // never arch-qualify a package without an arch
1467 else if (PkgVer
.end() == false)
1468 name
.append(":").append(PkgVer
.Arch());
1470 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1471 char * const fullname
= strdup(name
.c_str());
1472 Packages
.push_back(fullname
);
1476 // skip configure action if all sheduled packages disappeared
1477 if (oldSize
== Size
)
1484 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1486 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1487 a
!= Args
.end(); ++a
)
1490 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1491 p
!= Packages
.end(); ++p
)
1496 Args
.push_back(NULL
);
1502 /* Mask off sig int/quit. We do this because dpkg also does when
1503 it forks scripts. What happens is that when you hit ctrl-c it sends
1504 it to all processes in the group. Since dpkg ignores the signal
1505 it doesn't die but we do! So we must also ignore it */
1506 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1507 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1509 // Check here for any SIGINT
1510 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1514 // ignore SIGHUP as well (debian #463030)
1515 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1518 d
->progress
->StartDpkg();
1519 std::set
<int> KeepFDs
;
1520 KeepFDs
.insert(fd
[1]);
1521 MergeKeepFdsFromConfiguration(KeepFDs
);
1522 pid_t Child
= ExecFork(KeepFDs
);
1525 // This is the child
1526 SetupSlavePtyMagic();
1527 close(fd
[0]); // close the read end of the pipe
1529 dpkgChrootDirectory();
1531 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1534 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1538 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1541 // Discard everything in stdin before forking dpkg
1542 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1545 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1547 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1551 execvp(Args
[0], (char**) &Args
[0]);
1552 cerr
<< "Could not exec dpkg!" << endl
;
1557 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1563 // we read from dpkg here
1564 int const _dpkgin
= fd
[0];
1565 close(fd
[1]); // close the write end of the pipe
1568 sigemptyset(&d
->sigmask
);
1569 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1571 /* free vectors (and therefore memory) as we don't need the included data anymore */
1572 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1573 p
!= Packages
.end(); ++p
)
1577 // the result of the waitpid call
1580 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1582 // FIXME: move this to a function or something, looks ugly here
1583 // error handling, waitpid returned -1
1586 RunScripts("DPkg::Post-Invoke");
1588 // Restore sig int/quit
1589 signal(SIGQUIT
,old_SIGQUIT
);
1590 signal(SIGINT
,old_SIGINT
);
1592 signal(SIGHUP
,old_SIGHUP
);
1593 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1596 // wait for input or output here
1598 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1599 FD_SET(STDIN_FILENO
, &rfds
);
1600 FD_SET(_dpkgin
, &rfds
);
1602 FD_SET(d
->master
, &rfds
);
1604 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1605 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1606 &tv
, &d
->original_sigmask
);
1607 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1608 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1609 NULL
, &tv
, &d
->original_sigmask
);
1610 d
->progress
->Pulse();
1611 if (select_ret
== 0)
1613 else if (select_ret
< 0 && errno
== EINTR
)
1615 else if (select_ret
< 0)
1617 perror("select() returned error");
1621 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1622 DoTerminalPty(d
->master
);
1623 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1625 if(FD_ISSET(_dpkgin
, &rfds
))
1626 DoDpkgStatusFd(_dpkgin
);
1630 // Restore sig int/quit
1631 signal(SIGQUIT
,old_SIGQUIT
);
1632 signal(SIGINT
,old_SIGINT
);
1634 signal(SIGHUP
,old_SIGHUP
);
1635 // Check for an error code.
1636 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1638 // if it was set to "keep-dpkg-runing" then we won't return
1639 // here but keep the loop going and just report it as a error
1641 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1643 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1644 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1645 else if (WIFEXITED(Status
) != 0)
1646 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1648 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1649 _error
->Error("%s", d
->dpkg_error
.c_str());
1655 // dpkg is done at this point
1656 d
->progress
->Stop();
1660 if (pkgPackageManager::SigINTStop
)
1661 _error
->Warning(_("Operation was interrupted before it could finish"));
1663 if (RunScripts("DPkg::Post-Invoke") == false)
1666 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1668 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1669 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1670 unlink(oldpkgcache
.c_str()) == 0)
1672 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1673 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1675 _error
->PushToStack();
1676 pkgCacheFile CacheFile
;
1677 CacheFile
.BuildCaches(NULL
, true);
1678 _error
->RevertToStack();
1683 Cache
.writeStateFile(NULL
);
1684 return d
->dpkg_error
.empty();
1687 void SigINT(int /*sig*/) {
1688 pkgPackageManager::SigINTStop
= true;
1691 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1692 // ---------------------------------------------------------------------
1694 void pkgDPkgPM::Reset()
1696 List
.erase(List
.begin(),List
.end());
1699 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1700 // ---------------------------------------------------------------------
1702 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1704 // If apport doesn't exist or isn't installed do nothing
1705 // This e.g. prevents messages in 'universes' without apport
1706 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1707 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1710 string pkgname
, reportfile
, pkgver
, arch
;
1711 string::size_type pos
;
1714 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1716 std::clog
<< "configured to not write apport reports" << std::endl
;
1720 // only report the first errors
1721 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1723 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1727 // check if its not a follow up error
1728 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1729 if(strstr(errormsg
, needle
) != NULL
) {
1730 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1734 // do not report disk-full failures
1735 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1736 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1740 // do not report out-of-memory failures
1741 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1742 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1743 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1747 // do not report bugs regarding inaccessible local files
1748 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1749 strstr(errormsg
, "cannot access archive") != NULL
) {
1750 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1754 // do not report errors encountered when decompressing packages
1755 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1756 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1760 // do not report dpkg I/O errors, this is a format string, so we compare
1761 // the prefix and the suffix of the error with the dpkg error message
1762 vector
<string
> io_errors
;
1763 io_errors
.push_back(string("failed to read"));
1764 io_errors
.push_back(string("failed to write"));
1765 io_errors
.push_back(string("failed to seek"));
1766 io_errors
.push_back(string("unexpected end of file or stream"));
1768 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1770 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1771 if (list
.size() > 1) {
1772 // we need to split %s, VectorizeString only allows char so we need
1773 // to kill the "s" manually
1774 if (list
[1].size() > 1) {
1775 list
[1].erase(0, 1);
1776 if(strstr(errormsg
, list
[0].c_str()) &&
1777 strstr(errormsg
, list
[1].c_str())) {
1778 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1785 // get the pkgname and reportfile
1786 pkgname
= flNotDir(pkgpath
);
1787 pos
= pkgname
.find('_');
1788 if(pos
!= string::npos
)
1789 pkgname
= pkgname
.substr(0, pos
);
1791 // find the package versin and source package name
1792 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1793 if (Pkg
.end() == true)
1795 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1796 if (Ver
.end() == true)
1798 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1800 // if the file exists already, we check:
1801 // - if it was reported already (touched by apport).
1802 // If not, we do nothing, otherwise
1803 // we overwrite it. This is the same behaviour as apport
1804 // - if we have a report with the same pkgversion already
1806 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1807 if(FileExists(reportfile
))
1812 // check atime/mtime
1813 stat(reportfile
.c_str(), &buf
);
1814 if(buf
.st_mtime
> buf
.st_atime
)
1817 // check if the existing report is the same version
1818 report
= fopen(reportfile
.c_str(),"r");
1819 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1821 if(strstr(strbuf
,"Package:") == strbuf
)
1823 char pkgname
[255], version
[255];
1824 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1825 if(strcmp(pkgver
.c_str(), version
) == 0)
1835 // now write the report
1836 arch
= _config
->Find("APT::Architecture");
1837 report
= fopen(reportfile
.c_str(),"w");
1840 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1841 chmod(reportfile
.c_str(), 0);
1843 chmod(reportfile
.c_str(), 0600);
1844 fprintf(report
, "ProblemType: Package\n");
1845 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1846 time_t now
= time(NULL
);
1847 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1848 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1849 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1850 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1851 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1853 // ensure that the log is flushed
1855 fflush(d
->term_out
);
1857 // attach terminal log it if we have it
1858 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1859 if (!logfile_name
.empty())
1863 fprintf(report
, "DpkgTerminalLog:\n");
1864 log
= fopen(logfile_name
.c_str(),"r");
1868 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1869 fprintf(report
, " %s", buf
);
1870 fprintf(report
, " \n");
1875 // attach history log it if we have it
1876 string histfile_name
= _config
->FindFile("Dir::Log::History");
1877 if (!histfile_name
.empty())
1879 fprintf(report
, "DpkgHistoryLog:\n");
1880 FILE* log
= fopen(histfile_name
.c_str(),"r");
1884 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1885 fprintf(report
, " %s", buf
);
1890 // log the ordering, see dpkgpm.h and the "Ops" enum there
1891 const char *ops_str
[] = {
1899 fprintf(report
, "AptOrdering:\n");
1900 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1901 if ((*I
).Pkg
!= NULL
)
1902 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1904 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1906 // attach dmesg log (to learn about segfaults)
1907 if (FileExists("/bin/dmesg"))
1909 fprintf(report
, "Dmesg:\n");
1910 FILE *log
= popen("/bin/dmesg","r");
1914 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1915 fprintf(report
, " %s", buf
);
1920 // attach df -l log (to learn about filesystem status)
1921 if (FileExists("/bin/df"))
1924 fprintf(report
, "Df:\n");
1925 FILE *log
= popen("/bin/df -l","r");
1929 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1930 fprintf(report
, " %s", buf
);