Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp	(revision 41285)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp	(revision 41286)
@@ -22,4 +22,5 @@
 *******************************************************************************/
 #ifndef VBOX_ONLY_DOCS
+# include <iprt/message.h>
 # include <VBox/com/errorprint.h>
 #endif /* !VBOX_ONLY_DOCS */
@@ -53,18 +54,22 @@
 {
     /** Unknown / unhandled response. */
-    APIMON_RESPONSE_UNKNOWN    = 0,
+    APIMON_RESPONSE_NONE       = 0,
+    /** 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   = 200
+    APIMON_RESPONSE_SHUTDOWN   = 300
 };
 
-static Bstr                         g_strAPIMonGroups;
-
-static APIMON_RESPONSE              g_enmAPIMonIslnResp     = APIMON_RESPONSE_UNKNOWN;
+/** The VM group(s) the API monitor handles. If none, all VMs get handled. */
+static mapGroups                    g_vecAPIMonGroups;
+static APIMON_RESPONSE              g_enmAPIMonIslnResp     = APIMON_RESPONSE_NONE;
 static unsigned long                g_ulAPIMonIslnTimeoutMS = 0;
 static Bstr                         g_strAPIMonIslnLastBeat;
 static uint64_t                     g_uAPIMonIslnLastBeatMS = 0;
 
-int apimonResponseToEnum(const char *pszResponse, APIMON_RESPONSE *pResp)
+static int apimonResponseToEnum(const char *pszResponse, APIMON_RESPONSE *pResp)
 {
     AssertPtrReturn(pszResponse, VERR_INVALID_POINTER);
@@ -72,43 +77,211 @@
 
     int rc = VINF_SUCCESS;
-    if (   !RTStrICmp(pszResponse, "shutdown")
-        || !RTStrICmp(pszResponse, "poweroff"))
+    if (   !RTStrICmp(pszResponse, "poweroff")
+        || !RTStrICmp(pszResponse, "powerdown"))
+    {
+        *pResp = APIMON_RESPONSE_POWEROFF;
+    }
+    else if (   !RTStrICmp(pszResponse, "shutdown")
+             || !RTStrICmp(pszResponse, "shutoff"))
     {
         *pResp = APIMON_RESPONSE_SHUTDOWN;
     }
+    else if (!RTStrICmp(pszResponse, "save"))
+    {
+        *pResp = APIMON_RESPONSE_SAVE;
+    }
     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)
+    {
+        *pResp = APIMON_RESPONSE_NONE;
+        rc = VERR_INVALID_PARAMETER;
+    }
+
+    return rc;
+}
+
+static const char* apimonResponseToStr(APIMON_RESPONSE enmResp)
+{
+    if (APIMON_RESPONSE_POWEROFF == enmResp)
+        return "powering off";
+    else if (APIMON_RESPONSE_SHUTDOWN == enmResp)
+        return "shutting down";
+    else if (APIMON_RESPONSE_SAVE == enmResp)
+        return "saving state";
+    else if (APIMON_RESPONSE_NONE == enmResp)
+        return "none";
+
+    return "unknown";
+}
+
+/* Copied from VBoxManageInfo.cpp. */
+static const char *apimonMachineStateToName(MachineState_T machineState, bool fShort)
+{
+    switch (machineState)
+    {
+        case MachineState_PoweredOff:
+            return fShort ? "poweroff"             : "powered off";
+        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:
+            return fShort ? "gurumeditation"       : "guru meditation";
+        case MachineState_LiveSnapshotting:
+            return fShort ? "livesnapshotting"     : "live snapshotting";
+        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_TeleportingPausedVM:
+            return fShort ? "teleportingpausedvm"  : "teleporting paused vm";
+        case MachineState_TeleportingIn:
+            return fShort ? "teleportingin"        : "teleporting (incoming)";
+        case MachineState_RestoringSnapshot:
+            return fShort ? "restoringsnapshot"    : "restoring snapshot";
+        case MachineState_DeletingSnapshot:
+            return fShort ? "deletingsnapshot"     : "deleting snapshot";
+        case MachineState_DeletingSnapshotOnline:
+            return fShort ? "deletingsnapshotlive" : "deleting snapshot live";
+        case MachineState_DeletingSnapshotPaused:
+            return fShort ? "deletingsnapshotlivepaused" : "deleting snapshot live paused";
+        case MachineState_SettingUp:
+            return fShort ? "settingup"           : "setting up";
+        default:
+            break;
+    }
+    return "unknown";
+}
+
+static int apimonMachineControl(const Bstr &strUuid, PVBOXWATCHDOG_MACHINE pMachine,
+                                APIMON_RESPONSE enmResp, unsigned long ulTimeout)
 {
     /** @todo Add other commands (with enmResp) here. */
-    AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
-
-    serviceLog("Shutting down machine \"%ls\"\n", strUuid.raw());
+    AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
+
+    serviceLogVerbose(("apimon: Triggering \"%s\" (%RU32ms timeout) for machine \"%ls\"\n",
+                      apimonResponseToStr(enmResp), ulTimeout, strUuid.raw()));
+
+    if (   enmResp == APIMON_RESPONSE_NONE
+        || g_fDryrun)
+        return VINF_SUCCESS; /* Nothing to do. */
+
+    HRESULT rc;
+    ComPtr <IMachine> machine;
+    CHECK_ERROR_RET(g_pVirtualBox, FindMachine(strUuid.raw(),
+                                               machine.asOutParam()), VERR_NOT_FOUND);
 
     /* Open a session for the VM. */
-    HRESULT rc;
-    CHECK_ERROR_RET(pMachine->machine, LockMachine(g_pSession, LockType_Shared), VERR_ACCESS_DENIED);
+    CHECK_ERROR_RET(machine, LockMachine(g_pSession, LockType_Shared), VERR_ACCESS_DENIED);
+
     do
     {
-        /* get the associated console */
+
+        /* Get the associated console. */
         ComPtr<IConsole> console;
         CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
 
-        if (!g_fDryrun)
+        /* Query the machine's state to avoid unnecessary IPC. */
+        MachineState_T machineState;
+        CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
+        if (   machineState == MachineState_Running
+            || machineState == MachineState_Paused)
         {
             ComPtr<IProgress> progress;
-            CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
-            if (g_fVerbose)
+
+            switch (enmResp)
             {
-                serviceLogVerbose(("Waiting for shutting down machine \"%ls\" ...\n",
-                                   strUuid.raw()));
-                progress->WaitForCompletion(-1);
+                case APIMON_RESPONSE_POWEROFF:
+                    CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
+                    serviceLogVerbose(("apimon: Waiting for powering off machine \"%ls\" ...\n",
+                                       strUuid.raw()));
+                    progress->WaitForCompletion(ulTimeout);
+                    CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine \"%ls\"",
+                                         strUuid.raw()));
+                    break;
+
+                case APIMON_RESPONSE_SAVE:
+                {
+                    /* First pause so we don't trigger a live save which needs more time/resources. */
+                    bool fPaused = false;
+                    rc = console->Pause();
+                    if (FAILED(rc))
+                    {
+                        bool fError = true;
+                        if (rc == VBOX_E_INVALID_VM_STATE)
+                        {
+                            /* Check if we are already paused. */
+                            MachineState_T machineState;
+                            CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
+                            /* The error code was lost by the previous instruction. */
+                            rc = VBOX_E_INVALID_VM_STATE;
+                            if (machineState != MachineState_Paused)
+                            {
+                                serviceLog("apimon: Machine \"%s\" in invalid state %d -- %s\n",
+                                           strUuid.raw(), machineState, apimonMachineStateToName(machineState, false));
+                            }
+                            else
+                            {
+                                fError = false;
+                                fPaused = true;
+                            }
+                        }
+                        if (fError)
+                            break;
+                    }
+
+                    serviceLogVerbose(("apimon: Waiting for saving state of machine \"%ls\" ...\n",
+                                       strUuid.raw()));
+
+                    ComPtr<IProgress> progress;
+                    CHECK_ERROR(console, SaveState(progress.asOutParam()));
+                    if (FAILED(rc))
+                    {
+                        if (!fPaused)
+                            console->Resume();
+                        break;
+                    }
+
+                    progress->WaitForCompletion(ulTimeout);
+                    CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state of machine \"%ls\"",
+                                         strUuid.raw()));
+                    if (FAILED(rc))
+                    {
+                        if (!fPaused)
+                            console->Resume();
+                    }
+
+                    break;
+                }
+
+                case APIMON_RESPONSE_SHUTDOWN:
+                    CHECK_ERROR_BREAK(console, PowerButton());
+                    serviceLogVerbose(("apimon: Waiting for shutdown of machine \"%ls\" ...\n",
+                                       strUuid.raw()));
+                    progress->WaitForCompletion(ulTimeout);
+                    CHECK_PROGRESS_ERROR(progress, ("Failed to shutdown machine \"%ls\"",
+                                         strUuid.raw()));
+                    break;
+
+                default:
+                    AssertMsgFailed(("Response %d not implemented", enmResp));
+                    break;
             }
         }
+        else
+            serviceLog("apimon: Machine \"%s\" is in invalid state \"%s\" (%d) for triggering \"%s\"\n",
+                       strUuid.raw(), apimonMachineStateToName(machineState, false), machineState,
+                       apimonResponseToStr(enmResp));
     } while (0);
 
@@ -119,19 +292,68 @@
 }
 
-int apimonTrigger(APIMON_RESPONSE enmResp)
+static bool apimonHandleVM(const PVBOXWATCHDOG_MACHINE pMachine)
+{
+    bool fHandleVM = false;
+
+    try
+    {
+        mapGroupsIterConst itVMGroup = pMachine->groups.begin();
+        while (   itVMGroup != pMachine->groups.end()
+               && !fHandleVM)
+        {
+            mapGroupsIterConst itInGroup = g_vecAPIMonGroups.find(itVMGroup->first);
+            if (itInGroup != g_vecAPIMonGroups.end())
+                fHandleVM = true;
+
+            itVMGroup++;
+        }
+    }
+    catch (...)
+    {
+        AssertFailed();
+    }
+
+    return fHandleVM;
+}
+
+static int apimonTrigger(APIMON_RESPONSE enmResp)
 {
     int rc = VINF_SUCCESS;
 
-    /** @todo Add proper grouping support! */
-    bool fAllGroups = g_strAPIMonGroups.isEmpty();
+    bool fAllGroups = g_vecAPIMonGroups.empty();
     mapVMIter it = g_mapVM.begin();
+
+    if (it == g_mapVM.end())
+    {
+        serviceLog("apimon: No machines in list, skipping ...\n");
+        return rc;
+    }
+
     while (it != g_mapVM.end())
     {
-        if (   !it->second.group.compare(g_strAPIMonGroups, Bstr::CaseInsensitive)
-            || fAllGroups)
-        {
-            rc = apimonMachineControl(it->first /* Uuid */,
-                                       &it->second, enmResp);
-        }
+        bool fHandleVM = fAllGroups;
+        try
+        {
+            if (!fHandleVM)
+                fHandleVM = apimonHandleVM(&it->second);
+
+            if (fHandleVM)
+            {
+                int rc2 = apimonMachineControl(it->first /* Uuid */,
+                                               &it->second /* Machine */, enmResp, 30 * 1000 /* 30s timeout */);
+                if (RT_FAILURE(rc2))
+                    serviceLog("apimon: Controlling machine \"%ls\" (action: %s) failed with rc=%Rrc",
+                               it->first.raw(), apimonResponseToStr(enmResp), rc);
+
+                if (RT_SUCCESS(rc))
+                    rc = rc2; /* Store original error. */
+                /* Keep going. */
+            }
+        }
+        catch (...)
+        {
+            AssertFailed();
+        }
+
         it++;
     }
@@ -176,11 +398,15 @@
             case GETOPTDEF_APIMON_ISLN_TIMEOUT:
                 g_ulAPIMonIslnTimeoutMS = ValueUnion.u32;
-                if (g_ulAPIMonIslnTimeoutMS < 1000)
+                if (g_ulAPIMonIslnTimeoutMS < 1000) /* Don't allow timeouts < 1s. */
                     g_ulAPIMonIslnTimeoutMS = 1000;
                 break;
 
             case GETOPTDEF_APIMON_GROUPS:
-                g_strAPIMonGroups = ValueUnion.psz;
+            {
+                rc = groupAdd(g_vecAPIMonGroups, ValueUnion.psz, 0 /* Flags */);
+                if (RT_FAILURE(rc))
+                    rc = -1; /* Option unknown. */
                 break;
+            }
 
             default:
@@ -211,5 +437,5 @@
         if (!g_ulAPIMonIslnTimeoutMS) /* Still not set? Use a default. */
         {
-            serviceLogVerbose(("API monitor isolation timeout not given, defaulting to 30s\n"));
+            serviceLogVerbose(("apimon: API monitor isolation timeout not given, defaulting to 30s\n"));
 
             /* Default is 30 seconds timeout. */
@@ -218,12 +444,18 @@
 
         /* VM groups to watch for. */
-        if (g_strAPIMonGroups.isEmpty()) /* Not set by command line? */
+        if (g_vecAPIMonGroups.empty()) /* Not set by command line? */
         {
             CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/Groups").raw(),
-                                                          g_strAPIMonGroups.asOutParam()));
+                                                          strValue.asOutParam()));
+            if (!strValue.isEmpty())
+            {
+                int rc2 = groupAdd(g_vecAPIMonGroups, Utf8Str(strValue).c_str(), 0 /* Flags */);
+                if (RT_FAILURE(rc2))
+                    serviceLog("apimon: Warning: API monitor groups string invalid (%ls)\n", strValue.raw());
+            }
         }
 
         /* Host isolation command response. */
-        if (g_enmAPIMonIslnResp == APIMON_RESPONSE_UNKNOWN) /* Not set by command line? */
+        if (g_enmAPIMonIslnResp == APIMON_RESPONSE_NONE) /* Not set by command line? */
         {
             CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/IsolationResponse").raw(),
@@ -233,12 +465,7 @@
                 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",
+                    serviceLog("apimon: Warning: API monitor response string invalid (%ls), defaulting to no action\n",
                                strValue.raw());
-                    g_enmAPIMonIslnResp = APIMON_RESPONSE_SHUTDOWN;
-                }
             }
-            else
-                g_enmAPIMonIslnResp = APIMON_RESPONSE_SHUTDOWN;
         }
     } while (0);
@@ -257,5 +484,5 @@
     uint64_t uNow = RTTimeProgramMilliTS();
     uint64_t uDelta = uNow - uLastRun;
-    if (uDelta < 1000)
+    if (uDelta < 1000) /* Only check every second (or later). */
         return VINF_SUCCESS;
     uLastRun = uNow;
@@ -264,6 +491,8 @@
     HRESULT rc;
 
-    serviceLogVerbose(("Checking for API heartbeat (%RU64ms) ...\n",
+#ifdef DEBUG
+    serviceLogVerbose(("apimon: Checking for API heartbeat (%RU64ms) ...\n",
                        g_ulAPIMonIslnTimeoutMS));
+#endif
 
     do
@@ -276,5 +505,5 @@
             && g_strAPIMonIslnLastBeat.compare(strHeartbeat, Bstr::CaseSensitive))
         {
-            serviceLogVerbose(("API heartbeat received, resetting timeout\n"));
+            serviceLogVerbose(("apimon: API heartbeat received, resetting timeout\n"));
 
             g_uAPIMonIslnLastBeatMS = 0;
@@ -286,5 +515,5 @@
             if (g_uAPIMonIslnLastBeatMS > g_ulAPIMonIslnTimeoutMS)
             {
-                serviceLogVerbose(("No API heartbeat within time received (%RU64ms)\n",
+                serviceLogVerbose(("apimon: No API heartbeat within time received (%RU64ms)\n",
                                    g_ulAPIMonIslnTimeoutMS));
 
@@ -329,5 +558,8 @@
 {
     if (!fAvailable)
-        apimonTrigger(g_enmAPIMonIslnResp);
+    {
+        serviceLog(("apimon: VBoxSVC became unavailable, triggering action\n"));
+        return apimonTrigger(g_enmAPIMonIslnResp);
+    }
     return VINF_SUCCESS;
 }
Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp	(revision 41285)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp	(revision 41286)
@@ -205,5 +205,5 @@
     /* Only do ballooning if we have a maximum balloon size set. */
     PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
-                                              getPayload(pMachine, VBOX_MOD_BALLOONING_NAME);
+                                              payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
     AssertPtr(pData);
     pData->ulBalloonSizeMax = pMachine->machine.isNull()
@@ -298,5 +298,5 @@
 
         PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
-                                                  getPayload(pMachine, VBOX_MOD_BALLOONING_NAME);
+                                                  payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
         AssertPtr(pData);
 
@@ -316,9 +316,8 @@
                        lDelta > 0 ? "Inflating" : "Deflating", lDelta, lBalloonCur);
 
-            HRESULT rc;
-
             if (!g_fDryrun)
             {
                 /* Open a session for the VM. */
+                HRESULT rc;
                 CHECK_ERROR(pMachine->machine, LockMachine(g_pSession, LockType_Shared));
 
@@ -336,4 +335,6 @@
                         serviceLog("Error: Unable to set new balloon size %ld for machine \"%ls\", rc=%Rhrc",
                                    lBalloonCur, strUuid.raw(), rc);
+                    if (FAILED(rc))
+                        vrc = VERR_COM_IPRT_ERROR;
                 } while (0);
 
Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp	(revision 41285)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp	(revision 41286)
@@ -137,5 +137,5 @@
 static int machineAdd(const Bstr &strUuid);
 static int machineRemove(const Bstr &strUuid);
-static HRESULT watchdogSetup();
+static int watchdogSetup();
 static void watchdogShutdown();
 
@@ -264,7 +264,7 @@
                     {
                         serviceLog("VBoxSVC became available\n");
-                        HRESULT hrc = watchdogSetup();
-                        if (FAILED(hrc))
-                            serviceLog("Unable to re-set up watchdog (rc=%Rhrc)!\n", hrc);
+                        int rc2 = watchdogSetup();
+                        if (RT_FAILURE(rc2))
+                            serviceLog("Unable to re-set up watchdog (rc=%Rrc)!\n", rc2);
                     }
 
@@ -347,7 +347,8 @@
         Assert(!machine.isNull());
 
-        Bstr strGroup;
+        /* Note: Currently only one group per VM supported? We don't care. */
+        Bstr strGroups;
         CHECK_ERROR_BREAK(machine, GetExtraData(Bstr("VBoxInternal2/VMGroup").raw(),
-                                                strGroup.asOutParam()));
+                                                strGroups.asOutParam()));
 
         /*
@@ -356,5 +357,6 @@
         VBOXWATCHDOG_MACHINE m;
         m.machine = machine;
-        m.group = strGroup;
+        int rc2 = groupAdd(m.groups, Utf8Str(strGroups).c_str(), 0 /* Flags */);
+        AssertRC(rc2);
 
         mapVMIter it = g_mapVM.find(strUuid);
@@ -366,29 +368,27 @@
          * 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! */
+        mapGroupsIterConst itGroup = m.groups.begin();
+        while (itGroup != m.groups.end())
+        {
+            serviceLogVerbose(("Machine \"%ls\" is in VM group \"%s\"\n",
+                               strUuid.raw(), itGroup->first.c_str()));
+
             /* Add machine to group(s). */
-            mapGroupIter itGroup = g_mapGroup.find(strGroup);
-            if (itGroup == g_mapGroup.end())
+            mapGroupIter itGroups = g_mapGroup.find(itGroup->first);
+            if (itGroups == 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());
+                g_mapGroup.insert(std::make_pair(itGroup->first, vecMembers));
+
+                itGroups = g_mapGroup.find(itGroup->first);
+                Assert(itGroups != g_mapGroup.end());
             }
             else
-                itGroup->second.push_back(strUuid);
+                itGroups->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()));
+                               itGroup->first.c_str(), itGroups->second.size()));
+            itGroup++;
+        }
 
         /*
@@ -432,37 +432,48 @@
     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.raw(), vecMembers.size()));
-        if (!vecMembers.size())
-            g_mapGroup.erase(itGroup);
-    }
+    try
+    {
+        mapVMIter itVM = g_mapVM.find(strUuid);
+        Assert(itVM != g_mapVM.end());
+
+        /* Remove machine from group(s). */
+        mapGroupsIterConst itGroups = itVM->second.groups.begin();
+        while (itGroups != itVM->second.groups.end())
+        {
+            mapGroupIter itGroup = g_mapGroup.find(itGroups->first);
+            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 \"%s\" has %ld machines left\n",
+                               itGroup->first.c_str(), vecMembers.size()));
+            if (!vecMembers.size())
+            {
+                serviceLogVerbose(("Deleteting group \"%s\n", itGroup->first.c_str()));
+                g_mapGroup.erase(itGroup);
+            }
+
+            itGroups++;
+        }
 
 #ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
-    itVM->second.collector.setNull();
+        itVM->second.collector.setNull();
 #endif
-    itVM->second.machine.setNull();
-
-    /*
-     * Remove machine from map.
-     */
-    g_mapVM.erase(itVM);
+        itVM->second.machine.setNull();
+
+        /*
+         * Remove machine from map.
+         */
+        g_mapVM.erase(itVM);
+    }
+    catch (...)
+    {
+        AssertFailed();
+    }
 
     return rc;
@@ -880,14 +891,13 @@
  * @return  HRESULT
  */
-static HRESULT watchdogSetup()
+static int watchdogSetup()
 {
     serviceLogVerbose(("Setting up ...\n"));
 
+    /*
+     * Setup VirtualBox + session interfaces.
+     */
     HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
-    if (FAILED(rc))
-    {
-        RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
-    }
-    else
+    if (SUCCEEDED(rc))
     {
         rc = g_pSession.createInprocObject(CLSID_Session);
@@ -895,20 +905,21 @@
             RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
     }
-
-    do
-    {
-        /*
-         * Setup metrics.
-         */
+    else
+        RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
+
+    if (FAILED(rc))
+        return VERR_COM_OBJECT_NOT_FOUND;
+
+    /*
+     * Setup metrics.
+     */
 #ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
-        CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()));
+    CHECK_ERROR_RET(g_pVirtualBox,
+                    COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()), VERR_COM_UNEXPECTED);
 #endif
 
-        int vrc = RTCritSectInit(&g_csMachines);
-        if (RT_FAILURE(vrc))
-        {
-            rc = VBOX_E_IPRT_ERROR;
-            break;
-        }
+    int vrc = RTCritSectInit(&g_csMachines);
+    if (RT_SUCCESS(vrc))
+    {
 
         /*
@@ -916,13 +927,7 @@
          */
         vrc = vmListBuild();
-        if (RT_FAILURE(vrc))
-        {
-            rc = VBOX_E_IPRT_ERROR;
-            break;
-        }
-
-    } while (0);
-
-    return rc;
+    }
+
+    return vrc;
 }
 
@@ -1073,5 +1078,4 @@
         if (RT_FAILURE(rc))
             return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
-
         /* create release logger, to file */
         rc = com::VBoxLogRelCreate("Watchdog", pszLogFile,
@@ -1083,4 +1087,5 @@
         if (RT_FAILURE(rc))
             return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
+        AssertPtr(pLoggerReleaseFile);
     }
 #endif
@@ -1122,6 +1127,6 @@
         serviceLog("Running in dryrun mode\n");
 
-    hrc = watchdogSetup();
-    if (FAILED(hrc))
+    rc = watchdogSetup();
+    if (RT_FAILURE(rc))
         return RTEXITCODE_FAILURE;
 
Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h	(revision 41285)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogInternal.h	(revision 41286)
@@ -32,5 +32,8 @@
 #endif /* !VBOX_ONLY_DOCS */
 
+#include <algorithm>
 #include <map>
+#include <sstream>
+#include <string>
 #include <vector>
 
@@ -74,5 +77,9 @@
 typedef std::map<const char*, VBOXWATCHDOG_MODULE_PAYLOAD>::const_iterator mapPayloadIterConst;
 
-struct VBOXWATCHDOG_VM_GROUP;
+/** Group list (plus additional per-group flags, not used yet) for one VM.
+ *  Primary key is the group name, secondary specify flags (if any). */
+typedef std::map<Utf8Str, uint32_t> mapGroups;
+typedef std::map<Utf8Str, uint32_t>::iterator mapGroupsIter;
+typedef std::map<Utf8Str, uint32_t>::const_iterator mapGroupsIterConst;
 
 /** A machine's internal entry.
@@ -84,6 +91,7 @@
     ComPtr<IPerformanceCollector> collector;
 #endif
-    /** The machine's VM group(s). */
-    Bstr group;
+    /** The VM group(s) this machine belongs to.
+     *  Contains groups from per-machine "VBoxInternal2/VMGroup". */
+    mapGroups groups;
     /** Map containing the individual
      *  module payloads. */
@@ -94,13 +102,15 @@
 typedef std::map<Bstr, VBOXWATCHDOG_MACHINE>::const_iterator mapVMIterConst;
 
-/** Members of a VM group; currently only represented by the machine's UUID. */
+/** Members of a VM group; currently only represented by the machine's UUID.
+ *  Primary key is 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;
+/** A VM group. Can contain none, one or more group members.
+ *  Primary key is the group's name. */
+typedef std::map<Utf8Str, vecGroupMembers> mapGroup;
+typedef std::map<Utf8Str, vecGroupMembers>::iterator mapGroupIter;
+typedef std::map<Utf8Str, vecGroupMembers>::const_iterator mapGroupIterConst;
 
 /**
@@ -216,6 +226,8 @@
 #define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
 
+int groupAdd(mapGroups &groups, const char *pszGroupsToAdd, uint32_t fFlags);
+
 extern int getMetric(PVBOXWATCHDOG_MACHINE pMachine, const Bstr& strName, LONG *pulData);
-void* getPayload(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule);
+void* payloadFrom(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule);
 int payloadAlloc(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule, size_t cbSize, void **ppszPayload);
 void payloadFree(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule);
Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogUtils.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogUtils.cpp	(revision 41285)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdogUtils.cpp	(revision 41286)
@@ -23,4 +23,39 @@
 #include "VBoxWatchdogInternal.h"
 
+
+/**
+ * Adds a group / a set of groups to the specified map.
+ * If a group in the group map exists there will be no action.
+ *
+ * @return  IPRT status code.
+ * @param   groups                  Map to add group(s) to.
+ * @param   pszGroupsToAdd          Comma-separated string of one or more groups to add.
+ * @param   fFlags                  Flags to set to the groups added.
+ */
+int groupAdd(mapGroups &groups, const char *pszGroupsToAdd, uint32_t fFlags)
+{
+    AssertPtrReturn(pszGroupsToAdd, VERR_INVALID_POINTER);
+
+    try
+    {
+        std::istringstream strGroups(pszGroupsToAdd);
+        for(std::string strToken; getline(strGroups, strToken, ','); )
+        {
+            strToken.erase(remove_if(strToken.begin(), strToken.end(), isspace), strToken.end());
+
+            Utf8Str strTokenUtf8(strToken.c_str());
+            mapGroupsIterConst it = groups.find(strTokenUtf8);
+
+            if (it == groups.end())
+                groups.insert(std::make_pair(strTokenUtf8, fFlags));
+        }
+    }
+    catch (...)
+    {
+        AssertFailed();
+    }
+
+    return VINF_SUCCESS;
+}
 
 /**
@@ -105,5 +140,5 @@
  * @param   pszModule               Module name to get payload from.
  */
-void* getPayload(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule)
+void* payloadFrom(PVBOXWATCHDOG_MACHINE pMachine, const char *pszModule)
 {
     AssertPtrReturn(pMachine, NULL);
