aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--winsup/cygserver/client.cc570
-rw-r--r--winsup/cygserver/cygserver.cc933
-rw-r--r--winsup/cygserver/ipc.h84
-rw-r--r--winsup/cygserver/process.cc658
-rw-r--r--winsup/cygserver/shm.cc1379
-rwxr-xr-xwinsup/cygserver/shm.h191
-rw-r--r--winsup/cygserver/threaded_queue.cc500
-rw-r--r--winsup/cygserver/transport.cc93
-rw-r--r--winsup/cygserver/transport_pipes.cc387
-rw-r--r--winsup/cygserver/transport_sockets.cc412
-rw-r--r--winsup/cygserver/woutsup.h110
-rw-r--r--winsup/cygwin/ChangeLog1107
-rw-r--r--winsup/cygwin/Makefile.in43
-rwxr-xr-xwinsup/cygwin/cygserver.cc933
-rwxr-xr-xwinsup/cygwin/cygserver_client.cc570
-rw-r--r--winsup/cygwin/cygserver_ipc.h84
-rwxr-xr-xwinsup/cygwin/cygserver_process.cc658
-rwxr-xr-xwinsup/cygwin/cygserver_shm.cc1379
-rw-r--r--winsup/cygwin/cygserver_shm.h191
-rwxr-xr-xwinsup/cygwin/cygserver_transport.cc93
-rwxr-xr-xwinsup/cygwin/cygserver_transport_pipes.cc387
-rwxr-xr-xwinsup/cygwin/cygserver_transport_sockets.cc412
-rw-r--r--winsup/cygwin/dcrt0.cc4
-rw-r--r--winsup/cygwin/fhandler_tty.cc31
-rwxr-xr-xwinsup/cygwin/include/cygwin/cygserver.h211
-rwxr-xr-xwinsup/cygwin/include/cygwin/cygserver_process.h166
-rwxr-xr-xwinsup/cygwin/include/cygwin/cygserver_transport.h41
-rwxr-xr-xwinsup/cygwin/include/cygwin/cygserver_transport_pipes.h64
-rwxr-xr-xwinsup/cygwin/include/cygwin/cygserver_transport_sockets.h53
-rw-r--r--winsup/cygwin/include/cygwin/ipc.h58
-rw-r--r--winsup/cygwin/include/cygwin/msg.h92
-rw-r--r--winsup/cygwin/include/cygwin/sem.h95
-rw-r--r--winsup/cygwin/include/cygwin/shm.h119
-rw-r--r--winsup/cygwin/msg.cc48
-rw-r--r--winsup/cygwin/safe_memory.h53
-rw-r--r--winsup/cygwin/sem.cc41
-rw-r--r--winsup/cygwin/shm.cc1051
-rwxr-xr-xwinsup/cygwin/threaded_queue.cc500
-rwxr-xr-xwinsup/cygwin/threaded_queue.h140
-rw-r--r--winsup/cygwin/tty.cc16
-rw-r--r--winsup/cygwin/woutsup.h110
41 files changed, 9524 insertions, 4543 deletions
diff --git a/winsup/cygserver/client.cc b/winsup/cygserver/client.cc
index 1df23ad..138c9dd 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;
}
diff --git a/winsup/cygserver/cygserver.cc b/winsup/cygserver/cygserver.cc
index af9cee9..0c07403 100644
--- a/winsup/cygserver/cygserver.cc
+++ b/winsup/cygserver/cygserver.cc
@@ -4,61 +4,141 @@
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. */
+
+#include "woutsup.h"
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
-#include "wincap.h"
-#include "cygwin_version.h"
+#include <string.h>
+#include <unistd.h>
-#include "getopt.h"
+#include "cygerrno.h"
+#include "cygwin_version.h"
-#include "cygwin/cygserver_transport.h"
-#include "cygwin/cygserver_transport_pipes.h"
-#include "cygwin/cygserver_transport_sockets.h"
-#include "threaded_queue.h"
-#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver.h"
-#include "cygserver_shm.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
-/* for quieter operation, set to 0 */
-#define DEBUG 0
-#define debug_printf if (DEBUG) printf
+// Version string.
+static const char version[] = "$Revision$";
-GENERIC_MAPPING access_mapping;
-static class transport_layer_base *transport;
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ * Copied verbatim from "strace.cc".
+ */
+static int
+getfunc (char *in_dst, const char *func)
+{
+ const char *p;
+ const char *pe;
+ char *dst = in_dst;
+ for (p = func; (pe = strchr (p, '(')); p = pe + 1)
+ if (isalnum ((int)pe[-1]) || pe[-1] == '_')
+ break;
+ else if (isspace ((int)pe[-1]))
+ {
+ pe--;
+ break;
+ }
+ if (!pe)
+ pe = strchr (func, '\0');
+ for (p = pe; p > func; p--)
+ if (p != pe && *p == ' ')
+ {
+ p++;
+ break;
+ }
+ if (*p == '*')
+ p++;
+ while (p < pe)
+ *dst++ = *p++;
+
+ *dst++ = ':';
+ *dst++ = ' ';
+ *dst = '\0';
+
+ return dst - in_dst;
+}
+
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ */
+extern "C" void
+__cygserver__printf (const char *const function, const char *const fmt, ...)
+{
+ const DWORD lasterror = GetLastError ();
+ const int lasterrno = errno;
+
+ va_list ap;
+
+ char *const buf = (char *) alloca (BUFSIZ);
+
+ assert (buf);
-DWORD request_count = 0;
+ int len = 0;
-BOOL
+ if (function)
+ len += getfunc (buf, function);
+
+ va_start (ap, fmt);
+ len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap);
+ va_end (ap);
+
+ len += snprintf (buf + len, BUFSIZ - len, "\n");
+
+ const int actual = (len > BUFSIZ ? BUFSIZ : len);
+
+ write (2, buf, actual);
+
+ errno = lasterrno;
+ SetLastError (lasterror);
+
+ return;
+}
+
+#ifdef DEBUGGING
+
+int __stdcall
+__set_errno (const char *func, int ln, int val)
+{
+ debug_printf ("%s:%d val %d", func, ln, val);
+ return _impure_ptr->_errno = val;
+}
+
+#endif /* DEBUGGING */
+
+GENERIC_MAPPING access_mapping;
+
+static BOOL
setup_privileges ()
{
BOOL rc, ret_val;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES sPrivileges;
- rc = OpenProcessToken (GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken) ;
+ rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ;
if (!rc)
{
- printf ("error opening process token (%lu)\n", GetLastError ());
+ system_printf ("error opening process token (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid);
if (!rc)
{
- printf ("error getting prigilege luid (%lu)\n", GetLastError ());
+ system_printf ("error getting privilege luid (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
@@ -67,7 +147,8 @@ setup_privileges ()
rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ;
if (!rc)
{
- printf ("error adjusting prigilege level. (%lu)\n", GetLastError ());
+ system_printf ("error adjusting privilege level. (%lu)",
+ GetLastError ());
ret_val = FALSE;
goto out;
}
@@ -89,179 +170,223 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
- HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE)
+ HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE)
{
HANDLE local_handle = NULL;
int ret_val = EACCES;
- char sd_buf [1024];
- PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
- DWORD bytes_needed;
- PRIVILEGE_SET ps;
- DWORD ps_len = sizeof (ps);
- BOOL status;
if (from_process != GetCurrentProcess ())
{
+ if (!DuplicateHandle (from_process, from_handle,
+ GetCurrentProcess (), &local_handle,
+ 0, bInheritHandle,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("error getting handle(%u) to server (%lu)",
+ (unsigned int)from_handle, GetLastError ());
+ goto out;
+ }
+ } else
+ local_handle = from_handle;
- if (!DuplicateHandle (from_process, from_handle,
- GetCurrentProcess (), &local_handle,
- 0, bInheritHandle,
- DUPLICATE_SAME_ACCESS))
- {
- printf ("error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ());
- goto out;
- }
-} else
- local_handle = from_handle;
-
- if (!GetKernelObjectSecurity (local_handle,
- OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
- sd, sizeof (sd_buf), &bytes_needed))
+ if (!wincap.has_security ())
+ assert (!from_process_token);
+ else
{
- printf ("error getting handle SD (%lu)\n", GetLastError ());
- goto out;
- }
+ char sd_buf [1024];
+ PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
+ DWORD bytes_needed;
+ PRIVILEGE_SET ps;
+ DWORD ps_len = sizeof (ps);
+ BOOL status;
+
+ if (!GetKernelObjectSecurity (local_handle,
+ (OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION),
+ sd, sizeof (sd_buf), &bytes_needed))
+ {
+ system_printf ("error getting handle SD (%lu)", GetLastError ());
+ goto out;
+ }
- MapGenericMask (&access, &access_mapping);
+ MapGenericMask (&access, &access_mapping);
- if (!AccessCheck (sd, from_process_token, access, &access_mapping,
- &ps, &ps_len, &access, &status))
- {
- printf ("error checking access rights (%lu)\n", GetLastError ());
- goto out;
- }
+ if (!AccessCheck (sd, from_process_token, access, &access_mapping,
+ &ps, &ps_len, &access, &status))
+ {
+ system_printf ("error checking access rights (%lu)",
+ GetLastError ());
+ goto out;
+ }
- if (!status)
- {
- printf ("access to object denied\n");
- goto out;
+ if (!status)
+ {
+ system_printf ("access to object denied");
+ goto out;
+ }
}
if (!DuplicateHandle (from_process, from_handle,
to_process, to_handle_ptr,
access, bInheritHandle, 0))
{
- printf ("error getting handle to client (%lu)\n", GetLastError ());
+ system_printf ("error getting handle to client (%lu)", GetLastError ());
goto out;
}
- debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
+
+ // verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr);
ret_val = 0;
-out:
+ out:
if (local_handle && from_process != GetCurrentProcess ())
CloseHandle (local_handle);
return (ret_val);
}
-void
-client_request::serve (transport_layer_base *conn, class process_cache *cache)
-{
- printf ("*****************************************\n"
- "A call to the base client_request class has occured\n"
- "This indicates a mismatch in a virtual function definition somewhere\n");
- exit (1);
-}
+/*
+ * client_request_attach_tty::serve ()
+ */
void
-client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
+client_request_attach_tty::serve (transport_layer_base *const conn,
+ process_cache *)
{
- HANDLE from_process_handle = NULL;
- HANDLE to_process_handle = NULL;
- HANDLE token_handle = NULL;
- DWORD rc;
+ assert (conn);
+
+ assert (!error_code ());
- if (header.cb != sizeof (req))
+ if (!wincap.has_security ())
{
- header.error_code = EINVAL;
+ syscall_printf ("operation only supported on systems with security");
+ error_code (EINVAL);
+ msglen (0);
return;
}
- debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid,
- req.from_master, req.to_master,
- req.pid);
-
- debug_printf ("opening process %ld\n", req.master_pid);
- from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
- debug_printf ("opening process %ld\n", req.pid);
- to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
- if (!from_process_handle || !to_process_handle)
+ if (msglen () != sizeof (req))
{
- printf ("error opening process (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (req), msglen ());
+ error_code (EINVAL);
+ msglen (0);
+ return;
}
- debug_printf ("Impersonating client\n");
- conn->impersonate_client ();
+ msglen (0); // Until we fill in some fields.
- debug_printf ("about to open thread token\n");
- rc = OpenThreadToken (GetCurrentThread (),
- TOKEN_QUERY,
- TRUE,
- &token_handle);
+ // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld",
+ // req.master_pid, req.from_master, req.to_master,
+ // req.pid);
- debug_printf ("opened thread token, rc=%lu\n", rc);
- conn->revert_to_self ();
+ // verbose: debug_printf ("opening process %ld", req.master_pid);
- if (!rc)
+ const HANDLE from_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
+
+ if (!from_process_handle)
{
- printf ("error opening thread token (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ system_printf ("error opening `from' process, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ return;
}
- if (check_and_dup_handle (from_process_handle, to_process_handle,
- token_handle,
- GENERIC_READ,
- req.from_master,
- &req.from_master, TRUE) != 0)
+ // verbose: debug_printf ("opening process %ld", req.pid);
+
+ const HANDLE to_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
+
+ if (!to_process_handle)
{
- printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ system_printf ("error opening `to' process, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ error_code (EACCES);
+ return;
}
- if (req.to_master)
+ // verbose: debug_printf ("Impersonating client");
+ conn->impersonate_client ();
+
+ HANDLE token_handle = NULL;
+
+ // verbose: debug_printf ("about to open thread token");
+ const DWORD rc = OpenThreadToken (GetCurrentThread (),
+ TOKEN_QUERY,
+ TRUE,
+ &token_handle);
+
+ // verbose: debug_printf ("opened thread token, rc=%lu", rc);
+ conn->revert_to_self ();
+
+ if (!rc)
{
- if (check_and_dup_handle (from_process_handle, to_process_handle,
- token_handle,
- GENERIC_WRITE,
- req.to_master,
- &req.to_master, TRUE) != 0)
- {
- printf ("error duplicating to_master handle (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
- }
+ system_printf ("error opening thread token, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ error_code (EACCES);
+ return;
}
-#if DEBUG
- printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
- req.from_master, req.to_master);
-#endif
+ // From this point on, a reply body is returned to the client.
- header.error_code = 0;
+ const HANDLE from_master = req.from_master;
+ const HANDLE to_master = req.to_master;
-out:
- if (from_process_handle)
- CloseHandle (from_process_handle);
- if (to_process_handle)
- CloseHandle (to_process_handle);
- if (token_handle)
- CloseHandle (token_handle);
+ req.from_master = NULL;
+ req.to_master = NULL;
+
+ msglen (sizeof (req));
+
+ if (from_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_READ,
+ from_master,
+ &req.from_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating from_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ if (to_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_WRITE,
+ to_master,
+ &req.to_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating to_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ CloseHandle (token_handle);
+
+ debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
+ req.master_pid, from_master, to_master,
+ req.pid, req.from_master, req.to_master);
+
+ return;
}
void
-client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
+client_request_get_version::serve (transport_layer_base *, process_cache *)
{
- if (header.cb != sizeof (version))
- {
- header.error_code = EINVAL;
- return;
- }
- header.error_code = 0;
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
+ msglen (sizeof (version));
+
version.major = CYGWIN_SERVER_VERSION_MAJOR;
version.api = CYGWIN_SERVER_VERSION_API;
version.minor = CYGWIN_SERVER_VERSION_MINOR;
@@ -270,280 +395,380 @@ client_request_get_version::serve(transport_layer_base *conn, class process_cach
class server_request : public queue_request
{
- public:
- server_request (transport_layer_base *newconn, class process_cache *newcache);
- virtual void process ();
- private:
- char request_buffer [MAX_REQUEST_SIZE];
- transport_layer_base *conn;
- class process_cache *cache;
-};
+public:
+ server_request (transport_layer_base *const conn, process_cache *const cache)
+ : _conn (conn), _cache (cache)
+ {}
-class server_process_param : public queue_process_param
-{
- public:
- transport_layer_base *transport;
- server_process_param () : queue_process_param (false) {};
-};
+ virtual ~server_request ()
+ {
+ safe_delete (_conn);
+ }
-class server_request_queue : public threaded_queue
-{
- public:
- class process_cache *cache;
- void process_requests (transport_layer_base *transport);
- virtual void add (transport_layer_base *conn);
+ virtual void process ()
+ {
+ client_request::handle_request (_conn, _cache);
+ }
+
+private:
+ transport_layer_base *const _conn;
+ process_cache *const _cache;
};
-class server_request_queue request_queue;
-static DWORD WINAPI
-request_loop (LPVOID LpParam)
+class server_submission_loop : public queue_submission_loop
{
- class server_process_param *params = (server_process_param *) LpParam;
- class server_request_queue *queue = (server_request_queue *) params->queue;
- class transport_layer_base * transport = params->transport;
- while (queue->active)
+public:
+ server_submission_loop (threaded_queue *const queue,
+ transport_layer_base *const transport,
+ process_cache *const cache)
+ : queue_submission_loop (queue, false),
+ _transport (transport),
+ _cache (cache)
{
- transport_layer_base * new_conn = transport->accept ();
- /* FIXME: this is a little ugly. What we really want is to wait on two objects:
- * one for the pipe/socket, and one for being told to shutdown. Otherwise
- * this will stay a problem (we won't actually shutdown until the request
- * _AFTER_ the shutdown request. And sending ourselves a request is ugly
- */
- if (new_conn && queue->active)
- queue->add (new_conn);
+ assert (_transport);
+ assert (_cache);
}
- return 0;
-}
-/* TODO: check we are not being asked to service a already serviced transport */
+private:
+ transport_layer_base *const _transport;
+ process_cache *const _cache;
+
+ virtual void request_loop ();
+};
+
+/* FIXME: this is a little ugly. What we really want is to wait on
+ * two objects: one for the pipe/socket, and one for being told to
+ * shutdown. Otherwise this will stay a problem (we won't actually
+ * shutdown until the request _AFTER_ the shutdown request. And
+ * sending ourselves a request is ugly
+ */
void
-server_request_queue::process_requests (transport_layer_base *transport)
+server_submission_loop::request_loop ()
{
- class server_process_param *params = new server_process_param;
- params->transport = transport;
- threaded_queue::process_requests (params, request_loop);
+ /* I'd like the accepting thread's priority to be above any "normal"
+ * thread in the system to avoid overflowing the listen queue (for
+ * sockets; similar issues exist for named pipes); but, for example,
+ * a normal priority thread in a foregrounded process is boosted to
+ * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current
+ * thread's priority to a level one above that. This fails on
+ * win9x/ME so assume any failure in that call is due to that and
+ * simply call again at one priority level lower.
+ */
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise accept thread priority, error = %lu",
+ GetLastError ());
+
+ while (_running)
+ {
+ bool recoverable = false;
+ transport_layer_base *const conn = _transport->accept (&recoverable);
+ if (!conn && !recoverable)
+ {
+ system_printf ("fatal error on IPC transport: closing down");
+ return;
+ }
+ // EINTR probably implies a shutdown request; so back off for a
+ // moment to let the main thread take control, otherwise the
+ // server spins here receiving EINTR repeatedly since the signal
+ // handler in the main thread doesn't get a chance to be called.
+ if (!conn && errno == EINTR)
+ {
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL))
+ debug_printf ("failed to reset thread priority, error = %lu",
+ GetLastError ());
+
+ Sleep (0);
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise thread priority, error = %lu",
+ GetLastError ());
+ }
+ if (conn)
+ _queue->add (safe_new (server_request, conn, _cache));
+ }
+}
+
+client_request_shutdown::client_request_shutdown ()
+ : client_request (CYGSERVER_REQUEST_SHUTDOWN)
+{
+ // verbose: syscall_printf ("created");
}
void
-client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
+client_request_shutdown::serve (transport_layer_base *, process_cache *)
{
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
- /* tell the main thread to shutdown */
- request_queue.active=false;
+
+ kill (getpid (), SIGINT);
+
+ msglen (0);
}
-server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
+static sig_atomic_t shutdown_server = false;
+
+static void
+handle_signal (const int signum)
{
- conn = newconn;
- cache = newcache;
+ /* any signal makes us die :} */
+
+ shutdown_server = true;
}
-void
-server_request::process ()
+/*
+ * print_usage ()
+ */
+
+static void
+print_usage (const char *const pgm)
{
- ssize_t bytes_read, bytes_written;
- struct request_header* req_ptr = (struct request_header*) &request_buffer;
- client_request *req = NULL;
- debug_printf ("about to read\n");
+ printf ("Usage: %s [OPTIONS]\n", pgm);
+ printf (" -c, --cleanup-threads number of cleanup threads to use\n");
+ printf (" -h, --help output usage information and exit\n");
+ printf (" -r, --request-threads number of request threads to use\n");
+ printf (" -s, --shutdown shutdown the daemon\n");
+ printf (" -v, --version output version information and exit\n");
+}
- bytes_read = conn->read (request_buffer, sizeof (struct request_header));
- if (bytes_read != sizeof (struct request_header))
- {
- printf ("error reading from connection (%lu)\n", GetLastError ());
- goto out;
- }
- debug_printf ("got header (%ld)\n", bytes_read);
+/*
+ * print_version ()
+ */
- switch (req_ptr->req_id)
- {
- case CYGSERVER_REQUEST_GET_VERSION:
- req = new client_request_get_version (); break;
- case CYGSERVER_REQUEST_ATTACH_TTY:
- req = new client_request_attach_tty (); break;
- case CYGSERVER_REQUEST_SHUTDOWN:
- req = new client_request_shutdown (); break;
- case CYGSERVER_REQUEST_SHM_GET:
- req = new client_request_shm (); break;
- default:
- req = new client_request (CYGSERVER_REQUEST_INVALID, 0);
- req->header.error_code = ENOSYS;
- debug_printf ("Bad client request - returning ENOSYS\n");
- }
+static void
+print_version (const char *const pgm)
+{
+ char *vn = NULL;
- if (req->header.cb != req_ptr->cb)
- {
- debug_printf ("Mismatch in request buffer sizes\n");
- goto out;
- }
+ const char *const colon = strchr (version, ':');
- if (req->header.cb)
+ if (!colon)
{
-
- bytes_read = conn->read (req->buffer, req->header.cb);
- if (bytes_read != req->header.cb)
- {
- debug_printf ("error reading from connection (%lu)\n", GetLastError ());
- goto out;
- }
- debug_printf ("got body (%ld)\n",bytes_read);
+ vn = strdup ("?");
}
+ else
+ {
+ vn = strdup (colon + 2); // Skip ": "
- /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
- req->serve (conn, cache);
+ char *const spc = strchr (vn, ' ');
- if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header)))
- != sizeof(req->header) || (req->header.cb &&
- (bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb))
- {
- req->header.error_code = -1;
- printf ("error writing to connection (%lu)\n", GetLastError ());
- goto out;
+ if (spc)
+ *spc = '\0';
}
- debug_printf("Sent reply, size (%ld)\n",bytes_written);
- printf (".");
-
-out:
- conn->close ();
- delete conn;
- if (req)
- delete (req);
+ char buf[200];
+ snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
+ cygwin_version.dll_major / 1000,
+ cygwin_version.dll_major % 1000,
+ cygwin_version.dll_minor,
+ cygwin_version.api_major,
+ cygwin_version.api_minor,
+ cygwin_version.shared_data,
+ CYGWIN_SERVER_VERSION_MAJOR,
+ CYGWIN_SERVER_VERSION_API,
+ CYGWIN_SERVER_VERSION_MINOR,
+ CYGWIN_SERVER_VERSION_PATCH,
+ cygwin_version.mount_registry,
+ cygwin_version.dll_build_date);
+
+ printf ("%s (cygwin) %s\n", pgm, vn);
+ printf ("API version %s\n", buf);
+ printf ("Copyright 2001, 2002 Red Hat, Inc.\n");
+ printf ("Compiled on %s\n", __DATE__);
+
+ free (vn);
}
-void
-server_request_queue::add (transport_layer_base *conn)
-{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- /* every derived ::add must enter the section! */
- EnterCriticalSection (&queuelock);
- if (!running)
- {
- conn->close ();
- delete conn;
- LeaveCriticalSection (&queuelock);
- return;
- }
- queue_request * listrequest = new server_request (conn, cache);
- threaded_queue::add (listrequest);
- LeaveCriticalSection (&queuelock);
-}
+/*
+ * main ()
+ */
-void
-handle_signal (int signal)
+int
+main (const int argc, char *argv[])
{
- /* any signal makes us die :} */
- /* FIXME: link upwards, and then this becomes a trivial method call to
- * only shutdown _this queue_
- */
- /* tell the main thread to shutdown */
- request_queue.active=false;
-}
+ const struct option longopts[] = {
+ {"cleanup-threads", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {"request-threads", required_argument, NULL, 'r'},
+ {"shutdown", no_argument, NULL, 's'},
+ {"version", no_argument, NULL, 'v'},
+ {0, no_argument, NULL, 0}
+ };
-struct option longopts[] = {
- {"shutdown", no_argument, NULL, 's'},
- {0, no_argument, NULL, 0}
-};
+ const char opts[] = "c:hr:sv";
-char opts[] = "s";
+ int cleanup_threads = 2;
+ int request_threads = 10;
+ bool shutdown = false;
-int
-main (int argc, char **argv)
-{
- int shutdown=0;
- char i;
+ const char *pgm = NULL;
+
+ if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
+ pgm = *argv;
+ else
+ pgm++;
- while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
- switch (i)
+ wincap.init ();
+ if (wincap.has_security ())
+ setup_privileges ();
+
+ int opt;
+
+ while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+ switch (opt)
{
- case 's':
- shutdown = 1;
+ case 'c':
+ cleanup_threads = atoi (optarg);
+ if (cleanup_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of cleanup threads must be positive\n",
+ pgm);
+ exit (1);
+ }
+ break;
+
+ case 'h':
+ print_usage (pgm);
+ return 0;
+
+ case 'r':
+ request_threads = atoi (optarg);
+ if (request_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of request threads must be positive\n",
+ pgm);
+ exit (1);
+ }
break;
- default:
+
+ case 's':
+ shutdown = true;
break;
- /*NOTREACHED*/
+
+ case 'v':
+ print_version (pgm);
+ return 0;
+
+ case '?':
+ fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
+ exit (1);
}
- wincap.init();
- if (wincap.has_security ())
- setup_privileges ();
- transport = create_server_transport ();
+ if (optind != argc)
+ {
+ fprintf (stderr, "%s: too many arguments\n", pgm);
+ exit (1);
+ }
if (shutdown)
{
- if (!transport->connect())
+ /* Setting `cygserver_running' stops the request code making a
+ * version request, which is not much to the point.
+ */
+ cygserver_running = CYGSERVER_OK;
+
+ client_request_shutdown req;
+
+ if (req.make_request () == -1 || req.error_code ())
{
- printf ("couldn't establish connection with server\n");
+ fprintf (stderr, "%s: shutdown request failed: %s\n",
+ pgm, strerror (req.error_code ()));
exit (1);
}
- client_request_shutdown *request =
- new client_request_shutdown ();
- request->send (transport);
- transport->close();
- delete transport;
- delete request;
- exit(0);
+
+ // FIXME: It would be nice to wait here for the daemon to exit.
+
+ return 0;
}
- char version[200];
- /* Cygwin dll release */
- snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
- cygwin_version.dll_major / 1000,
- cygwin_version.dll_major % 1000,
- cygwin_version.dll_minor,
- cygwin_version.api_major,
- cygwin_version.api_minor,
- cygwin_version.shared_data,
- CYGWIN_SERVER_VERSION_MAJOR,
- CYGWIN_SERVER_VERSION_API,
- CYGWIN_SERVER_VERSION_MINOR,
- CYGWIN_SERVER_VERSION_PATCH,
- cygwin_version.mount_registry,
- cygwin_version.dll_build_date);
+#define SIGHANDLE(SIG) \
+ do \
+ { \
+ struct sigaction act; \
+ \
+ act.sa_handler = &handle_signal; \
+ act.sa_mask = 0; \
+ act.sa_flags = 0; \
+ \
+ if (sigaction (SIG, &act, NULL) == -1) \
+ { \
+ system_printf ("failed to install handler for " #SIG ": %s", \
+ strerror (errno)); \
+ exit (1); \
+ } \
+ } while (false)
+
+ SIGHANDLE (SIGHUP);
+ SIGHANDLE (SIGINT);
+ SIGHANDLE (SIGTERM);
+
+ print_version (pgm);
setbuf (stdout, NULL);
- printf ("daemon version %s starting up", version);
- if (signal (SIGQUIT, handle_signal) == SIG_ERR)
+ printf ("daemon starting up");
+
+ threaded_queue request_queue (request_threads);
+ printf (".");
+
+ transport_layer_base *const transport = create_server_transport ();
+ assert (transport);
+ printf (".");
+
+ process_cache cache (cleanup_threads);
+ printf (".");
+
+ server_submission_loop submission_loop (&request_queue, transport, &cache);
+ printf (".");
+
+ request_queue.add_submission_loop (&submission_loop);
+ printf (".");
+
+ if (transport->listen () == -1)
{
- printf ("\ncould not install signal handler (%d)- aborting startup\n", errno);
exit (1);
}
printf (".");
- transport->listen ();
- printf (".");
- class process_cache cache (2);
- request_queue.initial_workers = 10;
- request_queue.cache = &cache;
- request_queue.create_workers ();
- printf (".");
- request_queue.process_requests (transport);
+
+ cache.start ();
printf (".");
- cache.create_workers ();
+
+ request_queue.start ();
printf (".");
- cache.process_requests ();
- printf (".complete\n");
- /* TODO: wait on multiple objects - the thread handle for each request loop +
- * all the process handles. This should be done by querying the request_queue and
- * the process cache for all their handles, and then waiting for (say) 30 seconds.
- * after that we recreate the list of handles to wait on, and wait again.
- * the point of all this abstraction is that we can trivially server both sockets
- * and pipes simply by making a new transport, and then calling
- * request_queue.process_requests (transport2);
+
+ printf ("complete\n");
+
+ /* TODO: wait on multiple objects - the thread handle for each
+ * request loop + all the process handles. This should be done by
+ * querying the request_queue and the process cache for all their
+ * handles, and then waiting for (say) 30 seconds. after that we
+ * recreate the list of handles to wait on, and wait again. the
+ * point of all this abstraction is that we can trivially server
+ * both sockets and pipes simply by making a new transport, and then
+ * calling request_queue.process_requests (transport2);
*/
/* WaitForMultipleObjects abort && request_queue && process_queue && signal
-- if signal event then retrigger it
- */
- while (1 && request_queue.active)
- {
- sleep (1);
- }
- printf ("\nShutdown request recieved - new requests will be denied\n");
- request_queue.cleanup ();
+ */
+ while (!shutdown_server && request_queue.running () && cache.running ())
+ pause ();
+
+ printf ("\nShutdown request received - new requests will be denied\n");
+ request_queue.stop ();
printf ("All pending requests processed\n");
- transport->close ();
+ safe_delete (transport);
printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
- cache.cleanup ();
+ cache.stop ();
printf ("All outstanding process-cache activities completed\n");
printf ("daemon shutdown\n");
+
+ return 0;
}
diff --git a/winsup/cygserver/ipc.h b/winsup/cygserver/ipc.h
new file mode 100644
index 0000000..0d0ebbc
--- /dev/null
+++ b/winsup/cygserver/ipc.h
@@ -0,0 +1,84 @@
+/* cygserver_ipc.h
+
+ Copyright 2002 Red Hat, Inc.
+
+ Originally written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+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. */
+
+#ifndef __CYGSERVER_IPC_H__
+#define __CYGSERVER_IPC_H__
+
+#include <assert.h>
+#include <limits.h> /* For OPEN_MAX. */
+
+/*
+ * The sysv ipc id's (msgid, semid, shmid) are integers arranged such
+ * that they no subsystem will generate the same id as some other
+ * subsystem; nor do these ids overlap file descriptors (the other
+ * common integer ids). Since Cygwin can allocate more than OPEN_MAX
+ * file descriptors, it can't be guaranteed not to overlap, but it
+ * should help catch some errors.
+ *
+ * msgid's: OPEN_MAX, OPEN_MAX + 3, OPEN_MAX + 6, . . .
+ * semid's: OPEN_MAX + 1, OPEN_MAX + 4, OPEN_MAX + 7, . . .
+ * shmid's: OPEN_MAX + 2, OPEN_MAX + 5, OPEN_MAX + 8, . . .
+ *
+ * To further ensure that ids are unique, if ipc objects are created
+ * and destroyed and then re-created, they are given new ids by
+ * munging the basic id (as above) with a sequence number.
+ *
+ * Internal ipc id's, which are 0, 1, ... within each subsystem (and
+ * not munged with a sequence number), are used solely by the ipcs(8)
+ * interface.
+ */
+
+enum ipc_subsys_t
+ {
+ IPC_MSGOP = 0,
+ IPC_SEMOP = 1,
+ IPC_SHMOP = 2,
+ IPC_SUBSYS_COUNT
+ };
+
+/*
+ * IPCMNI - The absolute maximum number of simultaneous ipc ids for
+ * any one subsystem.
+ */
+
+enum
+ {
+ IPCMNI = 0x10000 // Must be a power of two.
+ };
+
+inline int
+ipc_int2ext (const int intid, const ipc_subsys_t subsys, long & sequence)
+{
+ assert (0 <= intid && intid < IPCMNI);
+
+ const long tmp = InterlockedIncrement (&sequence);
+
+ return (((tmp & 0x7fff) << 16)
+ | (OPEN_MAX + (intid * IPC_SUBSYS_COUNT) + subsys));
+}
+
+inline int
+ipc_ext2int_subsys (const int extid)
+{
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) % IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_ext2int (const int extid, const ipc_subsys_t subsys)
+{
+ if (ipc_ext2int_subsys (extid) != subsys)
+ return -1;
+ else
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) / IPC_SUBSYS_COUNT;
+}
+
+#endif /* __CYGSERVER_IPC_H__ */
diff --git a/winsup/cygserver/process.cc b/winsup/cygserver/process.cc
index dd13f37..7118bbc 100644
--- a/winsup/cygserver/process.cc
+++ b/winsup/cygserver/process.cc
@@ -4,385 +4,429 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
+#include "woutsup.h"
+
+#include <sys/types.h>
+
+#include <assert.h>
#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
-#include <pthread.h>
-#include <threaded_queue.h>
-#include <cygwin/cygserver_process.h>
-
-#define debug_printf if (DEBUG) printf
-#define DEBUG 1
-
-/* the cache structures and classes are designed for one cache per server process.
- * To make multiple process caches, a redesign will be needed
- */
-/* process cache */
-process_cache::process_cache (unsigned int num_initial_workers):
-head (NULL)
+#include "cygerrno.h"
+
+#include "cygwin/cygserver_process.h"
+
+/*****************************************************************************/
+
+#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
+
+/*****************************************************************************/
+
+process_cleanup::~process_cleanup ()
{
- /* there can only be one */
- InitializeCriticalSection (&cache_write_access);
- if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
- {
- printf ("Failed to create cache add trigger (%lu), terminating\n",
- GetLastError ());
- exit (1);
- }
- initial_workers = num_initial_workers;
+ safe_delete (_process);
}
-process_cache::~process_cache ()
+void
+process_cleanup::process ()
{
+ _process->cleanup ();
}
-class process *
-process_cache::process (long pid)
+/*****************************************************************************/
+
+/* cleanup_routine */
+cleanup_routine::~cleanup_routine ()
{
- class process *entry = head;
- /* TODO: make this more granular, so a search doesn't involve the write lock */
- EnterCriticalSection (&cache_write_access);
- if (!entry)
+}
+
+/*****************************************************************************/
+
+process::process (const pid_t cygpid, const DWORD winpid)
+ : _cygpid (cygpid),
+ _winpid (winpid),
+ _hProcess (NULL),
+ _cleaning_up (false),
+ _exit_status (STILL_ACTIVE),
+ _routines_head (NULL),
+ _next (NULL)
+{
+ _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
+ if (!_hProcess)
{
- entry = new class process (pid);
- entry->next =
- (class process *) InterlockedExchangePointer (&head, entry);
- PulseEvent (cache_add_trigger);
+ system_printf ("unable to obtain handle for new cache process %d(%lu)",
+ _cygpid, _winpid);
+ _hProcess = INVALID_HANDLE_VALUE;
+ _exit_status = 0;
}
else
- {
- while (entry->winpid != pid && entry->next)
- entry = entry->next;
- if (entry->winpid != pid)
- {
- class process *new_entry = new class process (pid);
- new_entry->next =
- (class process *) InterlockedExchangePointer (&entry->next,
- new_entry);
- entry = new_entry;
- PulseEvent (cache_add_trigger);
- }
- }
- LeaveCriticalSection (&cache_write_access);
- return entry;
+ debug_printf ("got handle %p for new cache process %d(%lu)",
+ _hProcess, _cygpid, _winpid);
+ InitializeCriticalSection (&_access);
}
-static DWORD WINAPI
-request_loop (LPVOID LpParam)
+process::~process ()
{
- class process_process_param *params = (process_process_param *) LpParam;
- return params->request_loop ();
+ DeleteCriticalSection (&_access);
+ (void) CloseHandle (_hProcess);
}
-void
-process_cache::process_requests ()
+/* No need to be thread-safe as this is only ever called by
+ * process_cache::remove_process (). If it has to be made thread-safe
+ * later on, it should not use the `access' critical section as that
+ * is held by the client request handlers for an arbitrary length of
+ * time, i.e. while they do whatever processing is required for a
+ * client request.
+ */
+DWORD
+process::check_exit_code ()
{
- class process_process_param *params = new process_process_param;
- threaded_queue::process_requests (params, request_loop);
+ if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
+ && _exit_status == STILL_ACTIVE
+ && !GetExitCodeProcess (_hProcess, &_exit_status))
+ {
+ system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
+ _cygpid, _winpid, GetLastError ());
+ _hProcess = INVALID_HANDLE_VALUE;
+ }
+ return _exit_status;
}
-void
-process_cache::add_task (class process * theprocess)
+bool
+process::add (cleanup_routine *const entry)
{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- /* every derived ::add must enter the section! */
- EnterCriticalSection (&queuelock);
- queue_request *listrequest = new process_cleanup (theprocess);
- threaded_queue::add (listrequest);
- LeaveCriticalSection (&queuelock);
-}
+ assert (entry);
-/* NOT fully MT SAFE: must be called by only one thread in a program */
-void
-process_cache::remove_process (class process *theprocess)
-{
- class process *entry = head;
- /* unlink */
- EnterCriticalSection (&cache_write_access);
- if (entry == theprocess)
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
{
- entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
- if (entry != theprocess)
- {
- printf ("Bug encountered, process cache corrupted\n");
- exit (1);
- }
+ entry->_next = _routines_head;
+ _routines_head = entry;
+ res = true;
}
- else
+
+ LeaveCriticalSection (&_access);
+ return res;
+}
+
+bool
+process::remove (const cleanup_routine *const entry)
+{
+ assert (entry);
+
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
{
- while (entry->next && entry->next != theprocess)
- entry = entry->next;
- class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
- if (temp != theprocess)
+ cleanup_routine *previous = NULL;
+
+ for (cleanup_routine *ptr = _routines_head;
+ ptr;
+ previous = ptr, ptr = ptr->_next)
{
- printf ("Bug encountered, process cache corrupted\n");
- exit (1);
+ if (*ptr == *entry)
+ {
+ if (previous)
+ previous->_next = ptr->_next;
+ else
+ _routines_head = ptr->_next;
+
+ safe_delete (ptr);
+ res = true;
+ break;
+ }
}
}
- LeaveCriticalSection (&cache_write_access);
- /* Process any cleanup tasks */
- add_task (theprocess);
+
+ LeaveCriticalSection (&_access);
+ return res;
}
-/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of
- * begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0]
- * NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed.
- * Who cares - It'll get caught the next time.
+/* This is single threaded. It's called after the process is removed
+ * from the cache, but inserts may be attemped by worker threads that
+ * have a pointer to it.
*/
-int
-process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
- ssize_t max_copy, int begin_at)
+void
+process::cleanup ()
{
- /* TODO:? grab a delete-lock, to prevent deletes during this process ? */
- class process *entry = head;
- int count = begin_at;
- /* skip begin_at entries */
- while (entry && count)
- {
- if (entry->exit_code () == STILL_ACTIVE)
- count--;
- entry = entry->next;
- }
- /* hit the end of the list within begin_at entries */
- if (count)
- return 0;
- HANDLE *hto = hdest;
- class process **eto = edest;
- while (entry && count < max_copy)
+ EnterCriticalSection (&_access);
+ assert (!is_active ());
+ assert (!_cleaning_up);
+ InterlockedExchange (&_cleaning_up, true);
+ cleanup_routine *entry = _routines_head;
+ _routines_head = NULL;
+ LeaveCriticalSection (&_access);
+
+ while (entry)
{
- /* hack */
- if (entry->exit_code () == STILL_ACTIVE)
- {
- *hto = entry->handle ();
- *eto = entry;
- count++;
- hto++;
- eto++;
- }
- entry = entry->next;
+ cleanup_routine *const ptr = entry;
+ entry = entry->_next;
+ ptr->cleanup (this);
+ safe_delete (ptr);
}
- return count;
}
-/* process's */
-/* global process crit section */
-static CRITICAL_SECTION process_access;
-static pthread_once_t process_init;
+/*****************************************************************************/
void
-do_process_init (void)
+process_cache::submission_loop::request_loop ()
{
- InitializeCriticalSection (&process_access);
- /* we don't have a cache shutdown capability today */
+ assert (this);
+ assert (_cache);
+ assert (_interrupt_event);
+
+ while (_running)
+ _cache->wait_for_processes (_interrupt_event);
}
-process::process (long pid):
-winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
+/*****************************************************************************/
+
+process_cache::process_cache (const unsigned int initial_workers)
+ : _queue (initial_workers),
+ _submitter (this, &_queue), // true == interruptible
+ _processes_count (0),
+ _processes_head (NULL),
+ _cache_add_trigger (NULL)
{
- pthread_once (&process_init, do_process_init);
- EnterCriticalSection (&process_access);
- thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
- if (!thehandle)
+ /* there can only be one */
+ InitializeCriticalSection (&_cache_write_access);
+
+ _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_cache_add_trigger)
{
- printf ("unable to obtain handle for new cache process %ld\n", pid);
- thehandle = INVALID_HANDLE_VALUE;
+ system_printf ("failed to create cache add trigger, error = %lu",
+ GetLastError ());
+ abort ();
}
- debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
- InitializeCriticalSection (&access);
- LeaveCriticalSection (&process_access);
-}
-process::~process ()
-{
- DeleteCriticalSection (&access);
+ _queue.add_submission_loop (&_submitter);
}
-HANDLE
-process::handle ()
+process_cache::~process_cache ()
{
-// DWORD exitstate = exit_code ();
-// if (exitstate == STILL_ACTIVE)
- return thehandle;
-
- /* FIXME: call the cleanup list ? */
-
-// CloseHandle (thehandle);
-// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
-// winpid);
-// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
-// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid);
-// /* FIXME: what if OpenProcess fails ? */
-// if (thehandle)
-// {
-// _exit_status = STILL_ACTIVE;
-// exit_code ();
-// }
-// else
-// thehandle = INVALID_HANDLE_VALUE;
-// return thehandle;
+ (void) CloseHandle (_cache_add_trigger);
+ DeleteCriticalSection (&_cache_write_access);
}
-DWORD process::exit_code ()
+/* This returns the process object to the caller already locked, that
+ * is, with the object's `access' critical region entered. Thus the
+ * caller must unlock the object when it's finished with it (via
+ * process::release ()). It must then not try to access the object
+ * afterwards, except by going through this routine again, as it may
+ * have been deleted once it has been unlocked.
+ */
+class process *
+process_cache::process (const pid_t cygpid, const DWORD winpid)
{
- if (_exit_status != STILL_ACTIVE)
- return _exit_status;
- bool
- err = GetExitCodeProcess (thehandle, &_exit_status);
- if (!err)
+ /* TODO: make this more granular, so a search doesn't involve the
+ * write lock.
+ */
+ EnterCriticalSection (&_cache_write_access);
+ class process *previous = NULL;
+ class process *entry = find (winpid, &previous);
+
+ if (!entry)
{
- debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
- thehandle = INVALID_HANDLE_VALUE;
- return _exit_status;
+ if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ system_printf (("process limit (%d processes) reached; "
+ "new connection refused for %d(%lu)"),
+ MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
+ cygpid, winpid);
+ set_errno (EAGAIN);
+ return NULL;
+ }
+
+ entry = safe_new (class process, cygpid, winpid);
+ if (!entry->is_active ())
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ safe_delete (entry);
+ set_errno (ESRCH);
+ return NULL;
+ }
+
+ if (previous)
+ {
+ entry->_next = previous->_next;
+ previous->_next = entry;
+ }
+ else
+ {
+ entry->_next = _processes_head;
+ _processes_head = entry;
+ }
+
+ _processes_count += 1;
+ SetEvent (_cache_add_trigger);
}
- else if (_exit_status == STILL_ACTIVE)
- return _exit_status;
- /* add new cleanup task etc etc ? */
- return _exit_status;
+
+ EnterCriticalSection (&entry->_access); // To be released by the caller.
+ LeaveCriticalSection (&_cache_write_access);
+ assert (entry);
+ assert (entry->_winpid == winpid);
+ return entry;
}
-/* this is single threaded. It's called after the process is removed from the cache,
- * but inserts may be attemped by worker threads that have a pointer to it */
void
-process::cleanup ()
+process_cache::wait_for_processes (const HANDLE interrupt_event)
{
- /* Serialize this */
- EnterCriticalSection (&access);
- InterlockedIncrement (&(long)cleaning_up);
- class cleanup_routine *entry = head;
- while (entry)
+ // Update `_wait_array' with handles of all current processes.
+ const size_t count = sync_wait_array (interrupt_event);
+
+ debug_printf ("waiting on %u objects in total (%u processes)",
+ count, _processes_count);
+
+ const DWORD rc = WaitForMultipleObjects (count, _wait_array,
+ FALSE, INFINITE);
+
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("could not wait on the process handles, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ const size_t start = rc - WAIT_OBJECT_0;
+
+ if (rc < WAIT_OBJECT_0 || start > count)
{
- class cleanup_routine *temp;
- entry->cleanup (winpid);
- temp = entry->next;
- delete entry;
- entry = temp;
+ system_printf (("unexpected return code %rc "
+ "from WaitForMultipleObjects: "
+ "expected [%u .. %u)"),
+ rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
+ abort ();
}
- LeaveCriticalSection (&access);
+
+ // Tell all the processes, from the signalled point up, the bad news.
+ for (size_t index = start; index != count; index++)
+ if (_process_array[index])
+ check_and_remove_process (index);
}
-bool
-process::add_cleanup_routine (class cleanup_routine *new_cleanup)
+/*
+ * process_cache::sync_wait_array ()
+ *
+ * Fill-in the wait array with the handles that the cache needs to wait on.
+ * These handles are:
+ * - the process_process_param's interrupt event
+ * - the process_cache's cache_add_trigger event
+ * - the handle for each live process in the cache.
+ *
+ * Return value: the number of live handles in the array.
+ */
+
+size_t
+process_cache::sync_wait_array (const HANDLE interrupt_event)
{
- if (cleaning_up)
- return false;
- EnterCriticalSection (&access);
- /* check that we didn't block with ::cleanup ()
- * This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call
- * which would be a whole lot easier
- */
- if (cleaning_up)
+ assert (this);
+ assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
+ assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
+
+ EnterCriticalSection (&_cache_write_access);
+
+ assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
+
+ size_t index = 0;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
{
- LeaveCriticalSection (&access);
- return false;
+ assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
+ assert (ptr->is_active ());
+
+ _wait_array[index] = ptr->handle ();
+ _process_array[index++] = ptr;
+
+ assert (index <= elements (_wait_array));
}
- new_cleanup->next = head;
- head = new_cleanup;
- LeaveCriticalSection (&access);
- return true;
+
+ /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
+ /* Well, not strictly `must', but it's more efficient if they are :-) */
+
+ _wait_array[index] = interrupt_event;
+ _process_array[index++] = NULL;
+
+ _wait_array[index] = _cache_add_trigger;
+ _process_array[index++] = NULL;
+
+ /* Phew, back to normal volume now. */
+
+ assert (index <= elements (_wait_array));
+
+ LeaveCriticalSection (&_cache_write_access);
+
+ return index;
}
-/* process_cleanup */
void
-process_cleanup::process ()
+process_cache::check_and_remove_process (const size_t index)
{
- theprocess->cleanup ();
- delete theprocess;
+ assert (this);
+ assert (index < elements (_wait_array) - SPECIALS_COUNT);
+
+ class process *const process = _process_array[index];
+
+ assert (process);
+ assert (process->handle () == _wait_array[index]);
+
+ if (process->check_exit_code () == STILL_ACTIVE)
+ return;
+
+ debug_printf ("process %d(%lu) has left the building ($? = %lu)",
+ process->_cygpid, process->_winpid, process->_exit_status);
+
+ /* Unlink the process object from the process list. */
+
+ EnterCriticalSection (&_cache_write_access);
+
+ class process *previous = NULL;
+
+ const class process *const tmp = find (process->_winpid, &previous);
+
+ assert (tmp == process);
+ assert (previous ? previous->_next == process : _processes_head == process);
+
+ if (previous)
+ previous->_next = process->_next;
+ else
+ _processes_head = process->_next;
+
+ _processes_count -= 1;
+ LeaveCriticalSection (&_cache_write_access);
+
+ /* Schedule any cleanup tasks for this process. */
+ _queue.add (safe_new (process_cleanup, process));
}
-/* process_process_param */
-DWORD
-process_process_param::request_loop ()
+class process *
+process_cache::find (const DWORD winpid, class process **previous)
{
- process_cache *cache = (process_cache *) queue;
- /* always malloc one, so there is no special case in the loop */
- ssize_t HandlesSize = 2;
- HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize);
- process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize);
- /* TODO: put [1] at the end as it will also get done if a process dies? */
- Handles[0] = interrupt;
- Handles[1] = cache->cache_add_trigger;
- while (cache->active && !shutdown)
- {
- int copied;
- copied = -1;
- int offset;
- offset = 1;
- int count;
- count = 2;
- while ((copied == HandlesSize - 2 - offset) || copied < 0)
- {
- /* we need more storage to cope with all the HANDLES */
- if (copied == HandlesSize - 2 - offset)
- {
- HANDLE *temp = (HANDLE *) realloc (Handles,
- sizeof (HANDLE) *
- HandlesSize + 10);
- if (!temp)
- {
- printf
- ("cannot allocate more storage for the handle array!\n");
- exit (1);
- }
- Handles = temp;
- process **ptemp = (process **) realloc (Entries,
- sizeof (LPVOID) *
- HandlesSize + 10);
- if (!ptemp)
- {
- printf
- ("cannot allocate more storage for the handle array!\n");
- exit (1);
- }
- Entries = ptemp;
- HandlesSize += 10;
- }
- offset += copied;
- copied =
- cache->handle_snapshot (&Handles[2], &Entries[2],
- HandlesSize - 2 - offset, offset);
- count += copied;
- }
- debug_printf ("waiting on %u objects\n", count);
- DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
- if (rc == WAIT_FAILED)
- {
- printf ("Could not wait on the process handles (%ld)!\n",
- GetLastError ());
- exit (1);
- }
- int objindex = rc - WAIT_OBJECT_0;
- if (objindex > 1 && objindex < count)
- {
- debug_printf ("Process %ld has left the building\n",
- Entries[objindex]->winpid);
- /* fire off the termination routines */
- cache->remove_process (Entries[objindex]);
- }
- else if (objindex >= 0 && objindex < 2)
- {
- /* 0 is shutdown - do nothing */
- /* 1 is a cache add event - just rebuild the object list */
- }
- else
- {
- printf
- ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");
- }
- }
- running = false;
- return 0;
+ if (previous)
+ *previous = NULL;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
+ if (ptr->_winpid == winpid)
+ return ptr;
+ else if (ptr->_winpid > winpid) // The list is sorted by winpid.
+ return NULL;
+ else if (previous)
+ *previous = ptr;
+
+ return NULL;
}
+
+/*****************************************************************************/
diff --git a/winsup/cygserver/shm.cc b/winsup/cygserver/shm.cc
index 260a5b1..18b1c3d 100644
--- a/winsup/cygserver/shm.cc
+++ b/winsup/cygserver/shm.cc
@@ -1,8 +1,9 @@
-/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
+/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin.
-Copyright 2001, 2002 Red Hat, Inc.
+ Copyright 2002 Red Hat, Inc.
-Originally written by Robert Collins <robert.collins@hotmail.com>
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
@@ -10,656 +11,886 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
-#ifdef __OUTSIDE_CYGWIN__
-#undef __INSIDE_CYGWIN__
-#else
-#include "winsup.h"
-#endif
-
-#ifndef __INSIDE_CYGWIN__
-#define DEBUG 0
-#define system_printf printf
-#define debug_printf if (DEBUG) printf
-#define api_fatal printf
-#include <stdio.h>
-#include <windows.h>
-#endif
+#include "woutsup.h"
-#include <sys/stat.h>
#include <errno.h>
-#include "cygerrno.h"
-#include <unistd.h>
-#include "security.h"
-//#include "fhandler.h"
-//#include "dtable.h"
-//#include "cygheap.h"
+#include <pthread.h>
#include <stdio.h>
-//#include "thread.h"
-#ifndef __INSIDE_CYGWIN__
-#define __INSIDE_CYGWIN__
-#include <cygwin/shm.h>
-#undef __INSIDE_CYGWIN__
-#else
-#include <cygwin/shm.h>
-#endif
-//#include "perprocess.h"
-#include <threaded_queue.h>
-#include <cygwin/cygserver_process.h>
-#include "cygserver_shm.h"
+#include <string.h>
+#include <time.h>
-// FIXME IS THIS CORRECT
-/* Implementation notes: We use two shared memory regions per key:
- * One for the control structure, and one for the shared memory.
- * While this has a higher overhead tham a single shared area,
- * It allows more flexability. As the entire code is transparent to the user
- * We can merge these in the future should it be needed.
- * Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just
- * keep monotonically incrementing - system wide.
- */
-size_t
-getsystemallocgranularity ()
-{
- SYSTEM_INFO sysinfo;
- static size_t buffer_offset = 0;
- if (buffer_offset)
- return buffer_offset;
- GetSystemInfo (&sysinfo);
- buffer_offset = sysinfo.dwAllocationGranularity;
- return buffer_offset;
-}
+#include "cygserver_ipc.h"
+#include "cygserver_shm.h"
+#include "security.h"
+#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
-client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET,
- sizeof (parameters))
-{
- buffer = (char *) &parameters;
-}
+/*---------------------------------------------------------------------------*
+ * class server_shmmgr
+ *
+ * A singleton class.
+ *---------------------------------------------------------------------------*/
-/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
- * Solution: manually calculate the next id value
- */
+#define shmmgr (server_shmmgr::instance ())
-#if 0
-extern
-"C" void *
-shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
+class server_shmmgr
{
- class shmid_ds *
- shm = (class shmid_ds *)
- shmid; //FIXME: verifyable object test
+private:
+ class attach_t
+ {
+ public:
+ class process *const _client;
+ unsigned int _refcnt;
+
+ attach_t *_next;
+
+ attach_t (class process *const client)
+ : _client (client),
+ _refcnt (0),
+ _next (NULL)
+ {}
+ };
+
+ class segment_t
+ {
+ private:
+ // Bits for the _flg field.
+ enum { IS_DELETED = 0x01 };
+
+ public:
+ const int _intid;
+ const int _shmid;
+ struct shmid_ds _ds;
+
+ segment_t *_next;
+
+ segment_t (const key_t key, const int intid, const HANDLE hFileMap);
+ ~segment_t ();
+
+ bool is_deleted () const
+ {
+ return _flg & IS_DELETED;
+ }
+
+ bool is_pending_delete () const
+ {
+ return !_ds.shm_nattch && is_deleted ();
+ }
- if (shmaddr)
+ void mark_deleted ()
{
- //FIXME: requested base address ?!
- set_errno (EINVAL);
- return (void *) -1;
+ assert (!is_deleted ());
+
+ _flg |= IS_DELETED;
}
- void *
- rv =
- MapViewOfFile (shm->attachmap,
+ int attach (class process *, HANDLE & hFileMap);
+ int detach (class process *);
+
+ private:
+ static long _sequence;
+ int _flg;
+ const HANDLE _hFileMap;
+ attach_t *_attach_head; // A list sorted by winpid;
- (parameters.in.shmflg & SHM_RDONLY) ?
- FILE_MAP_READ : FILE_MAP_WRITE, 0,
- 0, 0);
+ attach_t *find (const class process *, attach_t **previous = NULL);
+ };
- if (!rv)
+ class cleanup_t : public cleanup_routine
+ {
+ public:
+ cleanup_t (const segment_t *const segptr)
+ : cleanup_routine (reinterpret_cast<void *> (segptr->_shmid))
{
- //FIXME: translate GetLastError()
- set_errno (EACCES);
- return (void *) -1;
+ assert (key ());
}
-/* FIXME: this needs to be globally protected to prevent a mismatch betwen
- * attach count and attachees list
- */
-
- InterlockedIncrement (&shm->shm_nattch);
- _shmattach *
- attachnode =
- new
- _shmattach;
-
- attachnode->data = rv;
- attachnode->next =
- (_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead,
- (long int) attachnode);
- return rv;
+ int shmid () const { return reinterpret_cast<int> (key ()); }
+
+ virtual void cleanup (class process *const client)
+ {
+ const int res = shmmgr.shmdt (shmid (), client);
+
+ if (res != 0)
+ debug_printf ("process cleanup failed [shmid = %d]: %s",
+ shmid (), strerror (-res));
+ }
+ };
+
+public:
+ static server_shmmgr & instance ();
+
+ int shmat (HANDLE & hFileMap,
+ int shmid, int shmflg, class process *);
+ int shmctl (int & out_shmid, struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo, struct shm_info & out_shm_info,
+ const int shmid, int cmd, const struct shmid_ds &,
+ class process *);
+ int shmdt (int shmid, class process *);
+ int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t,
+ class process *);
+
+private:
+ static server_shmmgr *_instance;
+ static pthread_once_t _instance_once;
+
+ static void initialise_instance ();
+
+ CRITICAL_SECTION _segments_lock;
+ segment_t *_segments_head; // A list sorted by int_id.
+
+ int _shm_ids; // Number of shm segments (for ipcs(8)).
+ int _shm_tot; // Total bytes of shm segments (for ipcs(8)).
+ int _shm_atts; // Number of attached segments (for ipcs(8)).
+ int _intid_max; // Highest intid yet allocated (for ipcs(8)).
+
+ server_shmmgr ();
+ ~server_shmmgr ();
+
+ // Undefined (as this class is a singleton):
+ server_shmmgr (const server_shmmgr &);
+ server_shmmgr & operator= (const server_shmmgr &);
+
+ segment_t *find_by_key (key_t);
+ segment_t *find (int intid, segment_t **previous = NULL);
+
+ int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t);
+
+ segment_t *new_segment (key_t, size_t, HANDLE);
+ void delete_segment (segment_t *);
+};
+
+/* static */ long server_shmmgr::segment_t::_sequence = 0;
+
+/* static */ server_shmmgr *server_shmmgr::_instance = NULL;
+/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT;
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::segment_t (const key_t key,
+ const int intid,
+ const HANDLE hFileMap)
+ : _intid (intid),
+ _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)),
+ _next (NULL),
+ _flg (0),
+ _hFileMap (hFileMap),
+ _attach_head (NULL)
+{
+ assert (0 <= _intid && _intid < SHMMNI);
+
+ memset (&_ds, '\0', sizeof (_ds));
+ _ds.shm_perm.key = key;
}
-#endif
-
-/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
- * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
- * appropriately
- */
-
-/* Test result from openbsd: shm ids are persistent cross process if a handle is left
- * open. This could lead to resource starvation: we're not copying that behaviour
- * unless we have to. (It will involve acygwin1.dll gloal shared list :[ ).
- */
-/* FIXME: shmid should be a verifyable object
- */
-
-/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
- */
-
-extern GENERIC_MAPPING
- access_mapping;
-
-extern int
-check_and_dup_handle (HANDLE from_process, HANDLE to_process,
- HANDLE from_process_token,
- DWORD access,
- HANDLE from_handle,
- HANDLE * to_handle_ptr, BOOL bInheritHandle);
-
-//FIXME: where should this live
-static shmnode *
- shm_head =
- NULL;
-//FIXME: ditto.
-static shmnode *
- deleted_head = NULL;
-/* must be long for InterlockedIncrement */
-static long
- new_id =
- 0;
-static long
- new_private_key =
- 0;
-
-static void
-delete_shmnode (shmnode **nodeptr)
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::~segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::~segment_t ()
{
- shmnode *node = *nodeptr;
+ assert (!_attach_head);
- // remove from the list
- if (node == shm_head)
- shm_head = shm_head->next;
- else
+ if (!CloseHandle (_hFileMap))
+ syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::attach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::attach (class process *const client,
+ HANDLE & hFileMap)
+{
+ assert (client);
+
+ if (!DuplicateHandle (GetCurrentProcess (),
+ _hFileMap,
+ client->handle (),
+ &hFileMap,
+ 0,
+ FALSE, // bInheritHandle
+ DUPLICATE_SAME_ACCESS))
+ {
+ syscall_printf (("failed to duplicate handle for client "
+ "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"),
+ _ds.shm_perm.key, _shmid, _hFileMap);
+
+ return -EACCES; // FIXME: Case analysis?
+ }
+
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch += 1;
+ _ds.shm_atime = time (NULL); // FIXME: sub-second times.
+
+ attach_t *previous = NULL;
+ attach_t *attptr = find (client, &previous);
+
+ if (!attptr)
{
- shmnode *tempnode = shm_head;
- while (tempnode && tempnode->next != node)
- tempnode = tempnode->next;
- if (tempnode)
- tempnode->next = node->next;
- // else log the unexpected !
+ attptr = safe_new (attach_t, client);
+
+ if (previous)
+ {
+ attptr->_next = previous->_next;
+ previous->_next = attptr;
+ }
+ else
+ {
+ attptr->_next = _attach_head;
+ _attach_head = attptr;
+ }
}
- // release the shared data view
- UnmapViewOfFile (node->shmds->mapptr);
- delete node->shmds;
- CloseHandle (node->filemap);
- CloseHandle (node->attachmap);
+ attptr->_refcnt += 1;
- // free the memory
- delete node;
- nodeptr = NULL;
+ cleanup_t *const cleanup = safe_new (cleanup_t, this);
+
+ // FIXME: ::add should only fail if the process object is already
+ // cleaning up; but it can't be doing that since this thread has it
+ // locked.
+
+ const bool result = client->add (cleanup);
+
+ assert (result);
+
+ return 0;
}
-void
-client_request_shm::serve (transport_layer_base * conn, process_cache * cache)
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::detach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::detach (class process *const client)
{
-// DWORD sd_size = 4096;
-// char sd_buf[4096];
- PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf;
-// /* create a sd for our open requests based on shmflag & 0x01ff */
-// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
-// parameters.in.shmflg & 0x01ff, psd, &sd_size);
-
- HANDLE from_process_handle = NULL;
- HANDLE token_handle = NULL;
- DWORD rc;
-
- from_process_handle = cache->process (parameters.in.pid)->handle ();
- /* possible TODO: reduce the access on the handle before we use it */
- /* Note that unless we do this, we don't need to call CloseHandle - it's kept open
- * by the process cache until the process terminates.
- * We may need a refcount on the cache however...
- */
- if (!from_process_handle)
+ attach_t *previous = NULL;
+ attach_t *const attptr = find (client, &previous);
+
+ if (!attptr)
+ return -EINVAL;
+
+ if (client->is_active ())
{
- debug_printf ("error opening process (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- return;
+ const cleanup_t key (this);
+
+ if (!client->remove (&key))
+ syscall_printf (("failed to remove cleanup routine for %d(%lu) "
+ "[shmid = %d]"),
+ client->cygpid (), client->winpid (),
+ _shmid);
}
- conn->impersonate_client ();
+ attptr->_refcnt -= 1;
- rc = OpenThreadToken (GetCurrentThread (),
- TOKEN_QUERY, TRUE, &token_handle);
+ if (!attptr->_refcnt)
+ {
+ assert (previous ? previous->_next == attptr : _attach_head == attptr);
- conn->revert_to_self ();
+ if (previous)
+ previous->_next = attptr->_next;
+ else
+ _attach_head = attptr->_next;
- if (!rc)
- {
- debug_printf ("error opening thread token (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- CloseHandle (from_process_handle);
- return;
+ safe_delete (attptr);
}
+ assert (_ds.shm_nattch > 0);
- /* we trust the clients request - we will be doing it as them, and
- * the worst they can do is open their own permissions
- */
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch -= 1;
+ _ds.shm_dtime = time (NULL); // FIXME: sub-second times.
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::attach_t *
+server_shmmgr::segment_t::find (const class process *const client,
+ attach_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ // Nb. The _attach_head list is sorted by winpid.
+ for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next)
+ if (attptr->_client == client)
+ return attptr;
+ else if (attptr->_client->winpid () > client->winpid ())
+ return NULL;
+ else if (previous)
+ *previous = attptr;
- SECURITY_ATTRIBUTES sa;
- sa.nLength = sizeof (sa);
- sa.lpSecurityDescriptor = psd;
- sa.bInheritHandle = TRUE; /* the memory structures inherit ok */
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ server_shmmgr &
+server_shmmgr::instance ()
+{
+ pthread_once (&_instance_once, &initialise_instance);
+
+ assert (_instance);
+
+ return *_instance;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmat ()
+ *---------------------------------------------------------------------------*/
- char *shmname = NULL, *shmaname = NULL;
- char stringbuf[29], stringbuf1[29];
+int
+server_shmmgr::shmat (HANDLE & hFileMap,
+ const int shmid, const int shmflg,
+ class process *const client)
+{
+ syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)",
+ shmid, shmflg, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->attach (client, hFileMap);
+
+ if (!result)
+ _shm_atts += 1;
- /* TODO: make this code block a function! */
- if (parameters.in.type == SHM_REATTACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ hFileMap, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmctl ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmctl (int & out_shmid,
+ struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo,
+ struct shm_info & out_shm_info,
+ const int shmid, const int cmd,
+ const struct shmid_ds & ds,
+ class process *const client)
+{
+ syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)",
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ switch (cmd)
{
- /* just find and fill out the existing shm_id */
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
+ case IPC_STAT:
+ case SHM_STAT: // Uses intids rather than shmids.
+ case IPC_SET:
+ case IPC_RMID:
+ {
+ int intid;
+
+ if (cmd == SHM_STAT)
+ intid = shmid;
+ else
+ intid = ipc_ext2int (shmid, IPC_SHMOP);
+
+ segment_t *const segptr = find (intid);
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ switch (cmd)
{
- parameters.out.shm_id = tempnode->shm_id;
- parameters.out.key = tempnode->key;
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->filemap,
- &parameters.out.filemap, TRUE) != 0)
- {
- debug_printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- }
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
+ case IPC_STAT:
+ out_ds = segptr->_ds;
+ break;
+
+ case IPC_SET:
+ segptr->_ds.shm_perm.uid = ds.shm_perm.uid;
+ segptr->_ds.shm_perm.gid = ds.shm_perm.gid;
+ segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777;
+ segptr->_ds.shm_lpid = client->cygpid ();
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
+ break;
+
+ case IPC_RMID:
+ if (segptr->is_deleted ())
+ result = -EIDRM;
+ else
{
- debug_printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
+ segptr->mark_deleted ();
+ if (segptr->is_pending_delete ())
+ delete_segment (segptr);
}
- CloseHandle (token_handle);
- return;
+ break;
+
+ case SHM_STAT: // ipcs(8) i'face.
+ out_ds = segptr->_ds;
+ out_shmid = segptr->_shmid;
+ break;
}
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ }
+ break;
+
+ case IPC_INFO:
+ out_shminfo.shmmax = SHMMAX;
+ out_shminfo.shmmin = SHMMIN;
+ out_shminfo.shmmni = SHMMNI;
+ out_shminfo.shmseg = SHMSEG;
+ out_shminfo.shmall = SHMALL;
+ break;
+
+ case SHM_INFO: // ipcs(8) i'face.
+ out_shmid = _intid_max;
+ out_shm_info.shm_ids = _shm_ids;
+ out_shm_info.shm_tot = _shm_tot;
+ out_shm_info.shm_atts = _shm_atts;
+ break;
+
+ default:
+ result = -EINVAL;
+ break;
}
- /* someone attached */
- /* someone can send shm_id's they don't have and currently we will increment those
- * attach counts. If someone wants to fix that, please go ahead.
- * The problem is that shm_get has nothing to do with the ability to attach. Attach
- * requires a permission check, which we get the OS to do in MapViewOfFile.
- */
- if (parameters.in.type == SHM_ATTACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ -result,
+ shmid, cmd, client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ ((cmd == SHM_STAT || cmd == SHM_INFO)
+ ? out_shmid
+ : result),
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmdt ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmdt (const int shmid, class process *const client)
+{
+ syscall_printf ("shmdt (shmid = %d) for %d(%lu)",
+ shmid, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->detach (client);
+
+ if (!result)
+ _shm_atts -= 1;
+
+ if (!result && segptr->is_pending_delete ())
+ delete_segment (segptr);
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)",
+ -result, shmid, client->cygpid (), client->winpid ());
+ else
+ syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)",
+ result, shmid, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmget ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmget (int & out_shmid,
+ const key_t key, const size_t size, const int shmflg,
+ const uid_t uid, const gid_t gid,
+ class process *const client)
+{
+ syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ if (key == IPC_PRIVATE)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
{
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
- {
- InterlockedIncrement (&tempnode->shmds->shm_nattch);
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ segment_t *const segptr = find_by_key (key);
+
+ if (!segptr)
+ if (shmflg & IPC_CREAT)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
+ result = -ENOENT;
+ else if (segptr->is_deleted ())
+ result = -EIDRM;
+ else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
+ result = -EEXIST;
+ else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777)
+ result = -EACCES;
+ else if (size && segptr->_ds.shm_segsz < size)
+ result = -EINVAL;
+ else
+ result = segptr->_shmid;
}
- /* Someone detached */
- if (parameters.in.type == SHM_DETACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result >= 0)
{
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
- {
- InterlockedDecrement (&tempnode->shmds->shm_nattch);
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ out_shmid = result;
+ result = 0;
}
- /* Someone wants the ID removed. */
- if (parameters.in.type == SHM_DEL)
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ out_shmid,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::initialise_instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ void
+server_shmmgr::initialise_instance ()
+{
+ assert (!_instance);
+
+ _instance = safe_new0 (server_shmmgr);
+
+ assert (_instance);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::server_shmmgr ()
+ : _segments_head (NULL),
+ _shm_ids (0),
+ _shm_tot (0),
+ _shm_atts (0),
+ _intid_max (0)
+{
+ InitializeCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::~server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::~server_shmmgr ()
+{
+ DeleteCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find_by_key ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find_by_key (const key_t key)
+{
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_ds.shm_perm.key == key)
+ return segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find (const int intid, segment_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_intid == intid)
+ return segptr;
+ else if (segptr->_intid > intid) // The list is sorted by intid.
+ return NULL;
+ else if (previous)
+ *previous = segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::new_segment (const key_t key,
+ const size_t size,
+ const int shmflg,
+ const pid_t cygpid,
+ const uid_t uid,
+ const gid_t gid)
+{
+ if (size < SHMMIN || size > SHMMAX)
+ return -EINVAL;
+
+ const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE,
+ NULL, PAGE_READWRITE,
+ 0, size,
+ NULL);
+
+ if (!hFileMap)
{
- shmnode **tempnode = &shm_head;
- while (*tempnode)
- {
- if ((*tempnode)->shm_id == parameters.in.shm_id)
- {
- // unlink from the accessible node list
- shmnode *temp2 = *tempnode;
- *tempnode = temp2->next;
- // link into the deleted list
- temp2->next = deleted_head;
- deleted_head = temp2;
-
- // FIXME: when/where do we delete the handles?
- if (temp2->shmds->shm_nattch)
- {
- // FIXME: add to a pending queue?
- }
- else
- {
- delete_shmnode (&temp2);
- }
-
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = &(*tempnode)->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ syscall_printf ("failed to create file mapping [size = %lu]: %E", size);
+ return -ENOMEM; // FIXME
}
+ segment_t *const segptr = new_segment (key, size, hFileMap);
- if (parameters.in.type == SHM_CREATE)
+ if (!segptr)
{
- /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
- * to prevent races on shmget.
- */
+ (void) CloseHandle (hFileMap);
+ return -ENOSPC;
+ }
- if (parameters.in.key == IPC_PRIVATE)
- {
- /* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */
- /* The K refers to Key, the actual mapped area has D */
- long private_key = (int) InterlockedIncrement (&new_private_key);
- snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key);
- shmname = stringbuf;
- snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key);
- shmaname = stringbuf1;
- }
- else
- {
- /* create the mapping name (CYGWINSHMK0x0123456789abcdef */
- /* The K refers to Key, the actual mapped area has D */
-
- snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key);
- shmname = stringbuf;
- snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key);
- shmaname = stringbuf1;
- debug_printf ("system id strings are \n%s\n%s\n", shmname,
- shmaname);
- debug_printf ("key input value is 0x%0qx\n", parameters.in.key);
- }
+ segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid;
+ segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid;
+ segptr->_ds.shm_perm.mode = shmflg & 0777;
+ segptr->_ds.shm_segsz = size;
+ segptr->_ds.shm_cpid = cygpid;
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
- /* attempt to open the key */
+ return segptr->_shmid;
+}
- /* get an existing key */
- /* On unix the same shmid identifier is returned on multiple calls to shm_get
- * with the same key and size. Different modes is a ?.
- */
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *
+ * Allocate a new segment for the given key and file map with the
+ * lowest available intid and insert into the segment map.
+ *---------------------------------------------------------------------------*/
+server_shmmgr::segment_t *
+server_shmmgr::new_segment (const key_t key, const size_t size,
+ const HANDLE hFileMap)
+{
+ // FIXME: Overflow risk.
+ if (_shm_tot + size > SHMALL)
+ return NULL;
+ int intid = 0; // Next expected intid value.
+ segment_t *previous = NULL; // Insert pointer.
- /* walk the list of known keys and return the id if found. remember, we are
- * authoritative...
- */
+ // Find first unallocated intid.
+ for (segment_t *segptr = _segments_head;
+ segptr && segptr->_intid == intid;
+ segptr = segptr->_next, intid++)
+ {
+ previous = segptr;
+ }
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->key == parameters.in.key
- && parameters.in.key != IPC_PRIVATE)
- {
- // FIXME: free the mutex
- if (parameters.in.size
- && tempnode->shmds->shm_segsz < parameters.in.size)
- {
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
- }
- /* FIXME: can the same process call this twice without error ? test
- * on unix
- */
- if ((parameters.in.shmflg & IPC_CREAT)
- && (parameters.in.shmflg & IPC_EXCL))
- {
- header.error_code = EEXIST;
- debug_printf
- ("attempt to exclusively create already created shm_area with key 0x%0qx\n",
- parameters.in.key);
- // FIXME: free the mutex
- CloseHandle (token_handle);
- return;
- }
- // FIXME: do we need to other tests of the requested mode with the
- // tempnode->shm_id mode ? testcase on unix needed.
- // FIXME how do we do the security test? or
- // do we wait for shmat to bother with that?
- /* One possibly solution: impersonate the client, and then test we can
- * reopen the area. In fact we'll probably have to do that to get
- * handles back to them, alternatively just tell them the id, and then
- * let them attempt the open.
- */
- parameters.out.shm_id = tempnode->shm_id;
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->filemap,
- &parameters.out.filemap, TRUE) != 0)
- {
- printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
-/*mutex*/
- CloseHandle (token_handle);
- return;
- }
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
- {
- printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
-/*mutex*/
- CloseHandle (token_handle);
- return;
- }
+ /* By the time this condition is reached (given the default value of
+ * SHMMNI), the linear searches should all replaced by something
+ * just a *little* cleverer . . .
+ */
+ if (intid >= SHMMNI)
+ return NULL;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- /* couldn't find a currently open shm area. */
-
- /* create one */
- /* do this as the client */
- conn->impersonate_client ();
- /* This may need sh_none... it's only a control structure */
- HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile.
- &sa,
- PAGE_READWRITE, // protection
- 0x00000000,
- getsystemallocgranularity (),
- shmname // object name
- );
- int lasterr = GetLastError ();
- conn->revert_to_self ();
-
- if (filemap == NULL)
- {
- /* We failed to open the filemapping ? */
- system_printf ("failed to open file mapping: %lu\n",
- GetLastError ());
- // free the mutex
- // we can assume that it exists, and that it was an access problem.
- header.error_code = EACCES;
- CloseHandle (token_handle);
- return;
- }
+ segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap);
- /* successfully opened the control region mapping */
- /* did we create it ? */
- int oldmapping = lasterr == ERROR_ALREADY_EXISTS;
- if (oldmapping)
- {
- /* should never happen - we are the global daemon! */
-#if 0
- if ((parameters.in.shmflg & IPC_CREAT)
- && (parameters.in.shmflg & IPC_EXCL))
-#endif
- {
- /* FIXME free mutex */
- CloseHandle (filemap);
- header.error_code = EEXIST;
- CloseHandle (token_handle);
- return;
- }
- }
+ assert (segptr);
- /* we created a new mapping */
- if (parameters.in.key != IPC_PRIVATE &&
- (parameters.in.shmflg & IPC_CREAT) == 0)
- {
- CloseHandle (filemap);
- /* FIXME free mutex */
- header.error_code = ENOENT;
- CloseHandle (token_handle);
- return;
- }
+ if (previous)
+ {
+ segptr->_next = previous->_next;
+ previous->_next = segptr;
+ }
+ else
+ {
+ segptr->_next = _segments_head;
+ _segments_head = segptr;
+ }
- conn->impersonate_client ();
- void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
- conn->revert_to_self ();
+ _shm_ids += 1;
+ _shm_tot += size;
+ if (intid > _intid_max)
+ _intid_max = intid;
- if (!mapptr)
- {
- CloseHandle (filemap);
- //FIXME: close filemap and free the mutex
- /* we couldn't access the mapped area with the requested permissions */
- header.error_code = EACCES;
- CloseHandle (token_handle);
- return;
- }
+ return segptr;
+}
- conn->impersonate_client ();
- /* Now get the user data */
- HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile
- &sa,
- PAGE_READWRITE, // protection (FIXME)
- 0x00000000,
- parameters.in.size +
- parameters.in.size %
- getsystemallocgranularity (),
- shmaname // object name
- );
- conn->revert_to_self ();
-
- if (attachmap == NULL)
- {
- system_printf ("failed to get shm attachmap\n");
- header.error_code = ENOMEM;
- UnmapViewOfFile (mapptr);
- CloseHandle (filemap);
- /* FIXME exit the mutex */
- CloseHandle (token_handle);
- return;
- }
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::delete_segment ()
+ *---------------------------------------------------------------------------*/
- shmid_ds *shmtemp = new shmid_ds;
- if (!shmtemp)
- {
- system_printf ("failed to malloc shm node\n");
- header.error_code = ENOMEM;
- UnmapViewOfFile (mapptr);
- CloseHandle (filemap);
- CloseHandle (attachmap);
- /* FIXME exit mutex */
- CloseHandle (token_handle);
- return;
- }
+void
+server_shmmgr::delete_segment (segment_t *const segptr)
+{
+ assert (segptr);
+ assert (segptr->is_pending_delete ());
- /* fill out the node data */
- shmtemp->shm_perm.cuid = getuid ();
- shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
- shmtemp->shm_perm.cgid = getgid ();
- shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
- shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff;
- shmtemp->shm_lpid = 0;
- shmtemp->shm_nattch = 0;
- shmtemp->shm_atime = 0;
- shmtemp->shm_dtime = 0;
- shmtemp->shm_ctime = time (NULL);
- shmtemp->shm_segsz = parameters.in.size;
- *(shmid_ds *) mapptr = *shmtemp;
- shmtemp->mapptr = mapptr;
-
- /* no need for InterlockedExchange here, we're serialised by the global mutex */
- tempnode = new shmnode;
- tempnode->shmds = shmtemp;
- tempnode->shm_id = (int) InterlockedIncrement (&new_id);
- tempnode->key = parameters.in.key;
- tempnode->filemap = filemap;
- tempnode->attachmap = attachmap;
- tempnode->next = shm_head;
- shm_head = tempnode;
-
- /* we now have the area in the daemon list, opened.
-
- FIXME: leave the system wide shm mutex */
-
- parameters.out.shm_id = tempnode->shm_id;
- if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
- token_handle,
- DUPLICATE_SAME_ACCESS,
- tempnode->filemap, &parameters.out.filemap,
- TRUE) != 0)
- {
- printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- CloseHandle (token_handle);
-/* mutex et al */
- return;
- }
- if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
- token_handle,
- DUPLICATE_SAME_ACCESS,
- tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
- {
- printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- CloseHandle (from_process_handle);
- CloseHandle (token_handle);
-/* more cleanup... yay! */
- return;
- }
- CloseHandle (token_handle);
+ segment_t *previous = NULL;
+ const segment_t *const tmp = find (segptr->_intid, &previous);
+
+ assert (tmp == segptr);
+ assert (previous ? previous->_next == segptr : _segments_head == segptr);
+
+ if (previous)
+ previous->_next = segptr->_next;
+ else
+ _segments_head = segptr->_next;
+
+ assert (_shm_ids > 0);
+ _shm_ids -= 1;
+ _shm_tot -= segptr->_ds.shm_segsz;
+
+ safe_delete (segptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm ()
+ : client_request (CYGSERVER_REQUEST_SHM,
+ &_parameters, sizeof (_parameters))
+{
+ // verbose: syscall_printf ("created");
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::serve ()
+ *---------------------------------------------------------------------------*/
+
+void
+client_request_shm::serve (transport_layer_base *const conn,
+ process_cache *const cache)
+{
+ assert (conn);
+
+ assert (!error_code ());
+
+ if (msglen () != sizeof (_parameters.in))
+ {
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (_parameters), msglen ());
+ error_code (EINVAL);
+ msglen (0);
return;
}
- header.error_code = ENOSYS;
- CloseHandle (token_handle);
+ // FIXME: Get a return code out of this and don't continue on error.
+ conn->impersonate_client ();
+
+ class process *const client = cache->process (_parameters.in.cygpid,
+ _parameters.in.winpid);
+
+ if (!client)
+ {
+ error_code (EAGAIN);
+ msglen (0);
+ return;
+ }
+ int result = -EINVAL;
- return;
+ switch (_parameters.in.shmop)
+ {
+ case SHMOP_shmget:
+ result = shmmgr.shmget (_parameters.out.shmid,
+ _parameters.in.key, _parameters.in.size,
+ _parameters.in.shmflg,
+ _parameters.in.uid, _parameters.in.gid,
+ client);
+ break;
+
+ case SHMOP_shmat:
+ result = shmmgr.shmat (_parameters.out.hFileMap,
+ _parameters.in.shmid, _parameters.in.shmflg,
+ client);
+ break;
+
+ case SHMOP_shmdt:
+ result = shmmgr.shmdt (_parameters.in.shmid, client);
+ break;
+
+ case SHMOP_shmctl:
+ result = shmmgr.shmctl (_parameters.out.shmid,
+ _parameters.out.ds, _parameters.out.shminfo,
+ _parameters.out.shm_info,
+ _parameters.in.shmid, _parameters.in.cmd,
+ _parameters.in.ds,
+ client);
+ break;
+ }
+
+ client->release ();
+ conn->revert_to_self ();
+
+ if (result < 0)
+ {
+ error_code (-result);
+ msglen (0);
+ }
+ else
+ msglen (sizeof (_parameters.out));
}
diff --git a/winsup/cygserver/shm.h b/winsup/cygserver/shm.h
index f1dcaa5..b1ff353 100755
--- a/winsup/cygserver/shm.h
+++ b/winsup/cygserver/shm.h
@@ -1,7 +1,9 @@
-/* cygserver_shm.h
+/* cygserver_shm.h: Single unix specification IPC interface for Cygwin.
- Copyright 2001, 2002 Red Hat Inc.
- Written by Robert Collins <rbtcollins@hotmail.com>
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
@@ -9,84 +11,137 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
+#ifndef __CYGSERVER_SHM_H__
+#define __CYGSERVER_SHM_H__
+
#include <sys/types.h>
-#include "cygwin/cygserver_transport.h"
+#include <cygwin/shm.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "cygserver_ipc.h"
+
#include "cygwin/cygserver.h"
-#define SHM_CREATE 0
-#define SHM_REATTACH 1
-#define SHM_ATTACH 2
-#define SHM_DETACH 3
-#define SHM_DEL 4
+/*---------------------------------------------------------------------------*
+ * Values for the shminfo entries.
+ *
+ * Nb. The values are segregated between two enums so that the `small'
+ * values aren't promoted to `unsigned long' equivalents.
+ *---------------------------------------------------------------------------*/
+enum
+ {
+ SHMMAX = ULONG_MAX,
+ SHMSEG = ULONG_MAX,
+ SHMALL = ULONG_MAX
+ };
+
+enum
+ {
+ SHMMIN = 1,
+ SHMMNI = IPCMNI // Must be <= IPCMNI.
+ };
+
+/*---------------------------------------------------------------------------*
+ * class client_request_shm
+ *---------------------------------------------------------------------------*/
-class client_request_shm : public client_request
-{
- public:
#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, process_cache *cache);
+class transport_layer_base;
+class process_cache;
#endif
- client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t);
- client_request_shm ();
- client_request_shm (int, int, pid_t);
- client_request_shm (int, int);
- union {
- struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in;
- struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out;
- } parameters;
-};
-#ifndef __INSIDE_CYGWIN__
-class shm_cleanup : cleanup_routine
+class client_request_shm : public client_request
{
+ friend class client_request;
+
public:
- virtual void cleanup (long winpid);
-};
+ enum shmop_t
+ {
+ SHMOP_shmat,
+ SHMOP_shmctl,
+ SHMOP_shmdt,
+ SHMOP_shmget
+ };
+
+#ifdef __INSIDE_CYGWIN__
+ client_request_shm (int shmid, int shmflg); // shmat
+ client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl
+ client_request_shm (int shmid); // shmdt
+ client_request_shm (key_t, size_t, int shmflg); // shmget
#endif
-#if 0
-class _shmattach {
-public:
- void *data;
- class _shmattach *next;
-};
-class shmid_ds {
-public:
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
- HANDLE filemap;
- HANDLE attachmap;
- void *mapptr;
- class _shmattach *attachhead;
-};
+ // Accessors for out parameters.
-class shmnode {
-public:
- class shmid_ds * shmid;
- class shmnode *next;
- key_t key;
-};
-//....
-struct shmid_ds {
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
-};
+ int shmid () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shmid;
+ }
+
+ HANDLE hFileMap () const
+ {
+ assert (!error_code ());
+ return _parameters.out.hFileMap;
+ }
+
+ const struct shmid_ds & ds () const
+ {
+ assert (!error_code ());
+ return _parameters.out.ds;
+ }
+
+ const struct shminfo & shminfo () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shminfo;
+ }
+
+ const struct shm_info & shm_info () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shm_info;
+ }
-void *shmat(int, const void *, int);
-int shmctl(int, int, struct shmid_ds *);
-int shmdt(const void *);
-int shmget(key_t, size_t, int);
+private:
+ union
+ {
+ struct
+ {
+ shmop_t shmop;
+ key_t key;
+ size_t size;
+ int shmflg;
+ int shmid;
+ int cmd;
+ pid_t cygpid;
+ DWORD winpid;
+ uid_t uid;
+ gid_t gid;
+ struct shmid_ds ds;
+ } in;
+ struct {
+ int shmid;
+ union
+ {
+ HANDLE hFileMap;
+ struct shmid_ds ds;
+ struct shminfo shminfo;
+ struct shm_info shm_info;
+ };
+ } out;
+ } _parameters;
+
+#ifndef __INSIDE_CYGWIN__
+ client_request_shm ();
+#endif
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void serve (transport_layer_base *, process_cache *);
#endif
+};
+
+#endif /* __CYGSERVER_SHM_H__ */
diff --git a/winsup/cygserver/threaded_queue.cc b/winsup/cygserver/threaded_queue.cc
index 321fa16..ba0fe41 100644
--- a/winsup/cygserver/threaded_queue.cc
+++ b/winsup/cygserver/threaded_queue.cc
@@ -4,247 +4,405 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
+#include "woutsup.h"
+
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
#include <stdlib.h>
-#include "wincap.h"
#include "threaded_queue.h"
-#define DEBUG 1
-#define debug_printf if (DEBUG) printf
+
+/*****************************************************************************/
+
+/* queue_request */
+
+queue_request::~queue_request ()
+{}
+
+/*****************************************************************************/
/* threaded_queue */
-DWORD WINAPI
-worker_function (LPVOID LpParam)
+threaded_queue::threaded_queue (const size_t initial_workers)
+ : _workers_count (0),
+ _running (false),
+ _submitters_head (NULL),
+ _requests_count (0),
+ _requests_head (NULL),
+ _requests_sem (NULL)
{
- class threaded_queue *queue = (class threaded_queue *) LpParam;
- class queue_request *request;
- /* FIXME use a threadsafe pop instead for speed? */
- while (queue->active)
+ InitializeCriticalSection (&_queue_lock);
+
+ // This semaphore's count is the number of requests on the queue.
+ // The maximum count (129792) is calculated as MAXIMUM_WAIT_OBJECTS
+ // multiplied by max. threads per process (2028?), which is (a few)
+ // more requests than could ever be pending with the current design.
+
+ _requests_sem = CreateSemaphore (NULL, // SECURITY_ATTRIBUTES
+ 0, // Initial count
+ 129792, // Maximum count
+ NULL); // Anonymous
+
+ if (!_requests_sem)
{
- EnterCriticalSection (&queue->queuelock);
- while (!queue->request && queue->active)
- {
- LeaveCriticalSection (&queue->queuelock);
- DWORD rc = WaitForSingleObject (queue->event, INFINITE);
- if (rc == WAIT_FAILED)
- {
- printf ("Wait for event failed\n");
- queue->running--;
- ExitThread (0);
- }
- EnterCriticalSection (&queue->queuelock);
- }
- if (!queue->active)
- {
- queue->running--;
- LeaveCriticalSection (&queue->queuelock);
- ExitThread (0);
- }
- /* not needed, but it is efficient */
- request =
- (class queue_request *) InterlockedExchangePointer (&queue->request,
- queue->request->
- next);
- LeaveCriticalSection (&queue->queuelock);
- request->process ();
- delete request;
+ system_printf (("failed to create the request queue semaphore, "
+ "error = %lu"),
+ GetLastError ());
+ abort ();
}
- queue->running--;
- ExitThread (0);
+
+ create_workers (initial_workers);
}
-void
-threaded_queue::create_workers ()
+threaded_queue::~threaded_queue ()
{
- InitializeCriticalSection (&queuelock);
- if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+ if (_running)
+ stop ();
+
+ debug_printf ("deleting all pending queue requests");
+ queue_request *reqptr = _requests_head;
+ while (reqptr)
{
- printf ("Failed to create event queue (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ queue_request *const ptr = reqptr;
+ reqptr = reqptr->_next;
+ safe_delete (ptr);
}
- active = true;
- /* FIXME: Use a stack pair and create threads on the fly whenever
- * we have to to service a request.
- */
- for (unsigned int i = 0; i < initial_workers; i++)
+ DeleteCriticalSection (&_queue_lock);
+ if (_requests_sem)
+ (void) CloseHandle (_requests_sem);
+}
+
+/* FIXME: return success or failure rather than quitting */
+void
+threaded_queue::add_submission_loop (queue_submission_loop *const submitter)
+{
+ assert (this);
+ assert (submitter);
+ assert (submitter->_queue == this);
+ assert (!submitter->_next);
+
+ submitter->_next =
+ TInterlockedExchangePointer (&_submitters_head, submitter);
+
+ if (_running)
+ submitter->start ();
+}
+
+bool
+threaded_queue::start ()
+{
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = true;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (!was_running)
{
- HANDLE hThread;
- DWORD tid;
- hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
- if (hThread == NULL)
+ debug_printf ("starting all queue submission loops");
+
+ while (loopptr)
{
- printf ("Failed to create thread (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->start ();
}
- CloseHandle (hThread);
- running++;
}
+
+ return was_running;
}
-void
-threaded_queue::cleanup ()
+bool
+threaded_queue::stop ()
{
- /* harvest the threads */
- active = false;
- /* kill the request processing loops */
- queue_process_param *reqloop;
- /* make sure we don't race with a incoming request creation */
- EnterCriticalSection (&queuelock);
- reqloop =
- (queue_process_param *) InterlockedExchangePointer (&process_head, NULL);
- while (reqloop)
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = false;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (was_running)
{
- queue_process_param *t = reqloop;
- reqloop = reqloop->next;
- delete t;
+ debug_printf ("stopping all queue submission loops");
+ while (loopptr)
+ {
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->stop ();
+ }
+
+ ReleaseSemaphore (_requests_sem, _workers_count, NULL);
+ while (_workers_count)
+ {
+ debug_printf (("waiting for worker threads to terminate: "
+ "%lu still running"),
+ _workers_count);
+ Sleep (1000);
+ }
+ debug_printf ("all worker threads have terminated");
}
- LeaveCriticalSection (&queuelock);
- if (!running)
- return;
- printf ("Waiting for current queue threads to terminate\n");
- for (int n = running; n; n--)
- PulseEvent (event);
- while (running)
- sleep (1);
- DeleteCriticalSection (&queuelock);
- CloseHandle (event);
+
+ return was_running;
}
/* FIXME: return success or failure */
void
-threaded_queue::add (queue_request * therequest)
+threaded_queue::add (queue_request *const therequest)
{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- EnterCriticalSection (&queuelock);
- if (!running)
+ assert (this);
+ assert (therequest);
+ assert (!therequest->_next);
+
+ if (!_workers_count)
{
- printf ("No worker threads to handle request!\n");
+ system_printf ("warning: no worker threads to handle request!");
+ // FIXME: And then what?
}
- if (!request)
- request = therequest;
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_requests_head)
+ _requests_head = therequest;
else
{
- /* add to the queue end. */
- queue_request *listrequest = request;
- while (listrequest->next)
- listrequest = listrequest->next;
- listrequest->next = therequest;
+ /* Add to the queue end. */
+ queue_request *reqptr = _requests_head;
+ for (; reqptr->_next; reqptr = reqptr->_next)
+ {}
+ assert (reqptr);
+ assert (!reqptr->_next);
+ reqptr->_next = therequest;
}
- PulseEvent (event);
- LeaveCriticalSection (&queuelock);
+
+ _requests_count += 1;
+ assert (_requests_count > 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ (void) ReleaseSemaphore (_requests_sem, 1, NULL);
}
-/* FIXME: return success or failure rather than quitting */
+/*static*/ DWORD WINAPI
+threaded_queue::start_routine (const LPVOID lpParam)
+{
+ class threaded_queue *const queue = (class threaded_queue *) lpParam;
+ assert (queue);
+
+ queue->worker_loop ();
+
+ const long count = InterlockedDecrement (&queue->_workers_count);
+ assert (count >= 0);
+
+ if (queue->_running)
+ debug_printf ("worker loop has exited; thread about to terminate");
+
+ return 0;
+}
+
+/* Called from the constructor: so no need to be thread-safe until the
+ * worker threads start to be created; thus the interlocked increment
+ * of the `_workers_count' field.
+ */
+
void
-threaded_queue::process_requests (queue_process_param * params,
- threaded_queue_thread_function *
- request_loop)
+threaded_queue::create_workers (const size_t initial_workers)
{
- if (params->start (request_loop, this) == false)
- exit (1);
- params->next =
- (queue_process_param *) InterlockedExchangePointer (&process_head,
- params);
+ assert (initial_workers > 0);
+
+ for (unsigned int i = 0; i != initial_workers; i++)
+ {
+ const long count = InterlockedIncrement (&_workers_count);
+ assert (count > 0);
+
+ DWORD tid;
+ const HANDLE hThread =
+ CreateThread (NULL, 0, start_routine, this, 0, &tid);
+
+ if (!hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ (void) CloseHandle (hThread);
+ }
}
-/* queue_process_param */
-/* How does a constructor return an error? */
-queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
-interruptible
-(ninterruptible)
+void
+threaded_queue::worker_loop ()
{
- if (!interruptible)
- return;
- debug_printf ("creating an interruptible processing thread\n");
- if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+ while (true)
{
- printf ("Failed to create interrupt event (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ const DWORD rc = WaitForSingleObject (_requests_sem, INFINITE);
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("wait for request semaphore failed, error = %lu",
+ GetLastError ());
+ return;
+ }
+ assert (rc == WAIT_OBJECT_0);
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_running)
+ {
+ LeaveCriticalSection (&_queue_lock);
+ return;
+ }
+
+ assert (_requests_head);
+ queue_request *const reqptr = _requests_head;
+ _requests_head = reqptr->_next;
+
+ _requests_count -= 1;
+ assert (_requests_count >= 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ assert (reqptr);
+ reqptr->process ();
+ safe_delete (reqptr);
+ }
+}
+
+/*****************************************************************************/
+
+/* queue_submission_loop */
+
+queue_submission_loop::queue_submission_loop (threaded_queue *const queue,
+ const bool ninterruptible)
+ : _running (false),
+ _interrupt_event (NULL),
+ _queue (queue),
+ _interruptible (ninterruptible),
+ _hThread (NULL),
+ _tid (0),
+ _next (NULL)
+{
+ if (_interruptible)
+ {
+ // verbose: debug_printf ("creating an interruptible processing thread");
+
+ _interrupt_event = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_interrupt_event)
+ {
+ system_printf ("failed to create interrupt event, error = %lu",
+ GetLastError ());
+ abort ();
+ }
}
}
-queue_process_param::~queue_process_param ()
+queue_submission_loop::~queue_submission_loop ()
{
- if (running)
+ if (_running)
stop ();
- if (!interruptible)
- return;
- CloseHandle (interrupt);
+ if (_interrupt_event)
+ (void) CloseHandle (_interrupt_event);
+ if (_hThread)
+ (void) CloseHandle (_hThread);
}
bool
- queue_process_param::start (threaded_queue_thread_function * request_loop,
- threaded_queue * thequeue)
+queue_submission_loop::start ()
{
- queue = thequeue;
- hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
- if (hThread)
+ assert (this);
+ assert (!_hThread);
+
+ const bool was_running = _running;
+
+ if (!was_running)
{
- running = true;
- return true;
+ _running = true;
+
+ _hThread = CreateThread (NULL, 0, start_routine, this, 0, &_tid);
+ if (!_hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
}
- printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
- return false;
+
+ return was_running;
}
-void
-queue_process_param::stop ()
+bool
+queue_submission_loop::stop ()
{
- if (interruptible)
+ assert (this);
+ assert (_hThread && _hThread != INVALID_HANDLE_VALUE);
+
+ const bool was_running = _running;
+
+ if (_running)
{
- InterlockedExchange (&shutdown, true);
- PulseEvent (interrupt);
- /* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get
- * scheduled again, we print an error and exit. We _should_ loop or
- * try resignalling. We don't want to hand here though...
- */
- int n = 5;
- while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT);
- if (!n)
+ _running = false;
+
+ if (_interruptible)
{
- printf ("Process thread didn't shutdown cleanly after 200ms!\n");
- exit (1);
+ assert (_interrupt_event
+ && _interrupt_event != INVALID_HANDLE_VALUE);
+
+ SetEvent (_interrupt_event);
+
+ if (WaitForSingleObject (_hThread, 1000) == WAIT_TIMEOUT)
+ {
+ system_printf (("request loop thread %lu failed to shutdown "
+ "when asked politely: about to get heavy"),
+ _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ {
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
+ abort ();
+ }
+ }
}
else
- running = false;
- }
- else
- {
- printf ("killing request loop thread %ld\n", tid);
- int rc;
- if (!(rc = TerminateThread (hThread, 0)))
{
- printf ("error shutting down request loop worker thread\n");
+ // FIXME: could wait to see if the request loop notices that
+ // the submission loop is no longer running and shuts down
+ // voluntarily.
+
+ debug_printf ("killing request loop thread %lu", _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
}
- running = false;
}
- CloseHandle (hThread);
-}
-/* queue_request */
-queue_request::queue_request ():next (NULL)
-{
+ return was_running;
}
-void
-queue_request::process (void)
+/*static*/ DWORD WINAPI
+queue_submission_loop::start_routine (const LPVOID lpParam)
{
- printf ("\n**********************************************\n"
- "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
- "fault !!!\n" "***********************************************\n");
+ class queue_submission_loop *const submission_loop =
+ (class queue_submission_loop *) lpParam;
+ assert (submission_loop);
+
+ submission_loop->request_loop ();
+
+ debug_printf ("submission loop has exited; thread about to terminate");
+
+ submission_loop->stop ();
+
+ return 0;
}
+
+/*****************************************************************************/
diff --git a/winsup/cygserver/transport.cc b/winsup/cygserver/transport.cc
index 01f0444..8684a61 100644
--- a/winsup/cygserver/transport.cc
+++ b/winsup/cygserver/transport.cc
@@ -4,89 +4,48 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
-#include <sys/types.h>
#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
+
+#include "safe_memory.h"
+
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
-/* to allow this to link into cygwin and the .dll, a little magic is needed. */
-#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-#else
-#define debug_printf printf
-#endif
-
/* The factory */
-class transport_layer_base *create_server_transport()
+transport_layer_base *
+create_server_transport ()
{
- transport_layer_base *temp;
- /* currently there is only the base class! */
if (wincap.is_winnt ())
- temp = new transport_layer_pipes ();
+ return safe_new0 (transport_layer_pipes);
else
- temp = new transport_layer_sockets ();
- return temp;
+ return safe_new0 (transport_layer_sockets);
}
-
-transport_layer_base::transport_layer_base ()
-{
- /* should we throw an error of some sort ? */
-}
-
-void
-transport_layer_base::listen ()
-{
-}
-
-class transport_layer_base *
-transport_layer_base::accept ()
-{
- return NULL;
-}
-
-void
-transport_layer_base::close()
-{
-}
-
-ssize_t
-transport_layer_base::read (char *buf, size_t len)
-{
- return 0;
-}
-
-ssize_t
-transport_layer_base::write (char *buf, size_t len)
-{
- return 0;
-}
-
-bool
-transport_layer_base::connect ()
-{
- return false;
-}
+#ifndef __INSIDE_CYGWIN__
void
transport_layer_base::impersonate_client ()
-{
-}
+{}
void
transport_layer_base::revert_to_self ()
-{
-}
+{}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+transport_layer_base::~transport_layer_base ()
+{}
diff --git a/winsup/cygserver/transport_pipes.cc b/winsup/cygserver/transport_pipes.cc
index f222170..f318a75 100644
--- a/winsup/cygserver/transport_pipes.cc
+++ b/winsup/cygserver/transport_pipes.cc
@@ -4,207 +4,360 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
-#include <sys/socket.h>
+
+#include <assert.h>
+#include <errno.h>
#include <netdb.h>
-#include "wincap.h"
+#include <pthread.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
-/* to allow this to link into cygwin and the .dll, a little magic is needed. */
-#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-#else
-#define DEBUG 0
-#define debug_printf if (DEBUG) printf
+#ifndef __INSIDE_CYGWIN__
+#include "cygwin/cygserver.h"
#endif
-//SECURITY_DESCRIPTOR transport_layer_pipes::sd;
-//SECURITY_ATTRIBUTES transport_layer_pipes::sec_none_nih, transport_layer_pipes::sec_all_nih;
-//bool transport_layer_pipes::inited = false;
+enum
+ {
+ MAX_WAIT_NAMED_PIPE_RETRY = 64,
+ WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds
+ };
+
+#ifndef __INSIDE_CYGWIN__
+
+static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT;
+static CRITICAL_SECTION pipe_instance_lock;
+static long pipe_instance = 0;
+
+static void
+initialise_pipe_instance_lock ()
+{
+ assert (pipe_instance == 0);
+ InitializeCriticalSection (&pipe_instance_lock);
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+#ifndef __INSIDE_CYGWIN__
-transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
+transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe)
+ : _pipe_name (""),
+ _hPipe (hPipe),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
{
- inited = false; //FIXME: allow inited, sd, all_nih_.. to be static members
- pipe = new_pipe;
- if (inited != true)
- init_security();
-};
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ init_security ();
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
transport_layer_pipes::transport_layer_pipes ()
+ : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"),
+ _hPipe (NULL),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
{
- inited = false;
- pipe = NULL;
- strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
- if (inited != true)
- init_security();
+ init_security ();
}
void
-transport_layer_pipes::init_security()
+transport_layer_pipes::init_security ()
{
+ assert (wincap.has_security ());
+
/* FIXME: pthread_once or equivalent needed */
- InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE);
-
- sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
- sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
- sec_none_nih.lpSecurityDescriptor = NULL;
- sec_all_nih.lpSecurityDescriptor = &sd;
- inited = true;
+
+ InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE);
+
+ _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
+ _sec_all_nih.lpSecurityDescriptor = &_sd;
+ _sec_all_nih.bInheritHandle = FALSE;
}
-void
+transport_layer_pipes::~transport_layer_pipes ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
transport_layer_pipes::listen ()
{
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ _is_listening_endpoint = true;
+
/* no-op */
+ return 0;
}
class transport_layer_pipes *
-transport_layer_pipes::accept ()
+transport_layer_pipes::accept (bool *const recoverable)
{
- if (pipe)
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock);
+
+ EnterCriticalSection (&pipe_instance_lock);
+
+ // Read: http://www.securityinternals.com/research/papers/namedpipe.php
+ // See also the Microsoft security bulletins MS00-053 and MS01-031.
+
+ // FIXME: Remove FILE_CREATE_PIPE_INSTANCE.
+
+ const bool first_instance = (pipe_instance == 0);
+
+ const HANDLE accept_pipe =
+ CreateNamedPipe (_pipe_name,
+ (PIPE_ACCESS_DUPLEX
+ | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)),
+ (PIPE_TYPE_BYTE | PIPE_WAIT),
+ PIPE_UNLIMITED_INSTANCES,
+ 0, 0, 1000,
+ &_sec_all_nih);
+
+ const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE
+ && pipe_instance == 0
+ && GetLastError () == ERROR_ACCESS_DENIED);
+
+ if (accept_pipe != INVALID_HANDLE_VALUE)
+ InterlockedIncrement (&pipe_instance);
+
+ LeaveCriticalSection (&pipe_instance_lock);
+
+ if (duplicate)
{
- debug_printf ("Already have a pipe in this %p\n",this);
+ *recoverable = false;
+ system_printf ("failed to create named pipe: "
+ "is the daemon already running?");
return NULL;
}
- pipe = CreateNamedPipe (pipe_name,
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- 0, 0, 1000,
- &sec_all_nih );
- if (pipe == INVALID_HANDLE_VALUE)
+ if (accept_pipe == INVALID_HANDLE_VALUE)
{
- debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
+ debug_printf ("error creating pipe (%lu).", GetLastError ());
+ *recoverable = true; // FIXME: case analysis?
return NULL;
}
- if ( !ConnectNamedPipe ( pipe, NULL ) &&
- GetLastError () != ERROR_PIPE_CONNECTED)
+ assert (accept_pipe);
+
+ if (!ConnectNamedPipe (accept_pipe, NULL)
+ && GetLastError () != ERROR_PIPE_CONNECTED)
{
- printf ("error connecting to pipe (%lu)\n.", GetLastError ());
- CloseHandle (pipe);
- pipe = NULL;
+ debug_printf ("error connecting to pipe (%lu)\n.", GetLastError ());
+ (void) CloseHandle (accept_pipe);
+ *recoverable = true; // FIXME: case analysis?
return NULL;
}
- transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
- pipe = NULL;
-
- return new_conn;
+ return safe_new (transport_layer_pipes, accept_pipe);
}
+#endif /* !__INSIDE_CYGWIN__ */
+
void
-transport_layer_pipes::close()
+transport_layer_pipes::close ()
{
- debug_printf ("closing pipe %p\n", pipe);
- if (pipe && pipe != INVALID_HANDLE_VALUE)
+ // verbose: debug_printf ("closing pipe %p", _hPipe);
+
+ if (_hPipe)
{
- FlushFileBuffers (pipe);
- DisconnectNamedPipe (pipe);
- CloseHandle (pipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+#ifndef __INSIDE_CYGWIN__
+
+ if (_is_accepted_endpoint)
+ {
+ (void) FlushFileBuffers (_hPipe); // Blocks until client reads.
+ (void) DisconnectNamedPipe (_hPipe);
+ EnterCriticalSection (&pipe_instance_lock);
+ (void) CloseHandle (_hPipe);
+ assert (pipe_instance > 0);
+ InterlockedDecrement (&pipe_instance);
+ LeaveCriticalSection (&pipe_instance_lock);
+ }
+ else
+ (void) CloseHandle (_hPipe);
+
+#else /* __INSIDE_CYGWIN__ */
+
+ assert (!_is_accepted_endpoint);
+ (void) ForceCloseHandle (_hPipe);
+
+#endif /* __INSIDE_CYGWIN__ */
+
+ _hPipe = NULL;
}
}
ssize_t
-transport_layer_pipes::read (char *buf, size_t len)
+transport_layer_pipes::read (void *const buf, const size_t len)
{
- debug_printf ("reading from pipe %p\n", pipe);
- if (!pipe || pipe == INVALID_HANDLE_VALUE)
- return -1;
+ // verbose: debug_printf ("reading from pipe %p", _hPipe);
- DWORD bytes_read;
- DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
- if (!rc)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!ReadFile (_hPipe, buf, len, &count, NULL))
{
- debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
+ debug_printf ("error reading from pipe (%lu)", GetLastError ());
+ set_errno (EINVAL); // FIXME?
return -1;
}
- return bytes_read;
+
+ return count;
}
ssize_t
-transport_layer_pipes::write (char *buf, size_t len)
+transport_layer_pipes::write (void *const buf, const size_t len)
{
- debug_printf ("writing to pipe %p\n", pipe);
- DWORD bytes_written, rc;
- if (!pipe || pipe == INVALID_HANDLE_VALUE)
- return -1;
+ // verbose: debug_printf ("writing to pipe %p", _hPipe);
- rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
- if (!rc)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!WriteFile (_hPipe, buf, len, &count, NULL))
{
- debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
+ debug_printf ("error writing to pipe, error = %lu", GetLastError ());
+ set_errno (EINVAL); // FIXME?
return -1;
}
- return bytes_written;
+
+ return count;
}
-bool
+/*
+ * This routine holds a static variable, assume_cygserver, that is set
+ * if the transport has good reason to think that cygserver is
+ * running, i.e. if if successfully connected to it with the previous
+ * attempt. If this is set, the code tries a lot harder to get a
+ * connection, making the assumption that any failures are just
+ * congestion and overloading problems.
+ */
+
+int
transport_layer_pipes::connect ()
{
- if (pipe && pipe != INVALID_HANDLE_VALUE)
- {
- debug_printf ("Already have a pipe in this %p\n",this);
- return false;
- }
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
- while (1)
+ static bool assume_cygserver = false;
+
+ BOOL rc = TRUE;
+ int retries = 0;
+
+ while (rc)
{
- 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)
- /* got the pipe */
- return true;
-
- if (GetLastError () != ERROR_PIPE_BUSY)
+ _hPipe = CreateFile (_pipe_name,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &_sec_all_nih,
+ OPEN_EXISTING,
+ SECURITY_IMPERSONATION,
+ NULL);
+
+ if (_hPipe != INVALID_HANDLE_VALUE)
+ {
+ assert (_hPipe);
+#ifdef __INSIDE_CYGWIN__
+ ProtectHandle (_hPipe);
+#endif
+ assume_cygserver = true;
+ return 0;
+ }
+
+ _hPipe = NULL;
+
+ if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY)
{
- debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
- pipe = NULL;
- return false;
+ debug_printf ("Error opening the pipe (%lu)", GetLastError ());
+ return -1;
}
- if (!WaitNamedPipe (pipe_name, 20000))
- debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () );
- /* We loop here, because the pipe exists but is busy. If it doesn't exist
- * the != ERROR_PIPE_BUSY will catch it.
+
+ /* Note: `If no instances of the specified named pipe exist, the
+ * WaitNamedPipe function returns immediately, regardless of the
+ * time-out value.' Thus the explicit Sleep if the call fails
+ * with ERROR_FILE_NOT_FOUND.
*/
+ while (retries != MAX_WAIT_NAMED_PIPE_RETRY
+ && !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT)))
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND)
+ Sleep (0); // Give the server a chance.
+
+ retries += 1;
+ }
}
+
+ assert (retries == MAX_WAIT_NAMED_PIPE_RETRY);
+
+ system_printf ("lost connection to cygserver, error = %lu",
+ GetLastError ());
+
+ assume_cygserver = false;
+
+ return -1;
}
+#ifndef __INSIDE_CYGWIN__
+
void
transport_layer_pipes::impersonate_client ()
{
- debug_printf ("impersonating pipe %p\n", pipe);
- if (pipe && pipe != INVALID_HANDLE_VALUE)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (_is_accepted_endpoint);
+
+ // verbose: debug_printf ("impersonating pipe %p", _hPipe);
+ if (_hPipe)
{
- BOOL rv = ImpersonateNamedPipeClient (pipe);
- if (!rv)
- debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ if (!ImpersonateNamedPipeClient (_hPipe))
+ debug_printf ("Failed to Impersonate the client, (%lu)",
+ GetLastError ());
}
- debug_printf("I am who you are\n");
+ // verbose: debug_printf ("I am who you are");
}
void
transport_layer_pipes::revert_to_self ()
{
+ assert (_is_accepted_endpoint);
+
RevertToSelf ();
- debug_printf("I am who I yam\n");
+ // verbose: debug_printf ("I am who I yam");
}
+#endif /* !__INSIDE_CYGWIN__ */
diff --git a/winsup/cygserver/transport_sockets.cc b/winsup/cygserver/transport_sockets.cc
index a3a98b3..6ade14b 100644
--- a/winsup/cygserver/transport_sockets.cc
+++ b/winsup/cygserver/transport_sockets.cc
@@ -4,128 +4,384 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
+
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-extern "C" int
-cygwin_socket (int af, int type, int protocol);
-extern "C" int
-cygwin_connect (int fd,
- const struct sockaddr *name,
- int namelen);
-extern "C" int
-cygwin_accept (int fd, struct sockaddr *peer, int *len);
-extern "C" int
-cygwin_listen (int fd, int backlog);
-extern "C" int
-cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen);
-#else
-#define cygwin_accept(A,B,C) ::accept(A,B,C)
-#define cygwin_socket(A,B,C) ::socket(A,B,C)
-#define cygwin_listen(A,B) ::listen(A,B)
-#define cygwin_bind(A,B,C) ::bind(A,B,C)
-#define cygwin_connect(A,B,C) ::connect(A,B,C)
-#define debug_printf printf
-#endif
+extern "C" int cygwin_accept (int fd, struct sockaddr *, int *len);
+extern "C" int cygwin_bind (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_connect (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_listen (int fd, int backlog);
+extern "C" int cygwin_shutdown (int fd, int how);
+extern "C" int cygwin_socket (int af, int type, int protocol);
+
+#else /* __OUTSIDE_CYGWIN__ */
+
+#define cygwin_accept(A,B,C) ::accept (A,B,C)
+#define cygwin_bind(A,B,C) ::bind (A,B,C)
+#define cygwin_connect(A,B,C) ::connect (A,B,C)
+#define cygwin_listen(A,B) ::listen (A,B)
+#define cygwin_shutdown(A,B) ::shutdown (A,B)
+#define cygwin_socket(A,B,C) ::socket (A,B,C)
+
+#endif /* __OUTSIDE_CYGWIN__ */
-transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
+enum
+ {
+ MAX_CONNECT_RETRY = 64
+ };
+
+transport_layer_sockets::transport_layer_sockets (const int fd)
+ : _fd (fd),
+ _addr_len (0),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
{
- /* This may not be needed in this constructor - it's only used
- * when creating a connection via bind or connect
- */
- sockdetails.sa_family = AF_UNIX;
- strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
- sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
-};
-
-transport_layer_sockets::transport_layer_sockets (): fd (-1)
+ assert (_fd != -1);
+
+ memset (&_addr, '\0', sizeof (_addr));
+}
+
+transport_layer_sockets::transport_layer_sockets ()
+ : _fd (-1),
+ _addr_len (0),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
{
- sockdetails.sa_family = AF_UNIX;
- strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
- sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
+ memset (&_addr, '\0', sizeof (_addr));
+
+ _addr.sun_family = AF_UNIX;
+ strcpy (_addr.sun_path, "/tmp/cygdaemo"); // FIXME: $TMP?
+ _addr_len = SUN_LEN (&_addr);
}
-void
+transport_layer_sockets::~transport_layer_sockets ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
transport_layer_sockets::listen ()
{
- /* we want a thread pool based approach. */
- if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0)
- printf ("Socket not created error %d\n", errno);
- if (cygwin_bind(fd, &sockdetails, sdlen))
- printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno);
- if (cygwin_listen(fd, 5) < 0)
- printf ("And the OS just isn't listening, all it says is %d\n", errno);
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ debug_printf ("listen () [this = %p]", this);
+
+ struct stat sbuf;
+
+ if (stat (_addr.sun_path, &sbuf) == -1)
+ {
+ if (errno != ENOENT)
+ {
+ system_printf ("cannot access socket file `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+ }
+ else if (S_ISSOCK (sbuf.st_mode))
+ {
+ // The socket already exists: is a duplicate cygserver running?
+
+ const int newfd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (newfd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (newfd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ system_printf ("the daemon is already running");
+ (void) cygwin_shutdown (newfd, SHUT_WR);
+ char buf[BUFSIZ];
+ while (::read (newfd, buf, sizeof (buf)) > 0)
+ {}
+ (void) ::close (newfd);
+ return -1;
+ }
+
+ if (unlink (_addr.sun_path) == -1)
+ {
+ system_printf ("failed to remove `%s': %s",
+ _addr.sun_path, strerror (errno));
+ (void) ::close (newfd);
+ return -1;
+ }
+ }
+ else
+ {
+ system_printf ("cannot create socket `%s': File already exists",
+ _addr.sun_path);
+ return -1;
+ }
+
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_bind (_fd, (struct sockaddr *) &_addr, _addr_len) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to bind UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ _is_listening_endpoint = true; // i.e. this really means "have bound".
+
+ if (cygwin_listen (_fd, SOMAXCONN) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to listen on UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ debug_printf ("0 = listen () [this = %p, fd = %d]", this, _fd);
+
+ return 0;
}
class transport_layer_sockets *
-transport_layer_sockets::accept ()
+transport_layer_sockets::accept (bool *const recoverable)
{
- /* FIXME: check we have listened */
- int new_fd;
+ assert (_fd != -1);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ debug_printf ("accept () [this = %p, fd = %d]", this, _fd);
+
+ struct sockaddr_un client_addr;
+ socklen_t client_addr_len = sizeof (client_addr);
- if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
+ const int accept_fd =
+ cygwin_accept (_fd, (struct sockaddr *) &client_addr, &client_addr_len);
+
+ if (accept_fd == -1)
{
- printf ("Nup, could' accept. %d\n",errno);
+ system_printf ("failed to accept connection: %s", strerror (errno));
+ switch (errno)
+ {
+ case ECONNABORTED:
+ case EINTR:
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ case ENOMEM:
+ *recoverable = true;
+ break;
+
+ default:
+ *recoverable = false;
+ break;
+ }
return NULL;
}
- transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
-
- return new_conn;
+ debug_printf ("%d = accept () [this = %p, fd = %d]", accept_fd, this, _fd);
+ return safe_new (transport_layer_sockets, accept_fd);
}
+#endif /* !__INSIDE_CYGWIN__ */
+
void
-transport_layer_sockets::close()
+transport_layer_sockets::close ()
{
- /* FIXME - are we open? */
- ::close (fd);
+ debug_printf ("close () [this = %p, fd = %d]", this, _fd);
+
+ if (_is_listening_endpoint)
+ (void) unlink (_addr.sun_path);
+
+ if (_fd != -1)
+ {
+ (void) cygwin_shutdown (_fd, SHUT_WR);
+ if (!_is_listening_endpoint)
+ {
+ char buf[BUFSIZ];
+ while (::read (_fd, buf, sizeof (buf)) > 0)
+ {}
+ }
+ (void) ::close (_fd);
+ _fd = -1;
+ }
+
+ _is_listening_endpoint = false;
}
ssize_t
-transport_layer_sockets::read (char *buf, size_t len)
+transport_layer_sockets::read (void *const buf, const size_t buf_len)
{
- /* FIXME: are we open? */
- return ::read (fd, buf, len);
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *read_buf = static_cast<char *> (buf);
+ size_t read_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (read_buf_len != 0
+ && (res = ::read (_fd, read_buf, read_buf_len)) > 0)
+ {
+ read_buf += res;
+ read_buf_len -= res;
+
+ assert (read_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - read_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
}
ssize_t
-transport_layer_sockets::write (char *buf, size_t len)
+transport_layer_sockets::write (void *const buf, const size_t buf_len)
{
- /* FIXME: are we open? */
- return ::write (fd, buf, len);
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *write_buf = static_cast<char *> (buf);
+ size_t write_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (write_buf_len != 0
+ && (res = ::write (_fd, write_buf, write_buf_len)) > 0)
+ {
+ write_buf += res;
+ write_buf_len -= res;
+
+ assert (write_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - write_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
}
-bool
+int
transport_layer_sockets::connect ()
{
- /* are we already connected? */
- if (fd != -1)
- return false;
- fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
- if (cygwin_connect (fd, &sockdetails, sdlen) < 0)
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ static bool assume_cygserver = false;
+
+ debug_printf ("connect () [this = %p]", this);
+
+ for (int retries = 0; retries != MAX_CONNECT_RETRY; retries++)
{
- debug_printf("client connect failure %d\n", errno);
- ::close (fd);
- return false;
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (_fd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ assume_cygserver = true;
+ debug_printf ("0 = connect () [this = %p, fd = %d]", this, _fd);
+ return 0;
+ }
+
+ if (!assume_cygserver || errno != ECONNREFUSED)
+ {
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ (void) ::close (_fd);
+ _fd = -1;
+ return -1;
+ }
+
+ (void) ::close (_fd);
+ _fd = -1;
+ Sleep (0); // Give the server a chance.
}
- return true;
+
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ return -1;
}
diff --git a/winsup/cygserver/woutsup.h b/winsup/cygserver/woutsup.h
new file mode 100644
index 0000000..c048f1c
--- /dev/null
+++ b/winsup/cygserver/woutsup.h
@@ -0,0 +1,110 @@
+/* woutsup.h: for Cygwin code compiled outside the DLL (i.e. cygserver).
+
+ Copyright 2002 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. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef __INSIDE_CYGWIN__
+#error "woutsup.h is not for code being compiled inside the dll"
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#if _WIN32_WINNT < 0x0500
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#define WIN32_LEAN_AND_MEAN 1
+#define _WINGDI_H
+#define _WINUSER_H
+#define _WINNLS_H
+#define _WINVER_H
+#define _WINNETWK_H
+#define _WINSVC_H
+#include <windows.h>
+#include <wincrypt.h>
+#include <lmcons.h>
+#undef _WINGDI_H
+#undef _WINUSER_H
+#undef _WINNLS_H
+#undef _WINVER_H
+#undef _WINNETWK_H
+#undef _WINSVC_H
+
+#include "wincap.h"
+
+/* The one function we use from winuser.h most of the time */
+extern "C" DWORD WINAPI GetLastError (void);
+
+extern int cygserver_running;
+
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L
+#define NEW_MACRO_VARARGS
+#endif
+
+/*
+ * A reproduction of the <sys/strace.h> macros. This allows code that
+ * runs both inside and outside the Cygwin DLL to use the same macros
+ * for logging messages.
+ */
+
+extern "C" void __cygserver__printf (const char *, const char *, ...);
+
+#ifdef NEW_MACRO_VARARGS
+
+#define system_printf(...) \
+ do \
+ { \
+ __cygserver__printf (__PRETTY_FUNCTION__, __VA_ARGS__); \
+ } while (false)
+
+#define __noop_printf(...) do {;} while (false)
+
+#else /* !NEW_MACRO_VARARGS */
+
+#define system_printf(args...) \
+ do \
+ { \
+ __cygserver__printf (__PRETTY_FUNCTION__, ## args); \
+ } while (false)
+
+#define __noop_printf(args...) do {;} while (false)
+
+#endif /* !NEW_MACRO_VARARGS */
+
+#ifdef DEBUGGING
+#define debug_printf system_printf
+#define paranoid_printf system_printf
+#define select_printf system_printf
+#define sigproc_printf system_printf
+#define syscall_printf system_printf
+#define termios_printf system_printf
+#define wm_printf system_printf
+#define minimal_printf system_printf
+#define malloc_printf system_printf
+#define thread_printf system_printf
+#else
+#define debug_printf __noop_printf
+#define paranoid_printf __noop_printf
+#define select_printf __noop_printf
+#define sigproc_printf __noop_printf
+#define syscall_printf __noop_printf
+#define termios_printf __noop_printf
+#define wm_printf __noop_printf
+#define minimal_printf __noop_printf
+#define malloc_printf __noop_printf
+#define thread_printf __noop_printf
+#endif
+
+#include "safe_memory.h"
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index a8512a4..eaf19df 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,1110 @@
+2002-09-22 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ GNUify non-GNU formatted functions calls throughout.
+
+2002-09-22 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (with_strerr): Remove macro.
+ (server_shmmgr::segment_t::~segment_t): Remove calls to with_strerr.
+ (server_shmmgr::segment_t::attach): Ditto.
+ (server_shmmgr::new_segment): Ditto.
+ * shm.cc (with_strerr): Remove macro.
+ (client_shmmgr::shmdt): Remove calls to with_strerr.
+ (client_shmmgr::attach): Ditto.
+
+2002-09-21 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/sys/ipc.h: Move to "include/cygwin/ipc.h".
+ * include/sys/msg.h: Move to "include/cygwin/msg.h".
+ * include/sys/sem.h: Move to "include/cygwin/sem.h".
+ * include/sys/shm.h: Move to "include/cygwin/shm.h".
+ * include/cygwin/ipc.h: New file.
+ * include/cygwin/msg.h: Ditto.
+ * include/cygwin/sem.h: Ditto.
+ * include/cygwin/shm.h: Ditto.
+ * cygserver_shm.h: Update includes.
+ * msg.cc: Ditto.
+ * sem.cc: Ditto.
+
+2002-09-21 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * safe_memory.h (safe_delete): Make a templated function.
+ * cygserver.cc (~server_request): Update use of safe_delete.
+ (main): Ditto.
+ * cygserver_client.cc (client_request::handle_request): Ditto.
+ (client_request::make_request): Ditto.
+ * cygserver_process.cc (~process_cleanup): Ditto.
+ (process::remove): Ditto.
+ (process::cleanup): Ditto.
+ (process_cache::process): Ditto.
+ * cygserver_shm.cc (server_shmmgr::segment_t::detach): Ditto.
+ (server_shmmgr::delete_segment): Ditto.
+ * shm.cc (client_shmmgr::shmdt): Ditto.
+ * threaded_queue.cc (~threaded_queue): Ditto.
+ (threaded_queue::worker_loop): Ditto.
+
+2002-08-29 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * safe_memory.h: Replace #include <new> with an explicit
+ definition of the placement new operator.
+ (safe_delete): Remove unnecessary ## operator.
+
+2002-07-28 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (class server_shmmgr): Remove `cleanup_t'
+ friend declaration.
+ (cleanup_t::cleanup_t): Use the segment's shmid as the key rather
+ than the segment pointer itself.
+ (cleanup_t::segptr): Remove method.
+ (cleanup_t::shmid): New method.
+ (cleanup_t::cleanup): Update for new key value.
+ (server_shmmgr::find (segment_t *)): Remove method.
+ * include/cygwin/cygserver_process.h (cleanup_routine::key): Make
+ method const.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver_process.h
+ (cleanup_routine::_key): New field.
+ (cleanup_routine::cleanup_routine): Initialise new field with new
+ argument.
+ (cleanup_routine::operator==): New method.
+ (cleanup_routine::key): New method.
+ (cleanup_routine::cleanup): Make argument non-const.
+ (process::is_active): New method.
+ (process::remove): Ditto.
+ (process::check_exit_code): Rename method.
+ * cygserver_process.cc (process::add): Reorganize code.
+ (process::remove): New method.
+ (process::check_exit_code): Rename method.
+ (process::cleanup): Use new `process::is_active' method.
+ (process_cache::process): Ditto.
+ (process_cache::sync_wait_array): Ditto.
+ (process_cache::check_and_remove_process): Ditto.
+ * cygserver_shm.cc (server_shmmgr): Make `cleanup_t' a friend.
+ (segment_t::detach): Make argument non-const. Remove cleanup
+ object from client if appropriate.
+ (cleanup_t::_segptr): Remove field.
+ (cleanup_t::cleanup_t): Initialise parent explicitly. Remove
+ field.
+ (cleanup_t::segptr): New method.
+ (cleanup_t::cleanup): Add error checking and reporting.
+ (server_shmmgr::shmdt): Make argument non-const.
+ (server_shmmgr::find (segment_t *)): New method.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (client_request_shutdown::client_request_shutdown):
+ Comment out verbose tracing statement.
+ * cygserver_client.cc
+ (client_request_get_version::client_request_get_version): Ditto.
+ (client_request_attach_tty::client_request_attach_tty): Ditto.
+ * cygserver_shm.cc (client_request_shm::client_request_shm):
+ Ditto.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport_pipes.cc (transport_layer_pipes::listen):
+ Set `_is_listening_endpoint' appropriately.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver_transport.h
+ (transport_layer_base::listen): Change return type.
+ (transport_layer_base::connect): Ditto.
+ * include/cygwin/cygserver_transport_pipes.h
+ (transport_layer_pipes::listen): Change return type.
+ (transport_layer_pipes::connect): Ditto.
+ (transport_layer_pipes::_sec_none_nih): Remove unused field.
+ (transport_layer_pipes::_is_listening_endpoint): New field.
+ * cygserver_transport_pipes.cc: Synchronize with sockets code.
+ (transport_layer_pipes::transport_layer_pipes): Initialise new
+ field. Separate out asserts.
+ (transport_layer_pipes::listen): Change return type. Add asserts.
+ (transport_layer_pipes::accept): Add asserts.
+ (transport_layer_pipes::read): Change conditional to an assert.
+ Add assert.
+ (transport_layer_pipes::write): Ditto.
+ (transport_layer_pipes::connect): Change return type. Change
+ conditional to an assert. Add asserts. Rationalize error code
+ slightly.
+ (transport_layer_pipes::impersonate_client): Add asserts.
+ * include/cygwin/cygserver_transport_sockets.h
+ (transport_layer_sockets::listen): Change return type.
+ (transport_layer_sockets::connect): Ditto.
+ (transport_layer_sockets::_addr): Change type of field.
+ (transport_layer_sockets::_addr_len): Ditto.
+ (transport_layer_sockets::_is_accepted_endpoint): New field.
+ (transport_layer_sockets::_is_listening_endpoint): Ditto.
+ * cygserver_transport_sockets.cc
+ (MAX_CONNECT_RETRY): New constant.
+ (transport_layer_sockets::transport_layer_sockets): Initialise new
+ fields. Only initialise the socket address where necessary.
+ (transport_layer_sockets::listen): Change return type. Rewrite.
+ (transport_layer_sockets::accept): Add asserts. Add tracing
+ statements. Use a local variable to hold the accepted address.
+ (transport_layer_sockets::close): Add tracing statements. Unlink
+ the UNIX domain socket file as appropriate. Close the socket
+ cleanly.
+ (transport_layer_sockets::read): Rewrite method.
+ (transport_layer_sockets::write): Ditto.
+ (transport_layer_sockets::connect): Change return type. Rewrite.
+ * cygserver.cc (server_submission_loop::request_loop): Run the
+ listening thread at high priority with special handling for
+ shutdown.
+ (main): Print the request error code rather than errno in shutdown
+ request code. Install signal handlers with sigaction(2) to avoid
+ setting SA_RESTART. Check value of the listen method call, now it
+ has one.
+ * cygserver_client.cc (client_request::make_request): Check new
+ return value on connect method call.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver_transport_pipes.h
+ (cygserver_transport_pipes::_sd): Rename field.
+ (cygserver_transport_pipes::_sec_none_nih): Ditto.
+ (cygserver_transport_pipes::_sec_all_nih): Ditto.
+ (cygserver_transport_pipes::_pipe_name): Ditto.
+ (cygserver_transport_pipes::_hPipe): Ditto.
+ (cygserver_transport_pipes::_is_accepted_endpoint): Ditto.
+ * cygserver_transport_pipes.cc
+ (transport_layer_pipes::transport_layer_pipes): Rename fields.
+ (transport_layer_pipes::init_security): Ditto.
+ (transport_layer_pipes::listen): Ditto.
+ (transport_layer_pipes::accept): Ditto.
+ (transport_layer_pipes::close): Ditto.
+ (transport_layer_pipes::read): Ditto.
+ (transport_layer_pipes::write): Ditto.
+ (transport_layer_pipes::connect): Ditto.
+ (transport_layer_pipes::impersonate_client): Ditto.
+ (transport_layer_pipes::revert_to_self): Ditto.
+ * include/cygwin/cygserver_transport_sockets.h
+ (cygserver_transport_sockets::_fd): Rename field.
+ (cygserver_transport_sockets::_addr): Ditto.
+ (cygserver_transport_sockets::_addr_len): Ditto.
+ * cygserver_transport_sockets.cc
+ (transport_layer_sockets::transport_layer_sockets): Rename fields.
+ (transport_layer_sockets::listen): Ditto.
+ (transport_layer_sockets::accept): Ditto.
+ (transport_layer_sockets::close): Ditto.
+ (transport_layer_sockets::read): Ditto.
+ (transport_layer_sockets::write): Ditto.
+ (transport_layer_sockets::connect): Ditto.
+
+2002-07-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (with_strerr): Fix use of %p format.
+ * shm.cc (client_shmmgr::shmat): Ditto.
+ (client_shmmgr::shmctl): Ditto.
+ (client_shmmgr::shmdt): Ditto.
+ (client_shmmgr::attach): Ditto.
+
+2002-07-14 Christopher Faylor <cgf@redhat.com>
+
+ * woutsup.h (system_printf): Remove extraneous semicolon from macro
+ definition.
+
+2002-07-14 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport_pipes.cc
+ (transport_layer_pipes::connect): Use ProtectHandle in DLL code.
+ (transport_layer_pipes::close): Use ForceCloseHandle in DLL code.
+
+2002-07-13 Nicholas Wourms <nwourms@netscape.com>
+
+ * threaded_queue.h (class queue_submission_loop): Correct friend
+ declaration for GCC 3.1.1.
+ * include/cygwin/cygserver_process.h (class process): Ditto.
+ (class process_cache): Ditto.
+
+2002-07-12 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (server_shmmgr::shmdt): Only call
+ delete_segment if the segment exists [sic].
+
+2002-07-12 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * safe_memory.h: Include <new> rather than <new.h> for gcc 3.
+
+2002-07-11 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * safe_memory.h: New file extracted from "woutsup.h".
+ * woutsup.h: Move the "safe" new/delete macros into the new
+ "safe_memory.h" header file and include that here.
+ * cygserver_client.cc: Explicitly include "safe_memory.h" for
+ client-side code.
+ (client_request::make_request): Use the "safe" new/delete macros
+ unconditionally, i.e. use them on the client side as well as on
+ the server side.
+ * cygserver_transport.cc: Explicitly include "safe_memory.h" for
+ client-side code.
+ (create_server_transport): Use the "safe" new/delete macros
+ unconditionally, i.e. use them on the client side as well as on
+ the server side.
+ * shm.cc: Include "safe_memory.h".
+ (client_shmmgr::instance): Use the "safe" new/delete macros.
+ (client_shmmgr::shmdt): Ditto.
+ (client_shmmgr::new_segment): Ditto.
+
+2002-07-11 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_process (process::process): Add the client's cygpid
+ and winpid to all tracing statements as appropriate.
+ (process::exit_code): Ditto.
+ (process_cache::check_and_remove_process): Ditto.
+ * cygserver_shm.cc (server_shmmgr::shmat): Ditto.
+ (server_shmmgr::shmdt): Ditto.
+ (server_shmmgr::shmctl): Add a process object argument and remove
+ the explicit cygpid argument. Add the client's cygpid and winpid
+ to all tracing statements as appropriate.
+ (server_shmmgr::shmget): Ditto.
+ (client_request_shm::serve): Update for the new signature of the
+ shmctl and shmget methods.
+
+2002-07-11 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (client_request_shutdown::serve): Don't set the
+ shutdown flag directly, but send a SIGINT, as the signal handler
+ sets the flag and the signal breaks the pause(2) in the main loop.
+ (print_usage): Add new options.
+ (main): Add new --cleanup-threads and --request-threads options to
+ set the number of threads used by the daemon. Use pause(2) rather
+ the win32 Sleep in the main loop.
+ * shm.cc (shmat): Add sigframe.
+ (shmctl): Ditto.
+ (shmdt): Ditto.
+ (shmget): Ditto.
+
+2002-07-11 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc: Automatically detach processes from any
+ segments they are attached to at exit.
+ (class server_shmmgr::attach_t): New class.
+ (server_shmmgr::segment_t::IS_DELETED): Rename and make private.
+ (server_shmmgr::segment_t::_sequence): Make private.
+ (server_shmmgr::segment_t::_flg): Ditto.
+ (server_shmmgr::segment_t::_hFileMap): Ditto.
+ (server_shmmgr::segment_t::_attach_head): New private field.
+ (server_shmmgr::segment_t::segment_t): Initialise new fields.
+ Make non-inline.
+ (server_shmmgr::segment_t::~segment_t): New method.
+ (server_shmmgr::segment_t::is_deleted): Ditto.
+ (server_shmmgr::segment_t::is_pending_delete): Ditto.
+ (server_shmmgr::segment_t::mark_deleted): Ditto.
+ (server_shmmgr::segment_t::attach): Ditto.
+ (server_shmmgr::segment_t::detach): Ditto.
+ (server_shmmgr::segment_t::find): Ditto.
+ (class server_shmmgr::cleanup_t): New class.
+ (server_shmmgr::_shm_atts): New private field.
+ (server_shmmgr::shmat): Add a process object argument to replace
+ the removed process_cache, cygpid and winpid arguments. Remove
+ the process_cache manipulations. Move body of code to the
+ segment_t::attach method. Increment _shm_atts when required.
+ Update tracing statements.
+ (server_shmmgr::shmdt): Add a process object argument to replace
+ the removed cygpid argument. Move body of code to the
+ segment_t::detach method. Decrement _shm_atts when required.
+ Update tracing statements.
+ (server_shmmgr::shmget): Use the new segment_t::is_deleted method.
+ (server_shmmgr::server_shmmgr): Initialise the new _shm_atts
+ field.
+ (server_shmmgr::delete_segment): Remove the CloseHandle code, as
+ this is now done in the segment_t destructor.
+ (client_request_shm::serve): Look up the client's process object
+ and pass to the server_shmmgr::shmat and server_shmmgr::shmdt
+ methods rather than passing the cache, winpid and cygpid.
+ * cygserver_process.h: Add a cygpid to the process object to make
+ it more useful and then pass process objects rather than winpids
+ where possible.
+ (cleanup_routine::cleanup): Change argument to be a pointer to a
+ process object.
+ (class process): Re-order fields for no discernible reason.
+ (process::_cygpid): New field.
+ (process::process): Add a cygpid argument.
+ (process::winpid): New method.
+ (process::cygpid): Ditto.
+ (process::add): Make public, as it always should have been.
+ (process_cache::process): Add a cygpid argument.
+ * cygserver_process.cc (process::process): Add a cygpid argument
+ and use it to initialise the `_cygpid' field. Re-order
+ initialisers to match new field order.
+ (process::cleanup): Pass `this' rather than just the winpid to
+ cleanup_routine::cleanup.
+ (process_cache::process): Add a cygpid argument and pass it to the
+ process object constructor.
+ * include/sys/shm.h (shmatt_t): Make unsigned as per SUSv3.
+ (shm_info::shm_atts): New field.
+
+2002-07-11 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (class server_shmmgr::segment_t): Add `_'
+ prefix to the names of all fields.
+
+2002-07-10 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * msg.cc: New file of stub functions, no functionality.
+ * sem.cc: Ditto.
+ * shm.cc (client_shmmgr::shmctl): Add support for an out shm_info
+ buffer for the SHM_INFO command.
+ (client_shmmgr::shmget): Use %X to print keys.
+ * include/sys/ipc.h: Comment all fields and values.
+ (IPC_PRIVATE): Change to be non-negative.
+ * include/sys/msg.h: New file with SUSv3 and ipcs(8) interfaces.
+ * include/sys/sem.h: Ditto.
+ * include/sys/shm.h: Comment all fields and values.
+ (struct shm_info): New struct.
+ * cygserver_shm.h (client_request_shm::shminfo): Rename.
+ (client_request_shm::shm_info): New method.
+ (client_request_shm::_parameters.out.hFileMap): Move into union.
+ (client_request_shm::_parameters.out.shminfo): Rename.
+ (client_request_shm::_parameters.out.shm_info): New field.
+ * cygserver_shm.cc (server_shmmgr::_shm_ids): Rename.
+ (server_shmmgr::_shm_tot): New field.
+ (server_shmmgr::shmctl): Rename `out_shminfo' argument. Add
+ `out_shm_info' argument. Fill in the `out_shm_info' argument in
+ the SHM_INFO command.
+ (server_shmmgr::shmget): Check `shmflg' against the mode of
+ existing segments as per Stevens 1990, p. 123.
+ (server_shmmgr::server_shmmgr): Initialise the new `_shm_tot'
+ field.
+ (server_shmmgr::new_segment): Set ENOMEM if CreateFileMapping
+ fails. Pass `size' to new_segment.
+ (server_shmmgr::new_segment): Add size argument and use it to
+ check against and update the new `_shm_tot' field.
+ (server_shmmgr::delete_segment): Update the new `_shm_tot' field.
+ * Makefile.in (DLL_OFILES): Add new DLL object files.
+
+2002-07-09 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport_pipes.cc: The main change is to make the
+ client try harder to connect to the server if it's previously
+ connected, and so has good grounds for believing that the server
+ is running.
+ (MAX_WAIT_NAMED_PIPE_RETRY): Change to be an enumerator.
+ (WAIT_NAMED_PIPE_TIMEOUT): Ditto.
+ (transport_layer_pipes::accept): Use interlocked operators on
+ `pipe_instance'.
+ (transport_layer_pipes::close): Rearrange so that FlushFileBuffers
+ and DisconnectNamedPipe are only called for accepted endpoints.
+ Use interlocked operators on `pipe_instance'.
+ (transport_layer_pipes::read): Use set_errno where required.
+ (transport_layer_pipes::write): Ditto.
+ (transport_layer_pipes::connect): Add local static variable
+ `assume_cygserver'. Set it if a connection is made to cygserver,
+ clear it if a connection is not made even after retrying. If set,
+ ignore all errors from CreateFile and retry the connection. Catch
+ the situation where WaitNamedPipe fails to wait [sic] and add a
+ `Sleep (0)' so that the server gets a chance to run.
+
+2002-07-03 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * dcrt0.cc: Only check for cygserver if and when required.
+ (dll_crt0_1): Remove call to `cygserver_init ()'.
+ * fhandler_tty.cc (fhandler_tty_slave::open): Change the cygserver
+ logic to allow for the fact that `cygserver_init ()' may not yet
+ have been called.
+ (fhandler_tty_slave::cygserver_attach_tty): Tweak the cygserver
+ request logic to conform to the practice elsewhere in the code.
+ * tty.cc (tty::common_init): Add an explicit call to
+ `cygserver_init ()' if it hasn't already been called.
+ * include/cygwin/cygserver.h (CYGSERVER_UNAVAIL): Rename from
+ `CYGSERVER_DEAD'.
+ (client_request_get_version::check_version): Make available in
+ cygserver as well the DLL.
+ (check_cygserver_available): Ditto. Remove `check_version_too'
+ argument.
+ (cygserver_init): Ditto. And likewise.
+ * cygserver_client.cc (client_request_get_version::check_version):
+ Make available in cygserver as well the DLL.
+ (client_request::make_request): This may now be called without
+ `cygserver_init ()' having been called first. Detect this and
+ call it as required. Add tracing.
+ (check_cygserver_available): Make available in cygserver as well
+ the DLL. Remove `check_version_too' argument and always check the
+ version information. And since this is called from within
+ `cygserver_init ()', force `cygserver_running' before calling
+ `client_request::make_request ()'.
+ (cygserver_init): Make available in cygserver as well the DLL.
+ Remove `check_version_too' argument.
+
+2002-07-03 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc: Implement the ipcs(8) interfaces, IPC_INFO,
+ SHM_STAT and SHM_INFO.
+ (server_shmmgr::segment_t::sequence): New static field.
+ (server_shmmgr::segment_t::key): Remove field, use the new
+ ds.shm_perm.key field instead.
+ (server_shmmgr::segment_t::shmid): Remove field.
+ (server_shmmgr::segment_t::intid): New field.
+ (server_shmmgr::segment_t::segment_t): Use the `key' argument to
+ initialise `ds.shm_perm.key'. Change from using `shmid' to
+ `intid'.
+ (server_shmmgr::_shmseg_cnt): Renamed from `_shmid_cnt'.
+ (server_shmmgr::_intid_max): Renamed from `_shmid_max.
+ (server_shmmgr::shmat): Move the out arguments to the start of the
+ argument list. Rename the `pid' argument as `cygpid'. Add
+ tracing. Pass an intid to `find ()', not a shmid.
+ (server_shmmgr::shmctl): Add separate out arguments. Rename the
+ `pid' argument as `cygpid'. Add support for the ipcs(8)
+ interfaces. Add tracing. Pass an intid to `find ()', not a
+ shmid.
+ (server_shmmgr::shmdt): Rename the `pid' argument as `cygpid'.
+ Add tracing. Pass an intid to `find ()', not a shmid.
+ (server_shmmgr::shmget): Add a separate out arguments. Rename the
+ `pid' argument as `cygpid'. Add tracing.
+ (server_shmmgr::server_shmmgr): Update for new field names.
+ (server_shmmgr::find_by_key): Update for the new `ds.shm_perm.key'
+ field.
+ (server_shmmgr::find): Update to use the new `segment_t::intid'
+ field.
+ (server_shmmgr::new_segment): Rename the `pid' argument as
+ `cygpid'. Check that the requested size is within bounds. Handle
+ new error result from `new_segment (key, HANDLE)'.
+ (server_shmmgr::new_segment): Work with intids, not shmids. Check
+ that the new intid is within bounds. Update for new field names.
+ (server_shmmgr::delete_segment): Pass an intid to `find ()', not a
+ shmid. Update for new field names.
+ (client_request_shm::serve): Check that the incoming message
+ length is the size of the `_parameters.in' struct, not of the
+ whole in/out parameter union. Likewise, set the outgoing message
+ length to the size of the `_parameters.out' struct. Update for
+ the new server_shmmgr interfaces.
+ * include/sys/ipc.h (ipc_perm::key): New field.
+ * include/sys/shm.h (SHM_INFO): New constant.
+ * cygserver_ipc.h (IPCMNI): New constant.
+ (ipc_int2ext): Add `sequence' argument and munge this into the
+ external ipc id.
+ (ipc_ext2int_subsys): Unmunge the sequence number from the
+ external ipc id.
+ (ipc_ext2int): Ditto.
+ (ipc_inc_id): Remove.
+ (ipc_dec_id): Remove.
+ * cygserver_shm.h (SHMMAX): New constant.
+ (SHMMIN): Ditto.
+ (SHMMNI): Ditto.
+ (SHMSEG): Ditto.
+ (SHMALL): Ditto.
+ (client_request_shm::_parameters): Re-arrange as a union of two
+ separate structs, one for in arguments, the other for out.
+ (client_request_shm::shmid): Update for the new parameter layout.
+ (client_request_shm::ds): Ditto.
+ (client_request_shm::info): New method.
+ * shm.cc (client_shmmgr::_shmat_cnt): New static field.
+ (client_shmmgr::shmat): Add locking. Add tracing.
+ (client_shmmgr::shmctl): Update for ipcs(8) commands. Add
+ tracing. Add more argument checking.
+ (client_shmmgr::shmdt): Add locking. Add tracing. Update the new
+ `_shmat_cnt' field.
+ (client_shmmgr::shmget): Add tracing.
+ (client_shmmgr::fixup_shms_after_fork): Add tracing. Add
+ consistency checking.
+ (client_shmmgr::attach): Add more tracing.
+ (client_shmmgr::new_segment): Update the new `_shmat_cnt' field.
+ (client_request_shm::client_request_shm): Update for the new
+ parameter layout. Set the outgoing message length to the size of
+ the `_parameters.in' struct, not of the whole in/out parameter
+ union.
+
+2002-07-02 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * shm.cc: Remove the use of a static client_shmmgr object.
+ (client_shmmgr::_instance): New static variable.
+ (client_shmmgr::instance): Allocate a new shmmgr on the heap,
+ rather than using a local static object.
+
+2002-07-01 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport.cc (create_server_transport): Fix
+ cut-and-paste error.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_client.cc (client_request::handle_request): Don't
+ bother with the client request activity marker when compiled with
+ debugging output.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport_pipes.cc
+ (MAX_WAIT_NAMED_PIPE_RETRY): New constant.
+ (WAIT_NAMED_PIPE_TIMEOUT): Ditto.
+ (transport_layer_pipes::close): The `pipe' field is now either
+ NULL or a valid handle, and it should never have the value
+ `INVALID_HANDLE_VALUE'.
+ (transport_layer_pipes::read): Ditto.
+ (transport_layer_pipes::write): Ditto.
+ (transport_layer_pipes::connect): Ditto.
+ (transport_layer_pipes::impersonate_client): Ditto.
+ (transport_layer_pipes::connect): Ditto. New, but still bogus,
+ retry logic.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (server_shmmgr::server_shmmgr): All fields have
+ to be initialized now that the singleton is no longer static.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc (server_shmmgr::_instance): New static field.
+ (server_shmmgr::_instance_once): Ditto.
+ (server_shmmgr::initialise_instance): New static method.
+ (server_shmmgr::instance): Use a pthread_once_t rather than
+ relying on a local static variable.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * woutsup.h: Remove all uses of the C++ new and delete operators
+ throughout cygserver until they are fully thread-safe.
+ (safe_new0): New macro to replace the C++ new operator.
+ (safe_new): Ditto.
+ (safe_delete): New macro to replace the C++ delete operator.
+ * cygserver_client.cc (client_request::handle_request): Replace
+ all uses of the C++ new and delete operators with the new macros
+ from "woutsup.h".
+ (client_request::make_request): Ditto.
+ * cygserver_process.cc (~process_cleanup): Ditto.
+ (process::cleanup): Ditto.
+ (process_cache::process): Ditto.
+ (process_cache::check_and_remove_process): Ditto.
+ * cygserver_shm.cc (server_shmmgr::new_segment): Ditto.
+ (server_shmmgr::delete_segment): Ditto.
+ * cygserver_transport.cc (create_server_transport): Ditto.
+ * cygserver_transport_pipes.cc
+ (transport_layer_pipes::accept): Ditto.
+ * cygserver_transport_sockets.cc
+ (transport_layer_sockets::accept): Ditto.
+ * threaded_queue.cc (~threaded_queue): Ditto.
+ (threaded_queue::worker_loop): Ditto.
+ (threaded_queue::stop): Replace sleep(3) with win32 Sleep.
+ * cygserver.cc (~server_request): Replace all uses of the C++ new
+ and delete operators with the new macros from "woutsup.h".
+ (server_submission_loop::request_loop): Ditto.
+ (main): Ditto. Replace sleep(3) with win32 Sleep. Replace
+ iostreams with FILEs.
+ (print_usage): Replace iostreams with FILEs.
+ (print_version): Ditto.
+
+2002-06-30 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_transport_sockets.cc
+ (transport_layer_sockets::accept): Rename local variable
+ `accept_fd' to avoid shadowing the `fd' field.
+
+2002-06-29 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygwin_ipc.h: Moved (back) to "include/sys/ipc.h".
+ * cygwin_shm.h: Moved (back) to "include/sys/shm.h".
+ * include/sys/ipc.h: New file.
+ * include/sys/shm.h: New file.
+ * cygserver_shm.h: Update for new header file locations.
+ * ipc.cc: Ditto.
+
+2002-06-28 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_client.cc (client_request::make_request): Comment out
+ a verbose tracing statement.
+ * cygserver_process.cc (process_cache::sync_wait_array): Fix
+ broken assert.
+ * include/cygwin/cygserver.h (class client_request): Remove excess
+ use of `class' qualifier in declarations.
+ (class client_request_get_version): Ditto.
+ (class client_request_shutdown): Ditto.
+ (class client_request_attach_tty): Ditto.
+
+2002-06-28 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_ipc.h: New file.
+ * cygserver_shm.h: Re-written from scratch.
+ * cygserver_shm.cc: Ditto.
+ * shm.cc: Ditto.
+
+2002-06-28 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * threaded_queue.h (class queue_request): Re-write.
+ (threaded_queue_thread_function): Remove.
+ (class queue_process_param): Remove.
+ (class threaded_queue): Re-write.
+ (class queue_submission_loop): New version of the old
+ `queue_process_param' class.
+ (TInterlockedExchangePointer): New templated function.
+ (TInterlockedCompareExchangePointer): Ditto.
+ * threaded_queue.cc (worker_function): Remove.
+ (class threaded_queue): Re-write.
+ (class queue_process_param): Remove.
+ (class queue_submission_loop): New version of the old
+ `queue_process_param' class.
+ * include/cygwin/cygserver_process.h (process_cleanup): Re-write.
+ (class process_process_param): Remove.
+ (class cleanup_routine): Re-write.
+ (class process): Re-write.
+ (class process_cache): Re-write.
+ * cygserver_process.cc (process_cleanup): Re-write.
+ (class process_process_param): Remove.
+ (class cleanup_routine): Re-write.
+ (class process): Re-write.
+ (class process_cache): Re-write.
+ * cygserver.cc (request_count): Remove unused variable.
+ (class server_request): Move methods inline.
+ (class server_process_param): Remove.
+ (class server_request_queue): Remove.
+ (request_queue): Move into `main ()' and change type to
+ `threaded_queue'.
+ (request_loop): Remove.
+ (class server_submission_loop): New version of the old
+ `server_process_param' class.
+ (shutdown_server): New variable.
+ (client_request_shutdown::serve): Set `shutdown_server' to trigger
+ shutdown.
+ (handle_signal): Ditto.
+ (main): Install signal handler for SIGINT rather than SIGQUIT.
+ Use new interfaces for the `request_queue' and the `cache'.
+ Create a `server_submission_loop' and add to the `request_queue'.
+ Add check for the `shutdown_server' variable to the main loop.
+ * cygserver_shm.cc (client_request_shm::serve): Release the
+ process object after use.
+
+2002-06-27 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_client.cc (client_request::handle_request): Correct
+ tracing statement.
+ * cygserver_transport_pipes.cc: Remove local definition of
+ FILE_FLAG_FIRST_PIPE_INSTANCE constant.
+ * cygwin_ipc.h: Update copyright notice.
+ * cygwin_shm.h: Ditto.
+ * woutsup.h: Add definition of _WIN32_WINNT.
+
+2002-06-24 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_client (client_request::make_request): Replace my
+ inappropriate use of set_errno () with error_code () throughout.
+
+2002-06-24 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver.h: Add forward declarations of class
+ transport_layer_base and class process_cache to reduce
+ dependencies between header files.
+ * include/cygwin/cygserver_process.h: Add include of
+ "threaded_queue.h".
+ * cygserver.cc: Remove unnecessary cygserver header files.
+ * cygserver_client.cc: Ditto.
+ * cygserver_process.cc: Ditto.
+ * cygserver_shm.cc: Ditto.
+ * cygserver_shm.h: Ditto.
+ * cygserver_transport_pipes.cc: Ditto.
+ * dcrt0.cc: Ditto.
+ * fhandler_tty.cc: Ditto.
+ * tty.cc: Ditto.
+
+2002-06-24 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.h: Replace <sys/shm.h> with "cygwin_shm.h" after
+ merge from HEAD.
+ * cygwin_ipc.h: Update with changes to include/sys/ipc.h lost in
+ merge from HEAD.
+ * cygwin_shm.h: Ditto.
+
+2002-06-21 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc: The tests for a duplicate server instance are now
+ the responsibility of the transport layer.
+ (request_loop): Use new `recoverable' flag in call to
+ `cygserver_transport::accept ()' and shutdown on an unrecoverable
+ error.
+ (main): Never call `cygserver_init ()'. Fake `cygserver_running'
+ just for sending a shutdown request.
+ * cygserver_client.cc (client_request::send): Comment out
+ message-size tracing statements as verbose.
+ (client_request::handle): Ditto.
+ (client_request_get_version::check_version): #ifdef as DLL-only.
+ (check_cygserver_available): Ditto.
+ (cygserver_init): Ditto.
+ * include/cygwin/cygserver.h
+ (client_request_get_version::check_version): #ifdef as DLL-only.
+ (check_cygserver_available): Ditto.
+ (cygserver_init): Ditto.
+ * include/cygwin/cygserver_transport.h
+ (transport_layer_base::impersonate_client): #ifdef as
+ cygserver-only.
+ (transport_layer_base::revert_to_self): Ditto.
+ (transport_layer_base::listen): Ditto.
+ (transport_layer_base::accept): Ditto. Add a `recoverable' out
+ flag for error handling.
+ * include/cygwin/cygserver_transport_sockets.h: Ditto.
+ * include/cygwin/cygserver_transport_pipes.h: Ditto.
+ (transport_layer_pipes): Change type of the `pipe_name' field.
+ Remove the `inited' field, as unnecessary. Add new
+ `is_accepted_endpoint' field.
+ * include/cygwin/cygserver_transport.cc
+ (transport_layer_base::impersonate_client): #ifdef as
+ cygserver-only.
+ (transport_layer_base::revert_to_self): Ditto.
+ * include/cygwin/cygserver_transport_sockets.cc
+ (transport_layer_sockets::listen): #ifdef as cygserver-only.
+ (transport_layer_sockets::accept): #ifdef as cygserver-only.
+ Analyse any errno from `accept ()' and set `recoverable' as
+ appropriate.
+ * cygserver_transport_pipes.cc: Add local #define of
+ `FILE_FLAG_FIRST_PIPE_INSTANCE'.
+ (pipe_instance_lock_once): New variable.
+ (pipe_instance_lock): Ditto.
+ (pipe_instance): Ditto.
+ (initialise_pipe_instance_lock): New function.
+ (transport_layer_pipes::transport_layer_pipes): Change
+ initialization of `pipe_name'. Initialize `is_accepted_endpoint'
+ as appropriate. Remove use of `inited'.
+ (transport_layer_pipes::impersonate_client): #ifdef as
+ cygserver-only.
+ (transport_layer_pipes::revert_to_self): Ditto.
+ (transport_layer_pipes::listen): Ditto.
+ (transport_layer_pipes::accept): Ditto. Keep track of how often
+ many named pipes have been created, in the `pipe_instance'
+ variable, and pass the `FILE_FLAG_FIRST_PIPE_INSTANCE' flag on the
+ open of the first instance. Analyse the error code from
+ `CreateNamedPipe ()' and set the `recoverable' flag as
+ appropriate.
+ (transport_layer_pipes::close): Update the `pipe_instance' count.
+
+2002-06-18 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * woutsup.h (cygserver_running): Add declaration.
+ (api_fatal): Eliminate.
+ * include/cygwin/cygserver.h
+ (client_request_get_version::check_version): Change return type to
+ bool.
+ (check_cygserver_available): New function.
+ (cygserver_init): Add check_version_too argument.
+ * cygserver_client.cc (allow_daemon): Make a bool.
+ (client_request_get_version::make_request): See errno on error.
+ Remove special case for CYGSERVER_REQUEST_GET_VERSION; this is now
+ handled in cygserver_init().
+ (client_request_get_version::check_version): Use syscall_printf()
+ instead of api_fatal(). Return true if cygserver version is
+ compatible.
+ (check_cygserver_available): New function; code moved here from
+ cygserver_init().
+ (cygserver_init): Move some code into check_cygserver_available().
+ * cygserver.cc (__set_errno): Copy from debug.cc so that
+ set_errno() can be used when __OUTSIDE_CYGWIN__.
+ (main): Call cygserver_init() to set up cygserver_running and add
+ checks against this to (try and) prevent multiple copies of
+ cygserver running simultaneously. Remember to delete all
+ transport connections so that (one day) the transport classes can
+ tidy up on cygserver shutdown.
+
+2002-06-17 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (main): Adjust tracing output for a cleaner display
+ when compiled without --enable-debugging.
+ * threaded_queue.cc (threaded_queue::cleanup): Ditto.
+ (queue_process_param::stop): Ditto.
+ * include/cygwin/cygserver.h
+ (client_request::make_request): Make non-virtual.
+ (client_request::send): Make virtual and protected, not private.
+ (client_request_attach_tty::send): New virtual method.
+ * cygserver_client.cc: Use the `msglen()' accessor rather than
+ `_header.msglen' throughout.
+ (client_request_attach_tty::send): New method.
+ (client_request::make_request): Remove the explicit close of
+ `transport' as it is closed on deletion.
+
+2002-06-17 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver.h: Change the client_request classes to
+ give greater encapsulation and to allow variable length requests
+ and replies.
+ (enum cygserver_request_code): Now client_request::request_code_t.
+ (class request_header): Now client_request::header_t. Make a
+ union of the request_code and the error_code. The `cb' field,
+ which was the buffer length, is now the `size_t msglen' field.
+ (struct request_get_version): Now
+ client_request_get_version::request_get_version.
+ (struct request_shutdown): Remove unused type.
+ (struct request_attach_tty): Now
+ client_request_attach_tty::request_attach_tty.
+ (client_request::_buf): Make field const.
+ (client_request::_buflen): New const private field.
+ (client_request::request_code): New accessor.
+ (client_request::error_code): Ditto.
+ (client_request::msglen): Ditto.
+ (client_request::handle_request): New static method.
+ (client_request::make_request): New virtual method.
+ (client_request::handle): New method.
+ (client_request::send): Make private.
+ (client_request_get_version::check_version): New method.
+ (client_request_get_version::serve): Make private.
+ (client_request_get_version::version): Ditto.
+ (client_request_shutdown::serve): Ditto.
+ (client_request_attach_tty::req): Ditto.
+ (client_request_attach_tty::serve): Ditto.
+ (client_request_attach_tty::from_master): Make method const.
+ (client_request_attach_tty::from_master): Ditto.
+ * cygserver_client.cc
+ (client_request_get_version::client_request_get_version): Track
+ changes to the client_request classes.
+ (client_request_attach_tty::client_request_attach_tty): Ditto.
+ (client_request_get_version::check_version): New method to
+ encapsulate code from cygserver_init().
+ (client_request_shutdown::client_request_shutdown): Move into
+ "cygserver.cc".
+ (client_request::send): Track changes to the client_request
+ classes. Add more error checking.
+ (client_request::handle_request): New static method containing the
+ first half of the old server_request::process() code.
+ (client_request::make_request): New method to replace the old
+ cygserver_request() function.
+ (client_request::handle): New method containing the second half of
+ the old server_request::process() code.
+ (cygserver_init): Track changes to the client_request classes. In
+ particular, some code moved into the
+ client_request_get_version::check_version() method.
+ * cygserver.cc (client_request_attach_tty::serve): Track changes
+ to the client_request classes. In particular, only return a reply
+ body if some handles are successfully duplicated for the client.
+ And remove goto's.
+ (client_request_get_version::serve): Track changes to the
+ client_request classes.
+ (client_request_shutdown::serve): Ditto.
+ (class client_request_invalid): Dead, and so young too.
+ (server_request::request_buffer): Remove unnecessary field.
+ (client_request_shutdown::client_request_shutdown): Moved here
+ from "cygserver_client.cc".
+ (server_request::process): Implementation moved into the new
+ client_request::handle_request() and client_request::handle()
+ methods.
+ * cygserver_shm.h (class client_request_shm): Put client- and
+ server-specific interfaces inside #ifdef/#ifndef __INSIDE_CYGWIN__
+ guards.
+ (client_request_shm::serve): Make private.
+ * cygserver_shm.cc
+ (client_request_shm::client_request_shm): Track changes to the
+ client_request classes.
+ (client_request_shm::serve): Ditto
+ * shm.cc (client_request_shm::client_request_shm): Ditto. Use
+ alloc_sd() rather than set_security_attribute() to get access to
+ the SECURITY_DESCRIPTOR length, so that we can use it to set the
+ request body length.
+ (shmat): Track changes to the client_request classes. In
+ particular, allocate client_request objects on the stack rather
+ than on the heap, and use the client_request::make_request()
+ method rather than the old cygserver_request() function.
+ (shmdt): Ditto.
+ (shmctl): Ditto.
+ (shmget): Ditto.
+ * fhandler_tty.cc (fhandler_tty_slave::cygserver_attach_tty): Ditto.
+
+2002-06-17 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver_transport.h
+ (cygserver_transport::read): Change buffer type to void *.
+ (cygserver_transport::write): Ditto.
+ * include/cygwin/cygserver_transport_sockets.h
+ (cygserver_transport_sockets::read): Ditto.
+ (cygserver_transport_sockets::write): Ditto.
+ * include/cygwin/cygserver_transport_pipes.h
+ (cygserver_transport_pipes::read): Ditto.
+ (cygserver_transport_pipes::write): Ditto.
+ * cygserver_transport_sockets.cc
+ (cygserver_transport_sockets::read): Ditto.
+ (cygserver_transport_sockets::write): Ditto.
+ * cygserver_transport_pipes.cc
+ (cygserver_transport_pipes::read): Ditto. Set errno on error, to
+ match behaviour of cygserver_transport_sockets class.
+ (cygserver_transport_pipes::write): Ditto.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (version): New static variable.
+ (server_request_queue::add_connection): Remove my gratuitous use
+ of studly caps.
+ (setup_privileges): Declare static.
+ (handle_signal): Ditto.
+ (longopts): Make a local variable of main().
+ (opts): Ditto.
+ (print_usage): New function.
+ (print_version): Ditto (tip of the hat to Joshua Daniel Franklin
+ for inspiration here).
+ (main): More argument checking. Add --help and --version options.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver.h (client_request::serve): Make pure
+ virtual.
+ * cygserver.cc (client_request::serve): Remove definition of pure
+ virtual method.
+ (class client_request_invalid): New class.
+ (server_request::process): Use new client_request_invalid
+ class. And remove goto's.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (class server_request): Add virtual destructor.
+ (server_request_queue::addConnection): New method to replace bad
+ virtual add() method.
+ (request_loop): Replace call to queue->add() with call to
+ queue->addConnection().
+ (server_request::server_request): Use field initialization.
+ (server_request::~server_request): New virtual destructor.
+ (server_request::process): Remove close and delete of
+ transport_layer_base object. It is deleted by the server_request's
+ own destructor and closed by its own destructor.
+ * include/cygwin/cygserver.h
+ (client_request::operator request_header): Remove unused method.
+ * cygserver_client.cc: Ditto.
+ * include/cygwin/cygserver_process.h
+ (class cleanup_routine): Add virtual destructor.
+ (cleanup_routine::cleanup): Make pure virtual.
+ (class process_cache): Make destructor non-virtual.
+ (process_cache::add): Ditto.
+ * cygserver_process.cc
+ (cleanup_routine::~cleanup_routine): New virtual destructor.
+ * include/cygwin/cygserver_transport.h
+ (class transport_layer_base): Add virtual destructor.
+ * cygserver_transport.cc
+ (transport_layer_base::~transport_layer_base): New virtual
+ destructor.
+ * include/cygwin/cygserver_transport_pipes.h
+ (class transport_layer_pipes): Add virtual destructor.
+ * cygserver_transport_pipes.cc
+ (transport_layer_pipes::~transport_layer_pipes): New virtual
+ destructor.
+ (transport_layer_pipes::close): Null out handle after closing.
+ * include/cygwin/cygserver_transport_sockets.h
+ (class transport_layer_sockets): Add virtual destructor.
+ * cygserver_transport_sockets.cc
+ (transport_layer_sockets::~transport_layer_sockets): New virtual
+ destructor.
+ (transport_layer_sockets::close): Null out fd after closing.
+ * threaded_queue.h (class queue_request): Add virtual destructor.
+ (queue_request::process): Make pure virtual.
+ * threaded_queue.cc (~queue_request): New virtual destructor.
+ (queue_request::process): Remove definition of pure virtual
+ method.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver.h (client_request::send): Make
+ non-virtual.
+ (class client_request_attach_tty): Put client- and server-specific
+ interfaces inside #ifdef/#ifndef __INSIDE_CYGWIN__ guards.
+ * cygserver_client.cc: Ditto.
+ (cygserver_init): Fix error handling.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc: Throughout the code, check and correct level of
+ the XXX_printf() functions used. Comment out several of the
+ debug_printf() calls with "// verbose:". Reformat and correct
+ typos of some of the XXX_printf() formats.
+ * cygserver_process.cc: Ditto.
+ * cygserver_shm.cc: Ditto.
+ * cygserver_transport_pipes.cc: Ditto.
+ * cygserver_transport_sockets.cc: Ditto.
+ * shm.cc (hi_ulong): New function to allow printing of a 64-bit
+ key with current small_printf implementation.
+ (lo_ulong): Ditto.
+ (client_request_shm::client_request_shm): Use hi_ulong() and
+ lo_ulong() in call to debug_printf().
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver_shm.cc: Remove #define __INSIDE_CYGWIN__ from around
+ <sys/shm.h> as it no longer contains any internal code.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/sys/ipc.h (IPC_PRIVATE): Add cast to key_t.
+ (IPC_INFO): New flag for ipcs(8).
+ (IPC_RMID IPC_SET IPC_STAT): Renumber.
+ * include/sys/shm.h (SHM_RDONLY SHM_RND): Renumber with distinct
+ values [sic].
+ (class _shmattach): Internal type moved to "cygserver_shm.h".
+ (class shmnode): Ditto.
+ (class shmid_ds): Ditto.
+ (struct shmid_ds): Add spare fields.
+ (struct shminfo): New type for IPC_INFO interface.
+ * cygserver_shm.h: Remove obsolete #if 0 ... #endif block.
+ (class shm_cleanup): Remove unused class.
+ (struct _shmattach): Internal type moved from <sys/shm.h>.
+ (struct shmnode): Ditto.
+ (struct int_shmid_ds): Ditto. Renamed to avoid name clash with
+ public interface struct shmid_ds. Use the shmid_bs structure as a
+ field.
+ * cygserver_shm.cc: Remove obsolete #if 0 ... #endif block.
+ (client_request_shm::serve): Update for redefinition of
+ int_shmid_ds structure.
+ * shm.cc (build_inprocess_shmds): Ditto.
+ (fixup_shms_after_fork): Ditto.
+ (shmctl): Ditto.
+ (shmget): Ditto. Remove obsolete #if 0 ... #endif code.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * include/cygwin/cygserver_transport.h
+ (transport_layer_base::transport_layer_base): Remove since it is
+ now redundant.
+ (transport_layer_base::listen): Make a pure virtual method.
+ (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.cc
+ (transport_layer_base::transport_layer_base): Remove since it is
+ now redundant.
+ (transport_layer_base::listen): Remove since it is now a pure
+ virtual method.
+ (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.
+
+2002-06-16 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * cygserver.cc (check_and_dup_handle): Only use security code if
+ running on NT, i.e. if wincap.has_security().
+ (client_request_attach_tty::serve): Add check for has_security().
+ * cygserver_process.cc (process_cache::process): Use DWORD winpid
+ throughout to avoid win32 vs. cygwin pid confusion.
+ (process::process): Ditto.
+ * cygserver_shm.cc (client_request_shm::serve): Only use security
+ code if running on NT, i.e. if wincap.has_security().
+ * cygserver_shm.h (client_request_shm::parameters.in): Replace the
+ ambiguous pid field with cygpid and winpid fields.
+ (client_request_shm::client_request_shm): Reduce to only two
+ client-side constructors: one for SHM_CREATE, another for all the
+ other requests.
+ * shm.cc (client_request_shm::client_request_shm):
+ Ditto. Initialize cygpid and winpid fields here. On NT initialize
+ sd_buf here using set_security_attribute() to make use of the euid
+ and egid.
+ (shmat): Use new client_request_shm constructor.
+ (shmdt): Ditto.
+ (shmctl): Ditto.
+ (shmget): Ditto. Remove security code, now performed in the
+ relevant client_request_shm constructor.
+ * include/cygwin/cygserver_process.h: (class cleanup_routine):
+ Change winpid type to DWORD.
+ (class process): Ditto.
+
+2002-06-15 Conrad Scott <conrad.scott@dsl.pipex.com>
+
+ * woutsup.h: New file.
+ * cygserver.cc: Use "woutsup.h" and new XXX_printf macros.
+ (getfunc): New function, copied verbatim from "strace.cc".
+ (__cygserver__printf): New function.
+ * cygserver_client.cc: Use "woutsup.h" and new XXX_printf macros.
+ * cygserver_process.cc: Ditto.
+ * cygserver_shm.cc: Ditto.
+ * cygserver_transport.cc: Ditto.
+ * cygserver_transport_pipes.cc: Ditto.
+ * cygserver_transport_sockets.cc: Ditto.
+ * threaded_queue.cc: Ditto.
+ * shm.cc: Remove trailing \n from XXX_printf format strings.
+ * Makefile.in: Remove special __OUTSIDE_CYGWIN__ case for
+ cygserver_shm.cc.
+
2002-09-21 Christopher Faylor <cgf@redhat.com>
* init.cc (dll_entry): Temporarily disable thread detach code.
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index eaaccd8..7bdb6aa 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -118,24 +118,28 @@ MALLOC_OFILES=@MALLOC_OFILES@
DLL_IMPORTS:=$(w32api_lib)/libuuid.a $(w32api_lib)/libshell32.a $(w32api_lib)/libkernel32.a
# Please maintain this list in sorted order, with maximum files per 80 col line
-DLL_OFILES:=assert.o autoload.o cygheap.o cygserver_client.o \
- cygserver_transport.o cygserver_transport_pipes.o \
- cygserver_transport_sockets.o cygthread.o dcrt0.o debug.o delqueue.o \
- dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o exceptions.o \
- exec.o external.o fcntl.o fhandler.o fhandler_clipboard.o \
- fhandler_console.o fhandler_disk_file.o fhandler_dsp.o \
- fhandler_floppy.o fhandler_mem.o fhandler_proc.o fhandler_process.o \
- fhandler_random.o fhandler_raw.o fhandler_registry.o fhandler_serial.o \
- fhandler_socket.o fhandler_tape.o fhandler_termios.o fhandler_tty.o \
- fhandler_virtual.o fhandler_windows.o fhandler_zero.o fnmatch.o fork.o \
- glob.o grp.o heap.o init.o ioctl.o ipc.o localtime.o malloc.o \
- malloc_wrapper.o miscfuncs.o mmap.o net.o ntea.o passwd.o path.o \
- pinfo.o pipe.o poll.o pthread.o regcomp.o regerror.o regexec.o \
- regfree.o registry.o resource.o scandir.o sched.o sec_acl.o \
- sec_helper.o security.o select.o shared.o shm.o signal.o \
- sigproc.o smallprint.o spawn.o strace.o strsep.o sync.o syscalls.o \
- sysconf.o syslog.o termios.o thread.o times.o tty.o uinfo.o uname.o \
- v8_regexp.o v8_regerror.o v8_regsub.o wait.o wincap.o window.o \
+DLL_OFILES:= \
+ assert.o autoload.o cygheap.o cygserver_client.o \
+ cygserver_transport.o cygserver_transport_pipes.o \
+ cygserver_transport_sockets.o cygthread.o dcrt0.o debug.o \
+ delqueue.o dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o \
+ exceptions.o exec.o external.o fcntl.o fhandler.o \
+ fhandler_clipboard.o fhandler_console.o fhandler_disk_file.o \
+ fhandler_dsp.o fhandler_floppy.o fhandler_mem.o \
+ fhandler_proc.o fhandler_process.o fhandler_random.o \
+ fhandler_raw.o fhandler_registry.o fhandler_serial.o \
+ fhandler_socket.o fhandler_tape.o fhandler_termios.o \
+ fhandler_tty.o fhandler_virtual.o fhandler_windows.o \
+ fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o \
+ ioctl.o ipc.o localtime.o malloc.o malloc_wrapper.o \
+ miscfuncs.o mmap.o msg.o net.o ntea.o passwd.o path.o pinfo.o \
+ pipe.o poll.o pthread.o regcomp.o regerror.o regexec.o \
+ regfree.o registry.o resource.o scandir.o sched.o sec_acl.o \
+ sec_helper.o security.o select.o sem.o shared.o shm.o signal.o \
+ sigproc.o smallprint.o spawn.o strace.o strsep.o sync.o \
+ syscalls.o sysconf.o syslog.o termios.o thread.o times.o tty.o \
+ uinfo.o uname.o v8_regexp.o v8_regerror.o v8_regsub.o wait.o \
+ wincap.o window.o \
$(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS)
GMON_OFILES:=gmon.o mcount.o profil.o
@@ -334,9 +338,6 @@ cygserver_transport_sockets_outside.o: cygserver_transport_sockets.cc
cygserver_client_outside.o: cygserver_client.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
-cygserver_shm.o: cygserver_shm.cc
- $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
-
cygserver.exe: cygserver.o cygserver_shm.o cygserver_transport_outside.o cygserver_transport_pipes_outside.o cygserver_transport_sockets_outside.o cygserver_client_outside.o cygserver_process.o threaded_queue.o wincap.o version.o smallprint.o
$(CXX) -o $@ $^ -lstdc++
#ifdef VERBOSE
diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc
index af9cee9..0c07403 100755
--- a/winsup/cygwin/cygserver.cc
+++ b/winsup/cygwin/cygserver.cc
@@ -4,61 +4,141 @@
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. */
+
+#include "woutsup.h"
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
-#include "wincap.h"
-#include "cygwin_version.h"
+#include <string.h>
+#include <unistd.h>
-#include "getopt.h"
+#include "cygerrno.h"
+#include "cygwin_version.h"
-#include "cygwin/cygserver_transport.h"
-#include "cygwin/cygserver_transport_pipes.h"
-#include "cygwin/cygserver_transport_sockets.h"
-#include "threaded_queue.h"
-#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver.h"
-#include "cygserver_shm.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
-/* for quieter operation, set to 0 */
-#define DEBUG 0
-#define debug_printf if (DEBUG) printf
+// Version string.
+static const char version[] = "$Revision$";
-GENERIC_MAPPING access_mapping;
-static class transport_layer_base *transport;
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ * Copied verbatim from "strace.cc".
+ */
+static int
+getfunc (char *in_dst, const char *func)
+{
+ const char *p;
+ const char *pe;
+ char *dst = in_dst;
+ for (p = func; (pe = strchr (p, '(')); p = pe + 1)
+ if (isalnum ((int)pe[-1]) || pe[-1] == '_')
+ break;
+ else if (isspace ((int)pe[-1]))
+ {
+ pe--;
+ break;
+ }
+ if (!pe)
+ pe = strchr (func, '\0');
+ for (p = pe; p > func; p--)
+ if (p != pe && *p == ' ')
+ {
+ p++;
+ break;
+ }
+ if (*p == '*')
+ p++;
+ while (p < pe)
+ *dst++ = *p++;
+
+ *dst++ = ':';
+ *dst++ = ' ';
+ *dst = '\0';
+
+ return dst - in_dst;
+}
+
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ */
+extern "C" void
+__cygserver__printf (const char *const function, const char *const fmt, ...)
+{
+ const DWORD lasterror = GetLastError ();
+ const int lasterrno = errno;
+
+ va_list ap;
+
+ char *const buf = (char *) alloca (BUFSIZ);
+
+ assert (buf);
-DWORD request_count = 0;
+ int len = 0;
-BOOL
+ if (function)
+ len += getfunc (buf, function);
+
+ va_start (ap, fmt);
+ len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap);
+ va_end (ap);
+
+ len += snprintf (buf + len, BUFSIZ - len, "\n");
+
+ const int actual = (len > BUFSIZ ? BUFSIZ : len);
+
+ write (2, buf, actual);
+
+ errno = lasterrno;
+ SetLastError (lasterror);
+
+ return;
+}
+
+#ifdef DEBUGGING
+
+int __stdcall
+__set_errno (const char *func, int ln, int val)
+{
+ debug_printf ("%s:%d val %d", func, ln, val);
+ return _impure_ptr->_errno = val;
+}
+
+#endif /* DEBUGGING */
+
+GENERIC_MAPPING access_mapping;
+
+static BOOL
setup_privileges ()
{
BOOL rc, ret_val;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES sPrivileges;
- rc = OpenProcessToken (GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken) ;
+ rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ;
if (!rc)
{
- printf ("error opening process token (%lu)\n", GetLastError ());
+ system_printf ("error opening process token (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid);
if (!rc)
{
- printf ("error getting prigilege luid (%lu)\n", GetLastError ());
+ system_printf ("error getting privilege luid (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
@@ -67,7 +147,8 @@ setup_privileges ()
rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ;
if (!rc)
{
- printf ("error adjusting prigilege level. (%lu)\n", GetLastError ());
+ system_printf ("error adjusting privilege level. (%lu)",
+ GetLastError ());
ret_val = FALSE;
goto out;
}
@@ -89,179 +170,223 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
- HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE)
+ HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE)
{
HANDLE local_handle = NULL;
int ret_val = EACCES;
- char sd_buf [1024];
- PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
- DWORD bytes_needed;
- PRIVILEGE_SET ps;
- DWORD ps_len = sizeof (ps);
- BOOL status;
if (from_process != GetCurrentProcess ())
{
+ if (!DuplicateHandle (from_process, from_handle,
+ GetCurrentProcess (), &local_handle,
+ 0, bInheritHandle,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("error getting handle(%u) to server (%lu)",
+ (unsigned int)from_handle, GetLastError ());
+ goto out;
+ }
+ } else
+ local_handle = from_handle;
- if (!DuplicateHandle (from_process, from_handle,
- GetCurrentProcess (), &local_handle,
- 0, bInheritHandle,
- DUPLICATE_SAME_ACCESS))
- {
- printf ("error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ());
- goto out;
- }
-} else
- local_handle = from_handle;
-
- if (!GetKernelObjectSecurity (local_handle,
- OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
- sd, sizeof (sd_buf), &bytes_needed))
+ if (!wincap.has_security ())
+ assert (!from_process_token);
+ else
{
- printf ("error getting handle SD (%lu)\n", GetLastError ());
- goto out;
- }
+ char sd_buf [1024];
+ PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
+ DWORD bytes_needed;
+ PRIVILEGE_SET ps;
+ DWORD ps_len = sizeof (ps);
+ BOOL status;
+
+ if (!GetKernelObjectSecurity (local_handle,
+ (OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION),
+ sd, sizeof (sd_buf), &bytes_needed))
+ {
+ system_printf ("error getting handle SD (%lu)", GetLastError ());
+ goto out;
+ }
- MapGenericMask (&access, &access_mapping);
+ MapGenericMask (&access, &access_mapping);
- if (!AccessCheck (sd, from_process_token, access, &access_mapping,
- &ps, &ps_len, &access, &status))
- {
- printf ("error checking access rights (%lu)\n", GetLastError ());
- goto out;
- }
+ if (!AccessCheck (sd, from_process_token, access, &access_mapping,
+ &ps, &ps_len, &access, &status))
+ {
+ system_printf ("error checking access rights (%lu)",
+ GetLastError ());
+ goto out;
+ }
- if (!status)
- {
- printf ("access to object denied\n");
- goto out;
+ if (!status)
+ {
+ system_printf ("access to object denied");
+ goto out;
+ }
}
if (!DuplicateHandle (from_process, from_handle,
to_process, to_handle_ptr,
access, bInheritHandle, 0))
{
- printf ("error getting handle to client (%lu)\n", GetLastError ());
+ system_printf ("error getting handle to client (%lu)", GetLastError ());
goto out;
}
- debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
+
+ // verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr);
ret_val = 0;
-out:
+ out:
if (local_handle && from_process != GetCurrentProcess ())
CloseHandle (local_handle);
return (ret_val);
}
-void
-client_request::serve (transport_layer_base *conn, class process_cache *cache)
-{
- printf ("*****************************************\n"
- "A call to the base client_request class has occured\n"
- "This indicates a mismatch in a virtual function definition somewhere\n");
- exit (1);
-}
+/*
+ * client_request_attach_tty::serve ()
+ */
void
-client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
+client_request_attach_tty::serve (transport_layer_base *const conn,
+ process_cache *)
{
- HANDLE from_process_handle = NULL;
- HANDLE to_process_handle = NULL;
- HANDLE token_handle = NULL;
- DWORD rc;
+ assert (conn);
+
+ assert (!error_code ());
- if (header.cb != sizeof (req))
+ if (!wincap.has_security ())
{
- header.error_code = EINVAL;
+ syscall_printf ("operation only supported on systems with security");
+ error_code (EINVAL);
+ msglen (0);
return;
}
- debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid,
- req.from_master, req.to_master,
- req.pid);
-
- debug_printf ("opening process %ld\n", req.master_pid);
- from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
- debug_printf ("opening process %ld\n", req.pid);
- to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
- if (!from_process_handle || !to_process_handle)
+ if (msglen () != sizeof (req))
{
- printf ("error opening process (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (req), msglen ());
+ error_code (EINVAL);
+ msglen (0);
+ return;
}
- debug_printf ("Impersonating client\n");
- conn->impersonate_client ();
+ msglen (0); // Until we fill in some fields.
- debug_printf ("about to open thread token\n");
- rc = OpenThreadToken (GetCurrentThread (),
- TOKEN_QUERY,
- TRUE,
- &token_handle);
+ // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld",
+ // req.master_pid, req.from_master, req.to_master,
+ // req.pid);
- debug_printf ("opened thread token, rc=%lu\n", rc);
- conn->revert_to_self ();
+ // verbose: debug_printf ("opening process %ld", req.master_pid);
- if (!rc)
+ const HANDLE from_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
+
+ if (!from_process_handle)
{
- printf ("error opening thread token (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ system_printf ("error opening `from' process, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ return;
}
- if (check_and_dup_handle (from_process_handle, to_process_handle,
- token_handle,
- GENERIC_READ,
- req.from_master,
- &req.from_master, TRUE) != 0)
+ // verbose: debug_printf ("opening process %ld", req.pid);
+
+ const HANDLE to_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
+
+ if (!to_process_handle)
{
- printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
+ system_printf ("error opening `to' process, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ error_code (EACCES);
+ return;
}
- if (req.to_master)
+ // verbose: debug_printf ("Impersonating client");
+ conn->impersonate_client ();
+
+ HANDLE token_handle = NULL;
+
+ // verbose: debug_printf ("about to open thread token");
+ const DWORD rc = OpenThreadToken (GetCurrentThread (),
+ TOKEN_QUERY,
+ TRUE,
+ &token_handle);
+
+ // verbose: debug_printf ("opened thread token, rc=%lu", rc);
+ conn->revert_to_self ();
+
+ if (!rc)
{
- if (check_and_dup_handle (from_process_handle, to_process_handle,
- token_handle,
- GENERIC_WRITE,
- req.to_master,
- &req.to_master, TRUE) != 0)
- {
- printf ("error duplicating to_master handle (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- goto out;
- }
+ system_printf ("error opening thread token, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ error_code (EACCES);
+ return;
}
-#if DEBUG
- printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
- req.from_master, req.to_master);
-#endif
+ // From this point on, a reply body is returned to the client.
- header.error_code = 0;
+ const HANDLE from_master = req.from_master;
+ const HANDLE to_master = req.to_master;
-out:
- if (from_process_handle)
- CloseHandle (from_process_handle);
- if (to_process_handle)
- CloseHandle (to_process_handle);
- if (token_handle)
- CloseHandle (token_handle);
+ req.from_master = NULL;
+ req.to_master = NULL;
+
+ msglen (sizeof (req));
+
+ if (from_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_READ,
+ from_master,
+ &req.from_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating from_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ if (to_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_WRITE,
+ to_master,
+ &req.to_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating to_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ CloseHandle (token_handle);
+
+ debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
+ req.master_pid, from_master, to_master,
+ req.pid, req.from_master, req.to_master);
+
+ return;
}
void
-client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
+client_request_get_version::serve (transport_layer_base *, process_cache *)
{
- if (header.cb != sizeof (version))
- {
- header.error_code = EINVAL;
- return;
- }
- header.error_code = 0;
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
+ msglen (sizeof (version));
+
version.major = CYGWIN_SERVER_VERSION_MAJOR;
version.api = CYGWIN_SERVER_VERSION_API;
version.minor = CYGWIN_SERVER_VERSION_MINOR;
@@ -270,280 +395,380 @@ client_request_get_version::serve(transport_layer_base *conn, class process_cach
class server_request : public queue_request
{
- public:
- server_request (transport_layer_base *newconn, class process_cache *newcache);
- virtual void process ();
- private:
- char request_buffer [MAX_REQUEST_SIZE];
- transport_layer_base *conn;
- class process_cache *cache;
-};
+public:
+ server_request (transport_layer_base *const conn, process_cache *const cache)
+ : _conn (conn), _cache (cache)
+ {}
-class server_process_param : public queue_process_param
-{
- public:
- transport_layer_base *transport;
- server_process_param () : queue_process_param (false) {};
-};
+ virtual ~server_request ()
+ {
+ safe_delete (_conn);
+ }
-class server_request_queue : public threaded_queue
-{
- public:
- class process_cache *cache;
- void process_requests (transport_layer_base *transport);
- virtual void add (transport_layer_base *conn);
+ virtual void process ()
+ {
+ client_request::handle_request (_conn, _cache);
+ }
+
+private:
+ transport_layer_base *const _conn;
+ process_cache *const _cache;
};
-class server_request_queue request_queue;
-static DWORD WINAPI
-request_loop (LPVOID LpParam)
+class server_submission_loop : public queue_submission_loop
{
- class server_process_param *params = (server_process_param *) LpParam;
- class server_request_queue *queue = (server_request_queue *) params->queue;
- class transport_layer_base * transport = params->transport;
- while (queue->active)
+public:
+ server_submission_loop (threaded_queue *const queue,
+ transport_layer_base *const transport,
+ process_cache *const cache)
+ : queue_submission_loop (queue, false),
+ _transport (transport),
+ _cache (cache)
{
- transport_layer_base * new_conn = transport->accept ();
- /* FIXME: this is a little ugly. What we really want is to wait on two objects:
- * one for the pipe/socket, and one for being told to shutdown. Otherwise
- * this will stay a problem (we won't actually shutdown until the request
- * _AFTER_ the shutdown request. And sending ourselves a request is ugly
- */
- if (new_conn && queue->active)
- queue->add (new_conn);
+ assert (_transport);
+ assert (_cache);
}
- return 0;
-}
-/* TODO: check we are not being asked to service a already serviced transport */
+private:
+ transport_layer_base *const _transport;
+ process_cache *const _cache;
+
+ virtual void request_loop ();
+};
+
+/* FIXME: this is a little ugly. What we really want is to wait on
+ * two objects: one for the pipe/socket, and one for being told to
+ * shutdown. Otherwise this will stay a problem (we won't actually
+ * shutdown until the request _AFTER_ the shutdown request. And
+ * sending ourselves a request is ugly
+ */
void
-server_request_queue::process_requests (transport_layer_base *transport)
+server_submission_loop::request_loop ()
{
- class server_process_param *params = new server_process_param;
- params->transport = transport;
- threaded_queue::process_requests (params, request_loop);
+ /* I'd like the accepting thread's priority to be above any "normal"
+ * thread in the system to avoid overflowing the listen queue (for
+ * sockets; similar issues exist for named pipes); but, for example,
+ * a normal priority thread in a foregrounded process is boosted to
+ * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current
+ * thread's priority to a level one above that. This fails on
+ * win9x/ME so assume any failure in that call is due to that and
+ * simply call again at one priority level lower.
+ */
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise accept thread priority, error = %lu",
+ GetLastError ());
+
+ while (_running)
+ {
+ bool recoverable = false;
+ transport_layer_base *const conn = _transport->accept (&recoverable);
+ if (!conn && !recoverable)
+ {
+ system_printf ("fatal error on IPC transport: closing down");
+ return;
+ }
+ // EINTR probably implies a shutdown request; so back off for a
+ // moment to let the main thread take control, otherwise the
+ // server spins here receiving EINTR repeatedly since the signal
+ // handler in the main thread doesn't get a chance to be called.
+ if (!conn && errno == EINTR)
+ {
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL))
+ debug_printf ("failed to reset thread priority, error = %lu",
+ GetLastError ());
+
+ Sleep (0);
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise thread priority, error = %lu",
+ GetLastError ());
+ }
+ if (conn)
+ _queue->add (safe_new (server_request, conn, _cache));
+ }
+}
+
+client_request_shutdown::client_request_shutdown ()
+ : client_request (CYGSERVER_REQUEST_SHUTDOWN)
+{
+ // verbose: syscall_printf ("created");
}
void
-client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
+client_request_shutdown::serve (transport_layer_base *, process_cache *)
{
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
- /* tell the main thread to shutdown */
- request_queue.active=false;
+
+ kill (getpid (), SIGINT);
+
+ msglen (0);
}
-server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
+static sig_atomic_t shutdown_server = false;
+
+static void
+handle_signal (const int signum)
{
- conn = newconn;
- cache = newcache;
+ /* any signal makes us die :} */
+
+ shutdown_server = true;
}
-void
-server_request::process ()
+/*
+ * print_usage ()
+ */
+
+static void
+print_usage (const char *const pgm)
{
- ssize_t bytes_read, bytes_written;
- struct request_header* req_ptr = (struct request_header*) &request_buffer;
- client_request *req = NULL;
- debug_printf ("about to read\n");
+ printf ("Usage: %s [OPTIONS]\n", pgm);
+ printf (" -c, --cleanup-threads number of cleanup threads to use\n");
+ printf (" -h, --help output usage information and exit\n");
+ printf (" -r, --request-threads number of request threads to use\n");
+ printf (" -s, --shutdown shutdown the daemon\n");
+ printf (" -v, --version output version information and exit\n");
+}
- bytes_read = conn->read (request_buffer, sizeof (struct request_header));
- if (bytes_read != sizeof (struct request_header))
- {
- printf ("error reading from connection (%lu)\n", GetLastError ());
- goto out;
- }
- debug_printf ("got header (%ld)\n", bytes_read);
+/*
+ * print_version ()
+ */
- switch (req_ptr->req_id)
- {
- case CYGSERVER_REQUEST_GET_VERSION:
- req = new client_request_get_version (); break;
- case CYGSERVER_REQUEST_ATTACH_TTY:
- req = new client_request_attach_tty (); break;
- case CYGSERVER_REQUEST_SHUTDOWN:
- req = new client_request_shutdown (); break;
- case CYGSERVER_REQUEST_SHM_GET:
- req = new client_request_shm (); break;
- default:
- req = new client_request (CYGSERVER_REQUEST_INVALID, 0);
- req->header.error_code = ENOSYS;
- debug_printf ("Bad client request - returning ENOSYS\n");
- }
+static void
+print_version (const char *const pgm)
+{
+ char *vn = NULL;
- if (req->header.cb != req_ptr->cb)
- {
- debug_printf ("Mismatch in request buffer sizes\n");
- goto out;
- }
+ const char *const colon = strchr (version, ':');
- if (req->header.cb)
+ if (!colon)
{
-
- bytes_read = conn->read (req->buffer, req->header.cb);
- if (bytes_read != req->header.cb)
- {
- debug_printf ("error reading from connection (%lu)\n", GetLastError ());
- goto out;
- }
- debug_printf ("got body (%ld)\n",bytes_read);
+ vn = strdup ("?");
}
+ else
+ {
+ vn = strdup (colon + 2); // Skip ": "
- /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
- req->serve (conn, cache);
+ char *const spc = strchr (vn, ' ');
- if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header)))
- != sizeof(req->header) || (req->header.cb &&
- (bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb))
- {
- req->header.error_code = -1;
- printf ("error writing to connection (%lu)\n", GetLastError ());
- goto out;
+ if (spc)
+ *spc = '\0';
}
- debug_printf("Sent reply, size (%ld)\n",bytes_written);
- printf (".");
-
-out:
- conn->close ();
- delete conn;
- if (req)
- delete (req);
+ char buf[200];
+ snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
+ cygwin_version.dll_major / 1000,
+ cygwin_version.dll_major % 1000,
+ cygwin_version.dll_minor,
+ cygwin_version.api_major,
+ cygwin_version.api_minor,
+ cygwin_version.shared_data,
+ CYGWIN_SERVER_VERSION_MAJOR,
+ CYGWIN_SERVER_VERSION_API,
+ CYGWIN_SERVER_VERSION_MINOR,
+ CYGWIN_SERVER_VERSION_PATCH,
+ cygwin_version.mount_registry,
+ cygwin_version.dll_build_date);
+
+ printf ("%s (cygwin) %s\n", pgm, vn);
+ printf ("API version %s\n", buf);
+ printf ("Copyright 2001, 2002 Red Hat, Inc.\n");
+ printf ("Compiled on %s\n", __DATE__);
+
+ free (vn);
}
-void
-server_request_queue::add (transport_layer_base *conn)
-{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- /* every derived ::add must enter the section! */
- EnterCriticalSection (&queuelock);
- if (!running)
- {
- conn->close ();
- delete conn;
- LeaveCriticalSection (&queuelock);
- return;
- }
- queue_request * listrequest = new server_request (conn, cache);
- threaded_queue::add (listrequest);
- LeaveCriticalSection (&queuelock);
-}
+/*
+ * main ()
+ */
-void
-handle_signal (int signal)
+int
+main (const int argc, char *argv[])
{
- /* any signal makes us die :} */
- /* FIXME: link upwards, and then this becomes a trivial method call to
- * only shutdown _this queue_
- */
- /* tell the main thread to shutdown */
- request_queue.active=false;
-}
+ const struct option longopts[] = {
+ {"cleanup-threads", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {"request-threads", required_argument, NULL, 'r'},
+ {"shutdown", no_argument, NULL, 's'},
+ {"version", no_argument, NULL, 'v'},
+ {0, no_argument, NULL, 0}
+ };
-struct option longopts[] = {
- {"shutdown", no_argument, NULL, 's'},
- {0, no_argument, NULL, 0}
-};
+ const char opts[] = "c:hr:sv";
-char opts[] = "s";
+ int cleanup_threads = 2;
+ int request_threads = 10;
+ bool shutdown = false;
-int
-main (int argc, char **argv)
-{
- int shutdown=0;
- char i;
+ const char *pgm = NULL;
+
+ if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
+ pgm = *argv;
+ else
+ pgm++;
- while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
- switch (i)
+ wincap.init ();
+ if (wincap.has_security ())
+ setup_privileges ();
+
+ int opt;
+
+ while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+ switch (opt)
{
- case 's':
- shutdown = 1;
+ case 'c':
+ cleanup_threads = atoi (optarg);
+ if (cleanup_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of cleanup threads must be positive\n",
+ pgm);
+ exit (1);
+ }
+ break;
+
+ case 'h':
+ print_usage (pgm);
+ return 0;
+
+ case 'r':
+ request_threads = atoi (optarg);
+ if (request_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of request threads must be positive\n",
+ pgm);
+ exit (1);
+ }
break;
- default:
+
+ case 's':
+ shutdown = true;
break;
- /*NOTREACHED*/
+
+ case 'v':
+ print_version (pgm);
+ return 0;
+
+ case '?':
+ fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
+ exit (1);
}
- wincap.init();
- if (wincap.has_security ())
- setup_privileges ();
- transport = create_server_transport ();
+ if (optind != argc)
+ {
+ fprintf (stderr, "%s: too many arguments\n", pgm);
+ exit (1);
+ }
if (shutdown)
{
- if (!transport->connect())
+ /* Setting `cygserver_running' stops the request code making a
+ * version request, which is not much to the point.
+ */
+ cygserver_running = CYGSERVER_OK;
+
+ client_request_shutdown req;
+
+ if (req.make_request () == -1 || req.error_code ())
{
- printf ("couldn't establish connection with server\n");
+ fprintf (stderr, "%s: shutdown request failed: %s\n",
+ pgm, strerror (req.error_code ()));
exit (1);
}
- client_request_shutdown *request =
- new client_request_shutdown ();
- request->send (transport);
- transport->close();
- delete transport;
- delete request;
- exit(0);
+
+ // FIXME: It would be nice to wait here for the daemon to exit.
+
+ return 0;
}
- char version[200];
- /* Cygwin dll release */
- snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
- cygwin_version.dll_major / 1000,
- cygwin_version.dll_major % 1000,
- cygwin_version.dll_minor,
- cygwin_version.api_major,
- cygwin_version.api_minor,
- cygwin_version.shared_data,
- CYGWIN_SERVER_VERSION_MAJOR,
- CYGWIN_SERVER_VERSION_API,
- CYGWIN_SERVER_VERSION_MINOR,
- CYGWIN_SERVER_VERSION_PATCH,
- cygwin_version.mount_registry,
- cygwin_version.dll_build_date);
+#define SIGHANDLE(SIG) \
+ do \
+ { \
+ struct sigaction act; \
+ \
+ act.sa_handler = &handle_signal; \
+ act.sa_mask = 0; \
+ act.sa_flags = 0; \
+ \
+ if (sigaction (SIG, &act, NULL) == -1) \
+ { \
+ system_printf ("failed to install handler for " #SIG ": %s", \
+ strerror (errno)); \
+ exit (1); \
+ } \
+ } while (false)
+
+ SIGHANDLE (SIGHUP);
+ SIGHANDLE (SIGINT);
+ SIGHANDLE (SIGTERM);
+
+ print_version (pgm);
setbuf (stdout, NULL);
- printf ("daemon version %s starting up", version);
- if (signal (SIGQUIT, handle_signal) == SIG_ERR)
+ printf ("daemon starting up");
+
+ threaded_queue request_queue (request_threads);
+ printf (".");
+
+ transport_layer_base *const transport = create_server_transport ();
+ assert (transport);
+ printf (".");
+
+ process_cache cache (cleanup_threads);
+ printf (".");
+
+ server_submission_loop submission_loop (&request_queue, transport, &cache);
+ printf (".");
+
+ request_queue.add_submission_loop (&submission_loop);
+ printf (".");
+
+ if (transport->listen () == -1)
{
- printf ("\ncould not install signal handler (%d)- aborting startup\n", errno);
exit (1);
}
printf (".");
- transport->listen ();
- printf (".");
- class process_cache cache (2);
- request_queue.initial_workers = 10;
- request_queue.cache = &cache;
- request_queue.create_workers ();
- printf (".");
- request_queue.process_requests (transport);
+
+ cache.start ();
printf (".");
- cache.create_workers ();
+
+ request_queue.start ();
printf (".");
- cache.process_requests ();
- printf (".complete\n");
- /* TODO: wait on multiple objects - the thread handle for each request loop +
- * all the process handles. This should be done by querying the request_queue and
- * the process cache for all their handles, and then waiting for (say) 30 seconds.
- * after that we recreate the list of handles to wait on, and wait again.
- * the point of all this abstraction is that we can trivially server both sockets
- * and pipes simply by making a new transport, and then calling
- * request_queue.process_requests (transport2);
+
+ printf ("complete\n");
+
+ /* TODO: wait on multiple objects - the thread handle for each
+ * request loop + all the process handles. This should be done by
+ * querying the request_queue and the process cache for all their
+ * handles, and then waiting for (say) 30 seconds. after that we
+ * recreate the list of handles to wait on, and wait again. the
+ * point of all this abstraction is that we can trivially server
+ * both sockets and pipes simply by making a new transport, and then
+ * calling request_queue.process_requests (transport2);
*/
/* WaitForMultipleObjects abort && request_queue && process_queue && signal
-- if signal event then retrigger it
- */
- while (1 && request_queue.active)
- {
- sleep (1);
- }
- printf ("\nShutdown request recieved - new requests will be denied\n");
- request_queue.cleanup ();
+ */
+ while (!shutdown_server && request_queue.running () && cache.running ())
+ pause ();
+
+ printf ("\nShutdown request received - new requests will be denied\n");
+ request_queue.stop ();
printf ("All pending requests processed\n");
- transport->close ();
+ safe_delete (transport);
printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
- cache.cleanup ();
+ cache.stop ();
printf ("All outstanding process-cache activities completed\n");
printf ("daemon shutdown\n");
+
+ return 0;
}
diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc
index 1df23ad..138c9dd 100755
--- a/winsup/cygwin/cygserver_client.cc
+++ b/winsup/cygwin/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;
}
diff --git a/winsup/cygwin/cygserver_ipc.h b/winsup/cygwin/cygserver_ipc.h
new file mode 100644
index 0000000..0d0ebbc
--- /dev/null
+++ b/winsup/cygwin/cygserver_ipc.h
@@ -0,0 +1,84 @@
+/* cygserver_ipc.h
+
+ Copyright 2002 Red Hat, Inc.
+
+ Originally written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+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. */
+
+#ifndef __CYGSERVER_IPC_H__
+#define __CYGSERVER_IPC_H__
+
+#include <assert.h>
+#include <limits.h> /* For OPEN_MAX. */
+
+/*
+ * The sysv ipc id's (msgid, semid, shmid) are integers arranged such
+ * that they no subsystem will generate the same id as some other
+ * subsystem; nor do these ids overlap file descriptors (the other
+ * common integer ids). Since Cygwin can allocate more than OPEN_MAX
+ * file descriptors, it can't be guaranteed not to overlap, but it
+ * should help catch some errors.
+ *
+ * msgid's: OPEN_MAX, OPEN_MAX + 3, OPEN_MAX + 6, . . .
+ * semid's: OPEN_MAX + 1, OPEN_MAX + 4, OPEN_MAX + 7, . . .
+ * shmid's: OPEN_MAX + 2, OPEN_MAX + 5, OPEN_MAX + 8, . . .
+ *
+ * To further ensure that ids are unique, if ipc objects are created
+ * and destroyed and then re-created, they are given new ids by
+ * munging the basic id (as above) with a sequence number.
+ *
+ * Internal ipc id's, which are 0, 1, ... within each subsystem (and
+ * not munged with a sequence number), are used solely by the ipcs(8)
+ * interface.
+ */
+
+enum ipc_subsys_t
+ {
+ IPC_MSGOP = 0,
+ IPC_SEMOP = 1,
+ IPC_SHMOP = 2,
+ IPC_SUBSYS_COUNT
+ };
+
+/*
+ * IPCMNI - The absolute maximum number of simultaneous ipc ids for
+ * any one subsystem.
+ */
+
+enum
+ {
+ IPCMNI = 0x10000 // Must be a power of two.
+ };
+
+inline int
+ipc_int2ext (const int intid, const ipc_subsys_t subsys, long & sequence)
+{
+ assert (0 <= intid && intid < IPCMNI);
+
+ const long tmp = InterlockedIncrement (&sequence);
+
+ return (((tmp & 0x7fff) << 16)
+ | (OPEN_MAX + (intid * IPC_SUBSYS_COUNT) + subsys));
+}
+
+inline int
+ipc_ext2int_subsys (const int extid)
+{
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) % IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_ext2int (const int extid, const ipc_subsys_t subsys)
+{
+ if (ipc_ext2int_subsys (extid) != subsys)
+ return -1;
+ else
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) / IPC_SUBSYS_COUNT;
+}
+
+#endif /* __CYGSERVER_IPC_H__ */
diff --git a/winsup/cygwin/cygserver_process.cc b/winsup/cygwin/cygserver_process.cc
index dd13f37..7118bbc 100755
--- a/winsup/cygwin/cygserver_process.cc
+++ b/winsup/cygwin/cygserver_process.cc
@@ -4,385 +4,429 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
+#include "woutsup.h"
+
+#include <sys/types.h>
+
+#include <assert.h>
#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
-#include <pthread.h>
-#include <threaded_queue.h>
-#include <cygwin/cygserver_process.h>
-
-#define debug_printf if (DEBUG) printf
-#define DEBUG 1
-
-/* the cache structures and classes are designed for one cache per server process.
- * To make multiple process caches, a redesign will be needed
- */
-/* process cache */
-process_cache::process_cache (unsigned int num_initial_workers):
-head (NULL)
+#include "cygerrno.h"
+
+#include "cygwin/cygserver_process.h"
+
+/*****************************************************************************/
+
+#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
+
+/*****************************************************************************/
+
+process_cleanup::~process_cleanup ()
{
- /* there can only be one */
- InitializeCriticalSection (&cache_write_access);
- if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
- {
- printf ("Failed to create cache add trigger (%lu), terminating\n",
- GetLastError ());
- exit (1);
- }
- initial_workers = num_initial_workers;
+ safe_delete (_process);
}
-process_cache::~process_cache ()
+void
+process_cleanup::process ()
{
+ _process->cleanup ();
}
-class process *
-process_cache::process (long pid)
+/*****************************************************************************/
+
+/* cleanup_routine */
+cleanup_routine::~cleanup_routine ()
{
- class process *entry = head;
- /* TODO: make this more granular, so a search doesn't involve the write lock */
- EnterCriticalSection (&cache_write_access);
- if (!entry)
+}
+
+/*****************************************************************************/
+
+process::process (const pid_t cygpid, const DWORD winpid)
+ : _cygpid (cygpid),
+ _winpid (winpid),
+ _hProcess (NULL),
+ _cleaning_up (false),
+ _exit_status (STILL_ACTIVE),
+ _routines_head (NULL),
+ _next (NULL)
+{
+ _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
+ if (!_hProcess)
{
- entry = new class process (pid);
- entry->next =
- (class process *) InterlockedExchangePointer (&head, entry);
- PulseEvent (cache_add_trigger);
+ system_printf ("unable to obtain handle for new cache process %d(%lu)",
+ _cygpid, _winpid);
+ _hProcess = INVALID_HANDLE_VALUE;
+ _exit_status = 0;
}
else
- {
- while (entry->winpid != pid && entry->next)
- entry = entry->next;
- if (entry->winpid != pid)
- {
- class process *new_entry = new class process (pid);
- new_entry->next =
- (class process *) InterlockedExchangePointer (&entry->next,
- new_entry);
- entry = new_entry;
- PulseEvent (cache_add_trigger);
- }
- }
- LeaveCriticalSection (&cache_write_access);
- return entry;
+ debug_printf ("got handle %p for new cache process %d(%lu)",
+ _hProcess, _cygpid, _winpid);
+ InitializeCriticalSection (&_access);
}
-static DWORD WINAPI
-request_loop (LPVOID LpParam)
+process::~process ()
{
- class process_process_param *params = (process_process_param *) LpParam;
- return params->request_loop ();
+ DeleteCriticalSection (&_access);
+ (void) CloseHandle (_hProcess);
}
-void
-process_cache::process_requests ()
+/* No need to be thread-safe as this is only ever called by
+ * process_cache::remove_process (). If it has to be made thread-safe
+ * later on, it should not use the `access' critical section as that
+ * is held by the client request handlers for an arbitrary length of
+ * time, i.e. while they do whatever processing is required for a
+ * client request.
+ */
+DWORD
+process::check_exit_code ()
{
- class process_process_param *params = new process_process_param;
- threaded_queue::process_requests (params, request_loop);
+ if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
+ && _exit_status == STILL_ACTIVE
+ && !GetExitCodeProcess (_hProcess, &_exit_status))
+ {
+ system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
+ _cygpid, _winpid, GetLastError ());
+ _hProcess = INVALID_HANDLE_VALUE;
+ }
+ return _exit_status;
}
-void
-process_cache::add_task (class process * theprocess)
+bool
+process::add (cleanup_routine *const entry)
{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- /* every derived ::add must enter the section! */
- EnterCriticalSection (&queuelock);
- queue_request *listrequest = new process_cleanup (theprocess);
- threaded_queue::add (listrequest);
- LeaveCriticalSection (&queuelock);
-}
+ assert (entry);
-/* NOT fully MT SAFE: must be called by only one thread in a program */
-void
-process_cache::remove_process (class process *theprocess)
-{
- class process *entry = head;
- /* unlink */
- EnterCriticalSection (&cache_write_access);
- if (entry == theprocess)
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
{
- entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
- if (entry != theprocess)
- {
- printf ("Bug encountered, process cache corrupted\n");
- exit (1);
- }
+ entry->_next = _routines_head;
+ _routines_head = entry;
+ res = true;
}
- else
+
+ LeaveCriticalSection (&_access);
+ return res;
+}
+
+bool
+process::remove (const cleanup_routine *const entry)
+{
+ assert (entry);
+
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
{
- while (entry->next && entry->next != theprocess)
- entry = entry->next;
- class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
- if (temp != theprocess)
+ cleanup_routine *previous = NULL;
+
+ for (cleanup_routine *ptr = _routines_head;
+ ptr;
+ previous = ptr, ptr = ptr->_next)
{
- printf ("Bug encountered, process cache corrupted\n");
- exit (1);
+ if (*ptr == *entry)
+ {
+ if (previous)
+ previous->_next = ptr->_next;
+ else
+ _routines_head = ptr->_next;
+
+ safe_delete (ptr);
+ res = true;
+ break;
+ }
}
}
- LeaveCriticalSection (&cache_write_access);
- /* Process any cleanup tasks */
- add_task (theprocess);
+
+ LeaveCriticalSection (&_access);
+ return res;
}
-/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of
- * begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0]
- * NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed.
- * Who cares - It'll get caught the next time.
+/* This is single threaded. It's called after the process is removed
+ * from the cache, but inserts may be attemped by worker threads that
+ * have a pointer to it.
*/
-int
-process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
- ssize_t max_copy, int begin_at)
+void
+process::cleanup ()
{
- /* TODO:? grab a delete-lock, to prevent deletes during this process ? */
- class process *entry = head;
- int count = begin_at;
- /* skip begin_at entries */
- while (entry && count)
- {
- if (entry->exit_code () == STILL_ACTIVE)
- count--;
- entry = entry->next;
- }
- /* hit the end of the list within begin_at entries */
- if (count)
- return 0;
- HANDLE *hto = hdest;
- class process **eto = edest;
- while (entry && count < max_copy)
+ EnterCriticalSection (&_access);
+ assert (!is_active ());
+ assert (!_cleaning_up);
+ InterlockedExchange (&_cleaning_up, true);
+ cleanup_routine *entry = _routines_head;
+ _routines_head = NULL;
+ LeaveCriticalSection (&_access);
+
+ while (entry)
{
- /* hack */
- if (entry->exit_code () == STILL_ACTIVE)
- {
- *hto = entry->handle ();
- *eto = entry;
- count++;
- hto++;
- eto++;
- }
- entry = entry->next;
+ cleanup_routine *const ptr = entry;
+ entry = entry->_next;
+ ptr->cleanup (this);
+ safe_delete (ptr);
}
- return count;
}
-/* process's */
-/* global process crit section */
-static CRITICAL_SECTION process_access;
-static pthread_once_t process_init;
+/*****************************************************************************/
void
-do_process_init (void)
+process_cache::submission_loop::request_loop ()
{
- InitializeCriticalSection (&process_access);
- /* we don't have a cache shutdown capability today */
+ assert (this);
+ assert (_cache);
+ assert (_interrupt_event);
+
+ while (_running)
+ _cache->wait_for_processes (_interrupt_event);
}
-process::process (long pid):
-winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
+/*****************************************************************************/
+
+process_cache::process_cache (const unsigned int initial_workers)
+ : _queue (initial_workers),
+ _submitter (this, &_queue), // true == interruptible
+ _processes_count (0),
+ _processes_head (NULL),
+ _cache_add_trigger (NULL)
{
- pthread_once (&process_init, do_process_init);
- EnterCriticalSection (&process_access);
- thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
- if (!thehandle)
+ /* there can only be one */
+ InitializeCriticalSection (&_cache_write_access);
+
+ _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_cache_add_trigger)
{
- printf ("unable to obtain handle for new cache process %ld\n", pid);
- thehandle = INVALID_HANDLE_VALUE;
+ system_printf ("failed to create cache add trigger, error = %lu",
+ GetLastError ());
+ abort ();
}
- debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
- InitializeCriticalSection (&access);
- LeaveCriticalSection (&process_access);
-}
-process::~process ()
-{
- DeleteCriticalSection (&access);
+ _queue.add_submission_loop (&_submitter);
}
-HANDLE
-process::handle ()
+process_cache::~process_cache ()
{
-// DWORD exitstate = exit_code ();
-// if (exitstate == STILL_ACTIVE)
- return thehandle;
-
- /* FIXME: call the cleanup list ? */
-
-// CloseHandle (thehandle);
-// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
-// winpid);
-// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
-// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid);
-// /* FIXME: what if OpenProcess fails ? */
-// if (thehandle)
-// {
-// _exit_status = STILL_ACTIVE;
-// exit_code ();
-// }
-// else
-// thehandle = INVALID_HANDLE_VALUE;
-// return thehandle;
+ (void) CloseHandle (_cache_add_trigger);
+ DeleteCriticalSection (&_cache_write_access);
}
-DWORD process::exit_code ()
+/* This returns the process object to the caller already locked, that
+ * is, with the object's `access' critical region entered. Thus the
+ * caller must unlock the object when it's finished with it (via
+ * process::release ()). It must then not try to access the object
+ * afterwards, except by going through this routine again, as it may
+ * have been deleted once it has been unlocked.
+ */
+class process *
+process_cache::process (const pid_t cygpid, const DWORD winpid)
{
- if (_exit_status != STILL_ACTIVE)
- return _exit_status;
- bool
- err = GetExitCodeProcess (thehandle, &_exit_status);
- if (!err)
+ /* TODO: make this more granular, so a search doesn't involve the
+ * write lock.
+ */
+ EnterCriticalSection (&_cache_write_access);
+ class process *previous = NULL;
+ class process *entry = find (winpid, &previous);
+
+ if (!entry)
{
- debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
- thehandle = INVALID_HANDLE_VALUE;
- return _exit_status;
+ if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ system_printf (("process limit (%d processes) reached; "
+ "new connection refused for %d(%lu)"),
+ MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
+ cygpid, winpid);
+ set_errno (EAGAIN);
+ return NULL;
+ }
+
+ entry = safe_new (class process, cygpid, winpid);
+ if (!entry->is_active ())
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ safe_delete (entry);
+ set_errno (ESRCH);
+ return NULL;
+ }
+
+ if (previous)
+ {
+ entry->_next = previous->_next;
+ previous->_next = entry;
+ }
+ else
+ {
+ entry->_next = _processes_head;
+ _processes_head = entry;
+ }
+
+ _processes_count += 1;
+ SetEvent (_cache_add_trigger);
}
- else if (_exit_status == STILL_ACTIVE)
- return _exit_status;
- /* add new cleanup task etc etc ? */
- return _exit_status;
+
+ EnterCriticalSection (&entry->_access); // To be released by the caller.
+ LeaveCriticalSection (&_cache_write_access);
+ assert (entry);
+ assert (entry->_winpid == winpid);
+ return entry;
}
-/* this is single threaded. It's called after the process is removed from the cache,
- * but inserts may be attemped by worker threads that have a pointer to it */
void
-process::cleanup ()
+process_cache::wait_for_processes (const HANDLE interrupt_event)
{
- /* Serialize this */
- EnterCriticalSection (&access);
- InterlockedIncrement (&(long)cleaning_up);
- class cleanup_routine *entry = head;
- while (entry)
+ // Update `_wait_array' with handles of all current processes.
+ const size_t count = sync_wait_array (interrupt_event);
+
+ debug_printf ("waiting on %u objects in total (%u processes)",
+ count, _processes_count);
+
+ const DWORD rc = WaitForMultipleObjects (count, _wait_array,
+ FALSE, INFINITE);
+
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("could not wait on the process handles, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ const size_t start = rc - WAIT_OBJECT_0;
+
+ if (rc < WAIT_OBJECT_0 || start > count)
{
- class cleanup_routine *temp;
- entry->cleanup (winpid);
- temp = entry->next;
- delete entry;
- entry = temp;
+ system_printf (("unexpected return code %rc "
+ "from WaitForMultipleObjects: "
+ "expected [%u .. %u)"),
+ rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
+ abort ();
}
- LeaveCriticalSection (&access);
+
+ // Tell all the processes, from the signalled point up, the bad news.
+ for (size_t index = start; index != count; index++)
+ if (_process_array[index])
+ check_and_remove_process (index);
}
-bool
-process::add_cleanup_routine (class cleanup_routine *new_cleanup)
+/*
+ * process_cache::sync_wait_array ()
+ *
+ * Fill-in the wait array with the handles that the cache needs to wait on.
+ * These handles are:
+ * - the process_process_param's interrupt event
+ * - the process_cache's cache_add_trigger event
+ * - the handle for each live process in the cache.
+ *
+ * Return value: the number of live handles in the array.
+ */
+
+size_t
+process_cache::sync_wait_array (const HANDLE interrupt_event)
{
- if (cleaning_up)
- return false;
- EnterCriticalSection (&access);
- /* check that we didn't block with ::cleanup ()
- * This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call
- * which would be a whole lot easier
- */
- if (cleaning_up)
+ assert (this);
+ assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
+ assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
+
+ EnterCriticalSection (&_cache_write_access);
+
+ assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
+
+ size_t index = 0;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
{
- LeaveCriticalSection (&access);
- return false;
+ assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
+ assert (ptr->is_active ());
+
+ _wait_array[index] = ptr->handle ();
+ _process_array[index++] = ptr;
+
+ assert (index <= elements (_wait_array));
}
- new_cleanup->next = head;
- head = new_cleanup;
- LeaveCriticalSection (&access);
- return true;
+
+ /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
+ /* Well, not strictly `must', but it's more efficient if they are :-) */
+
+ _wait_array[index] = interrupt_event;
+ _process_array[index++] = NULL;
+
+ _wait_array[index] = _cache_add_trigger;
+ _process_array[index++] = NULL;
+
+ /* Phew, back to normal volume now. */
+
+ assert (index <= elements (_wait_array));
+
+ LeaveCriticalSection (&_cache_write_access);
+
+ return index;
}
-/* process_cleanup */
void
-process_cleanup::process ()
+process_cache::check_and_remove_process (const size_t index)
{
- theprocess->cleanup ();
- delete theprocess;
+ assert (this);
+ assert (index < elements (_wait_array) - SPECIALS_COUNT);
+
+ class process *const process = _process_array[index];
+
+ assert (process);
+ assert (process->handle () == _wait_array[index]);
+
+ if (process->check_exit_code () == STILL_ACTIVE)
+ return;
+
+ debug_printf ("process %d(%lu) has left the building ($? = %lu)",
+ process->_cygpid, process->_winpid, process->_exit_status);
+
+ /* Unlink the process object from the process list. */
+
+ EnterCriticalSection (&_cache_write_access);
+
+ class process *previous = NULL;
+
+ const class process *const tmp = find (process->_winpid, &previous);
+
+ assert (tmp == process);
+ assert (previous ? previous->_next == process : _processes_head == process);
+
+ if (previous)
+ previous->_next = process->_next;
+ else
+ _processes_head = process->_next;
+
+ _processes_count -= 1;
+ LeaveCriticalSection (&_cache_write_access);
+
+ /* Schedule any cleanup tasks for this process. */
+ _queue.add (safe_new (process_cleanup, process));
}
-/* process_process_param */
-DWORD
-process_process_param::request_loop ()
+class process *
+process_cache::find (const DWORD winpid, class process **previous)
{
- process_cache *cache = (process_cache *) queue;
- /* always malloc one, so there is no special case in the loop */
- ssize_t HandlesSize = 2;
- HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize);
- process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize);
- /* TODO: put [1] at the end as it will also get done if a process dies? */
- Handles[0] = interrupt;
- Handles[1] = cache->cache_add_trigger;
- while (cache->active && !shutdown)
- {
- int copied;
- copied = -1;
- int offset;
- offset = 1;
- int count;
- count = 2;
- while ((copied == HandlesSize - 2 - offset) || copied < 0)
- {
- /* we need more storage to cope with all the HANDLES */
- if (copied == HandlesSize - 2 - offset)
- {
- HANDLE *temp = (HANDLE *) realloc (Handles,
- sizeof (HANDLE) *
- HandlesSize + 10);
- if (!temp)
- {
- printf
- ("cannot allocate more storage for the handle array!\n");
- exit (1);
- }
- Handles = temp;
- process **ptemp = (process **) realloc (Entries,
- sizeof (LPVOID) *
- HandlesSize + 10);
- if (!ptemp)
- {
- printf
- ("cannot allocate more storage for the handle array!\n");
- exit (1);
- }
- Entries = ptemp;
- HandlesSize += 10;
- }
- offset += copied;
- copied =
- cache->handle_snapshot (&Handles[2], &Entries[2],
- HandlesSize - 2 - offset, offset);
- count += copied;
- }
- debug_printf ("waiting on %u objects\n", count);
- DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
- if (rc == WAIT_FAILED)
- {
- printf ("Could not wait on the process handles (%ld)!\n",
- GetLastError ());
- exit (1);
- }
- int objindex = rc - WAIT_OBJECT_0;
- if (objindex > 1 && objindex < count)
- {
- debug_printf ("Process %ld has left the building\n",
- Entries[objindex]->winpid);
- /* fire off the termination routines */
- cache->remove_process (Entries[objindex]);
- }
- else if (objindex >= 0 && objindex < 2)
- {
- /* 0 is shutdown - do nothing */
- /* 1 is a cache add event - just rebuild the object list */
- }
- else
- {
- printf
- ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");
- }
- }
- running = false;
- return 0;
+ if (previous)
+ *previous = NULL;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
+ if (ptr->_winpid == winpid)
+ return ptr;
+ else if (ptr->_winpid > winpid) // The list is sorted by winpid.
+ return NULL;
+ else if (previous)
+ *previous = ptr;
+
+ return NULL;
}
+
+/*****************************************************************************/
diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc
index 260a5b1..18b1c3d 100755
--- a/winsup/cygwin/cygserver_shm.cc
+++ b/winsup/cygwin/cygserver_shm.cc
@@ -1,8 +1,9 @@
-/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
+/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin.
-Copyright 2001, 2002 Red Hat, Inc.
+ Copyright 2002 Red Hat, Inc.
-Originally written by Robert Collins <robert.collins@hotmail.com>
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
@@ -10,656 +11,886 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
-#ifdef __OUTSIDE_CYGWIN__
-#undef __INSIDE_CYGWIN__
-#else
-#include "winsup.h"
-#endif
-
-#ifndef __INSIDE_CYGWIN__
-#define DEBUG 0
-#define system_printf printf
-#define debug_printf if (DEBUG) printf
-#define api_fatal printf
-#include <stdio.h>
-#include <windows.h>
-#endif
+#include "woutsup.h"
-#include <sys/stat.h>
#include <errno.h>
-#include "cygerrno.h"
-#include <unistd.h>
-#include "security.h"
-//#include "fhandler.h"
-//#include "dtable.h"
-//#include "cygheap.h"
+#include <pthread.h>
#include <stdio.h>
-//#include "thread.h"
-#ifndef __INSIDE_CYGWIN__
-#define __INSIDE_CYGWIN__
-#include <cygwin/shm.h>
-#undef __INSIDE_CYGWIN__
-#else
-#include <cygwin/shm.h>
-#endif
-//#include "perprocess.h"
-#include <threaded_queue.h>
-#include <cygwin/cygserver_process.h>
-#include "cygserver_shm.h"
+#include <string.h>
+#include <time.h>
-// FIXME IS THIS CORRECT
-/* Implementation notes: We use two shared memory regions per key:
- * One for the control structure, and one for the shared memory.
- * While this has a higher overhead tham a single shared area,
- * It allows more flexability. As the entire code is transparent to the user
- * We can merge these in the future should it be needed.
- * Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just
- * keep monotonically incrementing - system wide.
- */
-size_t
-getsystemallocgranularity ()
-{
- SYSTEM_INFO sysinfo;
- static size_t buffer_offset = 0;
- if (buffer_offset)
- return buffer_offset;
- GetSystemInfo (&sysinfo);
- buffer_offset = sysinfo.dwAllocationGranularity;
- return buffer_offset;
-}
+#include "cygserver_ipc.h"
+#include "cygserver_shm.h"
+#include "security.h"
+#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
-client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET,
- sizeof (parameters))
-{
- buffer = (char *) &parameters;
-}
+/*---------------------------------------------------------------------------*
+ * class server_shmmgr
+ *
+ * A singleton class.
+ *---------------------------------------------------------------------------*/
-/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
- * Solution: manually calculate the next id value
- */
+#define shmmgr (server_shmmgr::instance ())
-#if 0
-extern
-"C" void *
-shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
+class server_shmmgr
{
- class shmid_ds *
- shm = (class shmid_ds *)
- shmid; //FIXME: verifyable object test
+private:
+ class attach_t
+ {
+ public:
+ class process *const _client;
+ unsigned int _refcnt;
+
+ attach_t *_next;
+
+ attach_t (class process *const client)
+ : _client (client),
+ _refcnt (0),
+ _next (NULL)
+ {}
+ };
+
+ class segment_t
+ {
+ private:
+ // Bits for the _flg field.
+ enum { IS_DELETED = 0x01 };
+
+ public:
+ const int _intid;
+ const int _shmid;
+ struct shmid_ds _ds;
+
+ segment_t *_next;
+
+ segment_t (const key_t key, const int intid, const HANDLE hFileMap);
+ ~segment_t ();
+
+ bool is_deleted () const
+ {
+ return _flg & IS_DELETED;
+ }
+
+ bool is_pending_delete () const
+ {
+ return !_ds.shm_nattch && is_deleted ();
+ }
- if (shmaddr)
+ void mark_deleted ()
{
- //FIXME: requested base address ?!
- set_errno (EINVAL);
- return (void *) -1;
+ assert (!is_deleted ());
+
+ _flg |= IS_DELETED;
}
- void *
- rv =
- MapViewOfFile (shm->attachmap,
+ int attach (class process *, HANDLE & hFileMap);
+ int detach (class process *);
+
+ private:
+ static long _sequence;
+ int _flg;
+ const HANDLE _hFileMap;
+ attach_t *_attach_head; // A list sorted by winpid;
- (parameters.in.shmflg & SHM_RDONLY) ?
- FILE_MAP_READ : FILE_MAP_WRITE, 0,
- 0, 0);
+ attach_t *find (const class process *, attach_t **previous = NULL);
+ };
- if (!rv)
+ class cleanup_t : public cleanup_routine
+ {
+ public:
+ cleanup_t (const segment_t *const segptr)
+ : cleanup_routine (reinterpret_cast<void *> (segptr->_shmid))
{
- //FIXME: translate GetLastError()
- set_errno (EACCES);
- return (void *) -1;
+ assert (key ());
}
-/* FIXME: this needs to be globally protected to prevent a mismatch betwen
- * attach count and attachees list
- */
-
- InterlockedIncrement (&shm->shm_nattch);
- _shmattach *
- attachnode =
- new
- _shmattach;
-
- attachnode->data = rv;
- attachnode->next =
- (_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead,
- (long int) attachnode);
- return rv;
+ int shmid () const { return reinterpret_cast<int> (key ()); }
+
+ virtual void cleanup (class process *const client)
+ {
+ const int res = shmmgr.shmdt (shmid (), client);
+
+ if (res != 0)
+ debug_printf ("process cleanup failed [shmid = %d]: %s",
+ shmid (), strerror (-res));
+ }
+ };
+
+public:
+ static server_shmmgr & instance ();
+
+ int shmat (HANDLE & hFileMap,
+ int shmid, int shmflg, class process *);
+ int shmctl (int & out_shmid, struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo, struct shm_info & out_shm_info,
+ const int shmid, int cmd, const struct shmid_ds &,
+ class process *);
+ int shmdt (int shmid, class process *);
+ int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t,
+ class process *);
+
+private:
+ static server_shmmgr *_instance;
+ static pthread_once_t _instance_once;
+
+ static void initialise_instance ();
+
+ CRITICAL_SECTION _segments_lock;
+ segment_t *_segments_head; // A list sorted by int_id.
+
+ int _shm_ids; // Number of shm segments (for ipcs(8)).
+ int _shm_tot; // Total bytes of shm segments (for ipcs(8)).
+ int _shm_atts; // Number of attached segments (for ipcs(8)).
+ int _intid_max; // Highest intid yet allocated (for ipcs(8)).
+
+ server_shmmgr ();
+ ~server_shmmgr ();
+
+ // Undefined (as this class is a singleton):
+ server_shmmgr (const server_shmmgr &);
+ server_shmmgr & operator= (const server_shmmgr &);
+
+ segment_t *find_by_key (key_t);
+ segment_t *find (int intid, segment_t **previous = NULL);
+
+ int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t);
+
+ segment_t *new_segment (key_t, size_t, HANDLE);
+ void delete_segment (segment_t *);
+};
+
+/* static */ long server_shmmgr::segment_t::_sequence = 0;
+
+/* static */ server_shmmgr *server_shmmgr::_instance = NULL;
+/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT;
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::segment_t (const key_t key,
+ const int intid,
+ const HANDLE hFileMap)
+ : _intid (intid),
+ _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)),
+ _next (NULL),
+ _flg (0),
+ _hFileMap (hFileMap),
+ _attach_head (NULL)
+{
+ assert (0 <= _intid && _intid < SHMMNI);
+
+ memset (&_ds, '\0', sizeof (_ds));
+ _ds.shm_perm.key = key;
}
-#endif
-
-/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
- * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
- * appropriately
- */
-
-/* Test result from openbsd: shm ids are persistent cross process if a handle is left
- * open. This could lead to resource starvation: we're not copying that behaviour
- * unless we have to. (It will involve acygwin1.dll gloal shared list :[ ).
- */
-/* FIXME: shmid should be a verifyable object
- */
-
-/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
- */
-
-extern GENERIC_MAPPING
- access_mapping;
-
-extern int
-check_and_dup_handle (HANDLE from_process, HANDLE to_process,
- HANDLE from_process_token,
- DWORD access,
- HANDLE from_handle,
- HANDLE * to_handle_ptr, BOOL bInheritHandle);
-
-//FIXME: where should this live
-static shmnode *
- shm_head =
- NULL;
-//FIXME: ditto.
-static shmnode *
- deleted_head = NULL;
-/* must be long for InterlockedIncrement */
-static long
- new_id =
- 0;
-static long
- new_private_key =
- 0;
-
-static void
-delete_shmnode (shmnode **nodeptr)
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::~segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::~segment_t ()
{
- shmnode *node = *nodeptr;
+ assert (!_attach_head);
- // remove from the list
- if (node == shm_head)
- shm_head = shm_head->next;
- else
+ if (!CloseHandle (_hFileMap))
+ syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::attach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::attach (class process *const client,
+ HANDLE & hFileMap)
+{
+ assert (client);
+
+ if (!DuplicateHandle (GetCurrentProcess (),
+ _hFileMap,
+ client->handle (),
+ &hFileMap,
+ 0,
+ FALSE, // bInheritHandle
+ DUPLICATE_SAME_ACCESS))
+ {
+ syscall_printf (("failed to duplicate handle for client "
+ "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"),
+ _ds.shm_perm.key, _shmid, _hFileMap);
+
+ return -EACCES; // FIXME: Case analysis?
+ }
+
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch += 1;
+ _ds.shm_atime = time (NULL); // FIXME: sub-second times.
+
+ attach_t *previous = NULL;
+ attach_t *attptr = find (client, &previous);
+
+ if (!attptr)
{
- shmnode *tempnode = shm_head;
- while (tempnode && tempnode->next != node)
- tempnode = tempnode->next;
- if (tempnode)
- tempnode->next = node->next;
- // else log the unexpected !
+ attptr = safe_new (attach_t, client);
+
+ if (previous)
+ {
+ attptr->_next = previous->_next;
+ previous->_next = attptr;
+ }
+ else
+ {
+ attptr->_next = _attach_head;
+ _attach_head = attptr;
+ }
}
- // release the shared data view
- UnmapViewOfFile (node->shmds->mapptr);
- delete node->shmds;
- CloseHandle (node->filemap);
- CloseHandle (node->attachmap);
+ attptr->_refcnt += 1;
- // free the memory
- delete node;
- nodeptr = NULL;
+ cleanup_t *const cleanup = safe_new (cleanup_t, this);
+
+ // FIXME: ::add should only fail if the process object is already
+ // cleaning up; but it can't be doing that since this thread has it
+ // locked.
+
+ const bool result = client->add (cleanup);
+
+ assert (result);
+
+ return 0;
}
-void
-client_request_shm::serve (transport_layer_base * conn, process_cache * cache)
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::detach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::detach (class process *const client)
{
-// DWORD sd_size = 4096;
-// char sd_buf[4096];
- PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf;
-// /* create a sd for our open requests based on shmflag & 0x01ff */
-// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
-// parameters.in.shmflg & 0x01ff, psd, &sd_size);
-
- HANDLE from_process_handle = NULL;
- HANDLE token_handle = NULL;
- DWORD rc;
-
- from_process_handle = cache->process (parameters.in.pid)->handle ();
- /* possible TODO: reduce the access on the handle before we use it */
- /* Note that unless we do this, we don't need to call CloseHandle - it's kept open
- * by the process cache until the process terminates.
- * We may need a refcount on the cache however...
- */
- if (!from_process_handle)
+ attach_t *previous = NULL;
+ attach_t *const attptr = find (client, &previous);
+
+ if (!attptr)
+ return -EINVAL;
+
+ if (client->is_active ())
{
- debug_printf ("error opening process (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- return;
+ const cleanup_t key (this);
+
+ if (!client->remove (&key))
+ syscall_printf (("failed to remove cleanup routine for %d(%lu) "
+ "[shmid = %d]"),
+ client->cygpid (), client->winpid (),
+ _shmid);
}
- conn->impersonate_client ();
+ attptr->_refcnt -= 1;
- rc = OpenThreadToken (GetCurrentThread (),
- TOKEN_QUERY, TRUE, &token_handle);
+ if (!attptr->_refcnt)
+ {
+ assert (previous ? previous->_next == attptr : _attach_head == attptr);
- conn->revert_to_self ();
+ if (previous)
+ previous->_next = attptr->_next;
+ else
+ _attach_head = attptr->_next;
- if (!rc)
- {
- debug_printf ("error opening thread token (%lu)\n", GetLastError ());
- header.error_code = EACCES;
- CloseHandle (from_process_handle);
- return;
+ safe_delete (attptr);
}
+ assert (_ds.shm_nattch > 0);
- /* we trust the clients request - we will be doing it as them, and
- * the worst they can do is open their own permissions
- */
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch -= 1;
+ _ds.shm_dtime = time (NULL); // FIXME: sub-second times.
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::attach_t *
+server_shmmgr::segment_t::find (const class process *const client,
+ attach_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ // Nb. The _attach_head list is sorted by winpid.
+ for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next)
+ if (attptr->_client == client)
+ return attptr;
+ else if (attptr->_client->winpid () > client->winpid ())
+ return NULL;
+ else if (previous)
+ *previous = attptr;
- SECURITY_ATTRIBUTES sa;
- sa.nLength = sizeof (sa);
- sa.lpSecurityDescriptor = psd;
- sa.bInheritHandle = TRUE; /* the memory structures inherit ok */
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ server_shmmgr &
+server_shmmgr::instance ()
+{
+ pthread_once (&_instance_once, &initialise_instance);
+
+ assert (_instance);
+
+ return *_instance;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmat ()
+ *---------------------------------------------------------------------------*/
- char *shmname = NULL, *shmaname = NULL;
- char stringbuf[29], stringbuf1[29];
+int
+server_shmmgr::shmat (HANDLE & hFileMap,
+ const int shmid, const int shmflg,
+ class process *const client)
+{
+ syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)",
+ shmid, shmflg, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->attach (client, hFileMap);
+
+ if (!result)
+ _shm_atts += 1;
- /* TODO: make this code block a function! */
- if (parameters.in.type == SHM_REATTACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ hFileMap, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmctl ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmctl (int & out_shmid,
+ struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo,
+ struct shm_info & out_shm_info,
+ const int shmid, const int cmd,
+ const struct shmid_ds & ds,
+ class process *const client)
+{
+ syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)",
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ switch (cmd)
{
- /* just find and fill out the existing shm_id */
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
+ case IPC_STAT:
+ case SHM_STAT: // Uses intids rather than shmids.
+ case IPC_SET:
+ case IPC_RMID:
+ {
+ int intid;
+
+ if (cmd == SHM_STAT)
+ intid = shmid;
+ else
+ intid = ipc_ext2int (shmid, IPC_SHMOP);
+
+ segment_t *const segptr = find (intid);
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ switch (cmd)
{
- parameters.out.shm_id = tempnode->shm_id;
- parameters.out.key = tempnode->key;
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->filemap,
- &parameters.out.filemap, TRUE) != 0)
- {
- debug_printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- }
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
+ case IPC_STAT:
+ out_ds = segptr->_ds;
+ break;
+
+ case IPC_SET:
+ segptr->_ds.shm_perm.uid = ds.shm_perm.uid;
+ segptr->_ds.shm_perm.gid = ds.shm_perm.gid;
+ segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777;
+ segptr->_ds.shm_lpid = client->cygpid ();
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
+ break;
+
+ case IPC_RMID:
+ if (segptr->is_deleted ())
+ result = -EIDRM;
+ else
{
- debug_printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
+ segptr->mark_deleted ();
+ if (segptr->is_pending_delete ())
+ delete_segment (segptr);
}
- CloseHandle (token_handle);
- return;
+ break;
+
+ case SHM_STAT: // ipcs(8) i'face.
+ out_ds = segptr->_ds;
+ out_shmid = segptr->_shmid;
+ break;
}
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ }
+ break;
+
+ case IPC_INFO:
+ out_shminfo.shmmax = SHMMAX;
+ out_shminfo.shmmin = SHMMIN;
+ out_shminfo.shmmni = SHMMNI;
+ out_shminfo.shmseg = SHMSEG;
+ out_shminfo.shmall = SHMALL;
+ break;
+
+ case SHM_INFO: // ipcs(8) i'face.
+ out_shmid = _intid_max;
+ out_shm_info.shm_ids = _shm_ids;
+ out_shm_info.shm_tot = _shm_tot;
+ out_shm_info.shm_atts = _shm_atts;
+ break;
+
+ default:
+ result = -EINVAL;
+ break;
}
- /* someone attached */
- /* someone can send shm_id's they don't have and currently we will increment those
- * attach counts. If someone wants to fix that, please go ahead.
- * The problem is that shm_get has nothing to do with the ability to attach. Attach
- * requires a permission check, which we get the OS to do in MapViewOfFile.
- */
- if (parameters.in.type == SHM_ATTACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ -result,
+ shmid, cmd, client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ ((cmd == SHM_STAT || cmd == SHM_INFO)
+ ? out_shmid
+ : result),
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmdt ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmdt (const int shmid, class process *const client)
+{
+ syscall_printf ("shmdt (shmid = %d) for %d(%lu)",
+ shmid, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->detach (client);
+
+ if (!result)
+ _shm_atts -= 1;
+
+ if (!result && segptr->is_pending_delete ())
+ delete_segment (segptr);
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)",
+ -result, shmid, client->cygpid (), client->winpid ());
+ else
+ syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)",
+ result, shmid, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmget ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmget (int & out_shmid,
+ const key_t key, const size_t size, const int shmflg,
+ const uid_t uid, const gid_t gid,
+ class process *const client)
+{
+ syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ if (key == IPC_PRIVATE)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
{
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
- {
- InterlockedIncrement (&tempnode->shmds->shm_nattch);
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ segment_t *const segptr = find_by_key (key);
+
+ if (!segptr)
+ if (shmflg & IPC_CREAT)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
+ result = -ENOENT;
+ else if (segptr->is_deleted ())
+ result = -EIDRM;
+ else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
+ result = -EEXIST;
+ else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777)
+ result = -EACCES;
+ else if (size && segptr->_ds.shm_segsz < size)
+ result = -EINVAL;
+ else
+ result = segptr->_shmid;
}
- /* Someone detached */
- if (parameters.in.type == SHM_DETACH)
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result >= 0)
{
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->shm_id == parameters.in.shm_id)
- {
- InterlockedDecrement (&tempnode->shmds->shm_nattch);
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ out_shmid = result;
+ result = 0;
}
- /* Someone wants the ID removed. */
- if (parameters.in.type == SHM_DEL)
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ out_shmid,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::initialise_instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ void
+server_shmmgr::initialise_instance ()
+{
+ assert (!_instance);
+
+ _instance = safe_new0 (server_shmmgr);
+
+ assert (_instance);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::server_shmmgr ()
+ : _segments_head (NULL),
+ _shm_ids (0),
+ _shm_tot (0),
+ _shm_atts (0),
+ _intid_max (0)
+{
+ InitializeCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::~server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::~server_shmmgr ()
+{
+ DeleteCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find_by_key ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find_by_key (const key_t key)
+{
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_ds.shm_perm.key == key)
+ return segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find (const int intid, segment_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_intid == intid)
+ return segptr;
+ else if (segptr->_intid > intid) // The list is sorted by intid.
+ return NULL;
+ else if (previous)
+ *previous = segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::new_segment (const key_t key,
+ const size_t size,
+ const int shmflg,
+ const pid_t cygpid,
+ const uid_t uid,
+ const gid_t gid)
+{
+ if (size < SHMMIN || size > SHMMAX)
+ return -EINVAL;
+
+ const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE,
+ NULL, PAGE_READWRITE,
+ 0, size,
+ NULL);
+
+ if (!hFileMap)
{
- shmnode **tempnode = &shm_head;
- while (*tempnode)
- {
- if ((*tempnode)->shm_id == parameters.in.shm_id)
- {
- // unlink from the accessible node list
- shmnode *temp2 = *tempnode;
- *tempnode = temp2->next;
- // link into the deleted list
- temp2->next = deleted_head;
- deleted_head = temp2;
-
- // FIXME: when/where do we delete the handles?
- if (temp2->shmds->shm_nattch)
- {
- // FIXME: add to a pending queue?
- }
- else
- {
- delete_shmnode (&temp2);
- }
-
- header.error_code = 0;
- CloseHandle (token_handle);
- return;
- }
- tempnode = &(*tempnode)->next;
- }
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
+ syscall_printf ("failed to create file mapping [size = %lu]: %E", size);
+ return -ENOMEM; // FIXME
}
+ segment_t *const segptr = new_segment (key, size, hFileMap);
- if (parameters.in.type == SHM_CREATE)
+ if (!segptr)
{
- /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
- * to prevent races on shmget.
- */
+ (void) CloseHandle (hFileMap);
+ return -ENOSPC;
+ }
- if (parameters.in.key == IPC_PRIVATE)
- {
- /* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */
- /* The K refers to Key, the actual mapped area has D */
- long private_key = (int) InterlockedIncrement (&new_private_key);
- snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key);
- shmname = stringbuf;
- snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key);
- shmaname = stringbuf1;
- }
- else
- {
- /* create the mapping name (CYGWINSHMK0x0123456789abcdef */
- /* The K refers to Key, the actual mapped area has D */
-
- snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key);
- shmname = stringbuf;
- snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key);
- shmaname = stringbuf1;
- debug_printf ("system id strings are \n%s\n%s\n", shmname,
- shmaname);
- debug_printf ("key input value is 0x%0qx\n", parameters.in.key);
- }
+ segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid;
+ segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid;
+ segptr->_ds.shm_perm.mode = shmflg & 0777;
+ segptr->_ds.shm_segsz = size;
+ segptr->_ds.shm_cpid = cygpid;
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
- /* attempt to open the key */
+ return segptr->_shmid;
+}
- /* get an existing key */
- /* On unix the same shmid identifier is returned on multiple calls to shm_get
- * with the same key and size. Different modes is a ?.
- */
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *
+ * Allocate a new segment for the given key and file map with the
+ * lowest available intid and insert into the segment map.
+ *---------------------------------------------------------------------------*/
+server_shmmgr::segment_t *
+server_shmmgr::new_segment (const key_t key, const size_t size,
+ const HANDLE hFileMap)
+{
+ // FIXME: Overflow risk.
+ if (_shm_tot + size > SHMALL)
+ return NULL;
+ int intid = 0; // Next expected intid value.
+ segment_t *previous = NULL; // Insert pointer.
- /* walk the list of known keys and return the id if found. remember, we are
- * authoritative...
- */
+ // Find first unallocated intid.
+ for (segment_t *segptr = _segments_head;
+ segptr && segptr->_intid == intid;
+ segptr = segptr->_next, intid++)
+ {
+ previous = segptr;
+ }
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->key == parameters.in.key
- && parameters.in.key != IPC_PRIVATE)
- {
- // FIXME: free the mutex
- if (parameters.in.size
- && tempnode->shmds->shm_segsz < parameters.in.size)
- {
- header.error_code = EINVAL;
- CloseHandle (token_handle);
- return;
- }
- /* FIXME: can the same process call this twice without error ? test
- * on unix
- */
- if ((parameters.in.shmflg & IPC_CREAT)
- && (parameters.in.shmflg & IPC_EXCL))
- {
- header.error_code = EEXIST;
- debug_printf
- ("attempt to exclusively create already created shm_area with key 0x%0qx\n",
- parameters.in.key);
- // FIXME: free the mutex
- CloseHandle (token_handle);
- return;
- }
- // FIXME: do we need to other tests of the requested mode with the
- // tempnode->shm_id mode ? testcase on unix needed.
- // FIXME how do we do the security test? or
- // do we wait for shmat to bother with that?
- /* One possibly solution: impersonate the client, and then test we can
- * reopen the area. In fact we'll probably have to do that to get
- * handles back to them, alternatively just tell them the id, and then
- * let them attempt the open.
- */
- parameters.out.shm_id = tempnode->shm_id;
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->filemap,
- &parameters.out.filemap, TRUE) != 0)
- {
- printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
-/*mutex*/
- CloseHandle (token_handle);
- return;
- }
- if (check_and_dup_handle
- (GetCurrentProcess (), from_process_handle, token_handle,
- DUPLICATE_SAME_ACCESS, tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
- {
- printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
-/*mutex*/
- CloseHandle (token_handle);
- return;
- }
+ /* By the time this condition is reached (given the default value of
+ * SHMMNI), the linear searches should all replaced by something
+ * just a *little* cleverer . . .
+ */
+ if (intid >= SHMMNI)
+ return NULL;
- CloseHandle (token_handle);
- return;
- }
- tempnode = tempnode->next;
- }
- /* couldn't find a currently open shm area. */
-
- /* create one */
- /* do this as the client */
- conn->impersonate_client ();
- /* This may need sh_none... it's only a control structure */
- HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile.
- &sa,
- PAGE_READWRITE, // protection
- 0x00000000,
- getsystemallocgranularity (),
- shmname // object name
- );
- int lasterr = GetLastError ();
- conn->revert_to_self ();
-
- if (filemap == NULL)
- {
- /* We failed to open the filemapping ? */
- system_printf ("failed to open file mapping: %lu\n",
- GetLastError ());
- // free the mutex
- // we can assume that it exists, and that it was an access problem.
- header.error_code = EACCES;
- CloseHandle (token_handle);
- return;
- }
+ segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap);
- /* successfully opened the control region mapping */
- /* did we create it ? */
- int oldmapping = lasterr == ERROR_ALREADY_EXISTS;
- if (oldmapping)
- {
- /* should never happen - we are the global daemon! */
-#if 0
- if ((parameters.in.shmflg & IPC_CREAT)
- && (parameters.in.shmflg & IPC_EXCL))
-#endif
- {
- /* FIXME free mutex */
- CloseHandle (filemap);
- header.error_code = EEXIST;
- CloseHandle (token_handle);
- return;
- }
- }
+ assert (segptr);
- /* we created a new mapping */
- if (parameters.in.key != IPC_PRIVATE &&
- (parameters.in.shmflg & IPC_CREAT) == 0)
- {
- CloseHandle (filemap);
- /* FIXME free mutex */
- header.error_code = ENOENT;
- CloseHandle (token_handle);
- return;
- }
+ if (previous)
+ {
+ segptr->_next = previous->_next;
+ previous->_next = segptr;
+ }
+ else
+ {
+ segptr->_next = _segments_head;
+ _segments_head = segptr;
+ }
- conn->impersonate_client ();
- void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
- conn->revert_to_self ();
+ _shm_ids += 1;
+ _shm_tot += size;
+ if (intid > _intid_max)
+ _intid_max = intid;
- if (!mapptr)
- {
- CloseHandle (filemap);
- //FIXME: close filemap and free the mutex
- /* we couldn't access the mapped area with the requested permissions */
- header.error_code = EACCES;
- CloseHandle (token_handle);
- return;
- }
+ return segptr;
+}
- conn->impersonate_client ();
- /* Now get the user data */
- HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile
- &sa,
- PAGE_READWRITE, // protection (FIXME)
- 0x00000000,
- parameters.in.size +
- parameters.in.size %
- getsystemallocgranularity (),
- shmaname // object name
- );
- conn->revert_to_self ();
-
- if (attachmap == NULL)
- {
- system_printf ("failed to get shm attachmap\n");
- header.error_code = ENOMEM;
- UnmapViewOfFile (mapptr);
- CloseHandle (filemap);
- /* FIXME exit the mutex */
- CloseHandle (token_handle);
- return;
- }
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::delete_segment ()
+ *---------------------------------------------------------------------------*/
- shmid_ds *shmtemp = new shmid_ds;
- if (!shmtemp)
- {
- system_printf ("failed to malloc shm node\n");
- header.error_code = ENOMEM;
- UnmapViewOfFile (mapptr);
- CloseHandle (filemap);
- CloseHandle (attachmap);
- /* FIXME exit mutex */
- CloseHandle (token_handle);
- return;
- }
+void
+server_shmmgr::delete_segment (segment_t *const segptr)
+{
+ assert (segptr);
+ assert (segptr->is_pending_delete ());
- /* fill out the node data */
- shmtemp->shm_perm.cuid = getuid ();
- shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
- shmtemp->shm_perm.cgid = getgid ();
- shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
- shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff;
- shmtemp->shm_lpid = 0;
- shmtemp->shm_nattch = 0;
- shmtemp->shm_atime = 0;
- shmtemp->shm_dtime = 0;
- shmtemp->shm_ctime = time (NULL);
- shmtemp->shm_segsz = parameters.in.size;
- *(shmid_ds *) mapptr = *shmtemp;
- shmtemp->mapptr = mapptr;
-
- /* no need for InterlockedExchange here, we're serialised by the global mutex */
- tempnode = new shmnode;
- tempnode->shmds = shmtemp;
- tempnode->shm_id = (int) InterlockedIncrement (&new_id);
- tempnode->key = parameters.in.key;
- tempnode->filemap = filemap;
- tempnode->attachmap = attachmap;
- tempnode->next = shm_head;
- shm_head = tempnode;
-
- /* we now have the area in the daemon list, opened.
-
- FIXME: leave the system wide shm mutex */
-
- parameters.out.shm_id = tempnode->shm_id;
- if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
- token_handle,
- DUPLICATE_SAME_ACCESS,
- tempnode->filemap, &parameters.out.filemap,
- TRUE) != 0)
- {
- printf ("error duplicating filemap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- CloseHandle (token_handle);
-/* mutex et al */
- return;
- }
- if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
- token_handle,
- DUPLICATE_SAME_ACCESS,
- tempnode->attachmap,
- &parameters.out.attachmap, TRUE) != 0)
- {
- printf ("error duplicating attachmap handle (%lu)\n",
- GetLastError ());
- header.error_code = EACCES;
- CloseHandle (from_process_handle);
- CloseHandle (token_handle);
-/* more cleanup... yay! */
- return;
- }
- CloseHandle (token_handle);
+ segment_t *previous = NULL;
+ const segment_t *const tmp = find (segptr->_intid, &previous);
+
+ assert (tmp == segptr);
+ assert (previous ? previous->_next == segptr : _segments_head == segptr);
+
+ if (previous)
+ previous->_next = segptr->_next;
+ else
+ _segments_head = segptr->_next;
+
+ assert (_shm_ids > 0);
+ _shm_ids -= 1;
+ _shm_tot -= segptr->_ds.shm_segsz;
+
+ safe_delete (segptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm ()
+ : client_request (CYGSERVER_REQUEST_SHM,
+ &_parameters, sizeof (_parameters))
+{
+ // verbose: syscall_printf ("created");
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::serve ()
+ *---------------------------------------------------------------------------*/
+
+void
+client_request_shm::serve (transport_layer_base *const conn,
+ process_cache *const cache)
+{
+ assert (conn);
+
+ assert (!error_code ());
+
+ if (msglen () != sizeof (_parameters.in))
+ {
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (_parameters), msglen ());
+ error_code (EINVAL);
+ msglen (0);
return;
}
- header.error_code = ENOSYS;
- CloseHandle (token_handle);
+ // FIXME: Get a return code out of this and don't continue on error.
+ conn->impersonate_client ();
+
+ class process *const client = cache->process (_parameters.in.cygpid,
+ _parameters.in.winpid);
+
+ if (!client)
+ {
+ error_code (EAGAIN);
+ msglen (0);
+ return;
+ }
+ int result = -EINVAL;
- return;
+ switch (_parameters.in.shmop)
+ {
+ case SHMOP_shmget:
+ result = shmmgr.shmget (_parameters.out.shmid,
+ _parameters.in.key, _parameters.in.size,
+ _parameters.in.shmflg,
+ _parameters.in.uid, _parameters.in.gid,
+ client);
+ break;
+
+ case SHMOP_shmat:
+ result = shmmgr.shmat (_parameters.out.hFileMap,
+ _parameters.in.shmid, _parameters.in.shmflg,
+ client);
+ break;
+
+ case SHMOP_shmdt:
+ result = shmmgr.shmdt (_parameters.in.shmid, client);
+ break;
+
+ case SHMOP_shmctl:
+ result = shmmgr.shmctl (_parameters.out.shmid,
+ _parameters.out.ds, _parameters.out.shminfo,
+ _parameters.out.shm_info,
+ _parameters.in.shmid, _parameters.in.cmd,
+ _parameters.in.ds,
+ client);
+ break;
+ }
+
+ client->release ();
+ conn->revert_to_self ();
+
+ if (result < 0)
+ {
+ error_code (-result);
+ msglen (0);
+ }
+ else
+ msglen (sizeof (_parameters.out));
}
diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h
index f1dcaa5..b1ff353 100644
--- a/winsup/cygwin/cygserver_shm.h
+++ b/winsup/cygwin/cygserver_shm.h
@@ -1,7 +1,9 @@
-/* cygserver_shm.h
+/* cygserver_shm.h: Single unix specification IPC interface for Cygwin.
- Copyright 2001, 2002 Red Hat Inc.
- Written by Robert Collins <rbtcollins@hotmail.com>
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
@@ -9,84 +11,137 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
+#ifndef __CYGSERVER_SHM_H__
+#define __CYGSERVER_SHM_H__
+
#include <sys/types.h>
-#include "cygwin/cygserver_transport.h"
+#include <cygwin/shm.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "cygserver_ipc.h"
+
#include "cygwin/cygserver.h"
-#define SHM_CREATE 0
-#define SHM_REATTACH 1
-#define SHM_ATTACH 2
-#define SHM_DETACH 3
-#define SHM_DEL 4
+/*---------------------------------------------------------------------------*
+ * Values for the shminfo entries.
+ *
+ * Nb. The values are segregated between two enums so that the `small'
+ * values aren't promoted to `unsigned long' equivalents.
+ *---------------------------------------------------------------------------*/
+enum
+ {
+ SHMMAX = ULONG_MAX,
+ SHMSEG = ULONG_MAX,
+ SHMALL = ULONG_MAX
+ };
+
+enum
+ {
+ SHMMIN = 1,
+ SHMMNI = IPCMNI // Must be <= IPCMNI.
+ };
+
+/*---------------------------------------------------------------------------*
+ * class client_request_shm
+ *---------------------------------------------------------------------------*/
-class client_request_shm : public client_request
-{
- public:
#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, process_cache *cache);
+class transport_layer_base;
+class process_cache;
#endif
- client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t);
- client_request_shm ();
- client_request_shm (int, int, pid_t);
- client_request_shm (int, int);
- union {
- struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in;
- struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out;
- } parameters;
-};
-#ifndef __INSIDE_CYGWIN__
-class shm_cleanup : cleanup_routine
+class client_request_shm : public client_request
{
+ friend class client_request;
+
public:
- virtual void cleanup (long winpid);
-};
+ enum shmop_t
+ {
+ SHMOP_shmat,
+ SHMOP_shmctl,
+ SHMOP_shmdt,
+ SHMOP_shmget
+ };
+
+#ifdef __INSIDE_CYGWIN__
+ client_request_shm (int shmid, int shmflg); // shmat
+ client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl
+ client_request_shm (int shmid); // shmdt
+ client_request_shm (key_t, size_t, int shmflg); // shmget
#endif
-#if 0
-class _shmattach {
-public:
- void *data;
- class _shmattach *next;
-};
-class shmid_ds {
-public:
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
- HANDLE filemap;
- HANDLE attachmap;
- void *mapptr;
- class _shmattach *attachhead;
-};
+ // Accessors for out parameters.
-class shmnode {
-public:
- class shmid_ds * shmid;
- class shmnode *next;
- key_t key;
-};
-//....
-struct shmid_ds {
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
-};
+ int shmid () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shmid;
+ }
+
+ HANDLE hFileMap () const
+ {
+ assert (!error_code ());
+ return _parameters.out.hFileMap;
+ }
+
+ const struct shmid_ds & ds () const
+ {
+ assert (!error_code ());
+ return _parameters.out.ds;
+ }
+
+ const struct shminfo & shminfo () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shminfo;
+ }
+
+ const struct shm_info & shm_info () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shm_info;
+ }
-void *shmat(int, const void *, int);
-int shmctl(int, int, struct shmid_ds *);
-int shmdt(const void *);
-int shmget(key_t, size_t, int);
+private:
+ union
+ {
+ struct
+ {
+ shmop_t shmop;
+ key_t key;
+ size_t size;
+ int shmflg;
+ int shmid;
+ int cmd;
+ pid_t cygpid;
+ DWORD winpid;
+ uid_t uid;
+ gid_t gid;
+ struct shmid_ds ds;
+ } in;
+ struct {
+ int shmid;
+ union
+ {
+ HANDLE hFileMap;
+ struct shmid_ds ds;
+ struct shminfo shminfo;
+ struct shm_info shm_info;
+ };
+ } out;
+ } _parameters;
+
+#ifndef __INSIDE_CYGWIN__
+ client_request_shm ();
+#endif
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void serve (transport_layer_base *, process_cache *);
#endif
+};
+
+#endif /* __CYGSERVER_SHM_H__ */
diff --git a/winsup/cygwin/cygserver_transport.cc b/winsup/cygwin/cygserver_transport.cc
index 01f0444..8684a61 100755
--- a/winsup/cygwin/cygserver_transport.cc
+++ b/winsup/cygwin/cygserver_transport.cc
@@ -4,89 +4,48 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
-#include <sys/types.h>
#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
+
+#include "safe_memory.h"
+
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
-/* to allow this to link into cygwin and the .dll, a little magic is needed. */
-#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-#else
-#define debug_printf printf
-#endif
-
/* The factory */
-class transport_layer_base *create_server_transport()
+transport_layer_base *
+create_server_transport ()
{
- transport_layer_base *temp;
- /* currently there is only the base class! */
if (wincap.is_winnt ())
- temp = new transport_layer_pipes ();
+ return safe_new0 (transport_layer_pipes);
else
- temp = new transport_layer_sockets ();
- return temp;
+ return safe_new0 (transport_layer_sockets);
}
-
-transport_layer_base::transport_layer_base ()
-{
- /* should we throw an error of some sort ? */
-}
-
-void
-transport_layer_base::listen ()
-{
-}
-
-class transport_layer_base *
-transport_layer_base::accept ()
-{
- return NULL;
-}
-
-void
-transport_layer_base::close()
-{
-}
-
-ssize_t
-transport_layer_base::read (char *buf, size_t len)
-{
- return 0;
-}
-
-ssize_t
-transport_layer_base::write (char *buf, size_t len)
-{
- return 0;
-}
-
-bool
-transport_layer_base::connect ()
-{
- return false;
-}
+#ifndef __INSIDE_CYGWIN__
void
transport_layer_base::impersonate_client ()
-{
-}
+{}
void
transport_layer_base::revert_to_self ()
-{
-}
+{}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+transport_layer_base::~transport_layer_base ()
+{}
diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc
index f222170..f318a75 100755
--- a/winsup/cygwin/cygserver_transport_pipes.cc
+++ b/winsup/cygwin/cygserver_transport_pipes.cc
@@ -4,207 +4,360 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
-#include <sys/socket.h>
+
+#include <assert.h>
+#include <errno.h>
#include <netdb.h>
-#include "wincap.h"
+#include <pthread.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
-/* to allow this to link into cygwin and the .dll, a little magic is needed. */
-#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-#else
-#define DEBUG 0
-#define debug_printf if (DEBUG) printf
+#ifndef __INSIDE_CYGWIN__
+#include "cygwin/cygserver.h"
#endif
-//SECURITY_DESCRIPTOR transport_layer_pipes::sd;
-//SECURITY_ATTRIBUTES transport_layer_pipes::sec_none_nih, transport_layer_pipes::sec_all_nih;
-//bool transport_layer_pipes::inited = false;
+enum
+ {
+ MAX_WAIT_NAMED_PIPE_RETRY = 64,
+ WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds
+ };
+
+#ifndef __INSIDE_CYGWIN__
+
+static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT;
+static CRITICAL_SECTION pipe_instance_lock;
+static long pipe_instance = 0;
+
+static void
+initialise_pipe_instance_lock ()
+{
+ assert (pipe_instance == 0);
+ InitializeCriticalSection (&pipe_instance_lock);
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+#ifndef __INSIDE_CYGWIN__
-transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
+transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe)
+ : _pipe_name (""),
+ _hPipe (hPipe),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
{
- inited = false; //FIXME: allow inited, sd, all_nih_.. to be static members
- pipe = new_pipe;
- if (inited != true)
- init_security();
-};
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ init_security ();
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
transport_layer_pipes::transport_layer_pipes ()
+ : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"),
+ _hPipe (NULL),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
{
- inited = false;
- pipe = NULL;
- strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
- if (inited != true)
- init_security();
+ init_security ();
}
void
-transport_layer_pipes::init_security()
+transport_layer_pipes::init_security ()
{
+ assert (wincap.has_security ());
+
/* FIXME: pthread_once or equivalent needed */
- InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE);
-
- sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
- sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
- sec_none_nih.lpSecurityDescriptor = NULL;
- sec_all_nih.lpSecurityDescriptor = &sd;
- inited = true;
+
+ InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE);
+
+ _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
+ _sec_all_nih.lpSecurityDescriptor = &_sd;
+ _sec_all_nih.bInheritHandle = FALSE;
}
-void
+transport_layer_pipes::~transport_layer_pipes ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
transport_layer_pipes::listen ()
{
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ _is_listening_endpoint = true;
+
/* no-op */
+ return 0;
}
class transport_layer_pipes *
-transport_layer_pipes::accept ()
+transport_layer_pipes::accept (bool *const recoverable)
{
- if (pipe)
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock);
+
+ EnterCriticalSection (&pipe_instance_lock);
+
+ // Read: http://www.securityinternals.com/research/papers/namedpipe.php
+ // See also the Microsoft security bulletins MS00-053 and MS01-031.
+
+ // FIXME: Remove FILE_CREATE_PIPE_INSTANCE.
+
+ const bool first_instance = (pipe_instance == 0);
+
+ const HANDLE accept_pipe =
+ CreateNamedPipe (_pipe_name,
+ (PIPE_ACCESS_DUPLEX
+ | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)),
+ (PIPE_TYPE_BYTE | PIPE_WAIT),
+ PIPE_UNLIMITED_INSTANCES,
+ 0, 0, 1000,
+ &_sec_all_nih);
+
+ const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE
+ && pipe_instance == 0
+ && GetLastError () == ERROR_ACCESS_DENIED);
+
+ if (accept_pipe != INVALID_HANDLE_VALUE)
+ InterlockedIncrement (&pipe_instance);
+
+ LeaveCriticalSection (&pipe_instance_lock);
+
+ if (duplicate)
{
- debug_printf ("Already have a pipe in this %p\n",this);
+ *recoverable = false;
+ system_printf ("failed to create named pipe: "
+ "is the daemon already running?");
return NULL;
}
- pipe = CreateNamedPipe (pipe_name,
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- 0, 0, 1000,
- &sec_all_nih );
- if (pipe == INVALID_HANDLE_VALUE)
+ if (accept_pipe == INVALID_HANDLE_VALUE)
{
- debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
+ debug_printf ("error creating pipe (%lu).", GetLastError ());
+ *recoverable = true; // FIXME: case analysis?
return NULL;
}
- if ( !ConnectNamedPipe ( pipe, NULL ) &&
- GetLastError () != ERROR_PIPE_CONNECTED)
+ assert (accept_pipe);
+
+ if (!ConnectNamedPipe (accept_pipe, NULL)
+ && GetLastError () != ERROR_PIPE_CONNECTED)
{
- printf ("error connecting to pipe (%lu)\n.", GetLastError ());
- CloseHandle (pipe);
- pipe = NULL;
+ debug_printf ("error connecting to pipe (%lu)\n.", GetLastError ());
+ (void) CloseHandle (accept_pipe);
+ *recoverable = true; // FIXME: case analysis?
return NULL;
}
- transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
- pipe = NULL;
-
- return new_conn;
+ return safe_new (transport_layer_pipes, accept_pipe);
}
+#endif /* !__INSIDE_CYGWIN__ */
+
void
-transport_layer_pipes::close()
+transport_layer_pipes::close ()
{
- debug_printf ("closing pipe %p\n", pipe);
- if (pipe && pipe != INVALID_HANDLE_VALUE)
+ // verbose: debug_printf ("closing pipe %p", _hPipe);
+
+ if (_hPipe)
{
- FlushFileBuffers (pipe);
- DisconnectNamedPipe (pipe);
- CloseHandle (pipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+#ifndef __INSIDE_CYGWIN__
+
+ if (_is_accepted_endpoint)
+ {
+ (void) FlushFileBuffers (_hPipe); // Blocks until client reads.
+ (void) DisconnectNamedPipe (_hPipe);
+ EnterCriticalSection (&pipe_instance_lock);
+ (void) CloseHandle (_hPipe);
+ assert (pipe_instance > 0);
+ InterlockedDecrement (&pipe_instance);
+ LeaveCriticalSection (&pipe_instance_lock);
+ }
+ else
+ (void) CloseHandle (_hPipe);
+
+#else /* __INSIDE_CYGWIN__ */
+
+ assert (!_is_accepted_endpoint);
+ (void) ForceCloseHandle (_hPipe);
+
+#endif /* __INSIDE_CYGWIN__ */
+
+ _hPipe = NULL;
}
}
ssize_t
-transport_layer_pipes::read (char *buf, size_t len)
+transport_layer_pipes::read (void *const buf, const size_t len)
{
- debug_printf ("reading from pipe %p\n", pipe);
- if (!pipe || pipe == INVALID_HANDLE_VALUE)
- return -1;
+ // verbose: debug_printf ("reading from pipe %p", _hPipe);
- DWORD bytes_read;
- DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
- if (!rc)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!ReadFile (_hPipe, buf, len, &count, NULL))
{
- debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
+ debug_printf ("error reading from pipe (%lu)", GetLastError ());
+ set_errno (EINVAL); // FIXME?
return -1;
}
- return bytes_read;
+
+ return count;
}
ssize_t
-transport_layer_pipes::write (char *buf, size_t len)
+transport_layer_pipes::write (void *const buf, const size_t len)
{
- debug_printf ("writing to pipe %p\n", pipe);
- DWORD bytes_written, rc;
- if (!pipe || pipe == INVALID_HANDLE_VALUE)
- return -1;
+ // verbose: debug_printf ("writing to pipe %p", _hPipe);
- rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
- if (!rc)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!WriteFile (_hPipe, buf, len, &count, NULL))
{
- debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
+ debug_printf ("error writing to pipe, error = %lu", GetLastError ());
+ set_errno (EINVAL); // FIXME?
return -1;
}
- return bytes_written;
+
+ return count;
}
-bool
+/*
+ * This routine holds a static variable, assume_cygserver, that is set
+ * if the transport has good reason to think that cygserver is
+ * running, i.e. if if successfully connected to it with the previous
+ * attempt. If this is set, the code tries a lot harder to get a
+ * connection, making the assumption that any failures are just
+ * congestion and overloading problems.
+ */
+
+int
transport_layer_pipes::connect ()
{
- if (pipe && pipe != INVALID_HANDLE_VALUE)
- {
- debug_printf ("Already have a pipe in this %p\n",this);
- return false;
- }
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
- while (1)
+ static bool assume_cygserver = false;
+
+ BOOL rc = TRUE;
+ int retries = 0;
+
+ while (rc)
{
- 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)
- /* got the pipe */
- return true;
-
- if (GetLastError () != ERROR_PIPE_BUSY)
+ _hPipe = CreateFile (_pipe_name,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &_sec_all_nih,
+ OPEN_EXISTING,
+ SECURITY_IMPERSONATION,
+ NULL);
+
+ if (_hPipe != INVALID_HANDLE_VALUE)
+ {
+ assert (_hPipe);
+#ifdef __INSIDE_CYGWIN__
+ ProtectHandle (_hPipe);
+#endif
+ assume_cygserver = true;
+ return 0;
+ }
+
+ _hPipe = NULL;
+
+ if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY)
{
- debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
- pipe = NULL;
- return false;
+ debug_printf ("Error opening the pipe (%lu)", GetLastError ());
+ return -1;
}
- if (!WaitNamedPipe (pipe_name, 20000))
- debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () );
- /* We loop here, because the pipe exists but is busy. If it doesn't exist
- * the != ERROR_PIPE_BUSY will catch it.
+
+ /* Note: `If no instances of the specified named pipe exist, the
+ * WaitNamedPipe function returns immediately, regardless of the
+ * time-out value.' Thus the explicit Sleep if the call fails
+ * with ERROR_FILE_NOT_FOUND.
*/
+ while (retries != MAX_WAIT_NAMED_PIPE_RETRY
+ && !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT)))
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND)
+ Sleep (0); // Give the server a chance.
+
+ retries += 1;
+ }
}
+
+ assert (retries == MAX_WAIT_NAMED_PIPE_RETRY);
+
+ system_printf ("lost connection to cygserver, error = %lu",
+ GetLastError ());
+
+ assume_cygserver = false;
+
+ return -1;
}
+#ifndef __INSIDE_CYGWIN__
+
void
transport_layer_pipes::impersonate_client ()
{
- debug_printf ("impersonating pipe %p\n", pipe);
- if (pipe && pipe != INVALID_HANDLE_VALUE)
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (_is_accepted_endpoint);
+
+ // verbose: debug_printf ("impersonating pipe %p", _hPipe);
+ if (_hPipe)
{
- BOOL rv = ImpersonateNamedPipeClient (pipe);
- if (!rv)
- debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ if (!ImpersonateNamedPipeClient (_hPipe))
+ debug_printf ("Failed to Impersonate the client, (%lu)",
+ GetLastError ());
}
- debug_printf("I am who you are\n");
+ // verbose: debug_printf ("I am who you are");
}
void
transport_layer_pipes::revert_to_self ()
{
+ assert (_is_accepted_endpoint);
+
RevertToSelf ();
- debug_printf("I am who I yam\n");
+ // verbose: debug_printf ("I am who I yam");
}
+#endif /* !__INSIDE_CYGWIN__ */
diff --git a/winsup/cygwin/cygserver_transport_sockets.cc b/winsup/cygwin/cygserver_transport_sockets.cc
index a3a98b3..6ade14b 100755
--- a/winsup/cygwin/cygserver_transport_sockets.cc
+++ b/winsup/cygwin/cygserver_transport_sockets.cc
@@ -4,128 +4,384 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include "wincap.h"
+
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
-#include "winsup.h"
-extern "C" int
-cygwin_socket (int af, int type, int protocol);
-extern "C" int
-cygwin_connect (int fd,
- const struct sockaddr *name,
- int namelen);
-extern "C" int
-cygwin_accept (int fd, struct sockaddr *peer, int *len);
-extern "C" int
-cygwin_listen (int fd, int backlog);
-extern "C" int
-cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen);
-#else
-#define cygwin_accept(A,B,C) ::accept(A,B,C)
-#define cygwin_socket(A,B,C) ::socket(A,B,C)
-#define cygwin_listen(A,B) ::listen(A,B)
-#define cygwin_bind(A,B,C) ::bind(A,B,C)
-#define cygwin_connect(A,B,C) ::connect(A,B,C)
-#define debug_printf printf
-#endif
+extern "C" int cygwin_accept (int fd, struct sockaddr *, int *len);
+extern "C" int cygwin_bind (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_connect (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_listen (int fd, int backlog);
+extern "C" int cygwin_shutdown (int fd, int how);
+extern "C" int cygwin_socket (int af, int type, int protocol);
+
+#else /* __OUTSIDE_CYGWIN__ */
+
+#define cygwin_accept(A,B,C) ::accept (A,B,C)
+#define cygwin_bind(A,B,C) ::bind (A,B,C)
+#define cygwin_connect(A,B,C) ::connect (A,B,C)
+#define cygwin_listen(A,B) ::listen (A,B)
+#define cygwin_shutdown(A,B) ::shutdown (A,B)
+#define cygwin_socket(A,B,C) ::socket (A,B,C)
+
+#endif /* __OUTSIDE_CYGWIN__ */
-transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
+enum
+ {
+ MAX_CONNECT_RETRY = 64
+ };
+
+transport_layer_sockets::transport_layer_sockets (const int fd)
+ : _fd (fd),
+ _addr_len (0),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
{
- /* This may not be needed in this constructor - it's only used
- * when creating a connection via bind or connect
- */
- sockdetails.sa_family = AF_UNIX;
- strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
- sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
-};
-
-transport_layer_sockets::transport_layer_sockets (): fd (-1)
+ assert (_fd != -1);
+
+ memset (&_addr, '\0', sizeof (_addr));
+}
+
+transport_layer_sockets::transport_layer_sockets ()
+ : _fd (-1),
+ _addr_len (0),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
{
- sockdetails.sa_family = AF_UNIX;
- strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
- sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
+ memset (&_addr, '\0', sizeof (_addr));
+
+ _addr.sun_family = AF_UNIX;
+ strcpy (_addr.sun_path, "/tmp/cygdaemo"); // FIXME: $TMP?
+ _addr_len = SUN_LEN (&_addr);
}
-void
+transport_layer_sockets::~transport_layer_sockets ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
transport_layer_sockets::listen ()
{
- /* we want a thread pool based approach. */
- if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0)
- printf ("Socket not created error %d\n", errno);
- if (cygwin_bind(fd, &sockdetails, sdlen))
- printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno);
- if (cygwin_listen(fd, 5) < 0)
- printf ("And the OS just isn't listening, all it says is %d\n", errno);
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ debug_printf ("listen () [this = %p]", this);
+
+ struct stat sbuf;
+
+ if (stat (_addr.sun_path, &sbuf) == -1)
+ {
+ if (errno != ENOENT)
+ {
+ system_printf ("cannot access socket file `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+ }
+ else if (S_ISSOCK (sbuf.st_mode))
+ {
+ // The socket already exists: is a duplicate cygserver running?
+
+ const int newfd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (newfd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (newfd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ system_printf ("the daemon is already running");
+ (void) cygwin_shutdown (newfd, SHUT_WR);
+ char buf[BUFSIZ];
+ while (::read (newfd, buf, sizeof (buf)) > 0)
+ {}
+ (void) ::close (newfd);
+ return -1;
+ }
+
+ if (unlink (_addr.sun_path) == -1)
+ {
+ system_printf ("failed to remove `%s': %s",
+ _addr.sun_path, strerror (errno));
+ (void) ::close (newfd);
+ return -1;
+ }
+ }
+ else
+ {
+ system_printf ("cannot create socket `%s': File already exists",
+ _addr.sun_path);
+ return -1;
+ }
+
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_bind (_fd, (struct sockaddr *) &_addr, _addr_len) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to bind UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ _is_listening_endpoint = true; // i.e. this really means "have bound".
+
+ if (cygwin_listen (_fd, SOMAXCONN) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to listen on UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ debug_printf ("0 = listen () [this = %p, fd = %d]", this, _fd);
+
+ return 0;
}
class transport_layer_sockets *
-transport_layer_sockets::accept ()
+transport_layer_sockets::accept (bool *const recoverable)
{
- /* FIXME: check we have listened */
- int new_fd;
+ assert (_fd != -1);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ debug_printf ("accept () [this = %p, fd = %d]", this, _fd);
+
+ struct sockaddr_un client_addr;
+ socklen_t client_addr_len = sizeof (client_addr);
- if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
+ const int accept_fd =
+ cygwin_accept (_fd, (struct sockaddr *) &client_addr, &client_addr_len);
+
+ if (accept_fd == -1)
{
- printf ("Nup, could' accept. %d\n",errno);
+ system_printf ("failed to accept connection: %s", strerror (errno));
+ switch (errno)
+ {
+ case ECONNABORTED:
+ case EINTR:
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ case ENOMEM:
+ *recoverable = true;
+ break;
+
+ default:
+ *recoverable = false;
+ break;
+ }
return NULL;
}
- transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
-
- return new_conn;
+ debug_printf ("%d = accept () [this = %p, fd = %d]", accept_fd, this, _fd);
+ return safe_new (transport_layer_sockets, accept_fd);
}
+#endif /* !__INSIDE_CYGWIN__ */
+
void
-transport_layer_sockets::close()
+transport_layer_sockets::close ()
{
- /* FIXME - are we open? */
- ::close (fd);
+ debug_printf ("close () [this = %p, fd = %d]", this, _fd);
+
+ if (_is_listening_endpoint)
+ (void) unlink (_addr.sun_path);
+
+ if (_fd != -1)
+ {
+ (void) cygwin_shutdown (_fd, SHUT_WR);
+ if (!_is_listening_endpoint)
+ {
+ char buf[BUFSIZ];
+ while (::read (_fd, buf, sizeof (buf)) > 0)
+ {}
+ }
+ (void) ::close (_fd);
+ _fd = -1;
+ }
+
+ _is_listening_endpoint = false;
}
ssize_t
-transport_layer_sockets::read (char *buf, size_t len)
+transport_layer_sockets::read (void *const buf, const size_t buf_len)
{
- /* FIXME: are we open? */
- return ::read (fd, buf, len);
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *read_buf = static_cast<char *> (buf);
+ size_t read_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (read_buf_len != 0
+ && (res = ::read (_fd, read_buf, read_buf_len)) > 0)
+ {
+ read_buf += res;
+ read_buf_len -= res;
+
+ assert (read_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - read_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
}
ssize_t
-transport_layer_sockets::write (char *buf, size_t len)
+transport_layer_sockets::write (void *const buf, const size_t buf_len)
{
- /* FIXME: are we open? */
- return ::write (fd, buf, len);
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *write_buf = static_cast<char *> (buf);
+ size_t write_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (write_buf_len != 0
+ && (res = ::write (_fd, write_buf, write_buf_len)) > 0)
+ {
+ write_buf += res;
+ write_buf_len -= res;
+
+ assert (write_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - write_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
}
-bool
+int
transport_layer_sockets::connect ()
{
- /* are we already connected? */
- if (fd != -1)
- return false;
- fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
- if (cygwin_connect (fd, &sockdetails, sdlen) < 0)
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ static bool assume_cygserver = false;
+
+ debug_printf ("connect () [this = %p]", this);
+
+ for (int retries = 0; retries != MAX_CONNECT_RETRY; retries++)
{
- debug_printf("client connect failure %d\n", errno);
- ::close (fd);
- return false;
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (_fd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ assume_cygserver = true;
+ debug_printf ("0 = connect () [this = %p, fd = %d]", this, _fd);
+ return 0;
+ }
+
+ if (!assume_cygserver || errno != ECONNREFUSED)
+ {
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ (void) ::close (_fd);
+ _fd = -1;
+ return -1;
+ }
+
+ (void) ::close (_fd);
+ _fd = -1;
+ Sleep (0); // Give the server a chance.
}
- return true;
+
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ return -1;
}
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index 52b8202..701fb14 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -34,8 +34,6 @@ details. */
#include "cygwin_version.h"
#include "dll_init.h"
#include "cygthread.h"
-#include "cygwin/cygserver_transport.h"
-#include "cygwin/cygserver.h"
#define MAX_AT_FILE_LEVEL 10
@@ -686,8 +684,6 @@ dll_crt0_1 ()
/* Initialize signal/subprocess handling. */
sigproc_init ();
- cygserver_init ();
-
/* Connect to tty. */
tty_init ();
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index fd83ef7..9c2bb7f 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -24,7 +24,6 @@ details. */
#include "pinfo.h"
#include "cygheap.h"
#include "shared_info.h"
-#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#include "cygthread.h"
@@ -490,8 +489,8 @@ fhandler_tty_slave::open (path_conv *, int flags, mode_t)
HANDLE from_master_local, to_master_local;
if (!wincap.has_security () ||
- cygserver_running!=CYGSERVER_OK ||
- !cygserver_attach_tty ( &from_master_local, &to_master_local))
+ cygserver_running == CYGSERVER_UNAVAIL ||
+ !cygserver_attach_tty (&from_master_local, &to_master_local))
{
termios_printf ("cannot dup handles via server. using old method.");
@@ -547,29 +546,15 @@ fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_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);
+ client_request_attach_tty req ((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)
+ if (req.make_request () == -1 || req.error_code ())
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;
+ *from_master_ptr = req.from_master ();
+ *to_master_ptr = req.to_master ();
return 1;
}
diff --git a/winsup/cygwin/include/cygwin/cygserver.h b/winsup/cygwin/include/cygwin/cygserver.h
index c7a0492..d4ea70f 100755
--- a/winsup/cygwin/include/cygwin/cygserver.h
+++ b/winsup/cygwin/include/cygwin/cygserver.h
@@ -13,123 +13,172 @@ details. */
#ifndef _CYGSERVER_H_
#define _CYGSERVER_H_
-#define MAX_REQUEST_SIZE 128
+#ifdef __GNUC__
+#define CYGSERVER_PACKED __attribute__ ((packed))
+#else
+#define CYGSERVER_PACKED
+#endif
#define CYGWIN_SERVER_VERSION_MAJOR 1
#define CYGWIN_SERVER_VERSION_API 1
#define CYGWIN_SERVER_VERSION_MINOR 0
#define CYGWIN_SERVER_VERSION_PATCH 0
-
typedef enum {
- CYGSERVER_UNKNOWN=0,
- CYGSERVER_OK=1,
- CYGSERVER_DEAD=2
+ CYGSERVER_UNKNOWN = 0,
+ CYGSERVER_OK,
+ CYGSERVER_UNAVAIL
} cygserver_states;
-typedef enum {
- CYGSERVER_REQUEST_INVALID = 0,
- CYGSERVER_REQUEST_GET_VERSION,
- CYGSERVER_REQUEST_ATTACH_TTY,
- CYGSERVER_REQUEST_SHUTDOWN,
- CYGSERVER_REQUEST_SHM_GET,
- CYGSERVER_REQUEST_LAST
-} cygserver_request_code;
-
-class request_header
-{
- public:
- ssize_t cb;
- cygserver_request_code req_id;
- ssize_t error_code;
- request_header (cygserver_request_code id, ssize_t ncb) : cb (ncb), req_id (id), error_code (0) {} ;
-}
-#ifdef __GNUC__
- __attribute__ ((packed))
-#endif
-;
+/*---------------------------------------------------------------------------*
+ * class client_request
+ *---------------------------------------------------------------------------*/
-extern void cygserver_init ();
-
-#define INIT_REQUEST(req,id) \
- (req).header.cb = sizeof (req); \
- (req).header.req_id = id;
+class transport_layer_base;
-struct request_get_version
-{
- DWORD major, api, minor, patch;
-}
-#ifdef __GNUC__
- __attribute__ ((packed))
+#ifndef __INSIDE_CYGWIN__
+class process_cache;
#endif
-;
-struct request_shutdown
+class client_request
{
- int foo;
-}
-#ifdef __GNUC__
- __attribute__ ((packed))
+protected:
+ typedef enum {
+ CYGSERVER_REQUEST_INVALID,
+ CYGSERVER_REQUEST_GET_VERSION,
+ CYGSERVER_REQUEST_SHUTDOWN,
+ CYGSERVER_REQUEST_ATTACH_TTY,
+ CYGSERVER_REQUEST_SHM,
+ CYGSERVER_REQUEST_LAST
+ } request_code_t;
+
+ struct header_t
+ {
+ size_t msglen;
+ union
+ {
+ request_code_t request_code;
+ ssize_t error_code;
+ };
+
+ header_t () {};
+ header_t (request_code_t, size_t);
+ } CYGSERVER_PACKED;
+
+public:
+#ifndef __INSIDE_CYGWIN__
+ static void handle_request (transport_layer_base *, process_cache *);
#endif
-;
-struct request_attach_tty
-{
- DWORD pid, master_pid;
- HANDLE from_master, to_master;
-}
-#ifdef __GNUC__
- __attribute__ ((packed))
-#endif
-;
+ client_request (request_code_t request_code,
+ void *buf = NULL,
+ size_t bufsiz = 0);
+ virtual ~client_request ();
+
+ request_code_t request_code () const { return _header.request_code; }
+
+ ssize_t error_code () const { return _header.error_code; };
+ void error_code (ssize_t error_code) { _header.error_code = error_code; };
+
+ size_t msglen () const { return _header.msglen; };
+ void msglen (size_t len) { _header.msglen = len; };
+
+ int make_request ();
+
+protected:
+ virtual void send (transport_layer_base *);
+
+private:
+ header_t _header;
+ void * const _buf;
+ const size_t _buflen;
-class client_request
-{
- public:
- client_request (cygserver_request_code id, ssize_t data_size);
- virtual void send (transport_layer_base *conn);
#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, class process_cache *cache);
+ void handle (transport_layer_base *, process_cache *);
+ virtual void serve (transport_layer_base *, process_cache *) = 0;
#endif
- virtual operator struct request_header ();
- cygserver_request_code req_id () {return header.req_id;};
- virtual ~client_request();
- request_header header;
- char *buffer;
};
+/*---------------------------------------------------------------------------*
+ * class client_request_get_version
+ *---------------------------------------------------------------------------*/
+
class client_request_get_version : public client_request
{
- public:
+private:
+ struct request_get_version
+ {
+ DWORD major, api, minor, patch;
+ } CYGSERVER_PACKED;
+
+public:
+ client_request_get_version ();
+ bool check_version () const;
+
+private:
+ struct request_get_version version;
+
#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, class process_cache *cache);
+ virtual void serve (transport_layer_base *, process_cache *);
#endif
- client_request_get_version::client_request_get_version();
- struct request_get_version version;
};
+/*---------------------------------------------------------------------------*
+ * class client_request_shutdown
+ *
+ * Nb. This whole class is only !__INSIDE_CYGWIN__ since it is used
+ * solely by cygserver itself.
+ *---------------------------------------------------------------------------*/
+
+#ifndef __INSIDE_CYGWIN__
+
class client_request_shutdown : public client_request
{
- public:
-#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, class process_cache *cache);
-#endif
+public:
client_request_shutdown ();
+
+private:
+ virtual void serve (transport_layer_base *, process_cache *);
};
+#endif /* !__INSIDE_CYGWIN__ */
+
+/*---------------------------------------------------------------------------*
+ * class client_request_attach_tty
+ *---------------------------------------------------------------------------*/
+
class client_request_attach_tty : public client_request
{
- public:
-#ifndef __INSIDE_CYGWIN__
- virtual void serve (transport_layer_base *conn, class process_cache *cache);
-#endif
+private:
+ struct request_attach_tty
+ {
+ DWORD pid, master_pid;
+ HANDLE from_master, to_master;
+ } CYGSERVER_PACKED;
+
+public:
+#ifdef __INSIDE_CYGWIN__
+ client_request_attach_tty (DWORD nmaster_pid,
+ HANDLE nfrom_master, HANDLE nto_master);
+#else
client_request_attach_tty ();
- client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master);
- HANDLE from_master () {return req.from_master;};
- HANDLE to_master () {return req.to_master;};
+#endif
+
+ HANDLE from_master () const { return req.from_master; };
+ HANDLE to_master () const { return req.to_master; };
+
+protected:
+ virtual void send (transport_layer_base *);
+
+private:
struct request_attach_tty req;
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void serve (transport_layer_base *, process_cache *);
+#endif
};
-extern int cygserver_request (client_request *);
+extern bool check_cygserver_available ();
+extern void cygserver_init ();
-#endif /* _CYGSERVER+H+ */
+#endif /* _CYGSERVER_H_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_process.h b/winsup/cygwin/include/cygwin/cygserver_process.h
index c8ff40b..25c634e 100755
--- a/winsup/cygwin/include/cygwin/cygserver_process.h
+++ b/winsup/cygwin/include/cygwin/cygserver_process.h
@@ -4,81 +4,161 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
#ifndef _CYGSERVER_PROCESS_
#define _CYGSERVER_PROCESS_
-/* needs threaded_queue.h */
+#include <assert.h>
-class process_cleanup:public queue_request
+#include "threaded_queue.h"
+
+class process_cleanup : public queue_request
{
public:
+ process_cleanup (class process *const theprocess)
+ : _process (theprocess)
+ {
+ assert (_process);
+ }
+
+ virtual ~process_cleanup ();
+
virtual void process ();
- process_cleanup (class process *nprocess) : theprocess (nprocess) {};
+
private:
- class process * theprocess;
+ class process *const _process;
};
-class process_process_param:public queue_process_param
-{
- class process_cache *cache;
-public:
- DWORD request_loop ();
- process_process_param ():queue_process_param (true) {};
-};
+class process;
class cleanup_routine
{
+ friend class process;
+
public:
- cleanup_routine () : next (NULL) {};
- class cleanup_routine * next;
+ cleanup_routine (void *const key)
+ : _key (key),
+ _next (NULL)
+ {}
+
+ virtual ~cleanup_routine ();
+
+ bool operator== (const cleanup_routine &rhs) const
+ {
+ return _key == rhs._key;
+ }
+
+ void *key () const { return _key; }
+
/* MUST BE SYNCHRONOUS */
- virtual void cleanup (long winpid);
+ virtual void cleanup (class process *) = 0;
+
+private:
+ void *const _key;
+ cleanup_routine *_next;
};
+class process_cache;
+
class process
{
+ friend class process_cache;
+ friend class process_cleanup;
+
public:
- HANDLE handle ();
- long winpid;
- process (long);
+ process (pid_t cygpid, DWORD winpid);
~process ();
- DWORD exit_code ();
- class process * next;
- long refcount;
- bool add_cleanup_routine (class cleanup_routine *);
- void cleanup ();
+
+ pid_t cygpid () const { return _cygpid; }
+ DWORD winpid () const { return _winpid; }
+ HANDLE handle () const { return _hProcess; }
+
+ bool is_active () const { return _exit_status == STILL_ACTIVE; }
+
+ void hold () { EnterCriticalSection (&_access); }
+ void release () { LeaveCriticalSection (&_access); }
+
+ bool add (cleanup_routine *);
+ bool remove (const cleanup_routine *);
+
private:
+ const pid_t _cygpid;
+ const DWORD _winpid;
+ HANDLE _hProcess;
+ long _cleaning_up;
+ DWORD _exit_status; // Set in the constructor and in exit_code ().
+ cleanup_routine *_routines_head;
/* used to prevent races-on-delete */
- CRITICAL_SECTION access;
- volatile long cleaning_up;
- class cleanup_routine *head;
- HANDLE thehandle;
- DWORD _exit_status;
+ CRITICAL_SECTION _access;
+ class process *_next;
+
+ DWORD check_exit_code ();
+ void cleanup ();
};
-class process_cache:public threaded_queue
+class process_cache
{
+ // Number of special (i.e., non-process) handles in _wait_array.
+ // See wait_for_processes () and sync_wait_array () for details.
+ enum {
+ SPECIALS_COUNT = 2
+ };
+
+ class submission_loop : public queue_submission_loop
+ {
+ public:
+ submission_loop (process_cache *const cache, threaded_queue *const queue)
+ : queue_submission_loop (queue, true),
+ _cache (cache)
+ {
+ assert (_cache);
+ }
+
+ private:
+ process_cache *const _cache;
+
+ virtual void request_loop ();
+ };
+
+ friend class submission_loop;
+
public:
process_cache (unsigned int initial_workers);
- virtual ~ process_cache ();
- class process *process (long);
- /* remove a process from the cache */
- int handle_snapshot (HANDLE *, class process **, ssize_t, int);
- void remove_process (class process *);
- /* threaded_queue methods */
- void process_requests ();
- HANDLE cache_add_trigger;
+ ~process_cache ();
+
+ class process *process (pid_t cygpid, DWORD winpid);
+
+ bool running () const { return _queue.running (); }
+
+ bool start () { return _queue.start (); }
+ bool stop () { return _queue.stop (); }
private:
- virtual void add_task (class process *);
- class process *head;
- CRITICAL_SECTION cache_write_access;
+ threaded_queue _queue;
+ submission_loop _submitter;
+
+ size_t _processes_count;
+ class process *_processes_head; // A list sorted by winpid.
+
+ // Access to the _wait_array and related fields is not thread-safe,
+ // since they are used solely by wait_for_processes () and its callees.
+
+ HANDLE _wait_array[MAXIMUM_WAIT_OBJECTS];
+ class process *_process_array[MAXIMUM_WAIT_OBJECTS];
+
+ HANDLE _cache_add_trigger; // Actually both add and remove.
+ CRITICAL_SECTION _cache_write_access; // Actually both read and write access.
+
+ void wait_for_processes (HANDLE interrupt);
+ size_t sync_wait_array (HANDLE interrupt);
+ void check_and_remove_process (const size_t index);
+
+ class process *find (DWORD winpid, class process **previous = NULL);
};
#endif /* _CYGSERVER_PROCESS_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport.h b/winsup/cygwin/include/cygwin/cygserver_transport.h
index 493c9b5..915f35e 100755
--- a/winsup/cygwin/include/cygwin/cygserver_transport.h
+++ b/winsup/cygwin/include/cygwin/cygserver_transport.h
@@ -1,32 +1,39 @@
-/* cygserver.cc
+/* cygserver_transport.h
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
#ifndef _CYGSERVER_TRANSPORT_
#define _CYGSERVER_TRANSPORT_
-class transport_layer_base *create_server_transport();
-/* the base class does nothing. */
+class transport_layer_base *create_server_transport ();
+
class transport_layer_base
{
- public:
- virtual void listen ();
- virtual class transport_layer_base * accept ();
- virtual void close ();
- virtual ssize_t read (char *buf, size_t len);
- virtual ssize_t write (char *buf, size_t len);
- virtual bool connect();
- virtual void impersonate_client ();
- virtual void revert_to_self ();
- transport_layer_base ();
+public:
+#ifndef __INSIDE_CYGWIN__
+ virtual int listen () = 0;
+ virtual class transport_layer_base *accept (bool *recoverable) = 0;
+#endif
+
+ virtual void close () = 0;
+ virtual ssize_t read (void *buf, size_t len) = 0;
+ virtual ssize_t write (void *buf, size_t len) = 0;
+ virtual int connect () = 0;
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void impersonate_client ();
+ virtual void revert_to_self ();
+#endif
+
+ virtual ~transport_layer_base ();
};
#endif /* _CYGSERVER_TRANSPORT_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h b/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h
index 9ebeee2..4bea2eb 100755
--- a/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h
+++ b/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h
@@ -1,39 +1,53 @@
-/* cygserver.cc
+/* cygserver_transport_pipes.h
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
#ifndef _CYGSERVER_TRANSPORT_PIPES_
#define _CYGSERVER_TRANSPORT_PIPES_
+
/* Named pipes based transport, for security on NT */
class transport_layer_pipes : public transport_layer_base
{
- public:
- virtual void listen ();
- virtual class transport_layer_pipes * accept ();
- virtual void close ();
- virtual ssize_t read (char *buf, size_t len);
- virtual ssize_t write (char *buf, size_t len);
- virtual bool connect();
- virtual void impersonate_client ();
- virtual void revert_to_self ();
- transport_layer_pipes ();
-
- private:
- /* for pipe based communications */
- void init_security ();
- SECURITY_DESCRIPTOR sd;
- SECURITY_ATTRIBUTES sec_none_nih, sec_all_nih;
- char pipe_name [MAX_PATH];
- HANDLE pipe;
- bool inited;
- transport_layer_pipes (HANDLE new_pipe);
+public:
+#ifndef __INSIDE_CYGWIN__
+ virtual int listen ();
+ virtual class transport_layer_pipes *accept (bool *recoverable);
+#endif
+
+ virtual void close ();
+ virtual ssize_t read (void *buf, size_t len);
+ virtual ssize_t write (void *buf, size_t len);
+ virtual int connect ();
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void impersonate_client ();
+ virtual void revert_to_self ();
+#endif
+
+ transport_layer_pipes ();
+ virtual ~transport_layer_pipes ();
+
+private:
+ /* for pipe based communications */
+ void init_security ();
+
+ //FIXME: allow inited, sd, all_nih_.. to be static members
+ SECURITY_DESCRIPTOR _sd;
+ SECURITY_ATTRIBUTES _sec_all_nih;
+ const char *const _pipe_name;
+ HANDLE _hPipe;
+ const bool _is_accepted_endpoint;
+ bool _is_listening_endpoint;
+
+ transport_layer_pipes (HANDLE hPipe);
};
+
#endif /* _CYGSERVER_TRANSPORT_PIPES_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h b/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h
index 9881ca2..d960f9c 100755
--- a/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h
+++ b/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h
@@ -1,33 +1,46 @@
-/* cygserver.cc
+/* cygserver_transport_sockets.h
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
#ifndef _CYGSERVER_TRANSPORT_SOCKETS_
#define _CYGSERVER_TRANSPORT_SOCKETS_
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
class transport_layer_sockets : public transport_layer_base
{
- public:
- virtual void listen ();
- virtual class transport_layer_sockets * accept ();
- virtual void close ();
- virtual ssize_t read (char *buf, size_t len);
- virtual ssize_t write (char *buf, size_t len);
- virtual bool connect();
- transport_layer_sockets ();
-
- private:
- /* for socket based communications */
- int fd;
- struct sockaddr sockdetails;
- int sdlen;
- transport_layer_sockets (int newfd);
+public:
+#ifndef __INSIDE_CYGWIN__
+ virtual int listen ();
+ virtual class transport_layer_sockets *accept (bool *recoverable);
+#endif
+
+ virtual void close ();
+ virtual ssize_t read (void *buf, size_t len);
+ virtual ssize_t write (void *buf, size_t len);
+ virtual int connect ();
+
+ transport_layer_sockets ();
+ virtual ~transport_layer_sockets ();
+
+private:
+ /* for socket based communications */
+ int _fd;
+ struct sockaddr_un _addr;
+ socklen_t _addr_len;
+ const bool _is_accepted_endpoint;
+ bool _is_listening_endpoint;
+
+ transport_layer_sockets (int fd);
};
+
#endif /* _CYGSERVER_TRANSPORT_SOCKETS_ */
diff --git a/winsup/cygwin/include/cygwin/ipc.h b/winsup/cygwin/include/cygwin/ipc.h
index c718a17..8a88a10 100644
--- a/winsup/cygwin/include/cygwin/ipc.h
+++ b/winsup/cygwin/include/cygwin/ipc.h
@@ -1,6 +1,6 @@
/* sys/ipc.h
- Copyright 2001 Red Hat Inc.
+ Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
@@ -9,45 +9,45 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
+#ifndef _SYS_IPC_H
+#define _SYS_IPC_H
+
#ifdef __cplusplus
extern "C"
{
#endif
-#ifndef _SYS_IPC_H
-#define _SYS_IPC_H
-
-/* sys/types must be included before sys/ipc.h. We aren't meant to automatically
- * include it however
+struct ipc_perm
+{
+ uid_t uid; /* Owner's user ID. */
+ gid_t gid; /* Owner's group ID. */
+ uid_t cuid; /* Creator's user ID. */
+ gid_t cgid; /* Creator's group ID. */
+ mode_t mode; /* Read/write permission. */
+ key_t key;
+};
+
+/* Mode bits:
*/
+#define IPC_CREAT 0x0200 /* Create entry if key does not exist. */
+#define IPC_EXCL 0x0400 /* Fail if key exists. */
+#define IPC_NOWAIT 0x0800 /* Error if request must wait. */
-struct ipc_perm {
- uid_t uid;
- gid_t gid;
- uid_t cuid;
- gid_t cgid;
- mode_t mode;
-};
-
-/* the mode flags used with the _get functions use the low order 9 bits for a mode
- * request
+/* Keys:
*/
-#define IPC_CREAT 0x0200
-#define IPC_EXCL 0x0400
-#define IPC_NOWAIT 0x0800
-
-/* this is a value that will _never_ be a valid key from ftok */
-#define IPC_PRIVATE -2
+#define IPC_PRIVATE ((key_t) 0) /* Private key. */
-/* ctl commands 1000-1fff is ipc reserved */
-#define IPC_RMID 0x1003
-#define IPC_SET 0x1002
-#define IPC_STAT 0x1001
+/* Control commands:
+ */
+#define IPC_RMID 0x1000 /* Remove identifier. */
+#define IPC_SET 0x1001 /* Set options. */
+#define IPC_STAT 0x1002 /* Get options. */
+#define IPC_INFO 0x1003 /* For ipcs(8). */
-key_t ftok(const char *, int);
-
-#endif /* _SYS_IPC_H */
+key_t ftok (const char *path, int id);
#ifdef __cplusplus
}
#endif
+
+#endif /* _SYS_IPC_H */
diff --git a/winsup/cygwin/include/cygwin/msg.h b/winsup/cygwin/include/cygwin/msg.h
new file mode 100644
index 0000000..14e89f5
--- /dev/null
+++ b/winsup/cygwin/include/cygwin/msg.h
@@ -0,0 +1,92 @@
+/* sys/msg.h
+
+ Copyright 2002 Red Hat Inc.
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+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. */
+
+#ifndef _SYS_MSG_H
+#define _SYS_MSG_H
+
+#include <cygwin/ipc.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Message operation flags:
+ */
+#define MSG_NOERROR 0x01 /* No error if big message. */
+
+/* Command definitions for the semctl () function:
+ */
+#define MSG_STAT 0x2000 /* For ipcs(8) */
+#define MSG_INFO 0x2001 /* For ipcs(8) */
+
+/* Used for the number of messages in the message queue.
+ */
+typedef long int msgqnum_t;
+
+/* Used for the number of bytes allowed in a message queue.
+ */
+typedef long int msglen_t;
+
+struct msqid_ds
+{
+ struct ipc_perm msg_perm; /* Operation permission structure. */
+ msglen_t msg_cbytes; /* Number of bytes currently on queue. */
+ msgqnum_t msg_qnum; /* Number of messages currently on queue. */
+ msglen_t msg_qbytes; /* Maximum number of bytes allowed on queue. */
+ pid_t msg_lspid; /* Process ID of last msgsnd (). */
+ pid_t msg_lrpid; /* Process ID of last msgrcv (). */
+ timestruc_t msg_stim; /* Time of last msgsnd (). */
+ timestruc_t msg_rtim; /* Time of last msgrcv (). */
+ timestruc_t msg_ctim; /* Time of last change. */
+ long msg_spare4[2];
+};
+
+#define msg_stime msg_stim.tv_sec
+#define msg_rtime msg_rtim.tv_sec
+#define msg_ctime msg_ctim.tv_sec
+
+/* Buffer type for msgctl (IPC_INFO, ...) as used by ipcs(8).
+ */
+struct msginfo
+{
+ unsigned long msgpool; /* Maximum number of message bytes,
+ system wide. */
+ unsigned long msgmax; /* Maximum number of bytes per
+ message. */
+ unsigned long msgmnb; /* Maximum number of bytes on any one
+ message queue. */
+ unsigned long msgmni; /* Maximum number of message queues,
+ system wide. */
+ unsigned long msgtql; /* Maximum number of messages, system
+ wide. */
+ unsigned long msg_spare[4];
+};
+
+/* Buffer type for msgctl (MSG_INFO, ...) as used by ipcs(8).
+ */
+struct msg_info
+{
+ unsigned long msg_ids; /* Number of allocated queues. */
+ unsigned long msg_num; /* Number of messages, system wide. */
+ unsigned long msg_tot; /* Size in bytes of messages, system wide. */
+};
+
+int msgctl (int msqid, int cmd, struct msqid_ds *buf);
+int msgget (key_t key, int msgflg);
+ssize_t msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
+int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_MSG_H */
diff --git a/winsup/cygwin/include/cygwin/sem.h b/winsup/cygwin/include/cygwin/sem.h
new file mode 100644
index 0000000..a3ece9f
--- /dev/null
+++ b/winsup/cygwin/include/cygwin/sem.h
@@ -0,0 +1,95 @@
+/* sys/sem.h
+
+ Copyright 2002 Red Hat Inc.
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+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. */
+
+#ifndef _SYS_SEM_H
+#define _SYS_SEM_H
+
+#include <cygwin/ipc.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Semaphore operation flags:
+ */
+#define SEM_UNDO /* Set up adjust on exit entry. */
+
+/* Command definitions for the semctl () function:
+ */
+#define GETNCNT 0x3000 /* Get semncnt. */
+#define GETPID 0x3001 /* Get sempid. */
+#define GETVAL 0x3002 /* Get semval. */
+#define GETALL 0x3003 /* Get all cases of semval. */
+#define GETZCNT 0x3004 /* Get semzcnt. */
+#define SETVAL 0x3005 /* Set semval. */
+#define SETALL 0x3006 /* Set all cases of semval. */
+
+#define SEM_STAT 0x3010 /* For ipcs(8). */
+#define SEM_INFO 0x3011 /* For ipcs(8). */
+
+struct semid_ds
+{
+ struct ipc_perm sem_perm; /* Operation permission structure. */
+ unsigned short sem_nsems; /* Number of semaphores in set. */
+ timestruc_t sem_otim; /* Last semop () time. */
+ timestruc_t sem_ctim; /* Last time changed by semctl (). */
+ long sem_spare4[2];
+};
+
+#define sem_otime sem_otim.tv_sec
+#define sem_ctime sem_ctim.tv_sec
+
+struct sembuf
+{
+ unsigned short sem_num; /* Semaphore number. */
+ short sem_op; /* Semaphore operation. */
+ short sem_flg; /* Operation flags. */
+};
+
+/* Buffer type for semctl (IPC_INFO, ...) as used by ipcs(8).
+ */
+struct seminfo
+{
+ unsigned long semmni; /* Maximum number of unique semaphore
+ sets, system wide. */
+ unsigned long semmns; /* Maximum number of semaphores,
+ system wide. */
+ unsigned long semmsl; /* Maximum number of semaphores per
+ semaphore set. */
+ unsigned long semopm; /* Maximum number of operations per
+ semop call. */
+ unsigned long semmnu; /* Maximum number of undo structures,
+ system wide. */
+ unsigned long semume; /* Maximum number of undo entries per
+ undo structure. */
+ unsigned long semvmx; /* Maximum semaphore value. */
+ unsigned long semaem; /* Maximum adjust-on-exit value. */
+ unsigned long sem_spare[4];
+};
+
+/* Buffer type for semctl (SEM_INFO, ...) as used by ipcs(8).
+ */
+struct sem_info
+{
+ unsigned long sem_ids; /* Number of allocated semaphore sets. */
+ unsigned long sem_num; /* Number of allocated semaphores. */
+};
+
+int semctl (int semid, int semnum, int cmd, ...);
+int semget (key_t key, int nsems, int semflg);
+int semop (int semid, struct sembuf *sops, size_t nsops);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SEM_H */
diff --git a/winsup/cygwin/include/cygwin/shm.h b/winsup/cygwin/include/cygwin/shm.h
index eb037ba..b6b2d44 100644
--- a/winsup/cygwin/include/cygwin/shm.h
+++ b/winsup/cygwin/include/cygwin/shm.h
@@ -1,6 +1,6 @@
/* sys/shm.h
- Copyright 2001 Red Hat Inc.
+ Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
@@ -9,81 +9,86 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
+#ifndef _SYS_SHM_H
+#define _SYS_SHM_H
+
+#include <cygwin/ipc.h>
+
#ifdef __cplusplus
extern "C"
{
#endif
-#ifndef _SYS_SHM_H
-#define _SYS_SHM_H
-
-#include <cygwin/ipc.h>
+/* 64 Kb was hardcoded for x86. MS states this may change, but we need
+ * it in the header file.
+ */
+#define SHMLBA 65536 /* Segment low boundary address multiple. */
-#define SHM_RDONLY 1
-/* 64 Kb was hardcoded for x86. MS states this may change, but we need it in the header
- * file.
+/* Shared memory operation flags:
*/
-#define SHMLBA 65536
-#define SHM_RND 1
+#define SHM_RDONLY 0x01 /* Attach read-only (else read-write). */
+#define SHM_RND 0x02 /* Round attach address to SHMLBA. */
-typedef long int shmatt_t;
+/* Command definitions for the semctl () function:
+ */
+#define SHM_STAT 0x4000 /* For ipcs(8) */
+#define SHM_INFO 0x4001 /* For ipcs(8) */
-#if defined(__INSIDE_CYGWIN__) && defined(__cplusplus)
+/* Unsigned integer used for the number of current attaches.
+ */
+typedef unsigned int shmatt_t;
-class _shmattach {
-public:
- void *data;
- int shmflg;
- class _shmattach *next;
+struct shmid_ds
+{
+ struct ipc_perm shm_perm; /* Operation permission structure. */
+ size_t shm_segsz; /* Size of segment in bytes. */
+ pid_t shm_lpid; /* Process ID of last operation. */
+ pid_t shm_cpid; /* Process ID of creator. */
+ shmatt_t shm_nattch; /* Number of current attaches. */
+ timestruc_t shm_atim; /* Time of last shmat (). */
+ timestruc_t shm_dtim; /* Time of last shmdt (). */
+ timestruc_t shm_ctim; /* Time of last change by shmctl (). */
+ long shm_spare4[2];
};
-class shmid_ds {
-public:
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
- void *mapptr;
-};
+#define shm_atime shm_atim.tv_sec
+#define shm_dtime shm_dtim.tv_sec
+#define shm_ctime shm_ctim.tv_sec
-class shmnode {
-public:
- class shmid_ds * shmds;
- int shm_id;
- class shmnode *next;
- key_t key;
- HANDLE filemap;
- HANDLE attachmap;
- class _shmattach *attachhead;
+/* Buffer type for shmctl (IPC_INFO, ...) as used by ipcs(8).
+ */
+struct shminfo
+{
+ unsigned long shmmax; /* Maximum size in bytes of a shared
+ memory segment. */
+ unsigned long shmmin; /* Minimum size in bytes of a shared
+ memory segment. */
+ unsigned long shmmni; /* Maximum number of shared memory
+ segments, system wide. */
+ unsigned long shmseg; /* Maximum number of shared memory
+ segments attached per process. */
+ unsigned long shmall; /* Maximum number of bytes of shared
+ memory, system wide. */
+ unsigned long shm_spare[4];
};
-#else
-/* this is what we return when queried. It has no bitwise correspondence
- * the internal structures
+/* Buffer type for shmctl (SHM_INFO, ...) as used by ipcs(8).
*/
-struct shmid_ds {
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- pid_t shm_lpid;
- pid_t shm_cpid;
- shmatt_t shm_nattch;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
+struct shm_info
+{
+ unsigned long shm_ids; /* Number of allocated segments. */
+ unsigned long shm_tot; /* Size in bytes of allocated segments. */
+ unsigned long shm_atts; /* Number of attached segments, system
+ wide. */
};
-#endif /* __INSIDE_CYGWIN__ */
-
-void *shmat(int, const void *, int);
-int shmctl(int, int, struct shmid_ds *);
-int shmdt(const void *);
-int shmget(key_t, size_t, int);
-#endif /* _SYS_SHM_H */
+void *shmat (int shmid, const void *shmaddr, int shmflg);
+int shmctl (int shmid, int cmd, struct shmid_ds *buf);
+int shmdt (const void *shmaddr);
+int shmget (key_t key, size_t size, int shmflg);
#ifdef __cplusplus
}
#endif
+
+#endif /* _SYS_SHM_H */
diff --git a/winsup/cygwin/msg.cc b/winsup/cygwin/msg.cc
new file mode 100644
index 0000000..c76fd8e
--- /dev/null
+++ b/winsup/cygwin/msg.cc
@@ -0,0 +1,48 @@
+/* msg.cc: Single unix specification IPC interface for Cygwin.
+
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+
+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 <sys/types.h>
+#include <cygwin/msg.h>
+
+#include <errno.h>
+
+#include "cygerrno.h"
+
+extern "C" int
+msgctl (int msqid, int cmd, struct msqid_ds *buf)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+msgget (key_t key, int msgflg)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" ssize_t
+msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
diff --git a/winsup/cygwin/safe_memory.h b/winsup/cygwin/safe_memory.h
new file mode 100644
index 0000000..b5f3d95
--- /dev/null
+++ b/winsup/cygwin/safe_memory.h
@@ -0,0 +1,53 @@
+/* safe_memory.h
+
+ Copyright 2002 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. */
+
+#ifndef __SAFE_MEMORY_H__
+#define __SAFE_MEMORY_H__
+
+/*****************************************************************************/
+
+/* Temporary hack to get around the thread-unsafe new/delete in cygwin
+ * gcc 2.95.3. This should all be binned at the first opportunity,
+ * e.g. gcc 3.1 or sooner.
+ *
+ * The trick here is to do contruction via malloc(3) and then the
+ * placement new operator, and destruction via an explicit call to the
+ * destructor and then free(3).
+ */
+
+#include <stdlib.h>
+
+inline void *operator new (size_t, void *__p) throw () { return __p; }
+
+#define safe_new0(T) (new (malloc (sizeof (T))) T)
+
+#ifdef NEW_MACRO_VARARGS
+
+#define safe_new(T, ...) \
+ (new (malloc (sizeof (T))) T (__VA_ARGS__))
+
+#else /* !NEW_MACRO_VARARGS */
+
+#define safe_new(T, args...) \
+ (new (malloc (sizeof (T))) T (## args))
+
+#endif /* !NEW_MACRO_VARARGS */
+
+template <typename T> void
+safe_delete (T *const object)
+{
+ if (object)
+ {
+ object->~T ();
+ free (object);
+ }
+}
+
+#endif /* __SAFE_MEMORY_H__ */
diff --git a/winsup/cygwin/sem.cc b/winsup/cygwin/sem.cc
new file mode 100644
index 0000000..63aba8e
--- /dev/null
+++ b/winsup/cygwin/sem.cc
@@ -0,0 +1,41 @@
+/* sem.cc: Single unix specification IPC interface for Cygwin.
+
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+
+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 <sys/types.h>
+#include <cygwin/sem.h>
+
+#include <errno.h>
+
+#include "cygerrno.h"
+
+extern "C" int
+semctl (int semid, int semnum, int cmd, ...)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+semget (key_t key, int nsems, int semflg)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+semop (int semid, struct sembuf *sops, size_t nsops)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc
index 400f204..94a86e2 100644
--- a/winsup/cygwin/shm.cc
+++ b/winsup/cygwin/shm.cc
@@ -1,8 +1,9 @@
-/* shm.cc: Single unix specification IPC interface for Cygwin
+/* shm.cc: Single unix specification IPC interface for Cygwin.
-Copyright 2001, 2002 Red Hat, Inc.
+ Copyright 2002 Red Hat, Inc.
-Originally written by Robert Collins <robert.collins@hotmail.com>
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
@@ -11,548 +12,682 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
-#include <sys/stat.h>
+
+#include <sys/types.h>
+
+#include <assert.h>
#include <errno.h>
-#include "cygerrno.h"
-#include <unistd.h>
-#include "security.h"
-#include "fhandler.h"
-#include "path.h"
-#include "dtable.h"
-#include "cygheap.h"
#include <stdio.h>
-#include "thread.h"
-#include <cygwin/shm.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
+#include "safe_memory.h"
+#include "sigproc.h"
+
+#include "cygserver_ipc.h"
#include "cygserver_shm.h"
-// FIXME IS THIS CORRECT
-/* Implementation notes: We use two shared memory regions per key:
- * One for the control structure, and one for the shared memory.
- * While this has a higher overhead tham a single shared area,
- * It allows more flexability. As the entire code is transparent to the user
- * We can merge these in the future should it be needed.
- */
-extern "C" size_t
-getsystemallocgranularity ()
-{
- SYSTEM_INFO sysinfo;
- static size_t buffer_offset = 0;
- if (buffer_offset)
- return buffer_offset;
- GetSystemInfo (&sysinfo);
- buffer_offset = sysinfo.dwAllocationGranularity;
- return buffer_offset;
-}
+/*---------------------------------------------------------------------------*
+ * class client_shmmgr
+ *
+ * A singleton class.
+ *---------------------------------------------------------------------------*/
+
+#define shmmgr (client_shmmgr::instance ())
-client_request_shm::client_request_shm (int ntype, int nshm_id):
-client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+class client_shmmgr
{
- buffer = (char *) &parameters;
- parameters.in.shm_id = nshm_id;
- parameters.in.type = SHM_REATTACH;
- parameters.in.pid = GetCurrentProcessId ();
-}
+private:
+ class segment_t
+ {
+ public:
+ const int shmid;
+ const void *const shmaddr;
+ const int shmflg;
+ HANDLE hFileMap; // Updated by fixup_shms_after_fork ().
+
+ segment_t *next;
+
+ segment_t (const int shmid, const void *const shmaddr, const int shmflg,
+ const HANDLE hFileMap)
+ : shmid (shmid), shmaddr (shmaddr), shmflg (shmflg), hFileMap (hFileMap),
+ next (NULL)
+ {}
+ };
+
+public:
+ static client_shmmgr & instance ();
+
+ void *shmat (int shmid, const void *, int shmflg);
+ int shmctl (int shmid, int cmd, struct shmid_ds *);
+ int shmdt (const void *);
+ int shmget (key_t, size_t, int shmflg);
+
+ int fixup_shms_after_fork ();
+
+private:
+ static NO_COPY client_shmmgr *_instance;
+
+ CRITICAL_SECTION _segments_lock;
+ static segment_t *_segments_head; // List of attached segs by shmaddr.
+
+ static long _shmat_cnt; // No. of attached segs; for info. only.
+
+ client_shmmgr ();
+ ~client_shmmgr ();
+
+ // Undefined (as this class is a singleton):
+ client_shmmgr (const client_shmmgr &);
+ client_shmmgr & operator= (const client_shmmgr &);
+
+ segment_t *find (const void *, segment_t **previous = NULL);
+
+ void *attach (int shmid, const void *, int shmflg, HANDLE & hFileMap);
+
+ segment_t *new_segment (int shmid, const void *, int shmflg, HANDLE);
+};
+
+/* static */ NO_COPY client_shmmgr *client_shmmgr::_instance;
+
+/* The following two variables must be inherited by child processes
+ * since they are used by fixup_shms_after_fork () to re-attach to the
+ * parent's shm segments.
+ */
+/* static */ client_shmmgr::segment_t *client_shmmgr::_segments_head;
+/* static */ long client_shmmgr::_shmat_cnt;
+
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::instance ()
+ *---------------------------------------------------------------------------*/
-client_request_shm::client_request_shm (int ntype, int nshm_id, pid_t npid):
-client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+client_shmmgr &
+client_shmmgr::instance ()
{
- buffer = (char *) &parameters;
- parameters.in.shm_id = nshm_id;
- parameters.in.type = ntype;
- parameters.in.pid = npid;
+ if (!_instance)
+ _instance = safe_new0 (client_shmmgr);
+
+ assert (_instance);
+
+ return *_instance;
}
-client_request_shm::client_request_shm (key_t nkey, size_t nsize,
- int nshmflg,
- char psdbuf[4096],
- pid_t npid):
-client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::shmat ()
+ *---------------------------------------------------------------------------*/
+
+void *
+client_shmmgr::shmat (const int shmid,
+ const void *const shmaddr,
+ const int shmflg)
{
- buffer = (char *) &parameters;
- parameters.in.key = nkey;
- parameters.in.size = nsize;
- parameters.in.shmflg = nshmflg;
- parameters.in.type = SHM_CREATE;
- parameters.in.pid = npid;
- memcpy (parameters.in.sd_buf, psdbuf, 4096);
+ syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = 0%o)",
+ shmid, shmaddr, shmflg);
+
+ EnterCriticalSection (&_segments_lock);
+
+ HANDLE hFileMap = NULL;
+
+ void *const ptr = attach (shmid, shmaddr, shmflg, hFileMap);
+
+ if (ptr)
+ new_segment (shmid, ptr, shmflg, hFileMap);
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (ptr)
+ syscall_printf ("%p = shmat (shmid = %d, shmaddr = %p, shmflg = 0%o)",
+ ptr, shmid, shmaddr, shmflg);
+ // else
+ // See the syscall_printf in client_shmmgr::attach ().
+
+ return (ptr ? ptr : (void *) -1);
}
-static shmnode *shm_head = NULL;
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::shmctl ()
+ *---------------------------------------------------------------------------*/
-static shmnode *
-build_inprocess_shmds (HANDLE hfilemap, HANDLE hattachmap, key_t key,
- int shm_id)
+int
+client_shmmgr::shmctl (const int shmid,
+ const int cmd,
+ struct shmid_ds *const buf)
{
- HANDLE filemap = hfilemap;
- void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
+ syscall_printf ("shmctl (shmid = %d, cmd = 0x%x, buf = %p)",
+ shmid, cmd, buf);
+
+ // Check parameters and set up in parameters as required.
+
+ const struct shmid_ds *in_buf = NULL;
- if (!mapptr)
+ switch (cmd)
{
- CloseHandle (hfilemap);
- CloseHandle (hattachmap);
- //FIXME: close filemap and free the mutex
- /* we couldn't access the mapped area with the requested permissions */
- set_errno (EACCES);
- return NULL;
+ case IPC_SET:
+ if (__check_invalid_read_ptr_errno (buf, sizeof (struct shmid_ds)))
+ {
+ syscall_printf (("-1 [EFAULT] = "
+ "shmctl (shmid = %d, cmd = 0x%x, buf = %p)"),
+ shmid, cmd, buf);
+ set_errno (EFAULT);
+ return -1;
+ }
+ in_buf = buf;
+ break;
+
+ case IPC_STAT:
+ case SHM_STAT:
+ if (__check_null_invalid_struct_errno (buf, sizeof (struct shmid_ds)))
+ {
+ syscall_printf (("-1 [EFAULT] = "
+ "shmctl (shmid = %d, cmd = 0x%x, buf = %p)"),
+ shmid, cmd, buf);
+ set_errno (EFAULT);
+ return -1;
+ }
+ break;
+
+ case IPC_INFO:
+ if (__check_null_invalid_struct_errno (buf, sizeof (struct shminfo)))
+ {
+ syscall_printf (("-1 [EFAULT] = "
+ "shmctl (shmid = %d, cmd = 0x%x, buf = %p)"),
+ shmid, cmd, buf);
+ set_errno (EFAULT);
+ return -1;
+ }
+ break;
+
+ case SHM_INFO:
+ if (__check_null_invalid_struct_errno (buf, sizeof (struct shm_info)))
+ {
+ syscall_printf (("-1 [EFAULT] = "
+ "shmctl (shmid = %d, cmd = 0x%x, buf = %p)"),
+ shmid, cmd, buf);
+ set_errno (EFAULT);
+ return -1;
+ }
+ break;
}
- /* Now get the user data */
- HANDLE attachmap = hattachmap;
- shmid_ds *shmtemp = new shmid_ds;
- if (!shmtemp)
+ // Create and issue the command.
+
+ client_request_shm request (shmid, cmd, in_buf);
+
+ if (request.make_request () == -1 || request.error_code ())
{
- system_printf ("failed to malloc shm node\n");
- set_errno (ENOMEM);
- UnmapViewOfFile (mapptr);
- CloseHandle (filemap);
- CloseHandle (attachmap);
- /* exit mutex */
- return NULL;
+ syscall_printf (("-1 [%d] = "
+ "shmctl (shmid = %d, cmd = 0x%x, buf = %p)"),
+ request.error_code (), shmid, cmd, buf);
+ set_errno (request.error_code ());
+ return -1;
}
- /* get the system node data */
- *shmtemp = *(shmid_ds *) mapptr;
+ // Some commands require special processing for their out parameters.
+
+ int result = 0;
+
+ switch (cmd)
+ {
+ case IPC_STAT:
+ *buf = request.ds ();
+ break;
- /* process local data */
- shmnode *tempnode = new shmnode;
+ case IPC_INFO:
+ *(struct shminfo *) buf = request.shminfo ();
+ break;
- tempnode->filemap = filemap;
- tempnode->attachmap = attachmap;
- shmtemp->mapptr = mapptr;
+ case SHM_STAT: // ipcs(8) i'face.
+ result = request.shmid ();
+ *buf = request.ds ();
+ break;
- /* no need for InterlockedExchange here, we're serialised by the global mutex */
- tempnode->shmds = shmtemp;
- tempnode->shm_id = shm_id;
- tempnode->key = key;
- tempnode->next = shm_head;
- tempnode->attachhead = NULL;
- shm_head = tempnode;
+ case SHM_INFO: // ipcs(8) i'face.
+ result = request.shmid ();
+ *(struct shm_info *) buf = request.shm_info ();
+ break;
+ }
- /* FIXME: leave the system wide shm mutex */
+ syscall_printf ("%d = shmctl (shmid = %d, cmd = 0x%x, buf = %p)",
+ result, shmid, cmd, buf);
- return tempnode;
+ return result;
}
-static void
-delete_inprocess_shmds (shmnode **nodeptr)
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::shmdt ()
+ *
+ * According to Posix, the only error condition for this system call
+ * is EINVAL if shmaddr is not the address of the start of an attached
+ * shared memory segment. Given that, all other errors just generate
+ * tracing noise.
+ *---------------------------------------------------------------------------*/
+
+int
+client_shmmgr::shmdt (const void *const shmaddr)
{
- shmnode *node = *nodeptr;
+ syscall_printf ("shmdt (shmaddr = %p)", shmaddr);
- // remove from the list
- if (node == shm_head)
- shm_head = shm_head->next;
- else
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *previous = NULL;
+
+ segment_t *const segptr = find (shmaddr, &previous);
+
+ if (!segptr)
{
- shmnode *tempnode = shm_head;
- while (tempnode && tempnode->next != node)
- tempnode = tempnode->next;
- if (tempnode)
- tempnode->next = node->next;
- // else log the unexpected !
+ LeaveCriticalSection (&_segments_lock);
+ syscall_printf ("-1 [EINVAL] = shmdt (shmaddr = %p)", shmaddr);
+ set_errno (EINVAL);
+ return -1;
}
- // release the shared data view
- UnmapViewOfFile (node->shmds);
- CloseHandle (node->filemap);
- CloseHandle (node->attachmap);
+ assert (previous ? previous->next == segptr : _segments_head == segptr);
+
+ if (previous)
+ previous->next = segptr->next;
+ else
+ _segments_head = segptr->next;
+
+ LeaveCriticalSection (&_segments_lock);
+
+ const long cnt = InterlockedDecrement (&_shmat_cnt);
+ assert (cnt >= 0);
+
+ if (!UnmapViewOfFile ((void *) shmaddr))
+ syscall_printf (("failed to unmap view "
+ "[shmid = %d, handle = %p, shmaddr = %p]:"
+ "%E"),
+ segptr->shmid, segptr->hFileMap, shmaddr);
- // free the memory
- delete node;
- nodeptr = NULL;
+ assert (segptr->hFileMap);
+
+ if (!CloseHandle (segptr->hFileMap))
+ syscall_printf (("failed to close file map handle "
+ "[shmid = %d, handle = %p]: %E"),
+ segptr->shmid, segptr->hFileMap);
+
+ client_request_shm request (segptr->shmid);
+
+ if (request.make_request () == -1 || request.error_code ())
+ syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s",
+ segptr->shmid, segptr->hFileMap,
+ strerror (request.error_code ()));
+
+ safe_delete (segptr);
+
+ syscall_printf ("0 = shmdt (shmaddr = %p)", shmaddr);
+
+ return 0;
}
-int __stdcall
-fixup_shms_after_fork ()
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::shmget ()
+ *---------------------------------------------------------------------------*/
+
+int
+client_shmmgr::shmget (const key_t key, const size_t size, const int shmflg)
{
- shmnode *tempnode = shm_head;
- while (tempnode)
+ syscall_printf ("shmget (key = 0x%016X, size = %u, shmflg = 0%o)",
+ key, size, shmflg);
+
+ client_request_shm request (key, size, shmflg);
+
+ if (request.make_request () == -1 || request.error_code ())
{
- void *newshmds =
- MapViewOfFile (tempnode->filemap, FILE_MAP_WRITE, 0, 0, 0);
- if (!newshmds)
- {
- /* don't worry about handle cleanup, we're dying! */
- system_printf ("failed to reattach to shm control file view %x\n",
- tempnode);
- return 1;
- }
- tempnode->shmds = (class shmid_ds *) newshmds;
- tempnode->shmds->mapptr = newshmds;
- _shmattach *attachnode = tempnode->attachhead;
- while (attachnode)
- {
- void *newdata = MapViewOfFileEx (tempnode->attachmap,
- (attachnode->shmflg & SHM_RDONLY) ?
- FILE_MAP_READ : FILE_MAP_WRITE, 0,
- 0, 0, attachnode->data);
- if (newdata != attachnode->data)
- {
- /* don't worry about handle cleanup, we're dying! */
- system_printf ("failed to reattach to mapped file view %x\n",
- attachnode->data);
- return 1;
- }
- attachnode = attachnode->next;
- }
- tempnode = tempnode->next;
+ syscall_printf (("-1 [%d] = "
+ "shmget (key = 0x%016X, size = %u, shmflg = 0%o)"),
+ request.error_code (),
+ key, size, shmflg);
+ set_errno (request.error_code ());
+ return -1;
}
+
+ syscall_printf (("%d = shmget (key = 0x%016X, size = %u, shmflg = 0%o)"),
+ request.shmid (),
+ key, size, shmflg);
+
+ return request.shmid ();
+}
+
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::fixup_shms_after_fork ()
+ *
+ * The hFileMap handles are non-inheritable: so they have to be
+ * re-acquired from cygserver.
+ *
+ * Nb. This routine need not be thread-safe as it is only called at startup.
+ *---------------------------------------------------------------------------*/
+
+int
+client_shmmgr::fixup_shms_after_fork ()
+{
+ debug_printf ("re-attaching to shm segments: %d attached", _shmat_cnt);
+
+ {
+ int length = 0;
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next)
+ length += 1;
+
+ if (_shmat_cnt != length)
+ {
+ system_printf (("state inconsistent: "
+ "_shmat_cnt = %d, length of segments list = %d"),
+ _shmat_cnt, length);
+ return 1;
+ }
+ }
+
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next)
+ if (!attach (segptr->shmid,
+ segptr->shmaddr,
+ segptr->shmflg & ~SHM_RND,
+ segptr->hFileMap))
+ {
+ system_printf ("fatal error re-attaching to shm segment %d",
+ segptr->shmid);
+ return 1;
+ }
+
+ if (_shmat_cnt)
+ debug_printf ("re-attached all %d shm segments", _shmat_cnt);
+
return 0;
}
-/* this is ugly. Yes, I know that.
- * FIXME: abstract the lookup functionality,
- * So that it can be an array, list, whatever without us being worried
- */
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::client_shmmgr ()
+ *---------------------------------------------------------------------------*/
-/* FIXME: after fork, every memory area needs to have the attach count
- * incremented. This should be done in the server?
- */
+client_shmmgr::client_shmmgr ()
+{
+ InitializeCriticalSection (&_segments_lock);
+}
-/* FIXME: tell the daemon when we attach, so at process close it can clean up
- * the attach count
- */
-extern "C" void *
-shmat (int shmid, const void *shmaddr, int shmflg)
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::~client_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+client_shmmgr::~client_shmmgr ()
{
- shmnode *tempnode = shm_head;
- while (tempnode && tempnode->shm_id != shmid)
- tempnode = tempnode->next;
+ DeleteCriticalSection (&_segments_lock);
+}
- if (!tempnode)
- {
- /* couldn't find a currently open shm control area for the key - probably because
- * shmget hasn't been called.
- * Allocate a new control block - this has to be handled by the daemon */
- client_request_shm *req =
- new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ());
-
- int rc;
- if ((rc = cygserver_request (req)))
- {
- delete req;
- set_errno (ENOSYS); /* daemon communication failed */
- return (void *) -1;
- }
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::find ()
+ *---------------------------------------------------------------------------*/
- if (req->header.error_code) /* shm_get failed in the daemon */
- {
- set_errno (req->header.error_code);
- delete req;
- return (void *) -1;
- }
+client_shmmgr::segment_t *
+client_shmmgr::find (const void *const shmaddr, segment_t **previous)
+{
+ if (previous)
+ *previous = NULL;
- /* we've got the id, now we open the memory area ourselves.
- * This tests security automagically
- * FIXME: make this a method of shmnode ?
- */
- tempnode =
- build_inprocess_shmds (req->parameters.out.filemap,
- req->parameters.out.attachmap,
- req->parameters.out.key,
- req->parameters.out.shm_id);
- delete req;
- if (!tempnode)
- return (void *) -1;
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next)
+ if (segptr->shmaddr == shmaddr)
+ return segptr;
+ else if (segptr->shmaddr > shmaddr) // The list is sorted by shmaddr.
+ return NULL;
+ else if (previous)
+ *previous = segptr;
- }
+ return NULL;
+}
- // class shmid_ds *shm = tempnode->shmds;
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::attach ()
+ *
+ * The body of shmat (), also used by fixup_shms_after_fork ().
+ *---------------------------------------------------------------------------*/
+
+void *
+client_shmmgr::attach (const int shmid,
+ const void *shmaddr,
+ const int shmflg,
+ HANDLE & hFileMap)
+{
+ client_request_shm request (shmid, shmflg);
- if (shmaddr)
+ if (request.make_request () == -1 || request.error_code ())
{
- //FIXME: requested base address ?! (Don't forget to fix the fixup_after_fork too)
- set_errno (EINVAL);
- return (void *) -1;
+ syscall_printf (("-1 [%d] = "
+ "shmat (shmid = %d, shmaddr = %p, shmflg = 0%o)"),
+ request.error_code (), shmid, shmaddr, shmflg);
+ set_errno (request.error_code ());
+ return NULL;
}
- void *rv = MapViewOfFile (tempnode->attachmap,
- (shmflg & SHM_RDONLY) ? FILE_MAP_READ :
- FILE_MAP_WRITE, 0, 0, 0);
+ int result = 0;
+
+ const DWORD access = (shmflg & SHM_RDONLY) ? FILE_MAP_READ : FILE_MAP_WRITE;
- if (!rv)
+ if (shmaddr && (shmflg & SHM_RND))
+ shmaddr = (char *) shmaddr - ((ssize_t) shmaddr % SHMLBA);
+
+ void *const ptr =
+ MapViewOfFileEx (request.hFileMap (), access, 0, 0, 0, (void *) shmaddr);
+
+ if (!ptr)
{
- //FIXME: translate GetLastError()
- set_errno (EACCES);
- return (void *) -1;
+ syscall_printf (("failed to map view "
+ "[shmid = %d, handle = %p, shmaddr = %p]: %E"),
+ shmid, request.hFileMap (), shmaddr);
+ result = EINVAL; // FIXME
}
- /* tell the daemon we have attached */
- client_request_shm *req =
- new client_request_shm (SHM_ATTACH, shmid);
- int rc;
- if ((rc = cygserver_request (req)))
+ else if (shmaddr && ptr != shmaddr)
{
- debug_printf ("failed to tell deaemon that we have attached\n");
+ syscall_printf (("failed to map view at requested address "
+ "[shmid = %d, handle = %p]: "
+ "requested address = %p, mapped address = %p"),
+ shmid, request.hFileMap (),
+ shmaddr, ptr);
+ result = EINVAL; // FIXME
}
- delete req;
- _shmattach *attachnode = new _shmattach;
- attachnode->data = rv;
- attachnode->shmflg = shmflg;
- attachnode->next =
- (_shmattach *) InterlockedExchangePointer (&tempnode->attachhead,
- attachnode);
+ if (result != 0)
+ {
+ if (!CloseHandle (request.hFileMap ()))
+ syscall_printf (("failed to close file map handle "
+ "[shmid = %d, handle = %p]: %E"),
+ shmid, request.hFileMap ());
+
+ client_request_shm dt_req (shmid);
+ if (dt_req.make_request () == -1 || dt_req.error_code ())
+ syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s",
+ shmid, request.hFileMap (),
+ strerror (dt_req.error_code ()));
+
+ set_errno (result);
+ return NULL;
+ }
- return rv;
+ hFileMap = request.hFileMap ();
+ return ptr;
}
-/* FIXME: tell the daemon when we detach so it doesn't cleanup incorrectly.
- */
-extern "C" int
-shmdt (const void *shmaddr)
+/*---------------------------------------------------------------------------*
+ * client_shmmgr::new_segment ()
+ *
+ * Allocate a new segment for the given shmid, file map and address
+ * and insert into the segment map.
+ *---------------------------------------------------------------------------*/
+
+client_shmmgr::segment_t *
+client_shmmgr::new_segment (const int shmid,
+ const void *const shmaddr,
+ const int shmflg,
+ const HANDLE hFileMap)
{
- /* this should be "rare" so a hefty search is ok. If this is common, then we
- * should alter the data structs to allow more optimisation
- */
- shmnode *tempnode = shm_head;
- _shmattach *attachnode;
- while (tempnode)
+ assert (ipc_ext2int_subsys (shmid) == IPC_SHMOP);
+ assert (hFileMap);
+ assert (shmaddr);
+
+ segment_t *previous = NULL; // Insert pointer.
+
+ const segment_t *const tmp = find (shmaddr, &previous);
+
+ assert (!tmp);
+ assert (previous \
+ ? (!previous->next || previous->next->shmaddr > shmaddr) \
+ : (!_segments_head || _segments_head->shmaddr > shmaddr));
+
+ segment_t *const segptr =
+ safe_new (segment_t, shmid, shmaddr, shmflg, hFileMap);
+
+ assert (segptr);
+
+ if (previous)
{
- // FIXME: Race potential
- attachnode = tempnode->attachhead;
- while (attachnode && attachnode->data != shmaddr)
- attachnode = attachnode->next;
- if (attachnode)
- break;
- tempnode = tempnode->next;
+ segptr->next = previous->next;
+ previous->next = segptr;
}
- if (!tempnode)
+ else
{
- // dt cannot be called by an app that hasn't alreadu at'd
- set_errno (EINVAL);
- return -1;
+ segptr->next = _segments_head;
+ _segments_head = segptr;
}
- UnmapViewOfFile (attachnode->data);
- /* tell the daemon we have attached */
- client_request_shm *req =
- new client_request_shm (SHM_DETACH, tempnode->shm_id);
- int rc;
- if ((rc = cygserver_request (req)))
- {
- debug_printf ("failed to tell deaemon that we have detached\n");
- }
- delete req;
+ const long cnt = InterlockedIncrement (&_shmat_cnt);
+ assert (cnt > 0);
- return 0;
+ return segptr;
}
-//FIXME: who is allowed to perform STAT?
+/*---------------------------------------------------------------------------*
+ * shmat ()
+ *---------------------------------------------------------------------------*/
+
+extern "C" void *
+shmat (const int shmid, const void *const shmaddr, const int shmflg)
+{
+ sigframe thisframe (mainthread);
+ return shmmgr.shmat (shmid, shmaddr, shmflg);
+}
+
+/*---------------------------------------------------------------------------*
+ * shmctl ()
+ *---------------------------------------------------------------------------*/
+
extern "C" int
-shmctl (int shmid, int cmd, struct shmid_ds *buf)
+shmctl (const int shmid, const int cmd, struct shmid_ds *const buf)
{
- shmnode *tempnode = shm_head;
- while (tempnode && tempnode->shm_id != shmid)
- tempnode = tempnode->next;
- if (!tempnode)
- {
- /* couldn't find a currently open shm control area for the key - probably because
- * shmget hasn't been called.
- * Allocate a new control block - this has to be handled by the daemon */
- client_request_shm *req =
- new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ());
-
- int rc;
- if ((rc = cygserver_request (req)))
- {
- delete req;
- set_errno (ENOSYS); /* daemon communication failed */
- return -1;
- }
+ sigframe thisframe (mainthread);
+ return shmmgr.shmctl (shmid, cmd, buf);
+}
- if (req->header.error_code) /* shm_get failed in the daemon */
- {
- set_errno (req->header.error_code);
- delete req;
- return -1;
- }
+/*---------------------------------------------------------------------------*
+ * shmdt ()
+ *---------------------------------------------------------------------------*/
- /* we've got the id, now we open the memory area ourselves.
- * This tests security automagically
- * FIXME: make this a method of shmnode ?
- */
- tempnode =
- build_inprocess_shmds (req->parameters.out.filemap,
- req->parameters.out.attachmap,
- req->parameters.out.key,
- req->parameters.out.shm_id);
- delete req;
- if (!tempnode)
- return -1;
- }
+extern "C" int
+shmdt (const void *const shmaddr)
+{
+ sigframe thisframe (mainthread);
+ return shmmgr.shmdt (shmaddr);
+}
- switch (cmd)
- {
- case IPC_STAT:
- buf->shm_perm = tempnode->shmds->shm_perm;
- buf->shm_segsz = tempnode->shmds->shm_segsz;
- buf->shm_lpid = tempnode->shmds->shm_lpid;
- buf->shm_cpid = tempnode->shmds->shm_cpid;
- buf->shm_nattch = tempnode->shmds->shm_nattch;
- buf->shm_atime = tempnode->shmds->shm_atime;
- buf->shm_dtime = tempnode->shmds->shm_dtime;
- buf->shm_ctime = tempnode->shmds->shm_ctime;
- break;
- case IPC_RMID:
- {
- /* TODO: check permissions. Or possibly, the daemon gets to be the only
- * one with write access to the memory area?
- */
- if (tempnode->shmds->shm_nattch)
- system_printf
- ("call to shmctl with cmd= IPC_RMID when memory area still has"
- " attachees\n");
- /* how does this work?
- * we mark the ds area as "deleted", and the at and get calls all fail from now on
- * on, when nattch becomes 0, the mapped data area is destroyed.
- * and each process, as they touch this area detaches. eventually only the
- * daemon has an attach. The daemon gets asked to detach immediately.
- */
- //waiting for the daemon to handle terminating process's
- client_request_shm *req =
- new client_request_shm (SHM_DEL, shmid, GetCurrentProcessId ());
- int rc;
- if ((rc = cygserver_request (req)))
- {
- delete req;
- set_errno (ENOSYS); /* daemon communication failed */
- return -1;
- }
-
- if (req->header.error_code) /* shm_del failed in the daemon */
- {
- set_errno (req->header.error_code);
- delete req;
- return -1;
- }
-
- /* the daemon has deleted it's references */
- /* now for us */
-
- // FIXME: create a destructor
- delete_inprocess_shmds (&tempnode);
+/*---------------------------------------------------------------------------*
+ * shmget ()
+ *---------------------------------------------------------------------------*/
- }
- break;
- case IPC_SET:
- default:
- set_errno (EINVAL);
- return -1;
- }
- return 0;
+extern "C" int
+shmget (const key_t key, const size_t size, const int shmflg)
+{
+ sigframe thisframe (mainthread);
+ return shmmgr.shmget (key, size, shmflg);
}
-/* FIXME: evaluate getuid32() and getgid32() against the requested mode. Then
- * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
- * appropriately
- */
+/*---------------------------------------------------------------------------*
+ * fixup_shms_after_fork ()
+ *---------------------------------------------------------------------------*/
-/* FIXME: shmid should be a verifyable object
- */
+int __stdcall
+fixup_shms_after_fork ()
+{
+ return shmmgr.fixup_shms_after_fork ();
+}
-/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
- */
-extern "C" int
-shmget (key_t key, size_t size, int shmflg)
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm (const int shmid, const int shmflg)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
{
- DWORD sd_size = 4096;
- char sd_buf[4096];
- PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
- /* create a sd for our open requests based on shmflag & 0x01ff */
- InitializeSecurityDescriptor (psd,
- SECURITY_DESCRIPTOR_REVISION);
- psd = alloc_sd (getuid32 (), getgid32 (),
- shmflg & 0x01ff, psd, &sd_size);
-
- if (key == (key_t) - 1)
- {
- set_errno (ENOENT);
- return -1;
- }
+ _parameters.in.shmop = SHMOP_shmat;
- /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
- * to prevent races on shmget.
- */
+ _parameters.in.shmid = shmid;
+ _parameters.in.shmflg = shmflg;
- /* walk the list of currently open keys and return the id if found
- */
- shmnode *tempnode = shm_head;
- while (tempnode)
- {
- if (tempnode->key == key && key != IPC_PRIVATE)
- {
- // FIXME: free the mutex
- if (size && tempnode->shmds->shm_segsz < size)
- {
- set_errno (EINVAL);
- return -1;
- }
- if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
- {
- set_errno (EEXIST);
- // FIXME: free the mutex
- return -1;
- }
- // FIXME: do we need to other tests of the requested mode with the
- // tempnode->shmid mode ? testcase on unix needed.
- // FIXME do we need a security test? We are only examining the keys we already have open.
- // FIXME: what are the sec implications for fork () if we don't check here?
- return tempnode->shm_id;
- }
- tempnode = tempnode->next;
- }
- /* couldn't find a currently open shm control area for the key.
- * Allocate a new control block - this has to be handled by the daemon */
- client_request_shm *req =
- new client_request_shm (key, size, shmflg, sd_buf,
- GetCurrentProcessId ());
-
- int rc;
- if ((rc = cygserver_request (req)))
- {
- delete req;
- set_errno (ENOSYS); /* daemon communication failed */
- return -1;
- }
+ _parameters.in.cygpid = getpid ();
+ _parameters.in.winpid = GetCurrentProcessId ();
+ _parameters.in.uid = geteuid ();
+ _parameters.in.gid = getegid ();
- if (req->header.error_code) /* shm_get failed in the daemon */
- {
- set_errno (req->header.error_code);
- delete req;
- return -1;
- }
+ msglen (sizeof (_parameters.in));
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm (const int shmid,
+ const int cmd,
+ const struct shmid_ds *const buf)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmctl;
+
+ _parameters.in.shmid = shmid;
+ _parameters.in.cmd = cmd;
+ if (buf)
+ _parameters.in.ds = *buf;
+
+ _parameters.in.cygpid = getpid ();
+ _parameters.in.winpid = GetCurrentProcessId ();
+ _parameters.in.uid = geteuid ();
+ _parameters.in.gid = getegid ();
+
+ msglen (sizeof (_parameters.in));
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm (const int shmid)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmdt;
+
+ _parameters.in.shmid = shmid;
+
+ _parameters.in.cygpid = getpid ();
+ _parameters.in.winpid = GetCurrentProcessId ();
+ _parameters.in.uid = geteuid ();
+ _parameters.in.gid = getegid ();
+
+ msglen (sizeof (_parameters.in));
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm (const key_t key,
+ const size_t size,
+ const int shmflg)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmget;
+
+ _parameters.in.key = key;
+ _parameters.in.size = size;
+ _parameters.in.shmflg = shmflg;
+
+ _parameters.in.cygpid = getpid ();
+ _parameters.in.winpid = GetCurrentProcessId ();
+ _parameters.in.uid = geteuid ();
+ _parameters.in.gid = getegid ();
- /* we've got the id, now we open the memory area ourselves.
- * This tests security automagically
- * FIXME: make this a method of shmnode ?
- */
- shmnode *shmtemp = build_inprocess_shmds (req->parameters.out.filemap,
- req->parameters.out.attachmap,
- key,
- req->parameters.out.shm_id);
- delete req;
- if (shmtemp)
- return shmtemp->shm_id;
- return -1;
-
-
-#if 0
- /* fill out the node data */
- shmtemp->shm_perm.cuid = getuid32 ();
- shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
- shmtemp->shm_perm.cgid = getgid32 ();
- shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
- shmtemp->shm_perm.mode = shmflg & 0x01ff;
- shmtemp->shm_lpid = 0;
- shmtemp->shm_nattch = 0;
- shmtemp->shm_atime = 0;
- shmtemp->shm_dtime = 0;
- shmtemp->shm_ctime = time (NULL);
- shmtemp->shm_segsz = size;
- *(shmid_ds *) mapptr = *shmtemp;
- shmtemp->filemap = filemap;
- shmtemp->attachmap = attachmap;
- shmtemp->mapptr = mapptr;
-
-#endif
+ msglen (sizeof (_parameters.in));
}
diff --git a/winsup/cygwin/threaded_queue.cc b/winsup/cygwin/threaded_queue.cc
index 321fa16..ba0fe41 100755
--- a/winsup/cygwin/threaded_queue.cc
+++ b/winsup/cygwin/threaded_queue.cc
@@ -4,247 +4,405 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
+#include "woutsup.h"
+
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
-#include <windows.h>
#include <sys/types.h>
#include <stdlib.h>
-#include "wincap.h"
#include "threaded_queue.h"
-#define DEBUG 1
-#define debug_printf if (DEBUG) printf
+
+/*****************************************************************************/
+
+/* queue_request */
+
+queue_request::~queue_request ()
+{}
+
+/*****************************************************************************/
/* threaded_queue */
-DWORD WINAPI
-worker_function (LPVOID LpParam)
+threaded_queue::threaded_queue (const size_t initial_workers)
+ : _workers_count (0),
+ _running (false),
+ _submitters_head (NULL),
+ _requests_count (0),
+ _requests_head (NULL),
+ _requests_sem (NULL)
{
- class threaded_queue *queue = (class threaded_queue *) LpParam;
- class queue_request *request;
- /* FIXME use a threadsafe pop instead for speed? */
- while (queue->active)
+ InitializeCriticalSection (&_queue_lock);
+
+ // This semaphore's count is the number of requests on the queue.
+ // The maximum count (129792) is calculated as MAXIMUM_WAIT_OBJECTS
+ // multiplied by max. threads per process (2028?), which is (a few)
+ // more requests than could ever be pending with the current design.
+
+ _requests_sem = CreateSemaphore (NULL, // SECURITY_ATTRIBUTES
+ 0, // Initial count
+ 129792, // Maximum count
+ NULL); // Anonymous
+
+ if (!_requests_sem)
{
- EnterCriticalSection (&queue->queuelock);
- while (!queue->request && queue->active)
- {
- LeaveCriticalSection (&queue->queuelock);
- DWORD rc = WaitForSingleObject (queue->event, INFINITE);
- if (rc == WAIT_FAILED)
- {
- printf ("Wait for event failed\n");
- queue->running--;
- ExitThread (0);
- }
- EnterCriticalSection (&queue->queuelock);
- }
- if (!queue->active)
- {
- queue->running--;
- LeaveCriticalSection (&queue->queuelock);
- ExitThread (0);
- }
- /* not needed, but it is efficient */
- request =
- (class queue_request *) InterlockedExchangePointer (&queue->request,
- queue->request->
- next);
- LeaveCriticalSection (&queue->queuelock);
- request->process ();
- delete request;
+ system_printf (("failed to create the request queue semaphore, "
+ "error = %lu"),
+ GetLastError ());
+ abort ();
}
- queue->running--;
- ExitThread (0);
+
+ create_workers (initial_workers);
}
-void
-threaded_queue::create_workers ()
+threaded_queue::~threaded_queue ()
{
- InitializeCriticalSection (&queuelock);
- if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+ if (_running)
+ stop ();
+
+ debug_printf ("deleting all pending queue requests");
+ queue_request *reqptr = _requests_head;
+ while (reqptr)
{
- printf ("Failed to create event queue (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ queue_request *const ptr = reqptr;
+ reqptr = reqptr->_next;
+ safe_delete (ptr);
}
- active = true;
- /* FIXME: Use a stack pair and create threads on the fly whenever
- * we have to to service a request.
- */
- for (unsigned int i = 0; i < initial_workers; i++)
+ DeleteCriticalSection (&_queue_lock);
+ if (_requests_sem)
+ (void) CloseHandle (_requests_sem);
+}
+
+/* FIXME: return success or failure rather than quitting */
+void
+threaded_queue::add_submission_loop (queue_submission_loop *const submitter)
+{
+ assert (this);
+ assert (submitter);
+ assert (submitter->_queue == this);
+ assert (!submitter->_next);
+
+ submitter->_next =
+ TInterlockedExchangePointer (&_submitters_head, submitter);
+
+ if (_running)
+ submitter->start ();
+}
+
+bool
+threaded_queue::start ()
+{
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = true;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (!was_running)
{
- HANDLE hThread;
- DWORD tid;
- hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
- if (hThread == NULL)
+ debug_printf ("starting all queue submission loops");
+
+ while (loopptr)
{
- printf ("Failed to create thread (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->start ();
}
- CloseHandle (hThread);
- running++;
}
+
+ return was_running;
}
-void
-threaded_queue::cleanup ()
+bool
+threaded_queue::stop ()
{
- /* harvest the threads */
- active = false;
- /* kill the request processing loops */
- queue_process_param *reqloop;
- /* make sure we don't race with a incoming request creation */
- EnterCriticalSection (&queuelock);
- reqloop =
- (queue_process_param *) InterlockedExchangePointer (&process_head, NULL);
- while (reqloop)
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = false;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (was_running)
{
- queue_process_param *t = reqloop;
- reqloop = reqloop->next;
- delete t;
+ debug_printf ("stopping all queue submission loops");
+ while (loopptr)
+ {
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->stop ();
+ }
+
+ ReleaseSemaphore (_requests_sem, _workers_count, NULL);
+ while (_workers_count)
+ {
+ debug_printf (("waiting for worker threads to terminate: "
+ "%lu still running"),
+ _workers_count);
+ Sleep (1000);
+ }
+ debug_printf ("all worker threads have terminated");
}
- LeaveCriticalSection (&queuelock);
- if (!running)
- return;
- printf ("Waiting for current queue threads to terminate\n");
- for (int n = running; n; n--)
- PulseEvent (event);
- while (running)
- sleep (1);
- DeleteCriticalSection (&queuelock);
- CloseHandle (event);
+
+ return was_running;
}
/* FIXME: return success or failure */
void
-threaded_queue::add (queue_request * therequest)
+threaded_queue::add (queue_request *const therequest)
{
- /* safe to not "Try" because workers don't hog this, they wait on the event
- */
- EnterCriticalSection (&queuelock);
- if (!running)
+ assert (this);
+ assert (therequest);
+ assert (!therequest->_next);
+
+ if (!_workers_count)
{
- printf ("No worker threads to handle request!\n");
+ system_printf ("warning: no worker threads to handle request!");
+ // FIXME: And then what?
}
- if (!request)
- request = therequest;
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_requests_head)
+ _requests_head = therequest;
else
{
- /* add to the queue end. */
- queue_request *listrequest = request;
- while (listrequest->next)
- listrequest = listrequest->next;
- listrequest->next = therequest;
+ /* Add to the queue end. */
+ queue_request *reqptr = _requests_head;
+ for (; reqptr->_next; reqptr = reqptr->_next)
+ {}
+ assert (reqptr);
+ assert (!reqptr->_next);
+ reqptr->_next = therequest;
}
- PulseEvent (event);
- LeaveCriticalSection (&queuelock);
+
+ _requests_count += 1;
+ assert (_requests_count > 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ (void) ReleaseSemaphore (_requests_sem, 1, NULL);
}
-/* FIXME: return success or failure rather than quitting */
+/*static*/ DWORD WINAPI
+threaded_queue::start_routine (const LPVOID lpParam)
+{
+ class threaded_queue *const queue = (class threaded_queue *) lpParam;
+ assert (queue);
+
+ queue->worker_loop ();
+
+ const long count = InterlockedDecrement (&queue->_workers_count);
+ assert (count >= 0);
+
+ if (queue->_running)
+ debug_printf ("worker loop has exited; thread about to terminate");
+
+ return 0;
+}
+
+/* Called from the constructor: so no need to be thread-safe until the
+ * worker threads start to be created; thus the interlocked increment
+ * of the `_workers_count' field.
+ */
+
void
-threaded_queue::process_requests (queue_process_param * params,
- threaded_queue_thread_function *
- request_loop)
+threaded_queue::create_workers (const size_t initial_workers)
{
- if (params->start (request_loop, this) == false)
- exit (1);
- params->next =
- (queue_process_param *) InterlockedExchangePointer (&process_head,
- params);
+ assert (initial_workers > 0);
+
+ for (unsigned int i = 0; i != initial_workers; i++)
+ {
+ const long count = InterlockedIncrement (&_workers_count);
+ assert (count > 0);
+
+ DWORD tid;
+ const HANDLE hThread =
+ CreateThread (NULL, 0, start_routine, this, 0, &tid);
+
+ if (!hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ (void) CloseHandle (hThread);
+ }
}
-/* queue_process_param */
-/* How does a constructor return an error? */
-queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
-interruptible
-(ninterruptible)
+void
+threaded_queue::worker_loop ()
{
- if (!interruptible)
- return;
- debug_printf ("creating an interruptible processing thread\n");
- if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+ while (true)
{
- printf ("Failed to create interrupt event (%lu), terminating\n",
- GetLastError ());
- exit (1);
+ const DWORD rc = WaitForSingleObject (_requests_sem, INFINITE);
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("wait for request semaphore failed, error = %lu",
+ GetLastError ());
+ return;
+ }
+ assert (rc == WAIT_OBJECT_0);
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_running)
+ {
+ LeaveCriticalSection (&_queue_lock);
+ return;
+ }
+
+ assert (_requests_head);
+ queue_request *const reqptr = _requests_head;
+ _requests_head = reqptr->_next;
+
+ _requests_count -= 1;
+ assert (_requests_count >= 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ assert (reqptr);
+ reqptr->process ();
+ safe_delete (reqptr);
+ }
+}
+
+/*****************************************************************************/
+
+/* queue_submission_loop */
+
+queue_submission_loop::queue_submission_loop (threaded_queue *const queue,
+ const bool ninterruptible)
+ : _running (false),
+ _interrupt_event (NULL),
+ _queue (queue),
+ _interruptible (ninterruptible),
+ _hThread (NULL),
+ _tid (0),
+ _next (NULL)
+{
+ if (_interruptible)
+ {
+ // verbose: debug_printf ("creating an interruptible processing thread");
+
+ _interrupt_event = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_interrupt_event)
+ {
+ system_printf ("failed to create interrupt event, error = %lu",
+ GetLastError ());
+ abort ();
+ }
}
}
-queue_process_param::~queue_process_param ()
+queue_submission_loop::~queue_submission_loop ()
{
- if (running)
+ if (_running)
stop ();
- if (!interruptible)
- return;
- CloseHandle (interrupt);
+ if (_interrupt_event)
+ (void) CloseHandle (_interrupt_event);
+ if (_hThread)
+ (void) CloseHandle (_hThread);
}
bool
- queue_process_param::start (threaded_queue_thread_function * request_loop,
- threaded_queue * thequeue)
+queue_submission_loop::start ()
{
- queue = thequeue;
- hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
- if (hThread)
+ assert (this);
+ assert (!_hThread);
+
+ const bool was_running = _running;
+
+ if (!was_running)
{
- running = true;
- return true;
+ _running = true;
+
+ _hThread = CreateThread (NULL, 0, start_routine, this, 0, &_tid);
+ if (!_hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
}
- printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
- return false;
+
+ return was_running;
}
-void
-queue_process_param::stop ()
+bool
+queue_submission_loop::stop ()
{
- if (interruptible)
+ assert (this);
+ assert (_hThread && _hThread != INVALID_HANDLE_VALUE);
+
+ const bool was_running = _running;
+
+ if (_running)
{
- InterlockedExchange (&shutdown, true);
- PulseEvent (interrupt);
- /* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get
- * scheduled again, we print an error and exit. We _should_ loop or
- * try resignalling. We don't want to hand here though...
- */
- int n = 5;
- while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT);
- if (!n)
+ _running = false;
+
+ if (_interruptible)
{
- printf ("Process thread didn't shutdown cleanly after 200ms!\n");
- exit (1);
+ assert (_interrupt_event
+ && _interrupt_event != INVALID_HANDLE_VALUE);
+
+ SetEvent (_interrupt_event);
+
+ if (WaitForSingleObject (_hThread, 1000) == WAIT_TIMEOUT)
+ {
+ system_printf (("request loop thread %lu failed to shutdown "
+ "when asked politely: about to get heavy"),
+ _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ {
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
+ abort ();
+ }
+ }
}
else
- running = false;
- }
- else
- {
- printf ("killing request loop thread %ld\n", tid);
- int rc;
- if (!(rc = TerminateThread (hThread, 0)))
{
- printf ("error shutting down request loop worker thread\n");
+ // FIXME: could wait to see if the request loop notices that
+ // the submission loop is no longer running and shuts down
+ // voluntarily.
+
+ debug_printf ("killing request loop thread %lu", _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
}
- running = false;
}
- CloseHandle (hThread);
-}
-/* queue_request */
-queue_request::queue_request ():next (NULL)
-{
+ return was_running;
}
-void
-queue_request::process (void)
+/*static*/ DWORD WINAPI
+queue_submission_loop::start_routine (const LPVOID lpParam)
{
- printf ("\n**********************************************\n"
- "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
- "fault !!!\n" "***********************************************\n");
+ class queue_submission_loop *const submission_loop =
+ (class queue_submission_loop *) lpParam;
+ assert (submission_loop);
+
+ submission_loop->request_loop ();
+
+ debug_printf ("submission loop has exited; thread about to terminate");
+
+ submission_loop->stop ();
+
+ return 0;
}
+
+/*****************************************************************************/
diff --git a/winsup/cygwin/threaded_queue.h b/winsup/cygwin/threaded_queue.h
index d72541e..5b6fddc 100755
--- a/winsup/cygwin/threaded_queue.h
+++ b/winsup/cygwin/threaded_queue.h
@@ -4,64 +4,124 @@
Written by Robert Collins <rbtcollins@hotmail.com>
- 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. */
#ifndef _THREADED_QUEUE_
#define _THREADED_QUEUE_
+/*****************************************************************************/
+
/* a specific request */
class queue_request
{
- public:
- class queue_request *next;
- virtual void process ();
- queue_request();
+public:
+ queue_request *_next;
+
+ queue_request () : _next (NULL) {}
+ virtual ~queue_request ();
+
+ virtual void process () = 0;
};
+/*****************************************************************************/
-typedef DWORD WINAPI threaded_queue_thread_function (LPVOID);
-/* parameters for a request finding and submitting loop */
+/* a queue to allocate requests from n submission loops to x worker threads */
-class queue_process_param
+class queue_submission_loop;
+
+class threaded_queue
{
- public:
- bool start (threaded_queue_thread_function *, class threaded_queue *);
- void stop ();
- bool running;
- long int shutdown;
- class queue_process_param * next;
- class threaded_queue *queue;
- queue_process_param (bool ninterruptible);
- ~queue_process_param ();
- bool interruptible;
- HANDLE interrupt;
- HANDLE hThread;
- DWORD tid;
+public:
+ threaded_queue (size_t initial_workers = 1);
+ ~threaded_queue ();
+
+ void add_submission_loop (queue_submission_loop *);
+
+ bool running () const { return _running; }
+
+ bool start ();
+ bool stop ();
+
+ void add (queue_request *);
+
+private:
+ long _workers_count;
+ bool _running;
+
+ queue_submission_loop *_submitters_head;
+
+ long _requests_count; // Informational only.
+ queue_request *_requests_head;
+
+ CRITICAL_SECTION _queue_lock;
+ HANDLE _requests_sem; // == _requests_count
+
+ static DWORD WINAPI start_routine (LPVOID /* this */);
+
+ void create_workers (size_t initial_workers);
+ void worker_loop ();
};
-/* a queue to allocate requests from n submission loops to x worker threads */
+/*****************************************************************************/
-class threaded_queue
+/* parameters for a request finding and submitting loop */
+
+class queue_submission_loop
{
- public:
- CRITICAL_SECTION queuelock;
- HANDLE event;
- bool active;
- queue_request * request;
- unsigned int initial_workers;
- unsigned int running;
- void create_workers ();
- void cleanup ();
- void add (queue_request *);
- void process_requests (queue_process_param *, threaded_queue_thread_function *);
- threaded_queue () : active (false), request (NULL), initial_workers (1), running (0), process_head (NULL) {};
- private:
- queue_request *process_head;
+ friend class threaded_queue;
+
+public:
+ queue_submission_loop (threaded_queue *, bool ninterruptible);
+ virtual ~queue_submission_loop ();
+
+ bool start ();
+ bool stop ();
+
+ threaded_queue *queue () { return _queue; };
+
+protected:
+ bool _running;
+ HANDLE _interrupt_event;
+ threaded_queue *const _queue;
+
+private:
+ bool _interruptible;
+ HANDLE _hThread;
+ DWORD _tid;
+ queue_submission_loop *_next;
+
+ static DWORD WINAPI start_routine (LPVOID /* this */);
+ virtual void request_loop () = 0;
};
+#ifdef __cplusplus
+
+/*---------------------------------------------------------------------------*
+ * Some type-safe versions of the various interlocked functions.
+ *---------------------------------------------------------------------------*/
+
+template <typename T> T *
+TInterlockedExchangePointer (T **lvalue, T *rvalue)
+{
+ return reinterpret_cast<T *>
+ (InterlockedExchangePointer (reinterpret_cast<void **> (lvalue),
+ reinterpret_cast<void *> (rvalue)));
+}
+
+template <typename T> T *
+TInterlockedCompareExchangePointer (T **lvalue, T *rvalue1, T *rvalue2)
+{
+ return reinterpret_cast<T *>
+ (InterlockedCompareExchangePointer (reinterpret_cast<void **> (lvalue),
+ reinterpret_cast<void *> (rvalue1),
+ reinterpret_cast<void *> (rvalue2)));
+}
+
+#endif /* __cplusplus */
+
#endif /* _THREADED_QUEUE_ */
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index b564d66..982bfab 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -22,7 +22,6 @@ details. */
#include "dtable.h"
#include "cygheap.h"
#include "pinfo.h"
-#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#include "shared_info.h"
#include "cygthread.h"
@@ -405,10 +404,17 @@ tty::common_init (fhandler_pty_master *ptym)
/* FIXME: we shold NOT set the security wide open when the
daemon is running
*/
- if (wincap.has_security () && cygserver_running != CYGSERVER_OK &&
- (SetKernelObjectSecurity (hMainProc, DACL_SECURITY_INFORMATION,
- get_null_sd ()) == FALSE))
- system_printf ("Can't set process security, %E");
+ if (wincap.has_security ())
+ {
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
+
+ if (cygserver_running != CYGSERVER_OK
+ && !SetKernelObjectSecurity (hMainProc,
+ DACL_SECURITY_INFORMATION,
+ get_null_sd ()))
+ system_printf ("Can't set process security, %E");
+ }
/* Create synchronisation events */
diff --git a/winsup/cygwin/woutsup.h b/winsup/cygwin/woutsup.h
new file mode 100644
index 0000000..c048f1c
--- /dev/null
+++ b/winsup/cygwin/woutsup.h
@@ -0,0 +1,110 @@
+/* woutsup.h: for Cygwin code compiled outside the DLL (i.e. cygserver).
+
+ Copyright 2002 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. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef __INSIDE_CYGWIN__
+#error "woutsup.h is not for code being compiled inside the dll"
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#if _WIN32_WINNT < 0x0500
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#define WIN32_LEAN_AND_MEAN 1
+#define _WINGDI_H
+#define _WINUSER_H
+#define _WINNLS_H
+#define _WINVER_H
+#define _WINNETWK_H
+#define _WINSVC_H
+#include <windows.h>
+#include <wincrypt.h>
+#include <lmcons.h>
+#undef _WINGDI_H
+#undef _WINUSER_H
+#undef _WINNLS_H
+#undef _WINVER_H
+#undef _WINNETWK_H
+#undef _WINSVC_H
+
+#include "wincap.h"
+
+/* The one function we use from winuser.h most of the time */
+extern "C" DWORD WINAPI GetLastError (void);
+
+extern int cygserver_running;
+
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L
+#define NEW_MACRO_VARARGS
+#endif
+
+/*
+ * A reproduction of the <sys/strace.h> macros. This allows code that
+ * runs both inside and outside the Cygwin DLL to use the same macros
+ * for logging messages.
+ */
+
+extern "C" void __cygserver__printf (const char *, const char *, ...);
+
+#ifdef NEW_MACRO_VARARGS
+
+#define system_printf(...) \
+ do \
+ { \
+ __cygserver__printf (__PRETTY_FUNCTION__, __VA_ARGS__); \
+ } while (false)
+
+#define __noop_printf(...) do {;} while (false)
+
+#else /* !NEW_MACRO_VARARGS */
+
+#define system_printf(args...) \
+ do \
+ { \
+ __cygserver__printf (__PRETTY_FUNCTION__, ## args); \
+ } while (false)
+
+#define __noop_printf(args...) do {;} while (false)
+
+#endif /* !NEW_MACRO_VARARGS */
+
+#ifdef DEBUGGING
+#define debug_printf system_printf
+#define paranoid_printf system_printf
+#define select_printf system_printf
+#define sigproc_printf system_printf
+#define syscall_printf system_printf
+#define termios_printf system_printf
+#define wm_printf system_printf
+#define minimal_printf system_printf
+#define malloc_printf system_printf
+#define thread_printf system_printf
+#else
+#define debug_printf __noop_printf
+#define paranoid_printf __noop_printf
+#define select_printf __noop_printf
+#define sigproc_printf __noop_printf
+#define syscall_printf __noop_printf
+#define termios_printf __noop_printf
+#define wm_printf __noop_printf
+#define minimal_printf __noop_printf
+#define malloc_printf __noop_printf
+#define thread_printf __noop_printf
+#endif
+
+#include "safe_memory.h"