summaryrefslogtreecommitdiffstats
path: root/misc-utils/lslocks.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:32 +0000
commit8bb05ac73a5b448b339ce0bc8d396c82c459b47f (patch)
tree1fdda006866bca20d41cb206767ea5241e36852f /misc-utils/lslocks.c
parentAdding debian version 2.39.3-11. (diff)
downloadutil-linux-8bb05ac73a5b448b339ce0bc8d396c82c459b47f.tar.xz
util-linux-8bb05ac73a5b448b339ce0bc8d396c82c459b47f.zip
Merging upstream version 2.40.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--misc-utils/lslocks.c571
1 files changed, 436 insertions, 135 deletions
diff --git a/misc-utils/lslocks.c b/misc-utils/lslocks.c
index caca13f..3d70b04 100644
--- a/misc-utils/lslocks.c
+++ b/misc-utils/lslocks.c
@@ -31,6 +31,8 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <stdbool.h>
+#include <search.h>
#include <libmount.h>
#include <libsmartcols.h>
@@ -45,6 +47,8 @@
#include "closestream.h"
#include "optutils.h"
#include "procfs.h"
+#include "column-list-table.h"
+#include "fileutils.h"
/* column IDs */
enum {
@@ -59,15 +63,16 @@ enum {
COL_START,
COL_END,
COL_PATH,
- COL_BLOCKER
+ COL_BLOCKER,
+ COL_HOLDERS,
};
/* column names */
struct colinfo {
- const char *name; /* header */
- double whint; /* width hint (N < 1 is in percent of termwidth) */
- int flags; /* SCOLS_FL_* */
- const char *help;
+ const char * const name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* SCOLS_FL_* */
+ const char *help;
};
/* columns descriptions */
@@ -75,7 +80,7 @@ static struct colinfo infos[] = {
[COL_SRC] = { "COMMAND",15, 0, N_("command of the process holding the lock") },
[COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, N_("PID of the process holding the lock") },
[COL_TYPE] = { "TYPE", 5, SCOLS_FL_RIGHT, N_("kind of lock") },
- [COL_SIZE] = { "SIZE", 4, SCOLS_FL_RIGHT, N_("size of the lock") },
+ [COL_SIZE] = { "SIZE", 4, SCOLS_FL_RIGHT, N_("size of the lock, use <number> if --bytes is given") },
[COL_INODE] = { "INODE", 5, SCOLS_FL_RIGHT, N_("inode number") },
[COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
[COL_MODE] = { "MODE", 5, 0, N_("lock access mode") },
@@ -83,14 +88,13 @@ static struct colinfo infos[] = {
[COL_START] = { "START", 10, SCOLS_FL_RIGHT, N_("relative byte offset of the lock")},
[COL_END] = { "END", 10, SCOLS_FL_RIGHT, N_("ending offset of the lock")},
[COL_PATH] = { "PATH", 0, SCOLS_FL_TRUNC, N_("path of the locked file")},
- [COL_BLOCKER] = { "BLOCKER", 0, SCOLS_FL_RIGHT, N_("PID of the process blocking the lock") }
+ [COL_BLOCKER] = { "BLOCKER", 0, SCOLS_FL_RIGHT, N_("PID of the process blocking the lock") },
+ [COL_HOLDERS] = { "HOLDERS", 0, SCOLS_FL_WRAP, N_("HOLDERS of the lock") },
};
static int columns[ARRAY_SIZE(infos) * 2];
static size_t ncolumns;
-static pid_t pid = 0;
-
static struct libmnt_table *tab; /* /proc/self/mountinfo */
/* basic output flags */
@@ -115,9 +119,56 @@ struct lock {
unsigned int mandatory :1,
blocked :1;
uint64_t size;
+ int fd;
int id;
};
+struct lock_tnode {
+ dev_t dev;
+ ino_t inode;
+
+ struct list_head chain;
+};
+
+static int lock_tnode_compare(const void *a, const void *b)
+{
+ struct lock_tnode *anode = ((struct lock_tnode *)a);
+ struct lock_tnode *bnode = ((struct lock_tnode *)b);
+
+ if (anode->dev > bnode->dev)
+ return 1;
+ else if (anode->dev < bnode->dev)
+ return -1;
+
+ if (anode->inode > bnode->inode)
+ return 1;
+ else if (anode->inode < bnode->inode)
+ return -1;
+
+ return 0;
+}
+
+static void add_to_tree(void *troot, struct lock *l)
+{
+ struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
+ struct lock_tnode **head = tfind(&tmp, troot, lock_tnode_compare);
+ struct lock_tnode *new_head;
+
+ if (head) {
+ list_add_tail(&l->locks, &(*head)->chain);
+ return;
+ }
+
+ new_head = xmalloc(sizeof(*new_head));
+ new_head->dev = l->dev;
+ new_head->inode = l->inode;
+ INIT_LIST_HEAD(&new_head->chain);
+ if (tsearch(new_head, troot, lock_tnode_compare) == NULL)
+ errx(EXIT_FAILURE, _("failed to allocate memory"));
+
+ list_add_tail(&l->locks, &new_head->chain);
+}
+
static void rem_lock(struct lock *lock)
{
if (!lock)
@@ -170,13 +221,16 @@ static char *get_filename_sz(ino_t inode, pid_t lock_pid, size_t *size)
struct stat sb;
struct dirent *dp;
DIR *dirp;
- size_t len;
+ size_t sz;
int fd;
- char path[PATH_MAX], sym[PATH_MAX], *ret = NULL;
+ char path[PATH_MAX] = { 0 },
+ sym[PATH_MAX] = { 0 }, *ret = NULL;
*size = 0;
- memset(path, 0, sizeof(path));
- memset(sym, 0, sizeof(sym));
+
+ if (lock_pid < 0)
+ /* pid could be -1 for OFD locks */
+ return NULL;
/*
* We know the pid so we don't have to
@@ -187,16 +241,14 @@ static char *get_filename_sz(ino_t inode, pid_t lock_pid, size_t *size)
if (!(dirp = opendir(path)))
return NULL;
- if ((len = strlen(path)) >= (sizeof(path) - 2))
+ if ((sz = strlen(path)) >= (sizeof(path) - 2))
goto out;
if ((fd = dirfd(dirp)) < 0 )
goto out;
- while ((dp = readdir(dirp))) {
- if (!strcmp(dp->d_name, ".") ||
- !strcmp(dp->d_name, ".."))
- continue;
+ while ((dp = xreaddir(dirp))) {
+ ssize_t len;
errno = 0;
@@ -237,99 +289,260 @@ static ino_t get_dev_inode(char *str, dev_t *dev)
return inum;
}
-static int get_local_locks(struct list_head *locks)
+struct override_info {
+ pid_t pid;
+ const char *cmdname;
+};
+
+static bool is_holder(struct lock *l, struct lock *m)
{
- int i;
- FILE *fp;
- char buf[PATH_MAX], *tok = NULL;
- size_t sz;
- struct lock *l;
+ return (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);
+}
- if (!(fp = fopen(_PATH_PROC_LOCKS, "r")))
- return -1;
+static void patch_lock(struct lock *l, void *fallback)
+{
+ struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
+ struct lock_tnode **head = tfind(&tmp, fallback, lock_tnode_compare);
+ struct list_head *p;
- while (fgets(buf, sizeof(buf), fp)) {
+ if (!head)
+ return;
- l = xcalloc(1, sizeof(*l));
- INIT_LIST_HEAD(&l->locks);
+ list_for_each(p, &(*head)->chain) {
+ struct lock *m = list_entry(p, struct lock, locks);
+ if (is_holder(l, m)) {
+ /* size and id can be ignored. */
+ l->pid = m->pid;
+ l->cmdname = xstrdup(m->cmdname);
+ break;
+ }
+ }
+}
- for (tok = strtok(buf, " "), i = 0; tok;
- tok = strtok(NULL, " "), i++) {
+static void add_to_list(void *locks, struct lock *l)
+{
+ list_add(&l->locks, locks);
+}
- /*
- * /proc/locks has *exactly* 8 "blocks" of text
- * separated by ' ' - check <kernel>/fs/locks.c
- */
- switch (i) {
- case 0: /* ID: */
+static struct lock *get_lock(char *buf, struct override_info *oinfo, void *fallback)
+{
+ int i;
+ char *tok = NULL;
+ size_t sz;
+ struct lock *l = xcalloc(1, sizeof(*l));
+ INIT_LIST_HEAD(&l->locks);
+ l->fd = -1;
+
+ bool cmdname_unknown = false;
+
+ for (tok = strtok(buf, " "), i = 0; tok;
+ tok = strtok(NULL, " "), i++) {
+
+ /*
+ * /proc/locks has *exactly* 8 "blocks" of text
+ * separated by ' ' - check <kernel>/fs/locks.c
+ */
+ switch (i) {
+ case 0: /* 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 */
- l->blocked = 1;
- i--;
- } else
- l->type = xstrdup(tok);
- break;
+ }
+ break;
+ case 1: /* posix, flock, etc */
+ if (strcmp(tok, "->") == 0) { /* optional field */
+ l->blocked = 1;
+ i--;
+ } else
+ l->type = xstrdup(tok);
+ break;
- case 2: /* is this a mandatory lock? other values are advisory or noinode */
- l->mandatory = *tok == 'M' ? 1 : 0;
- break;
- case 3: /* lock mode */
- l->mode = xstrdup(tok);
- break;
+ case 2: /* is this a mandatory lock? other values are advisory or noinode */
+ l->mandatory = *tok == 'M' ? 1 : 0;
+ break;
+ case 3: /* lock mode */
+ l->mode = xstrdup(tok);
+ break;
- case 4: /* PID */
- /*
- * If user passed a pid we filter it later when adding
- * to the list, no need to worry now. OFD locks use -1 PID.
- */
+ case 4: /* PID */
+ /*
+ * If user passed a pid we filter it later when adding
+ * to the list, no need to worry now. OFD locks use -1 PID.
+ */
+ 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 = xstrdup(_("(unknown)"));
+ if (!l->cmdname) {
+ l->cmdname = NULL;
+ cmdname_unknown = true;
+ }
} else
- l->cmdname = xstrdup(_("(undefined)"));
- break;
+ l->cmdname = NULL;
+ }
+ break;
- case 5: /* device major:minor and inode number */
- l->inode = get_dev_inode(tok, &l->dev);
- break;
+ case 5: /* device major:minor and inode number */
+ l->inode = get_dev_inode(tok, &l->dev);
+ break;
- case 6: /* start */
- l->start = !strcmp(tok, "EOF") ? 0 :
- strtou64_or_err(tok, _("failed to parse start"));
- break;
+ case 6: /* start */
+ l->start = !strcmp(tok, "EOF") ? 0 :
+ strtou64_or_err(tok, _("failed to parse start"));
+ break;
- case 7: /* end */
- /* replace '\n' character */
- tok[strlen(tok)-1] = '\0';
- l->end = !strcmp(tok, "EOF") ? 0 :
- strtou64_or_err(tok, _("failed to parse end"));
- break;
- default:
- break;
- }
+ case 7: /* end */
+ /* replace '\n' character */
+ tok[strlen(tok)-1] = '\0';
+ l->end = !strcmp(tok, "EOF") ? 0 :
+ strtou64_or_err(tok, _("failed to parse end"));
+ break;
+ default:
+ break;
}
+ }
+
+ 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 */
+ if (!l->path && no_inaccessible) {
+ rem_lock(l);
+ return NULL;
+ }
- l->path = get_filename_sz(l->inode, l->pid, &sz);
+ if (!l->path) {
+ /* probably no permission to peek into l->pid's path */
+ l->path = get_fallback_filename(l->dev);
+ l->size = 0;
+ } else
+ l->size = sz;
- /* no permissions -- ignore */
- if (!l->path && no_inaccessible) {
- rem_lock(l);
+ return l;
+}
+
+static int get_pid_lock(void *locks, void (*add_lock)(void *, struct lock *), FILE *fp,
+ pid_t pid, const char *cmdname, int fd)
+{
+ 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) {
+ add_lock(locks, l);
+ l->fd = fd;
}
+ /* no break here.
+ Multiple recode locks can be taken via one fd. */
+ }
- if (!l->path) {
- /* probably no permission to peek into l->pid's path */
- l->path = get_fallback_filename(l->dev);
- l->size = 0;
- } else
- l->size = sz;
+ return 0;
+}
- list_add(&l->locks, locks);
+static int get_pid_locks(void *locks, void (*add_lock)(void *, struct lock *), 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, add_lock, fdinfo, pid, cmdname, (int)num);
+ fclose(fdinfo);
+ }
+
+ return rc;
+}
+
+static int get_pids_locks(void *locks, void (*add_lock)(void *, struct lock *))
+{
+ 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, add_lock, pc, pid, cmdname);
+ }
+
+ closedir(dir);
+ ul_unref_path(pc);
+
+ return rc;
+}
+
+static int get_proc_locks(void *locks, void (*add_lock)(void *, struct lock *), void *fallback)
+{
+ FILE *fp;
+ char buf[PATH_MAX];
+
+ if (!(fp = fopen(_PATH_PROC_LOCKS, "r")))
+ return -1;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ struct lock *l = get_lock(buf, NULL, fallback);
+ if (l)
+ add_lock(locks, l);
}
fclose(fp);
@@ -362,7 +575,7 @@ static inline int get_column_id(int num)
}
-static inline struct colinfo *get_column_info(unsigned num)
+static inline const struct colinfo *get_column_info(unsigned num)
{
return &infos[ get_column_id(num) ];
}
@@ -381,7 +594,13 @@ static pid_t get_blocker(int id, struct list_head *locks)
return 0;
}
-static void add_scols_line(struct libscols_table *table, struct lock *l, struct list_head *locks)
+static void xstrcoholder(char **str, struct lock *l)
+{
+ xstrfappend(str, "%d,%s,%d",
+ l->pid, l->cmdname, l->fd);
+}
+
+static void add_scols_line(struct libscols_table *table, struct lock *l, struct list_head *locks, void *pid_locks)
{
size_t i;
struct libscols_line *line;
@@ -450,6 +669,28 @@ static void add_scols_line(struct libscols_table *table, struct lock *l, struct
get_blocker(l->id, locks) : 0;
if (bl)
xasprintf(&str, "%d", (int) bl);
+ break;
+ }
+ case COL_HOLDERS:
+ {
+ struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
+ struct lock_tnode **head = tfind(&tmp, pid_locks, lock_tnode_compare);
+ struct list_head *p;
+
+ if (!head)
+ break;
+
+ list_for_each(p, &(*head)->chain) {
+ struct lock *m = list_entry(p, struct lock, locks);
+
+ if (!is_holder(l, m))
+ continue;
+
+ if (str)
+ xstrputc(&str, '\n');
+ xstrcoholder(&str, m);
+ }
+ break;
}
default:
break;
@@ -460,11 +701,52 @@ static void add_scols_line(struct libscols_table *table, struct lock *l, struct
}
}
-static int show_locks(struct list_head *locks)
+static void rem_locks(struct list_head *locks)
+{
+ struct list_head *p, *pnext;
+
+ /* destroy the list */
+ list_for_each_safe(p, pnext, locks) {
+ struct lock *l = list_entry(p, struct lock, locks);
+ rem_lock(l);
+ }
+}
+
+static void rem_tnode(void *node)
+{
+ struct lock_tnode *tnode = node;
+
+ rem_locks(&tnode->chain);
+ free(node);
+}
+
+static int get_json_type_for_column(int column_id, int representing_in_bytes)
+{
+ switch (column_id) {
+ case COL_SIZE:
+ if (!representing_in_bytes)
+ return SCOLS_JSON_STRING;
+ /* fallthrough */
+ case COL_PID:
+ case COL_START:
+ case COL_END:
+ case COL_BLOCKER:
+ case COL_INODE:
+ return SCOLS_JSON_NUMBER;
+ case COL_M:
+ return SCOLS_JSON_BOOLEAN;
+ case COL_HOLDERS:
+ return SCOLS_JSON_ARRAY_STRING;
+ default:
+ return SCOLS_JSON_STRING;
+ }
+}
+
+static int show_locks(struct list_head *locks, pid_t target_pid, void *pid_locks)
{
int rc = 0;
size_t i;
- struct list_head *p, *pnext;
+ struct list_head *p;
struct libscols_table *table;
table = scols_new_table();
@@ -480,34 +762,24 @@ static int show_locks(struct list_head *locks)
for (i = 0; i < ncolumns; i++) {
struct libscols_column *cl;
- struct colinfo *col = get_column_info(i);
+ const struct colinfo *col = get_column_info(i);
cl = scols_table_new_column(table, col->name, col->whint, col->flags);
if (!cl)
err(EXIT_FAILURE, _("failed to allocate output column"));
+ if (col->flags & SCOLS_FL_WRAP) {
+ scols_column_set_wrapfunc(cl,
+ scols_wrapnl_chunksize,
+ scols_wrapnl_nextchunk,
+ NULL);
+ scols_column_set_safechars(cl, "\n");
+ }
+
if (json) {
int id = get_column_id(i);
-
- switch (id) {
- case COL_SIZE:
- if (!bytes)
- break;
- /* fallthrough */
- case COL_PID:
- case COL_START:
- case COL_END:
- case COL_BLOCKER:
- case COL_INODE:
- scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
- break;
- case COL_M:
- scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
- break;
- default:
- scols_column_set_json_type(cl, SCOLS_JSON_STRING);
- break;
- }
+ int json_type = get_json_type_for_column(id, bytes);
+ scols_column_set_json_type(cl, json_type);
}
}
@@ -516,16 +788,10 @@ static int show_locks(struct list_head *locks)
list_for_each(p, locks) {
struct lock *l = list_entry(p, struct lock, locks);
- if (pid && pid != l->pid)
+ if (target_pid && target_pid != l->pid)
continue;
- add_scols_line(table, l, locks);
- }
-
- /* destroy the list */
- list_for_each_safe(p, pnext, locks) {
- struct lock *l = list_entry(p, struct lock, locks);
- rem_lock(l);
+ add_scols_line(table, l, locks, pid_locks);
}
scols_print_table(table);
@@ -537,7 +803,6 @@ static int show_locks(struct list_head *locks)
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
- size_t i;
fputs(USAGE_HEADER, out);
@@ -552,29 +817,48 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -J, --json use JSON output format\n"), out);
fputs(_(" -i, --noinaccessible ignore locks without read permissions\n"), out);
fputs(_(" -n, --noheadings don't print headings\n"), out);
- fputs(_(" -o, --output <list> define which output columns to use\n"), out);
+ fputs(_(" -o, --output <list> output columns (see --list-columns)\n"), out);
fputs(_(" --output-all output all columns\n"), out);
fputs(_(" -p, --pid <pid> display only locks held by this process\n"), out);
fputs(_(" -r, --raw use the raw output format\n"), out);
fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(24));
+ fputs(_(" -H, --list-columns list the available columns\n"), out);
+ fprintf(out, USAGE_HELP_OPTIONS(24));
+ fprintf(out, USAGE_MAN_TAIL("lslocks(8)"));
- fputs(USAGE_COLUMNS, out);
+ exit(EXIT_SUCCESS);
+}
- for (i = 0; i < ARRAY_SIZE(infos); i++)
- fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
+static void __attribute__((__noreturn__)) list_colunms(void)
+{
+ struct libscols_table *col_tb = xcolumn_list_table_new(
+ "lslocks-columns", stdout, raw, json);
+
+ for (size_t i = 0; i < ARRAY_SIZE(infos); i++) {
+ if (i != COL_SIZE) {
+ int json_type = get_json_type_for_column(i, bytes);
+ xcolumn_list_table_append_line(col_tb, infos[i].name,
+ json_type, NULL,
+ _(infos[i].help));
+ } else
+ xcolumn_list_table_append_line(col_tb, infos[i].name,
+ -1, "<string|number>",
+ _(infos[i].help));
+ }
- printf(USAGE_MAN_TAIL("lslocks(8)"));
+ scols_print_table(col_tb);
+ scols_unref_table(col_tb);
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
- int c, rc = 0;
- struct list_head locks;
+ int c, rc = 0, collist = 0;
+ struct list_head proc_locks;
+ void *pid_locks = NULL;
char *outarg = NULL;
enum {
OPT_OUTPUT_ALL = CHAR_MAX + 1
@@ -591,6 +875,7 @@ int main(int argc, char *argv[])
{ "noheadings", no_argument, NULL, 'n' },
{ "raw", no_argument, NULL, 'r' },
{ "noinaccessible", no_argument, NULL, 'i' },
+ { "list-columns", no_argument, NULL, 'H' },
{ NULL, 0, NULL, 0 }
};
@@ -599,13 +884,15 @@ int main(int argc, char *argv[])
{ 0 }
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ pid_t target_pid = 0;
+
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
close_stdout_atexit();
while ((c = getopt_long(argc, argv,
- "biJp:o:nruhV", long_opts, NULL)) != -1) {
+ "biJp:o:nruhVH", long_opts, NULL)) != -1) {
err_exclusive_options(c, long_opts, excl, excl_st);
@@ -620,7 +907,7 @@ int main(int argc, char *argv[])
json = 1;
break;
case 'p':
- pid = strtos32_or_err(optarg, _("invalid PID argument"));
+ target_pid = strtos32_or_err(optarg, _("invalid PID argument"));
break;
case 'o':
outarg = optarg;
@@ -639,6 +926,9 @@ int main(int argc, char *argv[])
disable_columns_truncate();
break;
+ case 'H':
+ collist = 1;
+ break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
@@ -648,7 +938,10 @@ int main(int argc, char *argv[])
}
}
- INIT_LIST_HEAD(&locks);
+ if (collist)
+ list_colunms(); /* print end exit */
+
+ INIT_LIST_HEAD(&proc_locks);
if (!ncolumns) {
/* default columns */
@@ -669,10 +962,18 @@ int main(int argc, char *argv[])
scols_init_debug(0);
- rc = get_local_locks(&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, add_to_tree);
+ rc = get_proc_locks(&proc_locks, add_to_list, &pid_locks);
+
+ if (!rc && !list_empty(&proc_locks))
+ rc = show_locks(&proc_locks, target_pid, &pid_locks);
- if (!rc && !list_empty(&locks))
- rc = show_locks(&locks);
+ tdestroy(pid_locks, rem_tnode);
+ rem_locks(&proc_locks);
mnt_unref_table(tab);
return rc;