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