summaryrefslogtreecommitdiffstats
path: root/misc-utils/lsfd-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc-utils/lsfd-file.c')
-rw-r--r--misc-utils/lsfd-file.c400
1 files changed, 360 insertions, 40 deletions
diff --git a/misc-utils/lsfd-file.c b/misc-utils/lsfd-file.c
index bdaac3f..9b91462 100644
--- a/misc-utils/lsfd-file.c
+++ b/misc-utils/lsfd-file.c
@@ -32,19 +32,24 @@
# endif
#endif
#include <linux/sched.h>
+#include <sys/shm.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <mqueue.h> /* mq_open */
-#include "xalloc.h"
-#include "nls.h"
#include "buffer.h"
#include "idcache.h"
#include "strutils.h"
-#include "libsmartcols.h"
+#include "procfs.h"
#include "lsfd.h"
static struct idcache *username_cache;
+static size_t pagesize;
+
static const char *assocstr[N_ASSOCS] = {
[ASSOC_CWD] = "cwd",
[ASSOC_EXE] = "exe",
@@ -102,16 +107,58 @@ static uint64_t get_map_length(struct file *file)
{
uint64_t res = 0;
- if (is_association(file, SHM) || is_association(file, MEM)) {
- static size_t pagesize = 0;
+ if (is_association(file, SHM) || is_association(file, MEM))
+ res = (file->map_end - file->map_start) / pagesize;
- if (!pagesize)
- pagesize = getpagesize();
+ return res;
+}
- res = (file->map_end - file->map_start) / pagesize;
+void decode_source(char *buf, size_t bufsize,
+ unsigned int dev_major, unsigned int dev_minor,
+ enum decode_source_level level)
+{
+ if (bufsize == 0)
+ return;
+
+ buf[0] = '\0';
+
+ if (level & DECODE_SOURCE_FILESYS_BIT) {
+ if (dev_major == 0) {
+ const char *filesystem = get_nodev_filesystem(dev_minor);
+ if (filesystem) {
+ xstrncpy(buf, filesystem, bufsize);
+ return;
+ }
+ }
}
- return res;
+ if (level & DECODE_SOURCE_PARTITION_BIT) {
+ dev_t dev = makedev(dev_major, dev_minor);
+ const char *partition = get_partition(dev);
+ if (partition) {
+ xstrncpy(buf, partition, bufsize);
+ return;
+ }
+ }
+
+ if (level & DECODE_SOURCE_MAJMIN_BIT)
+ snprintf(buf, bufsize, "%u:%u",
+ dev_major,
+ dev_minor);
+}
+
+static char *strnrstr(const char *haystack, const char *needle, size_t needle_len)
+{
+ char *last = strstr(haystack, needle);
+ if (last == NULL)
+ return NULL;
+
+ do {
+ char *current = strstr(last + needle_len, needle);
+ if (current == NULL)
+ return last;
+ last = current;
+ } while (1);
}
static bool file_fill_column(struct proc *proc,
@@ -122,7 +169,7 @@ static bool file_fill_column(struct proc *proc,
{
char *str = NULL;
mode_t ftype;
- const char *partition;
+ char buf[BUFSIZ];
switch(column_id) {
case COL_COMMAND:
@@ -130,8 +177,22 @@ static bool file_fill_column(struct proc *proc,
&& scols_line_set_data(ln, column_index, proc->command))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
- case COL_KNAME:
case COL_NAME:
+ if (file->name && file->stat.st_nlink == 0) {
+ char *d = strnrstr(file->name, "(deleted)",
+ sizeof("(deleted)") - 1);
+ if (d) {
+ int r;
+ *d = '\0';
+ r = scols_line_set_data(ln, column_index, file->name);
+ *d = '(';
+ if (r)
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ }
+ }
+ /* FALL THROUGH */
+ case COL_KNAME:
if (file->name
&& scols_line_set_data(ln, column_index, file->name))
err(EXIT_FAILURE, _("failed to add output data"));
@@ -172,33 +233,27 @@ static bool file_fill_column(struct proc *proc,
int assoc = file->association * -1;
if (assoc >= N_ASSOCS)
return false; /* INTERNAL ERROR */
- xasprintf(&str, "%s", assocstr[assoc]);
+ str = xstrdup(assocstr[assoc]);
}
break;
case COL_INODE:
xasprintf(&str, "%llu", (unsigned long long)file->stat.st_ino);
break;
case COL_SOURCE:
- if (major(file->stat.st_dev) == 0) {
- const char *filesystem = get_nodev_filesystem(minor(file->stat.st_dev));
- if (filesystem) {
- xasprintf(&str, "%s", filesystem);
- break;
- }
- }
- /* FALL THROUGH */
+ decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
+ DECODE_SOURCE_FILESYS);
+ str = xstrdup(buf);
+ break;
case COL_PARTITION:
- partition = get_partition(file->stat.st_dev);
- if (partition) {
- str = xstrdup(partition);
- break;
- }
- /* FALL THROUGH */
+ decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
+ DECODE_SOURCE_PARTITION);
+ str = xstrdup(buf);
+ break;
case COL_DEV:
case COL_MAJMIN:
- xasprintf(&str, "%u:%u",
- major(file->stat.st_dev),
- minor(file->stat.st_dev));
+ decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
+ DECODE_SOURCE_MAJMIN);
+ str = xstrdup(buf);
break;
case COL_RDEV:
xasprintf(&str, "%u:%u",
@@ -242,6 +297,24 @@ static bool file_fill_column(struct proc *proc,
else
xasprintf(&str, "---");
break;
+ case COL_XMODE: {
+ char r, w, x;
+ char D = file->stat.st_nlink == 0? 'D': '-';
+ char L = file->locked.write? 'L'
+ :file->locked.read? 'l'
+ : '-';
+ char m = file->multiplexed? 'm': '-';
+
+ if (does_file_has_fdinfo_alike(file)) {
+ r = file->mode & S_IRUSR? 'r': '-';
+ w = file->mode & S_IWUSR? 'w': '-';
+ x = (is_mapped_file(file)
+ && file->mode & S_IXUSR)? 'x': '-';
+ } else
+ r = w = x = '-';
+ xasprintf(&str, "%c%c%c%c%c%c", r, w, x, D, L, m);
+ break;
+ }
case COL_POS:
xasprintf(&str, "%" PRIu64,
(does_file_has_fdinfo_alike(file))? file->pos: 0);
@@ -268,7 +341,7 @@ static bool file_fill_column(struct proc *proc,
break;
default:
return false;
- };
+ }
if (!str)
err(EXIT_FAILURE, _("failed to add output data"));
@@ -277,6 +350,40 @@ static bool file_fill_column(struct proc *proc,
return true;
}
+enum lock_mode {
+ LOCK_NONE,
+ READ_LOCK,
+ WRITE_LOCK,
+};
+
+static unsigned int parse_lock_line(const char *line)
+{
+ char mode[6] = {0};
+
+ /* Exapmles of lines:
+ ----------------------------------------------------
+ 1: FLOCK ADVISORY READ 2283292 fd:03:26219728 0 EOF
+ 1: FLOCK ADVISORY WRITE 2283321 fd:03:26219728 0 EOF
+ 1: POSIX ADVISORY READ 2283190 fd:03:26219728 0 0
+ 1: POSIX ADVISORY WRITE 2283225 fd:03:26219728 0 0
+ 1: OFDLCK ADVISORY READ -1 fd:03:26219728 0 0
+ 1: OFDLCK ADVISORY WRITE -1 fd:03:26219728 0 0
+ 1: LEASE ACTIVE WRITE 2328907 fd:03:26219472 0 EOF
+ 1: LEASE ACTIVE READ 2326777 fd:03:26219472 0 EOF
+ ---------------------------------------------------- */
+
+ if (sscanf(line, "%*d: %*s %*s %5s %*s", mode) != 1)
+ return LOCK_NONE;
+
+ if (strcmp(mode, "READ") == 0)
+ return READ_LOCK;
+
+ if (strcmp(mode, "WRITE") == 0)
+ return WRITE_LOCK;
+
+ return LOCK_NONE;
+}
+
static int file_handle_fdinfo(struct file *file, const char *key, const char* value)
{
int rc;
@@ -290,6 +397,16 @@ static int file_handle_fdinfo(struct file *file, const char *key, const char* va
} else if (strcmp(key, "mnt_id") == 0) {
rc = ul_strtou32(value, &file->mnt_id, 10);
+ } else if (strcmp(key, "lock") == 0) {
+ switch (parse_lock_line(value)) {
+ case READ_LOCK:
+ file->locked.read = 1;
+ break;
+ case WRITE_LOCK:
+ file->locked.write = 1;
+ break;
+ }
+ rc = 1;
} else
return 0; /* ignore -- unknown item */
@@ -304,11 +421,95 @@ static void file_free_content(struct file *file)
free(file->name);
}
+static unsigned long get_minor_for_sysvipc(void)
+{
+ int id;
+ void *start;
+
+ pid_t self = getpid();
+ struct path_cxt *pc = NULL;
+ char map_file[sizeof("map_files/0000000000000000-ffffffffffffffff")];
+
+ struct stat sb;
+ unsigned long m = 0;
+
+ id = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0600);
+ if (id == -1)
+ return 0;
+
+ start = shmat(id, NULL, SHM_RDONLY);
+ if (start == (void *) -1) {
+ shmctl(id, IPC_RMID, NULL);
+ return 0;
+ }
+
+ pc = ul_new_path(NULL);
+ if (!pc)
+ goto out;
+
+ if (procfs_process_init_path(pc, self) != 0)
+ goto out;
+
+ snprintf(map_file, sizeof(map_file),
+ "map_files/%lx-%lx", (long)start, (long)start + pagesize);
+ if (ul_path_stat(pc, &sb, 0, map_file) < 0)
+ goto out;
+
+ m = minor(sb.st_dev);
+ out:
+ if (pc)
+ ul_unref_path(pc);
+ shmdt(start);
+ shmctl(id, IPC_RMID, NULL);
+ return m;
+}
+
+static unsigned long get_minor_for_mqueue(void)
+{
+ mqd_t mq;
+ char mq_name[BUFSIZ];
+ struct mq_attr attr = {
+ .mq_maxmsg = 1,
+ .mq_msgsize = 1,
+ };
+
+ pid_t self = getpid();
+ struct stat sb;
+
+ snprintf(mq_name, sizeof(mq_name), "/.lsfd-mqueue-nodev-test:%d", self);
+ mq = mq_open(mq_name, O_CREAT|O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR, &attr);
+ if (mq < 0)
+ return 0;
+
+ if (fstat((int)mq, &sb) < 0) {
+ mq_close(mq);
+ mq_unlink(mq_name);
+ return 0;
+ }
+
+ mq_close(mq);
+ mq_unlink(mq_name);
+ return minor(sb.st_dev);
+}
+
static void file_class_initialize(void)
{
+ unsigned long m;
+
+ if (!pagesize)
+ pagesize = getpagesize();
+
username_cache = new_idcache();
if (!username_cache)
err(EXIT_FAILURE, _("failed to allocate UID cache"));
+
+ m = get_minor_for_sysvipc();
+ if (m)
+ add_nodev(m, "tmpfs");
+
+ m = get_minor_for_mqueue();
+ if (m)
+ add_nodev(m, "mqueue");
}
static void file_class_finalize(void)
@@ -373,25 +574,25 @@ static void init_nsfs_file_content(struct file *file)
int ns_fd;
int ns_type;
- if (is_association (file, NS_CGROUP))
+ if (is_association(file, NS_CGROUP))
nsfs_file->clone_type = CLONE_NEWCGROUP;
- else if (is_association (file, NS_IPC))
+ else if (is_association(file, NS_IPC))
nsfs_file->clone_type = CLONE_NEWIPC;
- else if (is_association (file, NS_MNT))
+ else if (is_association(file, NS_MNT))
nsfs_file->clone_type = CLONE_NEWNS;
- else if (is_association (file, NS_NET))
+ else if (is_association(file, NS_NET))
nsfs_file->clone_type = CLONE_NEWNET;
- else if (is_association (file, NS_PID)
- || is_association (file, NS_PID4C))
+ else if (is_association(file, NS_PID)
+ || is_association(file, NS_PID4C))
nsfs_file->clone_type = CLONE_NEWPID;
#ifdef CLONE_NEWTIME
- else if (is_association (file, NS_TIME)
- || is_association (file, NS_TIME4C))
+ else if (is_association(file, NS_TIME)
+ || is_association(file, NS_TIME4C))
nsfs_file->clone_type = CLONE_NEWTIME;
#endif
- else if (is_association (file, NS_USER))
+ else if (is_association(file, NS_USER))
nsfs_file->clone_type = CLONE_NEWUSER;
- else if (is_association (file, NS_UTS))
+ else if (is_association(file, NS_UTS))
nsfs_file->clone_type = CLONE_NEWUTS;
if (nsfs_file->clone_type != -1)
@@ -463,3 +664,122 @@ const struct file_class nsfs_file_class = {
.fill_column = nsfs_file_fill_column,
.handle_fdinfo = NULL,
};
+
+/*
+ * POSIX Mqueue
+ */
+struct mqueue_file {
+ struct file file;
+ struct ipc_endpoint endpoint;
+};
+
+struct mqueue_file_ipc {
+ struct ipc ipc;
+ ino_t ino;
+};
+
+bool is_mqueue_dev(dev_t dev)
+{
+ const char *fs = get_nodev_filesystem(minor(dev));
+
+ if (fs && (strcmp (fs, "mqueue") == 0))
+ return true;
+
+ return false;
+}
+
+static inline char *mqueue_file_xstrendpoint(struct file *file)
+{
+ char *str = NULL;
+ xasprintf(&str, "%d,%s,%d%c%c",
+ file->proc->pid, file->proc->command, file->association,
+ (file->mode & S_IRUSR)? 'r': '-',
+ (file->mode & S_IWUSR)? 'w': '-');
+ return str;
+}
+
+static bool mqueue_file_fill_column(struct proc *proc __attribute__((__unused__)),
+ struct file *file __attribute__((__unused__)),
+ struct libscols_line *ln,
+ int column_id,
+ size_t column_index)
+{
+ switch (column_id) {
+ case COL_TYPE:
+ if (scols_line_set_data(ln, column_index, "mqueue"))
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ case COL_ENDPOINTS: {
+ char *str = NULL;
+ struct mqueue_file *this = (struct mqueue_file *)file;
+ struct list_head *e;
+ foreach_endpoint(e, this->endpoint) {
+ char *estr;
+ struct mqueue_file *other = list_entry(e, struct mqueue_file,
+ endpoint.endpoints);
+ if (this == other)
+ continue;
+ if (str)
+ xstrputc(&str, '\n');
+ estr = mqueue_file_xstrendpoint(&other->file);
+ xstrappend(&str, estr);
+ free(estr);
+ }
+ if (!str)
+ return false;
+ if (scols_line_refer_data(ln, column_index, str))
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+static unsigned int mqueue_file_get_hash(struct file *file)
+{
+ return (unsigned int)(file->stat.st_ino % UINT_MAX);
+}
+
+static bool mqueue_file_is_suitable_ipc(struct ipc *ipc, struct file *file)
+{
+ return ((struct mqueue_file_ipc *)ipc)->ino == file->stat.st_ino;
+}
+
+static const struct ipc_class *mqueue_file_get_ipc_class(struct file *file __attribute__((__unused__)))
+{
+ static const struct ipc_class mqueue_file_ipc_class = {
+ .size = sizeof(struct mqueue_file_ipc),
+ .get_hash = mqueue_file_get_hash,
+ .is_suitable_ipc = mqueue_file_is_suitable_ipc,
+ };
+ return &mqueue_file_ipc_class;
+}
+
+static void init_mqueue_file_content(struct file *file)
+{
+ struct mqueue_file *mqueue_file = (struct mqueue_file *)file;
+ struct ipc *ipc;
+ unsigned int hash;
+
+ init_endpoint(&mqueue_file->endpoint);
+ ipc = get_ipc(file);
+ if (ipc)
+ goto link;
+
+ ipc = new_ipc(mqueue_file_get_ipc_class(file));
+ ((struct mqueue_file_ipc *)ipc)->ino = file->stat.st_ino;
+
+ hash = mqueue_file_get_hash(file);
+ add_ipc(ipc, hash);
+ link:
+ add_endpoint(&mqueue_file->endpoint, ipc);
+}
+
+const struct file_class mqueue_file_class = {
+ .super = &file_class,
+ .size = sizeof(struct mqueue_file),
+ .initialize_content = init_mqueue_file_content,
+ .fill_column = mqueue_file_fill_column,
+ .get_ipc_class = mqueue_file_get_ipc_class,
+};