summaryrefslogtreecommitdiff
path: root/winsup/cygwin/cygserver_client.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/cygserver_client.cc')
-rwxr-xr-xwinsup/cygwin/cygserver_client.cc528
1 files changed, 528 insertions, 0 deletions
diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc
new file mode 100755
index 00000000000..f6683182d72
--- /dev/null
+++ b/winsup/cygwin/cygserver_client.cc
@@ -0,0 +1,528 @@
+/* cygserver_client.cc
+
+ Copyright 2001, 2002 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 "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.
+
+/* 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, &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).
+ */
+
+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;
+}
+
+#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))
+{
+ 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);
+}
+
+#else /* !__INSIDE_CYGWIN__ */
+
+client_request_attach_tty::client_request_attach_tty ()
+ : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
+{
+ // verbose: syscall_printf ("created");
+}
+
+#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 ());
+}
+
+#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);
+ }
+
+ client_request *req = NULL;
+
+ switch (header.request_code)
+ {
+ 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;
+ }
+
+ 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
+}
+
+#endif /* !__INSIDE_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 ()
+{}
+
+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);
+ safe_delete (transport);
+ return -1;
+ }
+
+ // verbose: debug_printf ("connected to server %p", transport);
+
+ send (transport);
+
+ safe_delete (transport);
+
+ return 0;
+}
+
+#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 ()
+{
+ 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_daemon)
+ {
+ 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;
+}