blob: 729f1563df8b961118ee260e6ae070683824c242 [file] [log] [blame]
/*
* RPCSS named pipe server
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <assert.h>
#include "rpcss.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
static HANDLE np_server_end;
static HANDLE np_server_work_event;
static CRITICAL_SECTION np_server_cs;
static LONG srv_thread_count;
static BOOL server_live;
LONG RPCSS_SrvThreadCount(void)
{
return srv_thread_count;
}
BOOL RPCSS_UnBecomePipeServer(void)
{
BOOL rslt = TRUE;
DWORD wait_result;
HANDLE master_mutex = RPCSS_GetMasterMutex();
WINE_TRACE("\n");
wait_result = WaitForSingleObject(master_mutex, MASTER_MUTEX_TIMEOUT);
switch (wait_result) {
case WAIT_ABANDONED: /* ? */
case WAIT_OBJECT_0:
/* we have ownership */
break;
case WAIT_FAILED:
case WAIT_TIMEOUT:
default:
WINE_ERR("This should never happen: couldn't enter mutex.\n");
/* this is totally unacceptable. no graceful out exists */
assert(FALSE);
}
/* 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 */
if (!RPCSS_ReadyToDie())
rslt = FALSE;
else {
WINE_TRACE("shutting down pipe.\n");
server_live = FALSE;
if (!CloseHandle(np_server_end))
WINE_WARN("Failed to close named pipe.\n");
if (!CloseHandle(np_server_work_event))
WINE_WARN("Failed to close the event handle.\n");
DeleteCriticalSection(&np_server_cs);
}
if (!ReleaseMutex(master_mutex))
WINE_ERR("Unable to leave master mutex!??\n");
return rslt;
}
static 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. */
RPCSS_SetMaxLazyTimeout(pMsg->message.ranmsg.timeout);
RPCSS_SetLazyTimeRemaining(RPCSS_GetMaxLazyTimeout());
pReply->as_uint = 0;
}
static void RPCSS_ServerProcessREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply,
char *vardata)
{
WINE_TRACE("\n");
RPCSS_RegisterRpcEndpoints(
pMsg->message.registerepmsg.iface,
pMsg->message.registerepmsg.object_count,
pMsg->message.registerepmsg.binding_count,
pMsg->message.registerepmsg.no_replace,
vardata,
pMsg->vardata_payload_size
);
/* no reply */
pReply->as_uint = 0;
}
static void RPCSS_ServerProcessUNREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg,
PRPCSS_NP_REPLY pReply, char *vardata)
{
WINE_TRACE("\n");
RPCSS_UnregisterRpcEndpoints(
pMsg->message.unregisterepmsg.iface,
pMsg->message.unregisterepmsg.object_count,
pMsg->message.unregisterepmsg.binding_count,
vardata,
pMsg->vardata_payload_size
);
/* no reply */
pReply->as_uint = 0;
}
static void RPCSS_ServerProcessRESOLVEEPMessage(PRPCSS_NP_MESSAGE pMsg,
PRPCSS_NP_REPLY pReply, char *vardata)
{
WINE_TRACE("\n");
/* for now, reply is placed into *pReply.as_string, on success, by RPCSS_ResolveRpcEndpoints */
ZeroMemory(pReply->as_string, MAX_RPCSS_NP_REPLY_STRING_LEN);
RPCSS_ResolveRpcEndpoints(
pMsg->message.resolveepmsg.iface,
pMsg->message.resolveepmsg.object,
vardata,
pReply->as_string
);
}
static void RPCSS_ServerProcessMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata)
{
WINE_TRACE("\n");
switch (pMsg->message_type) {
case RPCSS_NP_MESSAGE_TYPEID_RANMSG:
RPCSS_ServerProcessRANMessage(pMsg, pReply);
break;
case RPCSS_NP_MESSAGE_TYPEID_REGISTEREPMSG:
RPCSS_ServerProcessREGISTEREPMessage(pMsg, pReply, vardata);
break;
case RPCSS_NP_MESSAGE_TYPEID_UNREGISTEREPMSG:
RPCSS_ServerProcessUNREGISTEREPMessage(pMsg, pReply, vardata);
break;
case RPCSS_NP_MESSAGE_TYPEID_RESOLVEEPMSG:
RPCSS_ServerProcessRESOLVEEPMessage(pMsg, pReply, vardata);
break;
default:
WINE_ERR("Message type unknown!! No action taken.\n");
}
}
/* each message gets its own thread. this is it. */
static VOID HandlerThread(LPVOID lpvPipeHandle)
{
RPCSS_NP_MESSAGE msg, vardata_payload_msg;
char *c, *vardata = NULL;
RPCSS_NP_REPLY reply;
DWORD bytesread, written;
BOOL success, had_payload = FALSE;
HANDLE mypipe;
mypipe = (HANDLE) lpvPipeHandle;
WINE_TRACE("mypipe: %p\n", mypipe);
success = ReadFile(
mypipe, /* pipe handle */
(char *) &msg, /* message buffer */
sizeof(RPCSS_NP_MESSAGE), /* message buffer size */
&bytesread, /* receives number of bytes read */
NULL /* not overlapped */
);
if (msg.vardata_payload_size) {
had_payload = TRUE;
/* this fudge space allows us not to worry about exceeding the buffer space
on the last read */
vardata = LocalAlloc(LPTR, (msg.vardata_payload_size) + VARDATA_PAYLOAD_BYTES);
if (!vardata) {
WINE_ERR("vardata memory allocation failure.\n");
success = FALSE;
} else {
for ( c = vardata; (c - vardata) < msg.vardata_payload_size;
c += VARDATA_PAYLOAD_BYTES) {
success = ReadFile(
mypipe,
(char *) &vardata_payload_msg,
sizeof(RPCSS_NP_MESSAGE),
&bytesread,
NULL
);
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=%d,mt=%u,mt_exp=%u\n",
success ? "TRUE" : "FALSE", bytesread,
vardata_payload_msg.message_type, RPCSS_NP_MESSAGE_TYPEID_VARDATAPAYLOADMSG);
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);
if (had_payload) LocalFree(vardata);
WINE_TRACE("message processed, sending reply....\n");
success = WriteFile(
mypipe, /* pipe handle */
(char *) &reply, /* reply buffer */
sizeof(RPCSS_NP_REPLY), /* reply buffer size */
&written, /* receives number of bytes written */
NULL /* not overlapped */
);
if ( (!success) || (written != sizeof(RPCSS_NP_REPLY)) )
WINE_WARN("Message reply failed. (success=%d, br=%d)\n", success, written);
else
WINE_TRACE("Reply sent successfully.\n");
} else
WINE_WARN("Message receipt failed.\n");
FlushFileBuffers(mypipe);
DisconnectNamedPipe(mypipe);
CloseHandle(mypipe);
InterlockedDecrement(&srv_thread_count);
}
static VOID NPMainWorkThread(LPVOID ignored)
{
BOOL connected;
HANDLE hthread, master_mutex = RPCSS_GetMasterMutex();
DWORD threadid, wait_result;
WINE_TRACE("\n");
while (server_live) {
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. */
InterlockedIncrement(&srv_thread_count);
hthread = CreateThread(
NULL, /* no security attribute */
0, /* default stack size */
(LPTHREAD_START_ROUTINE) HandlerThread,
(LPVOID) np_server_end, /* thread parameter */
0, /* not suspended */
&threadid /* returns thread ID (not used) */
);
if (hthread) {
WINE_TRACE("Spawned handler thread: %p\n", hthread);
CloseHandle(hthread);
/* for safety's sake, hold the mutex while we switch the pipe */
wait_result = WaitForSingleObject(master_mutex, MASTER_MUTEX_TIMEOUT);
switch (wait_result) {
case WAIT_ABANDONED: /* ? */
case WAIT_OBJECT_0:
/* we have ownership */
break;
case WAIT_FAILED:
case WAIT_TIMEOUT:
default:
/* huh? */
wait_result = WAIT_FAILED;
}
if (wait_result == WAIT_FAILED) {
WINE_ERR("Couldn't enter master mutex. Expect prolems.\n");
} else {
/* now create a new named pipe instance to listen on */
np_server_end = CreateNamedPipe(
NAME_RPCSS_NAMED_PIPE, /* pipe name */
PIPE_ACCESS_DUPLEX, /* pipe open mode */
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, /* pipe-specific modes */
PIPE_UNLIMITED_INSTANCES, /* maximum instances */
sizeof(RPCSS_NP_REPLY), /* output buffer size */
sizeof(RPCSS_NP_MESSAGE), /* input buffer size */
2000, /* time-out interval */
NULL /* SD */
);
if (np_server_end == INVALID_HANDLE_VALUE) {
WINE_ERR("Failed to recreate named pipe!\n");
/* not sure what to do? */
assert(FALSE);
}
if (!ReleaseMutex(master_mutex))
WINE_ERR("Uh oh. Couldn't leave master mutex. Expect deadlock.\n");
}
} else {
WINE_ERR("Failed to spawn handler thread!\n");
DisconnectNamedPipe(np_server_end);
InterlockedDecrement(&srv_thread_count);
}
}
}
WINE_TRACE("Server thread shutdown.\n");
}
static HANDLE RPCSS_NPConnect(void)
{
HANDLE the_pipe = NULL;
DWORD dwmode, wait_result;
HANDLE master_mutex = RPCSS_GetMasterMutex();
WINE_TRACE("\n");
while (TRUE) {
wait_result = WaitForSingleObject(master_mutex, MASTER_MUTEX_TIMEOUT);
switch (wait_result) {
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
break;
case WAIT_FAILED:
case WAIT_TIMEOUT:
default:
WINE_ERR("This should never happen: couldn't enter mutex.\n");
return NULL;
}
/* try to open the client side of the named pipe. */
the_pipe = CreateFileA(
NAME_RPCSS_NAMED_PIPE, /* pipe name */
GENERIC_READ | GENERIC_WRITE, /* r/w access */
0, /* no sharing */
NULL, /* no security attributes */
OPEN_EXISTING, /* open an existing pipe */
0, /* default attributes */
NULL /* no template file */
);
if (the_pipe != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY) {
WINE_WARN("Unable to open named pipe %s (assuming unavailable).\n",
wine_dbgstr_a(NAME_RPCSS_NAMED_PIPE));
the_pipe = NULL;
break;
}
WINE_WARN("Named pipe busy (will wait)\n");
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. */
if (! WaitNamedPipeA(NAME_RPCSS_NAMED_PIPE, MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT))
{
WINE_ERR("Named pipe unavailable after waiting. Something is probably wrong.\n");
return NULL;
}
}
if (the_pipe) {
dwmode = PIPE_READMODE_MESSAGE;
/* SetNamedPipeHandleState not implemented ATM, but still seems to work somehow. */
if (! SetNamedPipeHandleState(the_pipe, &dwmode, NULL, NULL))
WINE_WARN("Failed to set pipe handle state\n");
}
if (!ReleaseMutex(master_mutex))
WINE_ERR("Uh oh, failed to leave the RPC Master Mutex!\n");
return the_pipe;
}
static BOOL RPCSS_SendReceiveNPMsg(HANDLE np, PRPCSS_NP_MESSAGE msg, PRPCSS_NP_REPLY reply)
{
DWORD count;
WINE_TRACE("(np == %p, msg == %p, reply == %p)\n", np, msg, reply);
if (! WriteFile(np, msg, sizeof(RPCSS_NP_MESSAGE), &count, NULL)) {
WINE_ERR("write failed.\n");
return FALSE;
}
if (count != sizeof(RPCSS_NP_MESSAGE)) {
WINE_ERR("write count mismatch.\n");
return FALSE;
}
if (! ReadFile(np, reply, sizeof(RPCSS_NP_REPLY), &count, NULL)) {
WINE_ERR("read failed.\n");
return FALSE;
}
if (count != sizeof(RPCSS_NP_REPLY)) {
WINE_ERR("read count mismatch, got %d.\n", count);
return FALSE;
}
/* message execution was successful */
return TRUE;
}
BOOL RPCSS_BecomePipeServer(void)
{
RPCSS_NP_MESSAGE msg;
RPCSS_NP_REPLY reply;
BOOL rslt = TRUE;
HANDLE client_handle, hthread, master_mutex = RPCSS_GetMasterMutex();
DWORD threadid, wait_result;
WINE_TRACE("\n");
wait_result = WaitForSingleObject(master_mutex, MASTER_MUTEX_TIMEOUT);
switch (wait_result) {
case WAIT_ABANDONED: /* ? */
case WAIT_OBJECT_0:
/* we have ownership */
break;
case WAIT_FAILED:
case WAIT_TIMEOUT:
default:
WINE_ERR("Couldn't enter master mutex.\n");
return FALSE;
}
/* 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 otherwise, we establish the pipe for ourselves and get
* ready to listen on it
*/
if ((client_handle = RPCSS_NPConnect()) != NULL) {
msg.message_type = RPCSS_NP_MESSAGE_TYPEID_RANMSG;
msg.message.ranmsg.timeout = RPCSS_GetMaxLazyTimeout();
msg.vardata_payload_size = 0;
if (!RPCSS_SendReceiveNPMsg(client_handle, &msg, &reply))
WINE_ERR("Something is amiss: RPC_SendReceive failed.\n");
rslt = FALSE;
}
if (rslt) {
np_server_work_event = CreateEventA(NULL, FALSE, FALSE, "RpcNpServerWorkEvent");
if (np_server_work_event == NULL) {
/* dunno what we can do then */
WINE_ERR("Unable to create the np_server_work_event\n");
assert(FALSE);
}
InitializeCriticalSection(&np_server_cs);
np_server_end = CreateNamedPipe(
NAME_RPCSS_NAMED_PIPE, /* pipe name */
PIPE_ACCESS_DUPLEX, /* pipe open mode */
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, /* pipe-specific modes */
PIPE_UNLIMITED_INSTANCES, /* maximum number of instances */
sizeof(RPCSS_NP_REPLY), /* output buffer size */
sizeof(RPCSS_NP_MESSAGE), /* input buffer size */
2000, /* time-out interval */
NULL /* SD */
);
if (np_server_end == INVALID_HANDLE_VALUE) {
WINE_ERR("Failed to create named pipe!\n");
DeleteCriticalSection(&np_server_cs);
if (!CloseHandle(np_server_work_event)) /* we will leak the handle... */
WINE_WARN("Failed to close np_server_work_event handle!\n");
np_server_work_event = NULL;
np_server_end = NULL;
rslt = FALSE;
}
}
server_live = rslt;
if (rslt) {
/* OK, now spawn the (single) server thread */
hthread = CreateThread(
NULL, /* no security attribute */
0, /* default stack size */
(LPTHREAD_START_ROUTINE) NPMainWorkThread,
NULL, /* thread parameter */
0, /* not suspended */
&threadid /* returns thread ID (not used) */
);
if (hthread) {
WINE_TRACE("Created server thread.\n");
CloseHandle(hthread);
} else {
WINE_ERR("Serious error: unable to create server thread!\n");
if (!CloseHandle(np_server_work_event)) /* we will leak the handle... */
WINE_WARN("Failed to close np_server_work_event handle!\n");
if (!CloseHandle(np_server_end)) /* we will leak the handle... */
WINE_WARN("Unable to close named pipe handle!\n");
DeleteCriticalSection(&np_server_cs);
np_server_end = NULL;
np_server_work_event = NULL;
rslt = FALSE;
server_live = FALSE;
}
}
if (!ReleaseMutex(master_mutex))
WINE_ERR("Unable to leave master mutex!??\n");
return rslt;
}
BOOL RPCSS_NPDoWork(void)
{
DWORD waitresult = WaitForSingleObject(np_server_work_event, 1000);
if (waitresult == WAIT_TIMEOUT)
return FALSE;
if (waitresult == WAIT_OBJECT_0)
return TRUE;
return FALSE;
}