/** * Progress cancelation callback. */ static void teleporterProgressCancelCallback(void *pvUser) { TeleporterState *pState = (TeleporterState *)pvUser; SSMR3Cancel(VMR3GetVM(pState->mpUVM)); if (!pState->mfIsSource) { TeleporterStateTrg *pStateTrg = (TeleporterStateTrg *)pState; RTTcpServerShutdown(pStateTrg->mhServer); } }
/** * Common worker for drvTCPPowerOff and drvTCPDestructor. * * @param pThis The instance data. */ static void drvTCPShutdownListener(PDRVTCP pThis) { /* * Signal shutdown of the listener thread. */ pThis->fShutdown = true; if ( pThis->fIsServer && pThis->hTcpServ != NULL) { int rc = RTTcpServerShutdown(pThis->hTcpServ); AssertRC(rc); pThis->hTcpServ = NULL; } }
/** * @copydoc FNRTTIMERLR */ static DECLCALLBACK(void) teleporterDstTimeout(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick) { /* This is harmless for any open connections. */ RTTcpServerShutdown((PRTTCPSERVER)pvUser); }
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; }