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/debsystem.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/install-progress.h>
21 #include <apt-pkg/packagemanager.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>
35 #include <sys/ioctl.h>
36 #include <sys/select.h>
58 APT_PURE
static string
59 AptHistoryRequestingUser()
62 "SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID", nullptr
65 for (int i
=0; env
[i
] != nullptr; i
++)
67 if (getenv(env
[i
]) != nullptr)
69 int uid
= atoi(getenv(env
[i
]));
72 struct passwd
*result
;
74 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
76 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
85 APT_PURE
static unsigned int
88 unsigned int size
= 0;
89 char **envp
= environ
;
92 size
+= strlen (*envp
++) + 1;
97 class pkgDPkgPMPrivate
100 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
101 term_out(NULL
), history_out(NULL
),
102 progress(NULL
), tt_is_valid(false), master(-1),
103 slave(NULL
), protect_slave_from_dying(-1),
111 bool stdin_is_dev_null
;
112 // the buffer we use for the dpkg status-fd reading
118 APT::Progress::PackageManager
*progress
;
125 int protect_slave_from_dying
;
129 sigset_t original_sigmask
;
136 // Maps the dpkg "processing" info to human readable names. Entry 0
137 // of each array is the key, entry 1 is the value.
138 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
139 std::make_pair("install", N_("Installing %s")),
140 std::make_pair("configure", N_("Configuring %s")),
141 std::make_pair("remove", N_("Removing %s")),
142 std::make_pair("purge", N_("Completely removing %s")),
143 std::make_pair("disappear", N_("Noting disappearance of %s")),
144 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
147 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
148 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
150 // Predicate to test whether an entry in the PackageProcessingOps
151 // array matches a string.
152 class MatchProcessingOp
157 explicit MatchProcessingOp(const char *the_target
)
162 bool operator()(const std::pair
<const char *, const char *> &pair
) const
164 return strcmp(pair
.first
, target
) == 0;
169 /* helper function to ionice the given PID
171 there is no C header for ionice yet - just the syscall interface
172 so we use the binary from util-linux
177 if (!FileExists("/usr/bin/ionice"))
179 pid_t Process
= ExecFork();
183 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
185 Args
[0] = "/usr/bin/ionice";
189 execv(Args
[0], (char **)Args
);
191 return ExecWait(Process
, "ionice");
194 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
195 // ---------------------------------------------------------------------
196 /* This is helpful when a package is no longer installed but has residual
200 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
202 pkgCache::VerIterator Ver
;
203 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
204 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
205 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
207 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
214 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
215 // ---------------------------------------------------------------------
217 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
218 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
222 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
223 // ---------------------------------------------------------------------
225 pkgDPkgPM::~pkgDPkgPM()
230 // DPkgPM::Install - Install a package /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Add an install operation to the sequence list */
233 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
235 if (File
.empty() == true || Pkg
.end() == true)
236 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
238 // If the filename string begins with DPkg::Chroot-Directory, return the
239 // substr that is within the chroot so dpkg can access it.
240 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
241 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
243 size_t len
= chrootdir
.length();
244 if (chrootdir
.at(len
- 1) == '/')
246 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
249 List
.push_back(Item(Item::Install
,Pkg
,File
));
254 // DPkgPM::Configure - Configure a package /*{{{*/
255 // ---------------------------------------------------------------------
256 /* Add a configure operation to the sequence list */
257 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
259 if (Pkg
.end() == true)
262 List
.push_back(Item(Item::Configure
, Pkg
));
264 // Use triggers for config calls if we configure "smart"
265 // as otherwise Pre-Depends will not be satisfied, see #526774
266 if (_config
->FindB("DPkg::TriggersPending", false) == true)
267 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
272 // DPkgPM::Remove - Remove a package /*{{{*/
273 // ---------------------------------------------------------------------
274 /* Add a remove operation to the sequence list */
275 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
277 if (Pkg
.end() == true)
281 List
.push_back(Item(Item::Purge
,Pkg
));
283 List
.push_back(Item(Item::Remove
,Pkg
));
287 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This is part of the helper script communication interface, it sends
290 very complete information down to the other end of the pipe.*/
291 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
293 return SendPkgsInfo(F
, 2);
295 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
297 // This version of APT supports only v3, so don't sent higher versions
299 fprintf(F
,"VERSION %u\n", Version
);
301 fprintf(F
,"VERSION 3\n");
303 /* Write out all of the configuration directives by walking the
304 configuration tree */
305 const Configuration::Item
*Top
= _config
->Tree(0);
308 if (Top
->Value
.empty() == false)
311 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
312 QuoteString(Top
->Value
,"\n").c_str());
321 while (Top
!= 0 && Top
->Next
== 0)
328 // Write out the package actions in order.
329 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
331 if(I
->Pkg
.end() == true)
334 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
336 fprintf(F
,"%s ",I
->Pkg
.Name());
338 // Current version which we are going to replace
339 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
340 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
341 CurVer
= FindNowVersion(I
->Pkg
);
343 if (CurVer
.end() == true)
348 fprintf(F
, "- - none ");
352 fprintf(F
, "%s ", CurVer
.VerStr());
354 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
357 // Show the compare operator between current and install version
358 if (S
.InstallVer
!= 0)
360 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
362 if (CurVer
.end() == false)
363 Comp
= InstVer
.CompareVer(CurVer
);
370 fprintf(F
, "%s ", InstVer
.VerStr());
372 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
379 fprintf(F
, "> - - none ");
382 // Show the filename/operation
383 if (I
->Op
== Item::Install
)
386 if (I
->File
[0] != '/')
387 fprintf(F
,"**ERROR**\n");
389 fprintf(F
,"%s\n",I
->File
.c_str());
391 else if (I
->Op
== Item::Configure
)
392 fprintf(F
,"**CONFIGURE**\n");
393 else if (I
->Op
== Item::Remove
||
394 I
->Op
== Item::Purge
)
395 fprintf(F
,"**REMOVE**\n");
403 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
404 // ---------------------------------------------------------------------
405 /* This looks for a list of scripts to run from the configuration file
406 each one is run and is fed on standard input a list of all .deb files
407 that are due to be installed. */
408 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
412 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
413 if (Opts
== 0 || Opts
->Child
== 0)
417 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
419 unsigned int Count
= 1;
420 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
422 if (Opts
->Value
.empty() == true)
425 if(_config
->FindB("Debug::RunScripts", false) == true)
426 std::clog
<< "Running external script with list of all .deb file: '"
427 << Opts
->Value
<< "'" << std::endl
;
429 // Determine the protocol version
430 string OptSec
= Opts
->Value
;
431 string::size_type Pos
;
432 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
433 Pos
= OptSec
.length();
434 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
436 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
437 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
440 std::set
<int> KeepFDs
;
441 MergeKeepFdsFromConfiguration(KeepFDs
);
443 if (pipe(Pipes
) != 0) {
444 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
447 if (InfoFD
!= (unsigned)Pipes
[0])
448 SetCloseExec(Pipes
[0],true);
450 KeepFDs
.insert(Pipes
[0]);
453 SetCloseExec(Pipes
[1],true);
455 // Purified Fork for running the script
456 pid_t Process
= ExecFork(KeepFDs
);
460 dup2(Pipes
[0], InfoFD
);
461 SetCloseExec(STDOUT_FILENO
,false);
462 SetCloseExec(STDIN_FILENO
,false);
463 SetCloseExec(STDERR_FILENO
,false);
466 strprintf(hookfd
, "%d", InfoFD
);
467 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
469 debSystem::DpkgChrootDirectory();
473 Args
[2] = Opts
->Value
.c_str();
475 execv(Args
[0],(char **)Args
);
479 FILE *F
= fdopen(Pipes
[1],"w");
481 result
= _error
->Errno("fdopen","Faild to open new FD");
485 // Feed it the filenames.
488 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
490 // Only deal with packages to be installed from .deb
491 if (I
->Op
!= Item::Install
)
495 if (I
->File
[0] != '/')
498 /* Feed the filename of each package that is pending install
500 fprintf(F
,"%s\n",I
->File
.c_str());
506 SendPkgsInfo(F
, Version
);
510 // Clean up the sub process
511 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
512 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
516 signal(SIGPIPE
, old_sigpipe
);
521 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
522 // ---------------------------------------------------------------------
525 void pkgDPkgPM::DoStdin(int master
)
527 unsigned char input_buf
[256] = {0,};
528 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
530 FileFd::Write(master
, input_buf
, len
);
532 d
->stdin_is_dev_null
= true;
535 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
536 // ---------------------------------------------------------------------
538 * read the terminal pty and write log
540 void pkgDPkgPM::DoTerminalPty(int master
)
542 unsigned char term_buf
[1024] = {0,0, };
544 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
545 if(len
== -1 && errno
== EIO
)
547 // this happens when the child is about to exit, we
548 // give it time to actually exit, otherwise we run
549 // into a race so we sleep for half a second.
550 struct timespec sleepfor
= { 0, 500000000 };
551 nanosleep(&sleepfor
, NULL
);
556 FileFd::Write(1, term_buf
, len
);
558 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
561 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
562 // ---------------------------------------------------------------------
565 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
567 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
569 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
571 /* dpkg sends strings like this:
572 'status: <pkg>: <pkg qstate>'
573 'status: <pkg>:<arch>: <pkg qstate>'
575 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
576 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
579 // we need to split on ": " (note the appended space) as the ':' is
580 // part of the pkgname:arch information that dpkg sends
582 // A dpkg error message may contain additional ":" (like
583 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
584 // so we need to ensure to not split too much
585 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
589 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
593 // build the (prefix, pkgname, action) tuple, position of this
594 // is different for "processing" or "status" messages
595 std::string prefix
= APT::String::Strip(list
[0]);
599 // "processing" has the form "processing: action: pkg or trigger"
600 // with action = ["install", "upgrade", "configure", "remove", "purge",
601 // "disappear", "trigproc"]
602 if (prefix
== "processing")
604 pkgname
= APT::String::Strip(list
[2]);
605 action
= APT::String::Strip(list
[1]);
606 // we don't care for the difference (as dpkg doesn't really either)
607 if (action
== "upgrade")
610 // "status" has the form: "status: pkg: state"
611 // with state in ["half-installed", "unpacked", "half-configured",
612 // "installed", "config-files", "not-installed"]
613 else if (prefix
== "status")
615 pkgname
= APT::String::Strip(list
[1]);
616 action
= APT::String::Strip(list
[2]);
619 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
624 /* handle the special cases first:
626 errors look like this:
627 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
628 and conffile-prompt like this
629 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
631 if (prefix
== "status")
633 if(action
== "error")
635 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
638 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
641 else if(action
== "conffile-prompt")
643 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
649 // at this point we know that we should have a valid pkgname, so build all
652 // dpkg does not always send "pkgname:arch" so we add it here if needed
653 if (pkgname
.find(":") == std::string::npos
)
655 // find the package in the group that is touched by dpkg
656 // if there are multiple pkgs dpkg would send us a full pkgname:arch
657 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
658 if (Grp
.end() == false)
660 pkgCache::PkgIterator P
= Grp
.PackageList();
661 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
663 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
665 pkgname
= P
.FullName();
672 const char* const pkg
= pkgname
.c_str();
673 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
674 std::string arch
= "";
675 if (pkgname
.find(":") != string::npos
)
676 arch
= StringSplit(pkgname
, ":")[1];
677 std::string i18n_pkgname
= pkgname
;
678 if (arch
.size() != 0)
679 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
681 // 'processing' from dpkg looks like
682 // 'processing: action: pkg'
683 if(prefix
== "processing")
685 const std::pair
<const char *, const char *> * const iter
=
686 std::find_if(PackageProcessingOpsBegin
,
687 PackageProcessingOpsEnd
,
688 MatchProcessingOp(action
.c_str()));
689 if(iter
== PackageProcessingOpsEnd
)
692 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
696 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
697 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
699 // FIXME: this needs a muliarch testcase
700 // FIXME2: is "pkgname" here reliable with dpkg only sending us
702 if (action
== "disappear")
703 handleDisappearAction(pkgname
);
707 if (prefix
== "status")
709 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
710 if(PackageOpsDone
[pkg
] < states
.size())
712 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
713 if (next_action
&& Debug
== true)
714 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
715 << " action: " << action
<< " (expected: '" << next_action
<< "' "
716 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
718 // check if the package moved to the next dpkg state
719 if(next_action
&& (action
== next_action
))
721 // only read the translation if there is actually a next action
722 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
724 // we moved from one dpkg state to a new one, report that
725 ++PackageOpsDone
[pkg
];
729 strprintf(msg
, translation
, i18n_pkgname
.c_str());
730 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
736 // DPkgPM::handleDisappearAction /*{{{*/
737 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
739 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
740 if (unlikely(Pkg
.end() == true))
743 // record the package name for display and stuff later
744 disappearedPkgs
.insert(Pkg
.FullName(true));
746 // the disappeared package was auto-installed - nothing to do
747 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
749 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
750 if (unlikely(PkgVer
.end() == true))
752 /* search in the list of dependencies for (Pre)Depends,
753 check if this dependency has a Replaces on our package
754 and if so transfer the manual installed flag to it */
755 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
757 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
758 Dep
->Type
!= pkgCache::Dep::PreDepends
)
760 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
761 if (unlikely(Tar
.end() == true))
763 // the package is already marked as manual
764 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
766 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
767 if (TarVer
.end() == true)
769 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
771 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
773 if (Pkg
!= Rep
.TargetPkg())
775 // okay, they are strongly connected - transfer manual-bit
777 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
778 Cache
[Tar
].Flags
&= ~Flag::Auto
;
784 // DPkgPM::DoDpkgStatusFd /*{{{*/
785 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
787 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
788 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
791 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
793 // process line by line from the buffer
794 char *p
= d
->dpkgbuf
, *q
= nullptr;
795 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
798 ProcessDpkgStatusLine(p
);
799 p
= q
+ 1; // continue with next line
802 // check if we stripped the buffer clean
803 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
809 // otherwise move the unprocessed tail to the start and update pos
810 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
811 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
814 // DPkgPM::WriteHistoryTag /*{{{*/
815 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
817 size_t const length
= value
.length();
820 // poor mans rstrip(", ")
821 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
822 value
.erase(length
- 2, 2);
823 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
825 // DPkgPM::OpenLog /*{{{*/
826 bool pkgDPkgPM::OpenLog()
828 string
const logdir
= _config
->FindDir("Dir::Log");
829 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
830 // FIXME: use a better string after freeze
831 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
835 time_t const t
= time(NULL
);
837 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
838 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
841 string
const logfile_name
= flCombine(logdir
,
842 _config
->Find("Dir::Log::Terminal"));
843 if (!logfile_name
.empty())
845 d
->term_out
= fopen(logfile_name
.c_str(),"a");
846 if (d
->term_out
== NULL
)
847 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
848 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
849 SetCloseExec(fileno(d
->term_out
), true);
850 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
852 struct passwd
*pw
= getpwnam("root");
853 struct group
*gr
= getgrnam("adm");
854 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
855 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
857 if (chmod(logfile_name
.c_str(), 0640) != 0)
858 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
859 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
862 // write your history
863 string
const history_name
= flCombine(logdir
,
864 _config
->Find("Dir::Log::History"));
865 if (!history_name
.empty())
867 d
->history_out
= fopen(history_name
.c_str(),"a");
868 if (d
->history_out
== NULL
)
869 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
870 SetCloseExec(fileno(d
->history_out
), true);
871 chmod(history_name
.c_str(), 0644);
872 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
873 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
874 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
876 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
878 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
879 if (Cache
[I
].NewInstall() == true)
880 HISTORYINFO(install
, CANDIDATE_AUTO
)
881 else if (Cache
[I
].ReInstall() == true)
882 HISTORYINFO(reinstall
, CANDIDATE
)
883 else if (Cache
[I
].Upgrade() == true)
884 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
885 else if (Cache
[I
].Downgrade() == true)
886 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
887 else if (Cache
[I
].Delete() == true)
888 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
892 line
->append(I
.FullName(false)).append(" (");
893 switch (infostring
) {
894 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
896 line
->append(Cache
[I
].CandVersion
);
897 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
898 line
->append(", automatic");
900 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
901 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
905 if (_config
->Exists("Commandline::AsString") == true)
906 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
907 std::string RequestingUser
= AptHistoryRequestingUser();
908 if (RequestingUser
!= "")
909 WriteHistoryTag("Requested-By", RequestingUser
);
910 WriteHistoryTag("Install", install
);
911 WriteHistoryTag("Reinstall", reinstall
);
912 WriteHistoryTag("Upgrade", upgrade
);
913 WriteHistoryTag("Downgrade",downgrade
);
914 WriteHistoryTag("Remove",remove
);
915 WriteHistoryTag("Purge",purge
);
916 fflush(d
->history_out
);
922 // DPkg::CloseLog /*{{{*/
923 bool pkgDPkgPM::CloseLog()
926 time_t t
= time(NULL
);
928 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
929 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
933 fprintf(d
->term_out
, "Log ended: ");
934 fprintf(d
->term_out
, "%s", timestr
);
935 fprintf(d
->term_out
, "\n");
942 if (disappearedPkgs
.empty() == false)
945 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
946 d
!= disappearedPkgs
.end(); ++d
)
948 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
949 disappear
.append(*d
);
951 disappear
.append(", ");
953 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
955 WriteHistoryTag("Disappeared", disappear
);
957 if (d
->dpkg_error
.empty() == false)
958 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
959 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
960 fclose(d
->history_out
);
962 d
->history_out
= NULL
;
969 // This implements a racy version of pselect for those architectures
970 // that don't have a working implementation.
971 // FIXME: Probably can be removed on Lenny+1
972 static int racy_pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
973 fd_set
*exceptfds
, const struct timespec
*timeout
,
974 const sigset_t
*sigmask
)
980 tv
.tv_sec
= timeout
->tv_sec
;
981 tv
.tv_usec
= timeout
->tv_nsec
/1000;
983 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
984 retval
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
);
985 sigprocmask(SIG_SETMASK
, &origmask
, 0);
990 // DPkgPM::BuildPackagesProgressMap /*{{{*/
991 void pkgDPkgPM::BuildPackagesProgressMap()
993 // map the dpkg states to the operations that are performed
994 // (this is sorted in the same way as Item::Ops)
995 static const struct DpkgState DpkgStatesOpMap
[][7] = {
998 {"half-installed", N_("Preparing %s")},
999 {"unpacked", N_("Unpacking %s") },
1002 // Configure operation
1004 {"unpacked",N_("Preparing to configure %s") },
1005 {"half-configured", N_("Configuring %s") },
1006 { "installed", N_("Installed %s")},
1011 {"half-configured", N_("Preparing for removal of %s")},
1012 {"half-installed", N_("Removing %s")},
1013 {"config-files", N_("Removed %s")},
1018 {"config-files", N_("Preparing to completely remove %s")},
1019 {"not-installed", N_("Completely removed %s")},
1024 // init the PackageOps map, go over the list of packages that
1025 // that will be [installed|configured|removed|purged] and add
1026 // them to the PackageOps map (the dpkg states it goes through)
1027 // and the PackageOpsTranslations (human readable strings)
1028 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1030 if((*I
).Pkg
.end() == true)
1033 string
const name
= (*I
).Pkg
.FullName();
1034 PackageOpsDone
[name
] = 0;
1035 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1037 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1041 /* one extra: We don't want the progress bar to reach 100%, especially not
1042 if we call dpkg --configure --pending and process a bunch of triggers
1043 while showing 100%. Also, spindown takes a while, so never reaching 100%
1044 is way more correct than reaching 100% while still doing stuff even if
1045 doing it this way is slightly bending the rules */
1049 bool pkgDPkgPM::Go(int StatusFd
)
1051 APT::Progress::PackageManager
*progress
= NULL
;
1053 progress
= APT::Progress::PackageManagerProgressFactory();
1055 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1057 return Go(progress
);
1060 void pkgDPkgPM::StartPtyMagic()
1062 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1065 if (d
->slave
!= NULL
)
1071 if (isatty(STDIN_FILENO
) == 0)
1072 d
->direct_stdin
= true;
1074 _error
->PushToStack();
1076 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1077 if (d
->master
== -1)
1078 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1079 else if (unlockpt(d
->master
) == -1)
1080 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1083 #ifdef HAVE_PTS_NAME_R
1084 char slave_name
[64]; // 64 is used by bionic
1085 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1087 char const * const slave_name
= ptsname(d
->master
);
1088 if (slave_name
== NULL
)
1090 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1093 d
->slave
= strdup(slave_name
);
1094 if (d
->slave
== NULL
)
1095 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1096 else if (grantpt(d
->master
) == -1)
1097 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1098 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1100 d
->tt_is_valid
= true;
1101 struct termios raw_tt
;
1102 // copy window size of stdout if its a 'good' terminal
1103 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1106 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1107 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1108 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1109 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1111 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1112 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1116 raw_tt
.c_lflag
&= ~ECHO
;
1117 raw_tt
.c_lflag
|= ISIG
;
1118 // block SIGTTOU during tcsetattr to prevent a hang if
1119 // the process is a member of the background process group
1120 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1121 sigemptyset(&d
->sigmask
);
1122 sigaddset(&d
->sigmask
, SIGTTOU
);
1123 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1124 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1125 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1126 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1129 if (d
->slave
!= NULL
)
1131 /* on linux, closing (and later reopening) all references to the slave
1132 makes the slave a death end, so we open it here to have one open all
1133 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1134 on kfreebsd we get an incorrect ("step like") output then while it has
1135 no problem with closing all references… so to avoid platform specific
1136 code here we combine both and be happy once more */
1137 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1142 if (_error
->PendingError() == true)
1144 if (d
->master
!= -1)
1149 if (d
->slave
!= NULL
)
1154 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1156 _error
->RevertToStack();
1158 void pkgDPkgPM::SetupSlavePtyMagic()
1160 if(d
->master
== -1 || d
->slave
== NULL
)
1163 if (close(d
->master
) == -1)
1164 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1167 _error
->FatalE("setsid", "Starting a new session for child failed!");
1169 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1171 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1172 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1173 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1176 unsigned short i
= 0;
1177 if (d
->direct_stdin
== true)
1180 if (dup2(slaveFd
, i
) == -1)
1181 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1183 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1184 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1190 void pkgDPkgPM::StopPtyMagic()
1192 if (d
->slave
!= NULL
)
1195 if (d
->protect_slave_from_dying
!= -1)
1197 close(d
->protect_slave_from_dying
);
1198 d
->protect_slave_from_dying
= -1;
1202 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1203 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1209 // DPkgPM::Go - Run the sequence /*{{{*/
1210 // ---------------------------------------------------------------------
1211 /* This globs the operations and calls dpkg
1213 * If it is called with a progress object apt will report the install
1214 * progress to this object. It maps the dpkg states a package goes
1215 * through to human readable (and i10n-able)
1216 * names and calculates a percentage for each step.
1218 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1220 pkgPackageManager::SigINTStop
= false;
1221 d
->progress
= progress
;
1223 // Generate the base argument list for dpkg
1224 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1225 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1226 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1227 [](std::string
const &s
) { return s
.c_str(); });
1228 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1229 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1230 size_t const BaseArgs
= Args
.size();
1235 // FIXME: do we really need this limit when we have MaxArgBytes?
1236 unsigned int const MaxArgs
= _config
->FindI("Dpkg::MaxArgs",32*1024);
1238 // try to figure out the max environment size
1239 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1242 OSArgMax
-= EnvironmentSize() - 2*1024;
1243 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1244 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1246 if (RunScripts("DPkg::Pre-Invoke") == false)
1249 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1252 // support subpressing of triggers processing for special
1253 // cases like d-i that runs the triggers handling manually
1254 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1255 if (_config
->FindB("DPkg::ConfigurePending", true) == true)
1256 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1259 BuildPackagesProgressMap();
1261 d
->stdin_is_dev_null
= false;
1266 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1268 // start pty magic before the loop
1271 // Tell the progress that its starting and fork dpkg
1272 d
->progress
->Start(d
->master
);
1274 // this loop is runs once per dpkg operation
1275 vector
<Item
>::const_iterator I
= List
.begin();
1276 while (I
!= List
.end())
1278 // Do all actions with the same Op in one run
1279 vector
<Item
>::const_iterator J
= I
;
1280 if (TriggersPending
== true)
1281 for (; J
!= List
.end(); ++J
)
1285 if (J
->Op
!= Item::TriggersPending
)
1287 vector
<Item
>::const_iterator T
= J
+ 1;
1288 if (T
!= List
.end() && T
->Op
== I
->Op
)
1293 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1296 // keep track of allocated strings for multiarch package names
1297 std::vector
<char *> Packages
;
1299 // start with the baseset of arguments
1300 unsigned long Size
= StartSize
;
1301 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1303 // Now check if we are within the MaxArgs limit
1305 // this code below is problematic, because it may happen that
1306 // the argument list is split in a way that A depends on B
1307 // and they are in the same "--configure A B" run
1308 // - with the split they may now be configured in different
1309 // runs, using Immediate-Configure-All can help prevent this.
1310 if (J
- I
> (signed)MaxArgs
)
1313 unsigned long const size
= MaxArgs
+ 10;
1315 Packages
.reserve(size
);
1319 unsigned long const size
= (J
- I
) + 10;
1321 Packages
.reserve(size
);
1326 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1328 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1329 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1331 ADDARGC("--status-fd");
1332 char status_fd_buf
[20];
1333 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1334 ADDARG(status_fd_buf
);
1335 unsigned long const Op
= I
->Op
;
1340 ADDARGC("--force-depends");
1341 ADDARGC("--force-remove-essential");
1342 ADDARGC("--remove");
1346 ADDARGC("--force-depends");
1347 ADDARGC("--force-remove-essential");
1351 case Item::Configure
:
1352 ADDARGC("--configure");
1355 case Item::ConfigurePending
:
1356 ADDARGC("--configure");
1357 ADDARGC("--pending");
1360 case Item::TriggersPending
:
1361 ADDARGC("--triggers-only");
1362 ADDARGC("--pending");
1366 ADDARGC("--unpack");
1367 ADDARGC("--auto-deconfigure");
1371 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1372 I
->Op
!= Item::ConfigurePending
)
1374 ADDARGC("--no-triggers");
1378 // Write in the file or package names
1379 if (I
->Op
== Item::Install
)
1381 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1383 if (I
->File
[0] != '/')
1384 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1385 Args
.push_back(I
->File
.c_str());
1386 Size
+= I
->File
.length();
1391 string
const nativeArch
= _config
->Find("APT::Architecture");
1392 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1393 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1395 if((*I
).Pkg
.end() == true)
1397 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1399 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1400 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1401 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1402 strcmp(I
->Pkg
.Arch(), "none") == 0))
1404 char const * const name
= I
->Pkg
.Name();
1409 pkgCache::VerIterator PkgVer
;
1410 std::string name
= I
->Pkg
.Name();
1411 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1413 PkgVer
= I
->Pkg
.CurrentVer();
1414 if(PkgVer
.end() == true)
1415 PkgVer
= FindNowVersion(I
->Pkg
);
1418 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1419 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1420 ; // never arch-qualify a package without an arch
1421 else if (PkgVer
.end() == false)
1422 name
.append(":").append(PkgVer
.Arch());
1424 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1425 char * const fullname
= strdup(name
.c_str());
1426 Packages
.push_back(fullname
);
1430 // skip configure action if all sheduled packages disappeared
1431 if (oldSize
== Size
)
1438 if (_config
->FindB("Debug::pkgDPkgPM",false) == true)
1440 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1441 a
!= Args
.end(); ++a
)
1444 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1445 p
!= Packages
.end(); ++p
)
1450 Args
.push_back(NULL
);
1456 /* Mask off sig int/quit. We do this because dpkg also does when
1457 it forks scripts. What happens is that when you hit ctrl-c it sends
1458 it to all processes in the group. Since dpkg ignores the signal
1459 it doesn't die but we do! So we must also ignore it */
1460 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1461 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1463 // Check here for any SIGINT
1464 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1468 // ignore SIGHUP as well (debian #463030)
1469 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1472 d
->progress
->StartDpkg();
1473 std::set
<int> KeepFDs
;
1474 KeepFDs
.insert(fd
[1]);
1475 MergeKeepFdsFromConfiguration(KeepFDs
);
1476 pid_t Child
= ExecFork(KeepFDs
);
1479 // This is the child
1480 SetupSlavePtyMagic();
1481 close(fd
[0]); // close the read end of the pipe
1483 debSystem::DpkgChrootDirectory();
1485 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1488 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1492 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1495 // Discard everything in stdin before forking dpkg
1496 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1499 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1501 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1505 execvp(Args
[0], (char**) &Args
[0]);
1506 cerr
<< "Could not exec dpkg!" << endl
;
1511 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1517 // we read from dpkg here
1518 int const _dpkgin
= fd
[0];
1519 close(fd
[1]); // close the write end of the pipe
1522 sigemptyset(&d
->sigmask
);
1523 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1525 /* free vectors (and therefore memory) as we don't need the included data anymore */
1526 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1527 p
!= Packages
.end(); ++p
)
1531 // the result of the waitpid call
1534 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1536 // FIXME: move this to a function or something, looks ugly here
1537 // error handling, waitpid returned -1
1540 RunScripts("DPkg::Post-Invoke");
1542 // Restore sig int/quit
1543 signal(SIGQUIT
,old_SIGQUIT
);
1544 signal(SIGINT
,old_SIGINT
);
1546 signal(SIGHUP
,old_SIGHUP
);
1547 return _error
->Errno("waitpid","Couldn't wait for subprocess");
1550 // wait for input or output here
1552 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1553 FD_SET(STDIN_FILENO
, &rfds
);
1554 FD_SET(_dpkgin
, &rfds
);
1556 FD_SET(d
->master
, &rfds
);
1558 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1559 select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1560 &tv
, &d
->original_sigmask
);
1561 if (select_ret
< 0 && (errno
== EINVAL
|| errno
== ENOSYS
))
1562 select_ret
= racy_pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
,
1563 NULL
, &tv
, &d
->original_sigmask
);
1564 d
->progress
->Pulse();
1565 if (select_ret
== 0)
1567 else if (select_ret
< 0 && errno
== EINTR
)
1569 else if (select_ret
< 0)
1571 perror("select() returned error");
1575 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1576 DoTerminalPty(d
->master
);
1577 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1579 if(FD_ISSET(_dpkgin
, &rfds
))
1580 DoDpkgStatusFd(_dpkgin
);
1584 // Restore sig int/quit
1585 signal(SIGQUIT
,old_SIGQUIT
);
1586 signal(SIGINT
,old_SIGINT
);
1588 signal(SIGHUP
,old_SIGHUP
);
1589 // Check for an error code.
1590 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1592 // if it was set to "keep-dpkg-runing" then we won't return
1593 // here but keep the loop going and just report it as a error
1595 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1597 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1598 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1599 else if (WIFEXITED(Status
) != 0)
1600 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1602 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1603 _error
->Error("%s", d
->dpkg_error
.c_str());
1609 // dpkg is done at this point
1610 d
->progress
->Stop();
1614 if (pkgPackageManager::SigINTStop
)
1615 _error
->Warning(_("Operation was interrupted before it could finish"));
1617 if (RunScripts("DPkg::Post-Invoke") == false)
1620 if (_config
->FindB("Debug::pkgDPkgPM",false) == false)
1622 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1623 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1624 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1626 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1627 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1629 _error
->PushToStack();
1630 pkgCacheFile CacheFile
;
1631 CacheFile
.BuildCaches(NULL
, true);
1632 _error
->RevertToStack();
1637 Cache
.writeStateFile(NULL
);
1638 return d
->dpkg_error
.empty();
1641 void SigINT(int /*sig*/) {
1642 pkgPackageManager::SigINTStop
= true;
1645 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1646 // ---------------------------------------------------------------------
1648 void pkgDPkgPM::Reset()
1650 List
.erase(List
.begin(),List
.end());
1653 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1654 // ---------------------------------------------------------------------
1656 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1658 // If apport doesn't exist or isn't installed do nothing
1659 // This e.g. prevents messages in 'universes' without apport
1660 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1661 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1664 string pkgname
, reportfile
, pkgver
, arch
;
1665 string::size_type pos
;
1668 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1670 std::clog
<< "configured to not write apport reports" << std::endl
;
1674 // only report the first errors
1675 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1677 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1681 // check if its not a follow up error
1682 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1683 if(strstr(errormsg
, needle
) != NULL
) {
1684 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1688 // do not report disk-full failures
1689 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1690 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1694 // do not report out-of-memory failures
1695 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1696 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1697 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1701 // do not report bugs regarding inaccessible local files
1702 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1703 strstr(errormsg
, "cannot access archive") != NULL
) {
1704 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1708 // do not report errors encountered when decompressing packages
1709 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1710 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1714 // do not report dpkg I/O errors, this is a format string, so we compare
1715 // the prefix and the suffix of the error with the dpkg error message
1716 vector
<string
> io_errors
;
1717 io_errors
.push_back(string("failed to read"));
1718 io_errors
.push_back(string("failed to write"));
1719 io_errors
.push_back(string("failed to seek"));
1720 io_errors
.push_back(string("unexpected end of file or stream"));
1722 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1724 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1725 if (list
.size() > 1) {
1726 // we need to split %s, VectorizeString only allows char so we need
1727 // to kill the "s" manually
1728 if (list
[1].size() > 1) {
1729 list
[1].erase(0, 1);
1730 if(strstr(errormsg
, list
[0].c_str()) &&
1731 strstr(errormsg
, list
[1].c_str())) {
1732 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1739 // get the pkgname and reportfile
1740 pkgname
= flNotDir(pkgpath
);
1741 pos
= pkgname
.find('_');
1742 if(pos
!= string::npos
)
1743 pkgname
= pkgname
.substr(0, pos
);
1745 // find the package version and source package name
1746 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1747 if (Pkg
.end() == true)
1749 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1750 if (Ver
.end() == true)
1752 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1754 // if the file exists already, we check:
1755 // - if it was reported already (touched by apport).
1756 // If not, we do nothing, otherwise
1757 // we overwrite it. This is the same behaviour as apport
1758 // - if we have a report with the same pkgversion already
1760 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1761 if(FileExists(reportfile
))
1766 // check atime/mtime
1767 stat(reportfile
.c_str(), &buf
);
1768 if(buf
.st_mtime
> buf
.st_atime
)
1771 // check if the existing report is the same version
1772 report
= fopen(reportfile
.c_str(),"r");
1773 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1775 if(strstr(strbuf
,"Package:") == strbuf
)
1777 char pkgname
[255], version
[255];
1778 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1779 if(strcmp(pkgver
.c_str(), version
) == 0)
1789 // now write the report
1790 arch
= _config
->Find("APT::Architecture");
1791 report
= fopen(reportfile
.c_str(),"w");
1794 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1795 chmod(reportfile
.c_str(), 0);
1797 chmod(reportfile
.c_str(), 0600);
1798 fprintf(report
, "ProblemType: Package\n");
1799 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1800 time_t now
= time(NULL
);
1801 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1802 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1803 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1804 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1805 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1807 // ensure that the log is flushed
1809 fflush(d
->term_out
);
1811 // attach terminal log it if we have it
1812 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1813 if (!logfile_name
.empty())
1817 fprintf(report
, "DpkgTerminalLog:\n");
1818 log
= fopen(logfile_name
.c_str(),"r");
1822 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1823 fprintf(report
, " %s", buf
);
1824 fprintf(report
, " \n");
1829 // attach history log it if we have it
1830 string histfile_name
= _config
->FindFile("Dir::Log::History");
1831 if (!histfile_name
.empty())
1833 fprintf(report
, "DpkgHistoryLog:\n");
1834 FILE* log
= fopen(histfile_name
.c_str(),"r");
1838 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1839 fprintf(report
, " %s", buf
);
1844 // log the ordering, see dpkgpm.h and the "Ops" enum there
1845 const char *ops_str
[] = {
1853 fprintf(report
, "AptOrdering:\n");
1854 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1855 if ((*I
).Pkg
!= NULL
)
1856 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1858 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1860 // attach dmesg log (to learn about segfaults)
1861 if (FileExists("/bin/dmesg"))
1863 fprintf(report
, "Dmesg:\n");
1864 FILE *log
= popen("/bin/dmesg","r");
1868 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1869 fprintf(report
, " %s", buf
);
1874 // attach df -l log (to learn about filesystem status)
1875 if (FileExists("/bin/df"))
1878 fprintf(report
, "Df:\n");
1879 FILE *log
= popen("/bin/df -l","r");
1883 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1884 fprintf(report
, " %s", buf
);