Remove some cygserver files.
This commit is contained in:
parent
b258e2c63b
commit
6c125ba745
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
|
@ -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));
|
||||
}
|
|
@ -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__ */
|
|
@ -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 ()
|
||||
{}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
Loading…
Reference in New Issue