1919 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1919 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* fhandler_tty.cc
 | |
| 
 | |
|    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
 | |
|    2006, 2007, 2008, 2009, 2010, 2011 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 <stdlib.h>
 | |
| #include <cygwin/kd.h>
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "dtable.h"
 | |
| #include "sigproc.h"
 | |
| #include "pinfo.h"
 | |
| #include "ntdll.h"
 | |
| #include "cygheap.h"
 | |
| #include "shared_info.h"
 | |
| #include "cygthread.h"
 | |
| #include "child_info.h"
 | |
| 
 | |
| #define close_maybe(h) \
 | |
|   do { \
 | |
|     if (h && h != INVALID_HANDLE_VALUE) \
 | |
|       CloseHandle (h); \
 | |
|   } while (0)
 | |
| 
 | |
| /* pty master control pipe messages */
 | |
| struct pipe_request {
 | |
|   DWORD pid;
 | |
| };
 | |
| 
 | |
| struct pipe_reply {
 | |
|   HANDLE from_master;
 | |
|   HANDLE to_master;
 | |
|   DWORD error;
 | |
| };
 | |
| 
 | |
| /* tty master stuff */
 | |
| 
 | |
| fhandler_tty_master NO_COPY *tty_master;
 | |
| 
 | |
| static void WINAPI process_input (void *) __attribute__((noreturn));		// Input queue thread
 | |
| static void WINAPI process_output (void *) __attribute__((noreturn));		// Output queue thread
 | |
| static void WINAPI process_ioctl (void *) __attribute__((noreturn));		// 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 ()
 | |
| {
 | |
|   termios_printf ("Creating master for tty%d", get_unit ());
 | |
| 
 | |
|   if (init_console ())
 | |
|     {
 | |
|       termios_printf ("can't create fhandler");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (!setup (false))
 | |
|     return 1;
 | |
| 
 | |
|   set_winsize (false);
 | |
| 
 | |
|   set_close_on_exec (true);
 | |
| 
 | |
|   new cygthread (process_input, cygself, "ttyin");
 | |
|   new cygthread (process_ioctl, cygself, "ttyioctl");
 | |
|   new cygthread (process_output, cygself, "ttyout");
 | |
| 
 | |
|   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
 | |
|     }
 | |
| #ifdef DEBUGGING
 | |
|   else if (osi > 0)
 | |
|     {
 | |
|       system_printf ("couldn't release output mutex but we seem to own it, %E");
 | |
|       try_to_debug ();
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* Process tty input. */
 | |
| 
 | |
| void
 | |
| fhandler_pty_master::doecho (const void *str, DWORD len)
 | |
| {
 | |
|   acquire_output_mutex (INFINITE);
 | |
|   if (!WriteFile (to_master, str, len, &len, NULL))
 | |
|     termios_printf ("Write to %p failed, %E", to_master);
 | |
| //  WaitForSingleObject (output_done_event, INFINITE);
 | |
|   release_output_mutex ();
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_pty_master::accept_input ()
 | |
| {
 | |
|   DWORD bytes_left;
 | |
|   int ret = 1;
 | |
| 
 | |
|   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 void 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 void 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 void WINAPI
 | |
| process_ioctl (void *)
 | |
| {
 | |
|   while (1)
 | |
|     {
 | |
|       WaitForSingleObject (tty_master->ioctl_request_event, INFINITE);
 | |
|       termios_printf ("ioctl() request");
 | |
|       tty *ttyp = tty_master->get_ttyp ();
 | |
|       ttyp->ioctl_retval =
 | |
|       tty_master->console->ioctl (ttyp->cmd,
 | |
| 				  (ttyp->cmd == KDSKBMETA)
 | |
| 				  ? (void *) ttyp->arg.value
 | |
| 				  : (void *) &ttyp->arg);
 | |
|       SetEvent (tty_master->ioctl_done_event);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**********************************************************************/
 | |
| /* Tty slave stuff */
 | |
| 
 | |
| fhandler_tty_slave::fhandler_tty_slave ()
 | |
|   : fhandler_tty_common (), inuse (NULL)
 | |
| {
 | |
|   uninterruptible_io (true);
 | |
| }
 | |
| 
 | |
| /* FIXME: This function needs to close handles when it has
 | |
|    a failing condition. */
 | |
| int
 | |
| fhandler_tty_slave::open (int flags, mode_t)
 | |
| {
 | |
|   HANDLE tty_owner, from_master_local, to_master_local;
 | |
|   HANDLE *handles[] =
 | |
|   {
 | |
|     &from_master_local, &input_available_event, &input_mutex, &inuse,
 | |
|     &ioctl_done_event, &ioctl_request_event, &output_done_event,
 | |
|     &output_mutex, &to_master_local, &tty_owner,
 | |
|     NULL
 | |
|   };
 | |
| 
 | |
|   const char *errmsg = NULL;
 | |
| 
 | |
|   for (HANDLE **h = handles; *h; h++)
 | |
|     **h = NULL;
 | |
| 
 | |
|   if (get_device () == FH_TTY)
 | |
|     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 fhandler_tty_slave archetype");
 | |
|       set_flags ((flags & ~O_TEXT) | O_BINARY);
 | |
|       cygheap->manage_console_count ("fhandler_tty_slave::open<arch>", 1);
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   tcinit (cygwin_shared->tty[get_unit ()], false);
 | |
| 
 | |
|   cygwin_shared->tty.attach (get_unit ());
 | |
| 
 | |
|   set_flags ((flags & ~O_TEXT) | O_BINARY);
 | |
|   /* Create synchronisation events */
 | |
|   char buf[MAX_PATH];
 | |
| 
 | |
|   /* 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.  */
 | |
|   shared_name (buf, OUTPUT_DONE_EVENT, get_unit ());
 | |
|   output_done_event = OpenEvent (MAXIMUM_ALLOWED, TRUE, buf);
 | |
| 
 | |
|   if (!(output_mutex = get_ttyp ()->open_output_mutex (MAXIMUM_ALLOWED)))
 | |
|     {
 | |
|       errmsg = "open output mutex failed, %E";
 | |
|       goto err;
 | |
|     }
 | |
|   if (!(input_mutex = get_ttyp ()->open_input_mutex (MAXIMUM_ALLOWED)))
 | |
|     {
 | |
|       errmsg = "open input mutex failed, %E";
 | |
|       goto err;
 | |
|     }
 | |
|   shared_name (buf, INPUT_AVAILABLE_EVENT, get_unit ());
 | |
|   if (!(input_available_event = OpenEvent (MAXIMUM_ALLOWED, TRUE, buf)))
 | |
|     {
 | |
|       errmsg = "open input event failed, %E";
 | |
|       goto err;
 | |
|     }
 | |
| 
 | |
|   /* The ioctl events may or may not exist.  See output_done_event,
 | |
|      above.  */
 | |
|   shared_name (buf, IOCTL_REQUEST_EVENT, get_unit ());
 | |
|   ioctl_request_event = OpenEvent (MAXIMUM_ALLOWED, TRUE, buf);
 | |
|   shared_name (buf, IOCTL_DONE_EVENT, get_unit ());
 | |
|   ioctl_done_event = OpenEvent (MAXIMUM_ALLOWED, TRUE, buf);
 | |
| 
 | |
|   /* FIXME: Needs a method to eliminate tty races */
 | |
|   {
 | |
|     /* Create security attribute.  Default permissions are 0620. */
 | |
|     security_descriptor sd;
 | |
|     sd.malloc (sizeof (SECURITY_DESCRIPTOR));
 | |
|     InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
 | |
|     SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
 | |
|     if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
 | |
| 					  S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
 | |
| 					  sd))
 | |
|       sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
 | |
|     acquire_output_mutex (500);
 | |
|     inuse = get_ttyp ()->create_inuse (&sa);
 | |
|     get_ttyp ()->was_opened = true;
 | |
|     release_output_mutex ();
 | |
|   }
 | |
| 
 | |
|   if (!get_ttyp ()->from_master || !get_ttyp ()->to_master)
 | |
|     {
 | |
|       errmsg = "tty handles have been closed";
 | |
|       set_errno (EACCES);
 | |
|       goto err_no_errno;
 | |
|     }
 | |
| 
 | |
|   if (get_ttyp ()->master_pid < 0)
 | |
|     {
 | |
|       errmsg = "*** master is closed";
 | |
|       set_errno (EAGAIN);
 | |
|       goto err_no_errno;
 | |
|     }
 | |
|   /* Three case for duplicating the pipe handles:
 | |
|      - Either we're the master.  In this case, just duplicate the handles.
 | |
|      - Or, we have the right to open the master process for handle duplication.
 | |
|        In this case, just duplicate the handles.
 | |
|      - Or, we have to ask the master process itself.  In this case, send our
 | |
|        pid to the master process and check the reply.  The reply contains
 | |
|        either the handles, or an error code which tells us why we didn't
 | |
|        get the handles. */
 | |
|   if (myself->pid == get_ttyp ()->master_pid)
 | |
|     {
 | |
|       /* This is the most common case, just calling openpty. */
 | |
|       termios_printf ("dup handles within myself.");
 | |
|       tty_owner = GetCurrentProcess ();
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       pinfo p (get_ttyp ()->master_pid);
 | |
|       if (!p)
 | |
| 	termios_printf ("*** couldn't find tty master");
 | |
|       else
 | |
| 	{
 | |
| 	  tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->dwProcessId);
 | |
| 	  if (tty_owner)
 | |
| 	    termios_printf ("dup handles directly since I'm allmighty.");
 | |
| 	}
 | |
|     }
 | |
|   if (tty_owner)
 | |
|     {
 | |
|       if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
 | |
| 			    GetCurrentProcess (), &from_master_local, 0, TRUE,
 | |
| 			    DUPLICATE_SAME_ACCESS))
 | |
| 	{
 | |
| 	  termios_printf ("can't duplicate input from %u/%p, %E",
 | |
| 			  get_ttyp ()->master_pid, get_ttyp ()->from_master);
 | |
| 	  __seterrno ();
 | |
| 	  goto err_no_msg;
 | |
| 	}
 | |
|       if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
 | |
| 			  GetCurrentProcess (), &to_master_local, 0, TRUE,
 | |
| 			  DUPLICATE_SAME_ACCESS))
 | |
| 	{
 | |
| 	  errmsg = "can't duplicate output, %E";
 | |
| 	  goto err;
 | |
| 	}
 | |
|       if (tty_owner != GetCurrentProcess ())
 | |
| 	CloseHandle (tty_owner);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       pipe_request req = { GetCurrentProcessId () };
 | |
|       pipe_reply repl;
 | |
|       DWORD len;
 | |
| 
 | |
|       __small_sprintf (buf, "\\\\.\\pipe\\cygwin-%S-tty%d-master-ctl",
 | |
| 		       &installation_key, get_unit ());
 | |
|       termios_printf ("dup handles via master control pipe %s", buf);
 | |
|       if (!CallNamedPipe (buf, &req, sizeof req, &repl, sizeof repl,
 | |
| 			  &len, 500))
 | |
| 	{
 | |
| 	  errmsg = "can't call master, %E";
 | |
| 	  goto err;
 | |
| 	}
 | |
|       from_master_local = repl.from_master;
 | |
|       to_master_local = repl.to_master;
 | |
|       if (!from_master_local || !to_master_local)
 | |
| 	{
 | |
| 	  SetLastError (repl.error);
 | |
| 	  errmsg = "error duplicating pipes, %E";
 | |
| 	  goto err;
 | |
| 	}
 | |
|     }
 | |
|   VerifyHandle (from_master_local);
 | |
|   VerifyHandle (to_master_local);
 | |
| 
 | |
|   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_close_on_exec (!!(flags & O_CLOEXEC));
 | |
| 
 | |
|   set_open_status ();
 | |
|   if (cygheap->manage_console_count ("fhandler_tty_slave::open", 1) == 1
 | |
|       && !output_done_event)
 | |
|     fhandler_console::need_invisible ();
 | |
| 
 | |
|   // FIXME: Do this better someday
 | |
|   arch = (fhandler_tty_slave *) cmalloc_abort (HEAP_ARCHETYPES, sizeof (*this));
 | |
|   *((fhandler_tty_slave **) cygheap->fdtab.add_archetype ()) = arch;
 | |
|   archetype = arch;
 | |
|   *arch = *this;
 | |
| 
 | |
| out:
 | |
|   usecount = 0;
 | |
|   arch->usecount++;
 | |
|   report_tty_counts (this, "opened", "");
 | |
|   myself->set_ctty (get_ttyp (), flags, arch);
 | |
| 
 | |
|   return 1;
 | |
| 
 | |
| err:
 | |
|   __seterrno ();
 | |
| err_no_errno:
 | |
|   termios_printf (errmsg);
 | |
| err_no_msg:
 | |
|   for (HANDLE **h = handles; *h; h++)
 | |
|     if (**h && **h != INVALID_HANDLE_VALUE)
 | |
|       CloseHandle (**h);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_tty_slave::close ()
 | |
| {
 | |
|   /* This used to always call fhandler_tty_common::close when hExeced but that
 | |
|      caused multiple closes of the handles associated with this tty.  Since
 | |
|      close_all_files is not called until after the cygwin process has synced
 | |
|      or before a non-cygwin process has exited, it should be safe to just
 | |
|      close this normally.  cgf 2006-05-20 */
 | |
|   cygheap->manage_console_count ("fhandler_tty_slave::close", -1);
 | |
| 
 | |
|   archetype->usecount--;
 | |
|   report_tty_counts (this, "closed", "");
 | |
| 
 | |
|   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 ());
 | |
|   if (inuse && !CloseHandle (inuse))
 | |
|     termios_printf ("CloseHandle (inuse), %E");
 | |
|   return fhandler_tty_common::close ();
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_tty_slave::init (HANDLE f, 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;
 | |
| 
 | |
|   int ret = open (flags);
 | |
| 
 | |
|   if (ret && !cygwin_finished_initializing && !being_debugged ())
 | |
|     {
 | |
|       /* This only occurs when called from dtable::init_std_file_from_handle
 | |
| 	 We have been started from a non-Cygwin process.  So we should become
 | |
| 	 tty process group leader.
 | |
| 	 TODO: Investigate how SIGTTIN should be handled with pure-windows
 | |
| 	 programs. */
 | |
|       pinfo p (tc->getpgid ());
 | |
|       /* We should only grab this when the process group owner for this
 | |
| 	 tty is a non-cygwin process or we've been started directly
 | |
| 	 from a non-Cygwin process with no Cygwin ancestry.  */
 | |
|       if (!p || ISSTATE (p, PID_NOTCYGWIN))
 | |
| 	{
 | |
| 	  termios_printf ("Setting process group leader to %d since %W(%d) is not a cygwin process",
 | |
| 			  myself->pgid, p->progname, p->pid);
 | |
| 	  tc->setpgid (myself->pgid);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (f != INVALID_HANDLE_VALUE)
 | |
|     CloseHandle (f);	/* Reopened by open */
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| ssize_t __stdcall
 | |
| 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 = time_to_wait;
 | |
|   while (len)
 | |
|     {
 | |
|       rc = WaitForMultipleObjects (2, w4, FALSE, waiter);
 | |
| 
 | |
|       if (rc == WAIT_TIMEOUT)
 | |
| 	{
 | |
| 	  termios_printf ("wait timed out, waiter %u", waiter);
 | |
| 	  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 && !bytes_in_pipe && !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;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_tty_slave::dup (fhandler_base *child)
 | |
| {
 | |
|   fhandler_tty_slave *arch = (fhandler_tty_slave *) archetype;
 | |
|   /* In dtable::dup_worker, the path_conv member has already been assigned
 | |
|      from "this" to "child".  Part of this assigment (path_conv::operator=)
 | |
|      is to allocate memory for the strings "path" and "normalized_path from
 | |
|      cygheap.  The below `child = *arch' statement will overwrite child's
 | |
|      path_conv again, this time from "*arch".  By doing that, it will allocate
 | |
|      new strings from cygheap, overwriting the old pointer values.  Thus, the
 | |
|      old allocated strings are lost, and we're leaking memory for each tty dup,
 | |
|      unless we free the strings here.
 | |
|      FIXME: We can't redefine path_conv::operator= so that it frees the old
 | |
|      strings.  Probably it would be most helpful to copy only the required
 | |
|      members from *arch, rather than copying everything. */
 | |
|   child->pc.free_strings ();
 | |
|   *(fhandler_tty_slave *) child = *arch;
 | |
|   child->set_flags (get_flags ());
 | |
|   child->usecount = 0;
 | |
|   arch->usecount++;
 | |
|   cygheap->manage_console_count ("fhandler_tty_slave::dup", 1);
 | |
|   report_tty_counts (child, "duped", "");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_pty_master::dup (fhandler_base *child)
 | |
| {
 | |
|   fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
 | |
|   /* See comment in fhandler_tty_slave::dup. */
 | |
|   child->pc.free_strings ();
 | |
|   *(fhandler_tty_master *) child = *arch;
 | |
|   child->set_flags (get_flags ());
 | |
|   child->usecount = 0;
 | |
|   arch->usecount++;
 | |
|   report_tty_counts (child, "duped master", "");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 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 = ((int) len) >= 0 ? 0 : -1;
 | |
|     }
 | |
|   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, %s", myself->pgid,
 | |
| 		      get_ttyp ()->getpgid (), myctty ());
 | |
|       raise (SIGTTOU);
 | |
|     }
 | |
| 
 | |
|   int retval;
 | |
|   switch (cmd)
 | |
|     {
 | |
|     case TIOCGWINSZ:
 | |
|     case TIOCSWINSZ:
 | |
|     case TIOCLINUX:
 | |
|     case KDGKBMETA:
 | |
|     case KDSKBMETA:
 | |
|       break;
 | |
|     case FIONBIO:
 | |
|       set_nonblocking (*(int *) arg);
 | |
|       retval = 0;
 | |
|       goto out;
 | |
|     case TIOCGPGRP:
 | |
|       {
 | |
| 	pid_t pid = this->tcgetpgrp ();
 | |
| 	if (pid < 0)
 | |
| 	  retval = -1;
 | |
| 	else
 | |
| 	  {
 | |
| 	    *((pid_t *) arg) = pid;
 | |
| 	    retval = 0;
 | |
| 	  }
 | |
|       }
 | |
|       goto out;
 | |
|     case TIOCSPGRP:
 | |
|       retval = this->tcsetpgrp ((pid_t) arg);
 | |
|       goto out;
 | |
|     default:
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   acquire_output_mutex (INFINITE);
 | |
| 
 | |
|   get_ttyp ()->cmd = cmd;
 | |
|   get_ttyp ()->ioctl_retval = 0;
 | |
|   int val;
 | |
|   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;
 | |
| 	      killsys (-get_ttyp ()->getpgid (), SIGWINCH);
 | |
| 	    }
 | |
| 	  if (ioctl_done_event)
 | |
| 	    WaitForSingleObject (ioctl_done_event, INFINITE);
 | |
| 	}
 | |
|       break;
 | |
|     case TIOCLINUX:
 | |
|       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 = (unsigned char) (get_ttyp ()->arg.value);
 | |
| 	}
 | |
|       break;
 | |
|     case KDGKBMETA:
 | |
|       if (ioctl_request_event)
 | |
| 	{
 | |
| 	  SetEvent (ioctl_request_event);
 | |
| 	  if (ioctl_done_event)
 | |
| 	    WaitForSingleObject (ioctl_done_event, INFINITE);
 | |
| 	  *(int *) arg = get_ttyp ()->arg.value;
 | |
| 	}
 | |
|       else
 | |
| 	get_ttyp ()->ioctl_retval = -EINVAL;
 | |
|       break;
 | |
|     case KDSKBMETA:
 | |
|       if (ioctl_request_event)
 | |
| 	{
 | |
| 	  get_ttyp ()->arg.value = (int) arg;
 | |
| 	  SetEvent (ioctl_request_event);
 | |
| 	  if (ioctl_done_event)
 | |
| 	    WaitForSingleObject (ioctl_done_event, INFINITE);
 | |
| 	}
 | |
|       else
 | |
| 	get_ttyp ()->ioctl_retval = -EINVAL;
 | |
|       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;
 | |
| }
 | |
| 
 | |
| int __stdcall
 | |
| fhandler_tty_slave::fstat (struct __stat64 *st)
 | |
| {
 | |
|   fhandler_base::fstat (st);
 | |
| 
 | |
|   bool to_close = false;
 | |
|   if (!input_available_event)
 | |
|     {
 | |
|       char buf[MAX_PATH];
 | |
|       shared_name (buf, INPUT_AVAILABLE_EVENT, get_unit ());
 | |
|       input_available_event = OpenEvent (READ_CONTROL, TRUE, buf);
 | |
|       if (input_available_event)
 | |
| 	to_close = true;
 | |
|     }
 | |
|   if (!input_available_event
 | |
|       || get_object_attribute (input_available_event, &st->st_uid, &st->st_gid,
 | |
| 			       &st->st_mode))
 | |
|     {
 | |
|       /* If we can't access the ACL, or if the tty doesn't actually exist,
 | |
|          then fake uid and gid to strict, system-like values. */
 | |
|       st->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
 | |
|       st->st_uid = 18;
 | |
|       st->st_gid = 544;
 | |
|     }
 | |
|   if (to_close)
 | |
|     CloseHandle (input_available_event);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Helper function for fchmod and fchown, which just opens all handles
 | |
|    and signals success via bool return. */
 | |
| bool
 | |
| fhandler_tty_slave::fch_open_handles ()
 | |
| {
 | |
|   char buf[MAX_PATH];
 | |
| 
 | |
|   tc = cygwin_shared->tty[get_unit ()];
 | |
|   shared_name (buf, INPUT_AVAILABLE_EVENT, get_unit ());
 | |
|   input_available_event = OpenEvent (READ_CONTROL | WRITE_DAC | WRITE_OWNER,
 | |
| 				     TRUE, buf);
 | |
|   output_mutex = get_ttyp ()->open_output_mutex (WRITE_DAC | WRITE_OWNER);
 | |
|   input_mutex = get_ttyp ()->open_input_mutex (WRITE_DAC | WRITE_OWNER);
 | |
|   inuse = get_ttyp ()->open_inuse (WRITE_DAC | WRITE_OWNER);
 | |
|   if (!input_available_event || !output_mutex || !input_mutex || !inuse)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return false;
 | |
|     }
 | |
|   /* These members are optional, no error checking */
 | |
|   shared_name (buf, OUTPUT_DONE_EVENT, get_unit ());
 | |
|   output_done_event = OpenEvent (WRITE_DAC | WRITE_OWNER, TRUE, buf);
 | |
|   shared_name (buf, IOCTL_REQUEST_EVENT, get_unit ());
 | |
|   ioctl_request_event = OpenEvent (WRITE_DAC | WRITE_OWNER, TRUE, buf);
 | |
|   shared_name (buf, IOCTL_DONE_EVENT, get_unit ());
 | |
|   ioctl_done_event = OpenEvent (WRITE_DAC | WRITE_OWNER, TRUE, buf);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* Helper function for fchmod and fchown, which sets the new security
 | |
|    descriptor on all objects representing the tty. */
 | |
| int
 | |
| fhandler_tty_slave::fch_set_sd (security_descriptor &sd, bool chown)
 | |
| {
 | |
|   security_descriptor sd_old;
 | |
| 
 | |
|   get_object_sd (input_available_event, sd_old);
 | |
|   if (!set_object_sd (input_available_event, sd, chown)
 | |
|       && !set_object_sd (output_mutex, sd, chown)
 | |
|       && !set_object_sd (input_mutex, sd, chown)
 | |
|       && !set_object_sd (inuse, sd, chown)
 | |
|       && (!output_done_event
 | |
| 	  || !set_object_sd (output_done_event, sd, chown))
 | |
|       && (!ioctl_request_event
 | |
| 	  || !set_object_sd (ioctl_request_event, sd, chown))
 | |
|       && (!ioctl_done_event
 | |
| 	  || !set_object_sd (ioctl_done_event, sd, chown)))
 | |
|     return 0;
 | |
|   set_object_sd (input_available_event, sd_old, chown);
 | |
|   set_object_sd (output_mutex, sd_old, chown);
 | |
|   set_object_sd (input_mutex, sd_old, chown);
 | |
|   set_object_sd (inuse, sd_old, chown);
 | |
|   if (!output_done_event)
 | |
|       set_object_sd (output_done_event, sd_old, chown);
 | |
|   if (!ioctl_request_event)
 | |
|       set_object_sd (ioctl_request_event, sd_old, chown);
 | |
|   if (!ioctl_done_event)
 | |
|       set_object_sd (ioctl_done_event, sd_old, chown);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /* Helper function for fchmod and fchown, which closes all object handles in
 | |
|    the tty. */
 | |
| void
 | |
| fhandler_tty_slave::fch_close_handles ()
 | |
| {
 | |
|   close_maybe (get_io_handle ());
 | |
|   close_maybe (get_output_handle ());
 | |
|   close_maybe (output_done_event);
 | |
|   close_maybe (ioctl_done_event);
 | |
|   close_maybe (ioctl_request_event);
 | |
|   close_maybe (input_available_event);
 | |
|   close_maybe (output_mutex);
 | |
|   close_maybe (input_mutex);
 | |
|   close_maybe (inuse);
 | |
| }
 | |
| 
 | |
| int __stdcall
 | |
| fhandler_tty_slave::fchmod (mode_t mode)
 | |
| {
 | |
|   int ret = -1;
 | |
|   bool to_close = false;
 | |
|   security_descriptor sd;
 | |
|   __uid32_t uid;
 | |
|   __gid32_t gid;
 | |
| 
 | |
|   if (!input_available_event)
 | |
|     {
 | |
|       to_close = true;
 | |
|       if (!fch_open_handles ())
 | |
| 	goto errout;
 | |
|     }
 | |
|   sd.malloc (sizeof (SECURITY_DESCRIPTOR));
 | |
|   InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
 | |
|   if (!get_object_attribute (input_available_event, &uid, &gid, NULL)
 | |
|       && !create_object_sd_from_attribute (NULL, uid, gid, S_IFCHR | mode, sd))
 | |
|     ret = fch_set_sd (sd, false);
 | |
| errout:
 | |
|   if (to_close)
 | |
|     fch_close_handles ();
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int __stdcall
 | |
| fhandler_tty_slave::fchown (__uid32_t uid, __gid32_t gid)
 | |
| {
 | |
|   int ret = -1;
 | |
|   bool to_close = false;
 | |
|   mode_t mode = 0;
 | |
|   __uid32_t o_uid;
 | |
|   __gid32_t o_gid;
 | |
|   security_descriptor sd;
 | |
| 
 | |
|   if (uid == ILLEGAL_UID && gid == ILLEGAL_GID)
 | |
|     return 0;
 | |
|   if (!input_available_event)
 | |
|     {
 | |
|       to_close = true;
 | |
|       if (!fch_open_handles ())
 | |
| 	goto errout;
 | |
|     }
 | |
|   sd.malloc (sizeof (SECURITY_DESCRIPTOR));
 | |
|   InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
 | |
|   if (!get_object_attribute (input_available_event, &o_uid, &o_gid, &mode))
 | |
|     {
 | |
|       if ((uid == ILLEGAL_UID || uid == o_uid)
 | |
| 	  && (gid == ILLEGAL_GID || gid == o_gid))
 | |
| 	ret = 0;
 | |
|       else if (!create_object_sd_from_attribute (input_available_event,
 | |
| 						 uid, gid, S_IFCHR | mode, sd))
 | |
| 	ret = fch_set_sd (sd, true);
 | |
|     }
 | |
| errout:
 | |
|   if (to_close)
 | |
|     fch_close_handles ();
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*******************************************************
 | |
|  fhandler_pty_master
 | |
| */
 | |
| fhandler_pty_master::fhandler_pty_master ()
 | |
|   : fhandler_tty_common (), pktmode (0), need_nl (0), dwProcessId (0)
 | |
| {
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_pty_master::open (int flags, mode_t)
 | |
| {
 | |
|   /* Note that allocate returns with the tty lock set if it was successful. */
 | |
|   int ntty = cygwin_shared->tty.allocate (false);
 | |
|   if (ntty < 0)
 | |
|     return 0;
 | |
| 
 | |
|   dev().devn = FHDEV (DEV_TTYM_MAJOR, ntty);
 | |
|   if (!setup (true))
 | |
|     {
 | |
|       lock_ttys::release ();
 | |
|       return 0;
 | |
|     }
 | |
|   lock_ttys::release ();
 | |
|   set_flags ((flags & ~O_TEXT) | O_BINARY);
 | |
|   set_open_status ();
 | |
| 
 | |
|   /* FIXME: Do this better someday */
 | |
|   fhandler_pty_master *arch = (fhandler_tty_master *) cmalloc_abort (HEAP_ARCHETYPES, sizeof (*this));
 | |
|   *((fhandler_pty_master **) cygheap->fdtab.add_archetype ()) = arch;
 | |
|   archetype = arch;
 | |
|   *arch = *this;
 | |
|   arch->dwProcessId = GetCurrentProcessId ();
 | |
| 
 | |
|   usecount = 0;
 | |
|   arch->usecount++;
 | |
|   char buf[sizeof ("opened pty master for ttyNNNNNNNNNNN")];
 | |
|   __small_sprintf (buf, "opened pty master for tty%d", get_unit ());
 | |
|   report_tty_counts (this, buf, "");
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| _off64_t
 | |
| fhandler_tty_common::lseek (_off64_t, int)
 | |
| {
 | |
|   set_errno (ESPIPE);
 | |
|   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 (!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);
 | |
|   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 ());
 | |
| 
 | |
|   if (!ForceCloseHandle (input_available_event))
 | |
|     termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_pty_master::close ()
 | |
| {
 | |
| #if 0
 | |
|   while (accept_input () > 0)
 | |
|     continue;
 | |
| #endif
 | |
|   archetype->usecount--;
 | |
|   report_tty_counts (this, "closing master", "");
 | |
| 
 | |
|   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;
 | |
|     }
 | |
| 
 | |
|   fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
 | |
|   termios_printf ("closing from_master(%p)/to_master(%p) since we own them(%d)",
 | |
| 		  arch->from_master, arch->to_master, arch->dwProcessId);
 | |
|   if (cygwin_finished_initializing)
 | |
|     {
 | |
|       if (arch->master_ctl && get_ttyp ()->master_pid == myself->pid)
 | |
| 	{
 | |
| 	  char buf[MAX_PATH];
 | |
| 	  pipe_request req = { (DWORD) -1 };
 | |
| 	  pipe_reply repl;
 | |
| 	  DWORD len;
 | |
| 
 | |
| 	  __small_sprintf (buf, "\\\\.\\pipe\\cygwin-%S-tty%d-master-ctl",
 | |
| 			   &installation_key, get_unit ());
 | |
| 	  CallNamedPipe (buf, &req, sizeof req, &repl, sizeof repl, &len, 500);
 | |
| 	  CloseHandle (arch->master_ctl);
 | |
| 	  arch->master_thread->detach ();
 | |
| 	}
 | |
|       if (!ForceCloseHandle (arch->from_master))
 | |
| 	termios_printf ("error closing from_master %p, %E", arch->from_master);
 | |
|       if (!ForceCloseHandle (arch->to_master))
 | |
| 	termios_printf ("error closing from_master %p, %E", arch->to_master);
 | |
|     }
 | |
|   fhandler_tty_common::close ();
 | |
| 
 | |
|   if (hExeced || get_ttyp ()->master_pid != myself->pid)
 | |
|     termios_printf ("not clearing: %d, master_pid %d", hExeced, get_ttyp ()->master_pid);
 | |
|   else
 | |
|     get_ttyp ()->set_master_closed ();
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| ssize_t __stdcall
 | |
| 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);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 	  killsys (-get_ttyp ()->getpgid (), SIGWINCH);
 | |
| 	}
 | |
|       break;
 | |
|     case TIOCGPGRP:
 | |
|       *((pid_t *) arg) = this->tcgetpgrp ();
 | |
|       break;
 | |
|     case TIOCSPGRP:
 | |
|       return this->tcsetpgrp ((pid_t) arg);
 | |
|     case FIONBIO:
 | |
|       set_nonblocking (*(int *) arg);
 | |
|       break;
 | |
|     default:
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| char *
 | |
| fhandler_pty_master::ptsname ()
 | |
| {
 | |
|   static char buf[TTY_NAME_MAX];
 | |
| 
 | |
|   __small_sprintf (buf, "/dev/tty%d", get_unit ());
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_tty_common::set_close_on_exec (bool val)
 | |
| {
 | |
|   // Cygwin processes will handle this specially on exec.
 | |
|   close_on_exec (val);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_tty_slave::fixup_after_fork (HANDLE parent)
 | |
| {
 | |
|   // fork_fixup (parent, inuse, "inuse");
 | |
|   // fhandler_tty_common::fixup_after_fork (parent);
 | |
|   report_tty_counts (this, "inherited", "");
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_tty_slave::fixup_after_exec ()
 | |
| {
 | |
|   if (!close_on_exec ())
 | |
|     fixup_after_fork (NULL);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_tty_master::init_console ()
 | |
| {
 | |
|   console = (fhandler_console *) build_fh_dev (*console_dev, "/dev/ttym");
 | |
|   if (console == NULL)
 | |
|     return -1;
 | |
| 
 | |
|   console->init (NULL, GENERIC_READ | GENERIC_WRITE, O_BINARY);
 | |
|   cygheap->manage_console_count ("fhandler_tty_master::init_console", -1, true);
 | |
|   console->uninterruptible_io (true);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C" BOOL WINAPI GetNamedPipeClientProcessId (HANDLE, PULONG);
 | |
| 
 | |
| /* This thread function handles the master control pipe.  It waits for a
 | |
|    client to connect.  Then it checks if the client process has permissions
 | |
|    to access the tty handles.  If so, it opens the client process and
 | |
|    duplicates the handles into that process.  If that fails, it sends a reply
 | |
|    with at least one handle set to NULL and an error code.  Last but not
 | |
|    least, the client is disconnected and the thread waits for the next client.
 | |
| 
 | |
|    A special case is when the master side of the tty is about to be closed.
 | |
|    The client side is the fhandler_pty_master::close function and it sends
 | |
|    a PID -1 in that case.  On Vista and later a check is performed that the
 | |
|    request to leave really comes from the master process itself.  On earlier
 | |
|    OSes there's no function to check for the PID of the client process so
 | |
|    we have to trust the client side.
 | |
| 
 | |
|    Since there's always only one pipe instance, there's a chance that clients
 | |
|    have to wait to connect to the master control pipe.  Therefore the client
 | |
|    calls to CallNamedPipe should have a big enough timeout value.  For now this
 | |
|    is 500ms.  Hope that's enough. */
 | |
| 
 | |
| DWORD
 | |
| fhandler_pty_master::pty_master_thread ()
 | |
| {
 | |
|   bool exit = false;
 | |
|   GENERIC_MAPPING map = { EVENT_QUERY_STATE, EVENT_MODIFY_STATE, 0,
 | |
| 			  EVENT_QUERY_STATE | EVENT_MODIFY_STATE };
 | |
|   pipe_request req;
 | |
|   DWORD len;
 | |
|   security_descriptor sd;
 | |
|   HANDLE token;
 | |
|   PRIVILEGE_SET ps;
 | |
|   BOOL ret;
 | |
|   DWORD pid;
 | |
| 
 | |
|   termios_printf ("Entered");
 | |
|   while (!exit && (ConnectNamedPipe (master_ctl, NULL) || GetLastError () == ERROR_PIPE_CONNECTED))
 | |
|     {
 | |
|       pipe_reply repl = { NULL, NULL, 0 };
 | |
|       bool deimp = false;
 | |
|       BOOL allow = FALSE;
 | |
|       ACCESS_MASK access = EVENT_MODIFY_STATE;
 | |
|       HANDLE client = NULL;
 | |
| 
 | |
|       if (!ReadFile (master_ctl, &req, sizeof req, &len, NULL))
 | |
|       	{
 | |
| 	  termios_printf ("ReadFile, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       /* This function is only available since Vista, unfortunately.
 | |
| 	 In earlier OSes we simply have to believe that the client
 | |
| 	 has no malicious intent (== sends arbitrary PIDs). */
 | |
|       if (!GetNamedPipeClientProcessId (master_ctl, &pid))
 | |
| 	pid = req.pid;
 | |
|       if (get_object_sd (input_available_event, sd))
 | |
| 	{
 | |
| 	  termios_printf ("get_object_sd, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       cygheap->user.deimpersonate ();
 | |
|       deimp = true;
 | |
|       if (!ImpersonateNamedPipeClient (master_ctl))
 | |
| 	{
 | |
| 	  termios_printf ("ImpersonateNamedPipeClient, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       if (!OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE, &token))
 | |
| 	{
 | |
| 	  termios_printf ("OpenThreadToken, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       len = sizeof ps;
 | |
|       ret = AccessCheck (sd, token, access, &map, &ps, &len, &access, &allow);
 | |
|       CloseHandle (token);
 | |
|       if (!ret)
 | |
| 	{
 | |
| 	  termios_printf ("AccessCheck, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       if (!RevertToSelf ())
 | |
| 	{
 | |
| 	  termios_printf ("RevertToSelf, %E");
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       if (req.pid == (DWORD) -1)	/* Request to finish thread. */
 | |
| 	{
 | |
| 	  /* Pre-Vista: Just believe in the good of the client process.
 | |
| 	     Post-Vista: Check if the requesting process is the master
 | |
| 	     process itself. */
 | |
| 	  if (pid == (DWORD) -1 || pid == GetCurrentProcessId ())
 | |
| 	    exit = true;
 | |
| 	  goto reply;
 | |
| 	}
 | |
|       if (allow)
 | |
| 	{
 | |
| 	  client = OpenProcess (PROCESS_DUP_HANDLE, FALSE, pid);
 | |
| 	  if (!client)
 | |
| 	    {
 | |
| 	      termios_printf ("OpenProcess, %E");
 | |
| 	      goto reply;
 | |
| 	    }
 | |
| 	  if (!DuplicateHandle (GetCurrentProcess (), from_master,
 | |
| 			       client, &repl.from_master,
 | |
| 			       0, TRUE, DUPLICATE_SAME_ACCESS))
 | |
| 	    {
 | |
| 	      termios_printf ("DuplicateHandle (from_master), %E");
 | |
| 	      goto reply;
 | |
| 	    }
 | |
| 	  if (!DuplicateHandle (GetCurrentProcess (), to_master,
 | |
| 				client, &repl.to_master,
 | |
| 				0, TRUE, DUPLICATE_SAME_ACCESS))
 | |
| 	    {
 | |
| 	      termios_printf ("DuplicateHandle (to_master), %E");
 | |
| 	      goto reply;
 | |
| 	    }
 | |
| 	}
 | |
| reply:
 | |
|       repl.error = GetLastError ();
 | |
|       if (client)
 | |
| 	CloseHandle (client);
 | |
|       if (deimp)
 | |
| 	cygheap->user.reimpersonate ();
 | |
|       sd.free ();
 | |
|       termios_printf ("Reply: from %p, to %p, error %lu",
 | |
| 		      repl.from_master, repl.to_master, repl.error );
 | |
|       if (!WriteFile (master_ctl, &repl, sizeof repl, &len, NULL))
 | |
| 	termios_printf ("WriteFile, %E");
 | |
|       if (!DisconnectNamedPipe (master_ctl))
 | |
| 	termios_printf ("DisconnectNamedPipe, %E");
 | |
|     }
 | |
|   termios_printf ("Leaving");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static DWORD WINAPI
 | |
| pty_master_thread (VOID *arg)           
 | |
| { 
 | |
|   return ((fhandler_pty_master *) arg)->pty_master_thread ();
 | |
| } 
 | |
| 
 | |
| bool
 | |
| fhandler_pty_master::setup (bool ispty)
 | |
| {
 | |
|   int res;
 | |
|   security_descriptor sd;
 | |
|   SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
 | |
| 
 | |
|   tty& t = *cygwin_shared->tty[get_unit ()];
 | |
| 
 | |
|   tcinit (&t, true);		/* Set termios information.  Force initialization. */
 | |
| 
 | |
|   const char *errstr = NULL;
 | |
|   DWORD pipe_mode = PIPE_NOWAIT;
 | |
| 
 | |
|   /* Create communication pipes */
 | |
|   char pipename[sizeof("ttyNNNN-from-master")];
 | |
|   __small_sprintf (pipename, "tty%d-from-master", get_unit ());
 | |
|   res = fhandler_pipe::create_selectable (ispty ? &sec_none : &sec_none_nih,
 | |
| 					  from_master, get_output_handle (),
 | |
| 					  128 * 1024, pipename);
 | |
|   if (res)
 | |
|     {
 | |
|       errstr = "input pipe";
 | |
|       goto err;
 | |
|     }
 | |
| 
 | |
|   if (!SetNamedPipeHandleState (get_output_handle (), &pipe_mode, NULL, NULL))
 | |
|     termios_printf ("can't set output_handle(%p) to non-blocking mode",
 | |
| 		    get_output_handle ());
 | |
| 
 | |
|   __small_sprintf (pipename, "tty%d-to-master", get_unit ());
 | |
|   res = fhandler_pipe::create_selectable (ispty ? &sec_none : &sec_none_nih,
 | |
| 					  get_io_handle (), to_master,
 | |
| 					  128 * 1024, pipename);
 | |
|   if (res)
 | |
|     {
 | |
|       errstr = "output pipe";
 | |
|       goto err;
 | |
|     }
 | |
| 
 | |
|   need_nl = 0;
 | |
| 
 | |
|   /* Create security attribute.  Default permissions are 0620. */
 | |
|   sd.malloc (sizeof (SECURITY_DESCRIPTOR));
 | |
|   InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
 | |
|   if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
 | |
| 					S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
 | |
| 					sd))
 | |
|     sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
 | |
| 
 | |
|   /* Create synchronisation events */
 | |
| 
 | |
|   if (!ispty)
 | |
|     {
 | |
|       if (!(output_done_event = t.get_event (errstr = OUTPUT_DONE_EVENT, &sa)))
 | |
| 	goto err;
 | |
|       if (!(ioctl_done_event = t.get_event (errstr = IOCTL_DONE_EVENT, &sa)))
 | |
| 	goto err;
 | |
|       if (!(ioctl_request_event = t.get_event (errstr = IOCTL_REQUEST_EVENT,
 | |
| 					       &sa)))
 | |
| 	goto err;
 | |
|     }
 | |
| 
 | |
|   /* Carefully check that the input_available_event didn't already exist.
 | |
|      This is a measure to make sure that the event security descriptor
 | |
|      isn't occupied by a malicious process.  We must make sure that the
 | |
|      event's security descriptor is what we expect it to be. */
 | |
|   if (!(input_available_event = t.get_event (errstr = INPUT_AVAILABLE_EVENT,
 | |
| 					     &sa, TRUE))
 | |
|       || GetLastError () == ERROR_ALREADY_EXISTS)
 | |
|     goto err;
 | |
| 
 | |
|   char buf[MAX_PATH];
 | |
|   errstr = shared_name (buf, OUTPUT_MUTEX, t.ntty);
 | |
|   if (!(output_mutex = CreateMutex (&sa, FALSE, buf)))
 | |
|     goto err;
 | |
| 
 | |
|   errstr = shared_name (buf, INPUT_MUTEX, t.ntty);
 | |
|   if (!(input_mutex = CreateMutex (&sa, FALSE, buf)))
 | |
|     goto err;
 | |
| 
 | |
|   /* Create master control pipe which allows the master to duplicate
 | |
|      the pty pipe handles to processes which deserve it. */
 | |
|   __small_sprintf (buf, "\\\\.\\pipe\\cygwin-%S-tty%d-master-ctl",
 | |
| 		   &installation_key, get_unit ());
 | |
|   master_ctl = CreateNamedPipe (buf, PIPE_ACCESS_DUPLEX,
 | |
| 				PIPE_WAIT | PIPE_TYPE_MESSAGE
 | |
| 				| PIPE_READMODE_MESSAGE, 1, 4096, 4096,
 | |
| 				0, &sec_all_nih);
 | |
|   if (master_ctl == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       errstr = "pty master control pipe";
 | |
|       goto err;
 | |
|     }
 | |
|   master_thread = new cygthread (::pty_master_thread, this, "pty_master");
 | |
|   if (!master_thread)
 | |
|     {
 | |
|       errstr = "pty master control thread";
 | |
|       goto err;
 | |
|     }
 | |
| 
 | |
|   t.from_master = from_master;
 | |
|   t.to_master = to_master;
 | |
|   // /* screws up tty master */ ProtectHandle1INH (output_mutex, output_mutex);
 | |
|   // /* screws up tty master */ ProtectHandle1INH (input_mutex, input_mutex);
 | |
|   t.winsize.ws_col = 80;
 | |
|   t.winsize.ws_row = 25;
 | |
|   t.master_pid = myself->pid;
 | |
| 
 | |
|   termios_printf ("tty%d opened - from_slave %p, to_slave %p", t.ntty,
 | |
| 		  get_io_handle (), get_output_handle ());
 | |
|   return true;
 | |
| 
 | |
| err:
 | |
|   __seterrno ();
 | |
|   close_maybe (get_io_handle ());
 | |
|   close_maybe (get_output_handle ());
 | |
|   close_maybe (output_done_event);
 | |
|   close_maybe (ioctl_done_event);
 | |
|   close_maybe (ioctl_request_event);
 | |
|   close_maybe (input_available_event);
 | |
|   close_maybe (output_mutex);
 | |
|   close_maybe (input_mutex);
 | |
|   close_maybe (from_master);
 | |
|   close_maybe (to_master);
 | |
|   close_maybe (master_ctl);
 | |
|   termios_printf ("tty%d open failed - failed to create %s", errstr);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_pty_master::fixup_after_fork (HANDLE parent)
 | |
| {
 | |
|   DWORD wpid = GetCurrentProcessId ();
 | |
|   fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
 | |
|   if (arch->dwProcessId != wpid)
 | |
|     {
 | |
|       tty& t = *get_ttyp ();
 | |
|       if (!DuplicateHandle (parent, arch->from_master, GetCurrentProcess (),
 | |
| 			    &arch->from_master, 0, false, DUPLICATE_SAME_ACCESS))
 | |
| 	system_printf ("couldn't duplicate from_parent(%p), %E", arch->from_master);
 | |
|       if (!DuplicateHandle (parent, arch->to_master, GetCurrentProcess (),
 | |
| 			    &arch->to_master, 0, false, DUPLICATE_SAME_ACCESS))
 | |
| 	system_printf ("couldn't duplicate to_parent(%p), %E", arch->from_master);
 | |
|       if (myself->pid == t.master_pid)
 | |
| 	{
 | |
| 	  t.from_master = arch->from_master;
 | |
| 	  t.to_master = arch->to_master;
 | |
| 	}
 | |
|       arch->dwProcessId = wpid;
 | |
|     }
 | |
|   from_master = arch->from_master;
 | |
|   to_master = arch->to_master;
 | |
|   report_tty_counts (this, "inherited master", "");
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_pty_master::fixup_after_exec ()
 | |
| {
 | |
|   if (!close_on_exec ())
 | |
|     fixup_after_fork (spawn_info->parent);
 | |
|   else
 | |
|     from_master = to_master = NULL;
 | |
| }
 |