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