VirtualBox

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

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

Frontends/SDL+Headless: fix VirtualBox callback registration leak

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