VirtualBox

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

Last change on this file since 40920 was 40013, checked in by vboxsync, 13 years ago

Build fix.

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