VirtualBox

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

Last change on this file since 12623 was 12423, checked in by vboxsync, 16 years ago

IPRT/SUP: Renamed RTLdrOpenAppSharedLib to RTLdrOpenAppPriv. Hardend versions of RTLdrOpen and RTLdrOpenAppPriv. Use the hardend versions where appropriate.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.9 KB
Line 
1/** @file
2 *
3 * VBox frontends: VBoxHeadless (headless frontend):
4 * Headless server executable
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <VBox/com/com.h>
24#include <VBox/com/string.h>
25#include <VBox/com/Guid.h>
26#include <VBox/com/ErrorInfo.h>
27#include <VBox/com/EventQueue.h>
28
29#include <VBox/com/VirtualBox.h>
30
31using namespace com;
32
33#define LOG_GROUP LOG_GROUP_GUI
34
35#include <VBox/log.h>
36#include <VBox/version.h>
37#ifdef VBOX_WITH_VRDP
38# include <VBox/vrdpapi.h>
39#endif
40#include <iprt/runtime.h>
41#include <iprt/stream.h>
42#include <iprt/ldr.h>
43#include <iprt/getopt.h>
44#include <iprt/env.h>
45
46#ifdef VBOX_FFMPEG
47#include <cstdlib>
48#include <cerrno>
49#include "VBoxHeadless.h"
50#include <iprt/env.h>
51#include <iprt/param.h>
52#include <iprt/process.h>
53#include <VBox/sup.h>
54#endif
55
56//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
57#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
58#include <signal.h>
59#endif
60
61#ifdef VBOX_WITH_VRDP
62# include "Framebuffer.h"
63#endif
64
65////////////////////////////////////////////////////////////////////////////////
66
67#define LogError(m,rc) \
68 do { \
69 Log (("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
70 RTPrintf ("%s", m); \
71 } while (0)
72
73////////////////////////////////////////////////////////////////////////////////
74
75/* global weak references (for event handlers) */
76static ComPtr <ISession, ComWeakRef> gSession;
77static ComPtr <IConsole, ComWeakRef> gConsole;
78static EventQueue *gEventQ = NULL;
79
80////////////////////////////////////////////////////////////////////////////////
81
82/**
83 * State change event.
84 */
85class StateChangeEvent : public Event
86{
87public:
88 StateChangeEvent (MachineState_T state) : mState (state) {}
89protected:
90 void *handler()
91 {
92 LogFlow (("VBoxHeadless: StateChangeEvent: %d\n", mState));
93 /* post the termination event if the machine has been PoweredDown/Saved/Aborted */
94 if (mState < MachineState_Running)
95 gEventQ->postEvent (NULL);
96 return 0;
97 }
98private:
99 MachineState_T mState;
100};
101
102/**
103 * Callback handler for machine events.
104 */
105class ConsoleCallback : public IConsoleCallback
106{
107public:
108
109 ConsoleCallback ()
110 {
111#ifndef VBOX_WITH_XPCOM
112 refcnt = 0;
113#endif
114 }
115
116 virtual ~ConsoleCallback() {}
117
118 NS_DECL_ISUPPORTS
119
120#ifndef VBOX_WITH_XPCOM
121 STDMETHOD_(ULONG, AddRef)()
122 {
123 return ::InterlockedIncrement(&refcnt);
124 }
125 STDMETHOD_(ULONG, Release)()
126 {
127 long cnt = ::InterlockedDecrement(&refcnt);
128 if (cnt == 0)
129 delete this;
130 return cnt;
131 }
132 STDMETHOD(QueryInterface) (REFIID riid , void **ppObj)
133 {
134 if (riid == IID_IUnknown)
135 {
136 *ppObj = this;
137 AddRef();
138 return S_OK;
139 }
140 if (riid == IID_IConsoleCallback)
141 {
142 *ppObj = this;
143 AddRef();
144 return S_OK;
145 }
146 *ppObj = NULL;
147 return E_NOINTERFACE;
148 }
149#endif
150
151 STDMETHOD(OnMousePointerShapeChange) (BOOL visible, BOOL alpha, ULONG xHot, ULONG yHot,
152 ULONG width, ULONG height, BYTE *shape)
153 {
154 return S_OK;
155 }
156
157 STDMETHOD(OnMouseCapabilityChange) (BOOL supportsAbsolute, BOOL needsHostCursor)
158 {
159 /* Emit absolute mouse event to actually enable the host mouse cursor. */
160 if (supportsAbsolute && gConsole)
161 {
162 ComPtr<IMouse> mouse;
163 gConsole->COMGETTER(Mouse)(mouse.asOutParam());
164 if (mouse)
165 {
166 mouse->PutMouseEventAbsolute(-1, -1, 0, 0);
167 }
168 }
169 return S_OK;
170 }
171
172 STDMETHOD(OnKeyboardLedsChange)(BOOL fNumLock, BOOL fCapsLock, BOOL fScrollLock)
173 {
174 return S_OK;
175 }
176
177 STDMETHOD(OnStateChange) (MachineState_T machineState)
178 {
179 gEventQ->postEvent (new StateChangeEvent (machineState));
180 return S_OK;
181 }
182
183 STDMETHOD(OnExtraDataChange) (BSTR key)
184 {
185 return S_OK;
186 }
187
188 STDMETHOD(OnAdditionsStateChange)()
189 {
190 return S_OK;
191 }
192
193 STDMETHOD(OnDVDDriveChange)()
194 {
195 return S_OK;
196 }
197
198 STDMETHOD(OnFloppyDriveChange)()
199 {
200 return S_OK;
201 }
202
203 STDMETHOD(OnNetworkAdapterChange) (INetworkAdapter *aNetworkAdapter)
204 {
205 return S_OK;
206 }
207
208 STDMETHOD(OnSerialPortChange) (ISerialPort *aSerialPort)
209 {
210 return S_OK;
211 }
212
213 STDMETHOD(OnParallelPortChange) (IParallelPort *aParallelPort)
214 {
215 return S_OK;
216 }
217
218 STDMETHOD(OnVRDPServerChange)()
219 {
220 return S_OK;
221 }
222
223 STDMETHOD(OnUSBControllerChange)()
224 {
225 return S_OK;
226 }
227
228 STDMETHOD(OnUSBDeviceStateChange) (IUSBDevice *aDevice, BOOL aAttached,
229 IVirtualBoxErrorInfo *aError)
230 {
231 return S_OK;
232 }
233
234 STDMETHOD(OnSharedFolderChange) (Scope_T aScope)
235 {
236 return S_OK;
237 }
238
239 STDMETHOD(OnRuntimeError)(BOOL fatal, INPTR BSTR id, INPTR BSTR message)
240 {
241 return S_OK;
242 }
243
244 STDMETHOD(OnCanShowWindow)(BOOL *canShow)
245 {
246 if (!canShow)
247 return E_POINTER;
248 /* Headless windows should not be shown */
249 *canShow = FALSE;
250 return S_OK;
251 }
252
253 STDMETHOD(OnShowWindow) (ULONG64 *winId)
254 {
255 /* OnCanShowWindow() always returns FALSE, so this call should never
256 * happen. */
257 AssertFailed();
258 if (!winId)
259 return E_POINTER;
260 *winId = 0;
261 return E_NOTIMPL;
262 }
263
264private:
265
266#ifndef VBOX_WITH_XPCOM
267 long refcnt;
268#endif
269};
270
271#ifdef VBOX_WITH_XPCOM
272NS_DECL_CLASSINFO (ConsoleCallback)
273NS_IMPL_THREADSAFE_ISUPPORTS1_CI (ConsoleCallback, IConsoleCallback)
274#endif
275
276#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
277static void SaveState(int sig)
278{
279 ComPtr <IProgress> progress = NULL;
280
281/** @todo Deal with nested signals, multithreaded signal dispatching (esp. on windows),
282 * and multiple signals (both SIGINT and SIGTERM in some order).
283 * Consider processing the signal request asynchronously since there are lots of things
284 * which aren't safe (like RTPrintf and printf IIRC) in a signal context. */
285
286 RTPrintf("Signal received, saving state.\n");
287
288 HRESULT rc = gConsole->SaveState(progress.asOutParam());
289 if (FAILED(S_OK))
290 {
291 RTPrintf("Error saving state! rc = 0x%x\n", rc);
292 return;
293 }
294 Assert(progress);
295 LONG cPercent = 0;
296
297 RTPrintf("0%%");
298 RTStrmFlush(g_pStdOut);
299 for (;;)
300 {
301 BOOL fCompleted = false;
302 rc = progress->COMGETTER(Completed)(&fCompleted);
303 if (FAILED(rc) || fCompleted)
304 break;
305 LONG cPercentNow;
306 rc = progress->COMGETTER(Percent)(&cPercentNow);
307 if (FAILED(rc))
308 break;
309 if ((cPercentNow / 10) != (cPercent / 10))
310 {
311 cPercent = cPercentNow;
312 RTPrintf("...%d%%", cPercentNow);
313 RTStrmFlush(g_pStdOut);
314 }
315
316 /* wait */
317 rc = progress->WaitForCompletion(100);
318 }
319
320 HRESULT lrc;
321 rc = progress->COMGETTER(ResultCode)(&lrc);
322 if (FAILED(rc))
323 lrc = ~0;
324 if (!lrc)
325 {
326 RTPrintf(" -- Saved the state successfully.\n");
327 RTThreadYield();
328 }
329 else
330 RTPrintf("-- Error saving state, lrc=%d (%#x)\n", lrc, lrc);
331
332}
333#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
334
335////////////////////////////////////////////////////////////////////////////////
336
337static void show_usage()
338{
339 RTPrintf("Usage:\n"
340 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
341#ifdef VBOX_WITH_VRDP
342 " -v, -vrdp, --vrdp on|off|config Enable (default) or disable the VRDP\n"
343 " server or don't change the setting\n"
344 " -p, -vrdpport, --vrdpport <port> Port number the VRDP server will bind\n"
345 " to\n"
346 " -a, -vrdpaddress, --vrdpaddress <ip> Interface IP the VRDP will bind to \n"
347#endif
348#ifdef VBOX_FFMPEG
349 " -c, -capture, --capture Record the VM screen output to a file\n"
350 " -w, --width Frame width when recording\n"
351 " -h, --height Frame height when recording\n"
352 " -r, --bitrate Recording bit rate when recording\n"
353 " -f, --filename File name when recording. The codec\n"
354 " used will be chosen based on the\n"
355 " file extension\n"
356#endif
357 "\n");
358}
359
360#ifdef VBOX_FFMPEG
361/**
362 * Parse the environment for variables which can influence the FFMPEG settings.
363 * purely for backwards compatibility.
364 * @param pulFrameWidth may be updated with a desired frame width
365 * @param pulFrameHeight may be updated with a desired frame height
366 * @param pulBitRate may be updated with a desired bit rate
367 * @param ppszFileName may be updated with a desired file name
368 */
369static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
370 unsigned long *pulBitRate, const char **ppszFileName)
371{
372 const char *pszEnvTemp;
373
374 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
375 {
376 errno = 0;
377 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
378 if (errno != 0)
379 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
380 else
381 *pulFrameWidth = ulFrameWidth;
382 }
383 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
384 {
385 errno = 0;
386 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
387 if (errno != 0)
388 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
389 else
390 *pulFrameHeight = ulFrameHeight;
391 }
392 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
393 {
394 errno = 0;
395 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
396 if (errno != 0)
397 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
398 else
399 *pulBitRate = ulBitRate;
400 }
401 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
402 *ppszFileName = pszEnvTemp;
403}
404#endif /* VBOX_FFMPEG defined */
405
406/**
407 * Entry point.
408 */
409extern "C" DECLEXPORT (int) TrustedMain (int argc, char **argv, char **envp)
410{
411#ifdef VBOX_WITH_VRDP
412 ULONG vrdpPort = ~0U;
413 const char *vrdpAddress = NULL;
414 const char *vrdpEnabled = NULL;
415#endif
416 unsigned fRawR0 = ~0U;
417 unsigned fRawR3 = ~0U;
418 unsigned fPATM = ~0U;
419 unsigned fCSAM = ~0U;
420#ifdef VBOX_FFMPEG
421 unsigned fFFMPEG = 0;
422 unsigned long ulFrameWidth = 800;
423 unsigned long ulFrameHeight = 600;
424 unsigned long ulBitRate = 300000;
425 char pszMPEGFile[RTPATH_MAX];
426 const char *pszFileNameParam = "VBox-%d.vob";
427#endif /* VBOX_FFMPEG */
428
429 /* Make sure that DISPLAY is unset, so that X11 bits do not get initialised
430 * on X11-using OSes. */
431 /** @todo this should really be taken care of in Main. */
432 RTEnvUnset("DISPLAY");
433
434 LogFlow (("VBoxHeadless STARTED.\n"));
435 RTPrintf ("VirtualBox Headless Interface %s\n"
436 "(C) 2008 Sun Microsystems, Inc.\n"
437 "All rights reserved\n\n",
438 VBOX_VERSION_STRING);
439
440 Guid id;
441 /* the below cannot be Bstr because on Linux Bstr doesn't work until XPCOM (nsMemory) is initialized */
442 const char *name = NULL;
443
444#ifdef VBOX_FFMPEG
445 /* Parse the environment */
446 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
447#endif
448
449 enum eHeadlessOptions
450 {
451 OPT_RAW_R0 = 0x100,
452 OPT_NO_RAW_R0,
453 OPT_RAW_R3,
454 OPT_NO_RAW_R3,
455 OPT_PATM,
456 OPT_NO_PATM,
457 OPT_CSAM,
458 OPT_NO_CSAM,
459 OPT_COMMENT,
460 };
461
462 static const RTOPTIONDEF g_aOptions[] =
463 {
464 { "-startvm", 's', RTGETOPT_REQ_STRING },
465 { "--startvm", 's', RTGETOPT_REQ_STRING },
466#ifdef VBOX_WITH_VRDP
467 { "-vrdpport", 'p', RTGETOPT_REQ_UINT32 },
468 { "--vrdpport", 'p', RTGETOPT_REQ_UINT32 },
469 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING },
470 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING },
471 { "-vrdp", 'v', RTGETOPT_REQ_STRING },
472 { "--vrdp", 'v', RTGETOPT_REQ_STRING },
473#endif /* VBOX_WITH_VRDP defined */
474 { "-rawr0", OPT_RAW_R0, 0 },
475 { "--rawr0", OPT_RAW_R0, 0 },
476 { "-norawr0", OPT_NO_RAW_R0, 0 },
477 { "--norawr0", OPT_NO_RAW_R0, 0 },
478 { "-rawr3", OPT_RAW_R3, 0 },
479 { "--rawr3", OPT_RAW_R3, 0 },
480 { "-norawr3", OPT_NO_RAW_R3, 0 },
481 { "--norawr3", OPT_NO_RAW_R3, 0 },
482 { "-patm", OPT_PATM, 0 },
483 { "--patm", OPT_PATM, 0 },
484 { "-nopatm", OPT_NO_PATM, 0 },
485 { "--nopatm", OPT_NO_PATM, 0 },
486 { "-csam", OPT_CSAM, 0 },
487 { "--csam", OPT_CSAM, 0 },
488 { "-nocsam", OPT_NO_CSAM, 0 },
489 { "--nocsam", OPT_NO_CSAM, 0 },
490#ifdef VBOX_FFMPEG
491 { "-capture", 'c', 0 },
492 { "--capture", 'c', 0 },
493 { "--width", 'w', RTGETOPT_REQ_UINT32 },
494 { "--height", 'h', RTGETOPT_REQ_UINT32 },
495 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
496 { "--filename", 'f', RTGETOPT_REQ_STRING },
497#endif /* VBOX_FFMPEG defined */
498 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
499 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
500 };
501
502 // parse the command line
503 int ch;
504 int i = 1;
505 RTOPTIONUNION ValueUnion;
506 while ((ch = RTGetOpt(argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), &i, &ValueUnion)))
507 {
508 if (ch < 0)
509 {
510 show_usage();
511 exit(-1);
512 }
513 switch(ch)
514 {
515 case 's':
516 id = ValueUnion.psz;
517 /* If the argument was not a UUID, then it must be a name. */
518 if (!id)
519 name = ValueUnion.psz;
520 break;
521#ifdef VBOX_WITH_VRDP
522 case 'p':
523 vrdpPort = ValueUnion.u32;
524 break;
525 case 'a':
526 vrdpAddress = ValueUnion.psz;
527 break;
528 case 'v':
529 vrdpEnabled = ValueUnion.psz;
530 break;
531#endif /* VBOX_WITH_VRDP defined */
532 case OPT_RAW_R0:
533 fRawR0 = true;
534 break;
535 case OPT_NO_RAW_R0:
536 fRawR0 = false;
537 break;
538 case OPT_RAW_R3:
539 fRawR3 = true;
540 break;
541 case OPT_NO_RAW_R3:
542 fRawR3 = false;
543 break;
544 case OPT_PATM:
545 fPATM = true;
546 break;
547 case OPT_NO_PATM:
548 fPATM = false;
549 break;
550 case OPT_CSAM:
551 fCSAM = true;
552 break;
553 case OPT_NO_CSAM:
554 fCSAM = false;
555 break;
556#ifdef VBOX_FFMPEG
557 case 'c':
558 fFFMPEG = true;
559 break;
560 case 'w':
561 ulFrameWidth = ValueUnion.u32;
562 break;
563 case 'h':
564 ulFrameHeight = ValueUnion.u32;
565 break;
566 case 'r':
567 ulBitRate = ValueUnion.u32;
568 break;
569 case 'f':
570 pszFileNameParam = ValueUnion.psz;
571 break;
572#endif /* VBOX_FFMPEG defined */
573 default: /* comment */
574 break;
575 }
576 }
577
578#ifdef VBOX_FFMPEG
579 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
580 {
581 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
582 return -1;
583 }
584 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
585 {
586 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
587 return -1;
588 }
589 if (ulBitRate < 300000 || ulBitRate > 1000000)
590 {
591 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
592 return -1;
593 }
594 /* Make sure we only have %d or %u (or none) in the file name specified */
595 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
596 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
597 {
598 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
599 return -1;
600 }
601 /* And no more than one % in the name */
602 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
603 {
604 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
605 return -1;
606 }
607 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
608#endif /* defined VBOX_FFMPEG */
609
610 if (!id && !name)
611 {
612 show_usage();
613 return -1;
614 }
615
616 HRESULT rc;
617
618 rc = com::Initialize();
619 if (FAILED (rc))
620 return rc;
621
622 do
623 {
624 ComPtr <IVirtualBox> virtualBox;
625 ComPtr <ISession> session;
626
627 /* create VirtualBox object */
628 rc = virtualBox.createLocalObject (CLSID_VirtualBox);
629 if (FAILED (rc))
630 {
631 com::ErrorInfo info;
632 if (info.isFullAvailable())
633 {
634 RTPrintf("Failed to create VirtualBox object! Error info: '%lS' (component %lS).\n",
635 info.getText().raw(), info.getComponent().raw());
636 }
637 else
638 RTPrintf("Failed to create VirtualBox object! No error information available (rc = 0x%x).\n", rc);
639 break;
640 }
641
642 /* create Session object */
643 rc = session.createInprocObject (CLSID_Session);
644 if (FAILED (rc))
645 {
646 LogError ("Cannot create Session object!", rc);
647 break;
648 }
649
650 /* find ID by name */
651 if (!id)
652 {
653 ComPtr <IMachine> m;
654 rc = virtualBox->FindMachine (Bstr (name), m.asOutParam());
655 if (FAILED (rc))
656 {
657 LogError ("Invalid machine name!\n", rc);
658 break;
659 }
660 m->COMGETTER(Id) (id.asOutParam());
661 AssertComRC (rc);
662 if (FAILED (rc))
663 break;
664 }
665
666 Log (("VBoxHeadless: Opening a session with machine (id={%s})...\n",
667 id.toString().raw()));
668
669 // open a session
670 CHECK_ERROR_BREAK(virtualBox, OpenSession (session, id));
671
672 /* get the console */
673 ComPtr <IConsole> console;
674 CHECK_ERROR_BREAK(session, COMGETTER (Console) (console.asOutParam()));
675
676 /* get the machine */
677 ComPtr <IMachine> machine;
678 CHECK_ERROR_BREAK(console, COMGETTER(Machine) (machine.asOutParam()));
679
680 ComPtr <IDisplay> display;
681 CHECK_ERROR_BREAK(console, COMGETTER(Display) (display.asOutParam()));
682
683#ifdef VBOX_FFMPEG
684 IFramebuffer *pFramebuffer = 0;
685 RTLDRMOD hLdrFFmpegFB;
686 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
687
688 if (fFFMPEG)
689 {
690 int rrc = VINF_SUCCESS, rcc = S_OK;
691
692 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
693 rrc = SUPR3HardenedLdrLoad("VBoxFFmpegFB", &hLdrFFmpegFB);
694
695 if (RT_SUCCESS(rrc))
696 {
697 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
698 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
699 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
700 if (RT_FAILURE(rrc))
701 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
702 }
703 else
704 LogError("Failed to load the video capture extension\n", rrc);
705 if (RT_SUCCESS(rrc))
706 {
707 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
708 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
709 pszMPEGFile, &pFramebuffer);
710 if (rcc != S_OK)
711 LogError("Failed to initialise video capturing - make sure that the file format\n"
712 "you wish to use is supported on your system\n", rcc);
713 }
714 if (RT_SUCCESS(rrc) && (S_OK == rcc))
715 {
716 Log2(("VBoxHeadless: Registering framebuffer\n"));
717 pFramebuffer->AddRef();
718 display->RegisterExternalFramebuffer(pFramebuffer);
719 }
720 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
721 rc = E_FAIL;
722 }
723 if (rc != S_OK)
724 {
725 return -1;
726 }
727#endif /* defined(VBOX_FFMPEG) */
728
729 ULONG cMonitors = 1;
730 machine->COMGETTER(MonitorCount)(&cMonitors);
731
732#ifdef VBOX_WITH_VRDP
733 unsigned uScreenId;
734 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
735 {
736#ifdef VBOX_FFMPEG
737 if (fFFMPEG && uScreenId == 0)
738 {
739 /* Already registered. */
740 continue;
741 }
742#endif /* defined(VBOX_FFMPEG) */
743 VRDPFramebuffer *pFramebuffer = new VRDPFramebuffer ();
744 if (!pFramebuffer)
745 {
746 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
747 return -1;
748 }
749 pFramebuffer->AddRef();
750 display->SetFramebuffer(uScreenId, pFramebuffer);
751 }
752#endif
753
754 /* get the machine debugger (isn't necessarily available) */
755 ComPtr <IMachineDebugger> machineDebugger;
756 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
757 if (machineDebugger)
758 {
759 Log(("Machine debugger available!\n"));
760 }
761
762 if (fRawR0 != ~0U)
763 {
764 if (!machineDebugger)
765 {
766 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
767 break;
768 }
769 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
770 }
771 if (fRawR3 != ~0U)
772 {
773 if (!machineDebugger)
774 {
775 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
776 break;
777 }
778 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
779 }
780 if (fPATM != ~0U)
781 {
782 if (!machineDebugger)
783 {
784 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
785 break;
786 }
787 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
788 }
789 if (fCSAM != ~0U)
790 {
791 if (!machineDebugger)
792 {
793 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
794 break;
795 }
796 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
797 }
798
799 /* create an event queue */
800 EventQueue eventQ;
801
802 /* initialize global references */
803 gSession = session;
804 gConsole = console;
805 gEventQ = &eventQ;
806
807 /* register a callback for machine events */
808 {
809 ConsoleCallback *callback = new ConsoleCallback ();
810 callback->AddRef();
811 CHECK_ERROR(console, RegisterCallback (callback));
812 callback->Release();
813 if (FAILED (rc))
814 break;
815 }
816
817#ifdef VBOX_WITH_VRDP
818 /* default is to enable the RDP server (backward compatibility) */
819 BOOL fVRDPEnable = true;
820 BOOL fVRDPEnabled;
821 ComPtr <IVRDPServer> vrdpServer;
822 CHECK_ERROR_BREAK(machine, COMGETTER (VRDPServer) (vrdpServer.asOutParam()));
823 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled) (&fVRDPEnabled));
824
825 if (vrdpEnabled != NULL)
826 {
827 /* -vrdp on|off|config */
828 if (!strcmp(vrdpEnabled, "off") || !strcmp(vrdpEnabled, "disable"))
829 fVRDPEnable = false;
830 else if (!strcmp(vrdpEnabled, "config"))
831 {
832 if (!fVRDPEnabled)
833 fVRDPEnable = false;
834 }
835 else if (strcmp(vrdpEnabled, "on") && strcmp(vrdpEnabled, "enable"))
836 {
837 RTPrintf("-vrdp requires an argument (on|off|config)\n");
838 break;
839 }
840 }
841
842 if (fVRDPEnable)
843 {
844 Log (("VBoxHeadless: Enabling VRDP server...\n"));
845
846 /* set VRDP port if requested by the user */
847 if (vrdpPort != ~0U)
848 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Port)(vrdpPort));
849 else
850 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Port)(&vrdpPort));
851 /* set VRDP address if requested by the user */
852 if (vrdpAddress != NULL)
853 {
854 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(NetAddress)(Bstr(vrdpAddress)));
855 }
856 /* enable VRDP server (only if currently disabled) */
857 if (!fVRDPEnabled)
858 {
859 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (TRUE));
860 }
861 }
862 else
863 {
864 /* disable VRDP server (only if currently enabled */
865 if (fVRDPEnabled)
866 {
867 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (FALSE));
868 }
869 }
870#endif
871 Log (("VBoxHeadless: Powering up the machine...\n"));
872#ifdef VBOX_WITH_VRDP
873 if (fVRDPEnable)
874 RTPrintf("Listening on port %d\n", !vrdpPort ? VRDP_DEFAULT_PORT : vrdpPort);
875#endif
876
877 ComPtr <IProgress> progress;
878 CHECK_ERROR_BREAK(console, PowerUp (progress.asOutParam()));
879
880 /* wait for result because there can be errors */
881 if (SUCCEEDED(progress->WaitForCompletion (-1)))
882 {
883 progress->COMGETTER(ResultCode)(&rc);
884 if (FAILED(rc))
885 {
886 com::ProgressErrorInfo info(progress);
887 if (info.isBasicAvailable())
888 {
889 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
890 }
891 else
892 {
893 RTPrintf("Error: failed to start machine. No error message available!\n");
894 }
895 }
896 }
897
898#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
899 signal(SIGINT, SaveState);
900 signal(SIGTERM, SaveState);
901#endif
902
903 Log (("VBoxHeadless: Waiting for PowerDown...\n"));
904
905 Event *e;
906
907 while (eventQ.waitForEvent (&e) && e)
908 eventQ.handleEvent (e);
909
910 Log (("VBoxHeadless: event loop has terminated...\n"));
911
912#ifdef VBOX_FFMPEG
913 if (pFramebuffer)
914 {
915 pFramebuffer->Release ();
916 Log(("Released framebuffer\n"));
917 pFramebuffer = NULL;
918 }
919#endif /* defined(VBOX_FFMPEG) */
920
921 /* we don't have to disable VRDP here because we don't save the settings of the VM */
922
923 /*
924 * Close the session. This will also uninitialize the console and
925 * unregister the callback we've registered before.
926 */
927 Log (("VBoxHeadless: Closing the session...\n"));
928 session->Close();
929 }
930 while (0);
931
932 com::Shutdown();
933
934 LogFlow (("VBoxHeadless FINISHED.\n"));
935
936 return rc;
937}
938
939
940#ifndef VBOX_WITH_HARDENING
941/**
942 * Main entry point.
943 */
944int main (int argc, char **argv, char **envp)
945{
946 // initialize VBox Runtime
947 RTR3InitAndSUPLib();
948 return TrustedMain (argc, argv, envp);
949}
950#endif /* !VBOX_WITH_HARDENING */
951
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