Remove some cygserver files.

This commit is contained in:
Christopher Faylor 2003-08-30 16:31:10 +00:00
parent b258e2c63b
commit 6c125ba745
11 changed files with 4 additions and 3786 deletions

View File

@ -1,3 +1,7 @@
2003-08-30 Christopher Faylor <cgf@redhat.com>
Remove some cygserver files.
2003-08-28 Christopher Faylor <cgf@redhat.com>
* sigproc.h: Make some functions regparm.

View File

@ -1,773 +0,0 @@
/* cygserver.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "woutsup.h"
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "cygerrno.h"
#include "cygwin_version.h"
#include "cygwin/cygserver.h"
#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver_transport.h"
// Version string.
static const char version[] = "$Revision$";
/*
* Support function for the XXX_printf () macros in "woutsup.h".
* Copied verbatim from "strace.cc".
*/
static int
getfunc (char *in_dst, const char *func)
{
const char *p;
const char *pe;
char *dst = in_dst;
for (p = func; (pe = strchr (p, '(')); p = pe + 1)
if (isalnum ((int)pe[-1]) || pe[-1] == '_')
break;
else if (isspace ((int)pe[-1]))
{
pe--;
break;
}
if (!pe)
pe = strchr (func, '\0');
for (p = pe; p > func; p--)
if (p != pe && *p == ' ')
{
p++;
break;
}
if (*p == '*')
p++;
while (p < pe)
*dst++ = *p++;
*dst++ = ':';
*dst++ = ' ';
*dst = '\0';
return dst - in_dst;
}
/*
* Support function for the XXX_printf () macros in "woutsup.h".
*/
extern "C" void
__cygserver__printf (const char *const function, const char *const fmt, ...)
{
const DWORD lasterror = GetLastError ();
const int lasterrno = errno;
va_list ap;
char *const buf = (char *) alloca (BUFSIZ);
assert (buf);
int len = 0;
if (function)
len += getfunc (buf, function);
va_start (ap, fmt);
len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap);
va_end (ap);
len += snprintf (buf + len, BUFSIZ - len, "\n");
const int actual = (len > BUFSIZ ? BUFSIZ : len);
write (2, buf, actual);
errno = lasterrno;
SetLastError (lasterror);
return;
}
#ifdef DEBUGGING
int __stdcall
__set_errno (const char *func, int ln, int val)
{
debug_printf ("%s:%d val %d", func, ln, val);
return _impure_ptr->_errno = val;
}
#endif /* DEBUGGING */
GENERIC_MAPPING access_mapping;
static BOOL
setup_privileges ()
{
BOOL rc, ret_val;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES sPrivileges;
rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ;
if (!rc)
{
system_printf ("error opening process token (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid);
if (!rc)
{
system_printf ("error getting privilege luid (%lu)", GetLastError ());
ret_val = FALSE;
goto out;
}
sPrivileges.PrivilegeCount = 1 ;
sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ;
if (!rc)
{
system_printf ("error adjusting privilege level. (%lu)",
GetLastError ());
ret_val = FALSE;
goto out;
}
access_mapping.GenericRead = FILE_READ_DATA;
access_mapping.GenericWrite = FILE_WRITE_DATA;
access_mapping.GenericExecute = 0;
access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA;
ret_val = TRUE;
out:
CloseHandle (hToken);
return ret_val;
}
int
check_and_dup_handle (HANDLE from_process, HANDLE to_process,
HANDLE from_process_token,
DWORD access,
HANDLE from_handle,
HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE)
{
HANDLE local_handle = NULL;
int ret_val = EACCES;
if (from_process != GetCurrentProcess ())
{
if (!DuplicateHandle (from_process, from_handle,
GetCurrentProcess (), &local_handle,
0, bInheritHandle,
DUPLICATE_SAME_ACCESS))
{
system_printf ("error getting handle(%u) to server (%lu)",
(unsigned int)from_handle, GetLastError ());
goto out;
}
} else
local_handle = from_handle;
if (!wincap.has_security ())
assert (!from_process_token);
else
{
char sd_buf [1024];
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf;
DWORD bytes_needed;
PRIVILEGE_SET ps;
DWORD ps_len = sizeof (ps);
BOOL status;
if (!GetKernelObjectSecurity (local_handle,
(OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION),
sd, sizeof (sd_buf), &bytes_needed))
{
system_printf ("error getting handle SD (%lu)", GetLastError ());
goto out;
}
MapGenericMask (&access, &access_mapping);
if (!AccessCheck (sd, from_process_token, access, &access_mapping,
&ps, &ps_len, &access, &status))
{
system_printf ("error checking access rights (%lu)",
GetLastError ());
goto out;
}
if (!status)
{
system_printf ("access to object denied");
goto out;
}
}
if (!DuplicateHandle (from_process, from_handle,
to_process, to_handle_ptr,
access, bInheritHandle, 0))
{
system_printf ("error getting handle to client (%lu)", GetLastError ());
goto out;
}
// verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr);
ret_val = 0;
out:
if (local_handle && from_process != GetCurrentProcess ())
CloseHandle (local_handle);
return (ret_val);
}
/*
* client_request_attach_tty::serve ()
*/
void
client_request_attach_tty::serve (transport_layer_base *const conn,
process_cache *)
{
assert (conn);
assert (!error_code ());
if (!wincap.has_security ())
{
syscall_printf ("operation only supported on systems with security");
error_code (EINVAL);
msglen (0);
return;
}
if (msglen () != sizeof (req))
{
syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
sizeof (req), msglen ());
error_code (EINVAL);
msglen (0);
return;
}
msglen (0); // Until we fill in some fields.
// verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld",
// req.master_pid, req.from_master, req.to_master,
// req.pid);
// verbose: debug_printf ("opening process %ld", req.master_pid);
const HANDLE from_process_handle =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
if (!from_process_handle)
{
system_printf ("error opening `from' process, error = %lu",
GetLastError ());
error_code (EACCES);
return;
}
// verbose: debug_printf ("opening process %ld", req.pid);
const HANDLE to_process_handle =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
if (!to_process_handle)
{
system_printf ("error opening `to' process, error = %lu",
GetLastError ());
CloseHandle (from_process_handle);
error_code (EACCES);
return;
}
// verbose: debug_printf ("Impersonating client");
conn->impersonate_client ();
HANDLE token_handle = NULL;
// verbose: debug_printf ("about to open thread token");
const DWORD rc = OpenThreadToken (GetCurrentThread (),
TOKEN_QUERY,
TRUE,
&token_handle);
// verbose: debug_printf ("opened thread token, rc=%lu", rc);
conn->revert_to_self ();
if (!rc)
{
system_printf ("error opening thread token, error = %lu",
GetLastError ());
CloseHandle (from_process_handle);
CloseHandle (to_process_handle);
error_code (EACCES);
return;
}
// From this point on, a reply body is returned to the client.
const HANDLE from_master = req.from_master;
const HANDLE to_master = req.to_master;
req.from_master = NULL;
req.to_master = NULL;
msglen (sizeof (req));
if (from_master)
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_READ,
from_master,
&req.from_master, TRUE) != 0)
{
system_printf ("error duplicating from_master handle, error = %lu",
GetLastError ());
error_code (EACCES);
}
if (to_master)
if (check_and_dup_handle (from_process_handle, to_process_handle,
token_handle,
GENERIC_WRITE,
to_master,
&req.to_master, TRUE) != 0)
{
system_printf ("error duplicating to_master handle, error = %lu",
GetLastError ());
error_code (EACCES);
}
CloseHandle (from_process_handle);
CloseHandle (to_process_handle);
CloseHandle (token_handle);
debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
req.master_pid, from_master, to_master,
req.pid, req.from_master, req.to_master);
return;
}
void
client_request_get_version::serve (transport_layer_base *, process_cache *)
{
assert (!error_code ());
if (msglen ())
syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
msglen (sizeof (version));
version.major = CYGWIN_SERVER_VERSION_MAJOR;
version.api = CYGWIN_SERVER_VERSION_API;
version.minor = CYGWIN_SERVER_VERSION_MINOR;
version.patch = CYGWIN_SERVER_VERSION_PATCH;
}
class server_request : public queue_request
{
public:
server_request (transport_layer_base *const conn, process_cache *const cache)
: _conn (conn), _cache (cache)
{}
virtual ~server_request ()
{
safe_delete (_conn);
}
virtual void process ()
{
client_request::handle_request (_conn, _cache);
}
private:
transport_layer_base *const _conn;
process_cache *const _cache;
};
class server_submission_loop : public queue_submission_loop
{
public:
server_submission_loop (threaded_queue *const queue,
transport_layer_base *const transport,
process_cache *const cache)
: queue_submission_loop (queue, false),
_transport (transport),
_cache (cache)
{
assert (_transport);
assert (_cache);
}
private:
transport_layer_base *const _transport;
process_cache *const _cache;
virtual void request_loop ();
};
/* FIXME: this is a little ugly. What we really want is to wait on
* two objects: one for the pipe/socket, and one for being told to
* shutdown. Otherwise this will stay a problem (we won't actually
* shutdown until the request _AFTER_ the shutdown request. And
* sending ourselves a request is ugly
*/
void
server_submission_loop::request_loop ()
{
/* I'd like the accepting thread's priority to be above any "normal"
* thread in the system to avoid overflowing the listen queue (for
* sockets; similar issues exist for named pipes); but, for example,
* a normal priority thread in a foregrounded process is boosted to
* THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current
* thread's priority to a level one above that. This fails on
* win9x/ME so assume any failure in that call is due to that and
* simply call again at one priority level lower.
*/
if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1))
if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST))
debug_printf ("failed to raise accept thread priority, error = %lu",
GetLastError ());
while (_running)
{
bool recoverable = false;
transport_layer_base *const conn = _transport->accept (&recoverable);
if (!conn && !recoverable)
{
system_printf ("fatal error on IPC transport: closing down");
return;
}
// EINTR probably implies a shutdown request; so back off for a
// moment to let the main thread take control, otherwise the
// server spins here receiving EINTR repeatedly since the signal
// handler in the main thread doesn't get a chance to be called.
if (!conn && errno == EINTR)
{
if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL))
debug_printf ("failed to reset thread priority, error = %lu",
GetLastError ());
Sleep (0);
if (!SetThreadPriority (GetCurrentThread (),
THREAD_PRIORITY_HIGHEST + 1))
if (!SetThreadPriority (GetCurrentThread (),
THREAD_PRIORITY_HIGHEST))
debug_printf ("failed to raise thread priority, error = %lu",
GetLastError ());
}
if (conn)
_queue->add (safe_new (server_request, conn, _cache));
}
}
client_request_shutdown::client_request_shutdown ()
: client_request (CYGSERVER_REQUEST_SHUTDOWN)
{
// verbose: syscall_printf ("created");
}
void
client_request_shutdown::serve (transport_layer_base *, process_cache *)
{
assert (!error_code ());
if (msglen ())
syscall_printf ("unexpected request body ignored: %lu bytes", msglen ());
/* FIXME: link upwards, and then this becomes a trivial method call to
* only shutdown _this queue_
*/
kill (getpid (), SIGINT);
msglen (0);
}
static sig_atomic_t shutdown_server = false;
static void
handle_signal (const int signum)
{
/* any signal makes us die :} */
shutdown_server = true;
}
/*
* print_usage ()
*/
static void
print_usage (const char *const pgm)
{
printf ("Usage: %s [OPTIONS]\n", pgm);
printf (" -c, --cleanup-threads number of cleanup threads to use\n");
printf (" -h, --help output usage information and exit\n");
printf (" -r, --request-threads number of request threads to use\n");
printf (" -s, --shutdown shutdown the daemon\n");
printf (" -v, --version output version information and exit\n");
}
/*
* print_version ()
*/
static void
print_version (const char *const pgm)
{
char *vn = NULL;
const char *const colon = strchr (version, ':');
if (!colon)
{
vn = strdup ("?");
}
else
{
vn = strdup (colon + 2); // Skip ": "
char *const spc = strchr (vn, ' ');
if (spc)
*spc = '\0';
}
char buf[200];
snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
cygwin_version.dll_major / 1000,
cygwin_version.dll_major % 1000,
cygwin_version.dll_minor,
cygwin_version.api_major,
cygwin_version.api_minor,
cygwin_version.shared_data,
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
cygwin_version.mount_registry,
cygwin_version.dll_build_date);
printf ("%s (cygwin) %s\n", pgm, vn);
printf ("API version %s\n", buf);
printf ("Copyright 2001, 2002 Red Hat, Inc.\n");
printf ("Compiled on %s\n", __DATE__);
free (vn);
}
/*
* main ()
*/
int
main (const int argc, char *argv[])
{
const struct option longopts[] = {
{"cleanup-threads", required_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{"request-threads", required_argument, NULL, 'r'},
{"shutdown", no_argument, NULL, 's'},
{"version", no_argument, NULL, 'v'},
{0, no_argument, NULL, 0}
};
const char opts[] = "c:hr:sv";
int cleanup_threads = 2;
int request_threads = 10;
bool shutdown = false;
const char *pgm = NULL;
if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
pgm = *argv;
else
pgm++;
wincap.init ();
if (wincap.has_security ())
setup_privileges ();
int opt;
while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
switch (opt)
{
case 'c':
cleanup_threads = atoi (optarg);
if (cleanup_threads <= 0)
{
fprintf (stderr,
"%s: number of cleanup threads must be positive\n",
pgm);
exit (1);
}
break;
case 'h':
print_usage (pgm);
return 0;
case 'r':
request_threads = atoi (optarg);
if (request_threads <= 0)
{
fprintf (stderr,
"%s: number of request threads must be positive\n",
pgm);
exit (1);
}
break;
case 's':
shutdown = true;
break;
case 'v':
print_version (pgm);
return 0;
case '?':
fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
exit (1);
}
if (optind != argc)
{
fprintf (stderr, "%s: too many arguments\n", pgm);
exit (1);
}
if (shutdown)
{
/* Setting `cygserver_running' stops the request code making a
* version request, which is not much to the point.
*/
cygserver_running = CYGSERVER_OK;
client_request_shutdown req;
if (req.make_request () == -1 || req.error_code ())
{
fprintf (stderr, "%s: shutdown request failed: %s\n",
pgm, strerror (req.error_code ()));
exit (1);
}
// FIXME: It would be nice to wait here for the daemon to exit.
return 0;
}
#define SIGHANDLE(SIG) \
do \
{ \
struct sigaction act; \
\
act.sa_handler = &handle_signal; \
act.sa_mask = 0; \
act.sa_flags = 0; \
\
if (sigaction (SIG, &act, NULL) == -1) \
{ \
system_printf ("failed to install handler for " #SIG ": %s", \
strerror (errno)); \
exit (1); \
} \
} while (false)
SIGHANDLE (SIGHUP);
SIGHANDLE (SIGINT);
SIGHANDLE (SIGTERM);
print_version (pgm);
setbuf (stdout, NULL);
printf ("daemon starting up");
threaded_queue request_queue (request_threads);
printf (".");
transport_layer_base *const transport = create_server_transport ();
assert (transport);
printf (".");
process_cache cache (cleanup_threads);
printf (".");
server_submission_loop submission_loop (&request_queue, transport, &cache);
printf (".");
request_queue.add_submission_loop (&submission_loop);
printf (".");
if (transport->listen () == -1)
{
exit (1);
}
printf (".");
cache.start ();
printf (".");
request_queue.start ();
printf (".");
printf ("complete\n");
/* TODO: wait on multiple objects - the thread handle for each
* request loop + all the process handles. This should be done by
* querying the request_queue and the process cache for all their
* handles, and then waiting for (say) 30 seconds. after that we
* recreate the list of handles to wait on, and wait again. the
* point of all this abstraction is that we can trivially server
* both sockets and pipes simply by making a new transport, and then
* calling request_queue.process_requests (transport2);
*/
/* WaitForMultipleObjects abort && request_queue && process_queue && signal
-- if signal event then retrigger it
*/
while (!shutdown_server && request_queue.running () && cache.running ())
pause ();
printf ("\nShutdown request received - new requests will be denied\n");
request_queue.stop ();
printf ("All pending requests processed\n");
safe_delete (transport);
printf ("No longer accepting requests - cygwin will operate in daemonless mode\n");
cache.stop ();
printf ("All outstanding process-cache activities completed\n");
printf ("daemon shutdown\n");
return 0;
}

View File

@ -1,528 +0,0 @@
/* cygserver_client.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Egor Duda <deo@logos-m.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifdef __OUTSIDE_CYGWIN__
#include "woutsup.h"
#else
#include "winsup.h"
#endif
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include "cygerrno.h"
#include "cygserver_shm.h"
#include "safe_memory.h"
#include "cygwin/cygserver.h"
#include "cygwin/cygserver_transport.h"
int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
/* On by default during development. For release, we probably want off
* by default.
*/
bool allow_daemon = true; // Nb: inherited by children.
client_request_get_version::client_request_get_version ()
: client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
{
msglen (0); // No parameters for request.
// verbose: syscall_printf ("created");
}
/*
* client_request_get_version::check_version ()
*
* The major version and API version numbers must match exactly. An
* older than expected minor version number is accepted (as long as
* the first numbers match, that is).
*/
bool
client_request_get_version::check_version () const
{
const bool ok = (version.major == CYGWIN_SERVER_VERSION_MAJOR
&& version.api == CYGWIN_SERVER_VERSION_API
&& version.minor <= CYGWIN_SERVER_VERSION_MINOR);
if (!ok)
syscall_printf (("incompatible version of cygwin server: "
"client version %d.%d.%d.%d, "
"server version %ld.%ld.%ld.%ld"),
CYGWIN_SERVER_VERSION_MAJOR,
CYGWIN_SERVER_VERSION_API,
CYGWIN_SERVER_VERSION_MINOR,
CYGWIN_SERVER_VERSION_PATCH,
version.major,
version.api,
version.minor,
version.patch);
return ok;
}
#ifdef __INSIDE_CYGWIN__
client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid,
HANDLE nfrom_master,
HANDLE nto_master)
: client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
{
req.pid = GetCurrentProcessId ();
req.master_pid = nmaster_pid;
req.from_master = nfrom_master;
req.to_master = nto_master;
syscall_printf (("created: pid = %lu, master_pid = %lu, "
"from_master = %lu, to_master = %lu"),
req.pid, req.master_pid, req.from_master, req.to_master);
}
#else /* !__INSIDE_CYGWIN__ */
client_request_attach_tty::client_request_attach_tty ()
: client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
{
// verbose: syscall_printf ("created");
}
#endif /* __INSIDE_CYGWIN__ */
/*
* client_request_attach_tty::send ()
*
* Wraps the base method to provide error handling support. If the
* reply contains a body but is flagged as an error, close any handles
* that have been returned by cygserver and then discard the message
* body, i.e. the client either sees a successful result with handles
* or an unsuccessful result with no handles.
*/
void
client_request_attach_tty::send (transport_layer_base * const conn)
{
client_request::send (conn);
if (msglen () && error_code ())
{
if (from_master ())
CloseHandle (from_master ());
if (to_master ())
CloseHandle (to_master ());
msglen (0);
}
}
client_request::header_t::header_t (const request_code_t request_code,
const size_t msglen)
: msglen (msglen),
request_code (request_code)
{
assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST);
}
// FIXME: also check write and read result for -1.
void
client_request::send (transport_layer_base * const conn)
{
assert (conn);
assert (!(msglen () && !_buf)); // i.e., msglen () implies _buf
assert (msglen () <= _buflen);
{
const ssize_t count = conn->write (&_header, sizeof (_header));
if (count != sizeof (_header))
{
assert (errno);
error_code (errno);
syscall_printf (("request header write failure: "
"only %ld bytes sent of %ld, "
"error = %d(%lu)"),
count, sizeof (_header),
errno, GetLastError ());
return;
}
}
if (msglen ())
{
const ssize_t count = conn->write (_buf, msglen ());
if (count == -1 || (size_t) count != msglen ())
{
assert (errno);
error_code (errno);
syscall_printf (("request body write failure: "
"only %ld bytes sent of %ld, "
"error = %d(%lu)"),
count, msglen (),
errno, GetLastError ());
return;
}
}
// verbose: syscall_printf ("request sent (%ld + %ld bytes)",
// sizeof (_header), msglen ());
{
const ssize_t count = conn->read (&_header, sizeof (_header));
if (count != sizeof (_header))
{
assert (errno);
error_code (errno);
syscall_printf (("reply header read failure: "
"only %ld bytes received of %ld, "
"error = %d(%lu)"),
count, sizeof (_header),
errno, GetLastError ());
return;
}
}
if (msglen () && !_buf)
{
system_printf ("no client buffer for reply body: %ld bytes needed",
msglen ());
error_code (EINVAL);
return;
}
if (msglen () > _buflen)
{
system_printf (("client buffer too small for reply body: "
"have %ld bytes and need %ld"),
_buflen, msglen ());
error_code (EINVAL);
return;
}
if (msglen ())
{
const ssize_t count = conn->read (_buf, msglen ());
if (count == -1 || (size_t) count != msglen ())
{
assert (errno);
error_code (errno);
syscall_printf (("reply body read failure: "
"only %ld bytes received of %ld, "
"error = %d(%lu)"),
count, msglen (),
errno, GetLastError ());
return;
}
}
// verbose: syscall_printf ("reply received (%ld + %ld bytes)",
// sizeof (_header), msglen ());
}
#ifndef __INSIDE_CYGWIN__
/*
* client_request::handle_request ()
*
* A server-side method.
*
* This is a factory method for the client_request subclasses. It
* reads the incoming request header and, based on its request code,
* creates an instance of the appropriate class.
*
* FIXME: If the incoming packet is malformed, the server drops it on
* the floor. Should it try and generate some sort of reply for the
* client? As it is, the client will simply get a broken connection.
*
* FIXME: also check write and read result for -1.
*/
/* static */ void
client_request::handle_request (transport_layer_base *const conn,
process_cache *const cache)
{
// verbose: debug_printf ("about to read");
header_t header;
{
const ssize_t count = conn->read (&header, sizeof (header));
if (count != sizeof (header))
{
syscall_printf (("request header read failure: "
"only %ld bytes received of %ld, "
"error = %d(%lu)"),
count, sizeof (header),
errno, GetLastError ());
return;
}
// verbose: debug_printf ("got header (%ld)", count);
}
client_request *req = NULL;
switch (header.request_code)
{
case CYGSERVER_REQUEST_GET_VERSION:
req = safe_new0 (client_request_get_version);
break;
case CYGSERVER_REQUEST_SHUTDOWN:
req = safe_new0 (client_request_shutdown);
break;
case CYGSERVER_REQUEST_ATTACH_TTY:
req = safe_new0 (client_request_attach_tty);
break;
case CYGSERVER_REQUEST_SHM:
req = safe_new0 (client_request_shm);
break;
default:
syscall_printf ("unknown request code %d received: request ignored",
header.request_code);
return;
}
assert (req);
req->msglen (header.msglen);
req->handle (conn, cache);
safe_delete (req);
#ifndef DEBUGGING
printf ("."); // A little noise when we're being quiet.
#endif
}
#endif /* !__INSIDE_CYGWIN__ */
client_request::client_request (request_code_t const id,
void * const buf,
size_t const buflen)
: _header (id, buflen),
_buf (buf),
_buflen (buflen)
{
assert ((!_buf && !_buflen) || (_buf && _buflen));
}
client_request::~client_request ()
{}
int
client_request::make_request ()
{
assert (cygserver_running == CYGSERVER_UNKNOWN \
|| cygserver_running == CYGSERVER_OK \
|| cygserver_running == CYGSERVER_UNAVAIL);
if (cygserver_running == CYGSERVER_UNKNOWN)
cygserver_init ();
assert (cygserver_running == CYGSERVER_OK \
|| cygserver_running == CYGSERVER_UNAVAIL);
/* Don't retry every request if the server's not there */
if (cygserver_running == CYGSERVER_UNAVAIL)
{
syscall_printf ("cygserver un-available");
error_code (ENOSYS);
return -1;
}
transport_layer_base *const transport = create_server_transport ();
assert (transport);
if (transport->connect () == -1)
{
if (errno)
error_code (errno);
else
error_code (ENOSYS);
safe_delete (transport);
return -1;
}
// verbose: debug_printf ("connected to server %p", transport);
send (transport);
safe_delete (transport);
return 0;
}
#ifndef __INSIDE_CYGWIN__
/*
* client_request::handle ()
*
* A server-side method.
*
* At this point, the header of an incoming request has been read and
* an appropriate client_request object constructed. This method has
* to read the request body into its buffer, if there is such a body,
* then perform the request and send back the results to the client.
*
* FIXME: If the incoming packet is malformed, the server drops it on
* the floor. Should it try and generate some sort of reply for the
* client? As it is, the client will simply get a broken connection.
*
* FIXME: also check write and read result for -1.
*/
void
client_request::handle (transport_layer_base *const conn,
process_cache *const cache)
{
if (msglen () && !_buf)
{
system_printf ("no buffer for request body: %ld bytes needed",
msglen ());
error_code (EINVAL);
return;
}
if (msglen () > _buflen)
{
system_printf (("buffer too small for request body: "
"have %ld bytes and need %ld"),
_buflen, msglen ());
error_code (EINVAL);
return;
}
if (msglen ())
{
const ssize_t count = conn->read (_buf, msglen ());
if (count == -1 || (size_t) count != msglen ())
{
assert (errno);
error_code (errno);
syscall_printf (("request body read failure: "
"only %ld bytes received of %ld, "
"error = %d(%lu)"),
count, msglen (),
errno, GetLastError ());
return;
}
}
// verbose: syscall_printf ("request received (%ld + %ld bytes)",
// sizeof (_header), msglen ());
error_code (0); // Overwrites the _header.request_code field.
/*
* This is not allowed to fail. We must return ENOSYS at a minimum
* to the client.
*/
serve (conn, cache);
{
const ssize_t count = conn->write (&_header, sizeof (_header));
if (count != sizeof (_header))
{
assert (errno);
error_code (errno);
syscall_printf (("reply header write failure: "
"only %ld bytes sent of %ld, "
"error = %d(%lu)"),
count, sizeof (_header),
errno, GetLastError ());
return;
}
}
if (msglen ())
{
const ssize_t count = conn->write (_buf, msglen ());
if (count == -1 || (size_t) count != msglen ())
{
assert (errno);
error_code (errno);
syscall_printf (("reply body write failure: "
"only %ld bytes sent of %ld, "
"error = %d(%lu)"),
count, msglen (),
errno, GetLastError ());
return;
}
}
// verbose: syscall_printf ("reply sent (%ld + %ld bytes)",
// sizeof (_header), msglen ());
}
#endif /* !__INSIDE_CYGWIN__ */
bool
check_cygserver_available ()
{
assert (cygserver_running == CYGSERVER_UNKNOWN \
|| cygserver_running == CYGSERVER_UNAVAIL);
cygserver_running = CYGSERVER_OK; // For make_request ().
client_request_get_version req;
/* This indicates that we failed to connect to cygserver at all but
* that's fine as cygwin doesn't need it to be running.
*/
if (req.make_request () == -1)
return false;
/* We connected to the server but something went wrong after that
* (in sending the message, in cygserver itself, or in receiving the
* reply).
*/
if (req.error_code ())
{
syscall_printf ("failure in cygserver version request: %d",
req.error_code ());
syscall_printf ("process will continue without cygserver support");
return false;
}
return req.check_version ();
}
void
cygserver_init ()
{
if (!allow_daemon)
{
syscall_printf ("cygserver use disabled in client");
cygserver_running = CYGSERVER_UNAVAIL;
return;
}
assert (cygserver_running == CYGSERVER_UNKNOWN \
|| cygserver_running == CYGSERVER_OK \
|| cygserver_running == CYGSERVER_UNAVAIL);
if (cygserver_running == CYGSERVER_OK)
return;
if (!check_cygserver_available ())
cygserver_running = CYGSERVER_UNAVAIL;
}

View File

@ -1,84 +0,0 @@
/* cygserver_ipc.h
Copyright 2002 Red Hat, Inc.
Originally written by Conrad Scott <conrad.scott@dsl.pipex.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef __CYGSERVER_IPC_H__
#define __CYGSERVER_IPC_H__
#include <assert.h>
#include <limits.h> /* For OPEN_MAX. */
/*
* The sysv ipc id's (msgid, semid, shmid) are integers arranged such
* that they no subsystem will generate the same id as some other
* subsystem; nor do these ids overlap file descriptors (the other
* common integer ids). Since Cygwin can allocate more than OPEN_MAX
* file descriptors, it can't be guaranteed not to overlap, but it
* should help catch some errors.
*
* msgid's: OPEN_MAX, OPEN_MAX + 3, OPEN_MAX + 6, . . .
* semid's: OPEN_MAX + 1, OPEN_MAX + 4, OPEN_MAX + 7, . . .
* shmid's: OPEN_MAX + 2, OPEN_MAX + 5, OPEN_MAX + 8, . . .
*
* To further ensure that ids are unique, if ipc objects are created
* and destroyed and then re-created, they are given new ids by
* munging the basic id (as above) with a sequence number.
*
* Internal ipc id's, which are 0, 1, ... within each subsystem (and
* not munged with a sequence number), are used solely by the ipcs(8)
* interface.
*/
enum ipc_subsys_t
{
IPC_MSGOP = 0,
IPC_SEMOP = 1,
IPC_SHMOP = 2,
IPC_SUBSYS_COUNT
};
/*
* IPCMNI - The absolute maximum number of simultaneous ipc ids for
* any one subsystem.
*/
enum
{
IPCMNI = 0x10000 // Must be a power of two.
};
inline int
ipc_int2ext (const int intid, const ipc_subsys_t subsys, long & sequence)
{
assert (0 <= intid && intid < IPCMNI);
const long tmp = InterlockedIncrement (&sequence);
return (((tmp & 0x7fff) << 16)
| (OPEN_MAX + (intid * IPC_SUBSYS_COUNT) + subsys));
}
inline int
ipc_ext2int_subsys (const int extid)
{
return ((extid & (IPCMNI - 1)) - OPEN_MAX) % IPC_SUBSYS_COUNT;
}
inline int
ipc_ext2int (const int extid, const ipc_subsys_t subsys)
{
if (ipc_ext2int_subsys (extid) != subsys)
return -1;
else
return ((extid & (IPCMNI - 1)) - OPEN_MAX) / IPC_SUBSYS_COUNT;
}
#endif /* __CYGSERVER_IPC_H__ */

View File

@ -1,431 +0,0 @@
/* cygserver_process.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "woutsup.h"
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include "cygerrno.h"
#include "cygwin/cygserver_process.h"
/*****************************************************************************/
#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY))
/*****************************************************************************/
process_cleanup::~process_cleanup ()
{
safe_delete (_process);
}
void
process_cleanup::process ()
{
_process->cleanup ();
}
/*****************************************************************************/
/* cleanup_routine */
cleanup_routine::~cleanup_routine ()
{
}
/*****************************************************************************/
process::process (const pid_t cygpid, const DWORD winpid)
: _cygpid (cygpid),
_winpid (winpid),
_hProcess (NULL),
_cleaning_up (false),
_exit_status (STILL_ACTIVE),
_routines_head (NULL),
_next (NULL)
{
_hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid);
if (!_hProcess)
{
system_printf ("unable to obtain handle for new cache process %d(%lu)",
_cygpid, _winpid);
_hProcess = INVALID_HANDLE_VALUE;
_exit_status = 0;
}
else
debug_printf ("got handle %p for new cache process %d(%lu)",
_hProcess, _cygpid, _winpid);
InitializeCriticalSection (&_access);
}
process::~process ()
{
DeleteCriticalSection (&_access);
(void) CloseHandle (_hProcess);
}
/* No need to be thread-safe as this is only ever called by
* process_cache::remove_process (). If it has to be made thread-safe
* later on, it should not use the `access' critical section as that
* is held by the client request handlers for an arbitrary length of
* time, i.e. while they do whatever processing is required for a
* client request.
*/
DWORD
process::check_exit_code ()
{
if (_hProcess && _hProcess != INVALID_HANDLE_VALUE
&& _exit_status == STILL_ACTIVE
&& !GetExitCodeProcess (_hProcess, &_exit_status))
{
system_printf ("failed to retrieve exit code for %d(%lu), error = %lu",
_cygpid, _winpid, GetLastError ());
_hProcess = INVALID_HANDLE_VALUE;
}
return _exit_status;
}
bool
process::add (cleanup_routine *const entry)
{
assert (entry);
bool res = false;
EnterCriticalSection (&_access);
if (!_cleaning_up)
{
entry->_next = _routines_head;
_routines_head = entry;
res = true;
}
LeaveCriticalSection (&_access);
return res;
}
bool
process::remove (const cleanup_routine *const entry)
{
assert (entry);
bool res = false;
EnterCriticalSection (&_access);
if (!_cleaning_up)
{
cleanup_routine *previous = NULL;
for (cleanup_routine *ptr = _routines_head;
ptr;
previous = ptr, ptr = ptr->_next)
{
if (*ptr == *entry)
{
if (previous)
previous->_next = ptr->_next;
else
_routines_head = ptr->_next;
safe_delete (ptr);
res = true;
break;
}
}
}
LeaveCriticalSection (&_access);
return res;
}
/* This is single threaded. It's called after the process is removed
* from the cache, but inserts may be attemped by worker threads that
* have a pointer to it.
*/
void
process::cleanup ()
{
EnterCriticalSection (&_access);
assert (!is_active ());
assert (!_cleaning_up);
InterlockedExchange (&_cleaning_up, true);
cleanup_routine *entry = _routines_head;
_routines_head = NULL;
LeaveCriticalSection (&_access);
while (entry)
{
cleanup_routine *const ptr = entry;
entry = entry->_next;
ptr->cleanup (this);
safe_delete (ptr);
}
}
/*****************************************************************************/
void
process_cache::submission_loop::request_loop ()
{
assert (this);
assert (_cache);
assert (_interrupt_event);
while (_running)
_cache->wait_for_processes (_interrupt_event);
}
/*****************************************************************************/
process_cache::process_cache (const unsigned int initial_workers)
: _queue (initial_workers),
_submitter (this, &_queue), // true == interruptible
_processes_count (0),
_processes_head (NULL),
_cache_add_trigger (NULL)
{
/* there can only be one */
InitializeCriticalSection (&_cache_write_access);
_cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES
FALSE, // Auto-reset
FALSE, // Initially non-signalled
NULL); // Anonymous
if (!_cache_add_trigger)
{
system_printf ("failed to create cache add trigger, error = %lu",
GetLastError ());
abort ();
}
_queue.add_submission_loop (&_submitter);
}
process_cache::~process_cache ()
{
(void) CloseHandle (_cache_add_trigger);
DeleteCriticalSection (&_cache_write_access);
}
/* This returns the process object to the caller already locked, that
* is, with the object's `access' critical region entered. Thus the
* caller must unlock the object when it's finished with it (via
* process::release ()). It must then not try to access the object
* afterwards, except by going through this routine again, as it may
* have been deleted once it has been unlocked.
*/
class process *
process_cache::process (const pid_t cygpid, const DWORD winpid)
{
/* TODO: make this more granular, so a search doesn't involve the
* write lock.
*/
EnterCriticalSection (&_cache_write_access);
class process *previous = NULL;
class process *entry = find (winpid, &previous);
if (!entry)
{
if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS)
{
LeaveCriticalSection (&_cache_write_access);
system_printf (("process limit (%d processes) reached; "
"new connection refused for %d(%lu)"),
MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT,
cygpid, winpid);
set_errno (EAGAIN);
return NULL;
}
entry = safe_new (class process, cygpid, winpid);
if (!entry->is_active ())
{
LeaveCriticalSection (&_cache_write_access);
safe_delete (entry);
set_errno (ESRCH);
return NULL;
}
if (previous)
{
entry->_next = previous->_next;
previous->_next = entry;
}
else
{
entry->_next = _processes_head;
_processes_head = entry;
}
_processes_count += 1;
SetEvent (_cache_add_trigger);
}
EnterCriticalSection (&entry->_access); // To be released by the caller.
LeaveCriticalSection (&_cache_write_access);
assert (entry);
assert (entry->_winpid == winpid);
return entry;
}
void
process_cache::wait_for_processes (const HANDLE interrupt_event)
{
// Update `_wait_array' with handles of all current processes.
const size_t count = sync_wait_array (interrupt_event);
debug_printf ("waiting on %u objects in total (%u processes)",
count, _processes_count);
const DWORD rc = WaitForMultipleObjects (count, _wait_array,
FALSE, INFINITE);
if (rc == WAIT_FAILED)
{
system_printf ("could not wait on the process handles, error = %lu",
GetLastError ());
abort ();
}
const size_t start = rc - WAIT_OBJECT_0;
if (rc < WAIT_OBJECT_0 || start > count)
{
system_printf (("unexpected return code %rc "
"from WaitForMultipleObjects: "
"expected [%u .. %u)"),
rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count);
abort ();
}
// Tell all the processes, from the signalled point up, the bad news.
for (size_t index = start; index != count; index++)
if (_process_array[index])
check_and_remove_process (index);
}
/*
* process_cache::sync_wait_array ()
*
* Fill-in the wait array with the handles that the cache needs to wait on.
* These handles are:
* - the process_process_param's interrupt event
* - the process_cache's cache_add_trigger event
* - the handle for each live process in the cache.
*
* Return value: the number of live handles in the array.
*/
size_t
process_cache::sync_wait_array (const HANDLE interrupt_event)
{
assert (this);
assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE);
assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE);
EnterCriticalSection (&_cache_write_access);
assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array));
size_t index = 0;
for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
{
assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE);
assert (ptr->is_active ());
_wait_array[index] = ptr->handle ();
_process_array[index++] = ptr;
assert (index <= elements (_wait_array));
}
/* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */
/* Well, not strictly `must', but it's more efficient if they are :-) */
_wait_array[index] = interrupt_event;
_process_array[index++] = NULL;
_wait_array[index] = _cache_add_trigger;
_process_array[index++] = NULL;
/* Phew, back to normal volume now. */
assert (index <= elements (_wait_array));
LeaveCriticalSection (&_cache_write_access);
return index;
}
void
process_cache::check_and_remove_process (const size_t index)
{
assert (this);
assert (index < elements (_wait_array) - SPECIALS_COUNT);
class process *const process = _process_array[index];
assert (process);
assert (process->handle () == _wait_array[index]);
if (process->check_exit_code () == STILL_ACTIVE)
return;
debug_printf ("process %d(%lu) has left the building ($? = %lu)",
process->_cygpid, process->_winpid, process->_exit_status);
/* Unlink the process object from the process list. */
EnterCriticalSection (&_cache_write_access);
class process *previous = NULL;
const class process *const tmp = find (process->_winpid, &previous);
assert (tmp == process);
assert (previous ? previous->_next == process : _processes_head == process);
if (previous)
previous->_next = process->_next;
else
_processes_head = process->_next;
_processes_count -= 1;
LeaveCriticalSection (&_cache_write_access);
/* Schedule any cleanup tasks for this process. */
_queue.add (safe_new (process_cleanup, process));
}
class process *
process_cache::find (const DWORD winpid, class process **previous)
{
if (previous)
*previous = NULL;
for (class process *ptr = _processes_head; ptr; ptr = ptr->_next)
if (ptr->_winpid == winpid)
return ptr;
else if (ptr->_winpid > winpid) // The list is sorted by winpid.
return NULL;
else if (previous)
*previous = ptr;
return NULL;
}
/*****************************************************************************/

View File

@ -1,896 +0,0 @@
/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin.
Copyright 2002 Red Hat, Inc.
Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "woutsup.h"
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "cygserver_ipc.h"
#include "cygserver_shm.h"
#include "security.h"
#include "cygwin/cygserver.h"
#include "cygwin/cygserver_process.h"
#include "cygwin/cygserver_transport.h"
/*---------------------------------------------------------------------------*
* class server_shmmgr
*
* A singleton class.
*---------------------------------------------------------------------------*/
#define shmmgr (server_shmmgr::instance ())
class server_shmmgr
{
private:
class attach_t
{
public:
class process *const _client;
unsigned int _refcnt;
attach_t *_next;
attach_t (class process *const client)
: _client (client),
_refcnt (0),
_next (NULL)
{}
};
class segment_t
{
private:
// Bits for the _flg field.
enum { IS_DELETED = 0x01 };
public:
const int _intid;
const int _shmid;
struct shmid_ds _ds;
segment_t *_next;
segment_t (const key_t key, const int intid, const HANDLE hFileMap);
~segment_t ();
bool is_deleted () const
{
return _flg & IS_DELETED;
}
bool is_pending_delete () const
{
return !_ds.shm_nattch && is_deleted ();
}
void mark_deleted ()
{
assert (!is_deleted ());
_flg |= IS_DELETED;
}
int attach (class process *, HANDLE & hFileMap);
int detach (class process *);
private:
static long _sequence;
int _flg;
const HANDLE _hFileMap;
attach_t *_attach_head; // A list sorted by winpid;
attach_t *find (const class process *, attach_t **previous = NULL);
};
class cleanup_t : public cleanup_routine
{
public:
cleanup_t (const segment_t *const segptr)
: cleanup_routine (reinterpret_cast<void *> (segptr->_shmid))
{
assert (key ());
}
int shmid () const { return reinterpret_cast<int> (key ()); }
virtual void cleanup (class process *const client)
{
const int res = shmmgr.shmdt (shmid (), client);
if (res != 0)
debug_printf ("process cleanup failed [shmid = %d]: %s",
shmid (), strerror (-res));
}
};
public:
static server_shmmgr & instance ();
int shmat (HANDLE & hFileMap,
int shmid, int shmflg, class process *);
int shmctl (int & out_shmid, struct shmid_ds & out_ds,
struct shminfo & out_shminfo, struct shm_info & out_shm_info,
const int shmid, int cmd, const struct shmid_ds &,
class process *);
int shmdt (int shmid, class process *);
int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t,
class process *);
private:
static server_shmmgr *_instance;
static pthread_once_t _instance_once;
static void initialise_instance ();
CRITICAL_SECTION _segments_lock;
segment_t *_segments_head; // A list sorted by int_id.
int _shm_ids; // Number of shm segments (for ipcs(8)).
int _shm_tot; // Total bytes of shm segments (for ipcs(8)).
int _shm_atts; // Number of attached segments (for ipcs(8)).
int _intid_max; // Highest intid yet allocated (for ipcs(8)).
server_shmmgr ();
~server_shmmgr ();
// Undefined (as this class is a singleton):
server_shmmgr (const server_shmmgr &);
server_shmmgr & operator= (const server_shmmgr &);
segment_t *find_by_key (key_t);
segment_t *find (int intid, segment_t **previous = NULL);
int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t);
segment_t *new_segment (key_t, size_t, HANDLE);
void delete_segment (segment_t *);
};
/* static */ long server_shmmgr::segment_t::_sequence = 0;
/* static */ server_shmmgr *server_shmmgr::_instance = NULL;
/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT;
/*---------------------------------------------------------------------------*
* server_shmmgr::segment_t::segment_t ()
*---------------------------------------------------------------------------*/
server_shmmgr::segment_t::segment_t (const key_t key,
const int intid,
const HANDLE hFileMap)
: _intid (intid),
_shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)),
_next (NULL),
_flg (0),
_hFileMap (hFileMap),
_attach_head (NULL)
{
assert (0 <= _intid && _intid < SHMMNI);
memset (&_ds, '\0', sizeof (_ds));
_ds.shm_perm.key = key;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::segment_t::~segment_t ()
*---------------------------------------------------------------------------*/
server_shmmgr::segment_t::~segment_t ()
{
assert (!_attach_head);
if (!CloseHandle (_hFileMap))
syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap);
}
/*---------------------------------------------------------------------------*
* server_shmmgr::segment_t::attach ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::segment_t::attach (class process *const client,
HANDLE & hFileMap)
{
assert (client);
if (!DuplicateHandle (GetCurrentProcess (),
_hFileMap,
client->handle (),
&hFileMap,
0,
FALSE, // bInheritHandle
DUPLICATE_SAME_ACCESS))
{
syscall_printf (("failed to duplicate handle for client "
"[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"),
_ds.shm_perm.key, _shmid, _hFileMap);
return -EACCES; // FIXME: Case analysis?
}
_ds.shm_lpid = client->cygpid ();
_ds.shm_nattch += 1;
_ds.shm_atime = time (NULL); // FIXME: sub-second times.
attach_t *previous = NULL;
attach_t *attptr = find (client, &previous);
if (!attptr)
{
attptr = safe_new (attach_t, client);
if (previous)
{
attptr->_next = previous->_next;
previous->_next = attptr;
}
else
{
attptr->_next = _attach_head;
_attach_head = attptr;
}
}
attptr->_refcnt += 1;
cleanup_t *const cleanup = safe_new (cleanup_t, this);
// FIXME: ::add should only fail if the process object is already
// cleaning up; but it can't be doing that since this thread has it
// locked.
const bool result = client->add (cleanup);
assert (result);
return 0;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::segment_t::detach ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::segment_t::detach (class process *const client)
{
attach_t *previous = NULL;
attach_t *const attptr = find (client, &previous);
if (!attptr)
return -EINVAL;
if (client->is_active ())
{
const cleanup_t key (this);
if (!client->remove (&key))
syscall_printf (("failed to remove cleanup routine for %d(%lu) "
"[shmid = %d]"),
client->cygpid (), client->winpid (),
_shmid);
}
attptr->_refcnt -= 1;
if (!attptr->_refcnt)
{
assert (previous ? previous->_next == attptr : _attach_head == attptr);
if (previous)
previous->_next = attptr->_next;
else
_attach_head = attptr->_next;
safe_delete (attptr);
}
assert (_ds.shm_nattch > 0);
_ds.shm_lpid = client->cygpid ();
_ds.shm_nattch -= 1;
_ds.shm_dtime = time (NULL); // FIXME: sub-second times.
return 0;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::segment_t::find ()
*---------------------------------------------------------------------------*/
server_shmmgr::attach_t *
server_shmmgr::segment_t::find (const class process *const client,
attach_t **previous)
{
if (previous)
*previous = NULL;
// Nb. The _attach_head list is sorted by winpid.
for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next)
if (attptr->_client == client)
return attptr;
else if (attptr->_client->winpid () > client->winpid ())
return NULL;
else if (previous)
*previous = attptr;
return NULL;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::instance ()
*---------------------------------------------------------------------------*/
/* static */ server_shmmgr &
server_shmmgr::instance ()
{
pthread_once (&_instance_once, &initialise_instance);
assert (_instance);
return *_instance;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::shmat ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::shmat (HANDLE & hFileMap,
const int shmid, const int shmflg,
class process *const client)
{
syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)",
shmid, shmflg, client->cygpid (), client->winpid ());
int result = 0;
EnterCriticalSection (&_segments_lock);
segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
if (!segptr)
result = -EINVAL;
else
result = segptr->attach (client, hFileMap);
if (!result)
_shm_atts += 1;
LeaveCriticalSection (&_segments_lock);
if (result < 0)
syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) "
"for %d(%lu)"),
-result, shmid, shmflg,
client->cygpid (), client->winpid ());
else
syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) "
"for %d(%lu)"),
hFileMap, shmid, shmflg,
client->cygpid (), client->winpid ());
return result;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::shmctl ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::shmctl (int & out_shmid,
struct shmid_ds & out_ds,
struct shminfo & out_shminfo,
struct shm_info & out_shm_info,
const int shmid, const int cmd,
const struct shmid_ds & ds,
class process *const client)
{
syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)",
shmid, cmd, client->cygpid (), client->winpid ());
int result = 0;
EnterCriticalSection (&_segments_lock);
switch (cmd)
{
case IPC_STAT:
case SHM_STAT: // Uses intids rather than shmids.
case IPC_SET:
case IPC_RMID:
{
int intid;
if (cmd == SHM_STAT)
intid = shmid;
else
intid = ipc_ext2int (shmid, IPC_SHMOP);
segment_t *const segptr = find (intid);
if (!segptr)
result = -EINVAL;
else
switch (cmd)
{
case IPC_STAT:
out_ds = segptr->_ds;
break;
case IPC_SET:
segptr->_ds.shm_perm.uid = ds.shm_perm.uid;
segptr->_ds.shm_perm.gid = ds.shm_perm.gid;
segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777;
segptr->_ds.shm_lpid = client->cygpid ();
segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
break;
case IPC_RMID:
if (segptr->is_deleted ())
result = -EIDRM;
else
{
segptr->mark_deleted ();
if (segptr->is_pending_delete ())
delete_segment (segptr);
}
break;
case SHM_STAT: // ipcs(8) i'face.
out_ds = segptr->_ds;
out_shmid = segptr->_shmid;
break;
}
}
break;
case IPC_INFO:
out_shminfo.shmmax = SHMMAX;
out_shminfo.shmmin = SHMMIN;
out_shminfo.shmmni = SHMMNI;
out_shminfo.shmseg = SHMSEG;
out_shminfo.shmall = SHMALL;
break;
case SHM_INFO: // ipcs(8) i'face.
out_shmid = _intid_max;
out_shm_info.shm_ids = _shm_ids;
out_shm_info.shm_tot = _shm_tot;
out_shm_info.shm_atts = _shm_atts;
break;
default:
result = -EINVAL;
break;
}
LeaveCriticalSection (&_segments_lock);
if (result < 0)
syscall_printf (("-1 [%d] = "
"shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
-result,
shmid, cmd, client->cygpid (), client->winpid ());
else
syscall_printf (("%d = "
"shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
((cmd == SHM_STAT || cmd == SHM_INFO)
? out_shmid
: result),
shmid, cmd, client->cygpid (), client->winpid ());
return result;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::shmdt ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::shmdt (const int shmid, class process *const client)
{
syscall_printf ("shmdt (shmid = %d) for %d(%lu)",
shmid, client->cygpid (), client->winpid ());
int result = 0;
EnterCriticalSection (&_segments_lock);
segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
if (!segptr)
result = -EINVAL;
else
result = segptr->detach (client);
if (!result)
_shm_atts -= 1;
if (!result && segptr->is_pending_delete ())
delete_segment (segptr);
LeaveCriticalSection (&_segments_lock);
if (result < 0)
syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)",
-result, shmid, client->cygpid (), client->winpid ());
else
syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)",
result, shmid, client->cygpid (), client->winpid ());
return result;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::shmget ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::shmget (int & out_shmid,
const key_t key, const size_t size, const int shmflg,
const uid_t uid, const gid_t gid,
class process *const client)
{
syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
"for %d(%lu)"),
key, size, shmflg,
client->cygpid (), client->winpid ());
int result = 0;
EnterCriticalSection (&_segments_lock);
if (key == IPC_PRIVATE)
result = new_segment (key, size, shmflg,
client->cygpid (), uid, gid);
else
{
segment_t *const segptr = find_by_key (key);
if (!segptr)
if (shmflg & IPC_CREAT)
result = new_segment (key, size, shmflg,
client->cygpid (), uid, gid);
else
result = -ENOENT;
else if (segptr->is_deleted ())
result = -EIDRM;
else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
result = -EEXIST;
else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777)
result = -EACCES;
else if (size && segptr->_ds.shm_segsz < size)
result = -EINVAL;
else
result = segptr->_shmid;
}
LeaveCriticalSection (&_segments_lock);
if (result >= 0)
{
out_shmid = result;
result = 0;
}
if (result < 0)
syscall_printf (("-1 [%d] = "
"shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
"for %d(%lu)"),
-result,
key, size, shmflg,
client->cygpid (), client->winpid ());
else
syscall_printf (("%d = "
"shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
"for %d(%lu)"),
out_shmid,
key, size, shmflg,
client->cygpid (), client->winpid ());
return result;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::initialise_instance ()
*---------------------------------------------------------------------------*/
/* static */ void
server_shmmgr::initialise_instance ()
{
assert (!_instance);
_instance = safe_new0 (server_shmmgr);
assert (_instance);
}
/*---------------------------------------------------------------------------*
* server_shmmgr::server_shmmgr ()
*---------------------------------------------------------------------------*/
server_shmmgr::server_shmmgr ()
: _segments_head (NULL),
_shm_ids (0),
_shm_tot (0),
_shm_atts (0),
_intid_max (0)
{
InitializeCriticalSection (&_segments_lock);
}
/*---------------------------------------------------------------------------*
* server_shmmgr::~server_shmmgr ()
*---------------------------------------------------------------------------*/
server_shmmgr::~server_shmmgr ()
{
DeleteCriticalSection (&_segments_lock);
}
/*---------------------------------------------------------------------------*
* server_shmmgr::find_by_key ()
*---------------------------------------------------------------------------*/
server_shmmgr::segment_t *
server_shmmgr::find_by_key (const key_t key)
{
for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
if (segptr->_ds.shm_perm.key == key)
return segptr;
return NULL;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::find ()
*---------------------------------------------------------------------------*/
server_shmmgr::segment_t *
server_shmmgr::find (const int intid, segment_t **previous)
{
if (previous)
*previous = NULL;
for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
if (segptr->_intid == intid)
return segptr;
else if (segptr->_intid > intid) // The list is sorted by intid.
return NULL;
else if (previous)
*previous = segptr;
return NULL;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::new_segment ()
*---------------------------------------------------------------------------*/
int
server_shmmgr::new_segment (const key_t key,
const size_t size,
const int shmflg,
const pid_t cygpid,
const uid_t uid,
const gid_t gid)
{
if (size < SHMMIN || size > SHMMAX)
return -EINVAL;
const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE,
0, size,
NULL);
if (!hFileMap)
{
syscall_printf ("failed to create file mapping [size = %lu]: %E", size);
return -ENOMEM; // FIXME
}
segment_t *const segptr = new_segment (key, size, hFileMap);
if (!segptr)
{
(void) CloseHandle (hFileMap);
return -ENOSPC;
}
segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid;
segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid;
segptr->_ds.shm_perm.mode = shmflg & 0777;
segptr->_ds.shm_segsz = size;
segptr->_ds.shm_cpid = cygpid;
segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
return segptr->_shmid;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::new_segment ()
*
* Allocate a new segment for the given key and file map with the
* lowest available intid and insert into the segment map.
*---------------------------------------------------------------------------*/
server_shmmgr::segment_t *
server_shmmgr::new_segment (const key_t key, const size_t size,
const HANDLE hFileMap)
{
// FIXME: Overflow risk.
if (_shm_tot + size > SHMALL)
return NULL;
int intid = 0; // Next expected intid value.
segment_t *previous = NULL; // Insert pointer.
// Find first unallocated intid.
for (segment_t *segptr = _segments_head;
segptr && segptr->_intid == intid;
segptr = segptr->_next, intid++)
{
previous = segptr;
}
/* By the time this condition is reached (given the default value of
* SHMMNI), the linear searches should all replaced by something
* just a *little* cleverer . . .
*/
if (intid >= SHMMNI)
return NULL;
segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap);
assert (segptr);
if (previous)
{
segptr->_next = previous->_next;
previous->_next = segptr;
}
else
{
segptr->_next = _segments_head;
_segments_head = segptr;
}
_shm_ids += 1;
_shm_tot += size;
if (intid > _intid_max)
_intid_max = intid;
return segptr;
}
/*---------------------------------------------------------------------------*
* server_shmmgr::delete_segment ()
*---------------------------------------------------------------------------*/
void
server_shmmgr::delete_segment (segment_t *const segptr)
{
assert (segptr);
assert (segptr->is_pending_delete ());
segment_t *previous = NULL;
const segment_t *const tmp = find (segptr->_intid, &previous);
assert (tmp == segptr);
assert (previous ? previous->_next == segptr : _segments_head == segptr);
if (previous)
previous->_next = segptr->_next;
else
_segments_head = segptr->_next;
assert (_shm_ids > 0);
_shm_ids -= 1;
_shm_tot -= segptr->_ds.shm_segsz;
safe_delete (segptr);
}
/*---------------------------------------------------------------------------*
* client_request_shm::client_request_shm ()
*---------------------------------------------------------------------------*/
client_request_shm::client_request_shm ()
: client_request (CYGSERVER_REQUEST_SHM,
&_parameters, sizeof (_parameters))
{
// verbose: syscall_printf ("created");
}
/*---------------------------------------------------------------------------*
* client_request_shm::serve ()
*---------------------------------------------------------------------------*/
void
client_request_shm::serve (transport_layer_base *const conn,
process_cache *const cache)
{
assert (conn);
assert (!error_code ());
if (msglen () != sizeof (_parameters.in))
{
syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
sizeof (_parameters), msglen ());
error_code (EINVAL);
msglen (0);
return;
}
// FIXME: Get a return code out of this and don't continue on error.
conn->impersonate_client ();
class process *const client = cache->process (_parameters.in.cygpid,
_parameters.in.winpid);
if (!client)
{
error_code (EAGAIN);
msglen (0);
return;
}
int result = -EINVAL;
switch (_parameters.in.shmop)
{
case SHMOP_shmget:
result = shmmgr.shmget (_parameters.out.shmid,
_parameters.in.key, _parameters.in.size,
_parameters.in.shmflg,
_parameters.in.uid, _parameters.in.gid,
client);
break;
case SHMOP_shmat:
result = shmmgr.shmat (_parameters.out.hFileMap,
_parameters.in.shmid, _parameters.in.shmflg,
client);
break;
case SHMOP_shmdt:
result = shmmgr.shmdt (_parameters.in.shmid, client);
break;
case SHMOP_shmctl:
result = shmmgr.shmctl (_parameters.out.shmid,
_parameters.out.ds, _parameters.out.shminfo,
_parameters.out.shm_info,
_parameters.in.shmid, _parameters.in.cmd,
_parameters.in.ds,
client);
break;
}
client->release ();
conn->revert_to_self ();
if (result < 0)
{
error_code (-result);
msglen (0);
}
else
msglen (sizeof (_parameters.out));
}

View File

@ -1,147 +0,0 @@
/* cygserver_shm.h: Single unix specification IPC interface for Cygwin.
Copyright 2002 Red Hat, Inc.
Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
Based on code by Robert Collins <robert.collins@hotmail.com>.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef __CYGSERVER_SHM_H__
#define __CYGSERVER_SHM_H__
#include <sys/types.h>
#include <cygwin/shm.h>
#include <assert.h>
#include <limits.h>
#include "cygserver_ipc.h"
#include "cygwin/cygserver.h"
/*---------------------------------------------------------------------------*
* Values for the shminfo entries.
*
* Nb. The values are segregated between two enums so that the `small'
* values aren't promoted to `unsigned long' equivalents.
*---------------------------------------------------------------------------*/
enum
{
SHMMAX = ULONG_MAX,
SHMSEG = ULONG_MAX,
SHMALL = ULONG_MAX
};
enum
{
SHMMIN = 1,
SHMMNI = IPCMNI // Must be <= IPCMNI.
};
/*---------------------------------------------------------------------------*
* class client_request_shm
*---------------------------------------------------------------------------*/
#ifndef __INSIDE_CYGWIN__
class transport_layer_base;
class process_cache;
#endif
class client_request_shm : public client_request
{
friend class client_request;
public:
enum shmop_t
{
SHMOP_shmat,
SHMOP_shmctl,
SHMOP_shmdt,
SHMOP_shmget
};
#ifdef __INSIDE_CYGWIN__
client_request_shm (int shmid, int shmflg); // shmat
client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl
client_request_shm (int shmid); // shmdt
client_request_shm (key_t, size_t, int shmflg); // shmget
#endif
// Accessors for out parameters.
int shmid () const
{
assert (!error_code ());
return _parameters.out.shmid;
}
HANDLE hFileMap () const
{
assert (!error_code ());
return _parameters.out.hFileMap;
}
const struct shmid_ds & ds () const
{
assert (!error_code ());
return _parameters.out.ds;
}
const struct shminfo & shminfo () const
{
assert (!error_code ());
return _parameters.out.shminfo;
}
const struct shm_info & shm_info () const
{
assert (!error_code ());
return _parameters.out.shm_info;
}
private:
union
{
struct
{
shmop_t shmop;
key_t key;
size_t size;
int shmflg;
int shmid;
int cmd;
pid_t cygpid;
DWORD winpid;
__uid32_t uid;
__gid32_t gid;
struct shmid_ds ds;
} in;
struct {
int shmid;
union
{
HANDLE hFileMap;
struct shmid_ds ds;
struct shminfo shminfo;
struct shm_info shm_info;
};
} out;
} _parameters;
#ifndef __INSIDE_CYGWIN__
client_request_shm ();
#endif
#ifndef __INSIDE_CYGWIN__
virtual void serve (transport_layer_base *, process_cache *);
#endif
};
#endif /* __CYGSERVER_SHM_H__ */

View File

@ -1,51 +0,0 @@
/* cygserver_transport.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifdef __OUTSIDE_CYGWIN__
#include "woutsup.h"
#else
#include "winsup.h"
#endif
#include <sys/socket.h>
#include "safe_memory.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#include "cygwin/cygserver_transport_sockets.h"
/* The factory */
transport_layer_base *
create_server_transport ()
{
if (wincap.is_winnt ())
return safe_new0 (transport_layer_pipes);
else
return safe_new0 (transport_layer_sockets);
}
#ifndef __INSIDE_CYGWIN__
void
transport_layer_base::impersonate_client ()
{}
void
transport_layer_base::revert_to_self ()
{}
#endif /* !__INSIDE_CYGWIN__ */
transport_layer_base::~transport_layer_base ()
{}

View File

@ -1,362 +0,0 @@
/* cygserver_transport_pipes.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifdef __OUTSIDE_CYGWIN__
#include "woutsup.h"
#else
#include "winsup.h"
#endif
#include <sys/types.h>
#include <assert.h>
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
#include "cygerrno.h"
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_pipes.h"
#ifndef __INSIDE_CYGWIN__
#include "cygwin/cygserver.h"
#endif
enum
{
MAX_WAIT_NAMED_PIPE_RETRY = 64,
WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds
};
#ifndef __INSIDE_CYGWIN__
static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT;
static CRITICAL_SECTION pipe_instance_lock;
static long pipe_instance = 0;
static void
initialise_pipe_instance_lock ()
{
assert (pipe_instance == 0);
InitializeCriticalSection (&pipe_instance_lock);
}
#endif /* !__INSIDE_CYGWIN__ */
#ifndef __INSIDE_CYGWIN__
transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe)
: _pipe_name (""),
_hPipe (hPipe),
_is_accepted_endpoint (true),
_is_listening_endpoint (false)
{
assert (_hPipe);
assert (_hPipe != INVALID_HANDLE_VALUE);
init_security ();
}
#endif /* !__INSIDE_CYGWIN__ */
transport_layer_pipes::transport_layer_pipes ()
: _pipe_name ("\\\\.\\pipe\\cygwin_lpc"),
_hPipe (NULL),
_is_accepted_endpoint (false),
_is_listening_endpoint (false)
{
init_security ();
}
void
transport_layer_pipes::init_security ()
{
assert (wincap.has_security ());
/* FIXME: pthread_once or equivalent needed */
InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE);
_sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
_sec_all_nih.lpSecurityDescriptor = &_sd;
_sec_all_nih.bInheritHandle = FALSE;
}
transport_layer_pipes::~transport_layer_pipes ()
{
close ();
}
#ifndef __INSIDE_CYGWIN__
int
transport_layer_pipes::listen ()
{
assert (!_hPipe);
assert (!_is_accepted_endpoint);
assert (!_is_listening_endpoint);
_is_listening_endpoint = true;
/* no-op */
return 0;
}
class transport_layer_pipes *
transport_layer_pipes::accept (bool *const recoverable)
{
assert (!_hPipe);
assert (!_is_accepted_endpoint);
assert (_is_listening_endpoint);
pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock);
EnterCriticalSection (&pipe_instance_lock);
// Read: http://www.securityinternals.com/research/papers/namedpipe.php
// See also the Microsoft security bulletins MS00-053 and MS01-031.
// FIXME: Remove FILE_CREATE_PIPE_INSTANCE.
const bool first_instance = (pipe_instance == 0);
const HANDLE accept_pipe =
CreateNamedPipe (_pipe_name,
(PIPE_ACCESS_DUPLEX
| (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)),
(PIPE_TYPE_BYTE | PIPE_WAIT),
PIPE_UNLIMITED_INSTANCES,
0, 0, 1000,
&_sec_all_nih);
const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE
&& pipe_instance == 0
&& GetLastError () == ERROR_ACCESS_DENIED);
if (accept_pipe != INVALID_HANDLE_VALUE)
InterlockedIncrement (&pipe_instance);
LeaveCriticalSection (&pipe_instance_lock);
if (duplicate)
{
*recoverable = false;
system_printf ("failed to create named pipe: "
"is the daemon already running?");
return NULL;
}
if (accept_pipe == INVALID_HANDLE_VALUE)
{
debug_printf ("error creating pipe (%lu).", GetLastError ());
*recoverable = true; // FIXME: case analysis?
return NULL;
}
assert (accept_pipe);
if (!ConnectNamedPipe (accept_pipe, NULL)
&& GetLastError () != ERROR_PIPE_CONNECTED)
{
debug_printf ("error connecting to pipe (%lu)", GetLastError ());
(void) CloseHandle (accept_pipe);
*recoverable = true; // FIXME: case analysis?
return NULL;
}
return safe_new (transport_layer_pipes, accept_pipe);
}
#endif /* !__INSIDE_CYGWIN__ */
void
transport_layer_pipes::close ()
{
// verbose: debug_printf ("closing pipe %p", _hPipe);
if (_hPipe)
{
assert (_hPipe != INVALID_HANDLE_VALUE);
#ifndef __INSIDE_CYGWIN__
if (_is_accepted_endpoint)
{
(void) FlushFileBuffers (_hPipe); // Blocks until client reads.
(void) DisconnectNamedPipe (_hPipe);
EnterCriticalSection (&pipe_instance_lock);
(void) CloseHandle (_hPipe);
assert (pipe_instance > 0);
InterlockedDecrement (&pipe_instance);
LeaveCriticalSection (&pipe_instance_lock);
}
else
(void) CloseHandle (_hPipe);
#else /* __INSIDE_CYGWIN__ */
assert (!_is_accepted_endpoint);
(void) ForceCloseHandle (_hPipe);
#endif /* __INSIDE_CYGWIN__ */
_hPipe = NULL;
}
}
ssize_t
transport_layer_pipes::read (void *const buf, const size_t len)
{
// verbose: debug_printf ("reading from pipe %p", _hPipe);
assert (_hPipe);
assert (_hPipe != INVALID_HANDLE_VALUE);
assert (!_is_listening_endpoint);
DWORD count;
if (!ReadFile (_hPipe, buf, len, &count, NULL))
{
debug_printf ("error reading from pipe (%lu)", GetLastError ());
set_errno (EINVAL); // FIXME?
return -1;
}
return count;
}
ssize_t
transport_layer_pipes::write (void *const buf, const size_t len)
{
// verbose: debug_printf ("writing to pipe %p", _hPipe);
assert (_hPipe);
assert (_hPipe != INVALID_HANDLE_VALUE);
assert (!_is_listening_endpoint);
DWORD count;
if (!WriteFile (_hPipe, buf, len, &count, NULL))
{
debug_printf ("error writing to pipe, error = %lu", GetLastError ());
set_errno (EINVAL); // FIXME?
return -1;
}
return count;
}
/*
* This routine holds a static variable, assume_cygserver, that is set
* if the transport has good reason to think that cygserver is
* running, i.e. if if successfully connected to it with the previous
* attempt. If this is set, the code tries a lot harder to get a
* connection, making the assumption that any failures are just
* congestion and overloading problems.
*/
int
transport_layer_pipes::connect ()
{
assert (!_hPipe);
assert (!_is_accepted_endpoint);
assert (!_is_listening_endpoint);
static bool assume_cygserver = false;
BOOL rc = TRUE;
int retries = 0;
while (rc)
{
_hPipe = CreateFile (_pipe_name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&_sec_all_nih,
OPEN_EXISTING,
SECURITY_IMPERSONATION,
NULL);
if (_hPipe != INVALID_HANDLE_VALUE)
{
assert (_hPipe);
#ifdef __INSIDE_CYGWIN__
ProtectHandle (_hPipe);
#endif
assume_cygserver = true;
return 0;
}
_hPipe = NULL;
if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY)
{
debug_printf ("Error opening the pipe (%lu)", GetLastError ());
return -1;
}
/* Note: `If no instances of the specified named pipe exist, the
* WaitNamedPipe function returns immediately, regardless of the
* time-out value.' Thus the explicit Sleep if the call fails
* with ERROR_FILE_NOT_FOUND.
*/
while (retries != MAX_WAIT_NAMED_PIPE_RETRY
&& !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT)))
{
if (GetLastError () == ERROR_FILE_NOT_FOUND)
Sleep (0); // Give the server a chance.
retries += 1;
}
}
assert (retries == MAX_WAIT_NAMED_PIPE_RETRY);
system_printf ("lost connection to cygserver, error = %lu",
GetLastError ());
assume_cygserver = false;
return -1;
}
#ifndef __INSIDE_CYGWIN__
void
transport_layer_pipes::impersonate_client ()
{
assert (_hPipe);
assert (_hPipe != INVALID_HANDLE_VALUE);
assert (_is_accepted_endpoint);
// verbose: debug_printf ("impersonating pipe %p", _hPipe);
if (_hPipe)
{
assert (_hPipe != INVALID_HANDLE_VALUE);
if (!ImpersonateNamedPipeClient (_hPipe))
debug_printf ("Failed to Impersonate the client, (%lu)",
GetLastError ());
}
// verbose: debug_printf ("I am who you are");
}
void
transport_layer_pipes::revert_to_self ()
{
assert (_is_accepted_endpoint);
RevertToSelf ();
// verbose: debug_printf ("I am who I yam");
}
#endif /* !__INSIDE_CYGWIN__ */

View File

@ -1,387 +0,0 @@
/* cygserver_transport_sockets.cc
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifdef __OUTSIDE_CYGWIN__
#include "woutsup.h"
#else
#include "winsup.h"
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "cygwin/cygserver_transport.h"
#include "cygwin/cygserver_transport_sockets.h"
/* to allow this to link into cygwin and the .dll, a little magic is needed. */
#ifndef __OUTSIDE_CYGWIN__
extern "C" int cygwin_accept (int fd, struct sockaddr *, int *len);
extern "C" int cygwin_bind (int fd, const struct sockaddr *, int len);
extern "C" int cygwin_connect (int fd, const struct sockaddr *, int len);
extern "C" int cygwin_listen (int fd, int backlog);
extern "C" int cygwin_shutdown (int fd, int how);
extern "C" int cygwin_socket (int af, int type, int protocol);
#else /* __OUTSIDE_CYGWIN__ */
#define cygwin_accept(A,B,C) ::accept (A,B,C)
#define cygwin_bind(A,B,C) ::bind (A,B,C)
#define cygwin_connect(A,B,C) ::connect (A,B,C)
#define cygwin_listen(A,B) ::listen (A,B)
#define cygwin_shutdown(A,B) ::shutdown (A,B)
#define cygwin_socket(A,B,C) ::socket (A,B,C)
#endif /* __OUTSIDE_CYGWIN__ */
enum
{
MAX_CONNECT_RETRY = 64
};
transport_layer_sockets::transport_layer_sockets (const int fd)
: _fd (fd),
_addr_len (0),
_is_accepted_endpoint (true),
_is_listening_endpoint (false)
{
assert (_fd != -1);
memset (&_addr, '\0', sizeof (_addr));
}
transport_layer_sockets::transport_layer_sockets ()
: _fd (-1),
_addr_len (0),
_is_accepted_endpoint (false),
_is_listening_endpoint (false)
{
memset (&_addr, '\0', sizeof (_addr));
_addr.sun_family = AF_UNIX;
strcpy (_addr.sun_path, "/tmp/cygdaemo"); // FIXME: $TMP?
_addr_len = SUN_LEN (&_addr);
}
transport_layer_sockets::~transport_layer_sockets ()
{
close ();
}
#ifndef __INSIDE_CYGWIN__
int
transport_layer_sockets::listen ()
{
assert (_fd == -1);
assert (!_is_accepted_endpoint);
assert (!_is_listening_endpoint);
debug_printf ("listen () [this = %p]", this);
struct stat sbuf;
if (stat (_addr.sun_path, &sbuf) == -1)
{
if (errno != ENOENT)
{
system_printf ("cannot access socket file `%s': %s",
_addr.sun_path, strerror (errno));
return -1;
}
}
else if (S_ISSOCK (sbuf.st_mode))
{
// The socket already exists: is a duplicate cygserver running?
const int newfd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
if (newfd == -1)
{
system_printf ("failed to create UNIX domain socket: %s",
strerror (errno));
return -1;
}
if (cygwin_connect (newfd, (struct sockaddr *) &_addr, _addr_len) == 0)
{
system_printf ("the daemon is already running");
(void) cygwin_shutdown (newfd, SHUT_WR);
char buf[BUFSIZ];
while (::read (newfd, buf, sizeof (buf)) > 0)
{}
(void) ::close (newfd);
return -1;
}
if (unlink (_addr.sun_path) == -1)
{
system_printf ("failed to remove `%s': %s",
_addr.sun_path, strerror (errno));
(void) ::close (newfd);
return -1;
}
}
else
{
system_printf ("cannot create socket `%s': File already exists",
_addr.sun_path);
return -1;
}
_fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
if (_fd == -1)
{
system_printf ("failed to create UNIX domain socket: %s",
strerror (errno));
return -1;
}
if (cygwin_bind (_fd, (struct sockaddr *) &_addr, _addr_len) == -1)
{
const int saved_errno = errno;
close ();
errno = saved_errno;
system_printf ("failed to bind UNIX domain socket `%s': %s",
_addr.sun_path, strerror (errno));
return -1;
}
_is_listening_endpoint = true; // i.e. this really means "have bound".
if (cygwin_listen (_fd, SOMAXCONN) == -1)
{
const int saved_errno = errno;
close ();
errno = saved_errno;
system_printf ("failed to listen on UNIX domain socket `%s': %s",
_addr.sun_path, strerror (errno));
return -1;
}
debug_printf ("0 = listen () [this = %p, fd = %d]", this, _fd);
return 0;
}
class transport_layer_sockets *
transport_layer_sockets::accept (bool *const recoverable)
{
assert (_fd != -1);
assert (!_is_accepted_endpoint);
assert (_is_listening_endpoint);
debug_printf ("accept () [this = %p, fd = %d]", this, _fd);
struct sockaddr_un client_addr;
socklen_t client_addr_len = sizeof (client_addr);
const int accept_fd =
cygwin_accept (_fd, (struct sockaddr *) &client_addr, &client_addr_len);
if (accept_fd == -1)
{
system_printf ("failed to accept connection: %s", strerror (errno));
switch (errno)
{
case ECONNABORTED:
case EINTR:
case EMFILE:
case ENFILE:
case ENOBUFS:
case ENOMEM:
*recoverable = true;
break;
default:
*recoverable = false;
break;
}
return NULL;
}
debug_printf ("%d = accept () [this = %p, fd = %d]", accept_fd, this, _fd);
return safe_new (transport_layer_sockets, accept_fd);
}
#endif /* !__INSIDE_CYGWIN__ */
void
transport_layer_sockets::close ()
{
debug_printf ("close () [this = %p, fd = %d]", this, _fd);
if (_is_listening_endpoint)
(void) unlink (_addr.sun_path);
if (_fd != -1)
{
(void) cygwin_shutdown (_fd, SHUT_WR);
if (!_is_listening_endpoint)
{
char buf[BUFSIZ];
while (::read (_fd, buf, sizeof (buf)) > 0)
{}
}
(void) ::close (_fd);
_fd = -1;
}
_is_listening_endpoint = false;
}
ssize_t
transport_layer_sockets::read (void *const buf, const size_t buf_len)
{
assert (_fd != -1);
assert (!_is_listening_endpoint);
assert (buf);
assert (buf_len > 0);
// verbose: debug_printf ("read (buf = %p, len = %u) [this = %p, fd = %d]",
// buf, buf_len, this, _fd);
char *read_buf = static_cast<char *> (buf);
size_t read_buf_len = buf_len;
ssize_t res = 0;
while (read_buf_len != 0
&& (res = ::read (_fd, read_buf, read_buf_len)) > 0)
{
read_buf += res;
read_buf_len -= res;
assert (read_buf_len >= 0);
}
if (res != -1)
{
if (res == 0)
errno = EIO; // FIXME?
res = buf_len - read_buf_len;
}
if (res != static_cast<ssize_t> (buf_len))
debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]: %s",
res, buf, buf_len, this, _fd,
(res == -1 ? strerror (errno) : "EOF"));
else
{
// verbose: debug_printf ("%d = read (buf = %p, len = %u) [this = %p, fd = %d]",
// res, buf, buf_len, this, _fd);
}
return res;
}
ssize_t
transport_layer_sockets::write (void *const buf, const size_t buf_len)
{
assert (_fd != -1);
assert (!_is_listening_endpoint);
assert (buf);
assert (buf_len > 0);
// verbose: debug_printf ("write (buf = %p, len = %u) [this = %p, fd = %d]",
// buf, buf_len, this, _fd);
char *write_buf = static_cast<char *> (buf);
size_t write_buf_len = buf_len;
ssize_t res = 0;
while (write_buf_len != 0
&& (res = ::write (_fd, write_buf, write_buf_len)) > 0)
{
write_buf += res;
write_buf_len -= res;
assert (write_buf_len >= 0);
}
if (res != -1)
{
if (res == 0)
errno = EIO; // FIXME?
res = buf_len - write_buf_len;
}
if (res != static_cast<ssize_t> (buf_len))
debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]: %s",
res, buf, buf_len, this, _fd,
(res == -1 ? strerror (errno) : "EOF"));
else
{
// verbose: debug_printf ("%d = write (buf = %p, len = %u) [this = %p, fd = %d]",
// res, buf, buf_len, this, _fd);
}
return res;
}
int
transport_layer_sockets::connect ()
{
assert (_fd == -1);
assert (!_is_accepted_endpoint);
assert (!_is_listening_endpoint);
static bool assume_cygserver = false;
debug_printf ("connect () [this = %p]", this);
for (int retries = 0; retries != MAX_CONNECT_RETRY; retries++)
{
_fd = cygwin_socket (AF_UNIX, SOCK_STREAM, 0);
if (_fd == -1)
{
system_printf ("failed to create UNIX domain socket: %s",
strerror (errno));
return -1;
}
if (cygwin_connect (_fd, (struct sockaddr *) &_addr, _addr_len) == 0)
{
assume_cygserver = true;
debug_printf ("0 = connect () [this = %p, fd = %d]", this, _fd);
return 0;
}
if (!assume_cygserver || errno != ECONNREFUSED)
{
debug_printf ("failed to connect to server: %s", strerror (errno));
(void) ::close (_fd);
_fd = -1;
return -1;
}
(void) ::close (_fd);
_fd = -1;
Sleep (0); // Give the server a chance.
}
debug_printf ("failed to connect to server: %s", strerror (errno));
return -1;
}

View File

@ -1,127 +0,0 @@
/* threaded_queue.h
Copyright 2001, 2002 Red Hat Inc.
Written by Robert Collins <rbtcollins@hotmail.com>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef _THREADED_QUEUE_
#define _THREADED_QUEUE_
/*****************************************************************************/
/* a specific request */
class queue_request
{
public:
queue_request *_next;
queue_request () : _next (NULL) {}
virtual ~queue_request ();
virtual void process () = 0;
};
/*****************************************************************************/
/* a queue to allocate requests from n submission loops to x worker threads */
class queue_submission_loop;
class threaded_queue
{
public:
threaded_queue (size_t initial_workers = 1);
~threaded_queue ();
void add_submission_loop (queue_submission_loop *);
bool running () const { return _running; }
bool start ();
bool stop ();
void add (queue_request *);
private:
long _workers_count;
bool _running;
queue_submission_loop *_submitters_head;
long _requests_count; // Informational only.
queue_request *_requests_head;
CRITICAL_SECTION _queue_lock;
HANDLE _requests_sem; // == _requests_count
static DWORD WINAPI start_routine (LPVOID /* this */);
void create_workers (size_t initial_workers);
void worker_loop ();
};
/*****************************************************************************/
/* parameters for a request finding and submitting loop */
class queue_submission_loop
{
friend class threaded_queue;
public:
queue_submission_loop (threaded_queue *, bool ninterruptible);
virtual ~queue_submission_loop ();
bool start ();
bool stop ();
threaded_queue *queue () { return _queue; };
protected:
bool _running;
HANDLE _interrupt_event;
threaded_queue *const _queue;
private:
bool _interruptible;
HANDLE _hThread;
DWORD _tid;
queue_submission_loop *_next;
static DWORD WINAPI start_routine (LPVOID /* this */);
virtual void request_loop () = 0;
};
#ifdef __cplusplus
/*---------------------------------------------------------------------------*
* Some type-safe versions of the various interlocked functions.
*---------------------------------------------------------------------------*/
template <typename T> T *
TInterlockedExchangePointer (T **lvalue, T *rvalue)
{
return reinterpret_cast<T *>
(InterlockedExchangePointer (reinterpret_cast<void **> (lvalue),
reinterpret_cast<void *> (rvalue)));
}
template <typename T> T *
TInterlockedCompareExchangePointer (T **lvalue, T *rvalue1, T *rvalue2)
{
return reinterpret_cast<T *>
(InterlockedCompareExchangePointer (reinterpret_cast<void **> (lvalue),
reinterpret_cast<void *> (rvalue1),
reinterpret_cast<void *> (rvalue2)));
}
#endif /* __cplusplus */
#endif /* _THREADED_QUEUE_ */