1 /*- 2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/conf.h> 32 #include <sys/fcntl.h> 33 #include <sys/filio.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/poll.h> 38 #include <sys/proc.h> 39 #include <sys/snoop.h> 40 #include <sys/sx.h> 41 #include <sys/systm.h> 42 #include <sys/tty.h> 43 #include <sys/uio.h> 44 45 static struct cdev *snp_dev; 46 static MALLOC_DEFINE(M_SNP, "snp", "tty snoop device"); 47 48 /* XXX: should be mtx, but TTY can be locked by Giant. */ 49 #if 0 50 static struct mtx snp_register_lock; 51 MTX_SYSINIT(snp_register_lock, &snp_register_lock, 52 "tty snoop registration", MTX_DEF); 53 #define SNP_LOCK() mtx_lock(&snp_register_lock) 54 #define SNP_UNLOCK() mtx_unlock(&snp_register_lock) 55 #else 56 static struct sx snp_register_lock; 57 SX_SYSINIT(snp_register_lock, &snp_register_lock, 58 "tty snoop registration"); 59 #define SNP_LOCK() sx_xlock(&snp_register_lock) 60 #define SNP_UNLOCK() sx_xunlock(&snp_register_lock) 61 #endif 62 63 #define SNPGTYY_32DEV _IOR('T', 89, uint32_t) 64 65 /* 66 * There is no need to have a big input buffer. In most typical setups, 67 * we won't inject much data into the TTY, because users can't type 68 * really fast. 69 */ 70 #define SNP_INPUT_BUFSIZE 16 71 /* 72 * The output buffer has to be really big. Right now we don't support 73 * any form of flow control, which means we lost any data we can't 74 * accept. We set the output buffer size to about twice the size of a 75 * pseudo-terminal/virtual console's output buffer. 76 */ 77 #define SNP_OUTPUT_BUFSIZE 16384 78 79 static d_open_t snp_open; 80 static d_read_t snp_read; 81 static d_write_t snp_write; 82 static d_ioctl_t snp_ioctl; 83 static d_poll_t snp_poll; 84 85 static struct cdevsw snp_cdevsw = { 86 .d_version = D_VERSION, 87 .d_open = snp_open, 88 .d_read = snp_read, 89 .d_write = snp_write, 90 .d_ioctl = snp_ioctl, 91 .d_poll = snp_poll, 92 .d_name = "snp", 93 }; 94 95 static th_getc_capture_t snp_getc_capture; 96 97 static struct ttyhook snp_hook = { 98 .th_getc_capture = snp_getc_capture, 99 }; 100 101 /* 102 * Per-instance structure. 103 * 104 * List of locks 105 * (r) locked by snp_register_lock on assignment 106 * (t) locked by tty_lock 107 */ 108 struct snp_softc { 109 struct tty *snp_tty; /* (r) TTY we're snooping. */ 110 struct ttyoutq snp_outq; /* (t) Output queue. */ 111 struct cv snp_outwait; /* (t) Output wait queue. */ 112 struct selinfo snp_outpoll; /* (t) Output polling. */ 113 }; 114 115 static void 116 snp_dtor(void *data) 117 { 118 struct snp_softc *ss = data; 119 struct tty *tp; 120 121 tp = ss->snp_tty; 122 if (tp != NULL) { 123 tty_lock(tp); 124 ttyoutq_free(&ss->snp_outq); 125 ttyhook_unregister(tp); 126 } 127 128 cv_destroy(&ss->snp_outwait); 129 free(ss, M_SNP); 130 } 131 132 /* 133 * Snoop device node routines. 134 */ 135 136 static int 137 snp_open(struct cdev *dev, int flag, int mode, struct thread *td) 138 { 139 struct snp_softc *ss; 140 141 /* Allocate per-snoop data. */ 142 ss = malloc(sizeof(struct snp_softc), M_SNP, M_WAITOK|M_ZERO); 143 cv_init(&ss->snp_outwait, "snp out"); 144 145 devfs_set_cdevpriv(ss, snp_dtor); 146 147 return (0); 148 } 149 150 static int 151 snp_read(struct cdev *dev, struct uio *uio, int flag) 152 { 153 int error, oresid = uio->uio_resid; 154 struct snp_softc *ss; 155 struct tty *tp; 156 157 if (uio->uio_resid == 0) 158 return (0); 159 160 error = devfs_get_cdevpriv((void **)&ss); 161 if (error != 0) 162 return (error); 163 164 tp = ss->snp_tty; 165 if (tp == NULL || tty_gone(tp)) 166 return (EIO); 167 168 tty_lock(tp); 169 for (;;) { 170 error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); 171 if (error != 0 || uio->uio_resid != oresid) 172 break; 173 174 /* Wait for more data. */ 175 if (flag & O_NONBLOCK) { 176 error = EWOULDBLOCK; 177 break; 178 } 179 error = cv_wait_sig(&ss->snp_outwait, tp->t_mtx); 180 if (error != 0) 181 break; 182 if (tty_gone(tp)) { 183 error = EIO; 184 break; 185 } 186 } 187 tty_unlock(tp); 188 189 return (error); 190 } 191 192 static int 193 snp_write(struct cdev *dev, struct uio *uio, int flag) 194 { 195 struct snp_softc *ss; 196 struct tty *tp; 197 int error, len; 198 char in[SNP_INPUT_BUFSIZE]; 199 200 error = devfs_get_cdevpriv((void **)&ss); 201 if (error != 0) 202 return (error); 203 204 tp = ss->snp_tty; 205 if (tp == NULL || tty_gone(tp)) 206 return (EIO); 207 208 while (uio->uio_resid > 0) { 209 /* Read new data. */ 210 len = imin(uio->uio_resid, sizeof in); 211 error = uiomove(in, len, uio); 212 if (error != 0) 213 return (error); 214 215 tty_lock(tp); 216 217 /* Driver could have abandoned the TTY in the mean time. */ 218 if (tty_gone(tp)) { 219 tty_unlock(tp); 220 return (ENXIO); 221 } 222 223 /* 224 * Deliver data to the TTY. Ignore errors for now, 225 * because we shouldn't bail out when we're running 226 * close to the watermarks. 227 */ 228 ttydisc_rint_simple(tp, in, len); 229 ttydisc_rint_done(tp); 230 231 tty_unlock(tp); 232 } 233 234 return (0); 235 } 236 237 static int 238 snp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 239 struct thread *td) 240 { 241 struct snp_softc *ss; 242 struct tty *tp; 243 int error; 244 245 error = devfs_get_cdevpriv((void **)&ss); 246 if (error != 0) 247 return (error); 248 249 switch (cmd) { 250 case SNPSTTY: 251 /* Bind TTY to snoop instance. */ 252 SNP_LOCK(); 253 if (ss->snp_tty != NULL) { 254 SNP_UNLOCK(); 255 return (EBUSY); 256 } 257 /* 258 * XXXRW / XXXJA: no capability check here. 259 */ 260 error = ttyhook_register(&ss->snp_tty, td->td_proc, 261 *(int *)data, &snp_hook, ss); 262 SNP_UNLOCK(); 263 if (error != 0) 264 return (error); 265 266 /* Now that went okay, allocate a buffer for the queue. */ 267 tp = ss->snp_tty; 268 tty_lock(tp); 269 ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE); 270 tty_unlock(tp); 271 272 return (0); 273 case SNPGTTY: 274 /* Obtain device number of associated TTY. */ 275 if (ss->snp_tty == NULL) 276 *(dev_t *)data = NODEV; 277 else 278 *(dev_t *)data = tty_udev(ss->snp_tty); 279 return (0); 280 case SNPGTYY_32DEV: 281 if (ss->snp_tty == NULL) 282 *(uint32_t *)data = -1; 283 else 284 *(uint32_t *)data = tty_udev(ss->snp_tty); /* trunc */ 285 return (0); 286 case FIONREAD: 287 tp = ss->snp_tty; 288 if (tp != NULL) { 289 tty_lock(tp); 290 *(int *)data = ttyoutq_bytesused(&ss->snp_outq); 291 tty_unlock(tp); 292 } else { 293 *(int *)data = 0; 294 } 295 return (0); 296 default: 297 return (ENOTTY); 298 } 299 } 300 301 static int 302 snp_poll(struct cdev *dev, int events, struct thread *td) 303 { 304 struct snp_softc *ss; 305 struct tty *tp; 306 int revents; 307 308 if (devfs_get_cdevpriv((void **)&ss) != 0) 309 return (events & 310 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 311 312 revents = 0; 313 314 if (events & (POLLIN | POLLRDNORM)) { 315 tp = ss->snp_tty; 316 if (tp != NULL) { 317 tty_lock(tp); 318 if (ttyoutq_bytesused(&ss->snp_outq) > 0) 319 revents |= events & (POLLIN | POLLRDNORM); 320 tty_unlock(tp); 321 } 322 } 323 324 if (revents == 0) 325 selrecord(td, &ss->snp_outpoll); 326 327 return (revents); 328 } 329 330 /* 331 * TTY hook events. 332 */ 333 334 static int 335 snp_modevent(module_t mod, int type, void *data) 336 { 337 338 switch (type) { 339 case MOD_LOAD: 340 snp_dev = make_dev(&snp_cdevsw, 0, 341 UID_ROOT, GID_WHEEL, 0600, "snp"); 342 return (0); 343 case MOD_UNLOAD: 344 /* XXX: Make existing users leave. */ 345 destroy_dev(snp_dev); 346 return (0); 347 default: 348 return (EOPNOTSUPP); 349 } 350 } 351 352 static void 353 snp_getc_capture(struct tty *tp, const void *buf, size_t len) 354 { 355 struct snp_softc *ss = ttyhook_softc(tp); 356 357 ttyoutq_write(&ss->snp_outq, buf, len); 358 359 cv_broadcast(&ss->snp_outwait); 360 selwakeup(&ss->snp_outpoll); 361 } 362 363 static moduledata_t snp_mod = { 364 "snp", 365 snp_modevent, 366 NULL 367 }; 368 369 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 370