Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygserver/client.cc')
-rw-r--r--winsup/cygserver/client.cc570
1 files changed, 446 insertions, 124 deletions
diff --git a/winsup/cygserver/client.cc b/winsup/cygserver/client.cc
index 1df23ad9f..138c9ddc0 100644
--- a/winsup/cygserver/client.cc
+++ b/winsup/cygserver/client.cc
@@ -4,204 +4,526 @@
Written by Egor Duda <deo@logos-m.ru>
- This file is part of Cygwin.
+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. */
+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__
-#undef __INSIDE_CYGWIN__
+#include "woutsup.h"
#else
#include "winsup.h"
#endif
-#ifndef __INSIDE_CYGWIN__
-#define debug_printf printf
-#define api_fatal printf
-#include <stdio.h>
-#include <windows.h>
-#endif
-#include <sys/socket.h>
+#include <assert.h>
#include <errno.h>
+#include <stdio.h>
#include <unistd.h>
-//#include "security.h"
-#include "cygwin/cygserver_transport.h"
-#include "cygwin/cygserver_transport_pipes.h"
-#include "cygwin/cygserver_transport_sockets.h"
+
+#include "cygerrno.h"
+#include "cygserver_shm.h"
+#include "safe_memory.h"
+
#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_transport.h"
+
+int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
-/* 0 = untested, 1 = running, 2 = dead */
-int cygserver_running=CYGSERVER_UNKNOWN;
-/* on by default during development. For release, we probably want off by default */
-int allow_daemon = TRUE;
+/* On by default during development. For release, we probably want off
+ * by default.
+ */
+bool allow_daemon = true; // Nb: inherited by children.
-client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version))
+client_request_get_version::client_request_get_version ()
+ : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
{
- buffer = (char *)&version;
+ msglen (0); // No parameters for request.
+
+ // verbose: syscall_printf ("created");
}
-client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
+/*
+ * 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).
+ */
+
+bool
+client_request_get_version::check_version () const
{
- buffer = (char *)&req;
- req.pid = 0;
- req.master_pid = 0;
- req.from_master = NULL;
- req.to_master = NULL;
+ 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 npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
+#ifdef __INSIDE_CYGWIN__
+
+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))
{
- buffer = (char *)&req;
- req.pid = npid;
+ req.pid = GetCurrentProcessId ();
req.master_pid = nmaster_pid;
req.from_master = nfrom_master;
req.to_master = nto_master;
-}
-client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0)
-{
- buffer = NULL;
+ 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);
}
-client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size)
+#else /* !__INSIDE_CYGWIN__ */
+
+client_request_attach_tty::client_request_attach_tty ()
+ : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
{
+ // verbose: syscall_printf ("created");
}
-client_request::~client_request ()
+#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::operator class request_header ()
+client_request::header_t::header_t (const request_code_t request_code,
+ const size_t msglen)
+ : msglen (msglen),
+ request_code (request_code)
{
- return header;
+ 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 *conn)
+client_request::send (transport_layer_base * const conn)
{
- if (!conn)
- return;
- debug_printf("this=%p, conn=%p\n",this, conn);
- ssize_t bytes_written, bytes_read;
- debug_printf("header.cb = %ld\n",header.cb);
- if ((bytes_written = conn->write ((char *)&header, sizeof (header)))
- != sizeof(header) || (header.cb &&
- (bytes_written = conn->write (buffer, header.cb)) != header.cb))
+ 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 ())
{
- header.error_code = -1;
- debug_printf ("bytes written != request size\n");
+ 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;
}
- debug_printf("Sent request, size (%ld)\n",bytes_written);
+ 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 ());
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+/*
+ * 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);
+ }
- if ((bytes_read = conn->read ((char *)&header, sizeof (header)))
- != sizeof (header) || (header.cb &&
- (bytes_read = conn->read (buffer, header.cb)) != header.cb))
+ client_request *req = NULL;
+
+ switch (header.request_code)
{
- header.error_code = -1;
- debug_printf("failed reading response \n");
+ case CYGSERVER_REQUEST_GET_VERSION:
+ req = safe_new0 (client_request_get_version);
+ break;
+ case CYGSERVER_REQUEST_SHUTDOWN:
+ req = safe_new0 (client_request_shutdown);
+ break;
+ case CYGSERVER_REQUEST_ATTACH_TTY:
+ req = safe_new0 (client_request_attach_tty);
+ break;
+ case CYGSERVER_REQUEST_SHM:
+ req = safe_new0 (client_request_shm);
+ break;
+ default:
+ syscall_printf ("unknown request code %d received: request ignored",
+ header.request_code);
return;
}
- debug_printf ("completed ok\n");
+
+ assert (req);
+
+ req->msglen (header.msglen);
+ req->handle (conn, cache);
+
+ safe_delete (req);
+
+#ifndef DEBUGGING
+ printf ("."); // A little noise when we're being quiet.
+#endif
}
-/* Oh, BTW: Fix the procedural basis and make this more intuitive. */
+#endif /* !__INSIDE_CYGWIN__ */
-int
-cygserver_request (client_request * req)
+client_request::client_request (request_code_t const id,
+ void * const buf,
+ size_t const buflen)
+ : _header (id, buflen),
+ _buf (buf),
+ _buflen (buflen)
{
- class transport_layer_base *transport;
+ assert ((!_buf && !_buflen) || (_buf && _buflen));
+}
- if (!req || allow_daemon != TRUE)
- return -1;
+client_request::~client_request ()
+{}
- /* dont' retry every request if the server's not there */
- if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION)
- return -1;
+int
+client_request::make_request ()
+{
+ assert (cygserver_running == CYGSERVER_UNKNOWN \
+ || cygserver_running == CYGSERVER_OK \
+ || cygserver_running == CYGSERVER_UNAVAIL);
- transport = create_server_transport ();
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
- /* FIXME: have at most one connection per thread. use TLS to store the details */
- /* logic is:
- * if not tlskey->conn, new conn,
- * then; transport=conn;
- */
- if (!transport->connect ())
+ 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)
{
- delete transport;
+ syscall_printf ("cygserver un-available");
+ error_code (ENOSYS);
return -1;
}
- debug_printf ("connected to server %p\n", transport);
+ transport_layer_base *const transport = create_server_transport ();
+
+ assert (transport);
+
+ if (transport->connect () == -1)
+ {
+ if (errno)
+ error_code (errno);
+ else
+ error_code (ENOSYS);
+ safe_delete (transport);
+ return -1;
+ }
- req->send(transport);
+ // verbose: debug_printf ("connected to server %p", transport);
- transport->close ();
+ send (transport);
- delete transport;
+ safe_delete (transport);
return 0;
}
-#if 0
-BOOL
+#ifndef __INSIDE_CYGWIN__
+
+/*
+ * 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 ());
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+bool
check_cygserver_available ()
{
- BOOL ret_val = FALSE;
- HANDLE pipe = CreateFile (pipe_name,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- &sec_all_nih,
- OPEN_EXISTING,
- 0,
- NULL);
- if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY)
- ret_val = TRUE;
-
- if (pipe && pipe != INVALID_HANDLE_VALUE)
- CloseHandle (pipe);
-
- return (ret_val);
+ 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 ();
}
-#endif
void
cygserver_init ()
{
- int rc;
- if (allow_daemon != TRUE)
+ if (!allow_daemon)
{
- cygserver_running = CYGSERVER_DEAD;
+ syscall_printf ("cygserver use disabled in client");
+ cygserver_running = CYGSERVER_UNAVAIL;
return;
}
- if (cygserver_running==CYGSERVER_OK)
+ assert (cygserver_running == CYGSERVER_UNKNOWN \
+ || cygserver_running == CYGSERVER_OK \
+ || cygserver_running == CYGSERVER_UNAVAIL);
+
+ if (cygserver_running == CYGSERVER_OK)
return;
- client_request_get_version *req =
- new client_request_get_version ();
-
- rc = cygserver_request (req);
- delete req;
- if (rc < 0)
- cygserver_running = CYGSERVER_DEAD;
- else if (rc > 0)
- api_fatal ("error connecting to cygwin server. error: %d", rc);
- else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR ||
- req->version.api != CYGWIN_SERVER_VERSION_API ||
- req->version.minor > CYGWIN_SERVER_VERSION_MINOR)
- api_fatal ("incompatible version of cygwin server.\n\
- 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,
- req->version.major,
- req->version.api,
- req->version.minor,
- req->version.patch);
- else
- cygserver_running = CYGSERVER_OK;
+ if (!check_cygserver_available ())
+ cygserver_running = CYGSERVER_UNAVAIL;
}