VirtualBox

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

Last change on this file since 36709 was 36709, checked in by vboxsync, 14 years ago

Frontends/VBoxBalloonCtrl: eliminate warning

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