From: Masatake YAMATO Date: Thu, 9 Nov 2023 22:08:45 +0000 (+0900) Subject: lslocks: use information extracted from "locks: " column of /proc/$pid/fdinfo/* X-Git-Tag: v2.40-rc1~163^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6c2c1b11a22a8a1e5ae4ab3425dde2b7f4150585;p=thirdparty%2Futil-linux.git lslocks: use information extracted from "locks: " column of /proc/$pid/fdinfo/* Signed-off-by: Masatake YAMATO --- diff --git a/misc-utils/lslocks.c b/misc-utils/lslocks.c index d60cb013e2..8fe8981ed9 100644 --- a/misc-utils/lslocks.c +++ b/misc-utils/lslocks.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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);