544 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			544 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
/* client.cc
 | 
						|
 | 
						|
   Copyright 2001, 2002, 2003 Red Hat Inc.
 | 
						|
 | 
						|
   Written by Egor Duda <deo@logos-m.ru>
 | 
						|
 | 
						|
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. */
 | 
						|
 | 
						|
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
 | 
						|
#ifdef __OUTSIDE_CYGWIN__
 | 
						|
#include "woutsup.h"
 | 
						|
#else
 | 
						|
#include "winsup.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "sigproc.h"
 | 
						|
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "cygserver_msg.h"
 | 
						|
#include "cygserver_sem.h"
 | 
						|
#include "cygserver_shm.h"
 | 
						|
 | 
						|
#include "cygserver.h"
 | 
						|
#include "transport.h"
 | 
						|
 | 
						|
int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
 | 
						|
 | 
						|
bool allow_server = false;	// Nb: inherited by children.
 | 
						|
 | 
						|
client_request_get_version::client_request_get_version ()
 | 
						|
  : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
 | 
						|
{
 | 
						|
  msglen (0);			// No parameters for request.
 | 
						|
 | 
						|
  // verbose: syscall_printf ("created");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * client_request_get_version::check_version ()
 | 
						|
 *
 | 
						|
 * The major version and API version numbers must match exactly.  An
 | 
						|
 * older than expected minor version number is accepted (as long as
 | 
						|
 * the first numbers match, that is).
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef __INSIDE_CYGWIN__
 | 
						|
 | 
						|
bool
 | 
						|
client_request_get_version::check_version () const
 | 
						|
{
 | 
						|
  const bool ok = (version.major == CYGWIN_SERVER_VERSION_MAJOR
 | 
						|
		   && version.api == CYGWIN_SERVER_VERSION_API
 | 
						|
		   && version.minor <= CYGWIN_SERVER_VERSION_MINOR);
 | 
						|
 | 
						|
  if (!ok)
 | 
						|
    syscall_printf (("incompatible version of cygwin server: "
 | 
						|
		     "client version %d.%d.%d.%d, "
 | 
						|
		     "server version %ld.%ld.%ld.%ld"),
 | 
						|
		    CYGWIN_SERVER_VERSION_MAJOR,
 | 
						|
		    CYGWIN_SERVER_VERSION_API,
 | 
						|
		    CYGWIN_SERVER_VERSION_MINOR,
 | 
						|
		    CYGWIN_SERVER_VERSION_PATCH,
 | 
						|
		    version.major,
 | 
						|
		    version.api,
 | 
						|
		    version.minor,
 | 
						|
		    version.patch);
 | 
						|
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid,
 | 
						|
						      HANDLE nfrom_master,
 | 
						|
						      HANDLE nto_master)
 | 
						|
  : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
 | 
						|
{
 | 
						|
  req.pid = GetCurrentProcessId ();
 | 
						|
  req.master_pid = nmaster_pid;
 | 
						|
  req.from_master = nfrom_master;
 | 
						|
  req.to_master = nto_master;
 | 
						|
 | 
						|
  syscall_printf (("created: pid = %lu, master_pid = %lu, "
 | 
						|
		   "from_master = %lu, to_master = %lu"),
 | 
						|
		  req.pid, req.master_pid, req.from_master, req.to_master);
 | 
						|
}
 | 
						|
#endif /* __INSIDE_CYGWIN__ */
 | 
						|
 | 
						|
/*
 | 
						|
 * client_request_attach_tty::send ()
 | 
						|
 *
 | 
						|
 * Wraps the base method to provide error handling support.  If the
 | 
						|
 * reply contains a body but is flagged as an error, close any handles
 | 
						|
 * that have been returned by cygserver and then discard the message
 | 
						|
 * body, i.e. the client either sees a successful result with handles
 | 
						|
 * or an unsuccessful result with no handles.
 | 
						|
 */
 | 
						|
 | 
						|
void
 | 
						|
client_request_attach_tty::send (transport_layer_base * const conn)
 | 
						|
{
 | 
						|
  client_request::send (conn);
 | 
						|
 | 
						|
  if (msglen () && error_code ())
 | 
						|
    {
 | 
						|
      if (from_master ())
 | 
						|
	CloseHandle (from_master ());
 | 
						|
      if (to_master ())
 | 
						|
	CloseHandle (to_master ());
 | 
						|
      msglen (0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
client_request::header_t::header_t (const request_code_t request_code,
 | 
						|
				    const size_t msglen)
 | 
						|
  : msglen (msglen),
 | 
						|
    request_code (request_code)
 | 
						|
{
 | 
						|
  assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST);
 | 
						|
}
 | 
						|
 | 
						|
// FIXME: also check write and read result for -1.
 | 
						|
 | 
						|
void
 | 
						|
client_request::send (transport_layer_base * const conn)
 | 
						|
{
 | 
						|
  assert (conn);
 | 
						|
  assert (!(msglen () && !_buf)); // i.e., msglen () implies _buf
 | 
						|
  assert (msglen () <= _buflen);
 | 
						|
 | 
						|
  {
 | 
						|
    const ssize_t count = conn->write (&_header, sizeof (_header));
 | 
						|
 | 
						|
    if (count != sizeof (_header))
 | 
						|
      {
 | 
						|
	assert (errno);
 | 
						|
	error_code (errno);
 | 
						|
	syscall_printf (("request header write failure: "
 | 
						|
			 "only %ld bytes sent of %ld, "
 | 
						|
			 "error = %d(%lu)"),
 | 
						|
			count, sizeof (_header),
 | 
						|
			errno, GetLastError ());
 | 
						|
	return;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  if (msglen ())
 | 
						|
    {
 | 
						|
      const ssize_t count = conn->write (_buf, msglen ());
 | 
						|
 | 
						|
      if (count == -1 || (size_t) count != msglen ())
 | 
						|
	{
 | 
						|
	  assert (errno);
 | 
						|
	  error_code (errno);
 | 
						|
	  syscall_printf (("request body write failure: "
 | 
						|
			   "only %ld bytes sent of %ld, "
 | 
						|
			   "error = %d(%lu)"),
 | 
						|
			  count, msglen (),
 | 
						|
			  errno, GetLastError ());
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  // verbose: syscall_printf ("request sent (%ld + %ld bytes)",
 | 
						|
  //			      sizeof (_header), msglen ());
 | 
						|
 | 
						|
  {
 | 
						|
    const ssize_t count = conn->read (&_header, sizeof (_header));
 | 
						|
 | 
						|
    if (count != sizeof (_header))
 | 
						|
      {
 | 
						|
	assert (errno);
 | 
						|
	error_code (errno);
 | 
						|
	syscall_printf (("reply header read failure: "
 | 
						|
			 "only %ld bytes received of %ld, "
 | 
						|
			 "error = %d(%lu)"),
 | 
						|
			count, sizeof (_header),
 | 
						|
			errno, GetLastError ());
 | 
						|
	return;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  if (msglen () && !_buf)
 | 
						|
    {
 | 
						|
      system_printf ("no client buffer for reply body: %ld bytes needed",
 | 
						|
		     msglen ());
 | 
						|
      error_code (EINVAL);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (msglen () > _buflen)
 | 
						|
    {
 | 
						|
      system_printf (("client buffer too small for reply body: "
 | 
						|
		      "have %ld bytes and need %ld"),
 | 
						|
		     _buflen, msglen ());
 | 
						|
      error_code (EINVAL);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (msglen ())
 | 
						|
    {
 | 
						|
      const ssize_t count = conn->read (_buf, msglen ());
 | 
						|
 | 
						|
      if (count == -1 || (size_t) count != msglen ())
 | 
						|
	{
 | 
						|
	  assert (errno);
 | 
						|
	  error_code (errno);
 | 
						|
	  syscall_printf (("reply body read failure: "
 | 
						|
			   "only %ld bytes received of %ld, "
 | 
						|
			   "error = %d(%lu)"),
 | 
						|
			  count, msglen (),
 | 
						|
			  errno, GetLastError ());
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  // verbose: syscall_printf ("reply received (%ld + %ld bytes)",
 | 
						|
  //			      sizeof (_header), msglen ());
 | 
						|
}
 | 
						|
 | 
						|
#ifdef __OUTSIDE_CYGWIN__
 | 
						|
 | 
						|
client_request_attach_tty::client_request_attach_tty ()
 | 
						|
  : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * client_request::handle_request ()
 | 
						|
 *
 | 
						|
 * A server-side method.
 | 
						|
 *
 | 
						|
 * This is a factory method for the client_request subclasses.  It
 | 
						|
 * reads the incoming request header and, based on its request code,
 | 
						|
 * creates an instance of the appropriate class.
 | 
						|
 *
 | 
						|
 * FIXME: If the incoming packet is malformed, the server drops it on
 | 
						|
 * the floor.  Should it try and generate some sort of reply for the
 | 
						|
 * client?  As it is, the client will simply get a broken connection.
 | 
						|
 *
 | 
						|
 * FIXME: also check write and read result for -1.
 | 
						|
 */
 | 
						|
 | 
						|
/* static */ void
 | 
						|
client_request::handle_request (transport_layer_base *const conn,
 | 
						|
				process_cache *const cache)
 | 
						|
{
 | 
						|
  // verbose: debug_printf ("about to read");
 | 
						|
 | 
						|
  header_t header;
 | 
						|
 | 
						|
  {
 | 
						|
    const ssize_t count = conn->read (&header, sizeof (header));
 | 
						|
 | 
						|
    if (count != sizeof (header))
 | 
						|
      {
 | 
						|
	syscall_printf (("request header read failure: "
 | 
						|
			 "only %ld bytes received of %ld, "
 | 
						|
			 "error = %d(%lu)"),
 | 
						|
			count, sizeof (header),
 | 
						|
			errno, GetLastError ());
 | 
						|
	return;
 | 
						|
      }
 | 
						|
 | 
						|
      // verbose: debug_printf ("got header (%ld)", count);
 | 
						|
  }
 | 
						|
 | 
						|
  client_request *req = NULL;
 | 
						|
 | 
						|
  switch (header.request_code)
 | 
						|
    {
 | 
						|
    case CYGSERVER_REQUEST_GET_VERSION:
 | 
						|
      req = new client_request_get_version;
 | 
						|
      break;
 | 
						|
    case CYGSERVER_REQUEST_SHUTDOWN:
 | 
						|
      req = new client_request_shutdown;
 | 
						|
      break;
 | 
						|
    case CYGSERVER_REQUEST_ATTACH_TTY:
 | 
						|
      req = new client_request_attach_tty;
 | 
						|
      break;
 | 
						|
    case CYGSERVER_REQUEST_MSG:
 | 
						|
      req = new client_request_msg;
 | 
						|
      break;
 | 
						|
    case CYGSERVER_REQUEST_SEM:
 | 
						|
      req = new client_request_sem;
 | 
						|
      break;
 | 
						|
    case CYGSERVER_REQUEST_SHM:
 | 
						|
      req = new client_request_shm;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      syscall_printf ("unknown request code %d received: request ignored",
 | 
						|
		      header.request_code);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  assert (req);
 | 
						|
 | 
						|
  req->msglen (header.msglen);
 | 
						|
  req->handle (conn, cache);
 | 
						|
 | 
						|
  delete req;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * client_request::handle ()
 | 
						|
 *
 | 
						|
 * A server-side method.
 | 
						|
 *
 | 
						|
 * At this point, the header of an incoming request has been read and
 | 
						|
 * an appropriate client_request object constructed.  This method has
 | 
						|
 * to read the request body into its buffer, if there is such a body,
 | 
						|
 * then perform the request and send back the results to the client.
 | 
						|
 *
 | 
						|
 * FIXME: If the incoming packet is malformed, the server drops it on
 | 
						|
 * the floor.  Should it try and generate some sort of reply for the
 | 
						|
 * client?  As it is, the client will simply get a broken connection.
 | 
						|
 *
 | 
						|
 * FIXME: also check write and read result for -1.
 | 
						|
 */
 | 
						|
 | 
						|
void
 | 
						|
client_request::handle (transport_layer_base *const conn,
 | 
						|
			process_cache *const cache)
 | 
						|
{
 | 
						|
  if (msglen () && !_buf)
 | 
						|
    {
 | 
						|
      system_printf ("no buffer for request body: %ld bytes needed",
 | 
						|
		     msglen ());
 | 
						|
      error_code (EINVAL);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (msglen () > _buflen)
 | 
						|
    {
 | 
						|
      system_printf (("buffer too small for request body: "
 | 
						|
		      "have %ld bytes and need %ld"),
 | 
						|
		     _buflen, msglen ());
 | 
						|
      error_code (EINVAL);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (msglen ())
 | 
						|
    {
 | 
						|
      const ssize_t count = conn->read (_buf, msglen ());
 | 
						|
 | 
						|
      if (count == -1 || (size_t) count != msglen ())
 | 
						|
	{
 | 
						|
	  assert (errno);
 | 
						|
	  error_code (errno);
 | 
						|
	  syscall_printf (("request body read failure: "
 | 
						|
			   "only %ld bytes received of %ld, "
 | 
						|
			   "error = %d(%lu)"),
 | 
						|
			  count, msglen (),
 | 
						|
			  errno, GetLastError ());
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  // verbose: syscall_printf ("request received (%ld + %ld bytes)",
 | 
						|
  //			      sizeof (_header), msglen ());
 | 
						|
 | 
						|
  error_code (0);		// Overwrites the _header.request_code field.
 | 
						|
 | 
						|
  /*
 | 
						|
   * This is not allowed to fail. We must return ENOSYS at a minimum
 | 
						|
   * to the client.
 | 
						|
   */
 | 
						|
  serve (conn, cache);
 | 
						|
 | 
						|
  {
 | 
						|
    const ssize_t count = conn->write (&_header, sizeof (_header));
 | 
						|
 | 
						|
    if (count != sizeof (_header))
 | 
						|
      {
 | 
						|
	assert (errno);
 | 
						|
	error_code (errno);
 | 
						|
	syscall_printf (("reply header write failure: "
 | 
						|
			 "only %ld bytes sent of %ld, "
 | 
						|
			 "error = %d(%lu)"),
 | 
						|
			count, sizeof (_header),
 | 
						|
			errno, GetLastError ());
 | 
						|
	return;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  if (msglen ())
 | 
						|
    {
 | 
						|
      const ssize_t count = conn->write (_buf, msglen ());
 | 
						|
 | 
						|
      if (count == -1 || (size_t) count != msglen ())
 | 
						|
	{
 | 
						|
	  assert (errno);
 | 
						|
	  error_code (errno);
 | 
						|
	  syscall_printf (("reply body write failure: "
 | 
						|
			   "only %ld bytes sent of %ld, "
 | 
						|
			   "error = %d(%lu)"),
 | 
						|
			  count, msglen (),
 | 
						|
			  errno, GetLastError ());
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  // verbose: syscall_printf ("reply sent (%ld + %ld bytes)",
 | 
						|
  //			      sizeof (_header), msglen ());
 | 
						|
}
 | 
						|
 | 
						|
/* The server side implementation of make_request.  Very simple. */
 | 
						|
int
 | 
						|
client_request::make_request ()
 | 
						|
{
 | 
						|
  transport_layer_base *const transport = create_server_transport ();
 | 
						|
  assert (transport);
 | 
						|
  if (transport->connect () == -1)
 | 
						|
    {
 | 
						|
      if (errno)
 | 
						|
	error_code (errno);
 | 
						|
      else
 | 
						|
	error_code (ENOSYS);
 | 
						|
      delete transport;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  send (transport);
 | 
						|
  delete transport;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif /* __OUTSIDE_CYGWIN__ */
 | 
						|
 | 
						|
client_request::client_request (request_code_t const id,
 | 
						|
				void * const buf,
 | 
						|
				size_t const buflen)
 | 
						|
  : _header (id, buflen),
 | 
						|
    _buf (buf),
 | 
						|
    _buflen (buflen)
 | 
						|
{
 | 
						|
  assert ((!_buf && !_buflen) || (_buf && _buflen));
 | 
						|
}
 | 
						|
 | 
						|
client_request::~client_request ()
 | 
						|
{}
 | 
						|
 | 
						|
#ifdef __INSIDE_CYGWIN__
 | 
						|
int
 | 
						|
client_request::make_request ()
 | 
						|
{
 | 
						|
  assert (cygserver_running == CYGSERVER_UNKNOWN	\
 | 
						|
	  || cygserver_running == CYGSERVER_OK		\
 | 
						|
	  || cygserver_running == CYGSERVER_UNAVAIL);
 | 
						|
 | 
						|
  if (cygserver_running == CYGSERVER_UNKNOWN)
 | 
						|
    cygserver_init ();
 | 
						|
 | 
						|
  assert (cygserver_running == CYGSERVER_OK		\
 | 
						|
	  || cygserver_running == CYGSERVER_UNAVAIL);
 | 
						|
 | 
						|
  /* Don't retry every request if the server's not there */
 | 
						|
  if (cygserver_running == CYGSERVER_UNAVAIL)
 | 
						|
    {
 | 
						|
      syscall_printf ("cygserver un-available");
 | 
						|
      error_code (ENOSYS);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  transport_layer_base *const transport = create_server_transport ();
 | 
						|
 | 
						|
  assert (transport);
 | 
						|
 | 
						|
  if (transport->connect () == -1)
 | 
						|
    {
 | 
						|
      if (errno)
 | 
						|
	error_code (errno);
 | 
						|
      else
 | 
						|
	error_code (ENOSYS);
 | 
						|
      delete transport;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  // verbose: debug_printf ("connected to server %p", transport);
 | 
						|
 | 
						|
  send (transport);
 | 
						|
 | 
						|
  delete transport;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
check_cygserver_available ()
 | 
						|
{
 | 
						|
  assert (cygserver_running == CYGSERVER_UNKNOWN	\
 | 
						|
	  || cygserver_running == CYGSERVER_UNAVAIL);
 | 
						|
 | 
						|
  cygserver_running = CYGSERVER_OK; // For make_request ().
 | 
						|
 | 
						|
  client_request_get_version req;
 | 
						|
 | 
						|
  /* This indicates that we failed to connect to cygserver at all but
 | 
						|
   * that's fine as cygwin doesn't need it to be running.
 | 
						|
   */
 | 
						|
  if (req.make_request () == -1)
 | 
						|
    return false;
 | 
						|
 | 
						|
  /* We connected to the server but something went wrong after that
 | 
						|
   * (in sending the message, in cygserver itself, or in receiving the
 | 
						|
   * reply).
 | 
						|
   */
 | 
						|
  if (req.error_code ())
 | 
						|
    {
 | 
						|
      syscall_printf ("failure in cygserver version request: %d",
 | 
						|
		      req.error_code ());
 | 
						|
      syscall_printf ("process will continue without cygserver support");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
  return req.check_version ();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
cygserver_init ()
 | 
						|
{
 | 
						|
  if (!allow_server)
 | 
						|
    {
 | 
						|
      syscall_printf ("cygserver use disabled in client");
 | 
						|
      cygserver_running = CYGSERVER_UNAVAIL;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  assert (cygserver_running == CYGSERVER_UNKNOWN	\
 | 
						|
	  || cygserver_running == CYGSERVER_OK		\
 | 
						|
	  || cygserver_running == CYGSERVER_UNAVAIL);
 | 
						|
 | 
						|
  if (cygserver_running == CYGSERVER_OK)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!check_cygserver_available ())
 | 
						|
    cygserver_running = CYGSERVER_UNAVAIL;
 | 
						|
}
 | 
						|
#endif /* __INSIDE_CYGWIN__ */
 |