VirtualBox

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

Last change on this file since 50117 was 46649, checked in by vboxsync, 11 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/* $Id: VBoxWatchdog.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2/** @file
3 * VBoxWatchdog.cpp - VirtualBox Watchdog.
4 */
5
6/*
7 * Copyright (C) 2011-2013 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 */
92static struct
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} g_aModules[] =
101{
102 { &g_ModBallooning, false /* Pre-inited */, true /* Enabled */ },
103 { &g_ModAPIMonitor, false /* Pre-inited */, true /* Enabled */ }
104};
105
106enum GETOPTDEF_WATCHDOG
107{
108 GETOPTDEF_WATCHDOG_DRYRUN = 1000
109};
110
111/**
112 * Command line arguments.
113 */
114static const RTGETOPTDEF g_aOptions[] = {
115#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
116 { "--background", 'b', RTGETOPT_REQ_NOTHING },
117#endif
118 /** For displayHelp(). */
119 { "--dryrun", GETOPTDEF_WATCHDOG_DRYRUN, RTGETOPT_REQ_NOTHING },
120 { "--help", 'h', RTGETOPT_REQ_NOTHING },
121 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
122 { "--pidfile", 'P', RTGETOPT_REQ_STRING },
123 { "--logfile", 'F', RTGETOPT_REQ_STRING },
124 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
125 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
126 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 }
127};
128
129/** Global static objects. */
130static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
131static ComPtr<IEventSource> g_pEventSource = NULL;
132static ComPtr<IEventSource> g_pEventSourceClient = NULL;
133static ComPtr<IEventListener> g_pVBoxEventListener = NULL;
134static NativeEventQueue *g_pEventQ = NULL;
135
136/* Prototypes. */
137static int machineAdd(const Bstr &strUuid);
138static int machineRemove(const Bstr &strUuid);
139static int watchdogSetup();
140static void watchdogShutdown();
141
142#ifdef RT_OS_WINDOWS
143/* Required for ATL. */
144static CComModule _Module;
145#endif
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 int rc2 = groupAdd(m.groups, strGroups.c_str(), 0 /* Flags */);
368 AssertRC(rc2);
369
370 mapVMIter it = g_mapVM.find(strUuid);
371 Assert(it == g_mapVM.end());
372 g_mapVM.insert(std::make_pair(strUuid, m));
373 serviceLogVerbose(("Added machine \"%ls\"\n", strUuid.raw()));
374
375 /*
376 * Get the machine's VM group(s).
377 */
378 mapGroupsIterConst itGroup = m.groups.begin();
379 while (itGroup != m.groups.end())
380 {
381 serviceLogVerbose(("Machine \"%ls\" is in VM group \"%s\"\n",
382 strUuid.raw(), itGroup->first.c_str()));
383
384 /* Add machine to group(s). */
385 mapGroupIter itGroups = g_mapGroup.find(itGroup->first);
386 if (itGroups == g_mapGroup.end())
387 {
388 vecGroupMembers vecMembers;
389 vecMembers.push_back(strUuid);
390 g_mapGroup.insert(std::make_pair(itGroup->first, vecMembers));
391
392 itGroups = g_mapGroup.find(itGroup->first);
393 Assert(itGroups != g_mapGroup.end());
394 }
395 else
396 itGroups->second.push_back(strUuid);
397 serviceLogVerbose(("Group \"%s\" has now %ld machine(s)\n",
398 itGroup->first.c_str(), itGroups->second.size()));
399 itGroup++;
400 }
401
402 /*
403 * Let all modules know. Typically all modules would register
404 * their per-machine payload here.
405 */
406 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
407 if (g_aModules[j].fEnabled)
408 {
409 rc2 = g_aModules[j].pDesc->pfnOnMachineRegistered(strUuid);
410 if (RT_FAILURE(rc2))
411 serviceLog("OnMachineRegistered: Module '%s' reported an error: %Rrc\n",
412 g_aModules[j].pDesc->pszName, rc);
413 /* Keep going. */
414 }
415
416 } while (0);
417
418 /** @todo Add std exception handling! */
419
420 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /* @todo Find a better error! */
421}
422
423static int machineDestroy(const Bstr &strUuid)
424{
425 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
426 int rc = VINF_SUCCESS;
427
428 /* Let all modules know. */
429 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
430 if (g_aModules[j].fEnabled)
431 {
432 int rc2 = g_aModules[j].pDesc->pfnOnMachineUnregistered(strUuid);
433 if (RT_FAILURE(rc2))
434 serviceLog("OnMachineUnregistered: Module '%s' reported an error: %Rrc\n",
435 g_aModules[j].pDesc->pszName, rc);
436 /* Keep going. */
437 }
438
439 /* Must log before erasing the iterator because of the UUID ref! */
440 serviceLogVerbose(("Removing machine \"%ls\"\n", strUuid.raw()));
441
442 try
443 {
444 mapVMIter itVM = g_mapVM.find(strUuid);
445 Assert(itVM != g_mapVM.end());
446
447 /* Remove machine from group(s). */
448 mapGroupsIterConst itGroups = itVM->second.groups.begin();
449 while (itGroups != itVM->second.groups.end())
450 {
451 mapGroupIter itGroup = g_mapGroup.find(itGroups->first);
452 Assert(itGroup != g_mapGroup.end());
453
454 vecGroupMembers vecMembers = itGroup->second;
455 vecGroupMembersIter itMember = std::find(vecMembers.begin(),
456 vecMembers.end(),
457 strUuid);
458 Assert(itMember != vecMembers.end());
459 vecMembers.erase(itMember);
460
461 serviceLogVerbose(("Group \"%s\" has %ld machines left\n",
462 itGroup->first.c_str(), vecMembers.size()));
463 if (!vecMembers.size())
464 {
465 serviceLogVerbose(("Deleting group \"%s\"\n", itGroup->first.c_str()));
466 g_mapGroup.erase(itGroup);
467 }
468
469 itGroups++;
470 }
471
472#ifndef VBOX_WATCHDOG_GLOBAL_PERFCOL
473 itVM->second.collector.setNull();
474#endif
475 itVM->second.machine.setNull();
476
477 /*
478 * Remove machine from map.
479 */
480 g_mapVM.erase(itVM);
481 }
482 catch (...)
483 {
484 AssertFailed();
485 }
486
487 return rc;
488}
489
490/**
491 * Removes a specified machine from the list of handled machines.
492 * Does not do locking -- needs to be done by caller!
493 *
494 * @return IPRT status code.
495 * @param strUuid UUID of the specified machine.
496 */
497static int machineRemove(const Bstr &strUuid)
498{
499 AssertReturn(!strUuid.isEmpty(), VERR_INVALID_PARAMETER);
500 int rc = VINF_SUCCESS;
501
502 mapVMIter it = g_mapVM.find(strUuid);
503 if (it != g_mapVM.end())
504 {
505 int rc2 = machineDestroy(strUuid);
506 if (RT_FAILURE(rc))
507 {
508 serviceLog(("Machine \"%ls\" failed to destroy, rc=%Rc\n"));
509 if (RT_SUCCESS(rc))
510 rc = rc2;
511 }
512 }
513 else
514 {
515 serviceLogVerbose(("Warning: Removing not added machine \"%ls\"\n", strUuid.raw()));
516 rc = VERR_NOT_FOUND;
517 }
518
519 return rc;
520}
521
522static void vmListDestroy()
523{
524 serviceLogVerbose(("Destroying VM list ...\n"));
525
526 int rc = RTCritSectEnter(&g_csMachines);
527 if (RT_SUCCESS(rc))
528 {
529 mapVMIter it = g_mapVM.begin();
530 while (it != g_mapVM.end())
531 {
532 machineDestroy(it->first);
533 it = g_mapVM.begin();
534 }
535
536 g_mapVM.clear();
537
538 rc = RTCritSectLeave(&g_csMachines);
539 }
540 AssertRC(rc);
541}
542
543static int vmListBuild()
544{
545 serviceLogVerbose(("Building VM list ...\n"));
546
547 int rc = RTCritSectEnter(&g_csMachines);
548 if (RT_SUCCESS(rc))
549 {
550 /*
551 * Make sure the list is empty.
552 */
553 g_mapVM.clear();
554
555 /*
556 * Get the list of all _running_ VMs
557 */
558 com::SafeIfaceArray<IMachine> machines;
559 HRESULT hrc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
560 if (SUCCEEDED(hrc))
561 {
562 /*
563 * Iterate through the collection
564 */
565 for (size_t i = 0; i < machines.size(); ++i)
566 {
567 if (machines[i])
568 {
569 Bstr strUUID;
570 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(strUUID.asOutParam()));
571
572 BOOL fAccessible;
573 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
574 if (!fAccessible)
575 {
576 serviceLogVerbose(("Machine \"%ls\" is inaccessible, skipping\n",
577 strUUID.raw()));
578 continue;
579 }
580
581 rc = machineAdd(strUUID);
582 if (RT_FAILURE(rc))
583 break;
584 }
585 }
586
587 if (!machines.size())
588 serviceLogVerbose(("No machines to add found at the moment!\n"));
589 }
590
591 int rc2 = RTCritSectLeave(&g_csMachines);
592 if (RT_SUCCESS(rc))
593 rc = rc2;
594 }
595 return rc;
596}
597
598/**
599 * Lazily calls the pfnPreInit method on each service.
600 *
601 * @returns VBox status code, error message displayed.
602 */
603static int watchdogLazyPreInit(void)
604{
605 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
606 if (!g_aModules[j].fPreInited)
607 {
608 int rc = g_aModules[j].pDesc->pfnPreInit();
609 if (RT_FAILURE(rc))
610 {
611 serviceLog("Module '%s' failed pre-init: %Rrc\n",
612 g_aModules[j].pDesc->pszName, rc);
613 return rc;
614 }
615 g_aModules[j].fPreInited = true;
616 }
617 return VINF_SUCCESS;
618}
619
620/**
621 * Starts all registered modules.
622 *
623 * @return IPRT status code.
624 * @return int
625 */
626static int watchdogStartModules()
627{
628 int rc = VINF_SUCCESS;
629
630 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
631 if (g_aModules[j].fEnabled)
632 {
633 rc = g_aModules[j].pDesc->pfnInit();
634 if (RT_FAILURE(rc))
635 {
636 if (rc != VERR_SERVICE_DISABLED)
637 {
638 serviceLog("Module '%s' failed to initialize: %Rrc\n",
639 g_aModules[j].pDesc->pszName, rc);
640 return rc;
641 }
642 g_aModules[j].fEnabled = false;
643 serviceLog(0, "Module '%s' was disabled because of missing functionality\n",
644 g_aModules[j].pDesc->pszName);
645
646 }
647 }
648
649 return rc;
650}
651
652static int watchdogShutdownModules()
653{
654 int rc = VINF_SUCCESS;
655
656 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
657 if (g_aModules[j].fEnabled)
658 {
659 int rc2 = g_aModules[j].pDesc->pfnStop();
660 if (RT_FAILURE(rc2))
661 {
662 serviceLog("Module '%s' failed to stop: %Rrc\n",
663 g_aModules[j].pDesc->pszName, rc);
664 /* Keep original rc. */
665 if (RT_SUCCESS(rc))
666 rc = rc2;
667 }
668 /* Keep going. */
669 }
670
671 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
672 if (g_aModules[j].fEnabled)
673 {
674 g_aModules[j].pDesc->pfnTerm();
675 }
676
677 return rc;
678}
679
680static RTEXITCODE watchdogMain(HandlerArg *a)
681{
682 HRESULT rc = S_OK;
683
684 do
685 {
686 /* Initialize global weak references. */
687 g_pEventQ = com::NativeEventQueue::getMainEventQueue();
688
689 /*
690 * Install signal handlers.
691 */
692 signal(SIGINT, signalHandler);
693 #ifdef SIGBREAK
694 signal(SIGBREAK, signalHandler);
695 #endif
696
697 /*
698 * Setup the global event listeners:
699 * - g_pEventSource for machine events
700 * - g_pEventSourceClient for VBoxClient events (like VBoxSVC handling)
701 */
702 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(EventSource)(g_pEventSource.asOutParam()));
703 CHECK_ERROR_BREAK(g_pVirtualBoxClient, COMGETTER(EventSource)(g_pEventSourceClient.asOutParam()));
704
705 ComObjPtr<VirtualBoxEventListenerImpl> vboxListenerImpl;
706 vboxListenerImpl.createObject();
707 vboxListenerImpl->init(new VirtualBoxEventListener());
708
709 com::SafeArray<VBoxEventType_T> eventTypes;
710 eventTypes.push_back(VBoxEventType_OnMachineRegistered);
711 eventTypes.push_back(VBoxEventType_OnMachineStateChanged);
712 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged); /* Processed by g_pEventSourceClient. */
713
714 g_pVBoxEventListener = vboxListenerImpl;
715 CHECK_ERROR_BREAK(g_pEventSource, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
716 CHECK_ERROR_BREAK(g_pEventSourceClient, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
717
718 /*
719 * Set up modules.
720 */
721 int vrc = watchdogStartModules();
722 if (RT_FAILURE(vrc))
723 break;
724
725 for (;;)
726 {
727 /*
728 * Do the actual work.
729 */
730
731 vrc = RTCritSectEnter(&g_csMachines);
732 if (RT_SUCCESS(vrc))
733 {
734 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
735 if (g_aModules[j].fEnabled)
736 {
737 int rc2 = g_aModules[j].pDesc->pfnMain();
738 if (RT_FAILURE(rc2))
739 serviceLog("Module '%s' reported an error: %Rrc\n",
740 g_aModules[j].pDesc->pszName, rc2);
741 /* Keep going. */
742 }
743
744 int rc2 = RTCritSectLeave(&g_csMachines);
745 if (RT_SUCCESS(vrc))
746 vrc = rc2;
747 AssertRC(vrc);
748 }
749
750 /*
751 * Process pending events, then wait for new ones. Note, this
752 * processes NULL events signalling event loop termination.
753 */
754 g_pEventQ->processEventQueue(50);
755
756 if (g_fCanceled)
757 {
758 serviceLog("Signal caught, exiting ...\n");
759 break;
760 }
761 }
762
763 signal(SIGINT, SIG_DFL);
764 #ifdef SIGBREAK
765 signal(SIGBREAK, SIG_DFL);
766 #endif
767
768 /* VirtualBox callback unregistration. */
769 if (g_pVBoxEventListener)
770 {
771 if (!g_pEventSource.isNull())
772 CHECK_ERROR(g_pEventSource, UnregisterListener(g_pVBoxEventListener));
773 g_pVBoxEventListener.setNull();
774 }
775
776 g_pEventSource.setNull();
777 g_pEventSourceClient.setNull();
778
779 vrc = watchdogShutdownModules();
780 AssertRC(vrc);
781
782 if (RT_FAILURE(vrc))
783 rc = VBOX_E_IPRT_ERROR;
784
785 } while (0);
786
787 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
788}
789
790void serviceLog(const char *pszFormat, ...)
791{
792 va_list args;
793 va_start(args, pszFormat);
794 char *psz = NULL;
795 RTStrAPrintfV(&psz, pszFormat, args);
796 va_end(args);
797
798 LogRel(("%s", psz));
799
800 RTStrFree(psz);
801}
802
803static void displayHeader()
804{
805 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Watchdog " VBOX_VERSION_STRING "\n"
806 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
807 "All rights reserved.\n\n");
808}
809
810/**
811 * Displays the help.
812 *
813 * @param pszImage Name of program name (image).
814 */
815static void displayHelp(const char *pszImage)
816{
817 AssertPtrReturnVoid(pszImage);
818
819 displayHeader();
820
821 RTStrmPrintf(g_pStdErr,
822 "Usage:\n"
823 " %s [-v|--verbose] [-h|-?|--help] [-P|--pidfile]\n"
824 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
825 " [-I|--loginterval=<seconds>]\n", pszImage);
826 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
827 if (g_aModules[j].pDesc->pszUsage)
828 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszUsage);
829
830 RTStrmPrintf(g_pStdErr, "\n"
831 "Options:\n");
832
833 for (unsigned i = 0;
834 i < RT_ELEMENTS(g_aOptions);
835 ++i)
836 {
837 std::string str(g_aOptions[i].pszLong);
838 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
839 {
840 str += ", -";
841 str += g_aOptions[i].iShort;
842 }
843 str += ":";
844
845 const char *pcszDescr = "";
846
847 switch (g_aOptions[i].iShort)
848 {
849 case GETOPTDEF_WATCHDOG_DRYRUN:
850 pcszDescr = "Dryrun mode -- do not perform any actions.";
851 break;
852
853 case 'h':
854 pcszDescr = "Print this help message and exit.";
855 break;
856
857#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
858 case 'b':
859 pcszDescr = "Run in background (daemon mode).";
860 break;
861#endif
862 case 'P':
863 pcszDescr = "Name of the PID file which is created when the daemon was started.";
864 break;
865
866 case 'F':
867 pcszDescr = "Name of file to write log to (no file).";
868 break;
869
870 case 'R':
871 pcszDescr = "Number of log files (0 disables log rotation).";
872 break;
873
874 case 'S':
875 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
876 break;
877
878 case 'I':
879 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
880 break;
881 }
882
883 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
884 }
885
886 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
887 {
888 if (g_aModules[j].pDesc->pszOptions)
889 RTPrintf("%s", g_aModules[j].pDesc->pszOptions);
890 }
891
892 /** @todo Change VBOXBALLOONCTRL_RELEASE_LOG to WATCHDOG*. */
893 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXBALLOONCTRL_RELEASE_LOG for logging options.\n");
894}
895
896/**
897 * Creates all global COM objects.
898 *
899 * @return HRESULT
900 */
901static int watchdogSetup()
902{
903 serviceLogVerbose(("Setting up ...\n"));
904
905 /*
906 * Setup VirtualBox + session interfaces.
907 */
908 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
909 if (SUCCEEDED(rc))
910 {
911 rc = g_pSession.createInprocObject(CLSID_Session);
912 if (FAILED(rc))
913 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
914 }
915 else
916 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
917
918 if (FAILED(rc))
919 return VERR_COM_OBJECT_NOT_FOUND;
920
921 /*
922 * Setup metrics.
923 */
924#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
925 CHECK_ERROR_RET(g_pVirtualBox,
926 COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()), VERR_COM_UNEXPECTED);
927#endif
928
929 int vrc = RTCritSectInit(&g_csMachines);
930 if (RT_SUCCESS(vrc))
931 {
932
933 /*
934 * Build up initial VM list.
935 */
936 vrc = vmListBuild();
937 }
938
939 return vrc;
940}
941
942static void watchdogShutdown()
943{
944 serviceLogVerbose(("Shutting down ...\n"));
945
946 vmListDestroy();
947
948 int rc = RTCritSectDelete(&g_csMachines);
949 AssertRC(rc);
950
951#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
952 g_pPerfCollector.setNull();
953#endif
954
955 g_pSession.setNull();
956 g_pVirtualBox.setNull();
957}
958
959int main(int argc, char *argv[])
960{
961 /*
962 * Before we do anything, init the runtime without loading
963 * the support driver.
964 */
965 int rc = RTR3InitExe(argc, &argv, 0);
966 if (RT_FAILURE(rc))
967 return RTMsgInitFailure(rc);
968
969 /*
970 * Parse the global options
971 */
972 int c;
973 const char *pszLogFile = NULL;
974 const char *pszPidFile = NULL;
975 RTGETOPTUNION ValueUnion;
976 RTGETOPTSTATE GetState;
977 RTGetOptInit(&GetState, argc, argv,
978 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
979 while ((c = RTGetOpt(&GetState, &ValueUnion)))
980 {
981 switch (c)
982 {
983 case GETOPTDEF_WATCHDOG_DRYRUN:
984 g_fDryrun = true;
985 break;
986
987 case 'h':
988 displayHelp(argv[0]);
989 return 0;
990
991 case 'v':
992 g_fVerbose = true;
993 break;
994
995#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
996 case 'b':
997 g_fDaemonize = true;
998 break;
999#endif
1000 case 'V':
1001 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1002 return 0;
1003
1004 case 'P':
1005 pszPidFile = ValueUnion.psz;
1006 break;
1007
1008 case 'F':
1009 pszLogFile = ValueUnion.psz;
1010 break;
1011
1012 case 'R':
1013 g_cHistory = ValueUnion.u32;
1014 break;
1015
1016 case 'S':
1017 g_uHistoryFileSize = ValueUnion.u64;
1018 break;
1019
1020 case 'I':
1021 g_uHistoryFileTime = ValueUnion.u32;
1022 break;
1023
1024 default:
1025 {
1026 bool fFound = false;
1027
1028 /** @todo Add "--disable-<module>" etc. here! */
1029
1030 if (!fFound)
1031 {
1032 rc = watchdogLazyPreInit();
1033 if (RT_FAILURE(rc))
1034 return RTEXITCODE_FAILURE;
1035
1036 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aModules); j++)
1037 {
1038 int iArgCnt = argc - GetState.iNext + 1;
1039 int iArgIndex = GetState.iNext - 1;
1040 int iConsumed = 0;
1041 rc = g_aModules[j].pDesc->pfnOption(iArgCnt,
1042 &argv[iArgIndex],
1043 &iConsumed);
1044 fFound = rc == 0;
1045 if (fFound)
1046 {
1047 GetState.iNext += iConsumed;
1048 break;
1049 }
1050 if (rc != -1)
1051 return rc;
1052 }
1053 }
1054 if (!fFound)
1055 return RTGetOptPrintError(c, &ValueUnion);
1056 continue;
1057 }
1058 }
1059 }
1060
1061 /** @todo Add "--quiet/-q" option to not show the header. */
1062 displayHeader();
1063
1064 /* create release logger, to stdout */
1065 char szError[RTPATH_MAX + 128];
1066 rc = com::VBoxLogRelCreate("Watchdog", g_fDaemonize ? NULL : pszLogFile,
1067 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1068 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1069 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
1070 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1071 szError, sizeof(szError));
1072 if (RT_FAILURE(rc))
1073 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1074
1075#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1076 if (g_fDaemonize)
1077 {
1078 /* prepare release logging */
1079 char szLogFile[RTPATH_MAX];
1080
1081 if (!pszLogFile || !*pszLogFile)
1082 {
1083 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1084 if (RT_FAILURE(rc))
1085 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
1086 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxballoonctrl.log");
1087 if (RT_FAILURE(rc))
1088 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
1089 pszLogFile = szLogFile;
1090 }
1091
1092 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, pszPidFile);
1093 if (RT_FAILURE(rc))
1094 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
1095 /* create release logger, to file */
1096 rc = com::VBoxLogRelCreate("Watchdog", pszLogFile,
1097 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1098 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1099 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
1100 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1101 szError, sizeof(szError));
1102 if (RT_FAILURE(rc))
1103 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1104 }
1105#endif
1106
1107#ifndef VBOX_ONLY_DOCS
1108 /*
1109 * Initialize COM.
1110 */
1111 using namespace com;
1112 HRESULT hrc = com::Initialize();
1113# ifdef VBOX_WITH_XPCOM
1114 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
1115 {
1116 char szHome[RTPATH_MAX] = "";
1117 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1118 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1119 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1120 }
1121# endif
1122 if (FAILED(hrc))
1123 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
1124
1125 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1126 if (FAILED(hrc))
1127 {
1128 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
1129 com::ErrorInfo info;
1130 if (!info.isFullAvailable() && !info.isBasicAvailable())
1131 {
1132 com::GluePrintRCMessage(hrc);
1133 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
1134 }
1135 else
1136 com::GluePrintErrorInfo(info);
1137 return RTEXITCODE_FAILURE;
1138 }
1139
1140 if (g_fDryrun)
1141 serviceLog("Running in dryrun mode\n");
1142
1143 rc = watchdogSetup();
1144 if (RT_FAILURE(rc))
1145 return RTEXITCODE_FAILURE;
1146
1147 HandlerArg handlerArg = { argc, argv };
1148 RTEXITCODE rcExit = watchdogMain(&handlerArg);
1149
1150 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
1151
1152 watchdogShutdown();
1153
1154 g_pVirtualBoxClient.setNull();
1155
1156 com::Shutdown();
1157
1158 return rcExit;
1159#else /* VBOX_ONLY_DOCS */
1160 return RTEXITCODE_SUCCESS;
1161#endif /* VBOX_ONLY_DOCS */
1162}
1163
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