Index: trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp	(revision 39988)
+++ trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp	(revision 40010)
@@ -22,37 +22,6 @@
 *******************************************************************************/
 #ifndef VBOX_ONLY_DOCS
-# include <VBox/com/com.h>
-# include <VBox/com/string.h>
-# include <VBox/com/Guid.h>
-# include <VBox/com/array.h>
-# include <VBox/com/ErrorInfo.h>
 # include <VBox/com/errorprint.h>
-
-# include <VBox/com/EventQueue.h>
-# include <VBox/com/listeners.h>
-# include <VBox/com/VirtualBox.h>
 #endif /* !VBOX_ONLY_DOCS */
-
-#include <VBox/err.h>
-#include <VBox/log.h>
-#include <VBox/version.h>
-
-#include <package-generated.h>
-
-#include <iprt/asm.h>
-#include <iprt/buildconfig.h>
-#include <iprt/critsect.h>
-#include <iprt/getopt.h>
-#include <iprt/initterm.h>
-#include <iprt/path.h>
-#include <iprt/process.h>
-#include <iprt/semaphore.h>
-#include <iprt/stream.h>
-#include <iprt/string.h>
-#include <iprt/system.h>
-
-#include <map>
-#include <string>
-#include <signal.h>
 
 #include "VBoxWatchdogInternal.h"
@@ -65,7 +34,7 @@
  * The module's RTGetOpt-IDs for the command line.
  */
-enum GETOPTDEF_APIMON
-{
-    GETOPTDEF_APIMON_ISLN_RESPONSE = 1000,
+static enum GETOPTDEF_APIMON
+{
+    GETOPTDEF_APIMON_ISLN_RESPONSE = 3000,
     GETOPTDEF_APIMON_ISLN_TIMEOUT,
     GETOPTDEF_APIMON_GROUPS
@@ -81,7 +50,93 @@
 };
 
-static unsigned long g_ulAPIMonIslnTimeoutMS = 0;
-static Bstr g_strAPIMonGroups;
-static Bstr g_strAPIMonIslnCmdResp;
+static enum APIMON_RESPONSE
+{
+    /** Unknown / unhandled response. */
+    APIMON_RESPONSE_UNKNOWN    = 0,
+    /** Tries to shut down all running VMs in
+     *  a gentle manner. */
+    APIMON_RESPONSE_SHUTDOWN   = 200
+};
+
+static Bstr                         g_strAPIMonGroups;
+
+static APIMON_RESPONSE              g_enmAPIMonIslnResp     = APIMON_RESPONSE_UNKNOWN;
+static unsigned long                g_ulAPIMonIslnTimeoutMS = 0;
+static Bstr                         g_strAPIMonIslnLastBeat;
+static uint64_t                     g_uAPIMonIslnLastBeatMS = 0;
+
+int apimonResponseToEnum(const char *pszResponse, APIMON_RESPONSE *pResp)
+{
+    AssertPtrReturn(pszResponse, VERR_INVALID_POINTER);
+    AssertPtrReturn(pResp, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+    if (   !RTStrICmp(pszResponse, "shutdown")
+        || !RTStrICmp(pszResponse, "poweroff"))
+    {
+        *pResp = APIMON_RESPONSE_SHUTDOWN;
+    }
+    else
+        *pResp = APIMON_RESPONSE_UNKNOWN;
+
+    return (*pResp > APIMON_RESPONSE_UNKNOWN ? VINF_SUCCESS : VERR_INVALID_PARAMETER);
+}
+
+int apimonMachineControl(const Bstr &strUuid, PVBOXWATCHDOG_MACHINE pMachine,
+                         APIMON_RESPONSE enmResp)
+{
+    /** @todo Add other commands (with enmResp) here. */
+    AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
+
+    serviceLog("Shutting down machine \"%ls\"\n", strUuid.raw());
+
+    /* Open a session for the VM. */
+    HRESULT rc;
+    CHECK_ERROR_RET(pMachine->machine, LockMachine(g_pSession, LockType_Shared), VERR_ACCESS_DENIED);
+    do
+    {
+        /* get the associated console */
+        ComPtr<IConsole> console;
+        CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
+
+        if (!g_fDryrun)
+        {
+            ComPtr<IProgress> progress;
+            CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
+            if (g_fVerbose)
+            {
+                serviceLogVerbose(("Waiting for shutting down machine \"%ls\" ...\n",
+                                   strUuid.raw()));
+                progress->WaitForCompletion(-1);
+            }
+        }
+    } while (0);
+
+    /* Unlock the machine again. */
+    g_pSession->UnlockMachine();
+
+    return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR;
+}
+
+int apimonTrigger(APIMON_RESPONSE enmResp)
+{
+    int rc = VINF_SUCCESS;
+
+    /** @todo Add proper grouping support! */
+    bool fAllGroups = g_strAPIMonGroups.isEmpty();
+    mapVMIter it = g_mapVM.begin();
+    while (it != g_mapVM.end())
+    {
+        if (   !it->second.group.compare(g_strAPIMonGroups, Bstr::CaseInsensitive)
+            || fAllGroups)
+        {
+            rc = apimonMachineControl(it->first /* Uuid */,
+                                       &it->second, enmResp);
+        }
+        it++;
+    }
+
+    return rc;
+}
 
 /* Callbacks. */
@@ -114,4 +169,7 @@
         {
             case GETOPTDEF_APIMON_ISLN_RESPONSE:
+                rc = apimonResponseToEnum(ValueUnion.psz, &g_enmAPIMonIslnResp);
+                if (RT_FAILURE(rc))
+                    rc = -1; /* Option unknown. */
                 break;
 
@@ -123,4 +181,5 @@
 
             case GETOPTDEF_APIMON_GROUPS:
+                g_strAPIMonGroups = ValueUnion.psz;
                 break;
 
@@ -140,16 +199,19 @@
     do
     {
-        Bstr bstrValue;
+        Bstr strValue;
 
         /* Host isolation timeout (in ms). */
         if (!g_ulAPIMonIslnTimeoutMS) /* Not set by command line? */
         {
-            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("VBoxInternal2/Watchdog/APIMonitor/IsolationTimeout").raw(),
-                                                          bstrValue.asOutParam()));
-            g_ulAPIMonIslnTimeoutMS = Utf8Str(bstrValue).toUInt32();
-        }
-        if (!g_ulAPIMonIslnTimeoutMS)
-        {
-             /* Default is 30 seconds timeout. */
+            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/IsolationTimeout").raw(),
+                                                          strValue.asOutParam()));
+            if (!strValue.isEmpty())
+                g_ulAPIMonIslnTimeoutMS = Utf8Str(strValue).toUInt32();
+        }
+        if (!g_ulAPIMonIslnTimeoutMS) /* Still not set? Use a default. */
+        {
+            serviceLogVerbose(("API monitor isolation timeout not given, defaulting to 30s\n"));
+
+            /* Default is 30 seconds timeout. */
             g_ulAPIMonIslnTimeoutMS = 30 * 1000;
         }
@@ -158,16 +220,33 @@
         if (g_strAPIMonGroups.isEmpty()) /* Not set by command line? */
         {
-            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("VBoxInternal2/Watchdog/APIMonitor/Groups").raw(),
+            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/Groups").raw(),
                                                           g_strAPIMonGroups.asOutParam()));
         }
 
         /* Host isolation command response. */
-        if (g_strAPIMonIslnCmdResp.isEmpty()) /* Not set by command line? */
-        {
-            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("VBoxInternal2/Watchdog/APIMonitor/IsolationResponse").raw(),
-                                                          g_strAPIMonIslnCmdResp.asOutParam()));
+        if (g_enmAPIMonIslnResp == APIMON_RESPONSE_UNKNOWN) /* Not set by command line? */
+        {
+            CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/IsolationResponse").raw(),
+                                                          strValue.asOutParam()));
+            if (!strValue.isEmpty())
+            {
+                int rc2 = apimonResponseToEnum(Utf8Str(strValue).c_str(), &g_enmAPIMonIslnResp);
+                if (RT_FAILURE(rc2))
+                {
+                    serviceLog("Warning: API monitor response string invalid (%ls), default to shutdown\n",
+                               strValue.raw());
+                    g_enmAPIMonIslnResp = APIMON_RESPONSE_SHUTDOWN;
+                }
+            }
+            else
+                g_enmAPIMonIslnResp = APIMON_RESPONSE_SHUTDOWN;
         }
     } while (0);
 
+    if (SUCCEEDED(rc))
+    {
+        g_uAPIMonIslnLastBeatMS = 0;
+    }
+
     return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /* @todo Find a better rc! */
 }
@@ -175,5 +254,49 @@
 static DECLCALLBACK(int) VBoxModAPIMonitorMain(void)
 {
-    return VINF_SUCCESS; /* Nothing to do here right now. */
+    static uint64_t uLastRun = 0;
+    uint64_t uNow = RTTimeProgramMilliTS();
+    uint64_t uDelta = uNow - uLastRun;
+    if (uDelta < 1000)
+        return VINF_SUCCESS;
+    uLastRun = uNow;
+
+    int vrc = VINF_SUCCESS;
+    HRESULT rc;
+
+    serviceLogVerbose(("Checking for API heartbeat (%RU64ms) ...\n",
+                       g_ulAPIMonIslnTimeoutMS));
+
+    do
+    {
+        Bstr strHeartbeat;
+        CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/Heartbeat").raw(),
+                                                      strHeartbeat.asOutParam()));
+        if (   SUCCEEDED(rc)
+            && !strHeartbeat.isEmpty()
+            && g_strAPIMonIslnLastBeat.compare(strHeartbeat, Bstr::CaseSensitive))
+        {
+            serviceLogVerbose(("API heartbeat received, resetting timeout\n"));
+
+            g_uAPIMonIslnLastBeatMS = 0;
+            g_strAPIMonIslnLastBeat = strHeartbeat;
+        }
+        else
+        {
+            g_uAPIMonIslnLastBeatMS += uDelta;
+            if (g_uAPIMonIslnLastBeatMS > g_ulAPIMonIslnTimeoutMS)
+            {
+                serviceLogVerbose(("No API heartbeat within time received (%RU64ms)\n",
+                                   g_ulAPIMonIslnTimeoutMS));
+
+                vrc = apimonTrigger(g_enmAPIMonIslnResp);
+                g_uAPIMonIslnLastBeatMS = 0;
+            }
+        }
+    } while (0);
+
+    if (FAILED(rc))
+        vrc = VERR_COM_IPRT_ERROR;
+
+    return vrc;
 }
 
@@ -205,4 +328,6 @@
 static DECLCALLBACK(int) VBoxModAPIMonitorOnServiceStateChanged(bool fAvailable)
 {
+    if (!fAvailable)
+        apimonTrigger(g_enmAPIMonIslnResp);
     return VINF_SUCCESS;
 }
@@ -225,7 +350,7 @@
     " [--apimon-groups=<string>]\n",
     /* pszOptions. */
-    "--apimon-isln-response   Sets the isolation response (shutdown VM).\n"
-    "--apimon-isln-timeout    Sets the isolation timeout in ms (none).\n"
-    "--apimon-groups          Sets the VM groups for monitoring (none).\n",
+    "--apimon-isln-response Sets the isolation response (shutdown VM).\n"
+    "--apimon-isln-timeout  Sets the isolation timeout in ms (30s).\n"
+    "--apimon-groups        Sets the VM groups for monitoring (none).\n",
     /* methods. */
     VBoxModAPIMonitorPreInit,
Index: trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp	(revision 39988)
+++ trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp	(revision 40010)
@@ -21,38 +21,7 @@
 *******************************************************************************/
 #ifndef VBOX_ONLY_DOCS
-# include <VBox/com/com.h>
-# include <VBox/com/string.h>
-# include <VBox/com/Guid.h>
-# include <VBox/com/array.h>
-# include <VBox/com/ErrorInfo.h>
 # include <VBox/com/errorprint.h>
-
-# include <VBox/com/EventQueue.h>
-# include <VBox/com/listeners.h>
-# include <VBox/com/VirtualBox.h>
 #endif /* !VBOX_ONLY_DOCS */
 
-#include <VBox/err.h>
-#include <VBox/log.h>
-#include <VBox/version.h>
-
-#include <package-generated.h>
-
-#include <iprt/asm.h>
-#include <iprt/buildconfig.h>
-#include <iprt/critsect.h>
-#include <iprt/getopt.h>
-#include <iprt/initterm.h>
-#include <iprt/path.h>
-#include <iprt/process.h>
-#include <iprt/semaphore.h>
-#include <iprt/stream.h>
-#include <iprt/string.h>
-#include <iprt/system.h>
-
-#include <map>
-#include <string>
-#include <signal.h>
-
 #include "VBoxWatchdogInternal.h"
 
@@ -64,7 +33,7 @@
  * The module's RTGetOpt-IDs for the command line.
  */
-enum GETOPTDEF_BALLOONCTRL
-{
-    GETOPTDEF_BALLOONCTRL_BALLOOINC = 1000,
+static enum GETOPTDEF_BALLOONCTRL
+{
+    GETOPTDEF_BALLOONCTRL_BALLOOINC = 2000,
     GETOPTDEF_BALLOONCTRL_BALLOONDEC,
     GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,
@@ -349,24 +318,27 @@
             HRESULT rc;
 
-            /* Open a session for the VM. */
-            CHECK_ERROR(pMachine->machine, LockMachine(g_pSession, LockType_Shared));
-
-            do
+            if (!g_fDryrun)
             {
-                /* Get the associated console. */
-                ComPtr<IConsole> console;
-                CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
-
-                ComPtr <IGuest> guest;
-                rc = console->COMGETTER(Guest)(guest.asOutParam());
-                if (SUCCEEDED(rc))
-                    CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)(lBalloonCur));
-                else
-                    serviceLog("Error: Unable to set new balloon size %ld for machine \"%ls\", rc=%Rhrc",
-                               lBalloonCur, strUuid.raw(), rc);
-            } while (0);
-
-            /* Unlock the machine again. */
-            g_pSession->UnlockMachine();
+                /* Open a session for the VM. */
+                CHECK_ERROR(pMachine->machine, LockMachine(g_pSession, LockType_Shared));
+
+                do
+                {
+                    /* Get the associated console. */
+                    ComPtr<IConsole> console;
+                    CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
+
+                    ComPtr <IGuest> guest;
+                    rc = console->COMGETTER(Guest)(guest.asOutParam());
+                    if (SUCCEEDED(rc))
+                        CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)(lBalloonCur));
+                    else
+                        serviceLog("Error: Unable to set new balloon size %ld for machine \"%ls\", rc=%Rhrc",
+                                   lBalloonCur, strUuid.raw(), rc);
+                } while (0);
+
+                /* Unlock the machine again. */
+                g_pSession->UnlockMachine();
+            }
         }
     }
Index: trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp	(revision 39988)
+++ trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp	(revision 40010)
@@ -54,4 +54,5 @@
 
 
+#include <algorithm>
 #include <string>
 #include <signal.h>
@@ -61,13 +62,11 @@
 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_fDryrun     = false;
 bool                                g_fVerbose    = false;
 ComPtr<IVirtualBox>                 g_pVirtualBox = NULL;
 ComPtr<ISession>                    g_pSession    = NULL;
 mapVM                               g_mapVM;
+mapGroup                            g_mapGroup;
 # ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
 ComPtr<IPerformanceCollector>       g_pPerfCollector = NULL;
@@ -107,4 +106,9 @@
 };
 
+static enum GETOPTDEF_WATCHDOG
+{
+    GETOPTDEF_WATCHDOG_DRYRUN = 1000
+};
+
 /**
  * Command line arguments.
@@ -115,4 +119,5 @@
 #endif
     /** For displayHelp(). */
+    { "--dryrun",               GETOPTDEF_WATCHDOG_DRYRUN,                 RTGETOPT_REQ_NOTHING },
     { "--help",                 'h',                                       RTGETOPT_REQ_NOTHING },
     { "--verbose",              'v',                                       RTGETOPT_REQ_NOTHING },
@@ -124,23 +129,14 @@
 };
 
-static unsigned long g_ulTimeoutMS = 500; /* Default is 500ms timeout. */
-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. */
 static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
-static ComPtr<IEventSource> g_pEventSource = NULL;
-static ComPtr<IEventSource> g_pEventSourceClient = NULL;
-static ComPtr<IEventListener> g_pVBoxEventListener = NULL;
-static EventQueue *g_pEventQ = NULL;
+static ComPtr<IEventSource>      g_pEventSource = NULL;
+static ComPtr<IEventSource>      g_pEventSourceClient = NULL;
+static ComPtr<IEventListener>    g_pVBoxEventListener = NULL;
+static EventQueue               *g_pEventQ = NULL;
 
 /* Prototypes. */
 static int machineAdd(const Bstr &strUuid);
 static int machineRemove(const Bstr &strUuid);
-//static int machineUpdate(const Bstr &strUuid, MachineState_T enmState);
 static HRESULT watchdogSetup();
 static void watchdogShutdown();
@@ -345,25 +341,61 @@
     HRESULT rc;
 
+    /** @todo Add exception handling! */
+
     do
     {
         ComPtr <IMachine> machine;
         CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine(strUuid.raw(), machine.asOutParam()));
-
-        MachineState_T machineState;
-        CHECK_ERROR_BREAK(machine, COMGETTER(State)(&machineState));
-
-        VBOXWATCHDOG_MACHINE m;
-        m.machine = machine;
+        Assert(!machine.isNull());
+
+        Bstr strGroup;
+        CHECK_ERROR_BREAK(machine, GetExtraData(Bstr("VBoxInternal2/VMGroup").raw(),
+                                                strGroup.asOutParam()));
 
         /*
          * Add machine to map.
          */
+        VBOXWATCHDOG_MACHINE m;
+        m.machine = machine;
+        m.group = strGroup;
+
         mapVMIter it = g_mapVM.find(strUuid);
         Assert(it == g_mapVM.end());
         g_mapVM.insert(std::make_pair(strUuid, m));
-
         serviceLogVerbose(("Added machine \"%ls\"\n", strUuid.raw()));
 
-        /* Let all modules know. */
+        /*
+         * Get the machine's VM group(s).
+         */
+        if (!strGroup.isEmpty())
+        {
+            serviceLogVerbose(("Machine \"%ls\" is in VM group \"%ls\"\n",
+                               strUuid.raw(), strGroup.raw()));
+
+            /** @todo Support more than one group! */
+            /* Add machine to group(s). */
+            mapGroupIter itGroup = g_mapGroup.find(strGroup);
+            if (itGroup == g_mapGroup.end())
+            {
+                vecGroupMembers vecMembers;
+                vecMembers.push_back(strUuid);
+                g_mapGroup.insert(std::make_pair(strGroup, vecMembers));
+
+                itGroup = g_mapGroup.find(strGroup);
+                Assert(itGroup != g_mapGroup.end());
+            }
+            else
+                itGroup->second.push_back(strUuid);
+            serviceLogVerbose(("Group \"%ls\" now has %ld machine(s)\n",
+                               strGroup.raw(), itGroup->second.size()));
+        }
+        else
+            serviceLogVerbose(("Machine \"%ls\" has no VM group assigned\n",
+                               strUuid.raw()));
+
+        /*
+         * Let all modules know. Typically all modules would register
+         * their per-machine payload here.
+         */
         for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
             if (g_aModules[j].fEnabled)
@@ -399,19 +431,40 @@
         }
 
-    mapVMIter it = g_mapVM.find(strUuid);
-    Assert(it != g_mapVM.end());
-
-#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
-    it->second.collector.setNull();
-#endif
-    it->second.machine.setNull();
-
     /* Must log before erasing the iterator because of the UUID ref! */
     serviceLogVerbose(("Removing machine \"%ls\"\n", strUuid.raw()));
+
+    mapVMIter itVM = g_mapVM.find(strUuid);
+    Assert(itVM != g_mapVM.end());
+
+    /* Remove machine from group(s). */
+    /** @todo Add support for multiple groups! */
+    Bstr strGroup = itVM->second.group;
+    if (!strGroup.isEmpty())
+    {
+        mapGroupIter itGroup = g_mapGroup.find(strGroup);
+        Assert(itGroup != g_mapGroup.end());
+
+        vecGroupMembers vecMembers = itGroup->second;
+        vecGroupMembersIter itMember = std::find(vecMembers.begin(),
+                                                 vecMembers.end(),
+                                                 strUuid);
+        Assert(itMember != vecMembers.end());
+        vecMembers.erase(itMember);
+
+        serviceLogVerbose(("Group \"%ls\" has %ld machines left\n",
+                           itGroup->first, vecMembers.size()));
+        if (!vecMembers.size())
+            g_mapGroup.erase(itGroup);
+    }
+
+#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
+    itVM->second.collector.setNull();
+#endif
+    itVM->second.machine.setNull();
 
     /*
      * Remove machine from map.
      */
-    g_mapVM.erase(it);
+    g_mapVM.erase(itVM);
 
     return rc;
@@ -440,5 +493,4 @@
                 rc = rc2;
         }
-
     }
     else
@@ -671,5 +723,5 @@
                         if (RT_FAILURE(rc2))
                             serviceLog("Module '%s' reported an error: %Rrc\n",
-                                       g_aModules[j].pDesc->pszName, rc);
+                                       g_aModules[j].pDesc->pszName, rc2);
                         /* Keep going. */
                     }
@@ -685,5 +737,5 @@
              * processes NULL events signalling event loop termination.
              */
-            g_pEventQ->processEventQueue(g_ulTimeoutMS / 10);
+            g_pEventQ->processEventQueue(500 / 10);
 
             if (g_fCanceled)
@@ -848,4 +900,8 @@
         switch (g_aOptions[i].iShort)
         {
+            case GETOPTDEF_WATCHDOG_DRYRUN:
+                pcszDescr = "Dryrun mode -- do not perform any actions.";
+                break;
+
             case 'h':
                 pcszDescr = "Print this help message and exit.";
@@ -984,4 +1040,8 @@
         switch (c)
         {
+            case GETOPTDEF_WATCHDOG_DRYRUN:
+                g_fDryrun = true;
+                break;
+
             case 'h':
                 displayHelp(argv[0]);
@@ -1030,5 +1090,5 @@
                 {
                     rc = watchdogLazyPreInit();
-                    if (RT_SUCCESS(rc))
+                    if (RT_FAILURE(rc))
                         return RTEXITCODE_FAILURE;
 
@@ -1143,4 +1203,7 @@
     }
 
+    if (g_fDryrun)
+        serviceLog("Running in dryrun mode\n");
+
     hrc = watchdogSetup();
     if (FAILED(hrc))
Index: trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h
===================================================================
--- trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h	(revision 39988)
+++ trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h	(revision 40010)
@@ -20,11 +20,14 @@
 
 #ifndef VBOX_ONLY_DOCS
-#include <VBox/com/com.h>
-#include <VBox/com/assert.h>
-#include <VBox/com/string.h>
-#include <VBox/com/VirtualBox.h>
-
-#include <iprt/getopt.h>
-#include <iprt/time.h>
+# include <iprt/getopt.h>
+# include <iprt/time.h>
+
+# include <VBox/err.h>
+# include <VBox/com/com.h>
+# include <VBox/com/string.h>
+# include <VBox/com/Guid.h>
+# include <VBox/com/array.h>
+# include <VBox/com/ErrorInfo.h>
+# include <VBox/com/VirtualBox.h>
 #endif /* !VBOX_ONLY_DOCS */
 
@@ -81,4 +84,6 @@
     ComPtr<IPerformanceCollector> collector;
 #endif
+    /** The machine's VM group(s). */
+    Bstr group;
     /** Map containing the individual
      *  module payloads. */
@@ -89,14 +94,13 @@
 typedef std::map<Bstr, VBOXWATCHDOG_MACHINE>::const_iterator mapVMIterConst;
 
-/** A VM group entry. Note that a machine can belong
- *  to more than one group. */
-typedef struct VBOXWATCHDOG_VM_GROUP
-{
-    /** UUIDs of machines belonging to this group. */
-    std::vector<Bstr> machine;
-} VBOXWATCHDOG_VM_GROUP;
-typedef std::map<Bstr, VBOXWATCHDOG_VM_GROUP> mapGroup;
-typedef std::map<Bstr, VBOXWATCHDOG_VM_GROUP>::iterator mapGroupIter;
-typedef std::map<Bstr, VBOXWATCHDOG_VM_GROUP>::const_iterator mapGroupIterConst;
+/** Members of a VM group; currently only represented by the machine's UUID. */
+typedef std::vector<Bstr> vecGroupMembers;
+typedef std::vector<Bstr>::iterator vecGroupMembersIter;
+typedef std::vector<Bstr>::const_iterator vecGroupMembersIterConst;
+
+/** A VM group. Can contain none, one or more group members. */
+typedef std::map<Bstr, vecGroupMembers> mapGroup;
+typedef std::map<Bstr, vecGroupMembers>::iterator mapGroupIter;
+typedef std::map<Bstr, vecGroupMembers>::const_iterator mapGroupIterConst;
 
 /**
@@ -195,8 +199,10 @@
 RT_C_DECLS_BEGIN
 
-extern bool g_fVerbose;
+extern bool                             g_fDryrun;
+extern bool                             g_fVerbose;
 extern ComPtr<IVirtualBox>              g_pVirtualBox;
 extern ComPtr<ISession>                 g_pSession;
 extern mapVM                            g_mapVM;
+extern mapGroup                         g_mapGroup;
 # ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
 extern ComPtr<IPerformanceCollector>    g_pPerfCollector;
