VirtualBox

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

Last change on this file since 85416 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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