diff options
-rw-r--r-- | debian/changelog | 4 | ||||
-rw-r--r-- | doc/frontend.txt | 22 | ||||
-rw-r--r-- | lib/dpkg/dbmodify.c | 21 | ||||
-rw-r--r-- | lib/dpkg/dpkg.h | 1 | ||||
-rw-r--r-- | man/dpkg.man | 4 |
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 |