VirtualBox

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

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

Main, Frontends: added support for virtual pointing devices with no relative reporting and cleaned up the VMMDev/mouse device absolute reporting interaction

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