xref: /freebsd/sys/dev/snp/snp.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 void		snp_detach(void *arg);
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(struct tty *tp, int flag)
141 {
142 	struct snoop *snp;
143 	int error;
144 
145 	snp = ttytosnp(tp);
146 	error = snp_down(snp);
147 	if (error != 0)
148 		return (error);
149 	error = ttylclose(tp, flag);
150 	return (error);
151 }
152 
153 static int
154 snplwrite(struct tty *tp, struct uio *uio, int flag)
155 {
156 	struct iovec iov;
157 	struct uio uio2;
158 	struct snoop *snp;
159 	int error, ilen;
160 	char *ibuf;
161 
162 	error = 0;
163 	ibuf = NULL;
164 	snp = ttytosnp(tp);
165 	while (uio->uio_resid > 0) {
166 		ilen = imin(512, uio->uio_resid);
167 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
168 		error = uiomove(ibuf, ilen, uio);
169 		if (error != 0)
170 			break;
171 		snp_in(snp, ibuf, ilen);
172 		/* Hackish, but probably the least of all evils. */
173 		iov.iov_base = ibuf;
174 		iov.iov_len = ilen;
175 		uio2.uio_iov = &iov;
176 		uio2.uio_iovcnt = 1;
177 		uio2.uio_offset = 0;
178 		uio2.uio_resid = ilen;
179 		uio2.uio_segflg = UIO_SYSSPACE;
180 		uio2.uio_rw = UIO_WRITE;
181 		uio2.uio_td = uio->uio_td;
182 		error = ttwrite(tp, &uio2, flag);
183 		if (error != 0)
184 			break;
185 		free(ibuf, M_SNP);
186 		ibuf = NULL;
187 	}
188 	if (ibuf != NULL)
189 		free(ibuf, M_SNP);
190 	return (error);
191 }
192 
193 static struct tty *
194 snpdevtotty(struct cdev *dev)
195 {
196 	struct cdevsw *cdp;
197 	struct tty *tp;
198 
199 	cdp = dev_refthread(dev);
200 	if (cdp == NULL)
201 		return (NULL);
202 	if (!(cdp->d_flags & D_TTY))
203 		tp = NULL;
204 	else
205 		tp = dev->si_tty;
206 	dev_relthread(dev);
207 	return (tp);
208 }
209 
210 #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
211 				 * interactive mode write is 3 bytes
212 				 * length for function keys...
213 				 */
214 
215 static int
216 snpwrite(struct cdev *dev, struct uio *uio, int flag)
217 {
218 	struct snoop *snp;
219 	struct tty *tp;
220 	int error, i, len;
221 	unsigned char c[SNP_INPUT_BUF];
222 
223 	snp = dev->si_drv1;
224 	tp = snp->snp_tty;
225 	if (tp == NULL)
226 		return (EIO);
227 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc)
228 		goto tty_input;
229 
230 	printf("snp%d: attempt to write to bad tty\n", snp->snp_unit);
231 	return (EIO);
232 
233 tty_input:
234 	if (!(tp->t_state & TS_ISOPEN))
235 		return (EIO);
236 
237 	while (uio->uio_resid > 0) {
238 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
239 		if ((error = uiomove(c, len, uio)) != 0)
240 			return (error);
241 		for (i=0; i < len; i++) {
242 			if (ttyinput(c[i], tp))
243 				return (EIO);
244 		}
245 	}
246 	return (0);
247 }
248 
249 
250 static int
251 snpread(struct cdev *dev, struct uio *uio, int flag)
252 {
253 	struct snoop *snp;
254 	int error, len, n, nblen, s;
255 	caddr_t from;
256 	char *nbuf;
257 
258 	snp = dev->si_drv1;
259 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
260 	    ("snoop buffer error"));
261 
262 	if (snp->snp_tty == NULL)
263 		return (EIO);
264 
265 	snp->snp_flags &= ~SNOOP_RWAIT;
266 
267 	do {
268 		if (snp->snp_len == 0) {
269 			if (flag & O_NONBLOCK)
270 				return (EWOULDBLOCK);
271 			snp->snp_flags |= SNOOP_RWAIT;
272 			error = tsleep(snp, (PZERO + 1) | PCATCH,
273 			    "snprd", 0);
274 			if (error != 0)
275 				return (error);
276 		}
277 	} while (snp->snp_len == 0);
278 
279 	n = snp->snp_len;
280 
281 	error = 0;
282 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
283 		len = min((unsigned)uio->uio_resid, snp->snp_len);
284 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
285 		if (len == 0)
286 			break;
287 
288 		error = uiomove(from, len, uio);
289 		snp->snp_base += len;
290 		snp->snp_len -= len;
291 	}
292 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
293 		snp->snp_flags &= ~SNOOP_OFLOW;
294 	}
295 	s = spltty();
296 	nblen = snp->snp_blen;
297 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
298 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
299 			nblen = nblen / 2;
300 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
301 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
302 			free(snp->snp_buf, M_SNP);
303 			snp->snp_buf = nbuf;
304 			snp->snp_blen = nblen;
305 			snp->snp_base = 0;
306 		}
307 	}
308 	splx(s);
309 
310 	return (error);
311 }
312 
313 static int
314 snp_in(struct snoop *snp, char *buf, int n)
315 {
316 	int s_free, s_tail;
317 	int s, len, nblen;
318 	caddr_t from, to;
319 	char *nbuf;
320 
321 	KASSERT(n >= 0, ("negative snoop char count"));
322 
323 	if (n == 0)
324 		return (0);
325 
326 	if (snp->snp_flags & SNOOP_DOWN) {
327 		printf("snp%d: more data to down interface\n", snp->snp_unit);
328 		return (0);
329 	}
330 
331 	if (snp->snp_flags & SNOOP_OFLOW) {
332 		printf("snp%d: buffer overflow\n", snp->snp_unit);
333 		/*
334 		 * On overflow we just repeat the standart close
335 		 * procedure...yes , this is waste of space but.. Then next
336 		 * read from device will fail if one would recall he is
337 		 * snooping and retry...
338 		 */
339 
340 		return (snp_down(snp));
341 	}
342 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
343 	s_free = snp->snp_blen - snp->snp_len;
344 
345 
346 	if (n > s_free) {
347 		s = spltty();
348 		nblen = snp->snp_blen;
349 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
350 			nblen = snp->snp_blen * 2;
351 			s_free = nblen - (snp->snp_len + snp->snp_base);
352 		}
353 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
354 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
355 			free(snp->snp_buf, M_SNP);
356 			snp->snp_buf = nbuf;
357 			snp->snp_blen = nblen;
358 			snp->snp_base = 0;
359 		} else {
360 			snp->snp_flags |= SNOOP_OFLOW;
361 			if (snp->snp_flags & SNOOP_RWAIT) {
362 				snp->snp_flags &= ~SNOOP_RWAIT;
363 				wakeup(snp);
364 			}
365 			splx(s);
366 			return (0);
367 		}
368 		splx(s);
369 	}
370 	if (n > s_tail) {
371 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
372 		to = (caddr_t)(snp->snp_buf);
373 		len = snp->snp_len;
374 		bcopy(from, to, len);
375 		snp->snp_base = 0;
376 	}
377 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
378 	bcopy(buf, to, n);
379 	snp->snp_len += n;
380 
381 	if (snp->snp_flags & SNOOP_RWAIT) {
382 		snp->snp_flags &= ~SNOOP_RWAIT;
383 		wakeup(snp);
384 	}
385 	selwakeuppri(&snp->snp_sel, PZERO + 1);
386 
387 	return (n);
388 }
389 
390 static int
391 snpopen(struct cdev *dev, int flag, int mode, struct thread *td)
392 {
393 	struct snoop *snp;
394 
395 	if (dev->si_drv1 == NULL) {
396 		dev->si_flags &= ~SI_CHEAPCLONE;
397 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
398 		    M_WAITOK | M_ZERO);
399 		snp->snp_unit = dev2unit(dev);
400 	} else
401 		return (EBUSY);
402 
403 	/*
404 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
405 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
406 	 */
407 	snp->snp_flags = SNOOP_OPEN;
408 
409 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
410 	snp->snp_blen = SNOOP_MINLEN;
411 	snp->snp_base = 0;
412 	snp->snp_len = 0;
413 
414 	/*
415 	 * snp_tty == NULL  is for inactive snoop devices.
416 	 */
417 	snp->snp_tty = NULL;
418 	snp->snp_target = NULL;
419 
420 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
421 	return (0);
422 }
423 
424 
425 static void
426 snp_detach(void *arg)
427 {
428 	struct snoop *snp;
429 	struct tty *tp;
430 
431 	snp = (struct snoop *)arg;
432 	snp->snp_base = 0;
433 	snp->snp_len = 0;
434 
435 	/*
436 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
437 	 * change it anyway.
438 	 */
439 	tp = snp->snp_tty;
440 	if (tp == NULL)
441 		goto detach_notty;
442 
443 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) {
444 		tp->t_state &= ~TS_SNOOP;
445 		tp->t_line = snp->snp_olddisc;
446 	} else
447 		printf("snp%d: bad attached tty data\n", snp->snp_unit);
448 
449 	snp->snp_tty = NULL;
450 	snp->snp_target = NULL;
451 
452 detach_notty:
453 	selwakeuppri(&snp->snp_sel, PZERO + 1);
454 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
455 		free(snp, M_SNP);
456 }
457 
458 static int
459 snpclose(struct cdev *dev, int flags, int fmt, struct thread *td)
460 {
461 	struct snoop *snp;
462 
463 	snp = dev->si_drv1;
464 	snp->snp_blen = 0;
465 	LIST_REMOVE(snp, snp_list);
466 	free(snp->snp_buf, M_SNP);
467 	snp->snp_flags &= ~SNOOP_OPEN;
468 	dev->si_drv1 = NULL;
469 	snp_detach(snp);
470 	destroy_dev_sched(dev);
471 
472 	return (0);
473 }
474 
475 static int
476 snp_down(struct snoop *snp)
477 {
478 
479 	if (snp->snp_blen != SNOOP_MINLEN) {
480 		free(snp->snp_buf, M_SNP);
481 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
482 		snp->snp_blen = SNOOP_MINLEN;
483 	}
484 	snp->snp_flags |= SNOOP_DOWN;
485 	snp_detach(snp);
486 
487 	return (0);
488 }
489 
490 static int
491 snpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
492     struct thread *td)
493 {
494 	struct snoop *snp;
495 	struct tty *tp;
496 	struct cdev *tdev;
497 	struct file *fp;
498 	int s;
499 
500 	snp = dev->si_drv1;
501 	switch (cmd) {
502 	case SNPSTTY:
503 		s = *(int *)data;
504 		if (s < 0)
505 			return (snp_down(snp));
506 
507 		if (fget(td, s, &fp) != 0)
508 			return (EINVAL);
509 		if (fp->f_type != DTYPE_VNODE ||
510 		    fp->f_vnode->v_type != VCHR ||
511 		    fp->f_vnode->v_rdev == NULL) {
512 			fdrop(fp, td);
513 			return (EINVAL);
514 		}
515 		tdev = fp->f_vnode->v_rdev;
516 		fdrop(fp, td);
517 
518 		if (snp->snp_tty != NULL)
519 			return (EBUSY);
520 
521 		tp = snpdevtotty(tdev);
522 		if (!tp)
523 			return (EINVAL);
524 		if (tp->t_state & TS_SNOOP)
525 			return (EBUSY);
526 
527 		s = spltty();
528 		tp->t_state |= TS_SNOOP;
529 		snp->snp_olddisc = tp->t_line;
530 		tp->t_line = snooplinedisc;
531 		snp->snp_tty = tp;
532 		snp->snp_target = tdev;
533 
534 		/*
535 		 * Clean overflow and down flags -
536 		 * we'll have a chance to get them in the future :)))
537 		 */
538 		snp->snp_flags &= ~SNOOP_OFLOW;
539 		snp->snp_flags &= ~SNOOP_DOWN;
540 		splx(s);
541 		break;
542 
543 	case SNPGTTY:
544 		/*
545 		 * We keep snp_target field specially to make
546 		 * SNPGTTY happy, else we can't know what is device
547 		 * major/minor for tty.
548 		 */
549 		*((dev_t *)data) = dev2udev(snp->snp_target);
550 		break;
551 
552 	case FIONBIO:
553 		break;
554 
555 	case FIOASYNC:
556 		if (*(int *)data)
557 			snp->snp_flags |= SNOOP_ASYNC;
558 		else
559 			snp->snp_flags &= ~SNOOP_ASYNC;
560 		break;
561 
562 	case FIONREAD:
563 		s = spltty();
564 		if (snp->snp_tty != NULL)
565 			*(int *)data = snp->snp_len;
566 		else
567 			if (snp->snp_flags & SNOOP_DOWN) {
568 				if (snp->snp_flags & SNOOP_OFLOW)
569 					*(int *)data = SNP_OFLOW;
570 				else
571 					*(int *)data = SNP_TTYCLOSE;
572 			} else {
573 				*(int *)data = SNP_DETACH;
574 			}
575 		splx(s);
576 		break;
577 
578 	default:
579 		return (ENOTTY);
580 	}
581 	return (0);
582 }
583 
584 static int
585 snppoll(struct cdev *dev, int events, struct thread *td)
586 {
587 	struct snoop *snp;
588 	int revents;
589 
590 	snp = dev->si_drv1;
591 	revents = 0;
592 	/*
593 	 * If snoop is down, we don't want to poll() forever so we return 1.
594 	 * Caller should see if we down via FIONREAD ioctl().  The last should
595 	 * return -1 to indicate down state.
596 	 */
597 	if (events & (POLLIN | POLLRDNORM)) {
598 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
599 			revents |= events & (POLLIN | POLLRDNORM);
600 		else
601 			selrecord(td, &snp->snp_sel);
602 	}
603 	return (revents);
604 }
605 
606 static void
607 snp_clone(void *arg, struct ucred *cred, char *name, int namelen,
608     struct cdev **dev)
609 {
610 	int u, i;
611 
612 	if (*dev != NULL)
613 		return;
614 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
615 		return;
616 	i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0);
617 	if (i)
618 		*dev = make_dev_credf(MAKEDEV_REF, &snp_cdevsw, unit2minor(u),
619 		     NULL, UID_ROOT, GID_WHEEL, 0600, "snp%d", u);
620 	if (*dev != NULL) {
621 		(*dev)->si_flags |= SI_CHEAPCLONE;
622 	}
623 }
624 
625 static int
626 snp_modevent(module_t mod, int type, void *data)
627 {
628 	static eventhandler_tag eh_tag;
629 
630 	switch (type) {
631 	case MOD_LOAD:
632 		/* XXX error checking. */
633 		clone_setup(&snpclones);
634 		eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
635 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
636 		break;
637 	case MOD_UNLOAD:
638 		if (!LIST_EMPTY(&snp_sclist))
639 			return (EBUSY);
640 		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
641 		drain_dev_clone_events();
642 		clone_cleanup(&snpclones);
643 		destroy_dev_drain(&snp_cdevsw);
644 		ldisc_deregister(snooplinedisc);
645 		break;
646 	default:
647 		return (EOPNOTSUPP);
648 		break;
649 	}
650 	return (0);
651 }
652 
653 static moduledata_t snp_mod = {
654         "snp",
655         snp_modevent,
656         NULL
657 };
658 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
659