VirtualBox

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

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

Merged VNC contribution; disabled by default and currently not finished (not externally visible yet either)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 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 ISession *gSession = NULL;
82static IConsole *gConsole = NULL;
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_VNC
532 " -n, -vnc Enable the built in VNC server\n"
533 " -m, -vncport TCP port number to use for the VNC server\n"
534 " -o, -vncpass <pw> Set the VNC server password\n"
535#endif
536#ifdef VBOX_WITH_VRDP
537 " -v, -vrdp, --vrdp on|off|config Enable (default) or disable the VRDP\n"
538 " server or don't change the setting\n"
539 " -p, -vrdpport, --vrdpport <ports> Comma-separated list of ports the VRDP\n"
540 " server can bind to. Use a dash between\n"
541 " two port numbers to specify a range\n"
542 " -a, -vrdpaddress, --vrdpaddress <ip> Interface IP the VRDP will bind to \n"
543#endif
544#ifdef VBOX_FFMPEG
545 " -c, -capture, --capture Record the VM screen output to a file\n"
546 " -w, --width Frame width when recording\n"
547 " -h, --height Frame height when recording\n"
548 " -r, --bitrate Recording bit rate when recording\n"
549 " -f, --filename File name when recording. The codec\n"
550 " used will be chosen based on the\n"
551 " file extension\n"
552#endif
553 "\n");
554}
555
556#ifdef VBOX_FFMPEG
557/**
558 * Parse the environment for variables which can influence the FFMPEG settings.
559 * purely for backwards compatibility.
560 * @param pulFrameWidth may be updated with a desired frame width
561 * @param pulFrameHeight may be updated with a desired frame height
562 * @param pulBitRate may be updated with a desired bit rate
563 * @param ppszFileName may be updated with a desired file name
564 */
565static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
566 unsigned long *pulBitRate, const char **ppszFileName)
567{
568 const char *pszEnvTemp;
569
570 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
571 {
572 errno = 0;
573 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
574 if (errno != 0)
575 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
576 else
577 *pulFrameWidth = ulFrameWidth;
578 }
579 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
580 {
581 errno = 0;
582 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
583 if (errno != 0)
584 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
585 else
586 *pulFrameHeight = ulFrameHeight;
587 }
588 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
589 {
590 errno = 0;
591 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
592 if (errno != 0)
593 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
594 else
595 *pulBitRate = ulBitRate;
596 }
597 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
598 *ppszFileName = pszEnvTemp;
599}
600#endif /* VBOX_FFMPEG defined */
601
602/**
603 * Entry point.
604 */
605extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
606{
607#ifdef VBOX_WITH_VRDP
608 const char *vrdpPort = NULL;
609 const char *vrdpAddress = NULL;
610 const char *vrdpEnabled = NULL;
611#endif
612#ifdef VBOX_WITH_VNC
613 bool fVNCEnable = false;
614 unsigned uVNCPort = 0; /* default port */
615 char *pszVNCPassword = NULL; /* no password */
616#endif
617 unsigned fRawR0 = ~0U;
618 unsigned fRawR3 = ~0U;
619 unsigned fPATM = ~0U;
620 unsigned fCSAM = ~0U;
621#ifdef VBOX_FFMPEG
622 unsigned fFFMPEG = 0;
623 unsigned long ulFrameWidth = 800;
624 unsigned long ulFrameHeight = 600;
625 unsigned long ulBitRate = 300000;
626 char pszMPEGFile[RTPATH_MAX];
627 const char *pszFileNameParam = "VBox-%d.vob";
628#endif /* VBOX_FFMPEG */
629
630 /* Make sure that DISPLAY is unset, so that X11 bits do not get initialised
631 * on X11-using OSes. */
632 /** @todo this should really be taken care of in Main. */
633 RTEnvUnset("DISPLAY");
634
635 LogFlow (("VBoxHeadless STARTED.\n"));
636 RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
637 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
638 "All rights reserved.\n\n");
639
640 Bstr id;
641 /* the below cannot be Bstr because on Linux Bstr doesn't work until XPCOM (nsMemory) is initialized */
642 const char *name = NULL;
643
644#ifdef VBOX_FFMPEG
645 /* Parse the environment */
646 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
647#endif
648
649 enum eHeadlessOptions
650 {
651 OPT_RAW_R0 = 0x100,
652 OPT_NO_RAW_R0,
653 OPT_RAW_R3,
654 OPT_NO_RAW_R3,
655 OPT_PATM,
656 OPT_NO_PATM,
657 OPT_CSAM,
658 OPT_NO_CSAM,
659 OPT_COMMENT,
660 };
661
662 static const RTGETOPTDEF s_aOptions[] =
663 {
664 { "-startvm", 's', RTGETOPT_REQ_STRING },
665 { "--startvm", 's', RTGETOPT_REQ_STRING },
666#ifdef VBOX_WITH_VRDP
667 { "-vrdpport", 'p', RTGETOPT_REQ_STRING },
668 { "--vrdpport", 'p', RTGETOPT_REQ_STRING },
669 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING },
670 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING },
671 { "-vrdp", 'v', RTGETOPT_REQ_STRING },
672 { "--vrdp", 'v', RTGETOPT_REQ_STRING },
673#endif /* VBOX_WITH_VRDP defined */
674#ifdef VBOX_WITH_VNC
675 { "-vncport", 'm', RTGETOPT_REQ_INT32 },
676 { "-vncpass", 'o', RTGETOPT_REQ_STRING },
677 { "-vnc", 'n', 0 },
678#endif /* VBOX_WITH_VNC */
679 { "-rawr0", OPT_RAW_R0, 0 },
680 { "--rawr0", OPT_RAW_R0, 0 },
681 { "-norawr0", OPT_NO_RAW_R0, 0 },
682 { "--norawr0", OPT_NO_RAW_R0, 0 },
683 { "-rawr3", OPT_RAW_R3, 0 },
684 { "--rawr3", OPT_RAW_R3, 0 },
685 { "-norawr3", OPT_NO_RAW_R3, 0 },
686 { "--norawr3", OPT_NO_RAW_R3, 0 },
687 { "-patm", OPT_PATM, 0 },
688 { "--patm", OPT_PATM, 0 },
689 { "-nopatm", OPT_NO_PATM, 0 },
690 { "--nopatm", OPT_NO_PATM, 0 },
691 { "-csam", OPT_CSAM, 0 },
692 { "--csam", OPT_CSAM, 0 },
693 { "-nocsam", OPT_NO_CSAM, 0 },
694 { "--nocsam", OPT_NO_CSAM, 0 },
695#ifdef VBOX_FFMPEG
696 { "-capture", 'c', 0 },
697 { "--capture", 'c', 0 },
698 { "--width", 'w', RTGETOPT_REQ_UINT32 },
699 { "--height", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
700 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
701 { "--filename", 'f', RTGETOPT_REQ_STRING },
702#endif /* VBOX_FFMPEG defined */
703 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
704 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
705 };
706
707 // parse the command line
708 int ch;
709 RTGETOPTUNION ValueUnion;
710 RTGETOPTSTATE GetState;
711 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
712 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
713 {
714 switch(ch)
715 {
716 case 's':
717 id = asGuidStr(ValueUnion.psz);
718 /* If the argument was not a UUID, then it must be a name. */
719 if (id.isEmpty())
720 name = ValueUnion.psz;
721 break;
722#ifdef VBOX_WITH_VRDP
723 case 'p':
724 vrdpPort = ValueUnion.psz;
725 break;
726 case 'a':
727 vrdpAddress = ValueUnion.psz;
728 break;
729 case 'v':
730 vrdpEnabled = ValueUnion.psz;
731 break;
732#endif /* VBOX_WITH_VRDP defined */
733#ifdef VBOX_WITH_VNC
734 case 'n':
735 fVNCEnable = true;
736 break;
737 case 'm':
738 uVNCPort = ValueUnion.i32;
739 break;
740 case 'o':
741 pszVNCPassword = (char*)ValueUnion.psz;
742 break;
743#endif /* VBOX_WITH_VNC */
744 case OPT_RAW_R0:
745 fRawR0 = true;
746 break;
747 case OPT_NO_RAW_R0:
748 fRawR0 = false;
749 break;
750 case OPT_RAW_R3:
751 fRawR3 = true;
752 break;
753 case OPT_NO_RAW_R3:
754 fRawR3 = false;
755 break;
756 case OPT_PATM:
757 fPATM = true;
758 break;
759 case OPT_NO_PATM:
760 fPATM = false;
761 break;
762 case OPT_CSAM:
763 fCSAM = true;
764 break;
765 case OPT_NO_CSAM:
766 fCSAM = false;
767 break;
768#ifdef VBOX_FFMPEG
769 case 'c':
770 fFFMPEG = true;
771 break;
772 case 'w':
773 ulFrameWidth = ValueUnion.u32;
774 break;
775 case 'r':
776 ulBitRate = ValueUnion.u32;
777 break;
778 case 'f':
779 pszFileNameParam = ValueUnion.psz;
780 break;
781#endif /* VBOX_FFMPEG defined */
782 case 'h':
783#ifdef VBOX_FFMPEG
784 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
785 {
786 ulFrameHeight = ValueUnion.u32;
787 break;
788 }
789#endif
790 show_usage();
791 return 0;
792 case OPT_COMMENT:
793 /* nothing to do */
794 break;
795 case 'V':
796 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
797 return 0;
798 default:
799 ch = RTGetOptPrintError(ch, &ValueUnion);
800 show_usage();
801 return ch;
802 }
803 }
804
805#ifdef VBOX_FFMPEG
806 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
807 {
808 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
809 return 1;
810 }
811 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
812 {
813 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
814 return 1;
815 }
816 if (ulBitRate < 300000 || ulBitRate > 1000000)
817 {
818 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
819 return 1;
820 }
821 /* Make sure we only have %d or %u (or none) in the file name specified */
822 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
823 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
824 {
825 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
826 return 1;
827 }
828 /* And no more than one % in the name */
829 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
830 {
831 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
832 return 1;
833 }
834 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
835#endif /* defined VBOX_FFMPEG */
836
837 if (id.isEmpty() && !name)
838 {
839 show_usage();
840 return 1;
841 }
842
843 HRESULT rc;
844
845 rc = com::Initialize();
846 if (FAILED(rc))
847 {
848 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
849 return 1;
850 }
851
852 ComPtr<IVirtualBox> virtualBox;
853 ComPtr<ISession> session;
854 bool fSessionOpened = false;
855 VirtualBoxCallback *vboxCallback = NULL;
856
857 do
858 {
859 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
860 if (FAILED(rc))
861 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
862 else
863 {
864 rc = session.createInprocObject(CLSID_Session);
865 if (FAILED(rc))
866 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
867 }
868
869 if (FAILED(rc))
870 {
871 com::ErrorInfo info;
872 if (!info.isFullAvailable() && !info.isBasicAvailable())
873 {
874 com::GluePrintRCMessage(rc);
875 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
876 }
877 else
878 GluePrintErrorInfo(info);
879 break;
880 }
881
882 /* find ID by name */
883 if (id.isEmpty())
884 {
885 ComPtr <IMachine> m;
886 rc = virtualBox->FindMachine(Bstr(name), m.asOutParam());
887 if (FAILED(rc))
888 {
889 LogError("Invalid machine name!\n", rc);
890 break;
891 }
892 m->COMGETTER(Id)(id.asOutParam());
893 AssertComRC(rc);
894 if (FAILED(rc))
895 break;
896 }
897
898 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
899 Utf8Str(id).raw()));
900
901 // open a session
902 CHECK_ERROR_BREAK(virtualBox, OpenSession(session, id));
903 fSessionOpened = true;
904
905 /* get the console */
906 ComPtr <IConsole> console;
907 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
908
909 /* get the machine */
910 ComPtr <IMachine> machine;
911 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
912
913 ComPtr <IDisplay> display;
914 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
915
916#ifdef VBOX_FFMPEG
917 IFramebuffer *pFramebuffer = 0;
918 RTLDRMOD hLdrFFmpegFB;
919 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
920
921 if (fFFMPEG)
922 {
923 int rrc = VINF_SUCCESS, rcc = S_OK;
924
925 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
926 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
927
928 if (RT_SUCCESS(rrc))
929 {
930 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
931 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
932 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
933 if (RT_FAILURE(rrc))
934 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
935 }
936 else
937 LogError("Failed to load the video capture extension\n", rrc);
938 if (RT_SUCCESS(rrc))
939 {
940 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
941 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
942 pszMPEGFile, &pFramebuffer);
943 if (rcc != S_OK)
944 LogError("Failed to initialise video capturing - make sure that the file format\n"
945 "you wish to use is supported on your system\n", rcc);
946 }
947 if (RT_SUCCESS(rrc) && (rcc == S_OK))
948 {
949 Log2(("VBoxHeadless: Registering framebuffer\n"));
950 pFramebuffer->AddRef();
951 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
952 }
953 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
954 rc = E_FAIL;
955 }
956 if (rc != S_OK)
957 {
958 break;
959 }
960#endif /* defined(VBOX_FFMPEG) */
961#ifdef VBOX_WITH_VNC
962 if (fVNCEnable)
963 {
964 VNCFB *pFramebuffer;
965 RTLDRMOD hLdrVNC;
966 PFNREGISTERVNCFB pfnRegisterVNCFB;
967
968 pFramebuffer = new VNCFB(console, uVNCPort, pszVNCPassword);
969 rc = pFramebuffer->init();
970 if (rc != S_OK)
971 {
972 LogError("Failed to load the vnc server extension, possibly due to a damaged file\n", rrc);
973 delete pFramebuffer;
974 break;
975 }
976
977 Log2(("VBoxHeadless: Registering VNC framebuffer\n"));
978 pFramebuffer->AddRef();
979 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
980 }
981 if (rc != S_OK)
982 break;
983#endif
984 ULONG cMonitors = 1;
985 machine->COMGETTER(MonitorCount)(&cMonitors);
986
987#ifdef VBOX_WITH_VRDP
988 unsigned uScreenId;
989 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
990 {
991#ifdef VBOX_FFMPEG
992 if (fFFMPEG && uScreenId == 0)
993 {
994 /* Already registered. */
995 continue;
996 }
997#endif /* defined(VBOX_FFMPEG) */
998 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer();
999 if (!pVRDPFramebuffer)
1000 {
1001 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
1002 break;
1003 }
1004 pVRDPFramebuffer->AddRef();
1005 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
1006 }
1007 if (uScreenId < cMonitors)
1008 {
1009 break;
1010 }
1011#endif
1012
1013 /* get the machine debugger (isn't necessarily available) */
1014 ComPtr <IMachineDebugger> machineDebugger;
1015 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
1016 if (machineDebugger)
1017 {
1018 Log(("Machine debugger available!\n"));
1019 }
1020
1021 if (fRawR0 != ~0U)
1022 {
1023 if (!machineDebugger)
1024 {
1025 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
1026 break;
1027 }
1028 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
1029 }
1030 if (fRawR3 != ~0U)
1031 {
1032 if (!machineDebugger)
1033 {
1034 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
1035 break;
1036 }
1037 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1038 }
1039 if (fPATM != ~0U)
1040 {
1041 if (!machineDebugger)
1042 {
1043 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
1044 break;
1045 }
1046 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1047 }
1048 if (fCSAM != ~0U)
1049 {
1050 if (!machineDebugger)
1051 {
1052 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
1053 break;
1054 }
1055 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1056 }
1057
1058 /* initialize global references */
1059 gSession = session;
1060 gConsole = console;
1061 gEventQ = com::EventQueue::getMainEventQueue();
1062
1063 /* register a callback for machine events */
1064 {
1065 ConsoleCallback *callback = new ConsoleCallback();
1066 callback->AddRef();
1067 CHECK_ERROR(console, RegisterCallback(callback));
1068 callback->Release();
1069 if (FAILED(rc))
1070 break;
1071 }
1072
1073#ifdef VBOX_WITH_VRDP
1074 /* default is to enable the RDP server (backward compatibility) */
1075 BOOL fVRDPEnable = true;
1076 BOOL fVRDPEnabled;
1077 ComPtr <IVRDPServer> vrdpServer;
1078 CHECK_ERROR_BREAK(machine, COMGETTER(VRDPServer)(vrdpServer.asOutParam()));
1079 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled)(&fVRDPEnabled));
1080
1081 if (vrdpEnabled != NULL)
1082 {
1083 /* -vrdp on|off|config */
1084 if (!strcmp(vrdpEnabled, "off") || !strcmp(vrdpEnabled, "disable"))
1085 fVRDPEnable = false;
1086 else if (!strcmp(vrdpEnabled, "config"))
1087 {
1088 if (!fVRDPEnabled)
1089 fVRDPEnable = false;
1090 }
1091 else if (strcmp(vrdpEnabled, "on") && strcmp(vrdpEnabled, "enable"))
1092 {
1093 RTPrintf("-vrdp requires an argument (on|off|config)\n");
1094 break;
1095 }
1096 }
1097
1098 if (fVRDPEnable)
1099 {
1100 Log(("VBoxHeadless: Enabling VRDP server...\n"));
1101
1102 /* set VRDP port if requested by the user */
1103 if (vrdpPort != NULL)
1104 {
1105 Bstr bstr = vrdpPort;
1106 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Ports)(bstr));
1107 }
1108 /* set VRDP address if requested by the user */
1109 if (vrdpAddress != NULL)
1110 {
1111 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(NetAddress)(Bstr(vrdpAddress)));
1112 }
1113 /* enable VRDP server (only if currently disabled) */
1114 if (!fVRDPEnabled)
1115 {
1116 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled)(TRUE));
1117 }
1118 }
1119 else
1120 {
1121 /* disable VRDP server (only if currently enabled */
1122 if (fVRDPEnabled)
1123 {
1124 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled)(FALSE));
1125 }
1126 }
1127#endif
1128 Log(("VBoxHeadless: Powering up the machine...\n"));
1129
1130 ComPtr <IProgress> progress;
1131 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1132
1133 /* wait for result because there can be errors */
1134 if (SUCCEEDED(progress->WaitForCompletion(-1)))
1135 {
1136 LONG progressRc;
1137 progress->COMGETTER(ResultCode)(&progressRc);
1138 rc = progressRc;
1139 if (FAILED(progressRc))
1140 {
1141 com::ProgressErrorInfo info(progress);
1142 if (info.isBasicAvailable())
1143 {
1144 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
1145 }
1146 else
1147 {
1148 RTPrintf("Error: failed to start machine. No error message available!\n");
1149 }
1150 }
1151 }
1152
1153 /* VirtualBox callback registration. */
1154 vboxCallback = new VirtualBoxCallback();
1155 vboxCallback->AddRef();
1156 CHECK_ERROR(virtualBox, RegisterCallback(vboxCallback));
1157 vboxCallback->Release();
1158 if (FAILED(rc))
1159 break;
1160
1161#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1162 signal(SIGINT, SaveState);
1163 signal(SIGTERM, SaveState);
1164#endif
1165
1166 Log(("VBoxHeadless: Waiting for PowerDown...\n"));
1167
1168 Event *e;
1169
1170 while (gEventQ->waitForEvent(&e) && e)
1171 gEventQ->handleEvent(e);
1172
1173 Log(("VBoxHeadless: event loop has terminated...\n"));
1174
1175#ifdef VBOX_FFMPEG
1176 if (pFramebuffer)
1177 {
1178 pFramebuffer->Release();
1179 Log(("Released framebuffer\n"));
1180 pFramebuffer = NULL;
1181 }
1182#endif /* defined(VBOX_FFMPEG) */
1183
1184 /* we don't have to disable VRDP here because we don't save the settings of the VM */
1185 }
1186 while (0);
1187
1188 /* VirtualBox callback unregistration. */
1189 if (vboxCallback)
1190 {
1191 vboxCallback->AddRef();
1192 CHECK_ERROR(virtualBox, UnregisterCallback(vboxCallback));
1193 vboxCallback->Release();
1194 }
1195
1196 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1197 gConsole = NULL;
1198
1199 if (fSessionOpened)
1200 {
1201 /*
1202 * Close the session. This will also uninitialize the console and
1203 * unregister the callback we've registered before.
1204 */
1205 Log(("VBoxHeadless: Closing the session...\n"));
1206 session->Close();
1207 }
1208
1209 /* Must be before com::Shutdown */
1210 session.setNull();
1211 virtualBox.setNull();
1212
1213 com::Shutdown();
1214
1215 LogFlow(("VBoxHeadless FINISHED.\n"));
1216
1217 return FAILED(rc) ? 1 : 0;
1218}
1219
1220
1221#ifndef VBOX_WITH_HARDENING
1222/**
1223 * Main entry point.
1224 */
1225int main(int argc, char **argv, char **envp)
1226{
1227 // initialize VBox Runtime
1228 int rc = RTR3InitAndSUPLib();
1229 if (RT_FAILURE(rc))
1230 {
1231 RTPrintf("VBoxHeadless: Runtime Error:\n"
1232 " %Rrc -- %Rrf\n", rc, rc);
1233 switch (rc)
1234 {
1235 case VERR_VM_DRIVER_NOT_INSTALLED:
1236 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1237 "loaded successfully. Aborting ...\n");
1238 break;
1239 default:
1240 break;
1241 }
1242 return 1;
1243 }
1244
1245 return TrustedMain(argc, argv, envp);
1246}
1247#endif /* !VBOX_WITH_HARDENING */
1248
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette