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
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
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<Item>::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
- d->nr_terminal_rows = win.ws_row;
+ 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);
-
- // setup terminal
- SetupTerminalScrollArea(d->nr_terminal_rows);
+ 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();
+ }
+
+ 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
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<Item>::const_iterator I = List.begin();
while (I != List.end())
{
// 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)
{
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);
}
}
}
-- 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"));