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);
+}
+ /*}}}*/
+
+
+// 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);
+
+ else 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;
}
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);
+ 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();
+ bool dpkgMultiArch = false;
+ if (dpkgAssertMultiArch > 0)
+ {
+ int Status = 0;
+ while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
+ {
+ if (errno == EINTR)
+ continue;
+ _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;
+ }
+
// this loop is runs once per operation
for (vector<Item>::const_iterator I = List.begin(); I != List.end();)
{
for (; J != List.end() && J->Op == I->Op; ++J)
/* nothing */;
- // Generate the argument list
- const char *Args[MaxArgs + 50];
// keep track of allocated strings for multiarch package names
- char *Packages[MaxArgs + 50];
- unsigned int pkgcount = 0;
+ std::vector<char *> Packages;
+
+ // start with the baseset of arguments
+ unsigned long Size = StartSize;
+ Args.erase(Args.begin() + BaseArgs, Args.end());
// Now check if we are within the MaxArgs limit
//
// - with the split they may now be configured in different
// runs, using Immediate-Configure-All can help prevent this.
if (J - I > (signed)MaxArgs)
+ {
J = I + MaxArgs;
-
- unsigned int n = 0;
- unsigned long Size = 0;
- string const Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
- Args[n++] = Tmp.c_str();
- Size += strlen(Args[n-1]);
-
- // Stick in any custom dpkg options
- Configuration::Item const *Opts = _config->Tree("DPkg::Options");
- if (Opts != 0)
+ unsigned long const size = MaxArgs + 10;
+ Args.reserve(size);
+ Packages.reserve(size);
+ }
+ else
{
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
- {
- if (Opts->Value.empty() == true)
- continue;
- Args[n++] = Opts->Value.c_str();
- Size += Opts->Value.length();
- }
+ unsigned long const size = (J - I) + 10;
+ Args.reserve(size);
+ Packages.reserve(size);
}
-
- char status_fd_buf[20];
+
int fd[2];
- pipe(fd);
-
- Args[n++] = "--status-fd";
- Size += strlen(Args[n-1]);
+ 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
+
+ ADDARGC("--status-fd");
+ char status_fd_buf[20];
snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]);
- Args[n++] = status_fd_buf;
- Size += strlen(Args[n-1]);
-
+ ADDARG(status_fd_buf);
unsigned long const Op = I->Op;
switch (I->Op)
{
case Item::Remove:
- Args[n++] = "--force-depends";
- Size += strlen(Args[n-1]);
- Args[n++] = "--force-remove-essential";
- Size += strlen(Args[n-1]);
- Args[n++] = "--remove";
- Size += strlen(Args[n-1]);
+ ADDARGC("--force-depends");
+ ADDARGC("--force-remove-essential");
+ ADDARGC("--remove");
break;
case Item::Purge:
- Args[n++] = "--force-depends";
- Size += strlen(Args[n-1]);
- Args[n++] = "--force-remove-essential";
- Size += strlen(Args[n-1]);
- Args[n++] = "--purge";
- Size += strlen(Args[n-1]);
+ ADDARGC("--force-depends");
+ ADDARGC("--force-remove-essential");
+ ADDARGC("--purge");
break;
case Item::Configure:
- Args[n++] = "--configure";
- Size += strlen(Args[n-1]);
+ ADDARGC("--configure");
break;
case Item::ConfigurePending:
- Args[n++] = "--configure";
- Size += strlen(Args[n-1]);
- Args[n++] = "--pending";
- Size += strlen(Args[n-1]);
+ ADDARGC("--configure");
+ ADDARGC("--pending");
break;
case Item::TriggersPending:
- Args[n++] = "--triggers-only";
- Size += strlen(Args[n-1]);
- Args[n++] = "--pending";
- Size += strlen(Args[n-1]);
+ ADDARGC("--triggers-only");
+ ADDARGC("--pending");
break;
case Item::Install:
- Args[n++] = "--unpack";
- Size += strlen(Args[n-1]);
- Args[n++] = "--auto-deconfigure";
- Size += strlen(Args[n-1]);
+ ADDARGC("--unpack");
+ ADDARGC("--auto-deconfigure");
break;
}
if (NoTriggers == true && I->Op != Item::TriggersPending &&
I->Op != Item::ConfigurePending)
{
- Args[n++] = "--no-triggers";
- Size += strlen(Args[n-1]);
+ ADDARGC("--no-triggers");
}
+#undef ADDARGC
// Write in the file or package names
if (I->Op == Item::Install)
{
if (I->File[0] != '/')
return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
- Args[n++] = I->File.c_str();
- Size += strlen(Args[n-1]);
+ Args.push_back(I->File.c_str());
+ Size += I->File.length();
}
- }
+ }
else
{
string const nativeArch = _config->Find("APT::Architecture");
continue;
if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.Name()) != disappearedPkgs.end())
continue;
- if (I->Pkg.Arch() == nativeArch || !strcmp(I->Pkg.Arch(), "all"))
- Args[n++] = I->Pkg.Name();
+ // 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
{
- Packages[pkgcount] = strdup(I->Pkg.FullName(false).c_str());
- Args[n++] = Packages[pkgcount++];
+ 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);
}
- Size += strlen(Args[n-1]);
}
// skip configure action if all sheduled packages disappeared
if (oldSize == Size)
continue;
}
- Args[n] = 0;
+#undef ADDARG
+
J = I;
if (_config->FindB("Debug::pkgDPkgPM",false) == true)
{
- for (unsigned int k = 0; k != n; k++)
- clog << Args[k] << ' ';
+ for (std::vector<const char *>::const_iterator a = Args.begin();
+ a != Args.end(); ++a)
+ clog << *a << ' ';
clog << endl;
continue;
}
-
+ Args.push_back(NULL);
+
cout << flush;
clog << flush;
cerr << flush;
<< (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);
/* No Job Control Stop Env is a magic dpkg var that prevents it
from using sigstop */
putenv((char *)"DPKG_NO_TSTP=yes");
- execvp(Args[0],(char **)Args);
+ execvp(Args[0], (char**) &Args[0]);
cerr << "Could not exec dpkg!" << endl;
_exit(100);
}
sigemptyset(&sigmask);
sigprocmask(SIG_BLOCK,&sigmask,&original_sigmask);
- /* clean up the temporary allocation for multiarch package names in
- the parent, so we don't leak memory when we return. */
- for (unsigned int i = 0; i < pkgcount; i++)
- free(Packages[i]);
+ /* free vectors (and therefore memory) as we don't need the included data anymore */
+ for (std::vector<char *>::const_iterator p = Packages.begin();
+ p != Packages.end(); ++p)
+ free(*p);
+ Packages.clear();
// the result of the waitpid call
int res;
}
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);