VirtualBox

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

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

Main,VMM,Frontends,++: Teminology. Added a bind address for the (target) teleporter.

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