diff options
-rw-r--r-- | sys/kern/tty.c | 90 | ||||
-rw-r--r-- | sys/kern/tty_ttydisc.c | 101 | ||||
-rw-r--r-- | sys/sys/tty.h | 10 | ||||
-rw-r--r-- | sys/sys/ttydevsw.h | 4 | ||||
-rw-r--r-- | sys/sys/ttydisc.h | 34 | ||||
-rw-r--r-- | sys/sys/ttyhook.h | 147 | ||||
-rw-r--r-- | usr.sbin/pstat/pstat.8 | 2 | ||||
-rw-r--r-- | usr.sbin/pstat/pstat.c | 1 |
8 files changed, 341 insertions, 48 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c index cd6a246..863b6ce 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/cons.h> #include <sys/fcntl.h> +#include <sys/file.h> #include <sys/filio.h> #ifdef COMPAT_43TTY #include <sys/ioctl_compat.h> @@ -59,6 +60,7 @@ __FBSDID("$FreeBSD$"); #include <sys/ttydefaults.h> #undef TTYDEFCHARS #include <sys/ucred.h> +#include <sys/vnode.h> #include <machine/stdarg.h> @@ -855,7 +857,7 @@ tty_alloc(struct ttydevsw *tsw, void *sc, struct mtx *mutex) tp = malloc(sizeof(struct tty), M_TTY, M_WAITOK|M_ZERO); tp->t_devsw = tsw; - tp->t_softc = sc; + tp->t_devswsoftc = sc; tp->t_flags = tsw->tsw_flags; tty_init_termios(tp); @@ -923,7 +925,7 @@ tty_rel_free(struct tty *tp) tty_lock_assert(tp, MA_OWNED); if (tp->t_sessioncnt != 0 || - (tp->t_flags & (TF_GONE|TF_OPENED)) != TF_GONE) { + (tp->t_flags & (TF_GONE|TF_OPENED|TF_HOOK)) != TF_GONE) { /* TTY is still in use. */ tty_unlock(tp); return; @@ -1653,6 +1655,89 @@ tty_hiwat_in_unblock(struct tty *tp) ttydevsw_inwakeup(tp); } +static int +ttyhook_defrint(struct tty *tp, char c, int flags) +{ + + if (ttyhook_rint_bypass(tp, &c, 1) != 1) + return (-1); + + return (0); +} + +int +ttyhook_register(struct tty **rtp, struct thread *td, int fd, + struct ttyhook *th, void *softc) +{ + struct tty *tp; + struct file *fp; + struct cdev *dev; + struct cdevsw *cdp; + int error; + + /* Validate the file descriptor. */ + if (fget(td, fd, &fp) != 0) + return (EINVAL); + + /* Make sure the vnode is bound to a character device. */ + error = EINVAL; + if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VCHR || + fp->f_vnode->v_rdev == NULL) + goto done1; + dev = fp->f_vnode->v_rdev; + + /* Make sure it is a TTY. */ + cdp = dev_refthread(dev); + if (cdp == NULL) + goto done1; + if ((cdp->d_flags & D_TTY) == 0) + goto done2; + tp = dev->si_drv1; + + /* Try to attach the hook to the TTY. */ + error = EBUSY; + tty_lock(tp); + MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); + if (tp->t_flags & TF_HOOK) + goto done3; + + tp->t_flags |= TF_HOOK; + tp->t_hook = th; + tp->t_hooksoftc = softc; + *rtp = tp; + error = 0; + + /* Maybe we can switch into bypass mode now. */ + ttydisc_optimize(tp); + + /* Silently convert rint() calls to rint_bypass() when possible. */ + if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) + th->th_rint = ttyhook_defrint; + +done3: tty_unlock(tp); +done2: dev_relthread(dev); +done1: fdrop(fp, td); + return (error); +} + +void +ttyhook_unregister(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + MPASS(tp->t_flags & TF_HOOK); + + /* Disconnect the hook. */ + tp->t_flags &= ~TF_HOOK; + tp->t_hook = NULL; + + /* Maybe we need to leave bypass mode. */ + ttydisc_optimize(tp); + + /* Maybe deallocate the TTY as well. */ + tty_rel_free(tp); +} + #include "opt_ddb.h" #ifdef DDB #include <ddb/ddb.h> @@ -1686,6 +1771,7 @@ static struct { { TF_EXCLUDE, 'X' }, { TF_BYPASS, 'l' }, { TF_ZOMBIE, 'Z' }, + { TF_HOOK, 's' }, { 0, '\0' }, }; diff --git a/sys/kern/tty_ttydisc.c b/sys/kern/tty_ttydisc.c index 693b480..0d7dd7f 100644 --- a/sys/kern/tty_ttydisc.c +++ b/sys/kern/tty_ttydisc.c @@ -79,6 +79,8 @@ SYSCTL_LONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, #define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) +#define TTY_STACKBUF 256 + void ttydisc_open(struct tty *tp) { @@ -100,6 +102,9 @@ ttydisc_close(struct tty *tp) ttydevsw_inwakeup(tp); ttydevsw_outwakeup(tp); } + + if (ttyhook_hashook(tp, close)) + ttyhook_close(tp); } static int @@ -433,7 +438,7 @@ ttydisc_write_oproc(struct tty *tp, char c) int ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) { - char ob[256]; + char ob[TTY_STACKBUF]; char *obstart; int error = 0; unsigned int oblen = 0; @@ -557,11 +562,12 @@ ttydisc_optimize(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); - if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && + if ((!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && (!CMP_FLAG(i, PARMRK) || CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && - !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { + !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) || + ttyhook_hashook(tp, rint_bypass)) { tp->t_flags |= TF_BYPASS; } else { tp->t_flags &= ~TF_BYPASS; @@ -818,6 +824,9 @@ ttydisc_rint(struct tty *tp, char c, int flags) tty_lock_assert(tp, MA_OWNED); atomic_add_long(&tty_nin, 1); + + if (ttyhook_hashook(tp, rint)) + return ttyhook_rint(tp, c, flags); if (tp->t_flags & TF_BYPASS) goto processed; @@ -1045,6 +1054,9 @@ ttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len) atomic_add_long(&tty_nin, len); + if (ttyhook_hashook(tp, rint_bypass)) + return ttyhook_rint_bypass(tp, buf, len); + ret = ttyinq_write(&tp->t_inq, buf, len, 0); ttyinq_canonicalize(&tp->t_inq); @@ -1057,12 +1069,38 @@ ttydisc_rint_done(struct tty *tp) tty_lock_assert(tp, MA_OWNED); + if (ttyhook_hashook(tp, rint_done)) + ttyhook_rint_done(tp); + /* Wake up readers. */ tty_wakeup(tp, FREAD); /* Wake up driver for echo. */ ttydevsw_outwakeup(tp); } +size_t +ttydisc_rint_poll(struct tty *tp) +{ + size_t l; + + tty_lock_assert(tp, MA_OWNED); + + if (ttyhook_hashook(tp, rint_poll)) + return ttyhook_rint_poll(tp); + + /* + * XXX: Still allow character input when there's no space in the + * buffers, but we haven't entered the high watermark. This is + * to allow backspace characters to be inserted when in + * canonical mode. + */ + l = ttyinq_bytesleft(&tp->t_inq); + if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) + return (1); + + return (l); +} + static void ttydisc_wakeup_watermark(struct tty *tp) { @@ -1093,9 +1131,15 @@ ttydisc_getc(struct tty *tp, void *buf, size_t len) if (tp->t_flags & TF_STOPPED) return (0); + if (ttyhook_hashook(tp, getc_inject)) + return ttyhook_getc_inject(tp, buf, len); + len = ttyoutq_read(&tp->t_outq, buf, len); - ttydisc_wakeup_watermark(tp); + if (ttyhook_hashook(tp, getc_capture)) + ttyhook_getc_capture(tp, buf, len); + + ttydisc_wakeup_watermark(tp); atomic_add_long(&tty_nout, len); return (len); @@ -1104,22 +1148,63 @@ ttydisc_getc(struct tty *tp, void *buf, size_t len) int ttydisc_getc_uio(struct tty *tp, struct uio *uio) { - int error; + int error = 0; int obytes = uio->uio_resid; + size_t len; + char buf[TTY_STACKBUF]; tty_lock_assert(tp, MA_OWNED); if (tp->t_flags & TF_STOPPED) return (0); - error = ttyoutq_read_uio(&tp->t_outq, tp, uio); - ttydisc_wakeup_watermark(tp); + /* + * When a TTY hook is attached, we cannot perform unbuffered + * copying to userspace. Just call ttydisc_getc() and + * temporarily store data in a shadow buffer. + */ + if (ttyhook_hashook(tp, getc_capture) || + ttyhook_hashook(tp, getc_inject)) { + while (uio->uio_resid > 0) { + /* Read to shadow buffer. */ + len = ttydisc_getc(tp, buf, + MIN(uio->uio_resid, sizeof buf)); + if (len == 0) + break; + + /* Copy to userspace. */ + tty_unlock(tp); + error = uiomove(buf, len, uio); + tty_lock(tp); + + if (error != 0) + break; + } + } else { + error = ttyoutq_read_uio(&tp->t_outq, tp, uio); - atomic_add_long(&tty_nout, obytes - uio->uio_resid); + ttydisc_wakeup_watermark(tp); + atomic_add_long(&tty_nout, obytes - uio->uio_resid); + } return (error); } +size_t +ttydisc_getc_poll(struct tty *tp) +{ + + tty_lock_assert(tp, MA_OWNED); + + if (tp->t_flags & TF_STOPPED) + return (0); + + if (ttyhook_hashook(tp, getc_poll)) + return ttyhook_getc_poll(tp); + + return ttyoutq_bytesused(&tp->t_outq); +} + /* * XXX: not really related to the TTYDISC, but we'd better put * tty_putchar() here, because we need to perform proper output diff --git a/sys/sys/tty.h b/sys/sys/tty.h index 54d487c..2250cbc 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -80,6 +80,7 @@ struct tty { #define TF_EXCLUDE 0x1000 /* Exclusive access. */ #define TF_BYPASS 0x2000 /* Optimized input path. */ #define TF_ZOMBIE 0x4000 /* Modem disconnect received. */ +#define TF_HOOK 0x8000 /* TTY has hook attached. */ unsigned int t_revokecnt; /* (t) revoke() count. */ /* Buffering mechanisms. */ @@ -112,13 +113,15 @@ struct tty { struct termios t_termios_lock_out; /* cua%s.lock. */ struct ttydevsw *t_devsw; /* (c) Driver hooks. */ + struct ttyhook *t_hook; /* (t) Capture/inject hook. */ /* Process signal delivery. */ struct pgrp *t_pgrp; /* (t) Foreground process group. */ struct session *t_session; /* (t) Associated session. */ unsigned int t_sessioncnt; /* (t) Backpointing sessions. */ - void *t_softc; /* (c) Soft config, for drivers. */ + void *t_devswsoftc; /* (c) Soft config, for drivers. */ + void *t_hooksoftc; /* (t) Soft config, for hooks. */ struct cdev *t_dev; /* (c) Primary character device. */ }; @@ -181,7 +184,7 @@ void tty_hiwat_in_unblock(struct tty *tp); dev_t tty_udev(struct tty *tp); #define tty_opened(tp) ((tp)->t_flags & TF_OPENED) #define tty_gone(tp) ((tp)->t_flags & TF_GONE) -#define tty_softc(tp) ((tp)->t_softc) +#define tty_softc(tp) ((tp)->t_devswsoftc) #define tty_devname(tp) devtoname((tp)->t_dev) /* Status line printing. */ @@ -192,8 +195,9 @@ int pts_alloc_external(int fd, struct thread *td, struct file *fp, struct cdev *dev, const char *name); /* Drivers and line disciplines also need to call these. */ -#include <sys/ttydevsw.h> #include <sys/ttydisc.h> +#include <sys/ttydevsw.h> +#include <sys/ttyhook.h> #endif /* _KERNEL */ #endif /* !_SYS_TTY_H_ */ diff --git a/sys/sys/ttydevsw.h b/sys/sys/ttydevsw.h index 842143d..e2278c3 100644 --- a/sys/sys/ttydevsw.h +++ b/sys/sys/ttydevsw.h @@ -97,9 +97,7 @@ ttydevsw_outwakeup(struct tty *tp) MPASS(!tty_gone(tp)); /* Prevent spurious wakeups. */ - if (tp->t_flags & TF_STOPPED) - return; - if (ttyoutq_bytesused(&tp->t_outq) == 0) + if (ttydisc_getc_poll(tp) == 0) return; tp->t_devsw->tsw_outwakeup(tp); diff --git a/sys/sys/ttydisc.h b/sys/sys/ttydisc.h index aeaabc6..2ea5466 100644 --- a/sys/sys/ttydisc.h +++ b/sys/sys/ttydisc.h @@ -54,8 +54,10 @@ void ttydisc_modem(struct tty *tp, int open); int ttydisc_rint(struct tty *tp, char c, int flags); size_t ttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len); void ttydisc_rint_done(struct tty *tp); +size_t ttydisc_rint_poll(struct tty *tp); size_t ttydisc_getc(struct tty *tp, void *buf, size_t len); int ttydisc_getc_uio(struct tty *tp, struct uio *uio); +size_t ttydisc_getc_poll(struct tty *tp); /* Error codes for ttydisc_rint(). */ #define TRE_FRAMING 0x01 @@ -81,36 +83,4 @@ ttydisc_write_poll(struct tty *tp) return ttyoutq_bytesleft(&tp->t_outq); } -static __inline size_t -ttydisc_rint_poll(struct tty *tp) -{ - size_t l; - - tty_lock_assert(tp, MA_OWNED); - - /* - * XXX: Still allow character input when there's no space in the - * buffers, but we haven't entered the high watermark. This is - * to allow backspace characters to be inserted when in - * canonical mode. - */ - l = ttyinq_bytesleft(&tp->t_inq); - if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) - return (1); - - return (l); -} - -static __inline size_t -ttydisc_getc_poll(struct tty *tp) -{ - - tty_lock_assert(tp, MA_OWNED); - - if (tp->t_flags & TF_STOPPED) - return (0); - - return ttyoutq_bytesused(&tp->t_outq); -} - #endif /* !_SYS_TTYDISC_H_ */ diff --git a/sys/sys/ttyhook.h b/sys/sys/ttyhook.h new file mode 100644 index 0000000..7a0a0bc --- /dev/null +++ b/sys/sys/ttyhook.h @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_TTYHOOK_H_ +#define _SYS_TTYHOOK_H_ + +#ifndef _SYS_TTY_H_ +#error "can only be included through <sys/tty.h>" +#endif /* !_SYS_TTY_H_ */ + +struct tty; + +/* + * Hooks interface, which allows to capture and inject traffic into the + * input and output paths of a TTY. + */ + +typedef int th_rint_t(struct tty *tp, char c, int flags); +typedef size_t th_rint_bypass_t(struct tty *tp, const void *buf, size_t len); +typedef void th_rint_done_t(struct tty *tp); +typedef size_t th_rint_poll_t(struct tty *tp); + +typedef size_t th_getc_inject_t(struct tty *tp, void *buf, size_t len); +typedef void th_getc_capture_t(struct tty *tp, const void *buf, size_t len); +typedef size_t th_getc_poll_t(struct tty *tp); + +typedef void th_close_t(struct tty *tp); + +struct ttyhook { + /* Character input. */ + th_rint_t *th_rint; + th_rint_bypass_t *th_rint_bypass; + th_rint_done_t *th_rint_done; + th_rint_poll_t *th_rint_poll; + + /* Character output. */ + th_getc_inject_t *th_getc_inject; + th_getc_capture_t *th_getc_capture; + th_getc_poll_t *th_getc_poll; + + th_close_t *th_close; +}; + +int ttyhook_register(struct tty **, struct thread *, int, + struct ttyhook *, void *); +void ttyhook_unregister(struct tty *); +#define ttyhook_softc(tp) ((tp)->t_hooksoftc) +#define ttyhook_hashook(tp,hook) ((tp)->t_hook != NULL && \ + (tp)->t_hook->th_ ## hook != NULL) + +static __inline int +ttyhook_rint(struct tty *tp, char c, int flags) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_hook->th_rint(tp, c, flags); +} + +static __inline size_t +ttyhook_rint_bypass(struct tty *tp, const void *buf, size_t len) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_hook->th_rint_bypass(tp, buf, len); +} + +static __inline void +ttyhook_rint_done(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + tp->t_hook->th_rint_done(tp); +} + +static __inline size_t +ttyhook_rint_poll(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_hook->th_rint_poll(tp); +} + +static __inline size_t +ttyhook_getc_inject(struct tty *tp, void *buf, size_t len) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_hook->th_getc_inject(tp, buf, len); +} + +static __inline void +ttyhook_getc_capture(struct tty *tp, const void *buf, size_t len) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + tp->t_hook->th_getc_capture(tp, buf, len); +} + +static __inline size_t +ttyhook_getc_poll(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + MPASS(!tty_gone(tp)); + + return tp->t_hook->th_getc_poll(tp); +} + +static __inline void +ttyhook_close(struct tty *tp) +{ + tty_lock_assert(tp, MA_OWNED); + + tp->t_hook->th_close(tp); +} + +#endif /* !_SYS_TTYHOOK_H_ */ diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8 index 2770cb7..5474f8b 100644 --- a/usr.sbin/pstat/pstat.8 +++ b/usr.sbin/pstat/pstat.8 @@ -202,6 +202,8 @@ output stopped (ixon flow control) block mode input routine in use .It Z connection lost +.It s +i/o being snooped .El .Pp The diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c index 51828cb..a1e81b3 100644 --- a/usr.sbin/pstat/pstat.c +++ b/usr.sbin/pstat/pstat.c @@ -312,6 +312,7 @@ static struct { { TF_EXCLUDE, 'X' }, { TF_BYPASS, 'l' }, { TF_ZOMBIE, 'Z' }, + { TF_HOOK, 's' }, { 0, '\0' }, }; |