diff options
Diffstat (limited to 'winsup/cygwin/cygserver.cc')
-rwxr-xr-x | winsup/cygwin/cygserver.cc | 964 |
1 files changed, 592 insertions, 372 deletions
diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc index 24a90dc20..0c0740379 100755 --- a/winsup/cygwin/cygserver.cc +++ b/winsup/cygwin/cygserver.cc @@ -1,72 +1,154 @@ /* cygserver.cc - Copyright 2001 Red Hat Inc. + Copyright 2001, 2002 Red Hat Inc. Written by Egor Duda <deo@logos-m.ru> - This file is part of Cygwin. +This file is part of Cygwin. - This software is a copyrighted work licensed under the terms of the - Cygwin license. Please consult the file "CYGWIN_LICENSE" for - details. */ +This 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 <errno.h> +#include <getopt.h> +#include <signal.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <windows.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <signal.h> -#include "wincap.h" -#include "cygwin_version.h" -#include "getopt.h" +#include "cygerrno.h" +#include "cygwin_version.h" -#include "cygwin/cygserver_transport.h" -#include "cygwin/cygserver_transport_pipes.h" -#include "cygwin/cygserver_transport_sockets.h" -#include "threaded_queue.h" -#include "cygwin/cygserver_process.h" #include "cygwin/cygserver.h" -#include "cygserver_shm.h" +#include "cygwin/cygserver_process.h" +#include "cygwin/cygserver_transport.h" -/* for quieter operation, set to 0 */ -#define DEBUG 0 -#define debug_printf if (DEBUG) printf +// Version string. +static const char version[] = "$Revision$"; -GENERIC_MAPPING access_mapping; -class transport_layer_base *transport; +/* + * 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); -DWORD request_count = 0; + len += snprintf (buf + len, BUFSIZ - len, "\n"); -BOOL + 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 ) + rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ; + if (!rc) { - printf ( "error opening process token (%lu)\n", GetLastError () ); + 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 ) + rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid); + if (!rc) { - printf ( "error getting prigilege luid (%lu)\n", GetLastError () ); + 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 ) + rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ; + if (!rc) { - printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () ); + system_printf ("error adjusting privilege level. (%lu)", + GetLastError ()); ret_val = FALSE; goto out; } @@ -79,194 +161,232 @@ setup_privileges () ret_val = TRUE; out: - CloseHandle ( hToken ); + 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) + DWORD access, + HANDLE from_handle, + HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE) { HANDLE local_handle = NULL; int ret_val = EACCES; - char sd_buf [1024]; - PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; - DWORD bytes_needed; - PRIVILEGE_SET ps; - DWORD ps_len = sizeof (ps); - BOOL status; if (from_process != GetCurrentProcess ()) -{ - - if (!DuplicateHandle (from_process, from_handle, - GetCurrentProcess (), &local_handle, - 0, bInheritHandle, - DUPLICATE_SAME_ACCESS)) { - printf ( "error getting handle(%u) to server (%lu)\n", (unsigned int)from_handle, GetLastError ()); - goto out; - } -} else - local_handle = from_handle; + if (!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 (!GetKernelObjectSecurity (local_handle, - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - sd, sizeof (sd_buf), &bytes_needed)) + if (!wincap.has_security ()) + assert (!from_process_token); + else { - printf ( "error getting handle SD (%lu)\n", GetLastError ()); - goto out; - } + 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); + MapGenericMask (&access, &access_mapping); - if (!AccessCheck (sd, from_process_token, access, &access_mapping, - &ps, &ps_len, &access, &status)) - { - printf ( "error checking access rights (%lu)\n", GetLastError ()); - goto out; - } + if (!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) - { - printf ( "access to object denied\n"); - 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)) + to_process, to_handle_ptr, + access, bInheritHandle, 0)) { - printf ( "error getting handle to client (%lu)\n", GetLastError ()); + 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: + + out: if (local_handle && from_process != GetCurrentProcess ()) CloseHandle (local_handle); 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) -{ - return check_and_dup_handle(from_process,to_process,from_process_token,access,from_handle,to_handle_ptr,FALSE); -} +/* + * client_request_attach_tty::serve () + */ void -client_request::serve (transport_layer_base *conn, class process_cache *cache) +client_request_attach_tty::serve (transport_layer_base *const conn, + process_cache *) { - printf ("*****************************************\n" - "A call to the base client_request class has occured\n" - "This indicates a mismatch in a virtual function definition somewhere\n"); - exit (1); -} + assert (conn); -void -client_request_attach_tty::serve(transport_layer_base *conn, class process_cache *cache) -{ - HANDLE from_process_handle = NULL; - HANDLE to_process_handle = NULL; - HANDLE token_handle = NULL; - DWORD rc; + assert (!error_code ()); - if (header.cb != sizeof (req)) + if (!wincap.has_security ()) { - header.error_code = EINVAL; + syscall_printf ("operation only supported on systems with security"); + error_code (EINVAL); + msglen (0); return; } -#if DEBUG - printf ("%ld:(%p,%p) -> %ld\n", req.master_pid, - req.from_master, req.to_master, - req.pid); -#endif - - from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); - to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); - if (!from_process_handle || !to_process_handle) + if (msglen () != sizeof (req)) { - printf ("error opening process (%lu)\n", GetLastError ()); - header.error_code = EACCES; - goto out; + syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + sizeof (req), msglen ()); + error_code (EINVAL); + msglen (0); + return; } - transport->impersonate_client (); - - rc = OpenThreadToken (GetCurrentThread (), - TOKEN_QUERY, - TRUE, - &token_handle); + msglen (0); // Until we fill in some fields. - transport->revert_to_self (); + // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld", + // req.master_pid, req.from_master, req.to_master, + // req.pid); - if (!rc) + // 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) { - printf ("error opening thread token (%lu)\n", GetLastError ()); - header.error_code = EACCES; - goto out; + system_printf ("error opening `from' process, error = %lu", + GetLastError ()); + error_code (EACCES); + return; } - if (check_and_dup_handle (from_process_handle, to_process_handle, - token_handle, - GENERIC_READ, - req.from_master, - &req.from_master) != 0) + // verbose: debug_printf ("opening process %ld", req.pid); + + const HANDLE to_process_handle = + OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); + + if (!to_process_handle) { - printf ("error duplicating from_master handle (%lu)\n", GetLastError ()); - header.error_code = EACCES; - goto out; + system_printf ("error opening `to' process, error = %lu", + GetLastError ()); + CloseHandle (from_process_handle); + error_code (EACCES); + return; } - if (req.to_master) + // 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) { - if (check_and_dup_handle (from_process_handle, to_process_handle, - token_handle, - GENERIC_WRITE, - req.to_master, - &req.to_master) != 0) - { - printf ("error duplicating to_master handle (%lu)\n", GetLastError ()); - header.error_code = EACCES; - goto out; - } + system_printf ("error opening thread token, error = %lu", + GetLastError ()); + CloseHandle (from_process_handle); + CloseHandle (to_process_handle); + error_code (EACCES); + return; } -#if DEBUG - printf ("%ld -> %ld(%p,%p)\n", req.master_pid, req.pid, - req.from_master, req.to_master); -#endif + // From this point on, a reply body is returned to the client. - header.error_code = 0; + const HANDLE from_master = req.from_master; + const HANDLE to_master = req.to_master; -out: - if (from_process_handle) - CloseHandle (from_process_handle); - if (to_process_handle) - CloseHandle (to_process_handle); - if (token_handle) - CloseHandle (token_handle); + 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 *conn, class process_cache *cache) +client_request_get_version::serve (transport_layer_base *, process_cache *) { - if (header.cb != sizeof (version)) - { - header.error_code = EINVAL; - return; - } - header.error_code = 0; + 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; @@ -275,280 +395,380 @@ client_request_get_version::serve(transport_layer_base *conn, class process_cach class server_request : public queue_request { - public: - server_request (transport_layer_base *newconn, class process_cache *newcache); - virtual void process (); - private: - char request_buffer [MAX_REQUEST_SIZE]; - transport_layer_base *conn; - class process_cache *cache; -}; +public: + server_request (transport_layer_base *const conn, process_cache *const cache) + : _conn (conn), _cache (cache) + {} -class server_process_param : public queue_process_param -{ - public: - transport_layer_base *transport; - server_process_param () : queue_process_param (false) {}; -}; + virtual ~server_request () + { + safe_delete (_conn); + } -class server_request_queue : public threaded_queue -{ - public: - class process_cache *cache; - void process_requests (transport_layer_base *transport); - virtual void add (transport_layer_base *conn); + virtual void process () + { + client_request::handle_request (_conn, _cache); + } + +private: + transport_layer_base *const _conn; + process_cache *const _cache; }; -class server_request_queue request_queue; -static DWORD WINAPI -request_loop (LPVOID LpParam) +class server_submission_loop : public queue_submission_loop { - class server_process_param *params = (server_process_param *) LpParam; - class server_request_queue *queue = (server_request_queue *) params->queue; - class transport_layer_base * transport = params->transport; - while (queue->active) +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) { - transport_layer_base * new_conn = transport->accept (); - /* FIXME: this is a little ugly. What we really want is to wait on two objects: - * one for the pipe/socket, and one for being told to shutdown. Otherwise - * this will stay a problem (we won't actually shutdown until the request - * _AFTER_ the shutdown request. And sending ourselves a request is ugly - */ - if (new_conn && queue->active) - queue->add (new_conn); + assert (_transport); + assert (_cache); } - return 0; -} -/* TODO: check we are not being asked to service a already serviced transport */ +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_request_queue::process_requests (transport_layer_base *transport) +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) { - class server_process_param *params = new server_process_param; - params->transport = transport; - threaded_queue::process_requests (params, request_loop); + // verbose: syscall_printf ("created"); } void -client_request_shutdown::serve (transport_layer_base *conn, class process_cache *cache) +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_ */ - /* tell the main thread to shutdown */ - request_queue.active=false; + + kill (getpid (), SIGINT); + + msglen (0); } -server_request::server_request (transport_layer_base *newconn, class process_cache *newcache) +static sig_atomic_t shutdown_server = false; + +static void +handle_signal (const int signum) { - conn = newconn; - cache = newcache; + /* any signal makes us die :} */ + + shutdown_server = true; } -void -server_request::process () +/* + * print_usage () + */ + +static void +print_usage (const char *const pgm) { - ssize_t bytes_read, bytes_written; - struct request_header* req_ptr = (struct request_header*) &request_buffer; - client_request *req = NULL; - debug_printf ("about to read\n"); + 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"); +} - bytes_read = conn->read (request_buffer, sizeof (struct request_header)); - if (bytes_read != sizeof (struct request_header)) - { - printf ("error reading from connection (%lu)\n", GetLastError ()); - goto out; - } - debug_printf ("got header (%ld)\n", bytes_read); +/* + * print_version () + */ - switch (req_ptr->req_id) - { - case CYGSERVER_REQUEST_GET_VERSION: - req = new client_request_get_version (); break; - case CYGSERVER_REQUEST_ATTACH_TTY: - req = new client_request_attach_tty (); break; - case CYGSERVER_REQUEST_SHUTDOWN: - req = new client_request_shutdown (); break; - case CYGSERVER_REQUEST_SHM_GET: - req = new client_request_shm (); break; - default: - req = new client_request (CYGSERVER_REQUEST_INVALID, 0); - req->header.error_code = ENOSYS; - debug_printf ("Bad client request - returning ENOSYS\n"); - } +static void +print_version (const char *const pgm) +{ + char *vn = NULL; - if (req->header.cb != req_ptr->cb) - { - debug_printf ("Mismatch in request buffer sizes\n"); - goto out; - } + const char *const colon = strchr (version, ':'); - if (req->header.cb) + if (!colon) { - - bytes_read = conn->read (req->buffer, req->header.cb); - if (bytes_read != req->header.cb) - { - debug_printf ("error reading from connection (%lu)\n", GetLastError ()); - goto out; - } - debug_printf ("got body (%ld)\n",bytes_read); + vn = strdup ("?"); } + else + { + vn = strdup (colon + 2); // Skip ": " - /* this is not allowed to fail. We must return ENOSYS at a minimum to the client */ - req->serve (conn, cache); + char *const spc = strchr (vn, ' '); - if ((bytes_written = conn->write ((char *)&req->header, sizeof (req->header))) - != sizeof(req->header) || (req->header.cb && - (bytes_written = conn->write (req->buffer, req->header.cb)) != req->header.cb)) - { - req->header.error_code = -1; - printf ("error writing to connection (%lu)\n", GetLastError ()); - goto out; + if (spc) + *spc = '\0'; } - debug_printf("Sent reply, size (%ld)\n",bytes_written); - printf ("."); - -out: - conn->close (); - delete conn; - if (req) - delete (req); + 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); } -void -server_request_queue::add (transport_layer_base *conn) -{ - /* safe to not "Try" because workers don't hog this, they wait on the event - */ - /* every derived ::add must enter the section! */ - EnterCriticalSection (&queuelock); - if (!running) - { - conn->close (); - delete conn; - LeaveCriticalSection (&queuelock); - return; - } - queue_request * listrequest = new server_request (conn, cache); - threaded_queue::add (listrequest); - LeaveCriticalSection (&queuelock); -} +/* + * main () + */ -void -handle_signal (int signal) +int +main (const int argc, char *argv[]) { - /* any signal makes us die :} */ - /* FIXME: link upwards, and then this becomes a trivial method call to - * only shutdown _this queue_ - */ - /* tell the main thread to shutdown */ - request_queue.active=false; -} + 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} + }; -struct option longopts[] = { - {"shutdown", no_argument, NULL, 's'}, - {0, no_argument, NULL, 0} -}; + const char opts[] = "c:hr:sv"; -char opts[] = "s"; + int cleanup_threads = 2; + int request_threads = 10; + bool shutdown = false; -int -main (int argc, char **argv) -{ - int shutdown=0; - char i; + 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 ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) - switch (i) + 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 = 1; - break; - default: - break; - /*NOTREACHED*/ + shutdown = true; + break; + + case 'v': + print_version (pgm); + return 0; + + case '?': + fprintf (stderr, "Try `%s --help' for more information.\n", pgm); + exit (1); } - wincap.init(); - if (wincap.has_security ()) - setup_privileges (); - transport = create_server_transport (); + if (optind != argc) + { + fprintf (stderr, "%s: too many arguments\n", pgm); + exit (1); + } if (shutdown) { - if (!transport->connect()) + /* 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 ()) { - printf ("couldn't establish connection with server\n"); + fprintf (stderr, "%s: shutdown request failed: %s\n", + pgm, strerror (req.error_code ())); exit (1); } - client_request_shutdown *request = - new client_request_shutdown (); - request->send (transport); - transport->close(); - delete transport; - delete request; - exit(0); + + // FIXME: It would be nice to wait here for the daemon to exit. + + return 0; } - char version[200]; - /* Cygwin dll release */ - snprintf (version, 200, "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s", - cygwin_version.dll_major / 1000, - cygwin_version.dll_major % 1000, - cygwin_version.dll_minor, - cygwin_version.api_major, - cygwin_version.api_minor, - cygwin_version.shared_data, - CYGWIN_SERVER_VERSION_MAJOR, - CYGWIN_SERVER_VERSION_API, - CYGWIN_SERVER_VERSION_MINOR, - CYGWIN_SERVER_VERSION_PATCH, - cygwin_version.mount_registry, - cygwin_version.dll_build_date); +#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 version %s starting up", version); - if (signal (SIGQUIT, handle_signal) == SIG_ERR) + 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) { - printf ("\ncould not install signal handler (%d)- aborting startup\n", errno); exit (1); } printf ("."); - transport->listen (); - printf ("."); - class process_cache cache (2); - request_queue.initial_workers = 10; - request_queue.cache = &cache; - request_queue.create_workers (); - printf ("."); - request_queue.process_requests (transport); + + cache.start (); printf ("."); - cache.create_workers (); + + request_queue.start (); printf ("."); - cache.process_requests (); - printf (".complete\n"); - /* TODO: wait on multiple objects - the thread handle for each request loop + - * all the process handles. This should be done by querying the request_queue and - * the process cache for all their handles, and then waiting for (say) 30 seconds. - * after that we recreate the list of handles to wait on, and wait again. - * the point of all this abstraction is that we can trivially server both sockets - * and pipes simply by making a new transport, and then calling - * request_queue.process_requests (transport2); + + printf ("complete\n"); + + /* TODO: wait on multiple objects - the thread handle for each + * request loop + all the process handles. This should be done by + * querying the request_queue and the process cache for all their + * handles, and then waiting for (say) 30 seconds. after that we + * recreate the list of handles to wait on, and wait again. the + * point of all this abstraction is that we can trivially server + * both sockets and pipes simply by making a new transport, and then + * calling request_queue.process_requests (transport2); */ /* WaitForMultipleObjects abort && request_queue && process_queue && signal -- if signal event then retrigger it - */ - while (1 && request_queue.active) - { - sleep (1); - } - printf ("\nShutdown request recieved - new requests will be denied\n"); - request_queue.cleanup (); + */ + 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"); - transport->close (); + safe_delete (transport); printf ("No longer accepting requests - cygwin will operate in daemonless mode\n"); - cache.cleanup (); + cache.stop (); printf ("All outstanding process-cache activities completed\n"); printf ("daemon shutdown\n"); + + return 0; } |