VirtualBox

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

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

back out Main/Session+Console hack introducing full/reduced console

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 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-2010 " 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
835 do
836 {
837 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
838 if (FAILED(rc))
839 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
840 else
841 {
842 rc = session.createInprocObject(CLSID_Session);
843 if (FAILED(rc))
844 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
845 }
846
847 if (FAILED(rc))
848 {
849 com::ErrorInfo info;
850 if (!info.isFullAvailable() && !info.isBasicAvailable())
851 {
852 com::GluePrintRCMessage(rc);
853 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
854 }
855 else
856 GluePrintErrorInfo(info);
857 break;
858 }
859
860 /* find ID by name */
861 if (!id)
862 {
863 ComPtr <IMachine> m;
864 rc = virtualBox->FindMachine (Bstr (name), m.asOutParam());
865 if (FAILED (rc))
866 {
867 LogError ("Invalid machine name!\n", rc);
868 break;
869 }
870 m->COMGETTER(Id) (id.asOutParam());
871 AssertComRC (rc);
872 if (FAILED (rc))
873 break;
874 }
875
876 Log (("VBoxHeadless: Opening a session with machine (id={%s})...\n",
877 Utf8Str(id).raw()));
878
879 // open a session
880 CHECK_ERROR_BREAK(virtualBox, OpenSession (session, id));
881 fSessionOpened = true;
882
883 /* get the console */
884 ComPtr <IConsole> console;
885 CHECK_ERROR_BREAK(session, COMGETTER (Console) (console.asOutParam()));
886
887 /* get the machine */
888 ComPtr <IMachine> machine;
889 CHECK_ERROR_BREAK(console, COMGETTER(Machine) (machine.asOutParam()));
890
891 ComPtr <IDisplay> display;
892 CHECK_ERROR_BREAK(console, COMGETTER(Display) (display.asOutParam()));
893
894#ifdef VBOX_FFMPEG
895 IFramebuffer *pFramebuffer = 0;
896 RTLDRMOD hLdrFFmpegFB;
897 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
898
899 if (fFFMPEG)
900 {
901 int rrc = VINF_SUCCESS, rcc = S_OK;
902
903 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
904 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
905
906 if (RT_SUCCESS(rrc))
907 {
908 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
909 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
910 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
911 if (RT_FAILURE(rrc))
912 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
913 }
914 else
915 LogError("Failed to load the video capture extension\n", rrc);
916 if (RT_SUCCESS(rrc))
917 {
918 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
919 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
920 pszMPEGFile, &pFramebuffer);
921 if (rcc != S_OK)
922 LogError("Failed to initialise video capturing - make sure that the file format\n"
923 "you wish to use is supported on your system\n", rcc);
924 }
925 if (RT_SUCCESS(rrc) && (S_OK == rcc))
926 {
927 Log2(("VBoxHeadless: Registering framebuffer\n"));
928 pFramebuffer->AddRef();
929 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
930 }
931 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
932 rc = E_FAIL;
933 }
934 if (rc != S_OK)
935 {
936 break;
937 }
938#endif /* defined(VBOX_FFMPEG) */
939
940 ULONG cMonitors = 1;
941 machine->COMGETTER(MonitorCount)(&cMonitors);
942
943#ifdef VBOX_WITH_VRDP
944 unsigned uScreenId;
945 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
946 {
947#ifdef VBOX_FFMPEG
948 if (fFFMPEG && uScreenId == 0)
949 {
950 /* Already registered. */
951 continue;
952 }
953#endif /* defined(VBOX_FFMPEG) */
954 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer ();
955 if (!pVRDPFramebuffer)
956 {
957 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
958 break;
959 }
960 pVRDPFramebuffer->AddRef();
961 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
962 }
963 if (uScreenId < cMonitors)
964 {
965 break;
966 }
967#endif
968
969 /* get the machine debugger (isn't necessarily available) */
970 ComPtr <IMachineDebugger> machineDebugger;
971 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
972 if (machineDebugger)
973 {
974 Log(("Machine debugger available!\n"));
975 }
976
977 if (fRawR0 != ~0U)
978 {
979 if (!machineDebugger)
980 {
981 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
982 break;
983 }
984 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
985 }
986 if (fRawR3 != ~0U)
987 {
988 if (!machineDebugger)
989 {
990 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
991 break;
992 }
993 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
994 }
995 if (fPATM != ~0U)
996 {
997 if (!machineDebugger)
998 {
999 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
1000 break;
1001 }
1002 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1003 }
1004 if (fCSAM != ~0U)
1005 {
1006 if (!machineDebugger)
1007 {
1008 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
1009 break;
1010 }
1011 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1012 }
1013
1014 /* initialize global references */
1015 gSession = session;
1016 gConsole = console;
1017 gEventQ = com::EventQueue::getMainEventQueue();
1018
1019 /* VirtualBox callback registration. */
1020 VirtualBoxCallback *vboxCallback = new VirtualBoxCallback();
1021 vboxCallback->AddRef();
1022 CHECK_ERROR(virtualBox, RegisterCallback(vboxCallback));
1023 vboxCallback->Release();
1024 if (FAILED (rc))
1025 break;
1026
1027 /* register a callback for machine events */
1028 {
1029 ConsoleCallback *callback = new ConsoleCallback ();
1030 callback->AddRef();
1031 CHECK_ERROR(console, RegisterCallback (callback));
1032 callback->Release();
1033 if (FAILED (rc))
1034 break;
1035 }
1036
1037#ifdef VBOX_WITH_VRDP
1038 /* default is to enable the RDP server (backward compatibility) */
1039 BOOL fVRDPEnable = true;
1040 BOOL fVRDPEnabled;
1041 ComPtr <IVRDPServer> vrdpServer;
1042 CHECK_ERROR_BREAK(machine, COMGETTER (VRDPServer) (vrdpServer.asOutParam()));
1043 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled) (&fVRDPEnabled));
1044
1045 if (vrdpEnabled != NULL)
1046 {
1047 /* -vrdp on|off|config */
1048 if (!strcmp(vrdpEnabled, "off") || !strcmp(vrdpEnabled, "disable"))
1049 fVRDPEnable = false;
1050 else if (!strcmp(vrdpEnabled, "config"))
1051 {
1052 if (!fVRDPEnabled)
1053 fVRDPEnable = false;
1054 }
1055 else if (strcmp(vrdpEnabled, "on") && strcmp(vrdpEnabled, "enable"))
1056 {
1057 RTPrintf("-vrdp requires an argument (on|off|config)\n");
1058 break;
1059 }
1060 }
1061
1062 if (fVRDPEnable)
1063 {
1064 Log (("VBoxHeadless: Enabling VRDP server...\n"));
1065
1066 /* set VRDP port if requested by the user */
1067 if (vrdpPort != NULL)
1068 {
1069 Bstr bstr = vrdpPort;
1070 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Ports)(bstr));
1071 }
1072 /* set VRDP address if requested by the user */
1073 if (vrdpAddress != NULL)
1074 {
1075 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(NetAddress)(Bstr(vrdpAddress)));
1076 }
1077 /* enable VRDP server (only if currently disabled) */
1078 if (!fVRDPEnabled)
1079 {
1080 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (TRUE));
1081 }
1082 }
1083 else
1084 {
1085 /* disable VRDP server (only if currently enabled */
1086 if (fVRDPEnabled)
1087 {
1088 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (FALSE));
1089 }
1090 }
1091#endif
1092 Log (("VBoxHeadless: Powering up the machine...\n"));
1093
1094 ComPtr <IProgress> progress;
1095 CHECK_ERROR_BREAK(console, PowerUp (progress.asOutParam()));
1096
1097 /* wait for result because there can be errors */
1098 if (SUCCEEDED(progress->WaitForCompletion (-1)))
1099 {
1100 LONG progressRc;
1101 progress->COMGETTER(ResultCode)(&progressRc);
1102 rc = progressRc;
1103 if (FAILED(progressRc))
1104 {
1105 com::ProgressErrorInfo info(progress);
1106 if (info.isBasicAvailable())
1107 {
1108 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
1109 }
1110 else
1111 {
1112 RTPrintf("Error: failed to start machine. No error message available!\n");
1113 }
1114 }
1115 }
1116
1117#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1118 signal(SIGINT, SaveState);
1119 signal(SIGTERM, SaveState);
1120#endif
1121
1122 Log (("VBoxHeadless: Waiting for PowerDown...\n"));
1123
1124 Event *e;
1125
1126 while (gEventQ->waitForEvent (&e) && e)
1127 gEventQ->handleEvent (e);
1128
1129 Log (("VBoxHeadless: event loop has terminated...\n"));
1130
1131#ifdef VBOX_FFMPEG
1132 if (pFramebuffer)
1133 {
1134 pFramebuffer->Release ();
1135 Log(("Released framebuffer\n"));
1136 pFramebuffer = NULL;
1137 }
1138#endif /* defined(VBOX_FFMPEG) */
1139
1140 /* we don't have to disable VRDP here because we don't save the settings of the VM */
1141 }
1142 while (0);
1143
1144 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1145 gConsole = NULL;
1146
1147 if (fSessionOpened)
1148 {
1149 /*
1150 * Close the session. This will also uninitialize the console and
1151 * unregister the callback we've registered before.
1152 */
1153 Log (("VBoxHeadless: Closing the session...\n"));
1154 session->Close();
1155 }
1156
1157 /* Must be before com::Shutdown */
1158 session.setNull();
1159 virtualBox.setNull();
1160
1161 com::Shutdown();
1162
1163 LogFlow (("VBoxHeadless FINISHED.\n"));
1164
1165 return rc;
1166}
1167
1168
1169#ifndef VBOX_WITH_HARDENING
1170/**
1171 * Main entry point.
1172 */
1173int main (int argc, char **argv, char **envp)
1174{
1175 // initialize VBox Runtime
1176 int rc = RTR3InitAndSUPLib();
1177 if (RT_FAILURE(rc))
1178 {
1179 RTPrintf("VBoxHeadless: Runtime Error:\n"
1180 " %Rrc -- %Rrf\n", rc, rc);
1181 switch (rc)
1182 {
1183 case VERR_VM_DRIVER_NOT_INSTALLED:
1184 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1185 "loaded successfully. Aborting ...\n");
1186 break;
1187 default:
1188 break;
1189 }
1190 return 1;
1191 }
1192
1193 return TrustedMain (argc, argv, envp);
1194}
1195#endif /* !VBOX_WITH_HARDENING */
1196
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