X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/51dbf3686ee62584d7c2a21b6209a45ebceac5c9..319810767180e5c57c296b06c93e3ebec9f36a8e:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index bc15b8819..3dca2b209 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -39,7 +41,64 @@ using namespace std; +namespace +{ + // Maps the dpkg "processing" info to human readable names. Entry 0 + // of each array is the key, entry 1 is the value. + const std::pair PackageProcessingOps[] = { + std::make_pair("install", N_("Installing %s")), + std::make_pair("configure", N_("Configuring %s")), + std::make_pair("remove", N_("Removing %s")), + std::make_pair("purge", N_("Completely removing %s")), + std::make_pair("trigproc", N_("Running post-installation trigger %s")) + }; + + const std::pair * const PackageProcessingOpsBegin = PackageProcessingOps; + const std::pair * const PackageProcessingOpsEnd = PackageProcessingOps + sizeof(PackageProcessingOps) / sizeof(PackageProcessingOps[0]); + + // Predicate to test whether an entry in the PackageProcessingOps + // array matches a string. + class MatchProcessingOp + { + const char *target; + + public: + MatchProcessingOp(const char *the_target) + : target(the_target) + { + } + + bool operator()(const std::pair &pair) const + { + return strcmp(pair.first, target) == 0; + } + }; +} +/* 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; + pid_t Process = ExecFork(); + if (Process == 0) + { + char buf[32]; + snprintf(buf, sizeof(buf), "-p%d", PID); + const char *Args[4]; + Args[0] = "/usr/bin/ionice"; + Args[1] = "-c3"; + Args[2] = buf; + Args[3] = 0; + execv(Args[0], (char **)Args); + } + return ExecWait(Process, "ionice"); +} // DPkgPM::pkgDPkgPM - Constructor /*{{{*/ // --------------------------------------------------------------------- @@ -76,8 +135,14 @@ bool pkgDPkgPM::Configure(PkgIterator Pkg) { if (Pkg.end() == true) return false; - - List.push_back(Item(Item::Configure,Pkg)); + + List.push_back(Item(Item::Configure, Pkg)); + + // Use triggers for config calls if we configure "smart" + // as otherwise Pre-Depends will not be satisfied, see #526774 + if (_config->FindB("DPkg::TriggersPending", false) == true) + List.push_back(Item(Item::TriggersPending, PkgIterator())); + return true; } /*}}}*/ @@ -132,6 +197,9 @@ bool pkgDPkgPM::SendV2Pkgs(FILE *F) // Write out the package actions in order. for (vector::iterator I = List.begin(); I != List.end(); I++) { + if(I->Pkg.end() == true) + continue; + pkgDepCache::StateCache &S = Cache[I->Pkg]; fprintf(F,"%s ",I->Pkg.Name()); @@ -320,10 +388,11 @@ void pkgDPkgPM::DoTerminalPty(int master) */ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) { + bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false); // the status we output ostringstream status; - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "got from dpkg '" << line << "'" << std::endl; @@ -333,6 +402,13 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) '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 and conffile-prompt like this 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited + + Newer versions of dpkg sent also: + 'processing: install: pkg' + 'processing: configure: pkg' + 'processing: remove: pkg' + 'processing: purge: pkg' - but for apt is it a ignored "unknown" action + 'processing: trigproc: trigger' */ char* list[5]; @@ -344,12 +420,42 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); if( list[0] == NULL || list[1] == NULL || list[2] == NULL) { - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "ignoring line: not enough ':'" << std::endl; return; } - char *pkg = list[1]; - char *action = _strstrip(list[2]); + const char* const pkg = list[1]; + const char* action = _strstrip(list[2]); + + // 'processing' from dpkg looks like + // 'processing: action: pkg' + if(strncmp(list[0], "processing", strlen("processing")) == 0) + { + char s[200]; + const char* const pkg_or_trigger = _strstrip(list[2]); + action = _strstrip( list[1]); + const std::pair * const iter = + std::find_if(PackageProcessingOpsBegin, + PackageProcessingOpsEnd, + MatchProcessingOp(action)); + if(iter == PackageProcessingOpsEnd) + { + if (Debug == true) + std::clog << "ignoring unknown action: " << action << std::endl; + return; + } + snprintf(s, sizeof(s), _(iter->second), pkg_or_trigger); + + status << "pmstatus:" << pkg_or_trigger + << ":" << (PackagesDone/float(PackagesTotal)*100.0) + << ":" << s + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + if (Debug == true) + std::clog << "send: '" << status.str() << "'" << endl; + return; + } if(strncmp(action,"error",strlen("error")) == 0) { @@ -359,11 +465,11 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) << endl; if(OutStatusFd > 0) write(OutStatusFd, status.str().c_str(), status.str().size()); - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "send: '" << status.str() << "'" << endl; return; } - if(strncmp(action,"conffile",strlen("conffile")) == 0) + else if(strncmp(action,"conffile",strlen("conffile")) == 0) { status << "pmconffile:" << list[1] << ":" << (PackagesDone/float(PackagesTotal)*100.0) @@ -371,12 +477,12 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) << endl; if(OutStatusFd > 0) write(OutStatusFd, status.str().c_str(), status.str().size()); - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "send: '" << status.str() << "'" << endl; return; } - vector &states = PackageOps[pkg]; + vector const &states = PackageOps[pkg]; const char *next_action = NULL; if(PackageOpsDone[pkg] < states.size()) next_action = states[PackageOpsDone[pkg]].state; @@ -399,15 +505,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) << endl; if(OutStatusFd > 0) write(OutStatusFd, status.str().c_str(), status.str().size()); - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "send: '" << status.str() << "'" << endl; } - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + if (Debug == true) std::clog << "(parsed from dpkg) pkg: " << pkg << " action: " << action << endl; } - -// DPkgPM::DoDpkgStatusFd /*{{{*/ + /*}}}*/ +// DPkgPM::DoDpkgStatusFd /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -444,7 +550,7 @@ void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd) dpkgbuf_pos = dpkgbuf+dpkgbuf_pos-p; } /*}}}*/ - +// DPkgPM::OpenLog /*{{{*/ bool pkgDPkgPM::OpenLog() { string logdir = _config->FindDir("Dir::Log"); @@ -455,19 +561,21 @@ bool pkgDPkgPM::OpenLog() if (!logfile_name.empty()) { term_out = fopen(logfile_name.c_str(),"a"); + if (term_out == NULL) + return _error->WarningE(_("Could not open file '%s'"), logfile_name.c_str()); + chmod(logfile_name.c_str(), 0600); // output current time char outstr[200]; time_t t = time(NULL); struct tm *tmp = localtime(&t); strftime(outstr, sizeof(outstr), "%F %T", tmp); - fprintf(term_out, "\nLog started: "); - fprintf(term_out, outstr); - fprintf(term_out, "\n"); + fprintf(term_out, "\nLog started: %s\n", outstr); } return true; } - + /*}}}*/ +// DPkg::CloseLog /*{{{*/ bool pkgDPkgPM::CloseLog() { if(term_out) @@ -477,14 +585,14 @@ bool pkgDPkgPM::CloseLog() struct tm *tmp = localtime(&t); strftime(outstr, sizeof(outstr), "%F %T", tmp); fprintf(term_out, "Log ended: "); - fprintf(term_out, outstr); + fprintf(term_out, "%s", outstr); fprintf(term_out, "\n"); fclose(term_out); } term_out = NULL; return true; } - + /*}}}*/ /*{{{*/ // This implements a racy version of pselect for those architectures // that don't have a working implementation. @@ -506,7 +614,6 @@ static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds, return retval; } /*}}}*/ - // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- /* This globs the operations and calls dpkg @@ -518,15 +625,28 @@ static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds, */ bool pkgDPkgPM::Go(int OutStatusFd) { - unsigned int MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); - unsigned int MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); + fd_set rfds; + struct timespec tv; + sigset_t sigmask; + sigset_t original_sigmask; + + unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); + unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); + bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false); if (RunScripts("DPkg::Pre-Invoke") == false) return false; if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false) return false; - + + // support subpressing of triggers processing for special + // cases like d-i that runs the triggers handling manually + bool const SmartConf = (_config->Find("PackageManager::Configure", "all") != "all"); + bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); + if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true) + List.push_back(Item(Item::ConfigurePending, PkgIterator())); + // 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] = { @@ -540,20 +660,12 @@ bool pkgDPkgPM::Go(int OutStatusFd) { {"unpacked",N_("Preparing to configure %s") }, {"half-configured", N_("Configuring %s") }, -#if 0 - {"triggers-awaited", N_("Processing triggers for %s") }, - {"triggers-pending", N_("Processing triggers for %s") }, -#endif { "installed", N_("Installed %s")}, {NULL, NULL} }, // Remove operation { {"half-configured", N_("Preparing for removal of %s")}, -#if 0 - {"triggers-awaited", N_("Preparing for removal of %s")}, - {"triggers-pending", N_("Preparing for removal of %s")}, -#endif {"half-installed", N_("Removing %s")}, {"config-files", N_("Removed %s")}, {NULL, NULL} @@ -570,16 +682,19 @@ bool pkgDPkgPM::Go(int OutStatusFd) // 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::iterator I = List.begin(); I != List.end();I++) + for (vector::const_iterator I = List.begin(); I != List.end();I++) { - string name = (*I).Pkg.Name(); + if((*I).Pkg.end() == true) + continue; + + string const name = (*I).Pkg.Name(); PackageOpsDone[name] = 0; for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; i++) { PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); PackagesTotal++; } - } + } stdin_is_dev_null = false; @@ -587,19 +702,42 @@ bool pkgDPkgPM::Go(int OutStatusFd) OpenLog(); // this loop is runs once per operation - for (vector::iterator I = List.begin(); I != List.end();) + for (vector::const_iterator I = List.begin(); I != List.end();) { - vector::iterator J = I; - for (; J != List.end() && J->Op == I->Op; J++); + // Do all actions with the same Op in one run + vector::const_iterator J = I; + if (TriggersPending == true) + for (; J != List.end(); J++) + { + if (J->Op == I->Op) + continue; + if (J->Op != Item::TriggersPending) + break; + vector::const_iterator T = J + 1; + if (T != List.end() && T->Op == I->Op) + continue; + break; + } + else + for (; J != List.end() && J->Op == I->Op; J++) + /* nothing */; // Generate the argument list const char *Args[MaxArgs + 50]; + + // 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 if (J - I > (signed)MaxArgs) J = I + MaxArgs; unsigned int n = 0; unsigned long Size = 0; - string Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); + string const Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); Args[n++] = Tmp.c_str(); Size += strlen(Args[n-1]); @@ -651,7 +789,21 @@ bool pkgDPkgPM::Go(int OutStatusFd) Args[n++] = "--configure"; Size += strlen(Args[n-1]); break; - + + case Item::ConfigurePending: + Args[n++] = "--configure"; + Size += strlen(Args[n-1]); + Args[n++] = "--pending"; + Size += strlen(Args[n-1]); + break; + + case Item::TriggersPending: + Args[n++] = "--triggers-only"; + Size += strlen(Args[n-1]); + Args[n++] = "--pending"; + Size += strlen(Args[n-1]); + break; + case Item::Install: Args[n++] = "--unpack"; Size += strlen(Args[n-1]); @@ -659,7 +811,14 @@ bool pkgDPkgPM::Go(int OutStatusFd) Size += strlen(Args[n-1]); break; } - + + if (NoTriggers == true && I->Op != Item::TriggersPending && + I->Op != Item::ConfigurePending) + { + Args[n++] = "--no-triggers"; + Size += strlen(Args[n-1]); + } + // Write in the file or package names if (I->Op == Item::Install) { @@ -675,6 +834,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) { for (;I != J && Size < MaxArgBytes; I++) { + if((*I).Pkg.end() == true) + continue; Args[n++] = I->Pkg.Name(); Size += strlen(Args[n-1]); } @@ -701,34 +862,55 @@ bool pkgDPkgPM::Go(int OutStatusFd) sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN); sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN); + // ignore SIGHUP as well (debian #463030) + sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN); + struct termios tt; - struct termios tt_out; struct winsize win; - int master; - int slave; - - // FIXME: setup sensible signal handling (*ick*) - tcgetattr(0, &tt); - tcgetattr(1, &tt_out); - ioctl(0, TIOCGWINSZ, (char *)&win); - if (openpty(&master, &slave, NULL, &tt_out, &win) < 0) + int master = -1; + int slave = -1; + + // if tcgetattr does not return zero there was a error + // and we do not do any pty magic + if (tcgetattr(0, &tt) == 0) { - const char *s = _("Can not write log, openpty() " - "failed (/dev/pts not mounted?)\n"); - fprintf(stderr, "%s",s); - fprintf(term_out, "%s",s); - master = slave = -1; - } else { - struct termios rtt; - rtt = tt; - cfmakeraw(&rtt); - rtt.c_lflag &= ~ECHO; - tcsetattr(0, TCSAFLUSH, &rtt); + ioctl(0, TIOCGWINSZ, (char *)&win); + if (openpty(&master, &slave, NULL, &tt, &win) < 0) + { + const char *s = _("Can not write log, openpty() " + "failed (/dev/pts not mounted?)\n"); + fprintf(stderr, "%s",s); + if(term_out) + fprintf(term_out, "%s",s); + master = slave = -1; + } else { + struct termios rtt; + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + // block SIGTTOU during tcsetattr to prevent a hang if + // the process is a member of the background process group + // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGTTOU); + sigprocmask(SIG_BLOCK,&sigmask, &original_sigmask); + tcsetattr(0, TCSAFLUSH, &rtt); + sigprocmask(SIG_SETMASK, &original_sigmask, 0); + } } // Fork dpkg pid_t Child; _config->Set("APT::Keep-Fds::",fd[1]); + // send status information that we are about to fork dpkg + if(OutStatusFd > 0) { + ostringstream status; + status << "pmstatus:dpkg-exec:" + << (PackagesDone/float(PackagesTotal)*100.0) + << ":" << _("Running dpkg") + << endl; + write(OutStatusFd, status.str().c_str(), status.str().size()); + } Child = ExecFork(); // This is the child @@ -746,6 +928,15 @@ bool pkgDPkgPM::Go(int OutStatusFd) } close(fd[0]); // close the read end of the pipe + if (_config->FindDir("DPkg::Chroot-Directory","/") != "/") + { + std::cerr << "Chrooting into " + << _config->FindDir("DPkg::Chroot-Directory") + << std::endl; + if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0) + _exit(100); + } + if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0) _exit(100); @@ -765,7 +956,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) _exit(100); } - /* No Job Control Stop Env is a magic dpkg var that prevents it from using sigstop */ putenv((char *)"DPKG_NO_TSTP=yes"); @@ -774,6 +964,10 @@ bool pkgDPkgPM::Go(int OutStatusFd) _exit(100); } + // apply ionice + if (_config->FindB("DPkg::UseIoNice", false) == true) + ionice(Child); + // clear the Keep-Fd again _config->Clear("APT::Keep-Fds",fd[1]); @@ -781,22 +975,18 @@ bool pkgDPkgPM::Go(int OutStatusFd) int Status = 0; // we read from dpkg here - int _dpkgin = fd[0]; + int const _dpkgin = fd[0]; close(fd[1]); // close the write end of the pipe - // the result of the waitpid call - int res; if(slave > 0) close(slave); // setups fds - fd_set rfds; - struct timespec tv; - sigset_t sigmask; - sigset_t original_sigmask; sigemptyset(&sigmask); sigprocmask(SIG_BLOCK,&sigmask,&original_sigmask); + // the result of the waitpid call + int res; int select_ret; while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { if(res < 0) { @@ -809,12 +999,13 @@ bool pkgDPkgPM::Go(int OutStatusFd) // 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"); } // wait for input or output here FD_ZERO(&rfds); - if (!stdin_is_dev_null) + if (master >= 0 && !stdin_is_dev_null) FD_SET(0, &rfds); FD_SET(_dpkgin, &rfds); if(master >= 0) @@ -848,6 +1039,7 @@ bool pkgDPkgPM::Go(int OutStatusFd) // Restore sig int/quit signal(SIGQUIT,old_SIGQUIT); signal(SIGINT,old_SIGINT); + signal(SIGHUP,old_SIGHUP); if(master >= 0) { @@ -861,7 +1053,7 @@ bool pkgDPkgPM::Go(int OutStatusFd) // if it was set to "keep-dpkg-runing" then we won't return // here but keep the loop going and just report it as a error // for later - bool stopOnError = _config->FindB("Dpkg::StopOnError",true); + bool const stopOnError = _config->FindB("Dpkg::StopOnError",true); if(stopOnError) RunScripts("DPkg::Post-Invoke"); @@ -884,6 +1076,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (RunScripts("DPkg::Post-Invoke") == false) return false; + + Cache.writeStateFile(NULL); return true; } /*}}}*/