class pkgDPkgPMPrivate
{
public:
- pkgDPkgPMPrivate() : dpkgbuf_pos(0), term_out(NULL), history_out(NULL)
+ pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
+ term_out(NULL), history_out(NULL)
{
+ dpkgbuf[0] = '\0';
}
bool stdin_is_dev_null;
// the buffer we use for the dpkg status-fd reading
{
if (!FileExists("/usr/bin/ionice"))
return false;
- pid_t Process = ExecFork();
+ pid_t Process = ExecFork();
if (Process == 0)
{
char buf[32];
return ExecWait(Process, "ionice");
}
+// 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 /*{{{*/
// ---------------------------------------------------------------------
/* */
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.Name());
+ 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.
return true;
}
/*}}}*/
-// DPkgPM::SendV2Pkgs - Send version 2 package info /*{{{*/
+// 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)
{
- fprintf(F,"VERSION 2\n");
-
- /* Write out all of the configuration directives by walking the
+ 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;)
pkgDepCache::StateCache &S = Cache[I->Pkg];
fprintf(F,"%s ",I->Pkg.Name());
- // Current version
- if (I->Pkg->CurrentVer == 0)
- fprintf(F,"- ");
+
+ // 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 ",I->Pkg.CurrentVer().VerStr());
-
- // Show the compare operator
- // Target version
+ {
+ 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 (I->Pkg->CurrentVer != 0)
- Comp = S.InstVerIter(Cache).CompareVer(I->Pkg.CurrentVer());
+ if (CurVer.end() == false)
+ Comp = InstVer.CompareVer(CurVer);
if (Comp < 0)
fprintf(F,"> ");
- if (Comp == 0)
+ else if (Comp == 0)
fprintf(F,"= ");
- if (Comp > 0)
+ else if (Comp > 0)
fprintf(F,"< ");
- fprintf(F,"%s ",S.InstVerIter(Cache).VerStr());
+ fprintf(F, "%s ", InstVer.VerStr());
+ if (Version >= 3)
+ fprintf(F, "%s %s ", InstVer.Arch(), InstVer.MultiArchType());
}
else
- fprintf(F,"> - ");
-
+ {
+ if (Version <= 2)
+ fprintf(F, "> - ");
+ else
+ fprintf(F, "> - - none ");
+ }
+
// Show the filename/operation
if (I->Op == Item::Install)
{
else
fprintf(F,"%s\n",I->File.c_str());
}
- if (I->Op == Item::Configure)
+ else if (I->Op == Item::Configure)
fprintf(F,"**CONFIGURE**\n");
- if (I->Op == Item::Remove ||
+ else if (I->Op == Item::Remove ||
I->Op == Item::Purge)
fprintf(F,"**REMOVE**\n");
SetCloseExec(STDIN_FILENO,false);
SetCloseExec(STDERR_FILENO,false);
- 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);
- }
-
+ dpkgChrootDirectory();
const char *Args[4];
Args[0] = "/bin/sh";
Args[1] = "-c";
}
}
else
- SendV2Pkgs(F);
+ SendPkgsInfo(F, Version);
fclose(F);
unsigned char input_buf[256] = {0,};
ssize_t len = read(0, input_buf, sizeof(input_buf));
if (len)
- write(master, input_buf, len);
+ FileFd::Write(master, input_buf, len);
else
d->stdin_is_dev_null = true;
}
}
if(len <= 0)
return;
- write(1, term_buf, len);
+ FileFd::Write(1, term_buf, len);
if(d->term_out)
fwrite(term_buf, len, sizeof(char), d->term_out);
}
<< ":" << s
<< endl;
if(OutStatusFd > 0)
- write(OutStatusFd, status.str().c_str(), status.str().size());
+ FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
if (Debug == true)
std::clog << "send: '" << status.str() << "'" << endl;
<< ":" << list[3]
<< endl;
if(OutStatusFd > 0)
- write(OutStatusFd, status.str().c_str(), status.str().size());
+ FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
if (Debug == true)
std::clog << "send: '" << status.str() << "'" << endl;
pkgFailures++;
<< ":" << list[3]
<< endl;
if(OutStatusFd > 0)
- write(OutStatusFd, status.str().c_str(), status.str().size());
+ FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
if (Debug == true)
std::clog << "send: '" << status.str() << "'" << endl;
return;
<< ":" << s
<< endl;
if(OutStatusFd > 0)
- write(OutStatusFd, status.str().c_str(), status.str().size());
+ FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
if (Debug == true)
std::clog << "send: '" << status.str() << "'" << endl;
}
pw = getpwnam("root");
gr = getgrnam("adm");
if (pw != NULL && gr != NULL)
- chown(logfile_name.c_str(), pw->pw_uid, gr->gr_gid);
- chmod(logfile_name.c_str(), 0644);
+ if(chown(logfile_name.c_str(), pw->pw_uid, gr->gr_gid) != 0)
+ _error->Errno("OpenLog", "chown failed");
+ chmod(logfile_name.c_str(), 0640);
fprintf(d->term_out, "\nLog started: %s\n", timestr);
}
d->history_out = fopen(history_name.c_str(),"a");
if (d->history_out == NULL)
return _error->WarningE("OpenLog", _("Could not open file '%s'"), history_name.c_str());
+ SetCloseExec(fileno(d->history_out), true);
chmod(history_name.c_str(), 0644);
fprintf(d->history_out, "\nStart-Date: %s\n", timestr);
string remove, purge, install, reinstall, upgrade, downgrade;
*/
bool pkgDPkgPM::Go(int OutStatusFd)
{
+ pkgPackageManager::SigINTStop = false;
+
+ // 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();
+
+ // Stick in any custom dpkg options
+ Configuration::Item const *Opts = _config->Tree("DPkg::Options");
+ if (Opts != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+ Args.push_back(Opts->Value.c_str());
+ StartSize += Opts->Value.length();
+ }
+ }
+
+ size_t const BaseArgs = Args.size();
+ // we need to detect if we can qualify packages with the architecture or not
+ Args.push_back("--assert-multi-arch");
+ Args.push_back(NULL);
+
+ pid_t dpkgAssertMultiArch = ExecFork();
+ if (dpkgAssertMultiArch == 0)
+ {
+ dpkgChrootDirectory();
+ // redirect everything to the ultimate sink as we only need the exit-status
+ int const nullfd = open("/dev/null", O_RDONLY);
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ execvp(Args[0], (char**) &Args[0]);
+ _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
+ _exit(2);
+ }
+
fd_set rfds;
struct timespec tv;
sigset_t sigmask;
// create log
OpenLog();
- // Generate the base argument list for dpkg
- std::vector<const char *> Args;
- unsigned long StartSize = 0;
- string const Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
- Args.push_back(Tmp.c_str());
- StartSize += Tmp.length();
-
- // Stick in any custom dpkg options
- Configuration::Item const *Opts = _config->Tree("DPkg::Options");
- if (Opts != 0)
+ bool dpkgMultiArch = false;
+ if (dpkgAssertMultiArch > 0)
{
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
+ int Status = 0;
+ while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
{
- if (Opts->Value.empty() == true)
+ if (errno == EINTR)
continue;
- Args.push_back(Opts->Value.c_str());
- StartSize += Opts->Value.length();
+ _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
+ break;
}
+ if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
+ dpkgMultiArch = true;
}
- size_t const BaseArgs = Args.size();
// this loop is runs once per operation
for (vector<Item>::const_iterator I = List.begin(); I != List.end();)
if (J - I > (signed)MaxArgs)
{
J = I + MaxArgs;
- Args.reserve(MaxArgs + 10);
+ unsigned long const size = MaxArgs + 10;
+ Args.reserve(size);
+ Packages.reserve(size);
}
else
{
- Args.reserve((J - I) + 10);
+ unsigned long const size = (J - I) + 10;
+ Args.reserve(size);
+ Packages.reserve(size);
}
-
int fd[2];
- pipe(fd);
+ if (pipe(fd) != 0)
+ return _error->Errno("pipe","Failed to create IPC pipe to dpkg");
#define ADDARG(X) Args.push_back(X); Size += strlen(X)
#define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
char status_fd_buf[20];
snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]);
ADDARG(status_fd_buf);
-
unsigned long const Op = I->Op;
switch (I->Op)
continue;
if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.Name()) != disappearedPkgs.end())
continue;
- if (I->Pkg.Arch() == nativeArch || !strcmp(I->Pkg.Arch(), "all"))
+ // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
+ if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch ||
+ strcmp(I->Pkg.Arch(), "all") == 0 ||
+ strcmp(I->Pkg.Arch(), "none") == 0))
{
char const * const name = I->Pkg.Name();
ADDARG(name);
}
else
{
- char * const fullname = strdup(I->Pkg.FullName(false).c_str());
+ pkgCache::VerIterator PkgVer;
+ std::string name = I->Pkg.Name();
+ if (Op == Item::Remove || Op == Item::Purge)
+ {
+ PkgVer = I->Pkg.CurrentVer();
+ if(PkgVer.end() == true)
+ PkgVer = FindNowVersion(I->Pkg);
+ }
+ else
+ PkgVer = Cache[I->Pkg].InstVerIter(Cache);
+ if (strcmp(I->Pkg.Arch(), "none") == 0)
+ ; // never arch-qualify a package without an arch
+ else if (PkgVer.end() == false)
+ name.append(":").append(PkgVer.Arch());
+ else
+ _error->Warning("Can not find PkgVer for '%s'", name.c_str());
+ char * const fullname = strdup(name.c_str());
Packages.push_back(fullname);
ADDARG(fullname);
}
<< (PackagesDone/float(PackagesTotal)*100.0)
<< ":" << _("Running dpkg")
<< endl;
- write(OutStatusFd, status.str().c_str(), status.str().size());
+ FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size());
}
Child = ExecFork();
}
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);
- }
+ dpkgChrootDirectory();
if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
_exit(100);
}
void SigINT(int sig) {
- if (_config->FindB("APT::Immediate-Configure-All",false))
- pkgPackageManager::SigINTStop = true;
-}
+ pkgPackageManager::SigINTStop = true;
+}
/*}}}*/
// pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
// ---------------------------------------------------------------------
/* */
void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
{
+ // If apport doesn't exist or isn't installed do nothing
+ // This e.g. prevents messages in 'universes' without apport
+ pkgCache::PkgIterator apportPkg = Cache.FindPkg("apport");
+ if (apportPkg.end() == true || apportPkg->CurrentVer == 0)
+ return;
+
string pkgname, reportfile, srcpkgname, pkgver, arch;
string::size_type pos;
FILE *report;
if(strstr(strbuf,"Package:") == strbuf)
{
char pkgname[255], version[255];
- if(sscanf(strbuf, "Package: %s %s", pkgname, version) == 2)
+ if(sscanf(strbuf, "Package: %254s %254s", pkgname, version) == 2)
if(strcmp(pkgver.c_str(), version) == 0)
{
fclose(report);
if (!logfile_name.empty())
{
FILE *log = NULL;
- char buf[1024];
fprintf(report, "DpkgTerminalLog:\n");
log = fopen(logfile_name.c_str(),"r");
if(log != NULL)
{
+ char buf[1024];
while( fgets(buf, sizeof(buf), log) != NULL)
fprintf(report, " %s", buf);
fclose(log);
const char *ops_str[] = {"Install", "Configure","Remove","Purge"};
fprintf(report, "AptOrdering:\n");
for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
- fprintf(report, " %s: %s\n", (*I).Pkg.Name(), ops_str[(*I).Op]);
+ if ((*I).Pkg != NULL)
+ fprintf(report, " %s: %s\n", (*I).Pkg.Name(), ops_str[(*I).Op]);
+ else
+ fprintf(report, " %s: %s\n", "NULL", 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");
+ FILE *log = popen("/bin/dmesg","r");
if(log != NULL)
{
+ char buf[1024];
while( fgets(buf, sizeof(buf), log) != NULL)
fprintf(report, " %s", buf);
pclose(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");
+ FILE *log = popen("/bin/df -l","r");
if(log != NULL)
{
+ char buf[1024];
while( fgets(buf, sizeof(buf), log) != NULL)
fprintf(report, " %s", buf);
pclose(log);