From 837c9eeccdc4bc99382394121bbb1b63eba2b866 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Wed, 1 Nov 2023 08:18:11 +0900 Subject: [PATCH 1/7] lsfd: make the order of calling finalize_* and initialize_* consistent Signed-off-by: Masatake YAMATO --- misc-utils/lsfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 862a0d4e0b2..0bb02e28210 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -2323,9 +2323,9 @@ int main(int argc, char *argv[]) /* cleanup */ delete(&ctl.procs, &ctl); - finalize_ipc_table(); finalize_devdrvs(); finalize_classes(); + finalize_ipc_table(); finalize_nodevs(); return 0; From f5bc75bff728036d140e9fdd54594166437f5114 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 2 Nov 2023 00:05:12 +0900 Subject: [PATCH 2/7] 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 --- misc-utils/lsfd-cdev.c | 169 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 5 deletions(-) diff --git a/misc-utils/lsfd-cdev.c b/misc-utils/lsfd-cdev.c index 70751bab970..3cb133fb080 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; From 6919acfff864d36f84d84b5509133377b2f051b5 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 2 Nov 2023 00:44:20 +0900 Subject: [PATCH 3/7] lsfd: add PTMX.TTY-INDEX column tty-index field in /proc/$pid/fdinfo/$fd is the data source of the column. The value is helpful for looking up the conterpart of /dev/ptmx. Signed-off-by: Masatake YAMATO --- misc-utils/lsfd-cdev.c | 50 ++++++++++++++++++++++++++++++++++++++++++ misc-utils/lsfd.1.adoc | 9 ++++++++ misc-utils/lsfd.c | 3 +++ misc-utils/lsfd.h | 1 + 4 files changed, 63 insertions(+) diff --git a/misc-utils/lsfd-cdev.c b/misc-utils/lsfd-cdev.c index 3cb133fb080..9bcbea35332 100644 --- a/misc-utils/lsfd-cdev.c +++ b/misc-utils/lsfd-cdev.c @@ -35,6 +35,7 @@ struct ttydrv { unsigned long major; unsigned long minor_start, minor_end; char *name; + bool is_ptmx; }; struct cdev { @@ -161,6 +162,9 @@ static struct ttydrv *new_ttydrv(unsigned int major, ttydrv->minor_start = minor_start; ttydrv->minor_end = minor_end; ttydrv->name = xstrdup(name); + ttydrv->is_ptmx = false; + if (strcmp(name, "ptmx") == 0) + ttydrv->is_ptmx = true; return ttydrv; } @@ -430,6 +434,8 @@ static struct cdev_ops cdev_tun_ops = { */ struct ttydata { const struct ttydrv *drv; +#define NO_TTY_INDEX -1 + int tty_index; /* used only in ptmx devices */ }; static bool cdev_tty_probe(struct cdev *cdev) { @@ -442,6 +448,7 @@ static bool cdev_tty_probe(struct cdev *cdev) { data = xmalloc(sizeof(struct ttydata)); data->drv = ttydrv; + data->tty_index = NO_TTY_INDEX; cdev->cdev_data = data; return true; @@ -453,6 +460,21 @@ static void cdev_tty_free(const struct cdev *cdev) free(cdev->cdev_data); } +static char * cdev_tty_get_name(struct cdev *cdev) +{ + struct ttydata *data = cdev->cdev_data; + char *str = NULL; + + if (!data->drv->is_ptmx) + return NULL; + + if (data->tty_index == NO_TTY_INDEX) + str = xstrdup("tty-index="); + else + xasprintf(&str, "tty-index=%d", data->tty_index); + return str; +} + static bool cdev_tty_fill_column(struct proc *proc __attribute__((__unused__)), struct cdev *cdev, struct libscols_line *ln __attribute__((__unused__)), @@ -471,16 +493,44 @@ static bool cdev_tty_fill_column(struct proc *proc __attribute__((__unused__)), xasprintf(str, "%s:%u", data->drv->name, minor(file->stat.st_rdev)); return true; + case COL_PTMX_TTY_INDEX: + if (data->drv->is_ptmx) { + xasprintf(str, "%d", data->tty_index); + return true; + } + return false; default: return false; } } +static int cdev_tty_handle_fdinfo(struct cdev *cdev, const char *key, const char *val) +{ + struct ttydata *data = cdev->cdev_data; + + if (!data->drv->is_ptmx) + return 0; + + if (strcmp(key, "tty-index") == 0) { + errno = 0; + data->tty_index = (int)strtol(val, NULL, 10); + if (errno) { + data->tty_index = NO_TTY_INDEX; + return 0; + } + return 1; + } + + return 0; +} + static struct cdev_ops cdev_tty_ops = { .parent = &cdev_generic_ops, .probe = cdev_tty_probe, .free = cdev_tty_free, + .get_name = cdev_tty_get_name, .fill_column = cdev_tty_fill_column, + .handle_fdinfo = cdev_tty_handle_fdinfo, }; static const struct cdev_ops *cdev_ops[] = { diff --git a/misc-utils/lsfd.1.adoc b/misc-utils/lsfd.1.adoc index 1f7e889d46c..1e9d6acb76e 100644 --- a/misc-utils/lsfd.1.adoc +++ b/misc-utils/lsfd.1.adoc @@ -304,6 +304,12 @@ state=_SOCK.STATE_[ id=_PING.ID_][ laddr=_INET.LADDR_ [ raddr=_INET.RADDR_]] PINGv6::: state=_SOCK.STATE_[ id=_PING.ID_][ laddr=_INET6.LADDR_ [ raddr=_INET6.RADDR_]] + +ptmx::: +tty-index=_PTMX.TTY-INDEX_ ++ +*lsfd* extracts _PTMX.TTY-INDEX_ from +``/proc/``_pid_``/fdinfo/``_fd_. ++ RAW::: state=_SOCK.STATE_[ protocol=_RAW.PROTOCOL_ [ laddr=_INET.LADDR_ [ raddr=_INET.RADDR_]]] + @@ -484,6 +490,9 @@ Interval. TIMERFD.REMAINING <``number``>:: Remaining time. +PTMX.TTY-INDEX <``number``>:: +TTY index of the counterpart. + TUN.IFACE <``string``>:: Network intrface behind the tun device. diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 0bb02e28210..76e0e66e179 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -275,6 +275,9 @@ static const struct colinfo infos[] = { [COL_POS] = { "POS", 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("file position") }, + [COL_PTMX_TTY_INDEX] = { "PTMX.TTY-INDEX", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("tty index of the counterpart") }, [COL_RAW_PROTOCOL] = { "RAW.PROTOCOL", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("protocol number of the raw socket") }, diff --git a/misc-utils/lsfd.h b/misc-utils/lsfd.h index c9c975392fc..a819e65096c 100644 --- a/misc-utils/lsfd.h +++ b/misc-utils/lsfd.h @@ -92,6 +92,7 @@ enum { COL_PIDFD_PID, COL_PING_ID, COL_POS, + COL_PTMX_TTY_INDEX, COL_RAW_PROTOCOL, COL_RDEV, COL_SIGNALFD_MASK, From 036a12d4888289579f324e32ebffd94c2827a506 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 2 Nov 2023 02:42:27 +0900 Subject: [PATCH 4/7] lsfd: add attach_xinfo and get_ipc_class methods to cdev_ops Signed-off-by: Masatake YAMATO --- misc-utils/lsfd-cdev.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/misc-utils/lsfd-cdev.c b/misc-utils/lsfd-cdev.c index 9bcbea35332..0f47169f834 100644 --- a/misc-utils/lsfd-cdev.c +++ b/misc-utils/lsfd-cdev.c @@ -57,7 +57,9 @@ struct cdev_ops { char **); void (*init)(const struct cdev *); void (*free)(const struct cdev *); + void (*attach_xinfo)(struct cdev *); int (*handle_fdinfo)(struct cdev *, const char *, const char *); + const struct ipc_class * (*get_ipc_class)(struct cdev *); }; static bool cdev_fill_column(struct proc *proc __attribute__((__unused__)), @@ -575,6 +577,14 @@ static void free_cdev_content(struct file *file) cdev->cdev_ops->free(cdev); } +static void cdev_attach_xinfo(struct file *file) +{ + struct cdev *cdev = (struct cdev *)file; + + if (cdev->cdev_ops->attach_xinfo) + cdev->cdev_ops->attach_xinfo(cdev); +} + static int cdev_handle_fdinfo(struct file *file, const char *key, const char *value) { struct cdev *cdev = (struct cdev *)file; @@ -584,6 +594,15 @@ static int cdev_handle_fdinfo(struct file *file, const char *key, const char *va return 0; /* Should be handled in parents */ } +static const struct ipc_class *cdev_get_ipc_class(struct file *file) +{ + struct cdev *cdev = (struct cdev *)file; + + if (cdev->cdev_ops->get_ipc_class) + return cdev->cdev_ops->get_ipc_class(cdev); + return NULL; +} + const struct file_class cdev_class = { .super = &file_class, .size = sizeof(struct cdev), @@ -592,5 +611,7 @@ const struct file_class cdev_class = { .fill_column = cdev_fill_column, .initialize_content = init_cdev_content, .free_content = free_cdev_content, + .attach_xinfo = cdev_attach_xinfo, .handle_fdinfo = cdev_handle_fdinfo, + .get_ipc_class = cdev_get_ipc_class, }; From 52e94d61b09a05f7ee5ef0dddf75e9c69b3f0368 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 2 Nov 2023 02:47:35 +0900 Subject: [PATCH 5/7] lsfd: fill ENDPOINTS column for pty devices An example output: $ ./lsfd -o+ENDPOINTS -Q '(COMMAND =~ "tmux") and ((SOURCE == "ptmx") or (SOURCE =~ "pts:.*"))' COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME ENDPOINTS tmux: client 3717086 yamato 0 rw---- CHR pts:9 27 12 /dev/pts/9 2143274,xfce4-terminal,34rw tmux: client 3717086 yamato 1 rw---- CHR pts:9 27 12 /dev/pts/9 2143274,xfce4-terminal,34rw tmux: client 3717086 yamato 2 rw---- CHR pts:9 27 12 /dev/pts/9 2143274,xfce4-terminal,34rw tmux: server 3717088 yamato 5 rw---m CHR pts:9 27 12 /dev/pts/9 2143274,xfce4-terminal,34rw tmux: server 3717088 yamato 8 rw---m CHR ptmx 24 208 tty-index=16 3717089,zsh,0rw 3717089,zsh,1rw 3717089,zsh,2rw 3717089,zsh,10rw $ ./lsfd -o+ENDPOINTS -Q '(PID == 3717089) and (FD == 0)' COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME ENDPOINTS zsh 3717089 yamato 0 rw---- CHR pts:16 27 19 /dev/pts/16 3717088,tmux: server,8rw Signed-off-by: Masatake YAMATO --- misc-utils/lsfd-cdev.c | 120 +++++++++++++++++++++++++++++++++++++++-- misc-utils/lsfd.1.adoc | 1 + 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/misc-utils/lsfd-cdev.c b/misc-utils/lsfd-cdev.c index 0f47169f834..63f95ed516b 100644 --- a/misc-utils/lsfd-cdev.c +++ b/misc-utils/lsfd-cdev.c @@ -35,7 +35,8 @@ struct ttydrv { unsigned long major; unsigned long minor_start, minor_end; char *name; - bool is_ptmx; + unsigned int is_ptmx: 1; + unsigned int is_pts: 1; }; struct cdev { @@ -164,9 +165,12 @@ static struct ttydrv *new_ttydrv(unsigned int major, ttydrv->minor_start = minor_start; ttydrv->minor_end = minor_end; ttydrv->name = xstrdup(name); - ttydrv->is_ptmx = false; + ttydrv->is_ptmx = 0; if (strcmp(name, "ptmx") == 0) - ttydrv->is_ptmx = true; + ttydrv->is_ptmx = 1; + ttydrv->is_pts = 0; + if (strcmp(name, "pts") == 0) + ttydrv->is_pts = 1; return ttydrv; } @@ -177,6 +181,11 @@ static void free_ttydrv(struct ttydrv *ttydrv) free(ttydrv); } +static bool is_pty(const struct ttydrv *ttydrv) +{ + return ttydrv->is_ptmx || ttydrv->is_pts; +} + static struct ttydrv *read_ttydrv(const char *line) { const char *p; @@ -435,9 +444,11 @@ static struct cdev_ops cdev_tun_ops = { * tty devices */ struct ttydata { + struct cdev *cdev; const struct ttydrv *drv; #define NO_TTY_INDEX -1 int tty_index; /* used only in ptmx devices */ + struct ipc_endpoint endpoint; }; static bool cdev_tty_probe(struct cdev *cdev) { @@ -449,6 +460,7 @@ static bool cdev_tty_probe(struct cdev *cdev) { return false; data = xmalloc(sizeof(struct ttydata)); + data->cdev = cdev; data->drv = ttydrv; data->tty_index = NO_TTY_INDEX; cdev->cdev_data = data; @@ -477,6 +489,16 @@ static char * cdev_tty_get_name(struct cdev *cdev) return str; } +static inline char *cdev_tty_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 cdev_tty_fill_column(struct proc *proc __attribute__((__unused__)), struct cdev *cdev, struct libscols_line *ln __attribute__((__unused__)), @@ -501,6 +523,30 @@ static bool cdev_tty_fill_column(struct proc *proc __attribute__((__unused__)), return true; } return false; + case COL_ENDPOINTS: + if (is_pty(data->drv)) { + struct ttydata *this = data; + struct list_head *e; + foreach_endpoint(e, data->endpoint) { + char *estr; + struct ttydata *other = list_entry(e, struct ttydata, endpoint.endpoints); + if (this == other) + continue; + + if ((this->drv->is_ptmx && !other->drv->is_pts) + || (this->drv->is_pts && !other->drv->is_ptmx)) + continue; + + if (*str) + xstrputc(str, '\n'); + estr = cdev_tty_xstrendpoint(&other->cdev->file); + xstrappend(str, estr); + free(estr); + } + if (*str) + return true; + } + return false; default: return false; } @@ -526,13 +572,81 @@ static int cdev_tty_handle_fdinfo(struct cdev *cdev, const char *key, const char return 0; } +struct cdev_pty_ipc { + struct ipc ipc; + int tty_index; +}; + +static unsigned int cdev_pty_get_hash(struct file *file) +{ + struct cdev *cdev = (struct cdev *)file; + struct ttydata *data = cdev->cdev_data; + + return data->drv->is_ptmx? + (unsigned int)data->tty_index: + (unsigned int)minor(file->stat.st_rdev); +} + +static bool cdev_pty_is_suitable_ipc(struct ipc *ipc, struct file *file) +{ + struct cdev *cdev = (struct cdev *)file; + struct ttydata *data = cdev->cdev_data; + struct cdev_pty_ipc *cdev_pty_ipc = (struct cdev_pty_ipc *)ipc; + + return (data->drv->is_ptmx)? + cdev_pty_ipc->tty_index == (int)data->tty_index: + cdev_pty_ipc->tty_index == (int)minor(file->stat.st_rdev); +} + +static const struct ipc_class *cdev_tty_get_ipc_class(struct cdev *cdev) +{ + static const struct ipc_class cdev_pty_ipc_class = { + .size = sizeof(struct cdev_pty_ipc), + .get_hash = cdev_pty_get_hash, + .is_suitable_ipc = cdev_pty_is_suitable_ipc, + }; + + struct ttydata *data = cdev->cdev_data; + + if (is_pty(data->drv)) + return &cdev_pty_ipc_class; + + return NULL; +} + +static void cdev_tty_attach_xinfo(struct cdev *cdev) +{ + struct ttydata *data = cdev->cdev_data; + struct ipc *ipc; + unsigned int hash; + + + if (! is_pty(data->drv)) + return; + + init_endpoint(&data->endpoint); + ipc = get_ipc(&cdev->file); + if (ipc) + goto link; + + ipc = new_ipc(cdev_tty_get_ipc_class(cdev)); + hash = cdev_pty_get_hash(&cdev->file); + ((struct cdev_pty_ipc *)ipc)->tty_index = (int)hash; + + add_ipc(ipc, hash); + link: + add_endpoint(&data->endpoint, ipc); +} + static struct cdev_ops cdev_tty_ops = { .parent = &cdev_generic_ops, .probe = cdev_tty_probe, .free = cdev_tty_free, .get_name = cdev_tty_get_name, .fill_column = cdev_tty_fill_column, + .attach_xinfo = cdev_tty_attach_xinfo, .handle_fdinfo = cdev_tty_handle_fdinfo, + .get_ipc_class = cdev_tty_get_ipc_class, }; static const struct cdev_ops *cdev_ops[] = { diff --git a/misc-utils/lsfd.1.adoc b/misc-utils/lsfd.1.adoc index 1e9d6acb76e..aef76ec5e3f 100644 --- a/misc-utils/lsfd.1.adoc +++ b/misc-utils/lsfd.1.adoc @@ -180,6 +180,7 @@ with the fd: FIFO type::: mqueue type::: +ptmx and pts sources::: _PID_,_COMMAND_,_ASSOC_[-r][-w] + The last characters ([-r][-w]) represents the read and/or From a76ea60adab8b1562a5ccb2d9b497e72be500b9f Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Sat, 4 Nov 2023 04:38:52 +0900 Subject: [PATCH 6/7] tests: (test_mkfds::pty) add a new factory Signed-off-by: Masatake YAMATO --- tests/helpers/test_mkfds.c | 108 ++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c index 8a1388adff4..cd5379bc2b7 100644 --- a/tests/helpers/test_mkfds.c +++ b/tests/helpers/test_mkfds.c @@ -3020,6 +3020,99 @@ static void *make_bpf_map(const struct factory *factory, struct fdesc fdescs[], return NULL; } +static void *make_pty(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int index, *indexp; + char *pts; + int pts_fd; + int ptmx_fd = posix_openpt(O_RDWR); + if (ptmx_fd < 0) + err(EXIT_FAILURE, "failed in opening /dev/ptmx"); + + if (unlockpt(ptmx_fd) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in unlockpt()"); + } + + if (ioctl(ptmx_fd, TIOCGPTN, &index) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in ioctl(TIOCGPTN)"); + } + + pts = ptsname(ptmx_fd); + if (pts == NULL) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in ptsname()"); + } + + if (ptmx_fd != fdescs[0].fd) { + if (dup2(ptmx_fd, fdescs[0].fd) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", ptmx_fd, fdescs[0].fd); + } + close(ptmx_fd); + ptmx_fd = fdescs[0].fd; + } + + pts_fd = open(pts, O_RDONLY); + if (pts_fd < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in opening %s", pts); + } + + if (pts_fd != fdescs[1].fd) { + if (dup2(pts_fd, fdescs[1].fd) < 0) { + int e = errno; + close(pts_fd); + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", pts_fd, fdescs[1].fd); + } + close(pts_fd); + pts_fd = fdescs[1].fd; + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + fdescs[1] = (struct fdesc){ + .fd = fdescs[1].fd, + .close = close_fdesc, + .data = NULL + }; + + indexp = xmalloc(sizeof(index)); + *indexp = index; + return indexp; +} + +static void report_pty(const struct factory *factory _U_, + int nth, void *data, FILE *fp) +{ + if (nth == 0) { + int *index = data; + fprintf(fp, "%d", *index); + } +} + +static void free_pty(const struct factory * factory _U_, void *data) +{ + free(data); +} + #define PARAM_END { .name = NULL, } static const struct factory factories[] = { { @@ -3817,7 +3910,20 @@ static const struct factory factories[] = { PARAM_END } }, - + { + .name = "pty", + .desc = "make a pair of ptmx and pts", + .priv = false, + .N = 2, + .EX_N = 0, + .EX_R = 1, + .make = make_pty, + .report = report_pty, + .free = free_pty, + .params = (struct parameter []) { + PARAM_END + } + }, }; static int count_parameters(const struct factory *factory) From bedfc7dd72ed7a50758c6290600b51fd8db656dd Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Sat, 4 Nov 2023 05:22:21 +0900 Subject: [PATCH 7/7] tests: (lsfd) add a case testing NAME, SOURCE, ENDPOINTS, and PTMX.TTY-INDEX columns of pts fds Signed-off-by: Masatake YAMATO --- tests/expected/lsfd/mkfds-pty | 10 +++++ tests/ts/lsfd/mkfds-pty | 73 +++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/expected/lsfd/mkfds-pty create mode 100755 tests/ts/lsfd/mkfds-pty diff --git a/tests/expected/lsfd/mkfds-pty b/tests/expected/lsfd/mkfds-pty new file mode 100644 index 00000000000..ac38fc974b7 --- /dev/null +++ b/tests/expected/lsfd/mkfds-pty @@ -0,0 +1,10 @@ +5 rw- CHR ptmx +ASSOC,MODE,TYPE,SOURCE: 0 +NAME: 0 +ENDPOINTS: 0 +PTMX.TTY-INDEX: 0 +6 r-- CHR +ASSOC,MODE,TYPE: 0 +SOURCE: 0 +NAME: 0 +ENDPOINTS: 0 diff --git a/tests/ts/lsfd/mkfds-pty b/tests/ts/lsfd/mkfds-pty new file mode 100755 index 00000000000..cc781ebe1e7 --- /dev/null +++ b/tests/ts/lsfd/mkfds-pty @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Copyright (C) 2023 Masatake YAMATO +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="ptmx and associated pts" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" + +ts_check_test_command "$TS_CMD_LSFD" +ts_check_test_command "$TS_HELPER_MKFDS" + +ts_cd "$TS_OUTDIR" + +PID= +FD0=5 +FD1=6 +INDEX= + +col_test() +{ + local col=$1 + local expected=$2 + local output + + output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o "$col" -Q "${EXPR}") + echo "$col": $? + if [[ "$output" != "$expected" ]]; then + echo "expected ${col} -Q ${EXPR}: ${expected}" + echo "output ${col} -Q ${EXPR}: ${output}" + fi +} + +{ + coproc MKFDS { "$TS_HELPER_MKFDS" pty $FD0 $FD1 ; } + if read -u ${MKFDS[0]} PID INDEX; then + EXPR='(FD == '$FD0')' + columns=ASSOC,MODE,TYPE,SOURCE + ${TS_CMD_LSFD} -p "${PID}" -n -r -o "$columns" -Q "${EXPR}" + echo "$columns": $? + + col_test NAME "tty-index=${INDEX}" + col_test ENDPOINTS "${PID},test_mkfds,${FD1}r-" + col_test PTMX.TTY-INDEX "$INDEX" + + EXPR='(FD == '$FD1')' + columns=ASSOC,MODE,TYPE + ${TS_CMD_LSFD} -p "${PID}" -n -r -o "$columns" -Q "${EXPR}" + echo "$columns": $? + + col_test SOURCE "pts:${INDEX}" + col_test NAME "/dev/pts/${INDEX}" + col_test ENDPOINTS "${PID},test_mkfds,${FD0}rw" + + echo DONE >&"${MKFDS[1]}" + fi + wait ${MKFDS_PID} +} > $TS_OUTPUT 2>&1 + +ts_finalize