1 /*- 2 * Copyright (c) 1995 Ugen J.S.Antsilevich 3 * 4 * Redistribution and use in source forms, with and without modification, 5 * are permitted provided that this entire comment appears intact. 6 * 7 * Redistribution in binary form may occur without any restrictions. 8 * Obviously, it would be nice if you gave credit where credit is due 9 * but requiring it would be too onerous. 10 * 11 * This software is provided ``AS IS'' without any warranties of any kind. 12 * 13 * Snoop stuff. 14 * 15 */ 16 17 #include <sys/cdefs.h> 18 __FBSDID("$FreeBSD$"); 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/fcntl.h> 23 #include <sys/filio.h> 24 #include <sys/malloc.h> 25 #include <sys/tty.h> 26 #include <sys/conf.h> 27 #include <sys/poll.h> 28 #include <sys/kernel.h> 29 #include <sys/module.h> 30 #include <sys/queue.h> 31 #include <sys/snoop.h> 32 #include <sys/uio.h> 33 #include <sys/file.h> 34 #include <sys/vnode.h> 35 36 static l_close_t snplclose; 37 static l_write_t snplwrite; 38 static d_open_t snpopen; 39 static d_read_t snpread; 40 static d_write_t snpwrite; 41 static d_ioctl_t snpioctl; 42 static d_poll_t snppoll; 43 44 static struct cdevsw snp_cdevsw = { 45 .d_version = D_VERSION, 46 .d_flags = D_PSEUDO | D_NEEDGIANT, 47 .d_open = snpopen, 48 .d_read = snpread, 49 .d_write = snpwrite, 50 .d_ioctl = snpioctl, 51 .d_poll = snppoll, 52 .d_name = "snp", 53 }; 54 55 static struct linesw snpdisc = { 56 .l_open = tty_open, 57 .l_close = snplclose, 58 .l_read = ttread, 59 .l_write = snplwrite, 60 .l_ioctl = l_nullioctl, 61 .l_rint = ttyinput, 62 .l_start = ttstart, 63 .l_modem = ttymodem 64 }; 65 66 /* 67 * This is the main snoop per-device structure. 68 */ 69 struct snoop { 70 LIST_ENTRY(snoop) snp_list; /* List glue. */ 71 struct cdev *snp_target; /* Target tty device. */ 72 struct tty *snp_tty; /* Target tty pointer. */ 73 u_long snp_len; /* Possible length. */ 74 u_long snp_base; /* Data base. */ 75 u_long snp_blen; /* Used length. */ 76 caddr_t snp_buf; /* Allocation pointer. */ 77 int snp_flags; /* Flags. */ 78 struct selinfo snp_sel; /* Select info. */ 79 int snp_olddisc; /* Old line discipline. */ 80 }; 81 82 /* 83 * Possible flags. 84 */ 85 #define SNOOP_ASYNC 0x0002 86 #define SNOOP_OPEN 0x0004 87 #define SNOOP_RWAIT 0x0008 88 #define SNOOP_OFLOW 0x0010 89 #define SNOOP_DOWN 0x0020 90 91 /* 92 * Other constants. 93 */ 94 #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 95 * 4K tested to be the minimum 96 * for which on normal tty 97 * usage there is no need to 98 * allocate more. 99 */ 100 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 101 * If we grow more,something 102 * really bad in this world.. 103 */ 104 105 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 106 /* 107 * The number of the "snoop" line discipline. This gets determined at 108 * module load time. 109 */ 110 static int snooplinedisc; 111 static struct cdev *snoopdev; 112 113 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 114 115 static struct tty *snpdevtotty(struct cdev *dev); 116 static void snp_detach(void *arg); 117 static int snp_down(struct snoop *snp); 118 static int snp_in(struct snoop *snp, char *buf, int n); 119 static int snp_modevent(module_t mod, int what, void *arg); 120 static struct snoop *ttytosnp(struct tty *); 121 122 static struct snoop * 123 ttytosnp(struct tty *tp) 124 { 125 struct snoop *snp; 126 127 LIST_FOREACH(snp, &snp_sclist, snp_list) { 128 if (snp->snp_tty == tp) 129 return (snp); 130 } 131 return (NULL); 132 } 133 134 static int 135 snplclose(struct tty *tp, int flag) 136 { 137 struct snoop *snp; 138 int error; 139 140 snp = ttytosnp(tp); 141 error = snp_down(snp); 142 if (error != 0) 143 return (error); 144 error = ttylclose(tp, flag); 145 return (error); 146 } 147 148 static int 149 snplwrite(struct tty *tp, struct uio *uio, int flag) 150 { 151 struct iovec iov; 152 struct uio uio2; 153 struct snoop *snp; 154 int error, ilen; 155 char *ibuf; 156 157 error = 0; 158 ibuf = NULL; 159 snp = ttytosnp(tp); 160 while (uio->uio_resid > 0) { 161 ilen = imin(512, uio->uio_resid); 162 ibuf = malloc(ilen, M_SNP, M_WAITOK); 163 error = uiomove(ibuf, ilen, uio); 164 if (error != 0) 165 break; 166 snp_in(snp, ibuf, ilen); 167 /* Hackish, but probably the least of all evils. */ 168 iov.iov_base = ibuf; 169 iov.iov_len = ilen; 170 uio2.uio_iov = &iov; 171 uio2.uio_iovcnt = 1; 172 uio2.uio_offset = 0; 173 uio2.uio_resid = ilen; 174 uio2.uio_segflg = UIO_SYSSPACE; 175 uio2.uio_rw = UIO_WRITE; 176 uio2.uio_td = uio->uio_td; 177 error = ttwrite(tp, &uio2, flag); 178 if (error != 0) 179 break; 180 free(ibuf, M_SNP); 181 ibuf = NULL; 182 } 183 if (ibuf != NULL) 184 free(ibuf, M_SNP); 185 return (error); 186 } 187 188 static struct tty * 189 snpdevtotty(struct cdev *dev) 190 { 191 struct cdevsw *cdp; 192 struct tty *tp; 193 194 cdp = dev_refthread(dev); 195 if (cdp == NULL) 196 return (NULL); 197 if (!(cdp->d_flags & D_TTY)) 198 tp = NULL; 199 else 200 tp = dev->si_tty; 201 dev_relthread(dev); 202 return (tp); 203 } 204 205 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 206 * interactive mode write is 3 bytes 207 * length for function keys... 208 */ 209 210 static int 211 snpwrite(struct cdev *dev, struct uio *uio, int flag) 212 { 213 struct snoop *snp; 214 struct tty *tp; 215 int error, i, len; 216 unsigned char c[SNP_INPUT_BUF]; 217 218 error = devfs_get_cdevpriv((void **)&snp); 219 if (error != 0) 220 return (error); 221 222 tp = snp->snp_tty; 223 if (tp == NULL) 224 return (EIO); 225 if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) 226 goto tty_input; 227 228 printf("snp: attempt to write to bad tty\n"); 229 return (EIO); 230 231 tty_input: 232 if (!(tp->t_state & TS_ISOPEN)) 233 return (EIO); 234 235 while (uio->uio_resid > 0) { 236 len = imin(uio->uio_resid, SNP_INPUT_BUF); 237 if ((error = uiomove(c, len, uio)) != 0) 238 return (error); 239 for (i=0; i < len; i++) { 240 if (ttyinput(c[i], tp)) 241 return (EIO); 242 } 243 } 244 return (0); 245 } 246 247 248 static int 249 snpread(struct cdev *dev, struct uio *uio, int flag) 250 { 251 struct snoop *snp; 252 int error, len, n, nblen, s; 253 caddr_t from; 254 char *nbuf; 255 256 error = devfs_get_cdevpriv((void **)&snp); 257 if (error != 0) 258 return (error); 259 260 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 261 ("snoop buffer error")); 262 263 if (snp->snp_tty == NULL) 264 return (EIO); 265 266 snp->snp_flags &= ~SNOOP_RWAIT; 267 268 do { 269 if (snp->snp_len == 0) { 270 if (flag & O_NONBLOCK) 271 return (EWOULDBLOCK); 272 snp->snp_flags |= SNOOP_RWAIT; 273 error = tsleep(snp, (PZERO + 1) | PCATCH, 274 "snprd", 0); 275 if (error != 0) 276 return (error); 277 } 278 } while (snp->snp_len == 0); 279 280 n = snp->snp_len; 281 282 error = 0; 283 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 284 len = min((unsigned)uio->uio_resid, snp->snp_len); 285 from = (caddr_t)(snp->snp_buf + snp->snp_base); 286 if (len == 0) 287 break; 288 289 error = uiomove(from, len, uio); 290 snp->snp_base += len; 291 snp->snp_len -= len; 292 } 293 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 294 snp->snp_flags &= ~SNOOP_OFLOW; 295 } 296 s = spltty(); 297 nblen = snp->snp_blen; 298 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 299 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 300 nblen = nblen / 2; 301 if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 302 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 303 free(snp->snp_buf, M_SNP); 304 snp->snp_buf = nbuf; 305 snp->snp_blen = nblen; 306 snp->snp_base = 0; 307 } 308 } 309 splx(s); 310 311 return (error); 312 } 313 314 static int 315 snp_in(struct snoop *snp, char *buf, int n) 316 { 317 int s_free, s_tail; 318 int s, len, nblen; 319 caddr_t from, to; 320 char *nbuf; 321 322 KASSERT(n >= 0, ("negative snoop char count")); 323 324 if (n == 0) 325 return (0); 326 327 if (snp->snp_flags & SNOOP_DOWN) { 328 printf("snp: more data to down interface\n"); 329 return (0); 330 } 331 332 if (snp->snp_flags & SNOOP_OFLOW) { 333 printf("snp: buffer overflow\n"); 334 /* 335 * On overflow we just repeat the standart close 336 * procedure...yes , this is waste of space but.. Then next 337 * read from device will fail if one would recall he is 338 * snooping and retry... 339 */ 340 341 return (snp_down(snp)); 342 } 343 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 344 s_free = snp->snp_blen - snp->snp_len; 345 346 347 if (n > s_free) { 348 s = spltty(); 349 nblen = snp->snp_blen; 350 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 351 nblen = snp->snp_blen * 2; 352 s_free = nblen - (snp->snp_len + snp->snp_base); 353 } 354 if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 355 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 356 free(snp->snp_buf, M_SNP); 357 snp->snp_buf = nbuf; 358 snp->snp_blen = nblen; 359 snp->snp_base = 0; 360 } else { 361 snp->snp_flags |= SNOOP_OFLOW; 362 if (snp->snp_flags & SNOOP_RWAIT) { 363 snp->snp_flags &= ~SNOOP_RWAIT; 364 wakeup(snp); 365 } 366 splx(s); 367 return (0); 368 } 369 splx(s); 370 } 371 if (n > s_tail) { 372 from = (caddr_t)(snp->snp_buf + snp->snp_base); 373 to = (caddr_t)(snp->snp_buf); 374 len = snp->snp_len; 375 bcopy(from, to, len); 376 snp->snp_base = 0; 377 } 378 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 379 bcopy(buf, to, n); 380 snp->snp_len += n; 381 382 if (snp->snp_flags & SNOOP_RWAIT) { 383 snp->snp_flags &= ~SNOOP_RWAIT; 384 wakeup(snp); 385 } 386 selwakeuppri(&snp->snp_sel, PZERO + 1); 387 388 return (n); 389 } 390 391 static void 392 snp_dtor(void *data) 393 { 394 struct snoop *snp = data; 395 396 snp->snp_blen = 0; 397 LIST_REMOVE(snp, snp_list); 398 free(snp->snp_buf, M_SNP); 399 snp->snp_flags &= ~SNOOP_OPEN; 400 snp_detach(snp); 401 } 402 403 static int 404 snpopen(struct cdev *dev, int flag, int mode, struct thread *td) 405 { 406 struct snoop *snp; 407 int error; 408 409 snp = malloc(sizeof(*snp), M_SNP, M_WAITOK | M_ZERO); 410 error = devfs_set_cdevpriv(snp, snp_dtor); 411 if (error != 0) { 412 free(snp, M_SNP); 413 return (error); 414 } 415 416 /* 417 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 418 * all previous settings (especially SNOOP_OFLOW) will be cleared. 419 */ 420 snp->snp_flags = SNOOP_OPEN; 421 422 snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 423 snp->snp_blen = SNOOP_MINLEN; 424 snp->snp_base = 0; 425 snp->snp_len = 0; 426 427 /* 428 * snp_tty == NULL is for inactive snoop devices. 429 */ 430 snp->snp_tty = NULL; 431 snp->snp_target = NULL; 432 433 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 434 return (0); 435 } 436 437 438 static void 439 snp_detach(void *arg) 440 { 441 struct snoop *snp; 442 struct tty *tp; 443 444 snp = (struct snoop *)arg; 445 snp->snp_base = 0; 446 snp->snp_len = 0; 447 448 /* 449 * If line disc. changed we do not touch this pointer, SLIP/PPP will 450 * change it anyway. 451 */ 452 tp = snp->snp_tty; 453 if (tp == NULL) 454 goto detach_notty; 455 456 if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) { 457 tp->t_state &= ~TS_SNOOP; 458 tp->t_line = snp->snp_olddisc; 459 } else 460 printf("snp: bad attached tty data\n"); 461 462 snp->snp_tty = NULL; 463 snp->snp_target = NULL; 464 465 detach_notty: 466 selwakeuppri(&snp->snp_sel, PZERO + 1); 467 if ((snp->snp_flags & SNOOP_OPEN) == 0) 468 free(snp, M_SNP); 469 } 470 471 static int 472 snp_down(struct snoop *snp) 473 { 474 475 if (snp->snp_blen != SNOOP_MINLEN) { 476 free(snp->snp_buf, M_SNP); 477 snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 478 snp->snp_blen = SNOOP_MINLEN; 479 } 480 snp->snp_flags |= SNOOP_DOWN; 481 snp_detach(snp); 482 483 return (0); 484 } 485 486 static int 487 snpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 488 struct thread *td) 489 { 490 struct snoop *snp; 491 struct tty *tp; 492 struct cdev *tdev; 493 struct file *fp; 494 int error, s; 495 496 error = devfs_get_cdevpriv((void **)&snp); 497 if (error != 0) 498 return (error); 499 500 switch (cmd) { 501 case SNPSTTY: 502 s = *(int *)data; 503 if (s < 0) 504 return (snp_down(snp)); 505 506 if (fget(td, s, &fp) != 0) 507 return (EINVAL); 508 if (fp->f_type != DTYPE_VNODE || 509 fp->f_vnode->v_type != VCHR || 510 fp->f_vnode->v_rdev == NULL) { 511 fdrop(fp, td); 512 return (EINVAL); 513 } 514 tdev = fp->f_vnode->v_rdev; 515 fdrop(fp, td); 516 517 if (snp->snp_tty != NULL) 518 return (EBUSY); 519 520 tp = snpdevtotty(tdev); 521 if (!tp) 522 return (EINVAL); 523 if (tp->t_state & TS_SNOOP) 524 return (EBUSY); 525 526 s = spltty(); 527 tp->t_state |= TS_SNOOP; 528 snp->snp_olddisc = tp->t_line; 529 tp->t_line = snooplinedisc; 530 snp->snp_tty = tp; 531 snp->snp_target = tdev; 532 533 /* 534 * Clean overflow and down flags - 535 * we'll have a chance to get them in the future :))) 536 */ 537 snp->snp_flags &= ~SNOOP_OFLOW; 538 snp->snp_flags &= ~SNOOP_DOWN; 539 splx(s); 540 break; 541 542 case SNPGTTY: 543 /* 544 * We keep snp_target field specially to make 545 * SNPGTTY happy, else we can't know what is device 546 * major/minor for tty. 547 */ 548 *((dev_t *)data) = dev2udev(snp->snp_target); 549 break; 550 551 case FIONBIO: 552 break; 553 554 case FIOASYNC: 555 if (*(int *)data) 556 snp->snp_flags |= SNOOP_ASYNC; 557 else 558 snp->snp_flags &= ~SNOOP_ASYNC; 559 break; 560 561 case FIONREAD: 562 s = spltty(); 563 if (snp->snp_tty != NULL) 564 *(int *)data = snp->snp_len; 565 else 566 if (snp->snp_flags & SNOOP_DOWN) { 567 if (snp->snp_flags & SNOOP_OFLOW) 568 *(int *)data = SNP_OFLOW; 569 else 570 *(int *)data = SNP_TTYCLOSE; 571 } else { 572 *(int *)data = SNP_DETACH; 573 } 574 splx(s); 575 break; 576 577 default: 578 return (ENOTTY); 579 } 580 return (0); 581 } 582 583 static int 584 snppoll(struct cdev *dev, int events, struct thread *td) 585 { 586 struct snoop *snp; 587 int revents; 588 589 if (devfs_get_cdevpriv((void **)&snp) != 0) 590 return (events & 591 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 592 593 revents = 0; 594 /* 595 * If snoop is down, we don't want to poll() forever so we return 1. 596 * Caller should see if we down via FIONREAD ioctl(). The last should 597 * return -1 to indicate down state. 598 */ 599 if (events & (POLLIN | POLLRDNORM)) { 600 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 601 revents |= events & (POLLIN | POLLRDNORM); 602 else 603 selrecord(td, &snp->snp_sel); 604 } 605 return (revents); 606 } 607 608 static int 609 snp_modevent(module_t mod, int type, void *data) 610 { 611 612 switch (type) { 613 case MOD_LOAD: 614 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 615 snoopdev = make_dev(&snp_cdevsw, 0, UID_ROOT, GID_WHEEL, 616 0600, "snp"); 617 /* For compatibility */ 618 make_dev_alias(snoopdev, "snp0"); 619 break; 620 case MOD_UNLOAD: 621 if (!LIST_EMPTY(&snp_sclist)) 622 return (EBUSY); 623 destroy_dev(snoopdev); 624 ldisc_deregister(snooplinedisc); 625 break; 626 default: 627 return (EOPNOTSUPP); 628 break; 629 } 630 return (0); 631 } 632 633 static moduledata_t snp_mod = { 634 "snp", 635 snp_modevent, 636 NULL 637 }; 638 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 639