VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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