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: head/sys/dev/snp/snp.c 196452 2009-08-23 08:04:40Z ed $");
|
28__FBSDID("$FreeBSD: head/sys/dev/snp/snp.c 223575 2011-06-26 18:26:20Z ed $"); |
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 45static struct cdev *snp_dev; 46static MALLOC_DEFINE(M_SNP, "snp", "tty snoop device"); 47 48/* XXX: should be mtx, but TTY can be locked by Giant. */ 49#if 0 50static struct mtx snp_register_lock; 51MTX_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 56static struct sx snp_register_lock; 57SX_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/* 64 * There is no need to have a big input buffer. In most typical setups, 65 * we won't inject much data into the TTY, because users can't type 66 * really fast. 67 */ 68#define SNP_INPUT_BUFSIZE 16 69/* 70 * The output buffer has to be really big. Right now we don't support 71 * any form of flow control, which means we lost any data we can't 72 * accept. We set the output buffer size to about twice the size of a 73 * pseudo-terminal/virtual console's output buffer. 74 */ 75#define SNP_OUTPUT_BUFSIZE 16384 76 77static d_open_t snp_open; 78static d_read_t snp_read; 79static d_write_t snp_write; 80static d_ioctl_t snp_ioctl; 81static d_poll_t snp_poll; 82 83static struct cdevsw snp_cdevsw = { 84 .d_version = D_VERSION, 85 .d_open = snp_open, 86 .d_read = snp_read, 87 .d_write = snp_write, 88 .d_ioctl = snp_ioctl, 89 .d_poll = snp_poll, 90 .d_name = "snp", 91}; 92 93static th_getc_capture_t snp_getc_capture; 94 95static struct ttyhook snp_hook = { 96 .th_getc_capture = snp_getc_capture, 97}; 98 99/* 100 * Per-instance structure. 101 * 102 * List of locks 103 * (r) locked by snp_register_lock on assignment 104 * (t) locked by tty_lock 105 */ 106struct snp_softc { 107 struct tty *snp_tty; /* (r) TTY we're snooping. */ 108 struct ttyoutq snp_outq; /* (t) Output queue. */ 109 struct cv snp_outwait; /* (t) Output wait queue. */ 110 struct selinfo snp_outpoll; /* (t) Output polling. */ 111}; 112 113static void 114snp_dtor(void *data) 115{ 116 struct snp_softc *ss = data; 117 struct tty *tp; 118 119 tp = ss->snp_tty; 120 if (tp != NULL) { 121 tty_lock(tp); 122 ttyoutq_free(&ss->snp_outq); 123 ttyhook_unregister(tp); 124 } 125 126 cv_destroy(&ss->snp_outwait); 127 free(ss, M_SNP); 128} 129 130/* 131 * Snoop device node routines. 132 */ 133 134static int 135snp_open(struct cdev *dev, int flag, int mode, struct thread *td) 136{ 137 struct snp_softc *ss; 138 139 /* Allocate per-snoop data. */ 140 ss = malloc(sizeof(struct snp_softc), M_SNP, M_WAITOK|M_ZERO); 141 cv_init(&ss->snp_outwait, "snp out"); 142 143 devfs_set_cdevpriv(ss, snp_dtor); 144 145 return (0); 146} 147 148static int 149snp_read(struct cdev *dev, struct uio *uio, int flag) 150{ 151 int error, oresid = uio->uio_resid; 152 struct snp_softc *ss; 153 struct tty *tp; 154 155 if (uio->uio_resid == 0) 156 return (0); 157 158 error = devfs_get_cdevpriv((void **)&ss); 159 if (error != 0) 160 return (error);
|
161
|
161 |
162 tp = ss->snp_tty; 163 if (tp == NULL || tty_gone(tp)) 164 return (EIO); 165 166 tty_lock(tp); 167 for (;;) { 168 error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); 169 if (error != 0 || uio->uio_resid != oresid) 170 break; 171 172 /* Wait for more data. */ 173 if (flag & O_NONBLOCK) { 174 error = EWOULDBLOCK; 175 break; 176 } 177 error = cv_wait_sig(&ss->snp_outwait, tp->t_mtx); 178 if (error != 0) 179 break; 180 if (tty_gone(tp)) { 181 error = EIO; 182 break; 183 } 184 } 185 tty_unlock(tp); 186 187 return (error); 188} 189 190static int 191snp_write(struct cdev *dev, struct uio *uio, int flag) 192{ 193 struct snp_softc *ss; 194 struct tty *tp; 195 int error, len; 196 char in[SNP_INPUT_BUFSIZE]; 197 198 error = devfs_get_cdevpriv((void **)&ss); 199 if (error != 0) 200 return (error);
|
201
|
201 |
202 tp = ss->snp_tty; 203 if (tp == NULL || tty_gone(tp)) 204 return (EIO); 205 206 while (uio->uio_resid > 0) { 207 /* Read new data. */ 208 len = imin(uio->uio_resid, sizeof in); 209 error = uiomove(in, len, uio); 210 if (error != 0) 211 return (error); 212 213 tty_lock(tp); 214 215 /* Driver could have abandoned the TTY in the mean time. */ 216 if (tty_gone(tp)) { 217 tty_unlock(tp); 218 return (ENXIO); 219 } 220 221 /* 222 * Deliver data to the TTY. Ignore errors for now, 223 * because we shouldn't bail out when we're running 224 * close to the watermarks. 225 */ 226 ttydisc_rint_simple(tp, in, len); 227 ttydisc_rint_done(tp); 228 229 tty_unlock(tp); 230 } 231 232 return (0); 233} 234 235static int 236snp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 237 struct thread *td) 238{ 239 struct snp_softc *ss; 240 struct tty *tp; 241 int error; 242 243 error = devfs_get_cdevpriv((void **)&ss); 244 if (error != 0) 245 return (error); 246 247 switch (cmd) { 248 case SNPSTTY: 249 /* Bind TTY to snoop instance. */ 250 SNP_LOCK(); 251 if (ss->snp_tty != NULL) { 252 SNP_UNLOCK(); 253 return (EBUSY); 254 } 255 error = ttyhook_register(&ss->snp_tty, td->td_proc, 256 *(int *)data, &snp_hook, ss); 257 SNP_UNLOCK(); 258 if (error != 0) 259 return (error); 260 261 /* Now that went okay, allocate a buffer for the queue. */ 262 tp = ss->snp_tty; 263 tty_lock(tp); 264 ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE); 265 tty_unlock(tp); 266 267 return (0); 268 case SNPGTTY: 269 /* Obtain device number of associated TTY. */ 270 if (ss->snp_tty == NULL) 271 *(dev_t *)data = NODEV; 272 else 273 *(dev_t *)data = tty_udev(ss->snp_tty); 274 return (0); 275 case FIONREAD: 276 tp = ss->snp_tty; 277 if (tp != NULL) { 278 tty_lock(tp); 279 *(int *)data = ttyoutq_bytesused(&ss->snp_outq); 280 tty_unlock(tp); 281 } else { 282 *(int *)data = 0; 283 } 284 return (0); 285 default: 286 return (ENOTTY); 287 } 288} 289 290static int 291snp_poll(struct cdev *dev, int events, struct thread *td) 292{ 293 struct snp_softc *ss; 294 struct tty *tp; 295 int revents; 296 297 if (devfs_get_cdevpriv((void **)&ss) != 0) 298 return (events & 299 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 300 301 revents = 0; 302 303 if (events & (POLLIN | POLLRDNORM)) { 304 tp = ss->snp_tty; 305 if (tp != NULL) { 306 tty_lock(tp); 307 if (ttyoutq_bytesused(&ss->snp_outq) > 0) 308 revents |= events & (POLLIN | POLLRDNORM); 309 tty_unlock(tp); 310 } 311 } 312 313 if (revents == 0) 314 selrecord(td, &ss->snp_outpoll); 315 316 return (revents); 317} 318 319/* 320 * TTY hook events. 321 */ 322 323static int 324snp_modevent(module_t mod, int type, void *data) 325{ 326 327 switch (type) { 328 case MOD_LOAD: 329 snp_dev = make_dev(&snp_cdevsw, 0, 330 UID_ROOT, GID_WHEEL, 0600, "snp"); 331 return (0); 332 case MOD_UNLOAD: 333 /* XXX: Make existing users leave. */ 334 destroy_dev(snp_dev); 335 return (0); 336 default: 337 return (EOPNOTSUPP); 338 } 339} 340 341static void 342snp_getc_capture(struct tty *tp, const void *buf, size_t len) 343{ 344 struct snp_softc *ss = ttyhook_softc(tp); 345 346 ttyoutq_write(&ss->snp_outq, buf, len); 347 348 cv_broadcast(&ss->snp_outwait); 349 selwakeup(&ss->snp_outpoll); 350} 351 352static moduledata_t snp_mod = { 353 "snp", 354 snp_modevent, 355 NULL 356}; 357 358DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
|