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