VirtualBox

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

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

VBoxHeadless: -Wshadow warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.0 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 do
662 {
663 ComPtr<IVirtualBox> virtualBox;
664 ComPtr<ISession> session;
665
666 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
667 if (FAILED(rc))
668 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
669 else
670 {
671 rc = session.createInprocObject(CLSID_Session);
672 if (FAILED(rc))
673 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
674 }
675
676 if (FAILED(rc))
677 {
678 com::ErrorInfo info;
679 if (!info.isFullAvailable() && !info.isBasicAvailable())
680 {
681 com::GluePrintRCMessage(rc);
682 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
683 }
684 else
685 GluePrintErrorInfo(info);
686 break;
687 }
688
689 /* find ID by name */
690 if (!id)
691 {
692 ComPtr <IMachine> m;
693 rc = virtualBox->FindMachine (Bstr (name), m.asOutParam());
694 if (FAILED (rc))
695 {
696 LogError ("Invalid machine name!\n", rc);
697 break;
698 }
699 m->COMGETTER(Id) (id.asOutParam());
700 AssertComRC (rc);
701 if (FAILED (rc))
702 break;
703 }
704
705 Log (("VBoxHeadless: Opening a session with machine (id={%s})...\n",
706 Utf8Str(id).raw()));
707
708 // open a session
709 CHECK_ERROR_BREAK(virtualBox, OpenSession (session, id));
710
711 /* get the console */
712 ComPtr <IConsole> console;
713 CHECK_ERROR_BREAK(session, COMGETTER (Console) (console.asOutParam()));
714
715 /* get the machine */
716 ComPtr <IMachine> machine;
717 CHECK_ERROR_BREAK(console, COMGETTER(Machine) (machine.asOutParam()));
718
719 ComPtr <IDisplay> display;
720 CHECK_ERROR_BREAK(console, COMGETTER(Display) (display.asOutParam()));
721
722#ifdef VBOX_FFMPEG
723 IFramebuffer *pFramebuffer = 0;
724 RTLDRMOD hLdrFFmpegFB;
725 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
726
727 if (fFFMPEG)
728 {
729 int rrc = VINF_SUCCESS, rcc = S_OK;
730
731 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
732 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
733
734 if (RT_SUCCESS(rrc))
735 {
736 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
737 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
738 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
739 if (RT_FAILURE(rrc))
740 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
741 }
742 else
743 LogError("Failed to load the video capture extension\n", rrc);
744 if (RT_SUCCESS(rrc))
745 {
746 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
747 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
748 pszMPEGFile, &pFramebuffer);
749 if (rcc != S_OK)
750 LogError("Failed to initialise video capturing - make sure that the file format\n"
751 "you wish to use is supported on your system\n", rcc);
752 }
753 if (RT_SUCCESS(rrc) && (S_OK == rcc))
754 {
755 Log2(("VBoxHeadless: Registering framebuffer\n"));
756 pFramebuffer->AddRef();
757 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
758 }
759 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
760 rc = E_FAIL;
761 }
762 if (rc != S_OK)
763 {
764 return -1;
765 }
766#endif /* defined(VBOX_FFMPEG) */
767
768 ULONG cMonitors = 1;
769 machine->COMGETTER(MonitorCount)(&cMonitors);
770
771#ifdef VBOX_WITH_VRDP
772 unsigned uScreenId;
773 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
774 {
775#ifdef VBOX_FFMPEG
776 if (fFFMPEG && uScreenId == 0)
777 {
778 /* Already registered. */
779 continue;
780 }
781#endif /* defined(VBOX_FFMPEG) */
782 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer ();
783 if (!pVRDPFramebuffer)
784 {
785 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
786 return -1;
787 }
788 pVRDPFramebuffer->AddRef();
789 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
790 }
791#endif
792
793 /* get the machine debugger (isn't necessarily available) */
794 ComPtr <IMachineDebugger> machineDebugger;
795 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
796 if (machineDebugger)
797 {
798 Log(("Machine debugger available!\n"));
799 }
800
801 if (fRawR0 != ~0U)
802 {
803 if (!machineDebugger)
804 {
805 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
806 break;
807 }
808 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
809 }
810 if (fRawR3 != ~0U)
811 {
812 if (!machineDebugger)
813 {
814 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
815 break;
816 }
817 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
818 }
819 if (fPATM != ~0U)
820 {
821 if (!machineDebugger)
822 {
823 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
824 break;
825 }
826 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
827 }
828 if (fCSAM != ~0U)
829 {
830 if (!machineDebugger)
831 {
832 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
833 break;
834 }
835 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
836 }
837
838 /* initialize global references */
839 gSession = session;
840 gConsole = console;
841 gEventQ = com::EventQueue::getMainEventQueue();
842
843 /* register a callback for machine events */
844 {
845 ConsoleCallback *callback = new ConsoleCallback ();
846 callback->AddRef();
847 CHECK_ERROR(console, RegisterCallback (callback));
848 callback->Release();
849 if (FAILED (rc))
850 break;
851 }
852
853#ifdef VBOX_WITH_VRDP
854 /* default is to enable the RDP server (backward compatibility) */
855 BOOL fVRDPEnable = true;
856 BOOL fVRDPEnabled;
857 ComPtr <IVRDPServer> vrdpServer;
858 CHECK_ERROR_BREAK(machine, COMGETTER (VRDPServer) (vrdpServer.asOutParam()));
859 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled) (&fVRDPEnabled));
860
861 if (vrdpEnabled != NULL)
862 {
863 /* -vrdp on|off|config */
864 if (!strcmp(vrdpEnabled, "off") || !strcmp(vrdpEnabled, "disable"))
865 fVRDPEnable = false;
866 else if (!strcmp(vrdpEnabled, "config"))
867 {
868 if (!fVRDPEnabled)
869 fVRDPEnable = false;
870 }
871 else if (strcmp(vrdpEnabled, "on") && strcmp(vrdpEnabled, "enable"))
872 {
873 RTPrintf("-vrdp requires an argument (on|off|config)\n");
874 break;
875 }
876 }
877
878 if (fVRDPEnable)
879 {
880 Log (("VBoxHeadless: Enabling VRDP server...\n"));
881
882 /* set VRDP port if requested by the user */
883 if (vrdpPort != NULL)
884 {
885 Bstr bstr = vrdpPort;
886 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Ports)(bstr));
887 }
888 /* set VRDP address if requested by the user */
889 if (vrdpAddress != NULL)
890 {
891 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(NetAddress)(Bstr(vrdpAddress)));
892 }
893 /* enable VRDP server (only if currently disabled) */
894 if (!fVRDPEnabled)
895 {
896 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (TRUE));
897 }
898 }
899 else
900 {
901 /* disable VRDP server (only if currently enabled */
902 if (fVRDPEnabled)
903 {
904 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (FALSE));
905 }
906 }
907#endif
908 Log (("VBoxHeadless: Powering up the machine...\n"));
909
910 ComPtr <IProgress> progress;
911 CHECK_ERROR_BREAK(console, PowerUp (progress.asOutParam()));
912
913 /* wait for result because there can be errors */
914 if (SUCCEEDED(progress->WaitForCompletion (-1)))
915 {
916 LONG progressRc;
917 progress->COMGETTER(ResultCode)(&progressRc);
918 rc = progressRc;
919 if (FAILED(progressRc))
920 {
921 com::ProgressErrorInfo info(progress);
922 if (info.isBasicAvailable())
923 {
924 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
925 }
926 else
927 {
928 RTPrintf("Error: failed to start machine. No error message available!\n");
929 }
930 }
931 }
932
933#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
934 signal(SIGINT, SaveState);
935 signal(SIGTERM, SaveState);
936#endif
937
938 Log (("VBoxHeadless: Waiting for PowerDown...\n"));
939
940 Event *e;
941
942 while (gEventQ->waitForEvent (&e) && e)
943 gEventQ->handleEvent (e);
944
945 Log (("VBoxHeadless: event loop has terminated...\n"));
946
947#ifdef VBOX_FFMPEG
948 if (pFramebuffer)
949 {
950 pFramebuffer->Release ();
951 Log(("Released framebuffer\n"));
952 pFramebuffer = NULL;
953 }
954#endif /* defined(VBOX_FFMPEG) */
955
956 /* we don't have to disable VRDP here because we don't save the settings of the VM */
957
958 /*
959 * Close the session. This will also uninitialize the console and
960 * unregister the callback we've registered before.
961 */
962 Log (("VBoxHeadless: Closing the session...\n"));
963 session->Close();
964 }
965 while (0);
966
967 com::Shutdown();
968
969 LogFlow (("VBoxHeadless FINISHED.\n"));
970
971 return rc;
972}
973
974
975#ifndef VBOX_WITH_HARDENING
976/**
977 * Main entry point.
978 */
979int main (int argc, char **argv, char **envp)
980{
981 // initialize VBox Runtime
982 int rc = RTR3InitAndSUPLib();
983 if (RT_FAILURE(rc))
984 {
985 RTPrintf("VBoxHeadless: Runtime Error:\n"
986 " %Rrc -- %Rrf\n", rc, rc);
987 switch (rc)
988 {
989 case VERR_VM_DRIVER_NOT_INSTALLED:
990 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
991 "loaded successfully. Aborting ...\n");
992 break;
993 default:
994 break;
995 }
996 return 1;
997 }
998
999 return TrustedMain (argc, argv, envp);
1000}
1001#endif /* !VBOX_WITH_HARDENING */
1002
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use