VirtualBox

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

Last change on this file since 25275 was 25027, checked in by vboxsync, 14 years ago

VBoxHeadless: properly close session if the VM failed to start (xTracker 4495).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use