Browse Source

Add support for dpkg frontend lock

The dpkg frontend lock is a lock dpkg tries to acquire
except if the frontend already acquires it.

This fixes a race condition in the install command where the
dpkg lock is not held for a short period of time between
different dpkg invocations.

For this reason we also define an environment variable
DPKG_FRONTEND_LOCKED for dpkg invocations so dpkg knows
not to try to acquire the frontend lock because it's held
by a parent process.

We can set DPKG_FRONTEND_LOCKED only if the frontend lock
really is held; that is, if our lock count is greater than 0
- otherwise an apt client not using the LockInner family of
functions would run dpkg without the frontend lock set, but
with DPKG_FRONTEND_LOCKED set. Such a process has a weaker
guarantee: Because dpkg would not lock the frontend lock
either, the process is prone to the existing races, and,
more importantly, so is a new style process.

Closes: #869546

[fixups: fix error messages, add public IsLocked() method, and
 make {Un,}LockInner return an error on !debSystem]
tags/debian/1.7.0_alpha3
Julian Andres Klode Julian Andres Klode 4 years ago
parent
commit
c2c8b4787b
6 changed files with 93 additions and 9 deletions
  1. +46
    -7
      apt-pkg/deb/debsystem.cc
  2. +4
    -0
      apt-pkg/deb/debsystem.h
  3. +4
    -0
      apt-pkg/deb/dpkgpm.cc
  4. +25
    -0
      apt-pkg/pkgsystem.cc
  5. +12
    -0
      apt-pkg/pkgsystem.h
  6. +2
    -2
      apt-private/private-install.cc

+ 46
- 7
apt-pkg/deb/debsystem.cc View File

@@ -44,10 +44,11 @@ debSystem debSys;

class APT_HIDDEN debSystemPrivate {
public:
debSystemPrivate() : LockFD(-1), LockCount(0), StatusFile(0)
debSystemPrivate() : FrontendLockFD(-1), LockFD(-1), LockCount(0), StatusFile(0)
{
}
// For locking support
int FrontendLockFD;
int LockFD;
unsigned LockCount;
@@ -85,21 +86,29 @@ bool debSystem::Lock()

// Create the lockfile
string AdminDir = flNotFile(_config->FindFile("Dir::State::status"));
d->LockFD = GetLock(AdminDir + "lock");
if (d->LockFD == -1)
string FrontendLockFile = AdminDir + "lock-frontend";
d->FrontendLockFD = GetLock(FrontendLockFile);
if (d->FrontendLockFD == -1)
{
if (errno == EACCES || errno == EAGAIN)
return _error->Error(_("Unable to lock the administration directory (%s), "
"is another process using it?"),AdminDir.c_str());
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 lock the administration directory (%s), "
"are you root?"),AdminDir.c_str());
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)
@@ -116,6 +125,21 @@ bool debSystem::Lock()
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 /*{{{*/
// ---------------------------------------------------------------------
@@ -129,11 +153,26 @@ bool debSystem::UnLock(bool NoErrors)
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 /*{{{*/


+ 4
- 0
apt-pkg/deb/debsystem.h View File

@@ -50,6 +50,10 @@ class debSystem : public pkgSystem
APT_HIDDEN static pid_t ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput);
APT_HIDDEN static bool SupportsMultiArch();
APT_HIDDEN static std::vector<std::string> SupportedArchitectures();

APT_HIDDEN bool LockInner();
APT_HIDDEN bool UnLockInner(bool NoErrors=false);
APT_HIDDEN bool IsLocked();
};

extern debSystem debSys;


+ 4
- 0
apt-pkg/deb/dpkgpm.cc View File

@@ -2010,6 +2010,10 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
else
setenv("DPKG_COLORS", "never", 0);

if (dynamic_cast<debSystem*>(_system) != nullptr
&& dynamic_cast<debSystem*>(_system)->IsLocked() == true) {
setenv("DPKG_FRONTEND_LOCKED", "true", 1);
}
execvp(Args[0], (char**) &Args[0]);
cerr << "Could not exec dpkg!" << endl;
_exit(100);


+ 25
- 0
apt-pkg/pkgsystem.cc View File

@@ -12,6 +12,7 @@
#include <config.h>

#include <apt-pkg/debsystem.h>
#include <apt-pkg/error.h>
#include <apt-pkg/macros.h>
#include <apt-pkg/pkgsystem.h>

@@ -85,4 +86,28 @@ map_id_t pkgSystem::GetVersionMapping(map_id_t const in) const
}
/*}}}*/

bool pkgSystem::LockInner() /*{{{*/
{
debSystem * const deb = dynamic_cast<debSystem *>(this);
if (deb != NULL)
return deb->LockInner();
return _error->Error("LockInner is not implemented");
}
/*}}}*/
bool pkgSystem::UnLockInner(bool NoErrors) /*{{{*/
{
debSystem * const deb = dynamic_cast<debSystem *>(this);
if (deb != NULL)
return deb->UnLockInner(NoErrors);
return _error->Error("UnLockInner is not implemented");
}
/*}}}*/
bool pkgSystem::IsLocked() /*{{{*/
{
debSystem * const deb = dynamic_cast<debSystem *>(this);
if (deb != NULL)
return deb->IsLocked();
return true;
}
/*}}}*/
pkgSystem::~pkgSystem() {}

+ 12
- 0
apt-pkg/pkgsystem.h View File

@@ -119,6 +119,18 @@ class pkgSystem

pkgSystem(char const * const Label, pkgVersioningSystem * const VS);
virtual ~pkgSystem();


/* companions to Lock()/UnLock
*
* These functions can be called prior to calling dpkg to release an inner
* lock without releasing the overall outer lock, so that dpkg can run
* correctly but no other APT instance can acquire the system lock.
*/
bool LockInner();
bool UnLockInner(bool NoErrors = false);
/// checks if the system is currently locked
bool IsLocked();
private:
pkgSystemPrivate * const d;
};


+ 2
- 2
apt-private/private-install.cc View File

@@ -346,7 +346,7 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
}

auto const progress = APT::Progress::PackageManagerProgressFactory();
_system->UnLock();
_system->UnLockInner();
pkgPackageManager::OrderResult const Res = PM->DoInstall(progress);
delete progress;

@@ -355,7 +355,7 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
if (Res == pkgPackageManager::Completed)
break;

_system->Lock();
_system->LockInner();

// Reload the fetcher object and loop again for media swapping
Fetcher.Shutdown();


Loading…
Cancel
Save