diff options
Diffstat (limited to 'misc-utils/lsfd-file.c')
-rw-r--r-- | misc-utils/lsfd-file.c | 400 |
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, +}; |