VirtualBox

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

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

Never use static instances of CComModule as it messes up the log filename by using VBoxRT.dll before it's initialized.

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