xref: /original-bsd/sys/kern/tty_pty.c (revision 7a7a259e)
1 /*
2  * Copyright (c) 1982 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	6.18 (Berkeley) 01/22/86
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_ispeed = tp->t_ospeed = EXTB;
73 		tp->t_flags = 0;	/* No features (nor raw mode) */
74 	} else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
75 		return (EBUSY);
76 	if (tp->t_oproc)			/* Ctrlr still around. */
77 		tp->t_state |= TS_CARR_ON;
78 	while ((tp->t_state & TS_CARR_ON) == 0) {
79 		tp->t_state |= TS_WOPEN;
80 		sleep((caddr_t)&tp->t_rawq, TTIPRI);
81 	}
82 	error = (*linesw[tp->t_line].l_open)(dev, tp);
83 	ptcwakeup(tp, FREAD|FWRITE);
84 	return (error);
85 }
86 
87 ptsclose(dev)
88 	dev_t dev;
89 {
90 	register struct tty *tp;
91 
92 	tp = &pt_tty[minor(dev)];
93 	(*linesw[tp->t_line].l_close)(tp);
94 	ttyclose(tp);
95 	ptcwakeup(tp, FREAD|FWRITE);
96 }
97 
98 ptsread(dev, uio)
99 	dev_t dev;
100 	struct uio *uio;
101 {
102 	register struct tty *tp = &pt_tty[minor(dev)];
103 	register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
104 	int error = 0;
105 
106 again:
107 	if (pti->pt_flags & PF_REMOTE) {
108 		while (tp == u.u_ttyp && u.u_procp->p_pgrp != tp->t_pgrp) {
109 			if ((u.u_procp->p_sigignore & sigmask(SIGTTIN)) ||
110 			    (u.u_procp->p_sigmask & sigmask(SIGTTIN)) ||
111 			    u.u_procp->p_flag&SVFORK)
112 				return (EIO);
113 			gsignal(u.u_procp->p_pgrp, SIGTTIN);
114 			sleep((caddr_t)&lbolt, TTIPRI);
115 		}
116 		if (tp->t_canq.c_cc == 0) {
117 			if (tp->t_state & TS_NBIO)
118 				return (EWOULDBLOCK);
119 			sleep((caddr_t)&tp->t_canq, TTIPRI);
120 			goto again;
121 		}
122 		while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
123 			if (ureadc(getc(&tp->t_canq), uio) < 0) {
124 				error = EFAULT;
125 				break;
126 			}
127 		if (tp->t_canq.c_cc == 1)
128 			(void) getc(&tp->t_canq);
129 		if (tp->t_canq.c_cc)
130 			return (error);
131 	} else
132 		if (tp->t_oproc)
133 			error = (*linesw[tp->t_line].l_read)(tp, uio);
134 	ptcwakeup(tp, FWRITE);
135 	return (error);
136 }
137 
138 /*
139  * Write to pseudo-tty.
140  * Wakeups of controlling tty will happen
141  * indirectly, when tty driver calls ptsstart.
142  */
143 ptswrite(dev, uio)
144 	dev_t dev;
145 	struct uio *uio;
146 {
147 	register struct tty *tp;
148 
149 	tp = &pt_tty[minor(dev)];
150 	if (tp->t_oproc == 0)
151 		return (EIO);
152 	return ((*linesw[tp->t_line].l_write)(tp, uio));
153 }
154 
155 /*
156  * Start output on pseudo-tty.
157  * Wake up process selecting or sleeping for input from controlling tty.
158  */
159 ptsstart(tp)
160 	struct tty *tp;
161 {
162 	register struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
163 
164 	if (tp->t_state & TS_TTSTOP)
165 		return;
166 	if (pti->pt_flags & PF_STOPPED) {
167 		pti->pt_flags &= ~PF_STOPPED;
168 		pti->pt_send = TIOCPKT_START;
169 	}
170 	ptcwakeup(tp, FREAD);
171 }
172 
173 ptcwakeup(tp, flag)
174 	struct tty *tp;
175 {
176 	struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
177 
178 	if (flag & FREAD) {
179 		if (pti->pt_selr) {
180 			selwakeup(pti->pt_selr, pti->pt_flags & PF_RCOLL);
181 			pti->pt_selr = 0;
182 			pti->pt_flags &= ~PF_RCOLL;
183 		}
184 		wakeup((caddr_t)&tp->t_outq.c_cf);
185 	}
186 	if (flag & FWRITE) {
187 		if (pti->pt_selw) {
188 			selwakeup(pti->pt_selw, pti->pt_flags & PF_WCOLL);
189 			pti->pt_selw = 0;
190 			pti->pt_flags &= ~PF_WCOLL;
191 		}
192 		wakeup((caddr_t)&tp->t_rawq.c_cf);
193 	}
194 }
195 
196 /*ARGSUSED*/
197 ptcopen(dev, flag)
198 	dev_t dev;
199 	int flag;
200 {
201 	register struct tty *tp;
202 	struct pt_ioctl *pti;
203 
204 	if (minor(dev) >= NPTY)
205 		return (ENXIO);
206 	tp = &pt_tty[minor(dev)];
207 	if (tp->t_oproc)
208 		return (EIO);
209 	tp->t_oproc = ptsstart;
210 	(void)(*linesw[tp->t_line].l_modem)(tp, 1);
211 	tp->t_state |= TS_CARR_ON;
212 	pti = &pt_ioctl[minor(dev)];
213 	pti->pt_flags = 0;
214 	pti->pt_send = 0;
215 	pti->pt_ucntl = 0;
216 	return (0);
217 }
218 
219 ptcclose(dev)
220 	dev_t dev;
221 {
222 	register struct tty *tp;
223 
224 	tp = &pt_tty[minor(dev)];
225 	(void)(*linesw[tp->t_line].l_modem)(tp, 0);
226 	tp->t_oproc = 0;		/* mark closed */
227 }
228 
229 ptcread(dev, uio)
230 	dev_t dev;
231 	struct uio *uio;
232 {
233 	register struct tty *tp = &pt_tty[minor(dev)];
234 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
235 	char buf[BUFSIZ];
236 	int error = 0, cc;
237 
238 	/*
239 	 * We want to block until the slave
240 	 * is open, and there's something to read;
241 	 * but if we lost the slave or we're NBIO,
242 	 * then return the appropriate error instead.
243 	 */
244 	for (;;) {
245 		if (tp->t_state&TS_ISOPEN) {
246 			if (pti->pt_flags&PF_PKT && pti->pt_send) {
247 				error = ureadc(pti->pt_send, uio);
248 				if (error)
249 					return (error);
250 				pti->pt_send = 0;
251 				return (0);
252 			}
253 			if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
254 				error = ureadc(pti->pt_ucntl, uio);
255 				if (error)
256 					return (error);
257 				pti->pt_ucntl = 0;
258 				return (0);
259 			}
260 			if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
261 				break;
262 		}
263 		if ((tp->t_state&TS_CARR_ON) == 0)
264 			return (EIO);
265 		if (pti->pt_flags&PF_NBIO)
266 			return (EWOULDBLOCK);
267 		sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI);
268 	}
269 	if (pti->pt_flags & (PF_PKT|PF_UCNTL))
270 		error = ureadc(0, uio);
271 	while (uio->uio_resid > 0 && error == 0) {
272 		cc = q_to_b(&tp->t_outq, buf, MIN(uio->uio_resid, BUFSIZ));
273 		if (cc <= 0)
274 			break;
275 		error = uiomove(buf, cc, UIO_READ, uio);
276 	}
277 	if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
278 		if (tp->t_state&TS_ASLEEP) {
279 			tp->t_state &= ~TS_ASLEEP;
280 			wakeup((caddr_t)&tp->t_outq);
281 		}
282 		if (tp->t_wsel) {
283 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
284 			tp->t_wsel = 0;
285 			tp->t_state &= ~TS_WCOLL;
286 		}
287 	}
288 	return (error);
289 }
290 
291 ptsstop(tp, flush)
292 	register struct tty *tp;
293 	int flush;
294 {
295 	struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)];
296 	int flag;
297 
298 	/* note: FLUSHREAD and FLUSHWRITE already ok */
299 	if (flush == 0) {
300 		flush = TIOCPKT_STOP;
301 		pti->pt_flags |= PF_STOPPED;
302 	} else
303 		pti->pt_flags &= ~PF_STOPPED;
304 	pti->pt_send |= flush;
305 	/* change of perspective */
306 	flag = 0;
307 	if (flush & FREAD)
308 		flag |= FWRITE;
309 	if (flush & FWRITE)
310 		flag |= FREAD;
311 	ptcwakeup(tp, flag);
312 }
313 
314 ptcselect(dev, rw)
315 	dev_t dev;
316 	int rw;
317 {
318 	register struct tty *tp = &pt_tty[minor(dev)];
319 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
320 	struct proc *p;
321 	int s;
322 
323 	if ((tp->t_state&TS_CARR_ON) == 0)
324 		return (1);
325 	switch (rw) {
326 
327 	case FREAD:
328 		/*
329 		 * Need to block timeouts (ttrstart).
330 		 */
331 		s = spltty();
332 		if ((tp->t_state&TS_ISOPEN) &&
333 		     tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) {
334 			splx(s);
335 			return (1);
336 		}
337 		splx(s);
338 		/* FALLTHROUGH */
339 
340 	case 0:					/* exceptional */
341 		if ((tp->t_state&TS_ISOPEN) &&
342 		    (pti->pt_flags&PF_PKT && pti->pt_send ||
343 		     pti->pt_flags&PF_UCNTL && pti->pt_ucntl))
344 			return (1);
345 		if ((p = pti->pt_selr) && p->p_wchan == (caddr_t)&selwait)
346 			pti->pt_flags |= PF_RCOLL;
347 		else
348 			pti->pt_selr = u.u_procp;
349 		break;
350 
351 
352 	case FWRITE:
353 		if (tp->t_state&TS_ISOPEN) {
354 			if (pti->pt_flags & PF_REMOTE) {
355 			    if (tp->t_canq.c_cc == 0)
356 				return (1);
357 			} else {
358 			    if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2)
359 				    return (1);
360 			    if (tp->t_canq.c_cc == 0 &&
361 			        tp->t_flags & (RAW|CBREAK) == 0)
362 				    return (1);
363 			}
364 		}
365 		if ((p = pti->pt_selw) && p->p_wchan == (caddr_t)&selwait)
366 			pti->pt_flags |= PF_WCOLL;
367 		else
368 			pti->pt_selw = u.u_procp;
369 		break;
370 
371 	}
372 	return (0);
373 }
374 
375 ptcwrite(dev, uio)
376 	dev_t dev;
377 	register struct uio *uio;
378 {
379 	register struct tty *tp = &pt_tty[minor(dev)];
380 	register struct iovec *iov;
381 	register char *cp;
382 	register int cc = 0;
383 	char locbuf[BUFSIZ];
384 	int cnt = 0;
385 	struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
386 	int error = 0;
387 
388 again:
389 	if ((tp->t_state&TS_ISOPEN) == 0)
390 		goto block;
391 	if (pti->pt_flags & PF_REMOTE) {
392 		if (tp->t_canq.c_cc)
393 			goto block;
394 		while (uio->uio_iovcnt > 0 && tp->t_canq.c_cc < TTYHOG - 1) {
395 			iov = uio->uio_iov;
396 			if (iov->iov_len == 0) {
397 				uio->uio_iovcnt--;
398 				uio->uio_iov++;
399 				continue;
400 			}
401 			if (cc == 0) {
402 				cc = MIN(iov->iov_len, BUFSIZ);
403 				cc = MIN(cc, TTYHOG - 1 - tp->t_canq.c_cc);
404 				cp = locbuf;
405 				error = uiomove(cp, cc, UIO_WRITE, uio);
406 				if (error)
407 					return (error);
408 				/* check again for safety */
409 				if ((tp->t_state&TS_ISOPEN) == 0)
410 					return (EIO);
411 			}
412 			if (cc)
413 				(void) b_to_q(cp, cc, &tp->t_canq);
414 			cc = 0;
415 		}
416 		(void) putc(0, &tp->t_canq);
417 		ttwakeup(tp);
418 		wakeup((caddr_t)&tp->t_canq);
419 		return (0);
420 	}
421 	while (uio->uio_iovcnt > 0) {
422 		iov = uio->uio_iov;
423 		if (cc == 0) {
424 			if (iov->iov_len == 0) {
425 				uio->uio_iovcnt--;
426 				uio->uio_iov++;
427 				continue;
428 			}
429 			cc = MIN(iov->iov_len, BUFSIZ);
430 			cp = locbuf;
431 			error = uiomove(cp, cc, UIO_WRITE, uio);
432 			if (error)
433 				return (error);
434 			/* check again for safety */
435 			if ((tp->t_state&TS_ISOPEN) == 0)
436 				return (EIO);
437 		}
438 		while (cc > 0) {
439 			if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
440 			   (tp->t_canq.c_cc > 0 ||
441 			      tp->t_flags & (RAW|CBREAK))) {
442 				wakeup((caddr_t)&tp->t_rawq);
443 				goto block;
444 			}
445 			(*linesw[tp->t_line].l_rint)(*cp++, tp);
446 			cnt++;
447 			cc--;
448 		}
449 		cc = 0;
450 	}
451 	return (0);
452 block:
453 	/*
454 	 * Come here to wait for slave to open, for space
455 	 * in outq, or space in rawq.
456 	 */
457 	if ((tp->t_state&TS_CARR_ON) == 0)
458 		return (EIO);
459 	if (pti->pt_flags & PF_NBIO) {
460 		iov->iov_base -= cc;
461 		iov->iov_len += cc;
462 		uio->uio_resid += cc;
463 		uio->uio_offset -= cc;
464 		if (cnt == 0)
465 			return (EWOULDBLOCK);
466 		return (0);
467 	}
468 	sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI);
469 	goto again;
470 }
471 
472 /*ARGSUSED*/
473 ptyioctl(dev, cmd, data, flag)
474 	caddr_t data;
475 	dev_t dev;
476 {
477 	register struct tty *tp = &pt_tty[minor(dev)];
478 	register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
479 	int stop, error;
480 	extern ttyinput();
481 
482 	/*
483 	 * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
484 	 * ttywflush(tp) will hang if there are characters in the outq.
485 	 */
486 	if (cdevsw[major(dev)].d_open == ptcopen)
487 		switch (cmd) {
488 
489 		case TIOCPKT:
490 			if (*(int *)data) {
491 				if (pti->pt_flags & PF_UCNTL)
492 					return (EINVAL);
493 				pti->pt_flags |= PF_PKT;
494 			} else
495 				pti->pt_flags &= ~PF_PKT;
496 			return (0);
497 
498 		case TIOCUCNTL:
499 			if (*(int *)data) {
500 				if (pti->pt_flags & PF_PKT)
501 					return (EINVAL);
502 				pti->pt_flags |= PF_UCNTL;
503 			} else
504 				pti->pt_flags &= ~PF_UCNTL;
505 			return (0);
506 
507 		case TIOCREMOTE:
508 			if (*(int *)data)
509 				pti->pt_flags |= PF_REMOTE;
510 			else
511 				pti->pt_flags &= ~PF_REMOTE;
512 			ttyflush(tp, FREAD|FWRITE);
513 			return (0);
514 
515 		case FIONBIO:
516 			if (*(int *)data)
517 				pti->pt_flags |= PF_NBIO;
518 			else
519 				pti->pt_flags &= ~PF_NBIO;
520 			return (0);
521 
522 		case TIOCSETP:
523 		case TIOCSETN:
524 		case TIOCSETD:
525 			while (getc(&tp->t_outq) >= 0)
526 				;
527 			break;
528 		}
529 	error = ttioctl(tp, cmd, data, flag);
530 	/*
531 	 * Since we use the tty queues internally,
532 	 * pty's can't be switched to disciplines which overwrite
533 	 * the queues.  We can't tell anything about the discipline
534 	 * from here...
535 	 */
536 	if (linesw[tp->t_line].l_rint != ttyinput) {
537 		(*linesw[tp->t_line].l_close)(tp);
538 		tp->t_line = 0;
539 		(void)(*linesw[tp->t_line].l_open)(dev, tp);
540 		error = ENOTTY;
541 	}
542 	if (error < 0) {
543 		if (pti->pt_flags & PF_UCNTL &&
544 		    (cmd & ~0xff) == _IO(u,0)) {
545 			if (cmd & 0xff) {
546 				pti->pt_ucntl = (u_char)cmd;
547 				ptcwakeup(tp, FREAD);
548 			}
549 			return (0);
550 		}
551 		error = ENOTTY;
552 	}
553 	stop = (tp->t_flags & RAW) == 0 &&
554 	    tp->t_stopc == CTRL(s) && tp->t_startc == CTRL(q);
555 	if (pti->pt_flags & PF_NOSTOP) {
556 		if (stop) {
557 			pti->pt_send &= ~TIOCPKT_NOSTOP;
558 			pti->pt_send |= TIOCPKT_DOSTOP;
559 			pti->pt_flags &= ~PF_NOSTOP;
560 			ptcwakeup(tp, FREAD);
561 		}
562 	} else {
563 		if (!stop) {
564 			pti->pt_send &= ~TIOCPKT_DOSTOP;
565 			pti->pt_send |= TIOCPKT_NOSTOP;
566 			pti->pt_flags |= PF_NOSTOP;
567 			ptcwakeup(tp, FREAD);
568 		}
569 	}
570 	return (error);
571 }
572 #endif
573