VirtualBox

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

Last change on this file since 22810 was 22810, checked in by vboxsync, 15 years ago

FE/Qt, Devices/Input, Main, FE/*: upgrade mouse device to an MS IntelliMouse Explorer (five buttons and tilt wheel)

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