VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp@ 56994

Last change on this file since 56994 was 55214, checked in by vboxsync, 10 years ago

Main/Console+Machine+Session+Snapshot: move the save state and snapshot related methods from IConsole to IMachine, with lots of unavoidable code restructuring and cleanup. Also define two new machine states (so that the "Saving" one is specifically for saving state now) which requires more changes everywhere
Frontends: necessary adjustments
doc/SDK: document the changes

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