VirtualBox

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

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

VBoxHeadless: added empty IVirtualBoxCallback.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use