VirtualBox

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

Last change on this file since 39943 was 39942, checked in by vboxsync, 13 years ago

VBoxBalloonCtrl: Update.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette