4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-18 04:19:21 +08:00
newlib-cygwin/winsup/cygwin/fhandler_tty.cc
Robert Collins f449bfef40 2002-02-28 Robert Collins <rbtcollins@hotmail.com>
* Merged cygwin_daemon into head minus the new shm and ipc exports.

2002-02-28  Robert Collins  <rbtcollins@hotmail.com>

	* fhandler_tty.cc (fhandler_tty_slave::open): More debugging.
	(fhandler_tty_slave::read): Fix printf type for the handle.
	* tty.cc (tty::common_init): Add a FIXME for security.

2002-01-29  Robert Collins  <rbtcollins@hotmail.com>

	* Makefile.in (OBJS): Remove duplicate localtime.o.

2002-01-17  Robert Collins  <rbtcollins@hotmail.com>

	* cygserver.cc (check_and_dup_handle): Consolidate the two variants for
	simplicity.
	Add Some basic debug output.
	(client_request_attach_tty::serve): Use the new debug_printf for clarity.
	Mark the duplicated handles as inheritable - fixup_after_fork() doesn't reopen
	tty's.

2002-01-16  Robert Collins  <rbtcollins@hotmail.com>

	* cygserver.cc (transport): Correct scope.
	(client_request_attach_tty::serve): Add more debug information.
	Fix erroneous use of transport instead of conn.
	* cygserver_transport_pipes.cc (transport_layer_pipes::close): More debug.
	(transport_layer_pipes::read): Ditto.
	(transport_layer_pipes::write): Ditto.
	(transport_layer_pipes::impersonate_client): Ditto.

Mon Oct  8  7:41:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* cygserver.cc (server_request::process): Rename client_request_shm_get to
	client_request_shm.
	* cygserver_process.cc (process_cache::add): Rename to add_task.
	Use process_cleanup instead of process_request.
	(process_cache::remove_process): New method.
	(process::process): Initialize new members.
	(process::~process): New member.
	(process::cleanup): New method.
	(process::add_cleanup_routine): New method.
	(process_request::process): Rename to process_cleanup.
	Call the process object's cleanup method and then delete it.
	(process_process_param::request_loop): Remove the signalling process.
	* cygserver_shm.cc: Globally rename client_request_shm_get to client_request_shm.
	(client_request_shm_get::serve): Handle attach request counting.
	* cygserver_shm.h: Globally rename client_request_shm_get to client_request_shm.
	(class shm_cleanup): New class.
	* shm.cc: Globally rename client_request_shm_get to client_request_shm.
	(client_request_shm::client_request_shm): New constructor for attach requests.
	(shmat): Use it.
	* include/cygwin/cygserver_process.h (class process_request): Rename to
	process_cleanup.
	(class cleanup_routine): New class.
	(class process): New members and methods to allow calling back when the process
	terminates.

Thu Oct  4 14:12:00 2001  Robert Collins <rbtcollins@hotmail.com>
	* cygserver.cc (request_loop): Make static.
	(main): Use new cache constructor syntax.
	Start cache worker threads.
	Cleanup the cache at shutdown.
	* cygserver_process.cc: Run indent.
	(process_cache::process_cache): Add a trigger to use when adding a process.
	(process_cache::process): Move process_entry to process.
	Insert at the end of the list.
	Trigger the request loop when new process's inserted.
	(process_cache::process_requests): Do it.
	(process_cache::add): New method.
	(process_cache::handle_snapshot): New method.
	(process::process): Merge in the process_entry fields.
	(process::handle): Make a stub function.
	(process::exit_code): New method.
	(process_request::process): New method.
	(process_process_param::request_loop): New method.
	* cygserver_shm.cc: New header dependency - threaded_queue.h.
	* threaded_queue.cc (threaded_queue::cleanup): Clearer messages.
	(queue_process_param::stop): Short spinlock on interruptible threads.
	* threaded_queue.h (class threaded_queue): New constructor.
	* include/cygwin/cygserver_process.h (process_request): New class.
	(process_entry): Remove.
	(process): Merge in process_entry.
	(process_cache): Inherit from threaded_queue.

Tue Oct  2 23:24:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* cygserver.cc (class server_process_param): Use new constructor syntax.
	* cygserver_process.cc (process_cache::~process_cache): New function.
	* threaded_queue.cc: Define condition debug_printf.
	Run indent.
	(threaded_queue::cleanup): Move queue_process_param guts to a method.
	(threaded_queue::process_requests): Ditto.
	(queue_process_param::queue_process_param): New method.
	(queue_process_param::~queue_process_param): Ditto.
	(queue_process_param::start): Ditto.
	(queue_process_param::stop): Ditto.
	* threaded_queue.h (class queue_process_param): Add support for
	interruptible request loops.
	* cygwin/include/cygwin/cygserver_process.h (class process_cache): Add
	destructor.

Tue Oct  2 23:24:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* cygserver_client.cc: New flag allow_daemon to disable the daemon completely.
	(cygserver_request): Check it.
	(cygserver_init): Ditto.
	* environ.cc (parse_thing): Add (no)daemon option.

Tue Oct  2 23:00:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* shm.cc: Update to handle include changes from HEAD.

Tue Oct  2 16:06:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* Makefile.in: Remove cygserver_shm.o from cygwin1.dll.
	Rename cygserver_shm_outside.o to cygserver_shm.o.
	* cygserver.cc (server_request::process): Use the new client_request
	constructor.
	* cygserver_client.cc: Remove the #ifdef's stubs for the server method
	within cygwin.
	(client_request_attach_tty::client_request_attach_tty): Use the new
	client_request constructor.
	(client_request_shutdown::client_request_shutdown): Ditto.
	(client_request::client_request): Ditto.
	* cygserver_shm.cc (client_request_shm_get::serve): Remove the
	#ifdef'd stub for in-cygwin builds.
	(client_request_shm_get::client_request_shm_get): Use the new
	client_request constructor, and remove the in-cygwin variants.
	* cygserver_shm.h (class client_request_shm_get): #ifndef test the
	serve method - it's only used in cygserver.
	* shm.cc (client_request_shm_get::client_request_shm_get): New function.
	* include/cygwin/cygserver.h (request_header): New constructor.
	(class client_request): Use it.
	New constructor accepting the header size.
	#ifndef test the server method - it's only used within cygserver.
	(client_request_get_version): #ifdef test the server method.
	(client_request_shutdown): Ditto.
	(client_request_attach_tty): Ditto.

Tue Oct  2  9:57:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* Makefile.in: add threaded_queue.o to cygserver.exe.
	* cygserver.cc: Include threaded_queue.h
	(class server_request): Inherit from queue_request.
	(class server_process_param): Inherit from queue_process_param.
	(class server_request_queue): Inherit from threaded_queue.
	(request_loop): Adjust for new types.
	(server_request_queue::process_requests): Remove guts to
	threaded_queue::process_requests.
	(server_request::server_request): Adjust for new types.
	(worker_function): Delete.
	(server_request_queue::create_workers): Delete.
	(server_request_queue::cleanup): Delete.
	(server_request_queue::add): Move guts to threaded_queue::add.
	* threaded_queue.cc: New file.
	* threaded_queue.h: New file.

Mon Oct  1 12:38:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* cygserver.cc (client_request::serve): New function.
	* cygserver_process.cc: Inlude <pthread.h> for pthread_once.
	(process_cache::process_cache): Initialise a crtiical section for write access.
	(process_cache::process): Use the critical section. Also add missing entries to
	the cache.
	(do_process_init): New function to initalise class process static variables.
	(process::process): Ensure that the process access critical section is
	initialised.
	(process::handle): Close the handle of old process's when they have terminated
	and we are returning the handle for a process with the same pid.
	* cygserver_shm.cc: Run indent.
	Include cygserver_process.h to allow process cache functionality.
	(client_request_shm_get::serve): New parameter for process cache support.
	Use the process cache, not OpenProcess to get a handle to the originating process.
	Fix a handle leak with token_handle.
	* cygserver_shm.h (class client_request_shm_get): Update ::serve for process
	cache support.
	* cygserver_transport_pipes.cc: Redefine debug_printf to be conditional on DEBUG.
	* include/cygwin/cygserver.h: Do not implement client_request::serve in the
	header.
	* include/cygwin/cygserver_process.h (class process_cache): Add a write access
	critical section to prevent races when requests from a  multithreaded
	application arrive.

Sun Sep 30 23:41:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* Makefile.in: Add cygserver_process.o to cygserver.exe.
	* cygserver.cc: Include signal.h and cygwin_version.h.
	Define debug_printf as a macro.
	Define DEBUG to a value.
	(client_request_attach_tty::serve): Add beginning of process cache support.
	Change from #ifdef DEBUG to work with new DEBUG style.
	(client_request_get_version::serve): Add beginning of process cache support.
	(class server_request): New prototype for support of process cache.
	(class queue_process_param): New class to allow request loop threading.
	(class server_request_queue): Add beginning of process cache support.
	Allow request loop threading.
	(request_loop): Thread function for request loops.
	(server_request_queue::process_requests): Initiator for threaded request loops.
	(client_request_shutdown::serve): Add beginning of process cache support.
	(server_request::server_request): Ditto.
	(server_request::process): Use debug_printf. Add beginning of process cache
	support.
	(server_request_queue::cleanup): Kill off any request loop threads.
	(server_request_queue::add): Add beginning of process cache support.
	(handle_signal): Trigger a shutdown.
	(main): Print out some useful info at startup - version, date time.
	Add process cache support.
	Spawn a separate thread for the transport request loop, thus allowing concurrent
	support for multiple transports.
	* cygserver_client.cc (client_request_get_version::serve): Add process cache
	support.
	(client_request_attach_tty::serve): Add process cache support.
	(client_request_shutdown::serve): Add process cache support.
	* cygsserver_process.cc: New file with the process cache support.
	* cygserver_shm.cc: Redefine debug_printf to allow conditional output.
	* cygwin.din: Export shmdt().
	* shm.cc: Run indent.
	Update FIXME's.
	(shmdt): New function.
	* include/cygwin/cygserver.h (class client_request): Add process cache support.
	(class client_request_get_version): Ditto.
	(class client_request_shutdown): Ditto.
	(class client_request_attach_tty): Ditto.
	* include/cygwin/cygserver_process.h: New header for process cache support.

Sun Sep 30  8:52:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* include/cygwin/cygserver_transport.h: Add copyright header.
	* include/cygwin/cygserver_transport_pipes.h: Ditto.
	* include/cygwin/cygserver_transport_sockets.h: Ditto.

Sat Sep 29 20:40:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* Makefile.in: Add cygserver_transport_sockets.o to DLL_OFILES.
	Add cygserver_transport_sockets_outside.o to cygserver.exe.
	* cygserver.cc: Include new include files.
	* cygserver_client.cc: Ditto.
	* cygserver_shm.h: No need to include <sys/socket.h> now.
	* cygerver_transport.cc: Include new include files.
	(transport_layer_base::transport_layer_base): Strip back to a stub.
	(transport_layer_base::listen): Ditto.
	(transport_layer_base::accept): Ditto.
	(transport_layer_base::close): Ditto.
	(transport_layer_base::read): Ditto.
	(transport_layer_base::write): Ditto.
	(transport_layer_base::connect): Ditto.
	* cygserver_transport_pipes.cc: Include new header
	"cygwin/cygserver_transport_pipes.h".
	* cygserver_transport_sockets.cc: New file.
	* dcrt0.cc: No need to include <sys/socket.h> now.
	* fhandler_tty.cc: Ditto.
	* tty.cc: Ditto.
	* include/cygwin/cygserver_transport.h: Strip the base class to a stub.
	Remove the cygserver_transport_pipes class.
	* include/cygwin/cygserver_transport_pipes.h: New file.
	* include/cygwin/cygserver_transport_sockets.h: New file.

Tue Sep 25 16:22:00 2001  Robert Collins <rbtcollins@hotmail.com>

	* autoload.cc: Add dynamic load statement for 'ImpersonateNamedPipeClient'.
	* Makefile.in: Add new object files, and build instructions for cygserver.exe.
	* cygwin.din: Export ftok, shmat, shmctl and shmget.
	* dcrt0.cc: Additional includes for cygserver support.
	(dll_crt0_1): Initialise the cygserver client.
	* fhandler.h (fhandler_tty): New method cygserver_attach_tty.
	* fhandler_tty.cc: Additional includes for cygserver support.
	(fhandler_tty_slave::open): Attempt to use the cygserver when obtaining
	handles from the parent process. On failure or 9x use the current method.
	(fhandler_tty_slave::cygserver_attach_tty): New function.
	* fork.cc (fork_child): Fixup shm memory mapped areas.
	* pinfo.h: Declare fixup_shms_after_fork().
	* security.h: Declare alloc_sd().
	* tty.cc: Additonal includes to support cygserver.
	(tty::common_init): Don't allow others to open us if the cygserver is running.
	* winsup.h: Declare cygserver_running.
	* cygserver.cc: New file.
	* cygserver_client.cc: New file.
	* cygserver_shm.cc: New file.
	* cygserver_shm.h: New file.
	* cygserver_transport.cc: New file.
	* cygserver_transport_pipes.cc: New file.
	* ipc.cc: New file.
	* shm.cc: New file.
	* include/cygwin/cygserver.h: New file.
	* include/cygwin/cygserver_transport.h: New file.
	* include/sys/ipc.h: New file.
	* include/sys/shm.h: New file.

2002-02-28  Robert Collins  <rbtcollins@hotmail.com>

	* thread.cc (semaphore::TryWait): Set errno as required by posix 1003.1.
	(__sem_wait): Ditto.
	(__sem_trywait): Ditto.
2002-02-28 14:30:38 +00:00

1232 lines
30 KiB
C++

/* fhandler_tty.cc
Copyright 1997, 1998, 2000, 2001 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 <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include "cygerrno.h"
#include "security.h"
#include "fhandler.h"
#include "path.h"
#include "dtable.h"
#include "sync.h"
#include "sigproc.h"
#include "pinfo.h"
#include "cygheap.h"
#include "shared_info.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.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 (int unit)
: fhandler_pty_master (FH_TTYM, unit), console (NULL), hThread (NULL)
{
}
int
fhandler_tty_master::init (int ntty)
{
HANDLE h;
termios_printf ("Creating master for tty%d", ntty);
if (init_console ())
{
termios_printf ("can't create fhandler");
return -1;
}
termios ti;
memset (&ti, 0, sizeof (ti));
console->tcsetattr (0, &ti);
ttynum = ntty;
cygwin_shared->tty[ttynum]->common_init (this);
inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
h = makethread (process_input, NULL, 0, "ttyin");
if (h == NULL)
{
termios_printf ("can't create input thread");
return -1;
}
else
{
SetThreadPriority (h, THREAD_PRIORITY_HIGHEST);
CloseHandle (h);
}
h = makethread (process_ioctl, NULL, 0, "ttyioctl");
if (h == NULL)
{
termios_printf ("can't create ioctl thread");
return -1;
}
else
{
SetThreadPriority (h, THREAD_PRIORITY_HIGHEST);
CloseHandle (h);
}
hThread = makethread (process_output, NULL, 0, "ttyout");
if (hThread != NULL)
SetThreadPriority (hThread, THREAD_PRIORITY_HIGHEST);
else
{
termios_printf ("can't create output thread");
return -1;
}
return 0;
}
#ifdef DEBUGGING
static class mutex_stack
{
public:
const char *fn;
int ln;
const char *tname;
} ostack[100];
static int osi;
#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 = threadname (0, 0);
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, written;
DWORD n;
DWORD rc;
char* p;
rc = WaitForSingleObject (input_mutex, INFINITE);
bytes_left = n = eat_readahead (-1);
get_ttyp ()->read_retval = 0;
p = rabuf;
if (n != 0)
{
while (bytes_left > 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");
break;
}
get_ttyp ()->read_retval += written;
p += written;
bytes_left -= written;
if (bytes_left > 0)
{
debug_printf ("to_slave pipe is full");
SetEvent (input_available_event);
ReleaseMutex (input_mutex);
Sleep (10);
rc = WaitForSingleObject (input_mutex, INFINITE);
}
}
}
else
termios_printf ("sending EOF to slave");
SetEvent (input_available_event);
ReleaseMutex (input_mutex);
return get_ttyp ()->read_retval;
}
static DWORD WINAPI
process_input (void *)
{
char rawbuf[INP_BUFFER_SIZE];
while (1)
{
int nraw = tty_master->console->read ((void *) rawbuf,
(size_t) INP_BUFFER_SIZE);
(void) tty_master->line_edit (rawbuf, nraw);
}
}
bool
fhandler_pty_master::hit_eof ()
{
if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ())
{
/* We have the only remaining open handle to this pty, and
the slave pty has been opened at least once. We treat
this as EOF. */
termios_printf ("all other handles closed");
return 1;
}
return 0;
}
/* Process tty output requests */
int
fhandler_pty_master::process_slave_output (char *buf, size_t len, 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. */
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;
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)
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)
{
termios_printf ("ReadFile %E");
ExitThread (0);
}
if (n == 0)
{
/* End of file. */
ExitThread (0);
}
n = tty_master->console->write ((void *) buf, (size_t) n);
tty_master->get_ttyp ()->write_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 (int num)
: fhandler_tty_common (FH_TTYS, num)
{
}
fhandler_tty_slave::fhandler_tty_slave ()
: fhandler_tty_common (FH_TTYS, 0)
{
}
/* FIXME: This function needs to close handles when it has
a failing condition. */
int
fhandler_tty_slave::open (path_conv *, int flags, mode_t)
{
tcinit (cygwin_shared->tty[ttynum]);
attach_tty (ttynum);
tc->set_ctty (ttynum, flags);
set_flags (flags);
/* Create synchronisation events */
char buf[40];
/* output_done_event may or may not exist. It will exist if the tty
was opened by fhandler_tty_master::init, normally called at
startup if use_tty is non-zero. It will not exist if this is a
pty opened by fhandler_pty_master::open. In the former case, tty
output is handled by a separate thread which controls output. */
__small_sprintf (buf, OUTPUT_DONE_EVENT, ttynum);
output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
if (!(output_mutex = get_ttyp ()->open_output_mutex ()))
{
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, ttynum);
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, ttynum);
ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
__small_sprintf (buf, IOCTL_DONE_EVENT, ttynum);
ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
/* FIXME: Needs a method to eliminate tty races */
{
acquire_output_mutex (500);
inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE);
get_ttyp ()->was_opened = TRUE;
release_output_mutex ();
}
/* Duplicate tty handles. */
if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave)
{
termios_printf ("tty handles have been closed");
set_errno (EACCES);
return 0;
}
HANDLE from_master_local, to_master_local;
if (!wincap.has_security () ||
cygserver_running!=CYGSERVER_OK ||
!cygserver_attach_tty ( &from_master_local, &to_master_local))
{
termios_printf ("cannot dup handles via server. using old method.");
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",
ttynum, 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);
ProtectHandle1 (from_master_local, from_pty);
set_output_handle (to_master_local);
ProtectHandle1 (to_master_local, to_pty);
set_open_status ();
termios_printf ("tty%d opened", ttynum);
return 1;
}
int
fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
LPHANDLE to_master_ptr)
{
if (!from_master_ptr || !to_master_ptr)
return 0;
client_request_attach_tty *request =
new client_request_attach_tty ((DWORD) GetCurrentProcessId (),
(DWORD) get_ttyp ()->master_pid,
(HANDLE) get_ttyp ()->from_master,
(HANDLE) get_ttyp ()->to_master);
if (cygserver_request (request) != 0 ||
request->header.error_code != 0)
return 0;
/*
struct request_attach_tty req;
INIT_REQUEST (req, CYGSERVER_REQUEST_ATTACH_TTY);
req.pid = GetCurrentProcessId ();
req.master_pid = get_ttyp ()->master_pid;
req.from_master = get_ttyp ()->from_master;
req.to_master = get_ttyp ()->to_master;
if (cygserver_request ((struct request_header*) &req) != 0)
return 0;
*/
*from_master_ptr = request->from_master ();
*to_master_ptr = request->to_master ();
delete request;
return 1;
}
void
fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
{
int mode = 0;
a &= GENERIC_READ | GENERIC_WRITE;
if (a == GENERIC_READ)
mode = O_RDONLY;
if (a == GENERIC_WRITE)
mode = O_WRONLY;
if (a == (GENERIC_READ | GENERIC_WRITE))
mode = O_RDWR;
open (0, mode);
}
int
fhandler_tty_slave::write (const void *ptr, size_t len)
{
DWORD n, towrite = len;
termios_printf ("tty%d, write(%x, %d)", ttynum, ptr, len);
acquire_output_mutex (INFINITE);
while (len)
{
n = min (OUT_BUFFER_SIZE, len);
char *buf = (char *)ptr;
ptr = (char *) ptr + n;
len -= n;
/* 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;
}
int __stdcall
fhandler_tty_slave::read (void *ptr, size_t len)
{
DWORD n;
int totalread = 0;
int vmin = INT_MAX;
int vtime = 0; /* Initialized to prevent -Wuninitialized warning */
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 (!(get_ttyp ()->ti.c_lflag & ICANON))
{
vmin = min (INP_BUFFER_SIZE, get_ttyp ()->ti.c_cc[VMIN]);
vtime = get_ttyp ()->ti.c_cc[VTIME];
if (vmin < 0) vmin = 0;
if (vtime < 0) vtime = 0;
if (vmin == 0)
time_to_wait = INFINITE;
else
time_to_wait = (vtime == 0 ? INFINITE : 100 * vtime);
}
else
time_to_wait = INFINITE;
w4[0] = signal_arrived;
w4[1] = input_available_event;
while (len)
{
rc = WaitForMultipleObjects (2, w4, FALSE, time_to_wait);
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);
return -1;
}
else if (rc == WAIT_FAILED)
{
termios_printf ("wait for input event failed, %E");
break;
}
else if (rc == WAIT_TIMEOUT)
break;
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;
}
readlen = min (bytes_in_pipe, min (len, sizeof (buf)));
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;
memcpy (ptr, buf, n);
ptr = (char *) ptr + n;
}
}
if (!bytes_in_pipe)
ResetEvent (input_available_event);
ReleaseMutex (input_mutex);
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 (totalread >= vmin && (vmin > 0 || totalread > 0))
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;
}
termios_printf ("%d=read(%x, %d)", totalread, ptr, len);
return totalread;
}
int
fhandler_tty_common::dup (fhandler_base *child)
{
fhandler_tty_slave *fts = (fhandler_tty_slave *) child;
int errind;
fts->ttynum = ttynum;
fts->tcinit (get_ttyp ());
attach_tty (ttynum);
tc->set_ctty (ttynum, openflags);
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)
{
return 0;
}
int
fhandler_tty_slave::ioctl (unsigned int cmd, void *arg)
{
termios_printf ("ioctl (%x)", cmd);
if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid &&
myself->ctty == ttynum && (get_ttyp ()->ti.c_lflag & TOSTOP))
{
/* background process */
termios_printf ("bg ioctl pgid %d, tpgid %d, ctty %d",
myself->pgid, get_ttyp ()->getpgid (), myself->ctty);
_raise (SIGTTOU);
}
switch (cmd)
{
case TIOCGWINSZ:
case TIOCSWINSZ:
break;
case FIONBIO:
set_nonblocking (*(int *) arg);
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:
get_ttyp ()->ioctl_retval = -1;
get_ttyp ()->arg.winsize = * (struct winsize *) arg;
if (ioctl_request_event)
SetEvent (ioctl_request_event);
if (ioctl_done_event)
WaitForSingleObject (ioctl_done_event, INFINITE);
break;
}
release_output_mutex ();
out:
termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd);
return get_ttyp ()->ioctl_retval;
}
/*******************************************************
fhandler_pty_master
*/
fhandler_pty_master::fhandler_pty_master (DWORD devtype, int unit)
: fhandler_tty_common (devtype, unit)
{
}
int
fhandler_pty_master::open (path_conv *, int flags, mode_t)
{
ttynum = cygwin_shared->tty.allocate_tty (0);
if (ttynum < 0)
return 0;
cygwin_shared->tty[ttynum]->common_init (this);
inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
set_flags (flags);
set_open_status ();
termios_printf ("opened pty master tty%d<%p>", ttynum, this);
return 1;
}
int
fhandler_tty_common::close ()
{
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;
termios_printf ("tty%d <%p,%p> closed", ttynum, get_handle (), get_output_handle ());
return 0;
}
int
fhandler_pty_master::close ()
{
#if 0
while (accept_input () > 0)
continue;
#endif
this->fhandler_tty_common::close ();
if (!get_ttyp ()->master_alive ())
{
termios_printf ("freeing tty%d (%d)", ttynum, get_ttyp ()->ntty);
#if 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)
{
(void) line_edit ((char *) ptr, len);
return len;
}
int __stdcall
fhandler_pty_master::read (void *ptr, size_t len)
{
return process_slave_output ((char *) ptr, len, pktmode);
}
int
fhandler_pty_master::tcgetattr (struct termios *t)
{
*t = cygwin_shared->tty[ttynum]->ti;
return 0;
}
int
fhandler_pty_master::tcsetattr (int, const struct termios *t)
{
cygwin_shared->tty[ttynum]->ti = *t;
return 0;
}
int
fhandler_pty_master::tcflush (int)
{
return 0;
}
int
fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
{
switch (cmd)
{
case TIOCPKT:
pktmode = * (int *) arg;
break;
case TIOCGWINSZ:
* (struct winsize *) arg = get_ttyp ()->winsize;
break;
case TIOCSWINSZ:
get_ttyp ()->winsize = * (struct winsize *) arg;
_kill (-get_ttyp ()->getpgid (), SIGWINCH);
break;
case FIONBIO:
set_nonblocking (*(int *) arg);
break;
default:
set_errno (EINVAL);
return -1;
}
return 0;
}
char *
fhandler_pty_master::ptsname (void)
{
static char buf[32];
__small_sprintf (buf, "/dev/tty%d", ttynum);
return buf;
}
void
fhandler_tty_common::set_close_on_exec (int val)
{
#ifndef DEBUGGING
this->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
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);
}
void
fhandler_tty_common::fixup_after_fork (HANDLE parent)
{
this->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)
{
this->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 ());
}
}
void
fhandler_tty_master::fixup_after_fork (HANDLE child)
{
this->fhandler_pty_master::fixup_after_fork (child);
console->fixup_after_fork (child);
}
void
fhandler_tty_master::fixup_after_exec (HANDLE)
{
console->close ();
init_console ();
return;
}
int
fhandler_tty_master::init_console ()
{
console = (fhandler_console *) cygheap->fdtab.build_fhandler (-1, FH_CONSOLE, "/dev/ttym");
if (console == NULL)
return -1;
console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY);
console->set_r_no_interrupt (1);
return 0;
}