FreeBSD/Linux Kernel Cross Reference
sys/drivers/tty/pty.c
1 /* pty.c - pseudo terminal driver Author: Kees J. Bot
2 * 30 Dec 1995
3 * PTYs can be seen as a bidirectional pipe with TTY
4 * input and output processing. For example a simple rlogin session:
5 *
6 * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7 * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
8 *
9 * This file takes care of copying data between the tty/pty device pairs and
10 * the open/read/write/close calls on the pty devices. The TTY task takes
11 * care of the input and output processing (interrupt, backspace, raw I/O,
12 * etc.) using the pty_read() and pty_write() functions as the "keyboard" and
13 * "screen" functions of the ttypX devices.
14 * Be careful when reading this code, the terms "reading" and "writing" are
15 * used both for the tty and the pty end of the pseudo tty. Writes to one
16 * end are to be read at the other end and vice-versa.
17 */
18
19 #include "../drivers.h"
20 #include <assert.h>
21 #include <termios.h>
22 #include <signal.h>
23 #include <minix/com.h>
24 #include <minix/callnr.h>
25 #include <sys/select.h>
26 #include "tty.h"
27
28 #if NR_PTYS > 0
29
30 /* PTY bookkeeping structure, one per pty/tty pair. */
31 typedef struct pty {
32 tty_t *tty; /* associated TTY structure */
33 char state; /* flags: busy, closed, ... */
34
35 /* Read call on /dev/ptypX. */
36 char rdsendreply; /* send a reply (instead of notify) */
37 char rdcaller; /* process making the call (usually FS) */
38 char rdproc; /* process that wants to read from the pty */
39 vir_bytes rdvir; /* virtual address in readers address space */
40 int rdleft; /* # bytes yet to be read */
41 int rdcum; /* # bytes written so far */
42
43 /* Write call to /dev/ptypX. */
44 char wrsendreply; /* send a reply (instead of notify) */
45 char wrcaller; /* process making the call (usually FS) */
46 char wrproc; /* process that wants to write to the pty */
47 vir_bytes wrvir; /* virtual address in writers address space */
48 int wrleft; /* # bytes yet to be written */
49 int wrcum; /* # bytes written so far */
50
51 /* Output buffer. */
52 int ocount; /* # characters in the buffer */
53 char *ohead, *otail; /* head and tail of the circular buffer */
54 char obuf[128]; /* buffer for bytes going to the pty reader */
55
56 /* select() data. */
57 int select_ops, /* Which operations do we want to know about? */
58 select_proc, /* Who wants to know about it? */
59 select_ready_ops; /* For callback. */
60 } pty_t;
61
62 #define PTY_ACTIVE 0x01 /* pty is open/active */
63 #define TTY_CLOSED 0x02 /* tty side has closed down */
64 #define PTY_CLOSED 0x04 /* pty side has closed down */
65
66 PRIVATE pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
67
68 FORWARD _PROTOTYPE( int pty_write, (tty_t *tp, int try) );
69 FORWARD _PROTOTYPE( void pty_echo, (tty_t *tp, int c) );
70 FORWARD _PROTOTYPE( void pty_start, (pty_t *pp) );
71 FORWARD _PROTOTYPE( void pty_finish, (pty_t *pp) );
72 FORWARD _PROTOTYPE( int pty_read, (tty_t *tp, int try) );
73 FORWARD _PROTOTYPE( int pty_close, (tty_t *tp, int try) );
74 FORWARD _PROTOTYPE( int pty_icancel, (tty_t *tp, int try) );
75 FORWARD _PROTOTYPE( int pty_ocancel, (tty_t *tp, int try) );
76 FORWARD _PROTOTYPE( int pty_select, (tty_t *tp, message *m) );
77
78 /*===========================================================================*
79 * do_pty *
80 *===========================================================================*/
81 PUBLIC void do_pty(tp, m_ptr)
82 tty_t *tp;
83 message *m_ptr;
84 {
85 /* Perform an open/close/read/write call on a /dev/ptypX device. */
86 pty_t *pp = tp->tty_priv;
87 int r;
88 phys_bytes p;
89
90 switch (m_ptr->m_type) {
91 case DEV_READ:
92 /* Check, store information on the reader, do I/O. */
93 if (pp->state & TTY_CLOSED) {
94 r = 0;
95 break;
96 }
97 if (pp->rdleft != 0 || pp->rdcum != 0) {
98 r = EIO;
99 break;
100 }
101 if (m_ptr->COUNT <= 0) {
102 r = EINVAL;
103 break;
104 }
105 #if DEAD_CODE
106 if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS,
107 m_ptr->COUNT) == 0) {
108 #else
109 if ((r = sys_umap(m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS,
110 m_ptr->COUNT, &p)) != OK) {
111 #endif
112 break;
113 }
114 pp->rdsendreply = TRUE;
115 pp->rdcaller = m_ptr->m_source;
116 pp->rdproc = m_ptr->PROC_NR;
117 pp->rdvir = (vir_bytes) m_ptr->ADDRESS;
118 pp->rdleft = m_ptr->COUNT;
119 pty_start(pp);
120 handle_events(tp);
121 if (pp->rdleft == 0) return; /* already done */
122
123 if (m_ptr->TTY_FLAGS & O_NONBLOCK) {
124 r = EAGAIN; /* don't suspend */
125 pp->rdleft = pp->rdcum = 0;
126 } else {
127 r = SUSPEND; /* do suspend */
128 pp->rdsendreply = FALSE;
129 }
130 break;
131
132 case DEV_WRITE:
133 /* Check, store information on the writer, do I/O. */
134 if (pp->state & TTY_CLOSED) {
135 r = EIO;
136 break;
137 }
138 if (pp->wrleft != 0 || pp->wrcum != 0) {
139 r = EIO;
140 break;
141 }
142 if (m_ptr->COUNT <= 0) {
143 r = EINVAL;
144 break;
145 }
146 #if DEAD_CODE
147 if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS,
148 m_ptr->COUNT) == 0) {
149 r = EFAULT;
150 #else
151 if ((r = sys_umap(m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS,
152 m_ptr->COUNT, &p)) != OK) {
153 #endif
154 break;
155 }
156 pp->wrsendreply = TRUE;
157 pp->wrcaller = m_ptr->m_source;
158 pp->wrproc = m_ptr->PROC_NR;
159 pp->wrvir = (vir_bytes) m_ptr->ADDRESS;
160 pp->wrleft = m_ptr->COUNT;
161 handle_events(tp);
162 if (pp->wrleft == 0) return; /* already done */
163
164 if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* don't suspend */
165 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
166 pp->wrleft = pp->wrcum = 0;
167 } else {
168 pp->wrsendreply = FALSE; /* do suspend */
169 r = SUSPEND;
170 }
171 break;
172
173 case DEV_OPEN:
174 r = pp->state != 0 ? EIO : OK;
175 pp->state |= PTY_ACTIVE;
176 pp->rdcum = 0;
177 pp->wrcum = 0;
178 break;
179
180 case DEV_CLOSE:
181 r = OK;
182 if (pp->state & TTY_CLOSED) {
183 pp->state = 0;
184 } else {
185 pp->state |= PTY_CLOSED;
186 sigchar(tp, SIGHUP);
187 }
188 break;
189
190 case DEV_SELECT:
191 r = pty_select(tp, m_ptr);
192 break;
193
194 case CANCEL:
195 if (m_ptr->PROC_NR == pp->rdproc) {
196 /* Cancel a read from a PTY. */
197 pp->rdleft = pp->rdcum = 0;
198 }
199 if (m_ptr->PROC_NR == pp->wrproc) {
200 /* Cancel a write to a PTY. */
201 pp->wrleft = pp->wrcum = 0;
202 }
203 r = EINTR;
204 break;
205
206 default:
207 r = EINVAL;
208 }
209 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
210 }
211
212 /*===========================================================================*
213 * pty_write *
214 *===========================================================================*/
215 PRIVATE int pty_write(tp, try)
216 tty_t *tp;
217 int try;
218 {
219 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
220 * /dev/ttypX to the output buffer.
221 */
222 pty_t *pp = tp->tty_priv;
223 int count, ocount, s;
224 phys_bytes user_phys;
225
226 /* PTY closed down? */
227 if (pp->state & PTY_CLOSED) {
228 if (try) return 1;
229 if (tp->tty_outleft > 0) {
230 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
231 tp->tty_outproc, EIO);
232 tp->tty_outleft = tp->tty_outcum = 0;
233 }
234 return;
235 }
236
237 /* While there is something to do. */
238 for (;;) {
239 ocount = buflen(pp->obuf) - pp->ocount;
240 if (try) return (ocount > 0);
241 count = bufend(pp->obuf) - pp->ohead;
242 if (count > ocount) count = ocount;
243 if (count > tp->tty_outleft) count = tp->tty_outleft;
244 if (count == 0 || tp->tty_inhibited)
245 break;
246
247 /* Copy from user space to the PTY output buffer. */
248 if ((s = sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir,
249 SELF, D, (vir_bytes) pp->ohead, (phys_bytes) count)) != OK) {
250 printf("pty tty%d: copy failed (error %d)\n", s);
251 break;
252 }
253
254 /* Perform output processing on the output buffer. */
255 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
256 if (count == 0) break;
257
258 /* Assume echoing messed up by output. */
259 tp->tty_reprint = TRUE;
260
261 /* Bookkeeping. */
262 pp->ocount += ocount;
263 if ((pp->ohead += ocount) >= bufend(pp->obuf))
264 pp->ohead -= buflen(pp->obuf);
265 pty_start(pp);
266 tp->tty_out_vir += count;
267 tp->tty_outcum += count;
268 if ((tp->tty_outleft -= count) == 0) {
269 /* Output is finished, reply to the writer. */
270 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
271 tp->tty_outproc, tp->tty_outcum);
272 tp->tty_outcum = 0;
273 }
274 }
275 pty_finish(pp);
276 return 1;
277 }
278
279 /*===========================================================================*
280 * pty_echo *
281 *===========================================================================*/
282 PRIVATE void pty_echo(tp, c)
283 tty_t *tp;
284 int c;
285 {
286 /* Echo one character. (Like pty_write, but only one character, optionally.) */
287
288 pty_t *pp = tp->tty_priv;
289 int count, ocount;
290
291 ocount = buflen(pp->obuf) - pp->ocount;
292 if (ocount == 0) return; /* output buffer full */
293 count = 1;
294 *pp->ohead = c; /* add one character */
295
296 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
297 if (count == 0) return;
298
299 pp->ocount += ocount;
300 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
301 pty_start(pp);
302 }
303
304 /*===========================================================================*
305 * pty_start *
306 *===========================================================================*/
307 PRIVATE void pty_start(pp)
308 pty_t *pp;
309 {
310 /* Transfer bytes written to the output buffer to the PTY reader. */
311 int count;
312
313 /* While there are things to do. */
314 for (;;) {
315 int s;
316 count = bufend(pp->obuf) - pp->otail;
317 if (count > pp->ocount) count = pp->ocount;
318 if (count > pp->rdleft) count = pp->rdleft;
319 if (count == 0) break;
320
321 /* Copy from the output buffer to the readers address space. */
322 if ((s = sys_vircopy(SELF, D, (vir_bytes)pp->otail,
323 (vir_bytes) pp->rdproc, D, (vir_bytes) pp->rdvir, (phys_bytes) count)) != OK) {
324 printf("pty tty%d: copy failed (error %d)\n", s);
325 break;
326 }
327
328 /* Bookkeeping. */
329 pp->ocount -= count;
330 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
331 pp->rdvir += count;
332 pp->rdcum += count;
333 pp->rdleft -= count;
334 }
335 }
336
337 /*===========================================================================*
338 * pty_finish *
339 *===========================================================================*/
340 PRIVATE void pty_finish(pp)
341 pty_t *pp;
342 {
343 /* Finish the read request of a PTY reader if there is at least one byte
344 * transferred.
345 */
346 if (pp->rdcum > 0) {
347 if (pp->rdsendreply) {
348 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
349 pp->rdleft = pp->rdcum = 0;
350 }
351 else
352 notify(pp->rdcaller);
353 }
354
355 }
356
357 /*===========================================================================*
358 * pty_read *
359 *===========================================================================*/
360 PRIVATE int pty_read(tp, try)
361 tty_t *tp;
362 int try;
363 {
364 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
365 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
366 */
367 pty_t *pp = tp->tty_priv;
368 char c;
369
370 if (pp->state & PTY_CLOSED) {
371 if (try) return 1;
372 if (tp->tty_inleft > 0) {
373 tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc,
374 tp->tty_incum);
375 tp->tty_inleft = tp->tty_incum = 0;
376 }
377 return 1;
378 }
379
380 if (try) {
381 if (pp->wrleft > 0)
382 return 1;
383 return 0;
384 }
385
386 while (pp->wrleft > 0) {
387 int s;
388
389 /* Transfer one character to 'c'. */
390 if ((s = sys_vircopy(pp->wrproc, D, (vir_bytes) pp->wrvir,
391 SELF, D, (vir_bytes) &c, (phys_bytes) 1)) != OK) {
392 printf("pty: copy failed (error %d)\n", s);
393 break;
394 }
395
396 /* Input processing. */
397 if (in_process(tp, &c, 1) == 0) break;
398
399 /* PTY writer bookkeeping. */
400 pp->wrvir++;
401 pp->wrcum++;
402 if (--pp->wrleft == 0) {
403 if (pp->wrsendreply) {
404 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
405 pp->wrcum);
406 pp->wrcum = 0;
407 }
408 else
409 notify(pp->wrcaller);
410 }
411 }
412 }
413
414 /*===========================================================================*
415 * pty_close *
416 *===========================================================================*/
417 PRIVATE int pty_close(tp, try)
418 tty_t *tp;
419 int try;
420 {
421 /* The tty side has closed, so shut down the pty side. */
422 pty_t *pp = tp->tty_priv;
423
424 if (!(pp->state & PTY_ACTIVE)) return;
425
426 if (pp->rdleft > 0) {
427 assert(!pp->rdsendreply);
428 notify(pp->rdcaller);
429 }
430
431 if (pp->wrleft > 0) {
432 assert(!pp->wrsendreply);
433 notify(pp->wrcaller);
434 }
435
436 if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED;
437 }
438
439 /*===========================================================================*
440 * pty_icancel *
441 *===========================================================================*/
442 PRIVATE int pty_icancel(tp, try)
443 tty_t *tp;
444 int try;
445 {
446 /* Discard waiting input. */
447 pty_t *pp = tp->tty_priv;
448
449 if (pp->wrleft > 0) {
450 assert(!pp->wrsendreply);
451 pp->wrcum += pp->wrleft;
452 pp->wrleft= 0;
453 notify(pp->wrcaller);
454 }
455 }
456
457 /*===========================================================================*
458 * pty_ocancel *
459 *===========================================================================*/
460 PRIVATE int pty_ocancel(tp, try)
461 tty_t *tp;
462 int try;
463 {
464 /* Drain the output buffer. */
465 pty_t *pp = tp->tty_priv;
466
467 pp->ocount = 0;
468 pp->otail = pp->ohead;
469 }
470
471 /*===========================================================================*
472 * pty_init *
473 *===========================================================================*/
474 PUBLIC void pty_init(tp)
475 tty_t *tp;
476 {
477 pty_t *pp;
478 int line;
479
480 /* Associate PTY and TTY structures. */
481 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
482 pp = tp->tty_priv = &pty_table[line];
483 pp->tty = tp;
484 pp->select_ops = 0;
485
486 /* Set up output queue. */
487 pp->ohead = pp->otail = pp->obuf;
488
489 /* Fill in TTY function hooks. */
490 tp->tty_devread = pty_read;
491 tp->tty_devwrite = pty_write;
492 tp->tty_echo = pty_echo;
493 tp->tty_icancel = pty_icancel;
494 tp->tty_ocancel = pty_ocancel;
495 tp->tty_close = pty_close;
496 tp->tty_select_ops = 0;
497 }
498
499 /*===========================================================================*
500 * pty_status *
501 *===========================================================================*/
502 PUBLIC int pty_status(message *m_ptr)
503 {
504 int i, event_found;
505 pty_t *pp;
506
507 event_found = 0;
508 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
509 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
510 pp->rdcum > 0) &&
511 pp->rdcaller == m_ptr->m_source)
512 {
513 m_ptr->m_type = DEV_REVIVE;
514 m_ptr->REP_PROC_NR = pp->rdproc;
515 m_ptr->REP_STATUS = pp->rdcum;
516
517 pp->rdleft = pp->rdcum = 0;
518 event_found = 1;
519 break;
520 }
521
522 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
523 pp->wrcum > 0) &&
524 pp->wrcaller == m_ptr->m_source)
525 {
526 m_ptr->m_type = DEV_REVIVE;
527 m_ptr->REP_PROC_NR = pp->wrproc;
528 if (pp->wrcum == 0)
529 m_ptr->REP_STATUS = EIO;
530 else
531 m_ptr->REP_STATUS = pp->wrcum;
532
533 pp->wrleft = pp->wrcum = 0;
534 event_found = 1;
535 break;
536 }
537
538 if (pp->select_ready_ops && pp->select_proc == m_ptr->m_source) {
539 m_ptr->m_type = DEV_IO_READY;
540 m_ptr->DEV_MINOR = PTYPX_MINOR + i;
541 m_ptr->DEV_SEL_OPS = pp->select_ready_ops;
542 pp->select_ready_ops = 0;
543 event_found = 1;
544 break;
545 }
546 }
547 return event_found;
548 }
549
550 /*===========================================================================*
551 * select_try_pty *
552 *===========================================================================*/
553 PRIVATE int select_try_pty(tty_t *tp, int ops)
554 {
555 pty_t *pp = tp->tty_priv;
556 int r = 0;
557
558 if (ops & SEL_WR) {
559 /* Write won't block on error. */
560 if (pp->state & TTY_CLOSED) r |= SEL_WR;
561 else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
562 else r |= SEL_WR;
563 }
564
565 if (ops & SEL_RD) {
566 /* Read won't block on error. */
567 if (pp->state & TTY_CLOSED) r |= SEL_RD;
568 else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
569 else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
570 }
571
572 return r;
573 }
574
575 /*===========================================================================*
576 * select_retry_pty *
577 *===========================================================================*/
578 PUBLIC void select_retry_pty(tty_t *tp)
579 {
580 pty_t *pp = tp->tty_priv;
581 int r;
582
583 /* See if the pty side of a pty is ready to return a select. */
584 if (pp->select_ops && (r=select_try_pty(tp, pp->select_ops))) {
585 pp->select_ops &= ~r;
586 pp->select_ready_ops |= r;
587 notify(pp->select_proc);
588 }
589 }
590
591 /*===========================================================================*
592 * pty_select *
593 *===========================================================================*/
594 PRIVATE int pty_select(tty_t *tp, message *m)
595 {
596 pty_t *pp = tp->tty_priv;
597 int ops, ready_ops = 0, watch;
598
599 ops = m->PROC_NR & (SEL_RD|SEL_WR|SEL_ERR);
600 watch = (m->PROC_NR & SEL_NOTIFY) ? 1 : 0;
601
602 ready_ops = select_try_pty(tp, ops);
603
604 if (!ready_ops && ops && watch) {
605 pp->select_ops |= ops;
606 pp->select_proc = m->m_source;
607 }
608
609 return ready_ops;
610 }
611
612 #endif /* NR_PTYS > 0 */
Cache object: d946d63c92e904900bee60054c218fec
|