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
)
206 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
207 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
209 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
216 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
217 // ---------------------------------------------------------------------
219 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
220 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
224 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
225 // ---------------------------------------------------------------------
227 pkgDPkgPM::~pkgDPkgPM()
232 // DPkgPM::Install - Install a package /*{{{*/
233 // ---------------------------------------------------------------------
234 /* Add an install operation to the sequence list */
235 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
237 if (File
.empty() == true || Pkg
.end() == true)
238 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
240 // If the filename string begins with DPkg::Chroot-Directory, return the
241 // substr that is within the chroot so dpkg can access it.
242 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
243 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
245 size_t len
= chrootdir
.length();
246 if (chrootdir
.at(len
- 1) == '/')
248 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
251 List
.push_back(Item(Item::Install
,Pkg
,File
));
256 // DPkgPM::Configure - Configure a package /*{{{*/
257 // ---------------------------------------------------------------------
258 /* Add a configure operation to the sequence list */
259 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
261 if (Pkg
.end() == true)
264 List
.push_back(Item(Item::Configure
, Pkg
));
266 // Use triggers for config calls if we configure "smart"
267 // as otherwise Pre-Depends will not be satisfied, see #526774
268 if (_config
->FindB("DPkg::TriggersPending", false) == true)
269 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
274 // DPkgPM::Remove - Remove a package /*{{{*/
275 // ---------------------------------------------------------------------
276 /* Add a remove operation to the sequence list */
277 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
279 if (Pkg
.end() == true)
283 List
.push_back(Item(Item::Purge
,Pkg
));
285 List
.push_back(Item(Item::Remove
,Pkg
));
289 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
290 // ---------------------------------------------------------------------
291 /* This is part of the helper script communication interface, it sends
292 very complete information down to the other end of the pipe.*/
293 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
295 return SendPkgsInfo(F
, 2);
297 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
299 // This version of APT supports only v3, so don't sent higher versions
301 fprintf(F
,"VERSION %u\n", Version
);
303 fprintf(F
,"VERSION 3\n");
305 /* Write out all of the configuration directives by walking the
306 configuration tree */
307 const Configuration::Item
*Top
= _config
->Tree(0);
310 if (Top
->Value
.empty() == false)
313 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
314 QuoteString(Top
->Value
,"\n").c_str());
323 while (Top
!= 0 && Top
->Next
== 0)
330 // Write out the package actions in order.
331 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
333 if(I
->Pkg
.end() == true)
336 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
338 fprintf(F
,"%s ",I
->Pkg
.Name());
340 // Current version which we are going to replace
341 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
342 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
343 CurVer
= FindNowVersion(I
->Pkg
);
345 if (CurVer
.end() == true)
350 fprintf(F
, "- - none ");
354 fprintf(F
, "%s ", CurVer
.VerStr());
356 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
359 // Show the compare operator between current and install version
360 if (S
.InstallVer
!= 0)
362 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
364 if (CurVer
.end() == false)
365 Comp
= InstVer
.CompareVer(CurVer
);
372 fprintf(F
, "%s ", InstVer
.VerStr());
374 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
381 fprintf(F
, "> - - none ");
384 // Show the filename/operation
385 if (I
->Op
== Item::Install
)
388 if (I
->File
[0] != '/')
389 fprintf(F
,"**ERROR**\n");
391 fprintf(F
,"%s\n",I
->File
.c_str());
393 else if (I
->Op
== Item::Configure
)
394 fprintf(F
,"**CONFIGURE**\n");
395 else if (I
->Op
== Item::Remove
||
396 I
->Op
== Item::Purge
)
397 fprintf(F
,"**REMOVE**\n");
405 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
406 // ---------------------------------------------------------------------
407 /* This looks for a list of scripts to run from the configuration file
408 each one is run and is fed on standard input a list of all .deb files
409 that are due to be installed. */
410 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
414 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
415 if (Opts
== 0 || Opts
->Child
== 0)
419 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
421 unsigned int Count
= 1;
422 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
424 if (Opts
->Value
.empty() == true)
427 if(_config
->FindB("Debug::RunScripts", false) == true)
428 std::clog
<< "Running external script with list of all .deb file: '"
429 << Opts
->Value
<< "'" << std::endl
;
431 // Determine the protocol version
432 string OptSec
= Opts
->Value
;
433 string::size_type Pos
;
434 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
435 Pos
= OptSec
.length();
436 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
438 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
439 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
442 std::set
<int> KeepFDs
;
443 MergeKeepFdsFromConfiguration(KeepFDs
);
445 if (pipe(Pipes
) != 0) {
446 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
449 if (InfoFD
!= (unsigned)Pipes
[0])
450 SetCloseExec(Pipes
[0],true);
452 KeepFDs
.insert(Pipes
[0]);
455 SetCloseExec(Pipes
[1],true);
457 // Purified Fork for running the script
458 pid_t Process
= ExecFork(KeepFDs
);
462 dup2(Pipes
[0], InfoFD
);
463 SetCloseExec(STDOUT_FILENO
,false);
464 SetCloseExec(STDIN_FILENO
,false);
465 SetCloseExec(STDERR_FILENO
,false);
468 strprintf(hookfd
, "%d", InfoFD
);
469 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
471 dpkgChrootDirectory();
475 Args
[2] = Opts
->Value
.c_str();
477 execv(Args
[0],(char **)Args
);
481 FILE *F
= fdopen(Pipes
[1],"w");
483 result
= _error
->Errno("fdopen","Faild to open new FD");
487 // Feed it the filenames.
490 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
492 // Only deal with packages to be installed from .deb
493 if (I
->Op
!= Item::Install
)
497 if (I
->File
[0] != '/')
500 /* Feed the filename of each package that is pending install
502 fprintf(F
,"%s\n",I
->File
.c_str());
508 SendPkgsInfo(F
, Version
);
512 // Clean up the sub process
513 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
514 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
518 signal(SIGPIPE
, old_sigpipe
);
523 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
524 // ---------------------------------------------------------------------
527 void pkgDPkgPM::DoStdin(int master
)
529 unsigned char input_buf
[256] = {0,};
530 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
532 FileFd::Write(master
, input_buf
, len
);
534 d
->stdin_is_dev_null
= true;
537 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
538 // ---------------------------------------------------------------------
540 * read the terminal pty and write log
542 void pkgDPkgPM::DoTerminalPty(int master
)
544 unsigned char term_buf
[1024] = {0,0, };
546 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
547 if(len
== -1 && errno
== EIO
)
549 // this happens when the child is about to exit, we
550 // give it time to actually exit, otherwise we run
551 // into a race so we sleep for half a second.
552 struct timespec sleepfor
= { 0, 500000000 };
553 nanosleep(&sleepfor
, NULL
);
558 FileFd::Write(1, term_buf
, len
);
560 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
563 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
564 // ---------------------------------------------------------------------
567 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
569 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
571 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
573 /* dpkg sends strings like this:
574 'status: <pkg>: <pkg qstate>'
575 'status: <pkg>:<arch>: <pkg qstate>'
577 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
578 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
581 // we need to split on ": " (note the appended space) as the ':' is
582 // part of the pkgname:arch information that dpkg sends
584 // A dpkg error message may contain additional ":" (like
585 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
586 // so we need to ensure to not split too much
587 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
591 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
595 // build the (prefix, pkgname, action) tuple, position of this
596 // is different for "processing" or "status" messages
597 std::string prefix
= APT::String::Strip(list
[0]);
601 // "processing" has the form "processing: action: pkg or trigger"
602 // with action = ["install", "upgrade", "configure", "remove", "purge",
603 // "disappear", "trigproc"]
604 if (prefix
== "processing")
606 pkgname
= APT::String::Strip(list
[2]);
607 action
= APT::String::Strip(list
[1]);
608 // we don't care for the difference (as dpkg doesn't really either)
609 if (action
== "upgrade")
612 // "status" has the form: "status: pkg: state"
613 // with state in ["half-installed", "unpacked", "half-configured",
614 // "installed", "config-files", "not-installed"]
615 else if (prefix
== "status")
617 pkgname
= APT::String::Strip(list
[1]);
618 action
= APT::String::Strip(list
[2]);
621 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
626 /* handle the special cases first:
628 errors look like this:
629 '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
630 and conffile-prompt like this
631 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
633 if (prefix
== "status")
635 if(action
== "error")
637 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
640 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
643 else if(action
== "conffile-prompt")
645 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
651 // at this point we know that we should have a valid pkgname, so build all
654 // dpkg does not always send "pkgname:arch" so we add it here if needed
655 if (pkgname
.find(":") == std::string::npos
)
657 // find the package in the group that is touched by dpkg
658 // if there are multiple pkgs dpkg would send us a full pkgname:arch
659 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
660 if (Grp
.end() == false)
662 pkgCache::PkgIterator P
= Grp
.PackageList();
663 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
665 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
667 pkgname
= P
.FullName();
674 const char* const pkg
= pkgname
.c_str();
675 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
676 std::string arch
= "";
677 if (pkgname
.find(":") != string::npos
)
678 arch
= StringSplit(pkgname
, ":")[1];
679 std::string i18n_pkgname
= pkgname
;
680 if (arch
.size() != 0)
681 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
683 // 'processing' from dpkg looks like
684 // 'processing: action: pkg'
685 if(prefix
== "processing")
687 const std::pair
<const char *, const char *> * const iter
=
688 std::find_if(PackageProcessingOpsBegin
,
689 PackageProcessingOpsEnd
,
690 MatchProcessingOp(action
.c_str()));
691 if(iter
== PackageProcessingOpsEnd
)
694 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
698 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
699 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
701 // FIXME: this needs a muliarch testcase
702 // FIXME2: is "pkgname" here reliable with dpkg only sending us
704 if (action
== "disappear")
705 handleDisappearAction(pkgname
);
709 if (prefix
== "status")
711 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
712 if(PackageOpsDone
[pkg
] < states
.size())
714 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
715 if (next_action
&& Debug
== true)
716 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
717 << " action: " << action
<< " (expected: '" << next_action
<< "' "
718 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
720 // check if the package moved to the next dpkg state
721 if(next_action
&& (action
== next_action
))
723 // only read the translation if there is actually a next action
724 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
726 // we moved from one dpkg state to a new one, report that
727 ++PackageOpsDone
[pkg
];
731 strprintf(msg
, translation
, i18n_pkgname
.c_str());
732 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
738 // DPkgPM::handleDisappearAction /*{{{*/
739 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
741 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
742 if (unlikely(Pkg
.end() == true))
745 // record the package name for display and stuff later
746 disappearedPkgs
.insert(Pkg
.FullName(true));
748 // the disappeared package was auto-installed - nothing to do
749 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
751 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
752 if (unlikely(PkgVer
.end() == true))
754 /* search in the list of dependencies for (Pre)Depends,
755 check if this dependency has a Replaces on our package
756 and if so transfer the manual installed flag to it */
757 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
759 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
760 Dep
->Type
!= pkgCache::Dep::PreDepends
)
762 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
763 if (unlikely(Tar
.end() == true))
765 // the package is already marked as manual
766 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
768 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
769 if (TarVer
.end() == true)
771 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
773 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
775 if (Pkg
!= Rep
.TargetPkg())
777 // okay, they are strongly connected - transfer manual-bit
779 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
780 Cache
[Tar
].Flags
&= ~Flag::Auto
;
786 // DPkgPM::DoDpkgStatusFd /*{{{*/
787 // ---------------------------------------------------------------------
790 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
795 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
796 d
->dpkgbuf_pos
+= len
;
800 // process line by line if we have a buffer
802 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
805 ProcessDpkgStatusLine(p
);
806 p
=q
+1; // continue with next line
809 // now move the unprocessed bits (after the final \n that is now a 0x0)
810 // to the start and update d->dpkgbuf_pos
811 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
815 // we are interessted in the first char *after* 0x0
818 // move the unprocessed tail to the start and update pos
819 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
820 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
823 // DPkgPM::WriteHistoryTag /*{{{*/
824 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
826 size_t const length
= value
.length();
829 // poor mans rstrip(", ")
830 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
831 value
.erase(length
- 2, 2);
832 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
834 // DPkgPM::OpenLog /*{{{*/
835 bool pkgDPkgPM::OpenLog()
837 string
const logdir
= _config
->FindDir("Dir::Log");
838 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
839 // FIXME: use a better string after freeze
840 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
844 time_t const t
= time(NULL
);
845 struct tm
const * const tmp
= localtime(&t
);
846 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
849 string
const logfile_name
= flCombine(logdir
,
850 _config
->Find("Dir::Log::Terminal"));
851 if (!logfile_name
.empty())
853 d
->term_out
= fopen(logfile_name
.c_str(),"a");
854 if (d
->term_out
== NULL
)
855 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
856 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
857 SetCloseExec(fileno(d
->term_out
), true);
858 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
860 struct passwd
*pw
= getpwnam("root");
861 struct group
*gr
= getgrnam("adm");
862 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
863 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
865 if (chmod(logfile_name
.c_str(), 0640) != 0)
866 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
867 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
870 // write your history
871 string
const history_name
= flCombine(logdir
,
872 _config
->Find("Dir::Log::History"));
873 if (!history_name
.empty())
875 d
->history_out
= fopen(history_name
.c_str(),"a");
876 if (d
->history_out
== NULL
)
877 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
878 SetCloseExec(fileno(d
->history_out
), true);
879 chmod(history_name
.c_str(), 0644);
880 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
881 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
882 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
884 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
886 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
887 if (Cache
[I
].NewInstall() == true)
888 HISTORYINFO(install
, CANDIDATE_AUTO
)
889 else if (Cache
[I
].ReInstall() == true)
890 HISTORYINFO(reinstall
, CANDIDATE
)
891 else if (Cache
[I
].Upgrade() == true)
892 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
893 else if (Cache
[I
].Downgrade() == true)
894 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
895 else if (Cache
[I
].Delete() == true)
896 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
900 line
->append(I
.FullName(false)).append(" (");
901 switch (infostring
) {
902 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
904 line
->append(Cache
[I
].CandVersion
);
905 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
906 line
->append(", automatic");
908 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
909 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
913 if (_config
->Exists("Commandline::AsString") == true)
914 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
915 WriteHistoryTag("Install", install
);
916 WriteHistoryTag("Reinstall", reinstall
);
917 WriteHistoryTag("Upgrade", upgrade
);
918 WriteHistoryTag("Downgrade",downgrade
);
919 WriteHistoryTag("Remove",remove
);
920 WriteHistoryTag("Purge",purge
);
921 fflush(d
->history_out
);
927 // DPkg::CloseLog /*{{{*/
928 bool pkgDPkgPM::CloseLog()
931 time_t t
= time(NULL
);
932 struct tm
*tmp
= localtime(&t
);
933 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
937 fprintf(d
->term_out
, "Log ended: ");
938 fprintf(d
->term_out
, "%s", timestr
);
939 fprintf(d
->term_out
, "\n");
946 if (disappearedPkgs
.empty() == false)
949 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
950 d
!= disappearedPkgs
.end(); ++d
)
952 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
953 disappear
.append(*d
);
955 disappear
.append(", ");
957 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
959 WriteHistoryTag("Disappeared", disappear
);
961 if (d
->dpkg_error
.empty() == false)
962 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
963 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
964 fclose(d
->history_out
);
966 d
->history_out
= NULL
;
973 // This implements a racy version of pselect for those architectures
974 // that don't have a working implementation.
975 // FIXME: Probably can be removed on Lenny+1
976 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
977 fd_set
*exceptfds
, const struct timespec
*timeout
,
978 const sigset_t
*sigmask
)
984 tv
.tv_sec
= timeout
->tv_sec
;
985 tv
.tv_usec
= timeout
->tv_nsec
/1000;
987 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
988 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
989 sigprocmask(SIG_SETMASK
, &origmask
, 0);
994 // DPkgPM::BuildPackagesProgressMap /*{{{*/
995 void pkgDPkgPM::BuildPackagesProgressMap()
997 // map the dpkg states to the operations that are performed
998 // (this is sorted in the same way as Item::Ops)
999 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1000 // Install operation
1002 {"half-installed", N_("Preparing %s")},
1003 {"unpacked", N_("Unpacking %s") },
1006 // Configure operation
1008 {"unpacked",N_("Preparing to configure %s") },
1009 {"half-configured", N_("Configuring %s") },
1010 { "installed", N_("Installed %s")},
1015 {"half-configured", N_("Preparing for removal of %s")},
1016 {"half-installed", N_("Removing %s")},
1017 {"config-files", N_("Removed %s")},
1022 {"config-files", N_("Preparing to completely remove %s")},
1023 {"not-installed", N_("Completely removed %s")},
1028 // init the PackageOps map, go over the list of packages that
1029 // that will be [installed|configured|removed|purged] and add
1030 // them to the PackageOps map (the dpkg states it goes through)
1031 // and the PackageOpsTranslations (human readable strings)
1032 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1034 if((*I
).Pkg
.end() == true)
1037 string
const name
= (*I
).Pkg
.FullName();
1038 PackageOpsDone
[name
] = 0;
1039 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1041 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1045 /* one extra: We don't want the progress bar to reach 100%, especially not
1046 if we call dpkg --configure --pending and process a bunch of triggers
1047 while showing 100%. Also, spindown takes a while, so never reaching 100%
1048 is way more correct than reaching 100% while still doing stuff even if
1049 doing it this way is slightly bending the rules */
1053 bool pkgDPkgPM::Go(int StatusFd
)
1055 APT::Progress::PackageManager
*progress
= NULL
;
1057 progress
= APT::Progress::PackageManagerProgressFactory();
1059 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1061 return Go(progress
);
1064 void pkgDPkgPM::StartPtyMagic()
1066 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1069 if (d
->slave
!= NULL
)
1075 if (isatty(STDIN_FILENO
) == 0)
1076 d
->direct_stdin
= true;
1078 _error
->PushToStack();
1080 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1081 if (d
->master
== -1)
1082 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1083 else if (unlockpt(d
->master
) == -1)
1084 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1087 char const * const slave_name
= ptsname(d
->master
);
1088 if (slave_name
== NULL
)
1089 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1092 d
->slave
= strdup(slave_name
);
1093 if (d
->slave
== NULL
)
1094 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1095 else if (grantpt(d
->master
) == -1)
1096 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1097 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1099 d
->tt_is_valid
= true;
1100 struct termios raw_tt
;
1101 // copy window size of stdout if its a 'good' terminal
1102 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1105 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1106 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1107 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1108 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1110 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1111 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1115 raw_tt
.c_lflag
&= ~ECHO
;
1116 raw_tt
.c_lflag
|= ISIG
;
1117 // block SIGTTOU during tcsetattr to prevent a hang if
1118 // the process is a member of the background process group
1119 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1120 sigemptyset(&d
->sigmask
);
1121 sigaddset(&d
->sigmask
, SIGTTOU
);
1122 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1123 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1124 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1125 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1128 if (d
->slave
!= NULL
)
1130 /* on linux, closing (and later reopening) all references to the slave
1131 makes the slave a death end, so we open it here to have one open all
1132 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1133 on kfreebsd we get an incorrect ("step like") output then while it has
1134 no problem with closing all references… so to avoid platform specific
1135 code here we combine both and be happy once more */
1136 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1141 if (_error
->PendingError() == true)
1143 if (d
->master
!= -1)
1148 if (d
->slave
!= NULL
)
1153 _error
->DumpErrors(std::cerr
);
1155 _error
->RevertToStack();
1157 void pkgDPkgPM::SetupSlavePtyMagic()
1159 if(d
->master
== -1 || d
->slave
== NULL
)
1162 if (close(d
->master
) == -1)
1163 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1166 _error
->FatalE("setsid", "Starting a new session for child failed!");
1168 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1170 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1171 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1172 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1175 unsigned short i
= 0;
1176 if (d
->direct_stdin
== true)
1179 if (dup2(slaveFd
, i
) == -1)
1180 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1182 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1183 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1189 void pkgDPkgPM::StopPtyMagic()
1191 if (d
->slave
!= NULL
)
1194 if (d
->protect_slave_from_dying
!= -1)
1196 close(d
->protect_slave_from_dying
);
1197 d
->protect_slave_from_dying
= -1;
1201 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1202 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1208 // DPkgPM::Go - Run the sequence /*{{{*/
1209 // ---------------------------------------------------------------------
1210 /* This globs the operations and calls dpkg
1212 * If it is called with a progress object apt will report the install
1213 * progress to this object. It maps the dpkg states a package goes
1214 * through to human readable (and i10n-able)
1215 * names and calculates a percentage for each step.
1217 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1219 pkgPackageManager::SigINTStop
= false;
1220 d
->progress
= progress
;
1222 // Generate the base argument list for dpkg
1223 unsigned long StartSize
= 0;
1224 std::vector
<const char *> Args
;
1225 std::string DpkgExecutable
= getDpkgExecutable();
1226 Args
.push_back(DpkgExecutable
.c_str());
1227 StartSize
+= DpkgExecutable
.length();
1229 // Stick in any custom dpkg options
1230 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1234 for (; Opts
!= 0; Opts
= Opts
->Next
)
1236 if (Opts
->Value
.empty() == true)
1238 Args
.push_back(Opts
->Value
.c_str());
1239 StartSize
+= Opts
->Value
.length();
1243 size_t const BaseArgs
= Args
.size();
1244 // we need to detect if we can qualify packages with the architecture or not
1245 Args
.push_back("--assert-multi-arch");
1246 Args
.push_back(NULL
);
1248 pid_t dpkgAssertMultiArch
= ExecFork();
1249 if (dpkgAssertMultiArch
== 0)
1251 dpkgChrootDirectory();
1252 // redirect everything to the ultimate sink as we only need the exit-status
1253 int const nullfd
= open("/dev/null", O_RDONLY
);
1254 dup2(nullfd
, STDIN_FILENO
);
1255 dup2(nullfd
, STDOUT_FILENO
);
1256 dup2(nullfd
, STDERR_FILENO
);
1257 execvp(Args
[0], (char**) &Args
[0]);
1258 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1265 // FIXME: do we really need this limit when we have MaxArgBytes?
1266 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1268 // try to figure out the max environment size
1269 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1272 OSArgMax
-= EnvironmentSize() - 2*1024;
1273 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1274 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1276 if (RunScripts("DPkg::Pre-Invoke") == false)
1279 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1282 // support subpressing of triggers processing for special
1283 // cases like d-i that runs the triggers handling manually
1284 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1285 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1286 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1289 BuildPackagesProgressMap();
1291 d
->stdin_is_dev_null
= false;
1296 bool dpkgMultiArch
= false;
1297 if (dpkgAssertMultiArch
> 0)
1300 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1304 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1307 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1308 dpkgMultiArch
= true;
1311 // start pty magic before the loop
1314 // Tell the progress that its starting and fork dpkg
1315 d
->progress
->Start(d
->master
);
1317 // this loop is runs once per dpkg operation
1318 vector
<Item
>::const_iterator I
= List
.begin();
1319 while (I
!= List
.end())
1321 // Do all actions with the same Op in one run
1322 vector
<Item
>::const_iterator J
= I
;
1323 if (TriggersPending
== true)
1324 for (; J
!= List
.end(); ++J
)
1328 if (J
->Op
!= Item::TriggersPending
)
1330 vector
<Item
>::const_iterator T
= J
+ 1;
1331 if (T
!= List
.end() && T
->Op
== I
->Op
)
1336 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1339 // keep track of allocated strings for multiarch package names
1340 std::vector
<char *> Packages
;
1342 // start with the baseset of arguments
1343 unsigned long Size
= StartSize
;
1344 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1346 // Now check if we are within the MaxArgs limit
1348 // this code below is problematic, because it may happen that
1349 // the argument list is split in a way that A depends on B
1350 // and they are in the same "--configure A B" run
1351 // - with the split they may now be configured in different
1352 // runs, using Immediate-Configure-All can help prevent this.
1353 if (J
- I
> (signed)MaxArgs
)
1356 unsigned long const size
= MaxArgs
+ 10;
1358 Packages
.reserve(size
);
1362 unsigned long const size
= (J
- I
) + 10;
1364 Packages
.reserve(size
);
1369 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1371 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1372 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1374 ADDARGC("--status-fd");
1375 char status_fd_buf
[20];
1376 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1377 ADDARG(status_fd_buf
);
1378 unsigned long const Op
= I
->Op
;
1383 ADDARGC("--force-depends");
1384 ADDARGC("--force-remove-essential");
1385 ADDARGC("--remove");
1389 ADDARGC("--force-depends");
1390 ADDARGC("--force-remove-essential");
1394 case Item::Configure
:
1395 ADDARGC("--configure");
1398 case Item::ConfigurePending
:
1399 ADDARGC("--configure");
1400 ADDARGC("--pending");
1403 case Item::TriggersPending
:
1404 ADDARGC("--triggers-only");
1405 ADDARGC("--pending");
1409 ADDARGC("--unpack");
1410 ADDARGC("--auto-deconfigure");
1414 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1415 I
->Op
!= Item::ConfigurePending
)
1417 ADDARGC("--no-triggers");
1421 // Write in the file or package names
1422 if (I
->Op
== Item::Install
)
1424 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1426 if (I
->File
[0] != '/')
1427 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1428 Args
.push_back(I
->File
.c_str());
1429 Size
+= I
->File
.length();
1434 string
const nativeArch
= _config
->Find("APT::Architecture");
1435 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1436 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1438 if((*I
).Pkg
.end() == true)
1440 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1442 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1443 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1444 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1445 strcmp(I
->Pkg
.Arch(), "none") == 0))
1447 char const * const name
= I
->Pkg
.Name();
1452 pkgCache::VerIterator PkgVer
;
1453 std::string name
= I
->Pkg
.Name();
1454 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1456 PkgVer
= I
->Pkg
.CurrentVer();
1457 if(PkgVer
.end() == true)
1458 PkgVer
= FindNowVersion(I
->Pkg
);
1461 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1462 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1463 ; // never arch-qualify a package without an arch
1464 else if (PkgVer
.end() == false)
1465 name
.append(":").append(PkgVer
.Arch());
1467 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1468 char * const fullname
= strdup(name
.c_str());
1469 Packages
.push_back(fullname
);
1473 // skip configure action if all sheduled packages disappeared
1474 if (oldSize
== Size
)
1481 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1483 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1484 a
!= Args
.end(); ++a
)
1487 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1488 p
!= Packages
.end(); ++p
)
1493 Args
.push_back(NULL
);
1499 /* Mask off sig int/quit. We do this because dpkg also does when
1500 it forks scripts. What happens is that when you hit ctrl-c it sends
1501 it to all processes in the group. Since dpkg ignores the signal
1502 it doesn't die but we do! So we must also ignore it */
1503 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1504 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1506 // Check here for any SIGINT
1507 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1511 // ignore SIGHUP as well (debian #463030)
1512 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1515 d
->progress
->StartDpkg();
1516 std::set
<int> KeepFDs
;
1517 KeepFDs
.insert(fd
[1]);
1518 MergeKeepFdsFromConfiguration(KeepFDs
);
1519 pid_t Child
= ExecFork(KeepFDs
);
1522 // This is the child
1523 SetupSlavePtyMagic();
1524 close(fd
[0]); // close the read end of the pipe
1526 dpkgChrootDirectory();
1528 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1531 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1535 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1538 // Discard everything in stdin before forking dpkg
1539 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1542 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1544 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1548 execvp(Args
[0], (char**) &Args
[0]);
1549 cerr
<< "Could not exec dpkg!" << endl
;
1554 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1560 // we read from dpkg here
1561 int const _dpkgin
= fd
[0];
1562 close(fd
[1]); // close the write end of the pipe
1565 sigemptyset(&d
->sigmask
);
1566 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1568 /* free vectors (and therefore memory) as we don't need the included data anymore */
1569 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1570 p
!= Packages
.end(); ++p
)
1574 // the result of the waitpid call
1577 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1579 // FIXME: move this to a function or something, looks ugly here
1580 // error handling, waitpid returned -1
1583 RunScripts("DPkg::Post-Invoke");
1585 // Restore sig int/quit
1586 signal(SIGQUIT
,old_SIGQUIT
);
1587 signal(SIGINT
,old_SIGINT
);
1589 signal(SIGHUP
,old_SIGHUP
);
1590 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1593 // wait for input or output here
1595 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1596 FD_SET(STDIN_FILENO
, &rfds
);
1597 FD_SET(_dpkgin
, &rfds
);
1599 FD_SET(d
->master
, &rfds
);
1601 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1602 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1603 &tv
, &d
->original_sigmask
);
1604 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1605 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1606 NULL
, &tv
, &d
->original_sigmask
);
1607 d
->progress
->Pulse();
1608 if (select_ret
== 0)
1610 else if (select_ret
< 0 && errno
== EINTR
)
1612 else if (select_ret
< 0)
1614 perror("select() returned error");
1618 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1619 DoTerminalPty(d
->master
);
1620 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1622 if(FD_ISSET(_dpkgin
, &rfds
))
1623 DoDpkgStatusFd(_dpkgin
);
1627 // Restore sig int/quit
1628 signal(SIGQUIT
,old_SIGQUIT
);
1629 signal(SIGINT
,old_SIGINT
);
1631 signal(SIGHUP
,old_SIGHUP
);
1632 // Check for an error code.
1633 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1635 // if it was set to "keep-dpkg-runing" then we won't return
1636 // here but keep the loop going and just report it as a error
1638 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1640 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1641 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1642 else if (WIFEXITED(Status
) != 0)
1643 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1645 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1646 _error
->Error("%s", d
->dpkg_error
.c_str());
1652 // dpkg is done at this point
1653 d
->progress
->Stop();
1657 if (pkgPackageManager::SigINTStop
)
1658 _error
->Warning(_("Operation was interrupted before it could finish"));
1660 if (RunScripts("DPkg::Post-Invoke") == false)
1663 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1665 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1666 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1667 unlink(oldpkgcache
.c_str()) == 0)
1669 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1670 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1672 _error
->PushToStack();
1673 pkgCacheFile CacheFile
;
1674 CacheFile
.BuildCaches(NULL
, true);
1675 _error
->RevertToStack();
1680 Cache
.writeStateFile(NULL
);
1681 return d
->dpkg_error
.empty();
1684 void SigINT(int /*sig*/) {
1685 pkgPackageManager::SigINTStop
= true;
1688 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1689 // ---------------------------------------------------------------------
1691 void pkgDPkgPM::Reset()
1693 List
.erase(List
.begin(),List
.end());
1696 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1697 // ---------------------------------------------------------------------
1699 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1701 // If apport doesn't exist or isn't installed do nothing
1702 // This e.g. prevents messages in 'universes' without apport
1703 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1704 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1707 string pkgname
, reportfile
, pkgver
, arch
;
1708 string::size_type pos
;
1711 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1713 std::clog
<< "configured to not write apport reports" << std::endl
;
1717 // only report the first errors
1718 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1720 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1724 // check if its not a follow up error
1725 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1726 if(strstr(errormsg
, needle
) != NULL
) {
1727 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1731 // do not report disk-full failures
1732 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1733 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1737 // do not report out-of-memory failures
1738 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1739 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1740 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1744 // do not report bugs regarding inaccessible local files
1745 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1746 strstr(errormsg
, "cannot access archive") != NULL
) {
1747 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1751 // do not report errors encountered when decompressing packages
1752 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1753 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1757 // do not report dpkg I/O errors, this is a format string, so we compare
1758 // the prefix and the suffix of the error with the dpkg error message
1759 vector
<string
> io_errors
;
1760 io_errors
.push_back(string("failed to read"));
1761 io_errors
.push_back(string("failed to write"));
1762 io_errors
.push_back(string("failed to seek"));
1763 io_errors
.push_back(string("unexpected end of file or stream"));
1765 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1767 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1768 if (list
.size() > 1) {
1769 // we need to split %s, VectorizeString only allows char so we need
1770 // to kill the "s" manually
1771 if (list
[1].size() > 1) {
1772 list
[1].erase(0, 1);
1773 if(strstr(errormsg
, list
[0].c_str()) &&
1774 strstr(errormsg
, list
[1].c_str())) {
1775 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1782 // get the pkgname and reportfile
1783 pkgname
= flNotDir(pkgpath
);
1784 pos
= pkgname
.find('_');
1785 if(pos
!= string::npos
)
1786 pkgname
= pkgname
.substr(0, pos
);
1788 // find the package versin and source package name
1789 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1790 if (Pkg
.end() == true)
1792 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1793 if (Ver
.end() == true)
1795 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1797 // if the file exists already, we check:
1798 // - if it was reported already (touched by apport).
1799 // If not, we do nothing, otherwise
1800 // we overwrite it. This is the same behaviour as apport
1801 // - if we have a report with the same pkgversion already
1803 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1804 if(FileExists(reportfile
))
1809 // check atime/mtime
1810 stat(reportfile
.c_str(), &buf
);
1811 if(buf
.st_mtime
> buf
.st_atime
)
1814 // check if the existing report is the same version
1815 report
= fopen(reportfile
.c_str(),"r");
1816 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1818 if(strstr(strbuf
,"Package:") == strbuf
)
1820 char pkgname
[255], version
[255];
1821 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1822 if(strcmp(pkgver
.c_str(), version
) == 0)
1832 // now write the report
1833 arch
= _config
->Find("APT::Architecture");
1834 report
= fopen(reportfile
.c_str(),"w");
1837 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1838 chmod(reportfile
.c_str(), 0);
1840 chmod(reportfile
.c_str(), 0600);
1841 fprintf(report
, "ProblemType: Package\n");
1842 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1843 time_t now
= time(NULL
);
1844 fprintf(report
, "Date: %s" , ctime(&now
));
1845 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1846 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1847 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1849 // ensure that the log is flushed
1851 fflush(d
->term_out
);
1853 // attach terminal log it if we have it
1854 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1855 if (!logfile_name
.empty())
1859 fprintf(report
, "DpkgTerminalLog:\n");
1860 log
= fopen(logfile_name
.c_str(),"r");
1864 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1865 fprintf(report
, " %s", buf
);
1866 fprintf(report
, " \n");
1871 // attach history log it if we have it
1872 string histfile_name
= _config
->FindFile("Dir::Log::History");
1873 if (!histfile_name
.empty())
1875 fprintf(report
, "DpkgHistoryLog:\n");
1876 FILE* log
= fopen(histfile_name
.c_str(),"r");
1880 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1881 fprintf(report
, " %s", buf
);
1886 // log the ordering, see dpkgpm.h and the "Ops" enum there
1887 const char *ops_str
[] = {
1895 fprintf(report
, "AptOrdering:\n");
1896 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1897 if ((*I
).Pkg
!= NULL
)
1898 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1900 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1902 // attach dmesg log (to learn about segfaults)
1903 if (FileExists("/bin/dmesg"))
1905 fprintf(report
, "Dmesg:\n");
1906 FILE *log
= popen("/bin/dmesg","r");
1910 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1911 fprintf(report
, " %s", buf
);
1916 // attach df -l log (to learn about filesystem status)
1917 if (FileExists("/bin/df"))
1920 fprintf(report
, "Df:\n");
1921 FILE *log
= popen("/bin/df -l","r");
1925 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1926 fprintf(report
, " %s", buf
);