summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2004-01-21 06:27:55 +0000
committerChristopher Faylor <cgf@redhat.com>2004-01-21 06:27:55 +0000
commit456a03998eedde4197b3e87e81a51a810d9f6bf3 (patch)
tree42612d81e9ad065b8ff113fc2a70fcb1e441a369
parenteaaabbf0111c67e39e98bbb920dfae004fbc44d8 (diff)
downloadgdb-456a03998eedde4197b3e87e81a51a810d9f6bf3.tar.gz
* fhandler_tty.cc (fhandler_tty::ioctl): Semi-revert 2003-09-26 change for
TIOCSWINSZ. It is not an error for ioctl_request_event to be missing.
-rw-r--r--winsup/cygwin/ChangeLog6
-rw-r--r--winsup/cygwin/fhandler_tty.cc1417
2 files changed, 1423 insertions, 0 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 94f4a9df20d..82e8f581ed3 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,9 @@
+2004-01-21 Christopher Faylor <cgf@redhat.com>
+
+ * fhandler_tty.cc (fhandler_tty::ioctl): Semi-revert 2003-09-26 change
+ for TIOCSWINSZ. It is not an error for ioctl_request_event to be
+ missing.
+
2004-01-20 Christopher Faylor <cgf@redhat.com>
* sigproc.cc (pending_signals::save): New function.
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
new file mode 100644
index 00000000000..122f3f33d5d
--- /dev/null
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -0,0 +1,1417 @@
+/* fhandler_tty.cc
+
+ Copyright 1997, 1998, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+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 "winsup.h"
+#include <wingdi.h>
+#include <winuser.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "cygserver.h"
+#include "cygthread.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 ()
+ : fhandler_pty_master (), console (NULL)
+{
+}
+
+int
+fhandler_tty_slave::get_unit ()
+{
+ return dev () == FH_TTY ? myself->ctty : dev ().minor;
+}
+
+void
+fhandler_tty_master::set_winsize (bool sendSIGWINCH)
+{
+ winsize w;
+ console->ioctl (TIOCGWINSZ, &w);
+ get_ttyp ()->winsize = w;
+ if (sendSIGWINCH)
+ tc->kill_pgrp (SIGWINCH);
+}
+
+int
+fhandler_tty_master::init ()
+{
+ slave = dev ();
+ termios_printf ("Creating master for tty%d", get_unit ());
+
+ if (init_console ())
+ {
+ termios_printf ("can't create fhandler");
+ return -1;
+ }
+
+ termios ti;
+ memset (&ti, 0, sizeof (ti));
+ console->tcsetattr (0, &ti);
+
+ cygwin_shared->tty[get_unit ()]->common_init (this);
+
+ set_winsize (false);
+
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+ set_close_on_exec (true);
+
+ cygthread *h;
+ h = new cygthread (process_input, cygself, "ttyin");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_ioctl, cygself, "ttyioctl");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_output, cygself, "ttyout");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ return 0;
+}
+
+#ifdef DEBUGGING
+static class mutex_stack
+{
+public:
+ const char *fn;
+ int ln;
+ const char *tname;
+} ostack[100];
+
+static int osi;
+#endif /*DEBUGGING*/
+
+DWORD
+fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln,
+ DWORD ms)
+{
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms);
+ DWORD res = WaitForSingleObject (output_mutex, ms);
+ if (res == WAIT_OBJECT_0)
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res);
+#else
+ ostack[osi].fn = fn;
+ ostack[osi].ln = ln;
+ ostack[osi].tname = cygthread::name ();
+ termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi);
+ osi++;
+#endif
+ }
+ return res;
+}
+
+void
+fhandler_tty_common::__release_output_mutex (const char *fn, int ln)
+{
+ if (ReleaseMutex (output_mutex))
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln);
+#else
+ 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
+ }
+}
+
+/* Process tty input. */
+
+void
+fhandler_pty_master::doecho (const void *str, DWORD len)
+{
+ acquire_output_mutex (INFINITE);
+ if (!WriteFile (get_ttyp ()->to_master, str, len, &len, NULL))
+ termios_printf ("Write to %p failed, %E", get_ttyp ()->to_master);
+// WaitForSingleObject (output_done_event, INFINITE);
+ release_output_mutex ();
+}
+
+int
+fhandler_pty_master::accept_input ()
+{
+ DWORD bytes_left;
+ int ret = 1;
+
+ (void) WaitForSingleObject (input_mutex, INFINITE);
+
+ bytes_left = eat_readahead (-1);
+
+ if (!bytes_left)
+ {
+ termios_printf ("sending EOF to slave");
+ get_ttyp ()->read_retval = 0;
+ }
+ else
+ {
+ char *p = rabuf;
+ DWORD rc;
+ DWORD written = 0;
+
+ termios_printf ("about to write %d chars to slave", bytes_left);
+ rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL);
+ if (!rc)
+ {
+ debug_printf ("error writing to pipe %E");
+ get_ttyp ()->read_retval = -1;
+ ret = -1;
+ }
+ else
+ {
+ get_ttyp ()->read_retval = 1;
+ p += written;
+ bytes_left -= written;
+ if (bytes_left > 0)
+ {
+ debug_printf ("to_slave pipe is full");
+ puts_readahead (p, bytes_left);
+ ret = 0;
+ }
+ }
+ }
+
+ SetEvent (input_available_event);
+ ReleaseMutex (input_mutex);
+ return ret;
+}
+
+static DWORD WINAPI
+process_input (void *)
+{
+ char rawbuf[INP_BUFFER_SIZE];
+
+ while (1)
+ {
+ size_t nraw = INP_BUFFER_SIZE;
+ tty_master->console->read ((void *) rawbuf, nraw);
+ if (tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti)
+ == line_edit_signalled)
+ tty_master->console->eat_readahead (-1);
+ }
+}
+
+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, int pktmode_on)
+{
+ size_t rlen;
+ char outbuf[OUT_BUFFER_SIZE + 1];
+ DWORD n;
+ int column = 0;
+ int rc = 0;
+
+ if (len == 0)
+ goto out;
+
+ if (need_nl)
+ {
+ /* We need to return a left over \n character, resulting from
+ \r\n conversion. Note that we already checked for FLUSHO and
+ output_stopped at the time that we read the character, so we
+ don't check again here. */
+ if (buf)
+ buf[0] = '\n';
+ need_nl = 0;
+ rc = 1;
+ goto out;
+ }
+
+
+ for (;;)
+ {
+ /* 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 ();
+
+ n = 0; // get_readahead_into_buffer (outbuf, len);
+ if (!n)
+ {
+ /* 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. */
+ while (1)
+ {
+ if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL))
+ goto err;
+ if (n > 0)
+ break;
+ if (hit_eof ())
+ goto out;
+ /* DISCARD (FLUSHO) and tcflush can finish here. */
+ if (n == 0 && (get_ttyp ()->ti.c_lflag & FLUSHO || !buf))
+ goto out;
+ if (n == 0 && is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ rc = -1;
+ break;
+ }
+
+ Sleep (10);
+ }
+
+ if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE)
+ goto err;
+ }
+
+ termios_printf ("bytes read %u", n);
+ get_ttyp ()->write_error = 0;
+ if (output_done_event != NULL)
+ SetEvent (output_done_event);
+
+ if (get_ttyp ()->ti.c_lflag & FLUSHO || !buf)
+ continue;
+
+ char *optr;
+ optr = buf;
+ if (pktmode_on)
+ *optr++ = TIOCPKT_DATA;
+
+ if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output
+ {
+ memcpy (optr, outbuf, n);
+ optr += n;
+ }
+ else // raw output mode
+ {
+ char *iptr = outbuf;
+
+ 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)
+ {
+ if (*iptr != '\n' || n != 0)
+ system_printf ("internal error: %d unexpected characters", n);
+ need_nl = 1;
+ break;
+ }
+
+ *optr++ = *iptr++;
+ }
+ }
+ rc = optr - buf;
+ break;
+
+ err:
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ rc = 0;
+ else
+ {
+ __seterrno ();
+ rc = -1;
+ }
+ break;
+ }
+
+out:
+ termios_printf ("returning %d", rc);
+ return rc;
+}
+
+static DWORD WINAPI
+process_output (void *)
+{
+ char buf[OUT_BUFFER_SIZE * 2];
+
+ for (;;)
+ {
+ int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0);
+ if (n <= 0)
+ {
+ if (n < 0)
+ termios_printf ("ReadFile %E");
+ ExitThread (0);
+ }
+ n = tty_master->console->write ((void *) buf, (size_t) n);
+ tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0;
+ }
+}
+
+
+/* Process tty ioctl requests */
+
+static DWORD WINAPI
+process_ioctl (void *)
+{
+ 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 ()
+ : fhandler_tty_common ()
+{
+ set_r_no_interrupt (1);
+}
+
+/* FIXME: This function needs to close handles when it has
+ a failing condition. */
+int
+fhandler_tty_slave::open (int flags, mode_t)
+{
+ if (get_device () == FH_TTY)
+ pc.dev.tty_to_real_device ();
+ fhandler_tty_slave *arch = (fhandler_tty_slave *)
+ cygheap->fdtab.find_archetype (pc.dev);
+ if (arch)
+ {
+ *this = *(fhandler_tty_slave *) arch;
+ termios_printf ("copied tty fhandler archetype");
+ cygheap->open_fhs++;
+ goto out;
+ }
+
+ tcinit (cygwin_shared->tty[get_unit ()]);
+
+ attach_tty (get_unit ());
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ /* 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, get_unit ());
+ output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ if (!(output_mutex = get_ttyp ()->open_output_mutex ()))
+ {
+ termios_printf ("open output mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ if (!(input_mutex = get_ttyp ()->open_input_mutex ()))
+ {
+ termios_printf ("open input mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ __small_sprintf (buf, INPUT_AVAILABLE_EVENT, get_unit ());
+ if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf)))
+ {
+ termios_printf ("open input event failed, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ /* The ioctl events may or may not exist. See output_done_event,
+ above. */
+ __small_sprintf (buf, IOCTL_REQUEST_EVENT, get_unit ());
+ ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+ __small_sprintf (buf, IOCTL_DONE_EVENT, get_unit ());
+ 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 from_master_local;
+ HANDLE to_master_local;
+ from_master_local = to_master_local = NULL;
+
+#ifdef USE_SERVER
+ if (!wincap.has_security ()
+ || cygserver_running == CYGSERVER_UNAVAIL
+ || !cygserver_attach_tty (&from_master_local, &to_master_local))
+#endif
+ {
+#ifdef USE_SERVER
+ termios_printf ("cannot dup handles via server. using old method.");
+#endif
+ HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+ get_ttyp ()->master_pid);
+ termios_printf ("tty own handle %p",tty_owner);
+ if (tty_owner == NULL)
+ {
+ termios_printf ("can't open tty (%d) handle process %d",
+ get_unit (), get_ttyp ()->master_pid);
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
+ hMainProc, &from_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate input, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
+ hMainProc, &to_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate output, %E");
+ __seterrno ();
+ return 0;
+ }
+ CloseHandle (tty_owner);
+ }
+
+ termios_printf ("duplicated from_master %p->%p from tty_owner",
+ get_ttyp ()->from_master, from_master_local);
+ termios_printf ("duplicated to_master %p->%p from tty_owner",
+ get_ttyp ()->to_master, to_master_local);
+
+ set_io_handle (from_master_local);
+ set_output_handle (to_master_local);
+
+ set_open_status ();
+ if (cygheap->open_fhs++ == 0 && !GetConsoleCP () && !output_done_event
+ && wincap.pty_needs_alloc_console () && !GetProcessWindowStation ())
+ {
+ BOOL b;
+ HWINSTA h = CreateWindowStation (NULL, 0, GENERIC_READ | GENERIC_WRITE, &sec_none_nih);
+ termios_printf ("CreateWindowStation %p, %E", h);
+ if (h)
+ {
+ b = SetProcessWindowStation (h);
+ termios_printf ("SetProcessWindowStation %d, %E", b);
+ }
+ b = AllocConsole (); // will cause flashing if workstation
+ // stuff fails
+ termios_printf ("%d = AllocConsole (), %E", b);
+ if (b)
+ init_console_handler ();
+ }
+
+ // FIXME: Do this better someday
+ arch = (fhandler_tty_slave *) cmalloc (HEAP_ARCHETYPES, sizeof (*this));
+ *((fhandler_tty_slave **) cygheap->fdtab.add_archetype ()) = arch;
+ archetype = arch;
+ *arch = *this;
+
+out:
+ usecount = 0;
+ archetype->usecount++;
+ report_tty_counts (this, "opened", "incremented ", "");
+ myself->set_ctty (get_ttyp (), flags, arch);
+
+ return 1;
+}
+
+int
+fhandler_tty_slave::close ()
+{
+ if (!--cygheap->open_fhs && myself->ctty == -1)
+ FreeConsole ();
+
+ archetype->usecount--;
+ report_tty_counts (this, "closed", "decremented ", "");
+
+ if (archetype->usecount)
+ {
+#ifdef DEBUGGING
+ if (archetype->usecount < 0)
+ system_printf ("error: usecount %d", archetype->usecount);
+#endif
+ termios_printf ("just returning because archetype usecount is != 0");
+ return 0;
+ }
+
+ termios_printf ("closing last open %s handle", ttyname ());
+ return fhandler_tty_common::close ();
+}
+
+int
+fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
+ LPHANDLE to_master_ptr)
+{
+#ifndef USE_SERVER
+ return 0;
+#else
+ if (!from_master_ptr || !to_master_ptr)
+ return 0;
+
+ client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid,
+ (HANDLE) get_ttyp ()->from_master,
+ (HANDLE) get_ttyp ()->to_master);
+
+ if (req.make_request () == -1 || req.error_code ())
+ return 0;
+
+ *from_master_ptr = req.from_master ();
+ *to_master_ptr = req.to_master ();
+
+ return 1;
+#endif
+}
+
+void
+fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
+{
+ int flags = 0;
+
+ a &= GENERIC_READ | GENERIC_WRITE;
+ if (a == GENERIC_READ)
+ flags = O_RDONLY;
+ if (a == GENERIC_WRITE)
+ flags = O_WRONLY;
+ if (a == (GENERIC_READ | GENERIC_WRITE))
+ flags = O_RDWR;
+
+ open (flags);
+}
+
+int
+fhandler_tty_slave::write (const void *ptr, size_t len)
+{
+ DWORD n, towrite = len;
+
+ termios_printf ("tty%d, write(%x, %d)", get_unit (), ptr, len);
+
+ acquire_output_mutex (INFINITE);
+
+ while (len)
+ {
+ n = min (OUT_BUFFER_SIZE, len);
+ char *buf = (char *)ptr;
+ ptr = (char *) ptr + n;
+ len -= n;
+
+ /* Previous write may have set write_error to != 0. Check it here.
+ This is less than optimal, but the alternative slows down tty
+ writes enormously. */
+ if (get_ttyp ()->write_error)
+ {
+ set_errno (get_ttyp ()->write_error);
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE)
+ {
+ DWORD err = GetLastError ();
+ termios_printf ("WriteFile failed, %E");
+ switch (err)
+ {
+ case ERROR_NO_DATA:
+ err = ERROR_IO_DEVICE;
+ default:
+ __seterrno_from_win_error (err);
+ }
+ raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (output_done_event != NULL)
+ {
+ DWORD rc;
+ DWORD x = n * 1000;
+ rc = WaitForSingleObject (output_done_event, x);
+ termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc);
+ }
+ }
+ release_output_mutex ();
+ return towrite;
+}
+
+void __stdcall
+fhandler_tty_slave::read (void *ptr, size_t& len)
+{
+ int totalread = 0;
+ int vmin = 0;
+ int vtime = 0; /* Initialized to prevent -Wuninitialized warning */
+ size_t readlen;
+ DWORD bytes_in_pipe;
+ char buf[INP_BUFFER_SIZE];
+ char peek_buf[INP_BUFFER_SIZE];
+ DWORD time_to_wait;
+ DWORD rc;
+ HANDLE w4[2];
+
+ termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ());
+
+ if (!ptr) /* Indicating tcflush(). */
+ time_to_wait = 0;
+ else if ((get_ttyp ()->ti.c_lflag & ICANON))
+ time_to_wait = INFINITE;
+ else
+ {
+ vmin = get_ttyp ()->ti.c_cc[VMIN];
+ if (vmin > INP_BUFFER_SIZE)
+ vmin = INP_BUFFER_SIZE;
+ vtime = get_ttyp ()->ti.c_cc[VTIME];
+ if (vmin < 0)
+ vmin = 0;
+ if (vtime < 0)
+ vtime = 0;
+ if (!vmin && !vtime)
+ time_to_wait = 0;
+ else
+ time_to_wait = !vtime ? INFINITE : 100 * vtime;
+ }
+
+ w4[0] = signal_arrived;
+ w4[1] = input_available_event;
+
+ DWORD waiter = !ptr ? 0 : INFINITE;
+ while (len)
+ {
+ rc = WaitForMultipleObjects (2, w4, FALSE, waiter);
+
+ if (rc == WAIT_TIMEOUT)
+ break;
+
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input event failed, %E");
+ break;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ {
+ /* if we've received signal after successfully reading some data,
+ just return all data successfully read */
+ if (totalread > 0)
+ break;
+ set_sig_errno (EINTR);
+ len = (size_t) -1;
+ return;
+ }
+
+ rc = WaitForSingleObject (input_mutex, 1000);
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input mutex failed, %E");
+ break;
+ }
+ else if (rc == WAIT_TIMEOUT)
+ {
+ termios_printf ("failed to acquire input mutex after input event arrived");
+ break;
+ }
+ if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+
+ /* On first peek determine no. of bytes to flush. */
+ if (!ptr && len == UINT_MAX)
+ len = (size_t) bytes_in_pipe;
+
+ if (ptr && !vmin && !time_to_wait)
+ {
+ ReleaseMutex (input_mutex);
+ len = (size_t) bytes_in_pipe;
+ return;
+ }
+
+ readlen = min (bytes_in_pipe, min (len, sizeof (buf)));
+
+ if (ptr && vmin && readlen > (unsigned) vmin)
+ readlen = vmin;
+
+ DWORD n = 0;
+ if (readlen)
+ {
+ termios_printf ("reading %d bytes (vtime %d)", readlen, vtime);
+ if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE)
+ {
+ termios_printf ("read failed, %E");
+ raise (SIGHUP);
+ }
+ /* MSDN states that 5th prameter can be used to determine total
+ number of bytes in pipe, but for some reason this number doesn't
+ change after successful read. So we have to peek into the pipe
+ again to see if input is still available */
+ if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+ if (n)
+ {
+ len -= n;
+ totalread += n;
+ if (ptr)
+ {
+ memcpy (ptr, buf, n);
+ ptr = (char *) ptr + n;
+ }
+ }
+ }
+
+ if (!bytes_in_pipe)
+ ResetEvent (input_available_event);
+
+ ReleaseMutex (input_mutex);
+
+ if (!ptr)
+ {
+ if (!bytes_in_pipe)
+ break;
+ continue;
+ }
+
+ 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;
+ }
+ if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ())
+ break;
+ if (vmin && totalread >= vmin)
+ break;
+
+ /* vmin == 0 && vtime == 0:
+ * we've already read all input, if any, so return immediately
+ * vmin == 0 && vtime > 0:
+ * we've waited for input 10*vtime ms in WFSO(input_available_event),
+ * no matter whether any input arrived, we shouldn't wait any longer,
+ * so return immediately
+ * vmin > 0 && vtime == 0:
+ * here, totalread < vmin, so continue waiting until more data
+ * arrive
+ * vmin > 0 && vtime > 0:
+ * similar to the previous here, totalread < vmin, and timer
+ * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT,
+ * so "restart timer" and wait until more data arrive
+ */
+
+ if (vmin == 0)
+ break;
+
+ if (n)
+ waiter = time_to_wait;
+ }
+ termios_printf ("%d=read(%x, %d)", totalread, ptr, len);
+ len = (size_t) totalread;
+ return;
+}
+
+int
+fhandler_tty_slave::dup (fhandler_base *child)
+{
+ fhandler_tty_slave *arch = (fhandler_tty_slave *) archetype;
+ *(fhandler_tty_slave *) child = *arch;
+ child->usecount = 0;
+ arch->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (child, "duped", "incremented ", "");
+ myself->set_ctty (get_ttyp (), openflags, arch);
+ return 0;
+}
+
+int
+fhandler_tty_common::dup (fhandler_base *child)
+{
+ fhandler_tty_slave *fts = (fhandler_tty_slave *) child;
+ int errind;
+
+ fts->tcinit (get_ttyp ());
+
+ attach_tty (get_unit ());
+
+ 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, input_available_event, hMainProc,
+ &fts->input_available_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 4;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, output_mutex, hMainProc,
+ &fts->output_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 5;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, input_mutex, hMainProc,
+ &fts->input_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 6;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, get_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 7;
+ goto err;
+ }
+ fts->set_io_handle (nh);
+
+ if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 8;
+ 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 = 9;
+ 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, const struct termios *t)
+{
+ acquire_output_mutex (INFINITE);
+ get_ttyp ()->ti = *t;
+ release_output_mutex ();
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ size_t len = UINT_MAX;
+ read (NULL, len);
+ ret = len >= 0;
+ }
+ if (queue == TCOFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+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 == get_unit () && (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);
+ }
+
+ int retval;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ case TIOCLINUX:
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ retval = 0;
+ goto out;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ acquire_output_mutex (INFINITE);
+
+ get_ttyp ()->cmd = cmd;
+ get_ttyp ()->ioctl_retval = 0;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ get_ttyp ()->arg.winsize = get_ttyp ()->winsize;
+ if (ioctl_request_event)
+ SetEvent (ioctl_request_event);
+ *(struct winsize *) arg = get_ttyp ()->arg.winsize;
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->arg.winsize = *(struct winsize *) arg;
+ if (ioctl_request_event)
+ {
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ SetEvent (ioctl_request_event);
+ }
+ else
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ }
+ break;
+ case TIOCLINUX:
+ int val = *(unsigned char *) arg;
+ if (val != 6 || !ioctl_request_event || !ioctl_done_event)
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ else
+ {
+ get_ttyp ()->arg.value = val;
+ SetEvent (ioctl_request_event);
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ *(unsigned char *) arg = get_ttyp ()->arg.value & 0xFF;
+ }
+ break;
+ }
+
+ release_output_mutex ();
+ retval = get_ttyp ()->ioctl_retval;
+ if (retval < 0)
+ {
+ set_errno (-retval);
+ retval = -1;
+ }
+
+out:
+ termios_printf ("%d = ioctl (%x)", retval, cmd);
+ return retval;
+}
+
+/*******************************************************
+ fhandler_pty_master
+*/
+fhandler_pty_master::fhandler_pty_master ()
+ : fhandler_tty_common ()
+{
+}
+
+int
+fhandler_pty_master::open (int flags, mode_t)
+{
+ int ntty = cygwin_shared->tty.allocate_tty (0);
+ if (ntty < 0)
+ return 0;
+
+ slave = *ttys_dev;
+ slave.setunit (ntty);
+ cygwin_shared->tty[ntty]->common_init (this);
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+
+ termios_printf ("opened pty master tty%d", get_unit ());
+ return 1;
+}
+
+int
+fhandler_tty_common::close ()
+{
+ termios_printf ("tty%d <%p,%p> closing", get_unit (), get_handle (), get_output_handle ());
+ 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 (inuse && !CloseHandle (inuse))
+ termios_printf ("CloseHandle (inuse), %E");
+ if (!ForceCloseHandle (input_mutex))
+ termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex);
+ if (!ForceCloseHandle (output_mutex))
+ termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+
+ /* Send EOF to slaves if master side is closed */
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("no more masters left. sending EOF");
+ SetEvent (input_available_event);
+ }
+
+ if (!ForceCloseHandle (input_available_event))
+ termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event);
+ if (!ForceCloseHandle1 (get_handle (), from_pty))
+ termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ());
+ if (!ForceCloseHandle1 (get_output_handle (), to_pty))
+ termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ());
+
+ inuse = NULL;
+ set_io_handle (NULL);
+ return 0;
+}
+
+int
+fhandler_pty_master::close ()
+{
+#if 0
+ while (accept_input () > 0)
+ continue;
+#endif
+ fhandler_tty_common::close ();
+
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("freeing tty%d (%d)", get_unit (), get_ttyp ()->ntty);
+#if 0
+ if (get_ttyp ()->to_slave)
+ ForceCloseHandle1 (get_ttyp ()->to_slave, to_slave);
+ if (get_ttyp ()->from_slave)
+ ForceCloseHandle1 (get_ttyp ()->from_slave, from_slave);
+#endif
+ 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)
+{
+ int i;
+ char *p = (char *) ptr;
+ termios ti = tc->ti;
+
+ for (i = 0; i < (int) len; i++)
+ {
+ line_edit_status status = line_edit (p++, 1, ti);
+ if (status > line_edit_signalled)
+ {
+ if (status != line_edit_pipe_full)
+ i = -1;
+ break;
+ }
+ }
+ return i;
+}
+
+void __stdcall
+fhandler_pty_master::read (void *ptr, size_t& len)
+{
+ len = (size_t) process_slave_output ((char *) ptr, len, pktmode);
+ return;
+}
+
+int
+fhandler_pty_master::tcgetattr (struct termios *t)
+{
+ *t = cygwin_shared->tty[get_unit ()]->ti;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcsetattr (int, const struct termios *t)
+{
+ cygwin_shared->tty[get_unit ()]->ti = *t;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ ret = process_slave_output (NULL, OUT_BUFFER_SIZE, 0);
+ else if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+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:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ return 0;
+}
+
+char *
+fhandler_pty_master::ptsname ()
+{
+ static char buf[32];
+
+ __small_sprintf (buf, "/dev/tty%d", get_unit ());
+ return buf;
+}
+
+void
+fhandler_tty_common::set_close_on_exec (int val)
+{
+ if (archetype)
+ set_close_on_exec_flag (val);
+ else
+ {
+ 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);
+ set_inheritance (input_mutex, val);
+ set_inheritance (input_available_event, val);
+ set_inheritance (output_handle, val);
+#ifndef DEBUGGING
+ fhandler_base::set_close_on_exec (val);
+#else
+ /* FIXME: This is a duplication from fhandler_base::set_close_on_exec.
+ It is here because we need to specify the "from_pty" stuff here or
+ we'll get warnings from ForceCloseHandle when debugging. */
+ set_inheritance (get_io_handle (), val);
+ set_close_on_exec_flag (val);
+#endif
+ }
+}
+
+void
+fhandler_tty_slave::fixup_after_fork (HANDLE parent)
+{
+ // fhandler_tty_common::fixup_after_fork (parent);
+ report_tty_counts (this, "inherited", "", "");
+}
+
+void
+fhandler_tty_common::fixup_after_fork (HANDLE parent)
+{
+ fhandler_termios::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");
+ if (input_mutex)
+ fork_fixup (parent, input_mutex, "input_mutex");
+ if (input_available_event)
+ fork_fixup (parent, input_available_event, "input_available_event");
+ fork_fixup (parent, inuse, "inuse");
+}
+
+void
+fhandler_pty_master::set_close_on_exec (int val)
+{
+ fhandler_tty_common::set_close_on_exec (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 ();
+ termios_printf ("from_slave %p, to_slave %p", get_handle (),
+ get_output_handle ());
+ }
+}
+
+int
+fhandler_tty_master::init_console ()
+{
+ console = (fhandler_console *) build_fh_dev (*console_dev, "/dev/ttym");
+ if (console == NULL)
+ return -1;
+
+ console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY);
+ cygheap->open_fhs--; /* handled when individual fds are opened */
+ console->set_r_no_interrupt (1);
+ return 0;
+}