VirtualBox

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

Last change on this file since 40012 was 40012, 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 40012 2012-02-06 22:30:18Z 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 int vrc = VINF_SUCCESS;
670
671 /* Initialize global weak references. */
672 g_pEventQ = com::EventQueue::getMainEventQueue();
673
674 /*
675 * Install signal handlers.
676 */
677 signal(SIGINT, signalHandler);
678 #ifdef SIGBREAK
679 signal(SIGBREAK, signalHandler);
680 #endif
681
682 /*
683 * Setup the global event listeners:
684 * - g_pEventSource for machine events
685 * - g_pEventSourceClient for VBoxClient events (like VBoxSVC handling)
686 */
687 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(EventSource)(g_pEventSource.asOutParam()));
688 CHECK_ERROR_BREAK(g_pVirtualBoxClient, COMGETTER(EventSource)(g_pEventSourceClient.asOutParam()));
689
690 ComObjPtr<VirtualBoxEventListenerImpl> vboxListenerImpl;
691 vboxListenerImpl.createObject();
692 vboxListenerImpl->init(new VirtualBoxEventListener());
693
694 com::SafeArray <VBoxEventType_T> eventTypes;
695 eventTypes.push_back(VBoxEventType_OnMachineRegistered);
696 eventTypes.push_back(VBoxEventType_OnMachineStateChanged);
697 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged); /* Processed by g_pEventSourceClient. */
698
699 g_pVBoxEventListener = vboxListenerImpl;
700 CHECK_ERROR_BREAK(g_pEventSource, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
701 CHECK_ERROR_BREAK(g_pEventSourceClient, RegisterListener(g_pVBoxEventListener, ComSafeArrayAsInParam(eventTypes), true /* Active listener */));
702
703 /*
704 * Set up modules.
705 */
706 rc = watchdogStartModules();
707 if (FAILED(rc))
708 break;
709
710 for (;;)
711 {
712 /*
713 * Do the actual work.
714 */
715
716 rc = RTCritSectEnter(&g_csMachines);
717 if (RT_SUCCESS(rc))
718 {
719 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
720 if (g_aModules[j].fEnabled)
721 {
722 int rc2 = g_aModules[j].pDesc->pfnMain();
723 if (RT_FAILURE(rc2))
724 serviceLog("Module '%s' reported an error: %Rrc\n",
725 g_aModules[j].pDesc->pszName, rc2);
726 /* Keep going. */
727 }
728
729 int rc2 = RTCritSectLeave(&g_csMachines);
730 if (RT_SUCCESS(rc))
731 rc = rc2;
732 AssertRC(rc);
733 }
734
735 /*
736 * Process pending events, then wait for new ones. Note, this
737 * processes NULL events signalling event loop termination.
738 */
739 g_pEventQ->processEventQueue(500 / 10);
740
741 if (g_fCanceled)
742 {
743 serviceLog("Signal caught, exiting ...\n");
744 break;
745 }
746 }
747
748 signal(SIGINT, SIG_DFL);
749 #ifdef SIGBREAK
750 signal(SIGBREAK, SIG_DFL);
751 #endif
752
753 /* VirtualBox callback unregistration. */
754 if (g_pVBoxEventListener)
755 {
756 if (!g_pEventSource.isNull())
757 CHECK_ERROR(g_pEventSource, UnregisterListener(g_pVBoxEventListener));
758 g_pVBoxEventListener.setNull();
759 }
760
761 g_pEventSource.setNull();
762 g_pEventSourceClient.setNull();
763
764 vrc = watchdogShutdownModules();
765 AssertRC(vrc);
766
767 if (RT_FAILURE(vrc))
768 rc = VBOX_E_IPRT_ERROR;
769
770 } while (0);
771
772 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
773}
774
775void serviceLog(const char *pszFormat, ...)
776{
777 va_list args;
778 va_start(args, pszFormat);
779 char *psz = NULL;
780 RTStrAPrintfV(&psz, pszFormat, args);
781 va_end(args);
782
783 LogRel(("%s", psz));
784
785 RTStrFree(psz);
786}
787
788static void logHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
789{
790 /* Some introductory information. */
791 static RTTIMESPEC s_TimeSpec;
792 char szTmp[256];
793 if (enmPhase == RTLOGPHASE_BEGIN)
794 RTTimeNow(&s_TimeSpec);
795 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
796
797 switch (enmPhase)
798 {
799 case RTLOGPHASE_BEGIN:
800 {
801 pfnLog(pLoggerRelease,
802 "VirtualBox Watchdog %s r%u %s (%s %s) release log\n"
803#ifdef VBOX_BLEEDING_EDGE
804 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
805#endif
806 "Log opened %s\n",
807 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
808 __DATE__, __TIME__, szTmp);
809
810 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
811 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
812 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
813 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
814 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
815 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
816 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
817 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
818 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
819 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
820 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
821
822 /* the package type is interesting for Linux distributions */
823 char szExecName[RTPATH_MAX];
824 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
825 pfnLog(pLoggerRelease,
826 "Executable: %s\n"
827 "Process ID: %u\n"
828 "Package type: %s"
829#ifdef VBOX_OSE
830 " (OSE)"
831#endif
832 "\n",
833 pszExecName ? pszExecName : "unknown",
834 RTProcSelf(),
835 VBOX_PACKAGE_STRING);
836 break;
837 }
838
839 case RTLOGPHASE_PREROTATE:
840 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
841 break;
842
843 case RTLOGPHASE_POSTROTATE:
844 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
845 break;
846
847 case RTLOGPHASE_END:
848 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
849 break;
850
851 default:
852 /* nothing */;
853 }
854}
855
856static void displayHeader()
857{
858 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Watchdog " VBOX_VERSION_STRING "\n"
859 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
860 "All rights reserved.\n\n");
861}
862
863/**
864 * Displays the help.
865 *
866 * @param pszImage Name of program name (image).
867 */
868static void displayHelp(const char *pszImage)
869{
870 AssertPtrReturnVoid(pszImage);
871
872 displayHeader();
873
874 RTStrmPrintf(g_pStdErr,
875 "Usage:\n"
876 " %s [-v|--verbose] [-h|-?|--help] [-P|--pidfile]\n"
877 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
878 " [-I|--loginterval=<seconds>]\n", pszImage);
879 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
880 if (g_aModules[j].pDesc->pszUsage)
881 RTStrmPrintf(g_pStdErr, "%s", g_aModules[j].pDesc->pszUsage);
882
883 RTStrmPrintf(g_pStdErr, "\n"
884 "Options:\n");
885
886 for (unsigned i = 0;
887 i < RT_ELEMENTS(g_aOptions);
888 ++i)
889 {
890 std::string str(g_aOptions[i].pszLong);
891 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
892 {
893 str += ", -";
894 str += g_aOptions[i].iShort;
895 }
896 str += ":";
897
898 const char *pcszDescr = "";
899
900 switch (g_aOptions[i].iShort)
901 {
902 case GETOPTDEF_WATCHDOG_DRYRUN:
903 pcszDescr = "Dryrun mode -- do not perform any actions.";
904 break;
905
906 case 'h':
907 pcszDescr = "Print this help message and exit.";
908 break;
909
910#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
911 case 'b':
912 pcszDescr = "Run in background (daemon mode).";
913 break;
914#endif
915 case 'P':
916 pcszDescr = "Name of the PID file which is created when the daemon was started.";
917 break;
918
919 case 'F':
920 pcszDescr = "Name of file to write log to (no file).";
921 break;
922
923 case 'R':
924 pcszDescr = "Number of log files (0 disables log rotation).";
925 break;
926
927 case 'S':
928 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
929 break;
930
931 case 'I':
932 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
933 break;
934 }
935
936 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
937 }
938
939 for (unsigned j = 0; j < RT_ELEMENTS(g_aModules); j++)
940 {
941 if (g_aModules[j].pDesc->pszOptions)
942 RTPrintf("%s", g_aModules[j].pDesc->pszOptions);
943 }
944
945 /** @todo Change VBOXBALLOONCTRL_RELEASE_LOG to WATCHDOG*. */
946 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXBALLOONCTRL_RELEASE_LOG for logging options.\n");
947}
948
949/**
950 * Creates all global COM objects.
951 *
952 * @return HRESULT
953 */
954static HRESULT watchdogSetup()
955{
956 serviceLogVerbose(("Setting up ...\n"));
957
958 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
959 if (FAILED(rc))
960 {
961 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
962 }
963 else
964 {
965 rc = g_pSession.createInprocObject(CLSID_Session);
966 if (FAILED(rc))
967 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
968 }
969
970 do
971 {
972 /*
973 * Setup metrics.
974 */
975#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
976 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(g_pPerfCollector.asOutParam()));
977#endif
978
979 int vrc = RTCritSectInit(&g_csMachines);
980 if (RT_FAILURE(vrc))
981 {
982 rc = VBOX_E_IPRT_ERROR;
983 break;
984 }
985
986 /*
987 * Build up initial VM list.
988 */
989 vrc = vmListBuild();
990 if (RT_FAILURE(vrc))
991 {
992 rc = VBOX_E_IPRT_ERROR;
993 break;
994 }
995
996 } while (0);
997
998 return rc;
999}
1000
1001static void watchdogShutdown()
1002{
1003 serviceLogVerbose(("Shutting down ...\n"));
1004
1005 vmListDestroy();
1006
1007 int rc = RTCritSectDelete(&g_csMachines);
1008 AssertRC(rc);
1009
1010#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
1011 g_pPerfCollector.setNull();
1012#endif
1013
1014 g_pSession.setNull();
1015 g_pVirtualBox.setNull();
1016}
1017
1018int main(int argc, char *argv[])
1019{
1020 /*
1021 * Before we do anything, init the runtime without loading
1022 * the support driver.
1023 */
1024 int rc = RTR3InitExe(argc, &argv, 0);
1025 if (RT_FAILURE(rc))
1026 return RTMsgInitFailure(rc);
1027
1028 /*
1029 * Parse the global options
1030 */
1031 int c;
1032 const char *pszLogFile = NULL;
1033 const char *pszPidFile = NULL;
1034 RTGETOPTUNION ValueUnion;
1035 RTGETOPTSTATE GetState;
1036 RTGetOptInit(&GetState, argc, argv,
1037 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
1038 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1039 {
1040 switch (c)
1041 {
1042 case GETOPTDEF_WATCHDOG_DRYRUN:
1043 g_fDryrun = true;
1044 break;
1045
1046 case 'h':
1047 displayHelp(argv[0]);
1048 return 0;
1049
1050 case 'v':
1051 g_fVerbose = true;
1052 break;
1053
1054#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1055 case 'b':
1056 g_fDaemonize = true;
1057 break;
1058#endif
1059 case 'V':
1060 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1061 return 0;
1062
1063 case 'P':
1064 pszPidFile = ValueUnion.psz;
1065 break;
1066
1067 case 'F':
1068 pszLogFile = ValueUnion.psz;
1069 break;
1070
1071 case 'R':
1072 g_cHistory = ValueUnion.u32;
1073 break;
1074
1075 case 'S':
1076 g_uHistoryFileSize = ValueUnion.u64;
1077 break;
1078
1079 case 'I':
1080 g_uHistoryFileTime = ValueUnion.u32;
1081 break;
1082
1083 default:
1084 {
1085 bool fFound = false;
1086
1087 /** @todo Add "--disable-<module>" etc. here! */
1088
1089 if (!fFound)
1090 {
1091 rc = watchdogLazyPreInit();
1092 if (RT_FAILURE(rc))
1093 return RTEXITCODE_FAILURE;
1094
1095 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aModules); j++)
1096 {
1097 rc = g_aModules[j].pDesc->pfnOption(1 /* Current value only. */,
1098 &argv[GetState.iNext - 1]);
1099 fFound = rc == 0;
1100 if (fFound)
1101 break;
1102 if (rc != -1)
1103 return rc;
1104 }
1105 }
1106 if (!fFound)
1107 return RTGetOptPrintError(c, &ValueUnion);
1108 continue;
1109 }
1110 }
1111 }
1112
1113 /** @todo Add "--quiet/-q" option to not show the header. */
1114 displayHeader();
1115
1116 /* create release logger */
1117 PRTLOGGER pLoggerRelease;
1118 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
1119 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
1120#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1121 fFlags |= RTLOGFLAGS_USECRLF;
1122#endif
1123 char szError[RTPATH_MAX + 128] = "";
1124 rc = RTLogCreateEx(&pLoggerRelease, fFlags, "all",
1125 "VBOXBALLOONCTRL_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_STDOUT,
1126 logHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
1127 szError, sizeof(szError), pszLogFile);
1128 if (RT_SUCCESS(rc))
1129 {
1130 /* register this logger as the release logger */
1131 RTLogRelSetDefaultInstance(pLoggerRelease);
1132
1133 /* Explicitly flush the log in case of VBOXWEBSRV_RELEASE_LOG=buffered. */
1134 RTLogFlush(pLoggerRelease);
1135 }
1136 else
1137 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
1138
1139#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1140 if (g_fDaemonize)
1141 {
1142 /* prepare release logging */
1143 char szLogFile[RTPATH_MAX];
1144
1145 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1146 if (RT_FAILURE(rc))
1147 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
1148 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxballoonctrl.log");
1149 if (RT_FAILURE(rc))
1150 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
1151
1152 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, pszPidFile);
1153 if (RT_FAILURE(rc))
1154 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
1155
1156 /* create release logger */
1157 PRTLOGGER pLoggerReleaseFile;
1158 static const char * const s_apszGroupsFile[] = VBOX_LOGGROUP_NAMES;
1159 RTUINT fFlagsFile = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
1160#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1161 fFlagsFile |= RTLOGFLAGS_USECRLF;
1162#endif
1163 char szErrorFile[RTPATH_MAX + 128] = "";
1164 int vrc = RTLogCreateEx(&pLoggerReleaseFile, fFlagsFile, "all",
1165 "VBOXBALLOONCTRL_RELEASE_LOG", RT_ELEMENTS(s_apszGroupsFile), s_apszGroupsFile, RTLOGDEST_FILE,
1166 logHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
1167 szErrorFile, sizeof(szErrorFile), szLogFile);
1168 if (RT_SUCCESS(vrc))
1169 {
1170 /* register this logger as the release logger */
1171 RTLogRelSetDefaultInstance(pLoggerReleaseFile);
1172
1173 /* Explicitly flush the log in case of VBOXBALLOONCTRL_RELEASE_LOG=buffered. */
1174 RTLogFlush(pLoggerReleaseFile);
1175 }
1176 else
1177 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szErrorFile, vrc);
1178 }
1179#endif
1180
1181#ifndef VBOX_ONLY_DOCS
1182 /*
1183 * Initialize COM.
1184 */
1185 using namespace com;
1186 HRESULT hrc = com::Initialize();
1187 if (FAILED(hrc))
1188 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
1189
1190 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1191 if (FAILED(hrc))
1192 {
1193 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
1194 com::ErrorInfo info;
1195 if (!info.isFullAvailable() && !info.isBasicAvailable())
1196 {
1197 com::GluePrintRCMessage(hrc);
1198 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
1199 }
1200 else
1201 com::GluePrintErrorInfo(info);
1202 return RTEXITCODE_FAILURE;
1203 }
1204
1205 if (g_fDryrun)
1206 serviceLog("Running in dryrun mode\n");
1207
1208 hrc = watchdogSetup();
1209 if (FAILED(hrc))
1210 return RTEXITCODE_FAILURE;
1211
1212 HandlerArg handlerArg = { argc, argv };
1213 RTEXITCODE rcExit = watchdogMain(&handlerArg);
1214
1215 EventQueue::getMainEventQueue()->processEventQueue(0);
1216
1217 watchdogShutdown();
1218
1219 g_pVirtualBoxClient.setNull();
1220
1221 com::Shutdown();
1222
1223 return rcExit;
1224#else /* VBOX_ONLY_DOCS */
1225 return RTEXITCODE_SUCCESS;
1226#endif /* VBOX_ONLY_DOCS */
1227}
1228
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