summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog4
-rw-r--r--doc/frontend.txt22
-rw-r--r--lib/dpkg/dbmodify.c21
-rw-r--r--lib/dpkg/dpkg.h1
-rw-r--r--man/dpkg.man4
5 files changed, 41 insertions, 11 deletions
diff --git a/debian/changelog b/debian/changelog
index afe1d4ab1..3542c13fc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -53,6 +53,10 @@ dpkg (1.19.1) UNRELEASED; urgency=medium
* Warn when using dpkg-divert --rename on a file from an Essential package.
* Use a single “struct filenamenode” definition for the entire code base.
Closes: #746766
+ * Add support for frontend locking. This makes it possible for frontends
+ using this new protocol, to safely lock the dpkg database w/o risk of
+ race conditions with other dpkg instances or frontends supporting the
+ same protocol. Thanks to Julian Andres Klode <jak@debian.org>.
* Architecture support:
- Add support for riscv64 CPU. Closes: #822914
Thanks to Manuel A. Fernandez Montecelo <mafm@debian.org>
diff --git a/doc/frontend.txt b/doc/frontend.txt
index 66280860c..9ea1e20f6 100644
--- a/doc/frontend.txt
+++ b/doc/frontend.txt
@@ -10,15 +10,15 @@ Database Locking
----------------
Any frontend needing to make sure no write operation is currently happening,
-should lock the dpkg database by locking the file «<admindir>/lock» using
-file record locks (i.e. fcntl(2) advisory locking). The whole file should
-be locked, as that's the most portable way to perform this operation; this
-can be achieved by using start=0, len=0 and whence=SEEK_SET.
+and no other frontend is running should first acquire the frontend lock at
+«<admindir>/lock-frontend», and then acquire the dpkg database lock at
+«<admindir>/lock». When the frontend invokes dpkg, it should set the
+environment variable DPKG_FRONTEND_LOCKED (to prevent dpkg from acquiring
+the frontend lock), and then release the dpkg database lock, which will be
+acquired by dpkg itself. This way no other frontend following this protocol
+can race to perform operations while another one has one in progress.
-Take into account there will be a race condition between the frontend
-unlocking the database and the invoked dpkg locking it again, in which
-another process could lock it.
-
-In the future this functionality will be available through a shared libdpkg
-library, and all frontends will be expected to switch to that instead,
-because this will fix the aforementioned race condition.
+These locks must be file record locks (i.e. fcntl(2) advisory locking), and
+the whole file should be locked, as that's the most portable way to perform
+this operation; this can be achieved by using start=0, len=0 and
+whence=SEEK_SET.
diff --git a/lib/dpkg/dbmodify.c b/lib/dpkg/dbmodify.c
index 9baa58373..4e1457429 100644
--- a/lib/dpkg/dbmodify.c
+++ b/lib/dpkg/dbmodify.c
@@ -50,6 +50,7 @@ static bool db_initialized;
static enum modstatdb_rw cstatus=-1, cflags=0;
static char *lockfile;
+static char *frontendlockfile;
static char *statusfile, *availablefile;
static char *importanttmpfile=NULL;
static FILE *importanttmp;
@@ -139,6 +140,7 @@ static const struct fni {
char **store;
} fnis[] = {
{ LOCKFILE, &lockfile },
+ { FRONTENDLOCKFILE, &frontendlockfile },
{ STATUSFILE, &statusfile },
{ AVAILFILE, &availablefile },
{ UPDATESDIR, &updatesdir },
@@ -184,6 +186,7 @@ modstatdb_done(void)
}
static int dblockfd = -1;
+static int frontendlockfd = -1;
bool
modstatdb_is_locked(void)
@@ -215,6 +218,18 @@ modstatdb_can_lock(void)
if (dblockfd >= 0)
return true;
+ if (getenv("DPKG_FRONTEND_LOCKED") == NULL) {
+ frontendlockfd = open(frontendlockfile, O_RDWR | O_CREAT | O_TRUNC, 0660);
+ if (frontendlockfd == -1) {
+ if (errno == EACCES || errno == EPERM)
+ return false;
+ else
+ ohshite(_("unable to open/create frontend lockfile"));
+ }
+ } else {
+ frontendlockfd = -1;
+ }
+
dblockfd = open(lockfile, O_RDWR | O_CREAT | O_TRUNC, 0660);
if (dblockfd == -1) {
if (errno == EACCES || errno == EPERM)
@@ -232,6 +247,9 @@ modstatdb_lock(void)
if (!modstatdb_can_lock())
ohshit(_("you do not have permission to lock the dpkg status database"));
+ if (frontendlockfd != -1)
+ file_lock(&frontendlockfd, FILE_LOCK_NOWAIT, frontendlockfile,
+ _("dpkg frontend"));
file_lock(&dblockfd, FILE_LOCK_NOWAIT, lockfile, _("dpkg status database"));
}
@@ -240,8 +258,11 @@ modstatdb_unlock(void)
{
/* Unlock. */
pop_cleanup(ehflag_normaltidy);
+ if (frontendlockfd != -1)
+ pop_cleanup(ehflag_normaltidy);
dblockfd = -1;
+ frontendlockfd = -1;
}
enum modstatdb_rw
diff --git a/lib/dpkg/dpkg.h b/lib/dpkg/dpkg.h
index 19b7914f4..7bc348039 100644
--- a/lib/dpkg/dpkg.h
+++ b/lib/dpkg/dpkg.h
@@ -78,6 +78,7 @@ DPKG_BEGIN_DECLS
#define STATUSFILE "status"
#define AVAILFILE "available"
#define LOCKFILE "lock"
+#define FRONTENDLOCKFILE "lock-frontend"
#define DIVERSIONSFILE "diversions"
#define STATOVERRIDEFILE "statoverride"
#define UPDATESDIR "updates/"
diff --git a/man/dpkg.man b/man/dpkg.man
index 4f6f14bb9..fdaa87b1c 100644
--- a/man/dpkg.man
+++ b/man/dpkg.man
@@ -888,6 +888,10 @@ Currently only used by \fB\-\-list\fP.
Sets the color mode (since dpkg 1.18.5).
The currently accepted values are: \fBauto\fP (default), \fBalways\fP and
\fBnever\fP.
+.TP
+.B DPKG_FRONTEND_LOCKED
+Set by a package manager frontend to notify dpkg that it should not acquire
+the frontend lock (since dpkg 1.19.1).
.SS Internal environment
.TP
.B DPKG_ROOT