##################################################################### */
/*}}}*/
// Includes /*{{{*/
+#include <config.h>
+
#include <apt-pkg/dpkgpm.h>
#include <apt-pkg/error.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/cachefile.h>
+#include <apt-pkg/packagemanager.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <pty.h>
-#include <config.h>
#include <apti18n.h>
/*}}}*/
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.
fprintf(F,"\n");
// Write out the package actions in order.
- for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
+ for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
{
if(I->Pkg.end() == true)
continue;
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";
// Feed it the filenames.
if (Version <= 1)
{
- for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
+ 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)
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;
- for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
{
enum { CANDIDATE, CANDIDATE_AUTO, CURRENT_CANDIDATE, CURRENT } infostring;
string *line = NULL;
*/
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;
// 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++)
+ for (vector<Item>::const_iterator I = List.begin(); I != List.end(); ++I)
{
if((*I).Pkg.end() == true)
continue;
string const name = (*I).Pkg.Name();
PackageOpsDone[name] = 0;
- for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; i++)
+ for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i)
{
PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]);
PackagesTotal++;
// 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();)
{
// Do all actions with the same Op in one run
vector<Item>::const_iterator J = I;
if (TriggersPending == true)
- for (; J != List.end(); J++)
+ for (; J != List.end(); ++J)
{
if (J->Op == I->Op)
continue;
break;
}
else
- for (; J != List.end() && J->Op == I->Op; J++)
+ 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
//
// the argument list is split in a way that A depends on B
// and they are in the same "--configure A B" run
// - with the split they may now be configured in different
- // runs
+ // 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)
{
- for (;I != J && Size < MaxArgBytes; I++)
+ for (;I != J && Size < MaxArgBytes; ++I)
{
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");
unsigned long const oldSize = I->Op == Item::Configure ? Size : 0;
- for (;I != J && Size < MaxArgBytes; I++)
+ for (;I != J && Size < MaxArgBytes; ++I)
{
if((*I).Pkg.end() == true)
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;
it to all processes in the group. Since dpkg ignores the signal
it doesn't die but we do! So we must also ignore it */
sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN);
- sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN);
-
+ sighandler_t old_SIGINT = signal(SIGINT,SigINT);
+
+ // Check here for any SIGINT
+ if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install))
+ break;
+
+
// ignore SIGHUP as well (debian #463030)
sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN);
// if tcgetattr does not return zero there was a error
// and we do not do any pty magic
- if (tcgetattr(0, &tt) == 0)
+ if (tcgetattr(STDOUT_FILENO, &tt) == 0)
{
ioctl(0, TIOCGWINSZ, (char *)&win);
if (openpty(&master, &slave, NULL, &tt, &win) < 0)
sigprocmask(SIG_SETMASK, &original_sigmask, 0);
}
}
-
// Fork dpkg
pid_t Child;
_config->Set("APT::Keep-Fds::",fd[1]);
<< (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;
// Restore sig int/quit
signal(SIGQUIT,old_SIGQUIT);
signal(SIGINT,old_SIGINT);
+
signal(SIGHUP,old_SIGHUP);
return _error->Errno("waitpid","Couldn't wait for subprocess");
}
// Restore sig int/quit
signal(SIGQUIT,old_SIGQUIT);
signal(SIGINT,old_SIGINT);
+
signal(SIGHUP,old_SIGHUP);
if(master >= 0)
}
}
CloseLog();
+
+ if (pkgPackageManager::SigINTStop)
+ _error->Warning(_("Operation was interrupted before it could finish"));
if (RunScripts("DPkg::Post-Invoke") == false)
return false;
Cache.writeStateFile(NULL);
return true;
}
+
+void SigINT(int sig) {
+ 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);
// log the ordering
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]);
+ for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
+ 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"))