summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt-pkg/deb/dpkgpm.cc338
-rw-r--r--apt-pkg/deb/dpkgpm.h14
-rw-r--r--apt-pkg/install-progress.cc305
-rw-r--r--apt-pkg/install-progress.h137
-rw-r--r--apt-pkg/makefile5
-rw-r--r--apt-pkg/packagemanager.cc49
-rw-r--r--apt-pkg/packagemanager.h16
-rw-r--r--apt-private/private-install.cc33
-rwxr-xr-xtest/integration/test-apt-progress-fd51
-rwxr-xr-xtest/integration/test-apt-progress-fd-deb82264
10 files changed, 735 insertions, 277 deletions
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc
index ae895c9e6..98fb7581a 100644
--- a/apt-pkg/deb/dpkgpm.cc
+++ b/apt-pkg/deb/dpkgpm.cc
@@ -19,6 +19,7 @@
#include <apt-pkg/fileutl.h>
#include <apt-pkg/cachefile.h>
#include <apt-pkg/packagemanager.h>
+#include <apt-pkg/install-progress.h>
#include <unistd.h>
#include <stdlib.h>
@@ -54,15 +55,12 @@ class pkgDPkgPMPrivate
public:
pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
term_out(NULL), history_out(NULL),
- last_reported_progress(0.0), nr_terminal_rows(0),
- fancy_progress_output(false)
+ progress(NULL)
{
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
@@ -71,10 +69,7 @@ public:
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;
};
namespace
@@ -137,6 +132,20 @@ 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()
{
@@ -513,7 +522,7 @@ void pkgDPkgPM::DoTerminalPty(int master)
// ---------------------------------------------------------------------
/*
*/
-void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line)
+void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
{
bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false);
if (Debug == true)
@@ -581,28 +590,16 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line)
{
if(action == "error")
{
- status << "pmerror:" << list[1]
- << ":" << (PackagesDone/float(PackagesTotal)*100.0)
- << ":" << list[3]
- << endl;
- if(OutStatusFd > 0)
- FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
- if (Debug == true)
- std::clog << "send: '" << status.str() << "'" << endl;
+ d->progress->Error(list[1], PackagesDone, PackagesTotal,
+ list[3]);
pkgFailures++;
WriteApportReport(list[1].c_str(), list[3].c_str());
return;
}
else if(action == "conffile")
{
- status << "pmconffile:" << list[1]
- << ":" << (PackagesDone/float(PackagesTotal)*100.0)
- << ":" << list[3]
- << endl;
- if(OutStatusFd > 0)
- FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
- if (Debug == true)
- std::clog << "send: '" << status.str() << "'" << endl;
+ d->progress->ConffilePrompt(list[1], PackagesDone, PackagesTotal,
+ list[3]);
return;
}
}
@@ -655,16 +652,8 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line)
return;
}
std::string msg;
- strprintf(msg, _(iter->second), short_pkgname.c_str());
-
- status << "pmstatus:" << short_pkgname
- << ":" << (PackagesDone/float(PackagesTotal)*100.0)
- << ":" << msg
- << endl;
- if(OutStatusFd > 0)
- FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
- if (Debug == true)
- std::clog << "send: '" << status.str() << "'" << endl;
+ strprintf(msg, _(iter->second), i18n_pkgname.c_str());
+ d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
// FIXME: this needs a muliarch testcase
// FIXME2: is "pkgname" here reliable with dpkg only sending us
@@ -687,23 +676,14 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line)
// action
const char *translation = _(states[PackageOpsDone[pkg]].str);
std::string msg;
- strprintf(msg, translation, short_pkgname.c_str());
-
+
// we moved from one dpkg state to a new one, report that
PackageOpsDone[pkg]++;
PackagesDone++;
- // build the status str
- status << "pmstatus:" << short_pkgname
- << ":" << (PackagesDone/float(PackagesTotal)*100.0)
- << ":" << msg
- << endl;
- if(_config->FindB("DPkgPM::Progress", false) == true)
- SendTerminalProgress(PackagesDone/float(PackagesTotal)*100.0);
+
+ strprintf(msg, translation, i18n_pkgname.c_str());
+ d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
- if(OutStatusFd > 0)
- FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
- if (Debug == true)
- std::clog << "send: '" << status.str() << "'" << endl;
}
if (Debug == true)
std::clog << "(parsed from dpkg) pkg: " << short_pkgname
@@ -763,7 +743,7 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname)
// ---------------------------------------------------------------------
/*
*/
-void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd)
+void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
{
char *p, *q;
int len;
@@ -778,7 +758,7 @@ void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd)
while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL)
{
*q = 0;
- ProcessDpkgStatusLine(OutStatusFd, p);
+ ProcessDpkgStatusLine(p);
p=q+1; // continue with next line
}
@@ -944,50 +924,6 @@ bool pkgDPkgPM::CloseLog()
return true;
}
/*}}}*/
-// DPkgPM::SendTerminalProgress /*{{{*/
-// ---------------------------------------------------------------------
-/* Send progress info to the terminal
- */
-void pkgDPkgPM::SendTerminalProgress(float percentage)
-{
- int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1);
-
- if(percentage < (d->last_reported_progress + reporting_steps))
- return;
-
- std::string progress_str;
- strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage);
- if (d->fancy_progress_output)
- {
- int row = d->nr_terminal_rows;
-
- static string save_cursor = "\033[s";
- static string restore_cursor = "\033[u";
-
- static string set_bg_color = "\033[42m"; // green
- static string set_fg_color = "\033[30m"; // black
-
- static string restore_bg = "\033[49m";
- static string restore_fg = "\033[39m";
-
- std::cout << save_cursor
- // move cursor position to last row
- << "\033[" << row << ";0f"
- << set_bg_color
- << set_fg_color
- << progress_str
- << restore_cursor
- << restore_bg
- << restore_fg;
- }
- else
- {
- std::cout << progress_str << "\r\n";
- }
- std::flush(std::cout);
-
- d->last_reported_progress = percentage;
-}
/*}}}*/
/*{{{*/
// This implements a racy version of pselect for those architectures
@@ -1009,73 +945,81 @@ static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds,
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++;
+ }
}
}
-
-
+ /*}}}*/
// DPkgPM::Go - Run the sequence /*{{{*/
// ---------------------------------------------------------------------
/* This globs the operations and calls dpkg
- *
- * If it is called with "OutStatusFd" set to a valid file descriptor
- * apt will report the install progress over this fd. It maps the
- * dpkg states a package goes through to human readable (and i10n-able)
+ *
+ * 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
+ * through to human readable (and i10n-able)
* names and calculates a percentage for each step.
-*/
-bool pkgDPkgPM::Go(int OutStatusFd)
+ */
+bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
{
pkgPackageManager::SigINTStop = false;
+ d->progress = progress;
// Generate the base argument list for dpkg
- std::vector<const char *> Args;
unsigned long StartSize = 0;
- 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);
- }
- }
- Args.push_back(Tmp.c_str());
- StartSize += Tmp.length();
+ std::vector<const char *> 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");
@@ -1132,54 +1076,8 @@ bool pkgDPkgPM::Go(int OutStatusFd)
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] = {
- // 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}
- },
- };
-
- // 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)
- {
- 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++;
- }
- }
+ // for the progress
+ BuildPackagesProgressMap();
d->stdin_is_dev_null = false;
@@ -1201,8 +1099,9 @@ bool pkgDPkgPM::Go(int OutStatusFd)
dpkgMultiArch = true;
}
- // this loop is runs once per operation
- for (vector<Item>::const_iterator I = List.begin(); I != List.end();)
+ // go over each item
+ vector<Item>::const_iterator I = List.begin();
+ while (I != List.end())
{
// Do all actions with the same Op in one run
vector<Item>::const_iterator J = I;
@@ -1403,8 +1302,7 @@ bool pkgDPkgPM::Go(int OutStatusFd)
_error->PushToStack();
if (tcgetattr(STDOUT_FILENO, &tt) == 0)
{
- ioctl(1, TIOCGWINSZ, (char *)&win);
- d->nr_terminal_rows = win.ws_row;
+ 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?"));
@@ -1434,20 +1332,14 @@ bool pkgDPkgPM::Go(int OutStatusFd)
_error->DumpErrors(std::cerr);
_error->RevertToStack();
- // Fork dpkg
- pid_t Child;
+ // this is the dpkg status-fd, we need to keep it
_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;
- FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
- }
- Child = ExecFork();
+ // Tell the progress that its starting and fork dpkg
+ // FIXME: this is called once per dpkg run which is *too often*
+ d->progress->Start();
+
+ pid_t Child = ExecFork();
// This is the child
if (Child == 0)
{
@@ -1484,10 +1376,6 @@ bool pkgDPkgPM::Go(int OutStatusFd)
if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
_exit(100);
}
- // setup terminal
- SetupTerminalScrollArea(d->nr_terminal_rows);
- SendTerminalProgress(PackagesDone/float(PackagesTotal)*100.0);
-
/* No Job Control Stop Env is a magic dpkg var that prevents it
from using sigstop */
putenv((char *)"DPKG_NO_TSTP=yes");
@@ -1549,13 +1437,16 @@ bool pkgDPkgPM::Go(int OutStatusFd)
FD_SET(_dpkgin, &rfds);
if(master >= 0)
FD_SET(master, &rfds);
- tv.tv_sec = 1;
- tv.tv_nsec = 0;
+ tv.tv_sec = 0;
+ tv.tv_nsec = d->progress->GetPulseInterval();
select_ret = pselect(max(master, _dpkgin)+1, &rfds, NULL, NULL,
&tv, &original_sigmask);
if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS))
select_ret = racy_pselect(max(master, _dpkgin)+1, &rfds, NULL,
NULL, &tv, &original_sigmask);
+
+ d->progress->Pulse();
+
if (select_ret == 0)
continue;
else if (select_ret < 0 && errno == EINTR)
@@ -1571,7 +1462,7 @@ bool pkgDPkgPM::Go(int OutStatusFd)
if(master >= 0 && FD_ISSET(0, &rfds))
DoStdin(master);
if(FD_ISSET(_dpkgin, &rfds))
- DoDpkgStatusFd(_dpkgin, OutStatusFd);
+ DoDpkgStatusFd(_dpkgin);
}
close(_dpkgin);
@@ -1611,7 +1502,7 @@ bool pkgDPkgPM::Go(int OutStatusFd)
if(stopOnError)
{
CloseLog();
- CleanupTerminal();
+ d->progress->Stop();
return false;
}
}
@@ -1619,10 +1510,7 @@ bool pkgDPkgPM::Go(int OutStatusFd)
CloseLog();
// dpkg is done at this point
- if(_config->FindB("DPkgPM::Progress", false) == true)
- SendTerminalProgress(100);
-
- CleanupTerminal();
+ d->progress->Stop();
if (pkgPackageManager::SigINTStop)
_error->Warning(_("Operation was interrupted before it could finish"));
diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h
index 1a58e1af5..5f00eee5d 100644
--- a/apt-pkg/deb/dpkgpm.h
+++ b/apt-pkg/deb/dpkgpm.h
@@ -23,6 +23,7 @@ using std::map;
class pkgDPkgPMPrivate;
+
class pkgDPkgPM : public pkgPackageManager
{
private:
@@ -83,11 +84,12 @@ class pkgDPkgPM : public pkgPackageManager
__deprecated bool SendV2Pkgs(FILE *F);
bool SendPkgsInfo(FILE * const F, unsigned int const &Version);
void WriteHistoryTag(std::string const &tag, std::string value);
+ std::string ExpandShortPackageName(pkgDepCache &Cache,
+ const std::string &short_pkgname);
// Terminal progress
void SetupTerminalScrollArea(int nr_scrolled_rows);
void SendTerminalProgress(float percentage);
- void CleanupTerminal();
// apport integration
void WriteApportReport(const char *pkgpath, const char *errormsg);
@@ -95,18 +97,22 @@ class pkgDPkgPM : public pkgPackageManager
// dpkg log
bool OpenLog();
bool CloseLog();
+
+ // helper
+ void BuildPackagesProgressMap();
// input processing
void DoStdin(int master);
void DoTerminalPty(int master);
- void DoDpkgStatusFd(int statusfd, int OutStatusFd);
- void ProcessDpkgStatusLine(int OutStatusFd, char *line);
+ void DoDpkgStatusFd(int statusfd);
+ void ProcessDpkgStatusLine(char *line);
// The Actuall installation implementation
virtual bool Install(PkgIterator Pkg,std::string File);
virtual bool Configure(PkgIterator Pkg);
virtual bool Remove(PkgIterator Pkg,bool Purge = false);
- virtual bool Go(int StatusFd=-1);
+
+ virtual bool Go(APT::Progress::PackageManager *progress);
virtual void Reset();
public:
diff --git a/apt-pkg/install-progress.cc b/apt-pkg/install-progress.cc
new file mode 100644
index 000000000..9d0d4e3f1
--- /dev/null
+++ b/apt-pkg/install-progress.cc
@@ -0,0 +1,305 @@
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/install-progress.h>
+
+#include <apti18n.h>
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sstream>
+#include <fcntl.h>
+
+namespace APT {
+namespace Progress {
+
+bool PackageManager::StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction)
+{
+ int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1);
+ percentage = StepsDone/(float)TotalSteps * 100.0;
+ strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage);
+
+ if(percentage < (last_reported_progress + reporting_steps))
+ return false;
+
+ return true;
+}
+
+PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
+ : StepsDone(0), StepsTotal(1)
+{
+ OutStatusFd = progress_fd;
+}
+
+void PackageManagerProgressFd::WriteToStatusFd(std::string s)
+{
+ if(OutStatusFd <= 0)
+ return;
+ FileFd::Write(OutStatusFd, s.c_str(), s.size());
+}
+
+void PackageManagerProgressFd::Start()
+{
+ if(OutStatusFd <= 0)
+ return;
+
+ // FIXME: use SetCloseExec here once it taught about throwing
+ // exceptions instead of doing _exit(100) on failure
+ fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
+
+ // send status information that we are about to fork dpkg
+ std::ostringstream status;
+ status << "pmstatus:dpkg-exec:"
+ << (StepsDone/float(StepsTotal)*100.0)
+ << ":" << _("Running dpkg")
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+void PackageManagerProgressFd::Stop()
+{
+ // clear the Keep-Fd again
+ _config->Clear("APT::Keep-Fds", OutStatusFd);
+}
+
+void PackageManagerProgressFd::Error(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ErrorMessage)
+{
+ std::ostringstream status;
+ status << "pmerror:" << PackageName
+ << ":" << (StepsDone/float(TotalSteps)*100.0)
+ << ":" << ErrorMessage
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+void PackageManagerProgressFd::ConffilePrompt(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ConfMessage)
+{
+ std::ostringstream status;
+ status << "pmconffile:" << PackageName
+ << ":" << (StepsDone/float(TotalSteps)*100.0)
+ << ":" << ConfMessage
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+
+bool PackageManagerProgressFd::StatusChanged(std::string PackageName,
+ unsigned int xStepsDone,
+ unsigned int xTotalSteps,
+ std::string pkg_action)
+{
+ StepsDone = xStepsDone;
+ StepsTotal = xTotalSteps;
+
+ // build the status str
+ std::ostringstream status;
+ status << "pmstatus:" << StringSplit(PackageName, ":")[0]
+ << ":" << (StepsDone/float(StepsTotal)*100.0)
+ << ":" << pkg_action
+ << std::endl;
+ WriteToStatusFd(status.str());
+
+ if(_config->FindB("Debug::APT::Progress::PackageManagerFd", false) == true)
+ std::cerr << "progress: " << PackageName << " " << xStepsDone
+ << " " << xTotalSteps << " " << pkg_action
+ << std::endl;
+
+
+ return true;
+}
+
+
+PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
+ : StepsDone(0), StepsTotal(1)
+{
+ OutStatusFd = progress_fd;
+}
+
+void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
+{
+ FileFd::Write(OutStatusFd, s.c_str(), s.size());
+}
+
+void PackageManagerProgressDeb822Fd::Start()
+{
+ // FIXME: use SetCloseExec here once it taught about throwing
+ // exceptions instead of doing _exit(100) on failure
+ fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
+
+ // send status information that we are about to fork dpkg
+ std::ostringstream status;
+ status << "Status: " << "progress" << std::endl
+ << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl
+ << "Message: " << _("Running dpkg") << std::endl
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+void PackageManagerProgressDeb822Fd::Stop()
+{
+ // clear the Keep-Fd again
+ _config->Clear("APT::Keep-Fds", OutStatusFd);
+}
+
+void PackageManagerProgressDeb822Fd::Error(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ErrorMessage)
+{
+ std::ostringstream status;
+ status << "Status: " << "Error" << std::endl
+ << "Package:" << PackageName << std::endl
+ << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl
+ << "Message: " << ErrorMessage << std::endl
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ConfMessage)
+{
+ std::ostringstream status;
+ status << "Status: " << "ConfFile" << std::endl
+ << "Package:" << PackageName << std::endl
+ << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl
+ << "Message: " << ConfMessage << std::endl
+ << std::endl;
+ WriteToStatusFd(status.str());
+}
+
+
+bool PackageManagerProgressDeb822Fd::StatusChanged(std::string PackageName,
+ unsigned int xStepsDone,
+ unsigned int xTotalSteps,
+ std::string message)
+{
+ StepsDone = xStepsDone;
+ StepsTotal = xTotalSteps;
+
+ // build the status str
+ std::ostringstream status;
+ status << "Status: " << "progress" << std::endl
+ << "Package: " << PackageName << std::endl
+ << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl
+ << "Message: " << message << std::endl
+ << std::endl;
+ WriteToStatusFd(status.str());
+
+ return true;
+}
+
+
+void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
+{
+ // 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);
+}
+
+PackageManagerFancy::PackageManagerFancy()
+ : nr_terminal_rows(-1)
+{
+ struct winsize win;
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) == 0)
+ {
+ nr_terminal_rows = win.ws_row;
+ }
+}
+
+void PackageManagerFancy::Start()
+{
+ if (nr_terminal_rows > 0)
+ SetupTerminalScrollArea(nr_terminal_rows);
+}
+
+void PackageManagerFancy::Stop()
+{
+ if (nr_terminal_rows > 0)
+ {
+ SetupTerminalScrollArea(nr_terminal_rows + 1);
+
+ // override the progress line (sledgehammer)
+ static const char* clear_screen_below_cursor = "\033[J";
+ std::cout << clear_screen_below_cursor;
+ }
+}
+
+bool PackageManagerFancy::StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction)
+{
+ if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
+ HumanReadableAction))
+ return false;
+
+ int row = nr_terminal_rows;
+
+ static string save_cursor = "\033[s";
+ static string restore_cursor = "\033[u";
+
+ static string set_bg_color = "\033[42m"; // green
+ static string set_fg_color = "\033[30m"; // black
+
+ static string restore_bg = "\033[49m";
+ static string restore_fg = "\033[39m";
+
+ std::cout << save_cursor
+ // move cursor position to last row
+ << "\033[" << row << ";0f"
+ << set_bg_color
+ << set_fg_color
+ << progress_str
+ << restore_cursor
+ << restore_bg
+ << restore_fg;
+ std::flush(std::cout);
+ last_reported_progress = percentage;
+
+ return true;
+}
+
+bool PackageManagerText::StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction)
+{
+ if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
+ return false;
+
+ std::cout << progress_str << "\r\n";
+ std::flush(std::cout);
+
+ last_reported_progress = percentage;
+
+ return true;
+}
+
+
+
+}; // namespace progress
+}; // namespace apt
diff --git a/apt-pkg/install-progress.h b/apt-pkg/install-progress.h
new file mode 100644
index 000000000..9c31eac92
--- /dev/null
+++ b/apt-pkg/install-progress.h
@@ -0,0 +1,137 @@
+#ifndef PKGLIB_IPROGRESS_H
+#define PKGLIB_IPROGRESS_H
+
+#include <string>
+#include <unistd.h>
+
+
+namespace APT {
+namespace Progress {
+
+ class PackageManager
+ {
+ private:
+ /** \brief dpointer placeholder */
+ void *d;
+
+ protected:
+ std::string progress_str;
+ float percentage;
+ int last_reported_progress;
+
+ public:
+ PackageManager()
+ : percentage(0.0), last_reported_progress(-1) {};
+ virtual ~PackageManager() {};
+
+ virtual void Start() {};
+ virtual void Stop() {};
+
+ virtual pid_t fork() {return fork(); };
+
+ virtual void Pulse() {};
+ virtual long GetPulseInterval() {
+ return 500000;
+ };
+
+ virtual bool StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction) ;
+ virtual void Error(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ErrorMessage) {};
+ virtual void ConffilePrompt(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ConfMessage) {};
+ };
+
+ class PackageManagerProgressFd : public PackageManager
+ {
+ protected:
+ int OutStatusFd;
+ int StepsDone;
+ int StepsTotal;
+ void WriteToStatusFd(std::string msg);
+
+ public:
+ PackageManagerProgressFd(int progress_fd);
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual bool StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction);
+ virtual void Error(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ErrorMessage);
+ virtual void ConffilePrompt(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ConfMessage);
+
+ };
+
+ class PackageManagerProgressDeb822Fd : public PackageManager
+ {
+ protected:
+ int OutStatusFd;
+ int StepsDone;
+ int StepsTotal;
+ void WriteToStatusFd(std::string msg);
+
+ public:
+ PackageManagerProgressDeb822Fd(int progress_fd);
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual bool StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction);
+ virtual void Error(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ErrorMessage);
+ virtual void ConffilePrompt(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string ConfMessage);
+ };
+
+ class PackageManagerFancy : public PackageManager
+ {
+ protected:
+ int nr_terminal_rows;
+ void SetupTerminalScrollArea(int nr_rows);
+
+ public:
+ PackageManagerFancy();
+ virtual void Start();
+ virtual void Stop();
+ virtual bool StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction);
+ };
+
+ class PackageManagerText : public PackageManager
+ {
+ public:
+ virtual bool StatusChanged(std::string PackageName,
+ unsigned int StepsDone,
+ unsigned int TotalSteps,
+ std::string HumanReadableAction);
+ };
+
+
+}; // namespace Progress
+}; // namespace APT
+
+#endif
diff --git a/apt-pkg/makefile b/apt-pkg/makefile
index dc943aad4..a90131f80 100644
--- a/apt-pkg/makefile
+++ b/apt-pkg/makefile
@@ -44,7 +44,7 @@ SOURCE+= pkgcache.cc version.cc depcache.cc \
pkgsystem.cc indexfile.cc pkgcachegen.cc acquire-item.cc \
indexrecords.cc vendor.cc vendorlist.cc cdrom.cc indexcopy.cc \
aptconfiguration.cc cachefilter.cc cacheset.cc edsp.cc \
- upgrade.cc update.cc
+ install-progress.cc upgrade.cc update.cc
HEADERS+= algorithms.h depcache.h pkgcachegen.h cacheiterators.h \
orderlist.h sourcelist.h packagemanager.h tagfile.h \
init.h pkgcache.h version.h progress.h pkgrecords.h \
@@ -52,7 +52,8 @@ HEADERS+= algorithms.h depcache.h pkgcachegen.h cacheiterators.h \
clean.h srcrecords.h cachefile.h versionmatch.h policy.h \
pkgsystem.h indexfile.h metaindex.h indexrecords.h vendor.h \
vendorlist.h cdrom.h indexcopy.h aptconfiguration.h \
- cachefilter.h cacheset.h edsp.h upgrade.h update.h
+ cachefilter.h cacheset.h edsp.h install-progress.h \
+ upgrade.h update.h
# Source code for the debian specific components
# In theory the deb headers do not need to be exported..
diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc
index 8c0d2e855..a2079a2e4 100644
--- a/apt-pkg/packagemanager.cc
+++ b/apt-pkg/packagemanager.cc
@@ -26,7 +26,6 @@
#include <apt-pkg/sptr.h>
#include <iostream>
-#include <fcntl.h>
#include <apti18n.h>
/*}}}*/
@@ -1028,32 +1027,54 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall()
return Completed;
}
+// PM::DoInstallPostFork - compat /*{{{*/
+// ---------------------------------------------------------------------
+ /*}}}*/
+pkgPackageManager::OrderResult
+pkgPackageManager::DoInstallPostFork(int statusFd)
+{
+ APT::Progress::PackageManager *progress = new
+ APT::Progress::PackageManagerProgressFd(statusFd);
+ pkgPackageManager::OrderResult res = DoInstallPostFork(progress);
+ delete progress;
+ return res;
+ }
/*}}}*/
// PM::DoInstallPostFork - Does install part that happens after the fork /*{{{*/
// ---------------------------------------------------------------------
pkgPackageManager::OrderResult
-pkgPackageManager::DoInstallPostFork(int statusFd)
+pkgPackageManager::DoInstallPostFork(APT::Progress::PackageManager *progress)
{
- if(statusFd > 0)
- // FIXME: use SetCloseExec here once it taught about throwing
- // exceptions instead of doing _exit(100) on failure
- fcntl(statusFd,F_SETFD,FD_CLOEXEC);
- bool goResult = Go(statusFd);
- if(goResult == false)
- return Failed;
-
- return Res;
+ bool goResult = Go(progress);
+ if(goResult == false)
+ return Failed;
+
+ return Res;
};
-
+ /*}}}*/
+// PM::DoInstall - Does the installation /*{{{*/
+// ---------------------------------------------------------------------
+/* compat */
+pkgPackageManager::OrderResult
+pkgPackageManager::DoInstall(int statusFd)
+{
+ APT::Progress::PackageManager *progress = new
+ APT::Progress::PackageManagerProgressFd(statusFd);
+ OrderResult res = DoInstall(progress);
+ delete progress;
+ return res;
+ }
+ /*}}}*/
// PM::DoInstall - Does the installation /*{{{*/
// ---------------------------------------------------------------------
/* This uses the filenames in FileNames and the information in the
DepCache to perform the installation of packages.*/
-pkgPackageManager::OrderResult pkgPackageManager::DoInstall(int statusFd)
+pkgPackageManager::OrderResult
+pkgPackageManager::DoInstall(APT::Progress::PackageManager *progress)
{
if(DoInstallPreFork() == Failed)
return Failed;
- return DoInstallPostFork(statusFd);
+ return DoInstallPostFork(progress);
}
/*}}}*/
diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h
index 1a6a9f01c..8ffac2f13 100644
--- a/apt-pkg/packagemanager.h
+++ b/apt-pkg/packagemanager.h
@@ -23,7 +23,9 @@
#ifndef PKGLIB_PACKAGEMANAGER_H
#define PKGLIB_PACKAGEMANAGER_H
+#include <apt-pkg/macros.h>
#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/install-progress.h>
#include <string>
#include <iostream>
@@ -39,6 +41,8 @@ class pkgDepCache;
class pkgSourceList;
class pkgOrderList;
class pkgRecords;
+
+
class pkgPackageManager : protected pkgCache::Namespace
{
public:
@@ -83,7 +87,7 @@ class pkgPackageManager : protected pkgCache::Namespace
virtual bool Install(PkgIterator /*Pkg*/,std::string /*File*/) {return false;};
virtual bool Configure(PkgIterator /*Pkg*/) {return false;};
virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;};
- virtual bool Go(int statusFd=-1) {return true;};
+ virtual bool Go(APT::Progress::PackageManager *progress) {return true;};
virtual void Reset() {};
// the result of the operation
@@ -96,7 +100,9 @@ class pkgPackageManager : protected pkgCache::Namespace
pkgRecords *Recs);
// Do the installation
- OrderResult DoInstall(int statusFd=-1);
+ OrderResult DoInstall(APT::Progress::PackageManager *progress);
+ // compat
+ __deprecated OrderResult DoInstall(int statusFd=-1);
// stuff that needs to be done before the fork() of a library that
// uses apt
@@ -106,7 +112,11 @@ class pkgPackageManager : protected pkgCache::Namespace
};
// stuff that needs to be done after the fork
- OrderResult DoInstallPostFork(int statusFd=-1);
+ OrderResult DoInstallPostFork(APT::Progress::PackageManager *progress);
+ // compat
+ __deprecated OrderResult DoInstallPostFork(int statusFd=-1);
+
+ // ?
bool FixMissing();
/** \brief returns all packages dpkg let disappear */
diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
index 643a6b370..d9d46dba0 100644
--- a/apt-private/private-install.cc
+++ b/apt-private/private-install.cc
@@ -23,6 +23,7 @@
#include <apt-pkg/pkgsystem.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/indexfile.h>
+#include <apt-pkg/install-progress.h>
#include <set>
#include <locale.h>
@@ -103,8 +104,14 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
if (_config->FindB("APT::Get::Simulate") == true)
{
pkgSimulate PM(Cache);
+
int status_fd = _config->FindI("APT::Status-Fd",-1);
- pkgPackageManager::OrderResult Res = PM.DoInstall(status_fd);
+ APT::Progress::PackageManager *progress = NULL;
+ if (status_fd > 0)
+ progress = new APT::Progress::PackageManagerProgressFd(status_fd);
+ pkgPackageManager::OrderResult Res = PM.DoInstall(progress);
+ delete progress;
+
if (Res == pkgPackageManager::Failed)
return false;
if (Res != pkgPackageManager::Completed)
@@ -332,8 +339,28 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
}
_system->UnLock();
- int status_fd = _config->FindI("APT::Status-Fd",-1);
- pkgPackageManager::OrderResult Res = PM->DoInstall(status_fd);
+
+ // FIXME: make this a factory
+ // select the right progress
+ int status_fd = _config->FindI("APT::Status-Fd", -1);
+ int status_deb822_fd = _config->FindI("APT::Status-deb822-Fd", -1);
+
+ APT::Progress::PackageManager *progress = NULL;
+ if (status_deb822_fd > 0)
+ progress = new APT::Progress::PackageManagerProgressDeb822Fd(
+ status_deb822_fd);
+ else if (status_fd > 0)
+ progress = new APT::Progress::PackageManagerProgressFd(status_fd);
+ else if(_config->FindB("Dpkg::Progress-Fancy", false) == true)
+ progress = new APT::Progress::PackageManagerFancy();
+ else if (_config->FindB("Dpkg::Progress",
+ _config->FindB("DpkgPM::Progress", false)) == true)
+ progress = new APT::Progress::PackageManagerText();
+ else
+ progress = new APT::Progress::PackageManager();
+ pkgPackageManager::OrderResult Res = PM->DoInstall(progress);
+ delete progress;
+
if (Res == pkgPackageManager::Failed || _error->PendingError() == true)
return false;
if (Res == pkgPackageManager::Completed)
diff --git a/test/integration/test-apt-progress-fd b/test/integration/test-apt-progress-fd
index 5f73c8f8c..9d250e949 100755
--- a/test/integration/test-apt-progress-fd
+++ b/test/integration/test-apt-progress-fd
@@ -18,14 +18,14 @@ testsuccess aptget install testing=0.1 -y -o APT::Status-Fd=3
testequal "dlstatus:1:0:Retrieving file 1 of 1
dlstatus:1:0:Retrieving file 1 of 1
pmstatus:dpkg-exec:0:Running dpkg
-pmstatus:testing:0:Installing testing
-pmstatus:testing:20:Preparing testing
-pmstatus:testing:40:Unpacking testing
-pmstatus:testing:60:Preparing to configure testing
+pmstatus:testing:0:Installing testing (amd64)
+pmstatus:testing:20:Preparing testing (amd64)
+pmstatus:testing:40:Unpacking testing (amd64)
+pmstatus:testing:60:Preparing to configure testing (amd64)
pmstatus:dpkg-exec:60:Running dpkg
-pmstatus:testing:60:Configuring testing
-pmstatus:testing:80:Configuring testing
-pmstatus:testing:100:Installed testing" cat apt-progress.log
+pmstatus:testing:60:Configuring testing (amd64)
+pmstatus:testing:80:Configuring testing (amd64)
+pmstatus:testing:100:Installed testing (amd64)" cat apt-progress.log
# upgrade
exec 3> apt-progress.log
@@ -33,23 +33,22 @@ testsuccess aptget install testing=0.8.15 -y -o APT::Status-Fd=3
testequal "dlstatus:1:0:Retrieving file 1 of 1
dlstatus:1:0:Retrieving file 1 of 1
pmstatus:dpkg-exec:0:Running dpkg
-pmstatus:testing:20:Preparing testing
-pmstatus:testing:40:Unpacking testing
-pmstatus:testing:60:Preparing to configure testing
+pmstatus:testing:20:Preparing testing (amd64)
+pmstatus:testing:40:Unpacking testing (amd64)
+pmstatus:testing:60:Preparing to configure testing (amd64)
pmstatus:dpkg-exec:60:Running dpkg
-pmstatus:testing:60:Configuring testing
-pmstatus:testing:80:Configuring testing
-pmstatus:testing:100:Installed testing" cat apt-progress.log
+pmstatus:testing:60:Configuring testing (amd64)
+pmstatus:testing:80:Configuring testing (amd64)
+pmstatus:testing:100:Installed testing (amd64)" cat apt-progress.log
# and remove
exec 3> apt-progress.log
testsuccess aptget remove testing -y -o APT::Status-Fd=3
testequal "pmstatus:dpkg-exec:0:Running dpkg
-pmstatus:testing:0:Removing testing
-pmstatus:testing:33.3333:Preparing for removal of testing
-pmstatus:testing:66.6667:Removing testing
-pmstatus:testing:100:Removed testing" cat apt-progress.log
-
+pmstatus:testing:0:Removing testing (amd64)
+pmstatus:testing:33.3333:Preparing for removal of testing (amd64)
+pmstatus:testing:66.6667:Removing testing (amd64)
+pmstatus:testing:100:Removed testing (amd64)" cat apt-progress.log
# install non-native and ensure we get proper progress info
exec 3> apt-progress.log
@@ -59,13 +58,13 @@ testsuccess aptget install testing2:i386 -y -o APT::Status-Fd=3
testequal "dlstatus:1:0:Retrieving file 1 of 1
dlstatus:1:0:Retrieving file 1 of 1
pmstatus:dpkg-exec:0:Running dpkg
-pmstatus:testing2:0:Installing testing2
-pmstatus:testing2:20:Preparing testing2
-pmstatus:testing2:40:Unpacking testing2
-pmstatus:testing2:60:Preparing to configure testing2
+pmstatus:testing2:0:Installing testing2 (i386)
+pmstatus:testing2:20:Preparing testing2 (i386)
+pmstatus:testing2:40:Unpacking testing2 (i386)
+pmstatus:testing2:60:Preparing to configure testing2 (i386)
pmstatus:dpkg-exec:60:Running dpkg
-pmstatus:testing2:60:Configuring testing2
-pmstatus:testing2:80:Configuring testing2
-pmstatus:testing2:100:Installed testing2" cat apt-progress.log
+pmstatus:testing2:60:Configuring testing2 (i386)
+pmstatus:testing2:80:Configuring testing2 (i386)
+pmstatus:testing2:100:Installed testing2 (i386)" cat apt-progress.log
-rm -f apt-progress*.log \ No newline at end of file
+rm -f apt-progress*.log
diff --git a/test/integration/test-apt-progress-fd-deb822 b/test/integration/test-apt-progress-fd-deb822
new file mode 100755
index 000000000..9d227942d
--- /dev/null
+++ b/test/integration/test-apt-progress-fd-deb822
@@ -0,0 +1,64 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture 'amd64' 'i386'
+
+buildsimplenativepackage 'testing' 'amd64' '0.1' 'stable'
+buildsimplenativepackage 'testing' 'all' '0.8.15' 'stable'
+buildsimplenativepackage 'testing2' 'amd64,i386' '0.8.15' 'stable'
+setupaptarchive
+
+# install native
+exec 3> apt-progress.log
+testsuccess aptget install testing=0.1 -y -o APT::Status-deb822-Fd=3
+
+testequal "Status: progress
+Percent: 0
+Message: Running dpkg
+
+Status: progress
+Package: testing:amd64
+Percent: 0
+Message: Installing testing (amd64)
+
+Status: progress
+Package: testing:amd64
+Percent: 20
+Message: Preparing testing (amd64)
+
+Status: progress
+Package: testing:amd64
+Percent: 40
+Message: Unpacking testing (amd64)
+
+Status: progress
+Package: testing:amd64
+Percent: 60
+Message: Preparing to configure testing (amd64)
+
+Status: progress
+Percent: 60
+Message: Running dpkg
+
+Status: progress
+Package: testing:amd64
+Percent: 60
+Message: Configuring testing (amd64)
+
+Status: progress
+Package: testing:amd64
+Percent: 80
+Message: Configuring testing (amd64)
+
+Status: progress
+Package: testing:amd64
+Percent: 100
+Message: Installed testing (amd64)
+" cat apt-progress.log
+
+
+rm -f apt-progress*.log