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