VBoxIPC.cpp revision a29027bb2378172c7c88610ad5311548b66f96f6
/* $Id$ */
/** @file
* VBoxIPC - IPC thread, acts as a (purely) local IPC server.
* Multiple sessions are supported, whereas every session
* has its own thread for processing requests.
*/
/*
* Copyright (C) 2010-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <windows.h>
#include "VBoxTray.h"
#include "VBoxTrayMsg.h"
#include "VBoxHelpers.h"
#include "VBoxIPC.h"
#include <iprt/critsect.h>
#include <iprt/localipc.h>
#include <VBoxGuestInternal.h>
#ifdef DEBUG
# define LOG_ENABLED
# define LOG_GROUP LOG_GROUP_DEFAULT
#endif
/**
* IPC context data.
*/
typedef struct VBOXIPCCONTEXT
{
/** Pointer to the service environment. */
const VBOXSERVICEENV *pEnv;
/** Handle for the local IPC server. */
/** Critical section serializing access to the session list, the state,
* the response event, the session event, and the thread event. */
/** List of all active IPC sessions. */
static VBOXIPCCONTEXT gCtx = {0};
/** Function pointer for GetLastInputInfo(). */
/**
* IPC per-session thread data.
*/
typedef struct VBOXIPCSESSION
{
/** The list node required to be part of the
* IPC session list. */
/** Pointer to the IPC context data. */
PVBOXIPCCONTEXT volatile pCtx;
/** The local ipc client handle. */
RTLOCALIPCSESSION volatile hSession;
/** Indicate that the thread should terminate ASAP. */
bool volatile fTerminate;
/** The thread handle. */
/** Static pointer to GetLastInputInfo() function. */
{
/** @todo Not implemented yet; don't return an error here. */
return VINF_SUCCESS;
}
{
NULL /* Exact read, blocking */);
if (RT_SUCCESS(rc))
{
/* Showing the balloon tooltip is not critical. */
LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n",
}
return rc;
}
{
/* No actual message from client. */
int rc = VINF_SUCCESS;
bool fLastInputAvailable = false;
{
/* Note: This only works up to 49.7 days (= 2^32, 32-bit counter)
since Windows was started. */
if (fRc)
{
fLastInputAvailable = true;
}
else
}
if (!fLastInputAvailable)
{
/* No last input available. */
}
if (RT_SUCCESS(rc))
return rc;
}
/**
* Initializes the IPC communication.
*
* @return IPRT status code.
* @param pEnv The IPC service's environment.
* @param ppInstance The instance pointer which refer to this object.
* @param pfStartThread Pointer to flag whether the IPC service can be started or not.
*/
{
/** ppInstance not used here. */
*pfStartThread = false;
if (RT_SUCCESS(rc))
{
if (!fRc)
if (RT_SUCCESS(rc))
{
char *pszUserName;
if (RT_SUCCESS(rc))
{
char szPipeName[255];
{
if (RT_SUCCESS(rc))
{
*ppInstance = &gCtx;
*pfStartThread = true;
/* GetLastInputInfo only is available starting at Windows 2000. */
LogRelFunc(("Local IPC server now running at \"%s\"\n",
szPipeName));
return VINF_SUCCESS;
}
}
else
rc = VERR_NO_MEMORY;
}
}
}
return rc;
}
{
/* Shut down local IPC server. */
{
if (RT_FAILURE(rc2))
}
/* Stop all remaining session threads. */
if (RT_SUCCESS(rc))
{
{
if (RT_FAILURE(rc2))
{
LogFlowFunc(("Stopping IPC session %p failed with rc=%Rrc\n",
/* Keep going. */
}
}
}
}
{
/* Shut down local IPC server. */
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
if (RT_SUCCESS(rc))
}
LogFlowFunc(("Waiting for remaining IPC sessions to shut down ...\n"));
/* Wait for all IPC session threads to shut down. */
bool fListIsEmpty = true;
do
{
if (RT_SUCCESS(rc2))
{
if (!fListIsEmpty) /* Don't hog CPU while waiting. */
RTThreadSleep(100);
}
if (RT_FAILURE(rc2))
break;
} while (!fListIsEmpty);
("Session thread list is not empty when it should\n"));
LogFlowFunc(("All remaining IPC sessions shut down\n"));
if (RT_SUCCESS(rc))
LogFlowFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
}
/**
* Services a client session.
*
* @returns VINF_SUCCESS.
* @param hThread The thread handle.
* @param pvSession Pointer to the session instance data.
*/
{
int rc = VINF_SUCCESS;
/*
* Process client requests until it quits or we're cancelled on termination.
*/
&& RT_SUCCESS(rc))
{
/* The next call will be cancelled via VBoxIPCStop if needed. */
if (RT_FAILURE(rc))
{
if (rc == VERR_CANCELLED)
{
rc = VINF_SUCCESS;
break;
}
else
LogFlowFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n",
}
else
{
NULL /* Exact read, blocking */);
bool fRejected = false; /* Reject current command? */
if (RT_SUCCESS(rc))
if ( !fRejected
&& RT_SUCCESS(rc))
{
{
break;
break;
break;
default:
{
/* Unknown command, reject. */
fRejected = true;
break;
}
}
if (RT_FAILURE(rc))
LogFlowFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n",
}
if (fRejected)
{
static int s_cRejectedCmds = 0;
if (++s_cRejectedCmds <= 3)
{
LogRelFunc(("Session %p: Received invalid/unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n",
{
/* Get and discard payload data. */
{
if (RT_FAILURE(rc))
break;
}
}
}
else
}
}
}
LogFlowFunc(("Session %p: Handler ended with rc=%Rrc\n",
/*
* Close the session.
*/
if (RT_FAILURE(rc2))
/*
* Clean up the session.
*/
if (RT_SUCCESS(rc2))
{
/* Remove this session from the session list. */
if (RT_SUCCESS(rc))
}
LogFlowFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n",
return rc;
}
{
if (RT_SUCCESS(rc))
{
if (pSession)
{
pSession->fTerminate = false;
/* Start IPC session thread. */
if (RT_SUCCESS(rc))
{
/* Add session thread to session IPC list. */
}
else
{
if (RT_FAILURE(rc2))
}
}
else
rc = VERR_NO_MEMORY;
}
return rc;
}
{
if (hSession)
return RTLocalIpcSessionClose(hSession);
return VINF_SUCCESS;
}
/**
* Thread function to wait for and process seamless mode change
* requests
*/
{
bool fShutdown = false;
for (;;)
{
if (RT_FAILURE(rc))
{
if (rc == VERR_CANCELLED)
{
LogFlow(("Cancelled\n"));
fShutdown = true;
}
else
}
if (fShutdown)
break;
if (RT_FAILURE(rc))
{
/* Keep going. */
}
break;
}
return 0;
}