1 // -*- mode: cpp; mode: fold -*-
3 // $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $
4 /* ######################################################################
6 DPKG Package Manager - Provide an interface to dpkg
8 ##################################################################### */
13 #include <apt-pkg/cachefile.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/depcache.h>
16 #include <apt-pkg/dpkgpm.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/pkgrecords.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
58 APT_PURE
static unsigned int
61 unsigned int size
= 0;
62 char **envp
= environ
;
65 size
+= strlen (*envp
++) + 1;
70 class pkgDPkgPMPrivate
73 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
74 term_out(NULL
), history_out(NULL
),
75 progress(NULL
), tt_is_valid(false), master(-1),
76 slave(NULL
), protect_slave_from_dying(-1),
84 bool stdin_is_dev_null
;
85 // the buffer we use for the dpkg status-fd reading
91 APT::Progress::PackageManager
*progress
;
98 int protect_slave_from_dying
;
102 sigset_t original_sigmask
;
109 // Maps the dpkg "processing" info to human readable names. Entry 0
110 // of each array is the key, entry 1 is the value.
111 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
112 std::make_pair("install", N_("Installing %s")),
113 std::make_pair("configure", N_("Configuring %s")),
114 std::make_pair("remove", N_("Removing %s")),
115 std::make_pair("purge", N_("Completely removing %s")),
116 std::make_pair("disappear", N_("Noting disappearance of %s")),
117 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
120 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
121 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
123 // Predicate to test whether an entry in the PackageProcessingOps
124 // array matches a string.
125 class MatchProcessingOp
130 MatchProcessingOp(const char *the_target
)
135 bool operator()(const std::pair
<const char *, const char *> &pair
) const
137 return strcmp(pair
.first
, target
) == 0;
142 /* helper function to ionice the given PID
144 there is no C header for ionice yet - just the syscall interface
145 so we use the binary from util-linux
150 if (!FileExists("/usr/bin/ionice"))
152 pid_t Process
= ExecFork();
156 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
158 Args
[0] = "/usr/bin/ionice";
162 execv(Args
[0], (char **)Args
);
164 return ExecWait(Process
, "ionice");
167 static std::string
getDpkgExecutable()
169 string Tmp
= _config
->Find("Dir::Bin::dpkg","dpkg");
170 string
const dpkgChrootDir
= _config
->FindDir("DPkg::Chroot-Directory", "/");
171 size_t dpkgChrootLen
= dpkgChrootDir
.length();
172 if (dpkgChrootDir
!= "/" && Tmp
.find(dpkgChrootDir
) == 0)
174 if (dpkgChrootDir
[dpkgChrootLen
- 1] == '/')
176 Tmp
= Tmp
.substr(dpkgChrootLen
);
181 // dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/
182 static void dpkgChrootDirectory()
184 std::string
const chrootDir
= _config
->FindDir("DPkg::Chroot-Directory");
185 if (chrootDir
== "/")
187 std::cerr
<< "Chrooting into " << chrootDir
<< std::endl
;
188 if (chroot(chrootDir
.c_str()) != 0)
196 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
197 // ---------------------------------------------------------------------
198 /* This is helpful when a package is no longer installed but has residual
202 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
204 pkgCache::VerIterator Ver
;
205 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
207 pkgCache::VerFileIterator Vf
= Ver
.FileList();
208 pkgCache::PkgFileIterator F
= Vf
.File();
209 for (F
= Vf
.File(); F
.end() == false; ++F
)
211 if (F
&& F
.Archive())
213 if (strcmp(F
.Archive(), "now"))
222 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
223 // ---------------------------------------------------------------------
225 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
226 : pkgPackageManager(Cache
), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
228 d
= new pkgDPkgPMPrivate();
231 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
232 // ---------------------------------------------------------------------
234 pkgDPkgPM::~pkgDPkgPM()
239 // DPkgPM::Install - Install a package /*{{{*/
240 // ---------------------------------------------------------------------
241 /* Add an install operation to the sequence list */
242 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
244 if (File
.empty() == true || Pkg
.end() == true)
245 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
247 // If the filename string begins with DPkg::Chroot-Directory, return the
248 // substr that is within the chroot so dpkg can access it.
249 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
250 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
252 size_t len
= chrootdir
.length();
253 if (chrootdir
.at(len
- 1) == '/')
255 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
258 List
.push_back(Item(Item::Install
,Pkg
,File
));
263 // DPkgPM::Configure - Configure a package /*{{{*/
264 // ---------------------------------------------------------------------
265 /* Add a configure operation to the sequence list */
266 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
268 if (Pkg
.end() == true)
271 List
.push_back(Item(Item::Configure
, Pkg
));
273 // Use triggers for config calls if we configure "smart"
274 // as otherwise Pre-Depends will not be satisfied, see #526774
275 if (_config
->FindB("DPkg::TriggersPending", false) == true)
276 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
281 // DPkgPM::Remove - Remove a package /*{{{*/
282 // ---------------------------------------------------------------------
283 /* Add a remove operation to the sequence list */
284 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
286 if (Pkg
.end() == true)
290 List
.push_back(Item(Item::Purge
,Pkg
));
292 List
.push_back(Item(Item::Remove
,Pkg
));
296 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This is part of the helper script communication interface, it sends
299 very complete information down to the other end of the pipe.*/
300 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
302 return SendPkgsInfo(F
, 2);
304 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
306 // This version of APT supports only v3, so don't sent higher versions
308 fprintf(F
,"VERSION %u\n", Version
);
310 fprintf(F
,"VERSION 3\n");
312 /* Write out all of the configuration directives by walking the
313 configuration tree */
314 const Configuration::Item
*Top
= _config
->Tree(0);
317 if (Top
->Value
.empty() == false)
320 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
321 QuoteString(Top
->Value
,"\n").c_str());
330 while (Top
!= 0 && Top
->Next
== 0)
337 // Write out the package actions in order.
338 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
340 if(I
->Pkg
.end() == true)
343 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
345 fprintf(F
,"%s ",I
->Pkg
.Name());
347 // Current version which we are going to replace
348 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
349 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
350 CurVer
= FindNowVersion(I
->Pkg
);
352 if (CurVer
.end() == true)
357 fprintf(F
, "- - none ");
361 fprintf(F
, "%s ", CurVer
.VerStr());
363 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
366 // Show the compare operator between current and install version
367 if (S
.InstallVer
!= 0)
369 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
371 if (CurVer
.end() == false)
372 Comp
= InstVer
.CompareVer(CurVer
);
379 fprintf(F
, "%s ", InstVer
.VerStr());
381 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
388 fprintf(F
, "> - - none ");
391 // Show the filename/operation
392 if (I
->Op
== Item::Install
)
395 if (I
->File
[0] != '/')
396 fprintf(F
,"**ERROR**\n");
398 fprintf(F
,"%s\n",I
->File
.c_str());
400 else if (I
->Op
== Item::Configure
)
401 fprintf(F
,"**CONFIGURE**\n");
402 else if (I
->Op
== Item::Remove
||
403 I
->Op
== Item::Purge
)
404 fprintf(F
,"**REMOVE**\n");
412 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
413 // ---------------------------------------------------------------------
414 /* This looks for a list of scripts to run from the configuration file
415 each one is run and is fed on standard input a list of all .deb files
416 that are due to be installed. */
417 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
421 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
422 if (Opts
== 0 || Opts
->Child
== 0)
426 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
428 unsigned int Count
= 1;
429 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
431 if (Opts
->Value
.empty() == true)
434 if(_config
->FindB("Debug::RunScripts", false) == true)
435 std::clog
<< "Running external script with list of all .deb file: '"
436 << Opts
->Value
<< "'" << std::endl
;
438 // Determine the protocol version
439 string OptSec
= Opts
->Value
;
440 string::size_type Pos
;
441 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
442 Pos
= OptSec
.length();
443 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
445 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
446 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
449 std::set
<int> KeepFDs
;
450 MergeKeepFdsFromConfiguration(KeepFDs
);
452 if (pipe(Pipes
) != 0) {
453 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
456 if (InfoFD
!= (unsigned)Pipes
[0])
457 SetCloseExec(Pipes
[0],true);
459 KeepFDs
.insert(Pipes
[0]);
462 SetCloseExec(Pipes
[1],true);
464 // Purified Fork for running the script
465 pid_t Process
= ExecFork(KeepFDs
);
469 dup2(Pipes
[0], InfoFD
);
470 SetCloseExec(STDOUT_FILENO
,false);
471 SetCloseExec(STDIN_FILENO
,false);
472 SetCloseExec(STDERR_FILENO
,false);
475 strprintf(hookfd
, "%d", InfoFD
);
476 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
478 dpkgChrootDirectory();
482 Args
[2] = Opts
->Value
.c_str();
484 execv(Args
[0],(char **)Args
);
488 FILE *F
= fdopen(Pipes
[1],"w");
490 result
= _error
->Errno("fdopen","Faild to open new FD");
494 // Feed it the filenames.
497 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
499 // Only deal with packages to be installed from .deb
500 if (I
->Op
!= Item::Install
)
504 if (I
->File
[0] != '/')
507 /* Feed the filename of each package that is pending install
509 fprintf(F
,"%s\n",I
->File
.c_str());
515 SendPkgsInfo(F
, Version
);
519 // Clean up the sub process
520 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
521 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
525 signal(SIGPIPE
, old_sigpipe
);
530 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
531 // ---------------------------------------------------------------------
534 void pkgDPkgPM::DoStdin(int master
)
536 unsigned char input_buf
[256] = {0,};
537 ssize_t len
= read(0, input_buf
, sizeof(input_buf
));
539 FileFd::Write(master
, input_buf
, len
);
541 d
->stdin_is_dev_null
= true;
544 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
545 // ---------------------------------------------------------------------
547 * read the terminal pty and write log
549 void pkgDPkgPM::DoTerminalPty(int master
)
551 unsigned char term_buf
[1024] = {0,0, };
553 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
554 if(len
== -1 && errno
== EIO
)
556 // this happens when the child is about to exit, we
557 // give it time to actually exit, otherwise we run
558 // into a race so we sleep for half a second.
559 struct timespec sleepfor
= { 0, 500000000 };
560 nanosleep(&sleepfor
, NULL
);
565 FileFd::Write(1, term_buf
, len
);
567 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
570 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
571 // ---------------------------------------------------------------------
574 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
576 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
578 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
580 /* dpkg sends strings like this:
581 'status: <pkg>: <pkg qstate>'
582 'status: <pkg>:<arch>: <pkg qstate>'
584 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
585 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
588 // we need to split on ": " (note the appended space) as the ':' is
589 // part of the pkgname:arch information that dpkg sends
591 // A dpkg error message may contain additional ":" (like
592 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
593 // so we need to ensure to not split too much
594 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
598 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
602 // build the (prefix, pkgname, action) tuple, position of this
603 // is different for "processing" or "status" messages
604 std::string prefix
= APT::String::Strip(list
[0]);
608 // "processing" has the form "processing: action: pkg or trigger"
609 // with action = ["install", "upgrade", "configure", "remove", "purge",
610 // "disappear", "trigproc"]
611 if (prefix
== "processing")
613 pkgname
= APT::String::Strip(list
[2]);
614 action
= APT::String::Strip(list
[1]);
615 // we don't care for the difference (as dpkg doesn't really either)
616 if (action
== "upgrade")
619 // "status" has the form: "status: pkg: state"
620 // with state in ["half-installed", "unpacked", "half-configured",
621 // "installed", "config-files", "not-installed"]
622 else if (prefix
== "status")
624 pkgname
= APT::String::Strip(list
[1]);
625 action
= APT::String::Strip(list
[2]);
628 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
633 /* handle the special cases first:
635 errors look like this:
636 '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
637 and conffile-prompt like this
638 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
640 if (prefix
== "status")
642 if(action
== "error")
644 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
647 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
650 else if(action
== "conffile-prompt")
652 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
658 // at this point we know that we should have a valid pkgname, so build all
661 // dpkg does not always send "pkgname:arch" so we add it here if needed
662 if (pkgname
.find(":") == std::string::npos
)
664 // find the package in the group that is touched by dpkg
665 // if there are multiple pkgs dpkg would send us a full pkgname:arch
666 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
667 if (Grp
.end() == false)
669 pkgCache::PkgIterator P
= Grp
.PackageList();
670 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
672 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
674 pkgname
= P
.FullName();
681 const char* const pkg
= pkgname
.c_str();
682 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
683 std::string arch
= "";
684 if (pkgname
.find(":") != string::npos
)
685 arch
= StringSplit(pkgname
, ":")[1];
686 std::string i18n_pkgname
= pkgname
;
687 if (arch
.size() != 0)
688 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
690 // 'processing' from dpkg looks like
691 // 'processing: action: pkg'
692 if(prefix
== "processing")
694 const std::pair
<const char *, const char *> * const iter
=
695 std::find_if(PackageProcessingOpsBegin
,
696 PackageProcessingOpsEnd
,
697 MatchProcessingOp(action
.c_str()));
698 if(iter
== PackageProcessingOpsEnd
)
701 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
705 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
706 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
708 // FIXME: this needs a muliarch testcase
709 // FIXME2: is "pkgname" here reliable with dpkg only sending us
711 if (action
== "disappear")
712 handleDisappearAction(pkgname
);
716 if (prefix
== "status")
718 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
719 if(PackageOpsDone
[pkg
] < states
.size())
721 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
722 if (next_action
&& Debug
== true)
723 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
724 << " action: " << action
<< " (expected: '" << next_action
<< "' "
725 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
727 // check if the package moved to the next dpkg state
728 if(next_action
&& (action
== next_action
))
730 // only read the translation if there is actually a next action
731 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
733 // we moved from one dpkg state to a new one, report that
734 ++PackageOpsDone
[pkg
];
738 strprintf(msg
, translation
, i18n_pkgname
.c_str());
739 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
745 // DPkgPM::handleDisappearAction /*{{{*/
746 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
748 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
749 if (unlikely(Pkg
.end() == true))
752 // record the package name for display and stuff later
753 disappearedPkgs
.insert(Pkg
.FullName(true));
755 // the disappeared package was auto-installed - nothing to do
756 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
758 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
759 if (unlikely(PkgVer
.end() == true))
761 /* search in the list of dependencies for (Pre)Depends,
762 check if this dependency has a Replaces on our package
763 and if so transfer the manual installed flag to it */
764 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
766 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
767 Dep
->Type
!= pkgCache::Dep::PreDepends
)
769 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
770 if (unlikely(Tar
.end() == true))
772 // the package is already marked as manual
773 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
775 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
776 if (TarVer
.end() == true)
778 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
780 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
782 if (Pkg
!= Rep
.TargetPkg())
784 // okay, they are strongly connected - transfer manual-bit
786 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
787 Cache
[Tar
].Flags
&= ~Flag::Auto
;
793 // DPkgPM::DoDpkgStatusFd /*{{{*/
794 // ---------------------------------------------------------------------
797 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
802 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
803 d
->dpkgbuf_pos
+= len
;
807 // process line by line if we have a buffer
809 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
812 ProcessDpkgStatusLine(p
);
813 p
=q
+1; // continue with next line
816 // now move the unprocessed bits (after the final \n that is now a 0x0)
817 // to the start and update d->dpkgbuf_pos
818 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
822 // we are interessted in the first char *after* 0x0
825 // move the unprocessed tail to the start and update pos
826 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
827 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
830 // DPkgPM::WriteHistoryTag /*{{{*/
831 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
833 size_t const length
= value
.length();
836 // poor mans rstrip(", ")
837 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
838 value
.erase(length
- 2, 2);
839 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
841 // DPkgPM::OpenLog /*{{{*/
842 bool pkgDPkgPM::OpenLog()
844 string
const logdir
= _config
->FindDir("Dir::Log");
845 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
846 // FIXME: use a better string after freeze
847 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
851 time_t const t
= time(NULL
);
852 struct tm
const * const tmp
= localtime(&t
);
853 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
856 string
const logfile_name
= flCombine(logdir
,
857 _config
->Find("Dir::Log::Terminal"));
858 if (!logfile_name
.empty())
860 d
->term_out
= fopen(logfile_name
.c_str(),"a");
861 if (d
->term_out
== NULL
)
862 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
863 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
864 SetCloseExec(fileno(d
->term_out
), true);
865 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
867 struct passwd
*pw
= getpwnam("root");
868 struct group
*gr
= getgrnam("adm");
869 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
870 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
872 if (chmod(logfile_name
.c_str(), 0640) != 0)
873 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
874 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
877 // write your history
878 string
const history_name
= flCombine(logdir
,
879 _config
->Find("Dir::Log::History"));
880 if (!history_name
.empty())
882 d
->history_out
= fopen(history_name
.c_str(),"a");
883 if (d
->history_out
== NULL
)
884 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
885 SetCloseExec(fileno(d
->history_out
), true);
886 chmod(history_name
.c_str(), 0644);
887 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
888 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
889 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
891 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
893 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
894 if (Cache
[I
].NewInstall() == true)
895 HISTORYINFO(install
, CANDIDATE_AUTO
)
896 else if (Cache
[I
].ReInstall() == true)
897 HISTORYINFO(reinstall
, CANDIDATE
)
898 else if (Cache
[I
].Upgrade() == true)
899 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
900 else if (Cache
[I
].Downgrade() == true)
901 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
902 else if (Cache
[I
].Delete() == true)
903 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
907 line
->append(I
.FullName(false)).append(" (");
908 switch (infostring
) {
909 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
911 line
->append(Cache
[I
].CandVersion
);
912 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
913 line
->append(", automatic");
915 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
916 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
920 if (_config
->Exists("Commandline::AsString") == true)
921 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
922 WriteHistoryTag("Install", install
);
923 WriteHistoryTag("Reinstall", reinstall
);
924 WriteHistoryTag("Upgrade", upgrade
);
925 WriteHistoryTag("Downgrade",downgrade
);
926 WriteHistoryTag("Remove",remove
);
927 WriteHistoryTag("Purge",purge
);
928 fflush(d
->history_out
);
934 // DPkg::CloseLog /*{{{*/
935 bool pkgDPkgPM::CloseLog()
938 time_t t
= time(NULL
);
939 struct tm
*tmp
= localtime(&t
);
940 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
944 fprintf(d
->term_out
, "Log ended: ");
945 fprintf(d
->term_out
, "%s", timestr
);
946 fprintf(d
->term_out
, "\n");
953 if (disappearedPkgs
.empty() == false)
956 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
957 d
!= disappearedPkgs
.end(); ++d
)
959 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
960 disappear
.append(*d
);
962 disappear
.append(", ");
964 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
966 WriteHistoryTag("Disappeared", disappear
);
968 if (d
->dpkg_error
.empty() == false)
969 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
970 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
971 fclose(d
->history_out
);
973 d
->history_out
= NULL
;
980 // This implements a racy version of pselect for those architectures
981 // that don't have a working implementation.
982 // FIXME: Probably can be removed on Lenny+1
983 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
984 fd_set
*exceptfds
, const struct timespec
*timeout
,
985 const sigset_t
*sigmask
)
991 tv
.tv_sec
= timeout
->tv_sec
;
992 tv
.tv_usec
= timeout
->tv_nsec
/1000;
994 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
995 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
996 sigprocmask(SIG_SETMASK
, &origmask
, 0);
1001 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1002 void pkgDPkgPM::BuildPackagesProgressMap()
1004 // map the dpkg states to the operations that are performed
1005 // (this is sorted in the same way as Item::Ops)
1006 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1007 // Install operation
1009 {"half-installed", N_("Preparing %s")},
1010 {"unpacked", N_("Unpacking %s") },
1013 // Configure operation
1015 {"unpacked",N_("Preparing to configure %s") },
1016 {"half-configured", N_("Configuring %s") },
1017 { "installed", N_("Installed %s")},
1022 {"half-configured", N_("Preparing for removal of %s")},
1023 {"half-installed", N_("Removing %s")},
1024 {"config-files", N_("Removed %s")},
1029 {"config-files", N_("Preparing to completely remove %s")},
1030 {"not-installed", N_("Completely removed %s")},
1035 // init the PackageOps map, go over the list of packages that
1036 // that will be [installed|configured|removed|purged] and add
1037 // them to the PackageOps map (the dpkg states it goes through)
1038 // and the PackageOpsTranslations (human readable strings)
1039 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1041 if((*I
).Pkg
.end() == true)
1044 string
const name
= (*I
).Pkg
.FullName();
1045 PackageOpsDone
[name
] = 0;
1046 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1048 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1052 /* one extra: We don't want the progress bar to reach 100%, especially not
1053 if we call dpkg --configure --pending and process a bunch of triggers
1054 while showing 100%. Also, spindown takes a while, so never reaching 100%
1055 is way more correct than reaching 100% while still doing stuff even if
1056 doing it this way is slightly bending the rules */
1060 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13)
1061 bool pkgDPkgPM::Go(int StatusFd
)
1063 APT::Progress::PackageManager
*progress
= NULL
;
1065 progress
= APT::Progress::PackageManagerProgressFactory();
1067 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1069 return GoNoABIBreak(progress
);
1073 void pkgDPkgPM::StartPtyMagic()
1075 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1078 if (d
->slave
!= NULL
)
1084 if (isatty(STDIN_FILENO
) == 0)
1085 d
->direct_stdin
= true;
1087 _error
->PushToStack();
1089 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1090 if (d
->master
== -1)
1091 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1092 else if (unlockpt(d
->master
) == -1)
1093 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1096 char const * const slave_name
= ptsname(d
->master
);
1097 if (slave_name
== NULL
)
1098 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1101 d
->slave
= strdup(slave_name
);
1102 if (d
->slave
== NULL
)
1103 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1104 else if (grantpt(d
->master
) == -1)
1105 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1106 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1108 d
->tt_is_valid
= true;
1109 struct termios raw_tt
;
1110 // copy window size of stdout if its a 'good' terminal
1111 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1114 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1115 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1116 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1117 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1119 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1120 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1124 raw_tt
.c_lflag
&= ~ECHO
;
1125 raw_tt
.c_lflag
|= ISIG
;
1126 // block SIGTTOU during tcsetattr to prevent a hang if
1127 // the process is a member of the background process group
1128 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1129 sigemptyset(&d
->sigmask
);
1130 sigaddset(&d
->sigmask
, SIGTTOU
);
1131 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1132 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1133 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1134 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1137 if (d
->slave
!= NULL
)
1139 /* on linux, closing (and later reopening) all references to the slave
1140 makes the slave a death end, so we open it here to have one open all
1141 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1142 on kfreebsd we get an incorrect ("step like") output then while it has
1143 no problem with closing all references… so to avoid platform specific
1144 code here we combine both and be happy once more */
1145 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1150 if (_error
->PendingError() == true)
1152 if (d
->master
!= -1)
1157 if (d
->slave
!= NULL
)
1162 _error
->DumpErrors(std::cerr
);
1164 _error
->RevertToStack();
1166 void pkgDPkgPM::SetupSlavePtyMagic()
1168 if(d
->master
== -1 || d
->slave
== NULL
)
1171 if (close(d
->master
) == -1)
1172 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1175 _error
->FatalE("setsid", "Starting a new session for child failed!");
1177 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1179 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1180 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1181 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1184 unsigned short i
= 0;
1185 if (d
->direct_stdin
== true)
1188 if (dup2(slaveFd
, i
) == -1)
1189 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1191 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1192 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1198 void pkgDPkgPM::StopPtyMagic()
1200 if (d
->slave
!= NULL
)
1203 if (d
->protect_slave_from_dying
!= -1)
1205 close(d
->protect_slave_from_dying
);
1206 d
->protect_slave_from_dying
= -1;
1210 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1211 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1217 // DPkgPM::Go - Run the sequence /*{{{*/
1218 // ---------------------------------------------------------------------
1219 /* This globs the operations and calls dpkg
1221 * If it is called with a progress object apt will report the install
1222 * progress to this object. It maps the dpkg states a package goes
1223 * through to human readable (and i10n-able)
1224 * names and calculates a percentage for each step.
1226 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
1227 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1229 bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager
*progress
)
1232 pkgPackageManager::SigINTStop
= false;
1233 d
->progress
= progress
;
1235 // Generate the base argument list for dpkg
1236 unsigned long StartSize
= 0;
1237 std::vector
<const char *> Args
;
1238 std::string DpkgExecutable
= getDpkgExecutable();
1239 Args
.push_back(DpkgExecutable
.c_str());
1240 StartSize
+= DpkgExecutable
.length();
1242 // Stick in any custom dpkg options
1243 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1247 for (; Opts
!= 0; Opts
= Opts
->Next
)
1249 if (Opts
->Value
.empty() == true)
1251 Args
.push_back(Opts
->Value
.c_str());
1252 StartSize
+= Opts
->Value
.length();
1256 size_t const BaseArgs
= Args
.size();
1257 // we need to detect if we can qualify packages with the architecture or not
1258 Args
.push_back("--assert-multi-arch");
1259 Args
.push_back(NULL
);
1261 pid_t dpkgAssertMultiArch
= ExecFork();
1262 if (dpkgAssertMultiArch
== 0)
1264 dpkgChrootDirectory();
1265 // redirect everything to the ultimate sink as we only need the exit-status
1266 int const nullfd
= open("/dev/null", O_RDONLY
);
1267 dup2(nullfd
, STDIN_FILENO
);
1268 dup2(nullfd
, STDOUT_FILENO
);
1269 dup2(nullfd
, STDERR_FILENO
);
1270 execvp(Args
[0], (char**) &Args
[0]);
1271 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1278 // FIXME: do we really need this limit when we have MaxArgBytes?
1279 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1281 // try to figure out the max environment size
1282 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1285 OSArgMax
-= EnvironmentSize() - 2*1024;
1286 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1287 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1289 if (RunScripts("DPkg::Pre-Invoke") == false)
1292 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1295 // support subpressing of triggers processing for special
1296 // cases like d-i that runs the triggers handling manually
1297 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1298 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1299 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1302 BuildPackagesProgressMap();
1304 d
->stdin_is_dev_null
= false;
1309 bool dpkgMultiArch
= false;
1310 if (dpkgAssertMultiArch
> 0)
1313 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1317 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1320 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1321 dpkgMultiArch
= true;
1324 // start pty magic before the loop
1327 // Tell the progress that its starting and fork dpkg
1328 d
->progress
->Start(d
->master
);
1330 // this loop is runs once per dpkg operation
1331 vector
<Item
>::const_iterator I
= List
.begin();
1332 while (I
!= List
.end())
1334 // Do all actions with the same Op in one run
1335 vector
<Item
>::const_iterator J
= I
;
1336 if (TriggersPending
== true)
1337 for (; J
!= List
.end(); ++J
)
1341 if (J
->Op
!= Item::TriggersPending
)
1343 vector
<Item
>::const_iterator T
= J
+ 1;
1344 if (T
!= List
.end() && T
->Op
== I
->Op
)
1349 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1352 // keep track of allocated strings for multiarch package names
1353 std::vector
<char *> Packages
;
1355 // start with the baseset of arguments
1356 unsigned long Size
= StartSize
;
1357 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1359 // Now check if we are within the MaxArgs limit
1361 // this code below is problematic, because it may happen that
1362 // the argument list is split in a way that A depends on B
1363 // and they are in the same "--configure A B" run
1364 // - with the split they may now be configured in different
1365 // runs, using Immediate-Configure-All can help prevent this.
1366 if (J
- I
> (signed)MaxArgs
)
1369 unsigned long const size
= MaxArgs
+ 10;
1371 Packages
.reserve(size
);
1375 unsigned long const size
= (J
- I
) + 10;
1377 Packages
.reserve(size
);
1382 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1384 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1385 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1387 ADDARGC("--status-fd");
1388 char status_fd_buf
[20];
1389 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1390 ADDARG(status_fd_buf
);
1391 unsigned long const Op
= I
->Op
;
1396 ADDARGC("--force-depends");
1397 ADDARGC("--force-remove-essential");
1398 ADDARGC("--remove");
1402 ADDARGC("--force-depends");
1403 ADDARGC("--force-remove-essential");
1407 case Item::Configure
:
1408 ADDARGC("--configure");
1411 case Item::ConfigurePending
:
1412 ADDARGC("--configure");
1413 ADDARGC("--pending");
1416 case Item::TriggersPending
:
1417 ADDARGC("--triggers-only");
1418 ADDARGC("--pending");
1422 ADDARGC("--unpack");
1423 ADDARGC("--auto-deconfigure");
1427 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1428 I
->Op
!= Item::ConfigurePending
)
1430 ADDARGC("--no-triggers");
1434 // Write in the file or package names
1435 if (I
->Op
== Item::Install
)
1437 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1439 if (I
->File
[0] != '/')
1440 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1441 Args
.push_back(I
->File
.c_str());
1442 Size
+= I
->File
.length();
1447 string
const nativeArch
= _config
->Find("APT::Architecture");
1448 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1449 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1451 if((*I
).Pkg
.end() == true)
1453 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1455 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1456 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1457 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1458 strcmp(I
->Pkg
.Arch(), "none") == 0))
1460 char const * const name
= I
->Pkg
.Name();
1465 pkgCache::VerIterator PkgVer
;
1466 std::string name
= I
->Pkg
.Name();
1467 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1469 PkgVer
= I
->Pkg
.CurrentVer();
1470 if(PkgVer
.end() == true)
1471 PkgVer
= FindNowVersion(I
->Pkg
);
1474 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1475 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1476 ; // never arch-qualify a package without an arch
1477 else if (PkgVer
.end() == false)
1478 name
.append(":").append(PkgVer
.Arch());
1480 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1481 char * const fullname
= strdup(name
.c_str());
1482 Packages
.push_back(fullname
);
1486 // skip configure action if all sheduled packages disappeared
1487 if (oldSize
== Size
)
1494 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1496 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1497 a
!= Args
.end(); ++a
)
1502 Args
.push_back(NULL
);
1508 /* Mask off sig int/quit. We do this because dpkg also does when
1509 it forks scripts. What happens is that when you hit ctrl-c it sends
1510 it to all processes in the group. Since dpkg ignores the signal
1511 it doesn't die but we do! So we must also ignore it */
1512 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1513 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1515 // Check here for any SIGINT
1516 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1520 // ignore SIGHUP as well (debian #463030)
1521 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1524 d
->progress
->StartDpkg();
1525 std::set
<int> KeepFDs
;
1526 KeepFDs
.insert(fd
[1]);
1527 MergeKeepFdsFromConfiguration(KeepFDs
);
1528 pid_t Child
= ExecFork(KeepFDs
);
1531 // This is the child
1532 SetupSlavePtyMagic();
1533 close(fd
[0]); // close the read end of the pipe
1535 dpkgChrootDirectory();
1537 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1540 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1544 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1547 // Discard everything in stdin before forking dpkg
1548 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1551 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1553 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1557 /* No Job Control Stop Env is a magic dpkg var that prevents it
1558 from using sigstop */
1559 putenv((char *)"DPKG_NO_TSTP=yes");
1560 execvp(Args
[0], (char**) &Args
[0]);
1561 cerr
<< "Could not exec dpkg!" << endl
;
1566 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1572 // we read from dpkg here
1573 int const _dpkgin
= fd
[0];
1574 close(fd
[1]); // close the write end of the pipe
1577 sigemptyset(&d
->sigmask
);
1578 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1580 /* free vectors (and therefore memory) as we don't need the included data anymore */
1581 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1582 p
!= Packages
.end(); ++p
)
1586 // the result of the waitpid call
1589 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1591 // FIXME: move this to a function or something, looks ugly here
1592 // error handling, waitpid returned -1
1595 RunScripts("DPkg::Post-Invoke");
1597 // Restore sig int/quit
1598 signal(SIGQUIT
,old_SIGQUIT
);
1599 signal(SIGINT
,old_SIGINT
);
1601 signal(SIGHUP
,old_SIGHUP
);
1602 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1605 // wait for input or output here
1607 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1608 FD_SET(STDIN_FILENO
, &rfds
);
1609 FD_SET(_dpkgin
, &rfds
);
1611 FD_SET(d
->master
, &rfds
);
1613 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1614 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1615 &tv
, &d
->original_sigmask
);
1616 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1617 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1618 NULL
, &tv
, &d
->original_sigmask
);
1619 d
->progress
->Pulse();
1620 if (select_ret
== 0)
1622 else if (select_ret
< 0 && errno
== EINTR
)
1624 else if (select_ret
< 0)
1626 perror("select() returned error");
1630 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1631 DoTerminalPty(d
->master
);
1632 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1634 if(FD_ISSET(_dpkgin
, &rfds
))
1635 DoDpkgStatusFd(_dpkgin
);
1639 // Restore sig int/quit
1640 signal(SIGQUIT
,old_SIGQUIT
);
1641 signal(SIGINT
,old_SIGINT
);
1643 signal(SIGHUP
,old_SIGHUP
);
1644 // Check for an error code.
1645 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1647 // if it was set to "keep-dpkg-runing" then we won't return
1648 // here but keep the loop going and just report it as a error
1650 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1652 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1653 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1654 else if (WIFEXITED(Status
) != 0)
1655 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1657 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1658 _error
->Error("%s", d
->dpkg_error
.c_str());
1664 // dpkg is done at this point
1665 d
->progress
->Stop();
1669 if (pkgPackageManager::SigINTStop
)
1670 _error
->Warning(_("Operation was interrupted before it could finish"));
1672 if (RunScripts("DPkg::Post-Invoke") == false)
1675 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1677 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1678 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1679 unlink(oldpkgcache
.c_str()) == 0)
1681 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1682 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1684 _error
->PushToStack();
1685 pkgCacheFile CacheFile
;
1686 CacheFile
.BuildCaches(NULL
, true);
1687 _error
->RevertToStack();
1692 Cache
.writeStateFile(NULL
);
1693 return d
->dpkg_error
.empty();
1696 void SigINT(int /*sig*/) {
1697 pkgPackageManager::SigINTStop
= true;
1700 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1701 // ---------------------------------------------------------------------
1703 void pkgDPkgPM::Reset()
1705 List
.erase(List
.begin(),List
.end());
1708 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1709 // ---------------------------------------------------------------------
1711 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1713 // If apport doesn't exist or isn't installed do nothing
1714 // This e.g. prevents messages in 'universes' without apport
1715 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1716 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1719 string pkgname
, reportfile
, srcpkgname
, pkgver
, arch
;
1720 string::size_type pos
;
1723 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1725 std::clog
<< "configured to not write apport reports" << std::endl
;
1729 // only report the first errors
1730 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1732 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1736 // check if its not a follow up error
1737 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1738 if(strstr(errormsg
, needle
) != NULL
) {
1739 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1743 // do not report disk-full failures
1744 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1745 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1749 // do not report out-of-memory failures
1750 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1751 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1752 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1756 // do not report bugs regarding inaccessible local files
1757 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1758 strstr(errormsg
, "cannot access archive") != NULL
) {
1759 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1763 // do not report errors encountered when decompressing packages
1764 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1765 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1769 // do not report dpkg I/O errors, this is a format string, so we compare
1770 // the prefix and the suffix of the error with the dpkg error message
1771 vector
<string
> io_errors
;
1772 io_errors
.push_back(string("failed to read"));
1773 io_errors
.push_back(string("failed to write"));
1774 io_errors
.push_back(string("failed to seek"));
1775 io_errors
.push_back(string("unexpected end of file or stream"));
1777 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1779 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1780 if (list
.size() > 1) {
1781 // we need to split %s, VectorizeString only allows char so we need
1782 // to kill the "s" manually
1783 if (list
[1].size() > 1) {
1784 list
[1].erase(0, 1);
1785 if(strstr(errormsg
, list
[0].c_str()) &&
1786 strstr(errormsg
, list
[1].c_str())) {
1787 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1794 // get the pkgname and reportfile
1795 pkgname
= flNotDir(pkgpath
);
1796 pos
= pkgname
.find('_');
1797 if(pos
!= string::npos
)
1798 pkgname
= pkgname
.substr(0, pos
);
1800 // find the package versin and source package name
1801 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1802 if (Pkg
.end() == true)
1804 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1805 if (Ver
.end() == true)
1807 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1808 pkgRecords
Recs(Cache
);
1809 pkgRecords::Parser
&Parse
= Recs
.Lookup(Ver
.FileList());
1810 srcpkgname
= Parse
.SourcePkg();
1811 if(srcpkgname
.empty())
1812 srcpkgname
= pkgname
;
1814 // if the file exists already, we check:
1815 // - if it was reported already (touched by apport).
1816 // If not, we do nothing, otherwise
1817 // we overwrite it. This is the same behaviour as apport
1818 // - if we have a report with the same pkgversion already
1820 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1821 if(FileExists(reportfile
))
1826 // check atime/mtime
1827 stat(reportfile
.c_str(), &buf
);
1828 if(buf
.st_mtime
> buf
.st_atime
)
1831 // check if the existing report is the same version
1832 report
= fopen(reportfile
.c_str(),"r");
1833 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1835 if(strstr(strbuf
,"Package:") == strbuf
)
1837 char pkgname
[255], version
[255];
1838 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1839 if(strcmp(pkgver
.c_str(), version
) == 0)
1849 // now write the report
1850 arch
= _config
->Find("APT::Architecture");
1851 report
= fopen(reportfile
.c_str(),"w");
1854 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1855 chmod(reportfile
.c_str(), 0);
1857 chmod(reportfile
.c_str(), 0600);
1858 fprintf(report
, "ProblemType: Package\n");
1859 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1860 time_t now
= time(NULL
);
1861 fprintf(report
, "Date: %s" , ctime(&now
));
1862 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1863 fprintf(report
, "SourcePackage: %s\n", srcpkgname
.c_str());
1864 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1866 // ensure that the log is flushed
1868 fflush(d
->term_out
);
1870 // attach terminal log it if we have it
1871 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1872 if (!logfile_name
.empty())
1876 fprintf(report
, "DpkgTerminalLog:\n");
1877 log
= fopen(logfile_name
.c_str(),"r");
1881 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1882 fprintf(report
, " %s", buf
);
1883 fprintf(report
, " \n");
1888 // attach history log it if we have it
1889 string histfile_name
= _config
->FindFile("Dir::Log::History");
1890 if (!histfile_name
.empty())
1892 fprintf(report
, "DpkgHistoryLog:\n");
1893 FILE* log
= fopen(histfile_name
.c_str(),"r");
1897 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1898 fprintf(report
, " %s", buf
);
1903 // log the ordering, see dpkgpm.h and the "Ops" enum there
1904 const char *ops_str
[] = {
1912 fprintf(report
, "AptOrdering:\n");
1913 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1914 if ((*I
).Pkg
!= NULL
)
1915 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1917 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1919 // attach dmesg log (to learn about segfaults)
1920 if (FileExists("/bin/dmesg"))
1922 fprintf(report
, "Dmesg:\n");
1923 FILE *log
= popen("/bin/dmesg","r");
1927 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1928 fprintf(report
, " %s", buf
);
1933 // attach df -l log (to learn about filesystem status)
1934 if (FileExists("/bin/df"))
1937 fprintf(report
, "Df:\n");
1938 FILE *log
= popen("/bin/df -l","r");
1942 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1943 fprintf(report
, " %s", buf
);