xref: /original-bsd/sys/kern/tty_pty.c (revision 2e5c0888)
1 /*
2  * Copyright (c) 1982, 1986, 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)tty_pty.c	7.10 (Berkeley) 04/03/90
18  */
19 
20 /*
21  * Pseudo-teletype Driver
22  * (Actually two drivers, requiring two entries in 'cdevsw')
23  */
24 #include "pty.h"
25 
26 #if NPTY > 0
27 #include "param.h"
28 #include "systm.h"
29 #include "ioctl.h"
30 #include "tty.h"
31 #include "user.h"
32 #include "conf.h"
33 #include "file.h"
34 #include "proc.h"
35 #include "uio.h"
36 #include "kernel.h"
37 #include "vnode.h"
38 
39 #if NPTY == 1
40 #undef NPTY
41 #define	NPTY	32		/* crude XXX */
42 #endif
43 
44 #define BUFSIZ 100		/* Chunk size iomoved to/from user */
45 
46 /*
47  * pts == /dev/tty[pqrs]?
48  * ptc == /dev/pty[pqrs]?
49  */
50 struct	tty pt_tty[NPTY];
51 struct	pt_ioctl {
52 	int	pt_flags;
53 	struct	proc *pt_selr, *pt_selw;
54 	u_char	pt_send;
55 	u_char	pt_ucntl;
56 } pt_ioctl[NPTY];
57 int	npty = NPTY;		/* for pstat -t */
58 
59 int ptydebug = 0;
60 
61 #define	PF_RCOLL	0x01
62 #define	PF_WCOLL	0x02
63 #define	PF_NBIO		0x04
64 #define	PF_PKT		0x08		/* packet mode */
65 #define	PF_STOPPED	0x10		/* user told stopped */
66 #define	PF_REMOTE	0x20		/* remote and flow controlled input */
67 #define	PF_NOSTOP	0x40
68 #define PF_UCNTL	0x80		/* user control mode */
69 
70 /*ARGSUSED*/
71 ptsopen(dev, flag)
72 	dev_t dev;
73 {
74 	register struct tty *tp;
75 	int error;
76 
77 #ifdef lint
78 	npty = npty;
79 #endif
80 	if (minor(dev) >= NPTY)
81 		return (ENXIO);
82 	tp = &pt_tty[minor(dev)];
83 	if ((tp->t_state & TS_ISOPEN) == 0) {
84 		ttychars(tp);		/* Set up default chars */
85 		tp->t_iflag = TTYDEF_IFLAG;
86 		tp->t_oflag = TTYDEF_OFLAG;
87 		tp->t_lflag = TTYDEF_LFLAG;
88 		tp->t_cflag = TTYDEF_CFLAG;
89 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
90 		ttsetwater(tp);		/* would be done in xxparam() */
91 	} else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
92 		return (EBUSY);
93 	if (tp->t_oproc)			/* Ctrlr still around. */
94 		tp->t_state |= TS_CARR_ON;
95 	while ((tp->t_state & TS_CARR_ON) == 0) {
96 		tp->t_state |= TS_WOPEN;
97 		if (flag&FNDELAY)
98 			break;
99 		if (error = tsleep((caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
100 		    ttopen, 0))
101 			return (error);
102 	}
103 	error = (*linesw[tp->t_line].l_open)(dev, tp, flag);
104 	ptcwakeup(tp, FREAD|FWRITE);
105 	return (error);
106 }
107 
108 ptsclose(dev)
109 	dev_t dev;
110 {
111 	register struct tty *tp;
112 
113 	tp = &pt_tty[minor(dev)];
114 	(*linesw[tp->t_line].l_close)(tp);
115 	ttyclose(tp);
116 	ptcwakeup(tp, FREAD|FWRITE);
117 }
118 
119 ptsread(dev, uio, flag)
120 	dev_t dev;
121 	struct uio *uio;
122 {
123 	register struct tty *tp = &pt_tty[minor(dev)];
124 	register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
125 	int error = 0;
126 
127 again:
128 	if (pti->pt_flags & PF_REMOTE) {
129 		while (isbackground(u.u_procp, tp)) {
130 			if ((u.u_procp->p_sigignore & sigmask(SIGTTIN)) ||
131 			    (u.u_procp->p_sigmask & sigmask(SIGTTIN)) ||
132 			    u.u_procp->p_pgrp->pg_jobc == 0 ||
133 			    u.u_procp->p_flag&SVFORK)
134 				return (EIO);
135 			pgsignal(u.u_procp->p_pgrp, SIGTTIN);
136 			if (error = tsleep((caddr_t)&lbolt, TTIPRI | PCATCH,
137 			    ttybg, 0))
138 				return (error);
139 		}
140 		if (tp->t_canq.c_cc == 0) {
141 			if (flag & IO_NDELAY)
142 				return (EWOULDBLOCK);
143 			if (error = tsleep((caddr_t)&tp->t_canq,
144 			    TTIPRI | PCATCH, ttyin, 0))
145 				return (error);
146 			goto again;
147 		}
148 		while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
149 			if (ureadc(getc(&tp->t_canq), uio) < 0) {
150 				error = EFAULT;
151 				break;
152 			}
153 		if (tp->t_canq.c_cc == 1)
154 			(void) getc(&tp->t_canq);
155 		if (tp->t_canq.c_cc)
156 			return (error);
157 	} else
158 		if (tp->t_oproc)
159 			error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
160 	ptcwakeup(tp, FWRITE);
161 	return (error);
162 }
163 
164 /*
165  * Write to pseudo-tty.
166  * Wakeups of controlling tty will happen
167  * indirectly, when tty driver calls ptsstart.
168  */
169 ptswrite(dev, uio, flag)
170 	dev_t dev;
171 	struct uio *uio;
172 {
173 	register struct tty *tp;
174 
175 	tp = &pt_tty[minor(dev)];
176 	if (tp->t_oproc == 0)
177 		return (EIO);
178 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
179 }
180 
181 /*
182  * Start output on pseudo-tty.
183  * Wake up process selecting or sleeping for input from controlling tty.
184  */
185 ptsstart(tp)
186 	struct tty *tp;
187 {
188 	register struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
189 
190 	if (tp->t_state & TS_TTSTOP)
191 		return;
192 	if (pti->pt_flags & PF_STOPPED) {
193 		pti->pt_flags &= ~PF_STOPPED;
194 		pti->pt_send = TIOCPKT_START;
195 	}
196 	ptcwakeup(tp, FREAD);
197 }
198 
199 ptcwakeup(tp, flag)
200 	struct tty *tp;
201 {
202 	struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
203 
204 	if (flag & FREAD) {
205 		if (pti->pt_selr) {
206 			selwakeup(pti->pt_selr, pti->pt_flags & PF_RCOLL);
207 			pti->pt_selr = 0;
208 			pti->pt_flags &= ~PF_RCOLL;
209 		}
210 		wakeup((caddr_t)&tp->t_outq.c_cf);
211 	}
212 	if (flag & FWRITE) {
213 		if (pti->pt_selw) {
214 			selwakeup(pti->pt_selw, pti->pt_flags & PF_WCOLL);
215 			pti->pt_selw = 0;
216 			pti->pt_flags &= ~PF_WCOLL;
217 		}
218 if (ptydebug) printf("WAKEUP c_cf %d\n", u.u_procp->p_pid);
219 		wakeup((caddr_t)&tp->t_rawq.c_cf);
220 	}
221 }
222 
223 /*ARGSUSED*/
224 ptcopen(dev, flag)
225 	dev_t dev;
226 	int flag;
227 {
228 	register struct tty *tp;
229 	struct pt_ioctl *pti;
230 
231 	if (minor(dev) >= NPTY)
232 		return (ENXIO);
233 	tp = &pt_tty[minor(dev)];
234 	if (tp->t_oproc)
235 		return (EIO);
236 	tp->t_oproc = ptsstart;
237 	(void)(*linesw[tp->t_line].l_modem)(tp, 1);
238 	pti = &pt_ioctl[minor(dev)];
239 	pti->pt_flags = 0;
240 	pti->pt_send = 0;
241 	pti->pt_ucntl = 0;
242 	return (0);
243 }
244 
245 ptcclose(dev)
246 	dev_t dev;
247 {
248 	register struct tty *tp;
249 
250 	tp = &pt_tty[minor(dev)];
251 	(void)(*linesw[tp->t_line].l_modem)(tp, 0);
252 	tp->t_state &= ~TS_CARR_ON;
253 	tp->t_oproc = 0;		/* mark closed */
254 	tp->t_session = 0;
255 }
256 
257 ptcread(dev, uio, flag)
258 	dev_t dev;
259 	struct uio *uio;
260 {
261 	register struct tty *tp = &pt_tty[minor(dev)];
262 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
263 	char buf[BUFSIZ];
264 	int error = 0, cc;
265 
266 	/*
267 	 * We want to block until the slave
268 	 * is open, and there's something to read;
269 	 * but if we lost the slave or we're NBIO,
270 	 * then return the appropriate error instead.
271 	 */
272 	for (;;) {
273 		if (tp->t_state&TS_ISOPEN) {
274 			if (pti->pt_flags&PF_PKT && pti->pt_send) {
275 				error = ureadc((int)pti->pt_send, uio);
276 				if (error)
277 					return (error);
278 				pti->pt_send = 0;
279 				return (0);
280 			}
281 			if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
282 				error = ureadc((int)pti->pt_ucntl, uio);
283 				if (error)
284 					return (error);
285 				pti->pt_ucntl = 0;
286 				return (0);
287 			}
288 			if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
289 				break;
290 		}
291 		if ((tp->t_state&TS_CARR_ON) == 0)
292 			return (0);	/* EOF */
293 		if (flag & IO_NDELAY)
294 			return (EWOULDBLOCK);
295 if (ptydebug) printf("SLEEP(1) c_cf %d\n", u.u_procp->p_pid);	/* XXX */
296 		if (error = tsleep((caddr_t)&tp->t_outq.c_cf, TTIPRI | PCATCH,
297 		    ttyin, 0))
298 			return (error);
299 	}
300 	if (pti->pt_flags & (PF_PKT|PF_UCNTL))
301 		error = ureadc(0, uio);
302 	while (uio->uio_resid > 0 && error == 0) {
303 		cc = q_to_b(&tp->t_outq, buf, MIN(uio->uio_resid, BUFSIZ));
304 		if (cc <= 0)
305 			break;
306 		error = uiomove(buf, cc, uio);
307 	}
308 	if (tp->t_outq.c_cc <= tp->t_lowat) {
309 		if (tp->t_state&TS_ASLEEP) {
310 			tp->t_state &= ~TS_ASLEEP;
311 			wakeup((caddr_t)&tp->t_outq);
312 		}
313 		if (tp->t_wsel) {
314 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
315 			tp->t_wsel = 0;
316 			tp->t_state &= ~TS_WCOLL;
317 		}
318 	}
319 	return (error);
320 }
321 
322 ptsstop(tp, flush)
323 	register struct tty *tp;
324 	int flush;
325 {
326 	struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
327 	int flag;
328 
329 	/* note: FLUSHREAD and FLUSHWRITE already ok */
330 	if (flush == 0) {
331 		flush = TIOCPKT_STOP;
332 		pti->pt_flags |= PF_STOPPED;
333 	} else
334 		pti->pt_flags &= ~PF_STOPPED;
335 	pti->pt_send |= flush;
336 	/* change of perspective */
337 	flag = 0;
338 	if (flush & FREAD)
339 		flag |= FWRITE;
340 	if (flush & FWRITE)
341 		flag |= FREAD;
342 	ptcwakeup(tp, flag);
343 }
344 
345 ptcselect(dev, rw)
346 	dev_t dev;
347 	int rw;
348 {
349 	register struct tty *tp = &pt_tty[minor(dev)];
350 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
351 	struct proc *p;
352 	int s;
353 
354 	if ((tp->t_state&TS_CARR_ON) == 0)
355 		return (1);
356 	switch (rw) {
357 
358 	case FREAD:
359 		/*
360 		 * Need to block timeouts (ttrstart).
361 		 */
362 		s = spltty();
363 		if ((tp->t_state&TS_ISOPEN) &&
364 		     tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) {
365 			splx(s);
366 			return (1);
367 		}
368 		splx(s);
369 		/* FALLTHROUGH */
370 
371 	case 0:					/* exceptional */
372 		if ((tp->t_state&TS_ISOPEN) &&
373 		    (pti->pt_flags&PF_PKT && pti->pt_send ||
374 		     pti->pt_flags&PF_UCNTL && pti->pt_ucntl))
375 			return (1);
376 		if ((p = pti->pt_selr) && p->p_wchan == (caddr_t)&selwait)
377 			pti->pt_flags |= PF_RCOLL;
378 		else
379 			pti->pt_selr = u.u_procp;
380 		break;
381 
382 
383 	case FWRITE:
384 		if (tp->t_state&TS_ISOPEN) {
385 			if (pti->pt_flags & PF_REMOTE) {
386 			    if (tp->t_canq.c_cc == 0)
387 				return (1);
388 			} else {
389 			    if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2)
390 				    return (1);
391 			    if (tp->t_canq.c_cc == 0 && (tp->t_iflag&ICANON))
392 				    return (1);
393 			}
394 		}
395 		if ((p = pti->pt_selw) && p->p_wchan == (caddr_t)&selwait)
396 			pti->pt_flags |= PF_WCOLL;
397 		else
398 			pti->pt_selw = u.u_procp;
399 		break;
400 
401 	}
402 	return (0);
403 }
404 
405 ptcwrite(dev, uio, flag)
406 	dev_t dev;
407 	register struct uio *uio;
408 {
409 	register struct tty *tp = &pt_tty[minor(dev)];
410 	register struct iovec *iov;
411 	register char *cp;
412 	register int cc = 0;
413 	char locbuf[BUFSIZ];
414 	int cnt = 0;
415 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
416 	int error = 0;
417 
418 again:
419 	if ((tp->t_state&TS_ISOPEN) == 0)
420 		goto block;
421 	if (pti->pt_flags & PF_REMOTE) {
422 		if (tp->t_canq.c_cc)
423 			goto block;
424 		while (uio->uio_iovcnt > 0 && tp->t_canq.c_cc < TTYHOG - 1) {
425 			iov = uio->uio_iov;
426 			if (iov->iov_len == 0) {
427 				uio->uio_iovcnt--;
428 				uio->uio_iov++;
429 				continue;
430 			}
431 			if (cc == 0) {
432 				cc = MIN(iov->iov_len, BUFSIZ);
433 				cc = MIN(cc, TTYHOG - 1 - tp->t_canq.c_cc);
434 				cp = locbuf;
435 				error = uiomove(cp, cc, uio);
436 				if (error)
437 					return (error);
438 				/* check again for safety */
439 				if ((tp->t_state&TS_ISOPEN) == 0)
440 					return (EIO);
441 			}
442 			if (cc)
443 				(void) b_to_q(cp, cc, &tp->t_canq);
444 			cc = 0;
445 		}
446 		(void) putc(0, &tp->t_canq);
447 		ttwakeup(tp);
448 		wakeup((caddr_t)&tp->t_canq);
449 		return (0);
450 	}
451 	while (uio->uio_iovcnt > 0) {
452 		iov = uio->uio_iov;
453 		if (cc == 0) {
454 			if (iov->iov_len == 0) {
455 				uio->uio_iovcnt--;
456 				uio->uio_iov++;
457 				continue;
458 			}
459 			cc = MIN(iov->iov_len, BUFSIZ);
460 			cp = locbuf;
461 			error = uiomove(cp, cc, uio);
462 			if (error)
463 				return (error);
464 			/* check again for safety */
465 			if ((tp->t_state&TS_ISOPEN) == 0)
466 				return (EIO);
467 		}
468 		while (cc > 0) {
469 			if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
470 			   (tp->t_canq.c_cc > 0 || !(tp->t_iflag&ICANON))) {
471 				wakeup((caddr_t)&tp->t_rawq);
472 				goto block;
473 			}
474 			(*linesw[tp->t_line].l_rint)(*cp++&0377, tp);
475 			cnt++;
476 			cc--;
477 		}
478 		cc = 0;
479 	}
480 	return (0);
481 block:
482 	/*
483 	 * Come here to wait for slave to open, for space
484 	 * in outq, or space in rawq.
485 	 */
486 	if ((tp->t_state&TS_CARR_ON) == 0)
487 		return (EIO);
488 	if ((pti->pt_flags & PF_NBIO) || (flag & IO_NDELAY)) {
489 		iov->iov_base -= cc;
490 		iov->iov_len += cc;
491 		uio->uio_resid += cc;
492 		uio->uio_offset -= cc;
493 		if (cnt == 0)
494 			return (EWOULDBLOCK);
495 		return (0);
496 	}
497 if (ptydebug) printf("SLEEP(2) c_cf %d\n", u.u_procp->p_pid);
498 	if (error = tsleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI | PCATCH,
499 	    ttyout, 0))
500 		return (error);
501 	goto again;
502 }
503 
504 /*ARGSUSED*/
505 ptyioctl(dev, cmd, data, flag)
506 	caddr_t data;
507 	dev_t dev;
508 {
509 	register struct tty *tp = &pt_tty[minor(dev)];
510 	register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
511 	register u_char *cc = tp->t_cc;
512 	int stop, error;
513 	extern ttyinput();
514 
515 	/*
516 	 * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
517 	 * ttywflush(tp) will hang if there are characters in the outq.
518 	 */
519 	if (cdevsw[major(dev)].d_open == ptcopen)
520 		switch (cmd) {
521 
522 		case TIOCPKT:
523 			if (*(int *)data) {
524 				if (pti->pt_flags & PF_UCNTL)
525 					return (EINVAL);
526 				pti->pt_flags |= PF_PKT;
527 			} else
528 				pti->pt_flags &= ~PF_PKT;
529 			return (0);
530 
531 		case TIOCUCNTL:
532 			if (*(int *)data) {
533 				if (pti->pt_flags & PF_PKT)
534 					return (EINVAL);
535 				pti->pt_flags |= PF_UCNTL;
536 			} else
537 				pti->pt_flags &= ~PF_UCNTL;
538 			return (0);
539 
540 		case TIOCREMOTE:
541 			if (*(int *)data)
542 				pti->pt_flags |= PF_REMOTE;
543 			else
544 				pti->pt_flags &= ~PF_REMOTE;
545 			ttyflush(tp, FREAD|FWRITE);
546 			return (0);
547 
548 		case FIONBIO:
549 			if (*(int *)data)
550 				pti->pt_flags |= PF_NBIO;
551 			else
552 				pti->pt_flags &= ~PF_NBIO;
553 			return (0);
554 
555 		case TIOCSETP:
556 		case TIOCSETN:
557 		case TIOCSETD:
558 		case TIOCSETA:
559 		case TIOCSETAW:
560 		case TIOCSETAF:
561 		case JUNK_TIOCSETAS:
562 		case JUNK_TIOCSETAWS:
563 		case JUNK_TIOCSETAFS:
564 			while (getc(&tp->t_outq) >= 0)
565 				;
566 			break;
567 		}
568 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
569 	if (error < 0)
570 		 error = ttioctl(tp, cmd, data, flag);
571 	/*
572 	 * Since we use the tty queues internally,
573 	 * pty's can't be switched to disciplines which overwrite
574 	 * the queues.  We can't tell anything about the discipline
575 	 * from here...
576 	 */
577 	if (linesw[tp->t_line].l_rint != ttyinput) {
578 		(*linesw[tp->t_line].l_close)(tp);
579 		tp->t_line = 0;
580 		(void)(*linesw[tp->t_line].l_open)(dev, tp, flag);
581 		error = ENOTTY;
582 	}
583 	if (error < 0) {
584 		if (pti->pt_flags & PF_UCNTL &&
585 		    (cmd & ~0xff) == UIOCCMD(0)) {
586 			if (cmd & 0xff) {
587 				pti->pt_ucntl = (u_char)cmd;
588 				ptcwakeup(tp, FREAD);
589 			}
590 			return (0);
591 		}
592 		error = ENOTTY;
593 	}
594 	stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s'))
595 		&& CCEQ(cc[VSTART], CTRL('q'));
596 	if (pti->pt_flags & PF_NOSTOP) {
597 		if (stop) {
598 			pti->pt_send &= ~TIOCPKT_NOSTOP;
599 			pti->pt_send |= TIOCPKT_DOSTOP;
600 			pti->pt_flags &= ~PF_NOSTOP;
601 			ptcwakeup(tp, FREAD);
602 		}
603 	} else {
604 		if (!stop) {
605 			pti->pt_send &= ~TIOCPKT_DOSTOP;
606 			pti->pt_send |= TIOCPKT_NOSTOP;
607 			pti->pt_flags |= PF_NOSTOP;
608 			ptcwakeup(tp, FREAD);
609 		}
610 	}
611 	return (error);
612 }
613 #endif
614