VirtualBox

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

Last change on this file since 41956 was 41956, checked in by vboxsync, 12 years ago

VBoxHeadless: Corrected the C define "VBOX_FFMPEG" to more meaningful one "VBOX_WITH_VIDEO_REC" .

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