X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/88a8975f156e452d9f3ebe76822b236e8962ebba..4adc750a2a8d0c6b2c03353b5538bc7446020f68:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 6ae85d2ea..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 /*{{{*/ @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +39,9 @@ #include #include #include + #include +#include #include #include #include @@ -47,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; @@ -64,8 +91,8 @@ EnvironmentSize() return size; } - -class pkgDPkgPMPrivate + /*}}}*/ +class pkgDPkgPMPrivate /*{{{*/ { public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), @@ -82,7 +109,7 @@ public: bool stdin_is_dev_null; // the buffer we use for the dpkg status-fd reading char dpkgbuf[1024]; - int dpkgbuf_pos; + size_t dpkgbuf_pos; FILE *term_out; FILE *history_out; string dpkg_error; @@ -101,7 +128,7 @@ public: bool direct_stdin; }; - + /*}}}*/ namespace { // Maps the dpkg "processing" info to human readable names. Entry 0 @@ -125,7 +152,7 @@ namespace const char *target; public: - MatchProcessingOp(const char *the_target) + explicit MatchProcessingOp(const char *the_target) : target(the_target) { } @@ -137,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; @@ -161,42 +185,13 @@ ionice(int PID) } return ExecWait(Process, "ionice"); } - -static std::string getDpkgExecutable() -{ - string Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); - string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/"); - size_t dpkgChrootLen = dpkgChrootDir.length(); - if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0) - { - if (dpkgChrootDir[dpkgChrootLen - 1] == '/') - --dpkgChrootLen; - Tmp = Tmp.substr(dpkgChrootLen); - } - return Tmp; -} - -// dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/ -static void dpkgChrootDirectory() -{ - std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory"); - if (chrootDir == "/") - return; - std::cerr << "Chrooting into " << chrootDir << std::endl; - if (chroot(chrootDir.c_str()) != 0) - _exit(100); - if (chdir("/") != 0) - _exit(100); -} /*}}}*/ - - // 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; @@ -276,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 @@ -317,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) { @@ -332,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 @@ -387,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; } @@ -415,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++) { @@ -432,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); @@ -466,7 +461,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) strprintf(hookfd, "%d", InfoFD); setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1); - dpkgChrootDirectory(); + debSystem::DpkgChrootDirectory(); const char *Args[4]; Args[0] = "/bin/sh"; Args[1] = "-c"; @@ -478,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) { @@ -494,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()); @@ -506,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()); @@ -550,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); @@ -571,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 @@ -697,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); + } } } } @@ -782,40 +801,33 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname) } /*}}}*/ // DPkgPM::DoDpkgStatusFd /*{{{*/ -// --------------------------------------------------------------------- -/* - */ void pkgDPkgPM::DoDpkgStatusFd(int statusfd) { - char *p, *q; - int len; - - len=read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos], sizeof(d->dpkgbuf)-d->dpkgbuf_pos); - d->dpkgbuf_pos += len; + ssize_t const len = read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos], + (sizeof(d->dpkgbuf)/sizeof(d->dpkgbuf[0])) - d->dpkgbuf_pos); if(len <= 0) return; + d->dpkgbuf_pos += (len / sizeof(d->dpkgbuf[0])); - // process line by line if we have a buffer - p = q = d->dpkgbuf; - while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL) + // process line by line from the buffer + char *p = d->dpkgbuf, *q = nullptr; + while((q=(char*)memchr(p, '\n', (d->dpkgbuf + d->dpkgbuf_pos) - p)) != nullptr) { - *q = 0; + *q = '\0'; ProcessDpkgStatusLine(p); - p=q+1; // continue with next line + p = q + 1; // continue with next line } - // now move the unprocessed bits (after the final \n that is now a 0x0) - // to the start and update d->dpkgbuf_pos - p = (char*)memrchr(d->dpkgbuf, 0, d->dpkgbuf_pos); - if(p == NULL) + // check if we stripped the buffer clean + if (p > (d->dpkgbuf + d->dpkgbuf_pos)) + { + d->dpkgbuf_pos = 0; return; + } - // we are interessted in the first char *after* 0x0 - p++; - - // move the unprocessed tail to the start and update pos - memmove(d->dpkgbuf, p, p-d->dpkgbuf); - d->dpkgbuf_pos = d->dpkgbuf+d->dpkgbuf_pos-p; + // otherwise move the unprocessed tail to the start and update pos + memmove(d->dpkgbuf, p, (p - d->dpkgbuf)); + d->dpkgbuf_pos = (d->dpkgbuf + d->dpkgbuf_pos) - p; } /*}}}*/ // DPkgPM::WriteHistoryTag /*{{{*/ @@ -840,7 +852,8 @@ bool pkgDPkgPM::OpenLog() // get current time char timestr[200]; time_t const t = time(NULL); - struct tm const * const tmp = localtime(&t); + struct tm tm_buf; + struct tm const * const tmp = localtime_r(&t, &tm_buf); strftime(timestr, sizeof(timestr), "%F %T", tmp); // open terminal log @@ -910,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); @@ -918,7 +934,7 @@ bool pkgDPkgPM::OpenLog() WriteHistoryTag("Purge",purge); fflush(d->history_out); } - + return true; } /*}}}*/ @@ -927,7 +943,8 @@ bool pkgDPkgPM::CloseLog() { char timestr[200]; time_t t = time(NULL); - struct tm *tmp = localtime(&t); + struct tm tm_buf; + struct tm *tmp = localtime_r(&t, &tm_buf); strftime(timestr, sizeof(timestr), "%F %T", tmp); if(d->term_out) @@ -966,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 @@ -1048,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) { @@ -1082,8 +1086,13 @@ void pkgDPkgPM::StartPtyMagic() _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master); else { +#ifdef HAVE_PTS_NAME_R + char slave_name[64]; // 64 is used by bionic + if (ptsname_r(d->master, slave_name, sizeof(slave_name)) != 0) +#else char const * const slave_name = ptsname(d->master); if (slave_name == NULL) +#endif _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master); else { @@ -1148,11 +1157,12 @@ void pkgDPkgPM::StartPtyMagic() free(d->slave); d->slave = NULL; } - _error->DumpErrors(std::cerr); + _error->DumpErrors(std::cerr, GlobalError::DEBUG, false); } _error->RevertToStack(); } -void pkgDPkgPM::SetupSlavePtyMagic() + /*}}}*/ +void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/ { if(d->master == -1 || d->slave == NULL) return; @@ -1184,7 +1194,8 @@ void pkgDPkgPM::SetupSlavePtyMagic() if (slaveFd != -1) close(slaveFd); } -void pkgDPkgPM::StopPtyMagic() + /*}}}*/ +void pkgDPkgPM::StopPtyMagic() /*{{{*/ { if (d->slave != NULL) free(d->slave); @@ -1194,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!"); @@ -1205,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 @@ -1218,51 +1229,17 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) d->progress = progress; // Generate the base argument list for dpkg - unsigned long StartSize = 0; - std::vector Args; - std::string DpkgExecutable = getDpkgExecutable(); - Args.push_back(DpkgExecutable.c_str()); - StartSize += DpkgExecutable.length(); - - // Stick in any custom dpkg options - Configuration::Item const *Opts = _config->Tree("DPkg::Options"); - if (Opts != 0) - { - Opts = Opts->Child; - for (; Opts != 0; Opts = Opts->Next) - { - if (Opts->Value.empty() == true) - continue; - Args.push_back(Opts->Value.c_str()); - StartSize += Opts->Value.length(); - } - } - + std::vector const sArgs = debSystem::GetDpkgBaseCommand(); + 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(), 0llu, + [](unsigned long long const i, std::string const &s) { return i + s.length(); }); size_t const BaseArgs = Args.size(); - // we need to detect if we can qualify packages with the architecture or not - Args.push_back("--assert-multi-arch"); - Args.push_back(NULL); - - pid_t dpkgAssertMultiArch = ExecFork(); - if (dpkgAssertMultiArch == 0) - { - dpkgChrootDirectory(); - // redirect everything to the ultimate sink as we only need the exit-status - int const nullfd = open("/dev/null", O_RDONLY); - dup2(nullfd, STDIN_FILENO); - dup2(nullfd, STDOUT_FILENO); - dup2(nullfd, STDERR_FILENO); - execvp(Args[0], (char**) &Args[0]); - _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!"); - _exit(2); - } 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) @@ -1277,39 +1254,44 @@ 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 OpenLog(); - bool dpkgMultiArch = false; - if (dpkgAssertMultiArch > 0) - { - int Status = 0; - while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch) - { - if (errno == EINTR) - continue; - _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch"); - break; - } - if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0) - dpkgMultiArch = true; - } + bool dpkgMultiArch = debSystem::SupportsMultiArch(); // 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 @@ -1334,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) @@ -1382,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; @@ -1475,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) @@ -1486,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); @@ -1494,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); @@ -1521,7 +1485,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) SetupSlavePtyMagic(); close(fd[0]); // close the read end of the pipe - dpkgChrootDirectory(); + debSystem::DpkgChrootDirectory(); if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0) _exit(100); @@ -1532,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); @@ -1570,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 @@ -1597,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)) @@ -1625,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); @@ -1648,21 +1614,17 @@ 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 && - unlink(oldpkgcache.c_str()) == 0) + RemoveFile("pkgDPkgPM::Go", oldpkgcache)) { std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache"); if (srcpkgcache.empty() == false && RealFileExists(srcpkgcache) == true) @@ -1675,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(); } @@ -1686,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()); } @@ -1694,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 @@ -1719,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; @@ -1768,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; @@ -1783,11 +1753,11 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) if(pos != string::npos) pkgname = pkgname.substr(0, pos); - // find the package versin and source package name + // find the package version and source package name pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname); if (Pkg.end() == true) return; - pkgCache::VerIterator Ver = Cache.GetCandidateVer(Pkg); + pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg); if (Ver.end() == true) return; pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr(); @@ -1798,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; @@ -1839,7 +1810,8 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) fprintf(report, "ProblemType: Package\n"); fprintf(report, "Architecture: %s\n", arch.c_str()); time_t now = time(NULL); - fprintf(report, "Date: %s" , ctime(&now)); + char ctime_buf[26]; // need at least 26 bytes according to ctime(3) + fprintf(report, "Date: %s" , ctime_r(&now, ctime_buf)); fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str()); fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName()); fprintf(report, "ErrorMessage:\n %s\n", errormsg);