|
|
|
// -*- mode: cpp; mode: fold -*-
|
|
|
|
// Description /*{{{*/
|
|
|
|
// $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $
|
|
|
|
/* ######################################################################
|
|
|
|
|
|
|
|
DPKG Package Manager - Provide an interface to dpkg
|
|
|
|
|
|
|
|
##################################################################### */
|
|
|
|
/*}}}*/
|
|
|
|
// Includes /*{{{*/
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <apt-pkg/cachefile.h>
|
|
|
|
#include <apt-pkg/configuration.h>
|
|
|
|
#include <apt-pkg/depcache.h>
|
|
|
|
#include <apt-pkg/dpkgpm.h>
|
|
|
|
#include <apt-pkg/error.h>
|
|
|
|
#include <apt-pkg/fileutl.h>
|
|
|
|
#include <apt-pkg/install-progress.h>
|
|
|
|
#include <apt-pkg/packagemanager.h>
|
|
|
|
#include <apt-pkg/pkgrecords.h>
|
|
|
|
#include <apt-pkg/strutl.h>
|
|
|
|
#include <apt-pkg/cacheiterators.h>
|
|
|
|
#include <apt-pkg/macros.h>
|
|
|
|
#include <apt-pkg/pkgcache.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <pty.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
|
|
|
#include <set>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <apti18n.h>
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
class pkgDPkgPMPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
|
|
|
|
term_out(NULL), history_out(NULL),
|
|
|
|
progress(NULL), master(-1), slave(-1)
|
|
|
|
{
|
|
|
|
dpkgbuf[0] = '\0';
|
|
|
|
}
|
|
|
|
~pkgDPkgPMPrivate()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
bool stdin_is_dev_null;
|
|
|
|
// the buffer we use for the dpkg status-fd reading
|
|
|
|
char dpkgbuf[1024];
|
|
|
|
int dpkgbuf_pos;
|
|
|
|
FILE *term_out;
|
|
|
|
FILE *history_out;
|
|
|
|
string dpkg_error;
|
|
|
|
APT::Progress::PackageManager *progress;
|
|
|
|
|
|
|
|
// pty stuff
|
|
|
|
struct termios tt;
|
|
|
|
int master;
|
|
|
|
int slave;
|
|
|
|
|
|
|
|
// signals
|
|
|
|
sigset_t sigmask;
|
|
|
|
sigset_t original_sigmask;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
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<const char *, const char *> 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("disappear", N_("Noting disappearance of %s")),
|
|
|
|
std::make_pair("trigproc", N_("Running post-installation trigger %s"))
|
|
|
|
};
|
|
|
|
|
|
|
|
const std::pair<const char *, const char *> * const PackageProcessingOpsBegin = PackageProcessingOps;
|
|
|
|
const std::pair<const char *, const char *> * 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<const char *, const char *> &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");
|
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
|
|
|
|
if (chrootDir == "/")
|
|
|
|
return;
|
|
|
|
std::cerr << "Chrooting into " << chrootDir << std::endl;
|
|
|
|
if (chroot(chrootDir.c_str()) != 0)
|
|
|
|
_exit(100);
|
|
|
|
if (chdir("/") != 0)
|
|
|
|
_exit(100);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
|
|
|
|
// FindNowVersion - Helper to find a Version in "now" state /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* This is helpful when a package is no longer installed but has residual
|
|
|
|
* config files
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
|
|
|
|
{
|
|
|
|
pkgCache::VerIterator Ver;
|
|
|
|
for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
|
|
|
|
{
|
|
|
|
pkgCache::VerFileIterator Vf = Ver.FileList();
|
|
|
|
pkgCache::PkgFileIterator F = Vf.File();
|
|
|
|
for (F = Vf.File(); F.end() == false; ++F)
|
|
|
|
{
|
|
|
|
if (F && F.Archive())
|
|
|
|
{
|
|
|
|
if (strcmp(F.Archive(), "now"))
|
|
|
|
return Ver;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ver;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
// DPkgPM::pkgDPkgPM - Constructor /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
|
|
|
|
: pkgPackageManager(Cache), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
|
|
|
|
{
|
|
|
|
d = new pkgDPkgPMPrivate();
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::pkgDPkgPM - Destructor /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
pkgDPkgPM::~pkgDPkgPM()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::Install - Install a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* Add an install operation to the sequence list */
|
|
|
|
bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
|
|
|
|
{
|
|
|
|
if (File.empty() == true || Pkg.end() == true)
|
|
|
|
return _error->Error("Internal Error, No file name for %s",Pkg.FullName().c_str());
|
|
|
|
|
|
|
|
// If the filename string begins with DPkg::Chroot-Directory, return the
|
|
|
|
// substr that is within the chroot so dpkg can access it.
|
|
|
|
string const chrootdir = _config->FindDir("DPkg::Chroot-Directory","/");
|
|
|
|
if (chrootdir != "/" && File.find(chrootdir) == 0)
|
|
|
|
{
|
|
|
|
size_t len = chrootdir.length();
|
|
|
|
if (chrootdir.at(len - 1) == '/')
|
|
|
|
len--;
|
|
|
|
List.push_back(Item(Item::Install,Pkg,File.substr(len)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
List.push_back(Item(Item::Install,Pkg,File));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::Configure - Configure a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* Add a configure operation to the sequence list */
|
|
|
|
bool pkgDPkgPM::Configure(PkgIterator Pkg)
|
|
|
|
{
|
|
|
|
if (Pkg.end() == true)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::Remove - Remove a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* Add a remove operation to the sequence list */
|
|
|
|
bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
|
|
|
|
{
|
|
|
|
if (Pkg.end() == true)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Purge == true)
|
|
|
|
List.push_back(Item(Item::Purge,Pkg));
|
|
|
|
else
|
|
|
|
List.push_back(Item(Item::Remove,Pkg));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* This is part of the helper script communication interface, it sends
|
|
|
|
very complete information down to the other end of the pipe.*/
|
|
|
|
bool pkgDPkgPM::SendV2Pkgs(FILE *F)
|
|
|
|
{
|
|
|
|
return SendPkgsInfo(F, 2);
|
|
|
|
}
|
|
|
|
bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
|
|
|
|
{
|
|
|
|
// This version of APT supports only v3, so don't sent higher versions
|
|
|
|
if (Version <= 3)
|
|
|
|
fprintf(F,"VERSION %u\n", Version);
|
|
|
|
else
|
|
|
|
fprintf(F,"VERSION 3\n");
|
|
|
|
|
|
|
|
/* Write out all of the configuration directives by walking the
|
|
|
|
configuration tree */
|
|
|
|
const Configuration::Item *Top = _config->Tree(0);
|
|
|
|
for (; Top != 0;)
|
|
|
|
{
|
|
|
|
if (Top->Value.empty() == false)
|
|
|
|
{
|
|
|
|
fprintf(F,"%s=%s\n",
|
|
|
|
QuoteString(Top->FullTag(),"=\"\n").c_str(),
|
|
|
|
QuoteString(Top->Value,"\n").c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Top->Child != 0)
|
|
|
|
{
|
|
|
|
Top = Top->Child;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (Top != 0 && Top->Next == 0)
|
|
|
|
Top = Top->Parent;
|
|
|
|
if (Top != 0)
|
|
|
|
Top = Top->Next;
|
|
|
|
}
|
|
|
|
fprintf(F,"\n");
|
|
|
|
|
|
|
|
// Write out the package actions in order.
|
|
|
|
for (vector<Item>::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());
|
|
|
|
|
|
|
|
// Current version which we are going to replace
|
|
|
|
pkgCache::VerIterator CurVer = I->Pkg.CurrentVer();
|
|
|
|
if (CurVer.end() == true && (I->Op == Item::Remove || I->Op == Item::Purge))
|
|
|
|
CurVer = FindNowVersion(I->Pkg);
|
|
|
|
|
|
|
|
if (CurVer.end() == true)
|
|
|
|
{
|
|
|
|
if (Version <= 2)
|
|
|
|
fprintf(F, "- ");
|
|
|
|
else
|
|
|
|
fprintf(F, "- - none ");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(F, "%s ", CurVer.VerStr());
|
|
|
|
if (Version >= 3)
|
|
|
|
fprintf(F, "%s %s ", CurVer.Arch(), CurVer.MultiArchType());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show the compare operator between current and install version
|
|
|
|
if (S.InstallVer != 0)
|
|
|
|
{
|
|
|
|
pkgCache::VerIterator const InstVer = S.InstVerIter(Cache);
|
|
|
|
int Comp = 2;
|
|
|
|
if (CurVer.end() == false)
|
|
|
|
Comp = InstVer.CompareVer(CurVer);
|
|
|
|
if (Comp < 0)
|
|
|
|
fprintf(F,"> ");
|
|
|
|
else if (Comp == 0)
|
|
|
|
fprintf(F,"= ");
|
|
|
|
else if (Comp > 0)
|
|
|
|
fprintf(F,"< ");
|
|
|
|
fprintf(F, "%s ", InstVer.VerStr());
|
|
|
|
if (Version >= 3)
|
|
|
|
fprintf(F, "%s %s ", InstVer.Arch(), InstVer.MultiArchType());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Version <= 2)
|
|
|
|
fprintf(F, "> - ");
|
|
|
|
else
|
|
|
|
fprintf(F, "> - - none ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show the filename/operation
|
|
|
|
if (I->Op == Item::Install)
|
|
|
|
{
|
|
|
|
// No errors here..
|
|
|
|
if (I->File[0] != '/')
|
|
|
|
fprintf(F,"**ERROR**\n");
|
|
|
|
else
|
|
|
|
fprintf(F,"%s\n",I->File.c_str());
|
|
|
|
}
|
|
|
|
else if (I->Op == Item::Configure)
|
|
|
|
fprintf(F,"**CONFIGURE**\n");
|
|
|
|
else if (I->Op == Item::Remove ||
|
|
|
|
I->Op == Item::Purge)
|
|
|
|
fprintf(F,"**REMOVE**\n");
|
|
|
|
|
|
|
|
if (ferror(F) != 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* This looks for a list of scripts to run from the configuration file
|
|
|
|
each one is run and is fed on standard input a list of all .deb files
|
|
|
|
that are due to be installed. */
|
|
|
|
bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
|
|
|
|
{
|
|
|
|
bool result = true;
|
|
|
|
|
|
|
|
Configuration::Item const *Opts = _config->Tree(Cnf);
|
|
|
|
if (Opts == 0 || Opts->Child == 0)
|
|
|
|
return true;
|
|
|
|
Opts = Opts->Child;
|
|
|
|
|
|
|
|
sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
|
unsigned int Count = 1;
|
|
|
|
for (; Opts != 0; Opts = Opts->Next, Count++)
|
|
|
|
{
|
|
|
|
if (Opts->Value.empty() == true)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(_config->FindB("Debug::RunScripts", false) == true)
|
|
|
|
std::clog << "Running external script with list of all .deb file: '"
|
|
|
|
<< Opts->Value << "'" << std::endl;
|
|
|
|
|
|
|
|
// Determine the protocol version
|
|
|
|
string OptSec = Opts->Value;
|
|
|
|
string::size_type Pos;
|
|
|
|
if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0)
|
|
|
|
Pos = OptSec.length();
|
|
|
|
OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
|
|
|
|
|
|
|
|
unsigned int Version = _config->FindI(OptSec+"::Version",1);
|
|
|
|
unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO);
|
|
|
|
|
|
|
|
// Create the pipes
|
|
|
|
std::set<int> KeepFDs;
|
|
|
|
MergeKeepFdsFromConfiguration(KeepFDs);
|
|
|
|
int Pipes[2];
|
|
|
|
if (pipe(Pipes) != 0) {
|
|
|
|
result = _error->Errno("pipe","Failed to create IPC pipe to subprocess");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (InfoFD != (unsigned)Pipes[0])
|
|
|
|
SetCloseExec(Pipes[0],true);
|
|
|
|
else
|
|
|
|
KeepFDs.insert(Pipes[0]);
|
|
|
|
|
|
|
|
|
|
|
|
SetCloseExec(Pipes[1],true);
|
|
|
|
|
|
|
|
// Purified Fork for running the script
|
|
|
|
pid_t Process = ExecFork(KeepFDs);
|
|
|
|
if (Process == 0)
|
|
|
|
{
|
|
|
|
// Setup the FDs
|
|
|
|
dup2(Pipes[0], InfoFD);
|
|
|
|
SetCloseExec(STDOUT_FILENO,false);
|
|
|
|
SetCloseExec(STDIN_FILENO,false);
|
|
|
|
SetCloseExec(STDERR_FILENO,false);
|
|
|
|
|
|
|
|
string hookfd;
|
|
|
|
strprintf(hookfd, "%d", InfoFD);
|
|
|
|
setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
|
|
|
|
|
|
|
|
dpkgChrootDirectory();
|
|
|
|
const char *Args[4];
|
|
|
|
Args[0] = "/bin/sh";
|
|
|
|
Args[1] = "-c";
|
|
|
|
Args[2] = Opts->Value.c_str();
|
|
|
|
Args[3] = 0;
|
|
|
|
execv(Args[0],(char **)Args);
|
|
|
|
_exit(100);
|
|
|
|
}
|
|
|
|
close(Pipes[0]);
|
|
|
|
FILE *F = fdopen(Pipes[1],"w");
|
|
|
|
if (F == 0) {
|
|
|
|
result = _error->Errno("fdopen","Faild to open new FD");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Feed it the filenames.
|
|
|
|
if (Version <= 1)
|
|
|
|
{
|
|
|
|
for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
|
|
|
|
{
|
|
|
|
// Only deal with packages to be installed from .deb
|
|
|
|
if (I->Op != Item::Install)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// No errors here..
|
|
|
|
if (I->File[0] != '/')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Feed the filename of each package that is pending install
|
|
|
|
into the pipe. */
|
|
|
|
fprintf(F,"%s\n",I->File.c_str());
|
|
|
|
if (ferror(F) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SendPkgsInfo(F, Version);
|
|
|
|
|
|
|
|
fclose(F);
|
|
|
|
|
|
|
|
// Clean up the sub process
|
|
|
|
if (ExecWait(Process,Opts->Value.c_str()) == false) {
|
|
|
|
result = _error->Error("Failure running script %s",Opts->Value.c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
signal(SIGPIPE, old_sigpipe);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
void pkgDPkgPM::DoStdin(int master)
|
|
|
|
{
|
|
|
|
unsigned char input_buf[256] = {0,};
|
|
|
|
ssize_t len = read(0, input_buf, sizeof(input_buf));
|
|
|
|
if (len)
|
|
|
|
FileFd::Write(master, input_buf, len);
|
|
|
|
else
|
|
|
|
d->stdin_is_dev_null = true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
* read the terminal pty and write log
|
|
|
|
*/
|
|
|
|
void pkgDPkgPM::DoTerminalPty(int master)
|
|
|
|
{
|
|
|
|
unsigned char term_buf[1024] = {0,0, };
|
|
|
|
|
|
|
|
ssize_t len=read(master, term_buf, sizeof(term_buf));
|
|
|
|
if(len == -1 && errno == EIO)
|
|
|
|
{
|
|
|
|
// this happens when the child is about to exit, we
|
|
|
|
// give it time to actually exit, otherwise we run
|
|
|
|
// into a race so we sleep for half a second.
|
|
|
|
struct timespec sleepfor = { 0, 500000000 };
|
|
|
|
nanosleep(&sleepfor, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(len <= 0)
|
|
|
|
return;
|
|
|
|
FileFd::Write(1, term_buf, len);
|
|
|
|
if(d->term_out)
|
|
|
|
fwrite(term_buf, len, sizeof(char), d->term_out);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// DPkgPM::ProcessDpkgStatusBuf /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
|
|
|
|
{
|
|
|
|
bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false);
|
|
|
|
if (Debug == true)
|
|
|
|
std::clog << "got from dpkg '" << line << "'" << std::endl;
|
|
|
|
|
|
|
|
/* dpkg sends strings like this:
|
|
|
|
'status: <pkg>: <pkg qstate>'
|
|
|
|
'status: <pkg>:<arch>: <pkg qstate>'
|
|
|
|
|
|
|
|
'processing: {install,configure,remove,purge,disappear,trigproc}: pkg'
|
|
|
|
'processing: {install,configure,remove,purge,disappear,trigproc}: trigger'
|
|
|
|
*/
|
|
|
|
|
|
|
|
// we need to split on ": " (note the appended space) as the ':' is
|
|
|
|
// part of the pkgname:arch information that dpkg sends
|
|
|
|
//
|
|
|
|
// A dpkg error message may contain additional ":" (like
|
|
|
|
// "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
|
|
|
|
// so we need to ensure to not split too much
|
|
|
|
std::vector<std::string> list = StringSplit(line, ": ", 4);
|
|
|
|
if(list.size() < 3)
|
|
|
|
{
|
|
|
|
if (Debug == true)
|
|
|
|
std::clog << "ignoring line: not enough ':'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// build the (prefix, pkgname, action) tuple, position of this
|
|
|
|
// is different for "processing" or "status" messages
|
|
|
|
std::string prefix = APT::String::Strip(list[0]);
|
|
|
|
std::string pkgname;
|
|
|
|
std::string action;
|
|
|
|
|
|
|
|
// "processing" has the form "processing: action: pkg or trigger"
|
|
|
|
// with action = ["install", "configure", "remove", "purge", "disappear",
|
|
|
|
// "trigproc"]
|
|
|
|
if (prefix == "processing")
|
|
|
|
{
|
|
|
|
pkgname = APT::String::Strip(list[2]);
|
|
|
|
action = APT::String::Strip(list[1]);
|
|
|
|
}
|
|
|
|
// "status" has the form: "status: pkg: state"
|
|
|
|
// with state in ["half-installed", "unpacked", "half-configured",
|
|
|
|
// "installed", "config-files", "not-installed"]
|
|
|
|
else if (prefix == "status")
|
|
|
|
{
|
|
|
|
pkgname = APT::String::Strip(list[1]);
|
|
|
|
action = APT::String::Strip(list[2]);
|
|
|
|
} else {
|
|
|
|
if (Debug == true)
|
|
|
|
std::clog << "unknown prefix '" << prefix << "'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handle the special cases first:
|
|
|
|
|
|
|
|
errors look like this:
|
|
|
|
'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:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
|
|
|
|
*/
|
|
|
|
if (prefix == "status")
|
|
|
|
{
|
|
|
|
if(action == "error")
|
|
|
|
{
|
|
|
|
d->progress->Error(list[1], PackagesDone, PackagesTotal,
|
|
|
|
list[3]);
|
|
|
|
pkgFailures++;
|
|
|
|
WriteApportReport(list[1].c_str(), list[3].c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(action == "conffile-prompt")
|
|
|
|
{
|
|
|
|
d->progress->ConffilePrompt(list[1], PackagesDone, PackagesTotal,
|
|
|
|
list[3]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// at this point we know that we should have a valid pkgname, so build all
|
|
|
|
// the info from it
|
|
|
|
|
|
|
|
// dpkg does not send always send "pkgname:arch" so we add it here
|
|
|
|
// if needed
|
|
|
|
if (pkgname.find(":") == std::string::npos)
|
|
|
|
{
|
|
|
|
// find the package in the group that is in a touched by dpkg
|
|
|
|
// if there are multiple dpkg will send us a full pkgname:arch
|
|
|
|
pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
|
|
|
|
if (Grp.end() == false)
|
|
|
|
{
|
|
|
|
pkgCache::PkgIterator P = Grp.PackageList();
|
|
|
|
for (; P.end() != true; P = Grp.NextPkg(P))
|
|
|
|
{
|
|
|
|
if(Cache[P].Mode != pkgDepCache::ModeKeep)
|
|
|
|
{
|
|
|
|
pkgname = P.FullName();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* const pkg = pkgname.c_str();
|
|
|
|
std::string short_pkgname = StringSplit(pkgname, ":")[0];
|
|
|
|
std::string arch = "";
|
|
|
|
if (pkgname.find(":") != string::npos)
|
|
|
|
arch = StringSplit(pkgname, ":")[1];
|
|
|
|
std::string i18n_pkgname = pkgname;
|
|
|
|
if (arch.size() != 0)
|
|
|
|
strprintf(i18n_pkgname, "%s (%s)", short_pkgname.c_str(), arch.c_str());
|
|
|
|
|
|
|
|
// 'processing' from dpkg looks like
|
|
|
|
// 'processing: action: pkg'
|
|
|
|
if(prefix == "processing")
|
|
|
|
{
|
|
|
|
const std::pair<const char *, const char *> * const iter =
|
|
|
|
std::find_if(PackageProcessingOpsBegin,
|
|
|
|
PackageProcessingOpsEnd,
|
|
|
|
MatchProcessingOp(action.c_str()));
|
|
|
|
if(iter == PackageProcessingOpsEnd)
|
|
|
|
{
|
|
|
|
if (Debug == true)
|
|
|
|
std::clog << "ignoring unknown action: " << action << std::endl;
|
|