VirtualBox

source: vbox/trunk/src/VBox/Main/ConsoleImplTeleporter.cpp@ 30739

Last change on this file since 30739 was 30739, checked in by vboxsync, 14 years ago

Main: remove VirtualBoxSupportTranslation template, add translation support to generic base class, clean up COM headers more, remove SupportErrorInfo.cpp|h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.0 KB
Line 
1/* $Id: ConsoleImplTeleporter.cpp 30739 2010-07-08 12:27:42Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation, The Teleporter Part.
4 */
5
6/*
7 * Copyright (C) 2009 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "ConsoleImpl.h"
23#include "Global.h"
24#include "ProgressImpl.h"
25
26#include "AutoCaller.h"
27#include "Logging.h"
28
29#include <iprt/asm.h>
30#include <iprt/err.h>
31#include <iprt/rand.h>
32#include <iprt/socket.h>
33#include <iprt/tcp.h>
34#include <iprt/timer.h>
35
36#include <VBox/vmapi.h>
37#include <VBox/ssm.h>
38#include <VBox/err.h>
39#include <VBox/version.h>
40#include <VBox/com/string.h>
41#include "VBox/com/ErrorInfo.h"
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/**
47 * Base class for the teleporter state.
48 *
49 * These classes are used as advanced structs, not as proper classes.
50 */
51class TeleporterState
52{
53public:
54 ComPtr<Console> mptrConsole;
55 PVM mpVM;
56 ComObjPtr<Progress> mptrProgress;
57 Utf8Str mstrPassword;
58 bool const mfIsSource;
59
60 /** @name stream stuff
61 * @{ */
62 RTSOCKET mhSocket;
63 uint64_t moffStream;
64 uint32_t mcbReadBlock;
65 bool volatile mfStopReading;
66 bool volatile mfEndOfStream;
67 bool volatile mfIOError;
68 /** @} */
69
70 TeleporterState(Console *pConsole, PVM pVM, Progress *pProgress, bool fIsSource)
71 : mptrConsole(pConsole)
72 , mpVM(pVM)
73 , mptrProgress(pProgress)
74 , mfIsSource(fIsSource)
75 , mhSocket(NIL_RTSOCKET)
76 , moffStream(UINT64_MAX / 2)
77 , mcbReadBlock(0)
78 , mfStopReading(false)
79 , mfEndOfStream(false)
80 , mfIOError(false)
81 {
82 }
83};
84
85
86/**
87 * Teleporter state used by the source side.
88 */
89class TeleporterStateSrc : public TeleporterState
90{
91public:
92 Utf8Str mstrHostname;
93 uint32_t muPort;
94 uint32_t mcMsMaxDowntime;
95 MachineState_T menmOldMachineState;
96 bool mfSuspendedByUs;
97 bool mfUnlockedMedia;
98
99 TeleporterStateSrc(Console *pConsole, PVM pVM, Progress *pProgress, MachineState_T enmOldMachineState)
100 : TeleporterState(pConsole, pVM, pProgress, true /*fIsSource*/)
101 , muPort(UINT32_MAX)
102 , mcMsMaxDowntime(250)
103 , menmOldMachineState(enmOldMachineState)
104 , mfSuspendedByUs(false)
105 , mfUnlockedMedia(false)
106 {
107 }
108};
109
110
111/**
112 * Teleporter state used by the destiation side.
113 */
114class TeleporterStateTrg : public TeleporterState
115{
116public:
117 IMachine *mpMachine;
118 IInternalMachineControl *mpControl;
119 PRTTCPSERVER mhServer;
120 PRTTIMERLR mphTimerLR;
121 bool mfLockedMedia;
122 int mRc;
123 Utf8Str mErrorText;
124
125 TeleporterStateTrg(Console *pConsole, PVM pVM, Progress *pProgress,
126 IMachine *pMachine, IInternalMachineControl *pControl,
127 PRTTIMERLR phTimerLR, bool fStartPaused)
128 : TeleporterState(pConsole, pVM, pProgress, false /*fIsSource*/)
129 , mpMachine(pMachine)
130 , mpControl(pControl)
131 , mhServer(NULL)
132 , mphTimerLR(phTimerLR)
133 , mfLockedMedia(false)
134 , mRc(VINF_SUCCESS)
135 , mErrorText()
136 {
137 }
138};
139
140
141/**
142 * TCP stream header.
143 *
144 * This is an extra layer for fixing the problem with figuring out when the SSM
145 * stream ends.
146 */
147typedef struct TELEPORTERTCPHDR
148{
149 /** Magic value. */
150 uint32_t u32Magic;
151 /** The size of the data block following this header.
152 * 0 indicates the end of the stream, while UINT32_MAX indicates
153 * cancelation. */
154 uint32_t cb;
155} TELEPORTERTCPHDR;
156/** Magic value for TELEPORTERTCPHDR::u32Magic. (Egberto Gismonti Amin) */
157#define TELEPORTERTCPHDR_MAGIC UINT32_C(0x19471205)
158/** The max block size. */
159#define TELEPORTERTCPHDR_MAX_SIZE UINT32_C(0x00fffff8)
160
161
162/*******************************************************************************
163* Global Variables *
164*******************************************************************************/
165static const char g_szWelcome[] = "VirtualBox-Teleporter-1.0\n";
166
167
168/**
169 * Reads a string from the socket.
170 *
171 * @returns VBox status code.
172 *
173 * @param pState The teleporter state structure.
174 * @param pszBuf The output buffer.
175 * @param cchBuf The size of the output buffer.
176 *
177 */
178static int teleporterTcpReadLine(TeleporterState *pState, char *pszBuf, size_t cchBuf)
179{
180 char *pszStart = pszBuf;
181 RTSOCKET Sock = pState->mhSocket;
182
183 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
184 *pszBuf = '\0';
185
186 /* dead simple approach. */
187 for (;;)
188 {
189 char ch;
190 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
191 if (RT_FAILURE(rc))
192 {
193 LogRel(("Teleporter: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
194 return rc;
195 }
196 if ( ch == '\n'
197 || ch == '\0')
198 return VINF_SUCCESS;
199 if (cchBuf <= 1)
200 {
201 LogRel(("Teleporter: String buffer overflow: '%s'\n", pszStart));
202 return VERR_BUFFER_OVERFLOW;
203 }
204 *pszBuf++ = ch;
205 *pszBuf = '\0';
206 cchBuf--;
207 }
208}
209
210
211/**
212 * Reads an ACK or NACK.
213 *
214 * @returns S_OK on ACK, E_FAIL+setError() on failure or NACK.
215 * @param pState The teleporter source state.
216 * @param pszWhich Which ACK is this this?
217 * @param pszNAckMsg Optional NACK message.
218 *
219 * @remarks the setError laziness forces this to be a Console member.
220 */
221HRESULT
222Console::teleporterSrcReadACK(TeleporterStateSrc *pState, const char *pszWhich,
223 const char *pszNAckMsg /*= NULL*/)
224{
225 char szMsg[256];
226 int vrc = teleporterTcpReadLine(pState, szMsg, sizeof(szMsg));
227 if (RT_FAILURE(vrc))
228 return setError(E_FAIL, tr("Failed reading ACK(%s): %Rrc"), pszWhich, vrc);
229
230 if (!strcmp(szMsg, "ACK"))
231 return S_OK;
232
233 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
234 {
235 char *pszMsgText = strchr(szMsg, ';');
236 if (pszMsgText)
237 *pszMsgText++ = '\0';
238
239 int32_t vrc2;
240 vrc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
241 if (vrc == VINF_SUCCESS)
242 {
243 /*
244 * Well formed NACK, transform it into an error.
245 */
246 if (pszNAckMsg)
247 {
248 LogRel(("Teleporter: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
249 return setError(E_FAIL, pszNAckMsg);
250 }
251
252 if (pszMsgText)
253 {
254 pszMsgText = RTStrStrip(pszMsgText);
255 for (size_t off = 0; pszMsgText[off]; off++)
256 if (pszMsgText[off] == '\r')
257 pszMsgText[off] = '\n';
258
259 LogRel(("Teleporter: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
260 if (strlen(pszMsgText) > 4)
261 return setError(E_FAIL, "%s", pszMsgText);
262 return setError(E_FAIL, "NACK(%s) - %Rrc (%d) '%s'", pszWhich, vrc2, vrc2, pszMsgText);
263 }
264
265 return setError(E_FAIL, "NACK(%s) - %Rrc (%d)", pszWhich, vrc2, vrc2);
266 }
267
268 if (pszMsgText)
269 pszMsgText[-1] = ';';
270 }
271 return setError(E_FAIL, tr("%s: Expected ACK or NACK, got '%s'"), pszWhich, szMsg);
272}
273
274
275/**
276 * Submitts a command to the destination and waits for the ACK.
277 *
278 * @returns S_OK on ACKed command, E_FAIL+setError() on failure.
279 *
280 * @param pState The teleporter source state.
281 * @param pszCommand The command.
282 * @param fWaitForAck Whether to wait for the ACK.
283 *
284 * @remarks the setError laziness forces this to be a Console member.
285 */
286HRESULT
287Console::teleporterSrcSubmitCommand(TeleporterStateSrc *pState, const char *pszCommand, bool fWaitForAck /*= true*/)
288{
289 int vrc = RTTcpSgWriteL(pState->mhSocket, 2, pszCommand, strlen(pszCommand), "\n", sizeof("\n") - 1);
290 if (RT_SUCCESS(vrc))
291 vrc = RTTcpFlush(pState->mhSocket);
292 if (RT_FAILURE(vrc))
293 return setError(E_FAIL, tr("Failed writing command '%s': %Rrc"), pszCommand, vrc);
294 if (!fWaitForAck)
295 return S_OK;
296 return teleporterSrcReadACK(pState, pszCommand);
297}
298
299
300/**
301 * @copydoc SSMSTRMOPS::pfnWrite
302 */
303static DECLCALLBACK(int) teleporterTcpOpWrite(void *pvUser, uint64_t offStream, const void *pvBuf, size_t cbToWrite)
304{
305 TeleporterState *pState = (TeleporterState *)pvUser;
306
307 AssertReturn(cbToWrite > 0, VINF_SUCCESS);
308 AssertReturn(cbToWrite < UINT32_MAX, VERR_OUT_OF_RANGE);
309 AssertReturn(pState->mfIsSource, VERR_INVALID_HANDLE);
310
311 for (;;)
312 {
313 TELEPORTERTCPHDR Hdr;
314 Hdr.u32Magic = TELEPORTERTCPHDR_MAGIC;
315 Hdr.cb = RT_MIN((uint32_t)cbToWrite, TELEPORTERTCPHDR_MAX_SIZE);
316 int rc = RTTcpSgWriteL(pState->mhSocket, 2, &Hdr, sizeof(Hdr), pvBuf, (size_t)Hdr.cb);
317 if (RT_FAILURE(rc))
318 {
319 LogRel(("Teleporter/TCP: Write error: %Rrc (cb=%#x)\n", rc, Hdr.cb));
320 return rc;
321 }
322 pState->moffStream += Hdr.cb;
323 if (Hdr.cb == cbToWrite)
324 return VINF_SUCCESS;
325
326 /* advance */
327 cbToWrite -= Hdr.cb;
328 pvBuf = (uint8_t const *)pvBuf + Hdr.cb;
329 }
330}
331
332
333/**
334 * Selects and poll for close condition.
335 *
336 * We can use a relatively high poll timeout here since it's only used to get
337 * us out of error paths. In the normal cause of events, we'll get a
338 * end-of-stream header.
339 *
340 * @returns VBox status code.
341 *
342 * @param pState The teleporter state data.
343 */
344static int teleporterTcpReadSelect(TeleporterState *pState)
345{
346 int rc;
347 do
348 {
349 rc = RTTcpSelectOne(pState->mhSocket, 1000);
350 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
351 {
352 pState->mfIOError = true;
353 LogRel(("Teleporter/TCP: Header select error: %Rrc\n", rc));
354 break;
355 }
356 if (pState->mfStopReading)
357 {
358 rc = VERR_EOF;
359 break;
360 }
361 } while (rc == VERR_TIMEOUT);
362 return rc;
363}
364
365
366/**
367 * @copydoc SSMSTRMOPS::pfnRead
368 */
369static DECLCALLBACK(int) teleporterTcpOpRead(void *pvUser, uint64_t offStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
370{
371 TeleporterState *pState = (TeleporterState *)pvUser;
372 AssertReturn(!pState->mfIsSource, VERR_INVALID_HANDLE);
373
374 for (;;)
375 {
376 int rc;
377
378 /*
379 * Check for various conditions and may have been signalled.
380 */
381 if (pState->mfEndOfStream)
382 return VERR_EOF;
383 if (pState->mfStopReading)
384 return VERR_EOF;
385 if (pState->mfIOError)
386 return VERR_IO_GEN_FAILURE;
387
388 /*
389 * If there is no more data in the current block, read the next
390 * block header.
391 */
392 if (!pState->mcbReadBlock)
393 {
394 rc = teleporterTcpReadSelect(pState);
395 if (RT_FAILURE(rc))
396 return rc;
397 TELEPORTERTCPHDR Hdr;
398 rc = RTTcpRead(pState->mhSocket, &Hdr, sizeof(Hdr), NULL);
399 if (RT_FAILURE(rc))
400 {
401 pState->mfIOError = true;
402 LogRel(("Teleporter/TCP: Header read error: %Rrc\n", rc));
403 return rc;
404 }
405
406 if (RT_UNLIKELY( Hdr.u32Magic != TELEPORTERTCPHDR_MAGIC
407 || Hdr.cb > TELEPORTERTCPHDR_MAX_SIZE
408 || Hdr.cb == 0))
409 {
410 if ( Hdr.u32Magic == TELEPORTERTCPHDR_MAGIC
411 && ( Hdr.cb == 0
412 || Hdr.cb == UINT32_MAX)
413 )
414 {
415 pState->mfEndOfStream = true;
416 pState->mcbReadBlock = 0;
417 return Hdr.cb ? VERR_SSM_CANCELLED : VERR_EOF;
418 }
419 pState->mfIOError = true;
420 LogRel(("Teleporter/TCP: Invalid block: u32Magic=%#x cb=%#x\n", Hdr.u32Magic, Hdr.cb));
421 return VERR_IO_GEN_FAILURE;
422 }
423
424 pState->mcbReadBlock = Hdr.cb;
425 if (pState->mfStopReading)
426 return VERR_EOF;
427 }
428
429 /*
430 * Read more data.
431 */
432 rc = teleporterTcpReadSelect(pState);
433 if (RT_FAILURE(rc))
434 return rc;
435 uint32_t cb = (uint32_t)RT_MIN(pState->mcbReadBlock, cbToRead);
436 rc = RTTcpRead(pState->mhSocket, pvBuf, cb, pcbRead);
437 if (RT_FAILURE(rc))
438 {
439 pState->mfIOError = true;
440 LogRel(("Teleporter/TCP: Data read error: %Rrc (cb=%#x)\n", rc, cb));
441 return rc;
442 }
443 if (pcbRead)
444 {
445 cb = (uint32_t)*pcbRead;
446 pState->moffStream += cb;
447 pState->mcbReadBlock -= cb;
448 return VINF_SUCCESS;
449 }
450 pState->moffStream += cb;
451 pState->mcbReadBlock -= cb;
452 if (cbToRead == cb)
453 return VINF_SUCCESS;
454
455 /* Advance to the next block. */
456 cbToRead -= cb;
457 pvBuf = (uint8_t *)pvBuf + cb;
458 }
459}
460
461
462/**
463 * @copydoc SSMSTRMOPS::pfnSeek
464 */
465static DECLCALLBACK(int) teleporterTcpOpSeek(void *pvUser, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
466{
467 return VERR_NOT_SUPPORTED;
468}
469
470
471/**
472 * @copydoc SSMSTRMOPS::pfnTell
473 */
474static DECLCALLBACK(uint64_t) teleporterTcpOpTell(void *pvUser)
475{
476 TeleporterState *pState = (TeleporterState *)pvUser;
477 return pState->moffStream;
478}
479
480
481/**
482 * @copydoc SSMSTRMOPS::pfnSize
483 */
484static DECLCALLBACK(int) teleporterTcpOpSize(void *pvUser, uint64_t *pcb)
485{
486 return VERR_NOT_SUPPORTED;
487}
488
489
490/**
491 * @copydoc SSMSTRMOPS::pfnIsOk
492 */
493static DECLCALLBACK(int) teleporterTcpOpIsOk(void *pvUser)
494{
495 TeleporterState *pState = (TeleporterState *)pvUser;
496
497 if (pState->mfIsSource)
498 {
499 /* Poll for incoming NACKs and errors from the other side */
500 int rc = RTTcpSelectOne(pState->mhSocket, 0);
501 if (rc != VERR_TIMEOUT)
502 {
503 if (RT_SUCCESS(rc))
504 {
505 LogRel(("Teleporter/TCP: Incoming data detect by IsOk, assuming it is a cancellation NACK.\n"));
506 rc = VERR_SSM_CANCELLED;
507 }
508 else
509 LogRel(("Teleporter/TCP: RTTcpSelectOne -> %Rrc (IsOk).\n", rc));
510 return rc;
511 }
512 }
513
514 return VINF_SUCCESS;
515}
516
517
518/**
519 * @copydoc SSMSTRMOPS::pfnClose
520 */
521static DECLCALLBACK(int) teleporterTcpOpClose(void *pvUser, bool fCancelled)
522{
523 TeleporterState *pState = (TeleporterState *)pvUser;
524
525 if (pState->mfIsSource)
526 {
527 TELEPORTERTCPHDR EofHdr;
528 EofHdr.u32Magic = TELEPORTERTCPHDR_MAGIC;
529 EofHdr.cb = fCancelled ? UINT32_MAX : 0;
530 int rc = RTTcpWrite(pState->mhSocket, &EofHdr, sizeof(EofHdr));
531 if (RT_SUCCESS(rc))
532 rc = RTTcpFlush(pState->mhSocket);
533 if (RT_FAILURE(rc))
534 {
535 LogRel(("Teleporter/TCP: EOF Header write error: %Rrc\n", rc));
536 return rc;
537 }
538 }
539 else
540 {
541 ASMAtomicWriteBool(&pState->mfStopReading, true);
542 RTTcpFlush(pState->mhSocket);
543 }
544
545 return VINF_SUCCESS;
546}
547
548
549/**
550 * Method table for a TCP based stream.
551 */
552static SSMSTRMOPS const g_teleporterTcpOps =
553{
554 SSMSTRMOPS_VERSION,
555 teleporterTcpOpWrite,
556 teleporterTcpOpRead,
557 teleporterTcpOpSeek,
558 teleporterTcpOpTell,
559 teleporterTcpOpSize,
560 teleporterTcpOpIsOk,
561 teleporterTcpOpClose,
562 SSMSTRMOPS_VERSION
563};
564
565
566/**
567 * Progress cancelation callback.
568 */
569static void teleporterProgressCancelCallback(void *pvUser)
570{
571 TeleporterState *pState = (TeleporterState *)pvUser;
572 SSMR3Cancel(pState->mpVM);
573 if (!pState->mfIsSource)
574 {
575 TeleporterStateTrg *pStateTrg = (TeleporterStateTrg *)pState;
576 RTTcpServerShutdown(pStateTrg->mhServer);
577 }
578}
579
580/**
581 * @copydoc PFNVMPROGRESS
582 */
583static DECLCALLBACK(int) teleporterProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
584{
585 TeleporterState *pState = (TeleporterState *)pvUser;
586 if (pState->mptrProgress)
587 {
588 HRESULT hrc = pState->mptrProgress->SetCurrentOperationProgress(uPercent);
589 if (FAILED(hrc))
590 {
591 /* check if the failure was caused by cancellation. */
592 BOOL fCancelled;
593 hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCancelled);
594 if (SUCCEEDED(hrc) && fCancelled)
595 {
596 SSMR3Cancel(pState->mpVM);
597 return VERR_SSM_CANCELLED;
598 }
599 }
600 }
601
602 return VINF_SUCCESS;
603}
604
605
606/**
607 * @copydoc FNRTTIMERLR
608 */
609static DECLCALLBACK(void) teleporterDstTimeout(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
610{
611 /* This is harmless for any open connections. */
612 RTTcpServerShutdown((PRTTCPSERVER)pvUser);
613}
614
615
616/**
617 * Do the teleporter.
618 *
619 * @returns VBox status code.
620 * @param pState The teleporter state.
621 */
622HRESULT
623Console::teleporterSrc(TeleporterStateSrc *pState)
624{
625 AutoCaller autoCaller(this);
626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
627
628 /*
629 * Wait for Console::Teleport to change the state.
630 */
631 { AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS); }
632
633 BOOL fCancelled = TRUE;
634 HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCancelled);
635 if (FAILED(hrc))
636 return hrc;
637 if (fCancelled)
638 return setError(E_FAIL, tr("cancelled"));
639
640 /*
641 * Try connect to the destination machine.
642 * (Note. The caller cleans up mhSocket, so we can return without worries.)
643 */
644 int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket);
645 if (RT_FAILURE(vrc))
646 return setError(E_FAIL, tr("Failed to connect to port %u on '%s': %Rrc"),
647 pState->muPort, pState->mstrHostname.c_str(), vrc);
648
649 /* Read and check the welcome message. */
650 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
651 RT_ZERO(szLine);
652 vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
653 if (RT_FAILURE(vrc))
654 return setError(E_FAIL, tr("Failed to read welcome message: %Rrc"), vrc);
655 if (strcmp(szLine, g_szWelcome))
656 return setError(E_FAIL, tr("Unexpected welcome %.*Rhxs"), sizeof(g_szWelcome) - 1, szLine);
657
658 /* password */
659 pState->mstrPassword.append('\n');
660 vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length());
661 if (RT_FAILURE(vrc))
662 return setError(E_FAIL, tr("Failed to send password: %Rrc"), vrc);
663
664 /* ACK */
665 hrc = teleporterSrcReadACK(pState, "password", tr("Invalid password"));
666 if (FAILED(hrc))
667 return hrc;
668
669 /*
670 * Start loading the state.
671 *
672 * Note! The saved state includes vital configuration data which will be
673 * verified against the VM config on the other end. This is all done
674 * in the first pass, so we should fail pretty promptly on misconfig.
675 */
676 hrc = teleporterSrcSubmitCommand(pState, "load");
677 if (FAILED(hrc))
678 return hrc;
679
680 RTSocketRetain(pState->mhSocket);
681 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
682 vrc = VMR3Teleport(pState->mpVM, pState->mcMsMaxDowntime,
683 &g_teleporterTcpOps, pvUser,
684 teleporterProgressCallback, pvUser,
685 &pState->mfSuspendedByUs);
686 RTSocketRelease(pState->mhSocket);
687 if (RT_FAILURE(vrc))
688 {
689 if ( vrc == VERR_SSM_CANCELLED
690 && RT_SUCCESS(RTTcpSelectOne(pState->mhSocket, 1)))
691 {
692 hrc = teleporterSrcReadACK(pState, "load-complete");
693 if (FAILED(hrc))
694 return hrc;
695 }
696 return setError(E_FAIL, tr("VMR3Teleport -> %Rrc"), vrc);
697 }
698
699 hrc = teleporterSrcReadACK(pState, "load-complete");
700 if (FAILED(hrc))
701 return hrc;
702
703 /*
704 * We're at the point of no return.
705 */
706 if (!pState->mptrProgress->notifyPointOfNoReturn())
707 {
708 teleporterSrcSubmitCommand(pState, "cancel", false /*fWaitForAck*/);
709 return E_FAIL;
710 }
711
712 /*
713 * Hand over any media which we might be sharing.
714 *
715 * Note! This is only important on localhost teleportations.
716 */
717 /** @todo Maybe we should only do this if it's a local teleportation... */
718 hrc = mControl->UnlockMedia();
719 if (FAILED(hrc))
720 return hrc;
721 pState->mfUnlockedMedia = true;
722
723 hrc = teleporterSrcSubmitCommand(pState, "lock-media");
724 if (FAILED(hrc))
725 return hrc;
726
727 /*
728 * The FINAL step is giving the target instructions how to proceed with the VM.
729 */
730 if ( vrc == VINF_SSM_LIVE_SUSPENDED
731 || pState->menmOldMachineState == MachineState_Paused)
732 hrc = teleporterSrcSubmitCommand(pState, "hand-over-paused");
733 else
734 hrc = teleporterSrcSubmitCommand(pState, "hand-over-resume");
735 if (FAILED(hrc))
736 return hrc;
737
738 /*
739 * teleporterSrcThreadWrapper will do the automatic power off because it
740 * has to release the AutoVMCaller.
741 */
742 return S_OK;
743}
744
745
746/**
747 * Static thread method wrapper.
748 *
749 * @returns VINF_SUCCESS (ignored).
750 * @param hThread The thread.
751 * @param pvUser Pointer to a TeleporterStateSrc instance.
752 */
753/*static*/ DECLCALLBACK(int)
754Console::teleporterSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
755{
756 TeleporterStateSrc *pState = (TeleporterStateSrc *)pvUser;
757
758 /*
759 * Console::teleporterSrc does the work, we just grab onto the VM handle
760 * and do the cleanups afterwards.
761 */
762 AutoVMCaller autoVMCaller(pState->mptrConsole);
763 HRESULT hrc = autoVMCaller.rc();
764
765 if (SUCCEEDED(hrc))
766 hrc = pState->mptrConsole->teleporterSrc(pState);
767
768 /* Close the connection ASAP on so that the other side can complete. */
769 if (pState->mhSocket != NIL_RTSOCKET)
770 {
771 RTTcpClientClose(pState->mhSocket);
772 pState->mhSocket = NIL_RTSOCKET;
773 }
774
775 /* Aaarg! setMachineState trashes error info on Windows, so we have to
776 complete things here on failure instead of right before cleanup. */
777 if (FAILED(hrc))
778 pState->mptrProgress->notifyComplete(hrc);
779
780 /* We can no longer be cancelled (success), or it doesn't matter any longer (failure). */
781 pState->mptrProgress->setCancelCallback(NULL, NULL);
782
783 /*
784 * Write lock the console before resetting mptrCancelableProgress and
785 * fixing the state.
786 */
787 AutoWriteLock autoLock(pState->mptrConsole COMMA_LOCKVAL_SRC_POS);
788 pState->mptrConsole->mptrCancelableProgress.setNull();
789
790 VMSTATE const enmVMState = VMR3GetState(pState->mpVM);
791 MachineState_T const enmMachineState = pState->mptrConsole->mMachineState;
792 if (SUCCEEDED(hrc))
793 {
794 /*
795 * Automatically shut down the VM on success.
796 *
797 * Note! We have to release the VM caller object or we'll deadlock in
798 * powerDown.
799 */
800 AssertLogRelMsg(enmVMState == VMSTATE_SUSPENDED, ("%s\n", VMR3GetStateName(enmVMState)));
801 AssertLogRelMsg(enmMachineState == MachineState_TeleportingPausedVM, ("%s\n", Global::stringifyMachineState(enmMachineState)));
802
803 autoVMCaller.release();
804
805 pState->mptrConsole->mVMIsAlreadyPoweringOff = true; /* (Make sure we stick in the TeleportingPausedVM state.) */
806 hrc = pState->mptrConsole->powerDown();
807 pState->mptrConsole->mVMIsAlreadyPoweringOff = false;
808
809 pState->mptrProgress->notifyComplete(hrc);
810 }
811 else
812 {
813 /*
814 * Work the state machinery on failure.
815 *
816 * If the state is no longer 'Teleporting*', some other operation has
817 * canceled us and there is nothing we need to do here. In all other
818 * cases, we've failed one way or another.
819 */
820 if ( enmMachineState == MachineState_Teleporting
821 || enmMachineState == MachineState_TeleportingPausedVM
822 )
823 {
824 if (pState->mfUnlockedMedia)
825 {
826 ErrorInfoKeeper Oak;
827 HRESULT hrc2 = pState->mptrConsole->mControl->LockMedia();
828 if (FAILED(hrc2))
829 {
830 uint64_t StartMS = RTTimeMilliTS();
831 do
832 {
833 RTThreadSleep(2);
834 hrc2 = pState->mptrConsole->mControl->LockMedia();
835 } while ( FAILED(hrc2)
836 && RTTimeMilliTS() - StartMS < 2000);
837 }
838 if (SUCCEEDED(hrc2))
839 pState->mfUnlockedMedia = true;
840 else
841 LogRel(("FATAL ERROR: Failed to re-take the media locks. hrc2=%Rhrc\n", hrc2));
842 }
843
844 switch (enmVMState)
845 {
846 case VMSTATE_RUNNING:
847 case VMSTATE_RUNNING_LS:
848 case VMSTATE_DEBUGGING:
849 case VMSTATE_DEBUGGING_LS:
850 case VMSTATE_POWERING_OFF:
851 case VMSTATE_POWERING_OFF_LS:
852 case VMSTATE_RESETTING:
853 case VMSTATE_RESETTING_LS:
854 Assert(!pState->mfSuspendedByUs);
855 Assert(!pState->mfUnlockedMedia);
856 pState->mptrConsole->setMachineState(MachineState_Running);
857 break;
858
859 case VMSTATE_GURU_MEDITATION:
860 case VMSTATE_GURU_MEDITATION_LS:
861 pState->mptrConsole->setMachineState(MachineState_Stuck);
862 break;
863
864 case VMSTATE_FATAL_ERROR:
865 case VMSTATE_FATAL_ERROR_LS:
866 pState->mptrConsole->setMachineState(MachineState_Paused);
867 break;
868
869 default:
870 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
871 case VMSTATE_SUSPENDED:
872 case VMSTATE_SUSPENDED_LS:
873 case VMSTATE_SUSPENDING:
874 case VMSTATE_SUSPENDING_LS:
875 case VMSTATE_SUSPENDING_EXT_LS:
876 if (!pState->mfUnlockedMedia)
877 {
878 pState->mptrConsole->setMachineState(MachineState_Paused);
879 if (pState->mfSuspendedByUs)
880 {
881 autoLock.leave();
882 int rc = VMR3Resume(pState->mpVM);
883 AssertLogRelMsgRC(rc, ("VMR3Resume -> %Rrc\n", rc));
884 autoLock.enter();
885 }
886 }
887 else
888 {
889 /* Faking a guru meditation is the best I can think of doing here... */
890 pState->mptrConsole->setMachineState(MachineState_Stuck);
891 }
892 break;
893 }
894 }
895 }
896 autoLock.leave();
897
898 /*
899 * Cleanup.
900 */
901 Assert(pState->mhSocket == NIL_RTSOCKET);
902 delete pState;
903
904 return VINF_SUCCESS; /* ignored */
905}
906
907
908/**
909 * Start teleporter to the specified target.
910 *
911 * @returns COM status code.
912 *
913 * @param aHostname The name of the target host.
914 * @param aPort The TCP port number.
915 * @param aPassword The password.
916 * @param aMaxDowntime Max allowed "downtime" in milliseconds.
917 * @param aProgress Where to return the progress object.
918 */
919STDMETHODIMP
920Console::Teleport(IN_BSTR aHostname, ULONG aPort, IN_BSTR aPassword, ULONG aMaxDowntime, IProgress **aProgress)
921{
922 /*
923 * Validate parameters, check+hold object status, write lock the object
924 * and validate the state.
925 */
926 CheckComArgOutPointerValid(aProgress);
927 CheckComArgStrNotEmptyOrNull(aHostname);
928 CheckComArgStrNotEmptyOrNull(aHostname);
929 CheckComArgExprMsg(aPort, aPort > 0 && aPort <= 65535, ("is %u", aPort));
930 CheckComArgExprMsg(aMaxDowntime, aMaxDowntime > 0, ("is %u", aMaxDowntime));
931
932 AutoCaller autoCaller(this);
933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
934
935 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
936 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
937
938 switch (mMachineState)
939 {
940 case MachineState_Running:
941 case MachineState_Paused:
942 break;
943
944 default:
945 return setError(VBOX_E_INVALID_VM_STATE,
946 tr("Invalid machine state: %s (must be Running or Paused)"),
947 Global::stringifyMachineState(mMachineState));
948 }
949
950
951 /*
952 * Create a progress object, spawn a worker thread and change the state.
953 * Note! The thread won't start working until we release the lock.
954 */
955 LogFlowThisFunc(("Initiating TELEPORT request...\n"));
956
957 ComObjPtr<Progress> ptrProgress;
958 HRESULT hrc = ptrProgress.createObject();
959 if (FAILED(hrc)) return hrc;
960 hrc = ptrProgress->init(static_cast<IConsole *>(this), Bstr(tr("Teleporter")), TRUE /*aCancelable*/);
961 if (FAILED(hrc)) return hrc;
962
963 TeleporterStateSrc *pState = new TeleporterStateSrc(this, mpVM, ptrProgress, mMachineState);
964 pState->mstrPassword = aPassword;
965 pState->mstrHostname = aHostname;
966 pState->muPort = aPort;
967 pState->mcMsMaxDowntime = aMaxDowntime;
968
969 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
970 ptrProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser);
971
972 int vrc = RTThreadCreate(NULL, Console::teleporterSrcThreadWrapper, (void *)pState, 0 /*cbStack*/,
973 RTTHREADTYPE_EMULATION, 0 /*fFlags*/, "Teleport");
974 if (RT_SUCCESS(vrc))
975 {
976 if (mMachineState == MachineState_Running)
977 hrc = setMachineState(MachineState_Teleporting);
978 else
979 hrc = setMachineState(MachineState_TeleportingPausedVM);
980 if (SUCCEEDED(hrc))
981 {
982 ptrProgress.queryInterfaceTo(aProgress);
983 mptrCancelableProgress = ptrProgress;
984 }
985 else
986 ptrProgress->Cancel();
987 }
988 else
989 {
990 ptrProgress->setCancelCallback(NULL, NULL);
991 delete pState;
992 hrc = setError(E_FAIL, tr("RTThreadCreate -> %Rrc"), vrc);
993 }
994
995 return hrc;
996}
997
998
999/**
1000 * Creates a TCP server that listens for the source machine and passes control
1001 * over to Console::teleporterTrgServeConnection().
1002 *
1003 * @returns VBox status code.
1004 * @param pVM The VM handle
1005 * @param pMachine The IMachine for the virtual machine.
1006 * @param pErrorMsg Pointer to the error string for VMSetError.
1007 * @param fStartPaused Whether to start it in the Paused (true) or
1008 * Running (false) state,
1009 * @param pProgress Pointer to the progress object.
1010 * @param pfPowerOffOnFailure Whether the caller should power off
1011 * the VM on failure.
1012 *
1013 * @remarks The caller expects error information to be set on failure.
1014 * @todo Check that all the possible failure paths sets error info...
1015 */
1016HRESULT
1017Console::teleporterTrg(PVM pVM, IMachine *pMachine, Utf8Str *pErrorMsg, bool fStartPaused,
1018 Progress *pProgress, bool *pfPowerOffOnFailure)
1019{
1020 LogThisFunc(("pVM=%p pMachine=%p fStartPaused=%RTbool pProgress=%p\n", pVM, pMachine, fStartPaused, pProgress));
1021
1022 *pfPowerOffOnFailure = true;
1023
1024 /*
1025 * Get the config.
1026 */
1027 ULONG uPort;
1028 HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
1029 if (FAILED(hrc))
1030 return hrc;
1031 ULONG const uPortOrg = uPort;
1032
1033 Bstr bstrAddress;
1034 hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
1035 if (FAILED(hrc))
1036 return hrc;
1037 Utf8Str strAddress(bstrAddress);
1038 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
1039
1040 Bstr bstrPassword;
1041 hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
1042 if (FAILED(hrc))
1043 return hrc;
1044 Utf8Str strPassword(bstrPassword);
1045 strPassword.append('\n'); /* To simplify password checking. */
1046
1047 /*
1048 * Create the TCP server.
1049 */
1050 int vrc;
1051 PRTTCPSERVER hServer;
1052 if (uPort)
1053 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1054 else
1055 {
1056 for (int cTries = 10240; cTries > 0; cTries--)
1057 {
1058 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
1059 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1060 if (vrc != VERR_NET_ADDRESS_IN_USE)
1061 break;
1062 }
1063 if (RT_SUCCESS(vrc))
1064 {
1065 hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
1066 if (FAILED(hrc))
1067 {
1068 RTTcpServerDestroy(hServer);
1069 return hrc;
1070 }
1071 }
1072 }
1073 if (RT_FAILURE(vrc))
1074 return setError(E_FAIL, tr("RTTcpServerCreateEx failed with status %Rrc"), vrc);
1075
1076 /*
1077 * Create a one-shot timer for timing out after 5 mins.
1078 */
1079 RTTIMERLR hTimerLR;
1080 vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterDstTimeout, hServer);
1081 if (RT_SUCCESS(vrc))
1082 {
1083 vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
1084 if (RT_SUCCESS(vrc))
1085 {
1086 /*
1087 * Do the job, when it returns we're done.
1088 */
1089 TeleporterStateTrg theState(this, pVM, pProgress, pMachine, mControl, &hTimerLR, fStartPaused);
1090 theState.mstrPassword = strPassword;
1091 theState.mhServer = hServer;
1092
1093 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(&theState));
1094 if (pProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser))
1095 {
1096 LogRel(("Teleporter: Waiting for incoming VM...\n"));
1097 hrc = pProgress->SetNextOperation(Bstr(tr("Waiting for incoming VM")), 1);
1098 if (SUCCEEDED(hrc))
1099 {
1100 vrc = RTTcpServerListen(hServer, Console::teleporterTrgServeConnection, &theState);
1101 pProgress->setCancelCallback(NULL, NULL);
1102
1103 if (vrc == VERR_TCP_SERVER_STOP)
1104 {
1105 vrc = theState.mRc;
1106 /* Power off the VM on failure unless the state callback
1107 already did that. */
1108 *pfPowerOffOnFailure = false;
1109 if (RT_SUCCESS(vrc))
1110 hrc = S_OK;
1111 else
1112 {
1113 VMSTATE enmVMState = VMR3GetState(pVM);
1114 if ( enmVMState != VMSTATE_OFF
1115 && enmVMState != VMSTATE_POWERING_OFF)
1116 *pfPowerOffOnFailure = true;
1117
1118 /* Set error. */
1119 if (pErrorMsg->length())
1120 hrc = setError(E_FAIL, "%s", pErrorMsg->c_str());
1121 else
1122 hrc = setError(E_FAIL, tr("Teleporation failed (%Rrc)"), vrc);
1123 }
1124 }
1125 else if (vrc == VERR_TCP_SERVER_SHUTDOWN)
1126 {
1127 BOOL fCancelled = TRUE;
1128 hrc = pProgress->COMGETTER(Canceled)(&fCancelled);
1129 if (FAILED(hrc) || fCancelled)
1130 hrc = setError(E_FAIL, tr("Teleporting canceled"));
1131 else
1132 hrc = setError(E_FAIL, tr("Teleporter timed out waiting for incoming connection"));
1133 LogRel(("Teleporter: RTTcpServerListen aborted - %Rrc\n", vrc));
1134 }
1135 else
1136 {
1137 hrc = setError(E_FAIL, tr("Unexpected RTTcpServerListen status code %Rrc"), vrc);
1138 LogRel(("Teleporter: Unexpected RTTcpServerListen rc: %Rrc\n", vrc));
1139 }
1140 }
1141 else
1142 LogThisFunc(("SetNextOperation failed, %Rhrc\n", hrc));
1143 }
1144 else
1145 {
1146 LogThisFunc(("Canceled - check point #1\n"));
1147 hrc = setError(E_FAIL, tr("Teleporting canceled"));
1148 }
1149 }
1150 else
1151 hrc = setError(E_FAIL, "RTTimerLRStart -> %Rrc", vrc);
1152
1153 RTTimerLRDestroy(hTimerLR);
1154 }
1155 else
1156 hrc = setError(E_FAIL, "RTTimerLRCreate -> %Rrc", vrc);
1157 RTTcpServerDestroy(hServer);
1158
1159 /*
1160 * If we change TeleporterPort above, set it back to it's original
1161 * value before returning.
1162 */
1163 if (uPortOrg != uPort)
1164 {
1165 ErrorInfoKeeper Eik;
1166 pMachine->COMSETTER(TeleporterPort)(uPortOrg);
1167 }
1168
1169 return hrc;
1170}
1171
1172
1173/**
1174 * Unlock the media.
1175 *
1176 * This is used in error paths.
1177 *
1178 * @param pState The teleporter state.
1179 */
1180static void teleporterTrgUnlockMedia(TeleporterStateTrg *pState)
1181{
1182 if (pState->mfLockedMedia)
1183 {
1184 pState->mpControl->UnlockMedia();
1185 pState->mfLockedMedia = false;
1186 }
1187}
1188
1189
1190static int teleporterTcpWriteACK(TeleporterStateTrg *pState, bool fAutomaticUnlock = true)
1191{
1192 int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1);
1193 if (RT_FAILURE(rc))
1194 {
1195 LogRel(("Teleporter: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
1196 if (fAutomaticUnlock)
1197 teleporterTrgUnlockMedia(pState);
1198 }
1199 RTTcpFlush(pState->mhSocket);
1200 return rc;
1201}
1202
1203
1204static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2, const char *pszMsgText = NULL)
1205{
1206 /*
1207 * Unlock media sending the NACK. That way the other doesn't have to spin
1208 * waiting to regain the locks.
1209 */
1210 teleporterTrgUnlockMedia(pState);
1211
1212 char szMsg[256];
1213 size_t cch;
1214 if (pszMsgText && *pszMsgText)
1215 {
1216 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
1217 for (size_t off = 6; off + 1 < cch; off++)
1218 if (szMsg[off] == '\n')
1219 szMsg[off] = '\r';
1220 }
1221 else
1222 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
1223 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
1224 if (RT_FAILURE(rc))
1225 LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
1226 RTTcpFlush(pState->mhSocket);
1227 return rc;
1228}
1229
1230
1231/**
1232 * @copydoc FNRTTCPSERVE
1233 *
1234 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
1235 */
1236/*static*/ DECLCALLBACK(int)
1237Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
1238{
1239 TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
1240 pState->mhSocket = Sock;
1241
1242 /*
1243 * Say hello.
1244 */
1245 int vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
1246 if (RT_FAILURE(vrc))
1247 {
1248 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc));
1249 return VINF_SUCCESS;
1250 }
1251
1252 /*
1253 * Password (includes '\n', see teleporterTrg).
1254 */
1255 const char *pszPassword = pState->mstrPassword.c_str();
1256 unsigned off = 0;
1257 while (pszPassword[off])
1258 {
1259 char ch;
1260 vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
1261 if ( RT_FAILURE(vrc)
1262 || pszPassword[off] != ch)
1263 {
1264 if (RT_FAILURE(vrc))
1265 LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc));
1266 else
1267 LogRel(("Teleporter: Invalid password (off=%u)\n", off));
1268 teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
1269 return VINF_SUCCESS;
1270 }
1271 off++;
1272 }
1273 vrc = teleporterTcpWriteACK(pState);
1274 if (RT_FAILURE(vrc))
1275 return VINF_SUCCESS;
1276
1277 /*
1278 * Update the progress bar, with peer name if available.
1279 */
1280 HRESULT hrc;
1281 RTNETADDR Addr;
1282 vrc = RTTcpGetPeerAddress(Sock, &Addr);
1283 if (RT_SUCCESS(vrc))
1284 {
1285 LogRel(("Teleporter: Incoming VM from %RTnaddr!\n", &Addr));
1286 hrc = pState->mptrProgress->SetNextOperation(Bstr(Utf8StrFmt(tr("Teleporting VM from %RTnaddr"), &Addr)), 8);
1287 }
1288 else
1289 {
1290 LogRel(("Teleporter: Incoming VM!\n"));
1291 hrc = pState->mptrProgress->SetNextOperation(Bstr(tr("Teleporting VM")), 8);
1292 }
1293 AssertMsg(SUCCEEDED(hrc) || hrc == E_FAIL, ("%Rhrc\n", hrc));
1294
1295 /*
1296 * Stop the server and cancel the timeout timer.
1297 *
1298 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
1299 * to it we must not return that value!
1300 */
1301 RTTcpServerShutdown(pState->mhServer);
1302 RTTimerLRDestroy(*pState->mphTimerLR);
1303 *pState->mphTimerLR = NIL_RTTIMERLR;
1304
1305 /*
1306 * Command processing loop.
1307 */
1308 bool fDone = false;
1309 for (;;)
1310 {
1311 char szCmd[128];
1312 vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd));
1313 if (RT_FAILURE(vrc))
1314 break;
1315
1316 if (!strcmp(szCmd, "load"))
1317 {
1318 vrc = teleporterTcpWriteACK(pState);
1319 if (RT_FAILURE(vrc))
1320 break;
1321
1322 int vrc2 = VMR3AtErrorRegister(pState->mpVM, Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
1323 RTSocketRetain(pState->mhSocket); /* For concurrent access by I/O thread and EMT. */
1324 pState->moffStream = 0;
1325
1326 void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState));
1327 vrc = VMR3LoadFromStream(pState->mpVM, &g_teleporterTcpOps, pvUser2,
1328 teleporterProgressCallback, pvUser2);
1329
1330 RTSocketRelease(pState->mhSocket);
1331 vrc2 = VMR3AtErrorDeregister(pState->mpVM, Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
1332
1333 if (RT_FAILURE(vrc))
1334 {
1335 LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc));
1336 teleporterTcpWriteNACK(pState, vrc, pState->mErrorText.c_str());
1337 break;
1338 }
1339
1340 /* The EOS might not have been read, make sure it is. */
1341 pState->mfStopReading = false;
1342 size_t cbRead;
1343 vrc = teleporterTcpOpRead(pvUser2, pState->moffStream, szCmd, 1, &cbRead);
1344 if (vrc != VERR_EOF)
1345 {
1346 LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc));
1347 teleporterTcpWriteNACK(pState, vrc);
1348 break;
1349 }
1350
1351 vrc = teleporterTcpWriteACK(pState);
1352 }
1353 else if (!strcmp(szCmd, "cancel"))
1354 {
1355 /* Don't ACK this. */
1356 LogRel(("Teleporter: Received cancel command.\n"));
1357 vrc = VERR_SSM_CANCELLED;
1358 }
1359 else if (!strcmp(szCmd, "lock-media"))
1360 {
1361 hrc = pState->mpControl->LockMedia();
1362 if (SUCCEEDED(hrc))
1363 {
1364 pState->mfLockedMedia = true;
1365 vrc = teleporterTcpWriteACK(pState);
1366 }
1367 else
1368 {
1369 vrc = VERR_FILE_LOCK_FAILED;
1370 teleporterTcpWriteNACK(pState, vrc);
1371 }
1372 }
1373 else if ( !strcmp(szCmd, "hand-over-resume")
1374 || !strcmp(szCmd, "hand-over-paused"))
1375 {
1376 /*
1377 * Point of no return.
1378 *
1379 * Note! Since we cannot tell whether a VMR3Resume failure is
1380 * destructive for the source or not, we have little choice
1381 * but to ACK it first and take any failures locally.
1382 *
1383 * Ideally, we should try resume it first and then ACK (or
1384 * NACK) the request since this would reduce latency and
1385 * make it possible to recover from some VMR3Resume failures.
1386 */
1387 if ( pState->mptrProgress->notifyPointOfNoReturn()
1388 && pState->mfLockedMedia)
1389 {
1390 vrc = teleporterTcpWriteACK(pState);
1391 if (RT_SUCCESS(vrc))
1392 {
1393 if (!strcmp(szCmd, "hand-over-resume"))
1394 vrc = VMR3Resume(pState->mpVM);
1395 else
1396 pState->mptrConsole->setMachineState(MachineState_Paused);
1397 fDone = true;
1398 break;
1399 }
1400 }
1401 else
1402 {
1403 vrc = pState->mfLockedMedia ? VERR_WRONG_ORDER : VERR_SSM_CANCELLED;
1404 teleporterTcpWriteNACK(pState, vrc);
1405 }
1406 }
1407 else
1408 {
1409 LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd));
1410 vrc = VERR_NOT_IMPLEMENTED;
1411 teleporterTcpWriteNACK(pState, vrc);
1412 }
1413
1414 if (RT_FAILURE(vrc))
1415 break;
1416 }
1417
1418 if (RT_SUCCESS(vrc) && !fDone)
1419 vrc = VERR_WRONG_ORDER;
1420 if (RT_FAILURE(vrc))
1421 teleporterTrgUnlockMedia(pState);
1422
1423 pState->mRc = vrc;
1424 pState->mhSocket = NIL_RTSOCKET;
1425 LogFlowFunc(("returns mRc=%Rrc\n", vrc));
1426 return VERR_TCP_SERVER_STOP;
1427}
1428
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette