VirtualBox

source: vbox/trunk/src/VBox/VMM/FTM.cpp@ 31770

Last change on this file since 31770 was 31770, checked in by vboxsync, 14 years ago

FT updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* $Id: FTM.cpp 31770 2010-08-19 08:53:41Z vboxsync $ */
2/** @file
3 * FTM - Fault Tolerance Manager
4 */
5
6/*
7 * Copyright (C) 2010 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#define LOG_GROUP LOG_GROUP_FTM
23#include "FTMInternal.h"
24#include <VBox/vm.h>
25#include <VBox/vmm.h>
26#include <VBox/err.h>
27#include <VBox/param.h>
28#include <VBox/ssm.h>
29#include <VBox/log.h>
30
31#include <iprt/assert.h>
32#include <iprt/thread.h>
33#include <iprt/string.h>
34#include <iprt/mem.h>
35#include <iprt/tcp.h>
36
37/*******************************************************************************
38* Global Variables *
39*******************************************************************************/
40static const char g_szWelcome[] = "VirtualBox-Fault-Tolerance-Sync-1.0\n";
41
42/**
43 * Initializes the FTM.
44 *
45 * @returns VBox status code.
46 * @param pVM The VM to operate on.
47 */
48VMMR3DECL(int) FTMR3Init(PVM pVM)
49{
50 /** @todo saved state for master nodes! */
51 pVM->ftm.s.pszAddress = NULL;
52 pVM->ftm.s.pszPassword = NULL;
53 pVM->fFaultTolerantMaster = false;
54 pVM->ftm.s.fIsStandbyNode = false;
55 pVM->ftm.s.standby.hServer = NULL;
56 pVM->ftm.s.master.hShutdownEvent = NIL_RTSEMEVENT;
57 pVM->ftm.s.hSocket = NIL_RTSOCKET;
58 return VINF_SUCCESS;
59}
60
61/**
62 * Terminates the FTM.
63 *
64 * Termination means cleaning up and freeing all resources,
65 * the VM itself is at this point powered off or suspended.
66 *
67 * @returns VBox status code.
68 * @param pVM The VM to operate on.
69 */
70VMMR3DECL(int) FTMR3Term(PVM pVM)
71{
72 if (pVM->ftm.s.pszAddress)
73 RTMemFree(pVM->ftm.s.pszAddress);
74 if (pVM->ftm.s.pszPassword)
75 RTMemFree(pVM->ftm.s.pszPassword);
76 if (pVM->ftm.s.hSocket != NIL_RTSOCKET)
77 RTTcpClientClose(pVM->ftm.s.hSocket);
78 if (pVM->ftm.s.standby.hServer)
79 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
80 if (pVM->ftm.s.master.hShutdownEvent != NIL_RTSEMEVENT)
81 RTSemEventDestroy(pVM->ftm.s.master.hShutdownEvent);
82
83 return VINF_SUCCESS;
84}
85
86
87static int ftmR3TcpWriteACK(PVM pVM)
88{
89 int rc = RTTcpWrite(pVM->ftm.s.hSocket, "ACK\n", sizeof("ACK\n") - 1);
90 if (RT_FAILURE(rc))
91 {
92 LogRel(("FTSync: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
93 }
94 return rc;
95}
96
97
98static int ftmR3TcpWriteNACK(PVM pVM, int32_t rc2, const char *pszMsgText = NULL)
99{
100 char szMsg[256];
101 size_t cch;
102 if (pszMsgText && *pszMsgText)
103 {
104 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
105 for (size_t off = 6; off + 1 < cch; off++)
106 if (szMsg[off] == '\n')
107 szMsg[off] = '\r';
108 }
109 else
110 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
111 int rc = RTTcpWrite(pVM->ftm.s.hSocket, szMsg, cch);
112 if (RT_FAILURE(rc))
113 LogRel(("FTSync: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
114 return rc;
115}
116
117/**
118 * Reads a string from the socket.
119 *
120 * @returns VBox status code.
121 *
122 * @param pState The teleporter state structure.
123 * @param pszBuf The output buffer.
124 * @param cchBuf The size of the output buffer.
125 *
126 */
127static int ftmR3TcpReadLine(PVM pVM, char *pszBuf, size_t cchBuf)
128{
129 char *pszStart = pszBuf;
130 RTSOCKET Sock = pVM->ftm.s.hSocket;
131
132 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
133 *pszBuf = '\0';
134
135 /* dead simple approach. */
136 for (;;)
137 {
138 char ch;
139 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
140 if (RT_FAILURE(rc))
141 {
142 LogRel(("FTSync: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
143 return rc;
144 }
145 if ( ch == '\n'
146 || ch == '\0')
147 return VINF_SUCCESS;
148 if (cchBuf <= 1)
149 {
150 LogRel(("FTSync: String buffer overflow: '%s'\n", pszStart));
151 return VERR_BUFFER_OVERFLOW;
152 }
153 *pszBuf++ = ch;
154 *pszBuf = '\0';
155 cchBuf--;
156 }
157}
158
159/**
160 * Reads an ACK or NACK.
161 *
162 * @returns VBox status code.
163 * @param pVM The VM to operate on.
164 * @param pszWhich Which ACK is this this?
165 * @param pszNAckMsg Optional NACK message.
166 */
167static int ftmR3TcpReadACK(PVM pVM, const char *pszWhich, const char *pszNAckMsg /*= NULL*/)
168{
169 char szMsg[256];
170 int rc = ftmR3TcpReadLine(pVM, szMsg, sizeof(szMsg));
171 if (RT_FAILURE(rc))
172 return rc;
173
174 if (!strcmp(szMsg, "ACK"))
175 return VINF_SUCCESS;
176
177 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
178 {
179 char *pszMsgText = strchr(szMsg, ';');
180 if (pszMsgText)
181 *pszMsgText++ = '\0';
182
183 int32_t vrc2;
184 rc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
185 if (rc == VINF_SUCCESS)
186 {
187 /*
188 * Well formed NACK, transform it into an error.
189 */
190 if (pszNAckMsg)
191 {
192 LogRel(("FTSync: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
193 return VERR_INTERNAL_ERROR;
194 }
195
196 if (pszMsgText)
197 {
198 pszMsgText = RTStrStrip(pszMsgText);
199 for (size_t off = 0; pszMsgText[off]; off++)
200 if (pszMsgText[off] == '\r')
201 pszMsgText[off] = '\n';
202
203 LogRel(("FTSync: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
204 }
205 return VERR_INTERNAL_ERROR_2;
206 }
207
208 if (pszMsgText)
209 pszMsgText[-1] = ';';
210 }
211 return VERR_INTERNAL_ERROR_3;
212}
213
214/**
215 * Thread function which starts syncing process for this master VM
216 *
217 * @param Thread The thread id.
218 * @param pvUser Not used
219 * @return VINF_SUCCESS (ignored).
220 *
221 * @note Locks the Console object for writing.
222 */
223static DECLCALLBACK(int) ftmR3MasterThread(RTTHREAD Thread, void *pvUser)
224{
225 int rc = VINF_SUCCESS;
226 PVM pVM = (PVM)pvUser;
227
228 for (;;)
229 {
230 /*
231 * Try connect to the standby machine.
232 */
233 rc = RTTcpClientConnect(pVM->ftm.s.pszAddress, pVM->ftm.s.uPort, &pVM->ftm.s.hSocket);
234 if (RT_SUCCESS(rc))
235 {
236 /* Disable Nagle. */
237 rc = RTTcpSetSendCoalescing(pVM->ftm.s.hSocket, false /*fEnable*/);
238 AssertRC(rc);
239
240 /* Read and check the welcome message. */
241 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
242 RT_ZERO(szLine);
243 rc = RTTcpRead(pVM->ftm.s.hSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
244 if ( RT_SUCCESS(rc)
245 && !strcmp(szLine, g_szWelcome))
246 {
247 /* password */
248 rc = RTTcpWrite(pVM->ftm.s.hSocket, pVM->ftm.s.pszPassword, strlen(pVM->ftm.s.pszPassword));
249 if (RT_SUCCESS(rc))
250 {
251 /* ACK */
252 rc = ftmR3TcpReadACK(pVM, "password", "Invalid password");
253 if (RT_SUCCESS(rc))
254 break;
255 }
256 }
257 rc = RTTcpClientClose(pVM->ftm.s.hSocket);
258 AssertRC(rc);
259 pVM->ftm.s.hSocket = NIL_RTSOCKET;
260 }
261 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, 1000 /* 1 second */);
262 if (rc != VERR_TIMEOUT)
263 return VINF_SUCCESS; /* told to quit */
264 }
265
266 /* Successfully initialized the connection to the standby node.
267 * Start the sync process.
268 */
269
270 return rc;
271}
272
273/**
274 * Listen for incoming traffic destined for the standby VM.
275 *
276 * @copydoc FNRTTCPSERVE
277 *
278 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
279 */
280static DECLCALLBACK(int) ftmR3StandbyServeConnection(RTSOCKET Sock, void *pvUser)
281{
282 PVM pVM = (PVM)pvUser;
283
284 pVM->ftm.s.hSocket = Sock;
285
286 /*
287 * Disable Nagle.
288 */
289 int rc = RTTcpSetSendCoalescing(Sock, false /*fEnable*/);
290 AssertRC(rc);
291
292 /* Send the welcome message to the master node. */
293 rc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
294 if (RT_FAILURE(rc))
295 {
296 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", rc));
297 return VINF_SUCCESS;
298 }
299
300 /*
301 * Password.
302 */
303 const char *pszPassword = pVM->ftm.s.pszPassword;
304 unsigned off = 0;
305 while (pszPassword[off])
306 {
307 char ch;
308 rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
309 if ( RT_FAILURE(rc)
310 || pszPassword[off] != ch)
311 {
312 if (RT_FAILURE(rc))
313 LogRel(("FTSync: Password read failure (off=%u): %Rrc\n", off, rc));
314 else
315 LogRel(("FTSync: Invalid password (off=%u)\n", off));
316 ftmR3TcpWriteNACK(pVM, VERR_AUTHENTICATION_FAILURE);
317 return VINF_SUCCESS;
318 }
319 off++;
320 }
321 rc = ftmR3TcpWriteACK(pVM);
322 if (RT_FAILURE(rc))
323 return VINF_SUCCESS;
324
325 /*
326 * Stop the server.
327 *
328 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
329 * to it we must not return that value!
330 */
331 RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
332
333 /*
334 * Command processing loop.
335 */
336 bool fDone = false;
337 for (;;)
338 {
339 char szCmd[128];
340 rc = ftmR3TcpReadLine(pVM, szCmd, sizeof(szCmd));
341 if (RT_FAILURE(rc))
342 break;
343
344 if (!strcmp(szCmd, "mem-sync"))
345 {
346 }
347 else
348 if (!strcmp(szCmd, "heartbeat"))
349 {
350 }
351 else
352 if (!strcmp(szCmd, "checkpoint"))
353 {
354 }
355 if (RT_FAILURE(rc))
356 break;
357 }
358 LogFlowFunc(("returns mRc=%Rrc\n", rc));
359 return VERR_TCP_SERVER_STOP;
360}
361
362/**
363 * Powers on the fault tolerant virtual machine.
364 *
365 * @returns VBox status code.
366 *
367 * @param pVM The VM to power on.
368 * @param fMaster FT master or standby
369 * @param uInterval FT sync interval
370 * @param pszAddress Standby VM address
371 * @param uPort Standby VM port
372 * @param pszPassword FT password (NULL for none)
373 *
374 * @thread Any thread.
375 * @vmstate Created
376 * @vmstateto PoweringOn+Running (master), PoweringOn+Running_FT (standby)
377 */
378VMMR3DECL(int) FTMR3PowerOn(PVM pVM, bool fMaster, unsigned uInterval, const char *pszAddress, unsigned uPort, const char *pszPassword)
379{
380 int rc = VINF_SUCCESS;
381
382 VMSTATE enmVMState = VMR3GetState(pVM);
383 AssertMsgReturn(enmVMState == VMSTATE_POWERING_ON,
384 ("%s\n", VMR3GetStateName(enmVMState)),
385 VERR_INTERNAL_ERROR_4);
386 AssertReturn(pszAddress, VERR_INVALID_PARAMETER);
387
388 pVM->ftm.s.uInterval = uInterval;
389 pVM->ftm.s.uPort = uPort;
390 pVM->ftm.s.pszAddress = RTStrDup(pszAddress);
391 if (pszPassword)
392 pVM->ftm.s.pszPassword = RTStrDup(pszPassword);
393 if (fMaster)
394 {
395 rc = RTSemEventCreate(&pVM->ftm.s.master.hShutdownEvent);
396 if (RT_FAILURE(rc))
397 return rc;
398
399 rc = RTThreadCreate(NULL, ftmR3MasterThread, pVM,
400 0, RTTHREADTYPE_IO /* higher than normal priority */, 0, "ftmR3MasterThread");
401 if (RT_FAILURE(rc))
402 return rc;
403
404 pVM->fFaultTolerantMaster = true;
405 return VMR3PowerOn(pVM);
406 }
407 else
408 {
409 /* standby */
410 rc = RTTcpServerCreateEx(pszAddress, uPort, &pVM->ftm.s.standby.hServer);
411 if (RT_FAILURE(rc))
412 return rc;
413 pVM->ftm.s.fIsStandbyNode = true;
414
415 rc = RTTcpServerListen(pVM->ftm.s.standby.hServer, ftmR3StandbyServeConnection, pVM);
416 /** @todo deal with the exit code to check if we should activate this standby VM. */
417
418 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
419 pVM->ftm.s.standby.hServer = NULL;
420 }
421 return rc;
422}
423
424/**
425 * Powers off the fault tolerant virtual machine (standby).
426 *
427 * @returns VBox status code.
428 *
429 * @param pVM The VM to power on.
430 */
431VMMR3DECL(int) FTMR3CancelStandby(PVM pVM)
432{
433 AssertReturn(!pVM->fFaultTolerantMaster, VERR_NOT_SUPPORTED);
434 Assert(pVM->ftm.s.standby.hServer);
435
436 return RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
437}
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