VirtualBox

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

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

Main: A little more live migration bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.9 KB
Line 
1/* $Id: ConsoleImpl-LiveMigration.cpp 23633 2009-10-08 22:14: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* Header Files *
24*******************************************************************************/
25#include "ConsoleImpl.h"
26#include "Logging.h"
27
28#include <iprt/err.h>
29#include <iprt/rand.h>
30#include <iprt/tcp.h>
31#include <iprt/timer.h>
32
33#include <VBox/vmapi.h>
34#include <VBox/ssm.h>
35#include <VBox/err.h>
36#include <VBox/version.h>
37#include <VBox/com/string.h>
38
39
40
41/*******************************************************************************
42* Structures and Typedefs *
43*******************************************************************************/
44/**
45 * Argument package for Console::migrationServeConnection.
46 */
47typedef struct MIGRATIONSTATE
48{
49 Console *pConsole;
50 IMachine *pMachine;
51 PVM pVM;
52 const char *pszPassword;
53 void *pvVMCallbackTask;
54 RTSOCKET hSocket;
55 uint64_t offStream;
56 int rc;
57} MIGRATIONSTATE;
58typedef MIGRATIONSTATE *PMIGRATIONSTATE;
59
60
61/*******************************************************************************
62* Global Variables *
63*******************************************************************************/
64static const char g_szWelcome[] = "VirtualBox-LiveMigration-1.0\n";
65
66
67/**
68 * @copydoc SSMSTRMOPS::pfnWrite
69 */
70static DECLCALLBACK(int) migrationTcpOpWrite(void *pvUser, uint64_t offStream, const void *pvBuf, size_t cbToWrite)
71{
72 PMIGRATIONSTATE pState = (PMIGRATIONSTATE)pvUser;
73 int rc = RTTcpWrite(pState->hSocket, pvBuf, cbToWrite);
74 if (RT_SUCCESS(rc))
75 {
76 pState->offStream += cbToWrite;
77 return VINF_SUCCESS;
78 }
79 return rc;
80}
81
82
83/**
84 * @copydoc SSMSTRMOPS::pfnRead
85 */
86static DECLCALLBACK(int) migrationTcpOpRead(void *pvUser, uint64_t offStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
87{
88 PMIGRATIONSTATE pState = (PMIGRATIONSTATE)pvUser;
89 int rc = RTTcpRead(pState->hSocket, pvBuf, cbToRead, pcbRead);
90 if (RT_SUCCESS(rc))
91 {
92 pState->offStream += pcbRead ? *pcbRead : cbToRead;
93 return VINF_SUCCESS;
94 }
95 return rc;
96}
97
98
99/**
100 * @copydoc SSMSTRMOPS::pfnSeek
101 */
102static DECLCALLBACK(int) migrationTcpOpSeek(void *pvUser, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
103{
104 return VERR_NOT_SUPPORTED;
105}
106
107
108/**
109 * @copydoc SSMSTRMOPS::pfnTell
110 */
111static DECLCALLBACK(uint64_t) migrationTcpOpTell(void *pvUser)
112{
113 PMIGRATIONSTATE pState = (PMIGRATIONSTATE)pvUser;
114 return pState->offStream;
115}
116
117
118/**
119 * @copydoc SSMSTRMOPS::pfnSize
120 */
121static DECLCALLBACK(int) migrationTcpOpSize(void *pvUser, uint64_t *pcb)
122{
123 return VERR_NOT_SUPPORTED;
124}
125
126
127/**
128 * @copydoc SSMSTRMOPS::pfnClose
129 */
130static DECLCALLBACK(int) migrationTcpOpClose(void *pvUser)
131{
132 return VINF_SUCCESS;
133}
134
135
136/**
137 * Method table for a TCP based stream.
138 */
139static SSMSTRMOPS const g_migrationTcpOps =
140{
141 SSMSTRMOPS_VERSION,
142 migrationTcpOpWrite,
143 migrationTcpOpRead,
144 migrationTcpOpSeek,
145 migrationTcpOpTell,
146 migrationTcpOpSize,
147 migrationTcpOpClose,
148 SSMSTRMOPS_VERSION
149};
150
151
152/**
153 * @copydoc FNRTTIMERLR
154 */
155static DECLCALLBACK(void) migrationTimeout(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
156{
157 /* This is harmless for any open connections. */
158 RTTcpServerShutdown((PRTTCPSERVER)pvUser);
159}
160
161
162/**
163 * Start live migration to the specified target.
164 *
165 * @returns COM status code.
166 *
167 * @param aHostname The name of the target host.
168 * @param aPort The TCP port number.
169 * @param aPassword The password.
170 * @param aProgress Where to return the progress object.
171 */
172STDMETHODIMP
173Console::Migrate(IN_BSTR aHostname, ULONG aPort, IN_BSTR aPassword, IProgress **aProgress)
174{
175 /*
176 * Validate parameters.
177 */
178
179 /*
180 * Try change the state, create a progress object and spawn a worker thread.
181 */
182
183 return E_FAIL;
184}
185
186
187/**
188 * Creates a TCP server that listens for the source machine and passes control
189 * over to Console::migrationServeConnection().
190 *
191 * @returns VBox status code.
192 * @param pVM The VM handle
193 * @param pMachine The IMachine for the virtual machine.
194 * @param pvVMCallbackTask The callback task pointer for
195 * stateProgressCallback().
196 */
197int
198Console::migrationLoadRemote(PVM pVM, IMachine *pMachine, void *pvVMCallbackTask)
199{
200 /*
201 * Get the config.
202 */
203 ULONG uPort;
204 HRESULT hrc = pMachine->COMGETTER(LiveMigrationPort)(&uPort);
205 if (FAILED(hrc))
206 return VERR_GENERAL_FAILURE;
207
208 Bstr bstrPassword;
209 hrc = pMachine->COMGETTER(LiveMigrationPassword)(bstrPassword.asOutParam());
210 if (FAILED(hrc))
211 return VERR_GENERAL_FAILURE;
212 Utf8Str strPassword(bstrPassword);
213 strPassword.append('\n'); /* always ends with a newline. */
214
215 Utf8Str strBind("");
216 /** @todo Add a bind address property. */
217 const char *pszBindAddress = strBind.isEmpty() ? NULL : strBind.c_str();
218
219 /*
220 * Create the TCP server.
221 */
222 int rc;
223 PRTTCPSERVER hServer;
224 if (uPort)
225 rc = RTTcpServerCreateEx(pszBindAddress, uPort, &hServer);
226 else
227 {
228 for (int cTries = 10240; cTries > 0; cTries--)
229 {
230 uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
231 rc = RTTcpServerCreateEx(pszBindAddress, uPort, &hServer);
232 if (rc != VERR_NET_ADDRESS_IN_USE)
233 break;
234 }
235 if (RT_SUCCESS(rc))
236 {
237 HRESULT hrc = pMachine->COMSETTER(LiveMigrationPort)(uPort);
238 if (FAILED(hrc))
239 {
240 RTTcpServerDestroy(hServer);
241 return VERR_GENERAL_FAILURE;
242 }
243 }
244 }
245 if (RT_FAILURE(rc))
246 return rc;
247
248 /*
249 * Create a one-shot timer for timing out after 5 mins.
250 */
251 RTTIMERLR hTimerLR;
252 rc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, migrationTimeout, hServer);
253 if (RT_SUCCESS(rc))
254 {
255 rc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
256 if (RT_SUCCESS(rc))
257 {
258 /*
259 * Do the job, when it returns we're done.
260 */
261 MIGRATIONSTATE State;
262 State.pConsole = this;
263 State.pMachine = pMachine;
264 State.pVM = pVM;
265 State.pszPassword = strPassword.c_str();
266 State.hSocket = NIL_RTSOCKET;
267 State.offStream = UINT64_MAX / 2;
268 State.rc = VINF_SUCCESS;
269
270 rc = RTTcpServerListen(hServer, Console::migrationServeConnection, &State);
271 if (rc == VERR_TCP_SERVER_STOP)
272 rc = State.rc;
273 if (RT_FAILURE(rc))
274 LogRel(("Migration: RTTcpServerListen -> %Rrc\n", rc));
275 }
276
277 RTTimerLRDestroy(hTimerLR);
278 }
279 RTTcpServerDestroy(hServer);
280
281 return rc;
282}
283
284
285/**
286 * Reads a string from the socket.
287 *
288 * @returns VBox status code.
289 *
290 * @param Sock The socket.
291 * @param pszBuf The output buffer.
292 * @param cchBuf The size of the output buffer.
293 *
294 */
295static int migrationReadLine(RTSOCKET Sock, char *pszBuf, size_t cchBuf)
296{
297 char *pszStart = pszBuf;
298 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
299
300 /* dead simple (stupid) approach. */
301 for (;;)
302 {
303 char ch;
304 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
305 if (RT_FAILURE(rc))
306 {
307 LogRel(("Migration: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
308 return rc;
309 }
310 if ( ch == '\n'
311 || ch == '\0')
312 return VINF_SUCCESS;
313 if (cchBuf <= 1)
314 {
315 LogRel(("Migration: String buffer overflow: '%s'\n", pszStart));
316 return VERR_BUFFER_OVERFLOW;
317 }
318 *pszBuf++ = ch;
319 *pszBuf = '\0';
320 cchBuf--;
321 }
322}
323
324
325/**
326 * @copydoc FNRTTCPSERVE
327 * VERR_TCP_SERVER_STOP
328 */
329/*static*/ DECLCALLBACK(int)
330Console::migrationServeConnection(RTSOCKET Sock, void *pvUser)
331{
332 PMIGRATIONSTATE pState = (PMIGRATIONSTATE)pvUser;
333 Console *pConsole = pState->pConsole;
334 IMachine *pMachine = pState->pMachine;
335
336 /*
337 * Say hello.
338 */
339 int rc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
340 if (RT_FAILURE(rc))
341 {
342 LogRel(("Migration: Failed to write welcome message: %Rrc\n", rc));
343 return VINF_SUCCESS;
344 }
345
346 /*
347 * Password (includes '\n', see migrationLoadRemote). If it's right, tell
348 * the TCP server to stop listening (frees up host resources and makes sure
349 * this is the last connection attempt).
350 */
351 const char *pszPassword = pState->pszPassword;
352 unsigned off = 0;
353 while (pszPassword[off])
354 {
355 char ch;
356 rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
357 if (RT_FAILURE(rc))
358 break;
359 if (pszPassword[off] != ch)
360 {
361 LogRel(("Migration: Invalid password (off=%u)\n", off));
362 return VINF_SUCCESS;
363 }
364 off++;
365 }
366
367 RTTcpServerShutdown((PRTTCPSERVER)pvUser);
368
369 /*
370 * Command processing loop.
371 */
372 pState->hSocket = Sock;
373 for (;;)
374 {
375 char szCmd[128];
376 rc = migrationReadLine(Sock, szCmd, sizeof(szCmd));
377 if (RT_FAILURE(rc))
378 break;
379
380 if (!strcmp(szCmd, "load"))
381 {
382 pState->offStream = 0;
383 rc = VMR3LoadFromStream(pState->pVM, &g_migrationTcpOps, pState,
384 Console::stateProgressCallback, pState->pvVMCallbackTask);
385 if (RT_FAILURE(rc))
386 {
387 LogRel(("Migration: VMR3LoadFromStream -> %Rrc\n", rc));
388 break;
389 }
390 }
391 /** @todo implement config verification and hardware compatability checks. Or
392 * maybe leave part of these to the saved state machinery? */
393 else if (!strcmp(szCmd, "done"))
394 break;
395 else
396 {
397 LogRel(("Migration: Unknown command '%s'\n", szCmd));
398 break;
399 }
400 }
401 pState->hSocket = NIL_RTSOCKET;
402
403 return VERR_TCP_SERVER_STOP;
404}
405
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