diff options
Diffstat (limited to 'winsup/cygwin/cygserver_client.cc')
-rwxr-xr-x | winsup/cygwin/cygserver_client.cc | 572 |
1 files changed, 447 insertions, 125 deletions
diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc index 8a1eeafef..138c9ddc0 100755 --- a/winsup/cygwin/cygserver_client.cc +++ b/winsup/cygwin/cygserver_client.cc @@ -1,207 +1,529 @@ /* cygserver_client.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. */ +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ #ifdef __OUTSIDE_CYGWIN__ -#undef __INSIDE_CYGWIN__ +#include "woutsup.h" #else #include "winsup.h" #endif -#ifndef __INSIDE_CYGWIN__ -#define debug_printf printf -#define api_fatal printf -#include <stdio.h> -#include <windows.h> -#endif -#include <sys/socket.h> +#include <assert.h> #include <errno.h> +#include <stdio.h> #include <unistd.h> -//#include "security.h" -#include "cygwin/cygserver_transport.h" -#include "cygwin/cygserver_transport_pipes.h" -#include "cygwin/cygserver_transport_sockets.h" + +#include "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. -/* 0 = untested, 1 = running, 2 = dead */ -int cygserver_running=CYGSERVER_UNKNOWN; -/* on by default during development. For release, we probably want off by default */ -int allow_daemon = TRUE; +/* 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, sizeof (version)) +client_request_get_version::client_request_get_version () + : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version)) { - buffer = (char *)&version; + msglen (0); // No parameters for request. + + // verbose: syscall_printf ("created"); } -client_request_attach_tty::client_request_attach_tty () : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +/* + * 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 { - buffer = (char *)&req; - req.pid = 0; - req.master_pid = 0; - req.from_master = NULL; - req.to_master = NULL; + 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; } -client_request_attach_tty::client_request_attach_tty (DWORD npid, DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) : client_request (CYGSERVER_REQUEST_ATTACH_TTY, sizeof (req)) +#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)) { - buffer = (char *)&req; - req.pid = npid; + req.pid = GetCurrentProcessId (); req.master_pid = nmaster_pid; req.from_master = nfrom_master; req.to_master = nto_master; -} -client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN, 0) -{ - buffer = NULL; + 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); } -client_request::client_request (cygserver_request_code id, ssize_t buffer_size) : header (id, buffer_size) +#else /* !__INSIDE_CYGWIN__ */ + +client_request_attach_tty::client_request_attach_tty () + : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) { + // verbose: syscall_printf ("created"); } -client_request::~client_request () +#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::operator class request_header () +client_request::header_t::header_t (const request_code_t request_code, + const size_t msglen) + : msglen (msglen), + request_code (request_code) { - return header; + 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 *conn) +client_request::send (transport_layer_base * const conn) { - if (!conn) - return; - debug_printf("this=%p, conn=%p\n",this, conn); - ssize_t bytes_written, bytes_read; - debug_printf("header.cb = %ld\n",header.cb); - if ((bytes_written = conn->write ((char *)&header, sizeof (header))) - != sizeof(header) || (header.cb && - (bytes_written = conn->write (buffer, header.cb)) != header.cb)) + 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 ()) { - header.error_code = -1; - debug_printf ("bytes written != request size\n"); + 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; } - debug_printf("Sent request, size (%ld)\n",bytes_written); + 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); + } - if ((bytes_read = conn->read ((char *)&header, sizeof (header))) - != sizeof (header) || (header.cb && - (bytes_read = conn->read (buffer, header.cb) ) != header.cb)) + client_request *req = NULL; + + switch (header.request_code) { - header.error_code = -1; - debug_printf("failed reading response \n"); + 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; } - debug_printf ("completed ok\n"); + + 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 } -/* Oh, BTW: Fix the procedural basis and make this more intuitive. */ +#endif /* !__INSIDE_CYGWIN__ */ -int -cygserver_request (client_request * req) +client_request::client_request (request_code_t const id, + void * const buf, + size_t const buflen) + : _header (id, buflen), + _buf (buf), + _buflen (buflen) { - class transport_layer_base *transport; + assert ((!_buf && !_buflen) || (_buf && _buflen)); +} - if (!req || allow_daemon != TRUE) - return -1; +client_request::~client_request () +{} - /* dont' retry every request if the server's not there */ - if (cygserver_running==CYGSERVER_DEAD && req->header.req_id != CYGSERVER_REQUEST_GET_VERSION) - return -1; +int +client_request::make_request () +{ + assert (cygserver_running == CYGSERVER_UNKNOWN \ + || cygserver_running == CYGSERVER_OK \ + || cygserver_running == CYGSERVER_UNAVAIL); - transport = create_server_transport (); + if (cygserver_running == CYGSERVER_UNKNOWN) + cygserver_init (); - /* FIXME: have at most one connection per thread. use TLS to store the details */ - /* logic is: - * if not tlskey->conn, new conn, - * then; transport=conn; - */ - if (!transport->connect ()) + 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) { - delete transport; + syscall_printf ("cygserver un-available"); + error_code (ENOSYS); return -1; } - debug_printf ("connected to server %p\n", transport); + 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; + } - req->send(transport); + // verbose: debug_printf ("connected to server %p", transport); - transport->close (); + send (transport); - delete transport; + safe_delete (transport); return 0; } -#if 0 -BOOL +#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 () { - BOOL ret_val = FALSE; - HANDLE pipe = CreateFile (pipe_name, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &sec_all_nih, - OPEN_EXISTING, - 0, - NULL); - if (pipe != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_BUSY) - ret_val = TRUE; - - if (pipe && pipe != INVALID_HANDLE_VALUE) - CloseHandle (pipe); - - return (ret_val); + 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 (); } -#endif void cygserver_init () { - int rc; - if (allow_daemon != TRUE) + if (!allow_daemon) { - cygserver_running = CYGSERVER_DEAD; + syscall_printf ("cygserver use disabled in client"); + cygserver_running = CYGSERVER_UNAVAIL; return; } - if (cygserver_running==CYGSERVER_OK) + assert (cygserver_running == CYGSERVER_UNKNOWN \ + || cygserver_running == CYGSERVER_OK \ + || cygserver_running == CYGSERVER_UNAVAIL); + + if (cygserver_running == CYGSERVER_OK) return; - client_request_get_version *req = - new client_request_get_version (); - - rc = cygserver_request (req); - delete req; - if (rc < 0) - cygserver_running = CYGSERVER_DEAD; - else if (rc > 0) - api_fatal ( "error connecting to cygwin server. error: %d", rc ); - else if (req->version.major != CYGWIN_SERVER_VERSION_MAJOR || - req->version.api != CYGWIN_SERVER_VERSION_API || - req->version.minor > CYGWIN_SERVER_VERSION_MINOR) - api_fatal ( "incompatible version of cygwin server.\n\ - client version %d.%d.%d.%d, server version%ld.%ld.%ld.%ld", - CYGWIN_SERVER_VERSION_MAJOR, - CYGWIN_SERVER_VERSION_API, - CYGWIN_SERVER_VERSION_MINOR, - CYGWIN_SERVER_VERSION_PATCH, - req->version.major, - req->version.api, - req->version.minor, - req->version.patch ); - else - cygserver_running = CYGSERVER_OK; + if (!check_cygserver_available ()) + cygserver_running = CYGSERVER_UNAVAIL; } |