VirtualBox

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

Last change on this file since 19015 was 18740, checked in by vboxsync, 16 years ago

VBoxHeadless: be a bit more verbose on runtime errors

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