VirtualBox

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

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

Main/LiveMigration: Make sure the end-of-stream marker is read before ACK'ing the load command.

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