VirtualBox

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

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

Added RTLdrLoadEx for exposing dlerror info.

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