From: Michael Vogt Date: Thu, 31 Oct 2013 20:03:33 +0000 (+0100) Subject: move pty magic into its own functions X-Git-Tag: 0.9.13.exp1~5 X-Git-Url: https://git.saurik.com/apt.git/commitdiff_plain/177296dffd8bf7d9ce5870b135c412958aab3756 move pty magic into its own functions --- 177296dffd8bf7d9ce5870b135c412958aab3756 diff --cc apt-pkg/deb/dpkgpm.cc index b252c02eb,ddfc7c04e..d1b11098c --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@@ -55,12 -54,16 +55,12 @@@ class pkgDPkgPMPrivat public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), term_out(NULL), history_out(NULL), - progress(NULL) - last_reported_progress(0.0), nr_terminal_rows(0), - fancy_progress_output(false), master(-1), slave(-1) ++ progress(NULL), master(-1), slave(-1) { dpkgbuf[0] = '\0'; - if(_config->FindB("Dpkg::Progress-Fancy", false) == true) - { - fancy_progress_output = true; - _config->Set("DpkgPM::Progress", true); - } - + } + ~pkgDPkgPMPrivate() + { } bool stdin_is_dev_null; // the buffer we use for the dpkg status-fd reading @@@ -69,7 -72,20 +69,17 @@@ FILE *term_out; FILE *history_out; string dpkg_error; - - float last_reported_progress; - int nr_terminal_rows; - bool fancy_progress_output; + APT::Progress::PackageManager *progress; + + // pty stuff + struct termios tt; + int master; + int slave; + + // signals + sigset_t sigmask; + sigset_t original_sigmask; + }; namespace @@@ -945,74 -1029,99 +955,126 @@@ static int racy_pselect(int nfds, fd_se sigprocmask(SIG_SETMASK, &origmask, 0); return retval; } -/*}}}*/ + /*}}}*/ -void pkgDPkgPM::SetupTerminalScrollArea(int nr_rows) +// DPkgPM::BuildPackagesProgressMap /*{{{*/ +void pkgDPkgPM::BuildPackagesProgressMap() { - if(!d->fancy_progress_output) - return; - - // scroll down a bit to avoid visual glitch when the screen - // area shrinks by one row - std::cout << "\n"; - - // save cursor - std::cout << "\033[s"; - - // set scroll region (this will place the cursor in the top left) - std::cout << "\033[1;" << nr_rows - 1 << "r"; - - // restore cursor but ensure its inside the scrolling area - std::cout << "\033[u"; - static const char *move_cursor_up = "\033[1A"; - std::cout << move_cursor_up; - std::flush(std::cout); -} + // 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] = { + // Install operation + { + {"half-installed", N_("Preparing %s")}, + {"unpacked", N_("Unpacking %s") }, + {NULL, NULL} + }, + // 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} + }, + }; -void pkgDPkgPM::CleanupTerminal() -{ - // reset scroll area - SetupTerminalScrollArea(d->nr_terminal_rows + 1); - if(d->fancy_progress_output) + // 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) { - // override the progress line (sledgehammer) - static const char* clear_screen_below_cursor = "\033[J"; - std::cout << clear_screen_below_cursor; - std::flush(std::cout); + if((*I).Pkg.end() == true) + continue; + + string const name = (*I).Pkg.FullName(); + PackageOpsDone[name] = 0; + for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i) + { + PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); + PackagesTotal++; + } } } + /*}}}*/ +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13) +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 GoNoABIBreak(progress); +} +#endif + void pkgDPkgPM::StartPtyMagic() + { + // setup the pty and stuff + struct winsize win; + + // if tcgetattr does not return zero there was a error + // and we do not do any pty magic + _error->PushToStack(); + if (tcgetattr(STDOUT_FILENO, &d->tt) == 0) + { + ioctl(1, TIOCGWINSZ, (char *)&win); - d->nr_terminal_rows = win.ws_row; + if (openpty(&d->master, &d->slave, NULL, &d->tt, &win) < 0) + { + _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); + d->master = d->slave = -1; + } else { + struct termios rtt; + rtt = d->tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + rtt.c_lflag |= ISIG; + // 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(&d->sigmask); + sigaddset(&d->sigmask, SIGTTOU); + sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask); + tcsetattr(0, TCSAFLUSH, &rtt); + sigprocmask(SIG_SETMASK, &d->original_sigmask, 0); + } + } + // complain only if stdout is either a terminal (but still failed) or is an invalid + // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. + else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) + _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); + + if (_error->PendingError() == true) + _error->DumpErrors(std::cerr); + _error->RevertToStack(); - - // setup terminal - SetupTerminalScrollArea(d->nr_terminal_rows); + } + + void pkgDPkgPM::StopPtyMagic() + { + if(d->slave > 0) + close(d->slave); + if(d->master >= 0) + { + tcsetattr(0, TCSAFLUSH, &d->tt); + close(d->master); + } + } + // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- /* This globs the operations and calls dpkg @@@ -1116,7 -1274,10 +1176,13 @@@ bool pkgDPkgPM::GoNoABIBreak(APT::Progr dpkgMultiArch = true; } - // go over each item + // start pty magic before the loop + StartPtyMagic(); + ++ // Tell the progress that its starting and fork dpkg ++ d->progress->Start(); ++ + // this loop is runs once per dpkg operation vector::const_iterator I = List.begin(); while (I != List.end()) { @@@ -1309,54 -1470,20 +1375,7 @@@ // ignore SIGHUP as well (debian #463030) sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN); - struct termios tt; - struct winsize win; - int master = -1; - int slave = -1; - - // if tcgetattr does not return zero there was a error - // and we do not do any pty magic - _error->PushToStack(); - if (tcgetattr(STDOUT_FILENO, &tt) == 0) - { - ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win); - if (openpty(&master, &slave, NULL, &tt, &win) < 0) - { - _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); - master = slave = -1; - } else { - struct termios rtt; - rtt = tt; - cfmakeraw(&rtt); - rtt.c_lflag &= ~ECHO; - rtt.c_lflag |= ISIG; - // 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); - } - } - // complain only if stdout is either a terminal (but still failed) or is an invalid - // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. - else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) - _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); - - if (_error->PendingError() == true) - _error->DumpErrors(std::cerr); - _error->RevertToStack(); - - // this is the dpkg status-fd, we need to keep it - // Fork dpkg - pid_t Child; -- _config->Set("APT::Keep-Fds::",fd[1]); - - // Tell the progress that its starting and fork dpkg - // FIXME: this is called once per dpkg run which is *too often* - d->progress->Start(); - // 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; - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - } -- - Child = ExecFork(); + pid_t Child = ExecFork(); // This is the child if (Child == 0) { @@@ -1474,12 -1596,12 +1488,12 @@@ continue; } - if(master >= 0 && FD_ISSET(master, &rfds)) - DoTerminalPty(master); - if(master >= 0 && FD_ISSET(0, &rfds)) - DoStdin(master); + if(d->master >= 0 && FD_ISSET(d->master, &rfds)) + DoTerminalPty(d->master); + if(d->master >= 0 && FD_ISSET(0, &rfds)) + DoStdin(d->master); if(FD_ISSET(_dpkgin, &rfds)) - DoDpkgStatusFd(_dpkgin, OutStatusFd); + DoDpkgStatusFd(_dpkgin); } close(_dpkgin); @@@ -1524,10 -1639,14 +1531,10 @@@ } } } -- CloseLog(); -- // dpkg is done at this point - if(_config->FindB("DPkgPM::Progress", false) == true) - SendTerminalProgress(100); - - CleanupTerminal(); + d->progress->Stop(); + StopPtyMagic(); ++ CloseLog(); if (pkgPackageManager::SigINTStop) _error->Warning(_("Operation was interrupted before it could finish")); diff --cc apt-pkg/deb/dpkgpm.h index 50b5d609b,5551b4383..06318d94f --- a/apt-pkg/deb/dpkgpm.h +++ b/apt-pkg/deb/dpkgpm.h @@@ -99,8 -96,8 +99,10 @@@ class pkgDPkgPM : public pkgPackageMana bool OpenLog(); bool CloseLog(); + // helper + void BuildPackagesProgressMap(); + void StartPtyMagic(); + void StopPtyMagic(); // input processing void DoStdin(int master);