VirtualBox

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

Last change on this file since 43342 was 42211, checked in by vboxsync, 12 years ago

Frontends/VBoxBalloonCtrl: switch to the machine groups API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1/* $Id: VBoxWatchdog.cpp 42211 2012-07-18 14:24:58Z 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 /*
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(("Deleteting 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::EventQueue::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(500 / 10);
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 rc = g_aModules[j].pDesc->pfnOption(1 /* Current value only. */,
1039 &argv[GetState.iNext - 1]);
1040 fFound = rc == 0;
1041 if (fFound)
1042 break;
1043 if (rc != -1)
1044 return rc;
1045 }
1046 }
1047 if (!fFound)
1048 return RTGetOptPrintError(c, &ValueUnion);
1049 continue;
1050 }
1051 }
1052 }
1053
1054 /** @todo Add "--quiet/-q" option to not show the header. */
1055 displayHeader();
1056
1057 /* create release logger, to stdout */
1058 char szError[RTPATH_MAX + 128];
1059 rc = com::VBoxLogRelCreate("Watchdog", g_fDaemonize ? NULL : pszLogFile,
1060 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1061 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1062 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
1063 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1064 szError, sizeof(szError));
1065 if (RT_FAILURE(rc))
1066 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1067
1068#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1069 if (g_fDaemonize)
1070 {
1071 /* prepare release logging */
1072 char szLogFile[RTPATH_MAX];
1073
1074 if (!pszLogFile || !*pszLogFile)
1075 {
1076 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1077 if (RT_FAILURE(rc))
1078 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
1079 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxballoonctrl.log");
1080 if (RT_FAILURE(rc))
1081 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
1082 pszLogFile = szLogFile;
1083 }
1084
1085 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, pszPidFile);
1086 if (RT_FAILURE(rc))
1087 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
1088 /* create release logger, to file */
1089 rc = com::VBoxLogRelCreate("Watchdog", pszLogFile,
1090 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1091 "all", "VBOXBALLOONCTRL_RELEASE_LOG",
1092 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
1093 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
1094 szError, sizeof(szError));
1095 if (RT_FAILURE(rc))
1096 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1097 }
1098#endif
1099
1100#ifndef VBOX_ONLY_DOCS
1101 /*
1102 * Initialize COM.
1103 */
1104 using namespace com;
1105 HRESULT hrc = com::Initialize();
1106# ifdef VBOX_WITH_XPCOM
1107 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
1108 {
1109 char szHome[RTPATH_MAX] = "";
1110 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1111 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1112 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1113 }
1114# endif
1115 if (FAILED(hrc))
1116 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
1117
1118 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1119 if (FAILED(hrc))
1120 {
1121 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
1122 com::ErrorInfo info;
1123 if (!info.isFullAvailable() && !info.isBasicAvailable())
1124 {
1125 com::GluePrintRCMessage(hrc);
1126 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
1127 }
1128 else
1129 com::GluePrintErrorInfo(info);
1130 return RTEXITCODE_FAILURE;
1131 }
1132
1133 if (g_fDryrun)
1134 serviceLog("Running in dryrun mode\n");
1135
1136 rc = watchdogSetup();
1137 if (RT_FAILURE(rc))
1138 return RTEXITCODE_FAILURE;
1139
1140 HandlerArg handlerArg = { argc, argv };
1141 RTEXITCODE rcExit = watchdogMain(&handlerArg);
1142
1143 EventQueue::getMainEventQueue()->processEventQueue(0);
1144
1145 watchdogShutdown();
1146
1147 g_pVirtualBoxClient.setNull();
1148
1149 com::Shutdown();
1150
1151 return rcExit;
1152#else /* VBOX_ONLY_DOCS */
1153 return RTEXITCODE_SUCCESS;
1154#endif /* VBOX_ONLY_DOCS */
1155}
1156
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