VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxWatchdog.cpp@ 60511

Last change on this file since 60511 was 59907, checked in by vboxsync, 9 years ago

FE/VBoxBalloonCtrl: Bugfixes for ballooning module + extended logging.

To not break backwards-compatibility, global "VBoxInternal/Guest/BalloonSizeMax" or the command line ("--balloon-max") will be used for specifying a maximum ballooning size. Writing 0 (default) or deleting the value will disable having a maximum.

The per-VM's "VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax" or "VBoxInternal/Guest/BalloonSizeMax" (legacy) will be used for setting the VM's balloon (limiting to the maximum ballooning size mentioned above, if set).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.0 KB
Line 
1/* $Id: VBoxWatchdog.cpp 59907 2016-03-03 12:57:45Z vboxsync $ */
2/** @file
3 * VBoxWatchdog.cpp - VirtualBox Watchdog.
4 */
5
6/*
7 * Copyright (C) 2011-2016 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#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29
30# include <VBox/com/NativeEventQueue.h>
31# include <VBox/com/listeners.h>
32# include <VBox/com/VirtualBox.h>
33#endif /* !VBOX_ONLY_DOCS */
34
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <VBox/version.h>
38
39#include <package-generated.h>
40
41#include <iprt/asm.h>
42#include <iprt/buildconfig.h>
43#include <iprt/critsect.h>
44#include <iprt/getopt.h>
45#include <iprt/initterm.h>
46#include <iprt/message.h>
47#include <iprt/path.h>
48#include <iprt/process.h>
49#include <iprt/semaphore.h>
50#include <iprt/stream.h>
51#include <iprt/string.h>
52#include <iprt/system.h>
53#include <iprt/time.h>
54
55
56#include <algorithm>
57#include <string>
58#include <signal.h>
59
60#include "VBoxWatchdogInternal.h"
61
62using namespace com;
63
64/** External globals. */
65bool g_fDryrun = false;
66bool g_fVerbose = false;
67ComPtr<IVirtualBox> g_pVirtualBox = NULL;
68ComPtr<ISession> g_pSession = NULL;
69mapVM g_mapVM;
70mapGroup g_mapGroup;
71# ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
72ComPtr<IPerformanceCollector> g_pPerfCollector = NULL;
73# endif
74
75/** The critical section for the machines map. */
76static RTCRITSECT g_csMachines;
77
78/** Set by the signal handler. */
79static volatile bool g_fCanceled = false;
80
81/** Logging parameters. */
82static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
83static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
84static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
85
86/** Run in background. */
87static bool g_fDaemonize = false;
88
89/**
90 * The details of the services that has been compiled in.
91 */
92typedef struct VBOXWATCHDOGMOD
93{
94 /** Pointer to the service descriptor. */
95 PCVBOXMODULE pDesc;
96 /** Whether Pre-init was called. */
97 bool fPreInited;
98 /** Whether the module is enabled or not. */
99 bool fEnabled;
100} VBOXWATCHDOGMOD, *PVBOXWATCHDOGMOD;
101
102static VBOXWATCHDOGMOD g_aModules[] =
103{
104 { &g_ModBallooning, false /* Pre-inited */, true /* Enabled */ },
105 { &g_ModAPIMonitor, false /* Pre-inited */, true /* Enabled */ }
106};
107
108enum GETOPTDEF_WATCHDOG
109{
110 GETOPTDEF_WATCHDOG_DISABLE_MODULE = 1000,
111 GETOPTDEF_WATCHDOG_DRYRUN
112};
113
114/**
115 * Command line arguments.
116 */
117static const RTGETOPTDEF g_aOptions[] = {
118#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
119 { "--background", 'b', RTGETOPT_REQ_NOTHING },
120#endif
121 /** For displayHelp(). */
122 { "--disable-<module>", GETOPTDEF_WATCHDOG_DISABLE_MODULE, RTGETOPT_REQ_NOTHING },
123 { "--dryrun", GETOPTDEF_WATCHDOG_DRYRUN, RTGETOPT_REQ_NOTHING },
124 { "--help", 'h', RTGETOPT_REQ_NOTHING },
125 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
126 { "--pidfile", 'P', RTGETOPT_REQ_STRING },
127 { "--logfile", 'F', RTGETOPT_REQ_STRING },
128 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
129 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
130 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 }
131};
132
133/** Global static objects. */
134static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
135static ComPtr<IEventSource> g_pEventSource = NULL;
136static ComPtr<IEventSource> g_pEventSourceClient = NULL;
137static ComPtr<IEventListener> g_pVBoxEventListener = NULL;
138static NativeEventQueue *g_pEventQ = NULL;
139
140/* Prototypes. */
141static int machineAdd(const Bstr &strUuid);
142static int machineRemove(const Bstr &strUuid);
143static int watchdogSetup();
144static void watchdogShutdown();
145
146#ifdef RT_OS_WINDOWS
147/* Required for ATL. */
148static CComModule _Module;
149#endif
150
151/**
152 * Handler for global events.
153 */
154class VirtualBoxEventListener
155{
156 public:
157 VirtualBoxEventListener()
158 {
159 }
160
161 virtual ~VirtualBoxEventListener()
162 {
163 }
164
165 HRESULT init()
166 {
167 return S_OK;
168 }
169
170 void uninit()
171 {
172 }
173
174 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
175 {
176 switch (aType)
177 {
178 case VBoxEventType_OnMachineRegistered:
179 {
180 ComPtr<IMachineRegisteredEvent> pEvent = aEvent;
181 Assert(pEvent);
182
183 Bstr uuid;
184 BOOL fRegistered;
185 HRESULT hr = pEvent->COMGETTER(Registered)(&fRegistered);
186 if (SUCCEEDED(hr))
187 hr = pEvent->COMGETTER(MachineId)(uuid.asOutParam());
188
189 if (SUCCEEDED(hr))
190 {
191 int rc = RTCritSectEnter(&g_csMachines);
192 if (RT_SUCCESS(rc))
193 {
194 rc = fRegistered
195 ? machineAdd(uuid)
196 : machineRemove(uuid);
197 int rc2 = RTCritSectLeave(&g_csMachines);
198 if (RT_SUCCESS(rc))
199 rc = rc2;
200 AssertRC(rc);
201 }
202 }
203 break;
204 }
205
206 case VBoxEventType_OnMachineStateChanged:
207 {
208 ComPtr<IMachineStateChangedEvent> pEvent = aEvent;
209 Assert(pEvent);
210
211 MachineState_T machineState;
212 Bstr uuid;
213
214 HRESULT hr = pEvent->COMGETTER(State)(&machineState);
215 if (SUCCEEDED(hr))
216 hr = pEvent->COMGETTER(MachineId)(uuid.asOutParam());
217
218 if (SUCCEEDED(hr))
219 {
220 int rc = RTCritSectEnter(&g_csMachines);
221 if (RT_SUCCESS(rc))
222 {
223 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
224 if (g_aModules[j].fEnabled)
225 {
226 int rc2 = g_aModules[j].pDesc->pfnOnMachineStateChanged(uuid,
227 machineState);
228 if (RT_FAILURE(rc2))
229 serviceLog("Module '%s' reported an error: %Rrc\n",
230 g_aModules[j].pDesc->pszName, rc);
231 /* Keep going. */
232 }
233
234 int rc2 = RTCritSectLeave(&g_csMachines);
235 if (RT_SUCCESS(rc))
236 rc = rc2;
237 AssertRC(rc);
238 }
239 }
240 break;
241 }
242
243 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
244 {
245 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
246 Assert(pVSACEv);
247 BOOL fAvailable = FALSE;
248 pVSACEv->COMGETTER(Available)(&fAvailable);
249
250 /* First, notify all modules. */
251 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
252 if (g_aModules[j].fEnabled)
253 {
254 int rc2 = g_aModules[j].pDesc->pfnOnServiceStateChanged(RT_BOOL(fAvailable));
255 if (RT_FAILURE(rc2))
256 serviceLog("Module '%s' reported an error: %Rrc\n",
257 g_aModules[j].pDesc->pszName, rc2);
258 /* Keep going. */
259 }
260
261 /* Do global teardown/re-creation stuff. */
262 if (!fAvailable)
263 {
264 serviceLog("VBoxSVC became unavailable\n");
265 watchdogShutdown();
266 }
267 else
268 {
269 serviceLog("VBoxSVC became available\n");
270 int rc2 = watchdogSetup();
271 if (RT_FAILURE(rc2))
272 serviceLog("Unable to re-set up watchdog (rc=%Rrc)!\n", rc2);
273 }
274
275 break;
276 }
277
278 default:
279 /* Not handled event, just skip it. */
280 break;
281 }
282
283 return S_OK;
284 }
285
286 private:
287};
288typedef ListenerImpl<VirtualBoxEventListener> VirtualBoxEventListenerImpl;
289VBOX_LISTENER_DECLARE(VirtualBoxEventListenerImpl)
290
291/**
292 * Signal handler that sets g_fGuestCtrlCanceled.
293 *
294 * This can be executed on any thread in the process, on Windows it may even be
295 * a thread dedicated to delivering this signal. Do not doing anything
296 * unnecessary here.
297 */
298static void signalHandler(int iSignal)
299{
300 NOREF(iSignal);
301 ASMAtomicWriteBool(&g_fCanceled, true);
302
303 if (g_pEventQ)
304 {
305 int rc = g_pEventQ->interruptEventQueueProcessing();
306 if (RT_FAILURE(rc))
307 serviceLog("Error: interruptEventQueueProcessing failed with rc=%Rrc\n", rc);
308 }
309}
310
311/**
312 * Installs a custom signal handler to get notified
313 * whenever the user wants to intercept the program.
314 */
315static void signalHandlerInstall()
316{
317 signal(SIGINT, signalHandler);
318#ifdef SIGBREAK
319 signal(SIGBREAK, signalHandler);
320#endif
321}
322
323/**
324 * Uninstalls a previously installed signal handler.
325 */
326static void signalHandlerUninstall()
327{
328 signal(SIGINT, SIG_DFL);
329#ifdef SIGBREAK
330 signal(SIGBREAK, SIG_DFL);
331#endif
332}
333
334/**
335 * Adds a specified machine to the list (map) of handled machines.
336 * Does not do locking -- needs to be done by caller!
337 *
338 * @return IPRT status code.
339 * @param strUuid UUID of the specified machine.
340 */
341static int machineAdd(const Bstr &strUuid)
342{
343 HRESULT rc;
344
345 /** @todo Add exception handling! */
346
347 do
348 {
349 ComPtr <IMachine> machine;
350 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine(strUuid.raw(), machine.asOutParam()));
351 Assert(!machine.isNull());
352
353 /*
354 * Get groups for this machine.
355 */
356 com::SafeArray<BSTR> groups;
357 CHECK_ERROR_BREAK(machine, COMGETTER(Groups)(ComSafeArrayAsOutParam(groups)));
358 Utf8Str strGroups;
359 for (size_t i = 0; i < groups.size(); i++)
360 {
361 if (i != 0)
362 strGroups.append(",");
363 strGroups.append(Utf8Str(groups[i]));
364 }
365
366 /*
367 * Add machine to map.
368 */
369 VBOXWATCHDOG_MACHINE m;
370 m.machine = machine;
371 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(m.strName.asOutParam()));
372
373 int rc2 = groupAdd(m.groups, strGroups.c_str(), 0 /* Flags */);
374 AssertRC(rc2);
375
376 mapVMIter it = g_mapVM.find(strUuid);
377 Assert(it == g_mapVM.end());
378 g_mapVM.insert(std::make_pair(strUuid, m));
379 serviceLogVerbose(("Added machine \"%ls\"\n", strUuid.raw()));
380
381 /*
382 * Get the machine's VM group(s).
383 */
384 mapGroupsIterConst itGroup = m.groups.begin();
385 while (itGroup != m.groups.end())
386 {
387 serviceLogVerbose(("Machine \"%ls\" is in VM group \"%s\"\n",
388 strUuid.raw(), itGroup->first.c_str()));
389
390 /* Add machine to group(s). */
391 mapGroupIter itGroups = g_mapGroup.find(itGroup->first);
392 if (itGroups == g_mapGroup.end())
393 {
394 vecGroupMembers vecMembers;
395 vecMembers.push_back(strUuid);
396 g_mapGroup.insert(std::make_pair(itGroup->first, vecMembers));
397
398 itGroups = g_mapGroup.find(itGroup->first);
399 Assert(itGroups != g_mapGroup.end());
400 }
401 else
402 itGroups->second.push_back(strUuid);
403 serviceLogVerbose(("Group \"%s\" has now %ld machine(s)\n",
404 itGroup->first.c_str(), itGroups->second.size()));
405 ++itGroup;
406 }
407
408 /*
409 * Let all modules know. Typically all modules would register
410 * their per-machine payload here.
411 */
412 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
413 if (g_aModules[j].fEnabled)
414 {
415 rc2 = g_aModules[j].pDesc->pfnOnMachineRegistered(strUuid);
416 if (RT_FAILURE(rc2))
417 serviceLog("OnMachineRegistered: Module '%s' reported an error: %Rrc\n",
418 g_aModules[j].pDesc->pszName, rc);
419 /* Keep going. */
420 }
421
422 } while (0);
423
424 /** @todo Add std exception handling! */
425
426 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /* @todo Find a better error! */
427}
428
429static int machineDestroy(const Bstr &strUuid)
430{
431 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
432 int rc = VINF_SUCCESS;
433
434 /* Let all modules know. */
435 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
436 if (g_aModules[j].fEnabled)
437 {
438 int rc2 = g_aModules[j].pDesc->pfnOnMachineUnregistered(strUuid);
439 if (RT_FAILURE(rc2))
440 serviceLog("OnMachineUnregistered: Module '%s' reported an error: %Rrc\n",
441 g_aModules[j].pDesc->pszName, rc);
442 /* Keep going. */
443 }
444
445 /* Must log before erasing the iterator because of the UUID ref! */
446 serviceLogVerbose(("Removing machine \"%ls\"\n", strUuid.raw()));
447
448 try
449 {
450 mapVMIter itVM = g_mapVM.find(strUuid);
451 Assert(itVM != g_mapVM.end());
452
453 /* Remove machine from group(s). */
454 mapGroupsIterConst itGroups = itVM->second.groups.begin();
455 while (itGroups != itVM->second.groups.end())
456 {
457 mapGroupIter itGroup = g_mapGroup.find(itGroups->first);
458 Assert(itGroup != g_mapGroup.end());
459
460 vecGroupMembers vecMembers = itGroup->second;
461 vecGroupMembersIter itMember = std::find(vecMembers.begin(),
462 vecMembers.end(),
463 strUuid);
464 Assert(itMember != vecMembers.end());
465 vecMembers.erase(itMember);
466
467 serviceLogVerbose(("Group \"%s\" has %ld machines left\n",
468 itGroup->first.c_str(), vecMembers.size()));
469 if (!vecMembers.size())
470 {
471 serviceLogVerbose(("Deleting group \"%s\"\n", itGroup->first.c_str()));
472 g_mapGroup.erase(itGroup);
473 }
474
475 ++itGroups;
476 }
477
478#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
479 itVM->second.collector.setNull();
480#endif
481 itVM->second.machine.setNull();
482
483 /*
484 * Remove machine from map.
485 */
486 g_mapVM.erase(itVM);
487 }
488 catch (...)
489 {
490 AssertFailed();
491 }
492
493 return rc;
494}
495
496/**
497 * Removes a specified machine from the list of handled machines.
498 * Does not do locking -- needs to be done by caller!
499 *
500 * @return IPRT status code.
501 * @param strUuid UUID of the specified machine.
502 */
503static int machineRemove(const Bstr &strUuid)
504{
505 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
506 int rc = VINF_SUCCESS;
507
508 mapVMIter it = g_mapVM.find(strUuid);
509 if (it != g_mapVM.end())
510 {
511 int rc2 = machineDestroy(strUuid);
512 if (RT_FAILURE(rc))
513 {
514 serviceLog(("Machine \"%ls\" failed to destroy, rc=%Rc\n"));
515 if (RT_SUCCESS(rc))
516 rc = rc2;
517 }
518 }
519 else
520 {
521 serviceLogVerbose(("Warning: Removing not added machine \"%ls\"\n", strUuid.raw()));
522 rc = VERR_NOT_FOUND;
523 }
524
525 return rc;
526}
527
528static void vmListDestroy()
529{
530 serviceLogVerbose(("Destroying VM list ...\n"));
531
532 int rc = RTCritSectEnter(&g_csMachines);
533 if (RT_SUCCESS(rc))
534 {
535 mapVMIter it = g_mapVM.begin();
536 while (it != g_mapVM.end())
537 {
538 machineDestroy(it->first);
539 it = g_mapVM.begin();
540 }
541
542 g_mapVM.clear();
543
544 rc = RTCritSectLeave(&g_csMachines);
545 }
546 AssertRC(rc);
547}
548
549static int vmListBuild()
550{
551 serviceLogVerbose(("Building VM list ...\n"));
552
553 int rc = RTCritSectEnter(&g_csMachines);
554 if (RT_SUCCESS(rc))
555 {
556 /*
557 * Make sure the list is empty.
558 */
559 g_mapVM.clear();
560
561 /*
562 * Get the list of all _running_ VMs
563 */
564 com::SafeIfaceArray<IMachine> machines;
565 HRESULT hrc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
566 if (SUCCEEDED(hrc))
567 {
568 /*
569 * Iterate through the collection
570 */
571 for (size_t i = 0; i < machines.size(); ++i)
572 {
573 if (machines[i])
574 {
575 Bstr strUUID;
576 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(strUUID.asOutParam()));
577
578 BOOL fAccessible;
579 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
580 if (!fAccessible)
581 {
582 serviceLogVerbose(("Machine \"%ls\" is inaccessible, skipping\n",
583 strUUID.raw()));
584 continue;
585 }
586
587 rc = machineAdd(strUUID);
588 if (RT_FAILURE(rc))
589 break;
590 }
591 }
592
593 if (!machines.size())
594 serviceLogVerbose(("No machines to add found at the moment!\n"));
595 }
596
597 int rc2 = RTCritSectLeave(&g_csMachines);
598 if (RT_SUCCESS(rc))
599 rc = rc2;
600 }
601 return rc;
602}
603
604/**
605 * Lazily calls the pfnPreInit method on each service.
606 *
607 * @returns VBox status code, error message displayed.
608 */
609static int watchdogLazyPreInit(void)
610{
611 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
612 if (!g_aModules[j].fPreInited)
613 {
614 int rc = g_aModules[j].pDesc->pfnPreInit();
615 if (RT_FAILURE(rc))
616 {
617 serviceLog("Module '%s' failed pre-init: %Rrc\n",
618 g_aModules[j].pDesc->pszName, rc);
619 return rc;
620 }
621 g_aModules[j].fPreInited = true;
622 }
623 return VINF_SUCCESS;
624}
625
626/**
627 * Starts all registered modules.
628 *
629 * @return IPRT status code.
630 * @return int
631 */
632static int watchdogStartModules()
633{
634 int rc = VINF_SUCCESS;
635
636 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
637 {
638 const PVBOXWATCHDOGMOD pMod = &g_aModules[j];
639 if (pMod->fEnabled)
640 {
641 int rc2 = pMod->pDesc->pfnInit();
642 if (RT_FAILURE(rc2))
643 {
644 if (rc2 != VERR_SERVICE_DISABLED)
645 {
646 serviceLog("Module '%s' failed to initialize: %Rrc\n", pMod->pDesc->pszName, rc2);
647 return rc;
648 }
649 pMod->fEnabled = false;
650 serviceLog("Module '%s' was disabled because of missing functionality\n", pMod->pDesc->pszName);
651
652 }
653 }
654 else
655 serviceLog("Module '%s' disabled, skipping ...\n", pMod->pDesc->pszName);
656 }
657
658 return rc;
659}
660
661static int watchdogShutdownModules()
662{
663 int rc = VINF_SUCCESS;
664
665 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
666 if (g_aModules[j].fEnabled)
667 {
668 int rc2 = g_aModules[j].pDesc->pfnStop();
669 if (RT_FAILURE(rc2))
670 {
671 serviceLog("Module '%s' failed to stop: %Rrc\n",
672 g_aModules[j].pDesc->pszName, rc);
673 /* Keep original rc. */
674 if (RT_SUCCESS(rc))
675 rc = rc2;
676 }
677 /* Keep going. */
678 }
679
680 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
681 if (g_aModules[j].fEnabled)
682 {
683 g_aModules[j].pDesc->pfnTerm();
684 }
685
686 return rc;
687}
688
689static RTEXITCODE watchdogMain(HandlerArg *a)
690{
691 HRESULT rc = S_OK;
692
693 do
694 {
695 /* Initialize global weak references. */
696 g_pEventQ = com::NativeEventQueue::getMainEventQueue();
697
698 /*
699 * Install signal handlers.
700 */
701 signal(SIGINT, signalHandler);
702 #ifdef SIGBREAK
703 signal(SIGBREAK, signalHandler);
704 #endif
705
706 /*
707 * Setup the global event listeners:
708 * - g_pEventSource for machine events
709 * - g_pEventSourceClient for VBoxClient events (like VBoxSVC handling)
710 */
711 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(EventSource)(g_pEventSource.asOutParam()));
712 CHECK_ERROR_BREAK(g_pVirtualBoxClient, COMGETTER(EventSource)(g_pEventSourceClient.asOutParam()));
713
714 ComObjPtr<VirtualBoxEventListenerImpl> vboxListenerImpl;
715 vboxListenerImpl.createObject();
716 vboxListenerImpl->init(new VirtualBoxEventListener());
717
718 com::SafeArray<VBoxEventType_T> eventTypes;
719 eventTypes.push_back(VBoxEventType_OnMachineRegistered);
720 eventTypes.push_back(VBoxEventType_OnMachineStateChanged);
721 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged); /* Processed by g_pEventSourceClient. */
722
723 g_pVBoxEventListener = vboxListenerImpl;
724 CHECK_ERROR_BREAK(g_pEventSource, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
725 CHECK_ERROR_BREAK(g_pEventSourceClient, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
726
727 /*
728 * Set up modules.
729 */
730 int vrc = watchdogStartModules();
731 if (RT_FAILURE(vrc))
732 break;
733
734 for (;;)
735 {
736 /*
737 * Do the actual work.
738 */
739
740 vrc = RTCritSectEnter(&g_csMachines);
741 if (RT_SUCCESS(vrc))
742 {
743 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
744 if (g_aModules[j].fEnabled)
745 {
746 int rc2 = g_aModules[j].pDesc->pfnMain();
747 if (RT_FAILURE(rc2))
748 serviceLog("Module '%s' reported an error: %Rrc\n",
749 g_aModules[j].pDesc->pszName, rc2);
750 /* Keep going. */
751 }
752
753 int rc2 = RTCritSectLeave(&g_csMachines);
754 if (RT_SUCCESS(vrc))
755 vrc = rc2;
756 AssertRC(vrc);
757 }
758
759 /*
760 * Process pending events, then wait for new ones. Note, this
761 * processes NULL events signalling event loop termination.
762 */
763 g_pEventQ->processEventQueue(50);
764
765 if (g_fCanceled)
766 {
767 serviceLog("Signal caught, exiting ...\n");
768 break;
769 }
770 }
771
772 signal(SIGINT, SIG_DFL);
773 #ifdef SIGBREAK
774 signal(SIGBREAK, SIG_DFL);
775 #endif
776
777 /* VirtualBox callback unregistration. */
778 if (g_pVBoxEventListener)
779 {
780 if (!g_pEventSource.isNull())
781 CHECK_ERROR(g_pEventSource, UnregisterListener(g_pVBoxEventListener));
782 g_pVBoxEventListener.setNull();
783 }
784
785 g_pEventSource.setNull();
786 g_pEventSourceClient.setNull();
787
788 vrc = watchdogShutdownModules();
789 AssertRC(vrc);
790
791 if (RT_FAILURE(vrc))
792 rc = VBOX_E_IPRT_ERROR;
793
794 } while (0);
795
796 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
797}
798
799void serviceLog(const char *pszFormat, ...)
800{
801 va_list args;
802 va_start(args, pszFormat);
803 char *psz = NULL;
804 RTStrAPrintfV(&psz, pszFormat, args);
805 va_end(args);
806
807 LogRel(("%s", psz));
808
809 RTStrFree(psz);
810}
811
812static void displayHeader()
813{
814 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Watchdog " VBOX_VERSION_STRING "\n"
815 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
816 "All rights reserved.\n\n");
817}
818
819/**
820 * Displays the help.
821 *
822 * @param pszImage Name of program name (image).
823 */
824static void displayHelp(const char *pszImage)
825{
826 AssertPtrReturnVoid(pszImage);
827
828 displayHeader();
829
830 RTStrmPrintf(g_pStdErr,
831 "Usage:\n"
832 " %s [-v|--verbose] [-h|-?|--help] [-P|--pidfile]\n"
833 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
834 " [-I|--loginterval=<seconds>]\n", pszImage);
835 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
836 if (g_aModules[j].pDesc->pszUsage)
837 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszUsage);
838
839 RTStrmPrintf(g_pStdErr, "\n"
840 "Options:\n");
841
842 for (unsigned i = 0;
843 i < RT_ELEMENTS(g_aOptions);
844 ++i)
845 {
846 std::string str(g_aOptions[i].pszLong);
847 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
848 {
849 str += ", -";
850 str += g_aOptions[i].iShort;
851 }
852 str += ":";
853
854 const char *pcszDescr = "";
855
856 switch (g_aOptions[i].iShort)
857 {
858 case GETOPTDEF_WATCHDOG_DISABLE_MODULE:
859 pcszDescr = "Disables a module. See module list for built-in modules.";
860 break;
861
862 case GETOPTDEF_WATCHDOG_DRYRUN:
863 pcszDescr = "Dryrun mode -- do not perform any actions.";
864 break;
865
866 case 'h':
867 pcszDescr = "Print this help message and exit.";
868 break;
869
870#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
871 case 'b':
872 pcszDescr = "Run in background (daemon mode).";
873 break;
874#endif
875 case 'P':
876 pcszDescr = "Name of the PID file which is created when the daemon was started.";
877 break;
878
879 case 'F':
880 pcszDescr = "Name of file to write log to (no file).";
881 break;
882
883 case 'R':
884 pcszDescr = "Number of log files (0 disables log rotation).";
885 break;
886
887 case 'S':
888 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
889 break;
890
891 case 'I':
892 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
893 break;
894 }
895
896 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
897 }
898
899 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
900 {
901 if (g_aModules[j].pDesc->pszOptions)
902 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszOptions);
903 }
904
905 /** @todo Change VBOXBALLOONCTRL_RELEASE_LOG to WATCHDOG*. */
906 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXBALLOONCTRL_RELEASE_LOG for logging options.\n");
907
908 RTStrmPrintf(g_pStdErr, "\nValid module names are: ");
909 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
910 {
911 if (j > 0)
912 RTStrmPrintf(g_pStdErr, ", ");
913 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszName);
914 }
915 RTStrmPrintf(g_pStdErr, "\n\n");
916}
917
918/**
919 * Creates all global COM objects.
920 *
921 * @return HRESULT
922 */
923static int watchdogSetup()
924{
925 serviceLogVerbose(("Setting up ...\n"));
926
927 /*
928 * Setup VirtualBox + session interfaces.
929 */
930 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
931 if (SUCCEEDED(rc))
932 {
933 rc = g_pSession.createInprocObject(CLSID_Session);
934 if (FAILED(rc))
935 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
936 }
937 else
938 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
939
940 if (FAILED(rc))
941 return VERR_COM_OBJECT_NOT_FOUND;
942
943 /*
944 * Setup metrics.
945 */
946#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
947 CHECK_ERROR_RET(g_pVirtualBox,
948 COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()), VERR_COM_UNEXPECTED);
949#endif
950
951 int vrc = RTCritSectInit(&g_csMachines);
952 if (RT_SUCCESS(vrc))
953 {
954
955 /*
956 * Build up initial VM list.
957 */
958 vrc = vmListBuild();
959 }
960
961 return vrc;
962}
963
964static void watchdogShutdown()
965{
966 serviceLogVerbose(("Shutting down ...\n"));
967
968 vmListDestroy();
969
970 int rc = RTCritSectDelete(&g_csMachines);
971 AssertRC(rc);
972
973#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
974 g_pPerfCollector.setNull();
975#endif
976
977 g_pSession.setNull();
978 g_pVirtualBox.setNull();
979}
980
981int main(int argc, char *argv[])
982{
983 /*
984 * Before we do anything, init the runtime without loading
985 * the support driver.
986 */
987 int rc = RTR3InitExe(argc, &argv, 0);
988 if (RT_FAILURE(rc))
989 return RTMsgInitFailure(rc);
990
991 /*
992 * Parse the global options
993 */
994 int c;
995 const char *pszLogFile = NULL;
996 const char *pszPidFile = NULL;
997 RTGETOPTUNION ValueUnion;
998 RTGETOPTSTATE GetState;
999 RTGetOptInit(&GetState, argc, argv,
1000 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
1001 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1002 {
1003 switch (c)
1004 {
1005 case GETOPTDEF_WATCHDOG_DRYRUN:
1006 g_fDryrun = true;
1007 break;
1008
1009 case 'h':
1010 displayHelp(argv[0]);
1011 return 0;
1012
1013 case 'v':
1014 g_fVerbose = true;
1015 break;
1016
1017#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1018 case 'b':
1019 g_fDaemonize = true;
1020 break;
1021#endif
1022 case 'V':
1023 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1024 return 0;
1025
1026 case 'P':
1027 pszPidFile = ValueUnion.psz;
1028 break;
1029
1030 case 'F':
1031 pszLogFile = ValueUnion.psz;
1032 break;
1033
1034 case 'R':
1035 g_cHistory = ValueUnion.u32;
1036 break;
1037
1038 case 'S':
1039 g_uHistoryFileSize = ValueUnion.u64;
1040 break;
1041
1042 case 'I':
1043 g_uHistoryFileTime = ValueUnion.u32;
1044 break;
1045
1046 default:
1047 {
1048 bool fFound = false;
1049
1050 char szModDisable[64];
1051 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aModules); j++)
1052 {
1053 if (!RTStrPrintf(szModDisable, sizeof(szModDisable), "--disable-%s", g_aModules[j].pDesc->pszName))
1054 continue;
1055
1056 if (!RTStrICmp(szModDisable, ValueUnion.psz))
1057 {
1058 g_aModules[j].fEnabled = false;
1059 fFound = true;
1060 }
1061 }
1062
1063 if (!fFound)
1064 {
1065 rc = watchdogLazyPreInit();
1066 if (RT_FAILURE(rc))
1067 return RTEXITCODE_FAILURE;
1068
1069 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aModules); j++)
1070 {
1071 if (!g_aModules[j].fEnabled)
1072 continue;
1073
1074 int iArgCnt = argc - GetState.iNext + 1;
1075 int iArgIndex = GetState.iNext - 1;
1076 int iConsumed = 0;
1077 rc = g_aModules[j].pDesc->pfnOption(iArgCnt,
1078 &argv[iArgIndex],
1079 &iConsumed);
1080 fFound = rc == 0;
1081 if (fFound)
1082 {
1083 GetState.iNext += iConsumed;
1084 break;
1085 }
1086 if (rc != -1)
1087 return rc;
1088 }
1089 }
1090 if (!fFound)
1091 return RTGetOptPrintError(c, &ValueUnion);
1092 continue;
1093 }
1094 }
1095 }
1096
1097 /** @todo Add "--quiet/-q" option to not show the header. */
1098 displayHeader();
1099
1100 /* create release logger, to stdout */
1101 char szError[RTPATH_MAX + 128];
1102 rc = com::VBoxLogRelCreate("Watchdog", g_fDaemonize ? NULL : pszLogFile,
1103 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1104 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1105 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
1106 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1107 szError, sizeof(szError));
1108 if (RT_FAILURE(rc))
1109 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1110
1111#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1112 if (g_fDaemonize)
1113 {
1114 /* prepare release logging */
1115 char szLogFile[RTPATH_MAX];
1116
1117 if (!pszLogFile || !*pszLogFile)
1118 {
1119 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1120 if (RT_FAILURE(rc))
1121 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
1122 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxballoonctrl.log");
1123 if (RT_FAILURE(rc))
1124 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
1125 pszLogFile = szLogFile;
1126 }
1127
1128 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, pszPidFile);
1129 if (RT_FAILURE(rc))
1130 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
1131 /* create release logger, to file */
1132 rc = com::VBoxLogRelCreate("Watchdog", pszLogFile,
1133 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1134 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1135 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
1136 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1137 szError, sizeof(szError));
1138 if (RT_FAILURE(rc))
1139 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1140 }
1141#endif
1142
1143#ifndef VBOX_ONLY_DOCS
1144 /*
1145 * Initialize COM.
1146 */
1147 using namespace com;
1148 HRESULT hrc = com::Initialize();
1149# ifdef VBOX_WITH_XPCOM
1150 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
1151 {
1152 char szHome[RTPATH_MAX] = "";
1153 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1154 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1155 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1156 }
1157# endif
1158 if (FAILED(hrc))
1159 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
1160
1161 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1162 if (FAILED(hrc))
1163 {
1164 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
1165 com::ErrorInfo info;
1166 if (!info.isFullAvailable() && !info.isBasicAvailable())
1167 {
1168 com::GluePrintRCMessage(hrc);
1169 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
1170 }
1171 else
1172 com::GluePrintErrorInfo(info);
1173 return RTEXITCODE_FAILURE;
1174 }
1175
1176 if (g_fDryrun)
1177 serviceLog("Running in dryrun mode\n");
1178
1179 rc = watchdogSetup();
1180 if (RT_FAILURE(rc))
1181 return RTEXITCODE_FAILURE;
1182
1183 HandlerArg handlerArg = { argc, argv };
1184 RTEXITCODE rcExit = watchdogMain(&handlerArg);
1185
1186 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
1187
1188 watchdogShutdown();
1189
1190 g_pVirtualBoxClient.setNull();
1191
1192 com::Shutdown();
1193
1194 return rcExit;
1195#else /* VBOX_ONLY_DOCS */
1196 return RTEXITCODE_SUCCESS;
1197#endif /* VBOX_ONLY_DOCS */
1198}
1199
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