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