VirtualBox

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

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

ConsoleImplTeleporter.cpp: Write some release log entries on the target side so it's clear what we're doing.

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