VirtualBox

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

Last change on this file since 24614 was 24614, checked in by vboxsync, 15 years ago

ConsoleImplTeleporter.cpp: changed to VM hand over and TCP disconnecting point.

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