diff options
author | Christopher Faylor <cgf@redhat.com> | 2004-01-21 06:27:55 +0000 |
---|---|---|
committer | Christopher Faylor <cgf@redhat.com> | 2004-01-21 06:27:55 +0000 |
commit | 456a03998eedde4197b3e87e81a51a810d9f6bf3 (patch) | |
tree | 42612d81e9ad065b8ff113fc2a70fcb1e441a369 | |
parent | eaaabbf0111c67e39e98bbb920dfae004fbc44d8 (diff) | |
download | gdb-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/ChangeLog | 6 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_tty.cc | 1417 |
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; +} |