VirtualBox

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

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

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.1 KB
Line 
1/* $Id: ConsoleImplTeleporter.cpp 33540 2010-10-28 09:27:05Z 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 destination 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),
959 Bstr(tr("Teleporter")).raw(),
960 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")).raw(), 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 fCanceled = TRUE;
1128 hrc = pProgress->COMGETTER(Canceled)(&fCanceled);
1129 if (FAILED(hrc) || fCanceled)
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 return rc;
1200}
1201
1202
1203static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2, const char *pszMsgText = NULL)
1204{
1205 /*
1206 * Unlock media sending the NACK. That way the other doesn't have to spin
1207 * waiting to regain the locks.
1208 */
1209 teleporterTrgUnlockMedia(pState);
1210
1211 char szMsg[256];
1212 size_t cch;
1213 if (pszMsgText && *pszMsgText)
1214 {
1215 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
1216 for (size_t off = 6; off + 1 < cch; off++)
1217 if (szMsg[off] == '\n')
1218 szMsg[off] = '\r';
1219 }
1220 else
1221 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
1222 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
1223 if (RT_FAILURE(rc))
1224 LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
1225 return rc;
1226}
1227
1228
1229/**
1230 * @copydoc FNRTTCPSERVE
1231 *
1232 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
1233 */
1234/*static*/ DECLCALLBACK(int)
1235Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
1236{
1237 TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
1238 pState->mhSocket = Sock;
1239
1240 /*
1241 * Disable Nagle and say hello.
1242 */
1243 int vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
1244 AssertRC(vrc);
1245 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(BstrFmt(tr("Teleporting VM from %RTnaddr"), &Addr).raw(), 8);
1287 }
1288 else
1289 {
1290 LogRel(("Teleporter: Incoming VM!\n"));
1291 hrc = pState->mptrProgress->SetNextOperation(Bstr(tr("Teleporting VM")).raw(), 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.

© 2023 Oracle
ContactPrivacy policyTerms of Use