<sys/socket.h>: turn off MSG_NOSIGNAL
[minix3.git] / drivers / pty / pty.c
blob772aa5b0931507a10747ed2d3ed7c0c7804ca563
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:
6 * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7 * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
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_slave_read() and pty_slave_write() functions as the
13 * "keyboard" and "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 (slave) and the pty (master) end of the pseudo tty.
16 * Writes to one end are to be read at the other end and vice-versa.
19 #include <minix/drivers.h>
20 #include <termios.h>
21 #include <assert.h>
22 #include <sys/termios.h>
23 #include <signal.h>
24 #include "tty.h"
26 /* PTY bookkeeping structure, one per pty/tty pair. */
27 typedef struct pty {
28 tty_t *tty; /* associated TTY structure */
29 char state; /* flags: busy, closed, ... */
31 /* Read call on master (/dev/ptypX). */
32 endpoint_t rdcaller; /* process making the call, or NONE if none */
33 cdev_id_t rdid; /* ID of suspended read request */
34 cp_grant_id_t rdgrant; /* grant for reader's address space */
35 size_t rdleft; /* # bytes yet to be read */
36 size_t rdcum; /* # bytes written so far */
38 /* Write call to master (/dev/ptypX). */
39 endpoint_t wrcaller; /* process making the call, or NONE if none*/
40 cdev_id_t wrid; /* ID of suspended write request */
41 cp_grant_id_t wrgrant; /* grant for writer's address space */
42 size_t wrleft; /* # bytes yet to be written */
43 size_t wrcum; /* # bytes written so far */
45 /* Output buffer. */
46 int ocount; /* # characters in the buffer */
47 char *ohead, *otail; /* head and tail of the circular buffer */
48 char obuf[2048]; /* buffer for bytes going to the pty reader */
50 /* select() data. */
51 unsigned int select_ops; /* Which operations do we want to know about? */
52 endpoint_t select_proc; /* Who wants to know about it? */
53 } pty_t;
55 #define TTY_ACTIVE 0x01 /* tty is open/active */
56 #define PTY_ACTIVE 0x02 /* pty is open/active */
57 #define TTY_CLOSED 0x04 /* tty side has closed down */
58 #define PTY_CLOSED 0x08 /* pty side has closed down */
60 static pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
62 static void pty_start(pty_t *pp);
63 static void pty_finish(pty_t *pp);
65 static int pty_master_open(devminor_t minor, int access,
66 endpoint_t user_endpt);
67 static int pty_master_close(devminor_t minor);
68 static ssize_t pty_master_read(devminor_t minor, u64_t position,
69 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
70 cdev_id_t id);
71 static ssize_t pty_master_write(devminor_t minor, u64_t position,
72 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
73 cdev_id_t id);
74 static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
75 static int pty_master_select(devminor_t minor, unsigned int ops,
76 endpoint_t endpt);
78 static struct chardriver pty_master_tab = {
79 .cdr_open = pty_master_open,
80 .cdr_close = pty_master_close,
81 .cdr_read = pty_master_read,
82 .cdr_write = pty_master_write,
83 .cdr_cancel = pty_master_cancel,
84 .cdr_select = pty_master_select
87 /*===========================================================================*
88 * pty_master_open *
89 *===========================================================================*/
90 static int pty_master_open(devminor_t minor, int UNUSED(access),
91 endpoint_t UNUSED(user_endpt))
93 tty_t *tp;
94 pty_t *pp;
96 assert(minor >= PTYPX_MINOR && minor < PTYPX_MINOR + NR_PTYS);
98 if ((tp = line2tty(minor)) == NULL)
99 return ENXIO;
100 pp = tp->tty_priv;
102 if (pp->state & PTY_ACTIVE)
103 return EIO;
105 pp->state |= PTY_ACTIVE;
106 pp->rdcum = 0;
107 pp->wrcum = 0;
109 return OK;
112 /*===========================================================================*
113 * pty_master_close *
114 *===========================================================================*/
115 static int pty_master_close(devminor_t minor)
117 tty_t *tp;
118 pty_t *pp;
120 if ((tp = line2tty(minor)) == NULL)
121 return ENXIO;
122 pp = tp->tty_priv;
124 if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
125 pp->state = 0;
126 } else {
127 pp->state |= PTY_CLOSED;
128 sigchar(tp, SIGHUP, 1);
131 return OK;
134 /*===========================================================================*
135 * pty_master_read *
136 *===========================================================================*/
137 static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
138 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
139 cdev_id_t id)
141 tty_t *tp;
142 pty_t *pp;
143 ssize_t r;
145 if ((tp = line2tty(minor)) == NULL)
146 return ENXIO;
147 pp = tp->tty_priv;
149 /* Check, store information on the reader, do I/O. */
150 if (pp->state & TTY_CLOSED)
151 return 0; /* EOF */
153 if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
154 return EIO;
156 if (size <= 0)
157 return EINVAL;
159 pp->rdcaller = endpt;
160 pp->rdid = id;
161 pp->rdgrant = grant;
162 pp->rdleft = size;
163 pty_start(pp);
165 handle_events(tp);
167 if (pp->rdleft == 0) {
168 pp->rdcaller = NONE;
169 return EDONTREPLY; /* already done */
172 if (flags & CDEV_NONBLOCK) {
173 r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
174 pp->rdleft = pp->rdcum = 0;
175 pp->rdcaller = NONE;
176 return r;
179 return EDONTREPLY; /* do suspend */
182 /*===========================================================================*
183 * pty_master_write *
184 *===========================================================================*/
185 static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
186 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
187 cdev_id_t id)
189 tty_t *tp;
190 pty_t *pp;
191 ssize_t r;
193 if ((tp = line2tty(minor)) == NULL)
194 return ENXIO;
195 pp = tp->tty_priv;
197 /* Check, store information on the writer, do I/O. */
198 if (pp->state & TTY_CLOSED)
199 return EIO;
201 if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
202 return EIO;
204 if (size <= 0)
205 return EINVAL;
207 pp->wrcaller = endpt;
208 pp->wrid = id;
209 pp->wrgrant = grant;
210 pp->wrleft = size;
212 handle_events(tp);
214 if (pp->wrleft == 0) {
215 pp->wrcaller = NONE;
216 return EDONTREPLY; /* already done */
219 if (flags & CDEV_NONBLOCK) {
220 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
221 pp->wrleft = pp->wrcum = 0;
222 pp->wrcaller = NONE;
223 return r;
226 return EDONTREPLY; /* do suspend */
229 /*===========================================================================*
230 * pty_master_cancel *
231 *===========================================================================*/
232 static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
234 tty_t *tp;
235 pty_t *pp;
236 int r;
238 if ((tp = line2tty(minor)) == NULL)
239 return ENXIO;
240 pp = tp->tty_priv;
242 if (pp->rdcaller == endpt && pp->rdid == id) {
243 /* Cancel a read from a PTY. */
244 r = pp->rdcum > 0 ? pp->rdcum : EINTR;
245 pp->rdleft = pp->rdcum = 0;
246 pp->rdcaller = NONE;
247 return r;
250 if (pp->wrcaller == endpt && pp->wrid == id) {
251 /* Cancel a write to a PTY. */
252 r = pp->wrcum > 0 ? pp->wrcum : EINTR;
253 pp->wrleft = pp->wrcum = 0;
254 pp->wrcaller = NONE;
255 return r;
258 /* Request not found. */
259 return EDONTREPLY;
262 /*===========================================================================*
263 * select_try_pty *
264 *===========================================================================*/
265 static int select_try_pty(tty_t *tp, int ops)
267 pty_t *pp = tp->tty_priv;
268 int r = 0;
270 if (ops & CDEV_OP_WR) {
271 /* Write won't block on error. */
272 if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
273 else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
274 else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
277 if (ops & CDEV_OP_RD) {
278 /* Read won't block on error. */
279 if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
280 else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
281 else if (pp->ocount > 0) r |= CDEV_OP_RD; /* Actual data. */
284 return r;
287 /*===========================================================================*
288 * select_retry_pty *
289 *===========================================================================*/
290 void select_retry_pty(tty_t *tp)
292 pty_t *pp = tp->tty_priv;
293 devminor_t minor;
294 int r;
296 /* See if the pty side of a pty is ready to return a select. */
297 if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
298 minor = PTYPX_MINOR + (int) (pp - pty_table);
299 chardriver_reply_select(pp->select_proc, minor, r);
300 pp->select_ops &= ~r;
304 /*===========================================================================*
305 * pty_master_select *
306 *===========================================================================*/
307 static int pty_master_select(devminor_t minor, unsigned int ops,
308 endpoint_t endpt)
310 tty_t *tp;
311 pty_t *pp;
312 int ready_ops, watch;
314 if ((tp = line2tty(minor)) == NULL)
315 return ENXIO;
316 pp = tp->tty_priv;
318 watch = (ops & CDEV_NOTIFY);
319 ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
321 ready_ops = select_try_pty(tp, ops);
323 ops &= ~ready_ops;
324 if (ops && watch) {
325 pp->select_ops |= ops;
326 pp->select_proc = endpt;
329 return ready_ops;
332 /*===========================================================================*
333 * do_pty *
334 *===========================================================================*/
335 void do_pty(message *m_ptr, int ipc_status)
337 /* Process a request for a PTY master (/dev/ptypX) device. */
339 chardriver_process(&pty_master_tab, m_ptr, ipc_status);
342 /*===========================================================================*
343 * pty_slave_write *
344 *===========================================================================*/
345 static int pty_slave_write(tty_t *tp, int try)
347 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
348 * /dev/ttypX to the output buffer.
350 pty_t *pp = tp->tty_priv;
351 int count, ocount, s;
353 /* PTY closed down? */
354 if (pp->state & PTY_CLOSED) {
355 if (try) return 1;
356 if (tp->tty_outleft > 0) {
357 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
358 tp->tty_outleft = tp->tty_outcum = 0;
359 tp->tty_outcaller = NONE;
361 return 0;
364 /* While there is something to do. */
365 for (;;) {
366 ocount = buflen(pp->obuf) - pp->ocount;
367 if (try) return (ocount > 0);
368 count = bufend(pp->obuf) - pp->ohead;
369 if (count > ocount) count = ocount;
370 if (count > tp->tty_outleft) count = tp->tty_outleft;
371 if (count == 0 || tp->tty_inhibited)
372 break;
374 /* Copy from user space to the PTY output buffer. */
375 if (tp->tty_outcaller == KERNEL) {
376 /* We're trying to print on kernel's behalf */
377 memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
378 count);
379 } else {
380 if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
381 tp->tty_outcum, (vir_bytes) pp->ohead,
382 count)) != OK) {
383 break;
387 /* Perform output processing on the output buffer. */
388 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
389 if (count == 0) break;
391 /* Assume echoing messed up by output. */
392 tp->tty_reprint = TRUE;
394 /* Bookkeeping. */
395 pp->ocount += ocount;
396 if ((pp->ohead += ocount) >= bufend(pp->obuf))
397 pp->ohead -= buflen(pp->obuf);
398 pty_start(pp);
400 tp->tty_outcum += count;
401 if ((tp->tty_outleft -= count) == 0) {
402 /* Output is finished, reply to the writer. */
403 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
404 tp->tty_outcum);
405 tp->tty_outcum = 0;
406 tp->tty_outcaller = NONE;
409 pty_finish(pp);
410 return 1;
413 /*===========================================================================*
414 * pty_slave_echo *
415 *===========================================================================*/
416 static void pty_slave_echo(tty_t *tp, int c)
418 /* Echo one character. (Like pty_write, but only one character, optionally.) */
420 pty_t *pp = tp->tty_priv;
421 int count, ocount;
423 ocount = buflen(pp->obuf) - pp->ocount;
424 if (ocount == 0) return; /* output buffer full */
425 count = 1;
426 *pp->ohead = c; /* add one character */
428 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
429 if (count == 0) return;
431 pp->ocount += ocount;
432 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
433 pty_start(pp);
436 /*===========================================================================*
437 * pty_start *
438 *===========================================================================*/
439 static void pty_start(pty_t *pp)
441 /* Transfer bytes written to the output buffer to the PTY reader. */
442 int count;
444 /* While there are things to do. */
445 for (;;) {
446 int s;
447 count = bufend(pp->obuf) - pp->otail;
448 if (count > pp->ocount) count = pp->ocount;
449 if (count > pp->rdleft) count = pp->rdleft;
450 if (count == 0) break;
452 /* Copy from the output buffer to the readers address space. */
453 if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
454 (vir_bytes) pp->otail, count)) != OK) {
455 break;
458 /* Bookkeeping. */
459 pp->ocount -= count;
460 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
461 pp->rdcum += count;
462 pp->rdleft -= count;
466 /*===========================================================================*
467 * pty_finish *
468 *===========================================================================*/
469 static void pty_finish(pty_t *pp)
471 /* Finish the read request of a PTY reader if there is at least one byte
472 * transferred.
475 if (pp->rdcum > 0) {
476 chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
477 pp->rdleft = pp->rdcum = 0;
478 pp->rdcaller = NONE;
482 /*===========================================================================*
483 * pty_slave_read *
484 *===========================================================================*/
485 static int pty_slave_read(tty_t *tp, int try)
487 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
488 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
490 pty_t *pp = tp->tty_priv;
491 char c;
493 if (pp->state & PTY_CLOSED) {
494 if (try) return 1;
495 if (tp->tty_inleft > 0) {
496 chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
497 tp->tty_incum);
498 tp->tty_inleft = tp->tty_incum = 0;
499 tp->tty_incaller = NONE;
501 return 1;
504 if (try) {
505 if (pp->wrleft > 0)
506 return 1;
507 return 0;
510 while (pp->wrleft > 0) {
511 int s;
513 /* Transfer one character to 'c'. */
514 if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
515 (vir_bytes) &c, 1)) != OK) {
516 printf("pty: safecopy failed (error %d)\n", s);
517 break;
520 /* Input processing. */
521 if (in_process(tp, &c, 1) == 0) break;
523 /* PTY writer bookkeeping. */
524 pp->wrcum++;
525 if (--pp->wrleft == 0) {
526 chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
527 pp->wrcum = 0;
528 pp->wrcaller = NONE;
532 return 0;
535 /*===========================================================================*
536 * pty_slave_open *
537 *===========================================================================*/
538 static int pty_slave_open(tty_t *tp, int UNUSED(try))
540 /* The tty side has been opened. */
541 pty_t *pp = tp->tty_priv;
543 assert(tp->tty_minor >= TTYPX_MINOR && tp->tty_minor < TTYPX_MINOR + NR_PTYS);
545 /* TTY_ACTIVE may already be set, which would indicate that the slave is
546 * reopened after being fully closed while the master is still open. In that
547 * case TTY_CLOSED will also be set, so clear that one.
549 pp->state |= TTY_ACTIVE;
550 pp->state &= ~TTY_CLOSED;
552 return 0;
555 /*===========================================================================*
556 * pty_slave_close *
557 *===========================================================================*/
558 static int pty_slave_close(tty_t *tp, int UNUSED(try))
560 /* The tty side has closed, so shut down the pty side. */
561 pty_t *pp = tp->tty_priv;
563 if (!(pp->state & PTY_ACTIVE)) return 0;
565 if (pp->rdleft > 0) {
566 chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
567 pp->rdleft = pp->rdcum = 0;
568 pp->rdcaller = NONE;
571 if (pp->wrleft > 0) {
572 chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
573 pp->wrleft = pp->wrcum = 0;
574 pp->wrcaller = NONE;
577 if (pp->state & PTY_CLOSED) pp->state = 0;
578 else pp->state |= TTY_CLOSED;
580 return 0;
583 /*===========================================================================*
584 * pty_slave_icancel *
585 *===========================================================================*/
586 static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
588 /* Discard waiting input. */
589 pty_t *pp = tp->tty_priv;
591 if (pp->wrleft > 0) {
592 chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
593 pp->wrcum = pp->wrleft = 0;
594 pp->wrcaller = NONE;
597 return 0;
600 /*===========================================================================*
601 * pty_slave_ocancel *
602 *===========================================================================*/
603 static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
605 /* Drain the output buffer. */
606 pty_t *pp = tp->tty_priv;
608 pp->ocount = 0;
609 pp->otail = pp->ohead;
611 return 0;
614 /*===========================================================================*
615 * pty_init *
616 *===========================================================================*/
617 void pty_init(tty_t *tp)
619 pty_t *pp;
620 int line;
622 /* Associate PTY and TTY structures. */
623 line = tp - tty_table;
624 pp = tp->tty_priv = &pty_table[line];
625 pp->tty = tp;
626 pp->select_ops = 0;
627 pp->rdcaller = NONE;
628 pp->wrcaller = NONE;
630 /* Set up output queue. */
631 pp->ohead = pp->otail = pp->obuf;
633 /* Fill in TTY function hooks. */
634 tp->tty_devread = pty_slave_read;
635 tp->tty_devwrite = pty_slave_write;
636 tp->tty_echo = pty_slave_echo;
637 tp->tty_icancel = pty_slave_icancel;
638 tp->tty_ocancel = pty_slave_ocancel;
639 tp->tty_open = pty_slave_open;
640 tp->tty_close = pty_slave_close;
641 tp->tty_select_ops = 0;