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

* Merged cygwin_daemon into head minus the new shm and ipc exports.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	* thread.cc (semaphore::TryWait): Set errno as required by posix 1003.1.
	(__sem_wait): Ditto.
	(__sem_trywait): Ditto.
This commit is contained in:
Robert Collins 2002-02-28 14:30:38 +00:00
parent 062401c9b4
commit f449bfef40
41 changed files with 6453 additions and 59 deletions

207
winsup/cygserver/client.cc Normal file
View File

@ -0,0 +1,207 @@
/* cygserver_client.cc
Copyright 2001 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __OUTSIDE_CYGWIN__
#undef __INSIDE_CYGWIN__
#else
#include "winsup.h"
#endif
#ifndef __INSIDE_CYGWIN__
#define debug_printf printf
#define api_fatal printf
#include <stdio.h>
#include <windows.h>
#endif
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
//#include "security.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
#include "cygwin/cygserver.h"
/* 0 = untested, 1 = running, 2 = dead */
int cygserver_running=CYGSERVER_UNKNOWN;
/* on by default during development. For release, we probably want off by default */
int allow_daemon = TRUE;
client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version))
{
buffer = (char *)&version;
}
client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
{
buffer = (char *)&req;
req.pid = 0;
req.master_pid = 0;
req.from_master = NULL;
req.to_master = NULL;
}
client_request_attach_tty::client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
{
buffer = (char *)&req;
req.pid = npid;
req.master_pid = nmaster_pid;
req.from_master = nfrom_master;
req.to_master = nto_master;
}
client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0)
{
buffer = NULL;
}
client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size)
{
}
client_request::~client_request ()
{
}
client_request::operator class request_header ()
{
return header;
}
void
client_request::send (transport_layer_base *conn)
{
if (!conn)
return;
debug_printf("this=%p, conn=%p\n",this, conn);
ssize_t bytes_written, bytes_read;
debug_printf("header.cb = %ld\n",header.cb);
if ((bytes_written = conn->write ((char *)&header, sizeof (header)))
!= sizeof(header) || (header.cb &&
(bytes_written = conn->write (buffer, header.cb)) != header.cb))
{
header.error_code = -1;
debug_printf ("bytes written != request size\n");
return;
}
debug_printf("Sent request, size (%ld)\n",bytes_written);
if ((bytes_read = conn->read ((char *)&header, sizeof (header)))
!= sizeof (header) || (header.cb &&
(bytes_read = conn->read (buffer, header.cb) ) != header.cb))
{
header.error_code = -1;
debug_printf("failed reading response \n");
return;
}
debug_printf ("completed ok\n");
}
/* Oh, BTW: Fix the procedural basis and make this more intuitive. */
int
cygserver_request (client_request * req)
{
class transport_layer_base *transport;
if (!req || allow_daemon != TRUE)
return -1;
/* dont' retry every request if the server's not there */
if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION)
return -1;
transport = create_server_transport ();
/* FIXME: have at most one connection per thread. use TLS to store the details */
/* logic is:
* if not tlskey->conn, new conn,
* then; transport=conn;
*/
if (!transport->connect ())
{
delete transport;
return -1;
}
debug_printf ("connected to server %p\n", transport);
req->send(transport);
transport->close ();
delete transport;
return 0;
}
#if 0
BOOL
check_cygserver_available ()
{
BOOL ret_val = FALSE;
HANDLE pipe = CreateFile (pipe_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec_all_nih,
OPEN_EXISTING,
0,
NULL);
if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY)
ret_val = TRUE;
if (pipe && pipe != INVALID_HANDLE_VALUE)
CloseHandle (pipe);
return (ret_val);
}
#endif
void
cygserver_init ()
{
int rc;
if (allow_daemon != TRUE)
{
cygserver_running = CYGSERVER_DEAD;
return;
}
if (cygserver_running==CYGSERVER_OK)
return;
client_request_get_version *req =
new client_request_get_version ();
rc = cygserver_request (req);
delete req;
if (rc < 0)
cygserver_running = CYGSERVER_DEAD;
else if (rc > 0)
api_fatal ( "error connecting to cygwin server. error: %d", rc );
else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR ||
req->version.api != CYGWIN_SERVER_VERSION_API ||
req->version.minor > CYGWIN_SERVER_VERSION_MINOR)
api_fatal ( "incompatible version of cygwin server.\n\
client version %d.%d.%d.%d, server version%ld.%ld.%ld.%ld",
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
req->version.major,
req->version.api,
req->version.minor,
req->version.patch );
else
cygserver_running = CYGSERVER_OK;
}

View File

@ -0,0 +1,548 @@
/* cygserver.cc
Copyright 2001 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include "wincap.h"
#include "cygwin_version.h"
#include "getopt.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
#include "threaded_queue.h"
#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver.h"
#include "cygserver_shm.h"
/* for quieter operation, set to 0 */
#define DEBUG 0
#define debug_printf if (DEBUG) printf
GENERIC_MAPPING access_mapping;
static class transport_layer_base *transport;
DWORD request_count = 0;
BOOL
setup_privileges ()
{
BOOL rc, ret_val;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES sPrivileges;
rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ;
if ( !rc )
{
printf ( "error opening process token (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
rc = LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid );
if ( !rc )
{
printf ( "error getting prigilege luid (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
sPrivileges.PrivilegeCount = 1 ;
sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ;
if ( !rc )
{
printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
access_mapping.GenericRead = FILE_READ_DATA;
access_mapping.GenericWrite = FILE_WRITE_DATA;
access_mapping.GenericExecute = 0;
access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA;
ret_val = TRUE;
out:
CloseHandle ( hToken );
return ret_val;
}
int
check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE)
{
HANDLE local_handle = NULL;
int ret_val = EACCES;
char sd_buf [1024];
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
DWORD bytes_needed;
PRIVILEGE_SET ps;
DWORD ps_len = sizeof (ps);
BOOL status;
if (from_process != GetCurrentProcess ())
{
if (!DuplicateHandle (from_process, from_handle,
GetCurrentProcess (), &local_handle,
0, bInheritHandle,
DUPLICATE_SAME_ACCESS))
{
printf ( "error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ());
goto out;
}
} else
local_handle = from_handle;
if (!GetKernelObjectSecurity (local_handle,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
sd, sizeof (sd_buf), &bytes_needed))
{
printf ( "error getting handle SD (%lu)\n", GetLastError ());
goto out;
}
MapGenericMask (&access, &access_mapping);
if (!AccessCheck (sd, from_process_token, access, &access_mapping,
&ps, &ps_len, &access, &status))
{
printf ( "error checking access rights (%lu)\n", GetLastError ());
goto out;
}
if (!status)
{
printf ( "access to object denied\n");
goto out;
}
if (!DuplicateHandle (from_process, from_handle,
to_process, to_handle_ptr,
access, bInheritHandle, 0))
{
printf ( "error getting handle to client (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
ret_val = 0;
out:
if (local_handle && from_process != GetCurrentProcess ())
CloseHandle (local_handle);
return (ret_val);
}
void
client_request::serve (transport_layer_base *conn, class process_cache *cache)
{
printf ("*****************************************\n"
"A call to the base client_request class has occured\n"
"This indicates a mismatch in a virtual function definition somewhere\n");
exit (1);
}
void
client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
{
HANDLE from_process_handle = NULL;
HANDLE to_process_handle = NULL;
HANDLE token_handle = NULL;
DWORD rc;
if (header.cb != sizeof (req))
{
header.error_code = EINVAL;
return;
}
debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid,
req.from_master, req.to_master,
req.pid);
debug_printf ("opening process %ld\n", req.master_pid);
from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
debug_printf ("opening process %ld\n", req.pid);
to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
if (!from_process_handle || !to_process_handle)
{
printf ("error opening process (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
debug_printf ("Impersonating client\n");
conn->impersonate_client ();
debug_printf ("about to open thread token\n");
rc = OpenThreadToken (GetCurrentThread (),
TOKEN_QUERY,
TRUE,
&token_handle);
debug_printf ("opened thread token, rc=%lu\n", rc);
conn->revert_to_self ();
if (!rc)
{
printf ("error opening thread token (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_READ,
req.from_master,
&req.from_master, TRUE) != 0)
{
printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
if (req.to_master)
{
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_WRITE,
req.to_master,
&req.to_master, TRUE) != 0)
{
printf ("error duplicating to_master handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
}
#if DEBUG
printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
req.from_master, req.to_master);
#endif
header.error_code = 0;
out:
if (from_process_handle)
CloseHandle (from_process_handle);
if (to_process_handle)
CloseHandle (to_process_handle);
if (token_handle)
CloseHandle (token_handle);
}
void
client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
{
if (header.cb != sizeof (version))
{
header.error_code = EINVAL;
return;
}
header.error_code = 0;
version.major = CYGWIN_SERVER_VERSION_MAJOR;
version.api = CYGWIN_SERVER_VERSION_API;
version.minor = CYGWIN_SERVER_VERSION_MINOR;
version.patch = CYGWIN_SERVER_VERSION_PATCH;
}
class server_request : public queue_request
{
public:
server_request (transport_layer_base *newconn, class process_cache *newcache);
virtual void process ();
private:
char request_buffer [MAX_REQUEST_SIZE];
transport_layer_base *conn;
class process_cache *cache;
};
class server_process_param : public queue_process_param
{
public:
transport_layer_base *transport;
server_process_param () : queue_process_param (false) {};
};
class server_request_queue : public threaded_queue
{
public:
class process_cache *cache;
void process_requests (transport_layer_base *transport);
virtual void add (transport_layer_base *conn);
};
class server_request_queue request_queue;
static DWORD WINAPI
request_loop (LPVOID LpParam)
{
class server_process_param *params = (server_process_param *) LpParam;
class server_request_queue *queue = (server_request_queue *) params->queue;
class transport_layer_base * transport = params->transport;
while (queue->active)
{
transport_layer_base * new_conn = transport->accept ();
/* FIXME: this is a little ugly. What we really want is to wait on two objects:
* one for the pipe/socket, and one for being told to shutdown. Otherwise
* this will stay a problem (we won't actually shutdown until the request
* _AFTER_ the shutdown request. And sending ourselves a request is ugly
*/
if (new_conn && queue->active)
queue->add (new_conn);
}
return 0;
}
/* TODO: check we are not being asked to service a already serviced transport */
void
server_request_queue::process_requests (transport_layer_base *transport)
{
class server_process_param *params = new server_process_param;
params->transport = transport;
threaded_queue::process_requests (params, request_loop);
}
void
client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
{
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
/* tell the main thread to shutdown */
request_queue.active=false;
}
server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
{
conn = newconn;
cache = newcache;
}
void
server_request::process ()
{
ssize_t bytes_read, bytes_written;
struct request_header* req_ptr = (struct request_header*) &request_buffer;
client_request *req = NULL;
debug_printf ("about to read\n");
bytes_read = conn->read (request_buffer, sizeof (struct request_header));
if (bytes_read != sizeof (struct request_header))
{
printf ("error reading from connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("got header (%ld)\n", bytes_read);
switch (req_ptr->req_id)
{
case CYGSERVER_REQUEST_GET_VERSION:
req = new client_request_get_version (); break;
case CYGSERVER_REQUEST_ATTACH_TTY:
req = new client_request_attach_tty (); break;
case CYGSERVER_REQUEST_SHUTDOWN:
req = new client_request_shutdown (); break;
case CYGSERVER_REQUEST_SHM_GET:
req = new client_request_shm (); break;
default:
req = new client_request (CYGSERVER_REQUEST_INVALID, 0);
req->header.error_code = ENOSYS;
debug_printf ("Bad client request - returning ENOSYS\n");
}
if (req->header.cb != req_ptr->cb)
{
debug_printf ("Mismatch in request buffer sizes\n");
goto out;
}
if (req->header.cb)
{
bytes_read = conn->read (req->buffer, req->header.cb);
if (bytes_read != req->header.cb)
{
debug_printf ("error reading from connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("got body (%ld)\n",bytes_read);
}
/* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
req->serve (conn, cache);
if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header)))
!= sizeof(req->header) || (req->header.cb &&
(bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb))
{
req->header.error_code = -1;
printf ("error writing to connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf("Sent reply, size (%ld)\n",bytes_written);
printf (".");
out:
conn->close ();
delete conn;
if (req)
delete (req);
}
void
server_request_queue::add (transport_layer_base *conn)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
/* every derived ::add must enter the section! */
EnterCriticalSection (&queuelock);
if (!running)
{
conn->close ();
delete conn;
LeaveCriticalSection (&queuelock);
return;
}
queue_request * listrequest = new server_request (conn, cache);
threaded_queue::add (listrequest);
LeaveCriticalSection (&queuelock);
}
void
handle_signal (int signal)
{
/* any signal makes us die :} */
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
/* tell the main thread to shutdown */
request_queue.active=false;
}
struct option longopts[] = {
{"shutdown", no_argument, NULL, 's'},
{0, no_argument, NULL, 0}
};
char opts[] = "s";
int
main (int argc, char **argv)
{
int shutdown=0;
char i;
while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
switch (i)
{
case 's':
shutdown = 1;
break;
default:
break;
/*NOTREACHED*/
}
wincap.init();
if (wincap.has_security ())
setup_privileges ();
transport = create_server_transport ();
if (shutdown)
{
if (!transport->connect())
{
printf ("couldn't establish connection with server\n");
exit (1);
}
client_request_shutdown *request =
new client_request_shutdown ();
request->send (transport);
transport->close();
delete transport;
delete request;
exit(0);
}
char version[200];
/* Cygwin dll release */
snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
cygwin_version.dll_major / 1000,
cygwin_version.dll_major % 1000,
cygwin_version.dll_minor,
cygwin_version.api_major,
cygwin_version.api_minor,
cygwin_version.shared_data,
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
cygwin_version.mount_registry,
cygwin_version.dll_build_date);
setbuf (stdout, NULL);
printf ("daemon version %s starting up", version);
if (signal (SIGQUIT, handle_signal) == SIG_ERR)
{
printf ("\ncould not install signal handler (%d)- aborting startup\n", errno);
exit (1);
}
printf (".");
transport->listen ();
printf (".");
class process_cache cache (2);
request_queue.initial_workers = 10;
request_queue.cache = &cache;
request_queue.create_workers ();
printf (".");
request_queue.process_requests (transport);
printf (".");
cache.create_workers ();
printf (".");
cache.process_requests ();
printf (".complete\n");
/* TODO: wait on multiple objects - the thread handle for each request loop +
* all the process handles. This should be done by querying the request_queue and
* the process cache for all their handles, and then waiting for (say) 30 seconds.
* after that we recreate the list of handles to wait on, and wait again.
* the point of all this abstraction is that we can trivially server both sockets
* and pipes simply by making a new transport, and then calling
* request_queue.process_requests (transport2);
*/
/* WaitForMultipleObjects abort && request_queue && process_queue && signal
-- if signal event then retrigger it
*/
while (1 && request_queue.active)
{
sleep (1);
}
printf ("\nShutdown request recieved - new requests will be denied\n");
request_queue.cleanup ();
printf ("All pending requests processed\n");
transport->close ();
printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
cache.cleanup ();
printf ("All outstanding process-cache activities completed\n");
printf ("daemon shutdown\n");
}

389
winsup/cygserver/process.cc Normal file
View File

@ -0,0 +1,389 @@
/* cygserver_process.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include <pthread.h>
#include <threaded_queue.h>
#include <cygwin/cygserver_process.h>
#define debug_printf if (DEBUG) printf
#define DEBUG 1
/* the cache structures and classes are designed for one cache per server process.
* To make multiple process caches, a redesign will be needed
*/
/* process cache */
process_cache::process_cache (unsigned int num_initial_workers):
head (NULL)
{
/* there can only be one */
InitializeCriticalSection (&cache_write_access);
if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create cache add trigger (%lu), terminating\n",
GetLastError ());
exit (1);
}
initial_workers = num_initial_workers;
}
process_cache::~process_cache ()
{
}
class process *
process_cache::process (long pid)
{
class process *entry = head;
/* TODO: make this more granular, so a search doesn't involve the write lock */
EnterCriticalSection (&cache_write_access);
if (!entry)
{
entry = new class process (pid);
entry->next =
(class process *) InterlockedExchangePointer (&head, entry);
PulseEvent (cache_add_trigger);
}
else
{
while (entry->winpid != pid && entry->next)
entry = entry->next;
if (entry->winpid != pid)
{
class process *new_entry = new class process (pid);
new_entry->next =
(class process *) InterlockedExchangePointer (&entry->next,
new_entry);
entry = new_entry;
PulseEvent (cache_add_trigger);
}
}
LeaveCriticalSection (&cache_write_access);
return entry;
}
static DWORD WINAPI
request_loop (LPVOID LpParam)
{
class process_process_param *params = (process_process_param *) LpParam;
return params->request_loop ();
}
void
process_cache::process_requests ()
{
class process_process_param *params = new process_process_param;
threaded_queue::process_requests (params, request_loop);
}
void
process_cache::add_task (class process * theprocess)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
/* every derived ::add must enter the section! */
EnterCriticalSection (&queuelock);
queue_request *listrequest = new process_cleanup (theprocess);
threaded_queue::add (listrequest);
LeaveCriticalSection (&queuelock);
}
/* NOT fully MT SAFE: must be called by only one thread in a program */
void
process_cache::remove_process (class process *theprocess)
{
class process *entry = head;
/* unlink */
EnterCriticalSection (&cache_write_access);
if (entry == theprocess)
{
entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
if (entry != theprocess)
{
printf ("Bug encountered, process cache corrupted\n");
exit (1);
}
}
else
{
while (entry->next && entry->next != theprocess)
entry = entry->next;
class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
if (temp != theprocess)
{
printf ("Bug encountered, process cache corrupted\n");
exit (1);
}
}
LeaveCriticalSection (&cache_write_access);
/* Process any cleanup tasks */
add_task (theprocess);
}
/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of
* begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0]
* NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed.
* Who cares - It'll get caught the next time.
*/
int
process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
ssize_t max_copy, int begin_at)
{
/* TODO:? grab a delete-lock, to prevent deletes during this process ? */
class process *entry = head;
int count = begin_at;
/* skip begin_at entries */
while (entry && count)
{
if (entry->exit_code () == STILL_ACTIVE)
count--;
entry = entry->next;
}
/* hit the end of the list within begin_at entries */
if (count)
return 0;
HANDLE *hto = hdest;
class process **eto = edest;
while (entry && count < max_copy)
{
/* hack */
if (entry->exit_code () == STILL_ACTIVE)
{
*hto = entry->handle ();
*eto = entry;
count++;
hto++;
eto++;
}
entry = entry->next;
}
return count;
}
/* process's */
/* global process crit section */
static CRITICAL_SECTION process_access;
static pthread_once_t process_init;
void
do_process_init (void)
{
InitializeCriticalSection (&process_access);
/* we don't have a cache shutdown capability today */
}
process::process (long pid):
winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
{
pthread_once (&process_init, do_process_init);
EnterCriticalSection (&process_access);
thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
if (!thehandle)
{
printf ("unable to obtain handle for new cache process %ld\n", pid);
thehandle = INVALID_HANDLE_VALUE;
}
debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
InitializeCriticalSection (&access);
LeaveCriticalSection (&process_access);
}
process::~process ()
{
DeleteCriticalSection (&access);
}
HANDLE
process::handle ()
{
// DWORD exitstate = exit_code ();
// if (exitstate == STILL_ACTIVE)
return thehandle;
/* FIXME: call the cleanup list ? */
// CloseHandle (thehandle);
// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
// winpid);
// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid);
// /* FIXME: what if OpenProcess fails ? */
// if (thehandle)
// {
// _exit_status = STILL_ACTIVE;
// exit_code ();
// }
// else
// thehandle = INVALID_HANDLE_VALUE;
// return thehandle;
}
DWORD process::exit_code ()
{
if (_exit_status != STILL_ACTIVE)
return _exit_status;
bool
err = GetExitCodeProcess (thehandle, &_exit_status);
if (!err)
{
debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
thehandle = INVALID_HANDLE_VALUE;
return _exit_status;
}
else if (_exit_status == STILL_ACTIVE)
return _exit_status;
/* add new cleanup task etc etc ? */
return _exit_status;
}
/* this is single threaded. It's called after the process is removed from the cache,
* but inserts may be attemped by worker threads that have a pointer to it */
void
process::cleanup ()
{
/* Serialize this */
EnterCriticalSection (&access);
InterlockedIncrement (&(long)cleaning_up);
class cleanup_routine *entry = head;
while (entry)
{
class cleanup_routine *temp;
entry->cleanup (winpid);
temp = entry->next;
delete entry;
entry = temp;
}
LeaveCriticalSection (&access);
}
bool
process::add_cleanup_routine (class cleanup_routine *new_cleanup)
{
if (cleaning_up)
return false;
EnterCriticalSection (&access);
/* check that we didn't block with ::cleanup ()
* This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call
* which would be a whole lot easier
*/
if (cleaning_up)
{
LeaveCriticalSection (&access);
return false;
}
new_cleanup->next = head;
head = new_cleanup;
LeaveCriticalSection (&access);
return true;
}
/* process_cleanup */
void
process_cleanup::process ()
{
theprocess->cleanup ();
delete theprocess;
}
/* process_process_param */
DWORD
process_process_param::request_loop ()
{
process_cache *cache = (process_cache *) queue;
/* always malloc one, so there is no special case in the loop */
ssize_t HandlesSize = 2;
HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize);
process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize);
/* TODO: put [1] at the end as it will also get done if a process dies? */
Handles[0] = interrupt;
Handles[1] = cache->cache_add_trigger;
while (cache->active && !shutdown)
{
int copied;
copied = -1;
int offset;
offset = 1;
int count;
count = 2;
while ((copied == HandlesSize - 2 - offset) || copied < 0)
{
/* we need more storage to cope with all the HANDLES */
if (copied == HandlesSize - 2 - offset)
{
HANDLE *temp = (HANDLE *) realloc (Handles,
sizeof (HANDLE) *
HandlesSize + 10);
if (!temp)
{
printf
("cannot allocate more storage for the handle array!\n");
exit (1);
}
Handles = temp;
process **ptemp = (process **) realloc (Entries,
sizeof (LPVOID) *
HandlesSize + 10);
if (!ptemp)
{
printf
("cannot allocate more storage for the handle array!\n");
exit (1);
}
Entries = ptemp;
HandlesSize += 10;
}
offset += copied;
copied =
cache->handle_snapshot (&Handles[2], &Entries[2],
HandlesSize - 2 - offset, offset);
count += copied;
}
debug_printf ("waiting on %u objects\n", count);
DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
if (rc == WAIT_FAILED)
{
printf ("Could not wait on the process handles (%ld)!\n",
GetLastError ());
exit (1);
}
int objindex = rc - WAIT_OBJECT_0;
if (objindex > 1 && objindex < count)
{
debug_printf ("Process %ld has left the building\n",
Entries[objindex]->winpid);
/* fire off the termination routines */
cache->remove_process (Entries[objindex]);
}
else if (objindex >= 0 && objindex < 2)
{
/* 0 is shutdown - do nothing */
/* 1 is a cache add event - just rebuild the object list */
}
else
{
printf
("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");
}
}
running = false;
return 0;
}

552
winsup/cygserver/shm.cc Normal file
View File

@ -0,0 +1,552 @@
/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
Copyright 2001 Red Hat, Inc.
Originally written by Robert Collins <robert.collins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __OUTSIDE_CYGWIN__
#undef __INSIDE_CYGWIN__
#else
#include "winsup.h"
#endif
#ifndef __INSIDE_CYGWIN__
#define DEBUG 0
#define system_printf printf
#define debug_printf if (DEBUG) printf
#define api_fatal printf
#include <stdio.h>
#include <windows.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "cygerrno.h"
#include <unistd.h>
#include "security.h"
//#include "fhandler.h"
//#include "dtable.h"
//#include "cygheap.h"
#include <stdio.h>
//#include "thread.h"
#ifndef __INSIDE_CYGWIN__
#define __INSIDE_CYGWIN__
#include <sys/shm.h>
#undef __INSIDE_CYGWIN__
#else
#include <sys/shm.h>
#endif
//#include "perprocess.h"
#include <threaded_queue.h>
#include <cygwin/cygserver_process.h>
#include "cygserver_shm.h"
// FIXME IS THIS CORRECT
/* Implementation notes: We use two shared memory regions per key:
* One for the control structure, and one for the shared memory.
* While this has a higher overhead tham a single shared area,
* It allows more flexability. As the entire code is transparent to the user
* We can merge these in the future should it be needed.
* Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just
* keep monotonically incrementing - system wide.
*/
size_t
getsystemallocgranularity ()
{
SYSTEM_INFO sysinfo;
static size_t buffer_offset = 0;
if (buffer_offset)
return buffer_offset;
GetSystemInfo (&sysinfo);
buffer_offset = sysinfo.dwAllocationGranularity;
return buffer_offset;
}
client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
{
buffer = (char *) &parameters;
}
/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
* Solution: manually calculate the next id value
*/
#if 0
extern "C" void *
shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
{
class shmid_ds *shm = (class shmid_ds *) shmid; //FIXME: verifyable object test
if (shmaddr)
{
//FIXME: requested base address ?!
set_errno (EINVAL);
return (void *) -1;
}
void *rv = MapViewOfFile (shm->attachmap,
(parameters.in.shmflg & SHM_RDONLY) ?
FILE_MAP_READ : FILE_MAP_WRITE, 0,
0, 0);
if (!rv)
{
//FIXME: translate GetLastError()
set_errno (EACCES);
return (void *) -1;
}
/* FIXME: this needs to be globally protected to prevent a mismatch betwen
* attach count and attachees list
*/
InterlockedIncrement (&shm->shm_nattch);
_shmattach *attachnode = new _shmattach;
attachnode->data = rv;
attachnode->next =
(_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead,
(long int) attachnode);
return rv;
}
#endif
/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
* choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
* appropriately
*/
/* Test result from openbsd: shm ids are persistent cross process if a handle is left
* open. This could lead to resource starvation: we're not copying that behaviour
* unless we have to. (It will involve acygwin1.dll gloal shared list :[ ).
*/
/* FIXME: shmid should be a verifyable object
*/
/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
*/
extern GENERIC_MAPPING access_mapping;
extern int
check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
HANDLE * to_handle_ptr, BOOL bInheritHandle);
//FIXME: where should this live
static shmnode *shm_head = NULL;
/* must be long for InterlockedIncrement */
static long new_id = 0;
static long new_private_key = 0;
void
client_request_shm::serve (transport_layer_base * conn,
process_cache * cache)
{
// DWORD sd_size = 4096;
// char sd_buf[4096];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf;
// /* create a sd for our open requests based on shmflag & 0x01ff */
// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
// parameters.in.shmflg & 0x01ff, psd, &sd_size);
HANDLE from_process_handle = NULL;
HANDLE token_handle = NULL;
DWORD rc;
from_process_handle = cache->process (parameters.in.pid)->handle ();
/* possible TODO: reduce the access on the handle before we use it */
/* Note that unless we do this, we don't need to call CloseHandle - it's kept open
* by the process cache until the process terminates.
* We may need a refcount on the cache however...
*/
if (!from_process_handle)
{
debug_printf ("error opening process (%lu)\n", GetLastError ());
header.error_code = EACCES;
return;
}
conn->impersonate_client ();
rc = OpenThreadToken (GetCurrentThread (),
TOKEN_QUERY, TRUE, &token_handle);
conn->revert_to_self ();
if (!rc)
{
debug_printf ("error opening thread token (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (from_process_handle);
return;
}
/* we trust the clients request - we will be doing it as them, and
* the worst they can do is open their own permissions
*/
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof (sa);
sa.lpSecurityDescriptor = psd;
sa.bInheritHandle = TRUE; /* the memory structures inherit ok */
char *shmname = NULL, *shmaname = NULL;
char stringbuf[29], stringbuf1[29];
/* TODO: make this code block a function! */
if (parameters.in.type == SHM_REATTACH)
{
/* just find and fill out the existing shm_id */
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->shm_id == parameters.in.shm_id)
{
parameters.out.shm_id = tempnode->shm_id;
parameters.out.key = tempnode->key;
if (check_and_dup_handle
(GetCurrentProcess (), from_process_handle, token_handle,
DUPLICATE_SAME_ACCESS, tempnode->filemap,
&parameters.out.filemap, TRUE) != 0)
{
debug_printf ("error duplicating filemap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
}
if (check_and_dup_handle
(GetCurrentProcess (), from_process_handle, token_handle,
DUPLICATE_SAME_ACCESS, tempnode->attachmap,
&parameters.out.attachmap, TRUE) != 0)
{
debug_printf ("error duplicating attachmap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
}
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* someone attached */
/* someone can send shm_id's they don't have and currently we will increment those
* attach counts. If someone wants to fix that, please go ahead.
* The problem is that shm_get has nothing to do with the ability to attach. Attach
* requires a permission check, which we get the OS to do in MapViewOfFile.
*/
if (parameters.in.type == SHM_ATTACH)
{
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->shm_id == parameters.in.shm_id)
{
InterlockedIncrement (&tempnode->shmds->shm_nattch);
header.error_code = 0;
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* it's a original request from the users */
/* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
* to prevent races on shmget.
*/
if (parameters.in.key == IPC_PRIVATE)
{
/* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */
/* The K refers to Key, the actual mapped area has D */
long private_key = (int) InterlockedIncrement (&new_private_key);
snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key);
shmname = stringbuf;
snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key);
shmaname = stringbuf1;
}
else
{
/* create the mapping name (CYGWINSHMK0x0123456789abcdef */
/* The K refers to Key, the actual mapped area has D */
snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key);
shmname = stringbuf;
snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key);
shmaname = stringbuf1;
debug_printf ("system id strings are \n%s\n%s\n", shmname, shmaname);
debug_printf ("key input value is 0x%0qx\n", parameters.in.key);
}
/* attempt to open the key */
/* get an existing key */
/* On unix the same shmid identifier is returned on multiple calls to shm_get
* with the same key and size. Different modes is a ?.
*/
/* walk the list of known keys and return the id if found. remember, we are
* authoritative...
*/
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->key == parameters.in.key
&& parameters.in.key != IPC_PRIVATE)
{
// FIXME: free the mutex
if (parameters.in.size
&& tempnode->shmds->shm_segsz < parameters.in.size)
{
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* FIXME: can the same process call this twice without error ? test
* on unix
*/
if ((parameters.in.shmflg & IPC_CREAT)
&& (parameters.in.shmflg & IPC_EXCL))
{
header.error_code = EEXIST;
debug_printf
("attempt to exclusively create already created shm_area with key 0x%0qx\n",
parameters.in.key);
// FIXME: free the mutex
CloseHandle (token_handle);
return;
}
// FIXME: do we need to other tests of the requested mode with the
// tempnode->shm_id mode ? testcase on unix needed.
// FIXME how do we do the security test? or
// do we wait for shmat to bother with that?
/* One possibly solution: impersonate the client, and then test we can
* reopen the area. In fact we'll probably have to do that to get
* handles back to them, alternatively just tell them the id, and then
* let them attempt the open.
*/
parameters.out.shm_id = tempnode->shm_id;
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->filemap,
&parameters.out.filemap, TRUE) != 0)
{
printf ("error duplicating filemap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
/*mutex*/
CloseHandle (token_handle);
return;
}
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->attachmap,
&parameters.out.attachmap, TRUE) != 0)
{
printf ("error duplicating attachmap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
/*mutex*/
CloseHandle (token_handle);
return;
}
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
/* couldn't find a currently open shm area. */
/* create one */
/* do this as the client */
conn->impersonate_client ();
/* This may need sh_none... it's only a control structure */
HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile.
&sa,
PAGE_READWRITE, // protection
0x00000000,
getsystemallocgranularity (),
shmname // object name
);
int lasterr = GetLastError ();
conn->revert_to_self ();
if (filemap == NULL)
{
/* We failed to open the filemapping ? */
system_printf ("failed to open file mapping: %lu\n", GetLastError ());
// free the mutex
// we can assume that it exists, and that it was an access problem.
header.error_code = EACCES;
CloseHandle (token_handle);
return;
}
/* successfully opened the control region mapping */
/* did we create it ? */
int oldmapping = lasterr == ERROR_ALREADY_EXISTS;
if (oldmapping)
{
/* should never happen - we are the global daemon! */
#if 0
if ((parameters.in.shmflg & IPC_CREAT)
&& (parameters.in.shmflg & IPC_EXCL))
#endif
{
/* FIXME free mutex */
CloseHandle (filemap);
header.error_code = EEXIST;
CloseHandle (token_handle);
return;
}
}
/* we created a new mapping */
if (parameters.in.key != IPC_PRIVATE &&
(parameters.in.shmflg & IPC_CREAT) == 0)
{
CloseHandle (filemap);
/* FIXME free mutex */
header.error_code = ENOENT;
CloseHandle (token_handle);
return;
}
conn->impersonate_client ();
void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
conn->revert_to_self ();
if (!mapptr)
{
CloseHandle (filemap);
//FIXME: close filemap and free the mutex
/* we couldn't access the mapped area with the requested permissions */
header.error_code = EACCES;
CloseHandle (token_handle);
return;
}
conn->impersonate_client ();
/* Now get the user data */
HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile
&sa,
PAGE_READWRITE, // protection (FIXME)
0x00000000,
parameters.in.size +
parameters.in.size %
getsystemallocgranularity (),
shmaname // object name
);
conn->revert_to_self ();
if (attachmap == NULL)
{
system_printf ("failed to get shm attachmap\n");
header.error_code = ENOMEM;
UnmapViewOfFile (mapptr);
CloseHandle (filemap);
/* FIXME exit the mutex */
CloseHandle (token_handle);
return;
}
shmid_ds *shmtemp = new shmid_ds;
if (!shmtemp)
{
system_printf ("failed to malloc shm node\n");
header.error_code = ENOMEM;
UnmapViewOfFile (mapptr);
CloseHandle (filemap);
CloseHandle (attachmap);
/* FIXME exit mutex */
CloseHandle (token_handle);
return;
}
/* fill out the node data */
shmtemp->shm_perm.cuid = getuid ();
shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
shmtemp->shm_perm.cgid = getgid ();
shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff;
shmtemp->shm_lpid = 0;
shmtemp->shm_nattch = 0;
shmtemp->shm_atime = 0;
shmtemp->shm_dtime = 0;
shmtemp->shm_ctime = time (NULL);
shmtemp->shm_segsz = parameters.in.size;
*(shmid_ds *) mapptr = *shmtemp;
shmtemp->mapptr = mapptr;
/* no need for InterlockedExchange here, we're serialised by the global mutex */
tempnode = new shmnode;
tempnode->shmds = shmtemp;
tempnode->shm_id = (int) InterlockedIncrement (&new_id);
tempnode->key = parameters.in.key;
tempnode->filemap = filemap;
tempnode->attachmap = attachmap;
tempnode->next = shm_head;
shm_head = tempnode;
/* we now have the area in the daemon list, opened.
FIXME: leave the system wide shm mutex */
parameters.out.shm_id = tempnode->shm_id;
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->filemap, &parameters.out.filemap,
TRUE) != 0)
{
printf ("error duplicating filemap handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (token_handle);
/* mutex et al */
return;
}
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->attachmap, &parameters.out.attachmap,
TRUE) != 0)
{
printf ("error duplicating attachmap handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (from_process_handle);
CloseHandle (token_handle);
/* more cleanup... yay! */
return;
}
CloseHandle (token_handle);
return;
}

91
winsup/cygserver/shm.h Executable file
View File

@ -0,0 +1,91 @@
/* cygserver_shm.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <sys/types.h>
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#define SHM_CREATE 0
#define SHM_REATTACH 1
#define SHM_ATTACH 2
#define SHM_DETACH 3
class client_request_shm : public client_request
{
public:
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, process_cache *cache);
#endif
client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t);
client_request_shm ();
client_request_shm (int, int, pid_t);
client_request_shm (int, int);
union {
struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in;
struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out;
} parameters;
};
#ifndef __INSIDE_CYGWIN__
class shm_cleanup : cleanup_routine
{
public:
virtual void cleanup (long winpid);
};
#endif
#if 0
class _shmattach {
public:
void *data;
class _shmattach *next;
};
class shmid_ds {
public:
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
HANDLE filemap;
HANDLE attachmap;
void *mapptr;
class _shmattach *attachhead;
};
class shmnode {
public:
class shmid_ds * shmid;
class shmnode *next;
key_t key;
};
//....
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
};
void *shmat(int, const void *, int);
int shmctl(int, int, struct shmid_ds *);
int shmdt(const void *);
int shmget(key_t, size_t, int);
#endif

View File

@ -0,0 +1,249 @@
/* threaded_queue.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include "wincap.h"
#include "threaded_queue.h"
#define DEBUG 1
#define debug_printf if (DEBUG) printf
/* threaded_queue */
DWORD WINAPI
worker_function (LPVOID LpParam)
{
class threaded_queue *queue = (class threaded_queue *) LpParam;
class queue_request *request;
/* FIXME use a threadsafe pop instead for speed? */
while (queue->active)
{
EnterCriticalSection (&queue->queuelock);
while (!queue->request && queue->active)
{
LeaveCriticalSection (&queue->queuelock);
DWORD rc = WaitForSingleObject (queue->event, INFINITE);
if (rc == WAIT_FAILED)
{
printf ("Wait for event failed\n");
queue->running--;
ExitThread (0);
}
EnterCriticalSection (&queue->queuelock);
}
if (!queue->active)
{
queue->running--;
LeaveCriticalSection (&queue->queuelock);
ExitThread (0);
}
/* not needed, but it is efficient */
request =
(class queue_request *) InterlockedExchangePointer (&queue->request,
queue->request->
next);
LeaveCriticalSection (&queue->queuelock);
request->process ();
delete request;
}
queue->running--;
ExitThread (0);
}
void
threaded_queue::create_workers ()
{
InitializeCriticalSection (&queuelock);
if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create event queue (%lu), terminating\n",
GetLastError ());
exit (1);
}
active = true;
/* FIXME: Use a stack pair and create threads on the fly whenever
* we have to to service a request.
*/
for (unsigned int i = 0; i < initial_workers; i++)
{
HANDLE hThread;
DWORD tid;
hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
if (hThread == NULL)
{
printf ("Failed to create thread (%lu), terminating\n",
GetLastError ());
exit (1);
}
CloseHandle (hThread);
running++;
}
}
void
threaded_queue::cleanup ()
{
/* harvest the threads */
active = false;
/* kill the request processing loops */
queue_process_param *reqloop;
/* make sure we don't race with a incoming request creation */
EnterCriticalSection (&queuelock);
reqloop =
(queue_process_param *) InterlockedExchangePointer (&process_head, NULL);
while (reqloop)
{
queue_process_param *t = reqloop;
reqloop = reqloop->next;
delete t;
}
LeaveCriticalSection (&queuelock);
if (!running)
return;
printf ("Waiting for current queue threads to terminate\n");
for (int n = running; n; n--)
PulseEvent (event);
while (running)
sleep (1);
DeleteCriticalSection (&queuelock);
CloseHandle (event);
}
/* FIXME: return success or failure */
void
threaded_queue::add (queue_request * therequest)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
EnterCriticalSection (&queuelock);
if (!running)
{
printf ("No worker threads to handle request!\n");
}
if (!request)
request = therequest;
else
{
/* add to the queue end. */
queue_request *listrequest = request;
while (listrequest->next)
listrequest = listrequest->next;
listrequest->next = therequest;
}
PulseEvent (event);
LeaveCriticalSection (&queuelock);
}
/* FIXME: return success or failure rather than quitting */
void
threaded_queue::process_requests (queue_process_param * params,
threaded_queue_thread_function *
request_loop)
{
if (params->start (request_loop, this) == false)
exit (1);
params->next =
(queue_process_param *) InterlockedExchangePointer (&process_head,
params);
}
/* queue_process_param */
/* How does a constructor return an error? */
queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
interruptible
(ninterruptible)
{
if (!interruptible)
return;
debug_printf ("creating an interruptible processing thread\n");
if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create interrupt event (%lu), terminating\n",
GetLastError ());
exit (1);
}
}
queue_process_param::~queue_process_param ()
{
if (running)
stop ();
if (!interruptible)
return;
CloseHandle (interrupt);
}
bool
queue_process_param::start (threaded_queue_thread_function * request_loop,
threaded_queue * thequeue)
{
queue = thequeue;
hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
if (hThread)
{
running = true;
return true;
}
printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
return false;
}
void
queue_process_param::stop ()
{
if (interruptible)
{
InterlockedExchange (&shutdown, true);
PulseEvent (interrupt);
/* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get
* scheduled again, we print an error and exit. We _should_ loop or
* try resignalling. We don't want to hand here though...
*/
int n = 5;
while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT);
if (!n)
{
printf ("Process thread didn't shutdown cleanly after 200ms!\n");
exit (1);
}
else
running = false;
}
else
{
printf ("killing request loop thread %ld\n", tid);
int rc;
if (!(rc = TerminateThread (hThread, 0)))
{
printf ("error shutting down request loop worker thread\n");
}
running = false;
}
CloseHandle (hThread);
}
/* queue_request */
queue_request::queue_request ():next (NULL)
{
}
void
queue_request::process (void)
{
printf ("\n**********************************************\n"
"Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
"fault !!!\n" "***********************************************\n");
}

View File

@ -0,0 +1,92 @@
/* cygserver_transport.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
#else
#define debug_printf printf
#endif
/* The factory */
class transport_layer_base *create_server_transport()
{
transport_layer_base *temp;
/* currently there is only the base class! */
if (wincap.is_winnt ())
temp = new transport_layer_pipes ();
else
temp = new transport_layer_base ();
return temp;
}
transport_layer_base::transport_layer_base ()
{
/* should we throw an error of some sort ? */
}
void
transport_layer_base::listen ()
{
}
class transport_layer_base *
transport_layer_base::accept ()
{
return NULL;
}
void
transport_layer_base::close()
{
}
ssize_t
transport_layer_base::read (char *buf, size_t len)
{
return 0;
}
ssize_t
transport_layer_base::write (char *buf, size_t len)
{
return 0;
}
bool
transport_layer_base::connect ()
{
return false;
}
void
transport_layer_base::impersonate_client ()
{
}
void
transport_layer_base::revert_to_self ()
{
}

View File

@ -0,0 +1,205 @@
/* cygserver_transport_pipes.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
#else
#define DEBUG 0
#define debug_printf if (DEBUG) printf
#endif
transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
{
pipe = new_pipe;
if (inited != true)
init_security();
};
transport_layer_pipes::transport_layer_pipes ()
{
pipe = NULL;
strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
if (inited != true)
init_security();
}
void
transport_layer_pipes::init_security()
{
/* FIXME: pthread_once or equivalent needed */
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE);
sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
sec_none_nih.lpSecurityDescriptor = NULL;
sec_all_nih.lpSecurityDescriptor = &sd;
inited = true;
}
void
transport_layer_pipes::listen ()
{
/* no-op */
}
class transport_layer_pipes *
transport_layer_pipes::accept ()
{
if (pipe)
{
debug_printf ("Already have a pipe in this %p\n",this);
return NULL;
}
pipe = CreateNamedPipe (pipe_name,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0, 0, 1000,
&sec_all_nih );
if (pipe == INVALID_HANDLE_VALUE)
{
debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
return NULL;
}
if ( !ConnectNamedPipe ( pipe, NULL ) &&
GetLastError () != ERROR_PIPE_CONNECTED)
{
printf ("error connecting to pipe (%lu)\n.", GetLastError ());
CloseHandle (pipe);
pipe = NULL;
return NULL;
}
transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
pipe = NULL;
return new_conn;
}
void
transport_layer_pipes::close()
{
debug_printf ("closing pipe %p\n", pipe);
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
FlushFileBuffers (pipe);
DisconnectNamedPipe (pipe);
CloseHandle (pipe);
}
}
ssize_t
transport_layer_pipes::read (char *buf, size_t len)
{
debug_printf ("reading from pipe %p\n", pipe);
if (!pipe || pipe == INVALID_HANDLE_VALUE)
return -1;
DWORD bytes_read;
DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
if (!rc)
{
debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
return -1;
}
return bytes_read;
}
ssize_t
transport_layer_pipes::write (char *buf, size_t len)
{
debug_printf ("writing to pipe %p\n", pipe);
DWORD bytes_written, rc;
if (!pipe || pipe == INVALID_HANDLE_VALUE)
return -1;
rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
if (!rc)
{
debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
return -1;
}
return bytes_written;
}
bool
transport_layer_pipes::connect ()
{
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
debug_printf ("Already have a pipe in this %p\n",this);
return false;
}
while (1)
{
pipe = CreateFile (pipe_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec_all_nih,
OPEN_EXISTING,
0, NULL);
if (pipe != INVALID_HANDLE_VALUE)
/* got the pipe */
return true;
if (GetLastError () != ERROR_PIPE_BUSY)
{
debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
pipe = NULL;
return false;
}
if (!WaitNamedPipe (pipe_name, 20000))
debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () );
/* We loop here, because the pipe exists but is busy. If it doesn't exist
* the != ERROR_PIPE_BUSY will catch it.
*/
}
}
void
transport_layer_pipes::impersonate_client ()
{
debug_printf ("impersonating pipe %p\n", pipe);
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
BOOL rv = ImpersonateNamedPipeClient (pipe);
if (!rv)
debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
}
debug_printf("I am who you are\n");
}
void
transport_layer_pipes::revert_to_self ()
{
RevertToSelf ();
debug_printf("I am who I yam\n");
}

View File

@ -0,0 +1,131 @@
/* cygserver_transport_sockets.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
extern "C" int
cygwin_socket (int af, int type, int protocol);
extern "C" int
cygwin_connect (int fd,
const struct sockaddr *name,
int namelen);
extern "C" int
cygwin_accept (int fd, struct sockaddr *peer, int *len);
extern "C" int
cygwin_listen (int fd, int backlog);
extern "C" int
cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen);
#else
#define cygwin_accept(A,B,C) ::accept(A,B,C)
#define cygwin_socket(A,B,C) ::socket(A,B,C)
#define cygwin_listen(A,B) ::listen(A,B)
#define cygwin_bind(A,B,C) ::bind(A,B,C)
#define cygwin_connect(A,B,C) ::connect(A,B,C)
#define debug_printf printf
#endif
transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
{
/* This may not be needed in this constructor - it's only used
* when creating a connection via bind or connect
*/
sockdetails.sa_family = AF_UNIX;
strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
};
transport_layer_sockets::transport_layer_sockets (): fd (-1)
{
sockdetails.sa_family = AF_UNIX;
strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
}
void
transport_layer_sockets::listen ()
{
/* we want a thread pool based approach. */
if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0)
printf ("Socket not created error %d\n", errno);
if (cygwin_bind(fd, &sockdetails, sdlen))
printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno);
if (cygwin_listen(fd, 5) < 0)
printf ("And the OS just isn't listening, all it says is %d\n", errno);
}
class transport_layer_sockets *
transport_layer_sockets::accept ()
{
/* FIXME: check we have listened */
int new_fd;
if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
{
printf ("Nup, could' accept. %d\n",errno);
return NULL;
}
transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
return new_conn;
}
void
transport_layer_sockets::close()
{
/* FIXME - are we open? */
::close (fd);
}
ssize_t
transport_layer_sockets::read (char *buf, size_t len)
{
/* FIXME: are we open? */
return ::read (fd, buf, len);
}
ssize_t
transport_layer_sockets::write (char *buf, size_t len)
{
/* FIXME: are we open? */
return ::write (fd, buf, len);
}
bool
transport_layer_sockets::connect ()
{
/* are we already connected? */
if (fd != -1)
return false;
fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
if (cygwin_connect (fd, &sockdetails, sdlen) < 0)
{
debug_printf("client connect failure %d\n", errno);
::close (fd);
return false;
}
return true;
}

View File

@ -1,3 +1,294 @@
2002-02-28 Robert Collins <rbtcollins@hotmail.com>
* Merged cygwin_daemon into head minus the new shm and ipc exports.
2002-02-28 Robert Collins <rbtcollins@hotmail.com>
* fhandler_tty.cc (fhandler_tty_slave::open): More debugging.
(fhandler_tty_slave::read): Fix printf type for the handle.
* tty.cc (tty::common_init): Add a FIXME for security.
2002-01-29 Robert Collins <rbtcollins@hotmail.com>
* Makefile.in (OBJS): Remove duplicate localtime.o.
2002-01-17 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (check_and_dup_handle): Consolidate the two variants for
simplicity.
Add Some basic debug output.
(client_request_attach_tty::serve): Use the new debug_printf for clarity.
Mark the duplicated handles as inheritable - fixup_after_fork() doesn't reopen
tty's.
2002-01-16 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (transport): Correct scope.
(client_request_attach_tty::serve): Add more debug information.
Fix erroneous use of transport instead of conn.
* cygserver_transport_pipes.cc (transport_layer_pipes::close): More debug.
(transport_layer_pipes::read): Ditto.
(transport_layer_pipes::write): Ditto.
(transport_layer_pipes::impersonate_client): Ditto.
Mon Oct 8 7:41:00 2001 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (server_request::process): Rename client_request_shm_get to
client_request_shm.
* cygserver_process.cc (process_cache::add): Rename to add_task.
Use process_cleanup instead of process_request.
(process_cache::remove_process): New method.
(process::process): Initialize new members.
(process::~process): New member.
(process::cleanup): New method.
(process::add_cleanup_routine): New method.
(process_request::process): Rename to process_cleanup.
Call the process object's cleanup method and then delete it.
(process_process_param::request_loop): Remove the signalling process.
* cygserver_shm.cc: Globally rename client_request_shm_get to client_request_shm.
(client_request_shm_get::serve): Handle attach request counting.
* cygserver_shm.h: Globally rename client_request_shm_get to client_request_shm.
(class shm_cleanup): New class.
* shm.cc: Globally rename client_request_shm_get to client_request_shm.
(client_request_shm::client_request_shm): New constructor for attach requests.
(shmat): Use it.
* include/cygwin/cygserver_process.h (class process_request): Rename to
process_cleanup.
(class cleanup_routine): New class.
(class process): New members and methods to allow calling back when the process
terminates.
Thu Oct 4 14:12:00 2001 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (request_loop): Make static.
(main): Use new cache constructor syntax.
Start cache worker threads.
Cleanup the cache at shutdown.
* cygserver_process.cc: Run indent.
(process_cache::process_cache): Add a trigger to use when adding a process.
(process_cache::process): Move process_entry to process.
Insert at the end of the list.
Trigger the request loop when new process's inserted.
(process_cache::process_requests): Do it.
(process_cache::add): New method.
(process_cache::handle_snapshot): New method.
(process::process): Merge in the process_entry fields.
(process::handle): Make a stub function.
(process::exit_code): New method.
(process_request::process): New method.
(process_process_param::request_loop): New method.
* cygserver_shm.cc: New header dependency - threaded_queue.h.
* threaded_queue.cc (threaded_queue::cleanup): Clearer messages.
(queue_process_param::stop): Short spinlock on interruptible threads.
* threaded_queue.h (class threaded_queue): New constructor.
* include/cygwin/cygserver_process.h (process_request): New class.
(process_entry): Remove.
(process): Merge in process_entry.
(process_cache): Inherit from threaded_queue.
Tue Oct 2 23:24:00 2001 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (class server_process_param): Use new constructor syntax.
* cygserver_process.cc (process_cache::~process_cache): New function.
* threaded_queue.cc: Define condition debug_printf.
Run indent.
(threaded_queue::cleanup): Move queue_process_param guts to a method.
(threaded_queue::process_requests): Ditto.
(queue_process_param::queue_process_param): New method.
(queue_process_param::~queue_process_param): Ditto.
(queue_process_param::start): Ditto.
(queue_process_param::stop): Ditto.
* threaded_queue.h (class queue_process_param): Add support for
interruptible request loops.
* cygwin/include/cygwin/cygserver_process.h (class process_cache): Add
destructor.
Tue Oct 2 23:24:00 2001 Robert Collins <rbtcollins@hotmail.com>
* cygserver_client.cc: New flag allow_daemon to disable the daemon completely.
(cygserver_request): Check it.
(cygserver_init): Ditto.
* environ.cc (parse_thing): Add (no)daemon option.
Tue Oct 2 23:00:00 2001 Robert Collins <rbtcollins@hotmail.com>
* shm.cc: Update to handle include changes from HEAD.
Tue Oct 2 16:06:00 2001 Robert Collins <rbtcollins@hotmail.com>
* Makefile.in: Remove cygserver_shm.o from cygwin1.dll.
Rename cygserver_shm_outside.o to cygserver_shm.o.
* cygserver.cc (server_request::process): Use the new client_request
constructor.
* cygserver_client.cc: Remove the #ifdef's stubs for the server method
within cygwin.
(client_request_attach_tty::client_request_attach_tty): Use the new
client_request constructor.
(client_request_shutdown::client_request_shutdown): Ditto.
(client_request::client_request): Ditto.
* cygserver_shm.cc (client_request_shm_get::serve): Remove the
#ifdef'd stub for in-cygwin builds.
(client_request_shm_get::client_request_shm_get): Use the new
client_request constructor, and remove the in-cygwin variants.
* cygserver_shm.h (class client_request_shm_get): #ifndef test the
serve method - it's only used in cygserver.
* shm.cc (client_request_shm_get::client_request_shm_get): New function.
* include/cygwin/cygserver.h (request_header): New constructor.
(class client_request): Use it.
New constructor accepting the header size.
#ifndef test the server method - it's only used within cygserver.
(client_request_get_version): #ifdef test the server method.
(client_request_shutdown): Ditto.
(client_request_attach_tty): Ditto.
Tue Oct 2 9:57:00 2001 Robert Collins <rbtcollins@hotmail.com>
* Makefile.in: add threaded_queue.o to cygserver.exe.
* cygserver.cc: Include threaded_queue.h
(class server_request): Inherit from queue_request.
(class server_process_param): Inherit from queue_process_param.
(class server_request_queue): Inherit from threaded_queue.
(request_loop): Adjust for new types.
(server_request_queue::process_requests): Remove guts to
threaded_queue::process_requests.
(server_request::server_request): Adjust for new types.
(worker_function): Delete.
(server_request_queue::create_workers): Delete.
(server_request_queue::cleanup): Delete.
(server_request_queue::add): Move guts to threaded_queue::add.
* threaded_queue.cc: New file.
* threaded_queue.h: New file.
Mon Oct 1 12:38:00 2001 Robert Collins <rbtcollins@hotmail.com>
* cygserver.cc (client_request::serve): New function.
* cygserver_process.cc: Inlude <pthread.h> for pthread_once.
(process_cache::process_cache): Initialise a crtiical section for write access.
(process_cache::process): Use the critical section. Also add missing entries to
the cache.
(do_process_init): New function to initalise class process static variables.
(process::process): Ensure that the process access critical section is
initialised.
(process::handle): Close the handle of old process's when they have terminated
and we are returning the handle for a process with the same pid.
* cygserver_shm.cc: Run indent.
Include cygserver_process.h to allow process cache functionality.
(client_request_shm_get::serve): New parameter for process cache support.
Use the process cache, not OpenProcess to get a handle to the originating process.
Fix a handle leak with token_handle.
* cygserver_shm.h (class client_request_shm_get): Update ::serve for process
cache support.
* cygserver_transport_pipes.cc: Redefine debug_printf to be conditional on DEBUG.
* include/cygwin/cygserver.h: Do not implement client_request::serve in the
header.
* include/cygwin/cygserver_process.h (class process_cache): Add a write access
critical section to prevent races when requests from a multithreaded
application arrive.
Sun Sep 30 23:41:00 2001 Robert Collins <rbtcollins@hotmail.com>
* Makefile.in: Add cygserver_process.o to cygserver.exe.
* cygserver.cc: Include signal.h and cygwin_version.h.
Define debug_printf as a macro.
Define DEBUG to a value.
(client_request_attach_tty::serve): Add beginning of process cache support.
Change from #ifdef DEBUG to work with new DEBUG style.
(client_request_get_version::serve): Add beginning of process cache support.
(class server_request): New prototype for support of process cache.
(class queue_process_param): New class to allow request loop threading.
(class server_request_queue): Add beginning of process cache support.
Allow request loop threading.
(request_loop): Thread function for request loops.
(server_request_queue::process_requests): Initiator for threaded request loops.
(client_request_shutdown::serve): Add beginning of process cache support.
(server_request::server_request): Ditto.
(server_request::process): Use debug_printf. Add beginning of process cache
support.
(server_request_queue::cleanup): Kill off any request loop threads.
(server_request_queue::add): Add beginning of process cache support.
(handle_signal): Trigger a shutdown.
(main): Print out some useful info at startup - version, date time.
Add process cache support.
Spawn a separate thread for the transport request loop, thus allowing concurrent
support for multiple transports.
* cygserver_client.cc (client_request_get_version::serve): Add process cache
support.
(client_request_attach_tty::serve): Add process cache support.
(client_request_shutdown::serve): Add process cache support.
* cygsserver_process.cc: New file with the process cache support.
* cygserver_shm.cc: Redefine debug_printf to allow conditional output.
* cygwin.din: Export shmdt().
* shm.cc: Run indent.
Update FIXME's.
(shmdt): New function.
* include/cygwin/cygserver.h (class client_request): Add process cache support.
(class client_request_get_version): Ditto.
(class client_request_shutdown): Ditto.
(class client_request_attach_tty): Ditto.
* include/cygwin/cygserver_process.h: New header for process cache support.
Sun Sep 30 8:52:00 2001 Robert Collins <rbtcollins@hotmail.com>
* include/cygwin/cygserver_transport.h: Add copyright header.
* include/cygwin/cygserver_transport_pipes.h: Ditto.
* include/cygwin/cygserver_transport_sockets.h: Ditto.
Sat Sep 29 20:40:00 2001 Robert Collins <rbtcollins@hotmail.com>
* Makefile.in: Add cygserver_transport_sockets.o to DLL_OFILES.
Add cygserver_transport_sockets_outside.o to cygserver.exe.
* cygserver.cc: Include new include files.
* cygserver_client.cc: Ditto.
* cygserver_shm.h: No need to include <sys/socket.h> now.
* cygerver_transport.cc: Include new include files.
(transport_layer_base::transport_layer_base): Strip back to a stub.
(transport_layer_base::listen): Ditto.
(transport_layer_base::accept): Ditto.
(transport_layer_base::close): Ditto.
(transport_layer_base::read): Ditto.
(transport_layer_base::write): Ditto.
(transport_layer_base::connect): Ditto.
* cygserver_transport_pipes.cc: Include new header
"cygwin/cygserver_transport_pipes.h".
* cygserver_transport_sockets.cc: New file.
* dcrt0.cc: No need to include <sys/socket.h> now.
* fhandler_tty.cc: Ditto.
* tty.cc: Ditto.
* include/cygwin/cygserver_transport.h: Strip the base class to a stub.
Remove the cygserver_transport_pipes class.
* include/cygwin/cygserver_transport_pipes.h: New file.
* include/cygwin/cygserver_transport_sockets.h: New file.
Tue Sep 25 16:22:00 2001 Robert Collins <rbtcollins@hotmail.com>
* autoload.cc: Add dynamic load statement for 'ImpersonateNamedPipeClient'.
* Makefile.in: Add new object files, and build instructions for cygserver.exe.
* cygwin.din: Export ftok, shmat, shmctl and shmget.
* dcrt0.cc: Additional includes for cygserver support.
(dll_crt0_1): Initialise the cygserver client.
* fhandler.h (fhandler_tty): New method cygserver_attach_tty.
* fhandler_tty.cc: Additional includes for cygserver support.
(fhandler_tty_slave::open): Attempt to use the cygserver when obtaining
handles from the parent process. On failure or 9x use the current method.
(fhandler_tty_slave::cygserver_attach_tty): New function.
* fork.cc (fork_child): Fixup shm memory mapped areas.
* pinfo.h: Declare fixup_shms_after_fork().
* security.h: Declare alloc_sd().
* tty.cc: Additonal includes to support cygserver.
(tty::common_init): Don't allow others to open us if the cygserver is running.
* winsup.h: Declare cygserver_running.
* cygserver.cc: New file.
* cygserver_client.cc: New file.
* cygserver_shm.cc: New file.
* cygserver_shm.h: New file.
* cygserver_transport.cc: New file.
* cygserver_transport_pipes.cc: New file.
* ipc.cc: New file.
* shm.cc: New file.
* include/cygwin/cygserver.h: New file.
* include/cygwin/cygserver_transport.h: New file.
* include/sys/ipc.h: New file.
* include/sys/shm.h: New file.
2002-02-28 Robert Collins <rbtcollins@hotmail.com>
* thread.cc (semaphore::TryWait): Set errno as required by posix 1003.1.
@ -554,10 +845,9 @@
2002-01-01 Christopher Faylor <cgf@redhat.com>
* speclib: Remove temp files automatically.
* speclib: Remove temp files automatically.
2002-01-01 Corinna Vinschen <corinna@vinschen.de>
* fhandler.h (fhandler_socket::sun_path): New private member.
(fhandler_socket::set_sun_path): New method.
(fhandler_socket::get_sun_path): Ditto.

View File

@ -118,17 +118,18 @@ MALLOC_OFILES=@MALLOC_OFILES@
DLL_IMPORTS:=$(w32api_lib)/libkernel32.a
# Please maintain this list in sorted order, with maximum files per line
DLL_OFILES:=assert.o autoload.o cygheap.o dcrt0.o debug.o delqueue.o dir.o \
dlfcn.o dll_init.o dtable.o environ.o errno.o exceptions.o exec.o \
external.o fcntl.o fhandler.o fhandler_clipboard.o fhandler_console.o \
DLL_OFILES:=assert.o autoload.o cygheap.o cygserver_client.o cygserver_transport.o \
cygserver_transport_pipes.o cygserver_transport_sockets.o dcrt0.o debug.o \
delqueue.o dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o exceptions.o \
exec.o external.o fcntl.o fhandler.o fhandler_clipboard.o fhandler_console.o \
fhandler_disk_file.o fhandler_dsp.o fhandler_floppy.o fhandler_mem.o \
fhandler_random.o fhandler_raw.o fhandler_serial.o fhandler_socket.o \
fhandler_tape.o fhandler_termios.o fhandler_tty.o fhandler_windows.o \
fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o ioctl.o \
fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o ioctl.o ipc.o \
localtime.o malloc.o miscfuncs.o mmap.o net.o ntea.o passwd.o path.o \
pinfo.o pipe.o poll.o pthread.o regcomp.o regerror.o regexec.o \
regfree.o registry.o resource.o scandir.o sched.o sec_acl.o \
sec_helper.o security.o select.o shared.o shortcut.o signal.o \
sec_helper.o security.o select.o shared.o shm.o shortcut.o signal.o \
sigproc.o smallprint.o spawn.o strace.o strsep.o sync.o syscalls.o \
sysconf.o syslog.o termios.o thread.o times.o tty.o uinfo.o uname.o \
v8_regexp.o v8_regerror.o v8_regsub.o wait.o wincap.o window.o \
@ -148,7 +149,7 @@ EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a
INSTOBJS:=automode.o binmode.o textmode.o
TARGET_LIBS:=$(LIB_NAME) $(SUBLIBS) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS)
.PHONY: all force dll_ofiles install all_target install_target all_host install_host \
install install_target install-libs install-headers
install install-libs install-headers
.SUFFIXES:
.SUFFIXES: .c .cc .def .a .o .d
@ -158,13 +159,14 @@ install_host=@install_host@
all: all_target $(all_host)
all_target: $(TARGET_LIBS)
all_target: $(TARGET_LIBS) cygserver.exe
all_host: new-$(LIB_NAME) cygrun.exe
force:
install: install-libs install-headers install-man $(install_host) $(install_target)
install: install-bin install-libs install-headers install-man install_target \
$(install_host) $(install_target)
uninstall: uninstall-libs uninstall-headers uninstall-man
@ -198,6 +200,9 @@ install-man:
$(INSTALL_DATA) $$i $(tooldir)/man/man7/`basename $$i` ; \
done
install_target: cygserver.exe
$(INSTALL_PROGRAM) cygserver.exe $(bindir)/cygserver.exe
install_host:
@ -301,6 +306,32 @@ cygrun.exe : cygrun.o $(LIB_NAME) $(w32api_lib)/libuser32.a \
$(w32api_lib)/libshell32.a $(w32api_lib)/libkernel32.a
$(CC) -nodefaultlibs -o $@ $^
cygserver_transport_outside.o: cygserver_transport.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
cygserver_transport_pipes_outside.o: cygserver_transport_pipes.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
cygserver_transport_sockets_outside.o: cygserver_transport_sockets.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
cygserver_client_outside.o: cygserver_client.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
cygserver_shm.o: cygserver_shm.cc
$(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
cygserver.exe: cygserver.o cygserver_shm.o cygserver_transport_outside.o cygserver_transport_pipes_outside.o cygserver_transport_sockets_outside.o cygserver_client_outside.o cygserver_process.o threaded_queue.o wincap.o version.o smallprint.o
$(CXX) -o $@ $^
#ifdef VERBOSE
# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS)
#else
# @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\
# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS)
#endif
#
Makefile: cygwin.din
$(DEF_FILE): cygwin.din config.status

View File

@ -326,6 +326,7 @@ LoadDLLfunc (GetSidSubAuthorityCount, 4, advapi32)
LoadDLLfunc (GetTokenInformation, 20, advapi32)
LoadDLLfunc (GetUserNameA, 8, advapi32)
LoadDLLfunc (ImpersonateLoggedOnUser, 4, advapi32)
LoadDLLfunc (ImpersonateNamedPipeClient, 4, advapi32)
LoadDLLfunc (InitializeAcl, 12, advapi32)
LoadDLLfunc (InitializeSecurityDescriptor, 8, advapi32)
LoadDLLfunc (InitializeSid, 12, advapi32)

548
winsup/cygwin/cygserver.cc Executable file
View File

@ -0,0 +1,548 @@
/* cygserver.cc
Copyright 2001 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include "wincap.h"
#include "cygwin_version.h"
#include "getopt.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
#include "threaded_queue.h"
#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver.h"
#include "cygserver_shm.h"
/* for quieter operation, set to 0 */
#define DEBUG 0
#define debug_printf if (DEBUG) printf
GENERIC_MAPPING access_mapping;
static class transport_layer_base *transport;
DWORD request_count = 0;
BOOL
setup_privileges ()
{
BOOL rc, ret_val;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES sPrivileges;
rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ;
if ( !rc )
{
printf ( "error opening process token (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
rc = LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid );
if ( !rc )
{
printf ( "error getting prigilege luid (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
sPrivileges.PrivilegeCount = 1 ;
sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ;
if ( !rc )
{
printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () );
ret_val = FALSE;
goto out;
}
access_mapping.GenericRead = FILE_READ_DATA;
access_mapping.GenericWrite = FILE_WRITE_DATA;
access_mapping.GenericExecute = 0;
access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA;
ret_val = TRUE;
out:
CloseHandle ( hToken );
return ret_val;
}
int
check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
HANDLE* to_handle_ptr, BOOL bInheritHandle = FALSE)
{
HANDLE local_handle = NULL;
int ret_val = EACCES;
char sd_buf [1024];
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
DWORD bytes_needed;
PRIVILEGE_SET ps;
DWORD ps_len = sizeof (ps);
BOOL status;
if (from_process != GetCurrentProcess ())
{
if (!DuplicateHandle (from_process, from_handle,
GetCurrentProcess (), &local_handle,
0, bInheritHandle,
DUPLICATE_SAME_ACCESS))
{
printf ( "error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ());
goto out;
}
} else
local_handle = from_handle;
if (!GetKernelObjectSecurity (local_handle,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
sd, sizeof (sd_buf), &bytes_needed))
{
printf ( "error getting handle SD (%lu)\n", GetLastError ());
goto out;
}
MapGenericMask (&access, &access_mapping);
if (!AccessCheck (sd, from_process_token, access, &access_mapping,
&ps, &ps_len, &access, &status))
{
printf ( "error checking access rights (%lu)\n", GetLastError ());
goto out;
}
if (!status)
{
printf ( "access to object denied\n");
goto out;
}
if (!DuplicateHandle (from_process, from_handle,
to_process, to_handle_ptr,
access, bInheritHandle, 0))
{
printf ( "error getting handle to client (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("Duplicated %p to %p\n", from_handle, *to_handle_ptr);
ret_val = 0;
out:
if (local_handle && from_process != GetCurrentProcess ())
CloseHandle (local_handle);
return (ret_val);
}
void
client_request::serve (transport_layer_base *conn, class process_cache *cache)
{
printf ("*****************************************\n"
"A call to the base client_request class has occured\n"
"This indicates a mismatch in a virtual function definition somewhere\n");
exit (1);
}
void
client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache)
{
HANDLE from_process_handle = NULL;
HANDLE to_process_handle = NULL;
HANDLE token_handle = NULL;
DWORD rc;
if (header.cb != sizeof (req))
{
header.error_code = EINVAL;
return;
}
debug_printf ("pid %ld:(%p,%p) -> pid %ld\n", req.master_pid,
req.from_master, req.to_master,
req.pid);
debug_printf ("opening process %ld\n", req.master_pid);
from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
debug_printf ("opening process %ld\n", req.pid);
to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
if (!from_process_handle || !to_process_handle)
{
printf ("error opening process (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
debug_printf ("Impersonating client\n");
conn->impersonate_client ();
debug_printf ("about to open thread token\n");
rc = OpenThreadToken (GetCurrentThread (),
TOKEN_QUERY,
TRUE,
&token_handle);
debug_printf ("opened thread token, rc=%lu\n", rc);
conn->revert_to_self ();
if (!rc)
{
printf ("error opening thread token (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_READ,
req.from_master,
&req.from_master, TRUE) != 0)
{
printf ("error duplicating from_master handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
if (req.to_master)
{
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_WRITE,
req.to_master,
&req.to_master, TRUE) != 0)
{
printf ("error duplicating to_master handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
goto out;
}
}
#if DEBUG
printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid,
req.from_master, req.to_master);
#endif
header.error_code = 0;
out:
if (from_process_handle)
CloseHandle (from_process_handle);
if (to_process_handle)
CloseHandle (to_process_handle);
if (token_handle)
CloseHandle (token_handle);
}
void
client_request_get_version::serve(transport_layer_base *conn, class process_cache *cache)
{
if (header.cb != sizeof (version))
{
header.error_code = EINVAL;
return;
}
header.error_code = 0;
version.major = CYGWIN_SERVER_VERSION_MAJOR;
version.api = CYGWIN_SERVER_VERSION_API;
version.minor = CYGWIN_SERVER_VERSION_MINOR;
version.patch = CYGWIN_SERVER_VERSION_PATCH;
}
class server_request : public queue_request
{
public:
server_request (transport_layer_base *newconn, class process_cache *newcache);
virtual void process ();
private:
char request_buffer [MAX_REQUEST_SIZE];
transport_layer_base *conn;
class process_cache *cache;
};
class server_process_param : public queue_process_param
{
public:
transport_layer_base *transport;
server_process_param () : queue_process_param (false) {};
};
class server_request_queue : public threaded_queue
{
public:
class process_cache *cache;
void process_requests (transport_layer_base *transport);
virtual void add (transport_layer_base *conn);
};
class server_request_queue request_queue;
static DWORD WINAPI
request_loop (LPVOID LpParam)
{
class server_process_param *params = (server_process_param *) LpParam;
class server_request_queue *queue = (server_request_queue *) params->queue;
class transport_layer_base * transport = params->transport;
while (queue->active)
{
transport_layer_base * new_conn = transport->accept ();
/* FIXME: this is a little ugly. What we really want is to wait on two objects:
* one for the pipe/socket, and one for being told to shutdown. Otherwise
* this will stay a problem (we won't actually shutdown until the request
* _AFTER_ the shutdown request. And sending ourselves a request is ugly
*/
if (new_conn && queue->active)
queue->add (new_conn);
}
return 0;
}
/* TODO: check we are not being asked to service a already serviced transport */
void
server_request_queue::process_requests (transport_layer_base *transport)
{
class server_process_param *params = new server_process_param;
params->transport = transport;
threaded_queue::process_requests (params, request_loop);
}
void
client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache)
{
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
/* tell the main thread to shutdown */
request_queue.active=false;
}
server_request::server_request (transport_layer_base *newconn, class process_cache *newcache)
{
conn = newconn;
cache = newcache;
}
void
server_request::process ()
{
ssize_t bytes_read, bytes_written;
struct request_header* req_ptr = (struct request_header*) &request_buffer;
client_request *req = NULL;
debug_printf ("about to read\n");
bytes_read = conn->read (request_buffer, sizeof (struct request_header));
if (bytes_read != sizeof (struct request_header))
{
printf ("error reading from connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("got header (%ld)\n", bytes_read);
switch (req_ptr->req_id)
{
case CYGSERVER_REQUEST_GET_VERSION:
req = new client_request_get_version (); break;
case CYGSERVER_REQUEST_ATTACH_TTY:
req = new client_request_attach_tty (); break;
case CYGSERVER_REQUEST_SHUTDOWN:
req = new client_request_shutdown (); break;
case CYGSERVER_REQUEST_SHM_GET:
req = new client_request_shm (); break;
default:
req = new client_request (CYGSERVER_REQUEST_INVALID, 0);
req->header.error_code = ENOSYS;
debug_printf ("Bad client request - returning ENOSYS\n");
}
if (req->header.cb != req_ptr->cb)
{
debug_printf ("Mismatch in request buffer sizes\n");
goto out;
}
if (req->header.cb)
{
bytes_read = conn->read (req->buffer, req->header.cb);
if (bytes_read != req->header.cb)
{
debug_printf ("error reading from connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf ("got body (%ld)\n",bytes_read);
}
/* this is not allowed to fail. We must return ENOSYS at a minimum to the client */
req->serve (conn, cache);
if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header)))
!= sizeof(req->header) || (req->header.cb &&
(bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb))
{
req->header.error_code = -1;
printf ("error writing to connection (%lu)\n", GetLastError ());
goto out;
}
debug_printf("Sent reply, size (%ld)\n",bytes_written);
printf (".");
out:
conn->close ();
delete conn;
if (req)
delete (req);
}
void
server_request_queue::add (transport_layer_base *conn)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
/* every derived ::add must enter the section! */
EnterCriticalSection (&queuelock);
if (!running)
{
conn->close ();
delete conn;
LeaveCriticalSection (&queuelock);
return;
}
queue_request * listrequest = new server_request (conn, cache);
threaded_queue::add (listrequest);
LeaveCriticalSection (&queuelock);
}
void
handle_signal (int signal)
{
/* any signal makes us die :} */
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
/* tell the main thread to shutdown */
request_queue.active=false;
}
struct option longopts[] = {
{"shutdown", no_argument, NULL, 's'},
{0, no_argument, NULL, 0}
};
char opts[] = "s";
int
main (int argc, char **argv)
{
int shutdown=0;
char i;
while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
switch (i)
{
case 's':
shutdown = 1;
break;
default:
break;
/*NOTREACHED*/
}
wincap.init();
if (wincap.has_security ())
setup_privileges ();
transport = create_server_transport ();
if (shutdown)
{
if (!transport->connect())
{
printf ("couldn't establish connection with server\n");
exit (1);
}
client_request_shutdown *request =
new client_request_shutdown ();
request->send (transport);
transport->close();
delete transport;
delete request;
exit(0);
}
char version[200];
/* Cygwin dll release */
snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
cygwin_version.dll_major / 1000,
cygwin_version.dll_major % 1000,
cygwin_version.dll_minor,
cygwin_version.api_major,
cygwin_version.api_minor,
cygwin_version.shared_data,
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
cygwin_version.mount_registry,
cygwin_version.dll_build_date);
setbuf (stdout, NULL);
printf ("daemon version %s starting up", version);
if (signal (SIGQUIT, handle_signal) == SIG_ERR)
{
printf ("\ncould not install signal handler (%d)- aborting startup\n", errno);
exit (1);
}
printf (".");
transport->listen ();
printf (".");
class process_cache cache (2);
request_queue.initial_workers = 10;
request_queue.cache = &cache;
request_queue.create_workers ();
printf (".");
request_queue.process_requests (transport);
printf (".");
cache.create_workers ();
printf (".");
cache.process_requests ();
printf (".complete\n");
/* TODO: wait on multiple objects - the thread handle for each request loop +
* all the process handles. This should be done by querying the request_queue and
* the process cache for all their handles, and then waiting for (say) 30 seconds.
* after that we recreate the list of handles to wait on, and wait again.
* the point of all this abstraction is that we can trivially server both sockets
* and pipes simply by making a new transport, and then calling
* request_queue.process_requests (transport2);
*/
/* WaitForMultipleObjects abort && request_queue && process_queue && signal
-- if signal event then retrigger it
*/
while (1 && request_queue.active)
{
sleep (1);
}
printf ("\nShutdown request recieved - new requests will be denied\n");
request_queue.cleanup ();
printf ("All pending requests processed\n");
transport->close ();
printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
cache.cleanup ();
printf ("All outstanding process-cache activities completed\n");
printf ("daemon shutdown\n");
}

207
winsup/cygwin/cygserver_client.cc Executable file
View File

@ -0,0 +1,207 @@
/* cygserver_client.cc
Copyright 2001 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __OUTSIDE_CYGWIN__
#undef __INSIDE_CYGWIN__
#else
#include "winsup.h"
#endif
#ifndef __INSIDE_CYGWIN__
#define debug_printf printf
#define api_fatal printf
#include <stdio.h>
#include <windows.h>
#endif
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
//#include "security.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
#include "cygwin/cygserver.h"
/* 0 = untested, 1 = running, 2 = dead */
int cygserver_running=CYGSERVER_UNKNOWN;
/* on by default during development. For release, we probably want off by default */
int allow_daemon = TRUE;
client_request_get_version::client_request_get_version () : client_request (CYGSERVER_REQUEST_GET_VERSION, sizeof (version))
{
buffer = (char *)&version;
}
client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
{
buffer = (char *)&req;
req.pid = 0;
req.master_pid = 0;
req.from_master = NULL;
req.to_master = NULL;
}
client_request_attach_tty::client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req))
{
buffer = (char *)&req;
req.pid = npid;
req.master_pid = nmaster_pid;
req.from_master = nfrom_master;
req.to_master = nto_master;
}
client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0)
{
buffer = NULL;
}
client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size)
{
}
client_request::~client_request ()
{
}
client_request::operator class request_header ()
{
return header;
}
void
client_request::send (transport_layer_base *conn)
{
if (!conn)
return;
debug_printf("this=%p, conn=%p\n",this, conn);
ssize_t bytes_written, bytes_read;
debug_printf("header.cb = %ld\n",header.cb);
if ((bytes_written = conn->write ((char *)&header, sizeof (header)))
!= sizeof(header) || (header.cb &&
(bytes_written = conn->write (buffer, header.cb)) != header.cb))
{
header.error_code = -1;
debug_printf ("bytes written != request size\n");
return;
}
debug_printf("Sent request, size (%ld)\n",bytes_written);
if ((bytes_read = conn->read ((char *)&header, sizeof (header)))
!= sizeof (header) || (header.cb &&
(bytes_read = conn->read (buffer, header.cb) ) != header.cb))
{
header.error_code = -1;
debug_printf("failed reading response \n");
return;
}
debug_printf ("completed ok\n");
}
/* Oh, BTW: Fix the procedural basis and make this more intuitive. */
int
cygserver_request (client_request * req)
{
class transport_layer_base *transport;
if (!req || allow_daemon != TRUE)
return -1;
/* dont' retry every request if the server's not there */
if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION)
return -1;
transport = create_server_transport ();
/* FIXME: have at most one connection per thread. use TLS to store the details */
/* logic is:
* if not tlskey->conn, new conn,
* then; transport=conn;
*/
if (!transport->connect ())
{
delete transport;
return -1;
}
debug_printf ("connected to server %p\n", transport);
req->send(transport);
transport->close ();
delete transport;
return 0;
}
#if 0
BOOL
check_cygserver_available ()
{
BOOL ret_val = FALSE;
HANDLE pipe = CreateFile (pipe_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec_all_nih,
OPEN_EXISTING,
0,
NULL);
if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY)
ret_val = TRUE;
if (pipe && pipe != INVALID_HANDLE_VALUE)
CloseHandle (pipe);
return (ret_val);
}
#endif
void
cygserver_init ()
{
int rc;
if (allow_daemon != TRUE)
{
cygserver_running = CYGSERVER_DEAD;
return;
}
if (cygserver_running==CYGSERVER_OK)
return;
client_request_get_version *req =
new client_request_get_version ();
rc = cygserver_request (req);
delete req;
if (rc < 0)
cygserver_running = CYGSERVER_DEAD;
else if (rc > 0)
api_fatal ( "error connecting to cygwin server. error: %d", rc );
else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR ||
req->version.api != CYGWIN_SERVER_VERSION_API ||
req->version.minor > CYGWIN_SERVER_VERSION_MINOR)
api_fatal ( "incompatible version of cygwin server.\n\
client version %d.%d.%d.%d, server version%ld.%ld.%ld.%ld",
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
req->version.major,
req->version.api,
req->version.minor,
req->version.patch );
else
cygserver_running = CYGSERVER_OK;
}

View File

@ -0,0 +1,389 @@
/* cygserver_process.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include <pthread.h>
#include <threaded_queue.h>
#include <cygwin/cygserver_process.h>
#define debug_printf if (DEBUG) printf
#define DEBUG 1
/* the cache structures and classes are designed for one cache per server process.
* To make multiple process caches, a redesign will be needed
*/
/* process cache */
process_cache::process_cache (unsigned int num_initial_workers):
head (NULL)
{
/* there can only be one */
InitializeCriticalSection (&cache_write_access);
if ((cache_add_trigger = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create cache add trigger (%lu), terminating\n",
GetLastError ());
exit (1);
}
initial_workers = num_initial_workers;
}
process_cache::~process_cache ()
{
}
class process *
process_cache::process (long pid)
{
class process *entry = head;
/* TODO: make this more granular, so a search doesn't involve the write lock */
EnterCriticalSection (&cache_write_access);
if (!entry)
{
entry = new class process (pid);
entry->next =
(class process *) InterlockedExchangePointer (&head, entry);
PulseEvent (cache_add_trigger);
}
else
{
while (entry->winpid != pid && entry->next)
entry = entry->next;
if (entry->winpid != pid)
{
class process *new_entry = new class process (pid);
new_entry->next =
(class process *) InterlockedExchangePointer (&entry->next,
new_entry);
entry = new_entry;
PulseEvent (cache_add_trigger);
}
}
LeaveCriticalSection (&cache_write_access);
return entry;
}
static DWORD WINAPI
request_loop (LPVOID LpParam)
{
class process_process_param *params = (process_process_param *) LpParam;
return params->request_loop ();
}
void
process_cache::process_requests ()
{
class process_process_param *params = new process_process_param;
threaded_queue::process_requests (params, request_loop);
}
void
process_cache::add_task (class process * theprocess)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
/* every derived ::add must enter the section! */
EnterCriticalSection (&queuelock);
queue_request *listrequest = new process_cleanup (theprocess);
threaded_queue::add (listrequest);
LeaveCriticalSection (&queuelock);
}
/* NOT fully MT SAFE: must be called by only one thread in a program */
void
process_cache::remove_process (class process *theprocess)
{
class process *entry = head;
/* unlink */
EnterCriticalSection (&cache_write_access);
if (entry == theprocess)
{
entry = (class process *) InterlockedExchangePointer (&head, theprocess->next);
if (entry != theprocess)
{
printf ("Bug encountered, process cache corrupted\n");
exit (1);
}
}
else
{
while (entry->next && entry->next != theprocess)
entry = entry->next;
class process *temp = (class process *) InterlockedExchangePointer (&entry->next, theprocess->next);
if (temp != theprocess)
{
printf ("Bug encountered, process cache corrupted\n");
exit (1);
}
}
LeaveCriticalSection (&cache_write_access);
/* Process any cleanup tasks */
add_task (theprocess);
}
/* copy <= max_copy HANDLEs to dest[], starting at an offset into _our list_ of
* begin_at. (Ie begin_at = 5, the first copied handle is still written to dest[0]
* NOTE: Thread safe, but not thread guaranteed - a newly added process may be missed.
* Who cares - It'll get caught the next time.
*/
int
process_cache::handle_snapshot (HANDLE * hdest, class process ** edest,
ssize_t max_copy, int begin_at)
{
/* TODO:? grab a delete-lock, to prevent deletes during this process ? */
class process *entry = head;
int count = begin_at;
/* skip begin_at entries */
while (entry && count)
{
if (entry->exit_code () == STILL_ACTIVE)
count--;
entry = entry->next;
}
/* hit the end of the list within begin_at entries */
if (count)
return 0;
HANDLE *hto = hdest;
class process **eto = edest;
while (entry && count < max_copy)
{
/* hack */
if (entry->exit_code () == STILL_ACTIVE)
{
*hto = entry->handle ();
*eto = entry;
count++;
hto++;
eto++;
}
entry = entry->next;
}
return count;
}
/* process's */
/* global process crit section */
static CRITICAL_SECTION process_access;
static pthread_once_t process_init;
void
do_process_init (void)
{
InitializeCriticalSection (&process_access);
/* we don't have a cache shutdown capability today */
}
process::process (long pid):
winpid (pid), next (NULL), cleaning_up (0), head (NULL), _exit_status (STILL_ACTIVE)
{
pthread_once (&process_init, do_process_init);
EnterCriticalSection (&process_access);
thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
if (!thehandle)
{
printf ("unable to obtain handle for new cache process %ld\n", pid);
thehandle = INVALID_HANDLE_VALUE;
}
debug_printf ("Got handle %p for new cache process %ld\n", thehandle, pid);
InitializeCriticalSection (&access);
LeaveCriticalSection (&process_access);
}
process::~process ()
{
DeleteCriticalSection (&access);
}
HANDLE
process::handle ()
{
// DWORD exitstate = exit_code ();
// if (exitstate == STILL_ACTIVE)
return thehandle;
/* FIXME: call the cleanup list ? */
// CloseHandle (thehandle);
// debug_printf ("Process id %ld has terminated, attempting to open a new handle\n",
// winpid);
// thehandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
// debug_printf ("Got handle %p when refreshing cache process %ld\n", thehandle, winpid);
// /* FIXME: what if OpenProcess fails ? */
// if (thehandle)
// {
// _exit_status = STILL_ACTIVE;
// exit_code ();
// }
// else
// thehandle = INVALID_HANDLE_VALUE;
// return thehandle;
}
DWORD process::exit_code ()
{
if (_exit_status != STILL_ACTIVE)
return _exit_status;
bool
err = GetExitCodeProcess (thehandle, &_exit_status);
if (!err)
{
debug_printf ("Failed to retrieve exit code (%ld)\n", GetLastError ());
thehandle = INVALID_HANDLE_VALUE;
return _exit_status;
}
else if (_exit_status == STILL_ACTIVE)
return _exit_status;
/* add new cleanup task etc etc ? */
return _exit_status;
}
/* this is single threaded. It's called after the process is removed from the cache,
* but inserts may be attemped by worker threads that have a pointer to it */
void
process::cleanup ()
{
/* Serialize this */
EnterCriticalSection (&access);
InterlockedIncrement (&(long)cleaning_up);
class cleanup_routine *entry = head;
while (entry)
{
class cleanup_routine *temp;
entry->cleanup (winpid);
temp = entry->next;
delete entry;
entry = temp;
}
LeaveCriticalSection (&access);
}
bool
process::add_cleanup_routine (class cleanup_routine *new_cleanup)
{
if (cleaning_up)
return false;
EnterCriticalSection (&access);
/* check that we didn't block with ::cleanup ()
* This rigmarole is to get around win9x's glaring missing TryEnterCriticalSection call
* which would be a whole lot easier
*/
if (cleaning_up)
{
LeaveCriticalSection (&access);
return false;
}
new_cleanup->next = head;
head = new_cleanup;
LeaveCriticalSection (&access);
return true;
}
/* process_cleanup */
void
process_cleanup::process ()
{
theprocess->cleanup ();
delete theprocess;
}
/* process_process_param */
DWORD
process_process_param::request_loop ()
{
process_cache *cache = (process_cache *) queue;
/* always malloc one, so there is no special case in the loop */
ssize_t HandlesSize = 2;
HANDLE *Handles = (HANDLE *) malloc (sizeof (HANDLE) * HandlesSize);
process **Entries = (process **) malloc (sizeof (LPVOID) * HandlesSize);
/* TODO: put [1] at the end as it will also get done if a process dies? */
Handles[0] = interrupt;
Handles[1] = cache->cache_add_trigger;
while (cache->active && !shutdown)
{
int copied;
copied = -1;
int offset;
offset = 1;
int count;
count = 2;
while ((copied == HandlesSize - 2 - offset) || copied < 0)
{
/* we need more storage to cope with all the HANDLES */
if (copied == HandlesSize - 2 - offset)
{
HANDLE *temp = (HANDLE *) realloc (Handles,
sizeof (HANDLE) *
HandlesSize + 10);
if (!temp)
{
printf
("cannot allocate more storage for the handle array!\n");
exit (1);
}
Handles = temp;
process **ptemp = (process **) realloc (Entries,
sizeof (LPVOID) *
HandlesSize + 10);
if (!ptemp)
{
printf
("cannot allocate more storage for the handle array!\n");
exit (1);
}
Entries = ptemp;
HandlesSize += 10;
}
offset += copied;
copied =
cache->handle_snapshot (&Handles[2], &Entries[2],
HandlesSize - 2 - offset, offset);
count += copied;
}
debug_printf ("waiting on %u objects\n", count);
DWORD rc = WaitForMultipleObjects (count, Handles, FALSE, INFINITE);
if (rc == WAIT_FAILED)
{
printf ("Could not wait on the process handles (%ld)!\n",
GetLastError ());
exit (1);
}
int objindex = rc - WAIT_OBJECT_0;
if (objindex > 1 && objindex < count)
{
debug_printf ("Process %ld has left the building\n",
Entries[objindex]->winpid);
/* fire off the termination routines */
cache->remove_process (Entries[objindex]);
}
else if (objindex >= 0 && objindex < 2)
{
/* 0 is shutdown - do nothing */
/* 1 is a cache add event - just rebuild the object list */
}
else
{
printf
("unexpected return code from WaitForMultiple objects in process_process_param::request_loop\n");
}
}
running = false;
return 0;
}

552
winsup/cygwin/cygserver_shm.cc Executable file
View File

@ -0,0 +1,552 @@
/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin
Copyright 2001 Red Hat, Inc.
Originally written by Robert Collins <robert.collins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __OUTSIDE_CYGWIN__
#undef __INSIDE_CYGWIN__
#else
#include "winsup.h"
#endif
#ifndef __INSIDE_CYGWIN__
#define DEBUG 0
#define system_printf printf
#define debug_printf if (DEBUG) printf
#define api_fatal printf
#include <stdio.h>
#include <windows.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "cygerrno.h"
#include <unistd.h>
#include "security.h"
//#include "fhandler.h"
//#include "dtable.h"
//#include "cygheap.h"
#include <stdio.h>
//#include "thread.h"
#ifndef __INSIDE_CYGWIN__
#define __INSIDE_CYGWIN__
#include <sys/shm.h>
#undef __INSIDE_CYGWIN__
#else
#include <sys/shm.h>
#endif
//#include "perprocess.h"
#include <threaded_queue.h>
#include <cygwin/cygserver_process.h>
#include "cygserver_shm.h"
// FIXME IS THIS CORRECT
/* Implementation notes: We use two shared memory regions per key:
* One for the control structure, and one for the shared memory.
* While this has a higher overhead tham a single shared area,
* It allows more flexability. As the entire code is transparent to the user
* We can merge these in the future should it be needed.
* Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just
* keep monotonically incrementing - system wide.
*/
size_t
getsystemallocgranularity ()
{
SYSTEM_INFO sysinfo;
static size_t buffer_offset = 0;
if (buffer_offset)
return buffer_offset;
GetSystemInfo (&sysinfo);
buffer_offset = sysinfo.dwAllocationGranularity;
return buffer_offset;
}
client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
{
buffer = (char *) &parameters;
}
/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail.
* Solution: manually calculate the next id value
*/
#if 0
extern "C" void *
shmat (int shmid, const void *shmaddr, int parameters.in.shmflg)
{
class shmid_ds *shm = (class shmid_ds *) shmid; //FIXME: verifyable object test
if (shmaddr)
{
//FIXME: requested base address ?!
set_errno (EINVAL);
return (void *) -1;
}
void *rv = MapViewOfFile (shm->attachmap,
(parameters.in.shmflg & SHM_RDONLY) ?
FILE_MAP_READ : FILE_MAP_WRITE, 0,
0, 0);
if (!rv)
{
//FIXME: translate GetLastError()
set_errno (EACCES);
return (void *) -1;
}
/* FIXME: this needs to be globally protected to prevent a mismatch betwen
* attach count and attachees list
*/
InterlockedIncrement (&shm->shm_nattch);
_shmattach *attachnode = new _shmattach;
attachnode->data = rv;
attachnode->next =
(_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead,
(long int) attachnode);
return rv;
}
#endif
/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
* choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
* appropriately
*/
/* Test result from openbsd: shm ids are persistent cross process if a handle is left
* open. This could lead to resource starvation: we're not copying that behaviour
* unless we have to. (It will involve acygwin1.dll gloal shared list :[ ).
*/
/* FIXME: shmid should be a verifyable object
*/
/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
*/
extern GENERIC_MAPPING access_mapping;
extern int
check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
HANDLE * to_handle_ptr, BOOL bInheritHandle);
//FIXME: where should this live
static shmnode *shm_head = NULL;
/* must be long for InterlockedIncrement */
static long new_id = 0;
static long new_private_key = 0;
void
client_request_shm::serve (transport_layer_base * conn,
process_cache * cache)
{
// DWORD sd_size = 4096;
// char sd_buf[4096];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf;
// /* create a sd for our open requests based on shmflag & 0x01ff */
// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
// parameters.in.shmflg & 0x01ff, psd, &sd_size);
HANDLE from_process_handle = NULL;
HANDLE token_handle = NULL;
DWORD rc;
from_process_handle = cache->process (parameters.in.pid)->handle ();
/* possible TODO: reduce the access on the handle before we use it */
/* Note that unless we do this, we don't need to call CloseHandle - it's kept open
* by the process cache until the process terminates.
* We may need a refcount on the cache however...
*/
if (!from_process_handle)
{
debug_printf ("error opening process (%lu)\n", GetLastError ());
header.error_code = EACCES;
return;
}
conn->impersonate_client ();
rc = OpenThreadToken (GetCurrentThread (),
TOKEN_QUERY, TRUE, &token_handle);
conn->revert_to_self ();
if (!rc)
{
debug_printf ("error opening thread token (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (from_process_handle);
return;
}
/* we trust the clients request - we will be doing it as them, and
* the worst they can do is open their own permissions
*/
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof (sa);
sa.lpSecurityDescriptor = psd;
sa.bInheritHandle = TRUE; /* the memory structures inherit ok */
char *shmname = NULL, *shmaname = NULL;
char stringbuf[29], stringbuf1[29];
/* TODO: make this code block a function! */
if (parameters.in.type == SHM_REATTACH)
{
/* just find and fill out the existing shm_id */
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->shm_id == parameters.in.shm_id)
{
parameters.out.shm_id = tempnode->shm_id;
parameters.out.key = tempnode->key;
if (check_and_dup_handle
(GetCurrentProcess (), from_process_handle, token_handle,
DUPLICATE_SAME_ACCESS, tempnode->filemap,
&parameters.out.filemap, TRUE) != 0)
{
debug_printf ("error duplicating filemap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
}
if (check_and_dup_handle
(GetCurrentProcess (), from_process_handle, token_handle,
DUPLICATE_SAME_ACCESS, tempnode->attachmap,
&parameters.out.attachmap, TRUE) != 0)
{
debug_printf ("error duplicating attachmap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
}
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* someone attached */
/* someone can send shm_id's they don't have and currently we will increment those
* attach counts. If someone wants to fix that, please go ahead.
* The problem is that shm_get has nothing to do with the ability to attach. Attach
* requires a permission check, which we get the OS to do in MapViewOfFile.
*/
if (parameters.in.type == SHM_ATTACH)
{
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->shm_id == parameters.in.shm_id)
{
InterlockedIncrement (&tempnode->shmds->shm_nattch);
header.error_code = 0;
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* it's a original request from the users */
/* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
* to prevent races on shmget.
*/
if (parameters.in.key == IPC_PRIVATE)
{
/* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */
/* The K refers to Key, the actual mapped area has D */
long private_key = (int) InterlockedIncrement (&new_private_key);
snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key);
shmname = stringbuf;
snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key);
shmaname = stringbuf1;
}
else
{
/* create the mapping name (CYGWINSHMK0x0123456789abcdef */
/* The K refers to Key, the actual mapped area has D */
snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key);
shmname = stringbuf;
snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key);
shmaname = stringbuf1;
debug_printf ("system id strings are \n%s\n%s\n", shmname, shmaname);
debug_printf ("key input value is 0x%0qx\n", parameters.in.key);
}
/* attempt to open the key */
/* get an existing key */
/* On unix the same shmid identifier is returned on multiple calls to shm_get
* with the same key and size. Different modes is a ?.
*/
/* walk the list of known keys and return the id if found. remember, we are
* authoritative...
*/
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->key == parameters.in.key
&& parameters.in.key != IPC_PRIVATE)
{
// FIXME: free the mutex
if (parameters.in.size
&& tempnode->shmds->shm_segsz < parameters.in.size)
{
header.error_code = EINVAL;
CloseHandle (token_handle);
return;
}
/* FIXME: can the same process call this twice without error ? test
* on unix
*/
if ((parameters.in.shmflg & IPC_CREAT)
&& (parameters.in.shmflg & IPC_EXCL))
{
header.error_code = EEXIST;
debug_printf
("attempt to exclusively create already created shm_area with key 0x%0qx\n",
parameters.in.key);
// FIXME: free the mutex
CloseHandle (token_handle);
return;
}
// FIXME: do we need to other tests of the requested mode with the
// tempnode->shm_id mode ? testcase on unix needed.
// FIXME how do we do the security test? or
// do we wait for shmat to bother with that?
/* One possibly solution: impersonate the client, and then test we can
* reopen the area. In fact we'll probably have to do that to get
* handles back to them, alternatively just tell them the id, and then
* let them attempt the open.
*/
parameters.out.shm_id = tempnode->shm_id;
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->filemap,
&parameters.out.filemap, TRUE) != 0)
{
printf ("error duplicating filemap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
/*mutex*/
CloseHandle (token_handle);
return;
}
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->attachmap,
&parameters.out.attachmap, TRUE) != 0)
{
printf ("error duplicating attachmap handle (%lu)\n",
GetLastError ());
header.error_code = EACCES;
/*mutex*/
CloseHandle (token_handle);
return;
}
CloseHandle (token_handle);
return;
}
tempnode = tempnode->next;
}
/* couldn't find a currently open shm area. */
/* create one */
/* do this as the client */
conn->impersonate_client ();
/* This may need sh_none... it's only a control structure */
HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile.
&sa,
PAGE_READWRITE, // protection
0x00000000,
getsystemallocgranularity (),
shmname // object name
);
int lasterr = GetLastError ();
conn->revert_to_self ();
if (filemap == NULL)
{
/* We failed to open the filemapping ? */
system_printf ("failed to open file mapping: %lu\n", GetLastError ());
// free the mutex
// we can assume that it exists, and that it was an access problem.
header.error_code = EACCES;
CloseHandle (token_handle);
return;
}
/* successfully opened the control region mapping */
/* did we create it ? */
int oldmapping = lasterr == ERROR_ALREADY_EXISTS;
if (oldmapping)
{
/* should never happen - we are the global daemon! */
#if 0
if ((parameters.in.shmflg & IPC_CREAT)
&& (parameters.in.shmflg & IPC_EXCL))
#endif
{
/* FIXME free mutex */
CloseHandle (filemap);
header.error_code = EEXIST;
CloseHandle (token_handle);
return;
}
}
/* we created a new mapping */
if (parameters.in.key != IPC_PRIVATE &&
(parameters.in.shmflg & IPC_CREAT) == 0)
{
CloseHandle (filemap);
/* FIXME free mutex */
header.error_code = ENOENT;
CloseHandle (token_handle);
return;
}
conn->impersonate_client ();
void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
conn->revert_to_self ();
if (!mapptr)
{
CloseHandle (filemap);
//FIXME: close filemap and free the mutex
/* we couldn't access the mapped area with the requested permissions */
header.error_code = EACCES;
CloseHandle (token_handle);
return;
}
conn->impersonate_client ();
/* Now get the user data */
HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile
&sa,
PAGE_READWRITE, // protection (FIXME)
0x00000000,
parameters.in.size +
parameters.in.size %
getsystemallocgranularity (),
shmaname // object name
);
conn->revert_to_self ();
if (attachmap == NULL)
{
system_printf ("failed to get shm attachmap\n");
header.error_code = ENOMEM;
UnmapViewOfFile (mapptr);
CloseHandle (filemap);
/* FIXME exit the mutex */
CloseHandle (token_handle);
return;
}
shmid_ds *shmtemp = new shmid_ds;
if (!shmtemp)
{
system_printf ("failed to malloc shm node\n");
header.error_code = ENOMEM;
UnmapViewOfFile (mapptr);
CloseHandle (filemap);
CloseHandle (attachmap);
/* FIXME exit mutex */
CloseHandle (token_handle);
return;
}
/* fill out the node data */
shmtemp->shm_perm.cuid = getuid ();
shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
shmtemp->shm_perm.cgid = getgid ();
shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff;
shmtemp->shm_lpid = 0;
shmtemp->shm_nattch = 0;
shmtemp->shm_atime = 0;
shmtemp->shm_dtime = 0;
shmtemp->shm_ctime = time (NULL);
shmtemp->shm_segsz = parameters.in.size;
*(shmid_ds *) mapptr = *shmtemp;
shmtemp->mapptr = mapptr;
/* no need for InterlockedExchange here, we're serialised by the global mutex */
tempnode = new shmnode;
tempnode->shmds = shmtemp;
tempnode->shm_id = (int) InterlockedIncrement (&new_id);
tempnode->key = parameters.in.key;
tempnode->filemap = filemap;
tempnode->attachmap = attachmap;
tempnode->next = shm_head;
shm_head = tempnode;
/* we now have the area in the daemon list, opened.
FIXME: leave the system wide shm mutex */
parameters.out.shm_id = tempnode->shm_id;
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->filemap, &parameters.out.filemap,
TRUE) != 0)
{
printf ("error duplicating filemap handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (token_handle);
/* mutex et al */
return;
}
if (check_and_dup_handle (GetCurrentProcess (), from_process_handle,
token_handle,
DUPLICATE_SAME_ACCESS,
tempnode->attachmap, &parameters.out.attachmap,
TRUE) != 0)
{
printf ("error duplicating attachmap handle (%lu)\n", GetLastError ());
header.error_code = EACCES;
CloseHandle (from_process_handle);
CloseHandle (token_handle);
/* more cleanup... yay! */
return;
}
CloseHandle (token_handle);
return;
}

View File

@ -0,0 +1,91 @@
/* cygserver_shm.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <sys/types.h>
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#define SHM_CREATE 0
#define SHM_REATTACH 1
#define SHM_ATTACH 2
#define SHM_DETACH 3
class client_request_shm : public client_request
{
public:
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, process_cache *cache);
#endif
client_request_shm (key_t, size_t, int, char psdbuf[4096], pid_t);
client_request_shm ();
client_request_shm (int, int, pid_t);
client_request_shm (int, int);
union {
struct {int type; pid_t pid; int shm_id; key_t key; size_t size; int shmflg; char sd_buf[4096];} in;
struct {int shm_id; HANDLE filemap; HANDLE attachmap; key_t key;} out;
} parameters;
};
#ifndef __INSIDE_CYGWIN__
class shm_cleanup : cleanup_routine
{
public:
virtual void cleanup (long winpid);
};
#endif
#if 0
class _shmattach {
public:
void *data;
class _shmattach *next;
};
class shmid_ds {
public:
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
HANDLE filemap;
HANDLE attachmap;
void *mapptr;
class _shmattach *attachhead;
};
class shmnode {
public:
class shmid_ds * shmid;
class shmnode *next;
key_t key;
};
//....
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
};
void *shmat(int, const void *, int);
int shmctl(int, int, struct shmid_ds *);
int shmdt(const void *);
int shmget(key_t, size_t, int);
#endif

View File

@ -0,0 +1,92 @@
/* cygserver_transport.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
#else
#define debug_printf printf
#endif
/* The factory */
class transport_layer_base *create_server_transport()
{
transport_layer_base *temp;
/* currently there is only the base class! */
if (wincap.is_winnt ())
temp = new transport_layer_pipes ();
else
temp = new transport_layer_base ();
return temp;
}
transport_layer_base::transport_layer_base ()
{
/* should we throw an error of some sort ? */
}
void
transport_layer_base::listen ()
{
}
class transport_layer_base *
transport_layer_base::accept ()
{
return NULL;
}
void
transport_layer_base::close()
{
}
ssize_t
transport_layer_base::read (char *buf, size_t len)
{
return 0;
}
ssize_t
transport_layer_base::write (char *buf, size_t len)
{
return 0;
}
bool
transport_layer_base::connect ()
{
return false;
}
void
transport_layer_base::impersonate_client ()
{
}
void
transport_layer_base::revert_to_self ()
{
}

View File

@ -0,0 +1,205 @@
/* cygserver_transport_pipes.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
#else
#define DEBUG 0
#define debug_printf if (DEBUG) printf
#endif
transport_layer_pipes::transport_layer_pipes (HANDLE new_pipe)
{
pipe = new_pipe;
if (inited != true)
init_security();
};
transport_layer_pipes::transport_layer_pipes ()
{
pipe = NULL;
strcpy(pipe_name, "\\\\.\\pipe\\cygwin_lpc");
if (inited != true)
init_security();
}
void
transport_layer_pipes::init_security()
{
/* FIXME: pthread_once or equivalent needed */
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE);
sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
sec_none_nih.lpSecurityDescriptor = NULL;
sec_all_nih.lpSecurityDescriptor = &sd;
inited = true;
}
void
transport_layer_pipes::listen ()
{
/* no-op */
}
class transport_layer_pipes *
transport_layer_pipes::accept ()
{
if (pipe)
{
debug_printf ("Already have a pipe in this %p\n",this);
return NULL;
}
pipe = CreateNamedPipe (pipe_name,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0, 0, 1000,
&sec_all_nih );
if (pipe == INVALID_HANDLE_VALUE)
{
debug_printf ("error creating pipe (%lu)\n.", GetLastError ());
return NULL;
}
if ( !ConnectNamedPipe ( pipe, NULL ) &&
GetLastError () != ERROR_PIPE_CONNECTED)
{
printf ("error connecting to pipe (%lu)\n.", GetLastError ());
CloseHandle (pipe);
pipe = NULL;
return NULL;
}
transport_layer_pipes *new_conn = new transport_layer_pipes (pipe);
pipe = NULL;
return new_conn;
}
void
transport_layer_pipes::close()
{
debug_printf ("closing pipe %p\n", pipe);
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
FlushFileBuffers (pipe);
DisconnectNamedPipe (pipe);
CloseHandle (pipe);
}
}
ssize_t
transport_layer_pipes::read (char *buf, size_t len)
{
debug_printf ("reading from pipe %p\n", pipe);
if (!pipe || pipe == INVALID_HANDLE_VALUE)
return -1;
DWORD bytes_read;
DWORD rc = ReadFile (pipe, buf, len, &bytes_read, NULL);
if (!rc)
{
debug_printf ("error reading from pipe (%lu)\n", GetLastError ());
return -1;
}
return bytes_read;
}
ssize_t
transport_layer_pipes::write (char *buf, size_t len)
{
debug_printf ("writing to pipe %p\n", pipe);
DWORD bytes_written, rc;
if (!pipe || pipe == INVALID_HANDLE_VALUE)
return -1;
rc = WriteFile (pipe, buf, len, &bytes_written, NULL);
if (!rc)
{
debug_printf ("error writing to pipe (%lu)\n", GetLastError ());
return -1;
}
return bytes_written;
}
bool
transport_layer_pipes::connect ()
{
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
debug_printf ("Already have a pipe in this %p\n",this);
return false;
}
while (1)
{
pipe = CreateFile (pipe_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sec_all_nih,
OPEN_EXISTING,
0, NULL);
if (pipe != INVALID_HANDLE_VALUE)
/* got the pipe */
return true;
if (GetLastError () != ERROR_PIPE_BUSY)
{
debug_printf ("Error opening the pipe (%lu)\n", GetLastError ());
pipe = NULL;
return false;
}
if (!WaitNamedPipe (pipe_name, 20000))
debug_printf ( "error connecting to server pipe after 20 seconds (%lu)\n", GetLastError () );
/* We loop here, because the pipe exists but is busy. If it doesn't exist
* the != ERROR_PIPE_BUSY will catch it.
*/
}
}
void
transport_layer_pipes::impersonate_client ()
{
debug_printf ("impersonating pipe %p\n", pipe);
if (pipe && pipe != INVALID_HANDLE_VALUE)
{
BOOL rv = ImpersonateNamedPipeClient (pipe);
if (!rv)
debug_printf ("Failed to Impersonate the client, (%lu)\n", GetLastError ());
}
debug_printf("I am who you are\n");
}
void
transport_layer_pipes::revert_to_self ()
{
RevertToSelf ();
debug_printf("I am who I yam\n");
}

View File

@ -0,0 +1,131 @@
/* cygserver_transport_sockets.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "wincap.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
#include "winsup.h"
extern "C" int
cygwin_socket (int af, int type, int protocol);
extern "C" int
cygwin_connect (int fd,
const struct sockaddr *name,
int namelen);
extern "C" int
cygwin_accept (int fd, struct sockaddr *peer, int *len);
extern "C" int
cygwin_listen (int fd, int backlog);
extern "C" int
cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen);
#else
#define cygwin_accept(A,B,C) ::accept(A,B,C)
#define cygwin_socket(A,B,C) ::socket(A,B,C)
#define cygwin_listen(A,B) ::listen(A,B)
#define cygwin_bind(A,B,C) ::bind(A,B,C)
#define cygwin_connect(A,B,C) ::connect(A,B,C)
#define debug_printf printf
#endif
transport_layer_sockets::transport_layer_sockets (int newfd): fd(newfd)
{
/* This may not be needed in this constructor - it's only used
* when creating a connection via bind or connect
*/
sockdetails.sa_family = AF_UNIX;
strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
};
transport_layer_sockets::transport_layer_sockets (): fd (-1)
{
sockdetails.sa_family = AF_UNIX;
strcpy (sockdetails.sa_data, "/tmp/cygdaemo");
sdlen = strlen(sockdetails.sa_data) + sizeof(sockdetails.sa_family);
}
void
transport_layer_sockets::listen ()
{
/* we want a thread pool based approach. */
if ((fd = cygwin_socket (AF_UNIX, SOCK_STREAM,0)) < 0)
printf ("Socket not created error %d\n", errno);
if (cygwin_bind(fd, &sockdetails, sdlen))
printf ("Bind doesn't like you. Tsk Tsk. Bind said %d\n", errno);
if (cygwin_listen(fd, 5) < 0)
printf ("And the OS just isn't listening, all it says is %d\n", errno);
}
class transport_layer_sockets *
transport_layer_sockets::accept ()
{
/* FIXME: check we have listened */
int new_fd;
if ((new_fd = cygwin_accept(fd, &sockdetails, &sdlen)) < 0)
{
printf ("Nup, could' accept. %d\n",errno);
return NULL;
}
transport_layer_sockets *new_conn = new transport_layer_sockets (new_fd);
return new_conn;
}
void
transport_layer_sockets::close()
{
/* FIXME - are we open? */
::close (fd);
}
ssize_t
transport_layer_sockets::read (char *buf, size_t len)
{
/* FIXME: are we open? */
return ::read (fd, buf, len);
}
ssize_t
transport_layer_sockets::write (char *buf, size_t len)
{
/* FIXME: are we open? */
return ::write (fd, buf, len);
}
bool
transport_layer_sockets::connect ()
{
/* are we already connected? */
if (fd != -1)
return false;
fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
if (cygwin_connect (fd, &sockdetails, sdlen) < 0)
{
debug_printf("client connect failure %d\n", errno);
::close (fd);
return false;
}
return true;
}

View File

@ -35,6 +35,8 @@ details. */
#include "shared_info.h"
#include "cygwin_version.h"
#include "dll_init.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#define MAX_AT_FILE_LEVEL 10
@ -685,6 +687,8 @@ dll_crt0_1 ()
/* Initialize signal/subprocess handling. */
sigproc_init ();
cygserver_init ();
/* Connect to tty. */
tty_init ();

View File

@ -28,6 +28,7 @@ details. */
#include "registry.h"
#include "environ.h"
extern BOOL allow_daemon;
extern BOOL allow_glob;
extern bool ignore_case_with_glob;
extern BOOL allow_ntea;
@ -510,6 +511,7 @@ static struct parse_thing
{"binmode", {x: &binmode}, justset, NULL, {{O_TEXT}, {O_BINARY}}},
{"check_case", {func: &check_case_init}, isfunc, NULL, {{0}, {0}}},
{"codepage", {func: &codepage_init}, isfunc, NULL, {{0}, {0}}},
{"daemon", {&allow_daemon}, justset, NULL, {{FALSE}, {TRUE}}},
{"envcache", {&envcache}, justset, NULL, {{TRUE}, {FALSE}}},
{"error_start", {func: &error_start_init}, isfunc, NULL, {{0}, {0}}},
{"export", {&export_settings}, justset, NULL, {{FALSE}, {TRUE}}},

View File

@ -840,6 +840,8 @@ class fhandler_tty_slave: public fhandler_tty_common
__off64_t lseek (__off64_t, int) { return 0; }
select_record *select_read (select_record *s);
int ready_for_read (int fd, DWORD howlong);
int cygserver_attach_tty (HANDLE*, HANDLE*);
};
class fhandler_pty_master: public fhandler_tty_common

View File

@ -26,6 +26,8 @@ details. */
#include "pinfo.h"
#include "cygheap.h"
#include "shared_info.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
/* Tty master stuff */
@ -510,38 +512,54 @@ fhandler_tty_slave::open (path_conv *, int flags, mode_t)
return 0;
}
HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
get_ttyp ()->master_pid);
if (tty_owner == NULL)
HANDLE from_master_local, to_master_local;
if (!wincap.has_security () ||
cygserver_running!=CYGSERVER_OK ||
!cygserver_attach_tty ( &from_master_local, &to_master_local))
{
termios_printf ("can't open tty (%d) handle process %d",
ttynum, get_ttyp ()->master_pid);
__seterrno ();
return 0;
termios_printf ("cannot dup handles via server. using old method.");
HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
get_ttyp ()->master_pid);
termios_printf ("tty own handle %p",tty_owner);
if (tty_owner == NULL)
{
termios_printf ("can't open tty (%d) handle process %d",
ttynum, get_ttyp ()->master_pid);
__seterrno ();
return 0;
}
if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
hMainProc, &from_master_local, 0, TRUE,
DUPLICATE_SAME_ACCESS))
{
termios_printf ("can't duplicate input, %E");
__seterrno ();
return 0;
}
if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
hMainProc, &to_master_local, 0, TRUE,
DUPLICATE_SAME_ACCESS))
{
termios_printf ("can't duplicate output, %E");
__seterrno ();
return 0;
}
CloseHandle (tty_owner);
}
HANDLE nh;
if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master, hMainProc, &nh, 0, TRUE,
DUPLICATE_SAME_ACCESS))
{
termios_printf ("can't duplicate input, %E");
__seterrno ();
return 0;
}
set_io_handle (nh);
ProtectHandle1 (nh, from_pty);
termios_printf ("duplicated from_master %p->%p from tty_owner %p",
get_ttyp ()->from_master, nh, tty_owner);
if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master, hMainProc, &nh, 0, TRUE,
DUPLICATE_SAME_ACCESS))
{
termios_printf ("can't duplicate output, %E");
__seterrno ();
return 0;
}
set_output_handle (nh);
ProtectHandle1 (nh, to_pty);
CloseHandle (tty_owner);
termios_printf ("duplicated from_master %p->%p from tty_owner",
get_ttyp ()->from_master, from_master_local);
termios_printf ("duplicated to_master %p->%p from tty_owner",
get_ttyp ()->to_master, to_master_local);
set_io_handle (from_master_local);
ProtectHandle1 (from_master_local, from_pty);
set_output_handle (to_master_local);
ProtectHandle1 (to_master_local, to_pty);
set_open_status ();
termios_printf ("tty%d opened", ttynum);
@ -549,6 +567,39 @@ fhandler_tty_slave::open (path_conv *, int flags, mode_t)
return 1;
}
int
fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
LPHANDLE to_master_ptr)
{
if (!from_master_ptr || !to_master_ptr)
return 0;
client_request_attach_tty *request =
new client_request_attach_tty ((DWORD) GetCurrentProcessId (),
(DWORD) get_ttyp ()->master_pid,
(HANDLE) get_ttyp ()->from_master,
(HANDLE) get_ttyp ()->to_master);
if (cygserver_request (request) != 0 ||
request->header.error_code != 0)
return 0;
/*
struct request_attach_tty req;
INIT_REQUEST (req, CYGSERVER_REQUEST_ATTACH_TTY);
req.pid = GetCurrentProcessId ();
req.master_pid = get_ttyp ()->master_pid;
req.from_master = get_ttyp ()->from_master;
req.to_master = get_ttyp ()->to_master;
if (cygserver_request ((struct request_header*) &req) != 0)
return 0;
*/
*from_master_ptr = request->from_master ();
*to_master_ptr = request->to_master ();
delete request;
return 1;
}
void
fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
{
@ -634,7 +685,7 @@ fhandler_tty_slave::read (void *ptr, size_t len)
DWORD rc;
HANDLE w4[2];
termios_printf ("read(%x, %d) handle %d", ptr, len, get_handle ());
termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ());
if (!(get_ttyp ()->ti.c_lflag & ICANON))
{

View File

@ -303,6 +303,9 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
if (fixup_mmaps_after_fork ())
api_fatal ("recreate_mmaps_after_fork_failed");
if (fixup_shms_after_fork ())
api_fatal ("recreate_shm areas after fork failed");
/* Set thread local stuff to zero. Under Windows 95/98 this is sometimes
non-zero, for some reason.
FIXME: There is a memory leak here after a fork. */

View File

@ -0,0 +1,135 @@
/* cygserver.h
Copyright 2001 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _CYGSERVER_H_
#define _CYGSERVER_H_
#define MAX_REQUEST_SIZE 128
#define CYGWIN_SERVER_VERSION_MAJOR 1
#define CYGWIN_SERVER_VERSION_API 1
#define CYGWIN_SERVER_VERSION_MINOR 0
#define CYGWIN_SERVER_VERSION_PATCH 0
typedef enum {
CYGSERVER_UNKNOWN=0,
CYGSERVER_OK=1,
CYGSERVER_DEAD=2
} cygserver_states;
typedef enum {
CYGSERVER_REQUEST_INVALID = 0,
CYGSERVER_REQUEST_GET_VERSION,
CYGSERVER_REQUEST_ATTACH_TTY,
CYGSERVER_REQUEST_SHUTDOWN,
CYGSERVER_REQUEST_SHM_GET,
CYGSERVER_REQUEST_LAST
} cygserver_request_code;
class request_header
{
public:
ssize_t cb;
cygserver_request_code req_id;
ssize_t error_code;
request_header (cygserver_request_code id, ssize_t ncb) : cb (ncb), req_id (id), error_code (0) {} ;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
;
extern void cygserver_init ();
#define INIT_REQUEST(req,id) \
(req).header.cb = sizeof (req); \
(req).header.req_id = id;
struct request_get_version
{
DWORD major, api, minor, patch;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
;
struct request_shutdown
{
int foo;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
;
struct request_attach_tty
{
DWORD pid, master_pid;
HANDLE from_master, to_master;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
;
class client_request
{
public:
client_request (cygserver_request_code id, ssize_t data_size);
virtual void send (transport_layer_base *conn);
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, class process_cache *cache);
#endif
virtual operator struct request_header ();
cygserver_request_code req_id () {return header.req_id;};
virtual ~client_request();
request_header header;
char *buffer;
};
class client_request_get_version : public client_request
{
public:
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, class process_cache *cache);
#endif
client_request_get_version::client_request_get_version();
struct request_get_version version;
};
class client_request_shutdown : public client_request
{
public:
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, class process_cache *cache);
#endif
client_request_shutdown ();
};
class client_request_attach_tty : public client_request
{
public:
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *conn, class process_cache *cache);
#endif
client_request_attach_tty ();
client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master);
HANDLE from_master () {return req.from_master;};
HANDLE to_master () {return req.to_master;};
struct request_attach_tty req;
};
extern int cygserver_request (client_request *);
#endif /* _CYGSERVER+H+ */

View File

@ -0,0 +1,84 @@
/* cygserver_process.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _CYGSERVER_PROCESS_
#define _CYGSERVER_PROCESS_
/* needs threaded_queue.h */
class process_cleanup:public queue_request
{
public:
virtual void process ();
process_cleanup (class process *nprocess) : theprocess (nprocess) {};
private:
class process * theprocess;
};
class process_process_param:public queue_process_param
{
class process_cache *cache;
public:
DWORD request_loop ();
process_process_param ():queue_process_param (true) {};
};
class cleanup_routine
{
public:
cleanup_routine () : next (NULL) {};
class cleanup_routine * next;
/* MUST BE SYNCHRONOUS */
virtual void cleanup (long winpid);
};
class process
{
public:
HANDLE handle ();
long winpid;
process (long);
~process ();
DWORD exit_code ();
class process * next;
long refcount;
bool add_cleanup_routine (class cleanup_routine *);
void cleanup ();
private:
/* used to prevent races-on-delete */
CRITICAL_SECTION access;
volatile long cleaning_up;
class cleanup_routine *head;
HANDLE thehandle;
DWORD _exit_status;
};
class process_cache:public threaded_queue
{
public:
process_cache (unsigned int initial_workers);
virtual ~ process_cache ();
class process *process (long);
/* remove a process from the cache */
int handle_snapshot (HANDLE *, class process **, ssize_t, int);
void remove_process (class process *);
/* threaded_queue methods */
void process_requests ();
HANDLE cache_add_trigger;
private:
virtual void add_task (class process *);
class process *head;
CRITICAL_SECTION cache_write_access;
};
#endif /* _CYGSERVER_PROCESS_ */

View File

@ -0,0 +1,32 @@
/* cygserver.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _CYGSERVER_TRANSPORT_
#define _CYGSERVER_TRANSPORT_
class transport_layer_base *create_server_transport();
/* the base class does nothing. */
class transport_layer_base
{
public:
virtual void listen ();
virtual class transport_layer_base * accept ();
virtual void close ();
virtual ssize_t read (char *buf, size_t len);
virtual ssize_t write (char *buf, size_t len);
virtual bool connect();
virtual void impersonate_client ();
virtual void revert_to_self ();
transport_layer_base ();
};
#endif /* _CYGSERVER_TRANSPORT_ */

View File

@ -0,0 +1,39 @@
/* cygserver.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _CYGSERVER_TRANSPORT_PIPES_
#define _CYGSERVER_TRANSPORT_PIPES_
/* Named pipes based transport, for security on NT */
class transport_layer_pipes : public transport_layer_base
{
public:
virtual void listen ();
virtual class transport_layer_pipes * accept ();
virtual void close ();
virtual ssize_t read (char *buf, size_t len);
virtual ssize_t write (char *buf, size_t len);
virtual bool connect();
virtual void impersonate_client ();
virtual void revert_to_self ();
transport_layer_pipes ();
private:
/* for pipe based communications */
void init_security ();
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sec_none_nih, sec_all_nih;
char pipe_name [MAX_PATH];
HANDLE pipe;
bool inited;
transport_layer_pipes (HANDLE new_pipe);
};
#endif /* _CYGSERVER_TRANSPORT_PIPES_ */

View File

@ -0,0 +1,33 @@
/* cygserver.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _CYGSERVER_TRANSPORT_SOCKETS_
#define _CYGSERVER_TRANSPORT_SOCKETS_
class transport_layer_sockets : public transport_layer_base
{
public:
virtual void listen ();
virtual class transport_layer_sockets * accept ();
virtual void close ();
virtual ssize_t read (char *buf, size_t len);
virtual ssize_t write (char *buf, size_t len);
virtual bool connect();
transport_layer_sockets ();
private:
/* for socket based communications */
int fd;
struct sockaddr sockdetails;
int sdlen;
transport_layer_sockets (int newfd);
};
#endif /* _CYGSERVER_TRANSPORT_SOCKETS_ */

View File

@ -1,9 +1,9 @@
/* $OpenBSD: fnmatch.h,v 1.5 2000/03/24 17:13:23 millert Exp $ */
/* $NetBSD: fnmatch.h,v 1.5 1994/10/26 00:55:53 cgd Exp $ */
/* $OpenBSD: fnmatch.h,v 1.5 2000/03/24 17:13:23 millert Exp $ */
/* $NetBSD: fnmatch.h,v 1.5 1994/10/26 00:55:53 cgd Exp $ */
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -15,8 +15,8 @@
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
@ -33,29 +33,31 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
*/
#ifndef _FNMATCH_H_
#define _FNMATCH_H_
#ifndef _FNMATCH_H_
#define _FNMATCH_H_
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOSYS 2 /* Function not supported (unused). */
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOSYS 2 /* Function not supported (unused). */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#ifndef _POSIX_SOURCE
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_IGNORECASE FNM_CASEFOLD
#define FNM_FILE_NAME FNM_PATHNAME
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_IGNORECASE FNM_CASEFOLD
#define FNM_FILE_NAME FNM_PATHNAME
#endif
#include <sys/cdefs.h>
__BEGIN_DECLS
int fnmatch __P((const char *, const char *, int));
int fnmatch __P((const char *, const char *, int));
__END_DECLS
#endif /* !_FNMATCH_H_ */

View File

@ -0,0 +1,52 @@
/* sys/ipc.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef _SYS_IPC_H
#define _SYS_IPC_H
/* sys/types must be included before sys/ipc.h. We aren't meant to automatically
* include it however
*/
struct ipc_perm {
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
mode_t mode;
};
/* the mode flags used with the _get functions use the low order 9 bits for a mode
* request
*/
#define IPC_CREAT 0x0200
#define IPC_EXCL 0x0400
#define IPC_NOWAIT 0x0800
/* this is a value that will _never_ be a valid key from ftok */
#define IPC_PRIVATE -2
#define IPC_RMID 0x0003
#define IPC_SET 0x0002
#define IPC_STAT 0x0001
key_t ftok(const char *, int);
#endif /* _SYS_IPC_H */
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,89 @@
/* sys/shm.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef _SYS_SHM_H
#define _SYS_SHM_H
#include <sys/ipc.h>
#define SHM_RDONLY 1
/* 64 Kb was hardcoded for x86. MS states this may change, but we need it in the header
* file.
*/
#define SHMLBA 65536
#define SHM_RND 1
typedef long int shmatt_t;
#if defined(__INSIDE_CYGWIN__) && defined(__cplusplus)
class _shmattach {
public:
void *data;
int shmflg;
class _shmattach *next;
};
class shmid_ds {
public:
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
void *mapptr;
};
class shmnode {
public:
class shmid_ds * shmds;
int shm_id;
class shmnode *next;
key_t key;
HANDLE filemap;
HANDLE attachmap;
class _shmattach *attachhead;
};
#else
/* this is what we return when queried. It has no bitwise correspondence
* the internal structures
*/
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
shmatt_t shm_nattch;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
};
#endif /* __INSIDE_CYGWIN__ */
void *shmat(int, const void *, int);
int shmctl(int, int, struct shmid_ds *);
int shmdt(const void *);
int shmget(key_t, size_t, int);
#endif /* _SYS_SHM_H */
#ifdef __cplusplus
}
#endif

39
winsup/cygwin/ipc.cc Normal file
View File

@ -0,0 +1,39 @@
/* ipc.cc: Single unix specification IPC interface for Cygwin
Copyright 2001 Red Hat, Inc.
Originally written by Robert Collins <robert.collins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <sys/ipc.h>
#include <sys/stat.h>
extern "C"
{
/* Notes: we return a valid key even if id's low order 8 bits are 0. */
key_t
ftok(const char *path, int id)
{
struct stat statbuf;
if (stat(path, &statbuf))
{
/* stat set the appropriate errno for us */
return (key_t) -1;
}
/* dev_t is short for cygwin
* ino_t is long for cygwin
* and we need 8 bits for the id.
* thus key_t is long long.
*/
return ((long long) statbuf.st_dev << (5*8)) | (statbuf.st_ino << (8) ) | (id & 0x00ff);
}
}

View File

@ -201,6 +201,8 @@ extern HANDLE hexec_proc;
/* For mmaps across fork(). */
int __stdcall fixup_mmaps_after_fork ();
/* for shm areas across fork (). */
int __stdcall fixup_shms_after_fork ();
void __stdcall fill_rusage (struct rusage *, HANDLE);
void __stdcall add_rusage (struct rusage *, struct rusage *);

View File

@ -204,6 +204,8 @@ extern SECURITY_ATTRIBUTES *__stdcall __sec_user (PVOID sa_buf, PSID sid2, BOOL
int __stdcall NTReadEA (const char *file, const char *attrname, char *buf, int len);
BOOL __stdcall NTWriteEA (const char *file, const char *attrname, const char *buf, int len);
PSECURITY_DESCRIPTOR alloc_sd (uid_t uid, gid_t gid, const char *logsrv, int attribute,
PSECURITY_DESCRIPTOR sd_ret, DWORD *sd_size_ret);
extern inline SECURITY_ATTRIBUTES *
sec_user_nih (char sa_buf[], PSID sid = NULL)

499
winsup/cygwin/shm.cc Normal file
View File

@ -0,0 +1,499 @@
/* shm.cc: Single unix specification IPC interface for Cygwin
Copyright 2001 Red Hat, Inc.
Originally written by Robert Collins <robert.collins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <sys/stat.h>
#include <errno.h>
#include "cygerrno.h"
#include <unistd.h>
#include "security.h"
#include "fhandler.h"
#include "path.h"
#include "dtable.h"
#include "cygheap.h"
#include <stdio.h>
#include "thread.h"
#include <sys/shm.h>
#include "perprocess.h"
#include "cygserver_shm.h"
// FIXME IS THIS CORRECT
/* Implementation notes: We use two shared memory regions per key:
* One for the control structure, and one for the shared memory.
* While this has a higher overhead tham a single shared area,
* It allows more flexability. As the entire code is transparent to the user
* We can merge these in the future should it be needed.
*/
extern "C" size_t
getsystemallocgranularity ()
{
SYSTEM_INFO sysinfo;
static size_t buffer_offset = 0;
if (buffer_offset)
return buffer_offset;
GetSystemInfo (&sysinfo);
buffer_offset = sysinfo.dwAllocationGranularity;
return buffer_offset;
}
client_request_shm::client_request_shm (int ntype, int nshm_id):
client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
{
buffer = (char *) &parameters;
parameters.in.shm_id = nshm_id;
parameters.in.type = SHM_REATTACH;
parameters.in.pid = GetCurrentProcessId ();
}
client_request_shm::client_request_shm (int ntype, int nshm_id, pid_t npid):
client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
{
buffer = (char *) &parameters;
parameters.in.shm_id = nshm_id;
parameters.in.type = ntype;
parameters.in.pid = npid;
}
client_request_shm::client_request_shm (key_t nkey, size_t nsize,
int nshmflg,
char psdbuf[4096],
pid_t npid):
client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters))
{
buffer = (char *) &parameters;
parameters.in.key = nkey;
parameters.in.size = nsize;
parameters.in.shmflg = nshmflg;
parameters.in.type = SHM_CREATE;
parameters.in.pid = npid;
memcpy (parameters.in.sd_buf, psdbuf, 4096);
}
static shmnode *shm_head = NULL;
static shmnode *
build_inprocess_shmds (HANDLE hfilemap, HANDLE hattachmap, key_t key,
int shm_id)
{
HANDLE filemap = hfilemap;
void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0);
if (!mapptr)
{
CloseHandle (hfilemap);
CloseHandle (hattachmap);
//FIXME: close filemap and free the mutex
/* we couldn't access the mapped area with the requested permissions */
set_errno (EACCES);
return NULL;
}
/* Now get the user data */
HANDLE attachmap = hattachmap;
shmid_ds *shmtemp = new shmid_ds;
if (!shmtemp)
{
system_printf ("failed to malloc shm node\n");
set_errno (ENOMEM);
UnmapViewOfFile (mapptr);
CloseHandle (filemap);
CloseHandle (attachmap);
/* exit mutex */
return NULL;
}
/* get the system node data */
*shmtemp = *(shmid_ds *) mapptr;
/* process local data */
shmnode *tempnode = new shmnode;
tempnode->filemap = filemap;
tempnode->attachmap = attachmap;
shmtemp->mapptr = mapptr;
/* no need for InterlockedExchange here, we're serialised by the global mutex */
tempnode->shmds = shmtemp;
tempnode->shm_id = shm_id;
tempnode->key = key;
tempnode->next = shm_head;
tempnode->attachhead = NULL;
shm_head = tempnode;
/* FIXME: leave the system wide shm mutex */
return tempnode;
}
int __stdcall
fixup_shms_after_fork ()
{
shmnode *tempnode = shm_head;
while (tempnode)
{
void *newshmds =
MapViewOfFile (tempnode->filemap, FILE_MAP_WRITE, 0, 0, 0);
if (!newshmds)
{
/* don't worry about handle cleanup, we're dying! */
system_printf ("failed to reattach to shm control file view %x\n",
tempnode);
return 1;
}
tempnode->shmds = (class shmid_ds *) newshmds;
tempnode->shmds->mapptr = newshmds;
_shmattach *attachnode = tempnode->attachhead;
while (attachnode)
{
void *newdata = MapViewOfFileEx (tempnode->attachmap,
(attachnode->shmflg & SHM_RDONLY) ?
FILE_MAP_READ : FILE_MAP_WRITE, 0,
0, 0, attachnode->data);
if (newdata != attachnode->data)
{
/* don't worry about handle cleanup, we're dying! */
system_printf ("failed to reattach to mapped file view %x\n",
attachnode->data);
return 1;
}
attachnode = attachnode->next;
}
tempnode = tempnode->next;
}
return 0;
}
/* this is ugly. Yes, I know that.
* FIXME: abstract the lookup functionality,
* So that it can be an array, list, whatever without us being worried
*/
/* FIXME: after fork, every memory area needs to have the attach count
* incremented. This should be done in the server?
*/
/* FIXME: tell the daemon when we attach, so at process close it can clean up
* the attach count
*/
extern "C" void *
shmat (int shmid, const void *shmaddr, int shmflg)
{
shmnode *tempnode = shm_head;
while (tempnode && tempnode->shm_id != shmid)
tempnode = tempnode->next;
if (!tempnode)
{
/* couldn't find a currently open shm control area for the key - probably because
* shmget hasn't been called.
* Allocate a new control block - this has to be handled by the daemon */
client_request_shm *req =
new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ());
int rc;
if ((rc = cygserver_request (req)))
{
delete req;
set_errno (ENOSYS); /* daemon communication failed */
return (void *) -1;
}
if (req->header.error_code) /* shm_get failed in the daemon */
{
set_errno (req->header.error_code);
delete req;
return (void *) -1;
}
/* we've got the id, now we open the memory area ourselves.
* This tests security automagically
* FIXME: make this a method of shmnode ?
*/
tempnode =
build_inprocess_shmds (req->parameters.out.filemap,
req->parameters.out.attachmap,
req->parameters.out.key,
req->parameters.out.shm_id);
delete req;
if (!tempnode)
return (void *) -1;
}
class shmid_ds *shm = tempnode->shmds;
if (shmaddr)
{
//FIXME: requested base address ?! (Don't forget to fix the fixup_after_fork too)
set_errno (EINVAL);
return (void *) -1;
}
void *rv = MapViewOfFile (tempnode->attachmap,
(shmflg & SHM_RDONLY) ? FILE_MAP_READ :
FILE_MAP_WRITE, 0, 0, 0);
if (!rv)
{
//FIXME: translate GetLastError()
set_errno (EACCES);
return (void *) -1;
}
/* tell the daemon we have attached */
client_request_shm *req =
new client_request_shm (SHM_ATTACH, shmid);
int rc;
if ((rc = cygserver_request (req)))
{
debug_printf ("failed to tell deaemon that we have attached\n");
}
delete req;
_shmattach *attachnode = new _shmattach;
attachnode->data = rv;
attachnode->shmflg = shmflg;
attachnode->next =
(_shmattach *) InterlockedExchangePointer (&tempnode->attachhead,
attachnode);
return rv;
}
/* FIXME: tell the daemon when we detach so it doesn't cleanup incorrectly.
*/
extern "C" int
shmdt (const void *shmaddr)
{
/* this should be "rare" so a hefty search is ok. If this is common, then we
* should alter the data structs to allow more optimisation
*/
}
//FIXME: who is allowed to perform STAT?
extern "C" int
shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
shmnode *tempnode = shm_head;
while (tempnode && tempnode->shm_id != shmid)
tempnode = tempnode->next;
if (!tempnode)
{
/* couldn't find a currently open shm control area for the key - probably because
* shmget hasn't been called.
* Allocate a new control block - this has to be handled by the daemon */
client_request_shm *req =
new client_request_shm (SHM_REATTACH, shmid, GetCurrentProcessId ());
int rc;
if ((rc = cygserver_request (req)))
{
delete req;
set_errno (ENOSYS); /* daemon communication failed */
return -1;
}
if (req->header.error_code) /* shm_get failed in the daemon */
{
set_errno (req->header.error_code);
delete req;
return -1;
}
/* we've got the id, now we open the memory area ourselves.
* This tests security automagically
* FIXME: make this a method of shmnode ?
*/
tempnode =
build_inprocess_shmds (req->parameters.out.filemap,
req->parameters.out.attachmap,
req->parameters.out.key,
req->parameters.out.shm_id);
delete req;
if (!tempnode)
return -1;
}
switch (cmd)
{
case IPC_STAT:
buf->shm_perm = tempnode->shmds->shm_perm;
buf->shm_segsz = tempnode->shmds->shm_segsz;
buf->shm_lpid = tempnode->shmds->shm_lpid;
buf->shm_cpid = tempnode->shmds->shm_cpid;
buf->shm_nattch = tempnode->shmds->shm_nattch;
buf->shm_atime = tempnode->shmds->shm_atime;
buf->shm_dtime = tempnode->shmds->shm_dtime;
buf->shm_ctime = tempnode->shmds->shm_ctime;
break;
case IPC_RMID:
{
/* TODO: check permissions. Or possibly, the daemon gets to be the only
* one with write access to the memory area?
*/
if (tempnode->shmds->shm_nattch)
system_printf
("call to shmctl with cmd= IPC_RMID when memory area still has"
" attachees\n");
/* how does this work?
* we mark the ds area as "deleted", and the at and get calls all fail from now on
* on, when nattch becomes 0, the mapped data area is destroyed.
* and each process, as they touch this area detaches. eventually only the
* daemon has an attach. The daemon gets asked to detach immediately.
*/
#if 0
//waiting for the daemon to handle terminating process's
client_request_shm *req =
new client_request_shm (SHM_DEL, shmid, GetCurrentProcessId ());
int rc;
if ((rc = cygserver_request (req)))
{
delete req;
set_errno (ENOSYS); /* daemon communication failed */
return -1;
}
if (req->header.error_code) /* shm_del failed in the daemon */
{
set_errno (req->header.error_code);
delete req;
return -1;
}
/* the daemon has deleted it's references */
/* now for us */
#endif
}
break;
case IPC_SET:
default:
set_errno (EINVAL);
return -1;
}
return 0;
}
/* FIXME: evaluate getuid() and getgid() against the requested mode. Then
* choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ
* appropriately
*/
/* FIXME: shmid should be a verifyable object
*/
/* FIXME: on NT we should check everything against the SD. On 95 we just emulate.
*/
extern "C" int
shmget (key_t key, size_t size, int shmflg)
{
DWORD sd_size = 4096;
char sd_buf[4096];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
/* create a sd for our open requests based on shmflag & 0x01ff */
psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (),
shmflg & 0x01ff, psd, &sd_size);
if (key == (key_t) - 1)
{
set_errno (ENOENT);
return -1;
}
/* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide
* to prevent races on shmget.
*/
/* walk the list of currently open keys and return the id if found
*/
shmnode *tempnode = shm_head;
while (tempnode)
{
if (tempnode->key == key && key != IPC_PRIVATE)
{
// FIXME: free the mutex
if (size && tempnode->shmds->shm_segsz < size)
{
set_errno (EINVAL);
return -1;
}
if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
{
set_errno (EEXIST);
// FIXME: free the mutex
return -1;
}
// FIXME: do we need to other tests of the requested mode with the
// tempnode->shmid mode ? testcase on unix needed.
// FIXME do we need a security test? We are only examining the keys we already have open.
// FIXME: what are the sec implications for fork () if we don't check here?
return tempnode->shm_id;
}
tempnode = tempnode->next;
}
/* couldn't find a currently open shm control area for the key.
* Allocate a new control block - this has to be handled by the daemon */
client_request_shm *req =
new client_request_shm (key, size, shmflg, sd_buf,
GetCurrentProcessId ());
int rc;
if ((rc = cygserver_request (req)))
{
delete req;
set_errno (ENOSYS); /* daemon communication failed */
return -1;
}
if (req->header.error_code) /* shm_get failed in the daemon */
{
set_errno (req->header.error_code);
delete req;
return -1;
}
/* we've got the id, now we open the memory area ourselves.
* This tests security automagically
* FIXME: make this a method of shmnode ?
*/
shmnode *shmtemp = build_inprocess_shmds (req->parameters.out.filemap,
req->parameters.out.attachmap,
key,
req->parameters.out.shm_id);
delete req;
if (shmtemp)
return shmtemp->shm_id;
return -1;
#if 0
/* fill out the node data */
shmtemp->shm_perm.cuid = getuid ();
shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid;
shmtemp->shm_perm.cgid = getgid ();
shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid;
shmtemp->shm_perm.mode = shmflg & 0x01ff;
shmtemp->shm_lpid = 0;
shmtemp->shm_nattch = 0;
shmtemp->shm_atime = 0;
shmtemp->shm_dtime = 0;
shmtemp->shm_ctime = time (NULL);
shmtemp->shm_segsz = size;
*(shmid_ds *) mapptr = *shmtemp;
shmtemp->filemap = filemap;
shmtemp->attachmap = attachmap;
shmtemp->mapptr = mapptr;
#endif
}

249
winsup/cygwin/threaded_queue.cc Executable file
View File

@ -0,0 +1,249 @@
/* threaded_queue.cc
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <windows.h>
#include <sys/types.h>
#include "wincap.h"
#include "threaded_queue.h"
#define DEBUG 1
#define debug_printf if (DEBUG) printf
/* threaded_queue */
DWORD WINAPI
worker_function (LPVOID LpParam)
{
class threaded_queue *queue = (class threaded_queue *) LpParam;
class queue_request *request;
/* FIXME use a threadsafe pop instead for speed? */
while (queue->active)
{
EnterCriticalSection (&queue->queuelock);
while (!queue->request && queue->active)
{
LeaveCriticalSection (&queue->queuelock);
DWORD rc = WaitForSingleObject (queue->event, INFINITE);
if (rc == WAIT_FAILED)
{
printf ("Wait for event failed\n");
queue->running--;
ExitThread (0);
}
EnterCriticalSection (&queue->queuelock);
}
if (!queue->active)
{
queue->running--;
LeaveCriticalSection (&queue->queuelock);
ExitThread (0);
}
/* not needed, but it is efficient */
request =
(class queue_request *) InterlockedExchangePointer (&queue->request,
queue->request->
next);
LeaveCriticalSection (&queue->queuelock);
request->process ();
delete request;
}
queue->running--;
ExitThread (0);
}
void
threaded_queue::create_workers ()
{
InitializeCriticalSection (&queuelock);
if ((event = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create event queue (%lu), terminating\n",
GetLastError ());
exit (1);
}
active = true;
/* FIXME: Use a stack pair and create threads on the fly whenever
* we have to to service a request.
*/
for (unsigned int i = 0; i < initial_workers; i++)
{
HANDLE hThread;
DWORD tid;
hThread = CreateThread (NULL, 0, worker_function, this, 0, &tid);
if (hThread == NULL)
{
printf ("Failed to create thread (%lu), terminating\n",
GetLastError ());
exit (1);
}
CloseHandle (hThread);
running++;
}
}
void
threaded_queue::cleanup ()
{
/* harvest the threads */
active = false;
/* kill the request processing loops */
queue_process_param *reqloop;
/* make sure we don't race with a incoming request creation */
EnterCriticalSection (&queuelock);
reqloop =
(queue_process_param *) InterlockedExchangePointer (&process_head, NULL);
while (reqloop)
{
queue_process_param *t = reqloop;
reqloop = reqloop->next;
delete t;
}
LeaveCriticalSection (&queuelock);
if (!running)
return;
printf ("Waiting for current queue threads to terminate\n");
for (int n = running; n; n--)
PulseEvent (event);
while (running)
sleep (1);
DeleteCriticalSection (&queuelock);
CloseHandle (event);
}
/* FIXME: return success or failure */
void
threaded_queue::add (queue_request * therequest)
{
/* safe to not "Try" because workers don't hog this, they wait on the event
*/
EnterCriticalSection (&queuelock);
if (!running)
{
printf ("No worker threads to handle request!\n");
}
if (!request)
request = therequest;
else
{
/* add to the queue end. */
queue_request *listrequest = request;
while (listrequest->next)
listrequest = listrequest->next;
listrequest->next = therequest;
}
PulseEvent (event);
LeaveCriticalSection (&queuelock);
}
/* FIXME: return success or failure rather than quitting */
void
threaded_queue::process_requests (queue_process_param * params,
threaded_queue_thread_function *
request_loop)
{
if (params->start (request_loop, this) == false)
exit (1);
params->next =
(queue_process_param *) InterlockedExchangePointer (&process_head,
params);
}
/* queue_process_param */
/* How does a constructor return an error? */
queue_process_param::queue_process_param (bool ninterruptible):running (false), shutdown (false),
interruptible
(ninterruptible)
{
if (!interruptible)
return;
debug_printf ("creating an interruptible processing thread\n");
if ((interrupt = CreateEvent (NULL, FALSE, FALSE, NULL)) == NULL)
{
printf ("Failed to create interrupt event (%lu), terminating\n",
GetLastError ());
exit (1);
}
}
queue_process_param::~queue_process_param ()
{
if (running)
stop ();
if (!interruptible)
return;
CloseHandle (interrupt);
}
bool
queue_process_param::start (threaded_queue_thread_function * request_loop,
threaded_queue * thequeue)
{
queue = thequeue;
hThread = CreateThread (NULL, 0, request_loop, this, 0, &tid);
if (hThread)
{
running = true;
return true;
}
printf ("Failed to create thread (%lu), terminating\n", GetLastError ());
return false;
}
void
queue_process_param::stop ()
{
if (interruptible)
{
InterlockedExchange (&shutdown, true);
PulseEvent (interrupt);
/* Wait up to 50 ms for the thread to exit. If it doesn't _and_ we get
* scheduled again, we print an error and exit. We _should_ loop or
* try resignalling. We don't want to hand here though...
*/
int n = 5;
while (n-- && WaitForSingleObject (hThread, 1000) == WAIT_TIMEOUT);
if (!n)
{
printf ("Process thread didn't shutdown cleanly after 200ms!\n");
exit (1);
}
else
running = false;
}
else
{
printf ("killing request loop thread %ld\n", tid);
int rc;
if (!(rc = TerminateThread (hThread, 0)))
{
printf ("error shutting down request loop worker thread\n");
}
running = false;
}
CloseHandle (hThread);
}
/* queue_request */
queue_request::queue_request ():next (NULL)
{
}
void
queue_request::process (void)
{
printf ("\n**********************************************\n"
"Oh no! we've hit the base queue_request process() function, and this indicates a coding\n"
"fault !!!\n" "***********************************************\n");
}

67
winsup/cygwin/threaded_queue.h Executable file
View File

@ -0,0 +1,67 @@
/* threaded_queue.h
Copyright 2001 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _THREADED_QUEUE_
#define _THREADED_QUEUE_
/* a specific request */
class queue_request
{
public:
class queue_request *next;
virtual void process ();
queue_request();
};
typedef DWORD WINAPI threaded_queue_thread_function (LPVOID);
/* parameters for a request finding and submitting loop */
class queue_process_param
{
public:
bool start (threaded_queue_thread_function *, class threaded_queue *);
void stop ();
bool running;
long int shutdown;
class queue_process_param * next;
class threaded_queue *queue;
queue_process_param (bool ninterruptible);
~queue_process_param ();
bool interruptible;
HANDLE interrupt;
HANDLE hThread;
DWORD tid;
};
/* a queue to allocate requests from n submission loops to x worker threads */
class threaded_queue
{
public:
CRITICAL_SECTION queuelock;
HANDLE event;
bool active;
queue_request * request;
unsigned int initial_workers;
unsigned int running;
void create_workers ();
void cleanup ();
void add (queue_request *);
void process_requests (queue_process_param *, threaded_queue_thread_function *);
threaded_queue () : active (false), request (NULL), initial_workers (1), running (0), process_head (NULL) {};
private:
queue_request *process_head;
};
#endif /* _THREADED_QUEUE_ */

View File

@ -24,6 +24,8 @@ details. */
#include "sync.h"
#include "sigproc.h"
#include "pinfo.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver.h"
#include "shared_info.h"
extern fhandler_tty_master *tty_master;
@ -396,7 +398,10 @@ tty::common_init (fhandler_pty_master *ptym)
/* Allow the others to open us (for handle duplication) */
if (wincap.has_security () &&
/* FIXME: we shold NOT set the security wide open when the
daemon is running
*/
if (wincap.has_security () && cygserver_running==CYGSERVER_OK &&
(SetKernelObjectSecurity (hMainProc, DACL_SECURITY_INFORMATION,
get_null_sd ()) == FALSE))
small_printf ("Can't set process security, %E");

View File

@ -75,6 +75,8 @@ extern "C" DWORD WINAPI GetLastError (void);
enum codepage_type {ansi_cp, oem_cp};
extern codepage_type current_codepage;
extern int cygserver_running;
/* Used to check if Cygwin DLL is dynamically loaded. */
extern int dynamically_loaded;