From: Masatake YAMATO Date: Wed, 1 Nov 2023 15:05:12 +0000 (+0900) Subject: lsfd: utilize /proc/tty/drivers for filling SOURCE column of tty devices X-Git-Tag: v2.40-rc1~165^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f5bc75bff728036d140e9fdd54594166437f5114;p=thirdparty%2Futil-linux.git lsfd: utilize /proc/tty/drivers for filling SOURCE column of tty devices The original code referenced /proc/devices using major numbers as keys to obtain the name of the device driver corresponding to a file descriptor. However, it turnes out that some drivers share a same major number; there are cases that a key is not unique. Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS ... About tty-related devices, /proc/tty/drivers is better information source; both major and minor numbers can be used as keys: /dev/tty /dev/tty 5 0 system:/dev/tty /dev/console /dev/console 5 1 system:console /dev/ptmx /dev/ptmx 5 2 system /dev/vc/0 /dev/vc/0 4 0 system:vtmaster usbserial /dev/ttyUSB 188 0-511 serial dbc_serial /dev/ttyDBC 242 0-63 serial serial /dev/ttyS 4 64-95 serial pty_slave /dev/pts 136 0-1048575 pty:slave pty_master /dev/ptm 128 0-1048575 pty:master unknown /dev/tty 4 1-63 console This commit extracts the second column of the /proc/tty/drivers as driver names after removing "/dev": tty, console, ptmx, ... With the orinal code: # lsfd -n -p 1789 -Q '(FD == 25)' systemd-logind 1789 root 25 rw- CHR /dev/vc/0:6 432 25 /dev/tty6 --------------------------------------------^^^^^^^^^^^ With this commit: # ./lsfd -n -p 1789 -Q '(FD == 25)' systemd-logind 1789 root 25 rw---- CHR tty:6 432 25 /dev/tty6 ----------------------------------------------^^^^^ Signed-off-by: Masatake YAMATO --- diff --git a/misc-utils/lsfd-cdev.c b/misc-utils/lsfd-cdev.c index 70751bab97..3cb133fb08 100644 --- a/misc-utils/lsfd-cdev.c +++ b/misc-utils/lsfd-cdev.c @@ -22,6 +22,7 @@ #include "lsfd.h" static struct list_head miscdevs; +static struct list_head ttydrvs; struct miscdev { struct list_head miscdevs; @@ -29,6 +30,13 @@ struct miscdev { char *name; }; +struct ttydrv { + struct list_head ttydrvs; + unsigned long major; + unsigned long minor_start, minor_end; + char *name; +}; + struct cdev { struct file file; const char *devdrv; @@ -38,7 +46,7 @@ struct cdev { struct cdev_ops { const struct cdev_ops *parent; - bool (*probe)(const struct cdev *); + bool (*probe)(struct cdev *); char * (*get_name)(struct cdev *); bool (*fill_column)(struct proc *, struct cdev *, @@ -139,22 +147,97 @@ static void read_misc(struct list_head *miscdevs_list, FILE *misc_fp) } } +#define TTY_DRIVERS_LINE_LEN0 1023 +#define TTY_DRIVERS_LINE_LEN (TTY_DRIVERS_LINE_LEN0 + 1) +static struct ttydrv *new_ttydrv(unsigned int major, + unsigned int minor_start, unsigned int minor_end, + const char *name) +{ + struct ttydrv *ttydrv = xmalloc(sizeof(*ttydrv)); + + INIT_LIST_HEAD(&ttydrv->ttydrvs); + + ttydrv->major = major; + ttydrv->minor_start = minor_start; + ttydrv->minor_end = minor_end; + ttydrv->name = xstrdup(name); + + return ttydrv; +} + +static void free_ttydrv(struct ttydrv *ttydrv) +{ + free(ttydrv->name); + free(ttydrv); +} + +static struct ttydrv *read_ttydrv(const char *line) +{ + const char *p; + char name[TTY_DRIVERS_LINE_LEN]; + unsigned long major; + unsigned long minor_range[2]; + + p = strchr(line, ' '); + if (p == NULL) + return NULL; + + p = strstr(p, "/dev/"); + if (p == NULL) + return NULL; + p += (sizeof("/dev/") - 1); /* Ignore the last null byte. */ + + if (sscanf(p, "%" stringify_value(TTY_DRIVERS_LINE_LEN0) "[^ ]", name) != 1) + return NULL; + + p += strlen (name); + if (sscanf(p, " %lu %lu-%lu ", &major, + minor_range, minor_range + 1) != 3) { + if (sscanf(p, " %lu %lu ", &major, minor_range) == 2) + minor_range[1] = minor_range[0]; + else + return NULL; + } + + return new_ttydrv(major, minor_range[0], minor_range[1], name); +} + +static void read_tty_drivers(struct list_head *ttydrvs_list, FILE *ttydrvs_fp) +{ + char line[TTY_DRIVERS_LINE_LEN]; + + while (fgets(line, sizeof(line), ttydrvs_fp)) { + struct ttydrv *ttydrv = read_ttydrv(line); + if (ttydrv) + list_add_tail(&ttydrv->ttydrvs, ttydrvs_list); + } +} + static void cdev_class_initialize(void) { FILE *misc_fp; + FILE *ttydrvs_fp; INIT_LIST_HEAD(&miscdevs); + INIT_LIST_HEAD(&ttydrvs); misc_fp = fopen("/proc/misc", "r"); if (misc_fp) { read_misc(&miscdevs, misc_fp); fclose(misc_fp); } + + ttydrvs_fp = fopen("/proc/tty/drivers", "r"); + if (ttydrvs_fp) { + read_tty_drivers(&ttydrvs, ttydrvs_fp); + fclose(ttydrvs_fp); + } } static void cdev_class_finalize(void) { list_free(&miscdevs, struct miscdev, miscdevs, free_miscdev); + list_free(&ttydrvs, struct ttydrv, ttydrvs, free_ttydrv); } const char *get_miscdev(unsigned long minor) @@ -168,10 +251,27 @@ const char *get_miscdev(unsigned long minor) return NULL; } +static const struct ttydrv *get_ttydrv(unsigned long major, + unsigned long minor) +{ + struct list_head *c; + + list_for_each(c, &ttydrvs) { + struct ttydrv *ttydrv = list_entry(c, struct ttydrv, ttydrvs); + if (ttydrv->major == major + && ttydrv->minor_start <= minor + && minor <= ttydrv->minor_end) + return ttydrv; + } + + return NULL; +} + + /* * generic (fallback implementation) */ -static bool cdev_generic_probe(const struct cdev *cdev __attribute__((__unused__))) { +static bool cdev_generic_probe(struct cdev *cdev __attribute__((__unused__))) { return true; } @@ -210,7 +310,7 @@ static struct cdev_ops cdev_generic_ops = { /* * misc device driver */ -static bool cdev_misc_probe(const struct cdev *cdev) { +static bool cdev_misc_probe(struct cdev *cdev) { return cdev->devdrv && strcmp(cdev->devdrv, "misc") == 0; } @@ -254,7 +354,7 @@ static struct cdev_ops cdev_misc_ops = { /* * tun devcie driver */ -static bool cdev_tun_probe(const struct cdev *cdev) +static bool cdev_tun_probe(struct cdev *cdev) { const char *miscdev; @@ -325,13 +425,72 @@ static struct cdev_ops cdev_tun_ops = { .handle_fdinfo = cdev_tun_handle_fdinfo, }; +/* + * tty devices + */ +struct ttydata { + const struct ttydrv *drv; +}; + +static bool cdev_tty_probe(struct cdev *cdev) { + const struct ttydrv *ttydrv = get_ttydrv(major(cdev->file.stat.st_rdev), + minor(cdev->file.stat.st_rdev)); + struct ttydata *data; + + if (!ttydrv) + return false; + + data = xmalloc(sizeof(struct ttydata)); + data->drv = ttydrv; + cdev->cdev_data = data; + + return true; +} + +static void cdev_tty_free(const struct cdev *cdev) +{ + if (cdev->cdev_data) + free(cdev->cdev_data); +} + +static bool cdev_tty_fill_column(struct proc *proc __attribute__((__unused__)), + struct cdev *cdev, + struct libscols_line *ln __attribute__((__unused__)), + int column_id, + size_t column_index __attribute__((__unused__)), + char **str) +{ + struct file *file = &cdev->file; + struct ttydata *data = cdev->cdev_data; + + switch(column_id) { + case COL_SOURCE: + if (data->drv->minor_start == data->drv->minor_end) + *str = xstrdup(data->drv->name); + else + xasprintf(str, "%s:%u", data->drv->name, + minor(file->stat.st_rdev)); + return true; + default: + return false; + } +} + +static struct cdev_ops cdev_tty_ops = { + .parent = &cdev_generic_ops, + .probe = cdev_tty_probe, + .free = cdev_tty_free, + .fill_column = cdev_tty_fill_column, +}; + static const struct cdev_ops *cdev_ops[] = { &cdev_tun_ops, &cdev_misc_ops, + &cdev_tty_ops, &cdev_generic_ops /* This must be at the end. */ }; -static const struct cdev_ops *cdev_probe(const struct cdev *cdev) +static const struct cdev_ops *cdev_probe(struct cdev *cdev) { const struct cdev_ops *r = NULL;