VirtualBox

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

Last change on this file since 23808 was 23806, checked in by vboxsync, 16 years ago

todo update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 KB
Line 
1/* $Id: ConsoleImplTeleporter.cpp 23806 2009-10-15 15:54:34Z 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 * Update: As much as possible will be taken care of by the first snapshot
565 * pass. */
566
567 /*
568 * Start loading the state.
569 */
570 hrc = teleporterSrcSubmitCommand(pState, "load");
571 if (FAILED(hrc))
572 return hrc;
573
574 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
575 vrc = VMR3Teleport(pState->mpVM, &g_teleporterTcpOps, pvUser, NULL/** @todo progress*/, pvUser);
576 if (vrc)
577 return setError(E_FAIL, tr("VMR3Teleport -> %Rrc"), vrc);
578
579 hrc = teleporterSrcReadACK(pState, "load-complete");
580 if (FAILED(hrc))
581 return hrc;
582
583 /*
584 * State fun? Automatic power off?
585 */
586 hrc = teleporterSrcSubmitCommand(pState, "done");
587 if (FAILED(hrc))
588 return hrc;
589
590 return S_OK;
591}
592
593
594/**
595 * Static thread method wrapper.
596 *
597 * @returns VINF_SUCCESS (ignored).
598 * @param hThread The thread.
599 * @param pvUser Pointer to a TeleporterStateSrc instance.
600 */
601/*static*/ DECLCALLBACK(int)
602Console::teleporterSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
603{
604 TeleporterStateSrc *pState = (TeleporterStateSrc *)pvUser;
605
606 AutoVMCaller autoVMCaller(pState->mptrConsole);
607 HRESULT hrc = autoVMCaller.rc();
608
609 if (SUCCEEDED(hrc))
610 hrc = pState->mptrConsole->teleporterSrc(pState);
611
612 pState->mptrProgress->notifyComplete(hrc);
613
614 AutoWriteLock autoLock(pState->mptrConsole);
615 if (pState->mptrConsole->mMachineState == MachineState_Saving)
616 {
617 VMSTATE enmVMState = VMR3GetState(pState->mpVM);
618 if (SUCCEEDED(hrc))
619 {
620 if (enmVMState == VMSTATE_SUSPENDED)
621 pState->mptrConsole->setMachineState(MachineState_Paused);
622 }
623 else
624 {
625 switch (enmVMState)
626 {
627 case VMSTATE_RUNNING:
628 case VMSTATE_RUNNING_LS:
629 case VMSTATE_DEBUGGING:
630 case VMSTATE_DEBUGGING_LS:
631 case VMSTATE_POWERING_OFF:
632 case VMSTATE_POWERING_OFF_LS:
633 case VMSTATE_RESETTING:
634 case VMSTATE_RESETTING_LS:
635 pState->mptrConsole->setMachineState(MachineState_Running);
636 break;
637 case VMSTATE_GURU_MEDITATION:
638 case VMSTATE_GURU_MEDITATION_LS:
639 pState->mptrConsole->setMachineState(MachineState_Stuck);
640 break;
641 default:
642 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
643 case VMSTATE_SUSPENDED:
644 case VMSTATE_SUSPENDED_LS:
645 case VMSTATE_SUSPENDING:
646 case VMSTATE_SUSPENDING_LS:
647 case VMSTATE_SUSPENDING_EXT_LS:
648 pState->mptrConsole->setMachineState(MachineState_Paused);
649 /** @todo somehow make the VMM report back external pause even on error. */
650 autoLock.leave();
651 VMR3Resume(pState->mpVM);
652 break;
653 }
654 }
655 }
656
657 if (pState->mhSocket != NIL_RTSOCKET)
658 {
659 RTTcpClientClose(pState->mhSocket);
660 pState->mhSocket = NIL_RTSOCKET;
661 }
662 delete pState;
663
664 return VINF_SUCCESS;
665}
666
667
668/**
669 * Start teleporter to the specified target.
670 *
671 * @returns COM status code.
672 *
673 * @param aHostname The name of the target host.
674 * @param aPort The TCP port number.
675 * @param aPassword The password.
676 * @param aProgress Where to return the progress object.
677 */
678STDMETHODIMP
679Console::Teleport(IN_BSTR aHostname, ULONG aPort, IN_BSTR aPassword, IProgress **aProgress)
680{
681 /*
682 * Validate parameters, check+hold object status, write lock the object
683 * and validate the state.
684 */
685 CheckComArgOutPointerValid(aProgress);
686 CheckComArgStrNotEmptyOrNull(aHostname);
687 CheckComArgNotNull(aHostname);
688 CheckComArgExprMsg(aPort, aPort > 0 && aPort <= 65535, ("is %u", aPort));
689
690 AutoCaller autoCaller(this);
691 CheckComRCReturnRC(autoCaller.rc());
692
693 AutoWriteLock autoLock(this);
694 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
695
696 switch (mMachineState)
697 {
698 case MachineState_Running:
699 case MachineState_Paused:
700 break;
701
702 default:
703 return setError(VBOX_E_INVALID_VM_STATE,
704 tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
705 Global::stringifyMachineState(mMachineState));
706 }
707
708
709 /*
710 * Create a progress object, spawn a worker thread and change the state.
711 * Note! The thread won't start working until we release the lock.
712 */
713 LogFlowThisFunc(("Initiating TELEPORTER request...\n"));
714
715 ComObjPtr<Progress> ptrTelportationProgress;
716 HRESULT hrc = ptrTelportationProgress.createObject();
717 CheckComRCReturnRC(hrc);
718 hrc = ptrTelportationProgress->init(static_cast<IConsole *>(this),
719 Bstr(tr("Teleporter")),
720 TRUE /*aCancelable*/);
721 CheckComRCReturnRC(hrc);
722
723 TeleporterStateSrc *pState = new TeleporterStateSrc(this, mpVM);
724 pState->mstrPassword = aPassword;
725 pState->mstrHostname = aHostname;
726 pState->muPort = aPort;
727 pState->mptrProgress = ptrTelportationProgress;
728
729 int vrc = RTThreadCreate(NULL, Console::teleporterSrcThreadWrapper, (void *)pState, 0 /*cbStack*/,
730 RTTHREADTYPE_EMULATION, 0 /*fFlags*/, "Teleport");
731 if (RT_SUCCESS(vrc))
732 {
733 hrc = setMachineState(MachineState_Saving);
734 if (SUCCEEDED(hrc))
735 ptrTelportationProgress.queryInterfaceTo(aProgress);
736 else
737 ptrTelportationProgress->Cancel();
738 }
739 else
740 {
741 delete pState;
742 hrc = setError(E_FAIL, tr("RTThreadCreate -> %Rrc"), vrc);
743 }
744
745 return hrc;
746}
747
748
749/**
750 * Creates a TCP server that listens for the source machine and passes control
751 * over to Console::teleporterTrgServeConnection().
752 *
753 * @returns VBox status code.
754 * @param pVM The VM handle
755 * @param pMachine The IMachine for the virtual machine.
756 * @param fStartPaused Whether to start it in the Paused (true) or
757 * Running (false) state,
758 * @param pvVMCallbackTask The callback task pointer for
759 * stateProgressCallback().
760 */
761int
762Console::teleporterTrg(PVM pVM, IMachine *pMachine, bool fStartPaused, void *pvVMCallbackTask)
763{
764 /*
765 * Get the config.
766 */
767 ULONG uPort;
768 HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
769 if (FAILED(hrc))
770 return VERR_GENERAL_FAILURE;
771 ULONG const uPortOrg = uPort;
772
773 Bstr bstrAddress;
774 hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
775 if (FAILED(hrc))
776 return VERR_GENERAL_FAILURE;
777 Utf8Str strAddress(bstrAddress);
778 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
779
780 Bstr bstrPassword;
781 hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
782 if (FAILED(hrc))
783 return VERR_GENERAL_FAILURE;
784 Utf8Str strPassword(bstrPassword);
785 strPassword.append('\n'); /* To simplify password checking. */
786
787 /*
788 * Create the TCP server.
789 */
790 int vrc;
791 PRTTCPSERVER hServer;
792 if (uPort)
793 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
794 else
795 {
796 for (int cTries = 10240; cTries > 0; cTries--)
797 {
798 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
799 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
800 if (vrc != VERR_NET_ADDRESS_IN_USE)
801 break;
802 }
803 if (RT_SUCCESS(vrc))
804 {
805 HRESULT hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
806 if (FAILED(hrc))
807 {
808 RTTcpServerDestroy(hServer);
809 return VERR_GENERAL_FAILURE;
810 }
811 }
812 }
813 if (RT_FAILURE(vrc))
814 return vrc;
815
816 /*
817 * Create a one-shot timer for timing out after 5 mins.
818 */
819 RTTIMERLR hTimerLR;
820 vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterTimeout, hServer);
821 if (RT_SUCCESS(vrc))
822 {
823 vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
824 if (RT_SUCCESS(vrc))
825 {
826 /*
827 * Do the job, when it returns we're done.
828 */
829 TeleporterStateTrg State(this, pVM, pMachine, &hTimerLR);
830 State.mstrPassword = strPassword;
831 State.mhServer = hServer;
832 State.mpvVMCallbackTask = pvVMCallbackTask;
833
834 vrc = RTTcpServerListen(hServer, Console::teleporterTrgServeConnection, &State);
835 if (vrc == VERR_TCP_SERVER_STOP)
836 vrc = State.mRc;
837 if (RT_SUCCESS(vrc))
838 {
839 if (fStartPaused)
840 setMachineState(MachineState_Paused);
841 else
842 vrc = VMR3Resume(pVM);
843 }
844 else
845 {
846 LogRel(("Teleporter: RTTcpServerListen -> %Rrc\n", vrc));
847 }
848 }
849
850 RTTimerLRDestroy(hTimerLR);
851 }
852 RTTcpServerDestroy(hServer);
853
854 /*
855 * If we change TeleporterPort above, set it back to it's original
856 * value before returning.
857 */
858 if (uPortOrg != uPort)
859 pMachine->COMSETTER(TeleporterPort)(uPortOrg);
860
861 return vrc;
862}
863
864
865static int teleporterTcpWriteACK(TeleporterStateTrg *pState)
866{
867 int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1);
868 if (RT_FAILURE(rc))
869 LogRel(("Teleporter: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
870 RTTcpFlush(pState->mhSocket);
871 return rc;
872}
873
874
875static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2)
876{
877 char szMsg[64];
878 size_t cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
879 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
880 if (RT_FAILURE(rc))
881 LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
882 RTTcpFlush(pState->mhSocket);
883 return rc;
884}
885
886
887/**
888 * @copydoc FNRTTCPSERVE
889 */
890/*static*/ DECLCALLBACK(int)
891Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
892{
893 TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
894 pState->mhSocket = Sock;
895
896 /*
897 * Say hello.
898 */
899 int vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
900 if (RT_FAILURE(vrc))
901 {
902 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc));
903 return VINF_SUCCESS;
904 }
905
906 /*
907 * Password (includes '\n', see teleporterTrg). If it's right, tell
908 * the TCP server to stop listening (frees up host resources and makes sure
909 * this is the last connection attempt).
910 */
911 const char *pszPassword = pState->mstrPassword.c_str();
912 unsigned off = 0;
913 while (pszPassword[off])
914 {
915 char ch;
916 vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
917 if ( RT_FAILURE(vrc)
918 || pszPassword[off] != ch)
919 {
920 if (RT_FAILURE(vrc))
921 LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc));
922 else
923 LogRel(("Teleporter: Invalid password (off=%u)\n", off));
924 teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
925 return VINF_SUCCESS;
926 }
927 off++;
928 }
929 vrc = teleporterTcpWriteACK(pState);
930 if (RT_FAILURE(vrc))
931 return vrc;
932
933 RTTcpServerShutdown(pState->mhServer);
934 RTTimerLRDestroy(*pState->mphTimerLR);
935 *pState->mphTimerLR = NIL_RTTIMERLR;
936
937 /*
938 * Command processing loop.
939 */
940 for (;;)
941 {
942 char szCmd[128];
943 vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd));
944 if (RT_FAILURE(vrc))
945 break;
946
947 if (!strcmp(szCmd, "load"))
948 {
949 vrc = teleporterTcpWriteACK(pState);
950 if (RT_FAILURE(vrc))
951 break;
952
953 pState->moffStream = 0;
954 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
955 vrc = VMR3LoadFromStream(pState->mpVM, &g_teleporterTcpOps, pvUser,
956 Console::stateProgressCallback, pState->mpvVMCallbackTask);
957 if (RT_FAILURE(vrc))
958 {
959 LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc));
960 teleporterTcpWriteNACK(pState, vrc);
961 break;
962 }
963
964 /* The EOS might not have been read, make sure it is. */
965 pState->mfStopReading = false;
966 size_t cbRead;
967 vrc = teleporterTcpOpRead(pvUser, pState->moffStream, szCmd, 1, &cbRead);
968 if (vrc != VERR_EOF)
969 {
970 LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc));
971 teleporterTcpWriteNACK(pState, vrc);
972 break;
973 }
974
975 vrc = teleporterTcpWriteACK(pState);
976 if (RT_FAILURE(vrc))
977 break;
978 }
979 /** @todo implement config verification and hardware compatability checks. Or
980 * maybe leave part of these to the saved state machinery? */
981 else if (!strcmp(szCmd, "done"))
982 {
983 vrc = teleporterTcpWriteACK(pState);
984 break;
985 }
986 else
987 {
988 LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd));
989 vrc = VERR_NOT_IMPLEMENTED;
990 teleporterTcpWriteNACK(pState, vrc);
991 break;
992 }
993 }
994
995 pState->mRc = vrc;
996 pState->mhSocket = NIL_RTSOCKET;
997 LogFlowFunc(("returns mRc=%Rrc\n", vrc));
998 return VERR_TCP_SERVER_STOP;
999}
1000
Note: See TracBrowser for help on using the repository browser.

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