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 #include "snp.h" 17 18 #if NSNP > 0 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/filio.h> 23 #include <sys/ioctl_compat.h> /* Oooh..We need O/NTTYDISC */ 24 #include <sys/proc.h> 25 #include <sys/tty.h> 26 #include <sys/fcntl.h> 27 #include <sys/conf.h> 28 #include <sys/uio.h> 29 #include <sys/kernel.h> 30 #include <sys/malloc.h> 31 #ifdef DEVFS 32 #include <sys/devfsext.h> 33 #endif /*DEVFS*/ 34 35 #include <sys/snoop.h> 36 37 static d_open_t snpopen; 38 static d_close_t snpclose; 39 static d_read_t snpread; 40 static d_write_t snpwrite; 41 static d_ioctl_t snpioctl; 42 static d_select_t snpselect; 43 44 #define CDEV_MAJOR 53 45 static struct cdevsw snp_cdevsw = 46 { snpopen, snpclose, snpread, snpwrite, /*53*/ 47 snpioctl, nostop, nullreset, nodevtotty,/* snoop */ 48 snpselect, nommap, NULL, "snp", NULL, -1 }; 49 50 51 #ifndef MIN 52 #define MIN(a,b) (((a)<(b))?(a):(b)) 53 #endif 54 55 static struct snoop snoopsw[NSNP]; 56 57 static struct tty *snpdevtotty __P((dev_t dev)); 58 static int snp_detach __P((struct snoop *snp)); 59 60 static struct tty * 61 snpdevtotty (dev) 62 dev_t dev; 63 { 64 struct cdevsw *cdp; 65 int maj; 66 67 maj = major(dev); 68 if ((u_int)maj >= nchrdev) 69 return (NULL); 70 cdp = cdevsw[maj]; 71 if (cdp == NULL) 72 return (NULL); 73 return ((*cdp->d_devtotty)(dev)); 74 } 75 76 #define SNP_INPUT_BUF 5 /* This is even too much,the maximal 77 * interactive mode write is 3 bytes 78 * length for function keys... 79 */ 80 81 static int 82 snpwrite(dev, uio, flag) 83 dev_t dev; 84 struct uio *uio; 85 int flag; 86 { 87 int unit = minor(dev), len, i, error; 88 struct snoop *snp = &snoopsw[unit]; 89 struct tty *tp; 90 char c[SNP_INPUT_BUF]; 91 92 if (snp->snp_tty == NULL) 93 return (EIO); 94 95 tp = snp->snp_tty; 96 97 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 98 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) 99 goto tty_input; 100 101 printf("Snoop: attempt to write to bad tty.\n"); 102 return (EIO); 103 104 tty_input: 105 if (!(tp->t_state & TS_ISOPEN)) 106 return (EIO); 107 108 while (uio->uio_resid > 0) { 109 len = MIN(uio->uio_resid,SNP_INPUT_BUF); 110 if ((error = uiomove(c, len, uio)) != 0) 111 return (error); 112 for (i=0;i<len;i++) { 113 if (ttyinput(c[i] , tp)) 114 return (EIO); 115 } 116 } 117 return 0; 118 119 } 120 121 122 static int 123 snpread(dev, uio, flag) 124 dev_t dev; 125 struct uio *uio; 126 int flag; 127 { 128 int unit = minor(dev), s; 129 struct snoop *snp = &snoopsw[unit]; 130 int len, n, nblen, error = 0; 131 caddr_t from; 132 char *nbuf; 133 134 #ifdef DIAGNOSTIC 135 if ((snp->snp_len + snp->snp_base) > snp->snp_blen) 136 panic("snoop buffer error"); 137 #endif 138 139 if (snp->snp_tty == NULL) 140 return (EIO); 141 142 snp->snp_flags &= ~SNOOP_RWAIT; 143 144 do { 145 if (snp->snp_len == 0) { 146 if (snp->snp_flags & SNOOP_NBIO) { 147 return EWOULDBLOCK; 148 } 149 snp->snp_flags |= SNOOP_RWAIT; 150 tsleep((caddr_t) snp, (PZERO + 1) | PCATCH, "snoopread", 0); 151 } 152 } while (snp->snp_len == 0); 153 154 n = snp->snp_len; 155 156 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 157 len = MIN(uio->uio_resid, snp->snp_len); 158 from = (caddr_t) (snp->snp_buf + snp->snp_base); 159 if (len == 0) 160 break; 161 162 error = uiomove(from, len, uio); 163 snp->snp_base += len; 164 snp->snp_len -= len; 165 } 166 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 167 snp->snp_flags &= ~SNOOP_OFLOW; 168 } 169 s = spltty(); 170 nblen = snp->snp_blen; 171 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 172 while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN)) 173 nblen = nblen / 2; 174 if (nbuf = malloc(nblen, M_TTYS, M_NOWAIT)) { 175 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 176 free(snp->snp_buf, M_TTYS); 177 snp->snp_buf = nbuf; 178 snp->snp_blen = nblen; 179 snp->snp_base = 0; 180 } 181 } 182 splx(s); 183 184 return error; 185 } 186 187 int 188 snpinc(snp, c) 189 struct snoop *snp; 190 char c; 191 { 192 char buf[1]; 193 194 buf[0]=c; 195 return (snpin(snp,buf,1)); 196 } 197 198 199 int 200 snpin(snp, buf, n) 201 struct snoop *snp; 202 char *buf; 203 int n; 204 { 205 int s_free, s_tail; 206 int s, len, nblen; 207 caddr_t from, to; 208 char *nbuf; 209 210 211 if (n == 0) 212 return 0; 213 214 #ifdef DIAGNOSTIC 215 if (n < 0) 216 panic("bad snoop char count"); 217 218 if (!(snp->snp_flags & SNOOP_OPEN)) { 219 printf("Snoop: data coming to closed device.\n"); 220 return 0; 221 } 222 #endif 223 if (snp->snp_flags & SNOOP_DOWN) { 224 printf("Snoop: more data to down interface.\n"); 225 return 0; 226 } 227 228 if (snp->snp_flags & SNOOP_OFLOW) { 229 printf("Snoop: buffer overflow.\n"); 230 /* 231 * On overflow we just repeat the standart close 232 * procedure...yes , this is waste of space but.. Then next 233 * read from device will fail if one would recall he is 234 * snooping and retry... 235 */ 236 237 return (snpdown(snp)); 238 } 239 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 240 s_free = snp->snp_blen - snp->snp_len; 241 242 243 if (n > s_free) { 244 s = spltty(); 245 nblen = snp->snp_blen; 246 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 247 nblen = snp->snp_blen * 2; 248 s_free = nblen - (snp->snp_len + snp->snp_base); 249 } 250 if ((n <= s_free) && (nbuf = malloc(nblen, M_TTYS, M_NOWAIT))) { 251 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 252 free(snp->snp_buf, M_TTYS); 253 snp->snp_buf = nbuf; 254 snp->snp_blen = nblen; 255 snp->snp_base = 0; 256 } else { 257 snp->snp_flags |= SNOOP_OFLOW; 258 if (snp->snp_flags & SNOOP_RWAIT) { 259 snp->snp_flags &= ~SNOOP_RWAIT; 260 wakeup((caddr_t) snp); 261 } 262 splx(s); 263 return 0; 264 } 265 splx(s); 266 } 267 if (n > s_tail) { 268 from = (caddr_t) (snp->snp_buf + snp->snp_base); 269 to = (caddr_t) (snp->snp_buf); 270 len = snp->snp_len; 271 bcopy(from, to, len); 272 snp->snp_base = 0; 273 } 274 to = (caddr_t) (snp->snp_buf + snp->snp_base + snp->snp_len); 275 bcopy(buf, to, n); 276 snp->snp_len += n; 277 278 if (snp->snp_flags & SNOOP_RWAIT) { 279 snp->snp_flags &= ~SNOOP_RWAIT; 280 wakeup((caddr_t) snp); 281 } 282 selwakeup(&snp->snp_sel); 283 snp->snp_sel.si_pid = 0; 284 285 return n; 286 } 287 288 static int 289 snpopen(dev, flag, mode, p) 290 dev_t dev; 291 int flag, mode; 292 struct proc *p; 293 { 294 struct snoop *snp; 295 register int unit, error; 296 297 if (error = suser(p->p_ucred, &p->p_acflag)) 298 return (error); 299 300 if ((unit = minor(dev)) >= NSNP) 301 return (ENXIO); 302 303 snp = &snoopsw[unit]; 304 305 if (snp->snp_flags & SNOOP_OPEN) 306 return (ENXIO); 307 308 /* 309 * We intentionally do not OR flags with SNOOP_OPEN,but set them so 310 * all previous settings (especially SNOOP_OFLOW) will be cleared. 311 */ 312 snp->snp_flags = SNOOP_OPEN; 313 314 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK); 315 snp->snp_blen = SNOOP_MINLEN; 316 snp->snp_base = 0; 317 snp->snp_len = 0; 318 319 /* 320 * snp_tty == NULL is for inactive snoop devices. 321 */ 322 snp->snp_tty = NULL; 323 snp->snp_target = -1; 324 return (0); 325 } 326 327 328 static int 329 snp_detach(snp) 330 struct snoop *snp; 331 { 332 struct tty *tp; 333 334 snp->snp_base = 0; 335 snp->snp_len = 0; 336 337 /* 338 * If line disc. changed we do not touch this pointer,SLIP/PPP will 339 * change it anyway. 340 */ 341 342 if (snp->snp_tty == NULL) 343 goto detach_notty; 344 345 tp = snp->snp_tty; 346 347 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 348 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) { 349 tp->t_sc = NULL; 350 tp->t_state &= ~TS_SNOOP; 351 } else 352 printf("Snoop: bad attached tty data.\n"); 353 354 snp->snp_tty = NULL; 355 snp->snp_target = -1; 356 357 detach_notty: 358 selwakeup(&snp->snp_sel); 359 snp->snp_sel.si_pid = 0; 360 361 return (0); 362 } 363 364 static int 365 snpclose(dev, flags, fmt, p) 366 dev_t dev; 367 int flags; 368 int fmt; 369 struct proc *p; 370 { 371 register int unit = minor(dev); 372 struct snoop *snp = &snoopsw[unit]; 373 374 snp->snp_blen = 0; 375 free(snp->snp_buf, M_TTYS); 376 snp->snp_flags &= ~SNOOP_OPEN; 377 378 return (snp_detach(snp)); 379 } 380 381 int 382 snpdown(snp) 383 struct snoop *snp; 384 { 385 snp->snp_blen = SNOOP_MINLEN; 386 free(snp->snp_buf, M_TTYS); 387 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK); 388 snp->snp_flags |= SNOOP_DOWN; 389 390 return (snp_detach(snp)); 391 } 392 393 394 static int 395 snpioctl(dev, cmd, data, flags, p) 396 dev_t dev; 397 int cmd; 398 caddr_t data; 399 int flags; 400 struct proc *p; 401 { 402 int unit = minor(dev), s; 403 dev_t tdev; 404 struct snoop *snp = &snoopsw[unit]; 405 struct tty *tp, *tpo; 406 407 switch (cmd) { 408 case SNPSTTY: 409 tdev = *((dev_t *) data); 410 if (tdev == -1) 411 return (snpdown(snp)); 412 413 tp = snpdevtotty(tdev); 414 if (!tp) 415 return (EINVAL); 416 417 if ((tp->t_sc != (caddr_t) snp) && (tp->t_state & TS_SNOOP)) 418 return (EBUSY); 419 420 if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC)) 421 return (EBUSY); 422 423 s = spltty(); 424 425 if (snp->snp_target == -1) { 426 tpo = snp->snp_tty; 427 if (tpo) 428 tpo->t_state &= ~TS_SNOOP; 429 } 430 431 tp->t_sc = (caddr_t) snp; 432 tp->t_state |= TS_SNOOP; 433 snp->snp_tty = tp; 434 snp->snp_target = tdev; 435 436 /* 437 * Clean overflow and down flags - 438 * we'll have a chance to get them in the future :))) 439 */ 440 snp->snp_flags &= ~SNOOP_OFLOW; 441 snp->snp_flags &= ~SNOOP_DOWN; 442 splx(s); 443 break; 444 445 case SNPGTTY: 446 /* 447 * We keep snp_target field specially to make 448 * SNPGTTY happy,else we can't know what is device 449 * major/minor for tty. 450 */ 451 *((dev_t *) data) = snp->snp_target; 452 break; 453 454 case FIONBIO: 455 if (*(int *) data) 456 snp->snp_flags |= SNOOP_NBIO; 457 else 458 snp->snp_flags &= ~SNOOP_NBIO; 459 break; 460 461 case FIOASYNC: 462 if (*(int *) data) 463 snp->snp_flags |= SNOOP_ASYNC; 464 else 465 snp->snp_flags &= ~SNOOP_ASYNC; 466 break; 467 468 case FIONREAD: 469 s = spltty(); 470 if (snp->snp_tty != NULL) 471 *(int *) data = snp->snp_len; 472 else 473 if (snp->snp_flags & SNOOP_DOWN) { 474 if (snp->snp_flags & SNOOP_OFLOW) 475 *(int *) data = SNP_OFLOW; 476 else 477 *(int *) data = SNP_TTYCLOSE; 478 } else { 479 *(int *) data = SNP_DETACH; 480 } 481 splx(s); 482 break; 483 484 default: 485 return (ENOTTY); 486 } 487 return (0); 488 } 489 490 491 static int 492 snpselect(dev, rw, p) 493 dev_t dev; 494 int rw; 495 struct proc *p; 496 { 497 int unit = minor(dev); 498 struct snoop *snp = &snoopsw[unit]; 499 500 if (rw != FREAD) 501 return 1; 502 503 if (snp->snp_len > 0) 504 return 1; 505 506 /* 507 * If snoop is down,we don't want to select() forever so we return 1. 508 * Caller should see if we down via FIONREAD ioctl().The last should 509 * return -1 to indicate down state. 510 */ 511 if (snp->snp_flags & SNOOP_DOWN) 512 return 1; 513 514 selrecord(p, &snp->snp_sel); 515 return 0; 516 } 517 518 #ifdef DEVFS 519 static void *snp_devfs_token[NSNP]; 520 #endif 521 static snp_devsw_installed = 0; 522 523 static void 524 snp_drvinit(void *unused) 525 { 526 dev_t dev; 527 #ifdef DEVFS 528 int i; 529 #endif 530 531 if( ! snp_devsw_installed ) { 532 dev = makedev(CDEV_MAJOR, 0); 533 cdevsw_add(&dev,&snp_cdevsw, NULL); 534 snp_devsw_installed = 1; 535 #ifdef DEVFS 536 for ( i = 0 ; i < NSNP ; i++) { 537 snp_devfs_token[i] = 538 devfs_add_devswf(&snp_cdevsw, i, DV_CHR, 0, 0, 539 0600, "snp%d", i); 540 } 541 #endif 542 } 543 } 544 545 SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL) 546 547 548 #endif 549