X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/f408836a80f20359c87d8815608af651199e3d73..c0723bf1a60daf45096998d4ae9feee3c44343f8:/apt-pkg/deb/debsystem.cc diff --git a/apt-pkg/deb/debsystem.cc b/apt-pkg/deb/debsystem.cc index 089a465c1..899f7328b 100644 --- a/apt-pkg/deb/debsystem.cc +++ b/apt-pkg/deb/debsystem.cc @@ -10,6 +10,8 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include + #include #include #include @@ -17,26 +19,48 @@ #include #include #include -#include -#include +#include +#include + +#include + +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include + +#include /*}}}*/ +using std::string; + debSystem debSys; +class APT_HIDDEN debSystemPrivate { +public: + debSystemPrivate() : LockFD(-1), LockCount(0), StatusFile(0) + { + } + // For locking support + int LockFD; + unsigned LockCount; + + debStatusIndex *StatusFile; +}; + // System::debSystem - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -debSystem::debSystem() +debSystem::debSystem() : pkgSystem("Debian dpkg interface", &debVS), d(new debSystemPrivate()) { - LockFD = -1; - LockCount = 0; - StatusFile = 0; - - Label = "Debian dpkg interface"; - VS = &debVS; } /*}}}*/ // System::~debSystem - Destructor /*{{{*/ @@ -44,7 +68,8 @@ debSystem::debSystem() /* */ debSystem::~debSystem() { - delete StatusFile; + delete d->StatusFile; + delete d; } /*}}}*/ // System::Lock - Get the lock /*{{{*/ @@ -54,16 +79,16 @@ debSystem::~debSystem() bool debSystem::Lock() { // Disable file locking - if (_config->FindB("Debug::NoLocking",false) == true || LockCount > 1) + if (_config->FindB("Debug::NoLocking",false) == true || d->LockCount > 1) { - LockCount++; + d->LockCount++; return true; } // Create the lockfile - string AdminDir = flNotFile(_config->Find("Dir::State::status")); - LockFD = GetLock(AdminDir + "lock"); - if (LockFD == -1) + 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), " @@ -76,13 +101,20 @@ bool debSystem::Lock() // See if we need to abort with a dirty journal if (CheckUpdates() == true) { - close(LockFD); - LockFD = -1; + close(d->LockFD); + 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 'sudo dpkg --configure -a' to correct the problem. ")); + "run '%s' to correct the problem. "), cmd); } - LockCount++; + d->LockCount++; return true; } @@ -92,15 +124,15 @@ bool debSystem::Lock() /* */ bool debSystem::UnLock(bool NoErrors) { - if (LockCount == 0 && NoErrors == true) + if (d->LockCount == 0 && NoErrors == true) return false; - if (LockCount < 1) + if (d->LockCount < 1) return _error->Error(_("Not locked")); - if (--LockCount == 0) + if (--d->LockCount == 0) { - close(LockFD); - LockCount = 0; + close(d->LockFD); + d->LockCount = 0; } return true; @@ -113,7 +145,7 @@ bool debSystem::UnLock(bool NoErrors) bool debSystem::CheckUpdates() { // Check for updates.. (dirty) - string File = flNotFile(_config->Find("Dir::State::status")) + "updates/"; + string File = flNotFile(_config->FindFile("Dir::State::status")) + "updates/"; DIR *DirP = opendir(File.c_str()); if (DirP == 0) return false; @@ -152,18 +184,38 @@ pkgPackageManager *debSystem::CreatePM(pkgDepCache *Cache) const // 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::userstatus","status.user"); // Defunct - Cnf.CndSet("Dir::State::status","/var/lib/dpkg/status"); - Cnf.CndSet("Dir::Bin::dpkg","/usr/bin/dpkg"); + 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"); - if (StatusFile) { - delete StatusFile; - StatusFile = 0; + if (d->StatusFile) { + delete d->StatusFile; + d->StatusFile = 0; } return true; @@ -171,9 +223,9 @@ bool debSystem::Initialize(Configuration &Cnf) /*}}}*/ // System::ArchiveSupported - Is a file format supported /*{{{*/ // --------------------------------------------------------------------- -/* The standard name for a deb is 'deb'.. There are no seperate versions +/* The standard name for a deb is 'deb'.. There are no separate versions of .deb to worry about.. */ -bool debSystem::ArchiveSupported(const char *Type) +APT_PURE bool debSystem::ArchiveSupported(const char *Type) { if (strcmp(Type,"deb") == 0) return true; @@ -187,9 +239,9 @@ bool debSystem::ArchiveSupported(const char *Type) signed debSystem::Score(Configuration const &Cnf) { signed Score = 0; - if (FileExists(Cnf.FindFile("Dir::State::status","/var/lib/dpkg/status")) == true) + if (FileExists(Cnf.FindFile("Dir::State::status",getDpkgStatusLocation(Cnf).c_str())) == true) Score += 10; - if (FileExists(Cnf.FindFile("Dir::Bin::dpkg","/usr/bin/dpkg")) == true) + if (FileExists(Cnf.Find("Dir::Bin::dpkg",BIN_DIR"/dpkg")) == true) Score += 10; if (FileExists("/etc/debian_version") == true) Score += 10; @@ -199,11 +251,11 @@ signed debSystem::Score(Configuration const &Cnf) // System::AddStatusFiles - Register the status files /*{{{*/ // --------------------------------------------------------------------- /* */ -bool debSystem::AddStatusFiles(vector &List) +bool debSystem::AddStatusFiles(std::vector &List) { - if (StatusFile == 0) - StatusFile = new debStatusIndex(_config->FindFile("Dir::State::status")); - List.push_back(StatusFile); + if (d->StatusFile == 0) + d->StatusFile = new debStatusIndex(_config->FindFile("Dir::State::status")); + List.push_back(d->StatusFile); return true; } /*}}}*/ @@ -213,14 +265,175 @@ bool debSystem::AddStatusFiles(vector &List) bool debSystem::FindIndex(pkgCache::PkgFileIterator File, pkgIndexFile *&Found) const { - if (StatusFile == 0) + if (d->StatusFile == 0) return false; - if (StatusFile->FindInCache(*File.Cache()) == File) + if (d->StatusFile->FindInCache(*File.Cache()) == File) { - Found = StatusFile; + 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 debSystem::GetDpkgBaseCommand() /*{{{*/ +{ + // Generate the base argument list for dpkg + std::vector 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 const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput)/*{{{*/ +{ + std::vector 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 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 debSystem::SupportedArchitectures() /*{{{*/ +{ + std::vector archs; + { + string const arch = _config->Find("APT::Architecture"); + if (arch.empty() == false) + archs.push_back(std::move(arch)); + } + + std::vector 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; +} + /*}}}*/