diff options
author | Christopher Faylor <me@cgf.cx> | 2000-02-17 19:38:33 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2000-02-17 19:38:33 +0000 |
commit | 1fd5e000ace55b323124c7e556a7a864b972a5c4 (patch) | |
tree | dc4fcf1e5e22a040716ef92c496b8d94959b2baa /winsup/cygwin/fhandler_tty.cc | |
parent | 369d8a8fd5e887eca547bf34bccfdf755c9e5397 (diff) | |
download | newlib-1fd5e000ace55b323124c7e556a7a864b972a5c4.zip newlib-1fd5e000ace55b323124c7e556a7a864b972a5c4.tar.gz newlib-1fd5e000ace55b323124c7e556a7a864b972a5c4.tar.bz2 |
import winsup-2000-02-17 snapshot
Diffstat (limited to 'winsup/cygwin/fhandler_tty.cc')
-rw-r--r-- | winsup/cygwin/fhandler_tty.cc | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc new file mode 100644 index 0000000..c4ede33 --- /dev/null +++ b/winsup/cygwin/fhandler_tty.cc @@ -0,0 +1,1070 @@ +/* fhandler_tty.cc + + Copyright 1997, 1998, 2000 Cygnus Solutions. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include "winsup.h" +#include <ctype.h> +#include <limits.h> + +/* Tty master stuff */ + +fhandler_tty_master NO_COPY *tty_master; + +static DWORD WINAPI process_input (void *); // Input queue thread +static DWORD WINAPI process_output (void *); // Output queue thread +static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread + +fhandler_tty_master::fhandler_tty_master (const char *name, int unit) : + fhandler_pty_master (name, FH_TTYM, unit) +{ + set_cb (sizeof *this); + console = NULL; + hThread = NULL; +} + +int +fhandler_tty_master::init (int ntty) +{ + HANDLE h; + termios_printf ("Creating master for tty%d", ntty); + + if (init_console ()) + { + termios_printf ("can't create fhandler"); + return -1; + } + + termios ti; + memset (&ti, 0, sizeof (ti)); + console->tcsetattr (0, &ti); + + ttynum = ntty; + + cygwin_shared->tty[ttynum]->common_init (this); + + h = makethread (process_input, NULL, 0, "ttyin"); + if (h == NULL) + { + termios_printf ("can't create input thread"); + return -1; + } + else + { + SetThreadPriority (h, THREAD_PRIORITY_HIGHEST); + CloseHandle (h); + } + + h = makethread (process_ioctl, NULL, 0, "ttyioctl"); + if (h == NULL) + { + termios_printf ("can't create ioctl thread"); + return -1; + } + else + { + SetThreadPriority (h, THREAD_PRIORITY_HIGHEST); + CloseHandle (h); + } + + hThread = makethread (process_output, NULL, 0, "ttyout"); + if (hThread != NULL) + SetThreadPriority (hThread, THREAD_PRIORITY_HIGHEST); + else + { + termios_printf ("can't create output thread"); + return -1; + } + + return 0; +} + +#ifdef DEBUGGING +static class mutex_stack +{ +public: + const char *fn; + int ln; + const char *tname; +} ostack[100]; + +static int osi = 0; +#endif /*DEBUGGING*/ + +DWORD +fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln, + DWORD ms) +{ + if (strace_active) + strace_printf (_STRACE_TERMIOS, "%F (%d): tty output_mutex: waiting %d ms", fn, ln, ms); + DWORD res = WaitForSingleObject (output_mutex, ms); + if (res == WAIT_OBJECT_0) + { +#ifdef DEBUGGING + ostack[osi].fn = fn; + ostack[osi].ln = ln; + ostack[osi].tname = threadname (0, 0); + termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi); + osi++; +#endif + } + if (strace_active) + strace_printf (_STRACE_TERMIOS, "%F (%d): tty output_mutex: acquired", fn, ln, res); + return res; +} + +void +fhandler_tty_common::__release_output_mutex (const char *fn, int ln) +{ + if (ReleaseMutex (output_mutex)) + { +#ifdef DEBUGGING + if (osi > 0) + osi--; + termios_printf ("released at %s:%d, osi %d", fn, ln, osi); + termios_printf(" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname); + ostack[osi].ln = -ln; +#endif + } + if (strace_active) + strace_printf (_STRACE_TERMIOS, "%F (%d): tty output_mutex released", fn, ln); +} + +#define acquire_output_mutex(ms) \ + __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms); + +#define release_output_mutex() \ + __release_output_mutex (__PRETTY_FUNCTION__, __LINE__); + +/* Process tty input. */ + +void +fhandler_pty_master::doecho (const void *str, DWORD len) +{ + acquire_output_mutex (INFINITE); + WriteFile (get_ttyp ()->to_master, str, len, &len, NULL); +// WaitForSingleObject (output_done_event, INFINITE); + release_output_mutex (); +} + +int +fhandler_pty_master::accept_input () +{ + DWORD written; + DWORD n; + const char dummy[1] = {'X'}; + const char *buf; + + n = get_ttyp ()->read_retval = eat_readahead (-1); + + if (n != 0) + buf = rabuf; + else + { + n = 1; + buf = dummy; + termios_printf ("sending EOF to slave"); + } + termios_printf ("about to write %d chars to slave", n); + if (!WriteFile (get_output_handle (), buf, n, &written, NULL)) + return -1; + return get_ttyp ()->read_retval; +} + +static DWORD WINAPI +process_input (void *arg) +{ + char rawbuf[INP_BUFFER_SIZE]; + + while (1) + { + int nraw = tty_master->console->read ((void *) rawbuf, + (size_t) INP_BUFFER_SIZE); + tty_master->line_edit (rawbuf, nraw); + } +} + +BOOL +fhandler_pty_master::hit_eof () +{ + if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ()) + { + /* We have the only remaining open handle to this pty, and + the slave pty has been opened at least once. We treat + this as EOF. */ + termios_printf ("all other handles closed"); + return 1; + } + return 0; +} + +/* Process tty output requests */ + +int +fhandler_pty_master::process_slave_output (char *buf, size_t len) +{ + size_t rlen; + char outbuf[OUT_BUFFER_SIZE]; + DWORD n; + int column = 0; + +again: + + if (len == 0) + return 0; + + if (neednl_) + { + /* We need to return a left over \n character, resulting from + \r\n conversion. Note that we already checked for FLUSHO and + OutputStopped at the time that we read the character, so we + don't check again here. */ + buf[0] = '\n'; + neednl_ = 0; + return 1; + } + + /* Set RLEN to the number of bytes to read from the pipe. */ + rlen = len; + if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR) + { + /* We are going to expand \n to \r\n, so don't read more than + half of the number of bytes requested. */ + rlen /= 2; + if (rlen == 0) + rlen = 1; + } + if (rlen > sizeof outbuf) + rlen = sizeof outbuf; + + HANDLE handle = get_io_handle (); + + /* Doing a busy wait like this is quite inefficient, but nothing + else seems to work completely. Windows should provide some sort + of overlapped I/O for pipes, or something, but it doesn't. */ + DWORD avail; + while (1) + { + if (! PeekNamedPipe (handle, NULL, 0, NULL, &avail, NULL)) + { + if (GetLastError () == ERROR_BROKEN_PIPE) + return 0; + __seterrno (); + return -1; + } + if (avail > 0) + break; + if (hit_eof ()) + return 0; + Sleep (10); + } + + if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE) + { + if (GetLastError () == ERROR_BROKEN_PIPE) + return 0; + __seterrno (); + return -1; + } + + termios_printf ("len=%u", n); + + if (get_ttyp ()->ti.c_lflag & FLUSHO) + { + get_ttyp ()->write_retval = n; + if (output_done_event != NULL) + SetEvent (output_done_event); + goto again; + } + + if (get_ttyp ()->OutputStopped) + WaitForSingleObject (restart_output_event, INFINITE); + + if (get_ttyp ()->ti.c_oflag & OPOST) // post-process output + { + char *iptr = outbuf, *optr = buf; + + while (n--) + { + switch (*iptr) + { + case '\r': + if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0) + { + iptr++; + continue; + } + if (get_ttyp ()->ti.c_oflag & OCRNL) + *iptr = '\n'; + else + column = 0; + break; + case '\n': + if (get_ttyp ()->ti.c_oflag & ONLCR) + { + *optr++ = '\r'; + column = 0; + } + if (get_ttyp ()->ti.c_oflag & ONLRET) + column = 0; + break; + default: + column++; + break; + } + + /* Don't store data past the end of the user's buffer. This + can happen if the user requests a read of 1 byte when + doing \r\n expansion. */ + if (optr - buf >= (int) len) + { + neednl_ = 1; + if (*iptr != '\n' || n != 0) + system_printf ("internal error: %d unexpected characters", n); + break; + } + + *optr++ = *iptr++; + } + return optr - buf; + } + else // raw output mode + { + memcpy (buf, outbuf, n); + return n; + } +} + +static DWORD WINAPI +process_output (void *arg) +{ + char buf[OUT_BUFFER_SIZE*2]; + int n; + + while (1) + { + n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE); + if (n < 0) + { + termios_printf ("ReadFile %E"); + ExitThread (0); + } + if (n == 0) + { + /* End of file. */ + ExitThread (0); + } + n = tty_master->console->write ((void *) buf, (size_t) n); + tty_master->get_ttyp ()->write_retval = n == -1 ? -get_errno () : n; + SetEvent (tty_master->output_done_event); + } +} + + +/* Process tty ioctl requests */ + +static DWORD WINAPI +process_ioctl (void *arg) +{ + while (1) + { + WaitForSingleObject (tty_master->ioctl_request_event, INFINITE); + termios_printf ("ioctl() request"); + tty_master->get_ttyp ()->ioctl_retval = + tty_master->console->ioctl (tty_master->get_ttyp ()->cmd, + (void *) &tty_master->get_ttyp ()->arg); + SetEvent (tty_master->ioctl_done_event); + } +} + +/**********************************************************************/ +/* Tty slave stuff */ + +fhandler_tty_slave::fhandler_tty_slave(int num, const char *name) : + fhandler_tty_common (FH_TTYS, name, num) +{ + set_cb (sizeof *this); + ttynum = num; + /* FIXME: This is wasteful. We should rewrite the set_name path to eliminate the + need for double allocates. */ + unix_path_name_ = (char *) realloc (unix_path_name_, strlen(win32_path_name_) + 1); + strcpy (unix_path_name_, win32_path_name_); + unix_path_name_[0] = unix_path_name_[4] = '/'; + debug_printf ("unix '%s', win32 '%s'", unix_path_name_, win32_path_name_); + inuse = NULL; +} + +fhandler_tty_slave::fhandler_tty_slave(const char *name) : + fhandler_tty_common (FH_TTYS, name, 0) +{ + set_cb (sizeof *this); + debug_printf ("here"); + inuse = NULL; +} + +int +fhandler_tty_slave::open (const char *, int flags, mode_t) +{ + tcinit (cygwin_shared->tty[ttynum]); + + attach_tty (ttynum); + set_ctty (ttynum, flags); + + set_flags (flags); + /* Create synchronisation events */ + char buf[40]; + + /* output_done_event may or may not exist. It will exist if the tty + was opened by fhandler_tty_master::init, normally called at + startup if use_tty is non-zero. It will not exist if this is a + pty opened by fhandler_pty_master::open. In the former case, tty + output is handled by a separate thread which controls output. */ + __small_sprintf (buf, OUTPUT_DONE_EVENT, ttynum); + output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + + if (!(output_mutex = get_ttyp()->open_output_mutex (TRUE))) + { + termios_printf ("open output mutex failed, %E"); + __seterrno (); + return 0; + } + + /* The ioctl events may or may not exist. See output_done_event, + above. */ + __small_sprintf (buf, IOCTL_REQUEST_EVENT, ttynum); + ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + __small_sprintf (buf, IOCTL_DONE_EVENT, ttynum); + ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + + /* FIXME: Needs a method to eliminate tty races */ + { + acquire_output_mutex (500); + inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE); + get_ttyp ()->was_opened = TRUE; + release_output_mutex (); + } + + /* Duplicate tty handles. */ + + if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave) + { + termios_printf ("tty handles have been closed"); + set_errno (EACCES); + return 0; + } + + HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, + get_ttyp ()->master_pid); + if (tty_owner == NULL) + { + termios_printf ("can't open tty(%d) handle process %d", + ttynum, get_ttyp ()->master_pid); + __seterrno (); + return 0; + } + + HANDLE nh; + if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master, hMainProc, &nh, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate input, %E"); + __seterrno (); + return 0; + } + set_io_handle (nh); + termios_printf ("duplicated from_master %p->%p from tty_owner %p", + get_ttyp ()->from_master, nh, tty_owner); + if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master, hMainProc, &nh, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate output, %E"); + __seterrno (); + return 0; + } + set_output_handle (nh); + CloseHandle (tty_owner); + + termios_printf("tty%d opened", ttynum); + + return 1; +} + +void +fhandler_tty_slave::init (HANDLE f, DWORD a, mode_t) +{ + int mode = 0; + + a &= GENERIC_READ | GENERIC_WRITE; + if (a == GENERIC_READ) + mode = O_RDONLY; + if (a == GENERIC_WRITE) + mode = O_WRONLY; + if (a == (GENERIC_READ | GENERIC_WRITE)) + mode = O_RDWR; + + open (0, mode); +} + +int +fhandler_tty_slave::write (const void *ptr, size_t len) +{ + DWORD n, towrite = len; + + termios_printf("tty%d, write(%x, %d)", ttynum, ptr, len); + + acquire_output_mutex (INFINITE); + + while (len) + { + n = min (OUT_BUFFER_SIZE, len); + char *buf = (char *)ptr; + ptr = (char *) ptr + n; + len -= n; + + if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE) + { + termios_printf ("WriteFile failed, %E"); + towrite = (DWORD) -1; + _raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ + break; + } + + if (output_done_event != NULL) + { + termios_printf("tty%d waiting for output_done", ttynum); + WaitForSingleObject (output_done_event, n * 1000); + } + + if (get_ttyp ()->write_retval < 0) + { + set_errno (-get_ttyp ()->write_retval); + towrite = (DWORD) -1; + break; + } + } + release_output_mutex (); + return towrite; +} + +int +fhandler_tty_slave::read (void *ptr, size_t len) +{ + DWORD n; + int totalread = 0; + int vmin = INT_MAX; + int vtime = 0; /* Initialized to prevent -Wuninitialized warning */ + char buf[INP_BUFFER_SIZE]; + + termios_printf("read(%x, %d) handle %d", ptr, len, get_handle ()); + + if (!(get_ttyp ()->ti.c_lflag & ICANON)) + { + vmin = get_ttyp ()->ti.c_cc[VMIN]; + vtime = get_ttyp ()->ti.c_cc[VTIME]; + } + + while (len) + { + wait: + termios_printf ("reading %d bytes (vtime %d)", + min ((unsigned) vmin, min (len, sizeof (buf))), vtime); + if (ReadFile (get_handle (), (unsigned *) buf, + min ((unsigned) vmin, min (len, sizeof (buf))), &n, NULL) == FALSE) + { + termios_printf ("read failed, %E"); + _raise (SIGHUP); + } + if (get_ttyp ()->read_retval < 0) // read error + { + set_errno (-get_ttyp ()->read_retval); + totalread = -1; + break; + } + if (get_ttyp ()->read_retval == 0) //EOF + { + termios_printf ("saw EOF"); + break; + } + len -= n; + totalread += n; + memcpy (ptr, buf, n); + ptr = (char *) ptr + n; + if (get_ttyp ()->ti.c_lflag & ICANON) + break; + else if (totalread >= vmin) + break; + + if (!PeekNamedPipe (get_handle (), NULL, 0, NULL, &n, NULL)) + { + termios_printf("PeekNamedPipe failed, %E"); + break; + } + if (n == 0) + { + if (get_flags () & (O_NONBLOCK | O_NDELAY)) + break; + + /* We can't enter to blocking Readfile - signals will be lost! + * So, poll the pipe for data. + * FIXME: try to avoid polling... + * FIXME: Current EINTR scheme does not take vmin/vtime into account. + */ + if (!(get_ttyp ()->ti.c_lflag & ICANON)) + { + termios_printf("vmin %d vtime %d", vmin, vtime); + if (vmin == 0 && vtime == 0) + return 0; // min = 0, time = 0 + if (vtime == 0) + goto wait; // min > 0, time = 0 + while (vtime--) + { + PeekNamedPipe (get_handle (), NULL, 0, NULL, &n, NULL); + if (n) + break; + Sleep(10); + } + if (vtime == 0) + return totalread; + } + } + } + termios_printf ("%d=read(%x, %d)", totalread, ptr, len); + return totalread; +} + +int +fhandler_tty_common::dup (fhandler_base *child) +{ + fhandler_tty_slave *fts = (fhandler_tty_slave *) child; + int errind; + + termios_printf ("here"); + fts->ttynum = ttynum; + fts->tcinit (get_ttyp ()); + + attach_tty (ttynum); + + HANDLE nh; + + if (output_done_event == NULL) + fts->output_done_event = NULL; + else if (!DuplicateHandle (hMainProc, output_done_event, hMainProc, + &fts->output_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 1; + goto err; + } + if (ioctl_request_event == NULL) + fts->ioctl_request_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_request_event, hMainProc, + &fts->ioctl_request_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 2; + goto err; + } + if (ioctl_done_event == NULL) + fts->ioctl_done_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_done_event, hMainProc, + &fts->ioctl_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 3; + goto err; + } + if (!DuplicateHandle (hMainProc, output_mutex, hMainProc, + &fts->output_mutex, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 4; + goto err; + } + if (!DuplicateHandle (hMainProc, get_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 5; + goto err; + } + fts->set_io_handle (nh); + + if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 6; + goto err; + } + fts->set_output_handle (nh); + + if (inuse == NULL) + fts->inuse = NULL; + else if (!DuplicateHandle (hMainProc, inuse, hMainProc, + &fts->inuse, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 7; + goto err; + } + return 0; + +err: + __seterrno (); + termios_printf ("dup %d failed in DuplicateHandle, %E", errind); + return -1; +} + +int +fhandler_tty_slave::tcgetattr (struct termios *t) +{ + *t = get_ttyp ()->ti; + return 0; +} + +int +fhandler_tty_slave::tcsetattr (int a, const struct termios *t) +{ + acquire_output_mutex (INFINITE); + get_ttyp ()->ti = *t; + release_output_mutex (); + return 0; +} + +int +fhandler_tty_slave::tcflush (int a) +{ + return 0; +} + +void +fhandler_tty_slave::send_ioctl_request (void) +{ + if (ioctl_request_event == NULL || ioctl_done_event == NULL) // slave of pty + return; + + acquire_output_mutex (INFINITE); + SetEvent (ioctl_request_event); + WaitForSingleObject (ioctl_done_event, INFINITE); + release_output_mutex (); +} + +int +fhandler_tty_slave::ioctl (unsigned int cmd, void *arg) +{ + termios_printf ("ioctl (%x)", cmd); + + if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid && + myself->ctty == ttynum && (get_ttyp ()->ti.c_lflag & TOSTOP)) + { + /* background process */ + termios_printf("bg ioctl pgid %d, tpgid %d, ctty %d", + myself->pgid, get_ttyp ()->getpgid (), myself->ctty); + _raise (SIGTTOU); + } + get_ttyp ()->cmd = cmd; + get_ttyp ()->ioctl_retval = 0; + switch (cmd) + { + case TIOCGWINSZ: + get_ttyp ()->arg.winsize = get_ttyp ()->winsize; + send_ioctl_request (); + * (struct winsize *) arg = get_ttyp ()->arg.winsize; + get_ttyp ()->winsize = get_ttyp ()->arg.winsize; + break; + case TIOCSWINSZ: + get_ttyp ()->ioctl_retval = -1; + get_ttyp ()->arg.winsize = * (struct winsize *) arg; + send_ioctl_request (); + break; + case FIONBIO: + if (* (int *) arg) + set_flags (get_flags () | O_NONBLOCK); + else + set_flags (get_flags () & ~O_NONBLOCK); + break; + default: + set_errno (EINVAL); + return -1; + } + termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd); + return get_ttyp ()->ioctl_retval; +} + +/******************************************************* + fhandler_pty_master +*/ +fhandler_pty_master::fhandler_pty_master (const char *name, DWORD devtype, int unit) : + fhandler_tty_common (devtype, name, unit) +{ + set_cb (sizeof *this); + ioctl_request_event = NULL; + ioctl_done_event = NULL; + restart_output_event = NULL; + pktmode = neednl_ = 0; + inuse = NULL; +} + +int +fhandler_pty_master::open (const char *, int flags, mode_t) +{ + ttynum = cygwin_shared->tty.allocate_tty (0); + if (ttynum < 0) + return 0; + + cygwin_shared->tty[ttynum]->common_init (this); + inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE); + set_flags (flags); + + termios_printf ("opened pty master tty%d<%p>", ttynum, this); + return 1; +} + +int +fhandler_tty_common::close () +{ +termios_printf ("here %p", this); + if (output_done_event && !CloseHandle (output_done_event)) + termios_printf ("CloseHandle (output_done_event), %E"); + if (ioctl_done_event && !CloseHandle (ioctl_done_event)) + termios_printf ("CloseHandle (ioctl_done_event), %E"); + if (ioctl_request_event && !CloseHandle (ioctl_request_event)) + termios_printf ("CloseHandle (ioctl_request_event), %E"); + if (restart_output_event && !CloseHandle (restart_output_event)) + termios_printf ("CloseHandle (restart_output_event), %E"); + if (inuse && !CloseHandle (inuse)) + termios_printf ("CloseHandle (inuse), %E"); + if (!ForceCloseHandle (output_mutex)) + termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex); + if (!CloseHandle (get_handle ())) + termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ()); + if (!CloseHandle (get_output_handle ())) + termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ()); + + inuse = NULL; + termios_printf ("tty%d closed", ttynum); + return 0; +} + +int +fhandler_pty_master::close () +{ +#if 0 + while (accept_input () > 0) + continue; +#endif + this->fhandler_tty_common::close (); + + if (!get_ttyp ()->master_alive ()) + { + termios_printf ("freeing tty%d (%d)", ttynum, get_ttyp ()->ntty); + if (get_ttyp ()->to_slave) + CloseHandle (get_ttyp ()->to_slave); + if (get_ttyp ()->from_slave) + CloseHandle (get_ttyp ()->from_slave); + if (get_ttyp ()->from_master) + CloseHandle (get_ttyp ()->from_master); + if (get_ttyp ()->to_master) + CloseHandle (get_ttyp ()->to_master); + get_ttyp ()->init (); + } + + return 0; +} + +int +fhandler_pty_master::write (const void *ptr, size_t len) +{ + line_edit ((char *) ptr, len); + return len; +} + +int +fhandler_pty_master::read (void *ptr, size_t len) +{ + DWORD n; + char *cptr = (char *) ptr; + + if (! PeekNamedPipe (get_handle (), NULL, 0, NULL, &n, NULL)) + { + if (GetLastError () == ERROR_BROKEN_PIPE) + { + /* On Unix, a read from a broken pipe returns EOF. */ + return 0; + } + __seterrno (); + return -1; + } + if (n == 0 + && (get_flags () & (O_NONBLOCK | O_NDELAY)) != 0) + { + set_errno (EAGAIN); + return -1; + } + if (pktmode) + { + *cptr++ = TIOCPKT_DATA; + len--; + } + n = process_slave_output (cptr, len); + if (n < 0) + return -1; + if (output_done_event != NULL) + SetEvent (output_done_event); + if (pktmode && n > 0) + n++; + return n; +} + +int +fhandler_pty_master::tcgetattr (struct termios *t) +{ + *t = cygwin_shared->tty[ttynum]->ti; + return 0; +} + +int +fhandler_pty_master::tcsetattr (int a, const struct termios *t) +{ + cygwin_shared->tty[ttynum]->ti = *t; + return 0; +} + +int +fhandler_pty_master::tcflush (int a) +{ + return 0; +} + +int +fhandler_pty_master::ioctl (unsigned int cmd, void *arg) +{ + switch (cmd) + { + case TIOCPKT: + pktmode = * (int *) arg; + break; + case TIOCGWINSZ: + * (struct winsize *) arg = get_ttyp ()->winsize; + break; + case TIOCSWINSZ: + get_ttyp ()->winsize = * (struct winsize *) arg; + _kill (-get_ttyp ()->getpgid (), SIGWINCH); + break; + case FIONBIO: + if (* (int *) arg) + set_flags (get_flags () | O_NONBLOCK); + else + set_flags (get_flags () & ~O_NONBLOCK); + break; + default: + set_errno (EINVAL); + return -1; + } + return 0; +} + +char * +fhandler_pty_master::ptsname (void) +{ + static char buf[32]; + + __small_sprintf (buf, "/dev/tty%d", ttynum); + return buf; +} + +void +fhandler_tty_common::set_close_on_exec (int val) +{ + this->fhandler_base::set_close_on_exec (val); + if (output_done_event) + set_inheritance (output_done_event, val); + if (ioctl_request_event) + set_inheritance (ioctl_request_event, val); + if (ioctl_done_event) + set_inheritance (ioctl_done_event, val); + if (inuse) + set_inheritance (inuse, val); + set_inheritance (output_mutex, val, "output_mutex"); + set_inheritance (output_handle, val); +} + +void +fhandler_tty_common::fixup_after_fork (HANDLE parent) +{ + this->fhandler_base::fixup_after_fork (parent); + if (output_done_event) + fork_fixup (parent, output_done_event, "output_done_event"); + if (ioctl_request_event) + fork_fixup (parent, ioctl_request_event, "ioctl_request_event"); + if (ioctl_done_event) + fork_fixup (parent, ioctl_done_event, "ioctl_done_event"); + if (output_mutex) + { + fork_fixup (parent, output_mutex, "output_mutex"); + ProtectHandle (output_mutex); + } + fork_fixup (parent, output_handle, "output_handle"); + fork_fixup (parent, inuse, "inuse"); +} + +void +fhandler_pty_master::set_close_on_exec (int val) +{ + this->fhandler_tty_common::set_close_on_exec (val); + set_inheritance (restart_output_event, val); + + /* FIXME: There is a console handle leak here. */ + if (get_ttyp ()->master_pid == GetCurrentProcessId ()) + { + get_ttyp ()->from_slave = get_handle (); + get_ttyp ()->to_slave = get_output_handle (); + } +} + +void +fhandler_pty_master::fixup_after_fork (HANDLE child) +{ + this->fhandler_tty_common::fixup_after_fork (child); + if (restart_output_event) + fork_fixup (child, restart_output_event, "restart_output_event"); +} + +void +fhandler_tty_master::fixup_after_fork (HANDLE child) +{ + this->fhandler_pty_master::fixup_after_fork (child); + console->fixup_after_fork (child); +} + +int +fhandler_tty_master::de_linearize (const char *buf, const char *unix_name, + const char *win32_name) +{ + int res = fhandler_base::de_linearize (buf, unix_name, win32_name); + console->close (); + init_console (); + return res; +} + +int +fhandler_tty_master::init_console () +{ + console = (fhandler_console *) dtable.build_fhandler (-1, FH_CONSOLE, "/dev/ttym"); + if (console == NULL) + return -1; + + console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY); + console->set_r_no_interrupt (1); + return 0; +} |