License: X11-MIT Changelog: * include/wine: rpcss_shared.h; programs/rpcss: epmap_server, rpcss_main.c, rpcss_np_server.c: Greg Turner <gmturner007@ameritech.net> - lots of comments (where appropriate ;) -- diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/include/wine/rpcss_shared.h ./include/wine/rpcss_shared.h --- ../wine.test/include/wine/rpcss_shared.h 2002-12-02 01:28:42.000000000 -0600 +++ ./include/wine/rpcss_shared.h 2002-12-02 01:30:11.000000000 -0600 @@ -6,24 +6,31 @@ #include "rpcdcep.h" +/* format: use an (implicitly unsigned) four-digit hex value. + bump this when the protocol changes */ #define RPCSS_NP_PROTOCOL_VERSION 0x0000 #define RPCSS_STRINGIFY_MACRO(x) RPCSS_STRINGIFY_MACRO2(x) #define RPCSS_STRINGIFY_MACRO2(x) #x - /* changing this should allow simultanously running rpcss instances - * to remain separate; this should be good enough for wine packagers and - * forks, which might need to keep their rpcss servers isolated from those - * generated by the tarballs, or for crude multiuser experiments. - */ +/* changing this should allow simultanously running rpcss instances + * to remain separate; this should be good enough for wine packagers and + * forks, which might need to keep their rpcss servers isolated from those + * generated by the tarballs, or for crude multiuser experiments. + */ #define RPCSS_NP_VARIANT "-wine-" #define STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION \ RPCSS_STRINGIFY_MACRO(RPCSS_NP_PROTOCOL_VERSION) +/* only local communications are supported so far on this pipe. + until this changes, we can just use a constant pipe-name */ #define RPCSS_NAMED_PIPE_NAME \ ("\\\\.\\pipe\\RpcssNP" RPCSS_NP_VARIANT STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION) - + +/* the "MasterMutex" is coupled with the "ServerWorkEvent" on the (rpcss) server- + side to provide atomicity of message processing, and ensure only one rpcss + is alive at a time */ #define RPCSS_MASTER_MUTEX_NAME \ ("RPCSSMasterMutex" RPCSS_NP_VARIANT STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION) diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/epmap_server.c ./programs/rpcss/epmap_server.c --- ../wine.test/programs/rpcss/epmap_server.c 2002-12-02 01:28:42.000000000 -0600 +++ ./programs/rpcss/epmap_server.c 2002-12-02 01:30:11.000000000 -0600 @@ -36,6 +36,20 @@ return rval; } +/*********************************************************** + * find_endpoint + * + * Find an endpoint in the registered-endpoints linklist + * + * PARAMS + * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match + * protseq [I] The protseq string to match + * object [I] the UUID to match + * + * RETURNS + * Success: the (struct epmap_entry *) describing the endpoint + * Failure: NULL + */ static struct epmap_entry *find_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, const UUID *object) { @@ -51,6 +65,20 @@ return NULL; } +/******************************************************* + * register_endpoint + * + * Add a set of endpoints to the registered-endpoints linklist (head). + * If obj_count is zero, adds the null object instead. + * + * PARAMS + * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to use + * protseq [I] The protseq string to use + * endpoint [I] pointer to objcount consecutive strings + * objects [I] pointer to objcount consecutive UUID structures + * obj_count [I] the number of objects to register + * no_replace [I] if false, duplicates replace (otherwise they just go in) + */ static void register_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, const char *endpoint, const UUID *objects, int objcount, int no_replace) @@ -86,6 +114,20 @@ } } +/******************************************************* + * unregister_endpoint + * + * Remove a set of endpoints from the registered-endpoints linklist. + * Failures ignored silently. + * + * PARAMS + * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match + * protseq [I] The protseq string to match + * endpoint [I] pointer to objcount consecutive strings + * objects [I] pointer to objcount consecutive UUID structures + * obj_count [I] the number of objects to register + * no_replace [I] if false, duplicates replace (otherwise they just go in) + */ static void unregister_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, const char *endpoint, const UUID *objects, int objcount) { @@ -130,6 +172,18 @@ } } +/******************************************************* + * resolve_endpoint + * + * Find an endpoint in the registered endpoints linklist and return + * its endpoint string. Truncated at MAX_RPCSS_NP_REPLY_STRING_LEN. + * + * PARAMS + * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match + * protseq [I] The protseq string to match + * object [I] pointer to the UUID structure to match + * rslt_ep [IO] buffer to receive the resulting endpoint string. + */ static void resolve_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, const UUID *object, char *rslt_ep) { @@ -138,6 +192,7 @@ if (!(map = find_endpoint(iface, protseq, object))) return; + /* FIXME: off-by-one for '\0'? */ len = min( MAX_RPCSS_NP_REPLY_STRING_LEN, strlen(map->endpoint)+1 ); if (len) memcpy(rslt_ep, map->endpoint, len); } diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/rpcss_main.c ./programs/rpcss/rpcss_main.c --- ../wine.test/programs/rpcss/rpcss_main.c 2002-12-02 01:28:42.000000000 -0600 +++ ./programs/rpcss/rpcss_main.c 2002-12-02 01:30:11.000000000 -0600 @@ -65,11 +65,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); static HANDLE master_mutex; +/* when activity occurs, what should the new lazy timeout be + reset to? */ static long max_lazy_timeout = RPCSS_DEFAULT_MAX_LAZY_TIMEOUT; - /* when do we just give up and bail? (UTC) */ static SYSTEMTIME lazy_timeout_time; + HANDLE RPCSS_GetMasterMutex() { return master_mutex; @@ -80,6 +82,24 @@ return max_lazy_timeout; } +/******************************************************** + * RPCSS_Work + * + * Does not actually do any work, but patiently waits a + * second or so for work to occur. If any does, returns + * TRUE. If not, returns FALSE. For now just maps to + * the NPDoWork function but if we support additional + * communication "transports", we will need to hack on this. + * + * RETURNS + * work occured: TRUE + * work did not occur: FALSE + */ +BOOL RPCSS_Work() +{ + return RPCSS_NPDoWork(); +} + #if defined(NONAMELESSSTRUCT) #define FILETIME_TO_ULARGEINT(filetime, ularge) \ ( ularge.s.LowPart = filetime.dwLowDateTime, \ @@ -98,7 +118,11 @@ #define TEN_MIL 10000000LL -/* returns time remaining in seconds */ +/******************************************************** + * RPCSS_GetLazyTimeRemining + * RETURNS + * max(Time remaining in seconds,0) as a long + */ long RPCSS_GetLazyTimeRemaining() { SYSTEMTIME st_just_now; @@ -118,6 +142,12 @@ return (ul_ltt.QuadPart - ul_jn.QuadPart) / TEN_MIL; } +/******************************************************** + * RPCSS_SetLazyTimeRemaining + * + * PARAMS + * seconds [I] new desired timeout in seconds + */ void RPCSS_SetLazyTimeRemaining(long seconds) { SYSTEMTIME st_just_now; @@ -145,11 +175,12 @@ #undef ULARGEINT_TO_FILETIME #undef TEN_MIL -BOOL RPCSS_Work() -{ - return RPCSS_NPDoWork(); -} - +/******************************************************** + * RPCSS_SetMaxLazyTimeout + * + * Put the supplied value into the MaxLazyTimeout variable, + * unless the timeout would become shorter as a result. + */ void RPCSS_SetMaxLazyTimeout(long mlt) { /* FIXME: this max ensures that no caller will decrease our wait time, @@ -158,12 +189,25 @@ max_lazy_timeout = max(RPCSS_GetLazyTimeRemaining(), mlt); } +/******************************************************** + * RPCSS_Empty + * + * Returns true iff all datastructures tracked by this + * RPCSS server process are empty. + */ BOOL RPCSS_Empty() { /* for now the only datastructure we check is the endpoint map */ return RPCSS_EpmapEmpty(); } +/******************************************************** + * RPCSS_ReadyToDie + * + * Returns true iff this process has no data in it's cache, + * no active message-handler threads, and is not still + * waiting for the timeout condition. + */ BOOL RPCSS_ReadyToDie() { long ltr = RPCSS_GetLazyTimeRemaining(); @@ -172,6 +216,12 @@ return ( empty && (ltr <= 0) && (stc == 0) ); } +/******************************************************** + * RPCSS_Initialize + * + * Initialize services, or, if we cannot become the main + * rpcss process, just return false. + */ BOOL RPCSS_Initialize() { WINE_TRACE("\n"); @@ -194,8 +244,13 @@ return TRUE; } -/* returns false if we discover at the last moment that we - aren't ready to terminate */ +/******************************************************** + * RPCSS_Shutdown + * + * Release the main server role, and clean up house. + * returns false if we discover at the last moment that we + * aren't ready to terminate + */ BOOL RPCSS_Shutdown() { if (!RPCSS_UnBecomePipeServer()) @@ -209,6 +264,14 @@ return TRUE; } +/******************************************************** + * RPCSS_MainLoop + * + * Called while we have the master rpcss server role. + * Just spins, waiting for the timeout condition. + * Each time a new message handler thread is spawned, the + * timeout is reset to max_lazy_timeout + */ void RPCSS_MainLoop() { BOOL did_something_new; diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/rpcss_np_server.c ./programs/rpcss/rpcss_np_server.c --- ../wine.test/programs/rpcss/rpcss_np_server.c 2002-12-02 01:28:42.000000000 -0600 +++ ./programs/rpcss/rpcss_np_server.c 2002-12-02 01:30:11.000000000 -0600 @@ -28,7 +28,13 @@ static HANDLE np_server_end; static HANDLE np_server_work_event; static CRITICAL_SECTION np_server_cs; + +/* a sanity checking mechanism to ensure that there are no + handler threads running when we decide it's time to shut down */ static LONG srv_thread_count; + +/* the "definitive" way to discover if our process is the active server + process */ static BOOL server_live; LONG RPCSS_SrvThreadCount() @@ -36,6 +42,14 @@ return srv_thread_count; } +/******************************************************** + * RPCSS_UnBecomePipeServer + * + * Release ownership of the pipe server role. To do so, + * we are required to get the master mutex; this ensures + * we will not race with another RPCSS instance that is + * simultaneously in RPCSS_BecomePipeServer(). + */ BOOL RPCSS_UnBecomePipeServer() { BOOL rslt = TRUE; @@ -60,8 +74,8 @@ } /* now that we have the master mutex, we can safely stop - listening on the pipe. Before we proceed, we do a final - check that it's OK to shut down to ensure atomicity */ + listening on the pipe. Before we proceed, we check that it's + still OK to shut down. */ if (!RPCSS_ReadyToDie()) rslt = FALSE; @@ -81,16 +95,27 @@ return rslt; } +/******************************************************** + * RPCSS_ServerProcessRANMessage + * + * Message Handler for the "RAN" message. + */ void RPCSS_ServerProcessRANMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply) { WINE_TRACE("\n"); - /* we do absolutely nothing, but on the server end, - the lazy timeout is reset as a result of our connection. */ + /* update the max lazy time out according to the message's instructions. */ RPCSS_SetMaxLazyTimeout(pMsg->message.ranmsg.timeout); + /* Set the current time remaining to the new max lazy timeout */ RPCSS_SetLazyTimeRemaining(RPCSS_GetMaxLazyTimeout()); + /* reply ignored */ pReply->as_uint = 0; } +/******************************************************** + * RPCSS_ServerProcessREGISTEREPMessage + * + * Message Handler for the "REGISTEREP" message. + */ void RPCSS_ServerProcessREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata) { @@ -109,6 +134,11 @@ pReply->as_uint = 0; } +/********************************************************** + * RPCSS_ServerProcessUNREGISTEREPMessage + * + * Message Handler for the "UNREGISTEREP" message. + */ void RPCSS_ServerProcessUNREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata) { @@ -126,6 +156,11 @@ pReply->as_uint = 0; } +/******************************************************** + * RPCSS_ServerProcessREGISTEREPMessage + * + * Message Handler for the "RESOLVEEP" message. + */ void RPCSS_ServerProcessRESOLVEEPMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata) { @@ -141,6 +176,11 @@ ); } +/******************************************************** + * RPCSS_ServerProcessMessage + * + * Forward a message to the appropriate handler. + */ void RPCSS_ServerProcessMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata) { WINE_TRACE("\n"); @@ -162,7 +202,12 @@ } } -/* each message gets its own thread. this is it. */ +/******************************************************** + * RPCSS_HandlerThread + * + * This thread is the "real" work horse of RPCSS. + * Each incoming message is routed here in a separate thread. + */ VOID RPCSS_HandlerThread(LPVOID lpvPipeHandle) { RPCSS_NP_MESSAGE msg, vardata_payload_msg; @@ -193,6 +238,7 @@ WINE_ERR("vardata memory allocation failure.\n"); success = FALSE; } else { + /* read each vardata payload into the vardata buffer */ for ( c = vardata; (c - vardata) < msg.vardata_payload_size; c += VARDATA_PAYLOAD_BYTES) { success = ReadFile( @@ -202,6 +248,8 @@ &bytesread, NULL ); + + /* some sanity checking */ if ( (!success) || (bytesread != sizeof(RPCSS_NP_MESSAGE)) || (vardata_payload_msg.message_type != RPCSS_NP_MESSAGE_TYPEID_VARDATAPAYLOADMSG) ) { WINE_ERR("vardata payload read failure! (s=%s,br=%ld,exp_br=%d,mt=%u,mt_exp=%u\n", @@ -210,20 +258,22 @@ success = FALSE; break; } + CopyMemory(c, vardata_payload_msg.message.vardatapayloadmsg.payload, VARDATA_PAYLOAD_BYTES); - WINE_TRACE("payload read.\n"); } } } if (success && (bytesread == sizeof(RPCSS_NP_MESSAGE))) { WINE_TRACE("read success.\n"); + /* process the message and send a reply, serializing requests. */ EnterCriticalSection(&np_server_cs); WINE_TRACE("processing message.\n"); RPCSS_ServerProcessMessage(&msg, &reply, vardata); LeaveCriticalSection(&np_server_cs); + /* we are done with the vardata buffer */ if (had_payload) LocalFree(vardata); WINE_TRACE("message processed, sending reply....\n"); @@ -236,6 +286,7 @@ NULL /* not overlapped */ ); + /* some sanity checking */ if ( (!success) || (written != sizeof(RPCSS_NP_REPLY)) ) WINE_WARN("Message reply failed. (successs=%s, br=%ld, exp_br=%d)\n", success ? "TRUE" : "FALSE", written, sizeof(RPCSS_NP_REPLY)); @@ -244,12 +295,24 @@ } else WINE_WARN("Message receipt failed.\n"); + /* this pipe handle is orphaned by the server; IOW, the process is done + with it, and the responsibility of closing it down is delegated to us */ FlushFileBuffers(mypipe); DisconnectNamedPipe(mypipe); CloseHandle(mypipe); + + /* The srv_thread_count was incremented for us by RPCSS_NPMainWorkThread */ InterlockedDecrement(&srv_thread_count); } +/******************************************************** + * RPCSS_NPMainWorkThread + * + * This thread "does work," currently conceptualized as + * spawning message-handler threads and relistening on the + * named-pipe, for the main process, which just spins + * gently waiting for the timeout conditions to occur. + */ VOID RPCSS_NPMainWorkThread(LPVOID ignored) { BOOL connected; @@ -258,17 +321,22 @@ WINE_TRACE("\n"); + /* keep doing our job until the main thread decides it's time + for us to shut down */ while (server_live) { + /* wait for a client to connect to the pipe */ connected = ConnectNamedPipe(np_server_end, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); if (connected) { - /* is "work" the act of connecting pipes, or the act of serving - requests successfully? for now I will make it the former. */ if (!SetEvent(np_server_work_event)) WINE_WARN("failed to signal np_server_work_event.\n"); - /* Create a thread for this client. */ + /* Create a thread for this client. Note that the srv_thread_count + is incremented here, but decremented in the thread itself, + unless we fail to create it. + FIXME: I think we need the mutex here for precise atomicity control, + perhaps we should acquire it beforehand */ InterlockedIncrement(&srv_thread_count); hthread = CreateThread( NULL, /* no security attribute */ @@ -324,8 +392,11 @@ WINE_ERR("Uh oh. Couldn't leave master mutex. Expect deadlock.\n"); } } else { + /* we couldn't spawn the thread */ WINE_ERR("Failed to spawn handler thread!\n"); DisconnectNamedPipe(np_server_end); + /* we did the increment above, but the thread won't be around to + decrement. */ InterlockedDecrement(&srv_thread_count); } } @@ -333,6 +404,15 @@ WINE_TRACE("Server thread shutdown.\n"); } +/******************************************************** + * RPCSS_NPConnect + * + * Attempts to connect as a client to an existing server pipe-end. + * + * RETURNS + * Success: the handle of the Named Pipe now listening for connections + * Failure: NULL + */ HANDLE RPCSS_NPConnect() { HANDLE the_pipe = NULL; @@ -369,6 +449,11 @@ if (the_pipe != INVALID_HANDLE_VALUE) break; + /* many reasons we could get INVALID_HANDLE_VALUE, but the most + probable (and harmless) is that some other process already + connected to that handle. We wait a moment for the pipe + to become available and try again with the new handle in + this case */ if (GetLastError() != ERROR_PIPE_BUSY) { WINE_WARN("Unable to open named pipe %s (assuming unavailable).\n", wine_dbgstr_a(RPCSS_NAMED_PIPE_NAME)); @@ -381,14 +466,14 @@ if (!ReleaseMutex(master_mutex)) WINE_ERR("Failed to release master mutex. Expect deadlock.\n"); - /* wait for the named pipe. We are only - willing to wait only 5 seconds. It should be available /very/ soon. */ + /* wait for the named pipe. We are only willing to wait only for + MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT units of time */ if (! WaitNamedPipeA(RPCSS_NAMED_PIPE_NAME, MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT)) { WINE_ERR("Named pipe unavailable after waiting. Something is probably wrong.\n"); + /* conveniently, we released the mutex above... */ return NULL; } - } if (the_pipe) { @@ -404,6 +489,22 @@ return the_pipe; } +/******************************************************** + * RPCSS_SendReceiveNPMessage + * + * Send a message to the server pipe specified and receive the + * reply from the message handler. Does not support messages + * with vardata payloads. + * + * PARAMS + * np [I] HANDLE of the named pipe to operate on + * msg [I] pointer to the message structure describing the message to send + * reply [IO] pointer to the reply structure, recieves the reply data + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ BOOL RPCSS_SendReceiveNPMsg(HANDLE np, PRPCSS_NP_MESSAGE msg, PRPCSS_NP_REPLY reply) { DWORD count; @@ -436,6 +537,17 @@ return TRUE; } +/******************************************************** + * RPCSS_BecomePipeServer + * + * Attempt to become the (only) active RPCSS server process. + * If another server is active, the update timeout message is + * sent to it. + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ BOOL RPCSS_BecomePipeServer() { RPCSS_NP_MESSAGE msg; @@ -462,10 +574,11 @@ /* now we have the master mutex. during this time we will * - * o check if an rpcss already listens on the pipe. If so, - * we will tell it we were invoked, which will cause the - * other end to update its timeouts. After, we just return - * false. + * o check if an rpcss already listens on the pipe. + * + * o If so, we will tell it we were invoked, which will cause + * the other end to update its timeouts. After, we just + * return false. * * o otherwise, we establish the pipe for ourselves and get * ready to listen on it @@ -544,6 +657,17 @@ return rslt; } +/******************************************************** + * RPCSS_NPDoWork() + * + * Doesn't really do work, but just waits for one second + * for some work to occur in the server thread. If so, + * returns TRUE, otherwise FALSE after one second. + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ BOOL RPCSS_NPDoWork() { DWORD waitresult = WaitForSingleObject(np_server_work_event, 1000); diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/include/wine/rpcss_shared.h ./include/wine/rpcss_shared.h --- ../wine.test/include/wine/rpcss_shared.h 2002-12-02 01:44:31.000000000 -0600 +++ ./include/wine/rpcss_shared.h 2002-12-02 01:46:13.000000000 -0600 @@ -1,3 +1,25 @@ +/* + * Copyright (C) 2002 Greg Turner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * rpcss_shared.h: + * + * Provides definitions shared between rpcss and rpcrt4 + */ + #ifndef __WINE_RPCSS_SHARED_H #define __WINE_RPCSS_SHARED_H -- gmt