VirtualBox

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

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

ConsoleImplTeleporter.cpp: Error info handling on windows (grrr!). Locking fix. Annoying warnings.

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