Beispiel #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;
}
/**
 * Do the teleporter.
 *
 * @returns VBox status code.
 * @param   pState              The teleporter state.
 */
HRESULT
Console::teleporterSrc(TeleporterStateSrc *pState)
{
    AutoCaller autoCaller(this);
    if (FAILED(autoCaller.rc())) return autoCaller.rc();

    /*
     * Wait for Console::Teleport to change the state.
     */
    {
        AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
    }

    BOOL fCanceled = TRUE;
    HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
    if (FAILED(hrc))
        return hrc;
    if (fCanceled)
        return setError(E_FAIL, tr("canceled"));

    /*
     * Try connect to the destination machine, disable Nagle.
     * (Note. The caller cleans up mhSocket, so we can return without worries.)
     */
    int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket);
    if (RT_FAILURE(vrc))
        return setError(E_FAIL, tr("Failed to connect to port %u on '%s': %Rrc"),
                        pState->muPort, pState->mstrHostname.c_str(), vrc);
    vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
    AssertRC(vrc);

    /* Read and check the welcome message. */
    char szLine[RT_MAX(128, sizeof(g_szWelcome))];
    RT_ZERO(szLine);
    vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
    if (RT_FAILURE(vrc))
        return setError(E_FAIL, tr("Failed to read welcome message: %Rrc"), vrc);
    if (strcmp(szLine, g_szWelcome))
        return setError(E_FAIL, tr("Unexpected welcome %.*Rhxs"), sizeof(g_szWelcome) - 1, szLine);

    /* password */
    pState->mstrPassword.append('\n');
    vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length());
    if (RT_FAILURE(vrc))
        return setError(E_FAIL, tr("Failed to send password: %Rrc"), vrc);

    /* ACK */
    hrc = teleporterSrcReadACK(pState, "password", tr("Invalid password"));
    if (FAILED(hrc))
        return hrc;

    /*
     * Start loading the state.
     *
     * Note! The saved state includes vital configuration data which will be
     *       verified against the VM config on the other end.  This is all done
     *       in the first pass, so we should fail pretty promptly on misconfig.
     */
    hrc = teleporterSrcSubmitCommand(pState, "load");
    if (FAILED(hrc))
        return hrc;

    RTSocketRetain(pState->mhSocket);
    void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
    vrc = VMR3Teleport(VMR3GetVM(pState->mpUVM),
                       pState->mcMsMaxDowntime,
                       &g_teleporterTcpOps,         pvUser,
                       teleporterProgressCallback,  pvUser,
                       &pState->mfSuspendedByUs);
    RTSocketRelease(pState->mhSocket);
    if (RT_FAILURE(vrc))
    {
        if (   vrc == VERR_SSM_CANCELLED
                && RT_SUCCESS(RTTcpSelectOne(pState->mhSocket, 1)))
        {
            hrc = teleporterSrcReadACK(pState, "load-complete");
            if (FAILED(hrc))
                return hrc;
        }
        return setError(E_FAIL, tr("VMR3Teleport -> %Rrc"), vrc);
    }

    hrc = teleporterSrcReadACK(pState, "load-complete");
    if (FAILED(hrc))
        return hrc;

    /*
     * We're at the point of no return.
     */
    if (!pState->mptrProgress->notifyPointOfNoReturn())
    {
        teleporterSrcSubmitCommand(pState, "cancel", false /*fWaitForAck*/);
        return E_FAIL;
    }

    /*
     * Hand over any media which we might be sharing.
     *
     * Note! This is only important on localhost teleportations.
     */
    /** @todo Maybe we should only do this if it's a local teleportation... */
    hrc = mControl->UnlockMedia();
    if (FAILED(hrc))
        return hrc;
    pState->mfUnlockedMedia = true;

    hrc = teleporterSrcSubmitCommand(pState, "lock-media");
    if (FAILED(hrc))
        return hrc;

    /*
     * The FINAL step is giving the target instructions how to proceed with the VM.
     */
    if (    vrc == VINF_SSM_LIVE_SUSPENDED
            ||  pState->menmOldMachineState == MachineState_Paused)
        hrc = teleporterSrcSubmitCommand(pState, "hand-over-paused");
    else
        hrc = teleporterSrcSubmitCommand(pState, "hand-over-resume");
    if (FAILED(hrc))
        return hrc;

    /*
     * teleporterSrcThreadWrapper will do the automatic power off because it
     * has to release the AutoVMCaller.
     */
    return S_OK;
}
Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
{
    TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
    pState->mhSocket = Sock;

    /*
     * Disable Nagle and say hello.
     */
    int vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
    AssertRC(vrc);
    vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
    if (RT_FAILURE(vrc))
    {
        LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc));
        return VINF_SUCCESS;
    }

    /*
     * Password (includes '\n', see teleporterTrg).
     */
    const char *pszPassword = pState->mstrPassword.c_str();
    unsigned    off = 0;
    while (pszPassword[off])
    {
        char ch;
        vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
        if (    RT_FAILURE(vrc)
                ||  pszPassword[off] != ch)
        {
            if (RT_FAILURE(vrc))
                LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc));
            else
                LogRel(("Teleporter: Invalid password (off=%u)\n", off));
            teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
            return VINF_SUCCESS;
        }
        off++;
    }
    vrc = teleporterTcpWriteACK(pState);
    if (RT_FAILURE(vrc))
        return VINF_SUCCESS;

    /*
     * Update the progress bar, with peer name if available.
     */
    HRESULT     hrc;
    RTNETADDR   Addr;
    vrc = RTTcpGetPeerAddress(Sock, &Addr);
    if (RT_SUCCESS(vrc))
    {
        LogRel(("Teleporter: Incoming VM from %RTnaddr!\n", &Addr));
        hrc = pState->mptrProgress->SetNextOperation(BstrFmt(tr("Teleporting VM from %RTnaddr"), &Addr).raw(), 8);
    }
    else
    {
        LogRel(("Teleporter: Incoming VM!\n"));
        hrc = pState->mptrProgress->SetNextOperation(Bstr(tr("Teleporting VM")).raw(), 8);
    }
    AssertMsg(SUCCEEDED(hrc) || hrc == E_FAIL, ("%Rhrc\n", hrc));

    /*
     * Stop the server and cancel the timeout timer.
     *
     * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
     *       to it we must not return that value!
     */
    RTTcpServerShutdown(pState->mhServer);
    RTTimerLRDestroy(*pState->mphTimerLR);
    *pState->mphTimerLR = NIL_RTTIMERLR;

    /*
     * Command processing loop.
     */
    bool fDone = false;
    for (;;)
    {
        char szCmd[128];
        vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd));
        if (RT_FAILURE(vrc))
            break;

        if (!strcmp(szCmd, "load"))
        {
            vrc = teleporterTcpWriteACK(pState);
            if (RT_FAILURE(vrc))
                break;

            int vrc2 = VMR3AtErrorRegisterU(pState->mpUVM,
                                            Console::genericVMSetErrorCallback, &pState->mErrorText);
            AssertRC(vrc2);
            RTSocketRetain(pState->mhSocket); /* For concurrent access by I/O thread and EMT. */
            pState->moffStream = 0;

            void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState));
            vrc = VMR3LoadFromStream(VMR3GetVM(pState->mpUVM),
                                     &g_teleporterTcpOps, pvUser2,
                                     teleporterProgressCallback, pvUser2);

            RTSocketRelease(pState->mhSocket);
            vrc2 = VMR3AtErrorDeregister(VMR3GetVM(pState->mpUVM), Console::genericVMSetErrorCallback, &pState->mErrorText);
            AssertRC(vrc2);

            if (RT_FAILURE(vrc))
            {
                LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc));
                teleporterTcpWriteNACK(pState, vrc, pState->mErrorText.c_str());
                break;
            }

            /* The EOS might not have been read, make sure it is. */
            pState->mfStopReading = false;
            size_t cbRead;
            vrc = teleporterTcpOpRead(pvUser2, pState->moffStream, szCmd, 1, &cbRead);
            if (vrc != VERR_EOF)
            {
                LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc));
                teleporterTcpWriteNACK(pState, vrc);
                break;
            }

            vrc = teleporterTcpWriteACK(pState);
        }
        else if (!strcmp(szCmd, "cancel"))
        {
            /* Don't ACK this. */
            LogRel(("Teleporter: Received cancel command.\n"));
            vrc = VERR_SSM_CANCELLED;
        }
        else if (!strcmp(szCmd, "lock-media"))
        {
            hrc = pState->mpControl->LockMedia();
            if (SUCCEEDED(hrc))
            {
                pState->mfLockedMedia = true;
                vrc = teleporterTcpWriteACK(pState);
            }
            else
            {
                vrc = VERR_FILE_LOCK_FAILED;
                teleporterTcpWriteNACK(pState, vrc);
            }
        }
        else if (   !strcmp(szCmd, "hand-over-resume")
                    || !strcmp(szCmd, "hand-over-paused"))
        {
            /*
             * Point of no return.
             *
             * Note! Since we cannot tell whether a VMR3Resume failure is
             *       destructive for the source or not, we have little choice
             *       but to ACK it first and take any failures locally.
             *
             *       Ideally, we should try resume it first and then ACK (or
             *       NACK) the request since this would reduce latency and
             *       make it possible to recover from some VMR3Resume failures.
             */
            if (   pState->mptrProgress->notifyPointOfNoReturn()
                    && pState->mfLockedMedia)
            {
                vrc = teleporterTcpWriteACK(pState);
                if (RT_SUCCESS(vrc))
                {
                    if (!strcmp(szCmd, "hand-over-resume"))
                        vrc = VMR3Resume(VMR3GetVM(pState->mpUVM));
                    else
                        pState->mptrConsole->setMachineState(MachineState_Paused);
                    fDone = true;
                    break;
                }
            }
            else
            {
                vrc = pState->mfLockedMedia ? VERR_WRONG_ORDER : VERR_SSM_CANCELLED;
                teleporterTcpWriteNACK(pState, vrc);
            }
        }
        else
        {
            LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd));
            vrc = VERR_NOT_IMPLEMENTED;
            teleporterTcpWriteNACK(pState, vrc);
        }

        if (RT_FAILURE(vrc))
            break;
    }

    if (RT_SUCCESS(vrc) && !fDone)
        vrc = VERR_WRONG_ORDER;
    if (RT_FAILURE(vrc))
        teleporterTrgUnlockMedia(pState);

    pState->mRc = vrc;
    pState->mhSocket = NIL_RTSOCKET;
    LogFlowFunc(("returns mRc=%Rrc\n", vrc));
    return VERR_TCP_SERVER_STOP;
}