VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp@ 73768

Last change on this file since 73768 was 73003, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.7 KB
Line 
1/* $Id: ConsoleImplTeleporter.cpp 73003 2018-07-09 11:09:32Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation, The Teleporter Part.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
23#include "LoggingNew.h"
24
25#include "ConsoleImpl.h"
26#include "Global.h"
27#include "ProgressImpl.h"
28
29#include "AutoCaller.h"
30#include "HashedPw.h"
31
32#include <iprt/asm.h>
33#include <iprt/err.h>
34#include <iprt/rand.h>
35#include <iprt/socket.h>
36#include <iprt/tcp.h>
37#include <iprt/timer.h>
38
39#include <VBox/vmm/vmapi.h>
40#include <VBox/vmm/ssm.h>
41#include <VBox/err.h>
42#include <VBox/version.h>
43#include <VBox/com/string.h>
44#include "VBox/com/ErrorInfo.h"
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50/**
51 * Base class for the teleporter state.
52 *
53 * These classes are used as advanced structs, not as proper classes.
54 */
55class TeleporterState
56{
57public:
58 ComPtr<Console> mptrConsole;
59 PUVM mpUVM;
60 ComObjPtr<Progress> mptrProgress;
61 Utf8Str mstrPassword;
62 bool const mfIsSource;
63
64 /** @name stream stuff
65 * @{ */
66 RTSOCKET mhSocket;
67 uint64_t moffStream;
68 uint32_t mcbReadBlock;
69 bool volatile mfStopReading;
70 bool volatile mfEndOfStream;
71 bool volatile mfIOError;
72 /** @} */
73
74 TeleporterState(Console *pConsole, PUVM pUVM, Progress *pProgress, bool fIsSource)
75 : mptrConsole(pConsole)
76 , mpUVM(pUVM)
77 , mptrProgress(pProgress)
78 , mfIsSource(fIsSource)
79 , mhSocket(NIL_RTSOCKET)
80 , moffStream(UINT64_MAX / 2)
81 , mcbReadBlock(0)
82 , mfStopReading(false)
83 , mfEndOfStream(false)
84 , mfIOError(false)
85 {
86 VMR3RetainUVM(mpUVM);
87 }
88
89 ~TeleporterState()
90 {
91 VMR3ReleaseUVM(mpUVM);
92 mpUVM = NULL;
93 }
94};
95
96
97/**
98 * Teleporter state used by the source side.
99 */
100class TeleporterStateSrc : public TeleporterState
101{
102public:
103 Utf8Str mstrHostname;
104 uint32_t muPort;
105 uint32_t mcMsMaxDowntime;
106 MachineState_T menmOldMachineState;
107 bool mfSuspendedByUs;
108 bool mfUnlockedMedia;
109
110 TeleporterStateSrc(Console *pConsole, PUVM pUVM, Progress *pProgress, MachineState_T enmOldMachineState)
111 : TeleporterState(pConsole, pUVM, pProgress, true /*fIsSource*/)
112 , muPort(UINT32_MAX)
113 , mcMsMaxDowntime(250)
114 , menmOldMachineState(enmOldMachineState)
115 , mfSuspendedByUs(false)
116 , mfUnlockedMedia(false)
117 {
118 }
119};
120
121
122/**
123 * Teleporter state used by the destination side.
124 */
125class TeleporterStateTrg : public TeleporterState
126{
127public:
128 IMachine *mpMachine;
129 IInternalMachineControl *mpControl;
130 PRTTCPSERVER mhServer;
131 PRTTIMERLR mphTimerLR;
132 bool mfLockedMedia;
133 int mRc;
134 Utf8Str mErrorText;
135
136 TeleporterStateTrg(Console *pConsole, PUVM pUVM, Progress *pProgress,
137 IMachine *pMachine, IInternalMachineControl *pControl,
138 PRTTIMERLR phTimerLR, bool fStartPaused)
139 : TeleporterState(pConsole, pUVM, pProgress, false /*fIsSource*/)
140 , mpMachine(pMachine)
141 , mpControl(pControl)
142 , mhServer(NULL)
143 , mphTimerLR(phTimerLR)
144 , mfLockedMedia(false)
145 , mRc(VINF_SUCCESS)
146 , mErrorText()
147 {
148 RT_NOREF(fStartPaused); /** @todo figure out why fStartPaused isn't used */
149 }
150};
151
152
153/**
154 * TCP stream header.
155 *
156 * This is an extra layer for fixing the problem with figuring out when the SSM
157 * stream ends.
158 */
159typedef struct TELEPORTERTCPHDR
160{
161 /** Magic value. */
162 uint32_t u32Magic;
163 /** The size of the data block following this header.
164 * 0 indicates the end of the stream, while UINT32_MAX indicates
165 * cancelation. */
166 uint32_t cb;
167} TELEPORTERTCPHDR;
168/** Magic value for TELEPORTERTCPHDR::u32Magic. (Egberto Gismonti Amin) */
169#define TELEPORTERTCPHDR_MAGIC UINT32_C(0x19471205)
170/** The max block size. */
171#define TELEPORTERTCPHDR_MAX_SIZE UINT32_C(0x00fffff8)
172
173
174/*********************************************************************************************************************************
175* Global Variables *
176*********************************************************************************************************************************/
177static const char g_szWelcome[] = "VirtualBox-Teleporter-1.0\n";
178
179
180/**
181 * Reads a string from the socket.
182 *
183 * @returns VBox status code.
184 *
185 * @param pState The teleporter state structure.
186 * @param pszBuf The output buffer.
187 * @param cchBuf The size of the output buffer.
188 *
189 */
190static int teleporterTcpReadLine(TeleporterState *pState, char *pszBuf, size_t cchBuf)
191{
192 char *pszStart = pszBuf;
193 RTSOCKET hSocket = pState->mhSocket;
194
195 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
196 *pszBuf = '\0';
197
198 /* dead simple approach. */
199 for (;;)
200 {
201 char ch;
202 int rc = RTTcpRead(hSocket, &ch, sizeof(ch), NULL);
203 if (RT_FAILURE(rc))
204 {
205 LogRel(("Teleporter: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
206 return rc;
207 }
208 if ( ch == '\n'
209 || ch == '\0')
210 return VINF_SUCCESS;
211 if (cchBuf <= 1)
212 {
213 LogRel(("Teleporter: String buffer overflow: '%s'\n", pszStart));
214 return VERR_BUFFER_OVERFLOW;
215 }
216 *pszBuf++ = ch;
217 *pszBuf = '\0';
218 cchBuf--;
219 }
220}
221
222
223/**
224 * Reads an ACK or NACK.
225 *
226 * @returns S_OK on ACK, E_FAIL+setError() on failure or NACK.
227 * @param pState The teleporter source state.
228 * @param pszWhich Which ACK is this this?
229 * @param pszNAckMsg Optional NACK message.
230 *
231 * @remarks the setError laziness forces this to be a Console member.
232 */
233HRESULT
234Console::i_teleporterSrcReadACK(TeleporterStateSrc *pState, const char *pszWhich,
235 const char *pszNAckMsg /*= NULL*/)
236{
237 char szMsg[256];
238 int vrc = teleporterTcpReadLine(pState, szMsg, sizeof(szMsg));
239 if (RT_FAILURE(vrc))
240 return setErrorBoth(E_FAIL, vrc, tr("Failed reading ACK(%s): %Rrc"), pszWhich, vrc);
241
242 if (!strcmp(szMsg, "ACK"))
243 return S_OK;
244
245 if (!strncmp(szMsg, RT_STR_TUPLE("NACK=")))
246 {
247 char *pszMsgText = strchr(szMsg, ';');
248 if (pszMsgText)
249 *pszMsgText++ = '\0';
250
251 int32_t vrc2;
252 vrc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
253 if (vrc == VINF_SUCCESS)
254 {
255 /*
256 * Well formed NACK, transform it into an error.
257 */
258 if (pszNAckMsg)
259 {
260 LogRel(("Teleporter: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
261 return setError(E_FAIL, pszNAckMsg);
262 }
263
264 if (pszMsgText)
265 {
266 pszMsgText = RTStrStrip(pszMsgText);
267 for (size_t off = 0; pszMsgText[off]; off++)
268 if (pszMsgText[off] == '\r')
269 pszMsgText[off] = '\n';
270
271 LogRel(("Teleporter: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
272 if (strlen(pszMsgText) > 4)
273 return setError(E_FAIL, "%s", pszMsgText);
274 return setError(E_FAIL, "NACK(%s) - %Rrc (%d) '%s'", pszWhich, vrc2, vrc2, pszMsgText);
275 }
276
277 return setError(E_FAIL, "NACK(%s) - %Rrc (%d)", pszWhich, vrc2, vrc2);
278 }
279
280 if (pszMsgText)
281 pszMsgText[-1] = ';';
282 }
283 return setError(E_FAIL, tr("%s: Expected ACK or NACK, got '%s'"), pszWhich, szMsg);
284}
285
286
287/**
288 * Submitts a command to the destination and waits for the ACK.
289 *
290 * @returns S_OK on ACKed command, E_FAIL+setError() on failure.
291 *
292 * @param pState The teleporter source state.
293 * @param pszCommand The command.
294 * @param fWaitForAck Whether to wait for the ACK.
295 *
296 * @remarks the setError laziness forces this to be a Console member.
297 */
298HRESULT Console::i_teleporterSrcSubmitCommand(TeleporterStateSrc *pState, const char *pszCommand, bool fWaitForAck /*= true*/)
299{
300 int vrc = RTTcpSgWriteL(pState->mhSocket, 2, pszCommand, strlen(pszCommand), "\n", sizeof("\n") - 1);
301 if (RT_FAILURE(vrc))
302 return setErrorBoth(E_FAIL, vrc, tr("Failed writing command '%s': %Rrc"), pszCommand, vrc);
303 if (!fWaitForAck)
304 return S_OK;
305 return i_teleporterSrcReadACK(pState, pszCommand);
306}
307
308
309/**
310 * @copydoc SSMSTRMOPS::pfnWrite
311 */
312static DECLCALLBACK(int) teleporterTcpOpWrite(void *pvUser, uint64_t offStream, const void *pvBuf, size_t cbToWrite)
313{
314 RT_NOREF(offStream);
315 TeleporterState *pState = (TeleporterState *)pvUser;
316
317 AssertReturn(cbToWrite > 0, VINF_SUCCESS);
318 AssertReturn(cbToWrite < UINT32_MAX, VERR_OUT_OF_RANGE);
319 AssertReturn(pState->mfIsSource, VERR_INVALID_HANDLE);
320
321 for (;;)
322 {
323 TELEPORTERTCPHDR Hdr;
324 Hdr.u32Magic = TELEPORTERTCPHDR_MAGIC;
325 Hdr.cb = RT_MIN((uint32_t)cbToWrite, TELEPORTERTCPHDR_MAX_SIZE);
326 int rc = RTTcpSgWriteL(pState->mhSocket, 2, &Hdr, sizeof(Hdr), pvBuf, (size_t)Hdr.cb);
327 if (RT_FAILURE(rc))
328 {
329 LogRel(("Teleporter/TCP: Write error: %Rrc (cb=%#x)\n", rc, Hdr.cb));
330 return rc;
331 }
332 pState->moffStream += Hdr.cb;
333 if (Hdr.cb == cbToWrite)
334 return VINF_SUCCESS;
335
336 /* advance */
337 cbToWrite -= Hdr.cb;
338 pvBuf = (uint8_t const *)pvBuf + Hdr.cb;
339 }
340}
341
342
343/**
344 * Selects and poll for close condition.
345 *
346 * We can use a relatively high poll timeout here since it's only used to get
347 * us out of error paths. In the normal cause of events, we'll get a
348 * end-of-stream header.
349 *
350 * @returns VBox status code.
351 *
352 * @param pState The teleporter state data.
353 */
354static int teleporterTcpReadSelect(TeleporterState *pState)
355{
356 int rc;
357 do
358 {
359 rc = RTTcpSelectOne(pState->mhSocket, 1000);
360 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
361 {
362 pState->mfIOError = true;
363 LogRel(("Teleporter/TCP: Header select error: %Rrc\n", rc));
364 break;
365 }
366 if (pState->mfStopReading)
367 {
368 rc = VERR_EOF;
369 break;
370 }
371 } while (rc == VERR_TIMEOUT);
372 return rc;
373}
374
375
376/**
377 * @copydoc SSMSTRMOPS::pfnRead
378 */
379static DECLCALLBACK(int) teleporterTcpOpRead(void *pvUser, uint64_t offStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
380{
381 RT_NOREF(offStream);
382 TeleporterState *pState = (TeleporterState *)pvUser;
383 AssertReturn(!pState->mfIsSource, VERR_INVALID_HANDLE);
384
385 for (;;)
386 {
387 int rc;
388
389 /*
390 * Check for various conditions and may have been signalled.
391 */
392 if (pState->mfEndOfStream)
393 return VERR_EOF;
394 if (pState->mfStopReading)
395 return VERR_EOF;
396 if (pState->mfIOError)
397 return VERR_IO_GEN_FAILURE;
398
399 /*
400 * If there is no more data in the current block, read the next
401 * block header.
402 */
403 if (!pState->mcbReadBlock)
404 {
405 rc = teleporterTcpReadSelect(pState);
406 if (RT_FAILURE(rc))
407 return rc;
408 TELEPORTERTCPHDR Hdr;
409 rc = RTTcpRead(pState->mhSocket, &Hdr, sizeof(Hdr), NULL);
410 if (RT_FAILURE(rc))
411 {
412 pState->mfIOError = true;
413 LogRel(("Teleporter/TCP: Header read error: %Rrc\n", rc));
414 return rc;
415 }
416
417 if (RT_UNLIKELY( Hdr.u32Magic != TELEPORTERTCPHDR_MAGIC
418 || Hdr.cb > TELEPORTERTCPHDR_MAX_SIZE
419 || Hdr.cb == 0))
420 {
421 if ( Hdr.u32Magic == TELEPORTERTCPHDR_MAGIC
422 && ( Hdr.cb == 0
423 || Hdr.cb == UINT32_MAX)
424 )
425 {
426 pState->mfEndOfStream = true;
427 pState->mcbReadBlock = 0;
428 return Hdr.cb ? VERR_SSM_CANCELLED : VERR_EOF;
429 }
430 pState->mfIOError = true;
431 LogRel(("Teleporter/TCP: Invalid block: u32Magic=%#x cb=%#x\n", Hdr.u32Magic, Hdr.cb));
432 return VERR_IO_GEN_FAILURE;
433 }
434
435 pState->mcbReadBlock = Hdr.cb;
436 if (pState->mfStopReading)
437 return VERR_EOF;
438 }
439
440 /*
441 * Read more data.
442 */
443 rc = teleporterTcpReadSelect(pState);
444 if (RT_FAILURE(rc))
445 return rc;
446 uint32_t cb = (uint32_t)RT_MIN(pState->mcbReadBlock, cbToRead);
447 rc = RTTcpRead(pState->mhSocket, pvBuf, cb, pcbRead);
448 if (RT_FAILURE(rc))
449 {
450 pState->mfIOError = true;
451 LogRel(("Teleporter/TCP: Data read error: %Rrc (cb=%#x)\n", rc, cb));
452 return rc;
453 }
454 if (pcbRead)
455 {
456 cb = (uint32_t)*pcbRead;
457 pState->moffStream += cb;
458 pState->mcbReadBlock -= cb;
459 return VINF_SUCCESS;
460 }
461 pState->moffStream += cb;
462 pState->mcbReadBlock -= cb;
463 if (cbToRead == cb)
464 return VINF_SUCCESS;
465
466 /* Advance to the next block. */
467 cbToRead -= cb;
468 pvBuf = (uint8_t *)pvBuf + cb;
469 }
470}
471
472
473/**
474 * @copydoc SSMSTRMOPS::pfnSeek
475 */
476static DECLCALLBACK(int) teleporterTcpOpSeek(void *pvUser, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
477{
478 RT_NOREF(pvUser, offSeek, uMethod, poffActual);
479 return VERR_NOT_SUPPORTED;
480}
481
482
483/**
484 * @copydoc SSMSTRMOPS::pfnTell
485 */
486static DECLCALLBACK(uint64_t) teleporterTcpOpTell(void *pvUser)
487{
488 TeleporterState *pState = (TeleporterState *)pvUser;
489 return pState->moffStream;
490}
491
492
493/**
494 * @copydoc SSMSTRMOPS::pfnSize
495 */
496static DECLCALLBACK(int) teleporterTcpOpSize(void *pvUser, uint64_t *pcb)
497{
498 RT_NOREF(pvUser, pcb);
499 return VERR_NOT_SUPPORTED;
500}
501
502
503/**
504 * @copydoc SSMSTRMOPS::pfnIsOk
505 */
506static DECLCALLBACK(int) teleporterTcpOpIsOk(void *pvUser)
507{
508 TeleporterState *pState = (TeleporterState *)pvUser;
509
510 if (pState->mfIsSource)
511 {
512 /* Poll for incoming NACKs and errors from the other side */
513 int rc = RTTcpSelectOne(pState->mhSocket, 0);
514 if (rc != VERR_TIMEOUT)
515 {
516 if (RT_SUCCESS(rc))
517 {
518 LogRel(("Teleporter/TCP: Incoming data detect by IsOk, assuming it is a cancellation NACK.\n"));
519 rc = VERR_SSM_CANCELLED;
520 }
521 else
522 LogRel(("Teleporter/TCP: RTTcpSelectOne -> %Rrc (IsOk).\n", rc));
523 return rc;
524 }
525 }
526
527 return VINF_SUCCESS;
528}
529
530
531/**
532 * @copydoc SSMSTRMOPS::pfnClose
533 */
534static DECLCALLBACK(int) teleporterTcpOpClose(void *pvUser, bool fCancelled)
535{
536 TeleporterState *pState = (TeleporterState *)pvUser;
537
538 if (pState->mfIsSource)
539 {
540 TELEPORTERTCPHDR EofHdr;
541 EofHdr.u32Magic = TELEPORTERTCPHDR_MAGIC;
542 EofHdr.cb = fCancelled ? UINT32_MAX : 0;
543 int rc = RTTcpWrite(pState->mhSocket, &EofHdr, sizeof(EofHdr));
544 if (RT_FAILURE(rc))
545 {
546 LogRel(("Teleporter/TCP: EOF Header write error: %Rrc\n", rc));
547 return rc;
548 }
549 }
550 else
551 {
552 ASMAtomicWriteBool(&pState->mfStopReading, true);
553 }
554
555 return VINF_SUCCESS;
556}
557
558
559/**
560 * Method table for a TCP based stream.
561 */
562static SSMSTRMOPS const g_teleporterTcpOps =
563{
564 SSMSTRMOPS_VERSION,
565 teleporterTcpOpWrite,
566 teleporterTcpOpRead,
567 teleporterTcpOpSeek,
568 teleporterTcpOpTell,
569 teleporterTcpOpSize,
570 teleporterTcpOpIsOk,
571 teleporterTcpOpClose,
572 SSMSTRMOPS_VERSION
573};
574
575
576/**
577 * Progress cancelation callback.
578 */
579static void teleporterProgressCancelCallback(void *pvUser)
580{
581 TeleporterState *pState = (TeleporterState *)pvUser;
582 SSMR3Cancel(pState->mpUVM);
583 if (!pState->mfIsSource)
584 {
585 TeleporterStateTrg *pStateTrg = (TeleporterStateTrg *)pState;
586 RTTcpServerShutdown(pStateTrg->mhServer);
587 }
588}
589
590/**
591 * @copydoc PFNVMPROGRESS
592 */
593static DECLCALLBACK(int) teleporterProgressCallback(PUVM pUVM, unsigned uPercent, void *pvUser)
594{
595 TeleporterState *pState = (TeleporterState *)pvUser;
596 if (pState->mptrProgress)
597 {
598 HRESULT hrc = pState->mptrProgress->SetCurrentOperationProgress(uPercent);
599 if (FAILED(hrc))
600 {
601 /* check if the failure was caused by cancellation. */
602 BOOL fCanceled;
603 hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
604 if (SUCCEEDED(hrc) && fCanceled)
605 {
606 SSMR3Cancel(pState->mpUVM);
607 return VERR_SSM_CANCELLED;
608 }
609 }
610 }
611
612 NOREF(pUVM);
613 return VINF_SUCCESS;
614}
615
616
617/**
618 * @copydoc FNRTTIMERLR
619 */
620static DECLCALLBACK(void) teleporterDstTimeout(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
621{
622 RT_NOREF(hTimerLR, iTick);
623 /* This is harmless for any open connections. */
624 RTTcpServerShutdown((PRTTCPSERVER)pvUser);
625}
626
627
628/**
629 * Do the teleporter.
630 *
631 * @returns VBox status code.
632 * @param pState The teleporter state.
633 */
634HRESULT Console::i_teleporterSrc(TeleporterStateSrc *pState)
635{
636 AutoCaller autoCaller(this);
637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
638
639 /*
640 * Wait for Console::Teleport to change the state.
641 */
642 { AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS); }
643
644 BOOL fCanceled = TRUE;
645 HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
646 if (FAILED(hrc))
647 return hrc;
648 if (fCanceled)
649 return setError(E_FAIL, tr("canceled"));
650
651 /*
652 * Try connect to the destination machine, disable Nagle.
653 * (Note. The caller cleans up mhSocket, so we can return without worries.)
654 */
655 int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket);
656 if (RT_FAILURE(vrc))
657 return setErrorBoth(E_FAIL, vrc, tr("Failed to connect to port %u on '%s': %Rrc"),
658 pState->muPort, pState->mstrHostname.c_str(), vrc);
659 vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
660 AssertRC(vrc);
661
662 /* Read and check the welcome message. */
663 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
664 RT_ZERO(szLine);
665 vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
666 if (RT_FAILURE(vrc))
667 return setErrorBoth(E_FAIL, vrc, tr("Failed to read welcome message: %Rrc"), vrc);
668 if (strcmp(szLine, g_szWelcome))
669 return setError(E_FAIL, tr("Unexpected welcome %.*Rhxs"), sizeof(g_szWelcome) - 1, szLine);
670
671 /* password */
672 pState->mstrPassword.append('\n');
673 vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length());
674 if (RT_FAILURE(vrc))
675 return setErrorBoth(E_FAIL, vrc, tr("Failed to send password: %Rrc"), vrc);
676
677 /* ACK */
678 hrc = i_teleporterSrcReadACK(pState, "password", tr("Invalid password"));
679 if (FAILED(hrc))
680 return hrc;
681
682 /*
683 * Start loading the state.
684 *
685 * Note! The saved state includes vital configuration data which will be
686 * verified against the VM config on the other end. This is all done
687 * in the first pass, so we should fail pretty promptly on misconfig.
688 */
689 hrc = i_teleporterSrcSubmitCommand(pState, "load");
690 if (FAILED(hrc))
691 return hrc;
692
693 RTSocketRetain(pState->mhSocket);
694 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
695 vrc = VMR3Teleport(pState->mpUVM,
696 pState->mcMsMaxDowntime,
697 &g_teleporterTcpOps, pvUser,
698 teleporterProgressCallback, pvUser,
699 &pState->mfSuspendedByUs);
700 RTSocketRelease(pState->mhSocket);
701 if (RT_FAILURE(vrc))
702 {
703 if ( vrc == VERR_SSM_CANCELLED
704 && RT_SUCCESS(RTTcpSelectOne(pState->mhSocket, 1)))
705 {
706 hrc = i_teleporterSrcReadACK(pState, "load-complete");
707 if (FAILED(hrc))
708 return hrc;
709 }
710 return setErrorBoth(E_FAIL, vrc, tr("VMR3Teleport -> %Rrc"), vrc);
711 }
712
713 hrc = i_teleporterSrcReadACK(pState, "load-complete");
714 if (FAILED(hrc))
715 return hrc;
716
717 /*
718 * We're at the point of no return.
719 */
720 if (!pState->mptrProgress->i_notifyPointOfNoReturn())
721 {
722 i_teleporterSrcSubmitCommand(pState, "cancel", false /*fWaitForAck*/);
723 return E_FAIL;
724 }
725
726 /*
727 * Hand over any media which we might be sharing.
728 *
729 * Note! This is only important on localhost teleportations.
730 */
731 /** @todo Maybe we should only do this if it's a local teleportation... */
732 hrc = mControl->UnlockMedia();
733 if (FAILED(hrc))
734 return hrc;
735 pState->mfUnlockedMedia = true;
736
737 hrc = i_teleporterSrcSubmitCommand(pState, "lock-media");
738 if (FAILED(hrc))
739 return hrc;
740
741 /*
742 * The FINAL step is giving the target instructions how to proceed with the VM.
743 */
744 if ( vrc == VINF_SSM_LIVE_SUSPENDED
745 || pState->menmOldMachineState == MachineState_Paused)
746 hrc = i_teleporterSrcSubmitCommand(pState, "hand-over-paused");
747 else
748 hrc = i_teleporterSrcSubmitCommand(pState, "hand-over-resume");
749 if (FAILED(hrc))
750 return hrc;
751
752 /*
753 * teleporterSrcThreadWrapper will do the automatic power off because it
754 * has to release the AutoVMCaller.
755 */
756 return S_OK;
757}
758
759
760/**
761 * Static thread method wrapper.
762 *
763 * @returns VINF_SUCCESS (ignored).
764 * @param hThreadSelf The thread.
765 * @param pvUser Pointer to a TeleporterStateSrc instance.
766 */
767/*static*/ DECLCALLBACK(int)
768Console::i_teleporterSrcThreadWrapper(RTTHREAD hThreadSelf, void *pvUser)
769{
770 RT_NOREF(hThreadSelf);
771 TeleporterStateSrc *pState = (TeleporterStateSrc *)pvUser;
772
773 /*
774 * Console::teleporterSrc does the work, we just grab onto the VM handle
775 * and do the cleanups afterwards.
776 */
777 SafeVMPtr ptrVM(pState->mptrConsole);
778 HRESULT hrc = ptrVM.rc();
779
780 if (SUCCEEDED(hrc))
781 hrc = pState->mptrConsole->i_teleporterSrc(pState);
782
783 /* Close the connection ASAP on so that the other side can complete. */
784 if (pState->mhSocket != NIL_RTSOCKET)
785 {
786 RTTcpClientClose(pState->mhSocket);
787 pState->mhSocket = NIL_RTSOCKET;
788 }
789
790 /* Aaarg! setMachineState trashes error info on Windows, so we have to
791 complete things here on failure instead of right before cleanup. */
792 if (FAILED(hrc))
793 pState->mptrProgress->i_notifyComplete(hrc);
794
795 /* We can no longer be canceled (success), or it doesn't matter any longer (failure). */
796 pState->mptrProgress->i_setCancelCallback(NULL, NULL);
797
798 /*
799 * Write lock the console before resetting mptrCancelableProgress and
800 * fixing the state.
801 */
802 AutoWriteLock autoLock(pState->mptrConsole COMMA_LOCKVAL_SRC_POS);
803 pState->mptrConsole->mptrCancelableProgress.setNull();
804
805 VMSTATE const enmVMState = VMR3GetStateU(pState->mpUVM);
806 MachineState_T const enmMachineState = pState->mptrConsole->mMachineState;
807 if (SUCCEEDED(hrc))
808 {
809 /*
810 * Automatically shut down the VM on success.
811 *
812 * Note! We have to release the VM caller object or we'll deadlock in
813 * powerDown.
814 */
815 AssertLogRelMsg(enmVMState == VMSTATE_SUSPENDED, ("%s\n", VMR3GetStateName(enmVMState)));
816 AssertLogRelMsg(enmMachineState == MachineState_TeleportingPausedVM,
817 ("%s\n", Global::stringifyMachineState(enmMachineState)));
818
819 ptrVM.release();
820
821 pState->mptrConsole->mVMIsAlreadyPoweringOff = true; /* (Make sure we stick in the TeleportingPausedVM state.) */
822 autoLock.release();
823
824 hrc = pState->mptrConsole->i_powerDown();
825
826 autoLock.acquire();
827 pState->mptrConsole->mVMIsAlreadyPoweringOff = false;
828
829 pState->mptrProgress->i_notifyComplete(hrc);
830 }
831 else
832 {
833 /*
834 * Work the state machinery on failure.
835 *
836 * If the state is no longer 'Teleporting*', some other operation has
837 * canceled us and there is nothing we need to do here. In all other
838 * cases, we've failed one way or another.
839 */
840 if ( enmMachineState == MachineState_Teleporting
841 || enmMachineState == MachineState_TeleportingPausedVM
842 )
843 {
844 if (pState->mfUnlockedMedia)
845 {
846 ErrorInfoKeeper Oak;
847 HRESULT hrc2 = pState->mptrConsole->mControl->LockMedia();
848 if (FAILED(hrc2))
849 {
850 uint64_t StartMS = RTTimeMilliTS();
851 do
852 {
853 RTThreadSleep(2);
854 hrc2 = pState->mptrConsole->mControl->LockMedia();
855 } while ( FAILED(hrc2)
856 && RTTimeMilliTS() - StartMS < 2000);
857 }
858 if (SUCCEEDED(hrc2))
859 pState->mfUnlockedMedia = true;
860 else
861 LogRel(("FATAL ERROR: Failed to re-take the media locks. hrc2=%Rhrc\n", hrc2));
862 }
863
864 switch (enmVMState)
865 {
866 case VMSTATE_RUNNING:
867 case VMSTATE_RUNNING_LS:
868 case VMSTATE_DEBUGGING:
869 case VMSTATE_DEBUGGING_LS:
870 case VMSTATE_POWERING_OFF:
871 case VMSTATE_POWERING_OFF_LS:
872 case VMSTATE_RESETTING:
873 case VMSTATE_RESETTING_LS:
874 case VMSTATE_SOFT_RESETTING:
875 case VMSTATE_SOFT_RESETTING_LS:
876 Assert(!pState->mfSuspendedByUs);
877 Assert(!pState->mfUnlockedMedia);
878 pState->mptrConsole->i_setMachineState(MachineState_Running);
879 break;
880
881 case VMSTATE_GURU_MEDITATION:
882 case VMSTATE_GURU_MEDITATION_LS:
883 pState->mptrConsole->i_setMachineState(MachineState_Stuck);
884 break;
885
886 case VMSTATE_FATAL_ERROR:
887 case VMSTATE_FATAL_ERROR_LS:
888 pState->mptrConsole->i_setMachineState(MachineState_Paused);
889 break;
890
891 default:
892 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
893 RT_FALL_THRU();
894 case VMSTATE_SUSPENDED:
895 case VMSTATE_SUSPENDED_LS:
896 case VMSTATE_SUSPENDING:
897 case VMSTATE_SUSPENDING_LS:
898 case VMSTATE_SUSPENDING_EXT_LS:
899 if (!pState->mfUnlockedMedia)
900 {
901 pState->mptrConsole->i_setMachineState(MachineState_Paused);
902 if (pState->mfSuspendedByUs)
903 {
904 autoLock.release();
905 int rc = VMR3Resume(pState->mpUVM, VMRESUMEREASON_TELEPORT_FAILED);
906 AssertLogRelMsgRC(rc, ("VMR3Resume -> %Rrc\n", rc));
907 autoLock.acquire();
908 }
909 }
910 else
911 {
912 /* Faking a guru meditation is the best I can think of doing here... */
913 pState->mptrConsole->i_setMachineState(MachineState_Stuck);
914 }
915 break;
916 }
917 }
918 }
919 autoLock.release();
920
921 /*
922 * Cleanup.
923 */
924 Assert(pState->mhSocket == NIL_RTSOCKET);
925 delete pState;
926
927 return VINF_SUCCESS; /* ignored */
928}
929
930
931/**
932 * Start teleporter to the specified target.
933 *
934 * @returns COM status code.
935 *
936 * @param aHostname The name of the target host.
937 * @param aTcpport The TCP port number.
938 * @param aPassword The password.
939 * @param aMaxDowntime Max allowed "downtime" in milliseconds.
940 * @param aProgress Where to return the progress object.
941 */
942HRESULT Console::teleport(const com::Utf8Str &aHostname, ULONG aTcpport, const com::Utf8Str &aPassword,
943 ULONG aMaxDowntime, ComPtr<IProgress> &aProgress)
944{
945 /*
946 * Validate parameters, check+hold object status, write lock the object
947 * and validate the state.
948 */
949 Utf8Str strPassword(aPassword);
950 if (!strPassword.isEmpty())
951 {
952 if (VBoxIsPasswordHashed(&strPassword))
953 return setError(E_INVALIDARG, tr("The specified password resembles a hashed password, expected plain text"));
954 VBoxHashPassword(&strPassword);
955 }
956
957 AutoCaller autoCaller(this);
958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
959
960 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
961 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
962
963 switch (mMachineState)
964 {
965 case MachineState_Running:
966 case MachineState_Paused:
967 break;
968
969 default:
970 return setError(VBOX_E_INVALID_VM_STATE, tr("Invalid machine state: %s (must be Running or Paused)"),
971 Global::stringifyMachineState(mMachineState));
972 }
973
974
975 /*
976 * Create a progress object, spawn a worker thread and change the state.
977 * Note! The thread won't start working until we release the lock.
978 */
979 LogFlowThisFunc(("Initiating TELEPORT request...\n"));
980
981 ComObjPtr<Progress> ptrProgress;
982 HRESULT hrc = ptrProgress.createObject();
983 if (SUCCEEDED(hrc))
984 hrc = ptrProgress->init(static_cast<IConsole *>(this),
985 Bstr(tr("Teleporter")).raw(),
986 TRUE /*aCancelable*/);
987 if (FAILED(hrc))
988 return hrc;
989
990 TeleporterStateSrc *pState = new TeleporterStateSrc(this, mpUVM, ptrProgress, mMachineState);
991 pState->mstrPassword = strPassword;
992 pState->mstrHostname = aHostname;
993 pState->muPort = aTcpport;
994 pState->mcMsMaxDowntime = aMaxDowntime;
995
996 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
997 ptrProgress->i_setCancelCallback(teleporterProgressCancelCallback, pvUser);
998
999 int vrc = RTThreadCreate(NULL, Console::i_teleporterSrcThreadWrapper, (void *)pState, 0 /*cbStack*/,
1000 RTTHREADTYPE_EMULATION, 0 /*fFlags*/, "Teleport");
1001 if (RT_SUCCESS(vrc))
1002 {
1003 if (mMachineState == MachineState_Running)
1004 hrc = i_setMachineState(MachineState_Teleporting);
1005 else
1006 hrc = i_setMachineState(MachineState_TeleportingPausedVM);
1007 if (SUCCEEDED(hrc))
1008 {
1009 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
1010 mptrCancelableProgress = aProgress;
1011 }
1012 else
1013 ptrProgress->Cancel();
1014 }
1015 else
1016 {
1017 ptrProgress->i_setCancelCallback(NULL, NULL);
1018 delete pState;
1019 hrc = setErrorBoth(E_FAIL, vrc, tr("RTThreadCreate -> %Rrc"), vrc);
1020 }
1021
1022 return hrc;
1023}
1024
1025
1026/**
1027 * Creates a TCP server that listens for the source machine and passes control
1028 * over to Console::teleporterTrgServeConnection().
1029 *
1030 * @returns VBox status code.
1031 * @param pUVM The user-mode VM handle
1032 * @param pMachine The IMachine for the virtual machine.
1033 * @param pErrorMsg Pointer to the error string for VMSetError.
1034 * @param fStartPaused Whether to start it in the Paused (true) or
1035 * Running (false) state,
1036 * @param pProgress Pointer to the progress object.
1037 * @param pfPowerOffOnFailure Whether the caller should power off
1038 * the VM on failure.
1039 *
1040 * @remarks The caller expects error information to be set on failure.
1041 * @todo Check that all the possible failure paths sets error info...
1042 */
1043HRESULT Console::i_teleporterTrg(PUVM pUVM, IMachine *pMachine, Utf8Str *pErrorMsg, bool fStartPaused,
1044 Progress *pProgress, bool *pfPowerOffOnFailure)
1045{
1046 LogThisFunc(("pUVM=%p pMachine=%p fStartPaused=%RTbool pProgress=%p\n", pUVM, pMachine, fStartPaused, pProgress));
1047
1048 *pfPowerOffOnFailure = true;
1049
1050 /*
1051 * Get the config.
1052 */
1053 ULONG uPort;
1054 HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
1055 if (FAILED(hrc))
1056 return hrc;
1057 ULONG const uPortOrg = uPort;
1058
1059 Bstr bstrAddress;
1060 hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
1061 if (FAILED(hrc))
1062 return hrc;
1063 Utf8Str strAddress(bstrAddress);
1064 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
1065
1066 Bstr bstrPassword;
1067 hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
1068 if (FAILED(hrc))
1069 return hrc;
1070 Utf8Str strPassword(bstrPassword);
1071 strPassword.append('\n'); /* To simplify password checking. */
1072
1073 /*
1074 * Create the TCP server.
1075 */
1076 int vrc = VINF_SUCCESS; /* Shut up MSC */
1077 PRTTCPSERVER hServer = NULL; /* ditto */
1078 if (uPort)
1079 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1080 else
1081 {
1082 for (int cTries = 10240; cTries > 0; cTries--)
1083 {
1084 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
1085 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1086 if (vrc != VERR_NET_ADDRESS_IN_USE)
1087 break;
1088 }
1089 if (RT_SUCCESS(vrc))
1090 {
1091 hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
1092 if (FAILED(hrc))
1093 {
1094 RTTcpServerDestroy(hServer);
1095 return hrc;
1096 }
1097 }
1098 }
1099 if (RT_FAILURE(vrc))
1100 return setErrorBoth(E_FAIL, vrc, tr("RTTcpServerCreateEx failed with status %Rrc"), vrc);
1101
1102 /*
1103 * Create a one-shot timer for timing out after 5 mins.
1104 */
1105 RTTIMERLR hTimerLR;
1106 vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterDstTimeout, hServer);
1107 if (RT_SUCCESS(vrc))
1108 {
1109 vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
1110 if (RT_SUCCESS(vrc))
1111 {
1112 /*
1113 * Do the job, when it returns we're done.
1114 */
1115 TeleporterStateTrg theState(this, pUVM, pProgress, pMachine, mControl, &hTimerLR, fStartPaused);
1116 theState.mstrPassword = strPassword;
1117 theState.mhServer = hServer;
1118
1119 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(&theState));
1120 if (pProgress->i_setCancelCallback(teleporterProgressCancelCallback, pvUser))
1121 {
1122 LogRel(("Teleporter: Waiting for incoming VM...\n"));
1123 hrc = pProgress->SetNextOperation(Bstr(tr("Waiting for incoming VM")).raw(), 1);
1124 if (SUCCEEDED(hrc))
1125 {
1126 vrc = RTTcpServerListen(hServer, Console::i_teleporterTrgServeConnection, &theState);
1127 pProgress->i_setCancelCallback(NULL, NULL);
1128
1129 if (vrc == VERR_TCP_SERVER_STOP)
1130 {
1131 vrc = theState.mRc;
1132 /* Power off the VM on failure unless the state callback
1133 already did that. */
1134 *pfPowerOffOnFailure = false;
1135 if (RT_SUCCESS(vrc))
1136 hrc = S_OK;
1137 else
1138 {
1139 VMSTATE enmVMState = VMR3GetStateU(pUVM);
1140 if ( enmVMState != VMSTATE_OFF
1141 && enmVMState != VMSTATE_POWERING_OFF)
1142 *pfPowerOffOnFailure = true;
1143
1144 /* Set error. */
1145 if (pErrorMsg->length())
1146 hrc = setError(E_FAIL, "%s", pErrorMsg->c_str());
1147 else
1148 hrc = setError(E_FAIL, tr("Teleporation failed (%Rrc)"), vrc);
1149 }
1150 }
1151 else if (vrc == VERR_TCP_SERVER_SHUTDOWN)
1152 {
1153 BOOL fCanceled = TRUE;
1154 hrc = pProgress->COMGETTER(Canceled)(&fCanceled);
1155 if (FAILED(hrc) || fCanceled)
1156 hrc = setError(E_FAIL, tr("Teleporting canceled"));
1157 else
1158 hrc = setError(E_FAIL, tr("Teleporter timed out waiting for incoming connection"));
1159 LogRel(("Teleporter: RTTcpServerListen aborted - %Rrc\n", vrc));
1160 }
1161 else
1162 {
1163 hrc = setErrorBoth(E_FAIL, vrc, tr("Unexpected RTTcpServerListen status code %Rrc"), vrc);
1164 LogRel(("Teleporter: Unexpected RTTcpServerListen rc: %Rrc\n", vrc));
1165 }
1166 }
1167 else
1168 LogThisFunc(("SetNextOperation failed, %Rhrc\n", hrc));
1169 }
1170 else
1171 {
1172 LogThisFunc(("Canceled - check point #1\n"));
1173 hrc = setError(E_FAIL, tr("Teleporting canceled"));
1174 }
1175 }
1176 else
1177 hrc = setErrorBoth(E_FAIL, vrc, "RTTimerLRStart -> %Rrc", vrc);
1178
1179 RTTimerLRDestroy(hTimerLR);
1180 }
1181 else
1182 hrc = setErrorBoth(E_FAIL, vrc, "RTTimerLRCreate -> %Rrc", vrc);
1183 RTTcpServerDestroy(hServer);
1184
1185 /*
1186 * If we change TeleporterPort above, set it back to it's original
1187 * value before returning.
1188 */
1189 if (uPortOrg != uPort)
1190 {
1191 ErrorInfoKeeper Eik;
1192 pMachine->COMSETTER(TeleporterPort)(uPortOrg);
1193 }
1194
1195 return hrc;
1196}
1197
1198
1199/**
1200 * Unlock the media.
1201 *
1202 * This is used in error paths.
1203 *
1204 * @param pState The teleporter state.
1205 */
1206static void teleporterTrgUnlockMedia(TeleporterStateTrg *pState)
1207{
1208 if (pState->mfLockedMedia)
1209 {
1210 pState->mpControl->UnlockMedia();
1211 pState->mfLockedMedia = false;
1212 }
1213}
1214
1215
1216static int teleporterTcpWriteACK(TeleporterStateTrg *pState, bool fAutomaticUnlock = true)
1217{
1218 int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1);
1219 if (RT_FAILURE(rc))
1220 {
1221 LogRel(("Teleporter: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
1222 if (fAutomaticUnlock)
1223 teleporterTrgUnlockMedia(pState);
1224 }
1225 return rc;
1226}
1227
1228
1229static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2, const char *pszMsgText = NULL)
1230{
1231 /*
1232 * Unlock media sending the NACK. That way the other doesn't have to spin
1233 * waiting to regain the locks.
1234 */
1235 teleporterTrgUnlockMedia(pState);
1236
1237 char szMsg[256];
1238 size_t cch;
1239 if (pszMsgText && *pszMsgText)
1240 {
1241 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
1242 for (size_t off = 6; off + 1 < cch; off++)
1243 if (szMsg[off] == '\n')
1244 szMsg[off] = '\r';
1245 }
1246 else
1247 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
1248 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
1249 if (RT_FAILURE(rc))
1250 LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
1251 return rc;
1252}
1253
1254
1255/**
1256 * @copydoc FNRTTCPSERVE
1257 *
1258 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
1259 */
1260/*static*/ DECLCALLBACK(int)
1261Console::i_teleporterTrgServeConnection(RTSOCKET hSocket, void *pvUser)
1262{
1263 TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
1264 pState->mhSocket = hSocket;
1265
1266 /*
1267 * Disable Nagle and say hello.
1268 */
1269 int vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
1270 AssertRC(vrc);
1271 vrc = RTTcpWrite(hSocket, g_szWelcome, sizeof(g_szWelcome) - 1);
1272 if (RT_FAILURE(vrc))
1273 {
1274 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc));
1275 return VINF_SUCCESS;
1276 }
1277
1278 /*
1279 * Password (includes '\n', see teleporterTrg).
1280 */
1281 const char *pszPassword = pState->mstrPassword.c_str();
1282 unsigned off = 0;
1283 while (pszPassword[off])
1284 {
1285 char ch;
1286 vrc = RTTcpRead(hSocket, &ch, sizeof(ch), NULL);
1287 if ( RT_FAILURE(vrc)
1288 || pszPassword[off] != ch)
1289 {
1290 if (RT_FAILURE(vrc))
1291 LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc));
1292 else
1293 LogRel(("Teleporter: Invalid password (off=%u)\n", off));
1294 teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
1295 return VINF_SUCCESS;
1296 }
1297 off++;
1298 }
1299 vrc = teleporterTcpWriteACK(pState);
1300 if (RT_FAILURE(vrc))
1301 return VINF_SUCCESS;
1302
1303 /*
1304 * Update the progress bar, with peer name if available.
1305 */
1306 HRESULT hrc;
1307 RTNETADDR Addr;
1308 vrc = RTTcpGetPeerAddress(hSocket, &Addr);
1309 if (RT_SUCCESS(vrc))
1310 {
1311 LogRel(("Teleporter: Incoming VM from %RTnaddr!\n", &Addr));
1312 hrc = pState->mptrProgress->SetNextOperation(BstrFmt(tr("Teleporting VM from %RTnaddr"), &Addr).raw(), 8);
1313 }
1314 else
1315 {
1316 LogRel(("Teleporter: Incoming VM!\n"));
1317 hrc = pState->mptrProgress->SetNextOperation(Bstr(tr("Teleporting VM")).raw(), 8);
1318 }
1319 AssertMsg(SUCCEEDED(hrc) || hrc == E_FAIL, ("%Rhrc\n", hrc));
1320
1321 /*
1322 * Stop the server and cancel the timeout timer.
1323 *
1324 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
1325 * to it we must not return that value!
1326 */
1327 RTTcpServerShutdown(pState->mhServer);
1328 RTTimerLRDestroy(*pState->mphTimerLR);
1329 *pState->mphTimerLR = NIL_RTTIMERLR;
1330
1331 /*
1332 * Command processing loop.
1333 */
1334 bool fDone = false;
1335 for (;;)
1336 {
1337 char szCmd[128];
1338 vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd));
1339 if (RT_FAILURE(vrc))
1340 break;
1341
1342 if (!strcmp(szCmd, "load"))
1343 {
1344 vrc = teleporterTcpWriteACK(pState);
1345 if (RT_FAILURE(vrc))
1346 break;
1347
1348 int vrc2 = VMR3AtErrorRegister(pState->mpUVM,
1349 Console::i_genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
1350 RTSocketRetain(pState->mhSocket); /* For concurrent access by I/O thread and EMT. */
1351 pState->moffStream = 0;
1352
1353 void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState));
1354 vrc = VMR3LoadFromStream(pState->mpUVM,
1355 &g_teleporterTcpOps, pvUser2,
1356 teleporterProgressCallback, pvUser2);
1357
1358 RTSocketRelease(pState->mhSocket);
1359 vrc2 = VMR3AtErrorDeregister(pState->mpUVM, Console::i_genericVMSetErrorCallback, &pState->mErrorText);
1360 AssertRC(vrc2);
1361
1362 if (RT_FAILURE(vrc))
1363 {
1364 LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc));
1365 teleporterTcpWriteNACK(pState, vrc, pState->mErrorText.c_str());
1366 break;
1367 }
1368
1369 /* The EOS might not have been read, make sure it is. */
1370 pState->mfStopReading = false;
1371 size_t cbRead;
1372 vrc = teleporterTcpOpRead(pvUser2, pState->moffStream, szCmd, 1, &cbRead);
1373 if (vrc != VERR_EOF)
1374 {
1375 LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc));
1376 teleporterTcpWriteNACK(pState, vrc);
1377 break;
1378 }
1379
1380 vrc = teleporterTcpWriteACK(pState);
1381 }
1382 else if (!strcmp(szCmd, "cancel"))
1383 {
1384 /* Don't ACK this. */
1385 LogRel(("Teleporter: Received cancel command.\n"));
1386 vrc = VERR_SSM_CANCELLED;
1387 }
1388 else if (!strcmp(szCmd, "lock-media"))
1389 {
1390 hrc = pState->mpControl->LockMedia();
1391 if (SUCCEEDED(hrc))
1392 {
1393 pState->mfLockedMedia = true;
1394 vrc = teleporterTcpWriteACK(pState);
1395 }
1396 else
1397 {
1398 vrc = VERR_FILE_LOCK_FAILED;
1399 teleporterTcpWriteNACK(pState, vrc);
1400 }
1401 }
1402 else if ( !strcmp(szCmd, "hand-over-resume")
1403 || !strcmp(szCmd, "hand-over-paused"))
1404 {
1405 /*
1406 * Point of no return.
1407 *
1408 * Note! Since we cannot tell whether a VMR3Resume failure is
1409 * destructive for the source or not, we have little choice
1410 * but to ACK it first and take any failures locally.
1411 *
1412 * Ideally, we should try resume it first and then ACK (or
1413 * NACK) the request since this would reduce latency and
1414 * make it possible to recover from some VMR3Resume failures.
1415 */
1416 if ( pState->mptrProgress->i_notifyPointOfNoReturn()
1417 && pState->mfLockedMedia)
1418 {
1419 vrc = teleporterTcpWriteACK(pState);
1420 if (RT_SUCCESS(vrc))
1421 {
1422 if (!strcmp(szCmd, "hand-over-resume"))
1423 vrc = VMR3Resume(pState->mpUVM, VMRESUMEREASON_TELEPORTED);
1424 else
1425 pState->mptrConsole->i_setMachineState(MachineState_Paused);
1426 fDone = true;
1427 break;
1428 }
1429 }
1430 else
1431 {
1432 vrc = pState->mfLockedMedia ? VERR_WRONG_ORDER : VERR_SSM_CANCELLED;
1433 teleporterTcpWriteNACK(pState, vrc);
1434 }
1435 }
1436 else
1437 {
1438 LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd));
1439 vrc = VERR_NOT_IMPLEMENTED;
1440 teleporterTcpWriteNACK(pState, vrc);
1441 }
1442
1443 if (RT_FAILURE(vrc))
1444 break;
1445 }
1446
1447 if (RT_SUCCESS(vrc) && !fDone)
1448 vrc = VERR_WRONG_ORDER;
1449 if (RT_FAILURE(vrc))
1450 teleporterTrgUnlockMedia(pState);
1451
1452 pState->mRc = vrc;
1453 pState->mhSocket = NIL_RTSOCKET;
1454 LogFlowFunc(("returns mRc=%Rrc\n", vrc));
1455 return VERR_TCP_SERVER_STOP;
1456}
1457
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use