1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 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 ss->snp_tty = NULL; 127 } 128 129 cv_destroy(&ss->snp_outwait); 130 free(ss, M_SNP); 131 } 132 133 /* 134 * Snoop device node routines. 135 */ 136 137 static int 138 snp_open(struct cdev *dev, int flag, int mode, struct thread *td) 139 { 140 struct snp_softc *ss; 141 142 /* Allocate per-snoop data. */ 143 ss = malloc(sizeof(struct snp_softc), M_SNP, M_WAITOK|M_ZERO); 144 cv_init(&ss->snp_outwait, "snp out"); 145 146 devfs_set_cdevpriv(ss, snp_dtor); 147 148 return (0); 149 } 150 151 static int 152 snp_read(struct cdev *dev, struct uio *uio, int flag) 153 { 154 int error, oresid = uio->uio_resid; 155 struct snp_softc *ss; 156 struct tty *tp; 157 158 if (uio->uio_resid == 0) 159 return (0); 160 161 error = devfs_get_cdevpriv((void **)&ss); 162 if (error != 0) 163 return (error); 164 165 tp = ss->snp_tty; 166 if (tp == NULL || tty_gone(tp)) 167 return (EIO); 168 169 tty_lock(tp); 170 for (;;) { 171 error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); 172 if (error != 0 || uio->uio_resid != oresid) 173 break; 174 175 /* Wait for more data. */ 176 if (flag & O_NONBLOCK) { 177 error = EWOULDBLOCK; 178 break; 179 } 180 error = cv_wait_sig(&ss->snp_outwait, tty_getlock(tp)); 181 if (error != 0) 182 break; 183 if (tty_gone(tp)) { 184 error = EIO; 185 break; 186 } 187 } 188 tty_unlock(tp); 189 190 return (error); 191 } 192 193 static int 194 snp_write(struct cdev *dev, struct uio *uio, int flag) 195 { 196 struct snp_softc *ss; 197 struct tty *tp; 198 int error, len; 199 char in[SNP_INPUT_BUFSIZE]; 200 201 error = devfs_get_cdevpriv((void **)&ss); 202 if (error != 0) 203 return (error); 204 205 tp = ss->snp_tty; 206 if (tp == NULL || tty_gone(tp)) 207 return (EIO); 208 209 while (uio->uio_resid > 0) { 210 /* Read new data. */ 211 len = imin(uio->uio_resid, sizeof in); 212 error = uiomove(in, len, uio); 213 if (error != 0) 214 return (error); 215 216 tty_lock(tp); 217 218 /* Driver could have abandoned the TTY in the mean time. */ 219 if (tty_gone(tp)) { 220 tty_unlock(tp); 221 return (ENXIO); 222 } 223 224 /* 225 * Deliver data to the TTY. Ignore errors for now, 226 * because we shouldn't bail out when we're running 227 * close to the watermarks. 228 */ 229 ttydisc_rint_simple(tp, in, len); 230 ttydisc_rint_done(tp); 231 232 tty_unlock(tp); 233 } 234 235 return (0); 236 } 237 238 static int 239 snp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 240 struct thread *td) 241 { 242 struct snp_softc *ss; 243 struct tty *tp; 244 int error; 245 246 error = devfs_get_cdevpriv((void **)&ss); 247 if (error != 0) 248 return (error); 249 250 switch (cmd) { 251 case SNPSTTY: 252 /* Bind TTY to snoop instance. */ 253 SNP_LOCK(); 254 tp = ss->snp_tty; 255 if (tp != NULL) { 256 if (*(int *)data == -1) { 257 tty_lock(tp); 258 ss->snp_tty = NULL; 259 ttyoutq_free(&ss->snp_outq); 260 ttyhook_unregister(tp); 261 error = 0; 262 } else { 263 error = EBUSY; 264 } 265 SNP_UNLOCK(); 266 return (error); 267 } 268 /* 269 * XXXRW / XXXJA: no capability check here. 270 */ 271 error = ttyhook_register(&ss->snp_tty, td->td_proc, 272 *(int *)data, &snp_hook, ss); 273 SNP_UNLOCK(); 274 if (error != 0) 275 return (error); 276 277 /* Now that went okay, allocate a buffer for the queue. */ 278 tp = ss->snp_tty; 279 tty_lock(tp); 280 ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE); 281 tty_unlock(tp); 282 283 return (0); 284 case SNPGTTY: 285 /* Obtain device number of associated TTY. */ 286 if (ss->snp_tty == NULL) 287 *(dev_t *)data = NODEV; 288 else 289 *(dev_t *)data = tty_udev(ss->snp_tty); 290 return (0); 291 case SNPGTYY_32DEV: 292 if (ss->snp_tty == NULL) 293 *(uint32_t *)data = -1; 294 else 295 *(uint32_t *)data = tty_udev(ss->snp_tty); /* trunc */ 296 return (0); 297 case FIONREAD: 298 tp = ss->snp_tty; 299 if (tp != NULL) { 300 tty_lock(tp); 301 if (tty_gone(tp)) 302 *(int *)data = SNP_TTYCLOSE; 303 else 304 *(int *)data = ttyoutq_bytesused(&ss->snp_outq); 305 tty_unlock(tp); 306 } else { 307 *(int *)data = SNP_DETACH; 308 } 309 return (0); 310 default: 311 return (ENOTTY); 312 } 313 } 314 315 static int 316 snp_poll(struct cdev *dev, int events, struct thread *td) 317 { 318 struct snp_softc *ss; 319 struct tty *tp; 320 int revents; 321 322 if (devfs_get_cdevpriv((void **)&ss) != 0) 323 return (events & 324 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 325 326 revents = 0; 327 328 if (events & (POLLIN | POLLRDNORM)) { 329 tp = ss->snp_tty; 330 if (tp != NULL) { 331 tty_lock(tp); 332 if (ttyoutq_bytesused(&ss->snp_outq) > 0) 333 revents |= events & (POLLIN | POLLRDNORM); 334 tty_unlock(tp); 335 } 336 } 337 338 if (revents == 0) 339 selrecord(td, &ss->snp_outpoll); 340 341 return (revents); 342 } 343 344 /* 345 * TTY hook events. 346 */ 347 348 static int 349 snp_modevent(module_t mod, int type, void *data) 350 { 351 352 switch (type) { 353 case MOD_LOAD: 354 snp_dev = make_dev(&snp_cdevsw, 0, 355 UID_ROOT, GID_WHEEL, 0600, "snp"); 356 return (0); 357 case MOD_UNLOAD: 358 /* XXX: Make existing users leave. */ 359 destroy_dev(snp_dev); 360 return (0); 361 default: 362 return (EOPNOTSUPP); 363 } 364 } 365 366 static void 367 snp_getc_capture(struct tty *tp, const void *buf, size_t len) 368 { 369 struct snp_softc *ss = ttyhook_softc(tp); 370 371 ttyoutq_write(&ss->snp_outq, buf, len); 372 373 cv_broadcast(&ss->snp_outwait); 374 selwakeup(&ss->snp_outpoll); 375 } 376 377 static moduledata_t snp_mod = { 378 "snp", 379 snp_modevent, 380 NULL 381 }; 382 383 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 384