VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp@ 35793

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

frontends: init ATL, used for listener instantiation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.1 KB
Line 
1/* $Id: VBoxHeadless.cpp 35741 2011-01-27 15:46:41Z vboxsync $ */
2/** @file
3 * VBoxHeadless - The VirtualBox Headless frontend for running VMs on servers.
4 */
5
6/*
7 * Copyright (C) 2006-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#include <VBox/com/com.h>
19#include <VBox/com/string.h>
20#include <VBox/com/array.h>
21#include <VBox/com/Guid.h>
22#include <VBox/com/ErrorInfo.h>
23#include <VBox/com/errorprint.h>
24#include <VBox/com/EventQueue.h>
25
26#include <VBox/com/VirtualBox.h>
27#include <VBox/com/listeners.h>
28
29using namespace com;
30
31#define LOG_GROUP LOG_GROUP_GUI
32
33#include <VBox/log.h>
34#include <VBox/version.h>
35#include <iprt/buildconfig.h>
36#include <iprt/ctype.h>
37#include <iprt/initterm.h>
38#include <iprt/stream.h>
39#include <iprt/ldr.h>
40#include <iprt/getopt.h>
41#include <iprt/env.h>
42#include <VBox/err.h>
43#include <VBox/VBoxVideo.h>
44
45#ifdef VBOX_FFMPEG
46#include <cstdlib>
47#include <cerrno>
48#include "VBoxHeadless.h"
49#include <iprt/env.h>
50#include <iprt/param.h>
51#include <iprt/process.h>
52#include <VBox/sup.h>
53#endif
54
55//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
56#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
57#include <signal.h>
58#endif
59
60#include "Framebuffer.h"
61#ifdef VBOX_WITH_VNC
62# include "FramebufferVNC.h"
63#endif
64
65#include "NullFramebuffer.h"
66
67////////////////////////////////////////////////////////////////////////////////
68
69#define LogError(m,rc) \
70 do { \
71 Log(("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
72 RTPrintf("%s\n", m); \
73 } while (0)
74
75////////////////////////////////////////////////////////////////////////////////
76
77/* global weak references (for event handlers) */
78static IConsole *gConsole = NULL;
79static EventQueue *gEventQ = NULL;
80
81/* flag whether frontend should terminate */
82static volatile bool g_fTerminateFE = false;
83
84#ifdef VBOX_WITH_VNC
85static VNCFB *g_pFramebufferVNC;
86#endif
87
88////////////////////////////////////////////////////////////////////////////////
89
90/**
91 * Handler for VirtualBoxClient events.
92 */
93class VirtualBoxClientEventListener
94{
95public:
96 VirtualBoxClientEventListener()
97 {
98 }
99
100 virtual ~VirtualBoxClientEventListener()
101 {
102 }
103
104 HRESULT init()
105 {
106 return S_OK;
107 }
108
109 void uninit()
110 {
111 }
112
113 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
114 {
115 switch (aType)
116 {
117 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
118 {
119 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
120 Assert(pVSACEv);
121 BOOL fAvailable = FALSE;
122 pVSACEv->COMGETTER(Available)(&fAvailable);
123 if (!fAvailable)
124 {
125 LogRel(("VBoxHeadless: VBoxSVC became unavailable, exiting.\n"));
126 RTPrintf("VBoxSVC became unavailable, exiting.\n");
127 /* Terminate the VM as cleanly as possible given that VBoxSVC
128 * is no longer present. */
129 g_fTerminateFE = true;
130 gEventQ->interruptEventQueueProcessing();
131 }
132 break;
133 }
134 default:
135 AssertFailed();
136 }
137
138 return S_OK;
139 }
140
141private:
142};
143
144/**
145 * Handler for global events.
146 */
147class VirtualBoxEventListener
148{
149public:
150 VirtualBoxEventListener()
151 {
152 mfNoLoggedInUsers = true;
153 }
154
155 virtual ~VirtualBoxEventListener()
156 {
157 }
158
159 HRESULT init()
160 {
161 return S_OK;
162 }
163
164 void uninit()
165 {
166 }
167
168 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
169 {
170 switch (aType)
171 {
172 case VBoxEventType_OnGuestPropertyChanged:
173 {
174 ComPtr<IGuestPropertyChangedEvent> gpcev = aEvent;
175 Assert(gpcev);
176
177 Bstr aKey;
178 gpcev->COMGETTER(Name)(aKey.asOutParam());
179
180 if (aKey == Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
181 {
182 /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
183 BOOL fProcessDisconnectOnGuestLogout = FALSE;
184 ComPtr <IMachine> machine;
185 HRESULT hrc = S_OK;
186
187 if (gConsole)
188 {
189 hrc = gConsole->COMGETTER(Machine)(machine.asOutParam());
190 if (SUCCEEDED(hrc) && machine)
191 {
192 Bstr id, machineId;
193 hrc = machine->COMGETTER(Id)(id.asOutParam());
194 gpcev->COMGETTER(MachineId)(machineId.asOutParam());
195 if (id == machineId)
196 {
197 Bstr value1;
198 hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
199 value1.asOutParam());
200 if (SUCCEEDED(hrc) && value1 == "1")
201 {
202 fProcessDisconnectOnGuestLogout = TRUE;
203 }
204 }
205 }
206 }
207
208 if (fProcessDisconnectOnGuestLogout)
209 {
210 Bstr value;
211 gpcev->COMGETTER(Value)(value.asOutParam());
212 Utf8Str utf8Value = value;
213 if (utf8Value == "true")
214 {
215 if (!mfNoLoggedInUsers) /* Only if the property really changes. */
216 {
217 mfNoLoggedInUsers = true;
218
219 /* If there is a connection, drop it. */
220 ComPtr<IVRDEServerInfo> info;
221 hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
222 if (SUCCEEDED(hrc) && info)
223 {
224 ULONG cClients = 0;
225 hrc = info->COMGETTER(NumberOfClients)(&cClients);
226 if (SUCCEEDED(hrc) && cClients > 0)
227 {
228 ComPtr <IVRDEServer> vrdeServer;
229 hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
230 if (SUCCEEDED(hrc) && vrdeServer)
231 {
232 LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n"));
233 vrdeServer->COMSETTER(Enabled)(FALSE);
234 vrdeServer->COMSETTER(Enabled)(TRUE);
235 }
236 }
237 }
238 }
239 }
240 else
241 {
242 mfNoLoggedInUsers = false;
243 }
244 }
245 }
246 break;
247 }
248 default:
249 AssertFailed();
250 }
251
252 return S_OK;
253 }
254
255private:
256 bool mfNoLoggedInUsers;
257};
258
259/**
260 * Handler for machine events.
261 */
262class ConsoleEventListener
263{
264public:
265 ConsoleEventListener() :
266 mLastVRDEPort(-1),
267 m_fIgnorePowerOffEvents(false)
268 {
269 }
270
271 virtual ~ConsoleEventListener()
272 {
273 }
274
275 HRESULT init()
276 {
277 return S_OK;
278 }
279
280 void uninit()
281 {
282 }
283
284 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
285 {
286 switch (aType)
287 {
288 case VBoxEventType_OnMouseCapabilityChanged:
289 {
290
291 ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
292 Assert(mccev);
293
294 BOOL fSupportsAbsolute = false;
295 mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute);
296
297 /* Emit absolute mouse event to actually enable the host mouse cursor. */
298 if (fSupportsAbsolute && gConsole)
299 {
300 ComPtr<IMouse> mouse;
301 gConsole->COMGETTER(Mouse)(mouse.asOutParam());
302 if (mouse)
303 {
304 mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
305 }
306 }
307#ifdef VBOX_WITH_VNC
308 if (g_pFramebufferVNC)
309 g_pFramebufferVNC->enableAbsMouse(fSupportsAbsolute);
310#endif
311 break;
312 }
313 case VBoxEventType_OnStateChanged:
314 {
315 ComPtr<IStateChangedEvent> scev = aEvent;
316 Assert(scev);
317
318 MachineState_T machineState;
319 scev->COMGETTER(State)(&machineState);
320
321 /* Terminate any event wait operation if the machine has been
322 * PoweredDown/Saved/Aborted. */
323 if (machineState < MachineState_Running && !m_fIgnorePowerOffEvents)
324 {
325 g_fTerminateFE = true;
326 gEventQ->interruptEventQueueProcessing();
327 }
328
329 break;
330 }
331 case VBoxEventType_OnVRDEServerInfoChanged:
332 {
333 ComPtr<IVRDEServerInfoChangedEvent> rdicev = aEvent;
334 Assert(rdicev);
335
336 if (gConsole)
337 {
338 ComPtr<IVRDEServerInfo> info;
339 gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
340 if (info)
341 {
342 LONG port;
343 info->COMGETTER(Port)(&port);
344 if (port != mLastVRDEPort)
345 {
346 if (port == -1)
347 RTPrintf("VRDE server is inactive.\n");
348 else if (port == 0)
349 RTPrintf("VRDE server failed to start.\n");
350 else
351 RTPrintf("VRDE server is listening on port %d.\n", port);
352
353 mLastVRDEPort = port;
354 }
355 }
356 }
357 break;
358 }
359 case VBoxEventType_OnCanShowWindow:
360 {
361 ComPtr<ICanShowWindowEvent> cswev = aEvent;
362 Assert(cswev);
363 cswev->AddVeto(NULL);
364 break;
365 }
366 case VBoxEventType_OnShowWindow:
367 {
368 ComPtr<IShowWindowEvent> swev = aEvent;
369 Assert(swev);
370 swev->COMSETTER(WinId)(0);
371 break;
372 }
373 default:
374 AssertFailed();
375 }
376 return S_OK;
377 }
378
379 void ignorePowerOffEvents(bool fIgnore)
380 {
381 m_fIgnorePowerOffEvents = fIgnore;
382 }
383
384private:
385
386 long mLastVRDEPort;
387 bool m_fIgnorePowerOffEvents;
388};
389
390typedef ListenerImpl<VirtualBoxClientEventListener> VirtualBoxClientEventListenerImpl;
391typedef ListenerImpl<VirtualBoxEventListener> VirtualBoxEventListenerImpl;
392typedef ListenerImpl<ConsoleEventListener> ConsoleEventListenerImpl;
393
394VBOX_LISTENER_DECLARE(VirtualBoxClientEventListenerImpl)
395VBOX_LISTENER_DECLARE(VirtualBoxEventListenerImpl)
396VBOX_LISTENER_DECLARE(ConsoleEventListenerImpl)
397
398#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
399static void SaveState(int sig)
400{
401 ComPtr <IProgress> progress = NULL;
402
403/** @todo Deal with nested signals, multithreaded signal dispatching (esp. on windows),
404 * and multiple signals (both SIGINT and SIGTERM in some order).
405 * Consider processing the signal request asynchronously since there are lots of things
406 * which aren't safe (like RTPrintf and printf IIRC) in a signal context. */
407
408 RTPrintf("Signal received, saving state.\n");
409
410 HRESULT rc = gConsole->SaveState(progress.asOutParam());
411 if (FAILED(rc))
412 {
413 RTPrintf("Error saving state! rc = 0x%x\n", rc);
414 return;
415 }
416 Assert(progress);
417 LONG cPercent = 0;
418
419 RTPrintf("0%%");
420 RTStrmFlush(g_pStdOut);
421 for (;;)
422 {
423 BOOL fCompleted = false;
424 rc = progress->COMGETTER(Completed)(&fCompleted);
425 if (FAILED(rc) || fCompleted)
426 break;
427 ULONG cPercentNow;
428 rc = progress->COMGETTER(Percent)(&cPercentNow);
429 if (FAILED(rc))
430 break;
431 if ((cPercentNow / 10) != (cPercent / 10))
432 {
433 cPercent = cPercentNow;
434 RTPrintf("...%d%%", cPercentNow);
435 RTStrmFlush(g_pStdOut);
436 }
437
438 /* wait */
439 rc = progress->WaitForCompletion(100);
440 }
441
442 HRESULT lrc;
443 rc = progress->COMGETTER(ResultCode)(&lrc);
444 if (FAILED(rc))
445 lrc = ~0;
446 if (!lrc)
447 {
448 RTPrintf(" -- Saved the state successfully.\n");
449 RTThreadYield();
450 }
451 else
452 RTPrintf("-- Error saving state, lrc=%d (%#x)\n", lrc, lrc);
453
454}
455#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
456
457////////////////////////////////////////////////////////////////////////////////
458
459static void show_usage()
460{
461 RTPrintf("Usage:\n"
462 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
463#ifdef VBOX_WITH_VNC
464 " -n, --vnc Enable the built in VNC server\n"
465 " -m, --vncport <port> TCP port number to use for the VNC server\n"
466 " -o, --vncpass <pw> Set the VNC server password\n"
467#endif
468 " -v, -vrde, --vrde on|off|config Enable (default) or disable the VRDE\n"
469 " server or don't change the setting\n"
470 " -e, -vrdeproperty, --vrdeproperty <name=[value]> Set a VRDE property:\n"
471 " \"TCP/Ports\" - comma-separated list of ports\n"
472 " the VRDE server can bind to. Use a dash between\n"
473 " two port numbers to specify a range\n"
474 " \"TCP/Address\" - interface IP the VRDE server\n"
475 " will bind to\n"
476#ifdef VBOX_FFMPEG
477 " -c, -capture, --capture Record the VM screen output to a file\n"
478 " -w, --width Frame width when recording\n"
479 " -h, --height Frame height when recording\n"
480 " -r, --bitrate Recording bit rate when recording\n"
481 " -f, --filename File name when recording. The codec\n"
482 " used will be chosen based on the\n"
483 " file extension\n"
484#endif
485 "\n");
486}
487
488#ifdef VBOX_FFMPEG
489/**
490 * Parse the environment for variables which can influence the FFMPEG settings.
491 * purely for backwards compatibility.
492 * @param pulFrameWidth may be updated with a desired frame width
493 * @param pulFrameHeight may be updated with a desired frame height
494 * @param pulBitRate may be updated with a desired bit rate
495 * @param ppszFileName may be updated with a desired file name
496 */
497static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
498 unsigned long *pulBitRate, const char **ppszFileName)
499{
500 const char *pszEnvTemp;
501
502 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
503 {
504 errno = 0;
505 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
506 if (errno != 0)
507 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
508 else
509 *pulFrameWidth = ulFrameWidth;
510 }
511 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
512 {
513 errno = 0;
514 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
515 if (errno != 0)
516 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
517 else
518 *pulFrameHeight = ulFrameHeight;
519 }
520 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
521 {
522 errno = 0;
523 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
524 if (errno != 0)
525 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
526 else
527 *pulBitRate = ulBitRate;
528 }
529 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
530 *ppszFileName = pszEnvTemp;
531}
532#endif /* VBOX_FFMPEG defined */
533
534#ifdef RT_OS_WINDOWS
535// Required for ATL
536static CComModule _Module;
537#endif
538
539/**
540 * Entry point.
541 */
542extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
543{
544 const char *vrdePort = NULL;
545 const char *vrdeAddress = NULL;
546 const char *vrdeEnabled = NULL;
547 unsigned cVRDEProperties = 0;
548 const char *aVRDEProperties[16];
549#ifdef VBOX_WITH_VNC
550 bool fVNCEnable = false;
551 unsigned uVNCPort = 0; /* default port */
552 char const *pszVNCPassword = NULL; /* no password */
553#endif
554 unsigned fRawR0 = ~0U;
555 unsigned fRawR3 = ~0U;
556 unsigned fPATM = ~0U;
557 unsigned fCSAM = ~0U;
558#ifdef VBOX_FFMPEG
559 unsigned fFFMPEG = 0;
560 unsigned long ulFrameWidth = 800;
561 unsigned long ulFrameHeight = 600;
562 unsigned long ulBitRate = 300000;
563 char pszMPEGFile[RTPATH_MAX];
564 const char *pszFileNameParam = "VBox-%d.vob";
565#endif /* VBOX_FFMPEG */
566
567
568 /* Make sure that DISPLAY is unset, so that X11 bits do not get initialised
569 * on X11-using OSes. */
570 /** @todo this should really be taken care of in Main. */
571 RTEnvUnset("DISPLAY");
572
573 LogFlow (("VBoxHeadless STARTED.\n"));
574 RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
575 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
576 "All rights reserved.\n\n");
577
578#ifdef VBOX_FFMPEG
579 /* Parse the environment */
580 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
581#endif
582
583 enum eHeadlessOptions
584 {
585 OPT_RAW_R0 = 0x100,
586 OPT_NO_RAW_R0,
587 OPT_RAW_R3,
588 OPT_NO_RAW_R3,
589 OPT_PATM,
590 OPT_NO_PATM,
591 OPT_CSAM,
592 OPT_NO_CSAM,
593 OPT_COMMENT
594 };
595
596 static const RTGETOPTDEF s_aOptions[] =
597 {
598 { "-startvm", 's', RTGETOPT_REQ_STRING },
599 { "--startvm", 's', RTGETOPT_REQ_STRING },
600 { "-vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
601 { "--vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
602 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
603 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
604 { "-vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
605 { "--vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
606 { "-vrde", 'v', RTGETOPT_REQ_STRING },
607 { "--vrde", 'v', RTGETOPT_REQ_STRING },
608 { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
609 { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
610#ifdef VBOX_WITH_VNC
611 { "--vncport", 'm', RTGETOPT_REQ_INT32 },
612 { "--vncpass", 'o', RTGETOPT_REQ_STRING },
613 { "--vnc", 'n', 0 },
614#endif /* VBOX_WITH_VNC */
615 { "-rawr0", OPT_RAW_R0, 0 },
616 { "--rawr0", OPT_RAW_R0, 0 },
617 { "-norawr0", OPT_NO_RAW_R0, 0 },
618 { "--norawr0", OPT_NO_RAW_R0, 0 },
619 { "-rawr3", OPT_RAW_R3, 0 },
620 { "--rawr3", OPT_RAW_R3, 0 },
621 { "-norawr3", OPT_NO_RAW_R3, 0 },
622 { "--norawr3", OPT_NO_RAW_R3, 0 },
623 { "-patm", OPT_PATM, 0 },
624 { "--patm", OPT_PATM, 0 },
625 { "-nopatm", OPT_NO_PATM, 0 },
626 { "--nopatm", OPT_NO_PATM, 0 },
627 { "-csam", OPT_CSAM, 0 },
628 { "--csam", OPT_CSAM, 0 },
629 { "-nocsam", OPT_NO_CSAM, 0 },
630 { "--nocsam", OPT_NO_CSAM, 0 },
631#ifdef VBOX_FFMPEG
632 { "-capture", 'c', 0 },
633 { "--capture", 'c', 0 },
634 { "--width", 'w', RTGETOPT_REQ_UINT32 },
635 { "--height", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
636 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
637 { "--filename", 'f', RTGETOPT_REQ_STRING },
638#endif /* VBOX_FFMPEG defined */
639 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
640 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
641 };
642
643 const char *pcszNameOrUUID = NULL;
644
645 // parse the command line
646 int ch;
647 RTGETOPTUNION ValueUnion;
648 RTGETOPTSTATE GetState;
649 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
650 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
651 {
652 switch(ch)
653 {
654 case 's':
655 pcszNameOrUUID = ValueUnion.psz;
656 break;
657 case 'p':
658 RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
659 vrdePort = ValueUnion.psz;
660 break;
661 case 'a':
662 RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
663 vrdeAddress = ValueUnion.psz;
664 break;
665 case 'v':
666 vrdeEnabled = ValueUnion.psz;
667 break;
668 case 'e':
669 if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
670 aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
671 else
672 RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
673 break;
674#ifdef VBOX_WITH_VNC
675 case 'n':
676 fVNCEnable = true;
677 break;
678 case 'm':
679 uVNCPort = ValueUnion.i32;
680 break;
681 case 'o':
682 pszVNCPassword = ValueUnion.psz;
683 break;
684#endif /* VBOX_WITH_VNC */
685 case OPT_RAW_R0:
686 fRawR0 = true;
687 break;
688 case OPT_NO_RAW_R0:
689 fRawR0 = false;
690 break;
691 case OPT_RAW_R3:
692 fRawR3 = true;
693 break;
694 case OPT_NO_RAW_R3:
695 fRawR3 = false;
696 break;
697 case OPT_PATM:
698 fPATM = true;
699 break;
700 case OPT_NO_PATM:
701 fPATM = false;
702 break;
703 case OPT_CSAM:
704 fCSAM = true;
705 break;
706 case OPT_NO_CSAM:
707 fCSAM = false;
708 break;
709#ifdef VBOX_FFMPEG
710 case 'c':
711 fFFMPEG = true;
712 break;
713 case 'w':
714 ulFrameWidth = ValueUnion.u32;
715 break;
716 case 'r':
717 ulBitRate = ValueUnion.u32;
718 break;
719 case 'f':
720 pszFileNameParam = ValueUnion.psz;
721 break;
722#endif /* VBOX_FFMPEG defined */
723 case 'h':
724#ifdef VBOX_FFMPEG
725 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
726 {
727 ulFrameHeight = ValueUnion.u32;
728 break;
729 }
730#endif
731 show_usage();
732 return 0;
733 case OPT_COMMENT:
734 /* nothing to do */
735 break;
736 case 'V':
737 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
738 return 0;
739 default:
740 ch = RTGetOptPrintError(ch, &ValueUnion);
741 show_usage();
742 return ch;
743 }
744 }
745
746#ifdef VBOX_FFMPEG
747 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
748 {
749 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
750 return 1;
751 }
752 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
753 {
754 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
755 return 1;
756 }
757 if (ulBitRate < 300000 || ulBitRate > 1000000)
758 {
759 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
760 return 1;
761 }
762 /* Make sure we only have %d or %u (or none) in the file name specified */
763 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
764 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
765 {
766 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
767 return 1;
768 }
769 /* And no more than one % in the name */
770 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
771 {
772 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
773 return 1;
774 }
775 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
776#endif /* defined VBOX_FFMPEG */
777
778 if (!pcszNameOrUUID)
779 {
780 show_usage();
781 return 1;
782 }
783
784 HRESULT rc;
785
786 rc = com::Initialize();
787 if (FAILED(rc))
788 {
789 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
790 return 1;
791 }
792
793 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
794 ComPtr<IVirtualBox> virtualBox;
795 ComPtr<ISession> session;
796 ComPtr<IMachine> machine;
797 bool fSessionOpened = false;
798 ComPtr<IEventListener> vboxClientListener;
799 ComPtr<IEventListener> vboxListener;
800 ComObjPtr<ConsoleEventListenerImpl> consoleListener;
801
802 do
803 {
804 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
805 if (FAILED(rc))
806 {
807 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBoxClient object!\n");
808 com::ErrorInfo info;
809 if (!info.isFullAvailable() && !info.isBasicAvailable())
810 {
811 com::GluePrintRCMessage(rc);
812 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
813 }
814 else
815 GluePrintErrorInfo(info);
816 break;
817 }
818
819 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
820 if (FAILED(rc))
821 {
822 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
823 break;
824 }
825 rc = pVirtualBoxClient->COMGETTER(Session)(session.asOutParam());
826 if (FAILED(rc))
827 {
828 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
829 break;
830 }
831
832 ComPtr<IMachine> m;
833
834 rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
835 if (FAILED(rc))
836 {
837 LogError("Invalid machine name or UUID!\n", rc);
838 break;
839 }
840 Bstr id;
841 m->COMGETTER(Id)(id.asOutParam());
842 AssertComRC(rc);
843 if (FAILED(rc))
844 break;
845
846 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
847 Utf8Str(id).c_str()));
848
849 // open a session
850 CHECK_ERROR_BREAK(m, LockMachine(session, LockType_Write));
851 fSessionOpened = true;
852
853 /* get the console */
854 ComPtr<IConsole> console;
855 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
856
857 /* get the mutable machine */
858 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
859
860 ComPtr<IDisplay> display;
861 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
862
863#ifdef VBOX_FFMPEG
864 IFramebuffer *pFramebuffer = 0;
865 RTLDRMOD hLdrFFmpegFB;
866 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
867
868 if (fFFMPEG)
869 {
870 HRESULT rcc = S_OK;
871 int rrc = VINF_SUCCESS;
872 RTERRINFOSTATIC ErrInfo;
873
874 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
875 RTErrInfoInitStatic(&ErrInfo);
876 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
877
878 if (RT_SUCCESS(rrc))
879 {
880 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
881 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
882 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
883 if (RT_FAILURE(rrc))
884 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
885 }
886 else
887 LogError("Failed to load the video capture extension\n", rrc); /** @todo stupid function, no formatting options. */
888 if (RT_SUCCESS(rrc))
889 {
890 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
891 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
892 pszMPEGFile, &pFramebuffer);
893 if (rcc != S_OK)
894 LogError("Failed to initialise video capturing - make sure that the file format\n"
895 "you wish to use is supported on your system\n", rcc);
896 }
897 if (RT_SUCCESS(rrc) && rcc == S_OK)
898 {
899 Log2(("VBoxHeadless: Registering framebuffer\n"));
900 pFramebuffer->AddRef();
901 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
902 }
903 if (!RT_SUCCESS(rrc) || rcc != S_OK)
904 rc = E_FAIL;
905 }
906 if (rc != S_OK)
907 {
908 break;
909 }
910#endif /* defined(VBOX_FFMPEG) */
911#ifdef VBOX_WITH_VNC
912 if (fVNCEnable)
913 {
914 Bstr machineName;
915 machine->COMGETTER(Name)(machineName.asOutParam());
916 g_pFramebufferVNC = new VNCFB(console, uVNCPort, pszVNCPassword);
917 rc = g_pFramebufferVNC->init(machineName.raw() ? Utf8Str(machineName.raw()).c_str() : "");
918 if (rc != S_OK)
919 {
920 LogError("Failed to load the vnc server extension, possibly due to a damaged file\n", rc);
921 delete g_pFramebufferVNC;
922 break;
923 }
924
925 Log2(("VBoxHeadless: Registering VNC framebuffer\n"));
926 g_pFramebufferVNC->AddRef();
927 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, g_pFramebufferVNC);
928 }
929 if (rc != S_OK)
930 break;
931#endif
932 ULONG cMonitors = 1;
933 machine->COMGETTER(MonitorCount)(&cMonitors);
934
935 unsigned uScreenId;
936 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
937 {
938# ifdef VBOX_FFMPEG
939 if (fFFMPEG && uScreenId == 0)
940 {
941 /* Already registered. */
942 continue;
943 }
944# endif
945# ifdef VBOX_WITH_VNC
946 if (fVNCEnable && uScreenId == 0)
947 {
948 /* Already registered. */
949 continue;
950 }
951# endif
952 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer();
953 if (!pVRDPFramebuffer)
954 {
955 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
956 break;
957 }
958 pVRDPFramebuffer->AddRef();
959 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
960 }
961 if (uScreenId < cMonitors)
962 {
963 break;
964 }
965
966 // fill in remaining slots with null framebuffers
967 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
968 {
969 ComPtr<IFramebuffer> fb;
970 LONG xOrigin, yOrigin;
971 HRESULT hrc2 = display->GetFramebuffer(uScreenId,
972 fb.asOutParam(),
973 &xOrigin, &yOrigin);
974 if (hrc2 == S_OK && fb.isNull())
975 {
976 NullFB *pNullFB = new NullFB();
977 pNullFB->AddRef();
978 pNullFB->init();
979 display->SetFramebuffer(uScreenId, pNullFB);
980 }
981 }
982
983 /* get the machine debugger (isn't necessarily available) */
984 ComPtr <IMachineDebugger> machineDebugger;
985 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
986 if (machineDebugger)
987 {
988 Log(("Machine debugger available!\n"));
989 }
990
991 if (fRawR0 != ~0U)
992 {
993 if (!machineDebugger)
994 {
995 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
996 break;
997 }
998 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
999 }
1000 if (fRawR3 != ~0U)
1001 {
1002 if (!machineDebugger)
1003 {
1004 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
1005 break;
1006 }
1007 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1008 }
1009 if (fPATM != ~0U)
1010 {
1011 if (!machineDebugger)
1012 {
1013 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
1014 break;
1015 }
1016 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1017 }
1018 if (fCSAM != ~0U)
1019 {
1020 if (!machineDebugger)
1021 {
1022 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
1023 break;
1024 }
1025 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1026 }
1027
1028 /* initialize global references */
1029 gConsole = console;
1030 gEventQ = com::EventQueue::getMainEventQueue();
1031
1032 /* VirtualBoxClient events registration. */
1033 {
1034 ComPtr<IEventSource> pES;
1035 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1036 ComObjPtr<VirtualBoxClientEventListenerImpl> listener;
1037 listener.createObject();
1038 listener->init(new VirtualBoxClientEventListener());
1039 vboxClientListener = listener;
1040 com::SafeArray<VBoxEventType_T> eventTypes;
1041 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
1042 CHECK_ERROR(pES, RegisterListener(vboxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1043 }
1044
1045 /* Console events registration. */
1046 {
1047 ComPtr<IEventSource> es;
1048 CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
1049 consoleListener.createObject();
1050 consoleListener->init(new ConsoleEventListener());
1051 com::SafeArray<VBoxEventType_T> eventTypes;
1052 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1053 eventTypes.push_back(VBoxEventType_OnStateChanged);
1054 eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
1055 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1056 eventTypes.push_back(VBoxEventType_OnShowWindow);
1057 CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
1058 }
1059
1060 /* default is to enable the remote desktop server (backward compatibility) */
1061 BOOL fVRDEEnable = true;
1062 BOOL fVRDEEnabled;
1063 ComPtr <IVRDEServer> vrdeServer;
1064 CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
1065 CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));
1066
1067 if (vrdeEnabled != NULL)
1068 {
1069 /* -vrdeServer on|off|config */
1070 if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
1071 fVRDEEnable = false;
1072 else if (!strcmp(vrdeEnabled, "config"))
1073 {
1074 if (!fVRDEEnabled)
1075 fVRDEEnable = false;
1076 }
1077 else if (strcmp(vrdeEnabled, "on") && strcmp(vrdeEnabled, "enable"))
1078 {
1079 RTPrintf("-vrdeServer requires an argument (on|off|config)\n");
1080 break;
1081 }
1082 }
1083
1084 if (fVRDEEnable)
1085 {
1086 Log(("VBoxHeadless: Enabling VRDE server...\n"));
1087
1088 /* set VRDE port if requested by the user */
1089 if (vrdePort != NULL)
1090 {
1091 Bstr bstr = vrdePort;
1092 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
1093 }
1094 /* set VRDE address if requested by the user */
1095 if (vrdeAddress != NULL)
1096 {
1097 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
1098 }
1099
1100 /* Set VRDE properties. */
1101 if (cVRDEProperties > 0)
1102 {
1103 for (unsigned i = 0; i < cVRDEProperties; i++)
1104 {
1105 /* Parse 'name=value' */
1106 char *pszProperty = RTStrDup(aVRDEProperties[i]);
1107 if (pszProperty)
1108 {
1109 char *pDelimiter = strchr(pszProperty, '=');
1110 if (pDelimiter)
1111 {
1112 *pDelimiter = '\0';
1113
1114 Bstr bstrName = pszProperty;
1115 Bstr bstrValue = &pDelimiter[1];
1116 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1117 }
1118 else
1119 {
1120 RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
1121 RTStrFree(pszProperty);
1122 rc = E_INVALIDARG;
1123 break;
1124 }
1125 RTStrFree(pszProperty);
1126 }
1127 else
1128 {
1129 RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
1130 rc = E_OUTOFMEMORY;
1131 break;
1132 }
1133 }
1134 if (FAILED(rc))
1135 break;
1136 }
1137
1138 /* enable VRDE server (only if currently disabled) */
1139 if (!fVRDEEnabled)
1140 {
1141 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
1142 }
1143 }
1144 else
1145 {
1146 /* disable VRDE server (only if currently enabled */
1147 if (fVRDEEnabled)
1148 {
1149 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
1150 }
1151 }
1152
1153 Log(("VBoxHeadless: Powering up the machine...\n"));
1154
1155 ComPtr <IProgress> progress;
1156 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1157
1158 /*
1159 * Wait for the result because there can be errors.
1160 *
1161 * It's vital to process events while waiting (teleportation deadlocks),
1162 * so we'll poll for the completion instead of waiting on it.
1163 */
1164 for (;;)
1165 {
1166 BOOL fCompleted;
1167 rc = progress->COMGETTER(Completed)(&fCompleted);
1168 if (FAILED(rc) || fCompleted)
1169 break;
1170
1171 /* Process pending events, then wait for new ones. Note, this
1172 * processes NULL events signalling event loop termination. */
1173 gEventQ->processEventQueue(0);
1174 if (!g_fTerminateFE)
1175 gEventQ->processEventQueue(500);
1176 }
1177
1178 if (SUCCEEDED(progress->WaitForCompletion(-1)))
1179 {
1180 /* Figure out if the operation completed with a failed status
1181 * and print the error message. Terminate immediately, and let
1182 * the cleanup code take care of potentially pending events. */
1183 LONG progressRc;
1184 progress->COMGETTER(ResultCode)(&progressRc);
1185 rc = progressRc;
1186 if (FAILED(rc))
1187 {
1188 com::ProgressErrorInfo info(progress);
1189 if (info.isBasicAvailable())
1190 {
1191 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
1192 }
1193 else
1194 {
1195 RTPrintf("Error: failed to start machine. No error message available!\n");
1196 }
1197 break;
1198 }
1199 }
1200
1201 /* VirtualBox events registration. */
1202 {
1203 ComPtr<IEventSource> es;
1204 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1205 ComObjPtr<VirtualBoxEventListenerImpl> listener;
1206 listener.createObject();
1207 listener->init(new VirtualBoxEventListener());
1208 vboxListener = listener;
1209 com::SafeArray<VBoxEventType_T> eventTypes;
1210 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
1211 CHECK_ERROR(es, RegisterListener(vboxListener, ComSafeArrayAsInParam(eventTypes), true));
1212 }
1213
1214#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1215 signal(SIGINT, SaveState);
1216 signal(SIGTERM, SaveState);
1217#endif
1218
1219 Log(("VBoxHeadless: Waiting for PowerDown...\n"));
1220
1221 while ( !g_fTerminateFE
1222 && RT_SUCCESS(gEventQ->processEventQueue(RT_INDEFINITE_WAIT)))
1223 /* nothing */ ;
1224
1225 Log(("VBoxHeadless: event loop has terminated...\n"));
1226
1227#ifdef VBOX_FFMPEG
1228 if (pFramebuffer)
1229 {
1230 pFramebuffer->Release();
1231 Log(("Released framebuffer\n"));
1232 pFramebuffer = NULL;
1233 }
1234#endif /* defined(VBOX_FFMPEG) */
1235
1236 /* we don't have to disable VRDE here because we don't save the settings of the VM */
1237 }
1238 while (0);
1239
1240 /*
1241 * Get the machine state.
1242 */
1243 MachineState_T machineState = MachineState_Aborted;
1244 if (!machine.isNull())
1245 machine->COMGETTER(State)(&machineState);
1246
1247 /*
1248 * Turn off the VM if it's running
1249 */
1250 if ( gConsole
1251 && ( machineState == MachineState_Running
1252 || machineState == MachineState_Teleporting
1253 || machineState == MachineState_LiveSnapshotting
1254 /** @todo power off paused VMs too? */
1255 )
1256 )
1257 do
1258 {
1259 consoleListener->getWrapped()->ignorePowerOffEvents(true);
1260 ComPtr<IProgress> pProgress;
1261 CHECK_ERROR_BREAK(gConsole, PowerDown(pProgress.asOutParam()));
1262 CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
1263 BOOL completed;
1264 CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
1265 ASSERT(completed);
1266 LONG hrc;
1267 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc));
1268 if (FAILED(hrc))
1269 {
1270 RTPrintf("VBoxHeadless: ERROR: Failed to power down VM!");
1271 com::ErrorInfo info;
1272 if (!info.isFullAvailable() && !info.isBasicAvailable())
1273 com::GluePrintRCMessage(hrc);
1274 else
1275 GluePrintErrorInfo(info);
1276 break;
1277 }
1278 } while (0);
1279
1280 /* VirtualBox callback unregistration. */
1281 if (vboxListener)
1282 {
1283 ComPtr<IEventSource> es;
1284 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1285 if (!es.isNull())
1286 CHECK_ERROR(es, UnregisterListener(vboxListener));
1287 vboxListener.setNull();
1288 }
1289
1290 /* Console callback unregistration. */
1291 if (consoleListener)
1292 {
1293 ComPtr<IEventSource> es;
1294 CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
1295 if (!es.isNull())
1296 CHECK_ERROR(es, UnregisterListener(consoleListener));
1297 consoleListener.setNull();
1298 }
1299
1300 /* VirtualBoxClient callback unregistration. */
1301 if (vboxClientListener)
1302 {
1303 ComPtr<IEventSource> pES;
1304 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1305 if (!pES.isNull())
1306 CHECK_ERROR(pES, UnregisterListener(vboxClientListener));
1307 vboxClientListener.setNull();
1308 }
1309
1310 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1311 gConsole = NULL;
1312
1313 if (fSessionOpened)
1314 {
1315 /*
1316 * Close the session. This will also uninitialize the console and
1317 * unregister the callback we've registered before.
1318 */
1319 Log(("VBoxHeadless: Closing the session...\n"));
1320 session->UnlockMachine();
1321 }
1322
1323 /* Must be before com::Shutdown */
1324 session.setNull();
1325 virtualBox.setNull();
1326 pVirtualBoxClient.setNull();
1327 machine.setNull();
1328
1329 com::Shutdown();
1330
1331 LogFlow(("VBoxHeadless FINISHED.\n"));
1332
1333 return FAILED(rc) ? 1 : 0;
1334}
1335
1336
1337#ifndef VBOX_WITH_HARDENING
1338/**
1339 * Main entry point.
1340 */
1341int main(int argc, char **argv, char **envp)
1342{
1343 // initialize VBox Runtime
1344 int rc = RTR3InitAndSUPLib();
1345 if (RT_FAILURE(rc))
1346 {
1347 RTPrintf("VBoxHeadless: Runtime Error:\n"
1348 " %Rrc -- %Rrf\n", rc, rc);
1349 switch (rc)
1350 {
1351 case VERR_VM_DRIVER_NOT_INSTALLED:
1352 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1353 "loaded successfully. Aborting ...\n");
1354 break;
1355 default:
1356 break;
1357 }
1358 return 1;
1359 }
1360
1361 return TrustedMain(argc, argv, envp);
1362}
1363#endif /* !VBOX_WITH_HARDENING */
1364
1365#ifdef VBOX_WITH_XPCOM
1366NS_DECL_CLASSINFO(NullFB)
1367NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NullFB, IFramebuffer)
1368#endif
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