VirtualBox

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

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

ConsoleImplTeleporter.cpp: The server end as well (most important).

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