diff -Nru dpkg-1.18.4ubuntu1.4/debian/changelog dpkg-1.18.4ubuntu1.5/debian/changelog --- dpkg-1.18.4ubuntu1.4/debian/changelog 2018-03-14 19:17:08.000000000 +0000 +++ dpkg-1.18.4ubuntu1.5/debian/changelog 2018-10-04 12:21:49.000000000 +0000 @@ -1,3 +1,13 @@ +dpkg (1.18.4ubuntu1.5) xenial; urgency=medium + + * Apply patch from upstream to add frontend locking (LP: #1796081): + - 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. + + -- Julian Andres Klode Thu, 04 Oct 2018 14:21:49 +0200 + dpkg (1.18.4ubuntu1.4) xenial; urgency=medium * Cherry-pick d01212f2d7e59fc713c66b5d60421ac2296c1463 from 1.18.5: diff -Nru dpkg-1.18.4ubuntu1.4/doc/frontend.txt dpkg-1.18.4ubuntu1.5/doc/frontend.txt --- dpkg-1.18.4ubuntu1.4/doc/frontend.txt 2015-11-25 21:45:14.000000000 +0000 +++ dpkg-1.18.4ubuntu1.5/doc/frontend.txt 2018-10-04 12:21:49.000000000 +0000 @@ -10,15 +10,15 @@ ---------------- Any frontend needing to make sure no write operation is currently happening, -should lock the dpkg database by locking the file «/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 +«/lock-frontend», and then acquire the dpkg database lock at +«/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 -Nru dpkg-1.18.4ubuntu1.4/lib/dpkg/dbmodify.c dpkg-1.18.4ubuntu1.5/lib/dpkg/dbmodify.c --- dpkg-1.18.4ubuntu1.4/lib/dpkg/dbmodify.c 2015-11-26 23:53:36.000000000 +0000 +++ dpkg-1.18.4ubuntu1.5/lib/dpkg/dbmodify.c 2018-10-04 12:21:49.000000000 +0000 @@ -51,6 +51,7 @@ 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; @@ -140,6 +141,7 @@ char **store; } fnis[] = { { LOCKFILE, &lockfile }, + { FRONTENDLOCKFILE, &frontendlockfile }, { STATUSFILE, &statusfile }, { AVAILFILE, &availablefile }, { UPDATESDIR, &updatesdir }, @@ -185,6 +187,7 @@ } static int dblockfd = -1; +static int frontendlockfd = -1; bool modstatdb_is_locked(void) @@ -216,6 +219,18 @@ 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) @@ -233,6 +248,9 @@ 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")); } @@ -241,8 +259,11 @@ { /* Unlock. */ pop_cleanup(ehflag_normaltidy); + if (frontendlockfd != -1) + pop_cleanup(ehflag_normaltidy); dblockfd = -1; + frontendlockfd = -1; } enum modstatdb_rw diff -Nru dpkg-1.18.4ubuntu1.4/lib/dpkg/dpkg.h dpkg-1.18.4ubuntu1.5/lib/dpkg/dpkg.h --- dpkg-1.18.4ubuntu1.4/lib/dpkg/dpkg.h 2015-12-12 20:49:24.000000000 +0000 +++ dpkg-1.18.4ubuntu1.5/lib/dpkg/dpkg.h 2018-10-04 12:21:49.000000000 +0000 @@ -76,6 +76,7 @@ #define STATUSFILE "status" #define AVAILFILE "available" #define LOCKFILE "lock" +#define FRONTENDLOCKFILE "lock-frontend" #define DIVERSIONSFILE "diversions" #define STATOVERRIDEFILE "statoverride" #define UPDATESDIR "updates/" diff -Nru dpkg-1.18.4ubuntu1.4/man/dpkg.1 dpkg-1.18.4ubuntu1.5/man/dpkg.1 --- dpkg-1.18.4ubuntu1.4/man/dpkg.1 2015-12-25 03:53:50.000000000 +0000 +++ dpkg-1.18.4ubuntu1.5/man/dpkg.1 2018-10-04 12:21:49.000000000 +0000 @@ -817,6 +817,10 @@ .B COLUMNS Sets the number of columns \fBdpkg\fP should use when displaying formatted text. Currently only used by \-l. +.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_SHELL_REASON