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