Example #1
0
/**
 * Tries to reconnect to the USB/IP host.
 *
 * @returns VBox status code.
 */
int USBProxyBackendUsbIp::reconnect()
{
    /* Make sure we are disconnected. */
    disconnect();

    /* Connect to the USB/IP host. */
    int rc = RTTcpClientConnect(m->pszHost, m->uPort, &m->hSocket);
    if (RT_SUCCESS(rc))
    {
        rc = RTTcpSetSendCoalescing(m->hSocket, false);
        if (RT_FAILURE(rc))
            LogRel(("USB/IP: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect increased latency\n", rc));

        rc = RTPollSetAddSocket(m->hPollSet, m->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
                                USBIP_POLL_ID_SOCKET);
        if (RT_FAILURE(rc))
        {
            RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
            m->hSocket = NIL_RTSOCKET;
        }
    }

    return rc;
}
/**
 * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd}
 */
static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart)
{
    return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
}
/**
 * Construct a TCP socket stream driver instance.
 *
 * @copydoc FNPDMDRVCONSTRUCT
 */
static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
    RT_NOREF(fFlags);
    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
    PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);

    /*
     * Init the static parts.
     */
    pThis->pDrvIns                      = pDrvIns;
    pThis->pszLocation                  = NULL;
    pThis->fIsServer                    = false;

    pThis->hTcpServ                     = NULL;
    pThis->hTcpSock                     = NIL_RTSOCKET;

    pThis->hPollSet                     = NIL_RTPOLLSET;
    pThis->hPipeWakeR                   = NIL_RTPIPE;
    pThis->hPipeWakeW                   = NIL_RTPIPE;
    pThis->fTcpSockInPollSet            = false;

    pThis->ListenThread                 = NIL_RTTHREAD;
    pThis->fShutdown                    = false;
    /* IBase */
    pDrvIns->IBase.pfnQueryInterface    = drvTCPQueryInterface;
    /* IStream */
    pThis->IStream.pfnPoll              = drvTcpPoll;
    pThis->IStream.pfnPollInterrupt     = drvTcpPollInterrupt;
    pThis->IStream.pfnRead              = drvTcpRead;
    pThis->IStream.pfnWrite             = drvTcpWrite;

    /*
     * Validate and read the configuration.
     */
    PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");

    int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
    rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);

    rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);

    rc = RTPollSetCreate(&pThis->hPollSet);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);

    rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
                            RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
                            DRVTCP_POLLSET_ID_WAKEUP);
    if (RT_FAILURE(rc))
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                   N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
                                   pDrvIns->iInstance, pThis->pszLocation);

    /*
     * Create/Open the socket.
     */
    if (pThis->fIsServer)
    {
        uint32_t uPort = 0;
        rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
                                       pDrvIns->iInstance);

        /** @todo Allow binding to distinct interfaces. */
        rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc,  RT_SRC_POS,
                                       N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);

        rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc,  RT_SRC_POS,
                                       N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
    }
    else
    {
        char *pszPort = strchr(pThis->pszLocation, ':');
        if (!pszPort)
            return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
                                       N_("DrvTCP#%d: The location misses the port to connect to"),
                                       pDrvIns->iInstance);

        *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
        uint32_t uPort = 0;
        rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
                                       pDrvIns->iInstance);

        rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
        *pszPort = ':'; /* Restore delimiter before checking the status. */
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d failed to connect to socket %s"),
                                       pDrvIns->iInstance, pThis->pszLocation);

        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
                                DRVTCP_POLLSET_ID_SOCKET);
        if (RT_FAILURE(rc))
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
                                       N_("DrvTCP#%d failed to add socket for %s to poll set"),
                                       pDrvIns->iInstance, pThis->pszLocation);

        pThis->fTcpSockInPollSet = true;
    }

    LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
    return VINF_SUCCESS;
}
/** @interface_method_impl{PDMISTREAM,pfnPoll} */
static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
{
    int rc = VINF_SUCCESS;
    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);

    if (pThis->hTcpSock != NIL_RTSOCKET)
    {
        if (!pThis->fTcpSockInPollSet)
        {
            rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                    fEvts, DRVTCP_POLLSET_ID_SOCKET);
            if (RT_SUCCESS(rc))
            {
                pThis->fTcpSockInPollSet = true;
                pThis->fXmitBufFull = false;
            }
        }
        else
        {
            /* Always include error event. */
            fEvts |= RTPOLL_EVT_ERROR;
            rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
            AssertRC(rc);
        }
    }

    if (RT_SUCCESS(rc))
    {
        while (RT_SUCCESS(rc))
        {
            uint32_t fEvtsRecv = 0;
            uint32_t idHnd = 0;

            /*
             * Just check for data available to be read if the send buffer wasn't full till now and
             * the caller wants to check whether writing is possible with the event set.
             *
             * On Windows the write event is only posted after a send operation returned
             * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting
             * for an event which would never happen if the buffer has space left.
             */
            if (   (fEvts & RTPOLL_EVT_WRITE)
                && !pThis->fXmitBufFull
                && pThis->fTcpSockInPollSet)
                cMillies = 0;

            rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
            if (RT_SUCCESS(rc))
            {
                if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
                {
                    /* We got woken up, drain the pipe and return. */
                    uint8_t bReason;
                    size_t cbRead = 0;
                    rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
                    AssertRC(rc);

                    if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
                        rc = VERR_INTERRUPTED;
                    else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
                    {
                        Assert(!pThis->fTcpSockInPollSet);
                        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                                fEvts, DRVTCP_POLLSET_ID_SOCKET);
                        if (RT_SUCCESS(rc))
                            pThis->fTcpSockInPollSet = true;
                    }
                    else
                        AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
                }
                else
                {
                    Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);

                    /* On error we close the socket here. */
                    if (fEvtsRecv & RTPOLL_EVT_ERROR)
                    {
                        rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
                        AssertRC(rc);

                        if (pThis->fIsServer)
                            RTTcpServerDisconnectClient2(pThis->hTcpSock);
                        else
                            RTSocketClose(pThis->hTcpSock);
                        pThis->hTcpSock = NIL_RTSOCKET;
                        pThis->fTcpSockInPollSet = false;
                        /* Continue with polling. */
                    }
                    else
                    {
                        if (fEvtsRecv & RTPOLL_EVT_WRITE)
                            pThis->fXmitBufFull = false;
                        else if (!pThis->fXmitBufFull)
                            fEvtsRecv |= RTPOLL_EVT_WRITE;
                        *pfEvts = fEvtsRecv;
                        break;
                    }
                }
            }
            else if (   rc == VERR_TIMEOUT
                     && !pThis->fXmitBufFull)
            {
                *pfEvts = RTPOLL_EVT_WRITE;
                rc = VINF_SUCCESS;
                break;
            }
        }
    }

    return rc;
}