DrvNamedPipe.cpp revision c58f1213e628a545081c70e26c6b67a841cff880
/* $Id$ */
/** @file
* Named pipe / local socket stream driver.
*/
/*
* Copyright (C) 2006-2012 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_NAMEDPIPE
#include <iprt/semaphore.h>
#include "VBoxDD.h"
#ifdef RT_OS_WINDOWS
# include <windows.h>
#else /* !RT_OS_WINDOWS */
# include <errno.h>
# include <unistd.h>
# ifndef SHUT_RDWR /* OS/2 */
# define SHUT_RDWR 3
# endif
#endif /* !RT_OS_WINDOWS */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Converts a pointer to DRVNAMEDPIPE::IMedia to a PDRVNAMEDPIPE. */
#define PDMISTREAM_2_DRVNAMEDPIPE(pInterface) ( (PDRVNAMEDPIPE)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAMEDPIPE, IStream)) )
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Named pipe driver instance data.
*
* @implements PDMISTREAM
*/
typedef struct DRVNAMEDPIPE
{
/** The stream interface. */
/** Pointer to the driver instance. */
/** Pointer to the named pipe file name. (Freed by MM) */
char *pszLocation;
/** Flag whether VirtualBox represents the server or client side. */
bool fIsServer;
#ifdef RT_OS_WINDOWS
/** File handle of the named pipe. */
/** Overlapped structure for writes. */
/** Overlapped structure for reads. */
/** Listen thread wakeup semaphore */
#else /* !RT_OS_WINDOWS */
/** Socket handle of the local socket for server. */
int LocalSocketServer;
/** Socket handle of the local socket. */
int LocalSocket;
#endif /* !RT_OS_WINDOWS */
/** Thread for listening for new connections. */
/** Flag to signal listening thread to shut down. */
bool volatile fShutdown;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/** @copydoc PDMISTREAM::pfnRead */
{
int rc = VINF_SUCCESS;
#ifdef RT_OS_WINDOWS
{
{
if ( uError == ERROR_PIPE_LISTENING
|| uError == ERROR_PIPE_NOT_CONNECTED)
{
cbReallyRead = 0;
/* wait a bit or else we'll be called right back. */
RTThreadSleep(100);
}
else
{
if (uError == ERROR_IO_PENDING)
{
uError = 0;
/* Wait for incoming bytes. */
uError = GetLastError();
}
}
}
if (RT_FAILURE(rc))
{
|| rc == VERR_BROKEN_PIPE
)
)
{
{
}
/* pretend success */
rc = VINF_SUCCESS;
}
cbReallyRead = 0;
}
}
#else /* !RT_OS_WINDOWS */
{
if (cbReallyRead == 0)
{
}
else if (cbReallyRead == -1)
{
cbReallyRead = 0;
}
*pcbRead = cbReallyRead;
}
#endif /* !RT_OS_WINDOWS */
else
{
RTThreadSleep(100);
*pcbRead = 0;
}
return rc;
}
/** @copydoc PDMISTREAM::pfnWrite */
static DECLCALLBACK(int) drvNamedPipeWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
{
int rc = VINF_SUCCESS;
#ifdef RT_OS_WINDOWS
{
{
if ( uError == ERROR_PIPE_LISTENING
|| uError == ERROR_PIPE_NOT_CONNECTED)
{
}
else if (uError != ERROR_IO_PENDING)
{
cbWritten = 0;
}
else
{
/* Wait for the write to complete. */
if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedWrite, &cbWritten, TRUE /*bWait*/) == FALSE)
}
}
if (RT_FAILURE(rc))
{
/** @todo WriteFile(pipe) has been observed to return ERROR_NO_DATA
* (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
* disconnected. */
|| rc == VERR_BROKEN_PIPE)
{
{
}
/* pretend success */
rc = VINF_SUCCESS;
}
cbWritten = 0;
}
}
#else /* !RT_OS_WINDOWS */
{
if (cbWritten == 0)
{
}
else if (cbWritten == -1)
{
cbWritten = 0;
}
}
#endif /* !RT_OS_WINDOWS */
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/* -=-=-=-=- listen thread -=-=-=-=- */
/**
* Receive thread loop.
*
* @returns 0 on success.
* @param ThreadSelf Thread handle to this thread.
* @param pvUser User argument.
*/
{
int rc = VINF_SUCCESS;
#ifdef RT_OS_WINDOWS
#endif
{
#ifdef RT_OS_WINDOWS
if ( !fConnected
{
if (hrc == ERROR_IO_PENDING)
{
hrc = 0;
hrc = GetLastError();
}
break;
if (hrc == ERROR_PIPE_CONNECTED)
{
}
else if (hrc != ERROR_SUCCESS)
{
break;
}
}
#else /* !RT_OS_WINDOWS */
{
break;
}
if (s == -1)
{
break;
}
{
close(s);
}
else
pThis->LocalSocket = s;
#endif /* !RT_OS_WINDOWS */
}
#ifdef RT_OS_WINDOWS
#endif
return VINF_SUCCESS;
}
/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
/**
* Common worker for drvNamedPipePowerOff and drvNamedPipeDestructor.
*
* @param pThis The instance data.
*/
{
/*
* Signal shutdown of the listener thread.
*/
#ifdef RT_OS_WINDOWS
{
/* Wake up listen thread */
}
#else
{
}
#endif
}
/**
* Power off a named pipe stream driver instance.
*
* This does most of the destruction work, to avoid ordering dependencies.
*
* @param pDrvIns The driver instance data.
*/
{
}
/**
* Destruct a named pipe stream driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that
* any non-VM resources can be freed correctly.
*
* @param pDrvIns The driver instance data.
*/
{
/*
* While the thread exits, clean up as much as we can.
*/
#ifdef RT_OS_WINDOWS
{
}
{
}
{
}
#else /* !RT_OS_WINDOWS */
{
}
&& pThis->pszLocation)
#endif /* !RT_OS_WINDOWS */
/*
* Wait for the thread.
*/
{
if (RT_SUCCESS(rc))
else
}
/*
* The last bits of cleanup.
*/
#ifdef RT_OS_WINDOWS
{
}
#endif
}
/**
* Construct a named pipe stream driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
{
/*
* Init the static parts.
*/
#ifdef RT_OS_WINDOWS
#else /* !RT_OS_WINDOWS */
#endif /* !RT_OS_WINDOWS */
/* IBase */
/* IStream */
/*
* Validate and read the configuration.
*/
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
/*
*/
#ifdef RT_OS_WINDOWS
{
1, /*nMaxInstances*/
32, /*nOutBufferSize*/
32, /*nOutBufferSize*/
10000, /*nDefaultTimeOut*/
NULL); /* lpSecurityAttributes*/
{
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create named pipe %s"),
}
if (RT_FAILURE(rc))
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread"),
}
else
{
/* Connect to the named pipe. */
{
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"),
}
}
#else /* !RT_OS_WINDOWS */
if (s == -1)
struct sockaddr_un addr;
{
/* Bind address to the local socket. */
pThis->LocalSocketServer = s;
N_("NamedPipe#%d failed to bind to local socket %s"),
if (RT_FAILURE(rc))
}
else
{
/* Connect to the local socket. */
pThis->LocalSocket = s;
N_("NamedPipe#%d failed to connect to local socket %s"),
}
#endif /* !RT_OS_WINDOWS */
LogRel(("NamedPipe: location %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
return VINF_SUCCESS;
}
/**
* Named pipe driver registration record.
*/
const PDMDRVREG g_DrvNamedPipe =
{
/* u32Version */
/* szName */
"NamedPipe",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Named Pipe stream driver.",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVNAMEDPIPE),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};