SUPSvcGrant.cpp revision c7814cf6e1240a519cbec0441e033d0e2470ed00
/* $Id$ */
/** @file
* VirtualBox Support Service - The Grant Service.
*/
/*
* Copyright (C) 2008-2010 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP
#include "SUPSvcInternal.h"
#include <iprt/critsect.h>
#include <iprt/semaphore.h>
#include <iprt/localipc.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to a client instance. */
typedef struct SUPSVCGRANTSESSION *PSUPSVCGRANTSESSION;
/** Pointer to a Grant service instance. */
typedef struct SUPSVCGRANT *PSUPSVCGRANT;
/**
* Grant service session data.
*/
typedef struct SUPSVCGRANTSESSION
{
/** Pointer to the next client in the list. */
/** Pointer to the previous client in the list. */
/** Pointer to the parent (the service instance). */
PSUPSVCGRANT volatile pParent;
/** The local ipc client handle. */
RTLOCALIPCSESSION volatile hSession;
/** Indicate that the thread should terminate ASAP. */
bool volatile fTerminate;
/** The thread handle. */
/**
* State grant service machine.
*/
typedef enum SUPSVCGRANTSTATE
{
/** The invalid zero entry. */
/** Creating - the thread is being started.
* Next: Paused or Butchered. */
/** Paused - the thread is blocked on it's user event semaphore.
* Next: Resuming, Terminating or Butchered.
* Prev: Creating, Pausing */
/** Resuming - the thread is being unblocked and ushered into RTLocalIpcServiceListen.
* Next: Listen or Butchered.
* Prev: Paused */
/** Listen - the thread is in RTLocalIpcServerListen or setting up an incoming session.
* Next: Pausing or Butchered.
* Prev: Resuming */
/** Pausing - Cancelling the listen and dropping any incoming sessions.
* Next: Paused or Butchered.
* Prev: Listen */
/** Butchered - The thread has quit because something when terribly wrong.
* Next: Destroyed
* Prev: Any. */
/** Pausing - Cancelling the listen and dropping any incoming sessions.
* Next: Destroyed
* Prev: Paused */
/** Destroyed - the instance is invalid.
* Prev: Butchered or Terminating */
/** The end of valid state values. */
/** The usual 32-bit blowup hack. */
kSupSvcGrantState_32BitHack = 0x7fffffff
/**
* Grant service instance data.
*/
typedef struct SUPSVCGRANT
{
/** The local ipc server handle. */
/** Critical section serializing access to the session list, the state,
* the response event, the session event, and the thread event. */
/** The service thread will signal this event when it has changed to
* the 'paused' or 'running' state. */
/** Event that's signaled on session termination. */
/** The handle to the service thread. */
/** Head of the session list. */
PSUPSVCGRANTSESSION volatile pSessionHead;
/** The service state. */
SUPSVCGRANTSTATE volatile enmState;
/** Critical section serializing access to the SUPR3HardenedVerify APIs. */
} SUPSVCGRANT;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Services a client session.
*
* @returns VINF_SUCCESS.
*
* @param hThread The thread handle.
* @param pvSession Pointer to the session instance data.
*/
{
/*
* Process client requests until it quits or we're cancelled on termination.
*/
{
RTThreadSleep(1000);
/** @todo */
}
/*
* Clean up the session.
*/
if (pParent)
else
if (hSession != NIL_RTLOCALIPCSESSION)
else
if (pParent)
{
}
Log(("supSvcGrantSessionThread(%p): exits\n"));
return VINF_SUCCESS;
}
/**
* Cleans up a session.
*
* This is called while inside the grant service critical section.
*
* @param pThis The session to destroy.
* @param pParent The parent.
*/
{
/*
* Unlink it.
*/
{
}
{
}
/*
* Free the resources associated with it.
*/
if (hSession != NIL_RTLOCALIPCSESSION)
}
/**
* Cleans up zombie sessions, locked.
*
* @param pThis Pointer to the grant service instance data.
*/
{
/*
* Iterate until be make it all the way thru the list.
*
* Only use the thread state as and indicator on whether we can destroy
* the session or not.
*/
do
{
{
if (RT_SUCCESS(rc))
{
break;
}
}
} while (pCur);
}
/**
* Cleans up zombie sessions.
*
* @returns VINF_SUCCESS, VBox error code on internal error.
*
* @param pThis Pointer to the grant service instance data.
* @param fOwnCritSect Whether we own the crit sect already. The state is preserved.
*/
{
if (RT_FAILURE(rc))
{
return rc;
}
return VINF_SUCCESS;
}
/**
* Gets the state name.
*
* @returns The state name string (read only).
* @param enmState The state.
*/
{
switch (enmState)
{
case kSupSvcGrantState_Invalid: return "Invalid";
case kSupSvcGrantState_Creating: return "Creating";
case kSupSvcGrantState_Paused: return "Paused";
case kSupSvcGrantState_Resuming: return "Resuming";
case kSupSvcGrantState_Listen: return "Listen";
case kSupSvcGrantState_Pausing: return "Pausing";
case kSupSvcGrantState_Butchered: return "Butchered";
case kSupSvcGrantState_Terminating: return "Terminating";
case kSupSvcGrantState_Destroyed: return "Destroyed";
default: return "?Unknown?";
}
}
/**
* Attempts to flip into the butchered state.
*
* @returns rc.
* @param pThis The instance data.
* @param fOwnCritSect Whether we own the crit sect already.
* @param pszFailed What failed.
* @param rc What to return (lazy bird).
*/
static int supSvcGrantThreadButchered(PSUPSVCGRANT pThis, bool fOwnCritSect, const char *pszFailed, int rc)
{
int rc2 = VINF_SUCCESS;
if (!fOwnCritSect)
if (RT_SUCCESS(rc2))
{
supSvcLogError("supSvcGrantThread(%s): Butchered; %Rrc: %s",
}
return rc;
}
/**
* Creates a new session.
*
* @returns VINF_SUCCESS on success, VBox error code on internal error.
*
* @param pThis Pointer to the grant service instance data.
* @param hSession The client session handle.
*/
{
/*
* Allocate and initialize a new session instance before entering the critsect.
*/
if (!pSession)
{
supSvcLogError("supSvcGrantThreadListen: failed to allocate session");
return VINF_SUCCESS; /* not fatal? */
}
pSession->fTerminate = false;
/*
* Enter the critsect, check the state, link it and fire off the session thread.
*/
if (RT_SUCCESS(rc))
{
/* check the state */
if (enmState == kSupSvcGrantState_Listen)
{
/* link it */
if (pThis->pSessionHead)
/* fire up the thread */
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
/*
* Successfully handled the client.
*/
return VINF_SUCCESS;
}
/* bail out */
}
else
rc = VINF_SUCCESS;
}
else
return rc;
}
/**
* Listen for a client session and kicks off the service thread for it.
*
* @returns VINF_SUCCESS on normal state change, failure if something gets screwed up.
*
* @param pThis Pointer to the grant service instance data.
*/
{
/*
* Wait for a client to connect and create a new session.
*/
if (RT_FAILURE(rc))
{
if (rc == VERR_CANCELLED)
LogFlow(("supSvcGrantThreadListen: cancelled\n"));
else if (rc == VERR_TRY_AGAIN)
/* for testing */;
else
return VINF_SUCCESS;
}
}
/**
* Grant service thread.
*
* This thread is the one listening for clients and kicks off
* the session threads and stuff.
*
* @returns VINF_SUCCESS on normal exit, VBox error status on failure.
* @param hThread The thread handle.
* @param pvThis Pointer to the grant service instance data.
*/
{
/*
* The state loop.
*/
for (;;)
{
/*
* Switch on the current state (requires critsect).
*/
if (RT_FAILURE(rc))
{
return rc;
}
switch (enmState)
{
if (RT_FAILURE(rc))
/* fall thru */
case kSupSvcGrantState_Paused:
break;
if (RT_FAILURE(rc))
/* fall thru */
case kSupSvcGrantState_Listen:
if (RT_FAILURE(rc))
{
return rc;
}
break;
Log(("supSvcGrantThread: Done\n"));
return VINF_SUCCESS;
default:
}
/*
* Massage the session list between clients and states.
*/
if (RT_FAILURE(rc))
return supSvcGrantThreadButchered(pThis, false /* fOwnCritSect */, "supSvcGrantCleanUpSessions", rc);
}
}
/**
* Waits for the service thread to respond to a state change.
*
* @returns VINF_SUCCESS on success, VERR_TIMEOUT if it doesn't respond in time, other error code on internal error.
*
* @param pThis Pointer to the grant service instance data.
* @param enmCurState The current state.
* @param enmNewState The new state we're waiting for it to enter.
*/
static int supSvcGrantWait(PSUPSVCGRANT pThis, SUPSVCGRANTSTATE enmCurState, SUPSVCGRANTSTATE enmNewState)
{
LogFlow(("supSvcGrantWait(,%s,%s): enter\n",
/*
* Wait a short while for the response event to be set.
*/
if (RT_SUCCESS(rc))
{
{
rc = VINF_SUCCESS;
}
{
/*
* Wait good while longer.
*/
{
if (RT_SUCCESS(rc))
{
/*
* Check the state whether we've succeeded.
*/
if (enmState == enmNewState)
rc = VINF_SUCCESS;
else if (enmState == enmCurState)
{
supSvcLogError("supSvcGrantWait(,%s,%s) - the thread doesn't respond in a timely manner, failing.",
rc = VERR_TIMEOUT;
}
else
{
}
}
else
supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
}
else
supSvcLogError("supSvcGrantWait(,%s,%s) - RTSemEventWait returns %Rrc",
}
else
{
}
}
else
supSvcLogError("supSvcGrantWait(,%s,%s) - RTCritSectEnter returns %Rrc",
Log(("supSvcGrantWait(,%s,%s): returns %Rrc\n",
return rc;
}
/** @copydoc SUPSVCSERVICE::pfnCreate */
{
/*
* Allocate and initialize the session data.
*/
if (!pThis)
{
supSvcLogError("supSvcGrantCreate - no memory");
return VERR_NO_MEMORY;
}
bool fFreeIt = true;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Create the local IPC instance and then finally fire up the thread.
*/
rc = RTLocalIpcServerCreate(&pThis->hServer, SUPSVC_GRANT_SERVICE_NAME, RTLOCALIPC_FLAGS_MULTI_SESSION);
if (RT_SUCCESS(rc))
{
rc = RTThreadCreate(&pThis->hThread, supSvcGrantThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GRANT");
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Successfully created the grant service!
*/
*ppvInstance = pThis;
return VINF_SUCCESS;
}
/*
* The thread FAILED to start in a timely manner!
*/
int cTries = 10;
if (RT_FAILURE(rc2))
{
/* poke it a few more times before giving up. */
while (--cTries > 0)
{
break;
}
}
}
else
}
else
}
else
}
else
}
else
}
else
if (fFreeIt)
return rc;
}
/** @copydoc SUPSVCSERVICE::pfnStart */
{
/*
* Change the state and signal the thread.
*/
if (RT_SUCCESS(rc))
{
bool fInCritSect = true;
if (enmState == kSupSvcGrantState_Paused)
{
if (RT_SUCCESS(rc))
{
/*
* Wait for the bugger to respond (no need to bitch here).
*/
fInCritSect = false;
}
}
else
if (fInCritSect)
}
else
{
}
}
/** @copydoc SUPSVCSERVICE::pfnTryStop */
{
/*
* Don't give up immediately.
*/
int rc;
for (;;)
{
/*
* First check the state to make sure the thing is actually running.
* If the critsect is butchered, just pretend success.
*/
if (RT_FAILURE(rc))
{
return VINF_SUCCESS;
}
if (enmState != kSupSvcGrantState_Listen)
{
return VINF_SUCCESS;
}
/*
* If there are no clients, usher the thread into the paused state.
*/
if (!pThis->pSessionHead)
{
else
{
if (RT_FAILURE(rc))
if (RT_FAILURE(rc2))
if (RT_FAILURE(rc3))
}
return VINF_SUCCESS;
}
/*
* Check the time limit, otherwise wait for a client event.
*/
{
unsigned cSessions = 0;
cSessions++;
supSvcLogError("supSvcGrantTryStop - %u active sessions after waiting %u ms", cSessions, (unsigned)u64Elapsed);
return VERR_TRY_AGAIN;
}
if (RT_FAILURE(rc))
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
}
}
/** @copydoc SUPSVCSERVICE::pfnStopAndDestroy */
{
int rc;
/*
* Attempt to stop the service, cancelling blocked server and client calls.
*/
if (enmState == kSupSvcGrantState_Listen)
{
/* try cancel local ipc operations that might be pending */
{
if (hSession != NIL_RTLOCALIPCSESSION)
}
/*
* Wait for the thread to respond (outside the crit sect).
*/
/*
* Wait for any lingering sessions to exit.
*/
if (pThis->pSessionHead)
{
do
{
/* Destroy the sessions since cancelling didn't do the trick. */
{
if (hSession != NIL_RTLOCALIPCSESSION)
{
if (RT_FAILURE(rc))
supSvcLogError("supSvcGrantStopAndDestroy: RTLocalIpcSessionClose(%p) returns %Rrc",
}
}
/* Check the time. */
break;
/* wait */
break;
/* cleanup and check again */
} while (pThis->pSessionHead);
}
}
/*
* Tell the service thread to terminate and wait for it to do so.
*/
{
if (RT_FAILURE(rc))
}
else if (RT_FAILURE(rc))
/*
* Kill the parent pointers of any lingering sessions.
*/
unsigned cSessions = 0;
if (cSessions)
/*
* Free the resource.
*/
}