VirtualBox

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

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

Main/VirtualBoxCallback: rename OnSnapshotDiscard to OnSnapshotDeleted for consistency, clean up wording and other minor issues

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