1433d6423SLionel Sambuc /* pty.c - pseudo terminal driver Author: Kees J. Bot 2433d6423SLionel Sambuc * 30 Dec 1995 3433d6423SLionel Sambuc * PTYs can be seen as a bidirectional pipe with TTY 4433d6423SLionel Sambuc * input and output processing. For example a simple rlogin session: 5433d6423SLionel Sambuc * 6433d6423SLionel Sambuc * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell 7433d6423SLionel Sambuc * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen 8433d6423SLionel Sambuc * 9433d6423SLionel Sambuc * This file takes care of copying data between the tty/pty device pairs and 10433d6423SLionel Sambuc * the open/read/write/close calls on the pty devices. The TTY task takes 11433d6423SLionel Sambuc * care of the input and output processing (interrupt, backspace, raw I/O, 12433d6423SLionel Sambuc * etc.) using the pty_slave_read() and pty_slave_write() functions as the 13433d6423SLionel Sambuc * "keyboard" and "screen" functions of the ttypX devices. 14433d6423SLionel Sambuc * Be careful when reading this code, the terms "reading" and "writing" are 15433d6423SLionel Sambuc * used both for the tty (slave) and the pty (master) end of the pseudo tty. 16433d6423SLionel Sambuc * Writes to one end are to be read at the other end and vice-versa. 17*da21d850SDavid van Moolenbroek * 18*da21d850SDavid van Moolenbroek * In addition to the above, PTY service now also supports Unix98 pseudo- 19*da21d850SDavid van Moolenbroek * terminal pairs, thereby allowing non-root users to allocate pseudoterminals. 20*da21d850SDavid van Moolenbroek * It requires the presence for PTYFS for this, and supports only old-style 21*da21d850SDavid van Moolenbroek * ptys when PTYFS is not running. For Unix98 ptys, the general idea is that a 22*da21d850SDavid van Moolenbroek * userland program opens a pty master by opening /dev/ptmx through the use of 23*da21d850SDavid van Moolenbroek * posxix_openpt(3). A slave node is allocated on PTYFS when the program calls 24*da21d850SDavid van Moolenbroek * grantpt(3) on the master. The program can then obtain the path name for the 25*da21d850SDavid van Moolenbroek * slave end through ptsname(3), and open the slave end using this path. 26*da21d850SDavid van Moolenbroek * 27*da21d850SDavid van Moolenbroek * Implementation-wise, the Unix98 and non-Unix98 pseudoterminals share the 28*da21d850SDavid van Moolenbroek * same pool of data structures, but use different ranges of minor numbers. 29*da21d850SDavid van Moolenbroek * Access to the two types may not be mixed, and thus, some parts of the code 30*da21d850SDavid van Moolenbroek * have checks to make sure a traditional slave is not opened for a master 31*da21d850SDavid van Moolenbroek * allocated through /dev/ptmx, etcetera. 32433d6423SLionel Sambuc */ 33433d6423SLionel Sambuc 34433d6423SLionel Sambuc #include <minix/drivers.h> 35*da21d850SDavid van Moolenbroek #include <paths.h> 36433d6423SLionel Sambuc #include <termios.h> 37433d6423SLionel Sambuc #include <assert.h> 38433d6423SLionel Sambuc #include <sys/termios.h> 39433d6423SLionel Sambuc #include <signal.h> 40433d6423SLionel Sambuc #include "tty.h" 41*da21d850SDavid van Moolenbroek #include "ptyfs.h" 42*da21d850SDavid van Moolenbroek 43*da21d850SDavid van Moolenbroek /* Device node attributes used for Unix98 slave nodes. */ 44*da21d850SDavid van Moolenbroek #define UNIX98_MODE (S_IFCHR | 0620) /* crw--w---- */ 45*da21d850SDavid van Moolenbroek 46*da21d850SDavid van Moolenbroek #define UNIX98_MASTER(index) (UNIX98_MINOR + (index) * 2) 47*da21d850SDavid van Moolenbroek #define UNIX98_SLAVE(index) (UNIX98_MINOR + (index) * 2 + 1) 48433d6423SLionel Sambuc 49433d6423SLionel Sambuc /* PTY bookkeeping structure, one per pty/tty pair. */ 50433d6423SLionel Sambuc typedef struct pty { 51433d6423SLionel Sambuc tty_t *tty; /* associated TTY structure */ 52433d6423SLionel Sambuc char state; /* flags: busy, closed, ... */ 53433d6423SLionel Sambuc 54433d6423SLionel Sambuc /* Read call on master (/dev/ptypX). */ 55433d6423SLionel Sambuc endpoint_t rdcaller; /* process making the call, or NONE if none */ 56433d6423SLionel Sambuc cdev_id_t rdid; /* ID of suspended read request */ 57433d6423SLionel Sambuc cp_grant_id_t rdgrant; /* grant for reader's address space */ 58433d6423SLionel Sambuc size_t rdleft; /* # bytes yet to be read */ 59433d6423SLionel Sambuc size_t rdcum; /* # bytes written so far */ 60433d6423SLionel Sambuc 61433d6423SLionel Sambuc /* Write call to master (/dev/ptypX). */ 62433d6423SLionel Sambuc endpoint_t wrcaller; /* process making the call, or NONE if none*/ 63433d6423SLionel Sambuc cdev_id_t wrid; /* ID of suspended write request */ 64433d6423SLionel Sambuc cp_grant_id_t wrgrant; /* grant for writer's address space */ 65433d6423SLionel Sambuc size_t wrleft; /* # bytes yet to be written */ 66433d6423SLionel Sambuc size_t wrcum; /* # bytes written so far */ 67433d6423SLionel Sambuc 68433d6423SLionel Sambuc /* Output buffer. */ 69433d6423SLionel Sambuc int ocount; /* # characters in the buffer */ 70433d6423SLionel Sambuc char *ohead, *otail; /* head and tail of the circular buffer */ 71391516ddSDavid van Moolenbroek char obuf[TTY_OUT_BYTES]; 72391516ddSDavid van Moolenbroek /* buffer for bytes going to the pty reader */ 73433d6423SLionel Sambuc 74433d6423SLionel Sambuc /* select() data. */ 75433d6423SLionel Sambuc unsigned int select_ops; /* Which operations do we want to know about? */ 76433d6423SLionel Sambuc endpoint_t select_proc; /* Who wants to know about it? */ 77*da21d850SDavid van Moolenbroek devminor_t select_minor; /* Which minor was being selected on? */ 78433d6423SLionel Sambuc } pty_t; 79433d6423SLionel Sambuc 80433d6423SLionel Sambuc #define TTY_ACTIVE 0x01 /* tty is open/active */ 81433d6423SLionel Sambuc #define PTY_ACTIVE 0x02 /* pty is open/active */ 82433d6423SLionel Sambuc #define TTY_CLOSED 0x04 /* tty side has closed down */ 83433d6423SLionel Sambuc #define PTY_CLOSED 0x08 /* pty side has closed down */ 84*da21d850SDavid van Moolenbroek #define PTY_UNIX98 0x10 /* pty pair is Unix98 */ 85433d6423SLionel Sambuc 86433d6423SLionel Sambuc static pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */ 87433d6423SLionel Sambuc 88433d6423SLionel Sambuc static void pty_start(pty_t *pp); 89433d6423SLionel Sambuc static void pty_finish(pty_t *pp); 90433d6423SLionel Sambuc 91433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int access, 92433d6423SLionel Sambuc endpoint_t user_endpt); 93433d6423SLionel Sambuc static int pty_master_close(devminor_t minor); 94433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t position, 95433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 96433d6423SLionel Sambuc cdev_id_t id); 97433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t position, 98433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 99433d6423SLionel Sambuc cdev_id_t id); 100*da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request, 101*da21d850SDavid van Moolenbroek endpoint_t endpt, cp_grant_id_t grant, int flags, 102*da21d850SDavid van Moolenbroek endpoint_t user_endpt, cdev_id_t id); 103433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); 104433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops, 105433d6423SLionel Sambuc endpoint_t endpt); 106433d6423SLionel Sambuc 107433d6423SLionel Sambuc static struct chardriver pty_master_tab = { 108433d6423SLionel Sambuc .cdr_open = pty_master_open, 109433d6423SLionel Sambuc .cdr_close = pty_master_close, 110433d6423SLionel Sambuc .cdr_read = pty_master_read, 111433d6423SLionel Sambuc .cdr_write = pty_master_write, 112*da21d850SDavid van Moolenbroek .cdr_ioctl = pty_master_ioctl, 113433d6423SLionel Sambuc .cdr_cancel = pty_master_cancel, 114433d6423SLionel Sambuc .cdr_select = pty_master_select 115433d6423SLionel Sambuc }; 116433d6423SLionel Sambuc 117433d6423SLionel Sambuc /*===========================================================================* 118*da21d850SDavid van Moolenbroek * get_free_pty * 119*da21d850SDavid van Moolenbroek *===========================================================================*/ 120*da21d850SDavid van Moolenbroek static tty_t *get_free_pty(void) 121*da21d850SDavid van Moolenbroek { 122*da21d850SDavid van Moolenbroek /* Return a pointer to a free tty structure, or NULL if no tty is free. */ 123*da21d850SDavid van Moolenbroek tty_t *tp; 124*da21d850SDavid van Moolenbroek pty_t *pp; 125*da21d850SDavid van Moolenbroek 126*da21d850SDavid van Moolenbroek for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) { 127*da21d850SDavid van Moolenbroek pp = tp->tty_priv; 128*da21d850SDavid van Moolenbroek 129*da21d850SDavid van Moolenbroek if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE))) 130*da21d850SDavid van Moolenbroek return tp; 131*da21d850SDavid van Moolenbroek } 132*da21d850SDavid van Moolenbroek 133*da21d850SDavid van Moolenbroek return NULL; 134*da21d850SDavid van Moolenbroek } 135*da21d850SDavid van Moolenbroek 136*da21d850SDavid van Moolenbroek /*===========================================================================* 137433d6423SLionel Sambuc * pty_master_open * 138433d6423SLionel Sambuc *===========================================================================*/ 139433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int UNUSED(access), 140433d6423SLionel Sambuc endpoint_t UNUSED(user_endpt)) 141433d6423SLionel Sambuc { 142433d6423SLionel Sambuc tty_t *tp; 143433d6423SLionel Sambuc pty_t *pp; 144*da21d850SDavid van Moolenbroek int r; 145433d6423SLionel Sambuc 146*da21d850SDavid van Moolenbroek if (minor == PTMX_MINOR) { 147*da21d850SDavid van Moolenbroek /* /dev/ptmx acts as a cloning device. We return a free PTY master and 148*da21d850SDavid van Moolenbroek * mark it as a UNIX98 type. 149*da21d850SDavid van Moolenbroek */ 150*da21d850SDavid van Moolenbroek if ((tp = get_free_pty()) == NULL) 151*da21d850SDavid van Moolenbroek return EAGAIN; /* POSIX says this is the right error code */ 152*da21d850SDavid van Moolenbroek 153*da21d850SDavid van Moolenbroek /* The following call has two purposes. First, we check right here 154*da21d850SDavid van Moolenbroek * whether PTYFS is running at all; if not, the PTMX device cannot be 155*da21d850SDavid van Moolenbroek * opened at all and userland can fall back to other allocation 156*da21d850SDavid van Moolenbroek * methods right away. Second, in the exceptional case that the PTY 157*da21d850SDavid van Moolenbroek * service is restarted while PTYFS keeps running, PTYFS may expose 158*da21d850SDavid van Moolenbroek * stale slave nodes, which are a security hole if not removed as soon 159*da21d850SDavid van Moolenbroek * as a new PTY pair is allocated. 160*da21d850SDavid van Moolenbroek */ 161*da21d850SDavid van Moolenbroek if (ptyfs_clear(tp->tty_index) != OK) 162*da21d850SDavid van Moolenbroek return EAGAIN; 163*da21d850SDavid van Moolenbroek 164*da21d850SDavid van Moolenbroek pp = tp->tty_priv; 165*da21d850SDavid van Moolenbroek pp->state |= PTY_UNIX98; 166*da21d850SDavid van Moolenbroek 167*da21d850SDavid van Moolenbroek minor = UNIX98_MASTER(tp->tty_index); 168*da21d850SDavid van Moolenbroek 169*da21d850SDavid van Moolenbroek r = CDEV_CLONED | minor; 170*da21d850SDavid van Moolenbroek } else { 171*da21d850SDavid van Moolenbroek /* There is no way to open Unix98 masters directly, except by messing 172*da21d850SDavid van Moolenbroek * with mknod. We disallow such tricks altogether, and thus, the rest 173*da21d850SDavid van Moolenbroek * of the code deals with opening a non-Unix98 master only. 174*da21d850SDavid van Moolenbroek */ 175*da21d850SDavid van Moolenbroek if (minor < PTYPX_MINOR || minor >= PTYPX_MINOR + NR_PTYS) 176*da21d850SDavid van Moolenbroek return EIO; 177433d6423SLionel Sambuc 178433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 179433d6423SLionel Sambuc return ENXIO; 180433d6423SLionel Sambuc pp = tp->tty_priv; 181433d6423SLionel Sambuc 182*da21d850SDavid van Moolenbroek /* For non-Unix98 PTYs, we allow the slave to be opened before the 183*da21d850SDavid van Moolenbroek * master, but the master may be opened only once. This is how userland 184*da21d850SDavid van Moolenbroek * is able to find a free non-Unix98 PTY pair. 185*da21d850SDavid van Moolenbroek */ 186433d6423SLionel Sambuc if (pp->state & PTY_ACTIVE) 187433d6423SLionel Sambuc return EIO; 188*da21d850SDavid van Moolenbroek assert(!(pp->state & PTY_UNIX98)); 189*da21d850SDavid van Moolenbroek 190*da21d850SDavid van Moolenbroek r = OK; 191*da21d850SDavid van Moolenbroek } 192433d6423SLionel Sambuc 193433d6423SLionel Sambuc pp->state |= PTY_ACTIVE; 194*da21d850SDavid van Moolenbroek 195433d6423SLionel Sambuc pp->rdcum = 0; 196433d6423SLionel Sambuc pp->wrcum = 0; 197433d6423SLionel Sambuc 198*da21d850SDavid van Moolenbroek return r; 199*da21d850SDavid van Moolenbroek } 200*da21d850SDavid van Moolenbroek 201*da21d850SDavid van Moolenbroek /*===========================================================================* 202*da21d850SDavid van Moolenbroek * pty_reset * 203*da21d850SDavid van Moolenbroek *===========================================================================*/ 204*da21d850SDavid van Moolenbroek static void pty_reset(tty_t *tp) 205*da21d850SDavid van Moolenbroek { 206*da21d850SDavid van Moolenbroek /* Both sides of a PTY pair have been closed. Clean up its state. */ 207*da21d850SDavid van Moolenbroek pty_t *pp; 208*da21d850SDavid van Moolenbroek 209*da21d850SDavid van Moolenbroek pp = tp->tty_priv; 210*da21d850SDavid van Moolenbroek 211*da21d850SDavid van Moolenbroek /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been 212*da21d850SDavid van Moolenbroek * allocated, but we don't care. Ignore failures altogether. 213*da21d850SDavid van Moolenbroek */ 214*da21d850SDavid van Moolenbroek if (pp->state & PTY_UNIX98) 215*da21d850SDavid van Moolenbroek (void)ptyfs_clear(tp->tty_index); 216*da21d850SDavid van Moolenbroek 217*da21d850SDavid van Moolenbroek pp->state = 0; 218433d6423SLionel Sambuc } 219433d6423SLionel Sambuc 220433d6423SLionel Sambuc /*===========================================================================* 221433d6423SLionel Sambuc * pty_master_close * 222433d6423SLionel Sambuc *===========================================================================*/ 223433d6423SLionel Sambuc static int pty_master_close(devminor_t minor) 224433d6423SLionel Sambuc { 225433d6423SLionel Sambuc tty_t *tp; 226433d6423SLionel Sambuc pty_t *pp; 227433d6423SLionel Sambuc 228433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 229433d6423SLionel Sambuc return ENXIO; 230433d6423SLionel Sambuc pp = tp->tty_priv; 231433d6423SLionel Sambuc 232433d6423SLionel Sambuc if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) { 233*da21d850SDavid van Moolenbroek pty_reset(tp); 234433d6423SLionel Sambuc } else { 235433d6423SLionel Sambuc pp->state |= PTY_CLOSED; 236673c4e01SDavid van Moolenbroek tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */ 237433d6423SLionel Sambuc sigchar(tp, SIGHUP, 1); 238433d6423SLionel Sambuc } 239433d6423SLionel Sambuc 240433d6423SLionel Sambuc return OK; 241433d6423SLionel Sambuc } 242433d6423SLionel Sambuc 243433d6423SLionel Sambuc /*===========================================================================* 244433d6423SLionel Sambuc * pty_master_read * 245433d6423SLionel Sambuc *===========================================================================*/ 246433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position), 247433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 248433d6423SLionel Sambuc cdev_id_t id) 249433d6423SLionel Sambuc { 250433d6423SLionel Sambuc tty_t *tp; 251433d6423SLionel Sambuc pty_t *pp; 252433d6423SLionel Sambuc ssize_t r; 253433d6423SLionel Sambuc 254433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 255433d6423SLionel Sambuc return ENXIO; 256433d6423SLionel Sambuc pp = tp->tty_priv; 257433d6423SLionel Sambuc 258433d6423SLionel Sambuc /* Check, store information on the reader, do I/O. */ 259433d6423SLionel Sambuc if (pp->state & TTY_CLOSED) 260433d6423SLionel Sambuc return 0; /* EOF */ 261433d6423SLionel Sambuc 262433d6423SLionel Sambuc if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0) 263433d6423SLionel Sambuc return EIO; 264433d6423SLionel Sambuc 265433d6423SLionel Sambuc if (size <= 0) 266433d6423SLionel Sambuc return EINVAL; 267433d6423SLionel Sambuc 268433d6423SLionel Sambuc pp->rdcaller = endpt; 269433d6423SLionel Sambuc pp->rdid = id; 270433d6423SLionel Sambuc pp->rdgrant = grant; 271433d6423SLionel Sambuc pp->rdleft = size; 272433d6423SLionel Sambuc pty_start(pp); 273433d6423SLionel Sambuc 274433d6423SLionel Sambuc handle_events(tp); 275433d6423SLionel Sambuc 276433d6423SLionel Sambuc if (pp->rdleft == 0) { 277433d6423SLionel Sambuc pp->rdcaller = NONE; 278433d6423SLionel Sambuc return EDONTREPLY; /* already done */ 279433d6423SLionel Sambuc } 280433d6423SLionel Sambuc 281433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK) { 282433d6423SLionel Sambuc r = pp->rdcum > 0 ? pp->rdcum : EAGAIN; 283433d6423SLionel Sambuc pp->rdleft = pp->rdcum = 0; 284433d6423SLionel Sambuc pp->rdcaller = NONE; 285433d6423SLionel Sambuc return r; 286433d6423SLionel Sambuc } 287433d6423SLionel Sambuc 288433d6423SLionel Sambuc return EDONTREPLY; /* do suspend */ 289433d6423SLionel Sambuc } 290433d6423SLionel Sambuc 291433d6423SLionel Sambuc /*===========================================================================* 292433d6423SLionel Sambuc * pty_master_write * 293433d6423SLionel Sambuc *===========================================================================*/ 294433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position), 295433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 296433d6423SLionel Sambuc cdev_id_t id) 297433d6423SLionel Sambuc { 298433d6423SLionel Sambuc tty_t *tp; 299433d6423SLionel Sambuc pty_t *pp; 300433d6423SLionel Sambuc ssize_t r; 301433d6423SLionel Sambuc 302433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 303433d6423SLionel Sambuc return ENXIO; 304433d6423SLionel Sambuc pp = tp->tty_priv; 305433d6423SLionel Sambuc 306433d6423SLionel Sambuc /* Check, store information on the writer, do I/O. */ 307433d6423SLionel Sambuc if (pp->state & TTY_CLOSED) 308433d6423SLionel Sambuc return EIO; 309433d6423SLionel Sambuc 310433d6423SLionel Sambuc if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0) 311433d6423SLionel Sambuc return EIO; 312433d6423SLionel Sambuc 313433d6423SLionel Sambuc if (size <= 0) 314433d6423SLionel Sambuc return EINVAL; 315433d6423SLionel Sambuc 316433d6423SLionel Sambuc pp->wrcaller = endpt; 317433d6423SLionel Sambuc pp->wrid = id; 318433d6423SLionel Sambuc pp->wrgrant = grant; 319433d6423SLionel Sambuc pp->wrleft = size; 320433d6423SLionel Sambuc 321433d6423SLionel Sambuc handle_events(tp); 322433d6423SLionel Sambuc 323433d6423SLionel Sambuc if (pp->wrleft == 0) { 324433d6423SLionel Sambuc pp->wrcaller = NONE; 325433d6423SLionel Sambuc return EDONTREPLY; /* already done */ 326433d6423SLionel Sambuc } 327433d6423SLionel Sambuc 328433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK) { 329433d6423SLionel Sambuc r = pp->wrcum > 0 ? pp->wrcum : EAGAIN; 330433d6423SLionel Sambuc pp->wrleft = pp->wrcum = 0; 331433d6423SLionel Sambuc pp->wrcaller = NONE; 332433d6423SLionel Sambuc return r; 333433d6423SLionel Sambuc } 334433d6423SLionel Sambuc 335433d6423SLionel Sambuc return EDONTREPLY; /* do suspend */ 336433d6423SLionel Sambuc } 337433d6423SLionel Sambuc 338433d6423SLionel Sambuc /*===========================================================================* 339*da21d850SDavid van Moolenbroek * pty_master_ioctl * 340*da21d850SDavid van Moolenbroek *===========================================================================*/ 341*da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request, 342*da21d850SDavid van Moolenbroek endpoint_t endpt, cp_grant_id_t grant, int flags, 343*da21d850SDavid van Moolenbroek endpoint_t user_endpt, cdev_id_t id) 344*da21d850SDavid van Moolenbroek { 345*da21d850SDavid van Moolenbroek tty_t *tp; 346*da21d850SDavid van Moolenbroek pty_t *pp; 347*da21d850SDavid van Moolenbroek uid_t uid; 348*da21d850SDavid van Moolenbroek struct ptmget pm; 349*da21d850SDavid van Moolenbroek size_t len; 350*da21d850SDavid van Moolenbroek 351*da21d850SDavid van Moolenbroek if ((tp = line2tty(minor)) == NULL) 352*da21d850SDavid van Moolenbroek return ENXIO; 353*da21d850SDavid van Moolenbroek pp = tp->tty_priv; 354*da21d850SDavid van Moolenbroek 355*da21d850SDavid van Moolenbroek /* Some IOCTLs are for the master side only. */ 356*da21d850SDavid van Moolenbroek switch (request) { 357*da21d850SDavid van Moolenbroek case TIOCGRANTPT: /* grantpt(3) */ 358*da21d850SDavid van Moolenbroek if (!(pp->state & PTY_UNIX98)) 359*da21d850SDavid van Moolenbroek break; 360*da21d850SDavid van Moolenbroek 361*da21d850SDavid van Moolenbroek if ((int)(uid = getnuid(user_endpt)) == -1) 362*da21d850SDavid van Moolenbroek return EACCES; 363*da21d850SDavid van Moolenbroek if (tty_gid == -1) { 364*da21d850SDavid van Moolenbroek printf("PTY: no tty group ID given at startup\n"); 365*da21d850SDavid van Moolenbroek return EACCES; 366*da21d850SDavid van Moolenbroek } 367*da21d850SDavid van Moolenbroek 368*da21d850SDavid van Moolenbroek /* Create or update the slave node. */ 369*da21d850SDavid van Moolenbroek if (ptyfs_set(tp->tty_index, UNIX98_MODE, uid, tty_gid, 370*da21d850SDavid van Moolenbroek makedev(PTY_MAJOR, UNIX98_SLAVE(tp->tty_index))) != OK) 371*da21d850SDavid van Moolenbroek return EACCES; 372*da21d850SDavid van Moolenbroek 373*da21d850SDavid van Moolenbroek return OK; 374*da21d850SDavid van Moolenbroek 375*da21d850SDavid van Moolenbroek case TIOCPTSNAME: /* ptsname(3) */ 376*da21d850SDavid van Moolenbroek if (!(pp->state & PTY_UNIX98)) 377*da21d850SDavid van Moolenbroek break; 378*da21d850SDavid van Moolenbroek 379*da21d850SDavid van Moolenbroek /* Since pm.sn is 16 bytes, we can have up to a million slaves. */ 380*da21d850SDavid van Moolenbroek memset(&pm, 0, sizeof(pm)); 381*da21d850SDavid van Moolenbroek 382*da21d850SDavid van Moolenbroek strlcpy(pm.sn, _PATH_DEV_PTS, sizeof(pm.sn)); 383*da21d850SDavid van Moolenbroek len = strlen(pm.sn); 384*da21d850SDavid van Moolenbroek 385*da21d850SDavid van Moolenbroek if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK) 386*da21d850SDavid van Moolenbroek return EINVAL; 387*da21d850SDavid van Moolenbroek 388*da21d850SDavid van Moolenbroek return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm)); 389*da21d850SDavid van Moolenbroek } 390*da21d850SDavid van Moolenbroek 391*da21d850SDavid van Moolenbroek /* TODO: historically, all IOCTLs on the master are processed as if issued on 392*da21d850SDavid van Moolenbroek * the slave end. Make sure that this can not cause problems, in particular 393*da21d850SDavid van Moolenbroek * with blocking IOCTLs. 394*da21d850SDavid van Moolenbroek */ 395*da21d850SDavid van Moolenbroek return tty_ioctl(minor, request, endpt, grant, flags, user_endpt, id); 396*da21d850SDavid van Moolenbroek } 397*da21d850SDavid van Moolenbroek 398*da21d850SDavid van Moolenbroek /*===========================================================================* 399433d6423SLionel Sambuc * pty_master_cancel * 400433d6423SLionel Sambuc *===========================================================================*/ 401433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id) 402433d6423SLionel Sambuc { 403433d6423SLionel Sambuc tty_t *tp; 404433d6423SLionel Sambuc pty_t *pp; 405433d6423SLionel Sambuc int r; 406433d6423SLionel Sambuc 407433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 408433d6423SLionel Sambuc return ENXIO; 409433d6423SLionel Sambuc pp = tp->tty_priv; 410433d6423SLionel Sambuc 411433d6423SLionel Sambuc if (pp->rdcaller == endpt && pp->rdid == id) { 412433d6423SLionel Sambuc /* Cancel a read from a PTY. */ 413433d6423SLionel Sambuc r = pp->rdcum > 0 ? pp->rdcum : EINTR; 414433d6423SLionel Sambuc pp->rdleft = pp->rdcum = 0; 415433d6423SLionel Sambuc pp->rdcaller = NONE; 416433d6423SLionel Sambuc return r; 417433d6423SLionel Sambuc } 418433d6423SLionel Sambuc 419433d6423SLionel Sambuc if (pp->wrcaller == endpt && pp->wrid == id) { 420433d6423SLionel Sambuc /* Cancel a write to a PTY. */ 421433d6423SLionel Sambuc r = pp->wrcum > 0 ? pp->wrcum : EINTR; 422433d6423SLionel Sambuc pp->wrleft = pp->wrcum = 0; 423433d6423SLionel Sambuc pp->wrcaller = NONE; 424433d6423SLionel Sambuc return r; 425433d6423SLionel Sambuc } 426433d6423SLionel Sambuc 427433d6423SLionel Sambuc /* Request not found. */ 428433d6423SLionel Sambuc return EDONTREPLY; 429433d6423SLionel Sambuc } 430433d6423SLionel Sambuc 431433d6423SLionel Sambuc /*===========================================================================* 432433d6423SLionel Sambuc * select_try_pty * 433433d6423SLionel Sambuc *===========================================================================*/ 434433d6423SLionel Sambuc static int select_try_pty(tty_t *tp, int ops) 435433d6423SLionel Sambuc { 436433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 437433d6423SLionel Sambuc int r = 0; 438433d6423SLionel Sambuc 439433d6423SLionel Sambuc if (ops & CDEV_OP_WR) { 440433d6423SLionel Sambuc /* Write won't block on error. */ 441433d6423SLionel Sambuc if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR; 442433d6423SLionel Sambuc else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR; 443433d6423SLionel Sambuc else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR; 444433d6423SLionel Sambuc } 445433d6423SLionel Sambuc 446433d6423SLionel Sambuc if (ops & CDEV_OP_RD) { 447433d6423SLionel Sambuc /* Read won't block on error. */ 448433d6423SLionel Sambuc if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD; 449433d6423SLionel Sambuc else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD; 450433d6423SLionel Sambuc else if (pp->ocount > 0) r |= CDEV_OP_RD; /* Actual data. */ 451433d6423SLionel Sambuc } 452433d6423SLionel Sambuc 453433d6423SLionel Sambuc return r; 454433d6423SLionel Sambuc } 455433d6423SLionel Sambuc 456433d6423SLionel Sambuc /*===========================================================================* 457433d6423SLionel Sambuc * select_retry_pty * 458433d6423SLionel Sambuc *===========================================================================*/ 459433d6423SLionel Sambuc void select_retry_pty(tty_t *tp) 460433d6423SLionel Sambuc { 461433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 462433d6423SLionel Sambuc int r; 463433d6423SLionel Sambuc 464433d6423SLionel Sambuc /* See if the pty side of a pty is ready to return a select. */ 465433d6423SLionel Sambuc if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) { 466*da21d850SDavid van Moolenbroek chardriver_reply_select(pp->select_proc, pp->select_minor, r); 467433d6423SLionel Sambuc pp->select_ops &= ~r; 468433d6423SLionel Sambuc } 469433d6423SLionel Sambuc } 470433d6423SLionel Sambuc 471433d6423SLionel Sambuc /*===========================================================================* 472433d6423SLionel Sambuc * pty_master_select * 473433d6423SLionel Sambuc *===========================================================================*/ 474433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops, 475433d6423SLionel Sambuc endpoint_t endpt) 476433d6423SLionel Sambuc { 477433d6423SLionel Sambuc tty_t *tp; 478433d6423SLionel Sambuc pty_t *pp; 479433d6423SLionel Sambuc int ready_ops, watch; 480433d6423SLionel Sambuc 481433d6423SLionel Sambuc if ((tp = line2tty(minor)) == NULL) 482433d6423SLionel Sambuc return ENXIO; 483433d6423SLionel Sambuc pp = tp->tty_priv; 484433d6423SLionel Sambuc 485433d6423SLionel Sambuc watch = (ops & CDEV_NOTIFY); 486433d6423SLionel Sambuc ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR); 487433d6423SLionel Sambuc 488433d6423SLionel Sambuc ready_ops = select_try_pty(tp, ops); 489433d6423SLionel Sambuc 490433d6423SLionel Sambuc ops &= ~ready_ops; 491433d6423SLionel Sambuc if (ops && watch) { 492433d6423SLionel Sambuc pp->select_ops |= ops; 493433d6423SLionel Sambuc pp->select_proc = endpt; 494*da21d850SDavid van Moolenbroek pp->select_minor = minor; 495433d6423SLionel Sambuc } 496433d6423SLionel Sambuc 497433d6423SLionel Sambuc return ready_ops; 498433d6423SLionel Sambuc } 499433d6423SLionel Sambuc 500433d6423SLionel Sambuc /*===========================================================================* 501433d6423SLionel Sambuc * do_pty * 502433d6423SLionel Sambuc *===========================================================================*/ 503433d6423SLionel Sambuc void do_pty(message *m_ptr, int ipc_status) 504433d6423SLionel Sambuc { 505433d6423SLionel Sambuc /* Process a request for a PTY master (/dev/ptypX) device. */ 506433d6423SLionel Sambuc 507433d6423SLionel Sambuc chardriver_process(&pty_master_tab, m_ptr, ipc_status); 508433d6423SLionel Sambuc } 509433d6423SLionel Sambuc 510433d6423SLionel Sambuc /*===========================================================================* 511433d6423SLionel Sambuc * pty_slave_write * 512433d6423SLionel Sambuc *===========================================================================*/ 513433d6423SLionel Sambuc static int pty_slave_write(tty_t *tp, int try) 514433d6423SLionel Sambuc { 515433d6423SLionel Sambuc /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on 516433d6423SLionel Sambuc * /dev/ttypX to the output buffer. 517433d6423SLionel Sambuc */ 518433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 519433d6423SLionel Sambuc int count, ocount, s; 520433d6423SLionel Sambuc 521433d6423SLionel Sambuc /* PTY closed down? */ 522433d6423SLionel Sambuc if (pp->state & PTY_CLOSED) { 523433d6423SLionel Sambuc if (try) return 1; 524433d6423SLionel Sambuc if (tp->tty_outleft > 0) { 525433d6423SLionel Sambuc chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO); 526433d6423SLionel Sambuc tp->tty_outleft = tp->tty_outcum = 0; 527433d6423SLionel Sambuc tp->tty_outcaller = NONE; 528433d6423SLionel Sambuc } 529433d6423SLionel Sambuc return 0; 530433d6423SLionel Sambuc } 531433d6423SLionel Sambuc 532433d6423SLionel Sambuc /* While there is something to do. */ 533433d6423SLionel Sambuc for (;;) { 534433d6423SLionel Sambuc ocount = buflen(pp->obuf) - pp->ocount; 535433d6423SLionel Sambuc if (try) return (ocount > 0); 536433d6423SLionel Sambuc count = bufend(pp->obuf) - pp->ohead; 537433d6423SLionel Sambuc if (count > ocount) count = ocount; 538433d6423SLionel Sambuc if (count > tp->tty_outleft) count = tp->tty_outleft; 539433d6423SLionel Sambuc if (count == 0 || tp->tty_inhibited) 540433d6423SLionel Sambuc break; 541433d6423SLionel Sambuc 542433d6423SLionel Sambuc /* Copy from user space to the PTY output buffer. */ 543433d6423SLionel Sambuc if (tp->tty_outcaller == KERNEL) { 544433d6423SLionel Sambuc /* We're trying to print on kernel's behalf */ 545433d6423SLionel Sambuc memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum, 546433d6423SLionel Sambuc count); 547433d6423SLionel Sambuc } else { 548433d6423SLionel Sambuc if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant, 549433d6423SLionel Sambuc tp->tty_outcum, (vir_bytes) pp->ohead, 550433d6423SLionel Sambuc count)) != OK) { 551433d6423SLionel Sambuc break; 552433d6423SLionel Sambuc } 553433d6423SLionel Sambuc } 554433d6423SLionel Sambuc 555433d6423SLionel Sambuc /* Perform output processing on the output buffer. */ 556433d6423SLionel Sambuc out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount); 557433d6423SLionel Sambuc if (count == 0) break; 558433d6423SLionel Sambuc 559433d6423SLionel Sambuc /* Assume echoing messed up by output. */ 560433d6423SLionel Sambuc tp->tty_reprint = TRUE; 561433d6423SLionel Sambuc 562433d6423SLionel Sambuc /* Bookkeeping. */ 563433d6423SLionel Sambuc pp->ocount += ocount; 564433d6423SLionel Sambuc if ((pp->ohead += ocount) >= bufend(pp->obuf)) 565433d6423SLionel Sambuc pp->ohead -= buflen(pp->obuf); 566433d6423SLionel Sambuc pty_start(pp); 567433d6423SLionel Sambuc 568433d6423SLionel Sambuc tp->tty_outcum += count; 569433d6423SLionel Sambuc if ((tp->tty_outleft -= count) == 0) { 570433d6423SLionel Sambuc /* Output is finished, reply to the writer. */ 571433d6423SLionel Sambuc chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, 572433d6423SLionel Sambuc tp->tty_outcum); 573433d6423SLionel Sambuc tp->tty_outcum = 0; 574433d6423SLionel Sambuc tp->tty_outcaller = NONE; 575433d6423SLionel Sambuc } 576433d6423SLionel Sambuc } 577433d6423SLionel Sambuc pty_finish(pp); 578433d6423SLionel Sambuc return 1; 579433d6423SLionel Sambuc } 580433d6423SLionel Sambuc 581433d6423SLionel Sambuc /*===========================================================================* 582433d6423SLionel Sambuc * pty_slave_echo * 583433d6423SLionel Sambuc *===========================================================================*/ 584433d6423SLionel Sambuc static void pty_slave_echo(tty_t *tp, int c) 585433d6423SLionel Sambuc { 586433d6423SLionel Sambuc /* Echo one character. (Like pty_write, but only one character, optionally.) */ 587433d6423SLionel Sambuc 588433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 589433d6423SLionel Sambuc int count, ocount; 590433d6423SLionel Sambuc 591433d6423SLionel Sambuc ocount = buflen(pp->obuf) - pp->ocount; 592433d6423SLionel Sambuc if (ocount == 0) return; /* output buffer full */ 593433d6423SLionel Sambuc count = 1; 594433d6423SLionel Sambuc *pp->ohead = c; /* add one character */ 595433d6423SLionel Sambuc 596433d6423SLionel Sambuc out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount); 597433d6423SLionel Sambuc if (count == 0) return; 598433d6423SLionel Sambuc 599433d6423SLionel Sambuc pp->ocount += ocount; 600433d6423SLionel Sambuc if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf); 601433d6423SLionel Sambuc pty_start(pp); 602433d6423SLionel Sambuc } 603433d6423SLionel Sambuc 604433d6423SLionel Sambuc /*===========================================================================* 605433d6423SLionel Sambuc * pty_start * 606433d6423SLionel Sambuc *===========================================================================*/ 607433d6423SLionel Sambuc static void pty_start(pty_t *pp) 608433d6423SLionel Sambuc { 609433d6423SLionel Sambuc /* Transfer bytes written to the output buffer to the PTY reader. */ 610433d6423SLionel Sambuc int count; 611433d6423SLionel Sambuc 612433d6423SLionel Sambuc /* While there are things to do. */ 613433d6423SLionel Sambuc for (;;) { 614433d6423SLionel Sambuc int s; 615433d6423SLionel Sambuc count = bufend(pp->obuf) - pp->otail; 616433d6423SLionel Sambuc if (count > pp->ocount) count = pp->ocount; 617433d6423SLionel Sambuc if (count > pp->rdleft) count = pp->rdleft; 618433d6423SLionel Sambuc if (count == 0) break; 619433d6423SLionel Sambuc 620433d6423SLionel Sambuc /* Copy from the output buffer to the readers address space. */ 621433d6423SLionel Sambuc if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum, 622433d6423SLionel Sambuc (vir_bytes) pp->otail, count)) != OK) { 623433d6423SLionel Sambuc break; 624433d6423SLionel Sambuc } 625433d6423SLionel Sambuc 626433d6423SLionel Sambuc /* Bookkeeping. */ 627433d6423SLionel Sambuc pp->ocount -= count; 628433d6423SLionel Sambuc if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf; 629433d6423SLionel Sambuc pp->rdcum += count; 630433d6423SLionel Sambuc pp->rdleft -= count; 631433d6423SLionel Sambuc } 632433d6423SLionel Sambuc } 633433d6423SLionel Sambuc 634433d6423SLionel Sambuc /*===========================================================================* 635433d6423SLionel Sambuc * pty_finish * 636433d6423SLionel Sambuc *===========================================================================*/ 637433d6423SLionel Sambuc static void pty_finish(pty_t *pp) 638433d6423SLionel Sambuc { 639433d6423SLionel Sambuc /* Finish the read request of a PTY reader if there is at least one byte 640433d6423SLionel Sambuc * transferred. 641433d6423SLionel Sambuc */ 642433d6423SLionel Sambuc 643433d6423SLionel Sambuc if (pp->rdcum > 0) { 644433d6423SLionel Sambuc chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum); 645433d6423SLionel Sambuc pp->rdleft = pp->rdcum = 0; 646433d6423SLionel Sambuc pp->rdcaller = NONE; 647433d6423SLionel Sambuc } 648433d6423SLionel Sambuc } 649433d6423SLionel Sambuc 650433d6423SLionel Sambuc /*===========================================================================* 651433d6423SLionel Sambuc * pty_slave_read * 652433d6423SLionel Sambuc *===========================================================================*/ 653433d6423SLionel Sambuc static int pty_slave_read(tty_t *tp, int try) 654433d6423SLionel Sambuc { 655433d6423SLionel Sambuc /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at 656433d6423SLionel Sambuc * a time, 99% of the writes will be for one byte, so no sense in being smart.) 657433d6423SLionel Sambuc */ 658433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 659433d6423SLionel Sambuc char c; 660433d6423SLionel Sambuc 661433d6423SLionel Sambuc if (pp->state & PTY_CLOSED) { 662433d6423SLionel Sambuc if (try) return 1; 663433d6423SLionel Sambuc if (tp->tty_inleft > 0) { 664433d6423SLionel Sambuc chardriver_reply_task(tp->tty_incaller, tp->tty_inid, 665433d6423SLionel Sambuc tp->tty_incum); 666433d6423SLionel Sambuc tp->tty_inleft = tp->tty_incum = 0; 667433d6423SLionel Sambuc tp->tty_incaller = NONE; 668433d6423SLionel Sambuc } 669433d6423SLionel Sambuc return 1; 670433d6423SLionel Sambuc } 671433d6423SLionel Sambuc 672433d6423SLionel Sambuc if (try) { 673433d6423SLionel Sambuc if (pp->wrleft > 0) 674433d6423SLionel Sambuc return 1; 675433d6423SLionel Sambuc return 0; 676433d6423SLionel Sambuc } 677433d6423SLionel Sambuc 678433d6423SLionel Sambuc while (pp->wrleft > 0) { 679433d6423SLionel Sambuc int s; 680433d6423SLionel Sambuc 681433d6423SLionel Sambuc /* Transfer one character to 'c'. */ 682433d6423SLionel Sambuc if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum, 683433d6423SLionel Sambuc (vir_bytes) &c, 1)) != OK) { 684433d6423SLionel Sambuc printf("pty: safecopy failed (error %d)\n", s); 685433d6423SLionel Sambuc break; 686433d6423SLionel Sambuc } 687433d6423SLionel Sambuc 688433d6423SLionel Sambuc /* Input processing. */ 689433d6423SLionel Sambuc if (in_process(tp, &c, 1) == 0) break; 690433d6423SLionel Sambuc 691433d6423SLionel Sambuc /* PTY writer bookkeeping. */ 692433d6423SLionel Sambuc pp->wrcum++; 693433d6423SLionel Sambuc if (--pp->wrleft == 0) { 694433d6423SLionel Sambuc chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum); 695433d6423SLionel Sambuc pp->wrcum = 0; 696433d6423SLionel Sambuc pp->wrcaller = NONE; 697433d6423SLionel Sambuc } 698433d6423SLionel Sambuc } 699433d6423SLionel Sambuc 700433d6423SLionel Sambuc return 0; 701433d6423SLionel Sambuc } 702433d6423SLionel Sambuc 703433d6423SLionel Sambuc /*===========================================================================* 704*da21d850SDavid van Moolenbroek * pty_slave_mayopen * 705*da21d850SDavid van Moolenbroek *===========================================================================*/ 706*da21d850SDavid van Moolenbroek static int pty_slave_mayopen(tty_t *tp, devminor_t line) 707*da21d850SDavid van Moolenbroek { 708*da21d850SDavid van Moolenbroek /* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */ 709*da21d850SDavid van Moolenbroek pty_t *pp; 710*da21d850SDavid van Moolenbroek int unix98_line, unix98_pty; 711*da21d850SDavid van Moolenbroek 712*da21d850SDavid van Moolenbroek pp = tp->tty_priv; 713*da21d850SDavid van Moolenbroek 714*da21d850SDavid van Moolenbroek /* A non-Unix98 slave may be opened even if the corresponding master is not 715*da21d850SDavid van Moolenbroek * opened yet, but PTY_UNIX98 is always clear for free ptys. A Unix98 slave 716*da21d850SDavid van Moolenbroek * may not be opened before its master, but this should not occur anyway. 717*da21d850SDavid van Moolenbroek */ 718*da21d850SDavid van Moolenbroek unix98_line = (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2); 719*da21d850SDavid van Moolenbroek unix98_pty = !!(pp->state & PTY_UNIX98); 720*da21d850SDavid van Moolenbroek 721*da21d850SDavid van Moolenbroek return (unix98_line == unix98_pty); 722*da21d850SDavid van Moolenbroek } 723*da21d850SDavid van Moolenbroek 724*da21d850SDavid van Moolenbroek /*===========================================================================* 725433d6423SLionel Sambuc * pty_slave_open * 726433d6423SLionel Sambuc *===========================================================================*/ 727433d6423SLionel Sambuc static int pty_slave_open(tty_t *tp, int UNUSED(try)) 728433d6423SLionel Sambuc { 729433d6423SLionel Sambuc /* The tty side has been opened. */ 730433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 731433d6423SLionel Sambuc 732433d6423SLionel Sambuc /* TTY_ACTIVE may already be set, which would indicate that the slave is 733433d6423SLionel Sambuc * reopened after being fully closed while the master is still open. In that 734433d6423SLionel Sambuc * case TTY_CLOSED will also be set, so clear that one. 735433d6423SLionel Sambuc */ 736433d6423SLionel Sambuc pp->state |= TTY_ACTIVE; 737433d6423SLionel Sambuc pp->state &= ~TTY_CLOSED; 738433d6423SLionel Sambuc 739433d6423SLionel Sambuc return 0; 740433d6423SLionel Sambuc } 741433d6423SLionel Sambuc 742433d6423SLionel Sambuc /*===========================================================================* 743433d6423SLionel Sambuc * pty_slave_close * 744433d6423SLionel Sambuc *===========================================================================*/ 745433d6423SLionel Sambuc static int pty_slave_close(tty_t *tp, int UNUSED(try)) 746433d6423SLionel Sambuc { 747433d6423SLionel Sambuc /* The tty side has closed, so shut down the pty side. */ 748433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 749433d6423SLionel Sambuc 750433d6423SLionel Sambuc if (!(pp->state & PTY_ACTIVE)) return 0; 751433d6423SLionel Sambuc 752433d6423SLionel Sambuc if (pp->rdleft > 0) { 753433d6423SLionel Sambuc chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum); 754433d6423SLionel Sambuc pp->rdleft = pp->rdcum = 0; 755433d6423SLionel Sambuc pp->rdcaller = NONE; 756433d6423SLionel Sambuc } 757433d6423SLionel Sambuc 758433d6423SLionel Sambuc if (pp->wrleft > 0) { 759433d6423SLionel Sambuc chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum); 760433d6423SLionel Sambuc pp->wrleft = pp->wrcum = 0; 761433d6423SLionel Sambuc pp->wrcaller = NONE; 762433d6423SLionel Sambuc } 763433d6423SLionel Sambuc 764*da21d850SDavid van Moolenbroek if (pp->state & PTY_CLOSED) pty_reset(tp); 765433d6423SLionel Sambuc else pp->state |= TTY_CLOSED; 766433d6423SLionel Sambuc 767433d6423SLionel Sambuc return 0; 768433d6423SLionel Sambuc } 769433d6423SLionel Sambuc 770433d6423SLionel Sambuc /*===========================================================================* 771433d6423SLionel Sambuc * pty_slave_icancel * 772433d6423SLionel Sambuc *===========================================================================*/ 773433d6423SLionel Sambuc static int pty_slave_icancel(tty_t *tp, int UNUSED(try)) 774433d6423SLionel Sambuc { 775433d6423SLionel Sambuc /* Discard waiting input. */ 776433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 777433d6423SLionel Sambuc 778433d6423SLionel Sambuc if (pp->wrleft > 0) { 779433d6423SLionel Sambuc chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft); 780433d6423SLionel Sambuc pp->wrcum = pp->wrleft = 0; 781433d6423SLionel Sambuc pp->wrcaller = NONE; 782433d6423SLionel Sambuc } 783433d6423SLionel Sambuc 784433d6423SLionel Sambuc return 0; 785433d6423SLionel Sambuc } 786433d6423SLionel Sambuc 787433d6423SLionel Sambuc /*===========================================================================* 788433d6423SLionel Sambuc * pty_slave_ocancel * 789433d6423SLionel Sambuc *===========================================================================*/ 790433d6423SLionel Sambuc static int pty_slave_ocancel(tty_t *tp, int UNUSED(try)) 791433d6423SLionel Sambuc { 792433d6423SLionel Sambuc /* Drain the output buffer. */ 793433d6423SLionel Sambuc pty_t *pp = tp->tty_priv; 794433d6423SLionel Sambuc 795433d6423SLionel Sambuc pp->ocount = 0; 796433d6423SLionel Sambuc pp->otail = pp->ohead; 797433d6423SLionel Sambuc 798433d6423SLionel Sambuc return 0; 799433d6423SLionel Sambuc } 800433d6423SLionel Sambuc 801433d6423SLionel Sambuc /*===========================================================================* 802433d6423SLionel Sambuc * pty_init * 803433d6423SLionel Sambuc *===========================================================================*/ 804433d6423SLionel Sambuc void pty_init(tty_t *tp) 805433d6423SLionel Sambuc { 806433d6423SLionel Sambuc pty_t *pp; 807433d6423SLionel Sambuc int line; 808433d6423SLionel Sambuc 809433d6423SLionel Sambuc /* Associate PTY and TTY structures. */ 810433d6423SLionel Sambuc line = tp - tty_table; 811433d6423SLionel Sambuc pp = tp->tty_priv = &pty_table[line]; 812433d6423SLionel Sambuc pp->tty = tp; 813433d6423SLionel Sambuc pp->select_ops = 0; 814433d6423SLionel Sambuc pp->rdcaller = NONE; 815433d6423SLionel Sambuc pp->wrcaller = NONE; 816433d6423SLionel Sambuc 817433d6423SLionel Sambuc /* Set up output queue. */ 818433d6423SLionel Sambuc pp->ohead = pp->otail = pp->obuf; 819433d6423SLionel Sambuc 820433d6423SLionel Sambuc /* Fill in TTY function hooks. */ 821433d6423SLionel Sambuc tp->tty_devread = pty_slave_read; 822433d6423SLionel Sambuc tp->tty_devwrite = pty_slave_write; 823433d6423SLionel Sambuc tp->tty_echo = pty_slave_echo; 824433d6423SLionel Sambuc tp->tty_icancel = pty_slave_icancel; 825433d6423SLionel Sambuc tp->tty_ocancel = pty_slave_ocancel; 826*da21d850SDavid van Moolenbroek tp->tty_mayopen = pty_slave_mayopen; 827433d6423SLionel Sambuc tp->tty_open = pty_slave_open; 828433d6423SLionel Sambuc tp->tty_close = pty_slave_close; 829433d6423SLionel Sambuc tp->tty_select_ops = 0; 830433d6423SLionel Sambuc } 831