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