X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/3111692d0be3b2dc401180728e51e1b1fd2525a1..2183a0862311cf2fbb82f61ecdcdf3816ce90040:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index a54c20330..500d4a260 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -76,6 +76,31 @@ 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) +{ + 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 /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -111,8 +136,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; } /*}}}*/ @@ -167,6 +198,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()); @@ -354,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; @@ -372,6 +407,7 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) 'processing: install: pkg' 'processing: configure: pkg' 'processing: remove: pkg' + 'processing: purge: pkg' - but for apt is it a ignored "unknown" action 'processing: trigproc: trigger' */ @@ -384,28 +420,28 @@ 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]; - char *pkg_or_trigger = _strstrip(list[2]); - action =_strstrip( list[1]); + 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 (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "ignoring unknwon action: " << action << std::endl; + if (Debug == true) + std::clog << "ignoring unknown action: " << action << std::endl; return; } snprintf(s, sizeof(s), _(iter->second), pkg_or_trigger); @@ -416,7 +452,7 @@ 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; } @@ -437,13 +473,13 @@ 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; pkgFailures++; WriteApportReport(list[1], list[3]); 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) @@ -451,12 +487,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; @@ -479,15 +515,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 /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -524,7 +560,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"); @@ -547,7 +583,8 @@ bool pkgDPkgPM::OpenLog() } return true; } - + /*}}}*/ +// DPkg::CloseLog /*{{{*/ bool pkgDPkgPM::CloseLog() { if(term_out) @@ -564,7 +601,7 @@ bool pkgDPkgPM::CloseLog() term_out = NULL; return true; } - + /*}}}*/ /*{{{*/ // This implements a racy version of pselect for those architectures // that don't have a working implementation. @@ -586,7 +623,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 @@ -603,9 +639,9 @@ bool pkgDPkgPM::Go(int OutStatusFd) sigset_t sigmask; sigset_t original_sigmask; - unsigned int MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); - unsigned int MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); - bool NoTriggers = _config->FindB("DPkg::NoTriggers",false); + 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; @@ -613,6 +649,12 @@ bool pkgDPkgPM::Go(int OutStatusFd) 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"); + 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] = { @@ -626,20 +668,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} @@ -656,16 +690,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; @@ -673,19 +710,28 @@ 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++); + vector::const_iterator J = I; + 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]); @@ -735,11 +781,23 @@ bool pkgDPkgPM::Go(int OutStatusFd) case Item::Configure: Args[n++] = "--configure"; - if (NoTriggers) - Args[n++] = "--no-triggers"; 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]); @@ -747,7 +805,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) { @@ -763,6 +828,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]); } @@ -793,40 +860,50 @@ bool pkgDPkgPM::Go(int OutStatusFd) 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; - // 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); + 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); + 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 @@ -844,6 +921,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); @@ -863,7 +949,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"); @@ -872,6 +957,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]); @@ -879,11 +968,9 @@ 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); @@ -891,6 +978,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) 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) { @@ -956,7 +1045,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"); @@ -1033,6 +1122,13 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) return; } + // do not report dpkg I/O errors + // XXX - this message is localized, but this only matches the English version. This is better than nothing. + if(strstr(errormsg, "short read in buffer_copy (")) { + std::clog << _("No apport report written because the error message indicates a dpkg I/O error") << std::endl; + return; + } + // get the pkgname and reportfile pkgname = flNotDir(pkgpath); pos = pkgname.find('_'); @@ -1125,6 +1221,46 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) fclose(log); } } + + // log the ordering + const char *ops_str[] = {"Install", "Configure","Remove","Purge"}; + fprintf(report, "AptOrdering:\n"); + for (vector::iterator I = List.begin(); I != List.end(); I++) + fprintf(report, " %s: %s\n", (*I).Pkg.Name(), ops_str[(*I).Op]); + + // attach dmesg log (to learn about segfaults) + if (FileExists("/bin/dmesg")) + { + FILE *log = NULL; + char buf[1024]; + + fprintf(report, "Dmesg:\n"); + log = popen("/bin/dmesg","r"); + if(log != NULL) + { + while( fgets(buf, sizeof(buf), log) != NULL) + fprintf(report, " %s", buf); + fclose(log); + } + } + + // attach df -l log (to learn about filesystem status) + if (FileExists("/bin/df")) + { + FILE *log = NULL; + char buf[1024]; + + fprintf(report, "Df:\n"); + log = popen("/bin/df -l","r"); + if(log != NULL) + { + while( fgets(buf, sizeof(buf), log) != NULL) + fprintf(report, " %s", buf); + fclose(log); + } + } + fclose(report); + } /*}}}*/