/** * @interface_method_impl{TXSTRANSPORT,pfnPollIn} */ static DECLCALLBACK(bool) txsTcpPollIn(void) { RTSOCKET hTcpClient = g_hTcpClient; if (hTcpClient == NIL_RTSOCKET) return false; int rc = RTTcpSelectOne(hTcpClient, 0/*cMillies*/); return RT_SUCCESS(rc); }
/** * Selects and poll for close condition. * * We can use a relatively high poll timeout here since it's only used to get * us out of error paths. In the normal cause of events, we'll get a * end-of-stream header. * * @returns VBox status code. * * @param pState The teleporter state data. */ static int teleporterTcpReadSelect(TeleporterState *pState) { int rc; do { rc = RTTcpSelectOne(pState->mhSocket, 1000); if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) { pState->mfIOError = true; LogRel(("Teleporter/TCP: Header select error: %Rrc\n", rc)); break; } if (pState->mfStopReading) { rc = VERR_EOF; break; } } while (rc == VERR_TIMEOUT); return rc; }
/** * @copydoc SSMSTRMOPS::pfnIsOk */ static DECLCALLBACK(int) teleporterTcpOpIsOk(void *pvUser) { TeleporterState *pState = (TeleporterState *)pvUser; if (pState->mfIsSource) { /* Poll for incoming NACKs and errors from the other side */ int rc = RTTcpSelectOne(pState->mhSocket, 0); if (rc != VERR_TIMEOUT) { if (RT_SUCCESS(rc)) { LogRel(("Teleporter/TCP: Incoming data detect by IsOk, assuming it is a cancellation NACK.\n")); rc = VERR_SSM_CANCELLED; } else LogRel(("Teleporter/TCP: RTTcpSelectOne -> %Rrc (IsOk).\n", rc)); return rc; } } return VINF_SUCCESS; }
/** * 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; }