VirtualBox

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

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

Main,VM: Working the teleportation state. Made VMR3Teleport report back in clear text whether it suspended the VM or not.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette