Parcourir la source

dpkg: Detect locked databases on --audit

Detect when another process has locked the database, and mention that
problematic dpkg --audit results might be due to ongoing operations.

Closes: #80252
Guillem Jover il y a 16 ans
Parent
commit
c3d9eea8d0
6 fichiers modifiés avec 68 ajouts et 1 suppressions
  1. 3 0
      debian/changelog
  2. 31 1
      lib/dpkg/dbmodify.c
  3. 1 0
      lib/dpkg/dpkg-db.h
  4. 22 0
      lib/dpkg/file.c
  5. 3 0
      lib/dpkg/file.h
  6. 8 0
      src/enquiry.c

+ 3 - 0
debian/changelog

@@ -68,6 +68,9 @@ dpkg (1.15.8) UNRELEASED; urgency=low
     packages.
   * Move Dpkg.pm and Dpkg/Gettext.pm from dpkg to libdpkg-perl.
   * Bump Standards-Version to 3.9.0.
+  * Detect when another process has locked the database, and mention that
+    problematic dpkg --audit results might be due to ongoing operations.
+    Closes: #80252
 
   [ Updated programs translations ]
   * Catalan (Guillem Jover).

+ 31 - 1
lib/dpkg/dbmodify.c

@@ -142,10 +142,40 @@ static const struct fni {
   {   NULL, NULL                                      }
 };
 
+static int dblockfd = -1;
+
+bool
+modstatdb_is_locked(const char *admindir)
+{
+  struct varbuf lockfile = VARBUF_INIT;
+  int lockfd;
+  bool locked;
+
+  varbufprintf(&lockfile, "%s/%s", admindir, LOCKFILE);
+
+  if (dblockfd == -1) {
+    lockfd = open(lockfile.buf, O_RDONLY);
+    if (lockfd == -1)
+      ohshite(_("unable to open lock file %s for testing"), lockfile.buf);
+  } else {
+    lockfd = dblockfd;
+  }
+
+  locked = file_is_locked(lockfd, lockfile.buf);
+
+  /* We only close the file if there was no lock open, otherwise we would
+   * release the existing lock on close. */
+  if (dblockfd == -1)
+    close(lockfd);
+
+  varbuf_destroy(&lockfile);
+
+  return locked;
+}
+
 void
 modstatdb_lock(const char *admindir)
 {
-  static int dblockfd = -1;
   int n;
   char *dblockfile = NULL;
 

+ 1 - 0
lib/dpkg/dpkg-db.h

@@ -199,6 +199,7 @@ enum modstatdb_rw {
   msdbrw_noavail= 0100,
 };
 
+bool modstatdb_is_locked(const char *admindir);
 void modstatdb_lock(const char *admindir);
 void modstatdb_unlock(void);
 enum modstatdb_rw modstatdb_init(const char *admindir, enum modstatdb_rw reqrwflags);

+ 22 - 0
lib/dpkg/file.c

@@ -83,6 +83,28 @@ file_unlock(void)
 	pop_cleanup(ehflag_normaltidy); /* Calls file_unlock_cleanup. */
 }
 
+/**
+ * Check if a file has a lock acquired.
+ *
+ * @param lockfd The file descriptor for the lock.
+ * @param filename The file name associated to the file descriptor.
+ */
+bool
+file_is_locked(int lockfd, const char *filename)
+{
+	struct flock fl;
+
+	file_lock_setup(&fl, F_WRLCK);
+
+	if (fcntl(lockfd, F_GETLK, &fl) == -1)
+		ohshit(_("unable to check file '%s' lock status"), filename);
+
+	if (fl.l_type == F_WRLCK && fl.l_pid != getpid())
+		return true;
+	else
+		return false;
+}
+
 /* lockfd must be allocated statically as its addresses is passed to
  * a cleanup handler. */
 void

+ 3 - 0
lib/dpkg/file.h

@@ -21,6 +21,8 @@
 #ifndef LIBDPKG_FILE_H
 #define LIBDPKG_FILE_H
 
+#include <stdbool.h>
+
 #include <dpkg/macros.h>
 
 DPKG_BEGIN_DECLS
@@ -30,6 +32,7 @@ DPKG_BEGIN_DECLS
  */
 void file_copy_perms(const char *src, const char *dst);
 
+bool file_is_locked(int lockfd, const char *filename);
 void file_lock(int *lockfd, const char *filename,
                const char *emsg, const char *emsg_eagain);
 void file_unlock(void);

+ 8 - 0
src/enquiry.c

@@ -124,6 +124,7 @@ static void describebriefly(struct pkginfo *pkg) {
 
 void audit(const char *const *argv) {
   const struct badstatinfo *bsi;
+  bool head_running = false;
 
   if (*argv)
     badusage(_("--%s takes no arguments"), cipaction->olong);
@@ -138,6 +139,13 @@ void audit(const char *const *argv) {
     it= iterpkgstart(); 
     while ((pkg= iterpkgnext(it))) {
       if (!bsi->yesno(pkg,bsi)) continue;
+      if (!head_running) {
+        if (modstatdb_is_locked(admindir))
+          puts(_(
+"Another process has locked the database for writing, and might currently be\n"
+"modifying it, some of the following problems might just be due to that.\n"));
+        head_running = true;
+      }
       if (!head) {
         fputs(gettext(bsi->explanation),stdout);
         head = true;