configure
cscope.out
depcomp
+errnos.h
GPATH
GRTAGS
GTAGS
bashcompletions += ['lsblk']
endif
+errnos_h = custom_target('errnos.h',
+ input : 'tools/all_errnos',
+ output : 'errnos.h',
+ command : ['tools/all_errnos', cc.cmd_array()]
+)
+
mq_libs = []
mq_libs += cc.find_library('rt', required : true)
exe = executable(
'lsfd',
- lsfd_sources,
+ lsfd_sources, errnos_h,
include_directories : includes,
link_with : [lib_common,
lib_smartcols],
endif
if BUILD_LSFD
+
+misc-utils/lsfd-file.c: errnos.h
+
+errnos.h: $(top_srcdir)/tools/all_errnos
+ @echo ' GEN $@'
+ @$(top_srcdir)/tools/all_errnos $(CC) $(CFLAGS)
+
+-include errnos.h.deps
+CLEANFILES += errnos.h errnos.h.deps
+
bin_PROGRAMS += lsfd
MANPAGES += misc-utils/lsfd.1
dist_noinst_DATA += misc-utils/lsfd.1.adoc
.fill_column = abst_fill_column,
};
+/*
+ * Error classes
+ */
+
+/* get_errno_name() --- the private replacement of strerrorname_np(3).
+ * Some platforms don't have strerrorname_np.
+ *
+ * Mainly copied from misc-utils/enosys.c.
+ */
+struct errno_s {
+ const char *const name;
+ long number;
+};
+
+static const struct errno_s errnos[] = {
+#define UL_ERRNO(name, nr) { name, nr },
+#include "errnos.h"
+#undef UL_ERRNO
+};
+
+static const char *get_errno_name(int ern)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(errnos); i ++) {
+ if (errnos[i].number == ern)
+ return errnos[i].name;
+ }
+ return NULL;
+}
+
+static bool error_fill_column(struct proc *proc __attribute__((__unused__)),
+ struct file *file __attribute__((__unused__)),
+ struct libscols_line *ln,
+ int column_id,
+ size_t column_index)
+{
+ char *str = NULL;
+ const char *ename;
+
+ switch(column_id) {
+ case COL_TYPE:
+ if (scols_line_set_data(ln, column_index, "ERROR"))
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ case COL_SOURCE:
+ ename = get_errno_name(file->error.number);
+ if (ename)
+ xasprintf(&str, "%s:%s",
+ file->error.syscall, ename);
+ else
+ xasprintf(&str, "%s:unknown(%d)",
+ file->error.syscall, file->error.number);
+ if (scols_line_refer_data(ln, column_index, str))
+ err(EXIT_FAILURE, _("failed to add output data"));
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct file_class error_class = {
+ .super = &abst_class,
+ .size = sizeof(struct file),
+ .fill_column = error_fill_column,
+};
+
+static void init_error_content(struct file *file)
+{
+ file->is_error = 1;
+}
+
+static bool readlink_error_fill_column(struct proc *proc __attribute__((__unused__)),
+ struct file *file __attribute__((__unused__)),
+ struct libscols_line *ln __attribute__((__unused__)),
+ int column_id,
+ size_t column_index __attribute__((__unused__)))
+{
+ switch(column_id) {
+ case COL_NAME:
+ case COL_KNAME:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct file_class readlink_error_class = {
+ .super = &error_class,
+ .size = sizeof(struct file),
+ .initialize_content = init_error_content,
+ .fill_column = readlink_error_fill_column,
+};
+
+const struct file_class stat_error_class = {
+ .super = &error_class,
+ .size = sizeof(struct file),
+ .initialize_content = init_error_content,
+};
+
/*
* Concrete file class
*/
SOURCE <``string``>::
File system, partition, or device containing the file.
+For the association having ERROR as the value for _TYPE_ coulumn, *lsfd*
+fills this column with _syscall_:_errno_.
STTYPE <``string``>::
Raw file types returned from *stat*(2): BLK, CHR, DIR, FIFO, LINK, REG, SOCK, or UNKN.
Cooked version of _STTYPE_. It is same as _STTYPE_ with exceptions.
For _SOCK_, print the value for _SOCK.PROTONAME_.
For _UNKN_, print the value for _AINODECLASS_ if _SOURCE_ is `anon_inodefs`.
++
+If *lsfd* gets an error when calling a syscall to know about a target
+file descriptor, *lsfd* fills this column for it with ERROR.
UDP.LADDR <``string``>::
Local IP address and local UDP port.
return file;
}
+static struct file *new_readlink_error_file(struct proc *proc, int error_no, int association)
+{
+ struct file *file;
+
+ file = xcalloc(1, readlink_error_class.size);
+ file->class = &readlink_error_class;
+
+ file->proc = proc;
+
+ INIT_LIST_HEAD(&file->files);
+ list_add_tail(&file->files, &proc->files);
+
+ file->error.syscall = "readlink";
+ file->error.number = error_no;
+ file->association = association;
+ file->name = NULL;
+
+ return file;
+}
+
+static struct file *new_stat_error_file(struct proc *proc, const char *name, int error_no, int association)
+{
+ struct file *file;
+
+ file = xcalloc(1, stat_error_class.size);
+ file->class = &stat_error_class;
+
+ file->proc = proc;
+
+ INIT_LIST_HEAD(&file->files);
+ list_add_tail(&file->files, &proc->files);
+
+ file->error.syscall = "stat";
+ file->error.number = error_no;
+ file->association = association;
+ file->name = xstrdup(name);
+
+ return file;
+}
+
static struct file *copy_file(struct file *old, int new_association)
{
struct file *file = xcalloc(1, old->class->size);
struct file *f, *prev;
if (ul_path_readlink(pc, sym, sizeof(sym), name) < 0)
- return NULL;
-
+ f = new_readlink_error_file(proc, errno, assoc);
/* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
* more than once. Let's try to reuse the previous file if the real
* path is the same to save stat() call.
*/
- prev = list_last_entry(&proc->files, struct file, files);
- if (prev && prev->name && strcmp(prev->name, sym) == 0)
+ else if ((prev = list_last_entry(&proc->files, struct file, files))
+ && (!prev->is_error)
+ && prev->name && strcmp(prev->name, sym) == 0)
f = copy_file(prev, assoc);
+ else if (ul_path_stat(pc, &sb, 0, name) < 0)
+ f = new_stat_error_file(proc, sym, errno, assoc);
else {
- const struct file_class *class;
-
- if (ul_path_stat(pc, &sb, 0, name) < 0)
- return NULL;
+ const struct file_class *class = stat2class(&sb);
- class = stat2class(&sb);
if (sockets_only
/* A nsfs file is not a socket but the nsfs file can
* be used as a entry point to collect information from
file_init_content(f);
+ if (f->is_error)
+ return f;
+
if (is_association(f, NS_MNT))
proc->ns_mnt = f->stat.st_ino;
else if (is_association(f, NS_NET))
*/
prev = list_last_entry(&proc->files, struct file, files);
- if (prev && prev->stat.st_dev == devno && prev->stat.st_ino == ino)
+ if (prev && (!prev->is_error)
+ && prev->stat.st_dev == devno && prev->stat.st_ino == ino)
f = copy_file(prev, -assoc);
else if ((path = strchr(buf, '/'))) {
rtrim_whitespace((unsigned char *) path);
try_map_files:
snprintf(map_file, sizeof(map_file), "map_files/%"PRIx64"-%"PRIx64, start, end);
if (ul_path_readlink(pc, sym, sizeof(sym), map_file) < 0)
- return;
- if (ul_path_stat(pc, &sb, 0, map_file) < 0)
- return;
- f = new_file(proc, stat2class(&sb), &sb, sym, -assoc);
+ f = new_readlink_error_file(proc, errno, -assoc);
+ else if (ul_path_stat(pc, &sb, 0, map_file) < 0)
+ f = new_stat_error_file(proc, sym, errno, -assoc);
+ else
+ f = new_file(proc, stat2class(&sb), &sb, sym, -assoc);
}
if (modestr[0] == 'r')
const struct file_class *class;
int association;
char *name;
- struct stat stat;
+ union {
+ struct stat stat;
+ struct {
+ int number;
+ const char *syscall;
+ } error;
+ };
mode_t mode;
struct proc *proc;
uint8_t locked_read:1,
locked_write:1,
- multiplexed:1;
+ multiplexed:1,
+ is_error:1;
};
#define is_opened_file(_f) ((_f)->association >= 0)
const struct ipc_class *(*get_ipc_class)(struct file *file);
};
-extern const struct file_class abst_class, file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class,
+extern const struct file_class abst_class, readlink_error_class, stat_error_class,
+ file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class,
nsfs_file_class, mqueue_file_class;
/*
tools/asciidoctor-includetracker.rb \
tools/asciidoctor-unicodeconverter.rb \
\
+ tools/all_errnos \
tools/all_syscalls
--- /dev/null
+#!/bin/bash
+
+# Derrived from all_syscalls.
+
+set -e
+
+OUTPUT=errnos.h
+ERRNO_INCLUDES="
+#include <sys/errno.h>
+"
+
+trap 'rm $OUTPUT $OUTPUT.deps' ERR
+
+"$@" -MD -MF "$OUTPUT.deps" <<< "$ERRNO_INCLUDES" -dM -E - \
+ | gawk 'match($0, /^#[ \t]*define[ \t]*E([^ ]+)/, res) { print "UL_ERRNO(\"E" res[1] "\", E" res[1] ")" }' \
+ | sort \
+ > "$OUTPUT"