1098ca2bdSWarner Losh /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4932ef5b5SEd Schouten * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 5932ef5b5SEd Schouten * All rights reserved. 6dde8a05bSUgen J.S. Antsilevich * 7932ef5b5SEd Schouten * Redistribution and use in source and binary forms, with or without 8932ef5b5SEd Schouten * modification, are permitted provided that the following conditions 9932ef5b5SEd Schouten * are met: 10932ef5b5SEd Schouten * 1. Redistributions of source code must retain the above copyright 11932ef5b5SEd Schouten * notice, this list of conditions and the following disclaimer. 12932ef5b5SEd Schouten * 2. Redistributions in binary form must reproduce the above copyright 13932ef5b5SEd Schouten * notice, this list of conditions and the following disclaimer in the 14932ef5b5SEd Schouten * documentation and/or other materials provided with the distribution. 15dde8a05bSUgen J.S. Antsilevich * 16932ef5b5SEd Schouten * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17932ef5b5SEd Schouten * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18932ef5b5SEd Schouten * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19932ef5b5SEd Schouten * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20932ef5b5SEd Schouten * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21932ef5b5SEd Schouten * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22932ef5b5SEd Schouten * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23932ef5b5SEd Schouten * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24932ef5b5SEd Schouten * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25932ef5b5SEd Schouten * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26932ef5b5SEd Schouten * SUCH DAMAGE. 27dde8a05bSUgen J.S. Antsilevich */ 28dde8a05bSUgen J.S. Antsilevich 29945ff31aSDavid E. O'Brien #include <sys/cdefs.h> 30945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31945ff31aSDavid E. O'Brien 32dde8a05bSUgen J.S. Antsilevich #include <sys/param.h> 33932ef5b5SEd Schouten #include <sys/conf.h> 341ef0fc1dSPoul-Henning Kamp #include <sys/fcntl.h> 3571455815SBruce Evans #include <sys/filio.h> 36dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h> 37932ef5b5SEd Schouten #include <sys/malloc.h> 38fe12f24bSPoul-Henning Kamp #include <sys/module.h> 39932ef5b5SEd Schouten #include <sys/poll.h> 40a9385ad1SAlexander Motin #include <sys/proc.h> 4187f6c662SJulian Elischer #include <sys/snoop.h> 42932ef5b5SEd Schouten #include <sys/sx.h> 43932ef5b5SEd Schouten #include <sys/systm.h> 44932ef5b5SEd Schouten #include <sys/tty.h> 451ef0fc1dSPoul-Henning Kamp #include <sys/uio.h> 4687f6c662SJulian Elischer 47932ef5b5SEd Schouten static struct cdev *snp_dev; 487fb6f685SEd Schouten static MALLOC_DEFINE(M_SNP, "snp", "tty snoop device"); 497fb6f685SEd Schouten 50932ef5b5SEd Schouten /* XXX: should be mtx, but TTY can be locked by Giant. */ 517fb6f685SEd Schouten #if 0 527fb6f685SEd Schouten static struct mtx snp_register_lock; 537fb6f685SEd Schouten MTX_SYSINIT(snp_register_lock, &snp_register_lock, 547fb6f685SEd Schouten "tty snoop registration", MTX_DEF); 557fb6f685SEd Schouten #define SNP_LOCK() mtx_lock(&snp_register_lock) 567fb6f685SEd Schouten #define SNP_UNLOCK() mtx_unlock(&snp_register_lock) 577fb6f685SEd Schouten #else 58932ef5b5SEd Schouten static struct sx snp_register_lock; 59932ef5b5SEd Schouten SX_SYSINIT(snp_register_lock, &snp_register_lock, 60932ef5b5SEd Schouten "tty snoop registration"); 617fb6f685SEd Schouten #define SNP_LOCK() sx_xlock(&snp_register_lock) 627fb6f685SEd Schouten #define SNP_UNLOCK() sx_xunlock(&snp_register_lock) 637fb6f685SEd Schouten #endif 64932ef5b5SEd Schouten 6569921123SKonstantin Belousov #define SNPGTYY_32DEV _IOR('T', 89, uint32_t) 6669921123SKonstantin Belousov 67932ef5b5SEd Schouten /* 68932ef5b5SEd Schouten * There is no need to have a big input buffer. In most typical setups, 69932ef5b5SEd Schouten * we won't inject much data into the TTY, because users can't type 70932ef5b5SEd Schouten * really fast. 71932ef5b5SEd Schouten */ 72932ef5b5SEd Schouten #define SNP_INPUT_BUFSIZE 16 73932ef5b5SEd Schouten /* 74932ef5b5SEd Schouten * The output buffer has to be really big. Right now we don't support 75932ef5b5SEd Schouten * any form of flow control, which means we lost any data we can't 76932ef5b5SEd Schouten * accept. We set the output buffer size to about twice the size of a 77932ef5b5SEd Schouten * pseudo-terminal/virtual console's output buffer. 78932ef5b5SEd Schouten */ 79932ef5b5SEd Schouten #define SNP_OUTPUT_BUFSIZE 16384 80932ef5b5SEd Schouten 81932ef5b5SEd Schouten static d_open_t snp_open; 82932ef5b5SEd Schouten static d_read_t snp_read; 83932ef5b5SEd Schouten static d_write_t snp_write; 84932ef5b5SEd Schouten static d_ioctl_t snp_ioctl; 85932ef5b5SEd Schouten static d_poll_t snp_poll; 8687f6c662SJulian Elischer 874e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = { 88dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 89932ef5b5SEd Schouten .d_open = snp_open, 90932ef5b5SEd Schouten .d_read = snp_read, 91932ef5b5SEd Schouten .d_write = snp_write, 92932ef5b5SEd Schouten .d_ioctl = snp_ioctl, 93932ef5b5SEd Schouten .d_poll = snp_poll, 947ac40f5fSPoul-Henning Kamp .d_name = "snp", 954e2f199eSPoul-Henning Kamp }; 9687f6c662SJulian Elischer 97932ef5b5SEd Schouten static th_getc_capture_t snp_getc_capture; 98932ef5b5SEd Schouten 99932ef5b5SEd Schouten static struct ttyhook snp_hook = { 100932ef5b5SEd Schouten .th_getc_capture = snp_getc_capture, 101f09f49f1SDima Dorfman }; 10253ac6efbSJulian Elischer 103101f105dSDima Dorfman /* 104932ef5b5SEd Schouten * Per-instance structure. 105932ef5b5SEd Schouten * 106932ef5b5SEd Schouten * List of locks 107932ef5b5SEd Schouten * (r) locked by snp_register_lock on assignment 108932ef5b5SEd Schouten * (t) locked by tty_lock 109101f105dSDima Dorfman */ 110932ef5b5SEd Schouten struct snp_softc { 111932ef5b5SEd Schouten struct tty *snp_tty; /* (r) TTY we're snooping. */ 112932ef5b5SEd Schouten struct ttyoutq snp_outq; /* (t) Output queue. */ 113932ef5b5SEd Schouten struct cv snp_outwait; /* (t) Output wait queue. */ 114932ef5b5SEd Schouten struct selinfo snp_outpoll; /* (t) Output polling. */ 115101f105dSDima Dorfman }; 116101f105dSDima Dorfman 1172e37c8eaSEd Schouten static void 1182e37c8eaSEd Schouten snp_dtor(void *data) 1192e37c8eaSEd Schouten { 120932ef5b5SEd Schouten struct snp_softc *ss = data; 121932ef5b5SEd Schouten struct tty *tp; 1222e37c8eaSEd Schouten 123932ef5b5SEd Schouten tp = ss->snp_tty; 124932ef5b5SEd Schouten if (tp != NULL) { 125932ef5b5SEd Schouten tty_lock(tp); 126932ef5b5SEd Schouten ttyoutq_free(&ss->snp_outq); 127932ef5b5SEd Schouten ttyhook_unregister(tp); 128932ef5b5SEd Schouten } 129932ef5b5SEd Schouten 130932ef5b5SEd Schouten cv_destroy(&ss->snp_outwait); 131932ef5b5SEd Schouten free(ss, M_SNP); 132932ef5b5SEd Schouten } 133932ef5b5SEd Schouten 134932ef5b5SEd Schouten /* 135932ef5b5SEd Schouten * Snoop device node routines. 136932ef5b5SEd Schouten */ 137932ef5b5SEd Schouten 138932ef5b5SEd Schouten static int 139932ef5b5SEd Schouten snp_open(struct cdev *dev, int flag, int mode, struct thread *td) 140932ef5b5SEd Schouten { 141932ef5b5SEd Schouten struct snp_softc *ss; 142932ef5b5SEd Schouten 143932ef5b5SEd Schouten /* Allocate per-snoop data. */ 144932ef5b5SEd Schouten ss = malloc(sizeof(struct snp_softc), M_SNP, M_WAITOK|M_ZERO); 145932ef5b5SEd Schouten cv_init(&ss->snp_outwait, "snp out"); 146932ef5b5SEd Schouten 147932ef5b5SEd Schouten devfs_set_cdevpriv(ss, snp_dtor); 148932ef5b5SEd Schouten 149932ef5b5SEd Schouten return (0); 1502e37c8eaSEd Schouten } 1512e37c8eaSEd Schouten 15287f6c662SJulian Elischer static int 153932ef5b5SEd Schouten snp_read(struct cdev *dev, struct uio *uio, int flag) 154dde8a05bSUgen J.S. Antsilevich { 155932ef5b5SEd Schouten int error, oresid = uio->uio_resid; 156932ef5b5SEd Schouten struct snp_softc *ss; 157932ef5b5SEd Schouten struct tty *tp; 158dde8a05bSUgen J.S. Antsilevich 159932ef5b5SEd Schouten if (uio->uio_resid == 0) 160932ef5b5SEd Schouten return (0); 161932ef5b5SEd Schouten 162932ef5b5SEd Schouten error = devfs_get_cdevpriv((void **)&ss); 163932ef5b5SEd Schouten if (error != 0) 164932ef5b5SEd Schouten return (error); 165932ef5b5SEd Schouten 166932ef5b5SEd Schouten tp = ss->snp_tty; 167932ef5b5SEd Schouten if (tp == NULL || tty_gone(tp)) 168932ef5b5SEd Schouten return (EIO); 169932ef5b5SEd Schouten 170932ef5b5SEd Schouten tty_lock(tp); 171932ef5b5SEd Schouten for (;;) { 172932ef5b5SEd Schouten error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); 173932ef5b5SEd Schouten if (error != 0 || uio->uio_resid != oresid) 174932ef5b5SEd Schouten break; 175932ef5b5SEd Schouten 176932ef5b5SEd Schouten /* Wait for more data. */ 177932ef5b5SEd Schouten if (flag & O_NONBLOCK) { 178932ef5b5SEd Schouten error = EWOULDBLOCK; 179932ef5b5SEd Schouten break; 180932ef5b5SEd Schouten } 181*ed623289SKyle Evans error = cv_wait_sig(&ss->snp_outwait, tty_getlock(tp)); 182932ef5b5SEd Schouten if (error != 0) 183932ef5b5SEd Schouten break; 184932ef5b5SEd Schouten if (tty_gone(tp)) { 185932ef5b5SEd Schouten error = EIO; 186932ef5b5SEd Schouten break; 187932ef5b5SEd Schouten } 188932ef5b5SEd Schouten } 189932ef5b5SEd Schouten tty_unlock(tp); 190932ef5b5SEd Schouten 1912e37c8eaSEd Schouten return (error); 1922e37c8eaSEd Schouten } 19377f77631SPaul Traina 194932ef5b5SEd Schouten static int 195932ef5b5SEd Schouten snp_write(struct cdev *dev, struct uio *uio, int flag) 196019b4d63SUgen J.S. Antsilevich { 197932ef5b5SEd Schouten struct snp_softc *ss; 198019b4d63SUgen J.S. Antsilevich struct tty *tp; 1995c67885aSEd Schouten int error, len; 200932ef5b5SEd Schouten char in[SNP_INPUT_BUFSIZE]; 201019b4d63SUgen J.S. Antsilevich 202932ef5b5SEd Schouten error = devfs_get_cdevpriv((void **)&ss); 203932ef5b5SEd Schouten if (error != 0) 204932ef5b5SEd Schouten return (error); 205932ef5b5SEd Schouten 206932ef5b5SEd Schouten tp = ss->snp_tty; 207932ef5b5SEd Schouten if (tp == NULL || tty_gone(tp)) 208932ef5b5SEd Schouten return (EIO); 209932ef5b5SEd Schouten 210932ef5b5SEd Schouten while (uio->uio_resid > 0) { 211932ef5b5SEd Schouten /* Read new data. */ 212932ef5b5SEd Schouten len = imin(uio->uio_resid, sizeof in); 213932ef5b5SEd Schouten error = uiomove(in, len, uio); 214932ef5b5SEd Schouten if (error != 0) 215932ef5b5SEd Schouten return (error); 216932ef5b5SEd Schouten 217932ef5b5SEd Schouten tty_lock(tp); 218932ef5b5SEd Schouten 219932ef5b5SEd Schouten /* Driver could have abandoned the TTY in the mean time. */ 220932ef5b5SEd Schouten if (tty_gone(tp)) { 221932ef5b5SEd Schouten tty_unlock(tp); 222932ef5b5SEd Schouten return (ENXIO); 223932ef5b5SEd Schouten } 224019b4d63SUgen J.S. Antsilevich 225019b4d63SUgen J.S. Antsilevich /* 226932ef5b5SEd Schouten * Deliver data to the TTY. Ignore errors for now, 227932ef5b5SEd Schouten * because we shouldn't bail out when we're running 228932ef5b5SEd Schouten * close to the watermarks. 229019b4d63SUgen J.S. Antsilevich */ 2305c67885aSEd Schouten ttydisc_rint_simple(tp, in, len); 231932ef5b5SEd Schouten ttydisc_rint_done(tp); 2325c67885aSEd Schouten 233932ef5b5SEd Schouten tty_unlock(tp); 23476e90dbcSBrian Feldman } 235dde8a05bSUgen J.S. Antsilevich 236542a8db5SKonstantin Belousov return (0); 237964587caSUgen J.S. Antsilevich } 238dde8a05bSUgen J.S. Antsilevich 23987f6c662SJulian Elischer static int 240932ef5b5SEd Schouten snp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 2417409f6cdSCraig Rodrigues struct thread *td) 242dde8a05bSUgen J.S. Antsilevich { 243932ef5b5SEd Schouten struct snp_softc *ss; 244316d90a3SKonstantin Belousov struct tty *tp; 245932ef5b5SEd Schouten int error; 246dde8a05bSUgen J.S. Antsilevich 247932ef5b5SEd Schouten error = devfs_get_cdevpriv((void **)&ss); 2482e37c8eaSEd Schouten if (error != 0) 2492e37c8eaSEd Schouten return (error); 2502e37c8eaSEd Schouten 251dde8a05bSUgen J.S. Antsilevich switch (cmd) { 252dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 253932ef5b5SEd Schouten /* Bind TTY to snoop instance. */ 2547fb6f685SEd Schouten SNP_LOCK(); 255932ef5b5SEd Schouten if (ss->snp_tty != NULL) { 2567fb6f685SEd Schouten SNP_UNLOCK(); 257932ef5b5SEd Schouten return (EBUSY); 25830ce1aadSOlivier Houchard } 259a9d2f8d8SRobert Watson /* 260a9d2f8d8SRobert Watson * XXXRW / XXXJA: no capability check here. 261a9d2f8d8SRobert Watson */ 262aa16f86cSEd Schouten error = ttyhook_register(&ss->snp_tty, td->td_proc, 263aa16f86cSEd Schouten *(int *)data, &snp_hook, ss); 2647fb6f685SEd Schouten SNP_UNLOCK(); 265932ef5b5SEd Schouten if (error != 0) 266932ef5b5SEd Schouten return (error); 267964587caSUgen J.S. Antsilevich 268932ef5b5SEd Schouten /* Now that went okay, allocate a buffer for the queue. */ 269932ef5b5SEd Schouten tp = ss->snp_tty; 270932ef5b5SEd Schouten tty_lock(tp); 271932ef5b5SEd Schouten ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE); 272932ef5b5SEd Schouten tty_unlock(tp); 273316d90a3SKonstantin Belousov 274932ef5b5SEd Schouten return (0); 275dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 276932ef5b5SEd Schouten /* Obtain device number of associated TTY. */ 277932ef5b5SEd Schouten if (ss->snp_tty == NULL) 278932ef5b5SEd Schouten *(dev_t *)data = NODEV; 279dde8a05bSUgen J.S. Antsilevich else 280932ef5b5SEd Schouten *(dev_t *)data = tty_udev(ss->snp_tty); 281932ef5b5SEd Schouten return (0); 28269921123SKonstantin Belousov case SNPGTYY_32DEV: 28369921123SKonstantin Belousov if (ss->snp_tty == NULL) 28469921123SKonstantin Belousov *(uint32_t *)data = -1; 28569921123SKonstantin Belousov else 28669921123SKonstantin Belousov *(uint32_t *)data = tty_udev(ss->snp_tty); /* trunc */ 28769921123SKonstantin Belousov return (0); 288dde8a05bSUgen J.S. Antsilevich case FIONREAD: 289932ef5b5SEd Schouten tp = ss->snp_tty; 290932ef5b5SEd Schouten if (tp != NULL) { 291932ef5b5SEd Schouten tty_lock(tp); 292932ef5b5SEd Schouten *(int *)data = ttyoutq_bytesused(&ss->snp_outq); 293932ef5b5SEd Schouten tty_unlock(tp); 294964587caSUgen J.S. Antsilevich } else { 295932ef5b5SEd Schouten *(int *)data = 0; 296964587caSUgen J.S. Antsilevich } 297932ef5b5SEd Schouten return (0); 298dde8a05bSUgen J.S. Antsilevich default: 299dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 300dde8a05bSUgen J.S. Antsilevich } 301dde8a05bSUgen J.S. Antsilevich } 302dde8a05bSUgen J.S. Antsilevich 30387f6c662SJulian Elischer static int 304932ef5b5SEd Schouten snp_poll(struct cdev *dev, int events, struct thread *td) 305dde8a05bSUgen J.S. Antsilevich { 306932ef5b5SEd Schouten struct snp_softc *ss; 307932ef5b5SEd Schouten struct tty *tp; 308f09f49f1SDima Dorfman int revents; 309dde8a05bSUgen J.S. Antsilevich 310932ef5b5SEd Schouten if (devfs_get_cdevpriv((void **)&ss) != 0) 3112e37c8eaSEd Schouten return (events & 3122e37c8eaSEd Schouten (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 3132e37c8eaSEd Schouten 314f09f49f1SDima Dorfman revents = 0; 315932ef5b5SEd Schouten 316dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 317932ef5b5SEd Schouten tp = ss->snp_tty; 318932ef5b5SEd Schouten if (tp != NULL) { 319932ef5b5SEd Schouten tty_lock(tp); 320932ef5b5SEd Schouten if (ttyoutq_bytesused(&ss->snp_outq) > 0) 321659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 322932ef5b5SEd Schouten tty_unlock(tp); 323dfd5dee1SPeter Wemm } 324932ef5b5SEd Schouten } 325932ef5b5SEd Schouten 326932ef5b5SEd Schouten if (revents == 0) 327932ef5b5SEd Schouten selrecord(td, &ss->snp_outpoll); 328932ef5b5SEd Schouten 329659ffb48SPeter Wemm return (revents); 330dde8a05bSUgen J.S. Antsilevich } 331dde8a05bSUgen J.S. Antsilevich 332932ef5b5SEd Schouten /* 333932ef5b5SEd Schouten * TTY hook events. 334932ef5b5SEd Schouten */ 335932ef5b5SEd Schouten 33647eaa5f5SDima Dorfman static int 3377409f6cdSCraig Rodrigues snp_modevent(module_t mod, int type, void *data) 33853ac6efbSJulian Elischer { 33953ac6efbSJulian Elischer 34047eaa5f5SDima Dorfman switch (type) { 34147eaa5f5SDima Dorfman case MOD_LOAD: 342932ef5b5SEd Schouten snp_dev = make_dev(&snp_cdevsw, 0, 343932ef5b5SEd Schouten UID_ROOT, GID_WHEEL, 0600, "snp"); 344932ef5b5SEd Schouten return (0); 34547eaa5f5SDima Dorfman case MOD_UNLOAD: 346932ef5b5SEd Schouten /* XXX: Make existing users leave. */ 347932ef5b5SEd Schouten destroy_dev(snp_dev); 348932ef5b5SEd Schouten return (0); 34947eaa5f5SDima Dorfman default: 3503e019deaSPoul-Henning Kamp return (EOPNOTSUPP); 35147eaa5f5SDima Dorfman } 352932ef5b5SEd Schouten } 353932ef5b5SEd Schouten 354932ef5b5SEd Schouten static void 355932ef5b5SEd Schouten snp_getc_capture(struct tty *tp, const void *buf, size_t len) 356932ef5b5SEd Schouten { 357932ef5b5SEd Schouten struct snp_softc *ss = ttyhook_softc(tp); 358932ef5b5SEd Schouten 359932ef5b5SEd Schouten ttyoutq_write(&ss->snp_outq, buf, len); 360932ef5b5SEd Schouten 361932ef5b5SEd Schouten cv_broadcast(&ss->snp_outwait); 362932ef5b5SEd Schouten selwakeup(&ss->snp_outpoll); 3637198bf47SJulian Elischer } 36453ac6efbSJulian Elischer 36547eaa5f5SDima Dorfman static moduledata_t snp_mod = { 36647eaa5f5SDima Dorfman "snp", 36747eaa5f5SDima Dorfman snp_modevent, 36847eaa5f5SDima Dorfman NULL 36947eaa5f5SDima Dorfman }; 370932ef5b5SEd Schouten 371b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 372