VirtualBox

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

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

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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