in elastic-ebpf/GPL/Events/Process/Probe.bpf.c [600:669]
static int tty_write__enter(struct kiocb *iocb, struct iov_iter *from)
{
if (is_consumer()) {
goto out;
}
struct file *f = BPF_CORE_READ(iocb, ki_filp);
struct tty_file_private *tfp = (struct tty_file_private *)BPF_CORE_READ(f, private_data);
struct tty_struct *tty = BPF_CORE_READ(tfp, tty);
// Obtain the real TTY
//
// @link: link to another pty (master -> slave and vice versa)
//
// https://elixir.bootlin.com/linux/v5.19.9/source/drivers/tty/tty_io.c#L2643
bool is_master = false;
struct ebpf_tty_dev master = {};
struct ebpf_tty_dev slave = {};
if (BPF_CORE_READ(tty, driver, type) == TTY_DRIVER_TYPE_PTY &&
BPF_CORE_READ(tty, driver, subtype) == PTY_TYPE_MASTER) {
struct tty_struct *tmp = BPF_CORE_READ(tty, link);
ebpf_tty_dev__fill(&master, tty);
ebpf_tty_dev__fill(&slave, tmp);
is_master = true;
} else {
ebpf_tty_dev__fill(&slave, tty);
}
if (slave.major == 0 && slave.minor == 0) {
goto out;
}
if ((is_master && !(master.termios.c_lflag & ECHO)) && !(slave.termios.c_lflag & ECHO)) {
goto out;
}
const struct iovec *iov;
if (FIELD_OFFSET(iov_iter, __iov))
iov = (const struct iovec *)((char *)from + FIELD_OFFSET(iov_iter, __iov));
else if (bpf_core_field_exists(from->iov))
iov = BPF_CORE_READ(from, iov);
else
goto out;
u64 nr_segs = BPF_CORE_READ(from, nr_segs);
nr_segs = nr_segs > MAX_NR_SEGS ? MAX_NR_SEGS : nr_segs;
if (nr_segs == 0) {
u64 count = BPF_CORE_READ(from, count);
(void)output_tty_event(&slave, (void *)iov, count);
goto out;
}
for (int seg = 0; seg < nr_segs; seg++) {
// NOTE(matt): this check needs to be here because the verifier
// detects an infinite loop otherwise.
if (seg >= MAX_NR_SEGS)
goto out;
struct iovec *cur_iov = (struct iovec *)&iov[seg];
const char *base = BPF_CORE_READ(cur_iov, iov_base);
size_t len = BPF_CORE_READ(cur_iov, iov_len);
if (output_tty_event(&slave, base, len)) {
goto out;
}
}
out:
return 0;
}