]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: utilize /proc/tty/drivers for filling SOURCE column of tty devices
authorMasatake YAMATO <yamato@redhat.com>
Wed, 1 Nov 2023 15:05:12 +0000 (00:05 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sat, 4 Nov 2023 08:03:05 +0000 (17:03 +0900)
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 <yamato@redhat.com>
misc-utils/lsfd-cdev.c

index 70751bab970070b98699195870f0cde570aa5940..3cb133fb0805b8a4b8fb16d6d2db7e6b9f54a10a 100644 (file)
@@ -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;