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
)
208 if (F
->Archive
!= 0 && strcmp(F
.Archive(), "now") == 0)
214 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
215 // ---------------------------------------------------------------------
217 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
218 : pkgPackageManager(Cache
), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
220 d
= new pkgDPkgPMPrivate();
223 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
224 // ---------------------------------------------------------------------
226 pkgDPkgPM::~pkgDPkgPM()
231 // DPkgPM::Install - Install a package /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Add an install operation to the sequence list */
234 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
236 if (File
.empty() == true || Pkg
.end() == true)
237 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
239 // If the filename string begins with DPkg::Chroot-Directory, return the
240 // substr that is within the chroot so dpkg can access it.
241 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
242 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
244 size_t len
= chrootdir
.length();
245 if (chrootdir
.at(len
- 1) == '/')
247 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
250 List
.push_back(Item(Item::Install
,Pkg
,File
));
255 // DPkgPM::Configure - Configure a package /*{{{*/
256 // ---------------------------------------------------------------------
257 /* Add a configure operation to the sequence list */
258 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
260 if (Pkg
.end() == true)
263 List
.push_back(Item(Item::Configure
, Pkg
));
265 // Use triggers for config calls if we configure "smart"
266 // as otherwise Pre-Depends will not be satisfied, see #526774
267 if (_config
->FindB("DPkg::TriggersPending", false) == true)
268 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
273 // DPkgPM::Remove - Remove a package /*{{{*/
274 // ---------------------------------------------------------------------
275 /* Add a remove operation to the sequence list */
276 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
278 if (Pkg
.end() == true)
282 List
.push_back(Item(Item::Purge
,Pkg
));
284 List
.push_back(Item(Item::Remove
,Pkg
));
288 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
289 // ---------------------------------------------------------------------
290 /* This is part of the helper script communication interface, it sends
291 very complete information down to the other end of the pipe.*/
292 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
294 return SendPkgsInfo(F
, 2);
296 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
298 // This version of APT supports only v3, so don't sent higher versions
300 fprintf(F
,"VERSION %u\n", Version
);
302 fprintf(F
,"VERSION 3\n");
304 /* Write out all of the configuration directives by walking the
305 configuration tree */
306 const Configuration::Item
*Top
= _config
->Tree(0);
309 if (Top
->Value
.empty() == false)
312 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
313 QuoteString(Top
->Value
,"\n").c_str());
322 while (Top
!= 0 && Top
->Next
== 0)
329 // Write out the package actions in order.
330 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
332 if(I
->Pkg
.end() == true)
335 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
337 fprintf(F
,"%s ",I
->Pkg
.Name());
339 // Current version which we are going to replace
340 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
341 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
342 CurVer
= FindNowVersion(I
->Pkg
);
344 if (CurVer
.end() == true)
349 fprintf(F
, "- - none ");
353 fprintf(F
, "%s ", CurVer
.VerStr());
355 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
358 // Show the compare operator between current and install version
359 if (S
.InstallVer
!= 0)
361 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
363 if (CurVer
.end() == false)
364 Comp
= InstVer
.CompareVer(CurVer
);
371 fprintf(F
, "%s ", InstVer
.VerStr());
373 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
380 fprintf(F
, "> - - none ");
383 // Show the filename/operation
384 if (I
->Op
== Item::Install
)
387 if (I
->File
[0] != '/')
388 fprintf(F
,"**ERROR**\n");
390 fprintf(F
,"%s\n",I
->File
.c_str());
392 else if (I
->Op
== Item::Configure
)
393 fprintf(F
,"**CONFIGURE**\n");
394 else if (I
->Op
== Item::Remove
||
395 I
->Op
== Item::Purge
)
396 fprintf(F
,"**REMOVE**\n");
404 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
405 // ---------------------------------------------------------------------
406 /* This looks for a list of scripts to run from the configuration file
407 each one is run and is fed on standard input a list of all .deb files
408 that are due to be installed. */
409 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
413 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
414 if (Opts
== 0 || Opts
->Child
== 0)
418 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
420 unsigned int Count
= 1;
421 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
423 if (Opts
->Value
.empty() == true)
426 if(_config
->FindB("Debug::RunScripts", false) == true)
427 std::clog
<< "Running external script with list of all .deb file: '"
428 << Opts
->Value
<< "'" << std::endl
;
430 // Determine the protocol version
431 string OptSec
= Opts
->Value
;
432 string::size_type Pos
;
433 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
434 Pos
= OptSec
.length();
435 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
437 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
438 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
441 std::set
<int> KeepFDs
;
442 MergeKeepFdsFromConfiguration(KeepFDs
);
444 if (pipe(Pipes
) != 0) {
445 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
448 if (InfoFD
!= (unsigned)Pipes
[0])
449 SetCloseExec(Pipes
[0],true);
451 KeepFDs
.insert(Pipes
[0]);
454 SetCloseExec(Pipes
[1],true);
456 // Purified Fork for running the script
457 pid_t Process
= ExecFork(KeepFDs
);
461 dup2(Pipes
[0], InfoFD
);
462 SetCloseExec(STDOUT_FILENO
,false);
463 SetCloseExec(STDIN_FILENO
,false);
464 SetCloseExec(STDERR_FILENO
,false);
467 strprintf(hookfd
, "%d", InfoFD
);
468 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
470 dpkgChrootDirectory();
474 Args
[2] = Opts
->Value
.c_str();
476 execv(Args
[0],(char **)Args
);
480 FILE *F
= fdopen(Pipes
[1],"w");
482 result
= _error
->Errno("fdopen","Faild to open new FD");
486 // Feed it the filenames.
489 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
491 // Only deal with packages to be installed from .deb
492 if (I
->Op
!= Item::Install
)
496 if (I
->File
[0] != '/')
499 /* Feed the filename of each package that is pending install
501 fprintf(F
,"%s\n",I
->File
.c_str());
507 SendPkgsInfo(F
, Version
);
511 // Clean up the sub process
512 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
513 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
517 signal(SIGPIPE
, old_sigpipe
);
522 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
523 // ---------------------------------------------------------------------
526 void pkgDPkgPM::DoStdin(int master
)
528 unsigned char input_buf
[256] = {0,};
529 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
531 FileFd::Write(master
, input_buf
, len
);
533 d
->stdin_is_dev_null
= true;
536 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
537 // ---------------------------------------------------------------------
539 * read the terminal pty and write log
541 void pkgDPkgPM::DoTerminalPty(int master
)
543 unsigned char term_buf
[1024] = {0,0, };
545 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
546 if(len
== -1 && errno
== EIO
)
548 // this happens when the child is about to exit, we
549 // give it time to actually exit, otherwise we run
550 // into a race so we sleep for half a second.
551 struct timespec sleepfor
= { 0, 500000000 };
552 nanosleep(&sleepfor
, NULL
);
557 FileFd::Write(1, term_buf
, len
);
559 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
562 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
563 // ---------------------------------------------------------------------
566 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
568 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
570 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
572 /* dpkg sends strings like this:
573 'status: <pkg>: <pkg qstate>'
574 'status: <pkg>:<arch>: <pkg qstate>'
576 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
577 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
580 // we need to split on ": " (note the appended space) as the ':' is
581 // part of the pkgname:arch information that dpkg sends
583 // A dpkg error message may contain additional ":" (like
584 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
585 // so we need to ensure to not split too much
586 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
590 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
594 // build the (prefix, pkgname, action) tuple, position of this
595 // is different for "processing" or "status" messages
596 std::string prefix
= APT::String::Strip(list
[0]);
600 // "processing" has the form "processing: action: pkg or trigger"
601 // with action = ["install", "upgrade", "configure", "remove", "purge",
602 // "disappear", "trigproc"]
603 if (prefix
== "processing")
605 pkgname
= APT::String::Strip(list
[2]);
606 action
= APT::String::Strip(list
[1]);
607 // we don't care for the difference (as dpkg doesn't really either)
608 if (action
== "upgrade")
611 // "status" has the form: "status: pkg: state"
612 // with state in ["half-installed", "unpacked", "half-configured",
613 // "installed", "config-files", "not-installed"]
614 else if (prefix
== "status")
616 pkgname
= APT::String::Strip(list
[1]);
617 action
= APT::String::Strip(list
[2]);
620 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
625 /* handle the special cases first:
627 errors look like this:
628 '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
629 and conffile-prompt like this
630 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
632 if (prefix
== "status")
634 if(action
== "error")
636 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
639 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
642 else if(action
== "conffile-prompt")
644 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
650 // at this point we know that we should have a valid pkgname, so build all
653 // dpkg does not always send "pkgname:arch" so we add it here if needed
654 if (pkgname
.find(":") == std::string::npos
)
656 // find the package in the group that is touched by dpkg
657 // if there are multiple pkgs dpkg would send us a full pkgname:arch
658 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
659 if (Grp
.end() == false)
661 pkgCache::PkgIterator P
= Grp
.PackageList();
662 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
664 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
666 pkgname
= P
.FullName();
673 const char* const pkg
= pkgname
.c_str();
674 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
675 std::string arch
= "";
676 if (pkgname
.find(":") != string::npos
)
677 arch
= StringSplit(pkgname
, ":")[1];
678 std::string i18n_pkgname
= pkgname
;
679 if (arch
.size() != 0)
680 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
682 // 'processing' from dpkg looks like
683 // 'processing: action: pkg'
684 if(prefix
== "processing")
686 const std::pair
<const char *, const char *> * const iter
=
687 std::find_if(PackageProcessingOpsBegin
,
688 PackageProcessingOpsEnd
,
689 MatchProcessingOp(action
.c_str()));
690 if(iter
== PackageProcessingOpsEnd
)
693 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
697 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
698 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
700 // FIXME: this needs a muliarch testcase
701 // FIXME2: is "pkgname" here reliable with dpkg only sending us
703 if (action
== "disappear")
704 handleDisappearAction(pkgname
);
708 if (prefix
== "status")
710 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
711 if(PackageOpsDone
[pkg
] < states
.size())
713 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
714 if (next_action
&& Debug
== true)
715 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
716 << " action: " << action
<< " (expected: '" << next_action
<< "' "
717 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
719 // check if the package moved to the next dpkg state
720 if(next_action
&& (action
== next_action
))
722 // only read the translation if there is actually a next action
723 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
725 // we moved from one dpkg state to a new one, report that
726 ++PackageOpsDone
[pkg
];
730 strprintf(msg
, translation
, i18n_pkgname
.c_str());
731 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
737 // DPkgPM::handleDisappearAction /*{{{*/
738 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
740 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
741 if (unlikely(Pkg
.end() == true))
744 // record the package name for display and stuff later
745 disappearedPkgs
.insert(Pkg
.FullName(true));
747 // the disappeared package was auto-installed - nothing to do
748 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
750 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
751 if (unlikely(PkgVer
.end() == true))
753 /* search in the list of dependencies for (Pre)Depends,
754 check if this dependency has a Replaces on our package
755 and if so transfer the manual installed flag to it */
756 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
758 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
759 Dep
->Type
!= pkgCache::Dep::PreDepends
)
761 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
762 if (unlikely(Tar
.end() == true))
764 // the package is already marked as manual
765 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
767 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
768 if (TarVer
.end() == true)
770 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
772 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
774 if (Pkg
!= Rep
.TargetPkg())
776 // okay, they are strongly connected - transfer manual-bit
778 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
779 Cache
[Tar
].Flags
&= ~Flag::Auto
;
785 // DPkgPM::DoDpkgStatusFd /*{{{*/
786 // ---------------------------------------------------------------------
789 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
794 len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
);
795 d
->dpkgbuf_pos
+= len
;
799 // process line by line if we have a buffer
801 while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
)
804 ProcessDpkgStatusLine(p
);
805 p
=q
+1; // continue with next line
808 // now move the unprocessed bits (after the final \n that is now a 0x0)
809 // to the start and update d->dpkgbuf_pos
810 p
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
);
814 // we are interessted in the first char *after* 0x0
817 // move the unprocessed tail to the start and update pos
818 memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
);
819 d
->dpkgbuf_pos
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
;
822 // DPkgPM::WriteHistoryTag /*{{{*/
823 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
825 size_t const length
= value
.length();
828 // poor mans rstrip(", ")
829 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
830 value
.erase(length
- 2, 2);
831 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
833 // DPkgPM::OpenLog /*{{{*/
834 bool pkgDPkgPM::OpenLog()
836 string
const logdir
= _config
->FindDir("Dir::Log");
837 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
838 // FIXME: use a better string after freeze
839 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
843 time_t const t
= time(NULL
);
844 struct tm
const * const tmp
= localtime(&t
);
845 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
848 string
const logfile_name
= flCombine(logdir
,
849 _config
->Find("Dir::Log::Terminal"));
850 if (!logfile_name
.empty())
852 d
->term_out
= fopen(logfile_name
.c_str(),"a");
853 if (d
->term_out
== NULL
)
854 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
855 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
856 SetCloseExec(fileno(d
->term_out
), true);
857 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
859 struct passwd
*pw
= getpwnam("root");
860 struct group
*gr
= getgrnam("adm");
861 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
862 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
864 if (chmod(logfile_name
.c_str(), 0640) != 0)
865 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
866 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
869 // write your history
870 string
const history_name
= flCombine(logdir
,
871 _config
->Find("Dir::Log::History"));
872 if (!history_name
.empty())
874 d
->history_out
= fopen(history_name
.c_str(),"a");
875 if (d
->history_out
== NULL
)
876 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
877 SetCloseExec(fileno(d
->history_out
), true);
878 chmod(history_name
.c_str(), 0644);
879 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
880 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
881 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
883 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
885 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
886 if (Cache
[I
].NewInstall() == true)
887 HISTORYINFO(install
, CANDIDATE_AUTO
)
888 else if (Cache
[I
].ReInstall() == true)
889 HISTORYINFO(reinstall
, CANDIDATE
)
890 else if (Cache
[I
].Upgrade() == true)
891 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
892 else if (Cache
[I
].Downgrade() == true)
893 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
894 else if (Cache
[I
].Delete() == true)
895 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
899 line
->append(I
.FullName(false)).append(" (");
900 switch (infostring
) {
901 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
903 line
->append(Cache
[I
].CandVersion
);
904 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
905 line
->append(", automatic");
907 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
908 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
912 if (_config
->Exists("Commandline::AsString") == true)
913 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
914 WriteHistoryTag("Install", install
);
915 WriteHistoryTag("Reinstall", reinstall
);
916 WriteHistoryTag("Upgrade", upgrade
);
917 WriteHistoryTag("Downgrade",downgrade
);
918 WriteHistoryTag("Remove",remove
);
919 WriteHistoryTag("Purge",purge
);
920 fflush(d
->history_out
);
926 // DPkg::CloseLog /*{{{*/
927 bool pkgDPkgPM::CloseLog()
930 time_t t
= time(NULL
);
931 struct tm
*tmp
= localtime(&t
);
932 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
936 fprintf(d
->term_out
, "Log ended: ");
937 fprintf(d
->term_out
, "%s", timestr
);
938 fprintf(d
->term_out
, "\n");
945 if (disappearedPkgs
.empty() == false)
948 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
949 d
!= disappearedPkgs
.end(); ++d
)
951 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
952 disappear
.append(*d
);
954 disappear
.append(", ");
956 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
958 WriteHistoryTag("Disappeared", disappear
);
960 if (d
->dpkg_error
.empty() == false)
961 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
962 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
963 fclose(d
->history_out
);
965 d
->history_out
= NULL
;
972 // This implements a racy version of pselect for those architectures
973 // that don't have a working implementation.
974 // FIXME: Probably can be removed on Lenny+1
975 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
976 fd_set
*exceptfds
, const struct timespec
*timeout
,
977 const sigset_t
*sigmask
)
983 tv
.tv_sec
= timeout
->tv_sec
;
984 tv
.tv_usec
= timeout
->tv_nsec
/1000;
986 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
987 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
988 sigprocmask(SIG_SETMASK
, &origmask
, 0);
993 // DPkgPM::BuildPackagesProgressMap /*{{{*/
994 void pkgDPkgPM::BuildPackagesProgressMap()
996 // map the dpkg states to the operations that are performed
997 // (this is sorted in the same way as Item::Ops)
998 static const struct DpkgState DpkgStatesOpMap
[][7] = {
1001 {"half-installed", N_("Preparing %s")},
1002 {"unpacked", N_("Unpacking %s") },
1005 // Configure operation
1007 {"unpacked",N_("Preparing to configure %s") },
1008 {"half-configured", N_("Configuring %s") },
1009 { "installed", N_("Installed %s")},
1014 {"half-configured", N_("Preparing for removal of %s")},
1015 {"half-installed", N_("Removing %s")},
1016 {"config-files", N_("Removed %s")},
1021 {"config-files", N_("Preparing to completely remove %s")},
1022 {"not-installed", N_("Completely removed %s")},
1027 // init the PackageOps map, go over the list of packages that
1028 // that will be [installed|configured|removed|purged] and add
1029 // them to the PackageOps map (the dpkg states it goes through)
1030 // and the PackageOpsTranslations (human readable strings)
1031 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1033 if((*I
).Pkg
.end() == true)
1036 string
const name
= (*I
).Pkg
.FullName();
1037 PackageOpsDone
[name
] = 0;
1038 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1040 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1044 /* one extra: We don't want the progress bar to reach 100%, especially not
1045 if we call dpkg --configure --pending and process a bunch of triggers
1046 while showing 100%. Also, spindown takes a while, so never reaching 100%
1047 is way more correct than reaching 100% while still doing stuff even if
1048 doing it this way is slightly bending the rules */
1052 bool pkgDPkgPM::Go(int StatusFd
)
1054 APT::Progress::PackageManager
*progress
= NULL
;
1056 progress
= APT::Progress::PackageManagerProgressFactory();
1058 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1060 return Go(progress
);
1063 void pkgDPkgPM::StartPtyMagic()
1065 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1068 if (d
->slave
!= NULL
)
1074 if (isatty(STDIN_FILENO
) == 0)
1075 d
->direct_stdin
= true;
1077 _error
->PushToStack();
1079 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1080 if (d
->master
== -1)
1081 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1082 else if (unlockpt(d
->master
) == -1)
1083 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1086 char const * const slave_name
= ptsname(d
->master
);
1087 if (slave_name
== NULL
)
1088 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1091 d
->slave
= strdup(slave_name
);
1092 if (d
->slave
== NULL
)
1093 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1094 else if (grantpt(d
->master
) == -1)
1095 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1096 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1098 d
->tt_is_valid
= true;
1099 struct termios raw_tt
;
1100 // copy window size of stdout if its a 'good' terminal
1101 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1104 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1105 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1106 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1107 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1109 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1110 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1114 raw_tt
.c_lflag
&= ~ECHO
;
1115 raw_tt
.c_lflag
|= ISIG
;
1116 // block SIGTTOU during tcsetattr to prevent a hang if
1117 // the process is a member of the background process group
1118 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1119 sigemptyset(&d
->sigmask
);
1120 sigaddset(&d
->sigmask
, SIGTTOU
);
1121 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1122 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1123 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1124 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1127 if (d
->slave
!= NULL
)
1129 /* on linux, closing (and later reopening) all references to the slave
1130 makes the slave a death end, so we open it here to have one open all
1131 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1132 on kfreebsd we get an incorrect ("step like") output then while it has
1133 no problem with closing all references… so to avoid platform specific
1134 code here we combine both and be happy once more */
1135 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1140 if (_error
->PendingError() == true)
1142 if (d
->master
!= -1)
1147 if (d
->slave
!= NULL
)
1152 _error
->DumpErrors(std::cerr
);
1154 _error
->RevertToStack();
1156 void pkgDPkgPM::SetupSlavePtyMagic()
1158 if(d
->master
== -1 || d
->slave
== NULL
)
1161 if (close(d
->master
) == -1)
1162 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1165 _error
->FatalE("setsid", "Starting a new session for child failed!");
1167 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1169 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1170 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1171 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1174 unsigned short i
= 0;
1175 if (d
->direct_stdin
== true)
1178 if (dup2(slaveFd
, i
) == -1)
1179 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1181 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1182 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1188 void pkgDPkgPM::StopPtyMagic()
1190 if (d
->slave
!= NULL
)
1193 if (d
->protect_slave_from_dying
!= -1)
1195 close(d
->protect_slave_from_dying
);
1196 d
->protect_slave_from_dying
= -1;
1200 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1201 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1207 // DPkgPM::Go - Run the sequence /*{{{*/
1208 // ---------------------------------------------------------------------
1209 /* This globs the operations and calls dpkg
1211 * If it is called with a progress object apt will report the install
1212 * progress to this object. It maps the dpkg states a package goes
1213 * through to human readable (and i10n-able)
1214 * names and calculates a percentage for each step.
1216 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1218 pkgPackageManager::SigINTStop
= false;
1219 d
->progress
= progress
;
1221 // Generate the base argument list for dpkg
1222 unsigned long StartSize
= 0;
1223 std::vector
<const char *> Args
;
1224 std::string DpkgExecutable
= getDpkgExecutable();
1225 Args
.push_back(DpkgExecutable
.c_str());
1226 StartSize
+= DpkgExecutable
.length();
1228 // Stick in any custom dpkg options
1229 Configuration::Item
const *Opts
= _config
->Tree("DPkg::Options");
1233 for (; Opts
!= 0; Opts
= Opts
->Next
)
1235 if (Opts
->Value
.empty() == true)
1237 Args
.push_back(Opts
->Value
.c_str());
1238 StartSize
+= Opts
->Value
.length();
1242 size_t const BaseArgs
= Args
.size();
1243 // we need to detect if we can qualify packages with the architecture or not
1244 Args
.push_back("--assert-multi-arch");
1245 Args
.push_back(NULL
);
1247 pid_t dpkgAssertMultiArch
= ExecFork();
1248 if (dpkgAssertMultiArch
== 0)
1250 dpkgChrootDirectory();
1251 // redirect everything to the ultimate sink as we only need the exit-status
1252 int const nullfd
= open("/dev/null", O_RDONLY
);
1253 dup2(nullfd
, STDIN_FILENO
);
1254 dup2(nullfd
, STDOUT_FILENO
);
1255 dup2(nullfd
, STDERR_FILENO
);
1256 execvp(Args
[0], (char**) &Args
[0]);
1257 _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
1264 // FIXME: do we really need this limit when we have MaxArgBytes?
1265 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1267 // try to figure out the max environment size
1268 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1271 OSArgMax
-= EnvironmentSize() - 2*1024;
1272 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1273 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1275 if (RunScripts("DPkg::Pre-Invoke") == false)
1278 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1281 // support subpressing of triggers processing for special
1282 // cases like d-i that runs the triggers handling manually
1283 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1284 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1285 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1288 BuildPackagesProgressMap();
1290 d
->stdin_is_dev_null
= false;
1295 bool dpkgMultiArch
= false;
1296 if (dpkgAssertMultiArch
> 0)
1299 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
1303 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
1306 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
1307 dpkgMultiArch
= true;
1310 // start pty magic before the loop
1313 // Tell the progress that its starting and fork dpkg
1314 d
->progress
->Start(d
->master
);
1316 // this loop is runs once per dpkg operation
1317 vector
<Item
>::const_iterator I
= List
.begin();
1318 while (I
!= List
.end())
1320 // Do all actions with the same Op in one run
1321 vector
<Item
>::const_iterator J
= I
;
1322 if (TriggersPending
== true)
1323 for (; J
!= List
.end(); ++J
)
1327 if (J
->Op
!= Item::TriggersPending
)
1329 vector
<Item
>::const_iterator T
= J
+ 1;
1330 if (T
!= List
.end() && T
->Op
== I
->Op
)
1335 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1338 // keep track of allocated strings for multiarch package names
1339 std::vector
<char *> Packages
;
1341 // start with the baseset of arguments
1342 unsigned long Size
= StartSize
;
1343 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1345 // Now check if we are within the MaxArgs limit
1347 // this code below is problematic, because it may happen that
1348 // the argument list is split in a way that A depends on B
1349 // and they are in the same "--configure A B" run
1350 // - with the split they may now be configured in different
1351 // runs, using Immediate-Configure-All can help prevent this.
1352 if (J
- I
> (signed)MaxArgs
)
1355 unsigned long const size
= MaxArgs
+ 10;
1357 Packages
.reserve(size
);
1361 unsigned long const size
= (J
- I
) + 10;
1363 Packages
.reserve(size
);
1368 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1370 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1371 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1373 ADDARGC("--status-fd");
1374 char status_fd_buf
[20];
1375 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1376 ADDARG(status_fd_buf
);
1377 unsigned long const Op
= I
->Op
;
1382 ADDARGC("--force-depends");
1383 ADDARGC("--force-remove-essential");
1384 ADDARGC("--remove");
1388 ADDARGC("--force-depends");
1389 ADDARGC("--force-remove-essential");
1393 case Item::Configure
:
1394 ADDARGC("--configure");
1397 case Item::ConfigurePending
:
1398 ADDARGC("--configure");
1399 ADDARGC("--pending");
1402 case Item::TriggersPending
:
1403 ADDARGC("--triggers-only");
1404 ADDARGC("--pending");
1408 ADDARGC("--unpack");
1409 ADDARGC("--auto-deconfigure");
1413 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1414 I
->Op
!= Item::ConfigurePending
)
1416 ADDARGC("--no-triggers");
1420 // Write in the file or package names
1421 if (I
->Op
== Item::Install
)
1423 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1425 if (I
->File
[0] != '/')
1426 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1427 Args
.push_back(I
->File
.c_str());
1428 Size
+= I
->File
.length();
1433 string
const nativeArch
= _config
->Find("APT::Architecture");
1434 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1435 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1437 if((*I
).Pkg
.end() == true)
1439 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1441 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1442 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1443 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1444 strcmp(I
->Pkg
.Arch(), "none") == 0))
1446 char const * const name
= I
->Pkg
.Name();
1451 pkgCache::VerIterator PkgVer
;
1452 std::string name
= I
->Pkg
.Name();
1453 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1455 PkgVer
= I
->Pkg
.CurrentVer();
1456 if(PkgVer
.end() == true)
1457 PkgVer
= FindNowVersion(I
->Pkg
);
1460 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1461 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1462 ; // never arch-qualify a package without an arch
1463 else if (PkgVer
.end() == false)
1464 name
.append(":").append(PkgVer
.Arch());
1466 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1467 char * const fullname
= strdup(name
.c_str());
1468 Packages
.push_back(fullname
);
1472 // skip configure action if all sheduled packages disappeared
1473 if (oldSize
== Size
)
1480 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1482 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1483 a
!= Args
.end(); ++a
)
1488 Args
.push_back(NULL
);
1494 /* Mask off sig int/quit. We do this because dpkg also does when
1495 it forks scripts. What happens is that when you hit ctrl-c it sends
1496 it to all processes in the group. Since dpkg ignores the signal
1497 it doesn't die but we do! So we must also ignore it */
1498 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1499 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1501 // Check here for any SIGINT
1502 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1506 // ignore SIGHUP as well (debian #463030)
1507 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1510 d
->progress
->StartDpkg();
1511 std::set
<int> KeepFDs
;
1512 KeepFDs
.insert(fd
[1]);
1513 MergeKeepFdsFromConfiguration(KeepFDs
);
1514 pid_t Child
= ExecFork(KeepFDs
);
1517 // This is the child
1518 SetupSlavePtyMagic();
1519 close(fd
[0]); // close the read end of the pipe
1521 dpkgChrootDirectory();
1523 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1526 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1530 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1533 // Discard everything in stdin before forking dpkg
1534 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1537 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1539 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1543 /* No Job Control Stop Env is a magic dpkg var that prevents it
1544 from using sigstop */
1545 putenv((char *)"DPKG_NO_TSTP=yes");
1546 execvp(Args
[0], (char**) &Args
[0]);
1547 cerr
<< "Could not exec dpkg!" << endl
;
1552 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1558 // we read from dpkg here
1559 int const _dpkgin
= fd
[0];
1560 close(fd
[1]); // close the write end of the pipe
1563 sigemptyset(&d
->sigmask
);
1564 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1566 /* free vectors (and therefore memory) as we don't need the included data anymore */
1567 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1568 p
!= Packages
.end(); ++p
)
1572 // the result of the waitpid call
1575 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1577 // FIXME: move this to a function or something, looks ugly here
1578 // error handling, waitpid returned -1
1581 RunScripts("DPkg::Post-Invoke");
1583 // Restore sig int/quit
1584 signal(SIGQUIT
,old_SIGQUIT
);
1585 signal(SIGINT
,old_SIGINT
);
1587 signal(SIGHUP
,old_SIGHUP
);
1588 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1591 // wait for input or output here
1593 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1594 FD_SET(STDIN_FILENO
, &rfds
);
1595 FD_SET(_dpkgin
, &rfds
);
1597 FD_SET(d
->master
, &rfds
);
1599 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1600 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1601 &tv
, &d
->original_sigmask
);
1602 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1603 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1604 NULL
, &tv
, &d
->original_sigmask
);
1605 d
->progress
->Pulse();
1606 if (select_ret
== 0)
1608 else if (select_ret
< 0 && errno
== EINTR
)
1610 else if (select_ret
< 0)
1612 perror("select() returned error");
1616 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1617 DoTerminalPty(d
->master
);
1618 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1620 if(FD_ISSET(_dpkgin
, &rfds
))
1621 DoDpkgStatusFd(_dpkgin
);
1625 // Restore sig int/quit
1626 signal(SIGQUIT
,old_SIGQUIT
);
1627 signal(SIGINT
,old_SIGINT
);
1629 signal(SIGHUP
,old_SIGHUP
);
1630 // Check for an error code.
1631 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1633 // if it was set to "keep-dpkg-runing" then we won't return
1634 // here but keep the loop going and just report it as a error
1636 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1638 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1639 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1640 else if (WIFEXITED(Status
) != 0)
1641 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1643 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1644 _error
->Error("%s", d
->dpkg_error
.c_str());
1650 // dpkg is done at this point
1651 d
->progress
->Stop();
1655 if (pkgPackageManager::SigINTStop
)
1656 _error
->Warning(_("Operation was interrupted before it could finish"));
1658 if (RunScripts("DPkg::Post-Invoke") == false)
1661 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1663 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1664 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1665 unlink(oldpkgcache
.c_str()) == 0)
1667 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1668 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1670 _error
->PushToStack();
1671 pkgCacheFile CacheFile
;
1672 CacheFile
.BuildCaches(NULL
, true);
1673 _error
->RevertToStack();
1678 Cache
.writeStateFile(NULL
);
1679 return d
->dpkg_error
.empty();
1682 void SigINT(int /*sig*/) {
1683 pkgPackageManager::SigINTStop
= true;
1686 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1687 // ---------------------------------------------------------------------
1689 void pkgDPkgPM::Reset()
1691 List
.erase(List
.begin(),List
.end());
1694 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1695 // ---------------------------------------------------------------------
1697 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1699 // If apport doesn't exist or isn't installed do nothing
1700 // This e.g. prevents messages in 'universes' without apport
1701 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1702 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1705 string pkgname
, reportfile
, pkgver
, arch
;
1706 string::size_type pos
;
1709 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1711 std::clog
<< "configured to not write apport reports" << std::endl
;
1715 // only report the first errors
1716 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1718 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1722 // check if its not a follow up error
1723 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1724 if(strstr(errormsg
, needle
) != NULL
) {
1725 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1729 // do not report disk-full failures
1730 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1731 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1735 // do not report out-of-memory failures
1736 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1737 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1738 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1742 // do not report bugs regarding inaccessible local files
1743 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1744 strstr(errormsg
, "cannot access archive") != NULL
) {
1745 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1749 // do not report errors encountered when decompressing packages
1750 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1751 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1755 // do not report dpkg I/O errors, this is a format string, so we compare
1756 // the prefix and the suffix of the error with the dpkg error message
1757 vector
<string
> io_errors
;
1758 io_errors
.push_back(string("failed to read"));
1759 io_errors
.push_back(string("failed to write"));
1760 io_errors
.push_back(string("failed to seek"));
1761 io_errors
.push_back(string("unexpected end of file or stream"));
1763 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1765 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1766 if (list
.size() > 1) {
1767 // we need to split %s, VectorizeString only allows char so we need
1768 // to kill the "s" manually
1769 if (list
[1].size() > 1) {
1770 list
[1].erase(0, 1);
1771 if(strstr(errormsg
, list
[0].c_str()) &&
1772 strstr(errormsg
, list
[1].c_str())) {
1773 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1780 // get the pkgname and reportfile
1781 pkgname
= flNotDir(pkgpath
);
1782 pos
= pkgname
.find('_');
1783 if(pos
!= string::npos
)
1784 pkgname
= pkgname
.substr(0, pos
);
1786 // find the package versin and source package name
1787 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1788 if (Pkg
.end() == true)
1790 pkgCache::VerIterator Ver
= Cache
.GetCandidateVer(Pkg
);
1791 if (Ver
.end() == true)
1793 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1795 // if the file exists already, we check:
1796 // - if it was reported already (touched by apport).
1797 // If not, we do nothing, otherwise
1798 // we overwrite it. This is the same behaviour as apport
1799 // - if we have a report with the same pkgversion already
1801 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1802 if(FileExists(reportfile
))
1807 // check atime/mtime
1808 stat(reportfile
.c_str(), &buf
);
1809 if(buf
.st_mtime
> buf
.st_atime
)
1812 // check if the existing report is the same version
1813 report
= fopen(reportfile
.c_str(),"r");
1814 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1816 if(strstr(strbuf
,"Package:") == strbuf
)
1818 char pkgname
[255], version
[255];
1819 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1820 if(strcmp(pkgver
.c_str(), version
) == 0)
1830 // now write the report
1831 arch
= _config
->Find("APT::Architecture");
1832 report
= fopen(reportfile
.c_str(),"w");
1835 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1836 chmod(reportfile
.c_str(), 0);
1838 chmod(reportfile
.c_str(), 0600);
1839 fprintf(report
, "ProblemType: Package\n");
1840 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1841 time_t now
= time(NULL
);
1842 fprintf(report
, "Date: %s" , ctime(&now
));
1843 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1844 #if APT_PKG_ABI >= 413
1845 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1847 pkgRecords
Recs(Cache
);
1848 pkgRecords::Parser
&Parse
= Recs
.Lookup(Ver
.FileList());
1849 std::string srcpkgname
= Parse
.SourcePkg();
1850 if(srcpkgname
.empty())
1851 srcpkgname
= pkgname
;
1852 fprintf(report
, "SourcePackage: %s\n", srcpkgname
.c_str());
1854 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1856 // ensure that the log is flushed
1858 fflush(d
->term_out
);
1860 // attach terminal log it if we have it
1861 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1862 if (!logfile_name
.empty())
1866 fprintf(report
, "DpkgTerminalLog:\n");
1867 log
= fopen(logfile_name
.c_str(),"r");
1871 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1872 fprintf(report
, " %s", buf
);
1873 fprintf(report
, " \n");
1878 // attach history log it if we have it
1879 string histfile_name
= _config
->FindFile("Dir::Log::History");
1880 if (!histfile_name
.empty())
1882 fprintf(report
, "DpkgHistoryLog:\n");
1883 FILE* log
= fopen(histfile_name
.c_str(),"r");
1887 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1888 fprintf(report
, " %s", buf
);
1893 // log the ordering, see dpkgpm.h and the "Ops" enum there
1894 const char *ops_str
[] = {
1902 fprintf(report
, "AptOrdering:\n");
1903 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1904 if ((*I
).Pkg
!= NULL
)
1905 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1907 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1909 // attach dmesg log (to learn about segfaults)
1910 if (FileExists("/bin/dmesg"))
1912 fprintf(report
, "Dmesg:\n");
1913 FILE *log
= popen("/bin/dmesg","r");
1917 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1918 fprintf(report
, " %s", buf
);
1923 // attach df -l log (to learn about filesystem status)
1924 if (FileExists("/bin/df"))
1927 fprintf(report
, "Df:\n");
1928 FILE *log
= popen("/bin/df -l","r");
1932 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1933 fprintf(report
, " %s", buf
);