VirtualBox

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

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

Main: Bstr makeover -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull() usage

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.0 KB
Line 
1/* $Id: ConsoleImplTeleporter.cpp 26550 2010-02-15 17:14:18Z 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 Console::Teleport(IN_BSTR aHostname,
897 ULONG aPort,
898 IN_BSTR aPassword,
899 ULONG aMaxDowntime,
900 IProgress **aProgress)
901{
902 /*
903 * Validate parameters, check+hold object status, write lock the object
904 * and validate the state.
905 */
906 CheckComArgOutPointerValid(aProgress);
907 CheckComArgStrNotEmptyOrNull(aHostname);
908 CheckComArgExprMsg(aPort, aPort > 0 && aPort <= 65535, ("is %u", aPort));
909 CheckComArgExprMsg(aMaxDowntime, aMaxDowntime > 0, ("is %u", aMaxDowntime));
910
911 AutoCaller autoCaller(this);
912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
913
914 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
915 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
916
917 switch (mMachineState)
918 {
919 case MachineState_Running:
920 case MachineState_Paused:
921 break;
922
923 default:
924 return setError(VBOX_E_INVALID_VM_STATE,
925 tr("Invalid machine state: %s (must be Running or Paused)"),
926 Global::stringifyMachineState(mMachineState));
927 }
928
929
930 /*
931 * Create a progress object, spawn a worker thread and change the state.
932 * Note! The thread won't start working until we release the lock.
933 */
934 LogFlowThisFunc(("Initiating TELEPORT request...\n"));
935
936 ComObjPtr<Progress> ptrProgress;
937 HRESULT hrc = ptrProgress.createObject();
938 if (FAILED(hrc)) return hrc;
939 hrc = ptrProgress->init(static_cast<IConsole *>(this), Bstr(tr("Teleporter")), TRUE /*aCancelable*/);
940 if (FAILED(hrc)) return hrc;
941
942 TeleporterStateSrc *pState = new TeleporterStateSrc(this, mpVM, ptrProgress, mMachineState);
943 pState->mstrPassword = aPassword;
944 pState->mstrHostname = aHostname;
945 pState->muPort = aPort;
946 pState->mcMsMaxDowntime = aMaxDowntime;
947
948 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
949 ptrProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser);
950
951 int vrc = RTThreadCreate(NULL, Console::teleporterSrcThreadWrapper, (void *)pState, 0 /*cbStack*/,
952 RTTHREADTYPE_EMULATION, 0 /*fFlags*/, "Teleport");
953 if (RT_SUCCESS(vrc))
954 {
955 if (mMachineState == MachineState_Running)
956 hrc = setMachineState(MachineState_Teleporting);
957 else
958 hrc = setMachineState(MachineState_TeleportingPausedVM);
959 if (SUCCEEDED(hrc))
960 {
961 ptrProgress.queryInterfaceTo(aProgress);
962 mptrCancelableProgress = ptrProgress;
963 }
964 else
965 ptrProgress->Cancel();
966 }
967 else
968 {
969 ptrProgress->setCancelCallback(NULL, NULL);
970 delete pState;
971 hrc = setError(E_FAIL, tr("RTThreadCreate -> %Rrc"), vrc);
972 }
973
974 return hrc;
975}
976
977
978/**
979 * Creates a TCP server that listens for the source machine and passes control
980 * over to Console::teleporterTrgServeConnection().
981 *
982 * @returns VBox status code.
983 * @param pVM The VM handle
984 * @param pMachine The IMachine for the virtual machine.
985 * @param fStartPaused Whether to start it in the Paused (true) or
986 * Running (false) state,
987 * @param pProgress Pointer to the progress object.
988 *
989 * @remarks The caller expects error information to be set on failure.
990 * @todo Check that all the possible failure paths sets error info...
991 */
992int
993Console::teleporterTrg(PVM pVM, IMachine *pMachine, bool fStartPaused, Progress *pProgress)
994{
995 /*
996 * Get the config.
997 */
998 ULONG uPort;
999 HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
1000 if (FAILED(hrc))
1001 return VERR_GENERAL_FAILURE;
1002 ULONG const uPortOrg = uPort;
1003
1004 Bstr bstrAddress;
1005 hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
1006 if (FAILED(hrc))
1007 return VERR_GENERAL_FAILURE;
1008 Utf8Str strAddress(bstrAddress);
1009 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
1010
1011 Bstr bstrPassword;
1012 hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
1013 if (FAILED(hrc))
1014 return VERR_GENERAL_FAILURE;
1015 Utf8Str strPassword(bstrPassword);
1016 strPassword.append('\n'); /* To simplify password checking. */
1017
1018 /*
1019 * Create the TCP server.
1020 */
1021 int vrc;
1022 PRTTCPSERVER hServer;
1023 if (uPort)
1024 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1025 else
1026 {
1027 for (int cTries = 10240; cTries > 0; cTries--)
1028 {
1029 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
1030 vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
1031 if (vrc != VERR_NET_ADDRESS_IN_USE)
1032 break;
1033 }
1034 if (RT_SUCCESS(vrc))
1035 {
1036 hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
1037 if (FAILED(hrc))
1038 {
1039 RTTcpServerDestroy(hServer);
1040 return VERR_GENERAL_FAILURE;
1041 }
1042 }
1043 }
1044 if (RT_FAILURE(vrc))
1045 return vrc;
1046
1047 /*
1048 * Create a one-shot timer for timing out after 5 mins.
1049 */
1050 RTTIMERLR hTimerLR;
1051 vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterDstTimeout, hServer);
1052 if (RT_SUCCESS(vrc))
1053 {
1054 vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
1055 if (RT_SUCCESS(vrc))
1056 {
1057 /*
1058 * Do the job, when it returns we're done.
1059 */
1060 TeleporterStateTrg theState(this, pVM, pProgress, pMachine, mControl, &hTimerLR, fStartPaused);
1061 theState.mstrPassword = strPassword;
1062 theState.mhServer = hServer;
1063
1064 void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(&theState));
1065 if (pProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser))
1066 {
1067 LogRel(("Teleporter: Waiting for incoming VM...\n"));
1068 vrc = RTTcpServerListen(hServer, Console::teleporterTrgServeConnection, &theState);
1069 pProgress->setCancelCallback(NULL, NULL);
1070
1071 bool fPowerOff = false;
1072 if (vrc == VERR_TCP_SERVER_STOP)
1073 {
1074 vrc = theState.mRc;
1075 /* Power off the VM on failure unless the state callback
1076 already did that. */
1077 if (RT_FAILURE(vrc))
1078 {
1079 VMSTATE enmVMState = VMR3GetState(pVM);
1080 if ( enmVMState != VMSTATE_OFF
1081 && enmVMState != VMSTATE_POWERING_OFF)
1082 fPowerOff = true;
1083 }
1084 }
1085 else if (vrc == VERR_TCP_SERVER_SHUTDOWN)
1086 {
1087 BOOL fCancelled = TRUE;
1088 hrc = pProgress->COMGETTER(Canceled)(&fCancelled);
1089 if (FAILED(hrc) || fCancelled)
1090 {
1091 setError(E_FAIL, tr("Teleporting canceled"));
1092 vrc = VERR_SSM_CANCELLED;
1093 }
1094 else
1095 {
1096 setError(E_FAIL, tr("Teleporter timed out waiting for incoming connection"));
1097 vrc = VERR_TIMEOUT;
1098 }
1099 LogRel(("Teleporter: RTTcpServerListen aborted - %Rrc\n", vrc));
1100 fPowerOff = true;
1101 }
1102 else
1103 {
1104 LogRel(("Teleporter: Unexpected RTTcpServerListen rc: %Rrc\n", vrc));
1105 vrc = VERR_IPE_UNEXPECTED_STATUS;
1106 fPowerOff = true;
1107 }
1108
1109 if (fPowerOff)
1110 {
1111 int vrc2 = VMR3PowerOff(pVM);
1112 AssertRC(vrc2);
1113 }
1114 }
1115 else
1116 vrc = VERR_SSM_CANCELLED;
1117 }
1118
1119 RTTimerLRDestroy(hTimerLR);
1120 }
1121 RTTcpServerDestroy(hServer);
1122
1123 /*
1124 * If we change TeleporterPort above, set it back to it's original
1125 * value before returning.
1126 */
1127 if (uPortOrg != uPort)
1128 pMachine->COMSETTER(TeleporterPort)(uPortOrg);
1129
1130 return vrc;
1131}
1132
1133
1134/**
1135 * Unlock the media.
1136 *
1137 * This is used in error paths.
1138 *
1139 * @param pState The teleporter state.
1140 */
1141static void teleporterTrgUnlockMedia(TeleporterStateTrg *pState)
1142{
1143 if (pState->mfLockedMedia)
1144 {
1145 pState->mpControl->UnlockMedia();
1146 pState->mfLockedMedia = false;
1147 }
1148}
1149
1150
1151static int teleporterTcpWriteACK(TeleporterStateTrg *pState, bool fAutomaticUnlock = true)
1152{
1153 int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1);
1154 if (RT_FAILURE(rc))
1155 {
1156 LogRel(("Teleporter: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
1157 if (fAutomaticUnlock)
1158 teleporterTrgUnlockMedia(pState);
1159 }
1160 RTTcpFlush(pState->mhSocket);
1161 return rc;
1162}
1163
1164
1165static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2)
1166{
1167 /*
1168 * Unlock media sending the NACK. That way the other doesn't have to spin
1169 * waiting to regain the locks.
1170 */
1171 teleporterTrgUnlockMedia(pState);
1172
1173 char szMsg[64];
1174 size_t cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
1175 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
1176 if (RT_FAILURE(rc))
1177 LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
1178 RTTcpFlush(pState->mhSocket);
1179 return rc;
1180}
1181
1182
1183/**
1184 * @copydoc FNRTTCPSERVE
1185 *
1186 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
1187 */
1188/*static*/ DECLCALLBACK(int)
1189Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
1190{
1191 TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser;
1192 pState->mhSocket = Sock;
1193
1194 /*
1195 * Say hello.
1196 */
1197 int vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
1198 if (RT_FAILURE(vrc))
1199 {
1200 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc));
1201 return VINF_SUCCESS;
1202 }
1203
1204 /*
1205 * Password (includes '\n', see teleporterTrg).
1206 */
1207 const char *pszPassword = pState->mstrPassword.c_str();
1208 unsigned off = 0;
1209 while (pszPassword[off])
1210 {
1211 char ch;
1212 vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
1213 if ( RT_FAILURE(vrc)
1214 || pszPassword[off] != ch)
1215 {
1216 if (RT_FAILURE(vrc))
1217 LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc));
1218 else
1219 LogRel(("Teleporter: Invalid password (off=%u)\n", off));
1220 teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
1221 return VINF_SUCCESS;
1222 }
1223 off++;
1224 }
1225 vrc = teleporterTcpWriteACK(pState);
1226 if (RT_FAILURE(vrc))
1227 return VINF_SUCCESS;
1228 LogRel(("Teleporter: Incoming VM!\n"));
1229
1230 /*
1231 * Stop the server and cancel the timeout timer.
1232 *
1233 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
1234 * to it we must not return that value!
1235 */
1236 RTTcpServerShutdown(pState->mhServer);
1237 RTTimerLRDestroy(*pState->mphTimerLR);
1238 *pState->mphTimerLR = NIL_RTTIMERLR;
1239
1240 /*
1241 * Command processing loop.
1242 */
1243 bool fDone = false;
1244 for (;;)
1245 {
1246 char szCmd[128];
1247 vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd));
1248 if (RT_FAILURE(vrc))
1249 break;
1250
1251 if (!strcmp(szCmd, "load"))
1252 {
1253 vrc = teleporterTcpWriteACK(pState);
1254 if (RT_FAILURE(vrc))
1255 break;
1256
1257 pState->moffStream = 0;
1258 void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState));
1259 vrc = VMR3LoadFromStream(pState->mpVM, &g_teleporterTcpOps, pvUser2,
1260 teleporterProgressCallback, pvUser2);
1261 if (RT_FAILURE(vrc))
1262 {
1263 LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc));
1264 teleporterTcpWriteNACK(pState, vrc);
1265 break;
1266 }
1267
1268 /* The EOS might not have been read, make sure it is. */
1269 pState->mfStopReading = false;
1270 size_t cbRead;
1271 vrc = teleporterTcpOpRead(pvUser2, pState->moffStream, szCmd, 1, &cbRead);
1272 if (vrc != VERR_EOF)
1273 {
1274 LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc));
1275 teleporterTcpWriteNACK(pState, vrc);
1276 break;
1277 }
1278
1279 vrc = teleporterTcpWriteACK(pState);
1280 }
1281 else if (!strcmp(szCmd, "cancel"))
1282 {
1283 /* Don't ACK this. */
1284 LogRel(("Teleporter: Received cancel command.\n"));
1285 vrc = VERR_SSM_CANCELLED;
1286 }
1287 else if (!strcmp(szCmd, "lock-media"))
1288 {
1289 HRESULT hrc = pState->mpControl->LockMedia();
1290 if (SUCCEEDED(hrc))
1291 {
1292 pState->mfLockedMedia = true;
1293 vrc = teleporterTcpWriteACK(pState);
1294 }
1295 else
1296 {
1297 vrc = VERR_FILE_LOCK_FAILED;
1298 teleporterTcpWriteNACK(pState, vrc);
1299 }
1300 }
1301 else if ( !strcmp(szCmd, "hand-over-resume")
1302 || !strcmp(szCmd, "hand-over-paused"))
1303 {
1304 /*
1305 * Point of no return.
1306 *
1307 * Note! Since we cannot tell whether a VMR3Resume failure is
1308 * destructive for the source or not, we have little choice
1309 * but to ACK it first and take any failures locally.
1310 *
1311 * Ideally, we should try resume it first and then ACK (or
1312 * NACK) the request since this would reduce latency and
1313 * make it possible to recover from some VMR3Resume failures.
1314 */
1315 if ( pState->mptrProgress->notifyPointOfNoReturn()
1316 && pState->mfLockedMedia)
1317 {
1318 vrc = teleporterTcpWriteACK(pState);
1319 if (RT_SUCCESS(vrc))
1320 {
1321 if (!strcmp(szCmd, "hand-over-resume"))
1322 vrc = VMR3Resume(pState->mpVM);
1323 else
1324 pState->mptrConsole->setMachineState(MachineState_Paused);
1325 fDone = true;
1326 break;
1327 }
1328 }
1329 else
1330 {
1331 vrc = pState->mfLockedMedia ? VERR_WRONG_ORDER : VERR_SSM_CANCELLED;
1332 teleporterTcpWriteNACK(pState, vrc);
1333 }
1334 }
1335 else
1336 {
1337 LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd));
1338 vrc = VERR_NOT_IMPLEMENTED;
1339 teleporterTcpWriteNACK(pState, vrc);
1340 }
1341
1342 if (RT_FAILURE(vrc))
1343 break;
1344 }
1345
1346 if (RT_SUCCESS(vrc) && !fDone)
1347 vrc = VERR_WRONG_ORDER;
1348 if (RT_FAILURE(vrc))
1349 teleporterTrgUnlockMedia(pState);
1350
1351 pState->mRc = vrc;
1352 pState->mhSocket = NIL_RTSOCKET;
1353 LogFlowFunc(("returns mRc=%Rrc\n", vrc));
1354 return VERR_TCP_SERVER_STOP;
1355}
1356
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