OSDN Git Service

2002-02-28 Robert Collins <rbtcollins@hotmail.com>
authorrbcollins <rbcollins>
Thu, 28 Feb 2002 14:30:26 +0000 (14:30 +0000)
committerrbcollins <rbcollins>
Thu, 28 Feb 2002 14:30:26 +0000 (14:30 +0000)
* Merged cygwin_daemon into head minus the new shm and ipc exports.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* thread.cc (semaphore::TryWait): Set errno as required by posix 1003.1.
(__sem_wait): Ditto.
(__sem_trywait): Ditto.

28 files changed:
winsup/cygserver/client.cc [new file with mode: 0644]
winsup/cygserver/cygserver.cc [new file with mode: 0644]
winsup/cygserver/process.cc [new file with mode: 0644]
winsup/cygserver/shm.cc [new file with mode: 0644]
winsup/cygserver/shm.h [new file with mode: 0755]
winsup/cygserver/threaded_queue.cc [new file with mode: 0644]
winsup/cygserver/transport.cc [new file with mode: 0644]
winsup/cygserver/transport_pipes.cc [new file with mode: 0644]
winsup/cygserver/transport_sockets.cc [new file with mode: 0644]
winsup/cygwin/cygserver.cc [new file with mode: 0755]
winsup/cygwin/cygserver_client.cc [new file with mode: 0755]
winsup/cygwin/cygserver_process.cc [new file with mode: 0755]
winsup/cygwin/cygserver_shm.cc [new file with mode: 0755]
winsup/cygwin/cygserver_shm.h [new file with mode: 0644]
winsup/cygwin/cygserver_transport.cc [new file with mode: 0755]
winsup/cygwin/cygserver_transport_pipes.cc [new file with mode: 0755]
winsup/cygwin/cygserver_transport_sockets.cc [new file with mode: 0755]
winsup/cygwin/include/cygwin/cygserver.h [new file with mode: 0755]
winsup/cygwin/include/cygwin/cygserver_process.h [new file with mode: 0755]
winsup/cygwin/include/cygwin/cygserver_transport.h [new file with mode: 0755]
winsup/cygwin/include/cygwin/cygserver_transport_pipes.h [new file with mode: 0755]
winsup/cygwin/include/cygwin/cygserver_transport_sockets.h [new file with mode: 0755]
winsup/cygwin/include/sys/ipc.h [new file with mode: 0644]
winsup/cygwin/include/sys/shm.h [new file with mode: 0644]
winsup/cygwin/ipc.cc [new file with mode: 0644]
winsup/cygwin/shm.cc [new file with mode: 0644]
winsup/cygwin/threaded_queue.cc [new file with mode: 0755]
winsup/cygwin/threaded_queue.h [new file with mode: 0755]

diff --git a/winsup/cygserver/client.cc b/winsup/cygserver/client.cc
new file mode 100644 (file)
index 0000000..8a1eeaf
--- /dev/null
@@ -0,0 +1,207 @@
+/* cygserver_client.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Egor Duda <deo@logos-m.ru>
+
+   This file is part of Cygwin.
+
+   This software is a copyrighted work licensed under the terms of the
+   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+   details. */
+
+#ifdef __OUTSIDE_CYGWIN__
+#undef __INSIDE_CYGWIN__
+#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 <errno.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 "cygwin/cygserver.h"
+
+/* 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;
+
+client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version))
+{
+  buffer = (char *)&version;
+}
+
+client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
+{
+  buffer = (char *)&req;
+  req.pid = 0;
+  req.master_pid = 0;
+  req.from_master = NULL;
+  req.to_master = NULL;
+}
+
+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))
+{
+  buffer = (char *)&req;
+  req.pid = npid;
+  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;
+}
+
+client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size)
+{
+}
+
+client_request::~client_request ()
+{
+}
+
+client_request::operator class request_header ()
+{
+  return header;
+}
+
+void
+client_request::send (transport_layer_base *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))
+    {
+      header.error_code = -1;
+      debug_printf ("bytes written != request size\n");
+      return;
+    }
+
+  debug_printf("Sent request, size (%ld)\n",bytes_written);
+
+  if ((bytes_read = conn->read ((char *)&header, sizeof (header)))
+    != sizeof (header) || (header.cb &&
+    (bytes_read = conn->read (buffer, header.cb) ) != header.cb))
+    {
+      header.error_code = -1;
+      debug_printf("failed reading response \n");
+      return;
+    }
+  debug_printf ("completed ok\n");
+}
+
+/* Oh, BTW: Fix the procedural basis and make this more intuitive. */
+
+int
+cygserver_request (client_request * req)
+{
+  class transport_layer_base *transport;
+
+  if (!req || allow_daemon != TRUE)
+    return -1;
+
+  /* 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;
+
+  transport = create_server_transport ();
+
+  /* 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 ())
+    {
+      delete transport;
+      return -1;
+    }
+
+  debug_printf ("connected to server %p\n", transport);
+
+  req->send(transport);
+
+  transport->close ();
+
+  delete transport;
+
+  return 0;
+}
+
+#if 0
+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);
+}
+#endif
+
+void
+cygserver_init ()
+{
+  int rc;
+  if (allow_daemon != TRUE)
+    {
+      cygserver_running = CYGSERVER_DEAD;
+      return;
+    }
+
+  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;
+}
diff --git a/winsup/cygserver/cygserver.cc b/winsup/cygserver/cygserver.cc
new file mode 100644 (file)
index 0000000..57f92c2
--- /dev/null
@@ -0,0 +1,548 @@
+/* cygserver.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Egor Duda <deo@logos-m.ru>
+
+   This file is part of Cygwin.
+
+   This software is a copyrighted work licensed under the terms of the
+   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+   details. */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <signal.h>
+#include "wincap.h"
+#include "cygwin_version.h"
+
+#include "getopt.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"
+
+/* for quieter operation, set to 0 */
+#define DEBUG 0
+#define debug_printf if (DEBUG) printf
+
+GENERIC_MAPPING access_mapping;
+static class transport_layer_base *transport;
+
+DWORD request_count = 0;
+
+BOOL
+setup_privileges ()
+{
+  BOOL rc, ret_val;
+  HANDLE hToken = NULL;
+  TOKEN_PRIVILEGES sPrivileges;
+
+  rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ;
+  if ( !rc )
+    {
+      printf ( "error opening process token (%lu)\n", 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 () );
+      ret_val = FALSE;
+      goto out;
+    }
+  sPrivileges.PrivilegeCount = 1 ;
+  sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
+  rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ;
+  if ( !rc )
+    {
+      printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () );
+      ret_val = FALSE;
+      goto out;
+    }
+
+  access_mapping.GenericRead = FILE_READ_DATA;
+  access_mapping.GenericWrite = FILE_WRITE_DATA;
+  access_mapping.GenericExecute = 0;
+  access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA;
+
+  ret_val = TRUE;
+
+out:
+  CloseHandle ( hToken );
+  return ret_val;
+}
+
+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 = 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))
+    {
+      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))
+    {
+      printf ( "error getting handle SD (%lu)\n", GetLastError ());
+      goto out;
+    }
+
+  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 (!status)
+    {
+      printf ( "access to object denied\n");
+      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 ());
+      goto out;
+    }
+  debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
+
+  ret_val = 0;
+                    
+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);
+}
+
+void
+client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
+{
+  HANDLE from_process_handle = NULL;
+  HANDLE to_process_handle = NULL;
+  HANDLE token_handle = NULL;
+  DWORD rc;
+
+  if (header.cb != sizeof (req))
+    {
+      header.error_code = EINVAL;
+      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)
+    {
+      printf ("error opening process (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  debug_printf ("Impersonating client\n");
+  conn->impersonate_client ();
+  
+  debug_printf ("about to open thread token\n");
+  rc = OpenThreadToken (GetCurrentThread (),
+                       TOKEN_QUERY,
+                        TRUE,
+                       &token_handle);
+
+  debug_printf ("opened thread token, rc=%lu\n", rc);
+  conn->revert_to_self ();
+
+  if (!rc)
+    {
+      printf ("error opening thread token (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  if (check_and_dup_handle (from_process_handle, to_process_handle,
+                            token_handle,
+                            GENERIC_READ,
+                            req.from_master,
+                            &req.from_master, TRUE) != 0)
+    {
+      printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  if (req.to_master)
+    {
+      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;
+       }
+    }
+
+#if DEBUG
+  printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
+                               req.from_master, req.to_master);
+#endif
+
+  header.error_code = 0;
+
+out:
+  if (from_process_handle)
+    CloseHandle (from_process_handle);
+  if (to_process_handle)
+    CloseHandle (to_process_handle);
+  if (token_handle)
+    CloseHandle (token_handle);
+}
+
+void
+client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
+{
+  if (header.cb != sizeof (version))
+    {
+      header.error_code = EINVAL;
+      return;
+    }
+  header.error_code = 0;
+  version.major = CYGWIN_SERVER_VERSION_MAJOR;
+  version.api   = CYGWIN_SERVER_VERSION_API;
+  version.minor = CYGWIN_SERVER_VERSION_MINOR;
+  version.patch = CYGWIN_SERVER_VERSION_PATCH;
+}
+
+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;
+};
+
+class server_process_param : public queue_process_param
+{
+  public:
+    transport_layer_base *transport;
+    server_process_param () : queue_process_param (false) {};
+};
+
+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);
+};
+class server_request_queue request_queue;
+
+static DWORD WINAPI
+request_loop (LPVOID LpParam)
+{
+  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)
+  {
+    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);
+  }
+  return 0;
+}
+
+/* TODO: check we are not being asked to service a already serviced transport */
+void
+server_request_queue::process_requests (transport_layer_base *transport)
+{
+  class server_process_param *params = new server_process_param;
+  params->transport = transport;
+  threaded_queue::process_requests (params, request_loop);
+}
+
+void
+client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
+{
+  /* 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;
+}
+
+server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
+{
+  conn = newconn;
+  cache = newcache;
+}
+
+void
+server_request::process ()
+{
+  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");
+
+  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);
+
+  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");
+    }
+
+  if (req->header.cb != req_ptr->cb)
+    {
+      debug_printf ("Mismatch in request buffer sizes\n");
+      goto out;
+    }
+
+  if (req->header.cb)
+    {
+
+      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);
+    }
+
+  /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
+  req->serve (conn, cache);
+
+  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;
+    }
+
+  debug_printf("Sent reply, size (%ld)\n",bytes_written);
+  printf (".");
+
+out:
+  conn->close ();
+  delete conn;
+  if (req)
+    delete (req);
+}
+
+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);
+}
+
+void
+handle_signal (int signal)
+{
+  /* 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;
+}
+
+struct option longopts[] = {
+  {"shutdown", no_argument, NULL, 's'},
+  {0, no_argument, NULL, 0}
+};
+
+char opts[] = "s";
+
+int
+main (int argc, char **argv)
+{
+  int shutdown=0;
+  char i;
+
+  while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+    switch (i)
+      {
+      case 's':
+        shutdown = 1;
+        break;
+      default:
+        break;
+       /*NOTREACHED*/
+      }
+
+  wincap.init();
+  if (wincap.has_security ())
+    setup_privileges ();
+  transport = create_server_transport ();
+
+  if (shutdown)
+    {
+      if (!transport->connect())
+       {
+         printf ("couldn't establish connection with server\n");
+         exit (1);
+       }
+      client_request_shutdown *request =
+       new client_request_shutdown ();
+      request->send (transport);
+      transport->close();
+      delete transport;
+      delete request;
+      exit(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);
+  setbuf (stdout, NULL);
+  printf ("daemon version %s starting up", version);
+  if (signal (SIGQUIT, handle_signal) == SIG_ERR)
+    {
+      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);
+  printf (".");
+  cache.create_workers ();
+  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);
+   */
+  /* 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 ();
+  printf ("All pending requests processed\n");
+  transport->close ();
+  printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
+  cache.cleanup ();
+  printf ("All outstanding process-cache activities completed\n");
+  printf ("daemon shutdown\n");
+}
diff --git a/winsup/cygserver/process.cc b/winsup/cygserver/process.cc
new file mode 100644 (file)
index 0000000..a5173af
--- /dev/null
@@ -0,0 +1,389 @@
+/* cygserver_process.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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)
+{
+  /* 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;
+}
+
+process_cache::~process_cache ()
+{
+}
+
+class process *
+process_cache::process (long pid)
+{
+  class process *entry = head;
+  /* TODO: make this more granular, so a search doesn't involve the write lock */
+  EnterCriticalSection (&cache_write_access);
+  if (!entry)
+    {
+      entry = new class process (pid);
+      entry->next =
+       (class process *) InterlockedExchangePointer (&head, entry);
+      PulseEvent (cache_add_trigger);
+    }
+  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;
+}
+
+static DWORD WINAPI
+request_loop (LPVOID LpParam)
+{
+  class process_process_param *params = (process_process_param *) LpParam;
+  return params->request_loop ();
+}
+
+void
+process_cache::process_requests ()
+{
+  class process_process_param *params = new process_process_param;
+  threaded_queue::process_requests (params, request_loop);
+}
+
+void
+process_cache::add_task (class process * theprocess)
+{
+  /* 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);
+}
+
+/* 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)
+    {
+      entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
+      if (entry != theprocess)
+        {
+          printf ("Bug encountered, process cache corrupted\n");
+         exit (1);
+        }
+    }
+  else
+    {
+      while (entry->next && entry->next != theprocess)
+       entry = entry->next;
+      class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
+      if (temp != theprocess)
+       {
+         printf ("Bug encountered, process cache corrupted\n");
+         exit (1);
+       }
+    }
+  LeaveCriticalSection (&cache_write_access);
+  /* Process any cleanup tasks */
+  add_task (theprocess);
+}
+       
+      
+/* 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.
+ */
+int
+process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
+                               ssize_t max_copy, int begin_at)
+{
+  /* 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)
+    {
+      /* hack */
+      if (entry->exit_code () == STILL_ACTIVE)
+       {
+         *hto = entry->handle ();
+         *eto = entry;
+         count++;
+         hto++;
+         eto++;
+       }
+      entry = entry->next;
+    }
+  return count;
+}
+
+/* process's */
+/* global process crit section */
+static CRITICAL_SECTION process_access;
+static pthread_once_t process_init;
+
+void
+do_process_init (void)
+{
+  InitializeCriticalSection (&process_access);
+  /* we don't have a cache shutdown capability today */
+}
+
+process::process (long pid):
+winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
+{
+  pthread_once (&process_init, do_process_init);
+  EnterCriticalSection (&process_access);
+  thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+  if (!thehandle)
+    {
+      printf ("unable to obtain handle for new cache process %ld\n", pid);
+      thehandle = INVALID_HANDLE_VALUE;
+    }
+  debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
+  InitializeCriticalSection (&access);
+  LeaveCriticalSection (&process_access);
+}
+
+process::~process ()
+{
+  DeleteCriticalSection (&access);
+}
+
+HANDLE
+process::handle ()
+{
+//  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;
+}
+
+DWORD process::exit_code ()
+{
+  if (_exit_status != STILL_ACTIVE)
+    return _exit_status;
+  bool
+    err = GetExitCodeProcess (thehandle, &_exit_status);
+  if (!err)
+    {
+      debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
+      thehandle = INVALID_HANDLE_VALUE;
+      return _exit_status;
+    }
+  else if (_exit_status == STILL_ACTIVE)
+    return _exit_status;
+  /* add new cleanup task etc etc ? */
+  return _exit_status;
+}
+
+/* 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 ()
+{
+  /* Serialize this */
+  EnterCriticalSection (&access);
+  InterlockedIncrement (&(long)cleaning_up);
+  class cleanup_routine *entry = head;
+  while (entry)
+    {
+      class cleanup_routine *temp;
+      entry->cleanup (winpid);
+      temp = entry->next;
+      delete entry;
+      entry = temp;
+    }
+  LeaveCriticalSection (&access);
+}
+
+bool
+process::add_cleanup_routine (class cleanup_routine *new_cleanup)
+{
+  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)
+    {
+      LeaveCriticalSection (&access);
+      return false;
+    }
+  new_cleanup->next = head;
+  head = new_cleanup;
+  LeaveCriticalSection (&access);
+  return true;
+}
+
+/* process_cleanup */
+void
+process_cleanup::process ()
+{
+  theprocess->cleanup ();
+  delete theprocess;
+}
+
+/* process_process_param */
+DWORD
+process_process_param::request_loop ()
+{
+  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;
+}
diff --git a/winsup/cygserver/shm.cc b/winsup/cygserver/shm.cc
new file mode 100644 (file)
index 0000000..c29938b
--- /dev/null
@@ -0,0 +1,552 @@
+/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
+
+   Copyright 2001 Red Hat, Inc.
+
+   Originally written by Robert Collins <robert.collins@hotmail.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. */
+
+
+#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 <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 <stdio.h>
+//#include "thread.h"
+#ifndef __INSIDE_CYGWIN__
+#define __INSIDE_CYGWIN__
+#include <sys/shm.h>
+#undef __INSIDE_CYGWIN__
+#else
+#include <sys/shm.h>
+#endif
+//#include "perprocess.h"
+#include <threaded_queue.h>
+#include <cygwin/cygserver_process.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.
+ * 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;
+}
+
+
+client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+{
+  buffer = (char *) &parameters;
+}
+
+/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
+ * Solution: manually calculate the next id value 
+ */
+
+#if 0
+extern "C" void *
+shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
+{
+  class shmid_ds *shm = (class shmid_ds *) shmid;      //FIXME: verifyable object test
+
+  if (shmaddr)
+    {
+      //FIXME: requested base address ?!
+      set_errno (EINVAL);
+      return (void *) -1;
+    }
+
+  void *rv = MapViewOfFile (shm->attachmap,
+
+
+                           (parameters.in.shmflg & SHM_RDONLY) ?
+                           FILE_MAP_READ : FILE_MAP_WRITE, 0,
+                           0, 0);
+
+  if (!rv)
+    {
+      //FIXME: translate GetLastError()
+      set_errno (EACCES);
+      return (void *) -1;
+    }
+
+/* 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;
+}
+#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;
+/* must be long for InterlockedIncrement */
+static long new_id = 0;
+static long new_private_key = 0;
+
+void
+client_request_shm::serve (transport_layer_base * conn,
+                              process_cache * cache)
+{
+//  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)
+    {
+      debug_printf ("error opening process (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      return;
+    }
+
+  conn->impersonate_client ();
+
+  rc = OpenThreadToken (GetCurrentThread (),
+                       TOKEN_QUERY, TRUE, &token_handle);
+
+  conn->revert_to_self ();
+
+  if (!rc)
+    {
+      debug_printf ("error opening thread token (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      CloseHandle (from_process_handle);
+      return;
+    }
+
+
+  /* we trust the clients request - we will be doing it as them, and
+   * the worst they can do is open their own permissions 
+   */
+
+
+  SECURITY_ATTRIBUTES sa;
+  sa.nLength = sizeof (sa);
+  sa.lpSecurityDescriptor = psd;
+  sa.bInheritHandle = TRUE;    /* the memory structures inherit ok */
+
+  char *shmname = NULL, *shmaname = NULL;
+  char stringbuf[29], stringbuf1[29];
+
+  /* TODO: make this code block a function! */
+  if (parameters.in.type == SHM_REATTACH)
+    {
+      /* just find and fill out the existing shm_id */
+      shmnode *tempnode = shm_head;
+      while (tempnode)
+       {
+         if (tempnode->shm_id == parameters.in.shm_id)
+           {
+             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)
+               {
+                 debug_printf ("error duplicating attachmap handle (%lu)\n",
+                               GetLastError ());
+                 header.error_code = EACCES;
+               }
+             CloseHandle (token_handle);
+             return;
+           }
+         tempnode = tempnode->next;
+       }
+      header.error_code = EINVAL;
+      CloseHandle (token_handle);
+      return;
+    }
+
+  /* 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)
+    {
+      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;
+    }
+
+  /* it's a original request from the users */
+
+  /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
+   * to prevent races on shmget.
+   */
+
+  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);
+    }
+
+  /* attempt to open the key */
+
+  /* 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 ?.
+   */
+
+
+
+  /* walk the list of known keys and return the id if found. remember, we are
+   * authoritative...
+   */
+
+  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;
+           }
+
+         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;
+    }
+
+  /* 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;
+       }
+    }
+
+  /* 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;
+    }
+
+  conn->impersonate_client ();
+  void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
+  conn->revert_to_self ();
+
+  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;
+    }
+
+  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;
+    }
+
+  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;
+    }
+
+  /* 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);
+  return;
+}
diff --git a/winsup/cygserver/shm.h b/winsup/cygserver/shm.h
new file mode 100755 (executable)
index 0000000..32947f8
--- /dev/null
@@ -0,0 +1,91 @@
+/* cygserver_shm.h
+
+   Copyright 2001 Red Hat Inc.
+   Written by Robert Collins <rbtcollins@hotmail.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 <sys/types.h>
+#include "cygwin/cygserver_transport.h"
+#include "cygwin/cygserver.h"
+
+#define SHM_CREATE 0
+#define SHM_REATTACH 1
+#define SHM_ATTACH 2
+#define SHM_DETACH 3
+
+
+class client_request_shm : public client_request
+{
+  public:
+#ifndef __INSIDE_CYGWIN__
+  virtual void serve (transport_layer_base *conn, process_cache *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
+{
+public:
+  virtual void cleanup (long winpid);
+};
+#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;
+};
+
+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;
+};
+
+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
diff --git a/winsup/cygserver/threaded_queue.cc b/winsup/cygserver/threaded_queue.cc
new file mode 100644 (file)
index 0000000..5fb22b1
--- /dev/null
@@ -0,0 +1,249 @@
+/* threaded_queue.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <windows.h>
+#include <sys/types.h>
+#include "wincap.h"
+#include "threaded_queue.h"
+#define DEBUG 1
+#define debug_printf if (DEBUG) printf
+
+/* threaded_queue */
+
+DWORD WINAPI
+worker_function (LPVOID LpParam)
+{
+  class threaded_queue *queue = (class threaded_queue *) LpParam;
+  class queue_request *request;
+  /* FIXME use a threadsafe pop instead for speed? */
+  while (queue->active)
+    {
+      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;
+    }
+  queue->running--;
+  ExitThread (0);
+}
+
+void
+threaded_queue::create_workers ()
+{
+  InitializeCriticalSection (&queuelock);
+  if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+    {
+      printf ("Failed to create event queue (%lu), terminating\n",
+             GetLastError ());
+      exit (1);
+    }
+  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++)
+    {
+      HANDLE hThread;
+      DWORD tid;
+      hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
+      if (hThread == NULL)
+       {
+         printf ("Failed to create thread (%lu), terminating\n",
+                 GetLastError ());
+         exit (1);
+       }
+      CloseHandle (hThread);
+      running++;
+    }
+}
+
+void
+threaded_queue::cleanup ()
+{
+  /* 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)
+    {
+      queue_process_param *t = reqloop;
+      reqloop = reqloop->next;
+      delete t;
+    }
+  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);
+}
+
+/* FIXME: return success or failure */
+void
+threaded_queue::add (queue_request * therequest)
+{
+  /* safe to not "Try" because workers don't hog this, they wait on the event
+   */
+  EnterCriticalSection (&queuelock);
+  if (!running)
+    {
+      printf ("No worker threads to handle request!\n");
+    }
+  if (!request)
+    request = therequest;
+  else
+    {
+      /* add to the queue end. */
+      queue_request *listrequest = request;
+      while (listrequest->next)
+       listrequest = listrequest->next;
+      listrequest->next = therequest;
+    }
+  PulseEvent (event);
+  LeaveCriticalSection (&queuelock);
+}
+
+/* FIXME: return success or failure rather than quitting */
+void
+threaded_queue::process_requests (queue_process_param * params,
+                                 threaded_queue_thread_function *
+                                 request_loop)
+{
+  if (params->start (request_loop, this) == false)
+    exit (1);
+  params->next =
+    (queue_process_param *) InterlockedExchangePointer (&process_head,
+                                                       params);
+}
+
+/* queue_process_param */
+/* How does a constructor return an error? */
+queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
+interruptible
+(ninterruptible)
+{
+  if (!interruptible)
+    return;
+  debug_printf ("creating an interruptible processing thread\n");
+  if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+    {
+      printf ("Failed to create interrupt event (%lu), terminating\n",
+             GetLastError ());
+      exit (1);
+    }
+}
+
+queue_process_param::~queue_process_param ()
+{
+  if (running)
+    stop ();
+  if (!interruptible)
+    return;
+  CloseHandle (interrupt);
+}
+
+bool
+  queue_process_param::start (threaded_queue_thread_function * request_loop,
+                             threaded_queue * thequeue)
+{
+  queue = thequeue;
+  hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
+  if (hThread)
+    {
+      running = true;
+      return true;
+    }
+  printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
+  return false;
+}
+
+void
+queue_process_param::stop ()
+{
+  if (interruptible)
+    {
+      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)
+       {
+         printf ("Process thread didn't shutdown cleanly after 200ms!\n");
+         exit (1);
+       }
+      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");
+       }
+      running = false;
+    }
+  CloseHandle (hThread);
+}
+
+/* queue_request */
+queue_request::queue_request ():next (NULL)
+{
+}
+
+void
+queue_request::process (void)
+{
+  printf ("\n**********************************************\n"
+         "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
+         "fault !!!\n" "***********************************************\n");
+}
diff --git a/winsup/cygserver/transport.cc b/winsup/cygserver/transport.cc
new file mode 100644 (file)
index 0000000..7c6e5fe
--- /dev/null
@@ -0,0 +1,92 @@
+/* cygserver_transport.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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_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 *temp;
+  /* currently there is only the base class! */
+  if (wincap.is_winnt ())
+    temp = new transport_layer_pipes ();
+  else
+    temp = new transport_layer_base ();
+  return temp;
+}
+
+
+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;
+}
+
+void
+transport_layer_base::impersonate_client ()
+{
+}
+
+void
+transport_layer_base::revert_to_self ()
+{
+}
diff --git a/winsup/cygserver/transport_pipes.cc b/winsup/cygserver/transport_pipes.cc
new file mode 100644 (file)
index 0000000..5b5a017
--- /dev/null
@@ -0,0 +1,205 @@
+/* cygserver_transport_pipes.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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_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
+#endif
+
+transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
+{
+  pipe = new_pipe;
+  if (inited != true)
+    init_security();
+};
+
+transport_layer_pipes::transport_layer_pipes () 
+{
+  pipe = NULL;
+  strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
+  if (inited != true)
+    init_security();
+}
+
+
+void
+transport_layer_pipes::init_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;
+}
+
+void
+transport_layer_pipes::listen ()
+{
+  /* no-op */
+}
+
+class transport_layer_pipes *
+transport_layer_pipes::accept ()
+{
+  if (pipe)
+    {
+      debug_printf ("Already have a pipe in this %p\n",this);
+      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)
+    {
+      debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
+      return NULL;
+    }
+
+  if ( !ConnectNamedPipe ( pipe, NULL ) &&
+     GetLastError () != ERROR_PIPE_CONNECTED)
+    {
+      printf ("error connecting to pipe (%lu)\n.", GetLastError ());
+      CloseHandle (pipe);
+      pipe = NULL;
+      return NULL;
+    }
+  
+  transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
+  pipe = NULL;
+
+  return new_conn;  
+}
+
+void
+transport_layer_pipes::close()
+{
+  debug_printf ("closing pipe %p\n", pipe);
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      FlushFileBuffers (pipe);
+      DisconnectNamedPipe (pipe);
+      CloseHandle (pipe);
+    }
+}
+
+ssize_t
+transport_layer_pipes::read (char *buf, size_t len)
+{
+  debug_printf ("reading from pipe %p\n", pipe);
+  if (!pipe || pipe == INVALID_HANDLE_VALUE)
+    return -1;
+
+  DWORD bytes_read;
+  DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
+  if (!rc)
+    {
+      debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
+      return -1;
+    }
+  return bytes_read;
+}
+
+ssize_t
+transport_layer_pipes::write (char *buf, size_t len)
+{
+  debug_printf ("writing to pipe %p\n", pipe);
+  DWORD bytes_written, rc;
+  if (!pipe || pipe == INVALID_HANDLE_VALUE)
+    return -1;
+
+  rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
+  if (!rc)
+    {
+      debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
+      return -1;
+    }
+  return bytes_written;
+}
+
+bool
+transport_layer_pipes::connect ()
+{
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      debug_printf ("Already have a pipe in this %p\n",this);
+      return false;
+    }
+
+  while (1)
+    {
+      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)
+        {
+          debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
+          pipe = NULL;
+          return false;
+        }
+      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.
+       */ 
+    }
+}
+
+void
+transport_layer_pipes::impersonate_client ()
+{
+  debug_printf ("impersonating pipe %p\n", pipe);
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      BOOL rv = ImpersonateNamedPipeClient (pipe);
+      if (!rv)
+       debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
+    }
+  debug_printf("I am who you are\n");
+}
+
+void
+transport_layer_pipes::revert_to_self ()
+{
+  RevertToSelf (); 
+  debug_printf("I am who I yam\n");
+}
+
diff --git a/winsup/cygserver/transport_sockets.cc b/winsup/cygserver/transport_sockets.cc
new file mode 100644 (file)
index 0000000..6b75365
--- /dev/null
@@ -0,0 +1,131 @@
+/* cygserver_transport_sockets.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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
+
+transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
+{
+  /* 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)
+{
+  sockdetails.sa_family = AF_UNIX;
+  strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
+  sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
+}
+
+void
+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);
+}
+
+class transport_layer_sockets *
+transport_layer_sockets::accept ()
+{
+  /* FIXME: check we have listened */
+  int new_fd;
+
+  if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
+    {
+      printf ("Nup, could' accept. %d\n",errno);
+      return NULL;
+    }
+  
+  transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
+
+  return new_conn;  
+
+}
+
+void
+transport_layer_sockets::close()
+{
+  /* FIXME - are we open? */
+  ::close (fd);
+}
+
+ssize_t
+transport_layer_sockets::read (char *buf, size_t len)
+{
+  /* FIXME: are we open? */
+  return ::read (fd, buf, len);
+}
+
+ssize_t
+transport_layer_sockets::write (char *buf, size_t len)
+{
+  /* FIXME: are we open? */
+  return ::write (fd, buf, len);
+}
+
+bool
+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)
+    {
+      debug_printf("client connect failure %d\n", errno);
+      ::close (fd);
+      return false;
+    }
+  return true;
+}
diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc
new file mode 100755 (executable)
index 0000000..57f92c2
--- /dev/null
@@ -0,0 +1,548 @@
+/* cygserver.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Egor Duda <deo@logos-m.ru>
+
+   This file is part of Cygwin.
+
+   This software is a copyrighted work licensed under the terms of the
+   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+   details. */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <signal.h>
+#include "wincap.h"
+#include "cygwin_version.h"
+
+#include "getopt.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"
+
+/* for quieter operation, set to 0 */
+#define DEBUG 0
+#define debug_printf if (DEBUG) printf
+
+GENERIC_MAPPING access_mapping;
+static class transport_layer_base *transport;
+
+DWORD request_count = 0;
+
+BOOL
+setup_privileges ()
+{
+  BOOL rc, ret_val;
+  HANDLE hToken = NULL;
+  TOKEN_PRIVILEGES sPrivileges;
+
+  rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ;
+  if ( !rc )
+    {
+      printf ( "error opening process token (%lu)\n", 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 () );
+      ret_val = FALSE;
+      goto out;
+    }
+  sPrivileges.PrivilegeCount = 1 ;
+  sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
+  rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ;
+  if ( !rc )
+    {
+      printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () );
+      ret_val = FALSE;
+      goto out;
+    }
+
+  access_mapping.GenericRead = FILE_READ_DATA;
+  access_mapping.GenericWrite = FILE_WRITE_DATA;
+  access_mapping.GenericExecute = 0;
+  access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA;
+
+  ret_val = TRUE;
+
+out:
+  CloseHandle ( hToken );
+  return ret_val;
+}
+
+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 = 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))
+    {
+      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))
+    {
+      printf ( "error getting handle SD (%lu)\n", GetLastError ());
+      goto out;
+    }
+
+  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 (!status)
+    {
+      printf ( "access to object denied\n");
+      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 ());
+      goto out;
+    }
+  debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
+
+  ret_val = 0;
+                    
+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);
+}
+
+void
+client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
+{
+  HANDLE from_process_handle = NULL;
+  HANDLE to_process_handle = NULL;
+  HANDLE token_handle = NULL;
+  DWORD rc;
+
+  if (header.cb != sizeof (req))
+    {
+      header.error_code = EINVAL;
+      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)
+    {
+      printf ("error opening process (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  debug_printf ("Impersonating client\n");
+  conn->impersonate_client ();
+  
+  debug_printf ("about to open thread token\n");
+  rc = OpenThreadToken (GetCurrentThread (),
+                       TOKEN_QUERY,
+                        TRUE,
+                       &token_handle);
+
+  debug_printf ("opened thread token, rc=%lu\n", rc);
+  conn->revert_to_self ();
+
+  if (!rc)
+    {
+      printf ("error opening thread token (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  if (check_and_dup_handle (from_process_handle, to_process_handle,
+                            token_handle,
+                            GENERIC_READ,
+                            req.from_master,
+                            &req.from_master, TRUE) != 0)
+    {
+      printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      goto out;
+    }
+
+  if (req.to_master)
+    {
+      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;
+       }
+    }
+
+#if DEBUG
+  printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
+                               req.from_master, req.to_master);
+#endif
+
+  header.error_code = 0;
+
+out:
+  if (from_process_handle)
+    CloseHandle (from_process_handle);
+  if (to_process_handle)
+    CloseHandle (to_process_handle);
+  if (token_handle)
+    CloseHandle (token_handle);
+}
+
+void
+client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
+{
+  if (header.cb != sizeof (version))
+    {
+      header.error_code = EINVAL;
+      return;
+    }
+  header.error_code = 0;
+  version.major = CYGWIN_SERVER_VERSION_MAJOR;
+  version.api   = CYGWIN_SERVER_VERSION_API;
+  version.minor = CYGWIN_SERVER_VERSION_MINOR;
+  version.patch = CYGWIN_SERVER_VERSION_PATCH;
+}
+
+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;
+};
+
+class server_process_param : public queue_process_param
+{
+  public:
+    transport_layer_base *transport;
+    server_process_param () : queue_process_param (false) {};
+};
+
+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);
+};
+class server_request_queue request_queue;
+
+static DWORD WINAPI
+request_loop (LPVOID LpParam)
+{
+  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)
+  {
+    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);
+  }
+  return 0;
+}
+
+/* TODO: check we are not being asked to service a already serviced transport */
+void
+server_request_queue::process_requests (transport_layer_base *transport)
+{
+  class server_process_param *params = new server_process_param;
+  params->transport = transport;
+  threaded_queue::process_requests (params, request_loop);
+}
+
+void
+client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
+{
+  /* 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;
+}
+
+server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
+{
+  conn = newconn;
+  cache = newcache;
+}
+
+void
+server_request::process ()
+{
+  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");
+
+  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);
+
+  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");
+    }
+
+  if (req->header.cb != req_ptr->cb)
+    {
+      debug_printf ("Mismatch in request buffer sizes\n");
+      goto out;
+    }
+
+  if (req->header.cb)
+    {
+
+      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);
+    }
+
+  /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
+  req->serve (conn, cache);
+
+  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;
+    }
+
+  debug_printf("Sent reply, size (%ld)\n",bytes_written);
+  printf (".");
+
+out:
+  conn->close ();
+  delete conn;
+  if (req)
+    delete (req);
+}
+
+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);
+}
+
+void
+handle_signal (int signal)
+{
+  /* 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;
+}
+
+struct option longopts[] = {
+  {"shutdown", no_argument, NULL, 's'},
+  {0, no_argument, NULL, 0}
+};
+
+char opts[] = "s";
+
+int
+main (int argc, char **argv)
+{
+  int shutdown=0;
+  char i;
+
+  while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+    switch (i)
+      {
+      case 's':
+        shutdown = 1;
+        break;
+      default:
+        break;
+       /*NOTREACHED*/
+      }
+
+  wincap.init();
+  if (wincap.has_security ())
+    setup_privileges ();
+  transport = create_server_transport ();
+
+  if (shutdown)
+    {
+      if (!transport->connect())
+       {
+         printf ("couldn't establish connection with server\n");
+         exit (1);
+       }
+      client_request_shutdown *request =
+       new client_request_shutdown ();
+      request->send (transport);
+      transport->close();
+      delete transport;
+      delete request;
+      exit(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);
+  setbuf (stdout, NULL);
+  printf ("daemon version %s starting up", version);
+  if (signal (SIGQUIT, handle_signal) == SIG_ERR)
+    {
+      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);
+  printf (".");
+  cache.create_workers ();
+  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);
+   */
+  /* 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 ();
+  printf ("All pending requests processed\n");
+  transport->close ();
+  printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
+  cache.cleanup ();
+  printf ("All outstanding process-cache activities completed\n");
+  printf ("daemon shutdown\n");
+}
diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc
new file mode 100755 (executable)
index 0000000..8a1eeaf
--- /dev/null
@@ -0,0 +1,207 @@
+/* cygserver_client.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Egor Duda <deo@logos-m.ru>
+
+   This file is part of Cygwin.
+
+   This software is a copyrighted work licensed under the terms of the
+   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+   details. */
+
+#ifdef __OUTSIDE_CYGWIN__
+#undef __INSIDE_CYGWIN__
+#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 <errno.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 "cygwin/cygserver.h"
+
+/* 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;
+
+client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version))
+{
+  buffer = (char *)&version;
+}
+
+client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
+{
+  buffer = (char *)&req;
+  req.pid = 0;
+  req.master_pid = 0;
+  req.from_master = NULL;
+  req.to_master = NULL;
+}
+
+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))
+{
+  buffer = (char *)&req;
+  req.pid = npid;
+  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;
+}
+
+client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size)
+{
+}
+
+client_request::~client_request ()
+{
+}
+
+client_request::operator class request_header ()
+{
+  return header;
+}
+
+void
+client_request::send (transport_layer_base *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))
+    {
+      header.error_code = -1;
+      debug_printf ("bytes written != request size\n");
+      return;
+    }
+
+  debug_printf("Sent request, size (%ld)\n",bytes_written);
+
+  if ((bytes_read = conn->read ((char *)&header, sizeof (header)))
+    != sizeof (header) || (header.cb &&
+    (bytes_read = conn->read (buffer, header.cb) ) != header.cb))
+    {
+      header.error_code = -1;
+      debug_printf("failed reading response \n");
+      return;
+    }
+  debug_printf ("completed ok\n");
+}
+
+/* Oh, BTW: Fix the procedural basis and make this more intuitive. */
+
+int
+cygserver_request (client_request * req)
+{
+  class transport_layer_base *transport;
+
+  if (!req || allow_daemon != TRUE)
+    return -1;
+
+  /* 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;
+
+  transport = create_server_transport ();
+
+  /* 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 ())
+    {
+      delete transport;
+      return -1;
+    }
+
+  debug_printf ("connected to server %p\n", transport);
+
+  req->send(transport);
+
+  transport->close ();
+
+  delete transport;
+
+  return 0;
+}
+
+#if 0
+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);
+}
+#endif
+
+void
+cygserver_init ()
+{
+  int rc;
+  if (allow_daemon != TRUE)
+    {
+      cygserver_running = CYGSERVER_DEAD;
+      return;
+    }
+
+  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;
+}
diff --git a/winsup/cygwin/cygserver_process.cc b/winsup/cygwin/cygserver_process.cc
new file mode 100755 (executable)
index 0000000..a5173af
--- /dev/null
@@ -0,0 +1,389 @@
+/* cygserver_process.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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)
+{
+  /* 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;
+}
+
+process_cache::~process_cache ()
+{
+}
+
+class process *
+process_cache::process (long pid)
+{
+  class process *entry = head;
+  /* TODO: make this more granular, so a search doesn't involve the write lock */
+  EnterCriticalSection (&cache_write_access);
+  if (!entry)
+    {
+      entry = new class process (pid);
+      entry->next =
+       (class process *) InterlockedExchangePointer (&head, entry);
+      PulseEvent (cache_add_trigger);
+    }
+  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;
+}
+
+static DWORD WINAPI
+request_loop (LPVOID LpParam)
+{
+  class process_process_param *params = (process_process_param *) LpParam;
+  return params->request_loop ();
+}
+
+void
+process_cache::process_requests ()
+{
+  class process_process_param *params = new process_process_param;
+  threaded_queue::process_requests (params, request_loop);
+}
+
+void
+process_cache::add_task (class process * theprocess)
+{
+  /* 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);
+}
+
+/* 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)
+    {
+      entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
+      if (entry != theprocess)
+        {
+          printf ("Bug encountered, process cache corrupted\n");
+         exit (1);
+        }
+    }
+  else
+    {
+      while (entry->next && entry->next != theprocess)
+       entry = entry->next;
+      class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
+      if (temp != theprocess)
+       {
+         printf ("Bug encountered, process cache corrupted\n");
+         exit (1);
+       }
+    }
+  LeaveCriticalSection (&cache_write_access);
+  /* Process any cleanup tasks */
+  add_task (theprocess);
+}
+       
+      
+/* 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.
+ */
+int
+process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
+                               ssize_t max_copy, int begin_at)
+{
+  /* 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)
+    {
+      /* hack */
+      if (entry->exit_code () == STILL_ACTIVE)
+       {
+         *hto = entry->handle ();
+         *eto = entry;
+         count++;
+         hto++;
+         eto++;
+       }
+      entry = entry->next;
+    }
+  return count;
+}
+
+/* process's */
+/* global process crit section */
+static CRITICAL_SECTION process_access;
+static pthread_once_t process_init;
+
+void
+do_process_init (void)
+{
+  InitializeCriticalSection (&process_access);
+  /* we don't have a cache shutdown capability today */
+}
+
+process::process (long pid):
+winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
+{
+  pthread_once (&process_init, do_process_init);
+  EnterCriticalSection (&process_access);
+  thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+  if (!thehandle)
+    {
+      printf ("unable to obtain handle for new cache process %ld\n", pid);
+      thehandle = INVALID_HANDLE_VALUE;
+    }
+  debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
+  InitializeCriticalSection (&access);
+  LeaveCriticalSection (&process_access);
+}
+
+process::~process ()
+{
+  DeleteCriticalSection (&access);
+}
+
+HANDLE
+process::handle ()
+{
+//  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;
+}
+
+DWORD process::exit_code ()
+{
+  if (_exit_status != STILL_ACTIVE)
+    return _exit_status;
+  bool
+    err = GetExitCodeProcess (thehandle, &_exit_status);
+  if (!err)
+    {
+      debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
+      thehandle = INVALID_HANDLE_VALUE;
+      return _exit_status;
+    }
+  else if (_exit_status == STILL_ACTIVE)
+    return _exit_status;
+  /* add new cleanup task etc etc ? */
+  return _exit_status;
+}
+
+/* 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 ()
+{
+  /* Serialize this */
+  EnterCriticalSection (&access);
+  InterlockedIncrement (&(long)cleaning_up);
+  class cleanup_routine *entry = head;
+  while (entry)
+    {
+      class cleanup_routine *temp;
+      entry->cleanup (winpid);
+      temp = entry->next;
+      delete entry;
+      entry = temp;
+    }
+  LeaveCriticalSection (&access);
+}
+
+bool
+process::add_cleanup_routine (class cleanup_routine *new_cleanup)
+{
+  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)
+    {
+      LeaveCriticalSection (&access);
+      return false;
+    }
+  new_cleanup->next = head;
+  head = new_cleanup;
+  LeaveCriticalSection (&access);
+  return true;
+}
+
+/* process_cleanup */
+void
+process_cleanup::process ()
+{
+  theprocess->cleanup ();
+  delete theprocess;
+}
+
+/* process_process_param */
+DWORD
+process_process_param::request_loop ()
+{
+  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;
+}
diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc
new file mode 100755 (executable)
index 0000000..c29938b
--- /dev/null
@@ -0,0 +1,552 @@
+/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
+
+   Copyright 2001 Red Hat, Inc.
+
+   Originally written by Robert Collins <robert.collins@hotmail.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. */
+
+
+#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 <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 <stdio.h>
+//#include "thread.h"
+#ifndef __INSIDE_CYGWIN__
+#define __INSIDE_CYGWIN__
+#include <sys/shm.h>
+#undef __INSIDE_CYGWIN__
+#else
+#include <sys/shm.h>
+#endif
+//#include "perprocess.h"
+#include <threaded_queue.h>
+#include <cygwin/cygserver_process.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.
+ * 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;
+}
+
+
+client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+{
+  buffer = (char *) &parameters;
+}
+
+/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
+ * Solution: manually calculate the next id value 
+ */
+
+#if 0
+extern "C" void *
+shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
+{
+  class shmid_ds *shm = (class shmid_ds *) shmid;      //FIXME: verifyable object test
+
+  if (shmaddr)
+    {
+      //FIXME: requested base address ?!
+      set_errno (EINVAL);
+      return (void *) -1;
+    }
+
+  void *rv = MapViewOfFile (shm->attachmap,
+
+
+                           (parameters.in.shmflg & SHM_RDONLY) ?
+                           FILE_MAP_READ : FILE_MAP_WRITE, 0,
+                           0, 0);
+
+  if (!rv)
+    {
+      //FIXME: translate GetLastError()
+      set_errno (EACCES);
+      return (void *) -1;
+    }
+
+/* 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;
+}
+#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;
+/* must be long for InterlockedIncrement */
+static long new_id = 0;
+static long new_private_key = 0;
+
+void
+client_request_shm::serve (transport_layer_base * conn,
+                              process_cache * cache)
+{
+//  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)
+    {
+      debug_printf ("error opening process (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      return;
+    }
+
+  conn->impersonate_client ();
+
+  rc = OpenThreadToken (GetCurrentThread (),
+                       TOKEN_QUERY, TRUE, &token_handle);
+
+  conn->revert_to_self ();
+
+  if (!rc)
+    {
+      debug_printf ("error opening thread token (%lu)\n", GetLastError ());
+      header.error_code = EACCES;
+      CloseHandle (from_process_handle);
+      return;
+    }
+
+
+  /* we trust the clients request - we will be doing it as them, and
+   * the worst they can do is open their own permissions 
+   */
+
+
+  SECURITY_ATTRIBUTES sa;
+  sa.nLength = sizeof (sa);
+  sa.lpSecurityDescriptor = psd;
+  sa.bInheritHandle = TRUE;    /* the memory structures inherit ok */
+
+  char *shmname = NULL, *shmaname = NULL;
+  char stringbuf[29], stringbuf1[29];
+
+  /* TODO: make this code block a function! */
+  if (parameters.in.type == SHM_REATTACH)
+    {
+      /* just find and fill out the existing shm_id */
+      shmnode *tempnode = shm_head;
+      while (tempnode)
+       {
+         if (tempnode->shm_id == parameters.in.shm_id)
+           {
+             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)
+               {
+                 debug_printf ("error duplicating attachmap handle (%lu)\n",
+                               GetLastError ());
+                 header.error_code = EACCES;
+               }
+             CloseHandle (token_handle);
+             return;
+           }
+         tempnode = tempnode->next;
+       }
+      header.error_code = EINVAL;
+      CloseHandle (token_handle);
+      return;
+    }
+
+  /* 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)
+    {
+      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;
+    }
+
+  /* it's a original request from the users */
+
+  /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
+   * to prevent races on shmget.
+   */
+
+  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);
+    }
+
+  /* attempt to open the key */
+
+  /* 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 ?.
+   */
+
+
+
+  /* walk the list of known keys and return the id if found. remember, we are
+   * authoritative...
+   */
+
+  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;
+           }
+
+         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;
+    }
+
+  /* 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;
+       }
+    }
+
+  /* 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;
+    }
+
+  conn->impersonate_client ();
+  void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
+  conn->revert_to_self ();
+
+  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;
+    }
+
+  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;
+    }
+
+  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;
+    }
+
+  /* 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);
+  return;
+}
diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h
new file mode 100644 (file)
index 0000000..32947f8
--- /dev/null
@@ -0,0 +1,91 @@
+/* cygserver_shm.h
+
+   Copyright 2001 Red Hat Inc.
+   Written by Robert Collins <rbtcollins@hotmail.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 <sys/types.h>
+#include "cygwin/cygserver_transport.h"
+#include "cygwin/cygserver.h"
+
+#define SHM_CREATE 0
+#define SHM_REATTACH 1
+#define SHM_ATTACH 2
+#define SHM_DETACH 3
+
+
+class client_request_shm : public client_request
+{
+  public:
+#ifndef __INSIDE_CYGWIN__
+  virtual void serve (transport_layer_base *conn, process_cache *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
+{
+public:
+  virtual void cleanup (long winpid);
+};
+#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;
+};
+
+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;
+};
+
+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
diff --git a/winsup/cygwin/cygserver_transport.cc b/winsup/cygwin/cygserver_transport.cc
new file mode 100755 (executable)
index 0000000..7c6e5fe
--- /dev/null
@@ -0,0 +1,92 @@
+/* cygserver_transport.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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_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 *temp;
+  /* currently there is only the base class! */
+  if (wincap.is_winnt ())
+    temp = new transport_layer_pipes ();
+  else
+    temp = new transport_layer_base ();
+  return temp;
+}
+
+
+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;
+}
+
+void
+transport_layer_base::impersonate_client ()
+{
+}
+
+void
+transport_layer_base::revert_to_self ()
+{
+}
diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc
new file mode 100755 (executable)
index 0000000..5b5a017
--- /dev/null
@@ -0,0 +1,205 @@
+/* cygserver_transport_pipes.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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_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
+#endif
+
+transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
+{
+  pipe = new_pipe;
+  if (inited != true)
+    init_security();
+};
+
+transport_layer_pipes::transport_layer_pipes () 
+{
+  pipe = NULL;
+  strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
+  if (inited != true)
+    init_security();
+}
+
+
+void
+transport_layer_pipes::init_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;
+}
+
+void
+transport_layer_pipes::listen ()
+{
+  /* no-op */
+}
+
+class transport_layer_pipes *
+transport_layer_pipes::accept ()
+{
+  if (pipe)
+    {
+      debug_printf ("Already have a pipe in this %p\n",this);
+      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)
+    {
+      debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
+      return NULL;
+    }
+
+  if ( !ConnectNamedPipe ( pipe, NULL ) &&
+     GetLastError () != ERROR_PIPE_CONNECTED)
+    {
+      printf ("error connecting to pipe (%lu)\n.", GetLastError ());
+      CloseHandle (pipe);
+      pipe = NULL;
+      return NULL;
+    }
+  
+  transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
+  pipe = NULL;
+
+  return new_conn;  
+}
+
+void
+transport_layer_pipes::close()
+{
+  debug_printf ("closing pipe %p\n", pipe);
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      FlushFileBuffers (pipe);
+      DisconnectNamedPipe (pipe);
+      CloseHandle (pipe);
+    }
+}
+
+ssize_t
+transport_layer_pipes::read (char *buf, size_t len)
+{
+  debug_printf ("reading from pipe %p\n", pipe);
+  if (!pipe || pipe == INVALID_HANDLE_VALUE)
+    return -1;
+
+  DWORD bytes_read;
+  DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
+  if (!rc)
+    {
+      debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
+      return -1;
+    }
+  return bytes_read;
+}
+
+ssize_t
+transport_layer_pipes::write (char *buf, size_t len)
+{
+  debug_printf ("writing to pipe %p\n", pipe);
+  DWORD bytes_written, rc;
+  if (!pipe || pipe == INVALID_HANDLE_VALUE)
+    return -1;
+
+  rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
+  if (!rc)
+    {
+      debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
+      return -1;
+    }
+  return bytes_written;
+}
+
+bool
+transport_layer_pipes::connect ()
+{
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      debug_printf ("Already have a pipe in this %p\n",this);
+      return false;
+    }
+
+  while (1)
+    {
+      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)
+        {
+          debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
+          pipe = NULL;
+          return false;
+        }
+      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.
+       */ 
+    }
+}
+
+void
+transport_layer_pipes::impersonate_client ()
+{
+  debug_printf ("impersonating pipe %p\n", pipe);
+  if (pipe && pipe != INVALID_HANDLE_VALUE)
+    {
+      BOOL rv = ImpersonateNamedPipeClient (pipe);
+      if (!rv)
+       debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
+    }
+  debug_printf("I am who you are\n");
+}
+
+void
+transport_layer_pipes::revert_to_self ()
+{
+  RevertToSelf (); 
+  debug_printf("I am who I yam\n");
+}
+
diff --git a/winsup/cygwin/cygserver_transport_sockets.cc b/winsup/cygwin/cygserver_transport_sockets.cc
new file mode 100755 (executable)
index 0000000..6b75365
--- /dev/null
@@ -0,0 +1,131 @@
+/* cygserver_transport_sockets.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <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
+
+transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
+{
+  /* 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)
+{
+  sockdetails.sa_family = AF_UNIX;
+  strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
+  sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
+}
+
+void
+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);
+}
+
+class transport_layer_sockets *
+transport_layer_sockets::accept ()
+{
+  /* FIXME: check we have listened */
+  int new_fd;
+
+  if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
+    {
+      printf ("Nup, could' accept. %d\n",errno);
+      return NULL;
+    }
+  
+  transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
+
+  return new_conn;  
+
+}
+
+void
+transport_layer_sockets::close()
+{
+  /* FIXME - are we open? */
+  ::close (fd);
+}
+
+ssize_t
+transport_layer_sockets::read (char *buf, size_t len)
+{
+  /* FIXME: are we open? */
+  return ::read (fd, buf, len);
+}
+
+ssize_t
+transport_layer_sockets::write (char *buf, size_t len)
+{
+  /* FIXME: are we open? */
+  return ::write (fd, buf, len);
+}
+
+bool
+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)
+    {
+      debug_printf("client connect failure %d\n", errno);
+      ::close (fd);
+      return false;
+    }
+  return true;
+}
diff --git a/winsup/cygwin/include/cygwin/cygserver.h b/winsup/cygwin/include/cygwin/cygserver.h
new file mode 100755 (executable)
index 0000000..3751863
--- /dev/null
@@ -0,0 +1,135 @@
+/* cygserver.h
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Egor Duda <deo@logos-m.ru>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _CYGSERVER_H_
+#define _CYGSERVER_H_
+
+#define MAX_REQUEST_SIZE 128
+
+#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_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
+;
+
+extern void cygserver_init ();
+
+#define INIT_REQUEST(req,id) \
+       (req).header.cb = sizeof (req); \
+       (req).header.req_id = id;
+
+struct request_get_version
+{
+  DWORD major, api, minor, patch;
+}
+#ifdef __GNUC__
+  __attribute__ ((packed))
+#endif
+;
+
+struct request_shutdown
+{
+  int foo;
+}
+#ifdef __GNUC__
+  __attribute__ ((packed))
+#endif
+;
+
+struct request_attach_tty
+{
+  DWORD pid, master_pid;
+  HANDLE from_master, to_master;
+}
+#ifdef __GNUC__
+  __attribute__ ((packed))
+#endif
+;
+
+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);
+#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 : public client_request
+{
+  public:
+#ifndef __INSIDE_CYGWIN__
+  virtual void serve (transport_layer_base *conn, class process_cache *cache);
+#endif
+  client_request_get_version::client_request_get_version();
+  struct request_get_version version;
+};
+
+class client_request_shutdown : public client_request
+{
+  public:
+#ifndef __INSIDE_CYGWIN__
+  virtual void serve (transport_layer_base *conn, class process_cache *cache);
+#endif
+  client_request_shutdown ();
+};
+
+class client_request_attach_tty : public client_request
+{
+  public:
+#ifndef __INSIDE_CYGWIN__
+  virtual void serve (transport_layer_base *conn, class process_cache *cache);
+#endif
+  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;};
+  struct request_attach_tty req;
+};
+
+extern int cygserver_request (client_request *);
+
+#endif /* _CYGSERVER+H+ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_process.h b/winsup/cygwin/include/cygwin/cygserver_process.h
new file mode 100755 (executable)
index 0000000..4a49212
--- /dev/null
@@ -0,0 +1,84 @@
+/* cygserver_process.h
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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_PROCESS_
+#define _CYGSERVER_PROCESS_
+
+/* needs threaded_queue.h */
+
+class process_cleanup:public queue_request
+{
+public:
+  virtual void process ();
+  process_cleanup (class process *nprocess) : theprocess (nprocess) {};
+private:
+  class process * theprocess;
+};
+
+class process_process_param:public queue_process_param
+{
+  class process_cache *cache;
+public:
+  DWORD request_loop ();
+  process_process_param ():queue_process_param (true) {};
+};
+
+class cleanup_routine
+{
+public:
+  cleanup_routine () : next (NULL) {};
+  class cleanup_routine * next;
+  /* MUST BE SYNCHRONOUS */
+  virtual void cleanup (long winpid);
+};
+
+class process
+{
+public:
+  HANDLE handle ();
+  long winpid;
+  process (long);
+  ~process ();
+  DWORD exit_code ();
+  class process * next;
+  long refcount;
+  bool add_cleanup_routine (class cleanup_routine *);
+  void cleanup ();
+private:
+  /* used to prevent races-on-delete */
+  CRITICAL_SECTION access;
+  volatile long cleaning_up;
+  class cleanup_routine *head;
+  HANDLE thehandle;
+  DWORD _exit_status;
+};
+
+class process_cache:public threaded_queue
+{
+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;
+
+private:
+  virtual void add_task (class process *);
+  class process *head;
+  CRITICAL_SECTION cache_write_access;
+};
+
+#endif /* _CYGSERVER_PROCESS_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport.h b/winsup/cygwin/include/cygwin/cygserver_transport.h
new file mode 100755 (executable)
index 0000000..1ed16ed
--- /dev/null
@@ -0,0 +1,32 @@
+/* cygserver.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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_TRANSPORT_
+#define _CYGSERVER_TRANSPORT_
+class transport_layer_base *create_server_transport();
+
+/* the base class does nothing. */
+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 ();
+};
+
+#endif /* _CYGSERVER_TRANSPORT_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h b/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h
new file mode 100755 (executable)
index 0000000..9afeebf
--- /dev/null
@@ -0,0 +1,39 @@
+/* cygserver.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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_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);
+};
+#endif /* _CYGSERVER_TRANSPORT_PIPES_ */
diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h b/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h
new file mode 100755 (executable)
index 0000000..94dc43e
--- /dev/null
@@ -0,0 +1,33 @@
+/* cygserver.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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_TRANSPORT_SOCKETS_
+#define _CYGSERVER_TRANSPORT_SOCKETS_
+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);
+};
+#endif /* _CYGSERVER_TRANSPORT_SOCKETS_ */
diff --git a/winsup/cygwin/include/sys/ipc.h b/winsup/cygwin/include/sys/ipc.h
new file mode 100644 (file)
index 0000000..8ddec6d
--- /dev/null
@@ -0,0 +1,52 @@
+/* sys/ipc.h
+
+   Copyright 2001 Red Hat Inc. 
+   Written by Robert Collins <rbtcollins@hotmail.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. */
+
+#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;
+  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
+ */
+#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_RMID 0x0003
+#define IPC_SET  0x0002
+#define IPC_STAT 0x0001
+
+key_t ftok(const char *, int);
+
+#endif /* _SYS_IPC_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/winsup/cygwin/include/sys/shm.h b/winsup/cygwin/include/sys/shm.h
new file mode 100644 (file)
index 0000000..208c5f2
--- /dev/null
@@ -0,0 +1,89 @@
+/* sys/shm.h
+
+   Copyright 2001 Red Hat Inc. 
+   Written by Robert Collins <rbtcollins@hotmail.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. */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifndef _SYS_SHM_H
+#define _SYS_SHM_H
+
+#include <sys/ipc.h>
+
+#define SHM_RDONLY 1
+/* 64 Kb was hardcoded for x86. MS states this may change, but we need it in the header
+ * file.
+ */
+#define SHMLBA     65536
+#define SHM_RND           1
+
+typedef long int shmatt_t;
+
+#if defined(__INSIDE_CYGWIN__) && defined(__cplusplus)
+
+class _shmattach {
+public:
+  void *data;
+  int shmflg;
+  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;
+  void *mapptr;
+};
+
+class shmnode {
+public:
+  class shmid_ds * shmds;
+  int shm_id;
+  class shmnode *next;
+  key_t key;
+  HANDLE filemap;
+  HANDLE attachmap;
+  class _shmattach *attachhead;
+};
+
+#else
+/* this is what we return when queried. It has no bitwise correspondence
+ * the internal structures 
+ */
+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;
+};
+#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 */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/winsup/cygwin/ipc.cc b/winsup/cygwin/ipc.cc
new file mode 100644 (file)
index 0000000..192a124
--- /dev/null
@@ -0,0 +1,39 @@
+/* ipc.cc: Single unix specification IPC interface for Cygwin
+
+   Copyright 2001 Red Hat, Inc.
+
+   Originally written by Robert Collins <robert.collins@hotmail.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/ipc.h>
+#include <sys/stat.h>
+
+extern "C"
+{
+
+/* Notes: we return a valid key even if id's low order 8 bits are 0. */
+key_t
+ftok(const char *path, int id)
+{
+  struct stat statbuf;
+  if (stat(path, &statbuf))
+    {
+      /* stat set the appropriate errno for us */
+      return (key_t) -1;
+    }
+  
+  /* dev_t is short for cygwin 
+   * ino_t is long for cygwin
+   * and we need 8 bits for the id.
+   * thus key_t is long long. 
+   */
+  return ((long long) statbuf.st_dev << (5*8)) | (statbuf.st_ino << (8) ) | (id & 0x00ff);
+}
+
+}
diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc
new file mode 100644 (file)
index 0000000..dbe9bb0
--- /dev/null
@@ -0,0 +1,499 @@
+/* shm.cc: Single unix specification IPC interface for Cygwin
+
+   Copyright 2001 Red Hat, Inc.
+
+   Originally written by Robert Collins <robert.collins@hotmail.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/stat.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 <sys/shm.h>
+#include "perprocess.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;
+}
+
+client_request_shm::client_request_shm (int ntype, int nshm_id):
+client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+{
+  buffer = (char *) &parameters;
+  parameters.in.shm_id = nshm_id;
+  parameters.in.type = SHM_REATTACH;
+  parameters.in.pid = GetCurrentProcessId ();
+}
+
+client_request_shm::client_request_shm (int ntype, int nshm_id, pid_t npid):
+client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
+{
+  buffer = (char *) &parameters;
+  parameters.in.shm_id = nshm_id;
+  parameters.in.type = ntype;
+  parameters.in.pid = npid;
+}
+
+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))
+{
+  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);
+}
+
+static shmnode *shm_head = NULL;
+
+static shmnode *
+build_inprocess_shmds (HANDLE hfilemap, HANDLE hattachmap, key_t key,
+                      int shm_id)
+{
+  HANDLE filemap = hfilemap;
+  void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
+
+  if (!mapptr)
+    {
+      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;
+    }
+
+  /* Now get the user data */
+  HANDLE attachmap = hattachmap;
+  shmid_ds *shmtemp = new shmid_ds;
+  if (!shmtemp)
+    {
+      system_printf ("failed to malloc shm node\n");
+      set_errno (ENOMEM);
+      UnmapViewOfFile (mapptr);
+      CloseHandle (filemap);
+      CloseHandle (attachmap);
+      /* exit mutex */
+      return NULL;
+    }
+
+  /* get the system node data */
+  *shmtemp = *(shmid_ds *) mapptr;
+
+  /* process local data */
+  shmnode *tempnode = new shmnode;
+
+  tempnode->filemap = filemap;
+  tempnode->attachmap = attachmap;
+  shmtemp->mapptr = mapptr;
+
+  /* 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;
+
+  /* FIXME: leave the system wide shm mutex */
+
+  return tempnode;
+}
+
+int __stdcall
+fixup_shms_after_fork ()
+{
+  shmnode *tempnode = shm_head;
+  while (tempnode)
+    {
+      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;
+    }
+  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
+ */
+
+/* FIXME: after fork, every memory area needs to have the attach count
+ * incremented. This should be done in the server?
+ */
+
+/* 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)
+{
+  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 (void *) -1;
+       }
+
+      if (req->header.error_code)      /* shm_get failed in the daemon */
+       {
+         set_errno (req->header.error_code);
+         delete req;
+         return (void *) -1;
+       }
+
+      /* 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;
+
+    }
+
+  class shmid_ds *shm = tempnode->shmds;
+
+  if (shmaddr)
+    {
+      //FIXME: requested base address ?! (Don't forget to fix the fixup_after_fork too)
+      set_errno (EINVAL);
+      return (void *) -1;
+    }
+
+  void *rv = MapViewOfFile (tempnode->attachmap,
+                           (shmflg & SHM_RDONLY) ? FILE_MAP_READ :
+                           FILE_MAP_WRITE, 0, 0, 0);
+
+  if (!rv)
+    {
+      //FIXME: translate GetLastError()
+      set_errno (EACCES);
+      return (void *) -1;
+    }
+  /* tell the daemon we have attached */
+  client_request_shm *req =
+    new client_request_shm (SHM_ATTACH, shmid);
+  int rc;
+  if ((rc = cygserver_request (req)))
+    {
+      debug_printf ("failed to tell deaemon that we have attached\n");
+    }
+  delete req;
+
+  _shmattach *attachnode = new _shmattach;
+  attachnode->data = rv;
+  attachnode->shmflg = shmflg;
+  attachnode->next =
+    (_shmattach *) InterlockedExchangePointer (&tempnode->attachhead,
+                                              attachnode);
+
+
+  return rv;
+}
+
+/* FIXME: tell the daemon when we detach so it doesn't cleanup incorrectly.
+ */
+extern "C" int
+shmdt (const void *shmaddr)
+{
+  /* 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
+   */
+  
+}
+
+//FIXME: who is allowed to perform STAT? 
+extern "C" int
+shmctl (int shmid, int cmd, struct shmid_ds *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;
+       }
+
+      if (req->header.error_code)      /* shm_get failed in the daemon */
+       {
+         set_errno (req->header.error_code);
+         delete req;
+         return -1;
+       }
+
+      /* 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;
+    }
+
+  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.
+        */
+#if 0
+//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 */
+       
+#endif 
+
+      }
+      break;
+    case IPC_SET:
+    default:
+      set_errno (EINVAL);
+      return -1;
+    }
+  return 0;
+}
+
+/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
+ * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE  |  FILE_MAP_READ
+ * appropriately
+ */
+
+/* FIXME: shmid should be a verifyable object
+ */
+
+/* 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)
+{
+  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 */
+  psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
+                 shmflg & 0x01ff, psd, &sd_size);
+
+  if (key == (key_t) - 1)
+    {
+      set_errno (ENOENT);
+      return -1;
+    }
+
+  /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
+   * to prevent races on shmget.
+   */
+
+  /* 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;
+    }
+
+  if (req->header.error_code)  /* shm_get failed in the daemon */
+    {
+      set_errno (req->header.error_code);
+      delete req;
+      return -1;
+    }
+
+  /* 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 = 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 = 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
+}
diff --git a/winsup/cygwin/threaded_queue.cc b/winsup/cygwin/threaded_queue.cc
new file mode 100755 (executable)
index 0000000..5fb22b1
--- /dev/null
@@ -0,0 +1,249 @@
+/* threaded_queue.cc
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <windows.h>
+#include <sys/types.h>
+#include "wincap.h"
+#include "threaded_queue.h"
+#define DEBUG 1
+#define debug_printf if (DEBUG) printf
+
+/* threaded_queue */
+
+DWORD WINAPI
+worker_function (LPVOID LpParam)
+{
+  class threaded_queue *queue = (class threaded_queue *) LpParam;
+  class queue_request *request;
+  /* FIXME use a threadsafe pop instead for speed? */
+  while (queue->active)
+    {
+      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;
+    }
+  queue->running--;
+  ExitThread (0);
+}
+
+void
+threaded_queue::create_workers ()
+{
+  InitializeCriticalSection (&queuelock);
+  if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+    {
+      printf ("Failed to create event queue (%lu), terminating\n",
+             GetLastError ());
+      exit (1);
+    }
+  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++)
+    {
+      HANDLE hThread;
+      DWORD tid;
+      hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
+      if (hThread == NULL)
+       {
+         printf ("Failed to create thread (%lu), terminating\n",
+                 GetLastError ());
+         exit (1);
+       }
+      CloseHandle (hThread);
+      running++;
+    }
+}
+
+void
+threaded_queue::cleanup ()
+{
+  /* 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)
+    {
+      queue_process_param *t = reqloop;
+      reqloop = reqloop->next;
+      delete t;
+    }
+  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);
+}
+
+/* FIXME: return success or failure */
+void
+threaded_queue::add (queue_request * therequest)
+{
+  /* safe to not "Try" because workers don't hog this, they wait on the event
+   */
+  EnterCriticalSection (&queuelock);
+  if (!running)
+    {
+      printf ("No worker threads to handle request!\n");
+    }
+  if (!request)
+    request = therequest;
+  else
+    {
+      /* add to the queue end. */
+      queue_request *listrequest = request;
+      while (listrequest->next)
+       listrequest = listrequest->next;
+      listrequest->next = therequest;
+    }
+  PulseEvent (event);
+  LeaveCriticalSection (&queuelock);
+}
+
+/* FIXME: return success or failure rather than quitting */
+void
+threaded_queue::process_requests (queue_process_param * params,
+                                 threaded_queue_thread_function *
+                                 request_loop)
+{
+  if (params->start (request_loop, this) == false)
+    exit (1);
+  params->next =
+    (queue_process_param *) InterlockedExchangePointer (&process_head,
+                                                       params);
+}
+
+/* queue_process_param */
+/* How does a constructor return an error? */
+queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
+interruptible
+(ninterruptible)
+{
+  if (!interruptible)
+    return;
+  debug_printf ("creating an interruptible processing thread\n");
+  if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
+    {
+      printf ("Failed to create interrupt event (%lu), terminating\n",
+             GetLastError ());
+      exit (1);
+    }
+}
+
+queue_process_param::~queue_process_param ()
+{
+  if (running)
+    stop ();
+  if (!interruptible)
+    return;
+  CloseHandle (interrupt);
+}
+
+bool
+  queue_process_param::start (threaded_queue_thread_function * request_loop,
+                             threaded_queue * thequeue)
+{
+  queue = thequeue;
+  hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
+  if (hThread)
+    {
+      running = true;
+      return true;
+    }
+  printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
+  return false;
+}
+
+void
+queue_process_param::stop ()
+{
+  if (interruptible)
+    {
+      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)
+       {
+         printf ("Process thread didn't shutdown cleanly after 200ms!\n");
+         exit (1);
+       }
+      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");
+       }
+      running = false;
+    }
+  CloseHandle (hThread);
+}
+
+/* queue_request */
+queue_request::queue_request ():next (NULL)
+{
+}
+
+void
+queue_request::process (void)
+{
+  printf ("\n**********************************************\n"
+         "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
+         "fault !!!\n" "***********************************************\n");
+}
diff --git a/winsup/cygwin/threaded_queue.h b/winsup/cygwin/threaded_queue.h
new file mode 100755 (executable)
index 0000000..b330d96
--- /dev/null
@@ -0,0 +1,67 @@
+/* threaded_queue.h
+
+   Copyright 2001 Red Hat Inc.
+
+   Written by Robert Collins <rbtcollins@hotmail.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 _THREADED_QUEUE_
+#define _THREADED_QUEUE_
+
+/* a specific request */
+
+class queue_request
+{
+  public:
+    class queue_request *next;
+    virtual void process ();
+    queue_request();
+};
+
+
+typedef DWORD WINAPI threaded_queue_thread_function (LPVOID);
+/* parameters for a request finding and submitting loop */
+
+class queue_process_param
+{
+  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;
+};
+
+/* a queue to allocate requests from n submission loops to x worker threads */
+
+class threaded_queue
+{
+  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;
+};
+
+#endif /* _THREADED_QUEUE_ */