#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <stdbool.h>
#include <libmount.h>
#include <libsmartcols.h>
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;
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++) {
*/
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 */
* 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 */
}
}
+ 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 */
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];
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);
}
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
}
INIT_LIST_HEAD(&proc_locks);
+ INIT_LIST_HEAD(&pid_locks);
if (!ncolumns) {
/* default columns */
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);