VBoxModAPIMonitor.cpp revision 8e42a64120db753317c89cfebb7762b905995a90
/* $Id$ */
/** @file
* VBoxModAPIMonitor - API monitor module for detecting host isolation.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifndef VBOX_ONLY_DOCS
#endif /* !VBOX_ONLY_DOCS */
#include "VBoxWatchdogInternal.h"
using namespace com;
#define VBOX_MOD_APIMON_NAME "apimon"
/**
* The module's RTGetOpt-IDs for the command line.
*/
enum GETOPTDEF_APIMON
{
GETOPTDEF_APIMON_GROUPS = 3000,
};
/**
* The module's command line arguments.
*/
static const RTGETOPTDEF g_aAPIMonitorOpts[] = {
};
enum APIMON_RESPONSE
{
/** Unknown / unhandled response. */
APIMON_RESPONSE_NONE = 0,
/** Pauses the VM execution. */
APIMON_RESPONSE_PAUSE = 10,
/** Does a hard power off. */
APIMON_RESPONSE_POWEROFF = 200,
/** Tries to save the current machine state. */
APIMON_RESPONSE_SAVE = 250,
/** Tries to shut down all running VMs in
* a gentle manner. */
APIMON_RESPONSE_SHUTDOWN = 300
};
/** The VM group(s) the API monitor handles. If none, all VMs get handled. */
static unsigned long g_ulAPIMonIslnTimeoutMS = 0;
static Bstr g_strAPIMonIslnLastBeat;
static unsigned long g_ulAPIMonResponseTimeoutMS = 0;
static uint64_t g_uAPIMonIslnLastBeatMS = 0;
{
int rc = VINF_SUCCESS;
{
}
{
}
{
}
{
}
{
}
else
{
}
return rc;
}
{
if (APIMON_RESPONSE_NONE == enmResp)
return "none";
else if (APIMON_RESPONSE_PAUSE == enmResp)
return "pausing";
else if (APIMON_RESPONSE_POWEROFF == enmResp)
return "powering off";
else if (APIMON_RESPONSE_SAVE == enmResp)
return "saving state";
else if (APIMON_RESPONSE_SHUTDOWN == enmResp)
return "shutting down";
return "unknown";
}
/* Copied from VBoxManageInfo.cpp. */
{
switch (machineState)
{
case MachineState_PoweredOff:
case MachineState_Saved:
return "saved";
case MachineState_Aborted:
return "aborted";
case MachineState_Teleported:
return "teleported";
case MachineState_Running:
return "running";
case MachineState_Paused:
return "paused";
case MachineState_Stuck:
case MachineState_Teleporting:
return "teleporting";
case MachineState_Starting:
return "starting";
case MachineState_Stopping:
return "stopping";
case MachineState_Saving:
return "saving";
case MachineState_Restoring:
return "restoring";
case MachineState_SettingUp:
default:
break;
}
return "unknown";
}
{
/** @todo Add other commands (with enmResp) here. */
serviceLogVerbose(("apimon: Triggering \"%s\" (%RU32ms timeout) for machine \"%ls\"\n",
if ( enmResp == APIMON_RESPONSE_NONE
|| g_fDryrun)
return VINF_SUCCESS; /* Nothing to do. */
do
{
/* Query the machine's state to avoid unnecessary IPC. */
if ( machineState == MachineState_Running
|| machineState == MachineState_Paused)
{
/* Open a session for the VM. */
do
{
/* Get the associated console. */
switch (enmResp)
{
case APIMON_RESPONSE_PAUSE:
if (machineState != MachineState_Paused)
{
serviceLogVerbose(("apimon: Pausing machine \"%ls\" ...\n",
}
break;
case APIMON_RESPONSE_POWEROFF:
serviceLogVerbose(("apimon: Powering off machine \"%ls\" ...\n",
break;
case APIMON_RESPONSE_SAVE:
{
serviceLogVerbose(("apimon: Saving state of machine \"%ls\" ...\n",
bool fPaused = false;
{
bool fError = true;
if (rc == VBOX_E_INVALID_VM_STATE)
{
/* Check if we are already paused. */
/* The error code was lost by the previous instruction. */
if (machineState != MachineState_Paused)
{
serviceLog("apimon: Machine \"%ls\" in invalid state %d -- %s\n",
}
else
{
fError = false;
fPaused = true;
}
}
if (fError)
break;
}
{
if (!fPaused)
break;
}
{
if (!fPaused)
}
break;
}
case APIMON_RESPONSE_SHUTDOWN:
break;
default:
break;
}
} while (0);
/* Unlock the machine again. */
}
else
serviceLogVerbose(("apimon: Warning: Could not trigger \"%s\" (%d) for machine \"%ls\"; in state \"%s\" (%d) currently\n",
} while (0);
}
{
bool fHandleVM = false;
try
{
&& !fHandleVM)
{
fHandleVM = true;
itVMGroup++;
}
}
catch (...)
{
AssertFailed();
}
return fHandleVM;
}
{
int rc = VINF_SUCCESS;
{
serviceLog("apimon: No machines in list, skipping ...\n");
return rc;
}
{
bool fHandleVM = fAllGroups;
try
{
if (!fHandleVM)
if (fHandleVM)
{
if (RT_FAILURE(rc2))
serviceLog("apimon: Controlling machine \"%ls\" (response \"%s\") failed with rc=%Rrc",
if (RT_SUCCESS(rc))
/* Keep going. */
}
}
catch (...)
{
AssertFailed();
}
it++;
}
return rc;
}
/* Callbacks. */
static DECLCALLBACK(int) VBoxModAPIMonitorPreInit(void)
{
return VINF_SUCCESS;
}
{
if (!argc) /* Take a shortcut. */
return -1;
0 /* First */, 0 /*fFlags*/);
if (RT_FAILURE(rc))
return rc;
rc = 0; /* Set default parsing result to valid. */
int c;
{
switch (c)
{
case GETOPTDEF_APIMON_GROUPS:
{
if (RT_FAILURE(rc))
break;
}
if (RT_FAILURE(rc))
break;
g_ulAPIMonIslnTimeoutMS = 1000;
break;
g_ulAPIMonResponseTimeoutMS = 5000;
break;
default:
break;
}
}
return rc;
}
static DECLCALLBACK(int) VBoxModAPIMonitorInit(void)
{
do
{
/* VM groups to watch for. */
{
strValue.asOutParam()));
{
if (RT_FAILURE(rc2))
}
}
/* Host isolation timeout (in ms). */
if (!g_ulAPIMonIslnTimeoutMS) /* Not set by command line? */
{
strValue.asOutParam()));
}
if (!g_ulAPIMonIslnTimeoutMS) /* Still not set? Use a default. */
{
serviceLogVerbose(("apimon: API monitor isolation timeout not given, defaulting to 30s\n"));
/* Default is 30 seconds timeout. */
}
/* Host isolation command response. */
{
strValue.asOutParam()));
{
if (RT_FAILURE(rc2))
serviceLog("apimon: Warning: API monitor response string invalid (%ls), defaulting to no action\n",
}
}
/* Trigger timeout (in ms). */
if (!g_ulAPIMonResponseTimeoutMS) /* Not set by command line? */
{
strValue.asOutParam()));
}
if (!g_ulAPIMonResponseTimeoutMS) /* Still not set? Use a default. */
{
serviceLogVerbose(("apimon: API monitor trigger timeout not given, defaulting to 30s\n"));
/* Default is 30 seconds timeout. */
}
#ifdef DEBUG
/* Groups. */
{
itGroups++;
}
serviceLogVerbose(("\n"));
#endif
} while (0);
{
}
}
static DECLCALLBACK(int) VBoxModAPIMonitorMain(void)
{
return VINF_SUCCESS;
int vrc = VINF_SUCCESS;
#ifdef DEBUG
serviceLogVerbose(("apimon: Checking for API heartbeat (%RU64ms) ...\n",
#endif
do
{
strHeartbeat.asOutParam()));
&& !strHeartbeat.isEmpty()
{
serviceLogVerbose(("apimon: API heartbeat received, resetting timeout\n"));
}
else
{
{
serviceLogVerbose(("apimon: No API heartbeat within time received (%RU64ms)\n",
}
}
} while (0);
return vrc;
}
static DECLCALLBACK(int) VBoxModAPIMonitorStop(void)
{
return VINF_SUCCESS;
}
static DECLCALLBACK(void) VBoxModAPIMonitorTerm(void)
{
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
if (!fAvailable)
{
serviceLog(("apimon: VBoxSVC became unavailable, triggering action\n"));
return apimonTrigger(g_enmAPIMonIslnResp);
}
return VINF_SUCCESS;
}
/**
* The 'apimonitor' module description.
*/
{
/* pszName. */
/* pszDescription. */
"API monitor for host isolation detection",
/* pszDepends. */
NULL,
/* uPriority. */
0 /* Not used */,
/* pszUsage. */
" [--apimon-groups=<string[,stringN]>]\n"
" [--apimon-isln-response=<cmd>] [--apimon-isln-timeout=<ms>]\n"
" [--apimon-resp-timeout=<ms>]",
/* pszOptions. */
"--apimon-groups Sets the VM groups for monitoring (all),\n"
" comma-separated list.\n"
"--apimon-isln-response Sets the isolation response to one of:\n"
" none, pause, poweroff, save, shutdown\n"
" (none).\n"
"--apimon-isln-timeout Sets the isolation timeout in ms (30s).\n"
"--apimon-resp-timeout Sets the response timeout in ms (30s).\n",
/* methods. */
/* callbacks. */
};