1 // -*- mode: cpp; mode: fold -*-
3 // $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $
4 /* ######################################################################
6 DPKG Package Manager - Provide an interface to dpkg
8 ##################################################################### */
13 #include <apt-pkg/cachefile.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/depcache.h>
16 #include <apt-pkg/dpkgpm.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/pkgrecords.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
58 APT_PURE
static unsigned int
61 unsigned int size
= 0;
62 char **envp
= environ
;
65 size
+= strlen (*envp
++) + 1;
70 class pkgDPkgPMPrivate
73 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
74 term_out(NULL
), history_out(NULL
),
75 progress(NULL
), tt_is_valid(false), master(-1),
76 slave(NULL
), protect_slave_from_dying(-1)
83 bool stdin_is_dev_null
;
84 // the buffer we use for the dpkg status-fd reading
90 APT::Progress::PackageManager
*progress
;
97 int protect_slave_from_dying
;
101 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
)
205 pkgCache::VerFileIterator Vf
= Ver
.FileList();
206 pkgCache::PkgFileIterator F
= Vf
.File();
207 for (F
= Vf
.File(); F
.end() == false; ++F
)
209 if (F
&& F
.Archive())
211 if (strcmp(F
.Archive(), "now"))
220 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
221 // ---------------------------------------------------------------------
223 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
224 : pkgPackageManager(Cache
), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
226 d
= new pkgDPkgPMPrivate();
229 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
230 // ---------------------------------------------------------------------
232 pkgDPkgPM::~pkgDPkgPM()
237 // DPkgPM::Install - Install a package /*{{{*/
238 // ---------------------------------------------------------------------
239 /* Add an install operation to the sequence list */
240 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
242 if (File
.empty() == true || Pkg
.end() == true)
243 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
245 // If the filename string begins with DPkg::Chroot-Directory, return the
246 // substr that is within the chroot so dpkg can access it.
247 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
248 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
250 size_t len
= chrootdir
.length();
251 if (chrootdir
.at(len
- 1) == '/')
253 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
256 List
.push_back(Item(Item::Install
,Pkg
,File
));
261 // DPkgPM::Configure - Configure a package /*{{{*/
262 // ---------------------------------------------------------------------
263 /* Add a configure operation to the sequence list */
264 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
266 if (Pkg
.end() == true)
269 List
.push_back(Item(Item::Configure
, Pkg
));
271 // Use triggers for config calls if we configure "smart"
272 // as otherwise Pre-Depends will not be satisfied, see #526774
273 if (_config
->FindB("DPkg::TriggersPending", false) == true)
274 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
279 // DPkgPM::Remove - Remove a package /*{{{*/
280 // ---------------------------------------------------------------------
281 /* Add a remove operation to the sequence list */
282 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
284 if (Pkg
.end() == true)
288 List
.push_back(Item(Item::Purge
,Pkg
));
290 List
.push_back(Item(Item::Remove
,Pkg
));
294 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
295 // ---------------------------------------------------------------------
296 /* This is part of the helper script communication interface, it sends
297 very complete information down to the other end of the pipe.*/
298 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
300 return SendPkgsInfo(F
, 2);
302 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
304 // This version of APT supports only v3, so don't sent higher versions
306 fprintf(F
,"VERSION %u\n", Version
);
308 fprintf(F
,"VERSION 3\n");
310 /* Write out all of the configuration directives by walking the
311 configuration tree */
312 const Configuration::Item
*Top
= _config
->Tree(0);
315 if (Top
->Value
.empty() == false)
318 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
319 QuoteString(Top
->Value
,"\n").c_str());
328 while (Top
!= 0 && Top
->Next
== 0)
335 // Write out the package actions in order.
336 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
338 if(I
->Pkg
.end() == true)
341 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
343 fprintf(F
,"%s ",I
->Pkg
.Name());
345 // Current version which we are going to replace
346 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
347 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
348 CurVer
= FindNowVersion(I
->Pkg
);
350 if (CurVer
.end() == true)
355 fprintf(F
, "- - none ");
359 fprintf(F
, "%s ", CurVer
.VerStr());
361 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
364 // Show the compare operator between current and install version
365 if (S
.InstallVer
!= 0)
367 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
369 if (CurVer
.end() == false)
370 Comp
= InstVer
.CompareVer(CurVer
);
377 fprintf(F
, "%s ", InstVer
.VerStr());
379 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
386 fprintf(F
, "> - - none ");
389 // Show the filename/operation
390 if (I
->Op
== Item::Install
)
393 if (I
->File
[0] != '/')
394 fprintf(F
,"**ERROR**\n");
396 fprintf(F
,"%s\n",I
->File
.c_str());
398 else if (I
->Op
== Item::Configure
)
399 fprintf(F
,"**CONFIGURE**\n");
400 else if (I
->Op
== Item::Remove
||
401 I
->Op
== Item::Purge
)
402 fprintf(F
,"**REMOVE**\n");
410 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
411 // ---------------------------------------------------------------------
412 /* This looks for a list of scripts to run from the configuration file
413 each one is run and is fed on standard input a list of all .deb files
414 that are due to be installed. */
415 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
419 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
420 if (Opts
== 0 || Opts
->Child
== 0)
424 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
426 unsigned int Count
= 1;
427 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
429 if (Opts
->Value
.empty() == true)
432 if(_config
->FindB("Debug::RunScripts", false) == true)
433 std::clog
<< "Running external script with list of all .deb file: '"
434 << Opts
->Value
<< "'" << std::endl
;
436 // Determine the protocol version
437 string OptSec
= Opts
->Value
;
438 string::size_type Pos
;
439 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
440 Pos
= OptSec
.length();
441 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
443 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
444 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
447 std::set
<int> KeepFDs
;
448 MergeKeepFdsFromConfiguration(KeepFDs
);
450 if (pipe(Pipes
) != 0) {
451 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
454 if (InfoFD
!= (unsigned)Pipes
[0])
455 SetCloseExec(Pipes
[0],true);
457 KeepFDs
.insert(Pipes
[0]);
460 SetCloseExec(Pipes
[1],true);
462 // Purified Fork for running the script
463 pid_t Process
= ExecFork(KeepFDs
);
467 dup2(Pipes
[0], InfoFD
);
468 SetCloseExec(STDOUT_FILENO
,false);
469 SetCloseExec(STDIN_FILENO
,false);
470 SetCloseExec(STDERR_FILENO
,false);
473 strprintf(hookfd
, "%d", InfoFD
);
474 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
476 dpkgChrootDirectory();
480 Args
[2] = Opts
->Value
.c_str();
482 execv(Args
[0],(char **)Args
);
486 FILE *F
= fdopen(Pipes
[1],"w");
488 result
= _error
->Errno("fdopen","Faild to open new FD");
492 // Feed it the filenames.
495 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
497 // Only deal with packages to be installed from .deb
498 if (I
->Op
!= Item::Install
)
502 if (I
->File
[0] != '/')
505 /* Feed the filename of each package that is pending install
507 fprintf(F
,"%s\n",I
->File
.c_str());
513 SendPkgsInfo(F
, Version
);
517 // Clean up the sub process
518 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
519 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
523 signal(SIGPIPE
, old_sigpipe
);
528 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
529 // ---------------------------------------------------------------------
532 void pkgDPkgPM::DoStdin(int master
)
534 unsigned char input_buf
[256] = {0,};
535 ssize_t len
= read(0, input_buf
, sizeof(input_buf
));
537 FileFd::Write(master
, input_buf
, len
);
539 d
->stdin_is_dev_null
= true;
542 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
543 // ---------------------------------------------------------------------
545 * read the terminal pty and write log
547 void pkgDPkgPM::DoTerminalPty(int master
)
549 unsigned char term_buf
[1024] = {0,0, };
551 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
552 if(len
== -1 && errno
== EIO
)
554 // this happens when the child is about to exit, we
555 // give it time to actually exit, otherwise we run
556 // into a race so we sleep for half a second.
557 struct timespec sleepfor
= { 0, 500000000 };
558 nanosleep(&sleepfor
, NULL
);
563 FileFd::Write(1, term_buf
, len
);
565 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
568 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
569 // ---------------------------------------------------------------------
572 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
574 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
576 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
578 /* dpkg sends strings like this:
579 'status: <pkg>: <pkg qstate>'
580 'status: <pkg>:<arch>: <pkg qstate>'
582 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
583 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
586 // we need to split on ": " (note the appended space) as the ':' is
587 // part of the pkgname:arch information that dpkg sends
589 // A dpkg error message may contain additional ":" (like
590 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
591 // so we need to ensure to not split too much
592 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
596 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
600 // build the (prefix, pkgname, action) tuple, position of this
601 // is different for "processing" or "status" messages
602 std::string prefix
= APT::String::Strip(list
[0]);
606 // "processing" has the form "processing: action: pkg or trigger"
607 // with action = ["install", "upgrade", "configure", "remove", "purge",
608 // "disappear", "trigproc"]
609 if (prefix
== "processing")
611 pkgname
= APT::String::Strip(list
[2]);
612 action
= APT::String::Strip(list
[1]);
613 // we don't care for the difference (as dpkg doesn't really either)
614 if (action
== "upgrade")
617 // "status" has the form: "status: pkg: state"
618 // with state in ["half-installed", "unpacked", "half-configured",
619 // "installed", "config-files", "not-installed"]
620 else if (prefix
== "status")
622 pkgname
= APT::String::Strip(list
[1]);
623 action
= APT::String::Strip(list
[2]);
626 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
631 /* handle the special cases first:
633 errors look like this:
634 '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
635 and conffile-prompt like this
636 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
638 if (prefix
== "status")
640 if(action
== "error")
642 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
645 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
648 else if(action
== "conffile-prompt")
650 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
656 // at this point we know that we should have a valid pkgname, so build all
659 // dpkg does not always send "pkgname:arch" so we add it here if needed
660 if (pkgname
.find(":") == std::string::npos
)
662 // find the package in the group that is touched by dpkg
663 // if there are multiple pkgs dpkg would send us a full pkgname:arch
664 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
665 if (Grp
.end() == false)
667 pkgCache::PkgIterator P
= Grp
.PackageList();
668 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
670 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
672 pkgname
= P
.FullName();
679 const char* const pkg
= pkgname
.c_str();
680 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
681 std::string arch
= "";
682 if (pkgname
.find(":") != string::npos
)
683 arch
= StringSplit(pkgname
, ":")[1];
684 std::string i18n_pkgname
= pkgname
;
685 if (arch
.size() != 0)
686 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
688 // 'processing' from dpkg looks like
689 // 'processing: action: pkg'
690 if(prefix
== "processing")
692 const std::pair
<const char *, const char *> * const iter
=
693 std::find_if(PackageProcessingOpsBegin
,
694 PackageProcessingOpsEnd
,
695 MatchProcessingOp(action
.c_str()));
696 if(iter
== PackageProcessingOpsEnd
)
699 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
703 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
704 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
706 // FIXME: this needs a muliarch testcase
707 // FIXME2: is "pkgname" here reliable with dpkg only sending us
709 if (action
== "disappear")
710 handleDisappearAction(pkgname
);
714 if (prefix
== "status")
716 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
717 if(PackageOpsDone
[pkg
] < states
.size())
719 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
720 if (next_action
&& Debug
== true)
721 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
722 << " action: " << action
<< " (expected: '" << next_action
<< "' "
723 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
725 // check if the package moved to the next dpkg state
726 if(next_action
&& (action
== next_action
))
728 // only read the translation if there is actually a next action
729 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
731 // we moved from one dpkg state to a new one, report that
732 ++PackageOpsDone
[pkg
];
736 strprintf(msg
, translation
, i18n_pkgname
.c_str());
737 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
743 // DPkgPM::handleDisappearAction /*{{{*/
744 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
746 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
747 if (unlikely(Pkg
.end() == true))
750 // record the package name for display and stuff later
751 disappearedPkgs
.insert(Pkg
.FullName(true));
753 // the disappeared package was auto-installed - nothing to do
754 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
756 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
757 if (unlikely(PkgVer
.end() == true))
759 /* search in the list of dependencies for (Pre)Depends,
760 check if this dependency has a Replaces on our package
761 and if so transfer the manual installed flag to it */
762 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
764 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
765 Dep
->Type
!= pkgCache::Dep::PreDepends
)
767 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
768 if (unlikely(Tar
.end() == true))
770 // the package is already marked as manual
771 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
773 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
774 if (TarVer
.end() == true)
776 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
778 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
780 if (Pkg
!= Rep
.TargetPkg())
782 // okay, they are strongly connected - transfer manual-bit
784 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
785 Cache
[Tar
].Flags
&= ~Flag::Auto
;
791 // DPkgPM::DoDpkgStatusFd /*{{{*/
792 // ---------------------------------------------------------------------
795 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
800 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
801 d
->dpkgbuf_pos
+= len
;
805 // process line by line if we have a buffer
807 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
810 ProcessDpkgStatusLine(p
);
811 p
=q
+1; // continue with next line
814 // now move the unprocessed bits (after the final \n that is now a 0x0)
815 // to the start and update d->dpkgbuf_pos
816 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
820 // we are interessted in the first char *after* 0x0
823 // move the unprocessed tail to the start and update pos
824 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
825 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
828 // DPkgPM::WriteHistoryTag /*{{{*/
829 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
831 size_t const length
= value
.length();
834 // poor mans rstrip(", ")
835 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
836 value
.erase(length
- 2, 2);
837 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
839 // DPkgPM::OpenLog /*{{{*/
840 bool pkgDPkgPM::OpenLog()
842 string
const logdir
= _config
->FindDir("Dir::Log");
843 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
844 // FIXME: use a better string after freeze
845 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
849 time_t const t
= time(NULL
);
850 struct tm
const * const tmp
= localtime(&t
);
851 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
854 string
const logfile_name
= flCombine(logdir
,
855 _config
->Find("Dir::Log::Terminal"));
856 if (!logfile_name
.empty())
858 d
->term_out
= fopen(logfile_name
.c_str(),"a");
859 if (d
->term_out
== NULL
)
860 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
861 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
862 SetCloseExec(fileno(d
->term_out
), true);
863 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
865 struct passwd
*pw
= getpwnam("root");
866 struct group
*gr
= getgrnam("adm");
867 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
868 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
870 if (chmod(logfile_name
.c_str(), 0640) != 0)
871 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
872 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
875 // write your history
876 string
const history_name
= flCombine(logdir
,
877 _config
->Find("Dir::Log::History"));
878 if (!history_name
.empty())
880 d
->history_out
= fopen(history_name
.c_str(),"a");
881 if (d
->history_out
== NULL
)
882 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
883 SetCloseExec(fileno(d
->history_out
), true);
884 chmod(history_name
.c_str(), 0644);
885 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
886 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
887 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
889 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
891 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
892 if (Cache
[I
].NewInstall() == true)
893 HISTORYINFO(install
, CANDIDATE_AUTO
)
894 else if (Cache
[I
].ReInstall() == true)
895 HISTORYINFO(reinstall
, CANDIDATE
)
896 else if (Cache
[I
].Upgrade() == true)
897 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
898 else if (Cache
[I
].Downgrade() == true)
899 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
900 else if (Cache
[I
].Delete() == true)
901 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
905 line
->append(I
.FullName(false)).append(" (");
906 switch (infostring
) {
907 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
909 line
->append(Cache
[I
].CandVersion
);
910 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
911 line
->append(", automatic");
913 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
914 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
918 if (_config
->Exists("Commandline::AsString") == true)
919 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
920 WriteHistoryTag("Install", install
);
921 WriteHistoryTag("Reinstall", reinstall
);
922 WriteHistoryTag("Upgrade", upgrade
);
923 WriteHistoryTag("Downgrade",downgrade
);
924 WriteHistoryTag("Remove",remove
);
925 WriteHistoryTag("Purge",purge
);
926 fflush(d
->history_out
);
932 // DPkg::CloseLog /*{{{*/
933 bool pkgDPkgPM::CloseLog()
936 time_t t
= time(NULL
);
937 struct tm
*tmp
= localtime(&t
);
938 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
942 fprintf(d
->term_out
, "Log ended: ");
943 fprintf(d
->term_out
, "%s", timestr
);
944 fprintf(d
->term_out
, "\n");
951 if (disappearedPkgs
.empty() == false)
954 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
955 d
!= disappearedPkgs
.end(); ++d
)
957 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
958 disappear
.append(*d
);
960 disappear
.append(", ");
962 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
964 WriteHistoryTag("Disappeared", disappear
);
966 if (d
->dpkg_error
.empty() == false)
967 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
968 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
969 fclose(d
->history_out
);
971 d
->history_out
= NULL
;
978 // This implements a racy version of pselect for those architectures
979 // that don't have a working implementation.
980 // FIXME: Probably can be removed on Lenny+1
981 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
982 fd_set
*exceptfds
, const struct timespec
*timeout
,
983 const sigset_t
*sigmask
)
989 tv
.tv_sec
= timeout
->tv_sec
;
990 tv
.tv_usec
= timeout
->tv_nsec
/1000;
992 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
993 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
994 sigprocmask(SIG_SETMASK
, &origmask
, 0);
999 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1000 void pkgDPkgPM::BuildPackagesProgressMap()
1002 // map the dpkg states to the operations that are performed
1003 // (this is sorted in the same way as Item::Ops)
1004 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1005 // Install operation
1007 {"half-installed", N_("Preparing %s")},
1008 {"unpacked", N_("Unpacking %s") },
1011 // Configure operation
1013 {"unpacked",N_("Preparing to configure %s") },
1014 {"half-configured", N_("Configuring %s") },
1015 { "installed", N_("Installed %s")},
1020 {"half-configured", N_("Preparing for removal of %s")},
1021 {"half-installed", N_("Removing %s")},
1022 {"config-files", N_("Removed %s")},
1027 {"config-files", N_("Preparing to completely remove %s")},
1028 {"not-installed", N_("Completely removed %s")},
1033 // init the PackageOps map, go over the list of packages that
1034 // that will be [installed|configured|removed|purged] and add
1035 // them to the PackageOps map (the dpkg states it goes through)
1036 // and the PackageOpsTranslations (human readable strings)
1037 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1039 if((*I
).Pkg
.end() == true)
1042 string
const name
= (*I
).Pkg
.FullName();
1043 PackageOpsDone
[name
] = 0;
1044 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1046 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1050 /* one extra: We don't want the progress bar to reach 100%, especially not
1051 if we call dpkg --configure --pending and process a bunch of triggers
1052 while showing 100%. Also, spindown takes a while, so never reaching 100%
1053 is way more correct than reaching 100% while still doing stuff even if
1054 doing it this way is slightly bending the rules */
1058 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13)
1059 bool pkgDPkgPM::Go(int StatusFd
)
1061 APT::Progress::PackageManager
*progress
= NULL
;
1063 progress
= APT::Progress::PackageManagerProgressFactory();
1065 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1067 return GoNoABIBreak(progress
);
1071 void pkgDPkgPM::StartPtyMagic()
1073 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1076 if (d
->slave
!= NULL
)
1082 _error
->PushToStack();
1084 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1085 if (d
->master
== -1)
1086 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1087 else if (unlockpt(d
->master
) == -1)
1088 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1091 char const * const slave_name
= ptsname(d
->master
);
1092 if (slave_name
== NULL
)
1093 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1096 d
->slave
= strdup(slave_name
);
1097 if (d
->slave
== NULL
)
1098 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1099 else if (grantpt(d
->master
) == -1)
1100 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1101 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1103 d
->tt_is_valid
= true;
1104 struct termios raw_tt
;
1105 // copy window size of stdout if its a 'good' terminal
1106 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1109 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1110 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1111 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1112 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1114 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1115 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1119 raw_tt
.c_lflag
&= ~ECHO
;
1120 raw_tt
.c_lflag
|= ISIG
;
1121 // block SIGTTOU during tcsetattr to prevent a hang if
1122 // the process is a member of the background process group
1123 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1124 sigemptyset(&d
->sigmask
);
1125 sigaddset(&d
->sigmask
, SIGTTOU
);
1126 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1127 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1128 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1129 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1132 if (d
->slave
!= NULL
)
1134 /* on linux, closing (and later reopening) all references to the slave
1135 makes the slave a death end, so we open it here to have one open all
1136 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1137 on kfreebsd we get an incorrect ("step like") output then while it has
1138 no problem with closing all references… so to avoid platform specific
1139 code here we combine both and be happy once more */
1140 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1145 if (_error
->PendingError() == true)
1147 if (d
->master
!= -1)
1152 if (d
->slave
!= NULL
)
1157 _error
->DumpErrors(std::cerr
);
1159 _error
->RevertToStack();
1161 void pkgDPkgPM::SetupSlavePtyMagic()
1163 if(d
->master
== -1 || d
->slave
== NULL
)
1166 if (close(d
->master
) == -1)
1167 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1170 _error
->FatalE("setsid", "Starting a new session for child failed!");
1172 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1174 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1175 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1176 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1179 for (unsigned short i
= 0; i
< 3; ++i
)
1180 if (dup2(slaveFd
, i
) == -1)
1181 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1183 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1184 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1190 void pkgDPkgPM::StopPtyMagic()
1192 if (d
->slave
!= NULL
)
1195 if (d
->protect_slave_from_dying
!= -1)
1197 close(d
->protect_slave_from_dying
);
1198 d
->protect_slave_from_dying
= -1;
1202 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1203 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1209 // DPkgPM::Go - Run the sequence /*{{{*/
1210 // ---------------------------------------------------------------------
1211 /* This globs the operations and calls dpkg
1213 * If it is called with a progress object apt will report the install
1214 * progress to this object. It maps the dpkg states a package goes
1215 * through to human readable (and i10n-able)
1216 * names and calculates a percentage for each step.
1218 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
1219 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1221 bool pkgDPkgPM::GoNoABIBreak(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
)
1494 Args
.push_back(NULL
);
1500 /* Mask off sig int/quit. We do this because dpkg also does when
1501 it forks scripts. What happens is that when you hit ctrl-c it sends
1502 it to all processes in the group. Since dpkg ignores the signal
1503 it doesn't die but we do! So we must also ignore it */
1504 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1505 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1507 // Check here for any SIGINT
1508 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1512 // ignore SIGHUP as well (debian #463030)
1513 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1516 d
->progress
->StartDpkg();
1517 std::set
<int> KeepFDs
;
1518 KeepFDs
.insert(fd
[1]);
1519 MergeKeepFdsFromConfiguration(KeepFDs
);
1520 pid_t Child
= ExecFork(KeepFDs
);
1523 // This is the child
1524 SetupSlavePtyMagic();
1525 close(fd
[0]); // close the read end of the pipe
1527 dpkgChrootDirectory();
1529 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1532 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1536 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1539 // Discard everything in stdin before forking dpkg
1540 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1543 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1545 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1549 /* No Job Control Stop Env is a magic dpkg var that prevents it
1550 from using sigstop */
1551 putenv((char *)"DPKG_NO_TSTP=yes");
1552 execvp(Args
[0], (char**) &Args
[0]);
1553 cerr
<< "Could not exec dpkg!" << endl
;
1558 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1564 // we read from dpkg here
1565 int const _dpkgin
= fd
[0];
1566 close(fd
[1]); // close the write end of the pipe
1569 sigemptyset(&d
->sigmask
);
1570 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1572 /* free vectors (and therefore memory) as we don't need the included data anymore */
1573 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1574 p
!= Packages
.end(); ++p
)
1578 // the result of the waitpid call
1581 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1583 // FIXME: move this to a function or something, looks ugly here
1584 // error handling, waitpid returned -1
1587 RunScripts("DPkg::Post-Invoke");
1589 // Restore sig int/quit
1590 signal(SIGQUIT
,old_SIGQUIT
);
1591 signal(SIGINT
,old_SIGINT
);
1593 signal(SIGHUP
,old_SIGHUP
);
1594 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1597 // wait for input or output here
1599 if (d
->master
>= 0 && !d
->stdin_is_dev_null
)
1601 FD_SET(_dpkgin
, &rfds
);
1603 FD_SET(d
->master
, &rfds
);
1605 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1606 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1607 &tv
, &d
->original_sigmask
);
1608 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1609 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1610 NULL
, &tv
, &d
->original_sigmask
);
1611 d
->progress
->Pulse();
1612 if (select_ret
== 0)
1614 else if (select_ret
< 0 && errno
== EINTR
)
1616 else if (select_ret
< 0)
1618 perror("select() returned error");
1622 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1623 DoTerminalPty(d
->master
);
1624 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1626 if(FD_ISSET(_dpkgin
, &rfds
))
1627 DoDpkgStatusFd(_dpkgin
);
1631 // Restore sig int/quit
1632 signal(SIGQUIT
,old_SIGQUIT
);
1633 signal(SIGINT
,old_SIGINT
);
1635 signal(SIGHUP
,old_SIGHUP
);
1636 // Check for an error code.
1637 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1639 // if it was set to "keep-dpkg-runing" then we won't return
1640 // here but keep the loop going and just report it as a error
1642 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1644 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1645 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1646 else if (WIFEXITED(Status
) != 0)
1647 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1649 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1650 _error
->Error("%s", d
->dpkg_error
.c_str());
1656 // dpkg is done at this point
1657 d
->progress
->Stop();
1661 if (pkgPackageManager::SigINTStop
)
1662 _error
->Warning(_("Operation was interrupted before it could finish"));
1664 if (RunScripts("DPkg::Post-Invoke") == false)
1667 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1669 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1670 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1671 unlink(oldpkgcache
.c_str()) == 0)
1673 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1674 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1676 _error
->PushToStack();
1677 pkgCacheFile CacheFile
;
1678 CacheFile
.BuildCaches(NULL
, true);
1679 _error
->RevertToStack();
1684 Cache
.writeStateFile(NULL
);
1685 return d
->dpkg_error
.empty();
1688 void SigINT(int /*sig*/) {
1689 pkgPackageManager::SigINTStop
= true;
1692 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1693 // ---------------------------------------------------------------------
1695 void pkgDPkgPM::Reset()
1697 List
.erase(List
.begin(),List
.end());
1700 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1701 // ---------------------------------------------------------------------
1703 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1705 // If apport doesn't exist or isn't installed do nothing
1706 // This e.g. prevents messages in 'universes' without apport
1707 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1708 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1711 string pkgname
, reportfile
, srcpkgname
, pkgver
, arch
;
1712 string::size_type pos
;
1715 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1717 std::clog
<< "configured to not write apport reports" << std::endl
;
1721 // only report the first errors
1722 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1724 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1728 // check if its not a follow up error
1729 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1730 if(strstr(errormsg
, needle
) != NULL
) {
1731 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1735 // do not report disk-full failures
1736 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1737 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1741 // do not report out-of-memory failures
1742 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1743 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1744 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1748 // do not report bugs regarding inaccessible local files
1749 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1750 strstr(errormsg
, "cannot access archive") != NULL
) {
1751 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1755 // do not report errors encountered when decompressing packages
1756 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1757 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1761 // do not report dpkg I/O errors, this is a format string, so we compare
1762 // the prefix and the suffix of the error with the dpkg error message
1763 vector
<string
> io_errors
;
1764 io_errors
.push_back(string("failed to read"));
1765 io_errors
.push_back(string("failed to write"));
1766 io_errors
.push_back(string("failed to seek"));
1767 io_errors
.push_back(string("unexpected end of file or stream"));
1769 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1771 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1772 if (list
.size() > 1) {
1773 // we need to split %s, VectorizeString only allows char so we need
1774 // to kill the "s" manually
1775 if (list
[1].size() > 1) {
1776 list
[1].erase(0, 1);
1777 if(strstr(errormsg
, list
[0].c_str()) &&
1778 strstr(errormsg
, list
[1].c_str())) {
1779 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1786 // get the pkgname and reportfile
1787 pkgname
= flNotDir(pkgpath
);
1788 pos
= pkgname
.find('_');
1789 if(pos
!= string::npos
)
1790 pkgname
= pkgname
.substr(0, pos
);
1792 // find the package versin and source package name
1793 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1794 if (Pkg
.end() == true)
1796 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1797 if (Ver
.end() == true)
1799 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1800 pkgRecords
Recs(Cache
);
1801 pkgRecords::Parser
&Parse
= Recs
.Lookup(Ver
.FileList());
1802 srcpkgname
= Parse
.SourcePkg();
1803 if(srcpkgname
.empty())
1804 srcpkgname
= pkgname
;
1806 // if the file exists already, we check:
1807 // - if it was reported already (touched by apport).
1808 // If not, we do nothing, otherwise
1809 // we overwrite it. This is the same behaviour as apport
1810 // - if we have a report with the same pkgversion already
1812 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1813 if(FileExists(reportfile
))
1818 // check atime/mtime
1819 stat(reportfile
.c_str(), &buf
);
1820 if(buf
.st_mtime
> buf
.st_atime
)
1823 // check if the existing report is the same version
1824 report
= fopen(reportfile
.c_str(),"r");
1825 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1827 if(strstr(strbuf
,"Package:") == strbuf
)
1829 char pkgname
[255], version
[255];
1830 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1831 if(strcmp(pkgver
.c_str(), version
) == 0)
1841 // now write the report
1842 arch
= _config
->Find("APT::Architecture");
1843 report
= fopen(reportfile
.c_str(),"w");
1846 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1847 chmod(reportfile
.c_str(), 0);
1849 chmod(reportfile
.c_str(), 0600);
1850 fprintf(report
, "ProblemType: Package\n");
1851 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1852 time_t now
= time(NULL
);
1853 fprintf(report
, "Date: %s" , ctime(&now
));
1854 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1855 fprintf(report
, "SourcePackage: %s\n", srcpkgname
.c_str());
1856 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1858 // ensure that the log is flushed
1860 fflush(d
->term_out
);
1862 // attach terminal log it if we have it
1863 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1864 if (!logfile_name
.empty())
1868 fprintf(report
, "DpkgTerminalLog:\n");
1869 log
= fopen(logfile_name
.c_str(),"r");
1873 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1874 fprintf(report
, " %s", buf
);
1875 fprintf(report
, " \n");
1880 // attach history log it if we have it
1881 string histfile_name
= _config
->FindFile("Dir::Log::History");
1882 if (!histfile_name
.empty())
1884 fprintf(report
, "DpkgHistoryLog:\n");
1885 FILE* log
= fopen(histfile_name
.c_str(),"r");
1889 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1890 fprintf(report
, " %s", buf
);
1896 const char *ops_str
[] = {"Install", "Configure","Remove","Purge"};
1897 fprintf(report
, "AptOrdering:\n");
1898 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1899 if ((*I
).Pkg
!= NULL
)
1900 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1902 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1904 // attach dmesg log (to learn about segfaults)
1905 if (FileExists("/bin/dmesg"))
1907 fprintf(report
, "Dmesg:\n");
1908 FILE *log
= popen("/bin/dmesg","r");
1912 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1913 fprintf(report
, " %s", buf
);
1918 // attach df -l log (to learn about filesystem status)
1919 if (FileExists("/bin/df"))
1922 fprintf(report
, "Df:\n");
1923 FILE *log
= popen("/bin/df -l","r");
1927 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1928 fprintf(report
, " %s", buf
);