VirtualBox

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

Last change on this file since 41288 was 41287, checked in by vboxsync, 13 years ago

VBoxBalloonCtrl: Added "--apimon-trigger-timeout", build fix.

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