diff options
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/cygrun.c | 67 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver.cc | 773 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_client.cc | 528 | ||||
-rw-r--r-- | winsup/cygwin/cygserver_ipc.h | 84 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_process.cc | 431 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_shm.cc | 896 | ||||
-rw-r--r-- | winsup/cygwin/cygserver_shm.h | 147 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_transport.cc | 51 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_transport_pipes.cc | 362 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_transport_sockets.cc | 387 | ||||
-rw-r--r-- | winsup/cygwin/devices.cc | 453 | ||||
-rw-r--r-- | winsup/cygwin/devices.gperf | 164 | ||||
-rw-r--r-- | winsup/cygwin/devices.h | 90 | ||||
-rw-r--r-- | winsup/cygwin/lib/getopt.c | 503 | ||||
-rw-r--r-- | winsup/cygwin/lib/iruserok.c | 319 | ||||
-rwxr-xr-x | winsup/cygwin/threaded_queue.cc | 408 | ||||
-rwxr-xr-x | winsup/cygwin/threaded_queue.h | 127 |
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 = ®istry_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 */, - ®istry_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_ */ |