VirtualBox

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

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

VMM,Main: Fixed bugs related to teleporting a paused VM.

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