VBoxWatchdog.cpp revision b0857f8608931dbbc92393a6cb8f478e65e7c2f1
/* $Id$ */
/** @file
* VBoxWatchdog.cpp - VirtualBox Watchdog.
*/
/*
* Copyright (C) 2011-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 <package-generated.h>
#include <iprt/buildconfig.h>
#include <iprt/critsect.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <string>
#include <signal.h>
#include "VBoxWatchdogInternal.h"
using namespace com;
/* When defined, use a global performance collector instead
* of a per-machine based one. */
#define VBOX_WATCHDOG_GLOBAL_PERFCOL
/** External globals. */
bool g_fVerbose = false;
/** The critical section for the machines map. */
static RTCRITSECT g_csMachines;
/** Set by the signal handler. */
static volatile bool g_fCanceled = false;
/** Logging parameters. */
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
/** Run in background. */
static bool g_fDaemonize = false;
#endif
/**
* The details of the services that has been compiled in.
*/
static struct
{
/** Pointer to the service descriptor. */
/** Whether Pre-init was called. */
bool fPreInited;
/** Whether the module is enabled or not. */
bool fEnabled;
} g_aModules[] =
{
};
/**
* Command line arguments.
*/
static const RTGETOPTDEF g_aOptions[] = {
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
#endif
/** For displayHelp(). */
};
static unsigned long g_ulMemoryBalloonIncrementMB = 256;
static unsigned long g_ulMemoryBalloonDecrementMB = 128;
/** Global balloon limit is 0, so disabled. Can be overridden by a per-VM
* "VBoxInternal/Guest/BalloonSizeMax" value. */
static unsigned long g_ulMemoryBalloonMaxMB = 0;
static unsigned long g_ulLowerMemoryLimitMB = 64;
/** Global static objects. */
# ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
# endif
/* Prototypes. */
//static int machineUpdate(const Bstr &strUuid, MachineState_T enmState);
static HRESULT watchdogSetup();
static void watchdogTeardown();
#ifdef RT_OS_WINDOWS
/* Required for ATL. */
static CComModule _Module;
#endif
/**
* Handler for global events.
*/
class VirtualBoxEventListener
{
public:
{
}
virtual ~VirtualBoxEventListener()
{
}
{
return S_OK;
}
void uninit()
{
}
{
switch (aType)
{
{
{
if (RT_SUCCESS(rc))
{
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
int rc2 = fRegistered
if (RT_FAILURE(rc2))
serviceLog("Module '%s' reported an error: %Rrc\n",
/* Keep going. */
}
#if 0
else if (!fRegistered)
#endif
if (RT_SUCCESS(rc))
}
}
break;
}
{
{
if (RT_SUCCESS(rc))
{
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
if (RT_FAILURE(rc2))
serviceLog("Module '%s' reported an error: %Rrc\n",
/* Keep going. */
}
//rc = machineUpdate(uuid, machineState);
if (RT_SUCCESS(rc))
}
}
break;
}
{
/* First, notify all modules. */
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
if (RT_FAILURE(rc2))
serviceLog("Module '%s' reported an error: %Rrc\n",
/* Keep going. */
}
/* Do global teardown/re-creation stuff. */
if (!fAvailable)
{
serviceLog("VBoxSVC became unavailable\n");
}
else
{
serviceLog("VBoxSVC became available\n");
}
break;
}
default:
/* Not handled event, just skip it. */
break;
}
return S_OK;
}
private:
};
/**
* Signal handler that sets g_fGuestCtrlCanceled.
*
* This can be executed on any thread in the process, on Windows it may even be
* a thread dedicated to delivering this signal. Do not doing anything
* unnecessary here.
*/
static void signalHandler(int iSignal)
{
ASMAtomicWriteBool(&g_fCanceled, true);
if (!g_pEventQ)
{
if (RT_FAILURE(rc))
}
}
/**
* Installs a custom signal handler to get notified
* whenever the user wants to intercept the program.
*/
static void signalHandlerInstall()
{
#ifdef SIGBREAK
#endif
}
/**
* Uninstalls a previously installed signal handler.
*/
static void signalHandlerUninstall()
{
#ifdef SIGBREAK
#endif
}
/**
* Indicates whether a VM is up and running (regardless of its running
* state, could be paused as well).
*
* @return bool Flag indicating whether the VM is running or not.
* @param enmState The VM's machine state to judge whether it's running or not.
*/
{
switch (enmState)
{
case MachineState_Running:
#if 0
/* Not required for ballooning. */
case MachineState_Teleporting:
case MachineState_Paused:
#endif
return true;
default:
break;
}
return false;
}
/**
* Determines whether the specified machine needs to be handled
* by this service.
*
* @return bool True if the machine needs handling, false if not.
* @param strUuid UUID of the specified machine.
*/
{
bool fHandled = false;
do
{
#if 0
if ( balloonGetMaxSize(machine)
{
fHandled = true;
}
#endif
}
while (0);
return fHandled;
}
/**
* Adds a specified machine to the list (map) of handled machines.
* Does not do locking -- needs to be done by caller!
*
* @return IPRT status code.
* @param strUuid UUID of the specified machine.
*/
{
do
{
#if 0
if ( !balloonGetMaxSize(machine)
|| !machineIsRunning(machineState))
{
/* This machine does not need to be added, just skip it! */
break;
}
#endif
////// TODO: Put this in module!
/*
* Setup metrics.
*/
#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
5 /* 5 seconds */,
1 /* One sample is enough */,
#else
5 /* 5 seconds */,
1 /* One sample is enough */,
#endif
///////////////// TODO END
/*
* Add machine to map.
*/
/* Register all module payloads. */
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
{
}
} while (0);
/** @todo Add std exception handling! */
}
/**
* Removes a specified machine from the list of handled machines.
* Does not do locking -- needs to be done by caller!
*
* @return IPRT status code.
* @param strUuid UUID of the specified machine.
*/
{
int rc = VINF_SUCCESS;
{
/* Must log before erasing the iterator because of the UUID ref! */
/*
* Remove machine from map.
*/
}
else
{
rc = VERR_NOT_FOUND;
}
return rc;
}
#if 0
/**
* Updates a specified machine according to its current machine state.
* That currently also could mean that a machine gets removed if it doesn't
* fit in our criteria anymore or a machine gets added if we need to handle
* it now (and didn't before).
* Does not do locking -- needs to be done by caller!
*
* @return IPRT status code.
* @param strUuid UUID of the specified machine.
* @param enmState The machine's current state.
*/
{
int rc = VINF_SUCCESS;
{
if (machineHandled(strUuid))
{
if (RT_SUCCESS(rc))
}
else
{
serviceLogVerbose(("Machine \"%ls\" (state: %u) does not need to be updated\n",
}
}
{
/*
* Ballooning stuff - start.
*/
#if 0
/* Our actual ballooning criteria. */
|| !machineIsRunning(enmState))
{
/* Current machine is not suited for ballooning anymore -
* remove it from our map. */
}
else
{
}
#endif
}
/*
* Ballooning stuff - end.
*/
return rc;
}
#endif
static void vmListDestroy()
{
serviceLogVerbose(("Destroying VM list ...\n"));
if (RT_SUCCESS(rc))
{
{
#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
#endif
it++;
}
}
}
static int vmListBuild()
{
serviceLogVerbose(("Building VM list ...\n"));
if (RT_SUCCESS(rc))
{
/*
* Make sure the list is empty.
*/
/*
* Get the list of all _running_ VMs
*/
{
/*
* Iterate through the collection
*/
{
if (machines[i])
{
if (!fAccessible)
{
serviceLogVerbose(("Machine \"%ls\" is inaccessible, skipping\n",
continue;
}
if (RT_FAILURE(rc))
break;
}
}
serviceLogVerbose(("No machines to add found at the moment!\n"));
}
if (RT_SUCCESS(rc))
}
return rc;
}
/**
* Lazily calls the pfnPreInit method on each service.
*
* @returns VBox status code, error message displayed.
*/
static int watchdogLazyPreInit(void)
{
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (!g_aModules[j].fPreInited)
{
if (RT_FAILURE(rc))
{
serviceLog("Module '%s' failed pre-init: %Rrc\n",
return rc;
}
g_aModules[j].fPreInited = true;
}
return VINF_SUCCESS;
}
/**
* Starts all registered modules.
*
* @return IPRT status code.
* @return int
*/
static int watchdogStartModules()
{
int rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
if (RT_FAILURE(rc))
{
if (rc != VERR_SERVICE_DISABLED)
{
serviceLog("Module '%s' failed to initialize: %Rrc\n",
return rc;
}
g_aModules[j].fEnabled = false;
serviceLog(0, "Module '%s' was disabled because of missing functionality\n",
}
}
return rc;
}
static int watchdogShutdownModules()
{
int rc = VINF_SUCCESS;
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
if (RT_FAILURE(rc2))
{
serviceLog("Module '%s' failed to stop: %Rrc\n",
/* Keep original rc. */
if (RT_SUCCESS(rc))
}
/* Keep going. */
}
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
}
return rc;
}
{
do
{
int vrc = VINF_SUCCESS;
/* Initialize global weak references. */
/*
* Install signal handlers.
*/
#ifdef SIGBREAK
#endif
/*
* Setup the global event listeners:
* - g_pEventSource for machine events
* - g_pEventSourceClient for VBoxClient events (like VBoxSVC handling)
*/
eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged); /* Processed by g_pEventSourceClient. */
CHECK_ERROR_BREAK(g_pEventSource, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
CHECK_ERROR_BREAK(g_pEventSourceClient, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
/*
* Set up modules.
*/
rc = watchdogStartModules();
break;
for (;;)
{
/*
* Do the actual work.
*/
/*
vrc = balloonCtrlCheck();
if (RT_FAILURE(vrc))
{
serviceLog("Error while doing ballooning control; rc=%Rrc\n", vrc);
break;
}*/
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
if (g_aModules[j].fEnabled)
{
if (RT_FAILURE(rc2))
serviceLog("Module '%s' reported an error: %Rrc\n",
/* Keep going. */
}
/*
* Process pending events, then wait for new ones. Note, this
* processes NULL events signalling event loop termination.
*/
if (g_fCanceled)
{
serviceLog("Signal caught, exiting ...\n");
break;
}
}
#ifdef SIGBREAK
#endif
/* VirtualBox callback unregistration. */
if (g_pVBoxEventListener)
{
if (!g_pEventSource.isNull())
}
if (RT_FAILURE(vrc))
} while (0);
}
void serviceLog(const char *pszFormat, ...)
{
}
{
/* Some introductory information. */
static RTTIMESPEC s_TimeSpec;
char szTmp[256];
if (enmPhase == RTLOGPHASE_BEGIN)
switch (enmPhase)
{
case RTLOGPHASE_BEGIN:
{
"VirtualBox Watchdog %s r%u %s (%s %s) release log\n"
#ifdef VBOX_BLEEDING_EDGE
#endif
"Log opened %s\n",
/* the package type is interesting for Linux distributions */
char szExecName[RTPATH_MAX];
"Executable: %s\n"
"Process ID: %u\n"
"Package type: %s"
#ifdef VBOX_OSE
" (OSE)"
#endif
"\n",
RTProcSelf(),
break;
}
case RTLOGPHASE_PREROTATE:
break;
case RTLOGPHASE_POSTROTATE:
break;
case RTLOGPHASE_END:
break;
default:
/* nothing */;
}
}
static void displayHeader()
{
"All rights reserved.\n\n");
}
/**
* Displays the help.
*
* @param pszImage Name of program name (image).
*/
static void displayHelp(const char *pszImage)
{
pszImage);
for (unsigned i = 0;
i < RT_ELEMENTS(g_aOptions);
++i)
{
{
str += ", -";
}
str += ":";
const char *pcszDescr = "";
switch (g_aOptions[i].iShort)
{
case 'h':
pcszDescr = "Print this help message and exit.";
break;
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
case 'b':
pcszDescr = "Run in background (daemon mode).";
break;
#endif
case 'P':
pcszDescr = "Name of the PID file which is created when the daemon was started.";
break;
case 'F':
pcszDescr = "Name of file to write log to (no file).";
break;
case 'R':
pcszDescr = "Number of log files (0 disables log rotation).";
break;
case 'S':
pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
break;
case 'I':
pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
break;
}
}
for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
{
}
/** @todo Change VBOXBALLOONCTRL_RELEASE_LOG to WATCHDOG*. */
RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXBALLOONCTRL_RELEASE_LOG for logging options.\n");
}
/**
* Creates all global COM objects.
*
* @return HRESULT
*/
static HRESULT watchdogSetup()
{
serviceLogVerbose(("Creating local objects ...\n"));
{
}
else
{
}
do
{
/*
* Setup metrics.
*/
#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
#endif
/*
* Build up initial VM list.
*/
int vrc = vmListBuild();
if (RT_FAILURE(vrc))
{
break;
}
} while (0);
return rc;
}
static void watchdogTeardown()
{
serviceLogVerbose(("Deleting local objects ...\n"));
#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
#endif
}
{
/*
* Before we do anything, init the runtime without loading
* the support driver.
*/
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/*
* Parse the global options
*/
int c;
const char *pszLogFile = NULL;
const char *pszPidFile = NULL;
{
switch (c)
{
case 'h':
displayHelp(argv[0]);
return 0;
case 'v':
g_fVerbose = true;
break;
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
case 'b':
g_fDaemonize = true;
break;
#endif
case 'V':
return 0;
case 'P':
break;
case 'F':
break;
case 'R':
break;
case 'S':
break;
case 'I':
break;
default:
{
bool fFound = false;
/** @todo Add "--disable-<module>" etc. here! */
if (!fFound)
{
rc = watchdogLazyPreInit();
if (RT_SUCCESS(rc))
return RTEXITCODE_FAILURE;
{
if (fFound)
break;
if (rc != -1)
return rc;
}
}
if (!fFound)
return RTGetOptPrintError(c, &ValueUnion);
continue;
}
}
}
/** @todo Add "--quiet/-q" option to not show the header. */
/* create release logger */
static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
#endif
if (RT_SUCCESS(rc))
{
/* register this logger as the release logger */
/* Explicitly flush the log in case of VBOXWEBSRV_RELEASE_LOG=buffered. */
}
else
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
if (g_fDaemonize)
{
/* prepare release logging */
char szLogFile[RTPATH_MAX];
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
/* create release logger */
static const char * const s_apszGroupsFile[] = VBOX_LOGGROUP_NAMES;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
#endif
if (RT_SUCCESS(vrc))
{
/* register this logger as the release logger */
/* Explicitly flush the log in case of VBOXBALLOONCTRL_RELEASE_LOG=buffered. */
}
else
return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szErrorFile, vrc);
}
#endif
#ifndef VBOX_ONLY_DOCS
/*
* Initialize COM.
*/
using namespace com;
{
{
RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
}
else
return RTEXITCODE_FAILURE;
}
hrc = watchdogSetup();
return RTEXITCODE_FAILURE;
return rcExit;
#else /* VBOX_ONLY_DOCS */
return RTEXITCODE_SUCCESS;
#endif /* VBOX_ONLY_DOCS */
}