VirtualBox

source: vbox/trunk/src/VBox/Main/ConsoleImpl-LiveMigration.cpp@ 23702

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

Main/LiveMigration: Resume the source VM if the migration failed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.5 KB
Line 
1/* $Id: ConsoleImpl-LiveMigration.cpp 23698 2009-10-12 14:07:52Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation, The Live Migration Part.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include "ConsoleImpl.h"
27#include "Global.h"
28#include "Logging.h"
29#include "ProgressImpl.h"
30
31#include <iprt/err.h>
32#include <iprt/rand.h>
33#include <iprt/tcp.h>
34#include <iprt/timer.h>
35
36#include <VBox/vmapi.h>
37#include <VBox/ssm.h>
38#include <VBox/err.h>
39#include <VBox/version.h>
40#include <VBox/com/string.h>
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/**
47 * Base class for the migration state.
48 *
49 * These classes are used as advanced structs, not as proper classes.
50 */
51class MigrationState
52{
53public:
54 ComPtr<Console> mptrConsole;
55 PVM mpVM;
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 MigrationState(Console *pConsole, PVM pVM, bool fIsSource)
70 : mptrConsole(pConsole)
71 , mpVM(pVM)
72 , mfIsSource(fIsSource)
73 , mhSocket(NIL_RTSOCKET)
74 , moffStream(UINT64_MAX / 2)
75 , mcbReadBlock(0)
76 , mfStopReading(false)
77 , mfEndOfStream(false)
78 , mfIOError(false)
79 {
80 }
81};
82
83
84/**
85 * Migration state used by the source side.
86 */
87class MigrationStateSrc : public MigrationState
88{
89public:
90 ComPtr<Progress> mptrProgress;
91 Utf8Str mstrHostname;
92 uint32_t muPort;
93
94 MigrationStateSrc(Console *pConsole, PVM pVM)
95 : MigrationState(pConsole, pVM, true /*fIsSource*/)
96 , muPort(UINT32_MAX)
97 {
98 }
99};
100
101
102/**
103 * Migration state used by the destiation side.
104 */
105class MigrationStateDst : public MigrationState
106{
107public:
108 IMachine *mpMachine;
109 void *mpvVMCallbackTask;
110 PRTTCPSERVER mhServer;
111 PRTTIMERLR mphTimerLR;
112 int mRc;
113
114 MigrationStateDst(Console *pConsole, PVM pVM, IMachine *pMachine, PRTTIMERLR phTimerLR)
115 : MigrationState(pConsole, pVM, false /*fIsSource*/)
116 , mpMachine(pMachine)
117 , mpvVMCallbackTask(NULL)
118 , mhServer(NULL)
119 , mphTimerLR(phTimerLR)
120 , mRc(VINF_SUCCESS)
121 {
122 }
123};
124
125
126/**
127 * TCP stream header.
128 *
129 * This is an extra layer for fixing the problem with figuring out when the SSM
130 * stream ends.
131 */
132typedef struct MIGRATIONTCPHDR
133{
134 /** Magic value. */
135 uint32_t u32Magic;
136 /** The size of the data block following this header.
137 * 0 indicates the end of the stream. */
138 uint32_t cb;
139} MIGRATIONTCPHDR;
140/** Magic value for MIGRATIONTCPHDR::u32Magic. (Egberto Gismonti Amin) */
141#define MIGRATIONTCPHDR_MAGIC UINT32_C(0x19471205)
142/** The max block size. */
143#define MIGRATIONTCPHDR_MAX_SIZE UINT32_C(0x00fffff8)
144
145
146/*******************************************************************************
147* Global Variables *
148*******************************************************************************/
149static const char g_szWelcome[] = "VirtualBox-LiveMigration-1.0\n";
150
151
152/**
153 * Reads a string from the socket.
154 *
155 * @returns VBox status code.
156 *
157 * @param pState The live migration state structure.
158 * @param pszBuf The output buffer.
159 * @param cchBuf The size of the output buffer.
160 *
161 */
162static int migrationTcpReadLine(MigrationState *pState, char *pszBuf, size_t cchBuf)
163{
164 char *pszStart = pszBuf;
165 RTSOCKET Sock = pState->mhSocket;
166
167 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
168
169 /* dead simple (stupid) approach. */
170 for (;;)
171 {
172 char ch;
173 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
174 if (RT_FAILURE(rc))
175 {
176 LogRel(("Migration: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
177 return rc;
178 }
179 if ( ch == '\n'
180 || ch == '\0')
181 return VINF_SUCCESS;
182 if (cchBuf <= 1)
183 {
184 LogRel(("Migration: String buffer overflow: '%s'\n", pszStart));
185 return VERR_BUFFER_OVERFLOW;
186 }
187 *pszBuf++ = ch;
188 *pszBuf = '\0';
189 cchBuf--;
190 }
191}
192
193
194/**
195 * Reads an ACK or NACK.
196 *
197 * @returns S_OK on ACK, E_FAIL+setError() on failure or NACK.
198 * @param pState The live migration source state.
199 * @param pszNAckMsg Optional NACK message.
200 *
201 * @remarks the setError laziness forces this to be a Console member.
202 */
203HRESULT
204Console::migrationSrcReadACK(MigrationStateSrc *pState, const char *pszNAckMsg /*= NULL*/)
205{
206 char szMsg[128];
207 int vrc = migrationTcpReadLine(pState, szMsg, sizeof(szMsg));
208 if (RT_FAILURE(vrc))
209 return setError(E_FAIL, tr("Failed reading ACK: %Rrc"), vrc);
210 if (strcmp(szMsg, "ACK"))
211 {
212 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
213 {
214 int32_t vrc2;
215 vrc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
216 if (vrc == VINF_SUCCESS)
217 {
218 if (pszNAckMsg)
219 {
220 LogRel(("Migration: NACK=%Rrc (%d)\n", vrc2, vrc2));
221 return setError(E_FAIL, pszNAckMsg);
222 }
223 return setError(E_FAIL, "NACK - %Rrc (%d)", vrc2, vrc2);
224 }
225 }
226 return setError(E_FAIL, tr("Expected ACK or NACK, got '%s'"), szMsg);
227 }
228 return S_OK;
229}
230
231
232/**
233 * Submitts a command to the destination and waits for the ACK.
234 *
235 * @returns S_OK on ACKed command, E_FAIL+setError() on failure.
236 *
237 * @param pState The live migration source state.
238 * @param pszCommand The command.
239 *
240 * @remarks the setError laziness forces this to be a Console member.
241 */
242HRESULT
243Console::migrationSrcSubmitCommand(MigrationStateSrc *pState, const char *pszCommand)
244{
245 size_t cchCommand = strlen(pszCommand);
246 int vrc = RTTcpWrite(pState->mhSocket, pszCommand, cchCommand);
247 if (RT_SUCCESS(vrc))
248 vrc = RTTcpWrite(pState->mhSocket, "\n", sizeof("\n") - 1);
249 if (RT_FAILURE(vrc))
250 return setError(E_FAIL, tr("Failed writing command '%s': %Rrc"), pszCommand, vrc);
251 return migrationSrcReadACK(pState);
252}
253
254
255/**
256 * @copydoc SSMSTRMOPS::pfnWrite
257 */
258static DECLCALLBACK(int) migrationTcpOpWrite(void *pvUser, uint64_t offStream, const void *pvBuf, size_t cbToWrite)
259{
260 MigrationState *pState = (MigrationState *)pvUser;
261
262 AssertReturn(cbToWrite > 0, VINF_SUCCESS);
263 AssertReturn(pState->mfIsSource, VERR_INVALID_HANDLE);
264
265 for (;;)
266 {
267 /* Write block header. */
268 MIGRATIONTCPHDR Hdr;
269 Hdr.u32Magic = MIGRATIONTCPHDR_MAGIC;
270 Hdr.cb = RT_MIN(cbToWrite, MIGRATIONTCPHDR_MAX_SIZE);
271 int rc = RTTcpWrite(pState->mhSocket, &Hdr, sizeof(Hdr));
272 if (RT_FAILURE(rc))
273 {
274 LogRel(("Migration/TCP: Header write error: %Rrc\n", rc));
275 return rc;
276 }
277
278 /* Write the data. */
279 rc = RTTcpWrite(pState->mhSocket, pvBuf, Hdr.cb);
280 if (RT_FAILURE(rc))
281 {
282 LogRel(("Migration/TCP: Data write error: %Rrc (cb=%#x)\n", rc, Hdr.cb));
283 return rc;
284 }
285 pState->moffStream += Hdr.cb;
286 if (Hdr.cb == cbToWrite)
287 return VINF_SUCCESS;
288
289 /* advance */
290 cbToWrite -= Hdr.cb;
291 pvBuf = (uint8_t const *)pvBuf + Hdr.cb;
292 }
293}
294
295
296/**
297 * Selects and poll for close condition.
298 *
299 * We can use a relatively high poll timeout here since it's only used to get
300 * us out of error paths. In the normal cause of events, we'll get a
301 * end-of-stream header.
302 *
303 * @returns VBox status code.
304 *
305 * @param pState The migration state data.
306 */
307static int migrationTcpReadSelect(MigrationState *pState)
308{
309 int rc;
310 do
311 {
312 rc = RTTcpSelectOne(pState->mhSocket, 1000);
313 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
314 {
315 pState->mfIOError = true;
316 LogRel(("Migration/TCP: Header select error: %Rrc\n", rc));
317 break;
318 }
319 if (pState->mfStopReading)
320 {
321 rc = VERR_EOF;
322 break;
323 }
324 } while (rc == VERR_TIMEOUT);
325 return rc;
326}
327
328
329/**
330 * @copydoc SSMSTRMOPS::pfnRead
331 */
332static DECLCALLBACK(int) migrationTcpOpRead(void *pvUser, uint64_t offStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
333{
334 MigrationState *pState = (MigrationState *)pvUser;
335 AssertReturn(!pState->mfIsSource, VERR_INVALID_HANDLE);
336
337 for (;;)
338 {
339 int rc;
340
341 /*
342 * Check for various conditions and may have been signalled.
343 */
344 if (pState->mfEndOfStream)
345 return VERR_EOF;
346 if (pState->mfStopReading)
347 return VERR_EOF;
348 if (pState->mfIOError)
349 return VERR_IO_GEN_FAILURE;
350
351 /*
352 * If there is no more data in the current block, read the next
353 * block header.
354 */
355 if (!pState->mcbReadBlock)
356 {
357 rc = migrationTcpReadSelect(pState);
358 if (RT_FAILURE(rc))
359 return rc;
360 MIGRATIONTCPHDR Hdr;
361 rc = RTTcpRead(pState->mhSocket, &Hdr, sizeof(Hdr), NULL);
362 if (RT_FAILURE(rc))
363 {
364 pState->mfIOError = true;
365 LogRel(("Migration/TCP: Header read error: %Rrc\n", rc));
366 return rc;
367 }
368 if ( Hdr.u32Magic != MIGRATIONTCPHDR_MAGIC
369 || Hdr.cb > MIGRATIONTCPHDR_MAX_SIZE)
370 {
371 pState->mfIOError = true;
372 LogRel(("Migration/TCP: Invalid block: u32Magic=%#x cb=%#x\n", Hdr.u32Magic, Hdr.cb));
373 return VERR_IO_GEN_FAILURE;
374 }
375
376 pState->mcbReadBlock = Hdr.cb;
377 if (!Hdr.cb)
378 {
379 pState->mfEndOfStream = true;
380 return VERR_EOF;
381 }
382
383 if (pState->mfStopReading)
384 return VERR_EOF;
385 }
386
387 /*
388 * Read more data.
389 */
390 rc = migrationTcpReadSelect(pState);
391 if (RT_FAILURE(rc))
392 return rc;
393 size_t cb = RT_MIN(pState->mcbReadBlock, cbToRead);
394 rc = RTTcpRead(pState->mhSocket, pvBuf, cb, pcbRead);
395 if (RT_FAILURE(rc))
396 {
397 pState->mfIOError = true;
398 LogRel(("Migration/TCP: Data read error: %Rrc (cb=%#x)\n", rc, cb));
399 return rc;
400 }
401 if (pcbRead)
402 {
403 pState->moffStream += *pcbRead;
404 pState->mcbReadBlock -= *pcbRead;
405 return VINF_SUCCESS;
406 }
407 pState->moffStream += cb;
408 pState->mcbReadBlock -= cb;
409 if (cbToRead == cb)
410 return VINF_SUCCESS;
411
412 /* Advance to the next block. */
413 cbToRead -= cb;
414 pvBuf = (uint8_t *)pvBuf + cb;
415 }
416}
417
418
419/**
420 * @copydoc SSMSTRMOPS::pfnSeek
421 */
422static DECLCALLBACK(int) migrationTcpOpSeek(void *pvUser, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
423{
424 return VERR_NOT_SUPPORTED;
425}
426
427
428/**
429 * @copydoc SSMSTRMOPS::pfnTell
430 */
431static DECLCALLBACK(uint64_t) migrationTcpOpTell(void *pvUser)
432{
433 MigrationState *pState = (MigrationState *)pvUser;
434 return pState->moffStream;
435}
436
437
438/**
439 * @copydoc SSMSTRMOPS::pfnSize
440 */
441static DECLCALLBACK(int) migrationTcpOpSize(void *pvUser, uint64_t *pcb)
442{
443 return VERR_NOT_SUPPORTED;
444}
445
446
447/**
448 * @copydoc SSMSTRMOPS::pfnClose
449 */
450static DECLCALLBACK(int) migrationTcpOpClose(void *pvUser)
451{
452 MigrationState *pState = (MigrationState *)pvUser;
453
454 if (pState->mfIsSource)
455 {
456 MIGRATIONTCPHDR EofHdr = { MIGRATIONTCPHDR_MAGIC, 0 };
457 int rc = RTTcpWrite(pState->mhSocket, &EofHdr, sizeof(EofHdr));
458 if (RT_FAILURE(rc))
459 {
460 LogRel(("Migration/TCP: EOF Header write error: %Rrc\n", rc));
461 return rc;
462 }
463 }
464 else
465 {
466 ASMAtomicWriteBool(&pState->mfStopReading, true);
467 RTTcpFlush(pState->mhSocket);
468 }
469
470 return VINF_SUCCESS;
471}
472
473
474/**
475 * Method table for a TCP based stream.
476 */
477static SSMSTRMOPS const g_migrationTcpOps =
478{
479 SSMSTRMOPS_VERSION,
480 migrationTcpOpWrite,
481 migrationTcpOpRead,
482 migrationTcpOpSeek,
483 migrationTcpOpTell,
484 migrationTcpOpSize,
485 migrationTcpOpClose,
486 SSMSTRMOPS_VERSION
487};
488
489
490/**
491 * @copydoc FNRTTIMERLR
492 */
493static DECLCALLBACK(void) migrationTimeout(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
494{
495 /* This is harmless for any open connections. */
496 RTTcpServerShutdown((PRTTCPSERVER)pvUser);
497}
498
499
500/**
501 * Do the live migration.
502 *
503 * @returns VBox status code.
504 * @param pState The migration state.
505 */
506HRESULT
507Console::migrationSrc(MigrationStateSrc *pState)
508{
509 AutoCaller autoCaller(this);
510 CheckComRCReturnRC(autoCaller.rc());
511
512 /*
513 * Wait for Console::Migrate to change the state.
514 */
515 { AutoWriteLock autoLock(); }
516
517 BOOL fCanceled = TRUE;
518 HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
519 if (FAILED(hrc))
520 return hrc;
521 if (fCanceled)
522 return setError(E_FAIL, tr("canceled"));
523
524 /*
525 * Try connect to the destination machine.
526 * (Note. The caller cleans up mhSocket, so we can return without worries.)
527 */
528 int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket);
529 if (RT_FAILURE(vrc))
530 return setError(E_FAIL, tr("Failed to connect to port %u on '%s': %Rrc"),
531 pState->muPort, pState->mstrHostname.c_str(), vrc);
532
533 /* Read and check the welcome message. */
534 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
535 vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
536 if (RT_FAILURE(vrc))
537 return setError(E_FAIL, tr("Failed to read welcome message: %Rrc"), vrc);
538 if (strcmp(szLine, g_szWelcome))
539 return setError(E_FAIL, tr("Unexpected welcome '%s'"), szLine);
540
541 /* password */
542 pState->mstrPassword.append('\n');
543 vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length());
544 if (RT_FAILURE(vrc))
545 return setError(E_FAIL, tr("Failed to send password: %Rrc"), vrc);
546
547 /* ACK */
548 hrc = migrationSrcReadACK(pState, tr("Invalid password"));
549 if (FAILED(hrc))
550 return hrc;
551
552 /*
553 * Do compatability checks of the VM config and the host hardware.
554 */
555 /** @todo later */
556
557 /*
558 * Start loading the state.
559 */
560 hrc = migrationSrcSubmitCommand(pState, "load");
561 if (FAILED(hrc))
562 return hrc;
563
564 void *pvUser = static_cast<void *>(static_cast<MigrationState *>(pState));
565 vrc = VMR3Migrate(pState->mpVM, &g_migrationTcpOps, pvUser, NULL/** @todo progress*/, pvUser);
566 if (vrc)
567 return setError(E_FAIL, tr("VMR3Migrate -> %Rrc"), vrc);
568
569 hrc = migrationSrcReadACK(pState);
570 if (FAILED(hrc))
571 return hrc;
572
573 /*
574 * State fun? Automatic power off?
575 */
576
577 return S_OK;
578}
579
580
581/**
582 * Static thread method wrapper.
583 *
584 * @returns VINF_SUCCESS (ignored).
585 * @param hThread The thread.
586 * @param pvUser Pointer to a MigrationStateSrc instance.
587 */
588/*static*/ DECLCALLBACK(int)
589Console::migrationSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
590{
591 MigrationStateSrc *pState = (MigrationStateSrc *)pvUser;
592
593 HRESULT hrc = pState->mptrConsole->migrationSrc(pState);
594 pState->mptrProgress->notifyComplete(hrc);
595
596 AutoWriteLock autoLock(pState->mptrConsole);
597 if (pState->mptrConsole->mMachineState == MachineState_Saving)
598 {
599 VMSTATE enmVMState = VMR3GetState(pState->mpVM);
600 if (SUCCEEDED(hrc))
601 {
602 if (enmVMState == VMSTATE_SUSPENDED)
603 pState->mptrConsole->setMachineState(MachineState_Paused);
604 }
605 else
606 {
607 switch (enmVMState)
608 {
609 case VMSTATE_RUNNING:
610 case VMSTATE_RUNNING_LS:
611 case VMSTATE_DEBUGGING:
612 case VMSTATE_DEBUGGING_LS:
613 case VMSTATE_POWERING_OFF:
614 case VMSTATE_POWERING_OFF_LS:
615 case VMSTATE_RESETTING:
616 case VMSTATE_RESETTING_LS:
617 pState->mptrConsole->setMachineState(MachineState_Running);
618 break;
619 case VMSTATE_GURU_MEDITATION:
620 case VMSTATE_GURU_MEDITATION_LS:
621 pState->mptrConsole->setMachineState(MachineState_Stuck);
622 break;
623 default:
624 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
625 case VMSTATE_SUSPENDED:
626 case VMSTATE_SUSPENDED_LS:
627 case VMSTATE_SUSPENDING:
628 case VMSTATE_SUSPENDING_LS:
629 case VMSTATE_SUSPENDING_EXT_LS:
630 pState->mptrConsole->setMachineState(MachineState_Paused);
631 pState->mptrConsole->Resume(); /** @todo somehow make the VMM report back external pause even on error. */
632 autoLock.unlock();
633 break;
634 }
635 }
636 }
637
638 if (pState->mhSocket != NIL_RTSOCKET)
639 {
640 RTTcpClientClose(pState->mhSocket);
641 pState->mhSocket = NIL_RTSOCKET;
642 }
643 delete pState;
644
645 return VINF_SUCCESS;
646}
647
648
649/**
650 * Start live migration to the specified target.
651 *
652 * @returns COM status code.
653 *
654 * @param aHostname The name of the target host.
655 * @param aPort The TCP port number.
656 * @param aPassword The password.
657 * @param aProgress Where to return the progress object.
658 */
659STDMETHODIMP
660Console::Migrate(IN_BSTR aHostname, ULONG aPort, IN_BSTR aPassword, IProgress **aProgress)
661{
662 /*
663 * Validate parameters, check+hold object status, write lock the object
664 * and validate the state.
665 */
666 CheckComArgOutPointerValid(aProgress);
667 CheckComArgStrNotEmptyOrNull(aHostname);
668 CheckComArgNotNull(aHostname);
669 CheckComArgExprMsg(aPort, aPort > 0 && aPort <= 65535, ("is %u", aPort));
670
671 AutoCaller autoCaller(this);
672 CheckComRCReturnRC(autoCaller.rc());
673
674 AutoWriteLock autoLock(this);
675 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
676
677 switch (mMachineState)
678 {
679 case MachineState_Running:
680 case MachineState_Paused:
681 break;
682
683 default:
684 return setError(VBOX_E_INVALID_VM_STATE,
685 tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
686 Global::stringifyMachineState(mMachineState));
687 }
688
689
690 /*
691 * Create a progress object, spawn a worker thread and change the state.
692 * Note! The thread won't start working until we release the lock.
693 */
694 LogFlowThisFunc(("Initiating LIVE MIGRATION request...\n"));
695
696 ComObjPtr<Progress> ptrMigrateProgress;
697 HRESULT hrc = ptrMigrateProgress.createObject();
698 CheckComRCReturnRC(hrc);
699 hrc = ptrMigrateProgress->init(static_cast<IConsole *>(this),
700 Bstr(tr("Live Migration")),
701 TRUE /*aCancelable*/);
702 CheckComRCReturnRC(hrc);
703
704 MigrationStateSrc *pState = new MigrationStateSrc(this, mpVM);
705 pState->mstrPassword = aPassword;
706 pState->mstrHostname = aHostname;
707 pState->muPort = aPort;
708 pState->mptrProgress = ptrMigrateProgress;
709
710 int vrc = RTThreadCreate(NULL, Console::migrationSrcThreadWrapper, (void *)pState, 0 /*cbStack*/,
711 RTTHREADTYPE_EMULATION, 0 /*fFlags*/, "Migrate");
712 if (RT_SUCCESS(vrc))
713 {
714 hrc = setMachineState(MachineState_Saving);
715 if (SUCCEEDED(hrc))
716 ptrMigrateProgress.queryInterfaceTo(aProgress);
717 else
718 ptrMigrateProgress->Cancel();
719 }
720 else
721 {
722 delete pState;
723 hrc = setError(E_FAIL, tr("RTThreadCreate -> %Rrc"), vrc);
724 }
725
726 return hrc;
727}
728
729
730/**
731 * Creates a TCP server that listens for the source machine and passes control
732 * over to Console::migrationDstServeConnection().
733 *
734 * @returns VBox status code.
735 * @param pVM The VM handle
736 * @param pMachine The IMachine for the virtual machine.
737 * @param fStartPaused Whether to start it in the Paused (true) or
738 * Running (false) state,
739 * @param pvVMCallbackTask The callback task pointer for
740 * stateProgressCallback().
741 */
742int
743Console::migrationDst(PVM pVM, IMachine *pMachine, bool fStartPaused, void *pvVMCallbackTask)
744{
745 /*
746 * Get the config.
747 */
748 ULONG uPort;
749 HRESULT hrc = pMachine->COMGETTER(LiveMigrationPort)(&uPort);
750 if (FAILED(hrc))
751 return VERR_GENERAL_FAILURE;
752
753 Bstr bstrPassword;
754 hrc = pMachine->COMGETTER(LiveMigrationPassword)(bstrPassword.asOutParam());
755 if (FAILED(hrc))
756 return VERR_GENERAL_FAILURE;
757 Utf8Str strPassword(bstrPassword);
758 strPassword.append('\n'); /* To simplify password checking. */
759
760 Utf8Str strBind("");
761 /** @todo Add a bind address property. */
762 const char *pszBindAddress = strBind.isEmpty() ? NULL : strBind.c_str();
763
764
765 /*
766 * Create the TCP server.
767 */
768 int vrc;
769 PRTTCPSERVER hServer;
770 if (uPort)
771 vrc = RTTcpServerCreateEx(pszBindAddress, uPort, &hServer);
772 else
773 {
774 for (int cTries = 10240; cTries > 0; cTries--)
775 {
776 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
777 vrc = RTTcpServerCreateEx(pszBindAddress, uPort, &hServer);
778 if (vrc != VERR_NET_ADDRESS_IN_USE)
779 break;
780 }
781 if (RT_SUCCESS(vrc))
782 {
783 HRESULT hrc = pMachine->COMSETTER(LiveMigrationPort)(uPort);
784 if (FAILED(hrc))
785 {
786 RTTcpServerDestroy(hServer);
787 return VERR_GENERAL_FAILURE;
788 }
789 }
790 }
791 if (RT_FAILURE(vrc))
792 return vrc;
793
794 /*
795 * Create a one-shot timer for timing out after 5 mins.
796 */
797 RTTIMERLR hTimerLR;
798 vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, migrationTimeout, hServer);
799 if (RT_SUCCESS(vrc))
800 {
801 vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
802 if (RT_SUCCESS(vrc))
803 {
804 /*
805 * Do the job, when it returns we're done.
806 */
807 MigrationStateDst State(this, pVM, pMachine, &hTimerLR);
808 State.mstrPassword = strPassword;
809 State.mhServer = hServer;
810 State.mpvVMCallbackTask = pvVMCallbackTask;
811
812 vrc = RTTcpServerListen(hServer, Console::migrationDstServeConnection, &State);
813 if (vrc == VERR_TCP_SERVER_STOP)
814 vrc = State.mRc;
815 if (RT_SUCCESS(vrc))
816 {
817 if (fStartPaused)
818 setMachineState(MachineState_Paused);
819 else
820 vrc = VMR3Resume(pVM);
821 }
822 else
823 {
824 LogRel(("Migration: RTTcpServerListen -> %Rrc\n", vrc));
825 }
826 }
827
828 RTTimerLRDestroy(hTimerLR);
829 }
830 RTTcpServerDestroy(hServer);
831
832 return vrc;
833}
834
835
836static int migrationTcpWriteACK(MigrationStateDst *pState)
837{
838 int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1);
839 if (RT_FAILURE(rc))
840 LogRel(("Migration: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
841 RTTcpFlush(pState->mhSocket);
842 return rc;
843}
844
845
846static int migrationTcpWriteNACK(MigrationStateDst *pState, int32_t rc2)
847{
848 char szMsg[64];
849 size_t cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
850 int rc = RTTcpWrite(pState->mhSocket, szMsg, cch);
851 if (RT_FAILURE(rc))
852 LogRel(("Migration: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
853 RTTcpFlush(pState->mhSocket);
854 return rc;
855}
856
857
858/**
859 * @copydoc FNRTTCPSERVE
860 */
861/*static*/ DECLCALLBACK(int)
862Console::migrationDstServeConnection(RTSOCKET Sock, void *pvUser)
863{
864 MigrationStateDst *pState = (MigrationStateDst *)pvUser;
865 pState->mhSocket = Sock;
866
867 /*
868 * Say hello.
869 */
870 int vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
871 if (RT_FAILURE(vrc))
872 {
873 LogRel(("Migration: Failed to write welcome message: %Rrc\n", vrc));
874 return VINF_SUCCESS;
875 }
876
877 /*
878 * Password (includes '\n', see migrationDst). If it's right, tell
879 * the TCP server to stop listening (frees up host resources and makes sure
880 * this is the last connection attempt).
881 */
882 const char *pszPassword = pState->mstrPassword.c_str();
883 unsigned off = 0;
884 while (pszPassword[off])
885 {
886 char ch;
887 vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
888 if ( RT_FAILURE(vrc)
889 || pszPassword[off] != ch)
890 {
891 if (RT_FAILURE(vrc))
892 LogRel(("Migration: Password read failure (off=%u): %Rrc\n", off, vrc));
893 else
894 LogRel(("Migration: Invalid password (off=%u)\n", off));
895 migrationTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE);
896 return VINF_SUCCESS;
897 }
898 off++;
899 }
900 vrc = migrationTcpWriteACK(pState);
901 if (RT_FAILURE(vrc))
902 return vrc;
903
904 RTTcpServerShutdown(pState->mhServer);
905 RTTimerLRDestroy(*pState->mphTimerLR);
906 *pState->mphTimerLR = NIL_RTTIMERLR;
907
908 /*
909 * Command processing loop.
910 */
911 for (;;)
912 {
913 char szCmd[128];
914 vrc = migrationTcpReadLine(pState, szCmd, sizeof(szCmd));
915 if (RT_FAILURE(vrc))
916 break;
917
918 if (!strcmp(szCmd, "load"))
919 {
920 vrc = migrationTcpWriteACK(pState);
921 if (RT_FAILURE(vrc))
922 break;
923
924 pState->moffStream = 0;
925 void *pvUser = static_cast<void *>(static_cast<MigrationState *>(pState));
926 vrc = VMR3LoadFromStream(pState->mpVM, &g_migrationTcpOps, pvUser,
927 Console::stateProgressCallback, pState->mpvVMCallbackTask);
928 if (RT_FAILURE(vrc))
929 {
930 LogRel(("Migration: VMR3LoadFromStream -> %Rrc\n", vrc));
931 migrationTcpWriteNACK(pState, vrc);
932 break;
933 }
934
935 vrc = migrationTcpWriteACK(pState);
936 if (RT_FAILURE(vrc))
937 break;
938 }
939 /** @todo implement config verification and hardware compatability checks. Or
940 * maybe leave part of these to the saved state machinery? */
941 else if (!strcmp(szCmd, "done"))
942 {
943 vrc = migrationTcpWriteACK(pState);
944 break;
945 }
946 else
947 {
948 LogRel(("Migration: Unknown command '%s'\n", szCmd));
949 break;
950 }
951 }
952
953 pState->mRc = vrc;
954 pState->mhSocket = NIL_RTSOCKET;
955 LogFlowFunc(("returns mRc=%Rrc\n", vrc));
956 return VERR_TCP_SERVER_STOP;
957}
958
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