VirtualBox

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

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

ConsoleImplTeleporter.cpp: Retain the socket before letting SSM have a go at it with two thread (EMT + I/O).

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