1139749Simp/*- 2184689Sed * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 3184689Sed * All rights reserved. 46390Sugen * 5184689Sed * Redistribution and use in source and binary forms, with or without 6184689Sed * modification, are permitted provided that the following conditions 7184689Sed * are met: 8184689Sed * 1. Redistributions of source code must retain the above copyright 9184689Sed * notice, this list of conditions and the following disclaimer. 10184689Sed * 2. Redistributions in binary form must reproduce the above copyright 11184689Sed * notice, this list of conditions and the following disclaimer in the 12184689Sed * documentation and/or other materials provided with the distribution. 136390Sugen * 14184689Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184689Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184689Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184689Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184689Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184689Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184689Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184689Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184689Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184689Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184689Sed * SUCH DAMAGE. 256390Sugen */ 266390Sugen 27119419Sobrien#include <sys/cdefs.h> 28119419Sobrien__FBSDID("$FreeBSD$"); 29119419Sobrien 306390Sugen#include <sys/param.h> 31184689Sed#include <sys/conf.h> 32139195Sphk#include <sys/fcntl.h> 3324205Sbde#include <sys/filio.h> 34184689Sed#include <sys/kernel.h> 3530354Sphk#include <sys/malloc.h> 36184689Sed#include <sys/module.h> 3729355Speter#include <sys/poll.h> 38186056Smav#include <sys/proc.h> 396390Sugen#include <sys/snoop.h> 40184689Sed#include <sys/sx.h> 41184689Sed#include <sys/systm.h> 42184689Sed#include <sys/tty.h> 43139195Sphk#include <sys/uio.h> 446390Sugen 45184689Sedstatic struct cdev *snp_dev; 46191999Sedstatic MALLOC_DEFINE(M_SNP, "snp", "tty snoop device"); 47191999Sed 48184689Sed/* XXX: should be mtx, but TTY can be locked by Giant. */ 49191999Sed#if 0 50191999Sedstatic struct mtx snp_register_lock; 51191999SedMTX_SYSINIT(snp_register_lock, &snp_register_lock, 52191999Sed "tty snoop registration", MTX_DEF); 53191999Sed#define SNP_LOCK() mtx_lock(&snp_register_lock) 54191999Sed#define SNP_UNLOCK() mtx_unlock(&snp_register_lock) 55191999Sed#else 56184689Sedstatic struct sx snp_register_lock; 57184689SedSX_SYSINIT(snp_register_lock, &snp_register_lock, 58184689Sed "tty snoop registration"); 59191999Sed#define SNP_LOCK() sx_xlock(&snp_register_lock) 60191999Sed#define SNP_UNLOCK() sx_xunlock(&snp_register_lock) 61191999Sed#endif 6212675Sjulian 6380326Sdd/* 64184689Sed * There is no need to have a big input buffer. In most typical setups, 65184689Sed * we won't inject much data into the TTY, because users can't type 66184689Sed * really fast. 6780326Sdd */ 68184689Sed#define SNP_INPUT_BUFSIZE 16 6980326Sdd/* 70184689Sed * The output buffer has to be really big. Right now we don't support 71184689Sed * any form of flow control, which means we lost any data we can't 72184689Sed * accept. We set the output buffer size to about twice the size of a 73184689Sed * pseudo-terminal/virtual console's output buffer. 7480326Sdd */ 75184689Sed#define SNP_OUTPUT_BUFSIZE 16384 7680326Sdd 77184689Sedstatic d_open_t snp_open; 78184689Sedstatic d_read_t snp_read; 79184689Sedstatic d_write_t snp_write; 80184689Sedstatic d_ioctl_t snp_ioctl; 81184689Sedstatic d_poll_t snp_poll; 8280326Sdd 83184689Sedstatic struct cdevsw snp_cdevsw = { 84184689Sed .d_version = D_VERSION, 85184689Sed .d_open = snp_open, 86184689Sed .d_read = snp_read, 87184689Sed .d_write = snp_write, 88184689Sed .d_ioctl = snp_ioctl, 89184689Sed .d_poll = snp_poll, 90184689Sed .d_name = "snp", 91184689Sed}; 9277016Sdd 93184689Sedstatic th_getc_capture_t snp_getc_capture; 9479864Sdd 95184689Sedstatic struct ttyhook snp_hook = { 96184689Sed .th_getc_capture = snp_getc_capture, 97184689Sed}; 9877748Sdd 99184689Sed/* 100184689Sed * Per-instance structure. 101184689Sed * 102184689Sed * List of locks 103184689Sed * (r) locked by snp_register_lock on assignment 104184689Sed * (t) locked by tty_lock 105184689Sed */ 106184689Sedstruct snp_softc { 107184689Sed struct tty *snp_tty; /* (r) TTY we're snooping. */ 108184689Sed struct ttyoutq snp_outq; /* (t) Output queue. */ 109184689Sed struct cv snp_outwait; /* (t) Output wait queue. */ 110184689Sed struct selinfo snp_outpoll; /* (t) Output polling. */ 111184689Sed}; 112137265Scognet 113184689Sedstatic void 114184689Sedsnp_dtor(void *data) 11577749Sdd{ 116184689Sed struct snp_softc *ss = data; 117184689Sed struct tty *tp; 11877749Sdd 119184689Sed tp = ss->snp_tty; 120184689Sed if (tp != NULL) { 121184689Sed tty_lock(tp); 122184689Sed ttyoutq_free(&ss->snp_outq); 123184689Sed ttyhook_unregister(tp); 12477016Sdd } 12577016Sdd 126184689Sed cv_destroy(&ss->snp_outwait); 127184689Sed free(ss, M_SNP); 1286712Spst} 1296712Spst 130184689Sed/* 131184689Sed * Snoop device node routines. 132184689Sed */ 1336774Sugen 13477748Sddstatic int 135184689Sedsnp_open(struct cdev *dev, int flag, int mode, struct thread *td) 1366774Sugen{ 137184689Sed struct snp_softc *ss; 1386774Sugen 139184689Sed /* Allocate per-snoop data. */ 140184689Sed ss = malloc(sizeof(struct snp_softc), M_SNP, M_WAITOK|M_ZERO); 141184689Sed cv_init(&ss->snp_outwait, "snp out"); 142181755Sed 143184689Sed devfs_set_cdevpriv(ss, snp_dtor); 1446774Sugen 14577748Sdd return (0); 1466774Sugen} 1476774Sugen 14877748Sddstatic int 149184689Sedsnp_read(struct cdev *dev, struct uio *uio, int flag) 1506390Sugen{ 151184689Sed int error, oresid = uio->uio_resid; 152184689Sed struct snp_softc *ss; 153184689Sed struct tty *tp; 1546447Sugen 155184689Sed if (uio->uio_resid == 0) 156184689Sed return (0); 157184689Sed 158184689Sed error = devfs_get_cdevpriv((void **)&ss); 159181755Sed if (error != 0) 160181755Sed return (error); 161223575Sed 162184689Sed tp = ss->snp_tty; 163184689Sed if (tp == NULL || tty_gone(tp)) 1646390Sugen return (EIO); 1656390Sugen 166184689Sed tty_lock(tp); 167184689Sed for (;;) { 168184689Sed error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); 169184689Sed if (error != 0 || uio->uio_resid != oresid) 170184689Sed break; 1716390Sugen 172184689Sed /* Wait for more data. */ 173184689Sed if (flag & O_NONBLOCK) { 174184689Sed error = EWOULDBLOCK; 175184689Sed break; 1766390Sugen } 177184689Sed error = cv_wait_sig(&ss->snp_outwait, tp->t_mtx); 178184689Sed if (error != 0) 1796390Sugen break; 180184689Sed if (tty_gone(tp)) { 181184689Sed error = EIO; 182184689Sed break; 1836390Sugen } 1846390Sugen } 185184689Sed tty_unlock(tp); 1866390Sugen 18777748Sdd return (error); 1886390Sugen} 1896390Sugen 19077748Sddstatic int 191184689Sedsnp_write(struct cdev *dev, struct uio *uio, int flag) 1926390Sugen{ 193184689Sed struct snp_softc *ss; 194184689Sed struct tty *tp; 195196452Sed int error, len; 196184689Sed char in[SNP_INPUT_BUFSIZE]; 1976390Sugen 198184689Sed error = devfs_get_cdevpriv((void **)&ss); 199184689Sed if (error != 0) 200184689Sed return (error); 201223575Sed 202184689Sed tp = ss->snp_tty; 203184689Sed if (tp == NULL || tty_gone(tp)) 204184689Sed return (EIO); 2056390Sugen 206184689Sed while (uio->uio_resid > 0) { 207184689Sed /* Read new data. */ 208184689Sed len = imin(uio->uio_resid, sizeof in); 209184689Sed error = uiomove(in, len, uio); 210184689Sed if (error != 0) 211184689Sed return (error); 2126390Sugen 213184689Sed tty_lock(tp); 2146455Sugen 215184689Sed /* Driver could have abandoned the TTY in the mean time. */ 216184689Sed if (tty_gone(tp)) { 217184689Sed tty_unlock(tp); 218184689Sed return (ENXIO); 219184689Sed } 220184689Sed 2216447Sugen /* 222184689Sed * Deliver data to the TTY. Ignore errors for now, 223184689Sed * because we shouldn't bail out when we're running 224184689Sed * close to the watermarks. 2256447Sugen */ 226196452Sed ttydisc_rint_simple(tp, in, len); 227196452Sed ttydisc_rint_done(tp); 2286390Sugen 229184689Sed tty_unlock(tp); 2306447Sugen } 2316390Sugen 2326390Sugen return (0); 2336390Sugen} 2346390Sugen 23577748Sddstatic int 236184689Sedsnp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 237167851Srodrigc struct thread *td) 2386390Sugen{ 239184689Sed struct snp_softc *ss; 240174888Skib struct tty *tp; 241184689Sed int error; 2426390Sugen 243184689Sed error = devfs_get_cdevpriv((void **)&ss); 244181755Sed if (error != 0) 245181755Sed return (error); 246181755Sed 2476390Sugen switch (cmd) { 2486390Sugen case SNPSTTY: 249184689Sed /* Bind TTY to snoop instance. */ 250191999Sed SNP_LOCK(); 251184689Sed if (ss->snp_tty != NULL) { 252191999Sed SNP_UNLOCK(); 253184689Sed return (EBUSY); 254150298Scognet } 255224778Srwatson /* 256224778Srwatson * XXXRW / XXXJA: no capability check here. 257224778Srwatson */ 258192062Sed error = ttyhook_register(&ss->snp_tty, td->td_proc, 259192062Sed *(int *)data, &snp_hook, ss); 260191999Sed SNP_UNLOCK(); 261184689Sed if (error != 0) 262184689Sed return (error); 2636455Sugen 264184689Sed /* Now that went okay, allocate a buffer for the queue. */ 265184689Sed tp = ss->snp_tty; 266184689Sed tty_lock(tp); 267184689Sed ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE); 268184689Sed tty_unlock(tp); 269174888Skib 270184689Sed return (0); 2716390Sugen case SNPGTTY: 272184689Sed /* Obtain device number of associated TTY. */ 273184689Sed if (ss->snp_tty == NULL) 274184689Sed *(dev_t *)data = NODEV; 2756390Sugen else 276184689Sed *(dev_t *)data = tty_udev(ss->snp_tty); 277184689Sed return (0); 2786390Sugen case FIONREAD: 279184689Sed tp = ss->snp_tty; 280184689Sed if (tp != NULL) { 281184689Sed tty_lock(tp); 282184689Sed *(int *)data = ttyoutq_bytesused(&ss->snp_outq); 283184689Sed tty_unlock(tp); 284184689Sed } else { 285184689Sed *(int *)data = 0; 286184689Sed } 287184689Sed return (0); 2886390Sugen default: 2896390Sugen return (ENOTTY); 2906390Sugen } 2916390Sugen} 2926390Sugen 29377748Sddstatic int 294184689Sedsnp_poll(struct cdev *dev, int events, struct thread *td) 2956390Sugen{ 296184689Sed struct snp_softc *ss; 297184689Sed struct tty *tp; 29877748Sdd int revents; 2996390Sugen 300184689Sed if (devfs_get_cdevpriv((void **)&ss) != 0) 301181755Sed return (events & 302181755Sed (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 303181755Sed 30477748Sdd revents = 0; 305184689Sed 30646568Speter if (events & (POLLIN | POLLRDNORM)) { 307184689Sed tp = ss->snp_tty; 308184689Sed if (tp != NULL) { 309184689Sed tty_lock(tp); 310184689Sed if (ttyoutq_bytesused(&ss->snp_outq) > 0) 311184689Sed revents |= events & (POLLIN | POLLRDNORM); 312184689Sed tty_unlock(tp); 313184689Sed } 31446568Speter } 315184689Sed 316184689Sed if (revents == 0) 317184689Sed selrecord(td, &ss->snp_outpoll); 318184689Sed 31929355Speter return (revents); 3206390Sugen} 3216390Sugen 322184689Sed/* 323184689Sed * TTY hook events. 324184689Sed */ 325184689Sed 32677016Sddstatic int 327167851Srodrigcsnp_modevent(module_t mod, int type, void *data) 32812517Sjulian{ 32912517Sjulian 33077016Sdd switch (type) { 33177016Sdd case MOD_LOAD: 332184689Sed snp_dev = make_dev(&snp_cdevsw, 0, 333184689Sed UID_ROOT, GID_WHEEL, 0600, "snp"); 334184689Sed return (0); 33577016Sdd case MOD_UNLOAD: 336184689Sed /* XXX: Make existing users leave. */ 337184689Sed destroy_dev(snp_dev); 338184689Sed return (0); 33977016Sdd default: 340132199Sphk return (EOPNOTSUPP); 34177016Sdd } 34212517Sjulian} 34312517Sjulian 344184689Sedstatic void 345184689Sedsnp_getc_capture(struct tty *tp, const void *buf, size_t len) 346184689Sed{ 347184689Sed struct snp_softc *ss = ttyhook_softc(tp); 348184689Sed 349184689Sed ttyoutq_write(&ss->snp_outq, buf, len); 350184689Sed 351184689Sed cv_broadcast(&ss->snp_outwait); 352184689Sed selwakeup(&ss->snp_outpoll); 353184689Sed} 354184689Sed 35577016Sddstatic moduledata_t snp_mod = { 356184689Sed "snp", 357184689Sed snp_modevent, 358184689Sed NULL 35977016Sdd}; 360184689Sed 361126077SphkDECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 362