VirtualBox

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

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

Main: Make IConsole::teleport() fail with the same message as the target when there are configuration errors.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use