VirtualBox

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

Last change on this file since 25275 was 25027, checked in by vboxsync, 15 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
RevLine 
[5834]1/** @file
2 *
3 * VBox frontends: VBoxHeadless (headless frontend):
4 * Headless server executable
5 */
6
7/*
[20928]8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
[5834]9 *
[6000]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.
[8155]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.
[5834]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>
[20928]27#include <VBox/com/errorprint.h>
[5834]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
[18626]41#include <iprt/ctype.h>
[14831]42#include <iprt/initterm.h>
[5834]43#include <iprt/stream.h>
44#include <iprt/ldr.h>
[7263]45#include <iprt/getopt.h>
[11102]46#include <iprt/env.h>
[18224]47#include <VBox/err.h>
[19817]48#include <VBox/VBoxVideo.h>
[5834]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>
[12423]57#include <VBox/sup.h>
[5834]58#endif
59
[7280]60//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
61#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
62#include <signal.h>
63#endif
64
[5842]65#ifdef VBOX_WITH_VRDP
66# include "Framebuffer.h"
67#endif
[5834]68
69////////////////////////////////////////////////////////////////////////////////
70
71#define LogError(m,rc) \
[7263]72 do { \
[5834]73 Log (("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
[18626]74 RTPrintf ("%s\n", m); \
[7263]75 } while (0)
[5834]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 */
[19134]109class ConsoleCallback : VBOX_SCRIPTABLE_IMPL(IConsoleCallback)
[5834]110{
111public:
112
113 ConsoleCallback ()
114 {
115#ifndef VBOX_WITH_XPCOM
116 refcnt = 0;
117#endif
[23998]118 mLastVRDPPort = -1;
[5834]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
[21520]138 VBOX_SCRIPTABLE_DISPATCH_IMPL(IConsoleCallback)
[5834]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 {
[22810]155 mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
[5834]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
[23643]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);
[23998]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 }
[23643]224 }
225 }
226#endif
227 return S_OK;
228 }
229
[5834]230 STDMETHOD(OnUSBControllerChange)()
231 {
232 return S_OK;
233 }
234
[17669]235 STDMETHOD(OnStorageControllerChange)()
236 {
237 return S_OK;
238 }
239
[23223]240 STDMETHOD(OnMediumChange)(IMediumAttachment * /* aMediumAttachment */)
241 {
242 return S_OK;
243 }
244
[5834]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
[15051]256 STDMETHOD(OnRuntimeError)(BOOL fatal, IN_BSTR id, IN_BSTR message)
[5834]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
[23998]286 long mLastVRDPPort;
[5834]287};
288
289#ifdef VBOX_WITH_XPCOM
290NS_DECL_CLASSINFO (ConsoleCallback)
291NS_IMPL_THREADSAFE_ISUPPORTS1_CI (ConsoleCallback, IConsoleCallback)
292#endif
293
[7280]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;
[18274]323 ULONG cPercentNow;
[7280]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
[5834]353////////////////////////////////////////////////////////////////////////////////
354
355static void show_usage()
356{
357 RTPrintf("Usage:\n"
[7263]358 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
[5834]359#ifdef VBOX_WITH_VRDP
[11442]360 " -v, -vrdp, --vrdp on|off|config Enable (default) or disable the VRDP\n"
361 " server or don't change the setting\n"
[23998]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"
[7263]365 " -a, -vrdpaddress, --vrdpaddress <ip> Interface IP the VRDP will bind to \n"
[5834]366#endif
[5892]367#ifdef VBOX_FFMPEG
[7263]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"
[5892]375#endif
[5834]376 "\n");
377}
378
[7263]379#ifdef VBOX_FFMPEG
[5834]380/**
[7263]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/**
[5834]426 * Entry point.
427 */
[11725]428extern "C" DECLEXPORT (int) TrustedMain (int argc, char **argv, char **envp)
[5834]429{
430#ifdef VBOX_WITH_VRDP
[23643]431 const char *vrdpPort = NULL;
[5834]432 const char *vrdpAddress = NULL;
[11442]433 const char *vrdpEnabled = NULL;
[5834]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;
[7263]441 unsigned long ulFrameWidth = 800;
442 unsigned long ulFrameHeight = 600;
443 unsigned long ulBitRate = 300000;
[5834]444 char pszMPEGFile[RTPATH_MAX];
[7263]445 const char *pszFileNameParam = "VBox-%d.vob";
[5834]446#endif /* VBOX_FFMPEG */
447
[11102]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");
[5834]452
453 LogFlow (("VBoxHeadless STARTED.\n"));
454 RTPrintf ("VirtualBox Headless Interface %s\n"
[15906]455 "(C) 2008-2009 Sun Microsystems, Inc.\n"
[16152]456 "All rights reserved.\n\n",
[5834]457 VBOX_VERSION_STRING);
458
[19239]459 Bstr id;
[5834]460 /* the below cannot be Bstr because on Linux Bstr doesn't work until XPCOM (nsMemory) is initialized */
461 const char *name = NULL;
462
[7263]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,
[11442]478 OPT_COMMENT,
[7263]479 };
480
[17091]481 static const RTGETOPTDEF s_aOptions[] =
[7263]482 {
483 { "-startvm", 's', RTGETOPT_REQ_STRING },
484 { "--startvm", 's', RTGETOPT_REQ_STRING },
485#ifdef VBOX_WITH_VRDP
[24981]486 { "-vrdpport", 'p', RTGETOPT_REQ_STRING },
487 { "--vrdpport", 'p', RTGETOPT_REQ_STRING },
[10174]488 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING },
489 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING },
[11442]490 { "-vrdp", 'v', RTGETOPT_REQ_STRING },
491 { "--vrdp", 'v', RTGETOPT_REQ_STRING },
[7263]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
[5834]521 // parse the command line
[7263]522 int ch;
[17091]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)))
[5834]527 {
[7263]528 switch(ch)
529 {
530 case 's':
[19239]531 id = asGuidStr(ValueUnion.psz);
[7263]532 /* If the argument was not a UUID, then it must be a name. */
533 if (!id)
534 name = ValueUnion.psz;
535 break;
[5834]536#ifdef VBOX_WITH_VRDP
[7263]537 case 'p':
[23643]538 vrdpPort = ValueUnion.psz;
[7263]539 break;
540 case 'a':
541 vrdpAddress = ValueUnion.psz;
542 break;
[11442]543 case 'v':
544 vrdpEnabled = ValueUnion.psz;
545 break;
[7263]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;
[5834]571#ifdef VBOX_FFMPEG
[7263]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 */
[17091]588 case VINF_GETOPT_NOT_OPTION:
[18626]589 RTPrintf("Invalid parameter '%s'\n\n", ValueUnion.psz);
590 show_usage();
591 return -1;
[18628]592 case OPT_COMMENT:
593 /* nothing to do */
594 break;
[18626]595 default:
596 if (ch > 0)
597 {
598 if (RT_C_IS_PRINT(ch))
599 RTPrintf("Invalid option -%c\n\n", ch);
600 else
[18628]601 RTPrintf("Invalid option case %i\n\n", ch);
[18626]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
[18628]608 RTPrintf("Error: %Rrs\n\n", ch);
[18626]609 show_usage();
610 return -1;
[5834]611 }
612 }
613
614#ifdef VBOX_FFMPEG
[7263]615 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
[5834]616 {
[7263]617 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
618 return -1;
[5834]619 }
[7263]620 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
[5834]621 {
[7263]622 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
623 return -1;
[5834]624 }
[7263]625 if (ulBitRate < 300000 || ulBitRate > 1000000)
[5834]626 {
[7263]627 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
628 return -1;
[5834]629 }
[7263]630 /* Make sure we only have %d or %u (or none) in the file name specified */
631 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
[5834]632 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
633 {
[7263]634 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
[5834]635 return -1;
636 }
637 /* And no more than one % in the name */
638 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
639 {
[7263]640 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
[5834]641 return -1;
642 }
[7263]643 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
[5834]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();
[16530]655 if (FAILED(rc))
656 {
[18224]657 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
[5834]658 return rc;
[16530]659 }
[5834]660
[25027]661 ComPtr<IVirtualBox> virtualBox;
662 ComPtr<ISession> session;
663 bool fSessionOpened = false;
664
[5834]665 do
666 {
[16530]667 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
668 if (FAILED(rc))
[18224]669 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
[16530]670 else
[5834]671 {
[16530]672 rc = session.createInprocObject(CLSID_Session);
673 if (FAILED(rc))
[18224]674 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
[16530]675 }
676
677 if (FAILED(rc))
678 {
[5834]679 com::ErrorInfo info;
[16530]680 if (!info.isFullAvailable() && !info.isBasicAvailable())
[5834]681 {
[16530]682 com::GluePrintRCMessage(rc);
683 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
[5834]684 }
685 else
[16530]686 GluePrintErrorInfo(info);
[5834]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",
[19239]707 Utf8Str(id).raw()));
[5834]708
709 // open a session
710 CHECK_ERROR_BREAK(virtualBox, OpenSession (session, id));
[25027]711 fSessionOpened = true;
[5834]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 {
[7305]731 int rrc = VINF_SUCCESS, rcc = S_OK;
732
[5834]733 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
[13637]734 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
[5834]735
[7305]736 if (RT_SUCCESS(rrc))
[5834]737 {
738 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
[7305]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);
[5834]743 }
[7305]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();
[19817]759 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
[7305]760 }
761 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
762 rc = E_FAIL;
[5834]763 }
764 if (rc != S_OK)
765 {
[25027]766 break;
[5834]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) */
[25016]784 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer ();
785 if (!pVRDPFramebuffer)
[5834]786 {
787 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
[25027]788 break;
[5834]789 }
[25016]790 pVRDPFramebuffer->AddRef();
791 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
[5834]792 }
[25027]793 if (uScreenId < cMonitors)
794 {
795 break;
796 }
[5834]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;
[22911]847 gEventQ = com::EventQueue::getMainEventQueue();
[5834]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
[11442]860 /* default is to enable the RDP server (backward compatibility) */
861 BOOL fVRDPEnable = true;
862 BOOL fVRDPEnabled;
[5834]863 ComPtr <IVRDPServer> vrdpServer;
864 CHECK_ERROR_BREAK(machine, COMGETTER (VRDPServer) (vrdpServer.asOutParam()));
[11442]865 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled) (&fVRDPEnabled));
[5834]866
[11442]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 }
[5834]883
[11442]884 if (fVRDPEnable)
[5834]885 {
[11442]886 Log (("VBoxHeadless: Enabling VRDP server...\n"));
887
888 /* set VRDP port if requested by the user */
[23643]889 if (vrdpPort != NULL)
890 {
891 Bstr bstr = vrdpPort;
892 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Ports)(bstr));
893 }
[11442]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 }
[5834]904 }
[11442]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 }
[5834]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 {
[20225]922 LONG progressRc;
923 progress->COMGETTER(ResultCode)(&progressRc);
924 rc = progressRc;
925 if (FAILED(progressRc))
[5834]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 }
[7280]938
939#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
940 signal(SIGINT, SaveState);
941 signal(SIGTERM, SaveState);
942#endif
943
[5834]944 Log (("VBoxHeadless: Waiting for PowerDown...\n"));
945
946 Event *e;
[23223]947
[22911]948 while (gEventQ->waitForEvent (&e) && e)
949 gEventQ->handleEvent (e);
[5834]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 */
[25027]963 }
964 while (0);
[5834]965
[25027]966 if (fSessionOpened)
967 {
[5834]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
[25027]976 /* Must be before com::Shutdown */
977 session.setNull();
978 virtualBox.setNull();
979
[5834]980 com::Shutdown();
981
982 LogFlow (("VBoxHeadless FINISHED.\n"));
983
984 return rc;
985}
986
[11725]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
[18224]995 int rc = RTR3InitAndSUPLib();
996 if (RT_FAILURE(rc))
997 {
[18740]998 RTPrintf("VBoxHeadless: Runtime Error:\n"
999 " %Rrc -- %Rrf\n", rc, rc);
[18224]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
[11725]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