VirtualBox

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

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

FE/Headless, FE/SDL: fixed a couple of copy'n'paste bugs

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