VirtualBox

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

Last change on this file since 79128 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.2 KB
Line 
1/* $Id: VBoxWatchdog.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxWatchdog.cpp - VirtualBox Watchdog.
4 */
5
6/*
7 * Copyright (C) 2011-2019 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#if 0 /** @todo signal handler installer / uninstallers are unused. */
308/**
309 * Installs a custom signal handler to get notified
310 * whenever the user wants to intercept the program.
311 */
312static void signalHandlerInstall()
313{
314 signal(SIGINT, signalHandler);
315#ifdef SIGBREAK
316 signal(SIGBREAK, signalHandler);
317#endif
318}
319
320/**
321 * Uninstalls a previously installed signal handler.
322 */
323static void signalHandlerUninstall()
324{
325 signal(SIGINT, SIG_DFL);
326#ifdef SIGBREAK
327 signal(SIGBREAK, SIG_DFL);
328#endif
329}
330#endif /* unused */
331
332/**
333 * Adds a specified machine to the list (map) of handled machines.
334 * Does not do locking -- needs to be done by caller!
335 *
336 * @return IPRT status code.
337 * @param strUuid UUID of the specified machine.
338 */
339static int machineAdd(const Bstr &strUuid)
340{
341 HRESULT rc;
342
343 /** @todo Add exception handling! */
344
345 do
346 {
347 ComPtr <IMachine> machine;
348 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine(strUuid.raw(), machine.asOutParam()));
349 Assert(!machine.isNull());
350
351 /*
352 * Get groups for this machine.
353 */
354 com::SafeArray<BSTR> groups;
355 CHECK_ERROR_BREAK(machine, COMGETTER(Groups)(ComSafeArrayAsOutParam(groups)));
356 Utf8Str strGroups;
357 for (size_t i = 0; i < groups.size(); i++)
358 {
359 if (i != 0)
360 strGroups.append(",");
361 strGroups.append(Utf8Str(groups[i]));
362 }
363
364 /*
365 * Add machine to map.
366 */
367 VBOXWATCHDOG_MACHINE m;
368 m.machine = machine;
369 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(m.strName.asOutParam()));
370
371 int rc2 = groupAdd(m.groups, strGroups.c_str(), 0 /* Flags */);
372 AssertRC(rc2);
373
374 Assert(g_mapVM.find(strUuid) == g_mapVM.end());
375 g_mapVM.insert(std::make_pair(strUuid, m));
376 serviceLogVerbose(("Added machine \"%ls\"\n", strUuid.raw()));
377
378 /*
379 * Get the machine's VM group(s).
380 */
381 mapGroupsIterConst itGroup = m.groups.begin();
382 while (itGroup != m.groups.end())
383 {
384 serviceLogVerbose(("Machine \"%ls\" is in VM group \"%s\"\n",
385 strUuid.raw(), itGroup->first.c_str()));
386
387 /* Add machine to group(s). */
388 mapGroupIter itGroups = g_mapGroup.find(itGroup->first);
389 if (itGroups == g_mapGroup.end())
390 {
391 vecGroupMembers vecMembers;
392 vecMembers.push_back(strUuid);
393 g_mapGroup.insert(std::make_pair(itGroup->first, vecMembers));
394
395 itGroups = g_mapGroup.find(itGroup->first);
396 Assert(itGroups != g_mapGroup.end());
397 }
398 else
399 itGroups->second.push_back(strUuid);
400 serviceLogVerbose(("Group \"%s\" has now %ld machine(s)\n",
401 itGroup->first.c_str(), itGroups->second.size()));
402 ++itGroup;
403 }
404
405 /*
406 * Let all modules know. Typically all modules would register
407 * their per-machine payload here.
408 */
409 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
410 if (g_aModules[j].fEnabled)
411 {
412 rc2 = g_aModules[j].pDesc->pfnOnMachineRegistered(strUuid);
413 if (RT_FAILURE(rc2))
414 serviceLog("OnMachineRegistered: Module '%s' reported an error: %Rrc\n",
415 g_aModules[j].pDesc->pszName, rc);
416 /* Keep going. */
417 }
418
419 } while (0);
420
421 /** @todo Add std exception handling! */
422
423 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /** @todo Find a better error! */
424}
425
426static int machineDestroy(const Bstr &strUuid)
427{
428 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
429 int rc = VINF_SUCCESS;
430
431 /* Let all modules know. */
432 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
433 if (g_aModules[j].fEnabled)
434 {
435 int rc2 = g_aModules[j].pDesc->pfnOnMachineUnregistered(strUuid);
436 if (RT_FAILURE(rc2))
437 serviceLog("OnMachineUnregistered: Module '%s' reported an error: %Rrc\n",
438 g_aModules[j].pDesc->pszName, rc);
439 /* Keep going. */
440 }
441
442 /* Must log before erasing the iterator because of the UUID ref! */
443 serviceLogVerbose(("Removing machine \"%ls\"\n", strUuid.raw()));
444
445 try
446 {
447 mapVMIter itVM = g_mapVM.find(strUuid);
448 Assert(itVM != g_mapVM.end());
449
450 /* Remove machine from group(s). */
451 mapGroupsIterConst itGroups = itVM->second.groups.begin();
452 while (itGroups != itVM->second.groups.end())
453 {
454 mapGroupIter itGroup = g_mapGroup.find(itGroups->first);
455 Assert(itGroup != g_mapGroup.end());
456
457 vecGroupMembers vecMembers = itGroup->second;
458 vecGroupMembersIter itMember = std::find(vecMembers.begin(),
459 vecMembers.end(),
460 strUuid);
461 Assert(itMember != vecMembers.end());
462 vecMembers.erase(itMember);
463
464 serviceLogVerbose(("Group \"%s\" has %ld machines left\n",
465 itGroup->first.c_str(), vecMembers.size()));
466 if (!vecMembers.size())
467 {
468 serviceLogVerbose(("Deleting group \"%s\"\n", itGroup->first.c_str()));
469 g_mapGroup.erase(itGroup);
470 }
471
472 ++itGroups;
473 }
474
475#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
476 itVM->second.collector.setNull();
477#endif
478 itVM->second.machine.setNull();
479
480 /*
481 * Remove machine from map.
482 */
483 g_mapVM.erase(itVM);
484 }
485 catch (...)
486 {
487 AssertFailed();
488 }
489
490 return rc;
491}
492
493/**
494 * Removes a specified machine from the list of handled machines.
495 * Does not do locking -- needs to be done by caller!
496 *
497 * @return IPRT status code.
498 * @param strUuid UUID of the specified machine.
499 */
500static int machineRemove(const Bstr &strUuid)
501{
502 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
503 int rc = VINF_SUCCESS;
504
505 mapVMIter it = g_mapVM.find(strUuid);
506 if (it != g_mapVM.end())
507 {
508 int rc2 = machineDestroy(strUuid);
509 if (RT_FAILURE(rc))
510 {
511 serviceLog(("Machine \"%ls\" failed to destroy, rc=%Rc\n"));
512 if (RT_SUCCESS(rc))
513 rc = rc2;
514 }
515 }
516 else
517 {
518 serviceLogVerbose(("Warning: Removing not added machine \"%ls\"\n", strUuid.raw()));
519 rc = VERR_NOT_FOUND;
520 }
521
522 return rc;
523}
524
525static void vmListDestroy()
526{
527 serviceLogVerbose(("Destroying VM list ...\n"));
528
529 int rc = RTCritSectEnter(&g_csMachines);
530 if (RT_SUCCESS(rc))
531 {
532 mapVMIter it = g_mapVM.begin();
533 while (it != g_mapVM.end())
534 {
535 machineDestroy(it->first);
536 it = g_mapVM.begin();
537 }
538
539 g_mapVM.clear();
540
541 rc = RTCritSectLeave(&g_csMachines);
542 }
543 AssertRC(rc);
544}
545
546static int vmListBuild()
547{
548 serviceLogVerbose(("Building VM list ...\n"));
549
550 int rc = RTCritSectEnter(&g_csMachines);
551 if (RT_SUCCESS(rc))
552 {
553 /*
554 * Make sure the list is empty.
555 */
556 g_mapVM.clear();
557
558 /*
559 * Get the list of all _running_ VMs
560 */
561 com::SafeIfaceArray<IMachine> machines;
562 HRESULT hrc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
563 if (SUCCEEDED(hrc))
564 {
565 /*
566 * Iterate through the collection
567 */
568 for (size_t i = 0; i < machines.size(); ++i)
569 {
570 if (machines[i])
571 {
572 Bstr strUUID;
573 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(strUUID.asOutParam()));
574
575 BOOL fAccessible;
576 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
577 if (!fAccessible)
578 {
579 serviceLogVerbose(("Machine \"%ls\" is inaccessible, skipping\n",
580 strUUID.raw()));
581 continue;
582 }
583
584 rc = machineAdd(strUUID);
585 if (RT_FAILURE(rc))
586 break;
587 }
588 }
589
590 if (!machines.size())
591 serviceLogVerbose(("No machines to add found at the moment!\n"));
592 }
593
594 int rc2 = RTCritSectLeave(&g_csMachines);
595 if (RT_SUCCESS(rc))
596 rc = rc2;
597 }
598 return rc;
599}
600
601/**
602 * Lazily calls the pfnPreInit method on each service.
603 *
604 * @returns VBox status code, error message displayed.
605 */
606static int watchdogLazyPreInit(void)
607{
608 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
609 if (!g_aModules[j].fPreInited)
610 {
611 int rc = g_aModules[j].pDesc->pfnPreInit();
612 if (RT_FAILURE(rc))
613 {
614 serviceLog("Module '%s' failed pre-init: %Rrc\n",
615 g_aModules[j].pDesc->pszName, rc);
616 return rc;
617 }
618 g_aModules[j].fPreInited = true;
619 }
620 return VINF_SUCCESS;
621}
622
623/**
624 * Starts all registered modules.
625 *
626 * @return IPRT status code.
627 * @return int
628 */
629static int watchdogStartModules()
630{
631 int rc = VINF_SUCCESS;
632
633 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
634 {
635 const PVBOXWATCHDOGMOD pMod = &g_aModules[j];
636 if (pMod->fEnabled)
637 {
638 int rc2 = pMod->pDesc->pfnInit();
639 if (RT_FAILURE(rc2))
640 {
641 if (rc2 != VERR_SERVICE_DISABLED)
642 {
643 serviceLog("Module '%s' failed to initialize: %Rrc\n", pMod->pDesc->pszName, rc2);
644 return rc;
645 }
646 pMod->fEnabled = false;
647 serviceLog("Module '%s' was disabled because of missing functionality\n", pMod->pDesc->pszName);
648
649 }
650 }
651 else
652 serviceLog("Module '%s' disabled, skipping ...\n", pMod->pDesc->pszName);
653 }
654
655 return rc;
656}
657
658static int watchdogShutdownModules()
659{
660 int rc = VINF_SUCCESS;
661
662 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
663 if (g_aModules[j].fEnabled)
664 {
665 int rc2 = g_aModules[j].pDesc->pfnStop();
666 if (RT_FAILURE(rc2))
667 {
668 serviceLog("Module '%s' failed to stop: %Rrc\n",
669 g_aModules[j].pDesc->pszName, rc);
670 /* Keep original rc. */
671 if (RT_SUCCESS(rc))
672 rc = rc2;
673 }
674 /* Keep going. */
675 }
676
677 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
678 if (g_aModules[j].fEnabled)
679 {
680 g_aModules[j].pDesc->pfnTerm();
681 }
682
683 return rc;
684}
685
686static RTEXITCODE watchdogMain(/*HandlerArg *a */)
687{
688 HRESULT rc = S_OK;
689
690 do
691 {
692 /* Initialize global weak references. */
693 g_pEventQ = com::NativeEventQueue::getMainEventQueue();
694
695 /*
696 * Install signal handlers.
697 */
698 signal(SIGINT, signalHandler);
699 #ifdef SIGBREAK
700 signal(SIGBREAK, signalHandler);
701 #endif
702
703 /*
704 * Setup the global event listeners:
705 * - g_pEventSource for machine events
706 * - g_pEventSourceClient for VBoxClient events (like VBoxSVC handling)
707 */
708 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(EventSource)(g_pEventSource.asOutParam()));
709 CHECK_ERROR_BREAK(g_pVirtualBoxClient, COMGETTER(EventSource)(g_pEventSourceClient.asOutParam()));
710
711 ComObjPtr<VirtualBoxEventListenerImpl> vboxListenerImpl;
712 vboxListenerImpl.createObject();
713 vboxListenerImpl->init(new VirtualBoxEventListener());
714
715 com::SafeArray<VBoxEventType_T> eventTypes;
716 eventTypes.push_back(VBoxEventType_OnMachineRegistered);
717 eventTypes.push_back(VBoxEventType_OnMachineStateChanged);
718 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged); /* Processed by g_pEventSourceClient. */
719
720 g_pVBoxEventListener = vboxListenerImpl;
721 CHECK_ERROR_BREAK(g_pEventSource, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
722 CHECK_ERROR_BREAK(g_pEventSourceClient, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
723
724 /*
725 * Set up modules.
726 */
727 int vrc = watchdogStartModules();
728 if (RT_FAILURE(vrc))
729 break;
730
731 for (;;)
732 {
733 /*
734 * Do the actual work.
735 */
736
737 vrc = RTCritSectEnter(&g_csMachines);
738 if (RT_SUCCESS(vrc))
739 {
740 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
741 if (g_aModules[j].fEnabled)
742 {
743 int rc2 = g_aModules[j].pDesc->pfnMain();
744 if (RT_FAILURE(rc2))
745 serviceLog("Module '%s' reported an error: %Rrc\n",
746 g_aModules[j].pDesc->pszName, rc2);
747 /* Keep going. */
748 }
749
750 int rc2 = RTCritSectLeave(&g_csMachines);
751 if (RT_SUCCESS(vrc))
752 vrc = rc2;
753 AssertRC(vrc);
754 }
755
756 /*
757 * Process pending events, then wait for new ones. Note, this
758 * processes NULL events signalling event loop termination.
759 */
760 g_pEventQ->processEventQueue(50);
761
762 if (g_fCanceled)
763 {
764 serviceLog("Signal caught, exiting ...\n");
765 break;
766 }
767 }
768
769 signal(SIGINT, SIG_DFL);
770 #ifdef SIGBREAK
771 signal(SIGBREAK, SIG_DFL);
772 #endif
773
774 /* VirtualBox callback unregistration. */
775 if (g_pVBoxEventListener)
776 {
777 if (!g_pEventSource.isNull())
778 CHECK_ERROR(g_pEventSource, UnregisterListener(g_pVBoxEventListener));
779 g_pVBoxEventListener.setNull();
780 }
781
782 g_pEventSource.setNull();
783 g_pEventSourceClient.setNull();
784
785 vrc = watchdogShutdownModules();
786 AssertRC(vrc);
787
788 if (RT_FAILURE(vrc))
789 rc = VBOX_E_IPRT_ERROR;
790
791 } while (0);
792
793 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
794}
795
796void serviceLog(const char *pszFormat, ...)
797{
798 va_list args;
799 va_start(args, pszFormat);
800 char *psz = NULL;
801 RTStrAPrintfV(&psz, pszFormat, args);
802 va_end(args);
803
804 LogRel(("%s", psz));
805
806 RTStrFree(psz);
807}
808
809static void displayHeader()
810{
811 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Watchdog " VBOX_VERSION_STRING "\n"
812 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
813 "All rights reserved.\n\n");
814}
815
816/**
817 * Displays the help.
818 *
819 * @param pszImage Name of program name (image).
820 */
821static void displayHelp(const char *pszImage)
822{
823 AssertPtrReturnVoid(pszImage);
824
825 displayHeader();
826
827 RTStrmPrintf(g_pStdErr,
828 "Usage:\n"
829 " %s [-v|--verbose] [-h|-?|--help] [-P|--pidfile]\n"
830 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
831 " [-I|--loginterval=<seconds>]\n", pszImage);
832 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
833 if (g_aModules[j].pDesc->pszUsage)
834 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszUsage);
835
836 RTStrmPrintf(g_pStdErr, "\n"
837 "Options:\n");
838
839 for (unsigned i = 0;
840 i < RT_ELEMENTS(g_aOptions);
841 ++i)
842 {
843 std::string str(g_aOptions[i].pszLong);
844 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
845 {
846 str += ", -";
847 str += g_aOptions[i].iShort;
848 }
849 str += ":";
850
851 const char *pcszDescr = "";
852
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 }
892
893 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
894 }
895
896 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
897 {
898 if (g_aModules[j].pDesc->pszOptions)
899 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszOptions);
900 }
901
902 /** @todo Change VBOXBALLOONCTRL_RELEASE_LOG to WATCHDOG*. */
903 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXBALLOONCTRL_RELEASE_LOG for logging options.\n");
904
905 RTStrmPrintf(g_pStdErr, "\nValid module names are: ");
906 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
907 {
908 if (j > 0)
909 RTStrmPrintf(g_pStdErr, ", ");
910 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszName);
911 }
912 RTStrmPrintf(g_pStdErr, "\n\n");
913}
914
915/**
916 * Creates all global COM objects.
917 *
918 * @return HRESULT
919 */
920static int watchdogSetup()
921{
922 serviceLogVerbose(("Setting up ...\n"));
923
924 /*
925 * Setup VirtualBox + session interfaces.
926 */
927 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
928 if (SUCCEEDED(rc))
929 {
930 rc = g_pSession.createInprocObject(CLSID_Session);
931 if (FAILED(rc))
932 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
933 }
934 else
935 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
936
937 if (FAILED(rc))
938 return VERR_COM_OBJECT_NOT_FOUND;
939
940 /*
941 * Setup metrics.
942 */
943#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
944 CHECK_ERROR_RET(g_pVirtualBox,
945 COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()), VERR_COM_UNEXPECTED);
946#endif
947
948 int vrc = RTCritSectInit(&g_csMachines);
949 if (RT_SUCCESS(vrc))
950 {
951
952 /*
953 * Build up initial VM list.
954 */
955 vrc = vmListBuild();
956 }
957
958 return vrc;
959}
960
961static void watchdogShutdown()
962{
963 serviceLogVerbose(("Shutting down ...\n"));
964
965 vmListDestroy();
966
967 int rc = RTCritSectDelete(&g_csMachines);
968 AssertRC(rc);
969
970#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
971 g_pPerfCollector.setNull();
972#endif
973
974 g_pSession.setNull();
975 g_pVirtualBox.setNull();
976}
977
978int main(int argc, char *argv[])
979{
980 /*
981 * Before we do anything, init the runtime without loading
982 * the support driver.
983 */
984 int rc = RTR3InitExe(argc, &argv, 0);
985 if (RT_FAILURE(rc))
986 return RTMsgInitFailure(rc);
987#ifdef RT_OS_WINDOWS
988 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
989#endif
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 RTERRINFOSTATIC ErrInfo;
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 RTErrInfoInitStatic(&ErrInfo));
1108 if (RT_FAILURE(rc))
1109 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, 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 RTErrInfoInitStatic(&ErrInfo));
1138 if (RT_FAILURE(rc))
1139 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, 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