VirtualBox

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

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

Main: Fixed assertion and double error message when cancelling a teleporter waiting for incoming connections.

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