You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

477 lines
14 KiB

// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* ######################################################################
System - Abstraction for running on different systems.
Basic general structure..
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
#include <config.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/debindexfile.h>
#include <apt-pkg/debsystem.h>
#include <apt-pkg/debversion.h>
#include <apt-pkg/dpkgpm.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/pkgcache.h>
#include <algorithm>
#include <string>
#include <vector>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <apti18n.h>
/*}}}*/
using std::string;
debSystem debSys;
class APT_HIDDEN debSystemPrivate {
public:
debSystemPrivate() : FrontendLockFD(-1), LockFD(-1), LockCount(0), StatusFile(0)
{
}
// For locking support
int FrontendLockFD;
int LockFD;
unsigned LockCount;
debStatusIndex *StatusFile;
};
// System::debSystem - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
debSystem::debSystem() : pkgSystem("Debian dpkg interface", &debVS), d(new debSystemPrivate())
{
}
/*}}}*/
// System::~debSystem - Destructor /*{{{*/
// ---------------------------------------------------------------------
/* */
debSystem::~debSystem()
{
delete d->StatusFile;
delete d;
}
/*}}}*/
// System::Lock - Get the lock /*{{{*/
// ---------------------------------------------------------------------
/* This mirrors the operations dpkg does when it starts up. Note the
checking of the updates directory. */
bool debSystem::Lock()
{
// Disable file locking
if (_config->FindB("Debug::NoLocking",false) == true || d->LockCount > 0)
{
d->LockCount++;
return true;
}
// Create the lockfile
string AdminDir = flNotFile(_config->FindFile("Dir::State::status"));
string FrontendLockFile = AdminDir + "lock-frontend";
d->FrontendLockFD = GetLock(FrontendLockFile);
if (d->FrontendLockFD == -1)
{
if (errno == EACCES || errno == EAGAIN)
return _error->Error(_("Unable to acquire the dpkg frontend lock (%s), "
"is another process using it?"),FrontendLockFile.c_str());
else
return _error->Error(_("Unable to acquire the dpkg frontend lock (%s), "
"are you root?"),FrontendLockFile.c_str());
}
if (LockInner() == false)
{
close(d->FrontendLockFD);
return false;
}
// See if we need to abort with a dirty journal
if (CheckUpdates() == true)
{
close(d->LockFD);
close(d->FrontendLockFD);
d->FrontendLockFD = -1;
d->LockFD = -1;
const char *cmd;
if (getenv("SUDO_USER") != NULL)
cmd = "sudo dpkg --configure -a";
else
cmd = "dpkg --configure -a";
// TRANSLATORS: the %s contains the recovery command, usually
// dpkg --configure -a
return _error->Error(_("dpkg was interrupted, you must manually "
"run '%s' to correct the problem. "), cmd);
}
d->LockCount++;
return true;
}
bool debSystem::LockInner() {
string AdminDir = flNotFile(_config->FindFile("Dir::State::status"));
d->LockFD = GetLock(AdminDir + "lock");
if (d->LockFD == -1)
{
if (errno == EACCES || errno == EAGAIN)
return _error->Error(_("Unable to lock the administration directory (%s), "
"is another process using it?"),AdminDir.c_str());
else
return _error->Error(_("Unable to lock the administration directory (%s), "
"are you root?"),AdminDir.c_str());
}
return true;
}
/*}}}*/
// System::UnLock - Drop a lock /*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::UnLock(bool NoErrors)
{
if (d->LockCount == 0 && NoErrors == true)
return false;
if (d->LockCount < 1)
return _error->Error(_("Not locked"));
if (--d->LockCount == 0)
{
close(d->FrontendLockFD);
close(d->LockFD);
d->LockCount = 0;
}
return true;
}
bool debSystem::UnLockInner(bool NoErrors) {
(void) NoErrors;
close(d->LockFD);
return true;
}
/*}}}*/
// System::IsLocked - Check if system is locked /*{{{*/
// ---------------------------------------------------------------------
/* This checks if the frontend lock is hold. The inner lock might be
* released. */
bool debSystem::IsLocked()
{
return d->LockCount > 0;
}
/*}}}*/
// System::CheckUpdates - Check if the updates dir is dirty /*{{{*/
// ---------------------------------------------------------------------
/* This does a check of the updates directory (dpkg journal) to see if it has
any entries in it. */
bool debSystem::CheckUpdates()
{
// Check for updates.. (dirty)
string File = flNotFile(_config->FindFile("Dir::State::status")) + "updates/";
DIR *DirP = opendir(File.c_str());
if (DirP == 0)
return false;
/* We ignore any files that are not all digits, this skips .,.. and
some tmp files dpkg will leave behind.. */
bool Damaged = false;
for (struct dirent *Ent = readdir(DirP); Ent != 0; Ent = readdir(DirP))
{
Damaged = true;
for (unsigned int I = 0; Ent->d_name[I] != 0; I++)
{
// Check if its not a digit..
if (isdigit(Ent->d_name[I]) == 0)
{
Damaged = false;
break;
}
}
if (Damaged == true)
break;
}
closedir(DirP);
return Damaged;
}
/*}}}*/
// System::CreatePM - Create the underlying package manager /*{{{*/
// ---------------------------------------------------------------------
/* */
pkgPackageManager *debSystem::CreatePM(pkgDepCache *Cache) const
{
return new pkgDPkgPM(Cache);
}
/*}}}*/
// System::Initialize - Setup the configuration space.. /*{{{*/
// ---------------------------------------------------------------------
/* These are the Debian specific configuration variables.. */
static std::string getDpkgStatusLocation(Configuration const &Cnf) {
Configuration PathCnf;
PathCnf.Set("Dir", Cnf.Find("Dir", "/"));
PathCnf.Set("Dir::State::status", "status");
auto const cnfstatedir = Cnf.Find("Dir::State", STATE_DIR + 1);
// if the state dir ends in apt, replace it with dpkg -
// for the default this gives us the same as the fallback below.
// This can't be a ../dpkg as that would play bad with symlinks
std::string statedir;
if (APT::String::Endswith(cnfstatedir, "/apt/"))
statedir.assign(cnfstatedir, 0, cnfstatedir.length() - 5);
else if (APT::String::Endswith(cnfstatedir, "/apt"))
statedir.assign(cnfstatedir, 0, cnfstatedir.length() - 4);
if (statedir.empty())
PathCnf.Set("Dir::State", "var/lib/dpkg");
else
PathCnf.Set("Dir::State", flCombine(statedir, "dpkg"));
return PathCnf.FindFile("Dir::State::status");
}
bool debSystem::Initialize(Configuration &Cnf)
{
/* These really should be jammed into a generic 'Local Database' engine
which is yet to be determined. The functions in pkgcachegen should
be the only users of these */
Cnf.CndSet("Dir::State::extended_states", "extended_states");
if (Cnf.Exists("Dir::State::status") == false)
Cnf.Set("Dir::State::status", getDpkgStatusLocation(Cnf));
Cnf.CndSet("Dir::Bin::dpkg",BIN_DIR"/dpkg");
* merged with apt--fixes--0 Patches applied: * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-7 * fixed incorrect man-page example * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-8 * changelog udpate * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-9 * we only need to check once for xmlto * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-10 * fix a bug in a man-page, fix a problem with overly long lines in apt-cdrom * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-11 * merged with apt--main--0 * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-12 * fix a incorrect error message (it always added .gz regardless what was downloaded) * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-13 * merged with main * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-14 * added Hashsum support for file and cdrom * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-15 * added README.arch * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-16 * merged with main * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-17 * move the changelog to the right place * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-18 * Change pkgPolicy::Pin from private to protected * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-19 * added a default constructor for PrvIterator * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-20 * applied otavios patch to reread the statusFile on debSystem::Initialize * otavio@debian.org--2005/apt--fixes--0--patch-28 Reread status configuration, needed for clients using independent apt ...
17 years ago
if (d->StatusFile) {
delete d->StatusFile;
d->StatusFile = 0;
* merged with apt--fixes--0 Patches applied: * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-7 * fixed incorrect man-page example * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-8 * changelog udpate * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-9 * we only need to check once for xmlto * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-10 * fix a bug in a man-page, fix a problem with overly long lines in apt-cdrom * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-11 * merged with apt--main--0 * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-12 * fix a incorrect error message (it always added .gz regardless what was downloaded) * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-13 * merged with main * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-14 * added Hashsum support for file and cdrom * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-15 * added README.arch * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-16 * merged with main * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-17 * move the changelog to the right place * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-18 * Change pkgPolicy::Pin from private to protected * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-19 * added a default constructor for PrvIterator * michael.vogt@ubuntu.com--2005/apt--fixes--0--patch-20 * applied otavios patch to reread the statusFile on debSystem::Initialize * otavio@debian.org--2005/apt--fixes--0--patch-28 Reread status configuration, needed for clients using independent apt ...
17 years ago
}
return true;
}
/*}}}*/
// System::ArchiveSupported - Is a file format supported /*{{{*/
// ---------------------------------------------------------------------
/* The standard name for a deb is 'deb'.. There are no separate versions
of .deb to worry about.. */
APT_PURE bool debSystem::ArchiveSupported(const char *Type)
{
if (strcmp(Type,"deb") == 0)
return true;
return false;
}
/*}}}*/
// System::Score - Determine how 'Debiany' this sys is.. /*{{{*/
// ---------------------------------------------------------------------
/* We check some files that are sure tell signs of this being a Debian
System.. */
signed debSystem::Score(Configuration const &Cnf)
{
signed Score = 0;
if (FileExists(Cnf.FindFile("Dir::State::status",getDpkgStatusLocation(Cnf).c_str())) == true)
Score += 10;
if (FileExists(Cnf.Find("Dir::Bin::dpkg",BIN_DIR"/dpkg")) == true)
Score += 10;
if (FileExists("/etc/debian_version") == true)
Score += 10;
return Score;
}
/*}}}*/
// System::AddStatusFiles - Register the status files /*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List)
{
if (d->StatusFile == 0)
d->StatusFile = new debStatusIndex(_config->FindFile("Dir::State::status"));
List.push_back(d->StatusFile);
return true;
}
/*}}}*/
// System::FindIndex - Get an index file for status files /*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::FindIndex(pkgCache::PkgFileIterator File,
pkgIndexFile *&Found) const
{
if (d->StatusFile == 0)
return false;
if (d->StatusFile->FindInCache(*File.Cache()) == File)
{
Found = d->StatusFile;
return true;
}
return false;
}
/*}}}*/
std::string debSystem::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;
}
/*}}}*/
std::vector<std::string> debSystem::GetDpkgBaseCommand() /*{{{*/
{
// Generate the base argument list for dpkg
std::vector<std::string> Args = { GetDpkgExecutable() };
// 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);
}
}
return Args;
}
/*}}}*/
void debSystem::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);
}
/*}}}*/
pid_t debSystem::ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput)/*{{{*/
{
std::vector<const char *> Args(sArgs.size(), NULL);
std::transform(sArgs.begin(), sArgs.end(), Args.begin(), [](std::string const &s) { return s.c_str(); });
Args.push_back(NULL);
int external[2] = {-1, -1};
if (inputFd != nullptr || outputFd != nullptr)
if (pipe(external) != 0)
{
_error->WarningE("dpkg", "Can't create IPC pipe for dpkg call");
return -1;
}
pid_t const dpkg = ExecFork();
if (dpkg == 0) {
int const nullfd = open("/dev/null", O_RDONLY);
if (inputFd == nullptr)
dup2(nullfd, STDIN_FILENO);
else
{
close(external[1]);
dup2(external[0], STDIN_FILENO);
}
if (outputFd == nullptr)
dup2(nullfd, STDOUT_FILENO);
else
{
close(external[0]);
dup2(external[1], STDOUT_FILENO);
}
if (DiscardOutput == true)
dup2(nullfd, STDERR_FILENO);
debSystem::DpkgChrootDirectory();
execvp(Args[0], (char**) &Args[0]);
_error->WarningE("dpkg", "Can't execute dpkg!");
_exit(100);
}
if (outputFd != nullptr)
{
close(external[1]);
*outputFd = external[0];
}
else if (inputFd != nullptr)
{
close(external[0]);
*inputFd = external[1];
}
return dpkg;
}
/*}}}*/
bool debSystem::SupportsMultiArch() /*{{{*/
{
std::vector<std::string> Args = GetDpkgBaseCommand();
Args.push_back("--assert-multi-arch");
pid_t const dpkgAssertMultiArch = ExecDpkg(Args, nullptr, nullptr, true);
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)
return true;
}
return false;
}
/*}}}*/
std::vector<std::string> debSystem::SupportedArchitectures() /*{{{*/
{
std::vector<std::string> archs;
{
string const arch = _config->Find("APT::Architecture");
if (arch.empty() == false)
archs.push_back(std::move(arch));
}
std::vector<std::string> sArgs = GetDpkgBaseCommand();
sArgs.push_back("--print-foreign-architectures");
int outputFd = -1;
pid_t const dpkgMultiArch = ExecDpkg(sArgs, nullptr, &outputFd, true);
if (dpkgMultiArch == -1)
return archs;
FILE *dpkg = fdopen(outputFd, "r");
if(dpkg != NULL) {
char* buf = NULL;
size_t bufsize = 0;
while (getline(&buf, &bufsize, dpkg) != -1)
{
char* tok_saveptr;
char* arch = strtok_r(buf, " ", &tok_saveptr);
while (arch != NULL) {
for (; isspace_ascii(*arch) != 0; ++arch);
if (arch[0] != '\0') {
char const* archend = arch;
for (; isspace_ascii(*archend) == 0 && *archend != '\0'; ++archend);
string a(arch, (archend - arch));
if (std::find(archs.begin(), archs.end(), a) == archs.end())
archs.push_back(a);
}
arch = strtok_r(NULL, " ", &tok_saveptr);
}
}
free(buf);
fclose(dpkg);
}
ExecWait(dpkgMultiArch, "dpkg --print-foreign-architectures", true);
return archs;
}
/*}}}*/