From d7de63c2c6c863046f8fa0d7b5a846229d5f640d Mon Sep 17 00:00:00 2001 From: rbcollins Date: Thu, 28 Feb 2002 14:30:26 +0000 Subject: [PATCH] 2002-02-28 Robert Collins * Merged cygwin_daemon into head minus the new shm and ipc exports. 2002-02-28 Robert Collins * fhandler_tty.cc (fhandler_tty_slave::open): More debugging. (fhandler_tty_slave::read): Fix printf type for the handle. * tty.cc (tty::common_init): Add a FIXME for security. 2002-01-29 Robert Collins * Makefile.in (OBJS): Remove duplicate localtime.o. 2002-01-17 Robert Collins * cygserver.cc (check_and_dup_handle): Consolidate the two variants for simplicity. Add Some basic debug output. (client_request_attach_tty::serve): Use the new debug_printf for clarity. Mark the duplicated handles as inheritable - fixup_after_fork() doesn't reopen tty's. 2002-01-16 Robert Collins * cygserver.cc (transport): Correct scope. (client_request_attach_tty::serve): Add more debug information. Fix erroneous use of transport instead of conn. * cygserver_transport_pipes.cc (transport_layer_pipes::close): More debug. (transport_layer_pipes::read): Ditto. (transport_layer_pipes::write): Ditto. (transport_layer_pipes::impersonate_client): Ditto. Mon Oct 8 7:41:00 2001 Robert Collins * cygserver.cc (server_request::process): Rename client_request_shm_get to client_request_shm. * cygserver_process.cc (process_cache::add): Rename to add_task. Use process_cleanup instead of process_request. (process_cache::remove_process): New method. (process::process): Initialize new members. (process::~process): New member. (process::cleanup): New method. (process::add_cleanup_routine): New method. (process_request::process): Rename to process_cleanup. Call the process object's cleanup method and then delete it. (process_process_param::request_loop): Remove the signalling process. * cygserver_shm.cc: Globally rename client_request_shm_get to client_request_shm. (client_request_shm_get::serve): Handle attach request counting. * cygserver_shm.h: Globally rename client_request_shm_get to client_request_shm. (class shm_cleanup): New class. * shm.cc: Globally rename client_request_shm_get to client_request_shm. (client_request_shm::client_request_shm): New constructor for attach requests. (shmat): Use it. * include/cygwin/cygserver_process.h (class process_request): Rename to process_cleanup. (class cleanup_routine): New class. (class process): New members and methods to allow calling back when the process terminates. Thu Oct 4 14:12:00 2001 Robert Collins * cygserver.cc (request_loop): Make static. (main): Use new cache constructor syntax. Start cache worker threads. Cleanup the cache at shutdown. * cygserver_process.cc: Run indent. (process_cache::process_cache): Add a trigger to use when adding a process. (process_cache::process): Move process_entry to process. Insert at the end of the list. Trigger the request loop when new process's inserted. (process_cache::process_requests): Do it. (process_cache::add): New method. (process_cache::handle_snapshot): New method. (process::process): Merge in the process_entry fields. (process::handle): Make a stub function. (process::exit_code): New method. (process_request::process): New method. (process_process_param::request_loop): New method. * cygserver_shm.cc: New header dependency - threaded_queue.h. * threaded_queue.cc (threaded_queue::cleanup): Clearer messages. (queue_process_param::stop): Short spinlock on interruptible threads. * threaded_queue.h (class threaded_queue): New constructor. * include/cygwin/cygserver_process.h (process_request): New class. (process_entry): Remove. (process): Merge in process_entry. (process_cache): Inherit from threaded_queue. Tue Oct 2 23:24:00 2001 Robert Collins * cygserver.cc (class server_process_param): Use new constructor syntax. * cygserver_process.cc (process_cache::~process_cache): New function. * threaded_queue.cc: Define condition debug_printf. Run indent. (threaded_queue::cleanup): Move queue_process_param guts to a method. (threaded_queue::process_requests): Ditto. (queue_process_param::queue_process_param): New method. (queue_process_param::~queue_process_param): Ditto. (queue_process_param::start): Ditto. (queue_process_param::stop): Ditto. * threaded_queue.h (class queue_process_param): Add support for interruptible request loops. * cygwin/include/cygwin/cygserver_process.h (class process_cache): Add destructor. Tue Oct 2 23:24:00 2001 Robert Collins * cygserver_client.cc: New flag allow_daemon to disable the daemon completely. (cygserver_request): Check it. (cygserver_init): Ditto. * environ.cc (parse_thing): Add (no)daemon option. Tue Oct 2 23:00:00 2001 Robert Collins * shm.cc: Update to handle include changes from HEAD. Tue Oct 2 16:06:00 2001 Robert Collins * Makefile.in: Remove cygserver_shm.o from cygwin1.dll. Rename cygserver_shm_outside.o to cygserver_shm.o. * cygserver.cc (server_request::process): Use the new client_request constructor. * cygserver_client.cc: Remove the #ifdef's stubs for the server method within cygwin. (client_request_attach_tty::client_request_attach_tty): Use the new client_request constructor. (client_request_shutdown::client_request_shutdown): Ditto. (client_request::client_request): Ditto. * cygserver_shm.cc (client_request_shm_get::serve): Remove the #ifdef'd stub for in-cygwin builds. (client_request_shm_get::client_request_shm_get): Use the new client_request constructor, and remove the in-cygwin variants. * cygserver_shm.h (class client_request_shm_get): #ifndef test the serve method - it's only used in cygserver. * shm.cc (client_request_shm_get::client_request_shm_get): New function. * include/cygwin/cygserver.h (request_header): New constructor. (class client_request): Use it. New constructor accepting the header size. #ifndef test the server method - it's only used within cygserver. (client_request_get_version): #ifdef test the server method. (client_request_shutdown): Ditto. (client_request_attach_tty): Ditto. Tue Oct 2 9:57:00 2001 Robert Collins * Makefile.in: add threaded_queue.o to cygserver.exe. * cygserver.cc: Include threaded_queue.h (class server_request): Inherit from queue_request. (class server_process_param): Inherit from queue_process_param. (class server_request_queue): Inherit from threaded_queue. (request_loop): Adjust for new types. (server_request_queue::process_requests): Remove guts to threaded_queue::process_requests. (server_request::server_request): Adjust for new types. (worker_function): Delete. (server_request_queue::create_workers): Delete. (server_request_queue::cleanup): Delete. (server_request_queue::add): Move guts to threaded_queue::add. * threaded_queue.cc: New file. * threaded_queue.h: New file. Mon Oct 1 12:38:00 2001 Robert Collins * cygserver.cc (client_request::serve): New function. * cygserver_process.cc: Inlude for pthread_once. (process_cache::process_cache): Initialise a crtiical section for write access. (process_cache::process): Use the critical section. Also add missing entries to the cache. (do_process_init): New function to initalise class process static variables. (process::process): Ensure that the process access critical section is initialised. (process::handle): Close the handle of old process's when they have terminated and we are returning the handle for a process with the same pid. * cygserver_shm.cc: Run indent. Include cygserver_process.h to allow process cache functionality. (client_request_shm_get::serve): New parameter for process cache support. Use the process cache, not OpenProcess to get a handle to the originating process. Fix a handle leak with token_handle. * cygserver_shm.h (class client_request_shm_get): Update ::serve for process cache support. * cygserver_transport_pipes.cc: Redefine debug_printf to be conditional on DEBUG. * include/cygwin/cygserver.h: Do not implement client_request::serve in the header. * include/cygwin/cygserver_process.h (class process_cache): Add a write access critical section to prevent races when requests from a multithreaded application arrive. Sun Sep 30 23:41:00 2001 Robert Collins * Makefile.in: Add cygserver_process.o to cygserver.exe. * cygserver.cc: Include signal.h and cygwin_version.h. Define debug_printf as a macro. Define DEBUG to a value. (client_request_attach_tty::serve): Add beginning of process cache support. Change from #ifdef DEBUG to work with new DEBUG style. (client_request_get_version::serve): Add beginning of process cache support. (class server_request): New prototype for support of process cache. (class queue_process_param): New class to allow request loop threading. (class server_request_queue): Add beginning of process cache support. Allow request loop threading. (request_loop): Thread function for request loops. (server_request_queue::process_requests): Initiator for threaded request loops. (client_request_shutdown::serve): Add beginning of process cache support. (server_request::server_request): Ditto. (server_request::process): Use debug_printf. Add beginning of process cache support. (server_request_queue::cleanup): Kill off any request loop threads. (server_request_queue::add): Add beginning of process cache support. (handle_signal): Trigger a shutdown. (main): Print out some useful info at startup - version, date time. Add process cache support. Spawn a separate thread for the transport request loop, thus allowing concurrent support for multiple transports. * cygserver_client.cc (client_request_get_version::serve): Add process cache support. (client_request_attach_tty::serve): Add process cache support. (client_request_shutdown::serve): Add process cache support. * cygsserver_process.cc: New file with the process cache support. * cygserver_shm.cc: Redefine debug_printf to allow conditional output. * cygwin.din: Export shmdt(). * shm.cc: Run indent. Update FIXME's. (shmdt): New function. * include/cygwin/cygserver.h (class client_request): Add process cache support. (class client_request_get_version): Ditto. (class client_request_shutdown): Ditto. (class client_request_attach_tty): Ditto. * include/cygwin/cygserver_process.h: New header for process cache support. Sun Sep 30 8:52:00 2001 Robert Collins * include/cygwin/cygserver_transport.h: Add copyright header. * include/cygwin/cygserver_transport_pipes.h: Ditto. * include/cygwin/cygserver_transport_sockets.h: Ditto. Sat Sep 29 20:40:00 2001 Robert Collins * Makefile.in: Add cygserver_transport_sockets.o to DLL_OFILES. Add cygserver_transport_sockets_outside.o to cygserver.exe. * cygserver.cc: Include new include files. * cygserver_client.cc: Ditto. * cygserver_shm.h: No need to include now. * cygerver_transport.cc: Include new include files. (transport_layer_base::transport_layer_base): Strip back to a stub. (transport_layer_base::listen): Ditto. (transport_layer_base::accept): Ditto. (transport_layer_base::close): Ditto. (transport_layer_base::read): Ditto. (transport_layer_base::write): Ditto. (transport_layer_base::connect): Ditto. * cygserver_transport_pipes.cc: Include new header "cygwin/cygserver_transport_pipes.h". * cygserver_transport_sockets.cc: New file. * dcrt0.cc: No need to include now. * fhandler_tty.cc: Ditto. * tty.cc: Ditto. * include/cygwin/cygserver_transport.h: Strip the base class to a stub. Remove the cygserver_transport_pipes class. * include/cygwin/cygserver_transport_pipes.h: New file. * include/cygwin/cygserver_transport_sockets.h: New file. Tue Sep 25 16:22:00 2001 Robert Collins * autoload.cc: Add dynamic load statement for 'ImpersonateNamedPipeClient'. * Makefile.in: Add new object files, and build instructions for cygserver.exe. * cygwin.din: Export ftok, shmat, shmctl and shmget. * dcrt0.cc: Additional includes for cygserver support. (dll_crt0_1): Initialise the cygserver client. * fhandler.h (fhandler_tty): New method cygserver_attach_tty. * fhandler_tty.cc: Additional includes for cygserver support. (fhandler_tty_slave::open): Attempt to use the cygserver when obtaining handles from the parent process. On failure or 9x use the current method. (fhandler_tty_slave::cygserver_attach_tty): New function. * fork.cc (fork_child): Fixup shm memory mapped areas. * pinfo.h: Declare fixup_shms_after_fork(). * security.h: Declare alloc_sd(). * tty.cc: Additonal includes to support cygserver. (tty::common_init): Don't allow others to open us if the cygserver is running. * winsup.h: Declare cygserver_running. * cygserver.cc: New file. * cygserver_client.cc: New file. * cygserver_shm.cc: New file. * cygserver_shm.h: New file. * cygserver_transport.cc: New file. * cygserver_transport_pipes.cc: New file. * ipc.cc: New file. * shm.cc: New file. * include/cygwin/cygserver.h: New file. * include/cygwin/cygserver_transport.h: New file. * include/sys/ipc.h: New file. * include/sys/shm.h: New file. 2002-02-28 Robert Collins * thread.cc (semaphore::TryWait): Set errno as required by posix 1003.1. (__sem_wait): Ditto. (__sem_trywait): Ditto. --- winsup/cygserver/client.cc | 207 ++++++++ winsup/cygserver/cygserver.cc | 548 ++++++++++++++++++++ winsup/cygserver/process.cc | 389 +++++++++++++++ winsup/cygserver/shm.cc | 552 +++++++++++++++++++++ winsup/cygserver/shm.h | 91 ++++ winsup/cygserver/threaded_queue.cc | 249 ++++++++++ winsup/cygserver/transport.cc | 92 ++++ winsup/cygserver/transport_pipes.cc | 205 ++++++++ winsup/cygserver/transport_sockets.cc | 131 +++++ winsup/cygwin/cygserver.cc | 548 ++++++++++++++++++++ winsup/cygwin/cygserver_client.cc | 207 ++++++++ winsup/cygwin/cygserver_process.cc | 389 +++++++++++++++ winsup/cygwin/cygserver_shm.cc | 552 +++++++++++++++++++++ winsup/cygwin/cygserver_shm.h | 91 ++++ winsup/cygwin/cygserver_transport.cc | 92 ++++ winsup/cygwin/cygserver_transport_pipes.cc | 205 ++++++++ winsup/cygwin/cygserver_transport_sockets.cc | 131 +++++ winsup/cygwin/include/cygwin/cygserver.h | 135 +++++ winsup/cygwin/include/cygwin/cygserver_process.h | 84 ++++ winsup/cygwin/include/cygwin/cygserver_transport.h | 32 ++ .../include/cygwin/cygserver_transport_pipes.h | 39 ++ .../include/cygwin/cygserver_transport_sockets.h | 33 ++ winsup/cygwin/include/sys/ipc.h | 52 ++ winsup/cygwin/include/sys/shm.h | 89 ++++ winsup/cygwin/ipc.cc | 39 ++ winsup/cygwin/shm.cc | 499 +++++++++++++++++++ winsup/cygwin/threaded_queue.cc | 249 ++++++++++ winsup/cygwin/threaded_queue.h | 67 +++ 28 files changed, 5997 insertions(+) create mode 100644 winsup/cygserver/client.cc create mode 100644 winsup/cygserver/cygserver.cc create mode 100644 winsup/cygserver/process.cc create mode 100644 winsup/cygserver/shm.cc create mode 100755 winsup/cygserver/shm.h create mode 100644 winsup/cygserver/threaded_queue.cc create mode 100644 winsup/cygserver/transport.cc create mode 100644 winsup/cygserver/transport_pipes.cc create mode 100644 winsup/cygserver/transport_sockets.cc create mode 100755 winsup/cygwin/cygserver.cc create mode 100755 winsup/cygwin/cygserver_client.cc create mode 100755 winsup/cygwin/cygserver_process.cc create mode 100755 winsup/cygwin/cygserver_shm.cc create mode 100644 winsup/cygwin/cygserver_shm.h create mode 100755 winsup/cygwin/cygserver_transport.cc create mode 100755 winsup/cygwin/cygserver_transport_pipes.cc create mode 100755 winsup/cygwin/cygserver_transport_sockets.cc create mode 100755 winsup/cygwin/include/cygwin/cygserver.h create mode 100755 winsup/cygwin/include/cygwin/cygserver_process.h create mode 100755 winsup/cygwin/include/cygwin/cygserver_transport.h create mode 100755 winsup/cygwin/include/cygwin/cygserver_transport_pipes.h create mode 100755 winsup/cygwin/include/cygwin/cygserver_transport_sockets.h create mode 100644 winsup/cygwin/include/sys/ipc.h create mode 100644 winsup/cygwin/include/sys/shm.h create mode 100644 winsup/cygwin/ipc.cc create mode 100644 winsup/cygwin/shm.cc create mode 100755 winsup/cygwin/threaded_queue.cc create mode 100755 winsup/cygwin/threaded_queue.h diff --git a/winsup/cygserver/client.cc b/winsup/cygserver/client.cc new file mode 100644 index 0000000000..8a1eeafef0 --- /dev/null +++ b/winsup/cygserver/client.cc @@ -0,0 +1,207 @@ +/* cygserver_client.cc + + Copyright 2001 Red Hat Inc. + + Written by Egor Duda + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifdef __OUTSIDE_CYGWIN__ +#undef __INSIDE_CYGWIN__ +#else +#include "winsup.h" +#endif + +#ifndef __INSIDE_CYGWIN__ +#define debug_printf printf +#define api_fatal printf +#include +#include +#endif +#include +#include +#include +//#include "security.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" +#include "cygwin/cygserver.h" + +/* 0 = untested, 1 = running, 2 = dead */ +int cygserver_running=CYGSERVER_UNKNOWN; +/* on by default during development. For release, we probably want off by default */ +int allow_daemon = TRUE; + +client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version)) +{ + buffer = (char *)&version; +} + +client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +{ + buffer = (char *)&req; + req.pid = 0; + req.master_pid = 0; + req.from_master = NULL; + req.to_master = NULL; +} + +client_request_attach_tty::client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +{ + buffer = (char *)&req; + req.pid = npid; + req.master_pid = nmaster_pid; + req.from_master = nfrom_master; + req.to_master = nto_master; +} + +client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0) +{ + buffer = NULL; +} + +client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size) +{ +} + +client_request::~client_request () +{ +} + +client_request::operator class request_header () +{ + return header; +} + +void +client_request::send (transport_layer_base *conn) +{ + if (!conn) + return; + debug_printf("this=%p, conn=%p\n",this, conn); + ssize_t bytes_written, bytes_read; + debug_printf("header.cb = %ld\n",header.cb); + if ((bytes_written = conn->write ((char *)&header, sizeof (header))) + != sizeof(header) || (header.cb && + (bytes_written = conn->write (buffer, header.cb)) != header.cb)) + { + header.error_code = -1; + debug_printf ("bytes written != request size\n"); + return; + } + + debug_printf("Sent request, size (%ld)\n",bytes_written); + + if ((bytes_read = conn->read ((char *)&header, sizeof (header))) + != sizeof (header) || (header.cb && + (bytes_read = conn->read (buffer, header.cb) ) != header.cb)) + { + header.error_code = -1; + debug_printf("failed reading response \n"); + return; + } + debug_printf ("completed ok\n"); +} + +/* Oh, BTW: Fix the procedural basis and make this more intuitive. */ + +int +cygserver_request (client_request * req) +{ + class transport_layer_base *transport; + + if (!req || allow_daemon != TRUE) + return -1; + + /* dont' retry every request if the server's not there */ + if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION) + return -1; + + transport = create_server_transport (); + + /* FIXME: have at most one connection per thread. use TLS to store the details */ + /* logic is: + * if not tlskey->conn, new conn, + * then; transport=conn; + */ + if (!transport->connect ()) + { + delete transport; + return -1; + } + + debug_printf ("connected to server %p\n", transport); + + req->send(transport); + + transport->close (); + + delete transport; + + return 0; +} + +#if 0 +BOOL +check_cygserver_available () +{ + BOOL ret_val = FALSE; + HANDLE pipe = CreateFile (pipe_name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_all_nih, + OPEN_EXISTING, + 0, + NULL); + if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY) + ret_val = TRUE; + + if (pipe && pipe != INVALID_HANDLE_VALUE) + CloseHandle (pipe); + + return (ret_val); +} +#endif + +void +cygserver_init () +{ + int rc; + if (allow_daemon != TRUE) + { + cygserver_running = CYGSERVER_DEAD; + return; + } + + if (cygserver_running==CYGSERVER_OK) + return; + + client_request_get_version *req = + new client_request_get_version (); + + rc = cygserver_request (req); + delete req; + if (rc < 0) + cygserver_running = CYGSERVER_DEAD; + else if (rc > 0) + api_fatal ( "error connecting to cygwin server. error: %d", rc ); + else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR || + req->version.api != CYGWIN_SERVER_VERSION_API || + req->version.minor > CYGWIN_SERVER_VERSION_MINOR) + api_fatal ( "incompatible version of cygwin server.\n\ + client version %d.%d.%d.%d, server version%ld.%ld.%ld.%ld", + CYGWIN_SERVER_VERSION_MAJOR, + CYGWIN_SERVER_VERSION_API, + CYGWIN_SERVER_VERSION_MINOR, + CYGWIN_SERVER_VERSION_PATCH, + req->version.major, + req->version.api, + req->version.minor, + req->version.patch ); + else + cygserver_running = CYGSERVER_OK; +} diff --git a/winsup/cygserver/cygserver.cc b/winsup/cygserver/cygserver.cc new file mode 100644 index 0000000000..57f92c2ac8 --- /dev/null +++ b/winsup/cygserver/cygserver.cc @@ -0,0 +1,548 @@ +/* cygserver.cc + + Copyright 2001 Red Hat Inc. + + Written by Egor Duda + + 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 +#include +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin_version.h" + +#include "getopt.h" + +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" +#include "threaded_queue.h" +#include "cygwin/cygserver_process.h" +#include "cygwin/cygserver.h" +#include "cygserver_shm.h" + +/* for quieter operation, set to 0 */ +#define DEBUG 0 +#define debug_printf if (DEBUG) printf + +GENERIC_MAPPING access_mapping; +static class transport_layer_base *transport; + +DWORD request_count = 0; + +BOOL +setup_privileges () +{ + BOOL rc, ret_val; + HANDLE hToken = NULL; + TOKEN_PRIVILEGES sPrivileges; + + rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ; + if ( !rc ) + { + printf ( "error opening process token (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + rc = LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid ); + if ( !rc ) + { + printf ( "error getting prigilege luid (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + sPrivileges.PrivilegeCount = 1 ; + sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ; + rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ; + if ( !rc ) + { + printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + + access_mapping.GenericRead = FILE_READ_DATA; + access_mapping.GenericWrite = FILE_WRITE_DATA; + access_mapping.GenericExecute = 0; + access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; + + ret_val = TRUE; + +out: + CloseHandle ( hToken ); + return ret_val; +} + +int +check_and_dup_handle (HANDLE from_process, HANDLE to_process, + HANDLE from_process_token, + DWORD access, + HANDLE from_handle, + HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE) +{ + HANDLE local_handle = NULL; + int ret_val = EACCES; + char sd_buf [1024]; + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; + DWORD bytes_needed; + PRIVILEGE_SET ps; + DWORD ps_len = sizeof (ps); + BOOL status; + + if (from_process != GetCurrentProcess ()) +{ + + if (!DuplicateHandle (from_process, from_handle, + GetCurrentProcess (), &local_handle, + 0, bInheritHandle, + DUPLICATE_SAME_ACCESS)) + { + printf ( "error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ()); + goto out; + } +} else + local_handle = from_handle; + + if (!GetKernelObjectSecurity (local_handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + sd, sizeof (sd_buf), &bytes_needed)) + { + printf ( "error getting handle SD (%lu)\n", GetLastError ()); + goto out; + } + + MapGenericMask (&access, &access_mapping); + + if (!AccessCheck (sd, from_process_token, access, &access_mapping, + &ps, &ps_len, &access, &status)) + { + printf ( "error checking access rights (%lu)\n", GetLastError ()); + goto out; + } + + if (!status) + { + printf ( "access to object denied\n"); + goto out; + } + + if (!DuplicateHandle (from_process, from_handle, + to_process, to_handle_ptr, + access, bInheritHandle, 0)) + { + printf ( "error getting handle to client (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr); + + ret_val = 0; + +out: + if (local_handle && from_process != GetCurrentProcess ()) + CloseHandle (local_handle); + + return (ret_val); +} + +void +client_request::serve (transport_layer_base *conn, class process_cache *cache) +{ + printf ("*****************************************\n" + "A call to the base client_request class has occured\n" + "This indicates a mismatch in a virtual function definition somewhere\n"); + exit (1); +} + +void +client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache) +{ + HANDLE from_process_handle = NULL; + HANDLE to_process_handle = NULL; + HANDLE token_handle = NULL; + DWORD rc; + + if (header.cb != sizeof (req)) + { + header.error_code = EINVAL; + return; + } + + debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid, + req.from_master, req.to_master, + req.pid); + + debug_printf ("opening process %ld\n", req.master_pid); + from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); + debug_printf ("opening process %ld\n", req.pid); + to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); + if (!from_process_handle || !to_process_handle) + { + printf ("error opening process (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + debug_printf ("Impersonating client\n"); + conn->impersonate_client (); + + debug_printf ("about to open thread token\n"); + rc = OpenThreadToken (GetCurrentThread (), + TOKEN_QUERY, + TRUE, + &token_handle); + + debug_printf ("opened thread token, rc=%lu\n", rc); + conn->revert_to_self (); + + if (!rc) + { + printf ("error opening thread token (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + if (check_and_dup_handle (from_process_handle, to_process_handle, + token_handle, + GENERIC_READ, + req.from_master, + &req.from_master, TRUE) != 0) + { + printf ("error duplicating from_master handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + if (req.to_master) + { + if (check_and_dup_handle (from_process_handle, to_process_handle, + token_handle, + GENERIC_WRITE, + req.to_master, + &req.to_master, TRUE) != 0) + { + printf ("error duplicating to_master handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + } + +#if DEBUG + printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid, + req.from_master, req.to_master); +#endif + + header.error_code = 0; + +out: + if (from_process_handle) + CloseHandle (from_process_handle); + if (to_process_handle) + CloseHandle (to_process_handle); + if (token_handle) + CloseHandle (token_handle); +} + +void +client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache) +{ + if (header.cb != sizeof (version)) + { + header.error_code = EINVAL; + return; + } + header.error_code = 0; + version.major = CYGWIN_SERVER_VERSION_MAJOR; + version.api = CYGWIN_SERVER_VERSION_API; + version.minor = CYGWIN_SERVER_VERSION_MINOR; + version.patch = CYGWIN_SERVER_VERSION_PATCH; +} + +class server_request : public queue_request +{ + public: + server_request (transport_layer_base *newconn, class process_cache *newcache); + virtual void process (); + private: + char request_buffer [MAX_REQUEST_SIZE]; + transport_layer_base *conn; + class process_cache *cache; +}; + +class server_process_param : public queue_process_param +{ + public: + transport_layer_base *transport; + server_process_param () : queue_process_param (false) {}; +}; + +class server_request_queue : public threaded_queue +{ + public: + class process_cache *cache; + void process_requests (transport_layer_base *transport); + virtual void add (transport_layer_base *conn); +}; +class server_request_queue request_queue; + +static DWORD WINAPI +request_loop (LPVOID LpParam) +{ + class server_process_param *params = (server_process_param *) LpParam; + class server_request_queue *queue = (server_request_queue *) params->queue; + class transport_layer_base * transport = params->transport; + while (queue->active) + { + transport_layer_base * new_conn = transport->accept (); + /* FIXME: this is a little ugly. What we really want is to wait on two objects: + * one for the pipe/socket, and one for being told to shutdown. Otherwise + * this will stay a problem (we won't actually shutdown until the request + * _AFTER_ the shutdown request. And sending ourselves a request is ugly + */ + if (new_conn && queue->active) + queue->add (new_conn); + } + return 0; +} + +/* TODO: check we are not being asked to service a already serviced transport */ +void +server_request_queue::process_requests (transport_layer_base *transport) +{ + class server_process_param *params = new server_process_param; + params->transport = transport; + threaded_queue::process_requests (params, request_loop); +} + +void +client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache) +{ + /* FIXME: link upwards, and then this becomes a trivial method call to + * only shutdown _this queue_ + */ + /* tell the main thread to shutdown */ + request_queue.active=false; +} + +server_request::server_request (transport_layer_base *newconn, class process_cache *newcache) +{ + conn = newconn; + cache = newcache; +} + +void +server_request::process () +{ + ssize_t bytes_read, bytes_written; + struct request_header* req_ptr = (struct request_header*) &request_buffer; + client_request *req = NULL; + debug_printf ("about to read\n"); + + bytes_read = conn->read (request_buffer, sizeof (struct request_header)); + if (bytes_read != sizeof (struct request_header)) + { + printf ("error reading from connection (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("got header (%ld)\n", bytes_read); + + switch (req_ptr->req_id) + { + case CYGSERVER_REQUEST_GET_VERSION: + req = new client_request_get_version (); break; + case CYGSERVER_REQUEST_ATTACH_TTY: + req = new client_request_attach_tty (); break; + case CYGSERVER_REQUEST_SHUTDOWN: + req = new client_request_shutdown (); break; + case CYGSERVER_REQUEST_SHM_GET: + req = new client_request_shm (); break; + default: + req = new client_request (CYGSERVER_REQUEST_INVALID, 0); + req->header.error_code = ENOSYS; + debug_printf ("Bad client request - returning ENOSYS\n"); + } + + if (req->header.cb != req_ptr->cb) + { + debug_printf ("Mismatch in request buffer sizes\n"); + goto out; + } + + if (req->header.cb) + { + + bytes_read = conn->read (req->buffer, req->header.cb); + if (bytes_read != req->header.cb) + { + debug_printf ("error reading from connection (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("got body (%ld)\n",bytes_read); + } + + /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */ + req->serve (conn, cache); + + if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header))) + != sizeof(req->header) || (req->header.cb && + (bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb)) + { + req->header.error_code = -1; + printf ("error writing to connection (%lu)\n", GetLastError ()); + goto out; + } + + debug_printf("Sent reply, size (%ld)\n",bytes_written); + printf ("."); + +out: + conn->close (); + delete conn; + if (req) + delete (req); +} + +void +server_request_queue::add (transport_layer_base *conn) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + /* every derived ::add must enter the section! */ + EnterCriticalSection (&queuelock); + if (!running) + { + conn->close (); + delete conn; + LeaveCriticalSection (&queuelock); + return; + } + queue_request * listrequest = new server_request (conn, cache); + threaded_queue::add (listrequest); + LeaveCriticalSection (&queuelock); +} + +void +handle_signal (int signal) +{ + /* any signal makes us die :} */ + /* FIXME: link upwards, and then this becomes a trivial method call to + * only shutdown _this queue_ + */ + /* tell the main thread to shutdown */ + request_queue.active=false; +} + +struct option longopts[] = { + {"shutdown", no_argument, NULL, 's'}, + {0, no_argument, NULL, 0} +}; + +char opts[] = "s"; + +int +main (int argc, char **argv) +{ + int shutdown=0; + char i; + + while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) + switch (i) + { + case 's': + shutdown = 1; + break; + default: + break; + /*NOTREACHED*/ + } + + wincap.init(); + if (wincap.has_security ()) + setup_privileges (); + transport = create_server_transport (); + + if (shutdown) + { + if (!transport->connect()) + { + printf ("couldn't establish connection with server\n"); + exit (1); + } + client_request_shutdown *request = + new client_request_shutdown (); + request->send (transport); + transport->close(); + delete transport; + delete request; + exit(0); + } + + char version[200]; + /* Cygwin dll release */ + snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s", + cygwin_version.dll_major / 1000, + cygwin_version.dll_major % 1000, + cygwin_version.dll_minor, + cygwin_version.api_major, + cygwin_version.api_minor, + cygwin_version.shared_data, + CYGWIN_SERVER_VERSION_MAJOR, + CYGWIN_SERVER_VERSION_API, + CYGWIN_SERVER_VERSION_MINOR, + CYGWIN_SERVER_VERSION_PATCH, + cygwin_version.mount_registry, + cygwin_version.dll_build_date); + setbuf (stdout, NULL); + printf ("daemon version %s starting up", version); + if (signal (SIGQUIT, handle_signal) == SIG_ERR) + { + printf ("\ncould not install signal handler (%d)- aborting startup\n", errno); + exit (1); + } + printf ("."); + transport->listen (); + printf ("."); + class process_cache cache (2); + request_queue.initial_workers = 10; + request_queue.cache = &cache; + request_queue.create_workers (); + printf ("."); + request_queue.process_requests (transport); + printf ("."); + cache.create_workers (); + printf ("."); + cache.process_requests (); + printf (".complete\n"); + /* TODO: wait on multiple objects - the thread handle for each request loop + + * all the process handles. This should be done by querying the request_queue and + * the process cache for all their handles, and then waiting for (say) 30 seconds. + * after that we recreate the list of handles to wait on, and wait again. + * the point of all this abstraction is that we can trivially server both sockets + * and pipes simply by making a new transport, and then calling + * request_queue.process_requests (transport2); + */ + /* WaitForMultipleObjects abort && request_queue && process_queue && signal + -- if signal event then retrigger it + */ + while (1 && request_queue.active) + { + sleep (1); + } + printf ("\nShutdown request recieved - new requests will be denied\n"); + request_queue.cleanup (); + printf ("All pending requests processed\n"); + transport->close (); + printf ("No longer accepting requests - cygwin will operate in daemonless mode\n"); + cache.cleanup (); + printf ("All outstanding process-cache activities completed\n"); + printf ("daemon shutdown\n"); +} diff --git a/winsup/cygserver/process.cc b/winsup/cygserver/process.cc new file mode 100644 index 0000000000..a5173afd35 --- /dev/null +++ b/winsup/cygserver/process.cc @@ -0,0 +1,389 @@ +/* cygserver_process.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include +#include +#include + +#define debug_printf if (DEBUG) printf +#define DEBUG 1 + +/* the cache structures and classes are designed for one cache per server process. + * To make multiple process caches, a redesign will be needed + */ + +/* process cache */ +process_cache::process_cache (unsigned int num_initial_workers): +head (NULL) +{ + /* there can only be one */ + InitializeCriticalSection (&cache_write_access); + if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create cache add trigger (%lu), terminating\n", + GetLastError ()); + exit (1); + } + initial_workers = num_initial_workers; +} + +process_cache::~process_cache () +{ +} + +class process * +process_cache::process (long pid) +{ + class process *entry = head; + /* TODO: make this more granular, so a search doesn't involve the write lock */ + EnterCriticalSection (&cache_write_access); + if (!entry) + { + entry = new class process (pid); + entry->next = + (class process *) InterlockedExchangePointer (&head, entry); + PulseEvent (cache_add_trigger); + } + else + { + while (entry->winpid != pid && entry->next) + entry = entry->next; + if (entry->winpid != pid) + { + class process *new_entry = new class process (pid); + new_entry->next = + (class process *) InterlockedExchangePointer (&entry->next, + new_entry); + entry = new_entry; + PulseEvent (cache_add_trigger); + } + } + LeaveCriticalSection (&cache_write_access); + return entry; +} + +static DWORD WINAPI +request_loop (LPVOID LpParam) +{ + class process_process_param *params = (process_process_param *) LpParam; + return params->request_loop (); +} + +void +process_cache::process_requests () +{ + class process_process_param *params = new process_process_param; + threaded_queue::process_requests (params, request_loop); +} + +void +process_cache::add_task (class process * theprocess) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + /* every derived ::add must enter the section! */ + EnterCriticalSection (&queuelock); + queue_request *listrequest = new process_cleanup (theprocess); + threaded_queue::add (listrequest); + LeaveCriticalSection (&queuelock); +} + +/* NOT fully MT SAFE: must be called by only one thread in a program */ +void +process_cache::remove_process (class process *theprocess) +{ + class process *entry = head; + /* unlink */ + EnterCriticalSection (&cache_write_access); + if (entry == theprocess) + { + entry = (class process *) InterlockedExchangePointer (&head, theprocess->next); + if (entry != theprocess) + { + printf ("Bug encountered, process cache corrupted\n"); + exit (1); + } + } + else + { + while (entry->next && entry->next != theprocess) + entry = entry->next; + class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next); + if (temp != theprocess) + { + printf ("Bug encountered, process cache corrupted\n"); + exit (1); + } + } + LeaveCriticalSection (&cache_write_access); + /* Process any cleanup tasks */ + add_task (theprocess); +} + + +/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of + * begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0] + * NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed. + * Who cares - It'll get caught the next time. + */ +int +process_cache::handle_snapshot (HANDLE * hdest, class process ** edest, + ssize_t max_copy, int begin_at) +{ + /* TODO:? grab a delete-lock, to prevent deletes during this process ? */ + class process *entry = head; + int count = begin_at; + /* skip begin_at entries */ + while (entry && count) + { + if (entry->exit_code () == STILL_ACTIVE) + count--; + entry = entry->next; + } + /* hit the end of the list within begin_at entries */ + if (count) + return 0; + HANDLE *hto = hdest; + class process **eto = edest; + while (entry && count < max_copy) + { + /* hack */ + if (entry->exit_code () == STILL_ACTIVE) + { + *hto = entry->handle (); + *eto = entry; + count++; + hto++; + eto++; + } + entry = entry->next; + } + return count; +} + +/* process's */ +/* global process crit section */ +static CRITICAL_SECTION process_access; +static pthread_once_t process_init; + +void +do_process_init (void) +{ + InitializeCriticalSection (&process_access); + /* we don't have a cache shutdown capability today */ +} + +process::process (long pid): +winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE) +{ + pthread_once (&process_init, do_process_init); + EnterCriticalSection (&process_access); + thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + if (!thehandle) + { + printf ("unable to obtain handle for new cache process %ld\n", pid); + thehandle = INVALID_HANDLE_VALUE; + } + debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid); + InitializeCriticalSection (&access); + LeaveCriticalSection (&process_access); +} + +process::~process () +{ + DeleteCriticalSection (&access); +} + +HANDLE +process::handle () +{ +// DWORD exitstate = exit_code (); +// if (exitstate == STILL_ACTIVE) + return thehandle; + + /* FIXME: call the cleanup list ? */ + +// CloseHandle (thehandle); +// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n", +// winpid); +// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid); +// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid); +// /* FIXME: what if OpenProcess fails ? */ +// if (thehandle) +// { +// _exit_status = STILL_ACTIVE; +// exit_code (); +// } +// else +// thehandle = INVALID_HANDLE_VALUE; +// return thehandle; +} + +DWORD process::exit_code () +{ + if (_exit_status != STILL_ACTIVE) + return _exit_status; + bool + err = GetExitCodeProcess (thehandle, &_exit_status); + if (!err) + { + debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ()); + thehandle = INVALID_HANDLE_VALUE; + return _exit_status; + } + else if (_exit_status == STILL_ACTIVE) + return _exit_status; + /* add new cleanup task etc etc ? */ + return _exit_status; +} + +/* this is single threaded. It's called after the process is removed from the cache, + * but inserts may be attemped by worker threads that have a pointer to it */ +void +process::cleanup () +{ + /* Serialize this */ + EnterCriticalSection (&access); + InterlockedIncrement (&(long)cleaning_up); + class cleanup_routine *entry = head; + while (entry) + { + class cleanup_routine *temp; + entry->cleanup (winpid); + temp = entry->next; + delete entry; + entry = temp; + } + LeaveCriticalSection (&access); +} + +bool +process::add_cleanup_routine (class cleanup_routine *new_cleanup) +{ + if (cleaning_up) + return false; + EnterCriticalSection (&access); + /* check that we didn't block with ::cleanup () + * This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call + * which would be a whole lot easier + */ + if (cleaning_up) + { + LeaveCriticalSection (&access); + return false; + } + new_cleanup->next = head; + head = new_cleanup; + LeaveCriticalSection (&access); + return true; +} + +/* process_cleanup */ +void +process_cleanup::process () +{ + theprocess->cleanup (); + delete theprocess; +} + +/* process_process_param */ +DWORD +process_process_param::request_loop () +{ + process_cache *cache = (process_cache *) queue; + /* always malloc one, so there is no special case in the loop */ + ssize_t HandlesSize = 2; + HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize); + process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize); + /* TODO: put [1] at the end as it will also get done if a process dies? */ + Handles[0] = interrupt; + Handles[1] = cache->cache_add_trigger; + while (cache->active && !shutdown) + { + int copied; + copied = -1; + int offset; + offset = 1; + int count; + count = 2; + while ((copied == HandlesSize - 2 - offset) || copied < 0) + { + /* we need more storage to cope with all the HANDLES */ + if (copied == HandlesSize - 2 - offset) + { + HANDLE *temp = (HANDLE *) realloc (Handles, + sizeof (HANDLE) * + HandlesSize + 10); + if (!temp) + { + printf + ("cannot allocate more storage for the handle array!\n"); + exit (1); + } + Handles = temp; + process **ptemp = (process **) realloc (Entries, + sizeof (LPVOID) * + HandlesSize + 10); + if (!ptemp) + { + printf + ("cannot allocate more storage for the handle array!\n"); + exit (1); + } + Entries = ptemp; + HandlesSize += 10; + } + offset += copied; + copied = + cache->handle_snapshot (&Handles[2], &Entries[2], + HandlesSize - 2 - offset, offset); + count += copied; + } + debug_printf ("waiting on %u objects\n", count); + DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE); + if (rc == WAIT_FAILED) + { + printf ("Could not wait on the process handles (%ld)!\n", + GetLastError ()); + exit (1); + } + int objindex = rc - WAIT_OBJECT_0; + if (objindex > 1 && objindex < count) + { + debug_printf ("Process %ld has left the building\n", + Entries[objindex]->winpid); + /* fire off the termination routines */ + cache->remove_process (Entries[objindex]); + } + else if (objindex >= 0 && objindex < 2) + { + /* 0 is shutdown - do nothing */ + /* 1 is a cache add event - just rebuild the object list */ + } + else + { + printf + ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n"); + } + } + running = false; + return 0; +} diff --git a/winsup/cygserver/shm.cc b/winsup/cygserver/shm.cc new file mode 100644 index 0000000000..c29938bb97 --- /dev/null +++ b/winsup/cygserver/shm.cc @@ -0,0 +1,552 @@ +/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin + + Copyright 2001 Red Hat, Inc. + + Originally written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + + +#ifdef __OUTSIDE_CYGWIN__ +#undef __INSIDE_CYGWIN__ +#else +#include "winsup.h" +#endif + +#ifndef __INSIDE_CYGWIN__ +#define DEBUG 0 +#define system_printf printf +#define debug_printf if (DEBUG) printf +#define api_fatal printf +#include +#include +#endif + +#include +#include +#include "cygerrno.h" +#include +#include "security.h" +//#include "fhandler.h" +//#include "dtable.h" +//#include "cygheap.h" +#include +//#include "thread.h" +#ifndef __INSIDE_CYGWIN__ +#define __INSIDE_CYGWIN__ +#include +#undef __INSIDE_CYGWIN__ +#else +#include +#endif +//#include "perprocess.h" +#include +#include +#include "cygserver_shm.h" + +// FIXME IS THIS CORRECT +/* Implementation notes: We use two shared memory regions per key: + * One for the control structure, and one for the shared memory. + * While this has a higher overhead tham a single shared area, + * It allows more flexability. As the entire code is transparent to the user + * We can merge these in the future should it be needed. + * Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just + * keep monotonically incrementing - system wide. + */ +size_t +getsystemallocgranularity () +{ + SYSTEM_INFO sysinfo; + static size_t buffer_offset = 0; + if (buffer_offset) + return buffer_offset; + GetSystemInfo (&sysinfo); + buffer_offset = sysinfo.dwAllocationGranularity; + return buffer_offset; +} + + +client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) +{ + buffer = (char *) ¶meters; +} + +/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail. + * Solution: manually calculate the next id value + */ + +#if 0 +extern "C" void * +shmat (int shmid, const void *shmaddr, int parameters.in.shmflg) +{ + class shmid_ds *shm = (class shmid_ds *) shmid; //FIXME: verifyable object test + + if (shmaddr) + { + //FIXME: requested base address ?! + set_errno (EINVAL); + return (void *) -1; + } + + void *rv = MapViewOfFile (shm->attachmap, + + + (parameters.in.shmflg & SHM_RDONLY) ? + FILE_MAP_READ : FILE_MAP_WRITE, 0, + 0, 0); + + if (!rv) + { + //FIXME: translate GetLastError() + set_errno (EACCES); + return (void *) -1; + } + +/* FIXME: this needs to be globally protected to prevent a mismatch betwen + * attach count and attachees list + */ + + InterlockedIncrement (&shm->shm_nattch); + _shmattach *attachnode = new _shmattach; + + attachnode->data = rv; + attachnode->next = + (_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead, + (long int) attachnode); + return rv; +} +#endif + +/* FIXME: evaluate getuid() and getgid() against the requested mode. Then + * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ + * appropriately + */ + +/* Test result from openbsd: shm ids are persistent cross process if a handle is left + * open. This could lead to resource starvation: we're not copying that behaviour + * unless we have to. (It will involve acygwin1.dll gloal shared list :[ ). + */ +/* FIXME: shmid should be a verifyable object + */ + +/* FIXME: on NT we should check everything against the SD. On 95 we just emulate. + */ + +extern GENERIC_MAPPING access_mapping; + +extern int +check_and_dup_handle (HANDLE from_process, HANDLE to_process, + HANDLE from_process_token, + DWORD access, + HANDLE from_handle, + HANDLE * to_handle_ptr, BOOL bInheritHandle); + +//FIXME: where should this live +static shmnode *shm_head = NULL; +/* must be long for InterlockedIncrement */ +static long new_id = 0; +static long new_private_key = 0; + +void +client_request_shm::serve (transport_layer_base * conn, + process_cache * cache) +{ +// DWORD sd_size = 4096; +// char sd_buf[4096]; + PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf; +// /* create a sd for our open requests based on shmflag & 0x01ff */ +// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (), +// parameters.in.shmflg & 0x01ff, psd, &sd_size); + + HANDLE from_process_handle = NULL; + HANDLE token_handle = NULL; + DWORD rc; + + from_process_handle = cache->process (parameters.in.pid)->handle (); + /* possible TODO: reduce the access on the handle before we use it */ + /* Note that unless we do this, we don't need to call CloseHandle - it's kept open + * by the process cache until the process terminates. + * We may need a refcount on the cache however... + */ + if (!from_process_handle) + { + debug_printf ("error opening process (%lu)\n", GetLastError ()); + header.error_code = EACCES; + return; + } + + conn->impersonate_client (); + + rc = OpenThreadToken (GetCurrentThread (), + TOKEN_QUERY, TRUE, &token_handle); + + conn->revert_to_self (); + + if (!rc) + { + debug_printf ("error opening thread token (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (from_process_handle); + return; + } + + + /* we trust the clients request - we will be doing it as them, and + * the worst they can do is open their own permissions + */ + + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof (sa); + sa.lpSecurityDescriptor = psd; + sa.bInheritHandle = TRUE; /* the memory structures inherit ok */ + + char *shmname = NULL, *shmaname = NULL; + char stringbuf[29], stringbuf1[29]; + + /* TODO: make this code block a function! */ + if (parameters.in.type == SHM_REATTACH) + { + /* just find and fill out the existing shm_id */ + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->shm_id == parameters.in.shm_id) + { + parameters.out.shm_id = tempnode->shm_id; + parameters.out.key = tempnode->key; + if (check_and_dup_handle + (GetCurrentProcess (), from_process_handle, token_handle, + DUPLICATE_SAME_ACCESS, tempnode->filemap, + ¶meters.out.filemap, TRUE) != 0) + { + debug_printf ("error duplicating filemap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; + } + if (check_and_dup_handle + (GetCurrentProcess (), from_process_handle, token_handle, + DUPLICATE_SAME_ACCESS, tempnode->attachmap, + ¶meters.out.attachmap, TRUE) != 0) + { + debug_printf ("error duplicating attachmap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; + } + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + + /* someone attached */ + /* someone can send shm_id's they don't have and currently we will increment those + * attach counts. If someone wants to fix that, please go ahead. + * The problem is that shm_get has nothing to do with the ability to attach. Attach + * requires a permission check, which we get the OS to do in MapViewOfFile. + */ + if (parameters.in.type == SHM_ATTACH) + { + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->shm_id == parameters.in.shm_id) + { + InterlockedIncrement (&tempnode->shmds->shm_nattch); + header.error_code = 0; + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + + /* it's a original request from the users */ + + /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide + * to prevent races on shmget. + */ + + if (parameters.in.key == IPC_PRIVATE) + { + /* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */ + /* The K refers to Key, the actual mapped area has D */ + long private_key = (int) InterlockedIncrement (&new_private_key); + snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key); + shmname = stringbuf; + snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key); + shmaname = stringbuf1; + } + else + { + /* create the mapping name (CYGWINSHMK0x0123456789abcdef */ + /* The K refers to Key, the actual mapped area has D */ + + snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key); + shmname = stringbuf; + snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key); + shmaname = stringbuf1; + debug_printf ("system id strings are \n%s\n%s\n", shmname, shmaname); + debug_printf ("key input value is 0x%0qx\n", parameters.in.key); + } + + /* attempt to open the key */ + + /* get an existing key */ + /* On unix the same shmid identifier is returned on multiple calls to shm_get + * with the same key and size. Different modes is a ?. + */ + + + + /* walk the list of known keys and return the id if found. remember, we are + * authoritative... + */ + + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->key == parameters.in.key + && parameters.in.key != IPC_PRIVATE) + { + // FIXME: free the mutex + if (parameters.in.size + && tempnode->shmds->shm_segsz < parameters.in.size) + { + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + /* FIXME: can the same process call this twice without error ? test + * on unix + */ + if ((parameters.in.shmflg & IPC_CREAT) + && (parameters.in.shmflg & IPC_EXCL)) + { + header.error_code = EEXIST; + debug_printf + ("attempt to exclusively create already created shm_area with key 0x%0qx\n", + parameters.in.key); + // FIXME: free the mutex + CloseHandle (token_handle); + return; + } + // FIXME: do we need to other tests of the requested mode with the + // tempnode->shm_id mode ? testcase on unix needed. + // FIXME how do we do the security test? or + // do we wait for shmat to bother with that? + /* One possibly solution: impersonate the client, and then test we can + * reopen the area. In fact we'll probably have to do that to get + * handles back to them, alternatively just tell them the id, and then + * let them attempt the open. + */ + parameters.out.shm_id = tempnode->shm_id; + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->filemap, + ¶meters.out.filemap, TRUE) != 0) + { + printf ("error duplicating filemap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; +/*mutex*/ + CloseHandle (token_handle); + return; + } + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->attachmap, + ¶meters.out.attachmap, TRUE) != 0) + { + printf ("error duplicating attachmap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; +/*mutex*/ + CloseHandle (token_handle); + return; + } + + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + /* couldn't find a currently open shm area. */ + + /* create one */ + /* do this as the client */ + conn->impersonate_client (); + /* This may need sh_none... it's only a control structure */ + HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile. + &sa, + PAGE_READWRITE, // protection + 0x00000000, + getsystemallocgranularity (), + shmname // object name + ); + int lasterr = GetLastError (); + conn->revert_to_self (); + + if (filemap == NULL) + { + /* We failed to open the filemapping ? */ + system_printf ("failed to open file mapping: %lu\n", GetLastError ()); + // free the mutex + // we can assume that it exists, and that it was an access problem. + header.error_code = EACCES; + CloseHandle (token_handle); + return; + } + + /* successfully opened the control region mapping */ + /* did we create it ? */ + int oldmapping = lasterr == ERROR_ALREADY_EXISTS; + if (oldmapping) + { + /* should never happen - we are the global daemon! */ +#if 0 + if ((parameters.in.shmflg & IPC_CREAT) + && (parameters.in.shmflg & IPC_EXCL)) +#endif + { + /* FIXME free mutex */ + CloseHandle (filemap); + header.error_code = EEXIST; + CloseHandle (token_handle); + return; + } + } + + /* we created a new mapping */ + if (parameters.in.key != IPC_PRIVATE && + (parameters.in.shmflg & IPC_CREAT) == 0) + { + CloseHandle (filemap); + /* FIXME free mutex */ + header.error_code = ENOENT; + CloseHandle (token_handle); + return; + } + + conn->impersonate_client (); + void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); + conn->revert_to_self (); + + if (!mapptr) + { + CloseHandle (filemap); + //FIXME: close filemap and free the mutex + /* we couldn't access the mapped area with the requested permissions */ + header.error_code = EACCES; + CloseHandle (token_handle); + return; + } + + conn->impersonate_client (); + /* Now get the user data */ + HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile + &sa, + PAGE_READWRITE, // protection (FIXME) + 0x00000000, + parameters.in.size + + parameters.in.size % + getsystemallocgranularity (), + shmaname // object name + ); + conn->revert_to_self (); + + if (attachmap == NULL) + { + system_printf ("failed to get shm attachmap\n"); + header.error_code = ENOMEM; + UnmapViewOfFile (mapptr); + CloseHandle (filemap); + /* FIXME exit the mutex */ + CloseHandle (token_handle); + return; + } + + shmid_ds *shmtemp = new shmid_ds; + if (!shmtemp) + { + system_printf ("failed to malloc shm node\n"); + header.error_code = ENOMEM; + UnmapViewOfFile (mapptr); + CloseHandle (filemap); + CloseHandle (attachmap); + /* FIXME exit mutex */ + CloseHandle (token_handle); + return; + } + + /* fill out the node data */ + shmtemp->shm_perm.cuid = getuid (); + shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid; + shmtemp->shm_perm.cgid = getgid (); + shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid; + shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff; + shmtemp->shm_lpid = 0; + shmtemp->shm_nattch = 0; + shmtemp->shm_atime = 0; + shmtemp->shm_dtime = 0; + shmtemp->shm_ctime = time (NULL); + shmtemp->shm_segsz = parameters.in.size; + *(shmid_ds *) mapptr = *shmtemp; + shmtemp->mapptr = mapptr; + + /* no need for InterlockedExchange here, we're serialised by the global mutex */ + tempnode = new shmnode; + tempnode->shmds = shmtemp; + tempnode->shm_id = (int) InterlockedIncrement (&new_id); + tempnode->key = parameters.in.key; + tempnode->filemap = filemap; + tempnode->attachmap = attachmap; + tempnode->next = shm_head; + shm_head = tempnode; + + /* we now have the area in the daemon list, opened. + + FIXME: leave the system wide shm mutex */ + + parameters.out.shm_id = tempnode->shm_id; + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->filemap, ¶meters.out.filemap, + TRUE) != 0) + { + printf ("error duplicating filemap handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (token_handle); +/* mutex et al */ + return; + } + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->attachmap, ¶meters.out.attachmap, + TRUE) != 0) + { + printf ("error duplicating attachmap handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (from_process_handle); + CloseHandle (token_handle); +/* more cleanup... yay! */ + return; + } + CloseHandle (token_handle); + return; +} diff --git a/winsup/cygserver/shm.h b/winsup/cygserver/shm.h new file mode 100755 index 0000000000..32947f8ecc --- /dev/null +++ b/winsup/cygserver/shm.h @@ -0,0 +1,91 @@ +/* cygserver_shm.h + + Copyright 2001 Red Hat Inc. + Written by Robert Collins + +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 +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver.h" + +#define SHM_CREATE 0 +#define SHM_REATTACH 1 +#define SHM_ATTACH 2 +#define SHM_DETACH 3 + + +class client_request_shm : public client_request +{ + public: +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, process_cache *cache); +#endif + client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t); + client_request_shm (); + client_request_shm (int, int, pid_t); + client_request_shm (int, int); + union { + struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in; + struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out; + } parameters; +}; + +#ifndef __INSIDE_CYGWIN__ +class shm_cleanup : cleanup_routine +{ +public: + virtual void cleanup (long winpid); +}; +#endif +#if 0 +class _shmattach { +public: + void *data; + class _shmattach *next; +}; + +class shmid_ds { +public: + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; + HANDLE filemap; + HANDLE attachmap; + void *mapptr; + class _shmattach *attachhead; +}; + +class shmnode { +public: + class shmid_ds * shmid; + class shmnode *next; + key_t key; +}; +//.... +struct shmid_ds { + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; +}; + +void *shmat(int, const void *, int); +int shmctl(int, int, struct shmid_ds *); +int shmdt(const void *); +int shmget(key_t, size_t, int); + +#endif diff --git a/winsup/cygserver/threaded_queue.cc b/winsup/cygserver/threaded_queue.cc new file mode 100644 index 0000000000..5fb22b1910 --- /dev/null +++ b/winsup/cygserver/threaded_queue.cc @@ -0,0 +1,249 @@ +/* threaded_queue.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include "wincap.h" +#include "threaded_queue.h" +#define DEBUG 1 +#define debug_printf if (DEBUG) printf + +/* threaded_queue */ + +DWORD WINAPI +worker_function (LPVOID LpParam) +{ + class threaded_queue *queue = (class threaded_queue *) LpParam; + class queue_request *request; + /* FIXME use a threadsafe pop instead for speed? */ + while (queue->active) + { + EnterCriticalSection (&queue->queuelock); + while (!queue->request && queue->active) + { + LeaveCriticalSection (&queue->queuelock); + DWORD rc = WaitForSingleObject (queue->event, INFINITE); + if (rc == WAIT_FAILED) + { + printf ("Wait for event failed\n"); + queue->running--; + ExitThread (0); + } + EnterCriticalSection (&queue->queuelock); + } + if (!queue->active) + { + queue->running--; + LeaveCriticalSection (&queue->queuelock); + ExitThread (0); + } + /* not needed, but it is efficient */ + request = + (class queue_request *) InterlockedExchangePointer (&queue->request, + queue->request-> + next); + LeaveCriticalSection (&queue->queuelock); + request->process (); + delete request; + } + queue->running--; + ExitThread (0); +} + +void +threaded_queue::create_workers () +{ + InitializeCriticalSection (&queuelock); + if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create event queue (%lu), terminating\n", + GetLastError ()); + exit (1); + } + active = true; + + /* FIXME: Use a stack pair and create threads on the fly whenever + * we have to to service a request. + */ + for (unsigned int i = 0; i < initial_workers; i++) + { + HANDLE hThread; + DWORD tid; + hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid); + if (hThread == NULL) + { + printf ("Failed to create thread (%lu), terminating\n", + GetLastError ()); + exit (1); + } + CloseHandle (hThread); + running++; + } +} + +void +threaded_queue::cleanup () +{ + /* harvest the threads */ + active = false; + /* kill the request processing loops */ + queue_process_param *reqloop; + /* make sure we don't race with a incoming request creation */ + EnterCriticalSection (&queuelock); + reqloop = + (queue_process_param *) InterlockedExchangePointer (&process_head, NULL); + while (reqloop) + { + queue_process_param *t = reqloop; + reqloop = reqloop->next; + delete t; + } + LeaveCriticalSection (&queuelock); + if (!running) + return; + printf ("Waiting for current queue threads to terminate\n"); + for (int n = running; n; n--) + PulseEvent (event); + while (running) + sleep (1); + DeleteCriticalSection (&queuelock); + CloseHandle (event); +} + +/* FIXME: return success or failure */ +void +threaded_queue::add (queue_request * therequest) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + EnterCriticalSection (&queuelock); + if (!running) + { + printf ("No worker threads to handle request!\n"); + } + if (!request) + request = therequest; + else + { + /* add to the queue end. */ + queue_request *listrequest = request; + while (listrequest->next) + listrequest = listrequest->next; + listrequest->next = therequest; + } + PulseEvent (event); + LeaveCriticalSection (&queuelock); +} + +/* FIXME: return success or failure rather than quitting */ +void +threaded_queue::process_requests (queue_process_param * params, + threaded_queue_thread_function * + request_loop) +{ + if (params->start (request_loop, this) == false) + exit (1); + params->next = + (queue_process_param *) InterlockedExchangePointer (&process_head, + params); +} + +/* queue_process_param */ +/* How does a constructor return an error? */ +queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false), +interruptible +(ninterruptible) +{ + if (!interruptible) + return; + debug_printf ("creating an interruptible processing thread\n"); + if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create interrupt event (%lu), terminating\n", + GetLastError ()); + exit (1); + } +} + +queue_process_param::~queue_process_param () +{ + if (running) + stop (); + if (!interruptible) + return; + CloseHandle (interrupt); +} + +bool + queue_process_param::start (threaded_queue_thread_function * request_loop, + threaded_queue * thequeue) +{ + queue = thequeue; + hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid); + if (hThread) + { + running = true; + return true; + } + printf ("Failed to create thread (%lu), terminating\n", GetLastError ()); + return false; +} + +void +queue_process_param::stop () +{ + if (interruptible) + { + InterlockedExchange (&shutdown, true); + PulseEvent (interrupt); + /* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get + * scheduled again, we print an error and exit. We _should_ loop or + * try resignalling. We don't want to hand here though... + */ + int n = 5; + while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT); + if (!n) + { + printf ("Process thread didn't shutdown cleanly after 200ms!\n"); + exit (1); + } + else + running = false; + } + else + { + printf ("killing request loop thread %ld\n", tid); + int rc; + if (!(rc = TerminateThread (hThread, 0))) + { + printf ("error shutting down request loop worker thread\n"); + } + running = false; + } + CloseHandle (hThread); +} + +/* queue_request */ +queue_request::queue_request ():next (NULL) +{ +} + +void +queue_request::process (void) +{ + printf ("\n**********************************************\n" + "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n" + "fault !!!\n" "***********************************************\n"); +} diff --git a/winsup/cygserver/transport.cc b/winsup/cygserver/transport.cc new file mode 100644 index 0000000000..7c6e5fe003 --- /dev/null +++ b/winsup/cygserver/transport.cc @@ -0,0 +1,92 @@ +/* cygserver_transport.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +#else +#define debug_printf printf +#endif + +/* The factory */ +class transport_layer_base *create_server_transport() +{ + transport_layer_base *temp; + /* currently there is only the base class! */ + if (wincap.is_winnt ()) + temp = new transport_layer_pipes (); + else + temp = new transport_layer_base (); + return temp; +} + + +transport_layer_base::transport_layer_base () +{ + /* should we throw an error of some sort ? */ +} + +void +transport_layer_base::listen () +{ +} + +class transport_layer_base * +transport_layer_base::accept () +{ + return NULL; +} + +void +transport_layer_base::close() +{ +} + +ssize_t +transport_layer_base::read (char *buf, size_t len) +{ + return 0; +} + +ssize_t +transport_layer_base::write (char *buf, size_t len) +{ + return 0; +} + +bool +transport_layer_base::connect () +{ + return false; +} + +void +transport_layer_base::impersonate_client () +{ +} + +void +transport_layer_base::revert_to_self () +{ +} diff --git a/winsup/cygserver/transport_pipes.cc b/winsup/cygserver/transport_pipes.cc new file mode 100644 index 0000000000..5b5a017c99 --- /dev/null +++ b/winsup/cygserver/transport_pipes.cc @@ -0,0 +1,205 @@ +/* cygserver_transport_pipes.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +#else +#define DEBUG 0 +#define debug_printf if (DEBUG) printf +#endif + +transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe) +{ + pipe = new_pipe; + if (inited != true) + init_security(); +}; + +transport_layer_pipes::transport_layer_pipes () +{ + pipe = NULL; + strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc"); + if (inited != true) + init_security(); +} + + +void +transport_layer_pipes::init_security() +{ + /* FIXME: pthread_once or equivalent needed */ + InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE); + + sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); + sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE; + sec_none_nih.lpSecurityDescriptor = NULL; + sec_all_nih.lpSecurityDescriptor = &sd; + inited = true; +} + +void +transport_layer_pipes::listen () +{ + /* no-op */ +} + +class transport_layer_pipes * +transport_layer_pipes::accept () +{ + if (pipe) + { + debug_printf ("Already have a pipe in this %p\n",this); + return NULL; + } + + pipe = CreateNamedPipe (pipe_name, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, 1000, + &sec_all_nih ); + if (pipe == INVALID_HANDLE_VALUE) + { + debug_printf ("error creating pipe (%lu)\n.", GetLastError ()); + return NULL; + } + + if ( !ConnectNamedPipe ( pipe, NULL ) && + GetLastError () != ERROR_PIPE_CONNECTED) + { + printf ("error connecting to pipe (%lu)\n.", GetLastError ()); + CloseHandle (pipe); + pipe = NULL; + return NULL; + } + + transport_layer_pipes *new_conn = new transport_layer_pipes (pipe); + pipe = NULL; + + return new_conn; +} + +void +transport_layer_pipes::close() +{ + debug_printf ("closing pipe %p\n", pipe); + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + FlushFileBuffers (pipe); + DisconnectNamedPipe (pipe); + CloseHandle (pipe); + } +} + +ssize_t +transport_layer_pipes::read (char *buf, size_t len) +{ + debug_printf ("reading from pipe %p\n", pipe); + if (!pipe || pipe == INVALID_HANDLE_VALUE) + return -1; + + DWORD bytes_read; + DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL); + if (!rc) + { + debug_printf ("error reading from pipe (%lu)\n", GetLastError ()); + return -1; + } + return bytes_read; +} + +ssize_t +transport_layer_pipes::write (char *buf, size_t len) +{ + debug_printf ("writing to pipe %p\n", pipe); + DWORD bytes_written, rc; + if (!pipe || pipe == INVALID_HANDLE_VALUE) + return -1; + + rc = WriteFile (pipe, buf, len, &bytes_written, NULL); + if (!rc) + { + debug_printf ("error writing to pipe (%lu)\n", GetLastError ()); + return -1; + } + return bytes_written; +} + +bool +transport_layer_pipes::connect () +{ + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + debug_printf ("Already have a pipe in this %p\n",this); + return false; + } + + while (1) + { + pipe = CreateFile (pipe_name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_all_nih, + OPEN_EXISTING, + 0, NULL); + + if (pipe != INVALID_HANDLE_VALUE) + /* got the pipe */ + return true; + + if (GetLastError () != ERROR_PIPE_BUSY) + { + debug_printf ("Error opening the pipe (%lu)\n", GetLastError ()); + pipe = NULL; + return false; + } + if (!WaitNamedPipe (pipe_name, 20000)) + debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () ); + /* We loop here, because the pipe exists but is busy. If it doesn't exist + * the != ERROR_PIPE_BUSY will catch it. + */ + } +} + +void +transport_layer_pipes::impersonate_client () +{ + debug_printf ("impersonating pipe %p\n", pipe); + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + BOOL rv = ImpersonateNamedPipeClient (pipe); + if (!rv) + debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ()); + } + debug_printf("I am who you are\n"); +} + +void +transport_layer_pipes::revert_to_self () +{ + RevertToSelf (); + debug_printf("I am who I yam\n"); +} + diff --git a/winsup/cygserver/transport_sockets.cc b/winsup/cygserver/transport_sockets.cc new file mode 100644 index 0000000000..6b75365b49 --- /dev/null +++ b/winsup/cygserver/transport_sockets.cc @@ -0,0 +1,131 @@ +/* cygserver_transport_sockets.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_sockets.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +extern "C" int +cygwin_socket (int af, int type, int protocol); +extern "C" int +cygwin_connect (int fd, + const struct sockaddr *name, + int namelen); +extern "C" int +cygwin_accept (int fd, struct sockaddr *peer, int *len); +extern "C" int +cygwin_listen (int fd, int backlog); +extern "C" int +cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen); + +#else +#define cygwin_accept(A,B,C) ::accept(A,B,C) +#define cygwin_socket(A,B,C) ::socket(A,B,C) +#define cygwin_listen(A,B) ::listen(A,B) +#define cygwin_bind(A,B,C) ::bind(A,B,C) +#define cygwin_connect(A,B,C) ::connect(A,B,C) +#define debug_printf printf +#endif + +transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd) +{ + /* This may not be needed in this constructor - it's only used + * when creating a connection via bind or connect + */ + sockdetails.sa_family = AF_UNIX; + strcpy (sockdetails.sa_data, "/tmp/cygdaemo"); + sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family); +}; + +transport_layer_sockets::transport_layer_sockets (): fd (-1) +{ + sockdetails.sa_family = AF_UNIX; + strcpy (sockdetails.sa_data, "/tmp/cygdaemo"); + sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family); +} + +void +transport_layer_sockets::listen () +{ + /* we want a thread pool based approach. */ + if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0) + printf ("Socket not created error %d\n", errno); + if (cygwin_bind(fd, &sockdetails, sdlen)) + printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno); + if (cygwin_listen(fd, 5) < 0) + printf ("And the OS just isn't listening, all it says is %d\n", errno); +} + +class transport_layer_sockets * +transport_layer_sockets::accept () +{ + /* FIXME: check we have listened */ + int new_fd; + + if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0) + { + printf ("Nup, could' accept. %d\n",errno); + return NULL; + } + + transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd); + + return new_conn; + +} + +void +transport_layer_sockets::close() +{ + /* FIXME - are we open? */ + ::close (fd); +} + +ssize_t +transport_layer_sockets::read (char *buf, size_t len) +{ + /* FIXME: are we open? */ + return ::read (fd, buf, len); +} + +ssize_t +transport_layer_sockets::write (char *buf, size_t len) +{ + /* FIXME: are we open? */ + return ::write (fd, buf, len); +} + +bool +transport_layer_sockets::connect () +{ + /* are we already connected? */ + if (fd != -1) + return false; + fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0); + if (cygwin_connect (fd, &sockdetails, sdlen) < 0) + { + debug_printf("client connect failure %d\n", errno); + ::close (fd); + return false; + } + return true; +} diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc new file mode 100755 index 0000000000..57f92c2ac8 --- /dev/null +++ b/winsup/cygwin/cygserver.cc @@ -0,0 +1,548 @@ +/* cygserver.cc + + Copyright 2001 Red Hat Inc. + + Written by Egor Duda + + 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 +#include +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin_version.h" + +#include "getopt.h" + +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" +#include "threaded_queue.h" +#include "cygwin/cygserver_process.h" +#include "cygwin/cygserver.h" +#include "cygserver_shm.h" + +/* for quieter operation, set to 0 */ +#define DEBUG 0 +#define debug_printf if (DEBUG) printf + +GENERIC_MAPPING access_mapping; +static class transport_layer_base *transport; + +DWORD request_count = 0; + +BOOL +setup_privileges () +{ + BOOL rc, ret_val; + HANDLE hToken = NULL; + TOKEN_PRIVILEGES sPrivileges; + + rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ; + if ( !rc ) + { + printf ( "error opening process token (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + rc = LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid ); + if ( !rc ) + { + printf ( "error getting prigilege luid (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + sPrivileges.PrivilegeCount = 1 ; + sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ; + rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ; + if ( !rc ) + { + printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () ); + ret_val = FALSE; + goto out; + } + + access_mapping.GenericRead = FILE_READ_DATA; + access_mapping.GenericWrite = FILE_WRITE_DATA; + access_mapping.GenericExecute = 0; + access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; + + ret_val = TRUE; + +out: + CloseHandle ( hToken ); + return ret_val; +} + +int +check_and_dup_handle (HANDLE from_process, HANDLE to_process, + HANDLE from_process_token, + DWORD access, + HANDLE from_handle, + HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE) +{ + HANDLE local_handle = NULL; + int ret_val = EACCES; + char sd_buf [1024]; + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; + DWORD bytes_needed; + PRIVILEGE_SET ps; + DWORD ps_len = sizeof (ps); + BOOL status; + + if (from_process != GetCurrentProcess ()) +{ + + if (!DuplicateHandle (from_process, from_handle, + GetCurrentProcess (), &local_handle, + 0, bInheritHandle, + DUPLICATE_SAME_ACCESS)) + { + printf ( "error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ()); + goto out; + } +} else + local_handle = from_handle; + + if (!GetKernelObjectSecurity (local_handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + sd, sizeof (sd_buf), &bytes_needed)) + { + printf ( "error getting handle SD (%lu)\n", GetLastError ()); + goto out; + } + + MapGenericMask (&access, &access_mapping); + + if (!AccessCheck (sd, from_process_token, access, &access_mapping, + &ps, &ps_len, &access, &status)) + { + printf ( "error checking access rights (%lu)\n", GetLastError ()); + goto out; + } + + if (!status) + { + printf ( "access to object denied\n"); + goto out; + } + + if (!DuplicateHandle (from_process, from_handle, + to_process, to_handle_ptr, + access, bInheritHandle, 0)) + { + printf ( "error getting handle to client (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr); + + ret_val = 0; + +out: + if (local_handle && from_process != GetCurrentProcess ()) + CloseHandle (local_handle); + + return (ret_val); +} + +void +client_request::serve (transport_layer_base *conn, class process_cache *cache) +{ + printf ("*****************************************\n" + "A call to the base client_request class has occured\n" + "This indicates a mismatch in a virtual function definition somewhere\n"); + exit (1); +} + +void +client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache) +{ + HANDLE from_process_handle = NULL; + HANDLE to_process_handle = NULL; + HANDLE token_handle = NULL; + DWORD rc; + + if (header.cb != sizeof (req)) + { + header.error_code = EINVAL; + return; + } + + debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid, + req.from_master, req.to_master, + req.pid); + + debug_printf ("opening process %ld\n", req.master_pid); + from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); + debug_printf ("opening process %ld\n", req.pid); + to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); + if (!from_process_handle || !to_process_handle) + { + printf ("error opening process (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + debug_printf ("Impersonating client\n"); + conn->impersonate_client (); + + debug_printf ("about to open thread token\n"); + rc = OpenThreadToken (GetCurrentThread (), + TOKEN_QUERY, + TRUE, + &token_handle); + + debug_printf ("opened thread token, rc=%lu\n", rc); + conn->revert_to_self (); + + if (!rc) + { + printf ("error opening thread token (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + if (check_and_dup_handle (from_process_handle, to_process_handle, + token_handle, + GENERIC_READ, + req.from_master, + &req.from_master, TRUE) != 0) + { + printf ("error duplicating from_master handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + + if (req.to_master) + { + if (check_and_dup_handle (from_process_handle, to_process_handle, + token_handle, + GENERIC_WRITE, + req.to_master, + &req.to_master, TRUE) != 0) + { + printf ("error duplicating to_master handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + goto out; + } + } + +#if DEBUG + printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid, + req.from_master, req.to_master); +#endif + + header.error_code = 0; + +out: + if (from_process_handle) + CloseHandle (from_process_handle); + if (to_process_handle) + CloseHandle (to_process_handle); + if (token_handle) + CloseHandle (token_handle); +} + +void +client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache) +{ + if (header.cb != sizeof (version)) + { + header.error_code = EINVAL; + return; + } + header.error_code = 0; + version.major = CYGWIN_SERVER_VERSION_MAJOR; + version.api = CYGWIN_SERVER_VERSION_API; + version.minor = CYGWIN_SERVER_VERSION_MINOR; + version.patch = CYGWIN_SERVER_VERSION_PATCH; +} + +class server_request : public queue_request +{ + public: + server_request (transport_layer_base *newconn, class process_cache *newcache); + virtual void process (); + private: + char request_buffer [MAX_REQUEST_SIZE]; + transport_layer_base *conn; + class process_cache *cache; +}; + +class server_process_param : public queue_process_param +{ + public: + transport_layer_base *transport; + server_process_param () : queue_process_param (false) {}; +}; + +class server_request_queue : public threaded_queue +{ + public: + class process_cache *cache; + void process_requests (transport_layer_base *transport); + virtual void add (transport_layer_base *conn); +}; +class server_request_queue request_queue; + +static DWORD WINAPI +request_loop (LPVOID LpParam) +{ + class server_process_param *params = (server_process_param *) LpParam; + class server_request_queue *queue = (server_request_queue *) params->queue; + class transport_layer_base * transport = params->transport; + while (queue->active) + { + transport_layer_base * new_conn = transport->accept (); + /* FIXME: this is a little ugly. What we really want is to wait on two objects: + * one for the pipe/socket, and one for being told to shutdown. Otherwise + * this will stay a problem (we won't actually shutdown until the request + * _AFTER_ the shutdown request. And sending ourselves a request is ugly + */ + if (new_conn && queue->active) + queue->add (new_conn); + } + return 0; +} + +/* TODO: check we are not being asked to service a already serviced transport */ +void +server_request_queue::process_requests (transport_layer_base *transport) +{ + class server_process_param *params = new server_process_param; + params->transport = transport; + threaded_queue::process_requests (params, request_loop); +} + +void +client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache) +{ + /* FIXME: link upwards, and then this becomes a trivial method call to + * only shutdown _this queue_ + */ + /* tell the main thread to shutdown */ + request_queue.active=false; +} + +server_request::server_request (transport_layer_base *newconn, class process_cache *newcache) +{ + conn = newconn; + cache = newcache; +} + +void +server_request::process () +{ + ssize_t bytes_read, bytes_written; + struct request_header* req_ptr = (struct request_header*) &request_buffer; + client_request *req = NULL; + debug_printf ("about to read\n"); + + bytes_read = conn->read (request_buffer, sizeof (struct request_header)); + if (bytes_read != sizeof (struct request_header)) + { + printf ("error reading from connection (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("got header (%ld)\n", bytes_read); + + switch (req_ptr->req_id) + { + case CYGSERVER_REQUEST_GET_VERSION: + req = new client_request_get_version (); break; + case CYGSERVER_REQUEST_ATTACH_TTY: + req = new client_request_attach_tty (); break; + case CYGSERVER_REQUEST_SHUTDOWN: + req = new client_request_shutdown (); break; + case CYGSERVER_REQUEST_SHM_GET: + req = new client_request_shm (); break; + default: + req = new client_request (CYGSERVER_REQUEST_INVALID, 0); + req->header.error_code = ENOSYS; + debug_printf ("Bad client request - returning ENOSYS\n"); + } + + if (req->header.cb != req_ptr->cb) + { + debug_printf ("Mismatch in request buffer sizes\n"); + goto out; + } + + if (req->header.cb) + { + + bytes_read = conn->read (req->buffer, req->header.cb); + if (bytes_read != req->header.cb) + { + debug_printf ("error reading from connection (%lu)\n", GetLastError ()); + goto out; + } + debug_printf ("got body (%ld)\n",bytes_read); + } + + /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */ + req->serve (conn, cache); + + if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header))) + != sizeof(req->header) || (req->header.cb && + (bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb)) + { + req->header.error_code = -1; + printf ("error writing to connection (%lu)\n", GetLastError ()); + goto out; + } + + debug_printf("Sent reply, size (%ld)\n",bytes_written); + printf ("."); + +out: + conn->close (); + delete conn; + if (req) + delete (req); +} + +void +server_request_queue::add (transport_layer_base *conn) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + /* every derived ::add must enter the section! */ + EnterCriticalSection (&queuelock); + if (!running) + { + conn->close (); + delete conn; + LeaveCriticalSection (&queuelock); + return; + } + queue_request * listrequest = new server_request (conn, cache); + threaded_queue::add (listrequest); + LeaveCriticalSection (&queuelock); +} + +void +handle_signal (int signal) +{ + /* any signal makes us die :} */ + /* FIXME: link upwards, and then this becomes a trivial method call to + * only shutdown _this queue_ + */ + /* tell the main thread to shutdown */ + request_queue.active=false; +} + +struct option longopts[] = { + {"shutdown", no_argument, NULL, 's'}, + {0, no_argument, NULL, 0} +}; + +char opts[] = "s"; + +int +main (int argc, char **argv) +{ + int shutdown=0; + char i; + + while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) + switch (i) + { + case 's': + shutdown = 1; + break; + default: + break; + /*NOTREACHED*/ + } + + wincap.init(); + if (wincap.has_security ()) + setup_privileges (); + transport = create_server_transport (); + + if (shutdown) + { + if (!transport->connect()) + { + printf ("couldn't establish connection with server\n"); + exit (1); + } + client_request_shutdown *request = + new client_request_shutdown (); + request->send (transport); + transport->close(); + delete transport; + delete request; + exit(0); + } + + char version[200]; + /* Cygwin dll release */ + snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s", + cygwin_version.dll_major / 1000, + cygwin_version.dll_major % 1000, + cygwin_version.dll_minor, + cygwin_version.api_major, + cygwin_version.api_minor, + cygwin_version.shared_data, + CYGWIN_SERVER_VERSION_MAJOR, + CYGWIN_SERVER_VERSION_API, + CYGWIN_SERVER_VERSION_MINOR, + CYGWIN_SERVER_VERSION_PATCH, + cygwin_version.mount_registry, + cygwin_version.dll_build_date); + setbuf (stdout, NULL); + printf ("daemon version %s starting up", version); + if (signal (SIGQUIT, handle_signal) == SIG_ERR) + { + printf ("\ncould not install signal handler (%d)- aborting startup\n", errno); + exit (1); + } + printf ("."); + transport->listen (); + printf ("."); + class process_cache cache (2); + request_queue.initial_workers = 10; + request_queue.cache = &cache; + request_queue.create_workers (); + printf ("."); + request_queue.process_requests (transport); + printf ("."); + cache.create_workers (); + printf ("."); + cache.process_requests (); + printf (".complete\n"); + /* TODO: wait on multiple objects - the thread handle for each request loop + + * all the process handles. This should be done by querying the request_queue and + * the process cache for all their handles, and then waiting for (say) 30 seconds. + * after that we recreate the list of handles to wait on, and wait again. + * the point of all this abstraction is that we can trivially server both sockets + * and pipes simply by making a new transport, and then calling + * request_queue.process_requests (transport2); + */ + /* WaitForMultipleObjects abort && request_queue && process_queue && signal + -- if signal event then retrigger it + */ + while (1 && request_queue.active) + { + sleep (1); + } + printf ("\nShutdown request recieved - new requests will be denied\n"); + request_queue.cleanup (); + printf ("All pending requests processed\n"); + transport->close (); + printf ("No longer accepting requests - cygwin will operate in daemonless mode\n"); + cache.cleanup (); + printf ("All outstanding process-cache activities completed\n"); + printf ("daemon shutdown\n"); +} diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc new file mode 100755 index 0000000000..8a1eeafef0 --- /dev/null +++ b/winsup/cygwin/cygserver_client.cc @@ -0,0 +1,207 @@ +/* cygserver_client.cc + + Copyright 2001 Red Hat Inc. + + Written by Egor Duda + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifdef __OUTSIDE_CYGWIN__ +#undef __INSIDE_CYGWIN__ +#else +#include "winsup.h" +#endif + +#ifndef __INSIDE_CYGWIN__ +#define debug_printf printf +#define api_fatal printf +#include +#include +#endif +#include +#include +#include +//#include "security.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" +#include "cygwin/cygserver.h" + +/* 0 = untested, 1 = running, 2 = dead */ +int cygserver_running=CYGSERVER_UNKNOWN; +/* on by default during development. For release, we probably want off by default */ +int allow_daemon = TRUE; + +client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version)) +{ + buffer = (char *)&version; +} + +client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +{ + buffer = (char *)&req; + req.pid = 0; + req.master_pid = 0; + req.from_master = NULL; + req.to_master = NULL; +} + +client_request_attach_tty::client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +{ + buffer = (char *)&req; + req.pid = npid; + req.master_pid = nmaster_pid; + req.from_master = nfrom_master; + req.to_master = nto_master; +} + +client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0) +{ + buffer = NULL; +} + +client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size) +{ +} + +client_request::~client_request () +{ +} + +client_request::operator class request_header () +{ + return header; +} + +void +client_request::send (transport_layer_base *conn) +{ + if (!conn) + return; + debug_printf("this=%p, conn=%p\n",this, conn); + ssize_t bytes_written, bytes_read; + debug_printf("header.cb = %ld\n",header.cb); + if ((bytes_written = conn->write ((char *)&header, sizeof (header))) + != sizeof(header) || (header.cb && + (bytes_written = conn->write (buffer, header.cb)) != header.cb)) + { + header.error_code = -1; + debug_printf ("bytes written != request size\n"); + return; + } + + debug_printf("Sent request, size (%ld)\n",bytes_written); + + if ((bytes_read = conn->read ((char *)&header, sizeof (header))) + != sizeof (header) || (header.cb && + (bytes_read = conn->read (buffer, header.cb) ) != header.cb)) + { + header.error_code = -1; + debug_printf("failed reading response \n"); + return; + } + debug_printf ("completed ok\n"); +} + +/* Oh, BTW: Fix the procedural basis and make this more intuitive. */ + +int +cygserver_request (client_request * req) +{ + class transport_layer_base *transport; + + if (!req || allow_daemon != TRUE) + return -1; + + /* dont' retry every request if the server's not there */ + if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION) + return -1; + + transport = create_server_transport (); + + /* FIXME: have at most one connection per thread. use TLS to store the details */ + /* logic is: + * if not tlskey->conn, new conn, + * then; transport=conn; + */ + if (!transport->connect ()) + { + delete transport; + return -1; + } + + debug_printf ("connected to server %p\n", transport); + + req->send(transport); + + transport->close (); + + delete transport; + + return 0; +} + +#if 0 +BOOL +check_cygserver_available () +{ + BOOL ret_val = FALSE; + HANDLE pipe = CreateFile (pipe_name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_all_nih, + OPEN_EXISTING, + 0, + NULL); + if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY) + ret_val = TRUE; + + if (pipe && pipe != INVALID_HANDLE_VALUE) + CloseHandle (pipe); + + return (ret_val); +} +#endif + +void +cygserver_init () +{ + int rc; + if (allow_daemon != TRUE) + { + cygserver_running = CYGSERVER_DEAD; + return; + } + + if (cygserver_running==CYGSERVER_OK) + return; + + client_request_get_version *req = + new client_request_get_version (); + + rc = cygserver_request (req); + delete req; + if (rc < 0) + cygserver_running = CYGSERVER_DEAD; + else if (rc > 0) + api_fatal ( "error connecting to cygwin server. error: %d", rc ); + else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR || + req->version.api != CYGWIN_SERVER_VERSION_API || + req->version.minor > CYGWIN_SERVER_VERSION_MINOR) + api_fatal ( "incompatible version of cygwin server.\n\ + client version %d.%d.%d.%d, server version%ld.%ld.%ld.%ld", + CYGWIN_SERVER_VERSION_MAJOR, + CYGWIN_SERVER_VERSION_API, + CYGWIN_SERVER_VERSION_MINOR, + CYGWIN_SERVER_VERSION_PATCH, + req->version.major, + req->version.api, + req->version.minor, + req->version.patch ); + else + cygserver_running = CYGSERVER_OK; +} diff --git a/winsup/cygwin/cygserver_process.cc b/winsup/cygwin/cygserver_process.cc new file mode 100755 index 0000000000..a5173afd35 --- /dev/null +++ b/winsup/cygwin/cygserver_process.cc @@ -0,0 +1,389 @@ +/* cygserver_process.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include +#include +#include + +#define debug_printf if (DEBUG) printf +#define DEBUG 1 + +/* the cache structures and classes are designed for one cache per server process. + * To make multiple process caches, a redesign will be needed + */ + +/* process cache */ +process_cache::process_cache (unsigned int num_initial_workers): +head (NULL) +{ + /* there can only be one */ + InitializeCriticalSection (&cache_write_access); + if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create cache add trigger (%lu), terminating\n", + GetLastError ()); + exit (1); + } + initial_workers = num_initial_workers; +} + +process_cache::~process_cache () +{ +} + +class process * +process_cache::process (long pid) +{ + class process *entry = head; + /* TODO: make this more granular, so a search doesn't involve the write lock */ + EnterCriticalSection (&cache_write_access); + if (!entry) + { + entry = new class process (pid); + entry->next = + (class process *) InterlockedExchangePointer (&head, entry); + PulseEvent (cache_add_trigger); + } + else + { + while (entry->winpid != pid && entry->next) + entry = entry->next; + if (entry->winpid != pid) + { + class process *new_entry = new class process (pid); + new_entry->next = + (class process *) InterlockedExchangePointer (&entry->next, + new_entry); + entry = new_entry; + PulseEvent (cache_add_trigger); + } + } + LeaveCriticalSection (&cache_write_access); + return entry; +} + +static DWORD WINAPI +request_loop (LPVOID LpParam) +{ + class process_process_param *params = (process_process_param *) LpParam; + return params->request_loop (); +} + +void +process_cache::process_requests () +{ + class process_process_param *params = new process_process_param; + threaded_queue::process_requests (params, request_loop); +} + +void +process_cache::add_task (class process * theprocess) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + /* every derived ::add must enter the section! */ + EnterCriticalSection (&queuelock); + queue_request *listrequest = new process_cleanup (theprocess); + threaded_queue::add (listrequest); + LeaveCriticalSection (&queuelock); +} + +/* NOT fully MT SAFE: must be called by only one thread in a program */ +void +process_cache::remove_process (class process *theprocess) +{ + class process *entry = head; + /* unlink */ + EnterCriticalSection (&cache_write_access); + if (entry == theprocess) + { + entry = (class process *) InterlockedExchangePointer (&head, theprocess->next); + if (entry != theprocess) + { + printf ("Bug encountered, process cache corrupted\n"); + exit (1); + } + } + else + { + while (entry->next && entry->next != theprocess) + entry = entry->next; + class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next); + if (temp != theprocess) + { + printf ("Bug encountered, process cache corrupted\n"); + exit (1); + } + } + LeaveCriticalSection (&cache_write_access); + /* Process any cleanup tasks */ + add_task (theprocess); +} + + +/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of + * begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0] + * NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed. + * Who cares - It'll get caught the next time. + */ +int +process_cache::handle_snapshot (HANDLE * hdest, class process ** edest, + ssize_t max_copy, int begin_at) +{ + /* TODO:? grab a delete-lock, to prevent deletes during this process ? */ + class process *entry = head; + int count = begin_at; + /* skip begin_at entries */ + while (entry && count) + { + if (entry->exit_code () == STILL_ACTIVE) + count--; + entry = entry->next; + } + /* hit the end of the list within begin_at entries */ + if (count) + return 0; + HANDLE *hto = hdest; + class process **eto = edest; + while (entry && count < max_copy) + { + /* hack */ + if (entry->exit_code () == STILL_ACTIVE) + { + *hto = entry->handle (); + *eto = entry; + count++; + hto++; + eto++; + } + entry = entry->next; + } + return count; +} + +/* process's */ +/* global process crit section */ +static CRITICAL_SECTION process_access; +static pthread_once_t process_init; + +void +do_process_init (void) +{ + InitializeCriticalSection (&process_access); + /* we don't have a cache shutdown capability today */ +} + +process::process (long pid): +winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE) +{ + pthread_once (&process_init, do_process_init); + EnterCriticalSection (&process_access); + thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + if (!thehandle) + { + printf ("unable to obtain handle for new cache process %ld\n", pid); + thehandle = INVALID_HANDLE_VALUE; + } + debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid); + InitializeCriticalSection (&access); + LeaveCriticalSection (&process_access); +} + +process::~process () +{ + DeleteCriticalSection (&access); +} + +HANDLE +process::handle () +{ +// DWORD exitstate = exit_code (); +// if (exitstate == STILL_ACTIVE) + return thehandle; + + /* FIXME: call the cleanup list ? */ + +// CloseHandle (thehandle); +// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n", +// winpid); +// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid); +// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid); +// /* FIXME: what if OpenProcess fails ? */ +// if (thehandle) +// { +// _exit_status = STILL_ACTIVE; +// exit_code (); +// } +// else +// thehandle = INVALID_HANDLE_VALUE; +// return thehandle; +} + +DWORD process::exit_code () +{ + if (_exit_status != STILL_ACTIVE) + return _exit_status; + bool + err = GetExitCodeProcess (thehandle, &_exit_status); + if (!err) + { + debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ()); + thehandle = INVALID_HANDLE_VALUE; + return _exit_status; + } + else if (_exit_status == STILL_ACTIVE) + return _exit_status; + /* add new cleanup task etc etc ? */ + return _exit_status; +} + +/* this is single threaded. It's called after the process is removed from the cache, + * but inserts may be attemped by worker threads that have a pointer to it */ +void +process::cleanup () +{ + /* Serialize this */ + EnterCriticalSection (&access); + InterlockedIncrement (&(long)cleaning_up); + class cleanup_routine *entry = head; + while (entry) + { + class cleanup_routine *temp; + entry->cleanup (winpid); + temp = entry->next; + delete entry; + entry = temp; + } + LeaveCriticalSection (&access); +} + +bool +process::add_cleanup_routine (class cleanup_routine *new_cleanup) +{ + if (cleaning_up) + return false; + EnterCriticalSection (&access); + /* check that we didn't block with ::cleanup () + * This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call + * which would be a whole lot easier + */ + if (cleaning_up) + { + LeaveCriticalSection (&access); + return false; + } + new_cleanup->next = head; + head = new_cleanup; + LeaveCriticalSection (&access); + return true; +} + +/* process_cleanup */ +void +process_cleanup::process () +{ + theprocess->cleanup (); + delete theprocess; +} + +/* process_process_param */ +DWORD +process_process_param::request_loop () +{ + process_cache *cache = (process_cache *) queue; + /* always malloc one, so there is no special case in the loop */ + ssize_t HandlesSize = 2; + HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize); + process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize); + /* TODO: put [1] at the end as it will also get done if a process dies? */ + Handles[0] = interrupt; + Handles[1] = cache->cache_add_trigger; + while (cache->active && !shutdown) + { + int copied; + copied = -1; + int offset; + offset = 1; + int count; + count = 2; + while ((copied == HandlesSize - 2 - offset) || copied < 0) + { + /* we need more storage to cope with all the HANDLES */ + if (copied == HandlesSize - 2 - offset) + { + HANDLE *temp = (HANDLE *) realloc (Handles, + sizeof (HANDLE) * + HandlesSize + 10); + if (!temp) + { + printf + ("cannot allocate more storage for the handle array!\n"); + exit (1); + } + Handles = temp; + process **ptemp = (process **) realloc (Entries, + sizeof (LPVOID) * + HandlesSize + 10); + if (!ptemp) + { + printf + ("cannot allocate more storage for the handle array!\n"); + exit (1); + } + Entries = ptemp; + HandlesSize += 10; + } + offset += copied; + copied = + cache->handle_snapshot (&Handles[2], &Entries[2], + HandlesSize - 2 - offset, offset); + count += copied; + } + debug_printf ("waiting on %u objects\n", count); + DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE); + if (rc == WAIT_FAILED) + { + printf ("Could not wait on the process handles (%ld)!\n", + GetLastError ()); + exit (1); + } + int objindex = rc - WAIT_OBJECT_0; + if (objindex > 1 && objindex < count) + { + debug_printf ("Process %ld has left the building\n", + Entries[objindex]->winpid); + /* fire off the termination routines */ + cache->remove_process (Entries[objindex]); + } + else if (objindex >= 0 && objindex < 2) + { + /* 0 is shutdown - do nothing */ + /* 1 is a cache add event - just rebuild the object list */ + } + else + { + printf + ("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n"); + } + } + running = false; + return 0; +} diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc new file mode 100755 index 0000000000..c29938bb97 --- /dev/null +++ b/winsup/cygwin/cygserver_shm.cc @@ -0,0 +1,552 @@ +/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin + + Copyright 2001 Red Hat, Inc. + + Originally written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + + +#ifdef __OUTSIDE_CYGWIN__ +#undef __INSIDE_CYGWIN__ +#else +#include "winsup.h" +#endif + +#ifndef __INSIDE_CYGWIN__ +#define DEBUG 0 +#define system_printf printf +#define debug_printf if (DEBUG) printf +#define api_fatal printf +#include +#include +#endif + +#include +#include +#include "cygerrno.h" +#include +#include "security.h" +//#include "fhandler.h" +//#include "dtable.h" +//#include "cygheap.h" +#include +//#include "thread.h" +#ifndef __INSIDE_CYGWIN__ +#define __INSIDE_CYGWIN__ +#include +#undef __INSIDE_CYGWIN__ +#else +#include +#endif +//#include "perprocess.h" +#include +#include +#include "cygserver_shm.h" + +// FIXME IS THIS CORRECT +/* Implementation notes: We use two shared memory regions per key: + * One for the control structure, and one for the shared memory. + * While this has a higher overhead tham a single shared area, + * It allows more flexability. As the entire code is transparent to the user + * We can merge these in the future should it be needed. + * Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just + * keep monotonically incrementing - system wide. + */ +size_t +getsystemallocgranularity () +{ + SYSTEM_INFO sysinfo; + static size_t buffer_offset = 0; + if (buffer_offset) + return buffer_offset; + GetSystemInfo (&sysinfo); + buffer_offset = sysinfo.dwAllocationGranularity; + return buffer_offset; +} + + +client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) +{ + buffer = (char *) ¶meters; +} + +/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail. + * Solution: manually calculate the next id value + */ + +#if 0 +extern "C" void * +shmat (int shmid, const void *shmaddr, int parameters.in.shmflg) +{ + class shmid_ds *shm = (class shmid_ds *) shmid; //FIXME: verifyable object test + + if (shmaddr) + { + //FIXME: requested base address ?! + set_errno (EINVAL); + return (void *) -1; + } + + void *rv = MapViewOfFile (shm->attachmap, + + + (parameters.in.shmflg & SHM_RDONLY) ? + FILE_MAP_READ : FILE_MAP_WRITE, 0, + 0, 0); + + if (!rv) + { + //FIXME: translate GetLastError() + set_errno (EACCES); + return (void *) -1; + } + +/* FIXME: this needs to be globally protected to prevent a mismatch betwen + * attach count and attachees list + */ + + InterlockedIncrement (&shm->shm_nattch); + _shmattach *attachnode = new _shmattach; + + attachnode->data = rv; + attachnode->next = + (_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead, + (long int) attachnode); + return rv; +} +#endif + +/* FIXME: evaluate getuid() and getgid() against the requested mode. Then + * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ + * appropriately + */ + +/* Test result from openbsd: shm ids are persistent cross process if a handle is left + * open. This could lead to resource starvation: we're not copying that behaviour + * unless we have to. (It will involve acygwin1.dll gloal shared list :[ ). + */ +/* FIXME: shmid should be a verifyable object + */ + +/* FIXME: on NT we should check everything against the SD. On 95 we just emulate. + */ + +extern GENERIC_MAPPING access_mapping; + +extern int +check_and_dup_handle (HANDLE from_process, HANDLE to_process, + HANDLE from_process_token, + DWORD access, + HANDLE from_handle, + HANDLE * to_handle_ptr, BOOL bInheritHandle); + +//FIXME: where should this live +static shmnode *shm_head = NULL; +/* must be long for InterlockedIncrement */ +static long new_id = 0; +static long new_private_key = 0; + +void +client_request_shm::serve (transport_layer_base * conn, + process_cache * cache) +{ +// DWORD sd_size = 4096; +// char sd_buf[4096]; + PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf; +// /* create a sd for our open requests based on shmflag & 0x01ff */ +// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (), +// parameters.in.shmflg & 0x01ff, psd, &sd_size); + + HANDLE from_process_handle = NULL; + HANDLE token_handle = NULL; + DWORD rc; + + from_process_handle = cache->process (parameters.in.pid)->handle (); + /* possible TODO: reduce the access on the handle before we use it */ + /* Note that unless we do this, we don't need to call CloseHandle - it's kept open + * by the process cache until the process terminates. + * We may need a refcount on the cache however... + */ + if (!from_process_handle) + { + debug_printf ("error opening process (%lu)\n", GetLastError ()); + header.error_code = EACCES; + return; + } + + conn->impersonate_client (); + + rc = OpenThreadToken (GetCurrentThread (), + TOKEN_QUERY, TRUE, &token_handle); + + conn->revert_to_self (); + + if (!rc) + { + debug_printf ("error opening thread token (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (from_process_handle); + return; + } + + + /* we trust the clients request - we will be doing it as them, and + * the worst they can do is open their own permissions + */ + + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof (sa); + sa.lpSecurityDescriptor = psd; + sa.bInheritHandle = TRUE; /* the memory structures inherit ok */ + + char *shmname = NULL, *shmaname = NULL; + char stringbuf[29], stringbuf1[29]; + + /* TODO: make this code block a function! */ + if (parameters.in.type == SHM_REATTACH) + { + /* just find and fill out the existing shm_id */ + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->shm_id == parameters.in.shm_id) + { + parameters.out.shm_id = tempnode->shm_id; + parameters.out.key = tempnode->key; + if (check_and_dup_handle + (GetCurrentProcess (), from_process_handle, token_handle, + DUPLICATE_SAME_ACCESS, tempnode->filemap, + ¶meters.out.filemap, TRUE) != 0) + { + debug_printf ("error duplicating filemap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; + } + if (check_and_dup_handle + (GetCurrentProcess (), from_process_handle, token_handle, + DUPLICATE_SAME_ACCESS, tempnode->attachmap, + ¶meters.out.attachmap, TRUE) != 0) + { + debug_printf ("error duplicating attachmap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; + } + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + + /* someone attached */ + /* someone can send shm_id's they don't have and currently we will increment those + * attach counts. If someone wants to fix that, please go ahead. + * The problem is that shm_get has nothing to do with the ability to attach. Attach + * requires a permission check, which we get the OS to do in MapViewOfFile. + */ + if (parameters.in.type == SHM_ATTACH) + { + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->shm_id == parameters.in.shm_id) + { + InterlockedIncrement (&tempnode->shmds->shm_nattch); + header.error_code = 0; + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + + /* it's a original request from the users */ + + /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide + * to prevent races on shmget. + */ + + if (parameters.in.key == IPC_PRIVATE) + { + /* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */ + /* The K refers to Key, the actual mapped area has D */ + long private_key = (int) InterlockedIncrement (&new_private_key); + snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key); + shmname = stringbuf; + snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key); + shmaname = stringbuf1; + } + else + { + /* create the mapping name (CYGWINSHMK0x0123456789abcdef */ + /* The K refers to Key, the actual mapped area has D */ + + snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key); + shmname = stringbuf; + snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key); + shmaname = stringbuf1; + debug_printf ("system id strings are \n%s\n%s\n", shmname, shmaname); + debug_printf ("key input value is 0x%0qx\n", parameters.in.key); + } + + /* attempt to open the key */ + + /* get an existing key */ + /* On unix the same shmid identifier is returned on multiple calls to shm_get + * with the same key and size. Different modes is a ?. + */ + + + + /* walk the list of known keys and return the id if found. remember, we are + * authoritative... + */ + + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->key == parameters.in.key + && parameters.in.key != IPC_PRIVATE) + { + // FIXME: free the mutex + if (parameters.in.size + && tempnode->shmds->shm_segsz < parameters.in.size) + { + header.error_code = EINVAL; + CloseHandle (token_handle); + return; + } + /* FIXME: can the same process call this twice without error ? test + * on unix + */ + if ((parameters.in.shmflg & IPC_CREAT) + && (parameters.in.shmflg & IPC_EXCL)) + { + header.error_code = EEXIST; + debug_printf + ("attempt to exclusively create already created shm_area with key 0x%0qx\n", + parameters.in.key); + // FIXME: free the mutex + CloseHandle (token_handle); + return; + } + // FIXME: do we need to other tests of the requested mode with the + // tempnode->shm_id mode ? testcase on unix needed. + // FIXME how do we do the security test? or + // do we wait for shmat to bother with that? + /* One possibly solution: impersonate the client, and then test we can + * reopen the area. In fact we'll probably have to do that to get + * handles back to them, alternatively just tell them the id, and then + * let them attempt the open. + */ + parameters.out.shm_id = tempnode->shm_id; + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->filemap, + ¶meters.out.filemap, TRUE) != 0) + { + printf ("error duplicating filemap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; +/*mutex*/ + CloseHandle (token_handle); + return; + } + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->attachmap, + ¶meters.out.attachmap, TRUE) != 0) + { + printf ("error duplicating attachmap handle (%lu)\n", + GetLastError ()); + header.error_code = EACCES; +/*mutex*/ + CloseHandle (token_handle); + return; + } + + CloseHandle (token_handle); + return; + } + tempnode = tempnode->next; + } + /* couldn't find a currently open shm area. */ + + /* create one */ + /* do this as the client */ + conn->impersonate_client (); + /* This may need sh_none... it's only a control structure */ + HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile. + &sa, + PAGE_READWRITE, // protection + 0x00000000, + getsystemallocgranularity (), + shmname // object name + ); + int lasterr = GetLastError (); + conn->revert_to_self (); + + if (filemap == NULL) + { + /* We failed to open the filemapping ? */ + system_printf ("failed to open file mapping: %lu\n", GetLastError ()); + // free the mutex + // we can assume that it exists, and that it was an access problem. + header.error_code = EACCES; + CloseHandle (token_handle); + return; + } + + /* successfully opened the control region mapping */ + /* did we create it ? */ + int oldmapping = lasterr == ERROR_ALREADY_EXISTS; + if (oldmapping) + { + /* should never happen - we are the global daemon! */ +#if 0 + if ((parameters.in.shmflg & IPC_CREAT) + && (parameters.in.shmflg & IPC_EXCL)) +#endif + { + /* FIXME free mutex */ + CloseHandle (filemap); + header.error_code = EEXIST; + CloseHandle (token_handle); + return; + } + } + + /* we created a new mapping */ + if (parameters.in.key != IPC_PRIVATE && + (parameters.in.shmflg & IPC_CREAT) == 0) + { + CloseHandle (filemap); + /* FIXME free mutex */ + header.error_code = ENOENT; + CloseHandle (token_handle); + return; + } + + conn->impersonate_client (); + void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); + conn->revert_to_self (); + + if (!mapptr) + { + CloseHandle (filemap); + //FIXME: close filemap and free the mutex + /* we couldn't access the mapped area with the requested permissions */ + header.error_code = EACCES; + CloseHandle (token_handle); + return; + } + + conn->impersonate_client (); + /* Now get the user data */ + HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile + &sa, + PAGE_READWRITE, // protection (FIXME) + 0x00000000, + parameters.in.size + + parameters.in.size % + getsystemallocgranularity (), + shmaname // object name + ); + conn->revert_to_self (); + + if (attachmap == NULL) + { + system_printf ("failed to get shm attachmap\n"); + header.error_code = ENOMEM; + UnmapViewOfFile (mapptr); + CloseHandle (filemap); + /* FIXME exit the mutex */ + CloseHandle (token_handle); + return; + } + + shmid_ds *shmtemp = new shmid_ds; + if (!shmtemp) + { + system_printf ("failed to malloc shm node\n"); + header.error_code = ENOMEM; + UnmapViewOfFile (mapptr); + CloseHandle (filemap); + CloseHandle (attachmap); + /* FIXME exit mutex */ + CloseHandle (token_handle); + return; + } + + /* fill out the node data */ + shmtemp->shm_perm.cuid = getuid (); + shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid; + shmtemp->shm_perm.cgid = getgid (); + shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid; + shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff; + shmtemp->shm_lpid = 0; + shmtemp->shm_nattch = 0; + shmtemp->shm_atime = 0; + shmtemp->shm_dtime = 0; + shmtemp->shm_ctime = time (NULL); + shmtemp->shm_segsz = parameters.in.size; + *(shmid_ds *) mapptr = *shmtemp; + shmtemp->mapptr = mapptr; + + /* no need for InterlockedExchange here, we're serialised by the global mutex */ + tempnode = new shmnode; + tempnode->shmds = shmtemp; + tempnode->shm_id = (int) InterlockedIncrement (&new_id); + tempnode->key = parameters.in.key; + tempnode->filemap = filemap; + tempnode->attachmap = attachmap; + tempnode->next = shm_head; + shm_head = tempnode; + + /* we now have the area in the daemon list, opened. + + FIXME: leave the system wide shm mutex */ + + parameters.out.shm_id = tempnode->shm_id; + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->filemap, ¶meters.out.filemap, + TRUE) != 0) + { + printf ("error duplicating filemap handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (token_handle); +/* mutex et al */ + return; + } + if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, + token_handle, + DUPLICATE_SAME_ACCESS, + tempnode->attachmap, ¶meters.out.attachmap, + TRUE) != 0) + { + printf ("error duplicating attachmap handle (%lu)\n", GetLastError ()); + header.error_code = EACCES; + CloseHandle (from_process_handle); + CloseHandle (token_handle); +/* more cleanup... yay! */ + return; + } + CloseHandle (token_handle); + return; +} diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h new file mode 100644 index 0000000000..32947f8ecc --- /dev/null +++ b/winsup/cygwin/cygserver_shm.h @@ -0,0 +1,91 @@ +/* cygserver_shm.h + + Copyright 2001 Red Hat Inc. + Written by Robert Collins + +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 +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver.h" + +#define SHM_CREATE 0 +#define SHM_REATTACH 1 +#define SHM_ATTACH 2 +#define SHM_DETACH 3 + + +class client_request_shm : public client_request +{ + public: +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, process_cache *cache); +#endif + client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t); + client_request_shm (); + client_request_shm (int, int, pid_t); + client_request_shm (int, int); + union { + struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in; + struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out; + } parameters; +}; + +#ifndef __INSIDE_CYGWIN__ +class shm_cleanup : cleanup_routine +{ +public: + virtual void cleanup (long winpid); +}; +#endif +#if 0 +class _shmattach { +public: + void *data; + class _shmattach *next; +}; + +class shmid_ds { +public: + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; + HANDLE filemap; + HANDLE attachmap; + void *mapptr; + class _shmattach *attachhead; +}; + +class shmnode { +public: + class shmid_ds * shmid; + class shmnode *next; + key_t key; +}; +//.... +struct shmid_ds { + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; +}; + +void *shmat(int, const void *, int); +int shmctl(int, int, struct shmid_ds *); +int shmdt(const void *); +int shmget(key_t, size_t, int); + +#endif diff --git a/winsup/cygwin/cygserver_transport.cc b/winsup/cygwin/cygserver_transport.cc new file mode 100755 index 0000000000..7c6e5fe003 --- /dev/null +++ b/winsup/cygwin/cygserver_transport.cc @@ -0,0 +1,92 @@ +/* cygserver_transport.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" +#include "cygwin/cygserver_transport_sockets.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +#else +#define debug_printf printf +#endif + +/* The factory */ +class transport_layer_base *create_server_transport() +{ + transport_layer_base *temp; + /* currently there is only the base class! */ + if (wincap.is_winnt ()) + temp = new transport_layer_pipes (); + else + temp = new transport_layer_base (); + return temp; +} + + +transport_layer_base::transport_layer_base () +{ + /* should we throw an error of some sort ? */ +} + +void +transport_layer_base::listen () +{ +} + +class transport_layer_base * +transport_layer_base::accept () +{ + return NULL; +} + +void +transport_layer_base::close() +{ +} + +ssize_t +transport_layer_base::read (char *buf, size_t len) +{ + return 0; +} + +ssize_t +transport_layer_base::write (char *buf, size_t len) +{ + return 0; +} + +bool +transport_layer_base::connect () +{ + return false; +} + +void +transport_layer_base::impersonate_client () +{ +} + +void +transport_layer_base::revert_to_self () +{ +} diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc new file mode 100755 index 0000000000..5b5a017c99 --- /dev/null +++ b/winsup/cygwin/cygserver_transport_pipes.cc @@ -0,0 +1,205 @@ +/* cygserver_transport_pipes.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +#else +#define DEBUG 0 +#define debug_printf if (DEBUG) printf +#endif + +transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe) +{ + pipe = new_pipe; + if (inited != true) + init_security(); +}; + +transport_layer_pipes::transport_layer_pipes () +{ + pipe = NULL; + strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc"); + if (inited != true) + init_security(); +} + + +void +transport_layer_pipes::init_security() +{ + /* FIXME: pthread_once or equivalent needed */ + InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE); + + sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); + sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE; + sec_none_nih.lpSecurityDescriptor = NULL; + sec_all_nih.lpSecurityDescriptor = &sd; + inited = true; +} + +void +transport_layer_pipes::listen () +{ + /* no-op */ +} + +class transport_layer_pipes * +transport_layer_pipes::accept () +{ + if (pipe) + { + debug_printf ("Already have a pipe in this %p\n",this); + return NULL; + } + + pipe = CreateNamedPipe (pipe_name, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + 0, 0, 1000, + &sec_all_nih ); + if (pipe == INVALID_HANDLE_VALUE) + { + debug_printf ("error creating pipe (%lu)\n.", GetLastError ()); + return NULL; + } + + if ( !ConnectNamedPipe ( pipe, NULL ) && + GetLastError () != ERROR_PIPE_CONNECTED) + { + printf ("error connecting to pipe (%lu)\n.", GetLastError ()); + CloseHandle (pipe); + pipe = NULL; + return NULL; + } + + transport_layer_pipes *new_conn = new transport_layer_pipes (pipe); + pipe = NULL; + + return new_conn; +} + +void +transport_layer_pipes::close() +{ + debug_printf ("closing pipe %p\n", pipe); + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + FlushFileBuffers (pipe); + DisconnectNamedPipe (pipe); + CloseHandle (pipe); + } +} + +ssize_t +transport_layer_pipes::read (char *buf, size_t len) +{ + debug_printf ("reading from pipe %p\n", pipe); + if (!pipe || pipe == INVALID_HANDLE_VALUE) + return -1; + + DWORD bytes_read; + DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL); + if (!rc) + { + debug_printf ("error reading from pipe (%lu)\n", GetLastError ()); + return -1; + } + return bytes_read; +} + +ssize_t +transport_layer_pipes::write (char *buf, size_t len) +{ + debug_printf ("writing to pipe %p\n", pipe); + DWORD bytes_written, rc; + if (!pipe || pipe == INVALID_HANDLE_VALUE) + return -1; + + rc = WriteFile (pipe, buf, len, &bytes_written, NULL); + if (!rc) + { + debug_printf ("error writing to pipe (%lu)\n", GetLastError ()); + return -1; + } + return bytes_written; +} + +bool +transport_layer_pipes::connect () +{ + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + debug_printf ("Already have a pipe in this %p\n",this); + return false; + } + + while (1) + { + pipe = CreateFile (pipe_name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_all_nih, + OPEN_EXISTING, + 0, NULL); + + if (pipe != INVALID_HANDLE_VALUE) + /* got the pipe */ + return true; + + if (GetLastError () != ERROR_PIPE_BUSY) + { + debug_printf ("Error opening the pipe (%lu)\n", GetLastError ()); + pipe = NULL; + return false; + } + if (!WaitNamedPipe (pipe_name, 20000)) + debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () ); + /* We loop here, because the pipe exists but is busy. If it doesn't exist + * the != ERROR_PIPE_BUSY will catch it. + */ + } +} + +void +transport_layer_pipes::impersonate_client () +{ + debug_printf ("impersonating pipe %p\n", pipe); + if (pipe && pipe != INVALID_HANDLE_VALUE) + { + BOOL rv = ImpersonateNamedPipeClient (pipe); + if (!rv) + debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ()); + } + debug_printf("I am who you are\n"); +} + +void +transport_layer_pipes::revert_to_self () +{ + RevertToSelf (); + debug_printf("I am who I yam\n"); +} + diff --git a/winsup/cygwin/cygserver_transport_sockets.cc b/winsup/cygwin/cygserver_transport_sockets.cc new file mode 100755 index 0000000000..6b75365b49 --- /dev/null +++ b/winsup/cygwin/cygserver_transport_sockets.cc @@ -0,0 +1,131 @@ +/* cygserver_transport_sockets.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include +#include +#include "wincap.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_sockets.h" + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifndef __OUTSIDE_CYGWIN__ +#include "winsup.h" +extern "C" int +cygwin_socket (int af, int type, int protocol); +extern "C" int +cygwin_connect (int fd, + const struct sockaddr *name, + int namelen); +extern "C" int +cygwin_accept (int fd, struct sockaddr *peer, int *len); +extern "C" int +cygwin_listen (int fd, int backlog); +extern "C" int +cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen); + +#else +#define cygwin_accept(A,B,C) ::accept(A,B,C) +#define cygwin_socket(A,B,C) ::socket(A,B,C) +#define cygwin_listen(A,B) ::listen(A,B) +#define cygwin_bind(A,B,C) ::bind(A,B,C) +#define cygwin_connect(A,B,C) ::connect(A,B,C) +#define debug_printf printf +#endif + +transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd) +{ + /* This may not be needed in this constructor - it's only used + * when creating a connection via bind or connect + */ + sockdetails.sa_family = AF_UNIX; + strcpy (sockdetails.sa_data, "/tmp/cygdaemo"); + sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family); +}; + +transport_layer_sockets::transport_layer_sockets (): fd (-1) +{ + sockdetails.sa_family = AF_UNIX; + strcpy (sockdetails.sa_data, "/tmp/cygdaemo"); + sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family); +} + +void +transport_layer_sockets::listen () +{ + /* we want a thread pool based approach. */ + if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0) + printf ("Socket not created error %d\n", errno); + if (cygwin_bind(fd, &sockdetails, sdlen)) + printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno); + if (cygwin_listen(fd, 5) < 0) + printf ("And the OS just isn't listening, all it says is %d\n", errno); +} + +class transport_layer_sockets * +transport_layer_sockets::accept () +{ + /* FIXME: check we have listened */ + int new_fd; + + if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0) + { + printf ("Nup, could' accept. %d\n",errno); + return NULL; + } + + transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd); + + return new_conn; + +} + +void +transport_layer_sockets::close() +{ + /* FIXME - are we open? */ + ::close (fd); +} + +ssize_t +transport_layer_sockets::read (char *buf, size_t len) +{ + /* FIXME: are we open? */ + return ::read (fd, buf, len); +} + +ssize_t +transport_layer_sockets::write (char *buf, size_t len) +{ + /* FIXME: are we open? */ + return ::write (fd, buf, len); +} + +bool +transport_layer_sockets::connect () +{ + /* are we already connected? */ + if (fd != -1) + return false; + fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0); + if (cygwin_connect (fd, &sockdetails, sdlen) < 0) + { + debug_printf("client connect failure %d\n", errno); + ::close (fd); + return false; + } + return true; +} diff --git a/winsup/cygwin/include/cygwin/cygserver.h b/winsup/cygwin/include/cygwin/cygserver.h new file mode 100755 index 0000000000..3751863f50 --- /dev/null +++ b/winsup/cygwin/include/cygwin/cygserver.h @@ -0,0 +1,135 @@ +/* cygserver.h + + Copyright 2001 Red Hat Inc. + + Written by Egor Duda + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _CYGSERVER_H_ +#define _CYGSERVER_H_ + +#define MAX_REQUEST_SIZE 128 + +#define CYGWIN_SERVER_VERSION_MAJOR 1 +#define CYGWIN_SERVER_VERSION_API 1 +#define CYGWIN_SERVER_VERSION_MINOR 0 +#define CYGWIN_SERVER_VERSION_PATCH 0 + + +typedef enum { + CYGSERVER_UNKNOWN=0, + CYGSERVER_OK=1, + CYGSERVER_DEAD=2 +} cygserver_states; + +typedef enum { + CYGSERVER_REQUEST_INVALID = 0, + CYGSERVER_REQUEST_GET_VERSION, + CYGSERVER_REQUEST_ATTACH_TTY, + CYGSERVER_REQUEST_SHUTDOWN, + CYGSERVER_REQUEST_SHM_GET, + CYGSERVER_REQUEST_LAST +} cygserver_request_code; + +class request_header +{ + public: + ssize_t cb; + cygserver_request_code req_id; + ssize_t error_code; + request_header (cygserver_request_code id, ssize_t ncb) : cb (ncb), req_id (id), error_code (0) {} ; +} +#ifdef __GNUC__ + __attribute__ ((packed)) +#endif +; + +extern void cygserver_init (); + +#define INIT_REQUEST(req,id) \ + (req).header.cb = sizeof (req); \ + (req).header.req_id = id; + +struct request_get_version +{ + DWORD major, api, minor, patch; +} +#ifdef __GNUC__ + __attribute__ ((packed)) +#endif +; + +struct request_shutdown +{ + int foo; +} +#ifdef __GNUC__ + __attribute__ ((packed)) +#endif +; + +struct request_attach_tty +{ + DWORD pid, master_pid; + HANDLE from_master, to_master; +} +#ifdef __GNUC__ + __attribute__ ((packed)) +#endif +; + +class client_request +{ + public: + client_request (cygserver_request_code id, ssize_t data_size); + virtual void send (transport_layer_base *conn); +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, class process_cache *cache); +#endif + virtual operator struct request_header (); + cygserver_request_code req_id () {return header.req_id;}; + virtual ~client_request(); + request_header header; + char *buffer; +}; + +class client_request_get_version : public client_request +{ + public: +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, class process_cache *cache); +#endif + client_request_get_version::client_request_get_version(); + struct request_get_version version; +}; + +class client_request_shutdown : public client_request +{ + public: +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, class process_cache *cache); +#endif + client_request_shutdown (); +}; + +class client_request_attach_tty : public client_request +{ + public: +#ifndef __INSIDE_CYGWIN__ + virtual void serve (transport_layer_base *conn, class process_cache *cache); +#endif + client_request_attach_tty (); + client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master); + HANDLE from_master () {return req.from_master;}; + HANDLE to_master () {return req.to_master;}; + struct request_attach_tty req; +}; + +extern int cygserver_request (client_request *); + +#endif /* _CYGSERVER+H+ */ diff --git a/winsup/cygwin/include/cygwin/cygserver_process.h b/winsup/cygwin/include/cygwin/cygserver_process.h new file mode 100755 index 0000000000..4a49212b2d --- /dev/null +++ b/winsup/cygwin/include/cygwin/cygserver_process.h @@ -0,0 +1,84 @@ +/* cygserver_process.h + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef _CYGSERVER_PROCESS_ +#define _CYGSERVER_PROCESS_ + +/* needs threaded_queue.h */ + +class process_cleanup:public queue_request +{ +public: + virtual void process (); + process_cleanup (class process *nprocess) : theprocess (nprocess) {}; +private: + class process * theprocess; +}; + +class process_process_param:public queue_process_param +{ + class process_cache *cache; +public: + DWORD request_loop (); + process_process_param ():queue_process_param (true) {}; +}; + +class cleanup_routine +{ +public: + cleanup_routine () : next (NULL) {}; + class cleanup_routine * next; + /* MUST BE SYNCHRONOUS */ + virtual void cleanup (long winpid); +}; + +class process +{ +public: + HANDLE handle (); + long winpid; + process (long); + ~process (); + DWORD exit_code (); + class process * next; + long refcount; + bool add_cleanup_routine (class cleanup_routine *); + void cleanup (); +private: + /* used to prevent races-on-delete */ + CRITICAL_SECTION access; + volatile long cleaning_up; + class cleanup_routine *head; + HANDLE thehandle; + DWORD _exit_status; +}; + +class process_cache:public threaded_queue +{ +public: + process_cache (unsigned int initial_workers); + virtual ~ process_cache (); + class process *process (long); + /* remove a process from the cache */ + int handle_snapshot (HANDLE *, class process **, ssize_t, int); + void remove_process (class process *); + /* threaded_queue methods */ + void process_requests (); + HANDLE cache_add_trigger; + +private: + virtual void add_task (class process *); + class process *head; + CRITICAL_SECTION cache_write_access; +}; + +#endif /* _CYGSERVER_PROCESS_ */ diff --git a/winsup/cygwin/include/cygwin/cygserver_transport.h b/winsup/cygwin/include/cygwin/cygserver_transport.h new file mode 100755 index 0000000000..1ed16edf78 --- /dev/null +++ b/winsup/cygwin/include/cygwin/cygserver_transport.h @@ -0,0 +1,32 @@ +/* cygserver.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef _CYGSERVER_TRANSPORT_ +#define _CYGSERVER_TRANSPORT_ +class transport_layer_base *create_server_transport(); + +/* the base class does nothing. */ +class transport_layer_base +{ + public: + virtual void listen (); + virtual class transport_layer_base * accept (); + virtual void close (); + virtual ssize_t read (char *buf, size_t len); + virtual ssize_t write (char *buf, size_t len); + virtual bool connect(); + virtual void impersonate_client (); + virtual void revert_to_self (); + transport_layer_base (); +}; + +#endif /* _CYGSERVER_TRANSPORT_ */ diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h b/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h new file mode 100755 index 0000000000..9afeebfefe --- /dev/null +++ b/winsup/cygwin/include/cygwin/cygserver_transport_pipes.h @@ -0,0 +1,39 @@ +/* cygserver.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef _CYGSERVER_TRANSPORT_PIPES_ +#define _CYGSERVER_TRANSPORT_PIPES_ +/* Named pipes based transport, for security on NT */ +class transport_layer_pipes : public transport_layer_base +{ + public: + virtual void listen (); + virtual class transport_layer_pipes * accept (); + virtual void close (); + virtual ssize_t read (char *buf, size_t len); + virtual ssize_t write (char *buf, size_t len); + virtual bool connect(); + virtual void impersonate_client (); + virtual void revert_to_self (); + transport_layer_pipes (); + + private: + /* for pipe based communications */ + void init_security (); + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sec_none_nih, sec_all_nih; + char pipe_name [MAX_PATH]; + HANDLE pipe; + bool inited; + transport_layer_pipes (HANDLE new_pipe); +}; +#endif /* _CYGSERVER_TRANSPORT_PIPES_ */ diff --git a/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h b/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h new file mode 100755 index 0000000000..94dc43e38b --- /dev/null +++ b/winsup/cygwin/include/cygwin/cygserver_transport_sockets.h @@ -0,0 +1,33 @@ +/* cygserver.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef _CYGSERVER_TRANSPORT_SOCKETS_ +#define _CYGSERVER_TRANSPORT_SOCKETS_ +class transport_layer_sockets : public transport_layer_base +{ + public: + virtual void listen (); + virtual class transport_layer_sockets * accept (); + virtual void close (); + virtual ssize_t read (char *buf, size_t len); + virtual ssize_t write (char *buf, size_t len); + virtual bool connect(); + transport_layer_sockets (); + + private: + /* for socket based communications */ + int fd; + struct sockaddr sockdetails; + int sdlen; + transport_layer_sockets (int newfd); +}; +#endif /* _CYGSERVER_TRANSPORT_SOCKETS_ */ diff --git a/winsup/cygwin/include/sys/ipc.h b/winsup/cygwin/include/sys/ipc.h new file mode 100644 index 0000000000..8ddec6d500 --- /dev/null +++ b/winsup/cygwin/include/sys/ipc.h @@ -0,0 +1,52 @@ +/* sys/ipc.h + + Copyright 2001 Red Hat Inc. + Written by Robert Collins + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _SYS_IPC_H +#define _SYS_IPC_H + +/* sys/types must be included before sys/ipc.h. We aren't meant to automatically + * include it however + */ + +struct ipc_perm { + uid_t uid; + gid_t gid; + uid_t cuid; + gid_t cgid; + mode_t mode; +}; + +/* the mode flags used with the _get functions use the low order 9 bits for a mode + * request + */ +#define IPC_CREAT 0x0200 +#define IPC_EXCL 0x0400 +#define IPC_NOWAIT 0x0800 + +/* this is a value that will _never_ be a valid key from ftok */ +#define IPC_PRIVATE -2 + +#define IPC_RMID 0x0003 +#define IPC_SET 0x0002 +#define IPC_STAT 0x0001 + +key_t ftok(const char *, int); + +#endif /* _SYS_IPC_H */ + +#ifdef __cplusplus +} +#endif diff --git a/winsup/cygwin/include/sys/shm.h b/winsup/cygwin/include/sys/shm.h new file mode 100644 index 0000000000..208c5f29f3 --- /dev/null +++ b/winsup/cygwin/include/sys/shm.h @@ -0,0 +1,89 @@ +/* sys/shm.h + + Copyright 2001 Red Hat Inc. + Written by Robert Collins + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _SYS_SHM_H +#define _SYS_SHM_H + +#include + +#define SHM_RDONLY 1 +/* 64 Kb was hardcoded for x86. MS states this may change, but we need it in the header + * file. + */ +#define SHMLBA 65536 +#define SHM_RND 1 + +typedef long int shmatt_t; + +#if defined(__INSIDE_CYGWIN__) && defined(__cplusplus) + +class _shmattach { +public: + void *data; + int shmflg; + class _shmattach *next; +}; + +class shmid_ds { +public: + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; + void *mapptr; +}; + +class shmnode { +public: + class shmid_ds * shmds; + int shm_id; + class shmnode *next; + key_t key; + HANDLE filemap; + HANDLE attachmap; + class _shmattach *attachhead; +}; + +#else +/* this is what we return when queried. It has no bitwise correspondence + * the internal structures + */ +struct shmid_ds { + struct ipc_perm shm_perm; + size_t shm_segsz; + pid_t shm_lpid; + pid_t shm_cpid; + shmatt_t shm_nattch; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; +}; +#endif /* __INSIDE_CYGWIN__ */ + +void *shmat(int, const void *, int); +int shmctl(int, int, struct shmid_ds *); +int shmdt(const void *); +int shmget(key_t, size_t, int); + +#endif /* _SYS_SHM_H */ + +#ifdef __cplusplus +} +#endif diff --git a/winsup/cygwin/ipc.cc b/winsup/cygwin/ipc.cc new file mode 100644 index 0000000000..192a124d52 --- /dev/null +++ b/winsup/cygwin/ipc.cc @@ -0,0 +1,39 @@ +/* ipc.cc: Single unix specification IPC interface for Cygwin + + Copyright 2001 Red Hat, Inc. + + Originally written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#include "winsup.h" +#include +#include + +extern "C" +{ + +/* Notes: we return a valid key even if id's low order 8 bits are 0. */ +key_t +ftok(const char *path, int id) +{ + struct stat statbuf; + if (stat(path, &statbuf)) + { + /* stat set the appropriate errno for us */ + return (key_t) -1; + } + + /* dev_t is short for cygwin + * ino_t is long for cygwin + * and we need 8 bits for the id. + * thus key_t is long long. + */ + return ((long long) statbuf.st_dev << (5*8)) | (statbuf.st_ino << (8) ) | (id & 0x00ff); +} + +} diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc new file mode 100644 index 0000000000..dbe9bb0e98 --- /dev/null +++ b/winsup/cygwin/shm.cc @@ -0,0 +1,499 @@ +/* shm.cc: Single unix specification IPC interface for Cygwin + + Copyright 2001 Red Hat, Inc. + + Originally written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#include "winsup.h" +#include +#include +#include "cygerrno.h" +#include +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include +#include "thread.h" +#include +#include "perprocess.h" +#include "cygserver_shm.h" + +// FIXME IS THIS CORRECT +/* Implementation notes: We use two shared memory regions per key: + * One for the control structure, and one for the shared memory. + * While this has a higher overhead tham a single shared area, + * It allows more flexability. As the entire code is transparent to the user + * We can merge these in the future should it be needed. + */ +extern "C" size_t +getsystemallocgranularity () +{ + SYSTEM_INFO sysinfo; + static size_t buffer_offset = 0; + if (buffer_offset) + return buffer_offset; + GetSystemInfo (&sysinfo); + buffer_offset = sysinfo.dwAllocationGranularity; + return buffer_offset; +} + +client_request_shm::client_request_shm (int ntype, int nshm_id): +client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) +{ + buffer = (char *) ¶meters; + parameters.in.shm_id = nshm_id; + parameters.in.type = SHM_REATTACH; + parameters.in.pid = GetCurrentProcessId (); +} + +client_request_shm::client_request_shm (int ntype, int nshm_id, pid_t npid): +client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) +{ + buffer = (char *) ¶meters; + parameters.in.shm_id = nshm_id; + parameters.in.type = ntype; + parameters.in.pid = npid; +} + +client_request_shm::client_request_shm (key_t nkey, size_t nsize, + int nshmflg, + char psdbuf[4096], + pid_t npid): +client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) +{ + buffer = (char *) ¶meters; + parameters.in.key = nkey; + parameters.in.size = nsize; + parameters.in.shmflg = nshmflg; + parameters.in.type = SHM_CREATE; + parameters.in.pid = npid; + memcpy (parameters.in.sd_buf, psdbuf, 4096); +} + +static shmnode *shm_head = NULL; + +static shmnode * +build_inprocess_shmds (HANDLE hfilemap, HANDLE hattachmap, key_t key, + int shm_id) +{ + HANDLE filemap = hfilemap; + void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); + + if (!mapptr) + { + CloseHandle (hfilemap); + CloseHandle (hattachmap); + //FIXME: close filemap and free the mutex + /* we couldn't access the mapped area with the requested permissions */ + set_errno (EACCES); + return NULL; + } + + /* Now get the user data */ + HANDLE attachmap = hattachmap; + shmid_ds *shmtemp = new shmid_ds; + if (!shmtemp) + { + system_printf ("failed to malloc shm node\n"); + set_errno (ENOMEM); + UnmapViewOfFile (mapptr); + CloseHandle (filemap); + CloseHandle (attachmap); + /* exit mutex */ + return NULL; + } + + /* get the system node data */ + *shmtemp = *(shmid_ds *) mapptr; + + /* process local data */ + shmnode *tempnode = new shmnode; + + tempnode->filemap = filemap; + tempnode->attachmap = attachmap; + shmtemp->mapptr = mapptr; + + /* no need for InterlockedExchange here, we're serialised by the global mutex */ + tempnode->shmds = shmtemp; + tempnode->shm_id = shm_id; + tempnode->key = key; + tempnode->next = shm_head; + tempnode->attachhead = NULL; + shm_head = tempnode; + + /* FIXME: leave the system wide shm mutex */ + + return tempnode; +} + +int __stdcall +fixup_shms_after_fork () +{ + shmnode *tempnode = shm_head; + while (tempnode) + { + void *newshmds = + MapViewOfFile (tempnode->filemap, FILE_MAP_WRITE, 0, 0, 0); + if (!newshmds) + { + /* don't worry about handle cleanup, we're dying! */ + system_printf ("failed to reattach to shm control file view %x\n", + tempnode); + return 1; + } + tempnode->shmds = (class shmid_ds *) newshmds; + tempnode->shmds->mapptr = newshmds; + _shmattach *attachnode = tempnode->attachhead; + while (attachnode) + { + void *newdata = MapViewOfFileEx (tempnode->attachmap, + (attachnode->shmflg & SHM_RDONLY) ? + FILE_MAP_READ : FILE_MAP_WRITE, 0, + 0, 0, attachnode->data); + if (newdata != attachnode->data) + { + /* don't worry about handle cleanup, we're dying! */ + system_printf ("failed to reattach to mapped file view %x\n", + attachnode->data); + return 1; + } + attachnode = attachnode->next; + } + tempnode = tempnode->next; + } + return 0; +} + +/* this is ugly. Yes, I know that. + * FIXME: abstract the lookup functionality, + * So that it can be an array, list, whatever without us being worried + */ + +/* FIXME: after fork, every memory area needs to have the attach count + * incremented. This should be done in the server? + */ + +/* FIXME: tell the daemon when we attach, so at process close it can clean up + * the attach count + */ +extern "C" void * +shmat (int shmid, const void *shmaddr, int shmflg) +{ + shmnode *tempnode = shm_head; + while (tempnode && tempnode->shm_id != shmid) + tempnode = tempnode->next; + + if (!tempnode) + { + /* couldn't find a currently open shm control area for the key - probably because + * shmget hasn't been called. + * Allocate a new control block - this has to be handled by the daemon */ + client_request_shm *req = + new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ()); + + int rc; + if ((rc = cygserver_request (req))) + { + delete req; + set_errno (ENOSYS); /* daemon communication failed */ + return (void *) -1; + } + + if (req->header.error_code) /* shm_get failed in the daemon */ + { + set_errno (req->header.error_code); + delete req; + return (void *) -1; + } + + /* we've got the id, now we open the memory area ourselves. + * This tests security automagically + * FIXME: make this a method of shmnode ? + */ + tempnode = + build_inprocess_shmds (req->parameters.out.filemap, + req->parameters.out.attachmap, + req->parameters.out.key, + req->parameters.out.shm_id); + delete req; + if (!tempnode) + return (void *) -1; + + } + + class shmid_ds *shm = tempnode->shmds; + + if (shmaddr) + { + //FIXME: requested base address ?! (Don't forget to fix the fixup_after_fork too) + set_errno (EINVAL); + return (void *) -1; + } + + void *rv = MapViewOfFile (tempnode->attachmap, + (shmflg & SHM_RDONLY) ? FILE_MAP_READ : + FILE_MAP_WRITE, 0, 0, 0); + + if (!rv) + { + //FIXME: translate GetLastError() + set_errno (EACCES); + return (void *) -1; + } + /* tell the daemon we have attached */ + client_request_shm *req = + new client_request_shm (SHM_ATTACH, shmid); + int rc; + if ((rc = cygserver_request (req))) + { + debug_printf ("failed to tell deaemon that we have attached\n"); + } + delete req; + + _shmattach *attachnode = new _shmattach; + attachnode->data = rv; + attachnode->shmflg = shmflg; + attachnode->next = + (_shmattach *) InterlockedExchangePointer (&tempnode->attachhead, + attachnode); + + + return rv; +} + +/* FIXME: tell the daemon when we detach so it doesn't cleanup incorrectly. + */ +extern "C" int +shmdt (const void *shmaddr) +{ + /* this should be "rare" so a hefty search is ok. If this is common, then we + * should alter the data structs to allow more optimisation + */ + +} + +//FIXME: who is allowed to perform STAT? +extern "C" int +shmctl (int shmid, int cmd, struct shmid_ds *buf) +{ + shmnode *tempnode = shm_head; + while (tempnode && tempnode->shm_id != shmid) + tempnode = tempnode->next; + if (!tempnode) + { + /* couldn't find a currently open shm control area for the key - probably because + * shmget hasn't been called. + * Allocate a new control block - this has to be handled by the daemon */ + client_request_shm *req = + new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ()); + + int rc; + if ((rc = cygserver_request (req))) + { + delete req; + set_errno (ENOSYS); /* daemon communication failed */ + return -1; + } + + if (req->header.error_code) /* shm_get failed in the daemon */ + { + set_errno (req->header.error_code); + delete req; + return -1; + } + + /* we've got the id, now we open the memory area ourselves. + * This tests security automagically + * FIXME: make this a method of shmnode ? + */ + tempnode = + build_inprocess_shmds (req->parameters.out.filemap, + req->parameters.out.attachmap, + req->parameters.out.key, + req->parameters.out.shm_id); + delete req; + if (!tempnode) + return -1; + } + + switch (cmd) + { + case IPC_STAT: + buf->shm_perm = tempnode->shmds->shm_perm; + buf->shm_segsz = tempnode->shmds->shm_segsz; + buf->shm_lpid = tempnode->shmds->shm_lpid; + buf->shm_cpid = tempnode->shmds->shm_cpid; + buf->shm_nattch = tempnode->shmds->shm_nattch; + buf->shm_atime = tempnode->shmds->shm_atime; + buf->shm_dtime = tempnode->shmds->shm_dtime; + buf->shm_ctime = tempnode->shmds->shm_ctime; + break; + case IPC_RMID: + { + /* TODO: check permissions. Or possibly, the daemon gets to be the only + * one with write access to the memory area? + */ + if (tempnode->shmds->shm_nattch) + system_printf + ("call to shmctl with cmd= IPC_RMID when memory area still has" + " attachees\n"); + /* how does this work? + * we mark the ds area as "deleted", and the at and get calls all fail from now on + * on, when nattch becomes 0, the mapped data area is destroyed. + * and each process, as they touch this area detaches. eventually only the + * daemon has an attach. The daemon gets asked to detach immediately. + */ +#if 0 +//waiting for the daemon to handle terminating process's + client_request_shm *req = + new client_request_shm (SHM_DEL, shmid, GetCurrentProcessId ()); + int rc; + if ((rc = cygserver_request (req))) + { + delete req; + set_errno (ENOSYS); /* daemon communication failed */ + return -1; + } + + if (req->header.error_code) /* shm_del failed in the daemon */ + { + set_errno (req->header.error_code); + delete req; + return -1; + } + + /* the daemon has deleted it's references */ + /* now for us */ + +#endif + + } + break; + case IPC_SET: + default: + set_errno (EINVAL); + return -1; + } + return 0; +} + +/* FIXME: evaluate getuid() and getgid() against the requested mode. Then + * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ + * appropriately + */ + +/* FIXME: shmid should be a verifyable object + */ + +/* FIXME: on NT we should check everything against the SD. On 95 we just emulate. + */ +extern "C" int +shmget (key_t key, size_t size, int shmflg) +{ + DWORD sd_size = 4096; + char sd_buf[4096]; + PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf; + /* create a sd for our open requests based on shmflag & 0x01ff */ + psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (), + shmflg & 0x01ff, psd, &sd_size); + + if (key == (key_t) - 1) + { + set_errno (ENOENT); + return -1; + } + + /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide + * to prevent races on shmget. + */ + + /* walk the list of currently open keys and return the id if found + */ + shmnode *tempnode = shm_head; + while (tempnode) + { + if (tempnode->key == key && key != IPC_PRIVATE) + { + // FIXME: free the mutex + if (size && tempnode->shmds->shm_segsz < size) + { + set_errno (EINVAL); + return -1; + } + if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) + { + set_errno (EEXIST); + // FIXME: free the mutex + return -1; + } + // FIXME: do we need to other tests of the requested mode with the + // tempnode->shmid mode ? testcase on unix needed. + // FIXME do we need a security test? We are only examining the keys we already have open. + // FIXME: what are the sec implications for fork () if we don't check here? + return tempnode->shm_id; + } + tempnode = tempnode->next; + } + /* couldn't find a currently open shm control area for the key. + * Allocate a new control block - this has to be handled by the daemon */ + client_request_shm *req = + new client_request_shm (key, size, shmflg, sd_buf, + GetCurrentProcessId ()); + + int rc; + if ((rc = cygserver_request (req))) + { + delete req; + set_errno (ENOSYS); /* daemon communication failed */ + return -1; + } + + if (req->header.error_code) /* shm_get failed in the daemon */ + { + set_errno (req->header.error_code); + delete req; + return -1; + } + + /* we've got the id, now we open the memory area ourselves. + * This tests security automagically + * FIXME: make this a method of shmnode ? + */ + shmnode *shmtemp = build_inprocess_shmds (req->parameters.out.filemap, + req->parameters.out.attachmap, + key, + req->parameters.out.shm_id); + delete req; + if (shmtemp) + return shmtemp->shm_id; + return -1; + + +#if 0 + /* fill out the node data */ + shmtemp->shm_perm.cuid = getuid (); + shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid; + shmtemp->shm_perm.cgid = getgid (); + shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid; + shmtemp->shm_perm.mode = shmflg & 0x01ff; + shmtemp->shm_lpid = 0; + shmtemp->shm_nattch = 0; + shmtemp->shm_atime = 0; + shmtemp->shm_dtime = 0; + shmtemp->shm_ctime = time (NULL); + shmtemp->shm_segsz = size; + *(shmid_ds *) mapptr = *shmtemp; + shmtemp->filemap = filemap; + shmtemp->attachmap = attachmap; + shmtemp->mapptr = mapptr; + +#endif +} diff --git a/winsup/cygwin/threaded_queue.cc b/winsup/cygwin/threaded_queue.cc new file mode 100755 index 0000000000..5fb22b1910 --- /dev/null +++ b/winsup/cygwin/threaded_queue.cc @@ -0,0 +1,249 @@ +/* threaded_queue.cc + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + 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 +#include +#include +#include +#include +#include "wincap.h" +#include "threaded_queue.h" +#define DEBUG 1 +#define debug_printf if (DEBUG) printf + +/* threaded_queue */ + +DWORD WINAPI +worker_function (LPVOID LpParam) +{ + class threaded_queue *queue = (class threaded_queue *) LpParam; + class queue_request *request; + /* FIXME use a threadsafe pop instead for speed? */ + while (queue->active) + { + EnterCriticalSection (&queue->queuelock); + while (!queue->request && queue->active) + { + LeaveCriticalSection (&queue->queuelock); + DWORD rc = WaitForSingleObject (queue->event, INFINITE); + if (rc == WAIT_FAILED) + { + printf ("Wait for event failed\n"); + queue->running--; + ExitThread (0); + } + EnterCriticalSection (&queue->queuelock); + } + if (!queue->active) + { + queue->running--; + LeaveCriticalSection (&queue->queuelock); + ExitThread (0); + } + /* not needed, but it is efficient */ + request = + (class queue_request *) InterlockedExchangePointer (&queue->request, + queue->request-> + next); + LeaveCriticalSection (&queue->queuelock); + request->process (); + delete request; + } + queue->running--; + ExitThread (0); +} + +void +threaded_queue::create_workers () +{ + InitializeCriticalSection (&queuelock); + if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create event queue (%lu), terminating\n", + GetLastError ()); + exit (1); + } + active = true; + + /* FIXME: Use a stack pair and create threads on the fly whenever + * we have to to service a request. + */ + for (unsigned int i = 0; i < initial_workers; i++) + { + HANDLE hThread; + DWORD tid; + hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid); + if (hThread == NULL) + { + printf ("Failed to create thread (%lu), terminating\n", + GetLastError ()); + exit (1); + } + CloseHandle (hThread); + running++; + } +} + +void +threaded_queue::cleanup () +{ + /* harvest the threads */ + active = false; + /* kill the request processing loops */ + queue_process_param *reqloop; + /* make sure we don't race with a incoming request creation */ + EnterCriticalSection (&queuelock); + reqloop = + (queue_process_param *) InterlockedExchangePointer (&process_head, NULL); + while (reqloop) + { + queue_process_param *t = reqloop; + reqloop = reqloop->next; + delete t; + } + LeaveCriticalSection (&queuelock); + if (!running) + return; + printf ("Waiting for current queue threads to terminate\n"); + for (int n = running; n; n--) + PulseEvent (event); + while (running) + sleep (1); + DeleteCriticalSection (&queuelock); + CloseHandle (event); +} + +/* FIXME: return success or failure */ +void +threaded_queue::add (queue_request * therequest) +{ + /* safe to not "Try" because workers don't hog this, they wait on the event + */ + EnterCriticalSection (&queuelock); + if (!running) + { + printf ("No worker threads to handle request!\n"); + } + if (!request) + request = therequest; + else + { + /* add to the queue end. */ + queue_request *listrequest = request; + while (listrequest->next) + listrequest = listrequest->next; + listrequest->next = therequest; + } + PulseEvent (event); + LeaveCriticalSection (&queuelock); +} + +/* FIXME: return success or failure rather than quitting */ +void +threaded_queue::process_requests (queue_process_param * params, + threaded_queue_thread_function * + request_loop) +{ + if (params->start (request_loop, this) == false) + exit (1); + params->next = + (queue_process_param *) InterlockedExchangePointer (&process_head, + params); +} + +/* queue_process_param */ +/* How does a constructor return an error? */ +queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false), +interruptible +(ninterruptible) +{ + if (!interruptible) + return; + debug_printf ("creating an interruptible processing thread\n"); + if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL) + { + printf ("Failed to create interrupt event (%lu), terminating\n", + GetLastError ()); + exit (1); + } +} + +queue_process_param::~queue_process_param () +{ + if (running) + stop (); + if (!interruptible) + return; + CloseHandle (interrupt); +} + +bool + queue_process_param::start (threaded_queue_thread_function * request_loop, + threaded_queue * thequeue) +{ + queue = thequeue; + hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid); + if (hThread) + { + running = true; + return true; + } + printf ("Failed to create thread (%lu), terminating\n", GetLastError ()); + return false; +} + +void +queue_process_param::stop () +{ + if (interruptible) + { + InterlockedExchange (&shutdown, true); + PulseEvent (interrupt); + /* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get + * scheduled again, we print an error and exit. We _should_ loop or + * try resignalling. We don't want to hand here though... + */ + int n = 5; + while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT); + if (!n) + { + printf ("Process thread didn't shutdown cleanly after 200ms!\n"); + exit (1); + } + else + running = false; + } + else + { + printf ("killing request loop thread %ld\n", tid); + int rc; + if (!(rc = TerminateThread (hThread, 0))) + { + printf ("error shutting down request loop worker thread\n"); + } + running = false; + } + CloseHandle (hThread); +} + +/* queue_request */ +queue_request::queue_request ():next (NULL) +{ +} + +void +queue_request::process (void) +{ + printf ("\n**********************************************\n" + "Oh no! we've hit the base queue_request process() function, and this indicates a coding\n" + "fault !!!\n" "***********************************************\n"); +} diff --git a/winsup/cygwin/threaded_queue.h b/winsup/cygwin/threaded_queue.h new file mode 100755 index 0000000000..b330d96ac3 --- /dev/null +++ b/winsup/cygwin/threaded_queue.h @@ -0,0 +1,67 @@ +/* threaded_queue.h + + Copyright 2001 Red Hat Inc. + + Written by Robert Collins + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef _THREADED_QUEUE_ +#define _THREADED_QUEUE_ + +/* a specific request */ + +class queue_request +{ + public: + class queue_request *next; + virtual void process (); + queue_request(); +}; + + +typedef DWORD WINAPI threaded_queue_thread_function (LPVOID); +/* parameters for a request finding and submitting loop */ + +class queue_process_param +{ + public: + bool start (threaded_queue_thread_function *, class threaded_queue *); + void stop (); + bool running; + long int shutdown; + class queue_process_param * next; + class threaded_queue *queue; + queue_process_param (bool ninterruptible); + ~queue_process_param (); + bool interruptible; + HANDLE interrupt; + HANDLE hThread; + DWORD tid; +}; + +/* a queue to allocate requests from n submission loops to x worker threads */ + +class threaded_queue +{ + public: + CRITICAL_SECTION queuelock; + HANDLE event; + bool active; + queue_request * request; + unsigned int initial_workers; + unsigned int running; + void create_workers (); + void cleanup (); + void add (queue_request *); + void process_requests (queue_process_param *, threaded_queue_thread_function *); + threaded_queue () : active (false), request (NULL), initial_workers (1), running (0), process_head (NULL) {}; + private: + queue_request *process_head; +}; + +#endif /* _THREADED_QUEUE_ */ -- 2.11.0