X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/258b9e512c4001e806c5c0966acecd3d742ec6e9..4adc750a2a8d0c6b2c03353b5538bc7446020f68:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 7355af9d5..c1b9a28f4 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1,10 +1,9 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $ /* ###################################################################### DPKG Package Manager - Provide an interface to dpkg - + ##################################################################### */ /*}}}*/ // Includes /*{{{*/ @@ -40,7 +39,9 @@ #include #include #include + #include +#include #include #include #include @@ -48,14 +49,39 @@ #include #include #include +#include +#include #include /*}}}*/ using namespace std; -APT_PURE static unsigned int -EnvironmentSize() +APT_PURE static string AptHistoryRequestingUser() /*{{{*/ +{ + const char* EnvKeys[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"}; + + for (const auto &Key: EnvKeys) + { + if (getenv(Key) != nullptr) + { + int uid = atoi(getenv(Key)); + if (uid > 0) { + struct passwd pwd; + struct passwd *result; + char buf[255]; + if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result != NULL) { + std::string res; + strprintf(res, "%s (%d)", pwd.pw_name, uid); + return res; + } + } + } + } + return ""; +} + /*}}}*/ +APT_PURE static unsigned int EnvironmentSize() /*{{{*/ { unsigned int size = 0; char **envp = environ; @@ -65,8 +91,8 @@ EnvironmentSize() return size; } - -class pkgDPkgPMPrivate + /*}}}*/ +class pkgDPkgPMPrivate /*{{{*/ { public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), @@ -102,7 +128,7 @@ public: bool direct_stdin; }; - + /*}}}*/ namespace { // Maps the dpkg "processing" info to human readable names. Entry 0 @@ -138,13 +164,10 @@ namespace }; } -/* helper function to ionice the given PID - - there is no C header for ionice yet - just the syscall interface - so we use the binary from util-linux -*/ -static bool -ionice(int PID) +// ionice - helper function to ionice the given PID /*{{{*/ +/* there is no C header for ionice yet - just the syscall interface + so we use the binary from util-linux */ +static bool ionice(int PID) { if (!FileExists("/usr/bin/ionice")) return false; @@ -162,13 +185,13 @@ ionice(int PID) } return ExecWait(Process, "ionice"); } - + /*}}}*/ // FindNowVersion - Helper to find a Version in "now" state /*{{{*/ // --------------------------------------------------------------------- -/* This is helpful when a package is no longer installed but has residual +/* This is helpful when a package is no longer installed but has residual * config files */ -static +static pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg) { pkgCache::VerIterator Ver; @@ -248,7 +271,7 @@ bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge) { if (Pkg.end() == true) return false; - + if (Purge == true) List.push_back(Item(Item::Purge,Pkg)); else @@ -289,14 +312,14 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version) Top = Top->Child; continue; } - + while (Top != 0 && Top->Next == 0) Top = Top->Parent; if (Top != 0) Top = Top->Next; - } + } fprintf(F,"\n"); - + // Write out the package actions in order. for (vector::iterator I = List.begin(); I != List.end(); ++I) { @@ -304,7 +327,7 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version) continue; pkgDepCache::StateCache &S = Cache[I->Pkg]; - + fprintf(F,"%s ",I->Pkg.Name()); // Current version which we are going to replace @@ -359,13 +382,13 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version) fprintf(F,"**ERROR**\n"); else fprintf(F,"%s\n",I->File.c_str()); - } + } else if (I->Op == Item::Configure) fprintf(F,"**CONFIGURE**\n"); else if (I->Op == Item::Remove || I->Op == Item::Purge) fprintf(F,"**REMOVE**\n"); - + if (ferror(F) != 0) return false; } @@ -387,7 +410,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) Opts = Opts->Child; sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN); - + unsigned int Count = 1; for (; Opts != 0; Opts = Opts->Next, Count++) { @@ -404,10 +427,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0) Pos = OptSec.length(); OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos); - + unsigned int Version = _config->FindI(OptSec+"::Version",1); unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO); - + // Create the pipes std::set KeepFDs; MergeKeepFdsFromConfiguration(KeepFDs); @@ -450,10 +473,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) close(Pipes[0]); FILE *F = fdopen(Pipes[1],"w"); if (F == 0) { - result = _error->Errno("fdopen","Faild to open new FD"); + result = _error->Errno("fdopen","Failed to open new FD"); break; } - + // Feed it the filenames. if (Version <= 1) { @@ -466,7 +489,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) // No errors here.. if (I->File[0] != '/') continue; - + /* Feed the filename of each package that is pending install into the pipe. */ fprintf(F,"%s\n",I->File.c_str()); @@ -478,7 +501,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) SendPkgsInfo(F, Version); fclose(F); - + // Clean up the sub process if (ExecWait(Process,Opts->Value.c_str()) == false) { result = _error->Error("Failure running script %s",Opts->Value.c_str()); @@ -522,18 +545,15 @@ void pkgDPkgPM::DoTerminalPty(int master) struct timespec sleepfor = { 0, 500000000 }; nanosleep(&sleepfor, NULL); return; - } - if(len <= 0) + } + if(len <= 0) return; FileFd::Write(1, term_buf, len); if(d->term_out) fwrite(term_buf, len, sizeof(char), d->term_out); } /*}}}*/ -// DPkgPM::ProcessDpkgStatusBuf /*{{{*/ -// --------------------------------------------------------------------- -/* - */ +// DPkgPM::ProcessDpkgStatusBuf /*{{{*/ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) { bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false); @@ -543,14 +563,14 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) /* dpkg sends strings like this: 'status: : ' 'status: :: ' - + 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg' 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger' */ // we need to split on ": " (note the appended space) as the ':' is // part of the pkgname:arch information that dpkg sends - // + // // A dpkg error message may contain additional ":" (like // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..." // so we need to ensure to not split too much @@ -669,37 +689,64 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); // FIXME: this needs a muliarch testcase - // FIXME2: is "pkgname" here reliable with dpkg only sending us + // FIXME2: is "pkgname" here reliable with dpkg only sending us // short pkgnames? if (action == "disappear") handleDisappearAction(pkgname); return; - } + } if (prefix == "status") { - vector const &states = PackageOps[pkg]; - if(PackageOpsDone[pkg] < states.size()) + std::vector &states = PackageOps[pkg]; + if (action == "triggers-pending") { - char const * const next_action = states[PackageOpsDone[pkg]].state; - if (next_action && Debug == true) + if (Debug == true) std::clog << "(parsed from dpkg) pkg: " << short_pkgname - << " action: " << action << " (expected: '" << next_action << "' " + << " action: " << action << " (prefix 2 to " << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; - // check if the package moved to the next dpkg state - if(next_action && (action == next_action)) + states.insert(states.begin(), {"installed", N_("Installed %s")}); + states.insert(states.begin(), {"half-configured", N_("Configuring %s")}); + PackagesTotal += 2; + } + else if(PackageOpsDone[pkg] < states.size()) + { + char const * next_action = states[PackageOpsDone[pkg]].state; + if (next_action) { - // only read the translation if there is actually a next action - char const * const translation = _(states[PackageOpsDone[pkg]].str); + /* + if (action == "half-installed" && strcmp("half-configured", next_action) == 0 && + PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state) + { + if (Debug == true) + std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action + << " pending trigger defused by unpack" << std::endl; + // unpacking a package defuses the pending trigger + PackageOpsDone[pkg] += 2; + PackagesDone += 2; + next_action = states[PackageOpsDone[pkg]].state; + } + */ + if (Debug == true) + std::clog << "(parsed from dpkg) pkg: " << short_pkgname + << " action: " << action << " (expected: '" << next_action << "' " + << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + + // check if the package moved to the next dpkg state + if(action == next_action) + { + // only read the translation if there is actually a next action + char const * const translation = _(states[PackageOpsDone[pkg]].str); - // we moved from one dpkg state to a new one, report that - ++PackageOpsDone[pkg]; - ++PackagesDone; + // we moved from one dpkg state to a new one, report that + ++PackageOpsDone[pkg]; + ++PackagesDone; - std::string msg; - strprintf(msg, translation, i18n_pkgname.c_str()); - d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); + std::string msg; + strprintf(msg, translation, i18n_pkgname.c_str()); + d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); + } } } } @@ -876,6 +923,9 @@ bool pkgDPkgPM::OpenLog() } if (_config->Exists("Commandline::AsString") == true) WriteHistoryTag("Commandline", _config->Find("Commandline::AsString")); + std::string RequestingUser = AptHistoryRequestingUser(); + if (RequestingUser != "") + WriteHistoryTag("Requested-By", RequestingUser); WriteHistoryTag("Install", install); WriteHistoryTag("Reinstall", reinstall); WriteHistoryTag("Upgrade", upgrade); @@ -884,7 +934,7 @@ bool pkgDPkgPM::OpenLog() WriteHistoryTag("Purge",purge); fflush(d->history_out); } - + return true; } /*}}}*/ @@ -933,79 +983,66 @@ bool pkgDPkgPM::CloseLog() return true; } /*}}}*/ - /*}}}*/ -/*{{{*/ -// This implements a racy version of pselect for those architectures -// that don't have a working implementation. -// FIXME: Probably can be removed on Lenny+1 -static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, const struct timespec *timeout, - const sigset_t *sigmask) -{ - sigset_t origmask; - struct timeval tv; - int retval; - - tv.tv_sec = timeout->tv_sec; - tv.tv_usec = timeout->tv_nsec/1000; - - sigprocmask(SIG_SETMASK, sigmask, &origmask); - retval = select(nfds, readfds, writefds, exceptfds, &tv); - sigprocmask(SIG_SETMASK, &origmask, 0); - return retval; -} - /*}}}*/ // DPkgPM::BuildPackagesProgressMap /*{{{*/ void pkgDPkgPM::BuildPackagesProgressMap() { // map the dpkg states to the operations that are performed // (this is sorted in the same way as Item::Ops) - static const struct DpkgState DpkgStatesOpMap[][7] = { + static const std::array, 4> DpkgStatesOpMap = {{ // Install operation - { - {"half-installed", N_("Preparing %s")}, - {"unpacked", N_("Unpacking %s") }, - {NULL, NULL} - }, + {{ + {"half-installed", N_("Preparing %s")}, + {"unpacked", N_("Unpacking %s") }, + {nullptr, nullptr} + }}, // Configure operation - { + {{ {"unpacked",N_("Preparing to configure %s") }, {"half-configured", N_("Configuring %s") }, { "installed", N_("Installed %s")}, - {NULL, NULL} - }, + }}, // Remove operation - { + {{ {"half-configured", N_("Preparing for removal of %s")}, {"half-installed", N_("Removing %s")}, {"config-files", N_("Removed %s")}, - {NULL, NULL} - }, + }}, // Purge operation - { + {{ {"config-files", N_("Preparing to completely remove %s")}, {"not-installed", N_("Completely removed %s")}, - {NULL, NULL} - }, - }; + {nullptr, nullptr} + }}, + }}; + static_assert(Item::Purge == 3, "Enum item has unexpected index for mapping array"); // init the PackageOps map, go over the list of packages that // that will be [installed|configured|removed|purged] and add // them to the PackageOps map (the dpkg states it goes through) // and the PackageOpsTranslations (human readable strings) - for (vector::const_iterator I = List.begin(); I != List.end(); ++I) + for (auto &&I : List) { - if((*I).Pkg.end() == true) + if(I.Pkg.end() == true) continue; - string const name = (*I).Pkg.FullName(); + string const name = I.Pkg.FullName(); PackageOpsDone[name] = 0; - for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i) + auto AddToPackageOps = std::back_inserter(PackageOps[name]); + if (I.Op == Item::Purge && I.Pkg->CurrentVer != 0) { - PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); - PackagesTotal++; + // purging a package which is installed first passes through remove states + auto const DpkgOps = DpkgStatesOpMap[Item::Remove]; + std::copy(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps); + PackagesTotal += DpkgOps.size(); } + auto const DpkgOps = DpkgStatesOpMap[I.Op]; + std::copy_if(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps, [&](DpkgState const &state) { + if (state.state == nullptr) + return false; + ++PackagesTotal; + return true; + }); } /* one extra: We don't want the progress bar to reach 100%, especially not if we call dpkg --configure --pending and process a bunch of triggers @@ -1015,18 +1052,18 @@ void pkgDPkgPM::BuildPackagesProgressMap() ++PackagesTotal; } /*}}}*/ -bool pkgDPkgPM::Go(int StatusFd) +bool pkgDPkgPM::Go(int StatusFd) /*{{{*/ { APT::Progress::PackageManager *progress = NULL; if (StatusFd == -1) progress = APT::Progress::PackageManagerProgressFactory(); else progress = new APT::Progress::PackageManagerProgressFd(StatusFd); - + return Go(progress); } - -void pkgDPkgPM::StartPtyMagic() + /*}}}*/ +void pkgDPkgPM::StartPtyMagic() /*{{{*/ { if (_config->FindB("Dpkg::Use-Pty", true) == false) { @@ -1124,7 +1161,8 @@ void pkgDPkgPM::StartPtyMagic() } _error->RevertToStack(); } -void pkgDPkgPM::SetupSlavePtyMagic() + /*}}}*/ +void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/ { if(d->master == -1 || d->slave == NULL) return; @@ -1156,7 +1194,8 @@ void pkgDPkgPM::SetupSlavePtyMagic() if (slaveFd != -1) close(slaveFd); } -void pkgDPkgPM::StopPtyMagic() + /*}}}*/ +void pkgDPkgPM::StopPtyMagic() /*{{{*/ { if (d->slave != NULL) free(d->slave); @@ -1166,7 +1205,7 @@ void pkgDPkgPM::StopPtyMagic() close(d->protect_slave_from_dying); d->protect_slave_from_dying = -1; } - if(d->master >= 0) + if(d->master >= 0) { if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSAFLUSH, &d->tt) == -1) _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!"); @@ -1177,7 +1216,7 @@ void pkgDPkgPM::StopPtyMagic() // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- -/* This globs the operations and calls dpkg +/* This globs the operations and calls dpkg * * If it is called with a progress object apt will report the install * progress to this object. It maps the dpkg states a package goes @@ -1194,16 +1233,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) std::vector Args(sArgs.size(), NULL); std::transform(sArgs.begin(), sArgs.end(), Args.begin(), [](std::string const &s) { return s.c_str(); }); - unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0, + unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0llu, [](unsigned long long const i, std::string const &s) { return i + s.length(); }); size_t const BaseArgs = Args.size(); fd_set rfds; struct timespec tv; - // FIXME: do we really need this limit when we have MaxArgBytes? - unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",32*1024); - // try to figure out the max environment size int OSArgMax = sysconf(_SC_ARG_MAX); if(OSArgMax < 0) @@ -1218,15 +1254,33 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false) return false; + auto const noopDPkgInvocation = _config->FindB("Debug::pkgDPkgPM",false); + // store auto-bits as they are supposed to be after dpkg is run + if (noopDPkgInvocation == false) + Cache.writeStateFile(NULL); + + decltype(List)::const_iterator::difference_type const notconfidx = + _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits::max() : + std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base()); + // support subpressing of triggers processing for special // cases like d-i that runs the triggers handling manually bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); - if (_config->FindB("DPkg::ConfigurePending", true) == true) + bool const ConfigurePending = _config->FindB("DPkg::ConfigurePending", true); + if (ConfigurePending) List.push_back(Item(Item::ConfigurePending, PkgIterator())); // for the progress BuildPackagesProgressMap(); + if (notconfidx != std::numeric_limits::max()) + { + if (ConfigurePending) + List.erase(std::next(List.begin(), notconfidx), std::prev(List.end())); + else + List.erase(std::next(List.begin(), notconfidx), List.end()); + } + d->stdin_is_dev_null = false; // create log @@ -1237,7 +1291,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) // start pty magic before the loop StartPtyMagic(); - // Tell the progress that its starting and fork dpkg + // Tell the progress that its starting and fork dpkg d->progress->Start(d->master); // this loop is runs once per dpkg operation @@ -1262,33 +1316,14 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) for (; J != List.end() && J->Op == I->Op; ++J) /* nothing */; - // keep track of allocated strings for multiarch package names - std::vector Packages; + auto const size = (J - I) + 10; // start with the baseset of arguments - unsigned long Size = StartSize; + auto Size = StartSize; Args.erase(Args.begin() + BaseArgs, Args.end()); - - // Now check if we are within the MaxArgs limit - // - // this code below is problematic, because it may happen that - // the argument list is split in a way that A depends on B - // and they are in the same "--configure A B" run - // - with the split they may now be configured in different - // runs, using Immediate-Configure-All can help prevent this. - if (J - I > (signed)MaxArgs) - { - J = I + MaxArgs; - unsigned long const size = MaxArgs + 10; - Args.reserve(size); - Packages.reserve(size); - } - else - { - unsigned long const size = (J - I) + 10; - Args.reserve(size); - Packages.reserve(size); - } + Args.reserve(size); + // keep track of allocated strings for multiarch package names + std::vector Packages(size, nullptr); int fd[2]; if (pipe(fd) != 0) @@ -1310,13 +1345,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) ADDARGC("--force-remove-essential"); ADDARGC("--remove"); break; - + case Item::Purge: ADDARGC("--force-depends"); ADDARGC("--force-remove-essential"); ADDARGC("--purge"); break; - + case Item::Configure: ADDARGC("--configure"); break; @@ -1403,8 +1438,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) #undef ADDARG J = I; - - if (_config->FindB("Debug::pkgDPkgPM",false) == true) + + if (noopDPkgInvocation == true) { for (std::vector::const_iterator a = Args.begin(); a != Args.end(); ++a) @@ -1414,6 +1449,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) p != Packages.end(); ++p) free(*p); Packages.clear(); + close(fd[0]); + close(fd[1]); continue; } Args.push_back(NULL); @@ -1422,18 +1459,17 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) clog << flush; cerr << flush; - /* Mask off sig int/quit. We do this because dpkg also does when + /* Mask off sig int/quit. We do this because dpkg also does when it forks scripts. What happens is that when you hit ctrl-c it sends - it to all processes in the group. Since dpkg ignores the signal + it to all processes in the group. Since dpkg ignores the signal it doesn't die but we do! So we must also ignore it */ sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN); sighandler_t old_SIGINT = signal(SIGINT,SigINT); - + // Check here for any SIGINT - if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install)) + if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install)) break; - - + // ignore SIGHUP as well (debian #463030) sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN); @@ -1460,33 +1496,37 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) int dummy = 0; if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0) _exit(100); - + // Discard everything in stdin before forking dpkg if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0) _exit(100); - + while (read(STDIN_FILENO,&dummy,1) == 1); - + if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0) _exit(100); } + // if color support isn't enabled/disabled explicitly tell + // dpkg to use the same state apt is using for its color support + if (_config->FindB("APT::Color", false) == true) + setenv("DPKG_COLORS", "always", 0); + else + setenv("DPKG_COLORS", "never", 0); + execvp(Args[0], (char**) &Args[0]); cerr << "Could not exec dpkg!" << endl; _exit(100); - } - - // apply ionice - if (_config->FindB("DPkg::UseIoNice", false) == true) - ionice(Child); - - // Wait for dpkg - int Status = 0; + } // we read from dpkg here int const _dpkgin = fd[0]; close(fd[1]); // close the write end of the pipe + // apply ionice + if (_config->FindB("DPkg::UseIoNice", false) == true) + ionice(Child); + // setups fds sigemptyset(&d->sigmask); sigprocmask(SIG_BLOCK,&d->sigmask,&d->original_sigmask); @@ -1498,22 +1538,16 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) Packages.clear(); // the result of the waitpid call + int Status = 0; int res; - int select_ret; + bool waitpid_failure = false; while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { if(res < 0) { - // FIXME: move this to a function or something, looks ugly here // error handling, waitpid returned -1 if (errno == EINTR) continue; - RunScripts("DPkg::Post-Invoke"); - - // Restore sig int/quit - signal(SIGQUIT,old_SIGQUIT); - signal(SIGINT,old_SIGINT); - - signal(SIGHUP,old_SIGHUP); - return _error->Errno("waitpid","Couldn't wait for subprocess"); + waitpid_failure = true; + break; } // wait for input or output here @@ -1525,22 +1559,19 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) FD_SET(d->master, &rfds); tv.tv_sec = 0; tv.tv_nsec = d->progress->GetPulseInterval(); - select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL, + auto const select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL, &tv, &d->original_sigmask); - if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS)) - select_ret = racy_pselect(max(d->master, _dpkgin)+1, &rfds, NULL, - NULL, &tv, &d->original_sigmask); d->progress->Pulse(); - if (select_ret == 0) - continue; - else if (select_ret < 0 && errno == EINTR) - continue; - else if (select_ret < 0) - { - perror("select() returned error"); - continue; - } - + if (select_ret == 0) + continue; + else if (select_ret < 0 && errno == EINTR) + continue; + else if (select_ret < 0) + { + perror("select() returned error"); + continue; + } + if(d->master >= 0 && FD_ISSET(d->master, &rfds)) DoTerminalPty(d->master); if(d->master >= 0 && FD_ISSET(0, &rfds)) @@ -1553,12 +1584,19 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) // Restore sig int/quit signal(SIGQUIT,old_SIGQUIT); signal(SIGINT,old_SIGINT); - signal(SIGHUP,old_SIGHUP); + + if (waitpid_failure == true) + { + strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]); + _error->Error("%s", d->dpkg_error.c_str()); + break; + } + // Check for an error code. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) { - // if it was set to "keep-dpkg-runing" then we won't return + // if it was set to "keep-dpkg-running" then we won't return // here but keep the loop going and just report it as a error // for later bool const stopOnError = _config->FindB("Dpkg::StopOnError",true); @@ -1576,17 +1614,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) } } // dpkg is done at this point - d->progress->Stop(); StopPtyMagic(); CloseLog(); if (pkgPackageManager::SigINTStop) _error->Warning(_("Operation was interrupted before it could finish")); - if (RunScripts("DPkg::Post-Invoke") == false) - return false; - - if (_config->FindB("Debug::pkgDPkgPM",false) == false) + if (noopDPkgInvocation == false) { std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache"); if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true && @@ -1603,7 +1637,15 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) } } - Cache.writeStateFile(NULL); + // disappearing packages can forward their auto-bit + if (disappearedPkgs.empty() == false) + Cache.writeStateFile(NULL); + + d->progress->Stop(); + + if (RunScripts("DPkg::Post-Invoke") == false) + return false; + return d->dpkg_error.empty(); } @@ -1614,7 +1656,7 @@ void SigINT(int /*sig*/) { // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgDPkgPM::Reset() +void pkgDPkgPM::Reset() { List.erase(List.begin(),List.end()); } @@ -1622,7 +1664,7 @@ void pkgDPkgPM::Reset() // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) +void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) { // If apport doesn't exist or isn't installed do nothing // This e.g. prevents messages in 'universes' without apport @@ -1647,20 +1689,20 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) return; } - // check if its not a follow up error + // check if its not a follow up error const char *needle = dgettext("dpkg", "dependency problems - leaving unconfigured"); if(strstr(errormsg, needle) != NULL) { std::clog << _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl; return; } - // do not report disk-full failures + // do not report disk-full failures if(strstr(errormsg, strerror(ENOSPC)) != NULL) { std::clog << _("No apport report written because the error message indicates a disk full error") << std::endl; return; } - // do not report out-of-memory failures + // do not report out-of-memory failures if(strstr(errormsg, strerror(ENOMEM)) != NULL || strstr(errormsg, "failed to allocate memory") != NULL) { std::clog << _("No apport report written because the error message indicates a out of memory error") << std::endl; @@ -1696,7 +1738,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) // to kill the "s" manually if (list[1].size() > 1) { list[1].erase(0, 1); - if(strstr(errormsg, list[0].c_str()) && + if(strstr(errormsg, list[0].c_str()) && strstr(errormsg, list[1].c_str())) { std::clog << _("No apport report written because the error message indicates a dpkg I/O error") << std::endl; return; @@ -1726,7 +1768,8 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) // we overwrite it. This is the same behaviour as apport // - if we have a report with the same pkgversion already // then we skip it - reportfile = flCombine("/var/crash",pkgname+".0.crash"); + _config->CndSet("Dir::Apport", "var/crash"); + reportfile = flCombine(_config->FindDir("Dir::Apport", "var/crash"), pkgname+".0.crash"); if(FileExists(reportfile)) { struct stat buf;