Add temporary debug code to allow better debugging of races
authorMilan Broz <gmazyland@gmail.com>
Sun, 15 Nov 2009 19:26:36 +0000 (19:26 +0000)
committerMilan Broz <gmazyland@gmail.com>
Sun, 15 Nov 2009 19:26:36 +0000 (19:26 +0000)
with various udev-event tools which wrongly scan and open
internal temporary cryptsetup devices.

If cryptsetup run in debug mode (--debug) and remove of device
fails, code scan /proc directory and tries to find process name
which locked that device.

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@141 36d66b0a-2a48-0410-832c-cd162a569da5

ChangeLog
lib/Makefile.am
lib/internal.h
lib/libdevmapper.c
lib/setup.c
lib/utils_debug.c [new file with mode: 0644]

index 387a9ed..413c49a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,7 @@
        * Add CRYPT_ prefix to enum defined in libcryptsetup.h.
        * Fix status call to fail when running as non-root user.
        * Check in configure if selinux libraries are required in static version.
+       * Add temporary debug code to find processes locking internal device.
 
 2009-09-30  Milan Broz  <mbroz@redhat.com>
        * Fix exported symbols and versions in libcryptsetup.
index 2597cf3..ab75137 100644 (file)
@@ -42,6 +42,7 @@ libcryptsetup_la_SOURCES = \
        blockdev.h                              \
        libcryptsetup.h                         \
        utils.c                                 \
+       utils_debug.c                           \
        backends.c                              \
        libdevmapper.c                          \
        gcrypt.c
index a49e4d1..fe9dfa0 100644 (file)
@@ -116,6 +116,9 @@ void logger(struct crypt_device *cd, int class, const char *file, int line, cons
        logger(c, CRYPT_LOG_ERROR, __FILE__, __LINE__, x); \
        set_error(x); } while(0)
 
+int crypt_get_debug_level(void);
+void debug_processes_using_device(const char *name);
+
 int crypt_memlock_inc(struct crypt_device *ctx);
 int crypt_memlock_dec(struct crypt_device *ctx);
 
index beaeb34..f4b0bdd 100644 (file)
@@ -249,6 +249,8 @@ int dm_remove_device(const char *name, int force, uint64_t size)
                if (--retries && r) {
                        log_dbg("WARNING: other process locked internal device %s, %s.",
                                name, retries ? "retrying remove" : "giving up");
+                       if (force && (crypt_get_debug_level() == CRYPT_LOG_DEBUG))
+                               debug_processes_using_device(name);
                        sleep(1);
                        if (force && !error_target) {
                                /* If force flag is set, replace device with error, read-only target.
index bd9bf25..43c2acb 100644 (file)
@@ -47,6 +47,11 @@ void crypt_set_debug_level(int level)
        _debug_level = level;
 }
 
+int crypt_get_debug_level()
+{
+       return _debug_level;
+}
+
 void crypt_log(struct crypt_device *cd, int class, const char *msg)
 {
        if (cd && cd->log)
diff --git a/lib/utils_debug.c b/lib/utils_debug.c
new file mode 100644 (file)
index 0000000..6616b5c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Temporary debug code to find processes locking internal cryptsetup devices.
+ * This code is intended to run only in debug mode.
+ *
+ * inspired by psmisc/fuser proc scanning code
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include "libcryptsetup.h"
+#include "internal.h"
+
+#define MAX_PATHNAME 1024
+#define MAX_SHORTNAME 64
+
+static int numeric_name(const char *name)
+{
+       return (name[0] < '0' || name[0] > '9') ? 0 : 1;
+}
+
+static int check_pid(const pid_t pid, const char *dev_name, const char *short_dev_name)
+{
+       char dirpath[MAX_SHORTNAME], fdpath[MAX_SHORTNAME], linkpath[MAX_PATHNAME];
+       DIR *dirp;
+       struct dirent *direntry;
+       size_t len;
+       int r = 0;
+
+       snprintf(dirpath, sizeof(dirpath), "/proc/%d/fd", pid);
+
+       if (!(dirp = opendir(dirpath)))
+               return r;
+
+       while ((direntry = readdir(dirp))) {
+               if (!numeric_name(direntry->d_name))
+                       continue;
+
+               snprintf(fdpath, sizeof(fdpath), "/proc/%d/fd/%s", pid, direntry->d_name);
+
+               if ((len = readlink(fdpath, linkpath, MAX_PATHNAME-1)) < 0)
+                       break;
+               linkpath[len] = '\0';
+
+               if (!strcmp(dev_name, linkpath)) {
+                       r = 1;
+                       break;
+                }
+
+               if (!strcmp(short_dev_name, linkpath)) {
+                       r = 2;
+                       break;
+                }
+       }
+       closedir(dirp);
+       return r;
+}
+
+static int read_proc_info(const pid_t pid, pid_t *ppid, char *name, int max_size)
+{
+       char path[MAX_SHORTNAME], info[max_size], c;
+       int fd, xpid, r = 0;
+
+       snprintf(path, sizeof(path), "/proc/%u/stat", pid);
+       if ((fd = open(path, O_RDONLY)) < 0)
+               return 0;
+
+       if (read(fd, info, max_size) > 0 &&
+           sscanf(info, "%d %s %c %d", &xpid, name, &c, ppid) == 4)
+               r = 1;
+
+       if (!r) {
+               *ppid = 0;
+               name[0] = '\0';
+       }
+       close(fd);
+       return r;
+}
+
+static void report_proc(const pid_t pid, const char *dev_name)
+{
+       char name[MAX_PATHNAME], name2[MAX_PATHNAME];
+       pid_t ppid, ppid2;
+
+       if (read_proc_info(pid, &ppid, name, MAX_PATHNAME) &&
+           read_proc_info(ppid, &ppid2, name2, MAX_PATHNAME))
+               log_dbg("WARNING: Process PID %u %s [PPID %u %s] spying on internal device %s.",
+                       pid, name, ppid, name2, dev_name);
+}
+
+void debug_processes_using_device(const char *dm_name)
+{
+       char short_dev_name[MAX_SHORTNAME], dev_name[MAX_PATHNAME];
+       DIR *proc_dir;
+       struct dirent *proc_dentry;
+       struct stat st;
+       pid_t pid;
+
+       if (crypt_get_debug_level() != CRYPT_LOG_DEBUG)
+               return;
+
+       snprintf(dev_name, sizeof(dev_name), "/dev/mapper/%s", dm_name);
+       if (stat(dev_name, &st) || !S_ISBLK(st.st_mode))
+               return;
+       snprintf(short_dev_name, sizeof(short_dev_name), "/dev/dm-%u", minor(st.st_rdev));
+
+       if (!(proc_dir = opendir("/proc")))
+               return;
+
+       while ((proc_dentry = readdir(proc_dir))) {
+               if (!numeric_name(proc_dentry->d_name))
+                       continue;
+
+               pid = atoi(proc_dentry->d_name);
+               switch(check_pid(pid, dev_name, short_dev_name)) {
+               case 1: report_proc(pid, dev_name);
+                       break;
+               case 2: report_proc(pid, short_dev_name);
+               default:
+                       break;
+               }
+       }
+       closedir(proc_dir);
+}