]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lslocks: use information extracted from "locks: " column of /proc/$pid/fdinfo/*
authorMasatake YAMATO <yamato@redhat.com>
Thu, 9 Nov 2023 22:08:45 +0000 (07:08 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sun, 12 Nov 2023 18:30:30 +0000 (03:30 +0900)
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
misc-utils/lslocks.c

index d60cb013e2c7871401619a4f7f3a8eee7a7b5512..8fe8981ed99bdb6c71c78eae12225352e8cd04a9 100644 (file)
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <stdbool.h>
 
 #include <libmount.h>
 #include <libsmartcols.h>
@@ -239,7 +240,34 @@ static ino_t get_dev_inode(char *str, dev_t *dev)
        return inum;
 }
 
-static struct lock *get_lock(char *buf)
+struct override_info {
+       pid_t pid;
+       const char *cmdname;
+};
+
+static void patch_lock(struct lock *l, struct list_head *fallback)
+{
+       struct list_head *p;
+
+       list_for_each(p, fallback) {
+               struct lock *m = list_entry(p, struct lock, locks);
+               if (l->start == m->start &&
+                   l->end == m->end &&
+                   l->inode == m->inode &&
+                   l->dev == m->dev &&
+                   l->mandatory == m->mandatory &&
+                   l->blocked == m->blocked &&
+                   strcmp(l->type, m->type) == 0 &&
+                   strcmp(l->mode, m->mode) == 0) {
+                       /* size and id can be ignored. */
+                       l->pid = m->pid;
+                       l->cmdname = xstrdup(m->cmdname);
+                       break;
+               }
+       }
+}
+
+static struct lock *get_lock(char *buf, struct override_info *oinfo, struct list_head *fallback)
 {
        int i;
        char *tok = NULL;
@@ -247,6 +275,8 @@ static struct lock *get_lock(char *buf)
        struct lock *l = xcalloc(1, sizeof(*l));
        INIT_LIST_HEAD(&l->locks);
 
+       bool cmdname_unknown = false;
+
        for (tok = strtok(buf, " "), i = 0; tok;
             tok = strtok(NULL, " "), i++) {
 
@@ -256,8 +286,12 @@ static struct lock *get_lock(char *buf)
                 */
                switch (i) {
                case 0: /* ID: */
-                       tok[strlen(tok) - 1] = '\0';
-                       l->id = strtos32_or_err(tok, _("failed to parse ID"));
+                       if (oinfo)
+                               l->id = -1;
+                       else {
+                               tok[strlen(tok) - 1] = '\0';
+                               l->id = strtos32_or_err(tok, _("failed to parse ID"));
+                       }
                        break;
                case 1: /* posix, flock, etc */
                        if (strcmp(tok, "->") == 0) {   /* optional field */
@@ -279,13 +313,20 @@ static struct lock *get_lock(char *buf)
                         * If user passed a pid we filter it later when adding
                         * to the list, no need to worry now. OFD locks use -1 PID.
                         */
-                       l->pid = strtos32_or_err(tok, _("failed to parse pid"));
-                       if (l->pid > 0) {
-                               l->cmdname = pid_get_cmdname(l->pid);
-                               if (!l->cmdname)
-                                       l->cmdname = xstrdup(_("(unknown)"));
-                       } else
-                               l->cmdname = xstrdup(_("(undefined)"));
+                       if (oinfo) {
+                               l->pid = oinfo->pid;
+                               l->cmdname = xstrdup(oinfo->cmdname);
+                       } else {
+                               l->pid = strtos32_or_err(tok, _("failed to parse pid"));
+                               if (l->pid > 0) {
+                                       l->cmdname = pid_get_cmdname(l->pid);
+                                       if (!l->cmdname) {
+                                               l->cmdname = NULL;
+                                               cmdname_unknown = true;
+                                       }
+                               } else
+                                       l->cmdname = NULL;
+                       }
                        break;
 
                case 5: /* device major:minor and inode number */
@@ -308,6 +349,14 @@ static struct lock *get_lock(char *buf)
                }
        }
 
+       if ((!l->blocked) && fallback && !l->cmdname)
+               patch_lock(l, fallback);
+       if (!l->cmdname) {
+               if (cmdname_unknown)
+                       l->cmdname = xstrdup(_("(unknown)"));
+               else
+                       l->cmdname = xstrdup(_("(undefined)"));
+       }
        l->path = get_filename_sz(l->inode, l->pid, &sz);
 
        /* no permissions -- ignore */
@@ -326,7 +375,96 @@ static struct lock *get_lock(char *buf)
        return l;
 }
 
-static int get_proc_locks(struct list_head *locks)
+static int get_pid_lock(struct list_head *locks, FILE *fp,
+                       pid_t pid, const char *cmdname)
+{
+       char buf[PATH_MAX];
+       struct override_info oinfo = {
+               .pid = pid,
+               .cmdname = cmdname,
+       };
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               struct lock *l;
+               if (strncmp(buf, "lock:\t", 6))
+                       continue;
+               l = get_lock(buf + 6, &oinfo, NULL);
+               if (l)
+                       list_add(&l->locks, locks);
+               /* no break here.
+                  Multiple recode locks can be taken via one fd. */
+       }
+
+       return 0;
+}
+
+static int get_pid_locks(struct list_head *locks, struct path_cxt *pc,
+                        pid_t pid, const char *cmdname)
+{
+       DIR *sub = NULL;
+       struct dirent *d = NULL;
+       int rc = 0;
+
+       while (ul_path_next_dirent(pc, &sub, "fdinfo", &d) == 0) {
+               uint64_t num;
+               FILE *fdinfo;
+
+               if (ul_strtou64(d->d_name, &num, 10) != 0)      /* only numbers */
+                       continue;
+
+               fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%ju", num);
+               if (fdinfo == NULL)
+                       continue;
+
+               get_pid_lock(locks, fdinfo, pid, cmdname);
+               fclose(fdinfo);
+       }
+
+       return rc;
+}
+
+static int get_pids_locks(struct list_head *locks)
+{
+       DIR *dir;
+       struct dirent *d;
+       struct path_cxt *pc = NULL;
+       int rc = 0;
+
+       pc = ul_new_path(NULL);
+       if (!pc)
+               err(EXIT_FAILURE, _("failed to alloc procfs handler"));
+
+       dir = opendir(_PATH_PROC);
+       if (!dir)
+               err(EXIT_FAILURE, _("failed to open /proc"));
+
+       while ((d = readdir(dir))) {
+               pid_t pid;
+               char buf[BUFSIZ];
+               const char *cmdname = NULL;
+
+               if (procfs_dirent_get_pid(d, &pid) != 0)
+                       continue;
+
+               if (procfs_process_init_path(pc, pid) != 0) {
+                       rc = -1;
+                       break;
+               }
+
+               if (procfs_process_get_cmdname(pc, buf, sizeof(buf)) <= 0)
+                       continue;
+               cmdname = buf;
+
+               get_pid_locks(locks, pc, pid, cmdname);
+       }
+
+       closedir(dir);
+       ul_unref_path(pc);
+
+       return rc;
+}
+
+static int get_proc_locks(struct list_head *locks, struct list_head *fallback)
 {
        FILE *fp;
        char buf[PATH_MAX];
@@ -335,7 +473,7 @@ static int get_proc_locks(struct list_head *locks)
                return -1;
 
        while (fgets(buf, sizeof(buf), fp)) {
-               struct lock *l = get_lock(buf);
+               struct lock *l = get_lock(buf, NULL, fallback);
                if (l)
                        list_add(&l->locks, locks);
        }
@@ -587,7 +725,7 @@ static void __attribute__((__noreturn__)) usage(void)
 int main(int argc, char *argv[])
 {
        int c, rc = 0;
-       struct list_head proc_locks;
+       struct list_head proc_locks, pid_locks;
        char *outarg = NULL;
        enum {
                OPT_OUTPUT_ALL = CHAR_MAX + 1
@@ -664,6 +802,7 @@ int main(int argc, char *argv[])
        }
 
        INIT_LIST_HEAD(&proc_locks);
+       INIT_LIST_HEAD(&pid_locks);
 
        if (!ncolumns) {
                /* default columns */
@@ -684,11 +823,17 @@ int main(int argc, char *argv[])
 
        scols_init_debug(0);
 
-       rc = get_proc_locks(&proc_locks);
+       /* get_pids_locks() get locks related information from "lock:" fields
+        * of /proc/$pid/fdinfo/$fd as fallback information.
+        * get_proc_locks() used the fallback information if /proc/locks
+        * doesn't provides enough information or provides staled information. */
+       get_pids_locks(&pid_locks);
+       rc = get_proc_locks(&proc_locks, &pid_locks);
 
        if (!rc && !list_empty(&proc_locks))
                rc = show_locks(&proc_locks, target_pid);
 
+       rem_locks(&pid_locks);
        rem_locks(&proc_locks);
 
        mnt_unref_table(tab);