VirtualBox

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

Last change on this file since 39936 was 39929, checked in by vboxsync, 13 years ago

VBoxBalloonCtrl: Starting to implement modules concept for ballooning and API monitor (work in progress).

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