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

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin')
-rw-r--r--winsup/cygwin/cygrun.c67
-rwxr-xr-xwinsup/cygwin/cygserver.cc773
-rwxr-xr-xwinsup/cygwin/cygserver_client.cc528
-rw-r--r--winsup/cygwin/cygserver_ipc.h84
-rwxr-xr-xwinsup/cygwin/cygserver_process.cc431
-rwxr-xr-xwinsup/cygwin/cygserver_shm.cc896
-rw-r--r--winsup/cygwin/cygserver_shm.h147
-rwxr-xr-xwinsup/cygwin/cygserver_transport.cc51
-rwxr-xr-xwinsup/cygwin/cygserver_transport_pipes.cc362
-rwxr-xr-xwinsup/cygwin/cygserver_transport_sockets.cc387
-rw-r--r--winsup/cygwin/devices.cc453
-rw-r--r--winsup/cygwin/devices.gperf164
-rw-r--r--winsup/cygwin/devices.h90
-rw-r--r--winsup/cygwin/lib/getopt.c503
-rw-r--r--winsup/cygwin/lib/iruserok.c319
-rwxr-xr-xwinsup/cygwin/threaded_queue.cc408
-rwxr-xr-xwinsup/cygwin/threaded_queue.h127
17 files changed, 5083 insertions, 707 deletions
diff --git a/winsup/cygwin/cygrun.c b/winsup/cygwin/cygrun.c
new file mode 100644
index 000000000..63d9863f7
--- /dev/null
+++ b/winsup/cygwin/cygrun.c
@@ -0,0 +1,67 @@
+/* cygrun.c: testsuite support program
+
+ Copyright 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* This program is intended to be used only by the testsuite. It runs
+ programs without using the cygwin api, so that the just-built dll
+ can be tested without interference from the currently installed
+ dll. */
+
+#include <stdio.h>
+#include <windows.h>
+#include <stdlib.h>
+
+int
+main (int argc, char **argv)
+{
+ STARTUPINFO sa;
+ PROCESS_INFORMATION pi;
+ DWORD ec = 1;
+ char *p;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: cygrun [program]\n");
+ exit (0);
+ }
+
+ SetEnvironmentVariable ("CYGWIN_TESTING", "1");
+ if ((p = getenv ("CYGWIN")) == NULL || (strstr (p, "ntsec") == NULL))
+ {
+ char buf[4096];
+ if (!p)
+ {
+ p = buf;
+ p[0] = '\0';
+ }
+ else
+ {
+ strcpy (buf, p);
+ strcat (buf, " ");
+ }
+ strcat(buf, "ntsec");
+ SetEnvironmentVariable ("CYGWIN", buf);
+ }
+
+ memset (&sa, 0, sizeof (sa));
+ memset (&pi, 0, sizeof (pi));
+ if (!CreateProcess (0, argv[1], 0, 0, 1, 0, 0, 0, &sa, &pi))
+ {
+ fprintf (stderr, "CreateProcess %s failed\n", argv[1]);
+ exit (1);
+ }
+
+ WaitForSingleObject (pi.hProcess, INFINITE);
+
+ GetExitCodeProcess (pi.hProcess, &ec);
+
+ CloseHandle (pi.hProcess);
+ CloseHandle (pi.hThread);
+ return ec;
+}
diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc
new file mode 100755
index 000000000..137730f9e
--- /dev/null
+++ b/winsup/cygwin/cygserver.cc
@@ -0,0 +1,773 @@
+/* cygserver.cc
+
+ Copyright 2001, 2002 Red Hat Inc.
+
+ Written by Egor Duda <deo@logos-m.ru>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "woutsup.h"
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
+#include "cygwin_version.h"
+
+#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
+
+// Version string.
+static const char version[] = "$Revision$";
+
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ * Copied verbatim from "strace.cc".
+ */
+static int
+getfunc (char *in_dst, const char *func)
+{
+ const char *p;
+ const char *pe;
+ char *dst = in_dst;
+ for (p = func; (pe = strchr (p, '(')); p = pe + 1)
+ if (isalnum ((int)pe[-1]) || pe[-1] == '_')
+ break;
+ else if (isspace ((int)pe[-1]))
+ {
+ pe--;
+ break;
+ }
+ if (!pe)
+ pe = strchr (func, '\0');
+ for (p = pe; p > func; p--)
+ if (p != pe && *p == ' ')
+ {
+ p++;
+ break;
+ }
+ if (*p == '*')
+ p++;
+ while (p < pe)
+ *dst++ = *p++;
+
+ *dst++ = ':';
+ *dst++ = ' ';
+ *dst = '\0';
+
+ return dst - in_dst;
+}
+
+/*
+ * Support function for the XXX_printf () macros in "woutsup.h".
+ */
+extern "C" void
+__cygserver__printf (const char *const function, const char *const fmt, ...)
+{
+ const DWORD lasterror = GetLastError ();
+ const int lasterrno = errno;
+
+ va_list ap;
+
+ char *const buf = (char *) alloca (BUFSIZ);
+
+ assert (buf);
+
+ int len = 0;
+
+ if (function)
+ len += getfunc (buf, function);
+
+ va_start (ap, fmt);
+ len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap);
+ va_end (ap);
+
+ len += snprintf (buf + len, BUFSIZ - len, "\n");
+
+ const int actual = (len > BUFSIZ ? BUFSIZ : len);
+
+ write (2, buf, actual);
+
+ errno = lasterrno;
+ SetLastError (lasterror);
+
+ return;
+}
+
+#ifdef DEBUGGING
+
+int __stdcall
+__set_errno (const char *func, int ln, int val)
+{
+ debug_printf ("%s:%d val %d", func, ln, val);
+ return _impure_ptr->_errno = val;
+}
+
+#endif /* DEBUGGING */
+
+GENERIC_MAPPING access_mapping;
+
+static BOOL
+setup_privileges ()
+{
+ BOOL rc, ret_val;
+ HANDLE hToken = NULL;
+ TOKEN_PRIVILEGES sPrivileges;
+
+ rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ;
+ if (!rc)
+ {
+ system_printf ("error opening process token (%lu)", GetLastError ());
+ ret_val = FALSE;
+ goto out;
+ }
+ rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid);
+ if (!rc)
+ {
+ system_printf ("error getting privilege luid (%lu)", 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)
+ {
+ system_printf ("error adjusting privilege level. (%lu)",
+ 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;
+
+ if (from_process != GetCurrentProcess ())
+ {
+ if (!DuplicateHandle (from_process, from_handle,
+ GetCurrentProcess (), &local_handle,
+ 0, bInheritHandle,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("error getting handle(%u) to server (%lu)",
+ (unsigned int)from_handle, GetLastError ());
+ goto out;
+ }
+ } else
+ local_handle = from_handle;
+
+ if (!wincap.has_security ())
+ assert (!from_process_token);
+ else
+ {
+ char sd_buf [1024];
+ PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
+ DWORD bytes_needed;
+ PRIVILEGE_SET ps;
+ DWORD ps_len = sizeof (ps);
+ BOOL status;
+
+ if (!GetKernelObjectSecurity (local_handle,
+ (OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION),
+ sd, sizeof (sd_buf), &bytes_needed))
+ {
+ system_printf ("error getting handle SD (%lu)", GetLastError ());
+ goto out;
+ }
+
+ MapGenericMask (&access, &access_mapping);
+
+ if (!AccessCheck (sd, from_process_token, access, &access_mapping,
+ &ps, &ps_len, &access, &status))
+ {
+ system_printf ("error checking access rights (%lu)",
+ GetLastError ());
+ goto out;
+ }
+
+ if (!status)
+ {
+ system_printf ("access to object denied");
+ goto out;
+ }
+ }
+
+ if (!DuplicateHandle (from_process, from_handle,
+ to_process, to_handle_ptr,
+ access, bInheritHandle, 0))
+ {
+ system_printf ("error getting handle to client (%lu)", GetLastError ());
+ goto out;
+ }
+
+ // verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr);
+
+ ret_val = 0;
+
+ out:
+ if (local_handle && from_process != GetCurrentProcess ())
+ CloseHandle (local_handle);
+
+ return (ret_val);
+}
+
+/*
+ * client_request_attach_tty::serve ()
+ */
+
+void
+client_request_attach_tty::serve (transport_layer_base *const conn,
+ process_cache *)
+{
+ assert (conn);
+
+ assert (!error_code ());
+
+ if (!wincap.has_security ())
+ {
+ syscall_printf ("operation only supported on systems with security");
+ error_code (EINVAL);
+ msglen (0);
+ return;
+ }
+
+ if (msglen () != sizeof (req))
+ {
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (req), msglen ());
+ error_code (EINVAL);
+ msglen (0);
+ return;
+ }
+
+ msglen (0); // Until we fill in some fields.
+
+ // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld",
+ // req.master_pid, req.from_master, req.to_master,
+ // req.pid);
+
+ // verbose: debug_printf ("opening process %ld", req.master_pid);
+
+ const HANDLE from_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
+
+ if (!from_process_handle)
+ {
+ system_printf ("error opening `from' process, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ return;
+ }
+
+ // verbose: debug_printf ("opening process %ld", req.pid);
+
+ const HANDLE to_process_handle =
+ OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
+
+ if (!to_process_handle)
+ {
+ system_printf ("error opening `to' process, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ error_code (EACCES);
+ return;
+ }
+
+ // verbose: debug_printf ("Impersonating client");
+ conn->impersonate_client ();
+
+ HANDLE token_handle = NULL;
+
+ // verbose: debug_printf ("about to open thread token");
+ const DWORD rc = OpenThreadToken (GetCurrentThread (),
+ TOKEN_QUERY,
+ TRUE,
+ &token_handle);
+
+ // verbose: debug_printf ("opened thread token, rc=%lu", rc);
+ conn->revert_to_self ();
+
+ if (!rc)
+ {
+ system_printf ("error opening thread token, error = %lu",
+ GetLastError ());
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ error_code (EACCES);
+ return;
+ }
+
+ // From this point on, a reply body is returned to the client.
+
+ const HANDLE from_master = req.from_master;
+ const HANDLE to_master = req.to_master;
+
+ req.from_master = NULL;
+ req.to_master = NULL;
+
+ msglen (sizeof (req));
+
+ if (from_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_READ,
+ from_master,
+ &req.from_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating from_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ if (to_master)
+ if (check_and_dup_handle (from_process_handle, to_process_handle,
+ token_handle,
+ GENERIC_WRITE,
+ to_master,
+ &req.to_master, TRUE) != 0)
+ {
+ system_printf ("error duplicating to_master handle, error = %lu",
+ GetLastError ());
+ error_code (EACCES);
+ }
+
+ CloseHandle (from_process_handle);
+ CloseHandle (to_process_handle);
+ CloseHandle (token_handle);
+
+ debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
+ req.master_pid, from_master, to_master,
+ req.pid, req.from_master, req.to_master);
+
+ return;
+}
+
+void
+client_request_get_version::serve (transport_layer_base *, process_cache *)
+{
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
+ msglen (sizeof (version));
+
+ version.major = CYGWIN_SERVER_VERSION_MAJOR;
+ version.api = CYGWIN_SERVER_VERSION_API;
+ version.minor = CYGWIN_SERVER_VERSION_MINOR;
+ version.patch = CYGWIN_SERVER_VERSION_PATCH;
+}
+
+class server_request : public queue_request
+{
+public:
+ server_request (transport_layer_base *const conn, process_cache *const cache)
+ : _conn (conn), _cache (cache)
+ {}
+
+ virtual ~server_request ()
+ {
+ safe_delete (_conn);
+ }
+
+ virtual void process ()
+ {
+ client_request::handle_request (_conn, _cache);
+ }
+
+private:
+ transport_layer_base *const _conn;
+ process_cache *const _cache;
+};
+
+class server_submission_loop : public queue_submission_loop
+{
+public:
+ server_submission_loop (threaded_queue *const queue,
+ transport_layer_base *const transport,
+ process_cache *const cache)
+ : queue_submission_loop (queue, false),
+ _transport (transport),
+ _cache (cache)
+ {
+ assert (_transport);
+ assert (_cache);
+ }
+
+private:
+ transport_layer_base *const _transport;
+ process_cache *const _cache;
+
+ virtual void request_loop ();
+};
+
+/* FIXME: this is a little ugly. What we really want is to wait on
+ * two objects: one for the pipe/socket, and one for being told to
+ * shutdown. Otherwise this will stay a problem (we won't actually
+ * shutdown until the request _AFTER_ the shutdown request. And
+ * sending ourselves a request is ugly
+ */
+void
+server_submission_loop::request_loop ()
+{
+ /* I'd like the accepting thread's priority to be above any "normal"
+ * thread in the system to avoid overflowing the listen queue (for
+ * sockets; similar issues exist for named pipes); but, for example,
+ * a normal priority thread in a foregrounded process is boosted to
+ * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current
+ * thread's priority to a level one above that. This fails on
+ * win9x/ME so assume any failure in that call is due to that and
+ * simply call again at one priority level lower.
+ */
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise accept thread priority, error = %lu",
+ GetLastError ());
+
+ while (_running)
+ {
+ bool recoverable = false;
+ transport_layer_base *const conn = _transport->accept (&recoverable);
+ if (!conn && !recoverable)
+ {
+ system_printf ("fatal error on IPC transport: closing down");
+ return;
+ }
+ // EINTR probably implies a shutdown request; so back off for a
+ // moment to let the main thread take control, otherwise the
+ // server spins here receiving EINTR repeatedly since the signal
+ // handler in the main thread doesn't get a chance to be called.
+ if (!conn && errno == EINTR)
+ {
+ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL))
+ debug_printf ("failed to reset thread priority, error = %lu",
+ GetLastError ());
+
+ Sleep (0);
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST + 1))
+ if (!SetThreadPriority (GetCurrentThread (),
+ THREAD_PRIORITY_HIGHEST))
+ debug_printf ("failed to raise thread priority, error = %lu",
+ GetLastError ());
+ }
+ if (conn)
+ _queue->add (safe_new (server_request, conn, _cache));
+ }
+}
+
+client_request_shutdown::client_request_shutdown ()
+ : client_request (CYGSERVER_REQUEST_SHUTDOWN)
+{
+ // verbose: syscall_printf ("created");
+}
+
+void
+client_request_shutdown::serve (transport_layer_base *, process_cache *)
+{
+ assert (!error_code ());
+
+ if (msglen ())
+ syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
+
+ /* FIXME: link upwards, and then this becomes a trivial method call to
+ * only shutdown _this queue_
+ */
+
+ kill (getpid (), SIGINT);
+
+ msglen (0);
+}
+
+static sig_atomic_t shutdown_server = false;
+
+static void
+handle_signal (const int signum)
+{
+ /* any signal makes us die :} */
+
+ shutdown_server = true;
+}
+
+/*
+ * print_usage ()
+ */
+
+static void
+print_usage (const char *const pgm)
+{
+ printf ("Usage: %s [OPTIONS]\n", pgm);
+ printf (" -c, --cleanup-threads number of cleanup threads to use\n");
+ printf (" -h, --help output usage information and exit\n");
+ printf (" -r, --request-threads number of request threads to use\n");
+ printf (" -s, --shutdown shutdown the daemon\n");
+ printf (" -v, --version output version information and exit\n");
+}
+
+/*
+ * print_version ()
+ */
+
+static void
+print_version (const char *const pgm)
+{
+ char *vn = NULL;
+
+ const char *const colon = strchr (version, ':');
+
+ if (!colon)
+ {
+ vn = strdup ("?");
+ }
+ else
+ {
+ vn = strdup (colon + 2); // Skip ": "
+
+ char *const spc = strchr (vn, ' ');
+
+ if (spc)
+ *spc = '\0';
+ }
+
+ char buf[200];
+ snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
+ cygwin_version.dll_major / 1000,
+ cygwin_version.dll_major % 1000,
+ cygwin_version.dll_minor,
+ cygwin_version.api_major,
+ cygwin_version.api_minor,
+ cygwin_version.shared_data,
+ CYGWIN_SERVER_VERSION_MAJOR,
+ CYGWIN_SERVER_VERSION_API,
+ CYGWIN_SERVER_VERSION_MINOR,
+ CYGWIN_SERVER_VERSION_PATCH,
+ cygwin_version.mount_registry,
+ cygwin_version.dll_build_date);
+
+ printf ("%s (cygwin) %s\n", pgm, vn);
+ printf ("API version %s\n", buf);
+ printf ("Copyright 2001, 2002 Red Hat, Inc.\n");
+ printf ("Compiled on %s\n", __DATE__);
+
+ free (vn);
+}
+
+/*
+ * main ()
+ */
+
+int
+main (const int argc, char *argv[])
+{
+ const struct option longopts[] = {
+ {"cleanup-threads", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {"request-threads", required_argument, NULL, 'r'},
+ {"shutdown", no_argument, NULL, 's'},
+ {"version", no_argument, NULL, 'v'},
+ {0, no_argument, NULL, 0}
+ };
+
+ const char opts[] = "c:hr:sv";
+
+ int cleanup_threads = 2;
+ int request_threads = 10;
+ bool shutdown = false;
+
+ const char *pgm = NULL;
+
+ if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
+ pgm = *argv;
+ else
+ pgm++;
+
+ wincap.init ();
+ if (wincap.has_security ())
+ setup_privileges ();
+
+ int opt;
+
+ while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+ switch (opt)
+ {
+ case 'c':
+ cleanup_threads = atoi (optarg);
+ if (cleanup_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of cleanup threads must be positive\n",
+ pgm);
+ exit (1);
+ }
+ break;
+
+ case 'h':
+ print_usage (pgm);
+ return 0;
+
+ case 'r':
+ request_threads = atoi (optarg);
+ if (request_threads <= 0)
+ {
+ fprintf (stderr,
+ "%s: number of request threads must be positive\n",
+ pgm);
+ exit (1);
+ }
+ break;
+
+ case 's':
+ shutdown = true;
+ break;
+
+ case 'v':
+ print_version (pgm);
+ return 0;
+
+ case '?':
+ fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
+ exit (1);
+ }
+
+ if (optind != argc)
+ {
+ fprintf (stderr, "%s: too many arguments\n", pgm);
+ exit (1);
+ }
+
+ if (shutdown)
+ {
+ /* Setting `cygserver_running' stops the request code making a
+ * version request, which is not much to the point.
+ */
+ cygserver_running = CYGSERVER_OK;
+
+ client_request_shutdown req;
+
+ if (req.make_request () == -1 || req.error_code ())
+ {
+ fprintf (stderr, "%s: shutdown request failed: %s\n",
+ pgm, strerror (req.error_code ()));
+ exit (1);
+ }
+
+ // FIXME: It would be nice to wait here for the daemon to exit.
+
+ return 0;
+ }
+
+#define SIGHANDLE(SIG) \
+ do \
+ { \
+ struct sigaction act; \
+ \
+ act.sa_handler = &handle_signal; \
+ act.sa_mask = 0; \
+ act.sa_flags = 0; \
+ \
+ if (sigaction (SIG, &act, NULL) == -1) \
+ { \
+ system_printf ("failed to install handler for " #SIG ": %s", \
+ strerror (errno)); \
+ exit (1); \
+ } \
+ } while (false)
+
+ SIGHANDLE (SIGHUP);
+ SIGHANDLE (SIGINT);
+ SIGHANDLE (SIGTERM);
+
+ print_version (pgm);
+ setbuf (stdout, NULL);
+ printf ("daemon starting up");
+
+ threaded_queue request_queue (request_threads);
+ printf (".");
+
+ transport_layer_base *const transport = create_server_transport ();
+ assert (transport);
+ printf (".");
+
+ process_cache cache (cleanup_threads);
+ printf (".");
+
+ server_submission_loop submission_loop (&request_queue, transport, &cache);
+ printf (".");
+
+ request_queue.add_submission_loop (&submission_loop);
+ printf (".");
+
+ if (transport->listen () == -1)
+ {
+ exit (1);
+ }
+ printf (".");
+
+ cache.start ();
+ printf (".");
+
+ request_queue.start ();
+ printf (".");
+
+ 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 (!shutdown_server && request_queue.running () && cache.running ())
+ pause ();
+
+ printf ("\nShutdown request received - new requests will be denied\n");
+ request_queue.stop ();
+ printf ("All pending requests processed\n");
+ safe_delete (transport);
+ printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
+ cache.stop ();
+ printf ("All outstanding process-cache activities completed\n");
+ printf ("daemon shutdown\n");
+
+ return 0;
+}
diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc
new file mode 100755
index 000000000..f6683182d
--- /dev/null
+++ b/winsup/cygwin/cygserver_client.cc
@@ -0,0 +1,528 @@
+/* cygserver_client.cc
+
+ Copyright 2001, 2002 Red Hat Inc.
+
+ Written by Egor Duda <deo@logos-m.ru>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* to allow this to link into cygwin and the .dll, a little magic is needed. */
+#ifdef __OUTSIDE_CYGWIN__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
+#include "cygserver_shm.h"
+#include "safe_memory.h"
+
+#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_transport.h"
+
+int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
+
+/* On by default during development. For release, we probably want off
+ * by default.
+ */
+bool allow_daemon = true; // Nb: inherited by children.
+
+client_request_get_version::client_request_get_version ()
+ : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
+{
+ msglen (0); // No parameters for request.
+
+ // verbose: syscall_printf ("created");
+}
+
+/*
+ * client_request_get_version::check_version ()
+ *
+ * The major version and API version numbers must match exactly. An
+ * older than expected minor version number is accepted (as long as
+ * the first numbers match, that is).
+ */
+
+bool
+client_request_get_version::check_version () const
+{
+ const bool ok = (version.major == CYGWIN_SERVER_VERSION_MAJOR
+ && version.api == CYGWIN_SERVER_VERSION_API
+ && version.minor <= CYGWIN_SERVER_VERSION_MINOR);
+
+ if (!ok)
+ syscall_printf (("incompatible version of cygwin server: "
+ "client version %d.%d.%d.%d, "
+ "server version %ld.%ld.%ld.%ld"),
+ CYGWIN_SERVER_VERSION_MAJOR,
+ CYGWIN_SERVER_VERSION_API,
+ CYGWIN_SERVER_VERSION_MINOR,
+ CYGWIN_SERVER_VERSION_PATCH,
+ version.major,
+ version.api,
+ version.minor,
+ version.patch);
+
+ return ok;
+}
+
+#ifdef __INSIDE_CYGWIN__
+
+client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid,
+ HANDLE nfrom_master,
+ HANDLE nto_master)
+ : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
+{
+ req.pid = GetCurrentProcessId ();
+ req.master_pid = nmaster_pid;
+ req.from_master = nfrom_master;
+ req.to_master = nto_master;
+
+ syscall_printf (("created: pid = %lu, master_pid = %lu, "
+ "from_master = %lu, to_master = %lu"),
+ req.pid, req.master_pid, req.from_master, req.to_master);
+}
+
+#else /* !__INSIDE_CYGWIN__ */
+
+client_request_attach_tty::client_request_attach_tty ()
+ : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
+{
+ // verbose: syscall_printf ("created");
+}
+
+#endif /* __INSIDE_CYGWIN__ */
+
+/*
+ * client_request_attach_tty::send ()
+ *
+ * Wraps the base method to provide error handling support. If the
+ * reply contains a body but is flagged as an error, close any handles
+ * that have been returned by cygserver and then discard the message
+ * body, i.e. the client either sees a successful result with handles
+ * or an unsuccessful result with no handles.
+ */
+
+void
+client_request_attach_tty::send (transport_layer_base * const conn)
+{
+ client_request::send (conn);
+
+ if (msglen () && error_code ())
+ {
+ if (from_master ())
+ CloseHandle (from_master ());
+ if (to_master ())
+ CloseHandle (to_master ());
+ msglen (0);
+ }
+}
+
+client_request::header_t::header_t (const request_code_t request_code,
+ const size_t msglen)
+ : msglen (msglen),
+ request_code (request_code)
+{
+ assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST);
+}
+
+// FIXME: also check write and read result for -1.
+
+void
+client_request::send (transport_layer_base * const conn)
+{
+ assert (conn);
+ assert (!(msglen () && !_buf)); // i.e., msglen () implies _buf
+ assert (msglen () <= _buflen);
+
+ {
+ const ssize_t count = conn->write (&_header, sizeof (_header));
+
+ if (count != sizeof (_header))
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("request header write failure: "
+ "only %ld bytes sent of %ld, "
+ "error = %d(%lu)"),
+ count, sizeof (_header),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ if (msglen ())
+ {
+ const ssize_t count = conn->write (_buf, msglen ());
+
+ if (count == -1 || (size_t) count != msglen ())
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("request body write failure: "
+ "only %ld bytes sent of %ld, "
+ "error = %d(%lu)"),
+ count, msglen (),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ // verbose: syscall_printf ("request sent (%ld + %ld bytes)",
+ // sizeof (_header), msglen ());
+
+ {
+ const ssize_t count = conn->read (&_header, sizeof (_header));
+
+ if (count != sizeof (_header))
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("reply header read failure: "
+ "only %ld bytes received of %ld, "
+ "error = %d(%lu)"),
+ count, sizeof (_header),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ if (msglen () && !_buf)
+ {
+ system_printf ("no client buffer for reply body: %ld bytes needed",
+ msglen ());
+ error_code (EINVAL);
+ return;
+ }
+
+ if (msglen () > _buflen)
+ {
+ system_printf (("client buffer too small for reply body: "
+ "have %ld bytes and need %ld"),
+ _buflen, msglen ());
+ error_code (EINVAL);
+ return;
+ }
+
+ if (msglen ())
+ {
+ const ssize_t count = conn->read (_buf, msglen ());
+
+ if (count == -1 || (size_t) count != msglen ())
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("reply body read failure: "
+ "only %ld bytes received of %ld, "
+ "error = %d(%lu)"),
+ count, msglen (),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ // verbose: syscall_printf ("reply received (%ld + %ld bytes)",
+ // sizeof (_header), msglen ());
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+/*
+ * client_request::handle_request ()
+ *
+ * A server-side method.
+ *
+ * This is a factory method for the client_request subclasses. It
+ * reads the incoming request header and, based on its request code,
+ * creates an instance of the appropriate class.
+ *
+ * FIXME: If the incoming packet is malformed, the server drops it on
+ * the floor. Should it try and generate some sort of reply for the
+ * client? As it is, the client will simply get a broken connection.
+ *
+ * FIXME: also check write and read result for -1.
+ */
+
+/* static */ void
+client_request::handle_request (transport_layer_base *const conn,
+ process_cache *const cache)
+{
+ // verbose: debug_printf ("about to read");
+
+ header_t header;
+
+ {
+ const ssize_t count = conn->read (&header, sizeof (header));
+
+ if (count != sizeof (header))
+ {
+ syscall_printf (("request header read failure: "
+ "only %ld bytes received of %ld, "
+ "error = %d(%lu)"),
+ count, sizeof (header),
+ errno, GetLastError ());
+ return;
+ }
+
+ // verbose: debug_printf ("got header (%ld)", count);
+ }
+
+ client_request *req = NULL;
+
+ switch (header.request_code)
+ {
+ case CYGSERVER_REQUEST_GET_VERSION:
+ req = safe_new0 (client_request_get_version);
+ break;
+ case CYGSERVER_REQUEST_SHUTDOWN:
+ req = safe_new0 (client_request_shutdown);
+ break;
+ case CYGSERVER_REQUEST_ATTACH_TTY:
+ req = safe_new0 (client_request_attach_tty);
+ break;
+ case CYGSERVER_REQUEST_SHM:
+ req = safe_new0 (client_request_shm);
+ break;
+ default:
+ syscall_printf ("unknown request code %d received: request ignored",
+ header.request_code);
+ return;
+ }
+
+ assert (req);
+
+ req->msglen (header.msglen);
+ req->handle (conn, cache);
+
+ safe_delete (req);
+
+#ifndef DEBUGGING
+ printf ("."); // A little noise when we're being quiet.
+#endif
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+client_request::client_request (request_code_t const id,
+ void * const buf,
+ size_t const buflen)
+ : _header (id, buflen),
+ _buf (buf),
+ _buflen (buflen)
+{
+ assert ((!_buf && !_buflen) || (_buf && _buflen));
+}
+
+client_request::~client_request ()
+{}
+
+int
+client_request::make_request ()
+{
+ assert (cygserver_running == CYGSERVER_UNKNOWN \
+ || cygserver_running == CYGSERVER_OK \
+ || cygserver_running == CYGSERVER_UNAVAIL);
+
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
+
+ assert (cygserver_running == CYGSERVER_OK \
+ || cygserver_running == CYGSERVER_UNAVAIL);
+
+ /* Don't retry every request if the server's not there */
+ if (cygserver_running == CYGSERVER_UNAVAIL)
+ {
+ syscall_printf ("cygserver un-available");
+ error_code (ENOSYS);
+ return -1;
+ }
+
+ transport_layer_base *const transport = create_server_transport ();
+
+ assert (transport);
+
+ if (transport->connect () == -1)
+ {
+ if (errno)
+ error_code (errno);
+ else
+ error_code (ENOSYS);
+ safe_delete (transport);
+ return -1;
+ }
+
+ // verbose: debug_printf ("connected to server %p", transport);
+
+ send (transport);
+
+ safe_delete (transport);
+
+ return 0;
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+/*
+ * client_request::handle ()
+ *
+ * A server-side method.
+ *
+ * At this point, the header of an incoming request has been read and
+ * an appropriate client_request object constructed. This method has
+ * to read the request body into its buffer, if there is such a body,
+ * then perform the request and send back the results to the client.
+ *
+ * FIXME: If the incoming packet is malformed, the server drops it on
+ * the floor. Should it try and generate some sort of reply for the
+ * client? As it is, the client will simply get a broken connection.
+ *
+ * FIXME: also check write and read result for -1.
+ */
+
+void
+client_request::handle (transport_layer_base *const conn,
+ process_cache *const cache)
+{
+ if (msglen () && !_buf)
+ {
+ system_printf ("no buffer for request body: %ld bytes needed",
+ msglen ());
+ error_code (EINVAL);
+ return;
+ }
+
+ if (msglen () > _buflen)
+ {
+ system_printf (("buffer too small for request body: "
+ "have %ld bytes and need %ld"),
+ _buflen, msglen ());
+ error_code (EINVAL);
+ return;
+ }
+
+ if (msglen ())
+ {
+ const ssize_t count = conn->read (_buf, msglen ());
+
+ if (count == -1 || (size_t) count != msglen ())
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("request body read failure: "
+ "only %ld bytes received of %ld, "
+ "error = %d(%lu)"),
+ count, msglen (),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ // verbose: syscall_printf ("request received (%ld + %ld bytes)",
+ // sizeof (_header), msglen ());
+
+ error_code (0); // Overwrites the _header.request_code field.
+
+ /*
+ * This is not allowed to fail. We must return ENOSYS at a minimum
+ * to the client.
+ */
+ serve (conn, cache);
+
+ {
+ const ssize_t count = conn->write (&_header, sizeof (_header));
+
+ if (count != sizeof (_header))
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("reply header write failure: "
+ "only %ld bytes sent of %ld, "
+ "error = %d(%lu)"),
+ count, sizeof (_header),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ if (msglen ())
+ {
+ const ssize_t count = conn->write (_buf, msglen ());
+
+ if (count == -1 || (size_t) count != msglen ())
+ {
+ assert (errno);
+ error_code (errno);
+ syscall_printf (("reply body write failure: "
+ "only %ld bytes sent of %ld, "
+ "error = %d(%lu)"),
+ count, msglen (),
+ errno, GetLastError ());
+ return;
+ }
+ }
+
+ // verbose: syscall_printf ("reply sent (%ld + %ld bytes)",
+ // sizeof (_header), msglen ());
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+bool
+check_cygserver_available ()
+{
+ assert (cygserver_running == CYGSERVER_UNKNOWN \
+ || cygserver_running == CYGSERVER_UNAVAIL);
+
+ cygserver_running = CYGSERVER_OK; // For make_request ().
+
+ client_request_get_version req;
+
+ /* This indicates that we failed to connect to cygserver at all but
+ * that's fine as cygwin doesn't need it to be running.
+ */
+ if (req.make_request () == -1)
+ return false;
+
+ /* We connected to the server but something went wrong after that
+ * (in sending the message, in cygserver itself, or in receiving the
+ * reply).
+ */
+ if (req.error_code ())
+ {
+ syscall_printf ("failure in cygserver version request: %d",
+ req.error_code ());
+ syscall_printf ("process will continue without cygserver support");
+ return false;
+ }
+
+ return req.check_version ();
+}
+
+void
+cygserver_init ()
+{
+ if (!allow_daemon)
+ {
+ syscall_printf ("cygserver use disabled in client");
+ cygserver_running = CYGSERVER_UNAVAIL;
+ return;
+ }
+
+ assert (cygserver_running == CYGSERVER_UNKNOWN \
+ || cygserver_running == CYGSERVER_OK \
+ || cygserver_running == CYGSERVER_UNAVAIL);
+
+ if (cygserver_running == CYGSERVER_OK)
+ return;
+
+ if (!check_cygserver_available ())
+ cygserver_running = CYGSERVER_UNAVAIL;
+}
diff --git a/winsup/cygwin/cygserver_ipc.h b/winsup/cygwin/cygserver_ipc.h
new file mode 100644
index 000000000..0d0ebbc76
--- /dev/null
+++ b/winsup/cygwin/cygserver_ipc.h
@@ -0,0 +1,84 @@
+/* cygserver_ipc.h
+
+ Copyright 2002 Red Hat, Inc.
+
+ Originally written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef __CYGSERVER_IPC_H__
+#define __CYGSERVER_IPC_H__
+
+#include <assert.h>
+#include <limits.h> /* For OPEN_MAX. */
+
+/*
+ * The sysv ipc id's (msgid, semid, shmid) are integers arranged such
+ * that they no subsystem will generate the same id as some other
+ * subsystem; nor do these ids overlap file descriptors (the other
+ * common integer ids). Since Cygwin can allocate more than OPEN_MAX
+ * file descriptors, it can't be guaranteed not to overlap, but it
+ * should help catch some errors.
+ *
+ * msgid's: OPEN_MAX, OPEN_MAX + 3, OPEN_MAX + 6, . . .
+ * semid's: OPEN_MAX + 1, OPEN_MAX + 4, OPEN_MAX + 7, . . .
+ * shmid's: OPEN_MAX + 2, OPEN_MAX + 5, OPEN_MAX + 8, . . .
+ *
+ * To further ensure that ids are unique, if ipc objects are created
+ * and destroyed and then re-created, they are given new ids by
+ * munging the basic id (as above) with a sequence number.
+ *
+ * Internal ipc id's, which are 0, 1, ... within each subsystem (and
+ * not munged with a sequence number), are used solely by the ipcs(8)
+ * interface.
+ */
+
+enum ipc_subsys_t
+ {
+ IPC_MSGOP = 0,
+ IPC_SEMOP = 1,
+ IPC_SHMOP = 2,
+ IPC_SUBSYS_COUNT
+ };
+
+/*
+ * IPCMNI - The absolute maximum number of simultaneous ipc ids for
+ * any one subsystem.
+ */
+
+enum
+ {
+ IPCMNI = 0x10000 // Must be a power of two.
+ };
+
+inline int
+ipc_int2ext (const int intid, const ipc_subsys_t subsys, long & sequence)
+{
+ assert (0 <= intid && intid < IPCMNI);
+
+ const long tmp = InterlockedIncrement (&sequence);
+
+ return (((tmp & 0x7fff) << 16)
+ | (OPEN_MAX + (intid * IPC_SUBSYS_COUNT) + subsys));
+}
+
+inline int
+ipc_ext2int_subsys (const int extid)
+{
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) % IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_ext2int (const int extid, const ipc_subsys_t subsys)
+{
+ if (ipc_ext2int_subsys (extid) != subsys)
+ return -1;
+ else
+ return ((extid & (IPCMNI - 1)) - OPEN_MAX) / IPC_SUBSYS_COUNT;
+}
+
+#endif /* __CYGSERVER_IPC_H__ */
diff --git a/winsup/cygwin/cygserver_process.cc b/winsup/cygwin/cygserver_process.cc
new file mode 100755
index 000000000..2cc7be19c
--- /dev/null
+++ b/winsup/cygwin/cygserver_process.cc
@@ -0,0 +1,431 @@
+/* cygserver_process.cc
+
+ Copyright 2001, 2002 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 "woutsup.h"
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "cygerrno.h"
+
+#include "cygwin/cygserver_process.h"
+
+/*****************************************************************************/
+
+#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
+
+/*****************************************************************************/
+
+process_cleanup::~process_cleanup ()
+{
+ safe_delete (_process);
+}
+
+void
+process_cleanup::process ()
+{
+ _process->cleanup ();
+}
+
+/*****************************************************************************/
+
+/* cleanup_routine */
+cleanup_routine::~cleanup_routine ()
+{
+}
+
+/*****************************************************************************/
+
+process::process (const pid_t cygpid, const DWORD winpid)
+ : _cygpid (cygpid),
+ _winpid (winpid),
+ _hProcess (NULL),
+ _cleaning_up (false),
+ _exit_status (STILL_ACTIVE),
+ _routines_head (NULL),
+ _next (NULL)
+{
+ _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
+ if (!_hProcess)
+ {
+ system_printf ("unable to obtain handle for new cache process %d(%lu)",
+ _cygpid, _winpid);
+ _hProcess = INVALID_HANDLE_VALUE;
+ _exit_status = 0;
+ }
+ else
+ debug_printf ("got handle %p for new cache process %d(%lu)",
+ _hProcess, _cygpid, _winpid);
+ InitializeCriticalSection (&_access);
+}
+
+process::~process ()
+{
+ DeleteCriticalSection (&_access);
+ (void) CloseHandle (_hProcess);
+}
+
+/* No need to be thread-safe as this is only ever called by
+ * process_cache::remove_process (). If it has to be made thread-safe
+ * later on, it should not use the `access' critical section as that
+ * is held by the client request handlers for an arbitrary length of
+ * time, i.e. while they do whatever processing is required for a
+ * client request.
+ */
+DWORD
+process::check_exit_code ()
+{
+ if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
+ && _exit_status == STILL_ACTIVE
+ && !GetExitCodeProcess (_hProcess, &_exit_status))
+ {
+ system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
+ _cygpid, _winpid, GetLastError ());
+ _hProcess = INVALID_HANDLE_VALUE;
+ }
+ return _exit_status;
+}
+
+bool
+process::add (cleanup_routine *const entry)
+{
+ assert (entry);
+
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
+ {
+ entry->_next = _routines_head;
+ _routines_head = entry;
+ res = true;
+ }
+
+ LeaveCriticalSection (&_access);
+ return res;
+}
+
+bool
+process::remove (const cleanup_routine *const entry)
+{
+ assert (entry);
+
+ bool res = false;
+ EnterCriticalSection (&_access);
+
+ if (!_cleaning_up)
+ {
+ cleanup_routine *previous = NULL;
+
+ for (cleanup_routine *ptr = _routines_head;
+ ptr;
+ previous = ptr, ptr = ptr->_next)
+ {
+ if (*ptr == *entry)
+ {
+ if (previous)
+ previous->_next = ptr->_next;
+ else
+ _routines_head = ptr->_next;
+
+ safe_delete (ptr);
+ res = true;
+ break;
+ }
+ }
+ }
+
+ LeaveCriticalSection (&_access);
+ return res;
+}
+
+/* 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 ()
+{
+ EnterCriticalSection (&_access);
+ assert (!is_active ());
+ assert (!_cleaning_up);
+ InterlockedExchange (&_cleaning_up, true);
+ cleanup_routine *entry = _routines_head;
+ _routines_head = NULL;
+ LeaveCriticalSection (&_access);
+
+ while (entry)
+ {
+ cleanup_routine *const ptr = entry;
+ entry = entry->_next;
+ ptr->cleanup (this);
+ safe_delete (ptr);
+ }
+}
+
+/*****************************************************************************/
+
+void
+process_cache::submission_loop::request_loop ()
+{
+ assert (this);
+ assert (_cache);
+ assert (_interrupt_event);
+
+ while (_running)
+ _cache->wait_for_processes (_interrupt_event);
+}
+
+/*****************************************************************************/
+
+process_cache::process_cache (const unsigned int initial_workers)
+ : _queue (initial_workers),
+ _submitter (this, &_queue), // true == interruptible
+ _processes_count (0),
+ _processes_head (NULL),
+ _cache_add_trigger (NULL)
+{
+ /* there can only be one */
+ InitializeCriticalSection (&_cache_write_access);
+
+ _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_cache_add_trigger)
+ {
+ system_printf ("failed to create cache add trigger, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ _queue.add_submission_loop (&_submitter);
+}
+
+process_cache::~process_cache ()
+{
+ (void) CloseHandle (_cache_add_trigger);
+ DeleteCriticalSection (&_cache_write_access);
+}
+
+/* This returns the process object to the caller already locked, that
+ * is, with the object's `access' critical region entered. Thus the
+ * caller must unlock the object when it's finished with it (via
+ * process::release ()). It must then not try to access the object
+ * afterwards, except by going through this routine again, as it may
+ * have been deleted once it has been unlocked.
+ */
+class process *
+process_cache::process (const pid_t cygpid, const DWORD winpid)
+{
+ /* TODO: make this more granular, so a search doesn't involve the
+ * write lock.
+ */
+ EnterCriticalSection (&_cache_write_access);
+ class process *previous = NULL;
+ class process *entry = find (winpid, &previous);
+
+ if (!entry)
+ {
+ if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ system_printf (("process limit (%d processes) reached; "
+ "new connection refused for %d(%lu)"),
+ MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
+ cygpid, winpid);
+ set_errno (EAGAIN);
+ return NULL;
+ }
+
+ entry = safe_new (class process, cygpid, winpid);
+ if (!entry->is_active ())
+ {
+ LeaveCriticalSection (&_cache_write_access);
+ safe_delete (entry);
+ set_errno (ESRCH);
+ return NULL;
+ }
+
+ if (previous)
+ {
+ entry->_next = previous->_next;
+ previous->_next = entry;
+ }
+ else
+ {
+ entry->_next = _processes_head;
+ _processes_head = entry;
+ }
+
+ _processes_count += 1;
+ SetEvent (_cache_add_trigger);
+ }
+
+ EnterCriticalSection (&entry->_access); // To be released by the caller.
+ LeaveCriticalSection (&_cache_write_access);
+ assert (entry);
+ assert (entry->_winpid == winpid);
+ return entry;
+}
+
+void
+process_cache::wait_for_processes (const HANDLE interrupt_event)
+{
+ // Update `_wait_array' with handles of all current processes.
+ const size_t count = sync_wait_array (interrupt_event);
+
+ debug_printf ("waiting on %u objects in total (%u processes)",
+ count, _processes_count);
+
+ const DWORD rc = WaitForMultipleObjects (count, _wait_array,
+ FALSE, INFINITE);
+
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("could not wait on the process handles, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ const size_t start = rc - WAIT_OBJECT_0;
+
+ if (rc < WAIT_OBJECT_0 || start > count)
+ {
+ system_printf (("unexpected return code %rc "
+ "from WaitForMultipleObjects: "
+ "expected [%u .. %u)"),
+ rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
+ abort ();
+ }
+
+ // Tell all the processes, from the signalled point up, the bad news.
+ for (size_t index = start; index != count; index++)
+ if (_process_array[index])
+ check_and_remove_process (index);
+}
+
+/*
+ * process_cache::sync_wait_array ()
+ *
+ * Fill-in the wait array with the handles that the cache needs to wait on.
+ * These handles are:
+ * - the process_process_param's interrupt event
+ * - the process_cache's cache_add_trigger event
+ * - the handle for each live process in the cache.
+ *
+ * Return value: the number of live handles in the array.
+ */
+
+size_t
+process_cache::sync_wait_array (const HANDLE interrupt_event)
+{
+ assert (this);
+ assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
+ assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
+
+ EnterCriticalSection (&_cache_write_access);
+
+ assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
+
+ size_t index = 0;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
+ {
+ assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
+ assert (ptr->is_active ());
+
+ _wait_array[index] = ptr->handle ();
+ _process_array[index++] = ptr;
+
+ assert (index <= elements (_wait_array));
+ }
+
+ /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
+ /* Well, not strictly `must', but it's more efficient if they are :-) */
+
+ _wait_array[index] = interrupt_event;
+ _process_array[index++] = NULL;
+
+ _wait_array[index] = _cache_add_trigger;
+ _process_array[index++] = NULL;
+
+ /* Phew, back to normal volume now. */
+
+ assert (index <= elements (_wait_array));
+
+ LeaveCriticalSection (&_cache_write_access);
+
+ return index;
+}
+
+void
+process_cache::check_and_remove_process (const size_t index)
+{
+ assert (this);
+ assert (index < elements (_wait_array) - SPECIALS_COUNT);
+
+ class process *const process = _process_array[index];
+
+ assert (process);
+ assert (process->handle () == _wait_array[index]);
+
+ if (process->check_exit_code () == STILL_ACTIVE)
+ return;
+
+ debug_printf ("process %d(%lu) has left the building ($? = %lu)",
+ process->_cygpid, process->_winpid, process->_exit_status);
+
+ /* Unlink the process object from the process list. */
+
+ EnterCriticalSection (&_cache_write_access);
+
+ class process *previous = NULL;
+
+ const class process *const tmp = find (process->_winpid, &previous);
+
+ assert (tmp == process);
+ assert (previous ? previous->_next == process : _processes_head == process);
+
+ if (previous)
+ previous->_next = process->_next;
+ else
+ _processes_head = process->_next;
+
+ _processes_count -= 1;
+ LeaveCriticalSection (&_cache_write_access);
+
+ /* Schedule any cleanup tasks for this process. */
+ _queue.add (safe_new (process_cleanup, process));
+}
+
+class process *
+process_cache::find (const DWORD winpid, class process **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
+ if (ptr->_winpid == winpid)
+ return ptr;
+ else if (ptr->_winpid > winpid) // The list is sorted by winpid.
+ return NULL;
+ else if (previous)
+ *previous = ptr;
+
+ return NULL;
+}
+
+/*****************************************************************************/
diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc
new file mode 100755
index 000000000..90053eec2
--- /dev/null
+++ b/winsup/cygwin/cygserver_shm.cc
@@ -0,0 +1,896 @@
+/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin.
+
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "woutsup.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "cygserver_ipc.h"
+#include "cygserver_shm.h"
+#include "security.h"
+
+#include "cygwin/cygserver.h"
+#include "cygwin/cygserver_process.h"
+#include "cygwin/cygserver_transport.h"
+
+/*---------------------------------------------------------------------------*
+ * class server_shmmgr
+ *
+ * A singleton class.
+ *---------------------------------------------------------------------------*/
+
+#define shmmgr (server_shmmgr::instance ())
+
+class server_shmmgr
+{
+private:
+ class attach_t
+ {
+ public:
+ class process *const _client;
+ unsigned int _refcnt;
+
+ attach_t *_next;
+
+ attach_t (class process *const client)
+ : _client (client),
+ _refcnt (0),
+ _next (NULL)
+ {}
+ };
+
+ class segment_t
+ {
+ private:
+ // Bits for the _flg field.
+ enum { IS_DELETED = 0x01 };
+
+ public:
+ const int _intid;
+ const int _shmid;
+ struct shmid_ds _ds;
+
+ segment_t *_next;
+
+ segment_t (const key_t key, const int intid, const HANDLE hFileMap);
+ ~segment_t ();
+
+ bool is_deleted () const
+ {
+ return _flg & IS_DELETED;
+ }
+
+ bool is_pending_delete () const
+ {
+ return !_ds.shm_nattch && is_deleted ();
+ }
+
+ void mark_deleted ()
+ {
+ assert (!is_deleted ());
+
+ _flg |= IS_DELETED;
+ }
+
+ int attach (class process *, HANDLE & hFileMap);
+ int detach (class process *);
+
+ private:
+ static long _sequence;
+
+ int _flg;
+ const HANDLE _hFileMap;
+ attach_t *_attach_head; // A list sorted by winpid;
+
+ attach_t *find (const class process *, attach_t **previous = NULL);
+ };
+
+ class cleanup_t : public cleanup_routine
+ {
+ public:
+ cleanup_t (const segment_t *const segptr)
+ : cleanup_routine (reinterpret_cast<void *> (segptr->_shmid))
+ {
+ assert (key ());
+ }
+
+ int shmid () const { return reinterpret_cast<int> (key ()); }
+
+ virtual void cleanup (class process *const client)
+ {
+ const int res = shmmgr.shmdt (shmid (), client);
+
+ if (res != 0)
+ debug_printf ("process cleanup failed [shmid = %d]: %s",
+ shmid (), strerror (-res));
+ }
+ };
+
+public:
+ static server_shmmgr & instance ();
+
+ int shmat (HANDLE & hFileMap,
+ int shmid, int shmflg, class process *);
+ int shmctl (int & out_shmid, struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo, struct shm_info & out_shm_info,
+ const int shmid, int cmd, const struct shmid_ds &,
+ class process *);
+ int shmdt (int shmid, class process *);
+ int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t,
+ class process *);
+
+private:
+ static server_shmmgr *_instance;
+ static pthread_once_t _instance_once;
+
+ static void initialise_instance ();
+
+ CRITICAL_SECTION _segments_lock;
+ segment_t *_segments_head; // A list sorted by int_id.
+
+ int _shm_ids; // Number of shm segments (for ipcs(8)).
+ int _shm_tot; // Total bytes of shm segments (for ipcs(8)).
+ int _shm_atts; // Number of attached segments (for ipcs(8)).
+ int _intid_max; // Highest intid yet allocated (for ipcs(8)).
+
+ server_shmmgr ();
+ ~server_shmmgr ();
+
+ // Undefined (as this class is a singleton):
+ server_shmmgr (const server_shmmgr &);
+ server_shmmgr & operator= (const server_shmmgr &);
+
+ segment_t *find_by_key (key_t);
+ segment_t *find (int intid, segment_t **previous = NULL);
+
+ int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t);
+
+ segment_t *new_segment (key_t, size_t, HANDLE);
+ void delete_segment (segment_t *);
+};
+
+/* static */ long server_shmmgr::segment_t::_sequence = 0;
+
+/* static */ server_shmmgr *server_shmmgr::_instance = NULL;
+/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT;
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::segment_t (const key_t key,
+ const int intid,
+ const HANDLE hFileMap)
+ : _intid (intid),
+ _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)),
+ _next (NULL),
+ _flg (0),
+ _hFileMap (hFileMap),
+ _attach_head (NULL)
+{
+ assert (0 <= _intid && _intid < SHMMNI);
+
+ memset (&_ds, '\0', sizeof (_ds));
+ _ds.shm_perm.key = key;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::~segment_t ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t::~segment_t ()
+{
+ assert (!_attach_head);
+
+ if (!CloseHandle (_hFileMap))
+ syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::attach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::attach (class process *const client,
+ HANDLE & hFileMap)
+{
+ assert (client);
+
+ if (!DuplicateHandle (GetCurrentProcess (),
+ _hFileMap,
+ client->handle (),
+ &hFileMap,
+ 0,
+ FALSE, // bInheritHandle
+ DUPLICATE_SAME_ACCESS))
+ {
+ syscall_printf (("failed to duplicate handle for client "
+ "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"),
+ _ds.shm_perm.key, _shmid, _hFileMap);
+
+ return -EACCES; // FIXME: Case analysis?
+ }
+
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch += 1;
+ _ds.shm_atime = time (NULL); // FIXME: sub-second times.
+
+ attach_t *previous = NULL;
+ attach_t *attptr = find (client, &previous);
+
+ if (!attptr)
+ {
+ attptr = safe_new (attach_t, client);
+
+ if (previous)
+ {
+ attptr->_next = previous->_next;
+ previous->_next = attptr;
+ }
+ else
+ {
+ attptr->_next = _attach_head;
+ _attach_head = attptr;
+ }
+ }
+
+ attptr->_refcnt += 1;
+
+ cleanup_t *const cleanup = safe_new (cleanup_t, this);
+
+ // FIXME: ::add should only fail if the process object is already
+ // cleaning up; but it can't be doing that since this thread has it
+ // locked.
+
+ const bool result = client->add (cleanup);
+
+ assert (result);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::detach ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::segment_t::detach (class process *const client)
+{
+ attach_t *previous = NULL;
+ attach_t *const attptr = find (client, &previous);
+
+ if (!attptr)
+ return -EINVAL;
+
+ if (client->is_active ())
+ {
+ const cleanup_t key (this);
+
+ if (!client->remove (&key))
+ syscall_printf (("failed to remove cleanup routine for %d(%lu) "
+ "[shmid = %d]"),
+ client->cygpid (), client->winpid (),
+ _shmid);
+ }
+
+ attptr->_refcnt -= 1;
+
+ if (!attptr->_refcnt)
+ {
+ assert (previous ? previous->_next == attptr : _attach_head == attptr);
+
+ if (previous)
+ previous->_next = attptr->_next;
+ else
+ _attach_head = attptr->_next;
+
+ safe_delete (attptr);
+ }
+
+ assert (_ds.shm_nattch > 0);
+
+ _ds.shm_lpid = client->cygpid ();
+ _ds.shm_nattch -= 1;
+ _ds.shm_dtime = time (NULL); // FIXME: sub-second times.
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::segment_t::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::attach_t *
+server_shmmgr::segment_t::find (const class process *const client,
+ attach_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ // Nb. The _attach_head list is sorted by winpid.
+
+ for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next)
+ if (attptr->_client == client)
+ return attptr;
+ else if (attptr->_client->winpid () > client->winpid ())
+ return NULL;
+ else if (previous)
+ *previous = attptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ server_shmmgr &
+server_shmmgr::instance ()
+{
+ pthread_once (&_instance_once, &initialise_instance);
+
+ assert (_instance);
+
+ return *_instance;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmat ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmat (HANDLE & hFileMap,
+ const int shmid, const int shmflg,
+ class process *const client)
+{
+ syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)",
+ shmid, shmflg, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->attach (client, hFileMap);
+
+ if (!result)
+ _shm_atts += 1;
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) "
+ "for %d(%lu)"),
+ hFileMap, shmid, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmctl ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmctl (int & out_shmid,
+ struct shmid_ds & out_ds,
+ struct shminfo & out_shminfo,
+ struct shm_info & out_shm_info,
+ const int shmid, const int cmd,
+ const struct shmid_ds & ds,
+ class process *const client)
+{
+ syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)",
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ switch (cmd)
+ {
+ case IPC_STAT:
+ case SHM_STAT: // Uses intids rather than shmids.
+ case IPC_SET:
+ case IPC_RMID:
+ {
+ int intid;
+
+ if (cmd == SHM_STAT)
+ intid = shmid;
+ else
+ intid = ipc_ext2int (shmid, IPC_SHMOP);
+
+ segment_t *const segptr = find (intid);
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ switch (cmd)
+ {
+ case IPC_STAT:
+ out_ds = segptr->_ds;
+ break;
+
+ case IPC_SET:
+ segptr->_ds.shm_perm.uid = ds.shm_perm.uid;
+ segptr->_ds.shm_perm.gid = ds.shm_perm.gid;
+ segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777;
+ segptr->_ds.shm_lpid = client->cygpid ();
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
+ break;
+
+ case IPC_RMID:
+ if (segptr->is_deleted ())
+ result = -EIDRM;
+ else
+ {
+ segptr->mark_deleted ();
+ if (segptr->is_pending_delete ())
+ delete_segment (segptr);
+ }
+ break;
+
+ case SHM_STAT: // ipcs(8) i'face.
+ out_ds = segptr->_ds;
+ out_shmid = segptr->_shmid;
+ break;
+ }
+ }
+ break;
+
+ case IPC_INFO:
+ out_shminfo.shmmax = SHMMAX;
+ out_shminfo.shmmin = SHMMIN;
+ out_shminfo.shmmni = SHMMNI;
+ out_shminfo.shmseg = SHMSEG;
+ out_shminfo.shmall = SHMALL;
+ break;
+
+ case SHM_INFO: // ipcs(8) i'face.
+ out_shmid = _intid_max;
+ out_shm_info.shm_ids = _shm_ids;
+ out_shm_info.shm_tot = _shm_tot;
+ out_shm_info.shm_atts = _shm_atts;
+ break;
+
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ -result,
+ shmid, cmd, client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
+ ((cmd == SHM_STAT || cmd == SHM_INFO)
+ ? out_shmid
+ : result),
+ shmid, cmd, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmdt ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmdt (const int shmid, class process *const client)
+{
+ syscall_printf ("shmdt (shmid = %d) for %d(%lu)",
+ shmid, client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
+
+ if (!segptr)
+ result = -EINVAL;
+ else
+ result = segptr->detach (client);
+
+ if (!result)
+ _shm_atts -= 1;
+
+ if (!result && segptr->is_pending_delete ())
+ delete_segment (segptr);
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result < 0)
+ syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)",
+ -result, shmid, client->cygpid (), client->winpid ());
+ else
+ syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)",
+ result, shmid, client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::shmget ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::shmget (int & out_shmid,
+ const key_t key, const size_t size, const int shmflg,
+ const uid_t uid, const gid_t gid,
+ class process *const client)
+{
+ syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ int result = 0;
+ EnterCriticalSection (&_segments_lock);
+
+ if (key == IPC_PRIVATE)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
+ {
+ segment_t *const segptr = find_by_key (key);
+
+ if (!segptr)
+ if (shmflg & IPC_CREAT)
+ result = new_segment (key, size, shmflg,
+ client->cygpid (), uid, gid);
+ else
+ result = -ENOENT;
+ else if (segptr->is_deleted ())
+ result = -EIDRM;
+ else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
+ result = -EEXIST;
+ else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777)
+ result = -EACCES;
+ else if (size && segptr->_ds.shm_segsz < size)
+ result = -EINVAL;
+ else
+ result = segptr->_shmid;
+ }
+
+ LeaveCriticalSection (&_segments_lock);
+
+ if (result >= 0)
+ {
+ out_shmid = result;
+ result = 0;
+ }
+
+ if (result < 0)
+ syscall_printf (("-1 [%d] = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ -result,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+ else
+ syscall_printf (("%d = "
+ "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
+ "for %d(%lu)"),
+ out_shmid,
+ key, size, shmflg,
+ client->cygpid (), client->winpid ());
+
+ return result;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::initialise_instance ()
+ *---------------------------------------------------------------------------*/
+
+/* static */ void
+server_shmmgr::initialise_instance ()
+{
+ assert (!_instance);
+
+ _instance = safe_new0 (server_shmmgr);
+
+ assert (_instance);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::server_shmmgr ()
+ : _segments_head (NULL),
+ _shm_ids (0),
+ _shm_tot (0),
+ _shm_atts (0),
+ _intid_max (0)
+{
+ InitializeCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::~server_shmmgr ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::~server_shmmgr ()
+{
+ DeleteCriticalSection (&_segments_lock);
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find_by_key ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find_by_key (const key_t key)
+{
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_ds.shm_perm.key == key)
+ return segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::find ()
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::find (const int intid, segment_t **previous)
+{
+ if (previous)
+ *previous = NULL;
+
+ for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
+ if (segptr->_intid == intid)
+ return segptr;
+ else if (segptr->_intid > intid) // The list is sorted by intid.
+ return NULL;
+ else if (previous)
+ *previous = segptr;
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *---------------------------------------------------------------------------*/
+
+int
+server_shmmgr::new_segment (const key_t key,
+ const size_t size,
+ const int shmflg,
+ const pid_t cygpid,
+ const uid_t uid,
+ const gid_t gid)
+{
+ if (size < SHMMIN || size > SHMMAX)
+ return -EINVAL;
+
+ const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE,
+ NULL, PAGE_READWRITE,
+ 0, size,
+ NULL);
+
+ if (!hFileMap)
+ {
+ syscall_printf ("failed to create file mapping [size = %lu]: %E", size);
+ return -ENOMEM; // FIXME
+ }
+
+ segment_t *const segptr = new_segment (key, size, hFileMap);
+
+ if (!segptr)
+ {
+ (void) CloseHandle (hFileMap);
+ return -ENOSPC;
+ }
+
+ segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid;
+ segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid;
+ segptr->_ds.shm_perm.mode = shmflg & 0777;
+ segptr->_ds.shm_segsz = size;
+ segptr->_ds.shm_cpid = cygpid;
+ segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
+
+ return segptr->_shmid;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::new_segment ()
+ *
+ * Allocate a new segment for the given key and file map with the
+ * lowest available intid and insert into the segment map.
+ *---------------------------------------------------------------------------*/
+
+server_shmmgr::segment_t *
+server_shmmgr::new_segment (const key_t key, const size_t size,
+ const HANDLE hFileMap)
+{
+ // FIXME: Overflow risk.
+ if (_shm_tot + size > SHMALL)
+ return NULL;
+
+ int intid = 0; // Next expected intid value.
+ segment_t *previous = NULL; // Insert pointer.
+
+ // Find first unallocated intid.
+ for (segment_t *segptr = _segments_head;
+ segptr && segptr->_intid == intid;
+ segptr = segptr->_next, intid++)
+ {
+ previous = segptr;
+ }
+
+ /* By the time this condition is reached (given the default value of
+ * SHMMNI), the linear searches should all replaced by something
+ * just a *little* cleverer . . .
+ */
+ if (intid >= SHMMNI)
+ return NULL;
+
+ segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap);
+
+ assert (segptr);
+
+ if (previous)
+ {
+ segptr->_next = previous->_next;
+ previous->_next = segptr;
+ }
+ else
+ {
+ segptr->_next = _segments_head;
+ _segments_head = segptr;
+ }
+
+ _shm_ids += 1;
+ _shm_tot += size;
+ if (intid > _intid_max)
+ _intid_max = intid;
+
+ return segptr;
+}
+
+/*---------------------------------------------------------------------------*
+ * server_shmmgr::delete_segment ()
+ *---------------------------------------------------------------------------*/
+
+void
+server_shmmgr::delete_segment (segment_t *const segptr)
+{
+ assert (segptr);
+ assert (segptr->is_pending_delete ());
+
+ segment_t *previous = NULL;
+
+ const segment_t *const tmp = find (segptr->_intid, &previous);
+
+ assert (tmp == segptr);
+ assert (previous ? previous->_next == segptr : _segments_head == segptr);
+
+ if (previous)
+ previous->_next = segptr->_next;
+ else
+ _segments_head = segptr->_next;
+
+ assert (_shm_ids > 0);
+ _shm_ids -= 1;
+ _shm_tot -= segptr->_ds.shm_segsz;
+
+ safe_delete (segptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::client_request_shm ()
+ *---------------------------------------------------------------------------*/
+
+client_request_shm::client_request_shm ()
+ : client_request (CYGSERVER_REQUEST_SHM,
+ &_parameters, sizeof (_parameters))
+{
+ // verbose: syscall_printf ("created");
+}
+
+/*---------------------------------------------------------------------------*
+ * client_request_shm::serve ()
+ *---------------------------------------------------------------------------*/
+
+void
+client_request_shm::serve (transport_layer_base *const conn,
+ process_cache *const cache)
+{
+ assert (conn);
+
+ assert (!error_code ());
+
+ if (msglen () != sizeof (_parameters.in))
+ {
+ syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
+ sizeof (_parameters), msglen ());
+ error_code (EINVAL);
+ msglen (0);
+ return;
+ }
+
+ // FIXME: Get a return code out of this and don't continue on error.
+ conn->impersonate_client ();
+
+ class process *const client = cache->process (_parameters.in.cygpid,
+ _parameters.in.winpid);
+
+ if (!client)
+ {
+ error_code (EAGAIN);
+ msglen (0);
+ return;
+ }
+
+ int result = -EINVAL;
+
+ switch (_parameters.in.shmop)
+ {
+ case SHMOP_shmget:
+ result = shmmgr.shmget (_parameters.out.shmid,
+ _parameters.in.key, _parameters.in.size,
+ _parameters.in.shmflg,
+ _parameters.in.uid, _parameters.in.gid,
+ client);
+ break;
+
+ case SHMOP_shmat:
+ result = shmmgr.shmat (_parameters.out.hFileMap,
+ _parameters.in.shmid, _parameters.in.shmflg,
+ client);
+ break;
+
+ case SHMOP_shmdt:
+ result = shmmgr.shmdt (_parameters.in.shmid, client);
+ break;
+
+ case SHMOP_shmctl:
+ result = shmmgr.shmctl (_parameters.out.shmid,
+ _parameters.out.ds, _parameters.out.shminfo,
+ _parameters.out.shm_info,
+ _parameters.in.shmid, _parameters.in.cmd,
+ _parameters.in.ds,
+ client);
+ break;
+ }
+
+ client->release ();
+ conn->revert_to_self ();
+
+ if (result < 0)
+ {
+ error_code (-result);
+ msglen (0);
+ }
+ else
+ msglen (sizeof (_parameters.out));
+}
diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h
new file mode 100644
index 000000000..5a5ee3820
--- /dev/null
+++ b/winsup/cygwin/cygserver_shm.h
@@ -0,0 +1,147 @@
+/* cygserver_shm.h: Single unix specification IPC interface for Cygwin.
+
+ Copyright 2002 Red Hat, Inc.
+
+ Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
+ Based on code by Robert Collins <robert.collins@hotmail.com>.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef __CYGSERVER_SHM_H__
+#define __CYGSERVER_SHM_H__
+
+#include <sys/types.h>
+#include <cygwin/shm.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "cygserver_ipc.h"
+
+#include "cygwin/cygserver.h"
+
+/*---------------------------------------------------------------------------*
+ * Values for the shminfo entries.
+ *
+ * Nb. The values are segregated between two enums so that the `small'
+ * values aren't promoted to `unsigned long' equivalents.
+ *---------------------------------------------------------------------------*/
+
+enum
+ {
+ SHMMAX = ULONG_MAX,
+ SHMSEG = ULONG_MAX,
+ SHMALL = ULONG_MAX
+ };
+
+enum
+ {
+ SHMMIN = 1,
+ SHMMNI = IPCMNI // Must be <= IPCMNI.
+ };
+
+/*---------------------------------------------------------------------------*
+ * class client_request_shm
+ *---------------------------------------------------------------------------*/
+
+#ifndef __INSIDE_CYGWIN__
+class transport_layer_base;
+class process_cache;
+#endif
+
+class client_request_shm : public client_request
+{
+ friend class client_request;
+
+public:
+ enum shmop_t
+ {
+ SHMOP_shmat,
+ SHMOP_shmctl,
+ SHMOP_shmdt,
+ SHMOP_shmget
+ };
+
+#ifdef __INSIDE_CYGWIN__
+ client_request_shm (int shmid, int shmflg); // shmat
+ client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl
+ client_request_shm (int shmid); // shmdt
+ client_request_shm (key_t, size_t, int shmflg); // shmget
+#endif
+
+ // Accessors for out parameters.
+
+ int shmid () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shmid;
+ }
+
+ HANDLE hFileMap () const
+ {
+ assert (!error_code ());
+ return _parameters.out.hFileMap;
+ }
+
+ const struct shmid_ds & ds () const
+ {
+ assert (!error_code ());
+ return _parameters.out.ds;
+ }
+
+ const struct shminfo & shminfo () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shminfo;
+ }
+
+ const struct shm_info & shm_info () const
+ {
+ assert (!error_code ());
+ return _parameters.out.shm_info;
+ }
+
+private:
+ union
+ {
+ struct
+ {
+ shmop_t shmop;
+ key_t key;
+ size_t size;
+ int shmflg;
+ int shmid;
+ int cmd;
+ pid_t cygpid;
+ DWORD winpid;
+ __uid32_t uid;
+ __gid32_t gid;
+ struct shmid_ds ds;
+ } in;
+
+ struct {
+ int shmid;
+ union
+ {
+ HANDLE hFileMap;
+ struct shmid_ds ds;
+ struct shminfo shminfo;
+ struct shm_info shm_info;
+ };
+ } out;
+ } _parameters;
+
+#ifndef __INSIDE_CYGWIN__
+ client_request_shm ();
+#endif
+
+#ifndef __INSIDE_CYGWIN__
+ virtual void serve (transport_layer_base *, process_cache *);
+#endif
+};
+
+#endif /* __CYGSERVER_SHM_H__ */
diff --git a/winsup/cygwin/cygserver_transport.cc b/winsup/cygwin/cygserver_transport.cc
new file mode 100755
index 000000000..8684a6148
--- /dev/null
+++ b/winsup/cygwin/cygserver_transport.cc
@@ -0,0 +1,51 @@
+/* cygserver_transport.cc
+
+ Copyright 2001, 2002 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. */
+
+/* to allow this to link into cygwin and the .dll, a little magic is needed. */
+#ifdef __OUTSIDE_CYGWIN__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <sys/socket.h>
+
+#include "safe_memory.h"
+
+#include "cygwin/cygserver_transport.h"
+#include "cygwin/cygserver_transport_pipes.h"
+#include "cygwin/cygserver_transport_sockets.h"
+
+/* The factory */
+transport_layer_base *
+create_server_transport ()
+{
+ if (wincap.is_winnt ())
+ return safe_new0 (transport_layer_pipes);
+ else
+ return safe_new0 (transport_layer_sockets);
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+void
+transport_layer_base::impersonate_client ()
+{}
+
+void
+transport_layer_base::revert_to_self ()
+{}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+transport_layer_base::~transport_layer_base ()
+{}
diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc
new file mode 100755
index 000000000..6d80defd4
--- /dev/null
+++ b/winsup/cygwin/cygserver_transport_pipes.cc
@@ -0,0 +1,362 @@
+/* cygserver_transport_pipes.cc
+
+ Copyright 2001, 2002 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. */
+
+/* to allow this to link into cygwin and the .dll, a little magic is needed. */
+#ifdef __OUTSIDE_CYGWIN__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "cygerrno.h"
+#include "cygwin/cygserver_transport.h"
+#include "cygwin/cygserver_transport_pipes.h"
+
+#ifndef __INSIDE_CYGWIN__
+#include "cygwin/cygserver.h"
+#endif
+
+enum
+ {
+ MAX_WAIT_NAMED_PIPE_RETRY = 64,
+ WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds
+ };
+
+#ifndef __INSIDE_CYGWIN__
+
+static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT;
+static CRITICAL_SECTION pipe_instance_lock;
+static long pipe_instance = 0;
+
+static void
+initialise_pipe_instance_lock ()
+{
+ assert (pipe_instance == 0);
+ InitializeCriticalSection (&pipe_instance_lock);
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+#ifndef __INSIDE_CYGWIN__
+
+transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe)
+ : _pipe_name (""),
+ _hPipe (hPipe),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
+{
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ init_security ();
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+transport_layer_pipes::transport_layer_pipes ()
+ : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"),
+ _hPipe (NULL),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
+{
+ init_security ();
+}
+
+void
+transport_layer_pipes::init_security ()
+{
+ assert (wincap.has_security ());
+
+ /* FIXME: pthread_once or equivalent needed */
+
+ InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE);
+
+ _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
+ _sec_all_nih.lpSecurityDescriptor = &_sd;
+ _sec_all_nih.bInheritHandle = FALSE;
+}
+
+transport_layer_pipes::~transport_layer_pipes ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
+transport_layer_pipes::listen ()
+{
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ _is_listening_endpoint = true;
+
+ /* no-op */
+ return 0;
+}
+
+class transport_layer_pipes *
+transport_layer_pipes::accept (bool *const recoverable)
+{
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock);
+
+ EnterCriticalSection (&pipe_instance_lock);
+
+ // Read: http://www.securityinternals.com/research/papers/namedpipe.php
+ // See also the Microsoft security bulletins MS00-053 and MS01-031.
+
+ // FIXME: Remove FILE_CREATE_PIPE_INSTANCE.
+
+ const bool first_instance = (pipe_instance == 0);
+
+ const HANDLE accept_pipe =
+ CreateNamedPipe (_pipe_name,
+ (PIPE_ACCESS_DUPLEX
+ | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)),
+ (PIPE_TYPE_BYTE | PIPE_WAIT),
+ PIPE_UNLIMITED_INSTANCES,
+ 0, 0, 1000,
+ &_sec_all_nih);
+
+ const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE
+ && pipe_instance == 0
+ && GetLastError () == ERROR_ACCESS_DENIED);
+
+ if (accept_pipe != INVALID_HANDLE_VALUE)
+ InterlockedIncrement (&pipe_instance);
+
+ LeaveCriticalSection (&pipe_instance_lock);
+
+ if (duplicate)
+ {
+ *recoverable = false;
+ system_printf ("failed to create named pipe: "
+ "is the daemon already running?");
+ return NULL;
+ }
+
+ if (accept_pipe == INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("error creating pipe (%lu).", GetLastError ());
+ *recoverable = true; // FIXME: case analysis?
+ return NULL;
+ }
+
+ assert (accept_pipe);
+
+ if (!ConnectNamedPipe (accept_pipe, NULL)
+ && GetLastError () != ERROR_PIPE_CONNECTED)
+ {
+ debug_printf ("error connecting to pipe (%lu)", GetLastError ());
+ (void) CloseHandle (accept_pipe);
+ *recoverable = true; // FIXME: case analysis?
+ return NULL;
+ }
+
+ return safe_new (transport_layer_pipes, accept_pipe);
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+void
+transport_layer_pipes::close ()
+{
+ // verbose: debug_printf ("closing pipe %p", _hPipe);
+
+ if (_hPipe)
+ {
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+#ifndef __INSIDE_CYGWIN__
+
+ if (_is_accepted_endpoint)
+ {
+ (void) FlushFileBuffers (_hPipe); // Blocks until client reads.
+ (void) DisconnectNamedPipe (_hPipe);
+ EnterCriticalSection (&pipe_instance_lock);
+ (void) CloseHandle (_hPipe);
+ assert (pipe_instance > 0);
+ InterlockedDecrement (&pipe_instance);
+ LeaveCriticalSection (&pipe_instance_lock);
+ }
+ else
+ (void) CloseHandle (_hPipe);
+
+#else /* __INSIDE_CYGWIN__ */
+
+ assert (!_is_accepted_endpoint);
+ (void) ForceCloseHandle (_hPipe);
+
+#endif /* __INSIDE_CYGWIN__ */
+
+ _hPipe = NULL;
+ }
+}
+
+ssize_t
+transport_layer_pipes::read (void *const buf, const size_t len)
+{
+ // verbose: debug_printf ("reading from pipe %p", _hPipe);
+
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!ReadFile (_hPipe, buf, len, &count, NULL))
+ {
+ debug_printf ("error reading from pipe (%lu)", GetLastError ());
+ set_errno (EINVAL); // FIXME?
+ return -1;
+ }
+
+ return count;
+}
+
+ssize_t
+transport_layer_pipes::write (void *const buf, const size_t len)
+{
+ // verbose: debug_printf ("writing to pipe %p", _hPipe);
+
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (!_is_listening_endpoint);
+
+ DWORD count;
+ if (!WriteFile (_hPipe, buf, len, &count, NULL))
+ {
+ debug_printf ("error writing to pipe, error = %lu", GetLastError ());
+ set_errno (EINVAL); // FIXME?
+ return -1;
+ }
+
+ return count;
+}
+
+/*
+ * This routine holds a static variable, assume_cygserver, that is set
+ * if the transport has good reason to think that cygserver is
+ * running, i.e. if if successfully connected to it with the previous
+ * attempt. If this is set, the code tries a lot harder to get a
+ * connection, making the assumption that any failures are just
+ * congestion and overloading problems.
+ */
+
+int
+transport_layer_pipes::connect ()
+{
+ assert (!_hPipe);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ static bool assume_cygserver = false;
+
+ BOOL rc = TRUE;
+ int retries = 0;
+
+ while (rc)
+ {
+ _hPipe = CreateFile (_pipe_name,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &_sec_all_nih,
+ OPEN_EXISTING,
+ SECURITY_IMPERSONATION,
+ NULL);
+
+ if (_hPipe != INVALID_HANDLE_VALUE)
+ {
+ assert (_hPipe);
+#ifdef __INSIDE_CYGWIN__
+ ProtectHandle (_hPipe);
+#endif
+ assume_cygserver = true;
+ return 0;
+ }
+
+ _hPipe = NULL;
+
+ if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY)
+ {
+ debug_printf ("Error opening the pipe (%lu)", GetLastError ());
+ return -1;
+ }
+
+ /* Note: `If no instances of the specified named pipe exist, the
+ * WaitNamedPipe function returns immediately, regardless of the
+ * time-out value.' Thus the explicit Sleep if the call fails
+ * with ERROR_FILE_NOT_FOUND.
+ */
+ while (retries != MAX_WAIT_NAMED_PIPE_RETRY
+ && !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT)))
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND)
+ Sleep (0); // Give the server a chance.
+
+ retries += 1;
+ }
+ }
+
+ assert (retries == MAX_WAIT_NAMED_PIPE_RETRY);
+
+ system_printf ("lost connection to cygserver, error = %lu",
+ GetLastError ());
+
+ assume_cygserver = false;
+
+ return -1;
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+void
+transport_layer_pipes::impersonate_client ()
+{
+ assert (_hPipe);
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+ assert (_is_accepted_endpoint);
+
+ // verbose: debug_printf ("impersonating pipe %p", _hPipe);
+ if (_hPipe)
+ {
+ assert (_hPipe != INVALID_HANDLE_VALUE);
+
+ if (!ImpersonateNamedPipeClient (_hPipe))
+ debug_printf ("Failed to Impersonate the client, (%lu)",
+ GetLastError ());
+ }
+ // verbose: debug_printf ("I am who you are");
+}
+
+void
+transport_layer_pipes::revert_to_self ()
+{
+ assert (_is_accepted_endpoint);
+
+ RevertToSelf ();
+ // verbose: debug_printf ("I am who I yam");
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
diff --git a/winsup/cygwin/cygserver_transport_sockets.cc b/winsup/cygwin/cygserver_transport_sockets.cc
new file mode 100755
index 000000000..6ade14bff
--- /dev/null
+++ b/winsup/cygwin/cygserver_transport_sockets.cc
@@ -0,0 +1,387 @@
+/* cygserver_transport_sockets.cc
+
+ Copyright 2001, 2002 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. */
+
+/* to allow this to link into cygwin and the .dll, a little magic is needed. */
+#ifdef __OUTSIDE_CYGWIN__
+#include "woutsup.h"
+#else
+#include "winsup.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "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__
+
+extern "C" int cygwin_accept (int fd, struct sockaddr *, int *len);
+extern "C" int cygwin_bind (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_connect (int fd, const struct sockaddr *, int len);
+extern "C" int cygwin_listen (int fd, int backlog);
+extern "C" int cygwin_shutdown (int fd, int how);
+extern "C" int cygwin_socket (int af, int type, int protocol);
+
+#else /* __OUTSIDE_CYGWIN__ */
+
+#define cygwin_accept(A,B,C) ::accept (A,B,C)
+#define cygwin_bind(A,B,C) ::bind (A,B,C)
+#define cygwin_connect(A,B,C) ::connect (A,B,C)
+#define cygwin_listen(A,B) ::listen (A,B)
+#define cygwin_shutdown(A,B) ::shutdown (A,B)
+#define cygwin_socket(A,B,C) ::socket (A,B,C)
+
+#endif /* __OUTSIDE_CYGWIN__ */
+
+enum
+ {
+ MAX_CONNECT_RETRY = 64
+ };
+
+transport_layer_sockets::transport_layer_sockets (const int fd)
+ : _fd (fd),
+ _addr_len (0),
+ _is_accepted_endpoint (true),
+ _is_listening_endpoint (false)
+{
+ assert (_fd != -1);
+
+ memset (&_addr, '\0', sizeof (_addr));
+}
+
+transport_layer_sockets::transport_layer_sockets ()
+ : _fd (-1),
+ _addr_len (0),
+ _is_accepted_endpoint (false),
+ _is_listening_endpoint (false)
+{
+ memset (&_addr, '\0', sizeof (_addr));
+
+ _addr.sun_family = AF_UNIX;
+ strcpy (_addr.sun_path, "/tmp/cygdaemo"); // FIXME: $TMP?
+ _addr_len = SUN_LEN (&_addr);
+}
+
+transport_layer_sockets::~transport_layer_sockets ()
+{
+ close ();
+}
+
+#ifndef __INSIDE_CYGWIN__
+
+int
+transport_layer_sockets::listen ()
+{
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ debug_printf ("listen () [this = %p]", this);
+
+ struct stat sbuf;
+
+ if (stat (_addr.sun_path, &sbuf) == -1)
+ {
+ if (errno != ENOENT)
+ {
+ system_printf ("cannot access socket file `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+ }
+ else if (S_ISSOCK (sbuf.st_mode))
+ {
+ // The socket already exists: is a duplicate cygserver running?
+
+ const int newfd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (newfd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (newfd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ system_printf ("the daemon is already running");
+ (void) cygwin_shutdown (newfd, SHUT_WR);
+ char buf[BUFSIZ];
+ while (::read (newfd, buf, sizeof (buf)) > 0)
+ {}
+ (void) ::close (newfd);
+ return -1;
+ }
+
+ if (unlink (_addr.sun_path) == -1)
+ {
+ system_printf ("failed to remove `%s': %s",
+ _addr.sun_path, strerror (errno));
+ (void) ::close (newfd);
+ return -1;
+ }
+ }
+ else
+ {
+ system_printf ("cannot create socket `%s': File already exists",
+ _addr.sun_path);
+ return -1;
+ }
+
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_bind (_fd, (struct sockaddr *) &_addr, _addr_len) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to bind UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ _is_listening_endpoint = true; // i.e. this really means "have bound".
+
+ if (cygwin_listen (_fd, SOMAXCONN) == -1)
+ {
+ const int saved_errno = errno;
+ close ();
+ errno = saved_errno;
+ system_printf ("failed to listen on UNIX domain socket `%s': %s",
+ _addr.sun_path, strerror (errno));
+ return -1;
+ }
+
+ debug_printf ("0 = listen () [this = %p, fd = %d]", this, _fd);
+
+ return 0;
+}
+
+class transport_layer_sockets *
+transport_layer_sockets::accept (bool *const recoverable)
+{
+ assert (_fd != -1);
+ assert (!_is_accepted_endpoint);
+ assert (_is_listening_endpoint);
+
+ debug_printf ("accept () [this = %p, fd = %d]", this, _fd);
+
+ struct sockaddr_un client_addr;
+ socklen_t client_addr_len = sizeof (client_addr);
+
+ const int accept_fd =
+ cygwin_accept (_fd, (struct sockaddr *) &client_addr, &client_addr_len);
+
+ if (accept_fd == -1)
+ {
+ system_printf ("failed to accept connection: %s", strerror (errno));
+ switch (errno)
+ {
+ case ECONNABORTED:
+ case EINTR:
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ case ENOMEM:
+ *recoverable = true;
+ break;
+
+ default:
+ *recoverable = false;
+ break;
+ }
+ return NULL;
+ }
+
+ debug_printf ("%d = accept () [this = %p, fd = %d]", accept_fd, this, _fd);
+
+ return safe_new (transport_layer_sockets, accept_fd);
+}
+
+#endif /* !__INSIDE_CYGWIN__ */
+
+void
+transport_layer_sockets::close ()
+{
+ debug_printf ("close () [this = %p, fd = %d]", this, _fd);
+
+ if (_is_listening_endpoint)
+ (void) unlink (_addr.sun_path);
+
+ if (_fd != -1)
+ {
+ (void) cygwin_shutdown (_fd, SHUT_WR);
+ if (!_is_listening_endpoint)
+ {
+ char buf[BUFSIZ];
+ while (::read (_fd, buf, sizeof (buf)) > 0)
+ {}
+ }
+ (void) ::close (_fd);
+ _fd = -1;
+ }
+
+ _is_listening_endpoint = false;
+}
+
+ssize_t
+transport_layer_sockets::read (void *const buf, const size_t buf_len)
+{
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *read_buf = static_cast<char *> (buf);
+ size_t read_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (read_buf_len != 0
+ && (res = ::read (_fd, read_buf, read_buf_len)) > 0)
+ {
+ read_buf += res;
+ read_buf_len -= res;
+
+ assert (read_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - read_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
+}
+
+ssize_t
+transport_layer_sockets::write (void *const buf, const size_t buf_len)
+{
+ assert (_fd != -1);
+ assert (!_is_listening_endpoint);
+
+ assert (buf);
+ assert (buf_len > 0);
+
+ // verbose: debug_printf ("write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // buf, buf_len, this, _fd);
+
+ char *write_buf = static_cast<char *> (buf);
+ size_t write_buf_len = buf_len;
+ ssize_t res = 0;
+
+ while (write_buf_len != 0
+ && (res = ::write (_fd, write_buf, write_buf_len)) > 0)
+ {
+ write_buf += res;
+ write_buf_len -= res;
+
+ assert (write_buf_len >= 0);
+ }
+
+ if (res != -1)
+ {
+ if (res == 0)
+ errno = EIO; // FIXME?
+
+ res = buf_len - write_buf_len;
+ }
+
+ if (res != static_cast<ssize_t> (buf_len))
+ debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]: %s",
+ res, buf, buf_len, this, _fd,
+ (res == -1 ? strerror (errno) : "EOF"));
+ else
+ {
+ // verbose: debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]",
+ // res, buf, buf_len, this, _fd);
+ }
+
+ return res;
+}
+
+int
+transport_layer_sockets::connect ()
+{
+ assert (_fd == -1);
+ assert (!_is_accepted_endpoint);
+ assert (!_is_listening_endpoint);
+
+ static bool assume_cygserver = false;
+
+ debug_printf ("connect () [this = %p]", this);
+
+ for (int retries = 0; retries != MAX_CONNECT_RETRY; retries++)
+ {
+ _fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (_fd == -1)
+ {
+ system_printf ("failed to create UNIX domain socket: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ if (cygwin_connect (_fd, (struct sockaddr *) &_addr, _addr_len) == 0)
+ {
+ assume_cygserver = true;
+ debug_printf ("0 = connect () [this = %p, fd = %d]", this, _fd);
+ return 0;
+ }
+
+ if (!assume_cygserver || errno != ECONNREFUSED)
+ {
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ (void) ::close (_fd);
+ _fd = -1;
+ return -1;
+ }
+
+ (void) ::close (_fd);
+ _fd = -1;
+ Sleep (0); // Give the server a chance.
+ }
+
+ debug_printf ("failed to connect to server: %s", strerror (errno));
+ return -1;
+}
diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc
deleted file mode 100644
index 9972c644a..000000000
--- a/winsup/cygwin/devices.cc
+++ /dev/null
@@ -1,453 +0,0 @@
-/* ANSI-C code produced by gperf version 2.7.2 */
-/* Command-line: gperf -c --key-positions=5-8,1-2,9-10 -r -t -C -E -T -L ANSI-C -Hdevhash -Ndevice::lookup -Z devstring -7 -G /cygnus/src/uberbaum/winsup/cygwin/devices.gperf */
-#include "winsup.h"
-#include <sys/types.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include "devices.h"
-#include "sys/cygwin.h"
-#include "tty.h"
-#include "pinfo.h"
-#undef __GNUC__
-static unsigned int devhash (const char *, unsigned)
- __attribute__ ((regparm (2)));
-enum
- {
- TOTAL_KEYWORDS = 54,
- MIN_WORD_LENGTH = 5,
- MAX_WORD_LENGTH = 14,
- MIN_HASH_VALUE = 131,
- MAX_HASH_VALUE = 358
- };
-
-/* maximum key range = 228, duplicates = 0 */
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
-devhash (register const char *str, register unsigned int len)
-{
- static const unsigned short asso_values[] =
- {
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 60, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 62, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 15, 359, 359, 359, 359, 359, 359,
- 359, 359, 359, 359, 359, 359, 359, 0, 2, 46,
- 42, 50, 3, 4, 36, 15, 10, 41, 61, 13,
- 26, 30, 52, 57, 45, 27, 59, 39, 51, 38,
- 32, 18, 14, 359, 359, 359, 359, 359
- };
- register int hval = len;
-
- switch (hval)
- {
- default:
- case 10:
- hval += asso_values[(unsigned) cyg_tolower (str[9])];
- case 9:
- hval += asso_values[(unsigned) cyg_tolower (str[8])];
- case 8:
- hval += asso_values[(unsigned) cyg_tolower (str[7])];
- case 7:
- hval += asso_values[(unsigned) cyg_tolower (str[6])];
- case 6:
- hval += asso_values[(unsigned) cyg_tolower (str[5])];
- case 5:
- hval += asso_values[(unsigned) cyg_tolower (str[4])];
- case 4:
- case 3:
- case 2:
- hval += asso_values[(unsigned) cyg_tolower (str[1])];
- case 1:
- hval += asso_values[(unsigned) cyg_tolower (str[0])];
- break;
- }
- return hval;
-}
-
-static NO_COPY const struct device wordlist[] =
- {
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""},
- {":bad:", FH_BAD, ":bad:", 0, 0, 0, 0},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""},
- {"/dev/fd", FH_FLOPPY, "\\Device\\Floppy%d", 0, 15},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {"/dev/fifo", FH_FIFO, "\\dev\\fifo", 0, 0, 0, 0},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {"/dev/sr", FH_CDROM, "\\Device\\CdRom%d", 0, 15},
- {""}, {""}, {""}, {""},
- {"/dev/mem", FH_MEM, "\\dev\\mem", 0, 0, 0, 0},
- {""},
- {"/dev/hda", FH_SDA, "\\Device\\Harddisk%d\\Partition%d", 1, 16, -1},
- {""},
- {"/dev/hdb", FH_SDB, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 15},
- {"/dev/hdf", FH_SDF, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 79},
- {"/dev/hdg", FH_SDG, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 95},
- {""}, {""},
- {"/dev/st", FH_TAPE, "\\Device\\Tape%d", 0, 127},
- {""}, {""},
- {"/dev/hdj", FH_SDJ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 143},
- {"/dev/com", FH_SERIAL, "\\.\\com%d", 1, 99},
- {""},
- {"/dev/hdm", FH_SDM, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 191},
- {"/dev/hdz", FH_SDZ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 399},
- {"/dev/hdi", FH_SDI, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 127},
- {""}, {""},
- {"/dev/hdy", FH_SDY, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 383},
- {""}, {""}, {""}, {""}, {""}, {""}, {""},
- {"/dev/hdn", FH_SDN, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 207},
- {"/dev/hds", FH_SDS, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 287},
- {""}, {""},
- {"/dev/hdo", FH_SDO, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 223},
- {""},
- {"/dev/hdx", FH_SDX, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 367},
- {""},
- {"/dev/nst", FH_NTAPE, "\\Device\\Tape%d", 0, 127},
- {""},
- {"/dev/hdh", FH_SDH, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 111},
- {"/dev/scd", FH_CDROM, "\\Device\\CdRom%d", 0, 15},
- {"/dev/hdw", FH_SDW, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 351},
- {"/dev/hdu", FH_SDU, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 319},
- {"/dev/kmem", FH_KMEM, "\\dev\\mem", 0, 0, 0, 0},
- {"/dev/hdk", FH_SDK, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 159},
- {"/dev/hdd", FH_SDD, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 47},
- {"/dev/dsp", FH_OSS_DSP, "\\dev\\dsp", 0, 0, 0, 0},
- {""},
- {"/dev/hdr", FH_SDR, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 271},
- {"/dev/hdc", FH_SDC, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 31},
- {""}, {""}, {""},
- {"/dev/hde", FH_SDE, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 63},
- {"/dev/hdv", FH_SDV, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 335},
- {"/dev/hdp", FH_SDP, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 239},
- {""}, {""}, {""}, {""},
- {"/dev/hdq", FH_SDQ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 255 /* FIXME 8 bit lunacy */},
- {"/dev/tty", FH_TTY, "\\dev\\tty", 0, 0, 0, 0},
- {"/dev/hdt", FH_SDT, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 303},
- {""},
- {"/dev/hdl", FH_SDL, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 175},
- {"/dev/zero", FH_ZERO, "\\dev\\zero", 0, 0, 0, 0},
- {""}, {""}, {""}, {""},
- {"/dev/conin", FH_CONIN, "conin", 0, 0, 0, 0},
- {"/dev/random", FH_RANDOM, "\\dev\\random", 0, 0, 0, 0},
- {""}, {""}, {""},
- {"/dev/ttym", FH_TTYM, "\\dev\\ttym", 0, 255, 0, 0},
- {""},
- {"/dev/ttyS", FH_SERIAL, "\\.\\com%d", 0, 99, -1},
- {""}, {""},
- {"/dev/windows", FH_WINDOWS, "\\dev\\windows", 0, 0, 0, 0},
- {"/dev/urandom", FH_URANDOM, "\\dev\\urandom", 0, 0, 0, 0},
- {"/dev/ptmx", FH_PTYM, "\\dev\\ptmx", 0, 0, 0, 0},
- {""}, {""}, {""}, {""}, {""},
- {"/dev/console", FH_CONSOLE, "\\dev\\console", 0, 0, 0, 0},
- {"/dev/ttys", FH_TTYS, "\\dev\\tty%d", 0, 255, 0, 0},
- {""}, {""}, {""}, {""}, {""},
- {"/dev/pipe", FH_PIPE, "\\dev\\pipe", 0, 0, 0, 0},
- {""}, {""}, {""},
- {"/dev/conout", FH_CONOUT, "conout", 0, 0, 0, 0},
- {"/dev/rawdrive", FH_RAWDRIVE, "\\DosDevices\\%c:", 0, 0, 0, 0},
- {""}, {""}, {""}, {""}, {""}, {""},
- {"/dev/clipboard", FH_CLIPBOARD, "\\dev\\clipboard", 0, 0, 0, 0},
- {""}, {""}, {""}, {""},
- {"/dev/port", FH_PORT, "\\dev\\port", 0, 0, 0, 0},
- {"/dev/null", FH_NULL, "nul", 0, 0, 0, 0}
- };
-
-static const device cygdrive_dev_storage =
- {"/cygdrive", FH_CYGDRIVE, "/cygdrive", 0, 0, 0, 0};
-
-static const device fs_dev_storage =
- {"", FH_FS, "", 0, 0, 0, 0};
-
-static const device proc_dev_storage =
- {"", FH_PROC, "", 0, 0, 0, 0};
-
-static const device registry_dev_storage =
- {"", FH_REGISTRY, "", 0, 0, 0, 0};
-
-static const device process_dev_storage =
- {"", FH_PROCESS, "", 0, 0, 0, 0};
-
-static const device tcp_dev_storage =
- {"/dev/inet/tcp", FH_TCP, "", 0, 0, 0, 0};
-
-static const device udp_dev_storage =
- {"/dev/inet/udp", FH_UCP, "", 0, 0, 0, 0};
-
-static const device icmp_dev_storage =
- {"/dev/inet/icmp", FH_ICMP, "", 0, 0, 0, 0};
-
-static const device unix_dev_storage =
- {"/dev/inet/unix", FH_UNIX, "", 0, 0, 0, 0};
-
-static const device stream_dev_storage =
- {"/dev/inet/stream", FH_STREAM, "", 0, 0, 0, 0};
-
-static const device dgram_dev_storage =
- {"/dev/inet/dgram", FH_DGRAM, "", 0, 0, 0, 0};
-
-static const device piper_dev_storage =
- {"", FH_PIPER, "", 0, 0, 0, 0};
-
-static const device pipew_dev_storage =
- {"", FH_PIPEW, "", 0, 0, 0, 0};
-
-static const device dev_fs =
- {"", FH_FS, "", 0, 0, 0, 0};
-const device *bad_dev = wordlist + 131;
-const device *cdrom_dev = wordlist + 241;
-const device *cdrom_dev1 = wordlist + 285;
-const device *clipboard_dev = wordlist + 352;
-const device *conin_dev = wordlist + 315;
-const device *conout_dev = wordlist + 344;
-const device *console_dev = wordlist + 333;
-const device *cygdrive_dev = &cygdrive_dev_storage;
-const device *dgram_dev = &dgram_dev_storage;
-const device *fifo_dev = wordlist + 222;
-const device *floppy_dev = wordlist + 214;
-const device *fs_dev = &fs_dev_storage;
-const device *icmp_dev = &icmp_dev_storage;
-const device *kmem_dev = wordlist + 288;
-const device *mem_dev = wordlist + 246;
-const device *ntape_dev = wordlist + 282;
-const device *null_dev = wordlist + 358;
-const device *oss_dsp_dev = wordlist + 291;
-const device *pipe_dev = wordlist + 340;
-const device *piper_dev = &piper_dev_storage;
-const device *pipew_dev = &pipew_dev_storage;
-const device *port_dev = wordlist + 357;
-const device *proc_dev = &proc_dev_storage;
-const device *process_dev = &process_dev_storage;
-const device *ptym_dev = wordlist + 327;
-const device *random_dev = wordlist + 316;
-const device *rawdrive_dev = wordlist + 345;
-const device *registry_dev = &registry_dev_storage;
-const device *sda_dev = wordlist + 248;
-const device *sdb_dev = wordlist + 250;
-const device *sdc_dev = wordlist + 294;
-const device *sdd_dev = wordlist + 290;
-const device *sde_dev = wordlist + 298;
-const device *sdf_dev = wordlist + 251;
-const device *sdg_dev = wordlist + 252;
-const device *sdh_dev = wordlist + 284;
-const device *sdi_dev = wordlist + 263;
-const device *sdj_dev = wordlist + 258;
-const device *sdk_dev = wordlist + 289;
-const device *sdl_dev = wordlist + 309;
-const device *sdm_dev = wordlist + 261;
-const device *sdn_dev = wordlist + 274;
-const device *sdo_dev = wordlist + 278;
-const device *sdp_dev = wordlist + 300;
-const device *sdq_dev = wordlist + 305;
-const device *sdr_dev = wordlist + 293;
-const device *sds_dev = wordlist + 275;
-const device *sdt_dev = wordlist + 307;
-const device *sdu_dev = wordlist + 287;
-const device *sdv_dev = wordlist + 299;
-const device *sdw_dev = wordlist + 286;
-const device *sdx_dev = wordlist + 280;
-const device *sdy_dev = wordlist + 266;
-const device *sdz_dev = wordlist + 262;
-const device *serial_dev = wordlist + 259;
-const device *serial_dev1 = wordlist + 322;
-const device *stream_dev = &stream_dev_storage;
-const device *tape_dev = wordlist + 255;
-const device *tcp_dev = &tcp_dev_storage;
-const device *tty_dev = wordlist + 306;
-const device *ttym_dev = wordlist + 320;
-const device *ttys_dev = wordlist + 334;
-const device *udp_dev = &udp_dev_storage;
-const device *unix_dev = &unix_dev_storage;
-const device *urandom_dev = wordlist + 326;
-const device *windows_dev = wordlist + 325;
-const device *zero_dev = wordlist + 310;
-
-const device *unit_devices[] =
-{
- wordlist + 241 /* cdrom */,
- wordlist + 259 /* serial */,
- wordlist + 320 /* ttym */,
- wordlist + 334 /* ttys */,
- wordlist + 214 /* floppy */,
- &tcp_dev_storage /* tcp */,
- wordlist + 345 /* rawdrive */,
- wordlist + 255 /* tape */
-};
-
-const device *uniq_devices[] =
-{
- wordlist + 131 /* bad */,
- &fs_dev_storage /* fs */,
- &process_dev_storage /* process */,
- &registry_dev_storage /* registry */,
- &proc_dev_storage /* proc */,
- wordlist + 222 /* fifo */,
- &pipew_dev_storage /* pipew */,
- &piper_dev_storage /* piper */,
- wordlist + 340 /* pipe */,
- wordlist + 246 /* mem */,
- wordlist + 288 /* kmem */,
- wordlist + 358 /* null */,
- wordlist + 310 /* zero */,
- wordlist + 357 /* port */,
- wordlist + 316 /* random */,
- wordlist + 326 /* urandom */,
- wordlist + 306 /* tty */,
- wordlist + 333 /* console */,
- wordlist + 327 /* ptym */,
- wordlist + 344 /* conout */,
- wordlist + 315 /* conin */,
- wordlist + 352 /* clipboard */,
- wordlist + 325 /* windows */,
- wordlist + 291 /* oss_dsp */,
- wordlist + 259 /* serial */
-};
-
-#ifdef __GNUC__
-__inline
-#endif
-const struct device *
-device::lookup (register const char *str, register unsigned int len)
-{
- if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
- {
- register int key = devhash (str, len);
-
- if (key <= MAX_HASH_VALUE && key >= 0)
- {
- register const char *s = wordlist[key].name;
-
- if (strncasematch (str, s, len))
- return &wordlist[key];
- }
- }
- return 0;
-}
-void
-device::parse (const char *s)
-{
- size_t len = strlen (s);
- const device *dev = lookup (s, len);
- unsigned unit = 0;
-
- if (!dev || !*dev)
- {
- size_t prior_len = len;
- while (len-- > 0 && isdigit (s[len]))
- continue;
- if (++len < prior_len)
- {
- dev = lookup (s, len);
- if (!dev || (!dev->upper && !dev->devn == FH_TTY))
- dev = NULL;
- else
- {
- unsigned n = atoi (s + len);
- if (dev->devn == FH_TTY)
- dev = ttys_dev; // SIGH
- if (n >= dev->lower && n <= dev->upper)
- unit = n;
- }
- }
- }
-
- if (!dev || !*dev)
- *this = *fs_dev;
- else if (dev->devn == FH_TTY)
- tty_to_real_device ();
- else
- {
- *this = *dev;
- if (!setunit (unit))
- devn = 0;
- }
-}
-
-void
-device::init ()
-{
- /* nothing to do... yet */
-}
-
-void
-device::parse (_major_t major, _minor_t minor)
-{
- _dev_t dev = FHDEV (major, 0);
-
- devn = 0;
-
- unsigned i;
- for (i = 0; i < (sizeof (unit_devices) / sizeof (unit_devices[0])); i++)
- if (unit_devices[i]->devn == dev)
- {
- *this = *unit_devices[i];
- this->setunit (minor);
- goto out;
- }
-
- dev = FHDEV (major, minor);
- for (i = 0; i < (sizeof (uniq_devices) / sizeof (uniq_devices[0])); i++)
- if (uniq_devices[i]->devn == dev)
- {
- *this = *uniq_devices[i];
- break;
- }
-
-out:
- if (!*this)
- devn = FHDEV (major, minor);
- return;
-}
-
-void
-device::parse (_dev_t dev)
-{
- parse (_major (dev), _minor (dev));
-}
-
-void
-device::tty_to_real_device ()
-{
- if (!real_tty_attached (myself))
- *this = myself->ctty < 0 ? *bad_dev : *console_dev;
- else
- {
- *this = *ttys_dev;
- setunit (myself->ctty);
- }
-}
diff --git a/winsup/cygwin/devices.gperf b/winsup/cygwin/devices.gperf
deleted file mode 100644
index 5251f59f3..000000000
--- a/winsup/cygwin/devices.gperf
+++ /dev/null
@@ -1,164 +0,0 @@
-%{
-#include "winsup.h"
-#include <sys/types.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include "devices.h"
-#include "sys/cygwin.h"
-#include "tty.h"
-#include "pinfo.h"
-#undef __GNUC__
-static unsigned int devhash (const char *, unsigned)
- __attribute__ ((regparm (2)));
-%}
-struct device;
-%%
-"/dev/tty", FH_TTY, "\\dev\\tty", 0, 0, 0, 0
-"/dev/ttys", FH_TTYS, "\\dev\\tty%d", 0, 255, 0, 0
-"/dev/console", FH_CONSOLE, "\\dev\\console", 0, 0, 0, 0
-"/dev/ttym", FH_TTYM, "\\dev\\ttym", 0, 255, 0, 0
-"/dev/ptmx", FH_PTYM, "\\dev\\ptmx", 0, 0, 0, 0
-"/dev/windows", FH_WINDOWS, "\\dev\\windows", 0, 0, 0, 0
-"/dev/dsp", FH_OSS_DSP, "\\dev\\dsp", 0, 0, 0, 0
-"/dev/conin", FH_CONIN, "conin", 0, 0, 0, 0
-"/dev/conout", FH_CONOUT, "conout", 0, 0, 0, 0
-"/dev/null", FH_NULL, "nul", 0, 0, 0, 0
-"/dev/zero", FH_ZERO, "\\dev\\zero", 0, 0, 0, 0
-"/dev/random", FH_RANDOM, "\\dev\\random", 0, 0, 0, 0
-"/dev/urandom", FH_URANDOM, "\\dev\\urandom", 0, 0, 0, 0
-"/dev/mem", FH_MEM, "\\dev\\mem", 0, 0, 0, 0
-"/dev/kmem", FH_KMEM, "\\dev\\mem", 0, 0, 0, 0
-"/dev/clipboard", FH_CLIPBOARD, "\\dev\\clipboard", 0, 0, 0, 0
-"/dev/port", FH_PORT, "\\dev\\port", 0, 0, 0, 0
-"/dev/com", FH_SERIAL, "\\.\\com%d", 1, 99
-"/dev/ttyS", FH_SERIAL, "\\.\\com%d", 0, 99, -1
-"/dev/pipe", FH_PIPE, "\\dev\\pipe", 0, 0, 0, 0
-"/dev/fifo", FH_FIFO, "\\dev\\fifo", 0, 0, 0, 0
-"/dev/st", FH_TAPE, "\\Device\\Tape%d", 0, 127
-"/dev/nst", FH_NTAPE, "\\Device\\Tape%d", 0, 127
-"/dev/fd", FH_FLOPPY, "\\Device\\Floppy%d", 0, 15
-"/dev/scd", FH_CDROM, "\\Device\\CdRom%d", 0, 15
-"/dev/sr", FH_CDROM, "\\Device\\CdRom%d", 0, 15
-"/dev/hda", FH_SDA, "\\Device\\Harddisk%d\\Partition%d", 1, 16, -1
-"/dev/hdb", FH_SDB, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 15
-"/dev/hdc", FH_SDC, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 31
-"/dev/hdd", FH_SDD, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 47
-"/dev/hde", FH_SDE, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 63
-"/dev/hdf", FH_SDF, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 79
-"/dev/hdg", FH_SDG, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 95
-"/dev/hdh", FH_SDH, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 111
-"/dev/hdi", FH_SDI, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 127
-"/dev/hdj", FH_SDJ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 143
-"/dev/hdk", FH_SDK, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 159
-"/dev/hdl", FH_SDL, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 175
-"/dev/hdm", FH_SDM, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 191
-"/dev/hdn", FH_SDN, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 207
-"/dev/hdo", FH_SDO, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 223
-"/dev/hdp", FH_SDP, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 239
-"/dev/hdq", FH_SDQ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 255 /* FIXME 8 bit lunacy */
-"/dev/hdr", FH_SDR, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 271
-"/dev/hds", FH_SDS, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 287
-"/dev/hdt", FH_SDT, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 303
-"/dev/hdu", FH_SDU, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 319
-"/dev/hdv", FH_SDV, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 335
-"/dev/hdw", FH_SDW, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 351
-"/dev/hdx", FH_SDX, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 367
-"/dev/hdy", FH_SDY, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 383
-"/dev/hdz", FH_SDZ, "\\Device\\Harddisk%d\\Partition%d", 1, 16, 399
-"/dev/rawdrive", FH_RAWDRIVE, "\\DosDevices\\%c:", 0, 0, 0, 0
-":bad:", FH_BAD, ":bad:", 0, 0, 0, 0
-%%
-void
-device::parse (const char *s)
-{
- size_t len = strlen (s);
- const device *dev = lookup (s, len);
- unsigned unit = 0;
-
- if (!dev || !*dev)
- {
- size_t prior_len = len;
- while (len-- > 0 && isdigit (s[len]))
- continue;
- if (++len < prior_len)
- {
- dev = lookup (s, len);
- if (!dev || (!dev->upper && !dev->devn == FH_TTY))
- dev = NULL;
- else
- {
- unsigned n = atoi (s + len);
- if (dev->devn == FH_TTY)
- dev = ttys_dev; // SIGH
- if (n >= dev->lower && n <= dev->upper)
- unit = n;
- }
- }
- }
-
- if (!dev || !*dev)
- *this = *fs_dev;
- else if (dev->devn == FH_TTY)
- tty_to_real_device ();
- else
- {
- *this = *dev;
- if (!setunit (unit))
- devn = 0;
- }
-}
-
-void
-device::init ()
-{
- /* nothing to do... yet */
-}
-
-void
-device::parse (_major_t major, _minor_t minor)
-{
- _dev_t dev = FHDEV (major, 0);
-
- devn = 0;
-
- unsigned i;
- for (i = 0; i < (sizeof (unit_devices) / sizeof (unit_devices[0])); i++)
- if (unit_devices[i]->devn == dev)
- {
- *this = *unit_devices[i];
- this->setunit (minor);
- goto out;
- }
-
- dev = FHDEV (major, minor);
- for (i = 0; i < (sizeof (uniq_devices) / sizeof (uniq_devices[0])); i++)
- if (uniq_devices[i]->devn == dev)
- {
- *this = *uniq_devices[i];
- break;
- }
-
-out:
- if (!*this)
- devn = FHDEV (major, minor);
- return;
-}
-
-void
-device::parse (_dev_t dev)
-{
- parse (_major (dev), _minor (dev));
-}
-
-void
-device::tty_to_real_device ()
-{
- if (!real_tty_attached (myself))
- *this = myself->ctty < 0 ? *bad_dev : *console_dev;
- else
- {
- *this = *ttys_dev;
- setunit (myself->ctty);
- }
-}
diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h
deleted file mode 100644
index c8b752402..000000000
--- a/winsup/cygwin/devices.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* devices.h
-
- Copyright 2002 Red Hat, Inc.
-
-This file is part of Cygwin.
-
-This software is a copyrighted work licensed under the terms of the
-Cygwin license. Please consult the file "CYGWIN_LICENSE" for
-details. */
-
-#ifndef _DEVICES_H_
-#define _DEVICES_H_
-
-#include <sys/ioctl.h>
-#include <fcntl.h>
-
-enum
-{
- FH_RBINARY = 0x00001000, /* binary read mode */
- FH_WBINARY = 0x00002000, /* binary write mode */
- FH_CLOEXEC = 0x00004000, /* close-on-exec */
- FH_RBINSET = 0x00008000, /* binary read mode has been explicitly set */
- FH_WBINSET = 0x00010000, /* binary write mode has been explicitly set */
- FH_APPEND = 0x00020000, /* always append */
- FH_ASYNC = 0x00040000, /* async I/O */
- FH_SIGCLOSE = 0x00080000, /* signal handler should close fd on interrupt */
-
- FH_SYMLINK = 0x00100000, /* is a symlink */
- FH_EXECABL = 0x00200000, /* file looked like it would run:
- * ends in .exe or .bat or begins with #! */
- FH_W95LSBUG = 0x00400000, /* set when lseek is called as a flag that
- * _write should check if we've moved beyond
- * EOF, zero filling if so. */
- FH_NOHANDLE = 0x00800000, /* No handle associated with fhandler. */
- FH_NOEINTR = 0x01000000, /* Set if I/O should be uninterruptible. */
- FH_FFIXUP = 0x02000000, /* Set if need to fixup after fork. */
- FH_LOCAL = 0x04000000, /* File is unix domain socket */
- FH_SHUTRD = 0x08000000, /* Socket saw a SHUT_RD */
- FH_SHUTWR = 0x10000000, /* Socket saw a SHUT_WR */
- FH_ISREMOTE = 0x10000000, /* File is on a remote drive */
- FH_DCEXEC = 0x20000000, /* Don't care if this is executable */
- FH_HASACLS = 0x40000000, /* True if fs of file has ACLS */
- FH_QUERYOPEN = 0x80000000, /* open file without requesting either read
- or write access */
-
- /* Device flags */
-
- /* Slow devices */
- FH_CONSOLE = 0x00000001, /* is a console */
- FH_CONIN = 0x00000002, /* console input */
- FH_CONOUT = 0x00000003, /* console output */
- FH_TTYM = 0x00000004, /* is a tty master */
- FH_TTYS = 0x00000005, /* is a tty slave */
- FH_PTYM = 0x00000006, /* is a pty master */
- FH_SERIAL = 0x00000007, /* is a serial port */
- FH_PIPE = 0x00000008, /* is a pipe */
- FH_PIPER = 0x00000009, /* read end of a pipe */
- FH_PIPEW = 0x0000000a, /* write end of a pipe */
- FH_SOCKET = 0x0000000b, /* is a socket */
- FH_WINDOWS = 0x0000000c, /* is a window */
- FH_SLOW = 0x00000010, /* "slow" device if below this */
-
- /* Fast devices */
- FH_DISK = 0x00000010, /* is a disk */
- FH_FLOPPY = 0x00000011, /* is a floppy */
- FH_TAPE = 0x00000012, /* is a tape */
- FH_NULL = 0x00000013, /* is the null device */
- FH_ZERO = 0x00000014, /* is the zero device */
- FH_RANDOM = 0x00000015, /* is a random device */
- FH_MEM = 0x00000016, /* is a mem device */
- FH_CLIPBOARD = 0x00000017, /* is a clipboard device */
- FH_OSS_DSP = 0x00000018, /* is a dsp audio device */
- FH_CYGDRIVE= 0x00000019, /* /cygdrive/x */
- FH_PROC = 0x0000001a, /* /proc */
- FH_REGISTRY =0x0000001b, /* /proc/registry */
- FH_PROCESS = 0x0000001c, /* /proc/<n> */
-
- FH_NDEV = 0x0000001d, /* Maximum number of devices */
- FH_DEVMASK = 0x00000fff, /* devices live here */
- FH_BAD = 0xffffffff
-};
-
-#define FHDEVN(n) ((n) & FH_DEVMASK)
-#define FHISSETF(x) __ISSETF (this, x, FH)
-#define FHSETF(x) __SETF (this, x, FH)
-#define FHCLEARF(x) __CLEARF (this, x, FH)
-#define FHCONDSETF(n, x) __CONDSETF(n, this, x, FH)
-
-#define FHSTATOFF 0
-#endif
diff --git a/winsup/cygwin/lib/getopt.c b/winsup/cygwin/lib/getopt.c
new file mode 100644
index 000000000..b5d5a23b9
--- /dev/null
+++ b/winsup/cygwin/lib/getopt.c
@@ -0,0 +1,503 @@
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define REPLACE_GETOPT
+
+#define _DIAGASSERT(x) do {} while (0)
+
+#ifdef REPLACE_GETOPT
+#ifdef __weak_alias
+__weak_alias(getopt,_getopt)
+#endif
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#ifdef __weak_alias
+__weak_alias(getopt_long,_getopt_long)
+#endif
+
+#ifndef __CYGWIN__
+#define __progname __argv[0]
+#else
+extern char __declspec(dllimport) *__progname;
+#endif
+
+#define IGNORE_FIRST (*options == '-' || *options == '+')
+#define PRINT_ERROR ((opterr) && ((*options != ':') \
+ || (IGNORE_FIRST && options[1] != ':')))
+
+#define IS_POSIXLY_CORRECT (getenv("POSIXLY_INCORRECT_GETOPT") == NULL)
+
+#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((IGNORE_FIRST && options[1] == ':') \
+ || (*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+static char EMSG[1];
+
+static int getopt_internal (int, char * const *, const char *);
+static int gcd (int, int);
+static void permute_args (int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+static void
+_vwarnx(const char *fmt, va_list ap)
+{
+ (void)fprintf(stderr, "%s: ", __progname);
+ if (fmt != NULL)
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+}
+
+static void
+warnx(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ _vwarnx(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(a, b)
+ int a;
+ int b;
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return b;
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(panonopt_start, panonopt_end, opt_end, nargv)
+ int panonopt_start;
+ int panonopt_end;
+ int opt_end;
+ char * const *nargv;
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ _DIAGASSERT(nargv != NULL);
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ * Returns -2 if -- is found (can be long option or end of options marker).
+ */
+static int
+getopt_internal(nargc, nargv, options)
+ int nargc;
+ char * const *nargv;
+ const char *options;
+{
+ char *oli; /* option letter list index */
+ int optchar;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+
+ optarg = NULL;
+
+ /*
+ * XXX Some programs (like rsyncd) expect to be able to
+ * XXX re-initialize optind to 0 and have getopt_long(3)
+ * XXX properly function again. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = 1;
+
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return -1;
+ }
+ if ((*(place = nargv[optind]) != '-')
+ || (place[1] == '\0')) { /* found non-option */
+ place = EMSG;
+ if (IN_ORDER) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return INORDER;
+ }
+ if (!PERMUTE) {
+ /*
+ * if no permutation wanted, stop parsing
+ * at first non-option
+ */
+ return -1;
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+ if (place[1] && *++place == '-') { /* found "--" */
+ place++;
+ return -2;
+ }
+ }
+ if ((optchar = (int)*place++) == (int)':' ||
+ (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+ /* option letter unknown or ':' */
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return BADCH;
+ }
+ if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
+ /* XXX: what if no long options provided (called by getopt)? */
+ if (*place)
+ return -2;
+
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return BADARG;
+ } else /* white space */
+ place = nargv[optind];
+ /*
+ * Handle -W arg the same as --arg (which causes getopt to
+ * stop parsing).
+ */
+ return -2;
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ /* XXX: disable test for :: if PC? (GNU doesn't) */
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return BADARG;
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return optchar;
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the real getopt]
+ */
+int
+getopt(nargc, nargv, options)
+ int nargc;
+ char * const *nargv;
+ const char *options;
+{
+ int retval;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ ++optind;
+ /*
+ * We found an option (--), so if we skipped non-options,
+ * we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end, optind,
+ nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ retval = -1;
+ }
+ return retval;
+}
+#endif
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, idx)
+ int nargc;
+ char * const *nargv;
+ const char *options;
+ const struct option *long_options;
+ int *idx;
+{
+ int retval;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+ _DIAGASSERT(long_options != NULL);
+ /* idx may be NULL */
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+ place = EMSG;
+
+ if (*current_argv == '\0') { /* found "--" */
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return -1;
+ }
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) ==
+ (unsigned)current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return BADCH;
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of
+ * flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return BADARG;
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use
+ * next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':'
+ * indicates no error should be generated
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring, current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless
+ * of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return BADARG;
+ }
+ } else { /* unknown option */
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return BADCH;
+ }
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ retval = 0;
+ } else
+ retval = long_options[match].val;
+ if (idx)
+ *idx = match;
+ }
+ return retval;
+}
diff --git a/winsup/cygwin/lib/iruserok.c b/winsup/cygwin/lib/iruserok.c
new file mode 100644
index 000000000..5e957184e
--- /dev/null
+++ b/winsup/cygwin/lib/iruserok.c
@@ -0,0 +1,319 @@
+/* Based on the rcmd.c.new file distributed with linux libc 5.4.19
+ Adapted to inetutils by Bernhard Rosenkraenzer <bero@startrek.in-trier.de>
+
+ Note that a lot in this file is superfluous; hopefully it won't be a
+ problem for systems that need it for iruserok &c.... */
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __CYGWIN__
+#define HAVE_MALLOC_H
+#define HAVE_STDLIB_H
+#define HAVE_STRING_H
+#define TIME_WITH_SYS_TIME
+#define PATH_HEQUIV "/etc/hosts.equiv"
+
+static int __ivaliduser();
+static int __icheckhost();
+
+struct hostent *cygwin_gethostbyname (const char *name);
+unsigned long cygwin_inet_addr (const char *cp);
+
+#define gethostbyname cygwin_gethostbyname
+#define inet_addr cygwin_inet_addr
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#include <netdb.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#ifndef __CYGWIN__
+#include <resolv.h>
+#endif
+
+int __check_rhosts_file = 1;
+const char *__rcmd_errstr;
+
+int
+ruserok(rhost, superuser, ruser, luser)
+ const char *rhost;
+ int superuser;
+ const char *ruser;
+ const char *luser;
+{
+ struct hostent *hp;
+ u_long addr;
+ char **ap;
+
+ if ((hp = gethostbyname(rhost)) == NULL)
+ return (-1);
+ for (ap = hp->h_addr_list; *ap; ++ap) {
+ bcopy(*ap, &addr, sizeof(addr));
+ if (iruserok(addr, superuser, ruser, luser) == 0)
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * New .rhosts strategy: We are passed an ip address. We spin through
+ * hosts.equiv and .rhosts looking for a match. When the .rhosts only
+ * has ip addresses, we don't have to trust a nameserver. When it
+ * contains hostnames, we spin through the list of addresses the nameserver
+ * gives us and look for a match.
+ *
+ * Returns 0 if ok, -1 if not ok.
+ */
+int
+iruserok(raddr, superuser, ruser, luser)
+ u_long raddr;
+ int superuser;
+ const char *ruser;
+ const char *luser;
+{
+ register const char *cp;
+ struct stat sbuf;
+ struct passwd *pwd;
+ FILE *hostf;
+ uid_t uid;
+ int first = 1;
+ char *pbuf;
+
+ first = 1;
+ hostf = superuser ? NULL : fopen(PATH_HEQUIV, "r");
+again:
+ if (hostf) {
+ if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
+ (void) fclose(hostf);
+ return(0);
+ }
+ (void) fclose(hostf);
+ }
+ if (first == 1 && (__check_rhosts_file || superuser)) {
+ first = 0;
+ if ((pwd = getpwnam(luser)) == NULL)
+ return(-1);
+
+ pbuf = malloc (strlen (pwd->pw_dir) + sizeof "/.rhosts");
+ if (! pbuf)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ strcpy (pbuf, pwd->pw_dir);
+ strcat (pbuf, "/.rhosts");
+
+ /*
+ * Change effective uid while opening .rhosts. If root and
+ * reading an NFS mounted file system, can't read files that
+ * are protected read/write owner only.
+ */
+ uid = geteuid();
+ (void)seteuid(pwd->pw_uid);
+ hostf = fopen(pbuf, "r");
+ (void)seteuid(uid);
+
+ if (hostf == NULL)
+ return(-1);
+ /*
+ * If not a regular file, or is owned by someone other than
+ * user or root or if writeable by anyone but the owner, quit.
+ */
+ cp = NULL;
+ if (lstat(pbuf, &sbuf) < 0)
+ cp = ".rhosts not regular file";
+ else if (!S_ISREG(sbuf.st_mode))
+ cp = ".rhosts not regular file";
+ else if (fstat(fileno(hostf), &sbuf) < 0)
+ cp = ".rhosts fstat failed";
+ else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
+ cp = "bad .rhosts owner";
+ else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
+ cp = ".rhosts writeable by other than owner";
+ /* If there were any problems, quit. */
+ if (cp) {
+ __rcmd_errstr = (char *) cp;
+ fclose(hostf);
+ return(-1);
+ }
+ goto again;
+ }
+ return (-1);
+}
+
+/*
+ * XXX
+ * Don't make static, used by lpd(8).
+ *
+ * Returns 0 if ok, -1 if not ok.
+ */
+#ifdef __CYGWIN__
+static
+#endif
+int
+__ivaliduser(hostf, raddr, luser, ruser)
+ FILE *hostf;
+ u_long raddr;
+ const char *luser;
+ const char *ruser;
+{
+ size_t buf_offs = 0;
+ size_t buf_len = 256;
+ char *buf = malloc (buf_len);
+
+ if (! buf)
+ return -1;
+
+ while (fgets(buf + buf_offs, buf_len - buf_offs, hostf)) {
+ /*int ch;*/
+ register char *user, *p;
+
+ if (strchr(buf + buf_offs, '\n') == NULL) {
+ /* No newline yet, read some more. */
+ buf_offs += strlen (buf + buf_offs);
+
+ if (buf_offs >= buf_len - 1) {
+ /* Make more room in BUF. */
+ char *new_buf;
+
+ buf_len += buf_len;
+ new_buf = realloc (buf, buf_len);
+
+ if (! new_buf) {
+ free (buf);
+ return -1;
+ }
+
+ buf = new_buf;
+ }
+
+ continue;
+ }
+
+ buf_offs = 0; /* Start at beginning next time around. */
+
+ p = buf;
+ while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
+ /* *p = isupper(*p) ? tolower(*p) : *p; -- Uli */
+ *p = tolower(*p); /* works for linux libc */
+ p++;
+ }
+ if (*p == ' ' || *p == '\t') {
+ *p++ = '\0';
+ while (*p == ' ' || *p == '\t')
+ p++;
+ user = p;
+ while (*p != '\n' && *p != ' ' &&
+ *p != '\t' && *p != '\0')
+ p++;
+ } else
+ user = p;
+ *p = '\0';
+
+ if (__icheckhost(raddr, buf) && !strcmp(ruser, *user ? user : luser)) {
+ free (buf);
+ return (0);
+ }
+ }
+
+ free (buf);
+
+ return (-1);
+}
+
+/*
+ * Returns "true" if match, 0 if no match.
+ */
+#ifdef __CYGWIN__
+static
+#endif
+int
+__icheckhost(raddr, lhost)
+ u_long raddr;
+ register char *lhost;
+{
+ register struct hostent *hp;
+ register u_long laddr;
+ register char **pp;
+
+ /* Try for raw ip address first. */
+ if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
+ return (raddr == laddr);
+
+ /* Better be a hostname. */
+ if ((hp = gethostbyname(lhost)) == NULL)
+ return (0);
+
+ /* Spin through ip addresses. */
+ for (pp = hp->h_addr_list; *pp; ++pp)
+ if (!bcmp(&raddr, *pp, sizeof(u_long)))
+ return (1);
+
+ /* No match. */
+ return (0);
+}
diff --git a/winsup/cygwin/threaded_queue.cc b/winsup/cygwin/threaded_queue.cc
new file mode 100755
index 000000000..ba0fe4178
--- /dev/null
+++ b/winsup/cygwin/threaded_queue.cc
@@ -0,0 +1,408 @@
+/* threaded_queue.cc
+
+ Copyright 2001, 2002 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 "woutsup.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include "threaded_queue.h"
+
+/*****************************************************************************/
+
+/* queue_request */
+
+queue_request::~queue_request ()
+{}
+
+/*****************************************************************************/
+
+/* threaded_queue */
+
+threaded_queue::threaded_queue (const size_t initial_workers)
+ : _workers_count (0),
+ _running (false),
+ _submitters_head (NULL),
+ _requests_count (0),
+ _requests_head (NULL),
+ _requests_sem (NULL)
+{
+ InitializeCriticalSection (&_queue_lock);
+
+ // This semaphore's count is the number of requests on the queue.
+ // The maximum count (129792) is calculated as MAXIMUM_WAIT_OBJECTS
+ // multiplied by max. threads per process (2028?), which is (a few)
+ // more requests than could ever be pending with the current design.
+
+ _requests_sem = CreateSemaphore (NULL, // SECURITY_ATTRIBUTES
+ 0, // Initial count
+ 129792, // Maximum count
+ NULL); // Anonymous
+
+ if (!_requests_sem)
+ {
+ system_printf (("failed to create the request queue semaphore, "
+ "error = %lu"),
+ GetLastError ());
+ abort ();
+ }
+
+ create_workers (initial_workers);
+}
+
+threaded_queue::~threaded_queue ()
+{
+ if (_running)
+ stop ();
+
+ debug_printf ("deleting all pending queue requests");
+ queue_request *reqptr = _requests_head;
+ while (reqptr)
+ {
+ queue_request *const ptr = reqptr;
+ reqptr = reqptr->_next;
+ safe_delete (ptr);
+ }
+
+ DeleteCriticalSection (&_queue_lock);
+ if (_requests_sem)
+ (void) CloseHandle (_requests_sem);
+}
+
+/* FIXME: return success or failure rather than quitting */
+void
+threaded_queue::add_submission_loop (queue_submission_loop *const submitter)
+{
+ assert (this);
+ assert (submitter);
+ assert (submitter->_queue == this);
+ assert (!submitter->_next);
+
+ submitter->_next =
+ TInterlockedExchangePointer (&_submitters_head, submitter);
+
+ if (_running)
+ submitter->start ();
+}
+
+bool
+threaded_queue::start ()
+{
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = true;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (!was_running)
+ {
+ debug_printf ("starting all queue submission loops");
+
+ while (loopptr)
+ {
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->start ();
+ }
+ }
+
+ return was_running;
+}
+
+bool
+threaded_queue::stop ()
+{
+ EnterCriticalSection (&_queue_lock);
+ const bool was_running = _running;
+ _running = false;
+ queue_submission_loop *loopptr = _submitters_head;
+ LeaveCriticalSection (&_queue_lock);
+
+ if (was_running)
+ {
+ debug_printf ("stopping all queue submission loops");
+ while (loopptr)
+ {
+ queue_submission_loop *const ptr = loopptr;
+ loopptr = loopptr->_next;
+ ptr->stop ();
+ }
+
+ ReleaseSemaphore (_requests_sem, _workers_count, NULL);
+ while (_workers_count)
+ {
+ debug_printf (("waiting for worker threads to terminate: "
+ "%lu still running"),
+ _workers_count);
+ Sleep (1000);
+ }
+ debug_printf ("all worker threads have terminated");
+ }
+
+ return was_running;
+}
+
+/* FIXME: return success or failure */
+void
+threaded_queue::add (queue_request *const therequest)
+{
+ assert (this);
+ assert (therequest);
+ assert (!therequest->_next);
+
+ if (!_workers_count)
+ {
+ system_printf ("warning: no worker threads to handle request!");
+ // FIXME: And then what?
+ }
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_requests_head)
+ _requests_head = therequest;
+ else
+ {
+ /* Add to the queue end. */
+ queue_request *reqptr = _requests_head;
+ for (; reqptr->_next; reqptr = reqptr->_next)
+ {}
+ assert (reqptr);
+ assert (!reqptr->_next);
+ reqptr->_next = therequest;
+ }
+
+ _requests_count += 1;
+ assert (_requests_count > 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ (void) ReleaseSemaphore (_requests_sem, 1, NULL);
+}
+
+/*static*/ DWORD WINAPI
+threaded_queue::start_routine (const LPVOID lpParam)
+{
+ class threaded_queue *const queue = (class threaded_queue *) lpParam;
+ assert (queue);
+
+ queue->worker_loop ();
+
+ const long count = InterlockedDecrement (&queue->_workers_count);
+ assert (count >= 0);
+
+ if (queue->_running)
+ debug_printf ("worker loop has exited; thread about to terminate");
+
+ return 0;
+}
+
+/* Called from the constructor: so no need to be thread-safe until the
+ * worker threads start to be created; thus the interlocked increment
+ * of the `_workers_count' field.
+ */
+
+void
+threaded_queue::create_workers (const size_t initial_workers)
+{
+ assert (initial_workers > 0);
+
+ for (unsigned int i = 0; i != initial_workers; i++)
+ {
+ const long count = InterlockedIncrement (&_workers_count);
+ assert (count > 0);
+
+ DWORD tid;
+ const HANDLE hThread =
+ CreateThread (NULL, 0, start_routine, this, 0, &tid);
+
+ if (!hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+
+ (void) CloseHandle (hThread);
+ }
+}
+
+void
+threaded_queue::worker_loop ()
+{
+ while (true)
+ {
+ const DWORD rc = WaitForSingleObject (_requests_sem, INFINITE);
+ if (rc == WAIT_FAILED)
+ {
+ system_printf ("wait for request semaphore failed, error = %lu",
+ GetLastError ());
+ return;
+ }
+ assert (rc == WAIT_OBJECT_0);
+
+ EnterCriticalSection (&_queue_lock);
+ if (!_running)
+ {
+ LeaveCriticalSection (&_queue_lock);
+ return;
+ }
+
+ assert (_requests_head);
+ queue_request *const reqptr = _requests_head;
+ _requests_head = reqptr->_next;
+
+ _requests_count -= 1;
+ assert (_requests_count >= 0);
+ LeaveCriticalSection (&_queue_lock);
+
+ assert (reqptr);
+ reqptr->process ();
+ safe_delete (reqptr);
+ }
+}
+
+/*****************************************************************************/
+
+/* queue_submission_loop */
+
+queue_submission_loop::queue_submission_loop (threaded_queue *const queue,
+ const bool ninterruptible)
+ : _running (false),
+ _interrupt_event (NULL),
+ _queue (queue),
+ _interruptible (ninterruptible),
+ _hThread (NULL),
+ _tid (0),
+ _next (NULL)
+{
+ if (_interruptible)
+ {
+ // verbose: debug_printf ("creating an interruptible processing thread");
+
+ _interrupt_event = CreateEvent (NULL, // SECURITY_ATTRIBUTES
+ FALSE, // Auto-reset
+ FALSE, // Initially non-signalled
+ NULL); // Anonymous
+
+ if (!_interrupt_event)
+ {
+ system_printf ("failed to create interrupt event, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+ }
+}
+
+queue_submission_loop::~queue_submission_loop ()
+{
+ if (_running)
+ stop ();
+ if (_interrupt_event)
+ (void) CloseHandle (_interrupt_event);
+ if (_hThread)
+ (void) CloseHandle (_hThread);
+}
+
+bool
+queue_submission_loop::start ()
+{
+ assert (this);
+ assert (!_hThread);
+
+ const bool was_running = _running;
+
+ if (!was_running)
+ {
+ _running = true;
+
+ _hThread = CreateThread (NULL, 0, start_routine, this, 0, &_tid);
+ if (!_hThread)
+ {
+ system_printf ("failed to create thread, error = %lu",
+ GetLastError ());
+ abort ();
+ }
+ }
+
+ return was_running;
+}
+
+bool
+queue_submission_loop::stop ()
+{
+ assert (this);
+ assert (_hThread && _hThread != INVALID_HANDLE_VALUE);
+
+ const bool was_running = _running;
+
+ if (_running)
+ {
+ _running = false;
+
+ if (_interruptible)
+ {
+ assert (_interrupt_event
+ && _interrupt_event != INVALID_HANDLE_VALUE);
+
+ SetEvent (_interrupt_event);
+
+ if (WaitForSingleObject (_hThread, 1000) == WAIT_TIMEOUT)
+ {
+ system_printf (("request loop thread %lu failed to shutdown "
+ "when asked politely: about to get heavy"),
+ _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ {
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
+ abort ();
+ }
+ }
+ }
+ else
+ {
+ // FIXME: could wait to see if the request loop notices that
+ // the submission loop is no longer running and shuts down
+ // voluntarily.
+
+ debug_printf ("killing request loop thread %lu", _tid);
+
+ if (!TerminateThread (_hThread, 0))
+ system_printf (("failed to kill request loop thread %lu"
+ ", error = %lu"),
+ _tid, GetLastError ());
+ }
+ }
+
+ return was_running;
+}
+
+/*static*/ DWORD WINAPI
+queue_submission_loop::start_routine (const LPVOID lpParam)
+{
+ class queue_submission_loop *const submission_loop =
+ (class queue_submission_loop *) lpParam;
+ assert (submission_loop);
+
+ submission_loop->request_loop ();
+
+ debug_printf ("submission loop has exited; thread about to terminate");
+
+ submission_loop->stop ();
+
+ return 0;
+}
+
+/*****************************************************************************/
diff --git a/winsup/cygwin/threaded_queue.h b/winsup/cygwin/threaded_queue.h
new file mode 100755
index 000000000..5b6fddc42
--- /dev/null
+++ b/winsup/cygwin/threaded_queue.h
@@ -0,0 +1,127 @@
+/* threaded_queue.h
+
+ Copyright 2001, 2002 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:
+ queue_request *_next;
+
+ queue_request () : _next (NULL) {}
+ virtual ~queue_request ();
+
+ virtual void process () = 0;
+};
+
+/*****************************************************************************/
+
+/* a queue to allocate requests from n submission loops to x worker threads */
+
+class queue_submission_loop;
+
+class threaded_queue
+{
+public:
+ threaded_queue (size_t initial_workers = 1);
+ ~threaded_queue ();
+
+ void add_submission_loop (queue_submission_loop *);
+
+ bool running () const { return _running; }
+
+ bool start ();
+ bool stop ();
+
+ void add (queue_request *);
+
+private:
+ long _workers_count;
+ bool _running;
+
+ queue_submission_loop *_submitters_head;
+
+ long _requests_count; // Informational only.
+ queue_request *_requests_head;
+
+ CRITICAL_SECTION _queue_lock;
+ HANDLE _requests_sem; // == _requests_count
+
+ static DWORD WINAPI start_routine (LPVOID /* this */);
+
+ void create_workers (size_t initial_workers);
+ void worker_loop ();
+};
+
+/*****************************************************************************/
+
+/* parameters for a request finding and submitting loop */
+
+class queue_submission_loop
+{
+ friend class threaded_queue;
+
+public:
+ queue_submission_loop (threaded_queue *, bool ninterruptible);
+ virtual ~queue_submission_loop ();
+
+ bool start ();
+ bool stop ();
+
+ threaded_queue *queue () { return _queue; };
+
+protected:
+ bool _running;
+ HANDLE _interrupt_event;
+ threaded_queue *const _queue;
+
+private:
+ bool _interruptible;
+ HANDLE _hThread;
+ DWORD _tid;
+ queue_submission_loop *_next;
+
+ static DWORD WINAPI start_routine (LPVOID /* this */);
+ virtual void request_loop () = 0;
+};
+
+#ifdef __cplusplus
+
+/*---------------------------------------------------------------------------*
+ * Some type-safe versions of the various interlocked functions.
+ *---------------------------------------------------------------------------*/
+
+template <typename T> T *
+TInterlockedExchangePointer (T **lvalue, T *rvalue)
+{
+ return reinterpret_cast<T *>
+ (InterlockedExchangePointer (reinterpret_cast<void **> (lvalue),
+ reinterpret_cast<void *> (rvalue)));
+}
+
+template <typename T> T *
+TInterlockedCompareExchangePointer (T **lvalue, T *rvalue1, T *rvalue2)
+{
+ return reinterpret_cast<T *>
+ (InterlockedCompareExchangePointer (reinterpret_cast<void **> (lvalue),
+ reinterpret_cast<void *> (rvalue1),
+ reinterpret_cast<void *> (rvalue2)));
+}
+
+#endif /* __cplusplus */
+
+#endif /* _THREADED_QUEUE_ */