VirtualBox

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

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