VirtualBox

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

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

Frontends/VBoxHeadless: error check typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.4 KB
Line 
1/* $Id: VBoxHeadless.cpp 33852 2010-11-08 14:54:02Z vboxsync $ */
2/** @file
3 * VBoxHeadless - The VirtualBox Headless frontend for running VMs on servers.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <VBox/com/com.h>
19#include <VBox/com/string.h>
20#include <VBox/com/array.h>
21#include <VBox/com/Guid.h>
22#include <VBox/com/ErrorInfo.h>
23#include <VBox/com/errorprint.h>
24#include <VBox/com/EventQueue.h>
25
26#include <VBox/com/VirtualBox.h>
27
28using namespace com;
29
30#define LOG_GROUP LOG_GROUP_GUI
31
32#include <VBox/log.h>
33#include <VBox/version.h>
34#include <iprt/buildconfig.h>
35#include <iprt/ctype.h>
36#include <iprt/initterm.h>
37#include <iprt/stream.h>
38#include <iprt/ldr.h>
39#include <iprt/getopt.h>
40#include <iprt/env.h>
41#include <VBox/err.h>
42#include <VBox/VBoxVideo.h>
43
44#ifdef VBOX_FFMPEG
45#include <cstdlib>
46#include <cerrno>
47#include "VBoxHeadless.h"
48#include <iprt/env.h>
49#include <iprt/param.h>
50#include <iprt/process.h>
51#include <VBox/sup.h>
52#endif
53
54//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
55#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
56#include <signal.h>
57#endif
58
59#include "Framebuffer.h"
60#ifdef VBOX_WITH_VNC
61# include "FramebufferVNC.h"
62#endif
63
64#include "NullFramebuffer.h"
65
66////////////////////////////////////////////////////////////////////////////////
67
68#define LogError(m,rc) \
69 do { \
70 Log(("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
71 RTPrintf("%s\n", m); \
72 } while (0)
73
74////////////////////////////////////////////////////////////////////////////////
75
76/* global weak references (for event handlers) */
77static ISession *gSession = NULL;
78static IConsole *gConsole = NULL;
79static EventQueue *gEventQ = NULL;
80
81/* flag whether frontend should terminate */
82static volatile bool g_fTerminateFE = false;
83
84#ifdef VBOX_WITH_VNC
85static VNCFB *g_pFramebufferVNC;
86#endif
87
88////////////////////////////////////////////////////////////////////////////////
89
90/**
91 * Handler for global events.
92 */
93class VirtualBoxEventListener :
94 VBOX_SCRIPTABLE_IMPL(IEventListener)
95{
96public:
97 VirtualBoxEventListener()
98 {
99#ifndef VBOX_WITH_XPCOM
100 refcnt = 0;
101#endif
102 mfNoLoggedInUsers = true;
103 }
104
105 virtual ~VirtualBoxEventListener()
106 {
107 }
108
109#ifndef VBOX_WITH_XPCOM
110 STDMETHOD_(ULONG, AddRef)()
111 {
112 return ::InterlockedIncrement(&refcnt);
113 }
114 STDMETHOD_(ULONG, Release)()
115 {
116 long cnt = ::InterlockedDecrement(&refcnt);
117 if (cnt == 0)
118 delete this;
119 return cnt;
120 }
121#endif
122 VBOX_SCRIPTABLE_DISPATCH_IMPL(IEventListener)
123
124 NS_DECL_ISUPPORTS
125
126 STDMETHOD(HandleEvent)(IEvent * aEvent)
127 {
128 VBoxEventType_T aType = VBoxEventType_Invalid;
129
130 aEvent->COMGETTER(Type)(&aType);
131 switch (aType)
132 {
133 case VBoxEventType_OnGuestPropertyChanged:
134 {
135 ComPtr<IGuestPropertyChangedEvent> gpcev = aEvent;
136 Assert(gpcev);
137
138 Bstr aKey;
139 gpcev->COMGETTER(Name)(aKey.asOutParam());
140
141 if (aKey == Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
142 {
143 /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
144 BOOL fProcessDisconnectOnGuestLogout = FALSE;
145 ComPtr <IMachine> machine;
146 HRESULT hrc = S_OK;
147
148 if (gConsole)
149 {
150 hrc = gConsole->COMGETTER(Machine)(machine.asOutParam());
151 if (SUCCEEDED(hrc) && machine)
152 {
153 Bstr id, machineId;
154 hrc = machine->COMGETTER(Id)(id.asOutParam());
155 gpcev->COMGETTER(MachineId)(machineId.asOutParam());
156 if (id == machineId)
157 {
158 Bstr value1;
159 hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
160 value1.asOutParam());
161 if (SUCCEEDED(hrc) && value1 == "1")
162 {
163 fProcessDisconnectOnGuestLogout = TRUE;
164 }
165 }
166 }
167 }
168
169 if (fProcessDisconnectOnGuestLogout)
170 {
171 Bstr value;
172 gpcev->COMGETTER(Value)(value.asOutParam());
173 Utf8Str utf8Value = value;
174 if (utf8Value == "true")
175 {
176 if (!mfNoLoggedInUsers) /* Only if the property really changes. */
177 {
178 mfNoLoggedInUsers = true;
179
180 /* If there is a connection, drop it. */
181 ComPtr<IVRDEServerInfo> info;
182 hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
183 if (SUCCEEDED(hrc) && info)
184 {
185 ULONG cClients = 0;
186 hrc = info->COMGETTER(NumberOfClients)(&cClients);
187 if (SUCCEEDED(hrc) && cClients > 0)
188 {
189 ComPtr <IVRDEServer> vrdeServer;
190 hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
191 if (SUCCEEDED(hrc) && vrdeServer)
192 {
193 vrdeServer->COMSETTER(Enabled)(FALSE);
194 vrdeServer->COMSETTER(Enabled)(TRUE);
195 }
196 }
197 }
198 }
199 }
200 else
201 {
202 mfNoLoggedInUsers = false;
203 }
204 }
205 }
206 break;
207 }
208 default:
209 AssertFailed();
210 }
211
212 return S_OK;
213 }
214
215private:
216#ifndef VBOX_WITH_XPCOM
217 long refcnt;
218#endif
219
220 bool mfNoLoggedInUsers;
221};
222
223
224/**
225 * Handler for machine events.
226 */
227class ConsoleEventListener :
228 VBOX_SCRIPTABLE_IMPL(IEventListener)
229{
230public:
231 ConsoleEventListener()
232 {
233#ifndef VBOX_WITH_XPCOM
234 refcnt = 0;
235#endif
236 mLastVRDEPort = -1;
237 }
238
239 virtual ~ConsoleEventListener()
240 {
241 }
242
243#ifndef VBOX_WITH_XPCOM
244 STDMETHOD_(ULONG, AddRef)()
245 {
246 return ::InterlockedIncrement(&refcnt);
247 }
248 STDMETHOD_(ULONG, Release)()
249 {
250 long cnt = ::InterlockedDecrement(&refcnt);
251 if (cnt == 0)
252 delete this;
253 return cnt;
254 }
255#endif
256 VBOX_SCRIPTABLE_DISPATCH_IMPL(IEventListener)
257
258 NS_DECL_ISUPPORTS
259
260 STDMETHOD(HandleEvent)(IEvent * aEvent)
261 {
262 VBoxEventType_T aType = VBoxEventType_Invalid;
263
264 aEvent->COMGETTER(Type)(&aType);
265 switch (aType)
266 {
267 case VBoxEventType_OnMouseCapabilityChanged:
268 {
269
270 ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
271 Assert(mccev);
272
273 BOOL fSupportsAbsolute = false;
274 mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute);
275
276 /* Emit absolute mouse event to actually enable the host mouse cursor. */
277 if (fSupportsAbsolute && gConsole)
278 {
279 ComPtr<IMouse> mouse;
280 gConsole->COMGETTER(Mouse)(mouse.asOutParam());
281 if (mouse)
282 {
283 mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
284 }
285 }
286#ifdef VBOX_WITH_VNC
287 if (g_pFramebufferVNC)
288 g_pFramebufferVNC->enableAbsMouse(fSupportsAbsolute);
289#endif
290 break;
291 }
292 case VBoxEventType_OnStateChanged:
293 {
294 ComPtr<IStateChangedEvent> scev = aEvent;
295 Assert(scev);
296
297 MachineState_T machineState;
298 scev->COMGETTER(State)(&machineState);
299
300 /* Terminate any event wait operation if the machine has been
301 * PoweredDown/Saved/Aborted. */
302 if (machineState < MachineState_Running)
303 {
304 g_fTerminateFE = true;
305 gEventQ->interruptEventQueueProcessing();
306 }
307
308 break;
309 }
310 case VBoxEventType_OnVRDEServerInfoChanged:
311 {
312 ComPtr<IVRDEServerInfoChangedEvent> rdicev = aEvent;
313 Assert(rdicev);
314
315 if (gConsole)
316 {
317 ComPtr<IVRDEServerInfo> info;
318 gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
319 if (info)
320 {
321 LONG port;
322 info->COMGETTER(Port)(&port);
323 if (port != mLastVRDEPort)
324 {
325 if (port == -1)
326 RTPrintf("VRDE server is inactive.\n");
327 else if (port == 0)
328 RTPrintf("VRDE server failed to start.\n");
329 else
330 RTPrintf("VRDE server is listening on port %d.\n", port);
331
332 mLastVRDEPort = port;
333 }
334 }
335 }
336 break;
337 }
338 case VBoxEventType_OnCanShowWindow:
339 {
340 ComPtr<ICanShowWindowEvent> cswev = aEvent;
341 Assert(cswev);
342 cswev->AddVeto(NULL);
343 break;
344 }
345 case VBoxEventType_OnShowWindow:
346 {
347 ComPtr<IShowWindowEvent> swev = aEvent;
348 Assert(swev);
349 swev->COMSETTER(WinId)(0);
350 break;
351 }
352 default:
353 AssertFailed();
354 }
355 return S_OK;
356 }
357
358private:
359
360#ifndef VBOX_WITH_XPCOM
361 long refcnt;
362#endif
363 long mLastVRDEPort;
364};
365
366#ifdef VBOX_WITH_XPCOM
367NS_DECL_CLASSINFO(VirtualBoxEventListener)
368NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VirtualBoxEventListener, IEventListener)
369NS_DECL_CLASSINFO(ConsoleEventListener)
370NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ConsoleEventListener, IEventListener)
371#endif
372
373#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
374static void SaveState(int sig)
375{
376 ComPtr <IProgress> progress = NULL;
377
378/** @todo Deal with nested signals, multithreaded signal dispatching (esp. on windows),
379 * and multiple signals (both SIGINT and SIGTERM in some order).
380 * Consider processing the signal request asynchronously since there are lots of things
381 * which aren't safe (like RTPrintf and printf IIRC) in a signal context. */
382
383 RTPrintf("Signal received, saving state.\n");
384
385 HRESULT rc = gConsole->SaveState(progress.asOutParam());
386 if (FAILED(rc))
387 {
388 RTPrintf("Error saving state! rc = 0x%x\n", rc);
389 return;
390 }
391 Assert(progress);
392 LONG cPercent = 0;
393
394 RTPrintf("0%%");
395 RTStrmFlush(g_pStdOut);
396 for (;;)
397 {
398 BOOL fCompleted = false;
399 rc = progress->COMGETTER(Completed)(&fCompleted);
400 if (FAILED(rc) || fCompleted)
401 break;
402 ULONG cPercentNow;
403 rc = progress->COMGETTER(Percent)(&cPercentNow);
404 if (FAILED(rc))
405 break;
406 if ((cPercentNow / 10) != (cPercent / 10))
407 {
408 cPercent = cPercentNow;
409 RTPrintf("...%d%%", cPercentNow);
410 RTStrmFlush(g_pStdOut);
411 }
412
413 /* wait */
414 rc = progress->WaitForCompletion(100);
415 }
416
417 HRESULT lrc;
418 rc = progress->COMGETTER(ResultCode)(&lrc);
419 if (FAILED(rc))
420 lrc = ~0;
421 if (!lrc)
422 {
423 RTPrintf(" -- Saved the state successfully.\n");
424 RTThreadYield();
425 }
426 else
427 RTPrintf("-- Error saving state, lrc=%d (%#x)\n", lrc, lrc);
428
429}
430#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
431
432////////////////////////////////////////////////////////////////////////////////
433
434static void show_usage()
435{
436 RTPrintf("Usage:\n"
437 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
438#ifdef VBOX_WITH_VNC
439 " -n, --vnc Enable the built in VNC server\n"
440 " -m, --vncport <port> TCP port number to use for the VNC server\n"
441 " -o, --vncpass <pw> Set the VNC server password\n"
442#endif
443 " -v, -vrde, --vrde on|off|config Enable (default) or disable the VRDE\n"
444 " server or don't change the setting\n"
445 " -e, -vrdeproperty, --vrdeproperty <name=[value]> Set a VRDE property:\n"
446 " \"TCP/Ports\" - comma-separated list of ports\n"
447 " the VRDE server can bind to. Use a dash between\n"
448 " two port numbers to specify a range\n"
449 " \"TCP/Address\" - interface IP the VRDE server\n"
450 " will bind to\n"
451#ifdef VBOX_FFMPEG
452 " -c, -capture, --capture Record the VM screen output to a file\n"
453 " -w, --width Frame width when recording\n"
454 " -h, --height Frame height when recording\n"
455 " -r, --bitrate Recording bit rate when recording\n"
456 " -f, --filename File name when recording. The codec\n"
457 " used will be chosen based on the\n"
458 " file extension\n"
459#endif
460 "\n");
461}
462
463#ifdef VBOX_FFMPEG
464/**
465 * Parse the environment for variables which can influence the FFMPEG settings.
466 * purely for backwards compatibility.
467 * @param pulFrameWidth may be updated with a desired frame width
468 * @param pulFrameHeight may be updated with a desired frame height
469 * @param pulBitRate may be updated with a desired bit rate
470 * @param ppszFileName may be updated with a desired file name
471 */
472static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
473 unsigned long *pulBitRate, const char **ppszFileName)
474{
475 const char *pszEnvTemp;
476
477 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
478 {
479 errno = 0;
480 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
481 if (errno != 0)
482 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
483 else
484 *pulFrameWidth = ulFrameWidth;
485 }
486 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
487 {
488 errno = 0;
489 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
490 if (errno != 0)
491 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
492 else
493 *pulFrameHeight = ulFrameHeight;
494 }
495 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
496 {
497 errno = 0;
498 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
499 if (errno != 0)
500 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
501 else
502 *pulBitRate = ulBitRate;
503 }
504 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
505 *ppszFileName = pszEnvTemp;
506}
507#endif /* VBOX_FFMPEG defined */
508
509/**
510 * Entry point.
511 */
512extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
513{
514 const char *vrdePort = NULL;
515 const char *vrdeAddress = NULL;
516 const char *vrdeEnabled = NULL;
517 unsigned cVRDEProperties = 0;
518 const char *aVRDEProperties[16];
519#ifdef VBOX_WITH_VNC
520 bool fVNCEnable = false;
521 unsigned uVNCPort = 0; /* default port */
522 char const *pszVNCPassword = NULL; /* no password */
523#endif
524 unsigned fRawR0 = ~0U;
525 unsigned fRawR3 = ~0U;
526 unsigned fPATM = ~0U;
527 unsigned fCSAM = ~0U;
528#ifdef VBOX_FFMPEG
529 unsigned fFFMPEG = 0;
530 unsigned long ulFrameWidth = 800;
531 unsigned long ulFrameHeight = 600;
532 unsigned long ulBitRate = 300000;
533 char pszMPEGFile[RTPATH_MAX];
534 const char *pszFileNameParam = "VBox-%d.vob";
535#endif /* VBOX_FFMPEG */
536
537 /* Make sure that DISPLAY is unset, so that X11 bits do not get initialised
538 * on X11-using OSes. */
539 /** @todo this should really be taken care of in Main. */
540 RTEnvUnset("DISPLAY");
541
542 LogFlow (("VBoxHeadless STARTED.\n"));
543 RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
544 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
545 "All rights reserved.\n\n");
546
547#ifdef VBOX_FFMPEG
548 /* Parse the environment */
549 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
550#endif
551
552 enum eHeadlessOptions
553 {
554 OPT_RAW_R0 = 0x100,
555 OPT_NO_RAW_R0,
556 OPT_RAW_R3,
557 OPT_NO_RAW_R3,
558 OPT_PATM,
559 OPT_NO_PATM,
560 OPT_CSAM,
561 OPT_NO_CSAM,
562 OPT_COMMENT
563 };
564
565 static const RTGETOPTDEF s_aOptions[] =
566 {
567 { "-startvm", 's', RTGETOPT_REQ_STRING },
568 { "--startvm", 's', RTGETOPT_REQ_STRING },
569 { "-vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
570 { "--vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
571 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
572 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
573 { "-vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
574 { "--vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
575 { "-vrde", 'v', RTGETOPT_REQ_STRING },
576 { "--vrde", 'v', RTGETOPT_REQ_STRING },
577 { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
578 { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
579#ifdef VBOX_WITH_VNC
580 { "--vncport", 'm', RTGETOPT_REQ_INT32 },
581 { "--vncpass", 'o', RTGETOPT_REQ_STRING },
582 { "--vnc", 'n', 0 },
583#endif /* VBOX_WITH_VNC */
584 { "-rawr0", OPT_RAW_R0, 0 },
585 { "--rawr0", OPT_RAW_R0, 0 },
586 { "-norawr0", OPT_NO_RAW_R0, 0 },
587 { "--norawr0", OPT_NO_RAW_R0, 0 },
588 { "-rawr3", OPT_RAW_R3, 0 },
589 { "--rawr3", OPT_RAW_R3, 0 },
590 { "-norawr3", OPT_NO_RAW_R3, 0 },
591 { "--norawr3", OPT_NO_RAW_R3, 0 },
592 { "-patm", OPT_PATM, 0 },
593 { "--patm", OPT_PATM, 0 },
594 { "-nopatm", OPT_NO_PATM, 0 },
595 { "--nopatm", OPT_NO_PATM, 0 },
596 { "-csam", OPT_CSAM, 0 },
597 { "--csam", OPT_CSAM, 0 },
598 { "-nocsam", OPT_NO_CSAM, 0 },
599 { "--nocsam", OPT_NO_CSAM, 0 },
600#ifdef VBOX_FFMPEG
601 { "-capture", 'c', 0 },
602 { "--capture", 'c', 0 },
603 { "--width", 'w', RTGETOPT_REQ_UINT32 },
604 { "--height", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
605 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
606 { "--filename", 'f', RTGETOPT_REQ_STRING },
607#endif /* VBOX_FFMPEG defined */
608 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
609 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
610 };
611
612 const char *pcszNameOrUUID = NULL;
613
614 // parse the command line
615 int ch;
616 RTGETOPTUNION ValueUnion;
617 RTGETOPTSTATE GetState;
618 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
619 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
620 {
621 switch(ch)
622 {
623 case 's':
624 pcszNameOrUUID = ValueUnion.psz;
625 break;
626 case 'p':
627 RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
628 vrdePort = ValueUnion.psz;
629 break;
630 case 'a':
631 RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
632 vrdeAddress = ValueUnion.psz;
633 break;
634 case 'v':
635 vrdeEnabled = ValueUnion.psz;
636 break;
637 case 'e':
638 if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
639 aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
640 else
641 RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
642 break;
643#ifdef VBOX_WITH_VNC
644 case 'n':
645 fVNCEnable = true;
646 break;
647 case 'm':
648 uVNCPort = ValueUnion.i32;
649 break;
650 case 'o':
651 pszVNCPassword = ValueUnion.psz;
652 break;
653#endif /* VBOX_WITH_VNC */
654 case OPT_RAW_R0:
655 fRawR0 = true;
656 break;
657 case OPT_NO_RAW_R0:
658 fRawR0 = false;
659 break;
660 case OPT_RAW_R3:
661 fRawR3 = true;
662 break;
663 case OPT_NO_RAW_R3:
664 fRawR3 = false;
665 break;
666 case OPT_PATM:
667 fPATM = true;
668 break;
669 case OPT_NO_PATM:
670 fPATM = false;
671 break;
672 case OPT_CSAM:
673 fCSAM = true;
674 break;
675 case OPT_NO_CSAM:
676 fCSAM = false;
677 break;
678#ifdef VBOX_FFMPEG
679 case 'c':
680 fFFMPEG = true;
681 break;
682 case 'w':
683 ulFrameWidth = ValueUnion.u32;
684 break;
685 case 'r':
686 ulBitRate = ValueUnion.u32;
687 break;
688 case 'f':
689 pszFileNameParam = ValueUnion.psz;
690 break;
691#endif /* VBOX_FFMPEG defined */
692 case 'h':
693#ifdef VBOX_FFMPEG
694 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
695 {
696 ulFrameHeight = ValueUnion.u32;
697 break;
698 }
699#endif
700 show_usage();
701 return 0;
702 case OPT_COMMENT:
703 /* nothing to do */
704 break;
705 case 'V':
706 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
707 return 0;
708 default:
709 ch = RTGetOptPrintError(ch, &ValueUnion);
710 show_usage();
711 return ch;
712 }
713 }
714
715#ifdef VBOX_FFMPEG
716 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
717 {
718 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
719 return 1;
720 }
721 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
722 {
723 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
724 return 1;
725 }
726 if (ulBitRate < 300000 || ulBitRate > 1000000)
727 {
728 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
729 return 1;
730 }
731 /* Make sure we only have %d or %u (or none) in the file name specified */
732 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
733 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
734 {
735 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
736 return 1;
737 }
738 /* And no more than one % in the name */
739 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
740 {
741 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
742 return 1;
743 }
744 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
745#endif /* defined VBOX_FFMPEG */
746
747 if (!pcszNameOrUUID)
748 {
749 show_usage();
750 return 1;
751 }
752
753 HRESULT rc;
754
755 rc = com::Initialize();
756 if (FAILED(rc))
757 {
758 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
759 return 1;
760 }
761
762 ComPtr<IVirtualBox> virtualBox;
763 ComPtr<ISession> session;
764 bool fSessionOpened = false;
765 IEventListener *vboxListener = NULL, *consoleListener = NULL;
766
767 do
768 {
769 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
770 if (FAILED(rc))
771 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
772 else
773 {
774 rc = session.createInprocObject(CLSID_Session);
775 if (FAILED(rc))
776 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
777 }
778
779 if (FAILED(rc))
780 {
781 com::ErrorInfo info;
782 if (!info.isFullAvailable() && !info.isBasicAvailable())
783 {
784 com::GluePrintRCMessage(rc);
785 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
786 }
787 else
788 GluePrintErrorInfo(info);
789 break;
790 }
791
792 ComPtr<IMachine> m;
793
794 rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
795 if (FAILED(rc))
796 {
797 LogError("Invalid machine name or UUID!\n", rc);
798 break;
799 }
800 Bstr id;
801 m->COMGETTER(Id)(id.asOutParam());
802 AssertComRC(rc);
803 if (FAILED(rc))
804 break;
805
806 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
807 Utf8Str(id).c_str()));
808
809 // open a session
810 CHECK_ERROR_BREAK(m, LockMachine(session, LockType_Write));
811 fSessionOpened = true;
812
813 /* get the console */
814 ComPtr<IConsole> console;
815 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
816
817 /* get the mutable machine */
818 ComPtr<IMachine> machine;
819 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
820
821 ComPtr<IDisplay> display;
822 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
823
824#ifdef VBOX_FFMPEG
825 IFramebuffer *pFramebuffer = 0;
826 RTLDRMOD hLdrFFmpegFB;
827 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
828
829 if (fFFMPEG)
830 {
831 int rrc = VINF_SUCCESS, rcc = S_OK;
832
833 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
834 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
835
836 if (RT_SUCCESS(rrc))
837 {
838 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
839 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
840 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
841 if (RT_FAILURE(rrc))
842 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
843 }
844 else
845 LogError("Failed to load the video capture extension\n", rrc);
846 if (RT_SUCCESS(rrc))
847 {
848 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
849 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
850 pszMPEGFile, &pFramebuffer);
851 if (rcc != S_OK)
852 LogError("Failed to initialise video capturing - make sure that the file format\n"
853 "you wish to use is supported on your system\n", rcc);
854 }
855 if (RT_SUCCESS(rrc) && (rcc == S_OK))
856 {
857 Log2(("VBoxHeadless: Registering framebuffer\n"));
858 pFramebuffer->AddRef();
859 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
860 }
861 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
862 rc = E_FAIL;
863 }
864 if (rc != S_OK)
865 {
866 break;
867 }
868#endif /* defined(VBOX_FFMPEG) */
869#ifdef VBOX_WITH_VNC
870 if (fVNCEnable)
871 {
872 Bstr machineName;
873 machine->COMGETTER(Name)(machineName.asOutParam());
874 g_pFramebufferVNC = new VNCFB(console, uVNCPort, pszVNCPassword);
875 rc = g_pFramebufferVNC->init(machineName.raw() ? Utf8Str(machineName.raw()).c_str() : "");
876 if (rc != S_OK)
877 {
878 LogError("Failed to load the vnc server extension, possibly due to a damaged file\n", rc);
879 delete g_pFramebufferVNC;
880 break;
881 }
882
883 Log2(("VBoxHeadless: Registering VNC framebuffer\n"));
884 g_pFramebufferVNC->AddRef();
885 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, g_pFramebufferVNC);
886 }
887 if (rc != S_OK)
888 break;
889#endif
890 ULONG cMonitors = 1;
891 machine->COMGETTER(MonitorCount)(&cMonitors);
892
893 unsigned uScreenId;
894 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
895 {
896# ifdef VBOX_FFMPEG
897 if (fFFMPEG && uScreenId == 0)
898 {
899 /* Already registered. */
900 continue;
901 }
902# endif
903# ifdef VBOX_WITH_VNC
904 if (fVNCEnable && uScreenId == 0)
905 {
906 /* Already registered. */
907 continue;
908 }
909# endif
910 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer();
911 if (!pVRDPFramebuffer)
912 {
913 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
914 break;
915 }
916 pVRDPFramebuffer->AddRef();
917 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
918 }
919 if (uScreenId < cMonitors)
920 {
921 break;
922 }
923
924 // fill in remaining slots with null framebuffers
925 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
926 {
927 ComPtr<IFramebuffer> fb;
928 LONG xOrigin, yOrigin;
929 HRESULT hrc2 = display->GetFramebuffer(uScreenId,
930 fb.asOutParam(),
931 &xOrigin, &yOrigin);
932 if (hrc2 == S_OK && fb.isNull())
933 {
934 NullFB *pNullFB = new NullFB();
935 pNullFB->AddRef();
936 pNullFB->init();
937 display->SetFramebuffer(uScreenId, pNullFB);
938 }
939 }
940
941 /* get the machine debugger (isn't necessarily available) */
942 ComPtr <IMachineDebugger> machineDebugger;
943 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
944 if (machineDebugger)
945 {
946 Log(("Machine debugger available!\n"));
947 }
948
949 if (fRawR0 != ~0U)
950 {
951 if (!machineDebugger)
952 {
953 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
954 break;
955 }
956 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
957 }
958 if (fRawR3 != ~0U)
959 {
960 if (!machineDebugger)
961 {
962 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
963 break;
964 }
965 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
966 }
967 if (fPATM != ~0U)
968 {
969 if (!machineDebugger)
970 {
971 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
972 break;
973 }
974 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
975 }
976 if (fCSAM != ~0U)
977 {
978 if (!machineDebugger)
979 {
980 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
981 break;
982 }
983 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
984 }
985
986 /* initialize global references */
987 gSession = session;
988 gConsole = console;
989 gEventQ = com::EventQueue::getMainEventQueue();
990
991 /* Console events registration. */
992 {
993 ComPtr<IEventSource> es;
994 CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
995 consoleListener = new ConsoleEventListener();
996 consoleListener->AddRef();
997 com::SafeArray <VBoxEventType_T> eventTypes;
998 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
999 eventTypes.push_back(VBoxEventType_OnStateChanged);
1000 eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
1001 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1002 eventTypes.push_back(VBoxEventType_OnShowWindow);
1003 CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
1004 }
1005
1006 /* default is to enable the remote desktop server (backward compatibility) */
1007 BOOL fVRDEEnable = true;
1008 BOOL fVRDEEnabled;
1009 ComPtr <IVRDEServer> vrdeServer;
1010 CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
1011 CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));
1012
1013 if (vrdeEnabled != NULL)
1014 {
1015 /* -vrdeServer on|off|config */
1016 if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
1017 fVRDEEnable = false;
1018 else if (!strcmp(vrdeEnabled, "config"))
1019 {
1020 if (!fVRDEEnabled)
1021 fVRDEEnable = false;
1022 }
1023 else if (strcmp(vrdeEnabled, "on") && strcmp(vrdeEnabled, "enable"))
1024 {
1025 RTPrintf("-vrdeServer requires an argument (on|off|config)\n");
1026 break;
1027 }
1028 }
1029
1030 if (fVRDEEnable)
1031 {
1032 Log(("VBoxHeadless: Enabling VRDE server...\n"));
1033
1034 /* set VRDE port if requested by the user */
1035 if (vrdePort != NULL)
1036 {
1037 Bstr bstr = vrdePort;
1038 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
1039 }
1040 /* set VRDE address if requested by the user */
1041 if (vrdeAddress != NULL)
1042 {
1043 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
1044 }
1045
1046 /* Set VRDE properties. */
1047 if (cVRDEProperties > 0)
1048 {
1049 for (unsigned i = 0; i < cVRDEProperties; i++)
1050 {
1051 /* Parse 'name=value' */
1052 char *pszProperty = RTStrDup(aVRDEProperties[i]);
1053 if (pszProperty)
1054 {
1055 char *pDelimiter = strchr(pszProperty, '=');
1056 if (pDelimiter)
1057 {
1058 *pDelimiter = '\0';
1059
1060 Bstr bstrName = pszProperty;
1061 Bstr bstrValue = &pDelimiter[1];
1062 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1063 }
1064 else
1065 {
1066 RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
1067 RTStrFree(pszProperty);
1068 rc = E_INVALIDARG;
1069 break;
1070 }
1071 RTStrFree(pszProperty);
1072 }
1073 else
1074 {
1075 RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
1076 rc = E_OUTOFMEMORY;
1077 break;
1078 }
1079 }
1080 if (FAILED(rc))
1081 break;
1082 }
1083
1084 /* enable VRDE server (only if currently disabled) */
1085 if (!fVRDEEnabled)
1086 {
1087 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
1088 }
1089 }
1090 else
1091 {
1092 /* disable VRDE server (only if currently enabled */
1093 if (fVRDEEnabled)
1094 {
1095 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
1096 }
1097 }
1098
1099 Log(("VBoxHeadless: Powering up the machine...\n"));
1100
1101 ComPtr <IProgress> progress;
1102 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1103
1104 /*
1105 * Wait for the result because there can be errors.
1106 *
1107 * It's vital to process events while waiting (teleportation deadlocks),
1108 * so we'll poll for the completion instead of waiting on it.
1109 */
1110 for (;;)
1111 {
1112 BOOL fCompleted;
1113 rc = progress->COMGETTER(Completed)(&fCompleted);
1114 if (FAILED(rc) || fCompleted)
1115 break;
1116
1117 /* Process pending events, then wait for new ones. Note, this
1118 * processes NULL events signalling event loop termination. */
1119 gEventQ->processEventQueue(0);
1120 if (!g_fTerminateFE)
1121 gEventQ->processEventQueue(500);
1122 }
1123
1124 if (SUCCEEDED(progress->WaitForCompletion(-1)))
1125 {
1126 /* Figure out if the operation completed with a failed status
1127 * and print the error message. Terminate immediately, and let
1128 * the cleanup code take care of potentially pending events. */
1129 LONG progressRc;
1130 progress->COMGETTER(ResultCode)(&progressRc);
1131 rc = progressRc;
1132 if (FAILED(rc))
1133 {
1134 com::ProgressErrorInfo info(progress);
1135 if (info.isBasicAvailable())
1136 {
1137 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
1138 }
1139 else
1140 {
1141 RTPrintf("Error: failed to start machine. No error message available!\n");
1142 }
1143 break;
1144 }
1145 }
1146
1147 /* VirtualBox events registration. */
1148 {
1149 ComPtr<IEventSource> es;
1150 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1151 vboxListener = new VirtualBoxEventListener();
1152 vboxListener->AddRef();
1153 com::SafeArray <VBoxEventType_T> eventTypes;
1154 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
1155 CHECK_ERROR(es, RegisterListener(vboxListener, ComSafeArrayAsInParam(eventTypes), true));
1156 }
1157
1158#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1159 signal(SIGINT, SaveState);
1160 signal(SIGTERM, SaveState);
1161#endif
1162
1163 Log(("VBoxHeadless: Waiting for PowerDown...\n"));
1164
1165 while ( !g_fTerminateFE
1166 && RT_SUCCESS(gEventQ->processEventQueue(RT_INDEFINITE_WAIT)))
1167 /* nothing */ ;
1168
1169 Log(("VBoxHeadless: event loop has terminated...\n"));
1170
1171#ifdef VBOX_FFMPEG
1172 if (pFramebuffer)
1173 {
1174 pFramebuffer->Release();
1175 Log(("Released framebuffer\n"));
1176 pFramebuffer = NULL;
1177 }
1178#endif /* defined(VBOX_FFMPEG) */
1179
1180 /* we don't have to disable VRDE here because we don't save the settings of the VM */
1181 }
1182 while (0);
1183
1184 /* VirtualBox callback unregistration. */
1185 if (vboxListener)
1186 {
1187 ComPtr<IEventSource> es;
1188 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1189 CHECK_ERROR(es, UnregisterListener(vboxListener));
1190 vboxListener->Release();
1191 }
1192
1193 /* Console callback unregistration. */
1194 if (consoleListener)
1195 {
1196 ComPtr<IEventSource> es;
1197 CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
1198 CHECK_ERROR(es, UnregisterListener(consoleListener));
1199 consoleListener->Release();
1200 }
1201
1202 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1203 gConsole = NULL;
1204
1205 if (fSessionOpened)
1206 {
1207 /*
1208 * Close the session. This will also uninitialize the console and
1209 * unregister the callback we've registered before.
1210 */
1211 Log(("VBoxHeadless: Closing the session...\n"));
1212 session->UnlockMachine();
1213 }
1214
1215 /* Must be before com::Shutdown */
1216 session.setNull();
1217 virtualBox.setNull();
1218
1219 com::Shutdown();
1220
1221 LogFlow(("VBoxHeadless FINISHED.\n"));
1222
1223 return FAILED(rc) ? 1 : 0;
1224}
1225
1226
1227#ifndef VBOX_WITH_HARDENING
1228/**
1229 * Main entry point.
1230 */
1231int main(int argc, char **argv, char **envp)
1232{
1233 // initialize VBox Runtime
1234 int rc = RTR3InitAndSUPLib();
1235 if (RT_FAILURE(rc))
1236 {
1237 RTPrintf("VBoxHeadless: Runtime Error:\n"
1238 " %Rrc -- %Rrf\n", rc, rc);
1239 switch (rc)
1240 {
1241 case VERR_VM_DRIVER_NOT_INSTALLED:
1242 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1243 "loaded successfully. Aborting ...\n");
1244 break;
1245 default:
1246 break;
1247 }
1248 return 1;
1249 }
1250
1251 return TrustedMain(argc, argv, envp);
1252}
1253#endif /* !VBOX_WITH_HARDENING */
1254
1255#ifdef VBOX_WITH_XPCOM
1256NS_DECL_CLASSINFO(NullFB)
1257NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NullFB, IFramebuffer)
1258#endif
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