VirtualBox

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

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

VBoxHeadless: ExtraData option to terminate RDP connection if the user logs out of the guest OS (xTracker 4294)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/** @file
2 *
3 * VBox frontends: VBoxHeadless (headless frontend):
4 * Headless server executable
5 */
6
7/*
8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <VBox/com/com.h>
24#include <VBox/com/string.h>
25#include <VBox/com/Guid.h>
26#include <VBox/com/ErrorInfo.h>
27#include <VBox/com/errorprint.h>
28#include <VBox/com/EventQueue.h>
29
30#include <VBox/com/VirtualBox.h>
31
32using namespace com;
33
34#define LOG_GROUP LOG_GROUP_GUI
35
36#include <VBox/log.h>
37#include <VBox/version.h>
38#ifdef VBOX_WITH_VRDP
39# include <VBox/vrdpapi.h>
40#endif
41#include <iprt/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 value;
215 hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout"), value.asOutParam());
216 if (SUCCEEDED(hrc) && value == "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(OnUSBDeviceStateChange) (IUSBDevice *aDevice, BOOL aAttached,
410 IVirtualBoxErrorInfo *aError)
411 {
412 return S_OK;
413 }
414
415 STDMETHOD(OnSharedFolderChange) (Scope_T aScope)
416 {
417 return S_OK;
418 }
419
420 STDMETHOD(OnRuntimeError)(BOOL fatal, IN_BSTR id, IN_BSTR message)
421 {
422 return S_OK;
423 }
424
425 STDMETHOD(OnCanShowWindow)(BOOL *canShow)
426 {
427 if (!canShow)
428 return E_POINTER;
429 /* Headless windows should not be shown */
430 *canShow = FALSE;
431 return S_OK;
432 }
433
434 STDMETHOD(OnShowWindow) (ULONG64 *winId)
435 {
436 /* OnCanShowWindow() always returns FALSE, so this call should never
437 * happen. */
438 AssertFailed();
439 if (!winId)
440 return E_POINTER;
441 *winId = 0;
442 return E_NOTIMPL;
443 }
444
445private:
446
447#ifndef VBOX_WITH_XPCOM
448 long refcnt;
449#endif
450 long mLastVRDPPort;
451};
452
453#ifdef VBOX_WITH_XPCOM
454NS_DECL_CLASSINFO (VirtualBoxCallback)
455NS_IMPL_THREADSAFE_ISUPPORTS1_CI (VirtualBoxCallback, IVirtualBoxCallback)
456NS_DECL_CLASSINFO (ConsoleCallback)
457NS_IMPL_THREADSAFE_ISUPPORTS1_CI (ConsoleCallback, IConsoleCallback)
458#endif
459
460#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
461static void SaveState(int sig)
462{
463 ComPtr <IProgress> progress = NULL;
464
465/** @todo Deal with nested signals, multithreaded signal dispatching (esp. on windows),
466 * and multiple signals (both SIGINT and SIGTERM in some order).
467 * Consider processing the signal request asynchronously since there are lots of things
468 * which aren't safe (like RTPrintf and printf IIRC) in a signal context. */
469
470 RTPrintf("Signal received, saving state.\n");
471
472 HRESULT rc = gConsole->SaveState(progress.asOutParam());
473 if (FAILED(S_OK))
474 {
475 RTPrintf("Error saving state! rc = 0x%x\n", rc);
476 return;
477 }
478 Assert(progress);
479 LONG cPercent = 0;
480
481 RTPrintf("0%%");
482 RTStrmFlush(g_pStdOut);
483 for (;;)
484 {
485 BOOL fCompleted = false;
486 rc = progress->COMGETTER(Completed)(&fCompleted);
487 if (FAILED(rc) || fCompleted)
488 break;
489 ULONG cPercentNow;
490 rc = progress->COMGETTER(Percent)(&cPercentNow);
491 if (FAILED(rc))
492 break;
493 if ((cPercentNow / 10) != (cPercent / 10))
494 {
495 cPercent = cPercentNow;
496 RTPrintf("...%d%%", cPercentNow);
497 RTStrmFlush(g_pStdOut);
498 }
499
500 /* wait */
501 rc = progress->WaitForCompletion(100);
502 }
503
504 HRESULT lrc;
505 rc = progress->COMGETTER(ResultCode)(&lrc);
506 if (FAILED(rc))
507 lrc = ~0;
508 if (!lrc)
509 {
510 RTPrintf(" -- Saved the state successfully.\n");
511 RTThreadYield();
512 }
513 else
514 RTPrintf("-- Error saving state, lrc=%d (%#x)\n", lrc, lrc);
515
516}
517#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
518
519////////////////////////////////////////////////////////////////////////////////
520
521static void show_usage()
522{
523 RTPrintf("Usage:\n"
524 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
525#ifdef VBOX_WITH_VRDP
526 " -v, -vrdp, --vrdp on|off|config Enable (default) or disable the VRDP\n"
527 " server or don't change the setting\n"
528 " -p, -vrdpport, --vrdpport <ports> Comma-separated list of ports the VRDP\n"
529 " server can bind to. Use a dash between\n"
530 " two port numbers to specify a range\n"
531 " -a, -vrdpaddress, --vrdpaddress <ip> Interface IP the VRDP will bind to \n"
532#endif
533#ifdef VBOX_FFMPEG
534 " -c, -capture, --capture Record the VM screen output to a file\n"
535 " -w, --width Frame width when recording\n"
536 " -h, --height Frame height when recording\n"
537 " -r, --bitrate Recording bit rate when recording\n"
538 " -f, --filename File name when recording. The codec\n"
539 " used will be chosen based on the\n"
540 " file extension\n"
541#endif
542 "\n");
543}
544
545#ifdef VBOX_FFMPEG
546/**
547 * Parse the environment for variables which can influence the FFMPEG settings.
548 * purely for backwards compatibility.
549 * @param pulFrameWidth may be updated with a desired frame width
550 * @param pulFrameHeight may be updated with a desired frame height
551 * @param pulBitRate may be updated with a desired bit rate
552 * @param ppszFileName may be updated with a desired file name
553 */
554static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
555 unsigned long *pulBitRate, const char **ppszFileName)
556{
557 const char *pszEnvTemp;
558
559 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
560 {
561 errno = 0;
562 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
563 if (errno != 0)
564 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
565 else
566 *pulFrameWidth = ulFrameWidth;
567 }
568 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
569 {
570 errno = 0;
571 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
572 if (errno != 0)
573 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
574 else
575 *pulFrameHeight = ulFrameHeight;
576 }
577 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
578 {
579 errno = 0;
580 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
581 if (errno != 0)
582 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
583 else
584 *pulBitRate = ulBitRate;
585 }
586 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
587 *ppszFileName = pszEnvTemp;
588}
589#endif /* VBOX_FFMPEG defined */
590
591/**
592 * Entry point.
593 */
594extern "C" DECLEXPORT (int) TrustedMain (int argc, char **argv, char **envp)
595{
596#ifdef VBOX_WITH_VRDP
597 const char *vrdpPort = NULL;
598 const char *vrdpAddress = NULL;
599 const char *vrdpEnabled = NULL;
600#endif
601 unsigned fRawR0 = ~0U;
602 unsigned fRawR3 = ~0U;
603 unsigned fPATM = ~0U;
604 unsigned fCSAM = ~0U;
605#ifdef VBOX_FFMPEG
606 unsigned fFFMPEG = 0;
607 unsigned long ulFrameWidth = 800;
608 unsigned long ulFrameHeight = 600;
609 unsigned long ulBitRate = 300000;
610 char pszMPEGFile[RTPATH_MAX];
611 const char *pszFileNameParam = "VBox-%d.vob";
612#endif /* VBOX_FFMPEG */
613
614 /* Make sure that DISPLAY is unset, so that X11 bits do not get initialised
615 * on X11-using OSes. */
616 /** @todo this should really be taken care of in Main. */
617 RTEnvUnset("DISPLAY");
618
619 LogFlow (("VBoxHeadless STARTED.\n"));
620 RTPrintf ("VirtualBox Headless Interface %s\n"
621 "(C) 2008-2009 Sun Microsystems, Inc.\n"
622 "All rights reserved.\n\n",
623 VBOX_VERSION_STRING);
624
625 Bstr id;
626 /* the below cannot be Bstr because on Linux Bstr doesn't work until XPCOM (nsMemory) is initialized */
627 const char *name = NULL;
628
629#ifdef VBOX_FFMPEG
630 /* Parse the environment */
631 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
632#endif
633
634 enum eHeadlessOptions
635 {
636 OPT_RAW_R0 = 0x100,
637 OPT_NO_RAW_R0,
638 OPT_RAW_R3,
639 OPT_NO_RAW_R3,
640 OPT_PATM,
641 OPT_NO_PATM,
642 OPT_CSAM,
643 OPT_NO_CSAM,
644 OPT_COMMENT,
645 };
646
647 static const RTGETOPTDEF s_aOptions[] =
648 {
649 { "-startvm", 's', RTGETOPT_REQ_STRING },
650 { "--startvm", 's', RTGETOPT_REQ_STRING },
651#ifdef VBOX_WITH_VRDP
652 { "-vrdpport", 'p', RTGETOPT_REQ_STRING },
653 { "--vrdpport", 'p', RTGETOPT_REQ_STRING },
654 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING },
655 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING },
656 { "-vrdp", 'v', RTGETOPT_REQ_STRING },
657 { "--vrdp", 'v', RTGETOPT_REQ_STRING },
658#endif /* VBOX_WITH_VRDP defined */
659 { "-rawr0", OPT_RAW_R0, 0 },
660 { "--rawr0", OPT_RAW_R0, 0 },
661 { "-norawr0", OPT_NO_RAW_R0, 0 },
662 { "--norawr0", OPT_NO_RAW_R0, 0 },
663 { "-rawr3", OPT_RAW_R3, 0 },
664 { "--rawr3", OPT_RAW_R3, 0 },
665 { "-norawr3", OPT_NO_RAW_R3, 0 },
666 { "--norawr3", OPT_NO_RAW_R3, 0 },
667 { "-patm", OPT_PATM, 0 },
668 { "--patm", OPT_PATM, 0 },
669 { "-nopatm", OPT_NO_PATM, 0 },
670 { "--nopatm", OPT_NO_PATM, 0 },
671 { "-csam", OPT_CSAM, 0 },
672 { "--csam", OPT_CSAM, 0 },
673 { "-nocsam", OPT_NO_CSAM, 0 },
674 { "--nocsam", OPT_NO_CSAM, 0 },
675#ifdef VBOX_FFMPEG
676 { "-capture", 'c', 0 },
677 { "--capture", 'c', 0 },
678 { "--width", 'w', RTGETOPT_REQ_UINT32 },
679 { "--height", 'h', RTGETOPT_REQ_UINT32 },
680 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
681 { "--filename", 'f', RTGETOPT_REQ_STRING },
682#endif /* VBOX_FFMPEG defined */
683 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
684 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
685 };
686
687 // parse the command line
688 int ch;
689 RTGETOPTUNION ValueUnion;
690 RTGETOPTSTATE GetState;
691 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
692 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
693 {
694 switch(ch)
695 {
696 case 's':
697 id = asGuidStr(ValueUnion.psz);
698 /* If the argument was not a UUID, then it must be a name. */
699 if (!id)
700 name = ValueUnion.psz;
701 break;
702#ifdef VBOX_WITH_VRDP
703 case 'p':
704 vrdpPort = ValueUnion.psz;
705 break;
706 case 'a':
707 vrdpAddress = ValueUnion.psz;
708 break;
709 case 'v':
710 vrdpEnabled = ValueUnion.psz;
711 break;
712#endif /* VBOX_WITH_VRDP defined */
713 case OPT_RAW_R0:
714 fRawR0 = true;
715 break;
716 case OPT_NO_RAW_R0:
717 fRawR0 = false;
718 break;
719 case OPT_RAW_R3:
720 fRawR3 = true;
721 break;
722 case OPT_NO_RAW_R3:
723 fRawR3 = false;
724 break;
725 case OPT_PATM:
726 fPATM = true;
727 break;
728 case OPT_NO_PATM:
729 fPATM = false;
730 break;
731 case OPT_CSAM:
732 fCSAM = true;
733 break;
734 case OPT_NO_CSAM:
735 fCSAM = false;
736 break;
737#ifdef VBOX_FFMPEG
738 case 'c':
739 fFFMPEG = true;
740 break;
741 case 'w':
742 ulFrameWidth = ValueUnion.u32;
743 break;
744 case 'h':
745 ulFrameHeight = ValueUnion.u32;
746 break;
747 case 'r':
748 ulBitRate = ValueUnion.u32;
749 break;
750 case 'f':
751 pszFileNameParam = ValueUnion.psz;
752 break;
753#endif /* VBOX_FFMPEG defined */
754 case VINF_GETOPT_NOT_OPTION:
755 RTPrintf("Invalid parameter '%s'\n\n", ValueUnion.psz);
756 show_usage();
757 return -1;
758 case OPT_COMMENT:
759 /* nothing to do */
760 break;
761 default:
762 if (ch > 0)
763 {
764 if (RT_C_IS_PRINT(ch))
765 RTPrintf("Invalid option -%c\n\n", ch);
766 else
767 RTPrintf("Invalid option case %i\n\n", ch);
768 }
769 else if (ch == VERR_GETOPT_UNKNOWN_OPTION)
770 RTPrintf("Unknown option: %s\n\n", ValueUnion.psz);
771 else if (ValueUnion.pDef)
772 RTPrintf("%s: %Rrs\n\n", ValueUnion.pDef->pszLong, ch);
773 else
774 RTPrintf("Error: %Rrs\n\n", ch);
775 show_usage();
776 return -1;
777 }
778 }
779
780#ifdef VBOX_FFMPEG
781 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
782 {
783 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
784 return -1;
785 }
786 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
787 {
788 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
789 return -1;
790 }
791 if (ulBitRate < 300000 || ulBitRate > 1000000)
792 {
793 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
794 return -1;
795 }
796 /* Make sure we only have %d or %u (or none) in the file name specified */
797 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
798 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
799 {
800 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
801 return -1;
802 }
803 /* And no more than one % in the name */
804 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
805 {
806 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
807 return -1;
808 }
809 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
810#endif /* defined VBOX_FFMPEG */
811
812 if (!id && !name)
813 {
814 show_usage();
815 return -1;
816 }
817
818 HRESULT rc;
819
820 rc = com::Initialize();
821 if (FAILED(rc))
822 {
823 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
824 return rc;
825 }
826
827 ComPtr<IVirtualBox> virtualBox;
828 ComPtr<ISession> session;
829 bool fSessionOpened = false;
830
831 do
832 {
833 rc = virtualBox.createLocalObject(CLSID_VirtualBox);
834 if (FAILED(rc))
835 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBox object!\n");
836 else
837 {
838 rc = session.createInprocObject(CLSID_Session);
839 if (FAILED(rc))
840 RTPrintf("VBoxHeadless: ERROR: failed to create a session object!\n");
841 }
842
843 if (FAILED(rc))
844 {
845 com::ErrorInfo info;
846 if (!info.isFullAvailable() && !info.isBasicAvailable())
847 {
848 com::GluePrintRCMessage(rc);
849 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
850 }
851 else
852 GluePrintErrorInfo(info);
853 break;
854 }
855
856 /* find ID by name */
857 if (!id)
858 {
859 ComPtr <IMachine> m;
860 rc = virtualBox->FindMachine (Bstr (name), m.asOutParam());
861 if (FAILED (rc))
862 {
863 LogError ("Invalid machine name!\n", rc);
864 break;
865 }
866 m->COMGETTER(Id) (id.asOutParam());
867 AssertComRC (rc);
868 if (FAILED (rc))
869 break;
870 }
871
872 Log (("VBoxHeadless: Opening a session with machine (id={%s})...\n",
873 Utf8Str(id).raw()));
874
875 // open a session
876 CHECK_ERROR_BREAK(virtualBox, OpenSession (session, id));
877 fSessionOpened = true;
878
879 /* get the console */
880 ComPtr <IConsole> console;
881 CHECK_ERROR_BREAK(session, COMGETTER (Console) (console.asOutParam()));
882
883 /* get the machine */
884 ComPtr <IMachine> machine;
885 CHECK_ERROR_BREAK(console, COMGETTER(Machine) (machine.asOutParam()));
886
887 ComPtr <IDisplay> display;
888 CHECK_ERROR_BREAK(console, COMGETTER(Display) (display.asOutParam()));
889
890#ifdef VBOX_FFMPEG
891 IFramebuffer *pFramebuffer = 0;
892 RTLDRMOD hLdrFFmpegFB;
893 PFNREGISTERFFMPEGFB pfnRegisterFFmpegFB;
894
895 if (fFFMPEG)
896 {
897 int rrc = VINF_SUCCESS, rcc = S_OK;
898
899 Log2(("VBoxHeadless: loading VBoxFFmpegFB shared library\n"));
900 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxFFmpegFB", &hLdrFFmpegFB);
901
902 if (RT_SUCCESS(rrc))
903 {
904 Log2(("VBoxHeadless: looking up symbol VBoxRegisterFFmpegFB\n"));
905 rrc = RTLdrGetSymbol(hLdrFFmpegFB, "VBoxRegisterFFmpegFB",
906 reinterpret_cast<void **>(&pfnRegisterFFmpegFB));
907 if (RT_FAILURE(rrc))
908 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
909 }
910 else
911 LogError("Failed to load the video capture extension\n", rrc);
912 if (RT_SUCCESS(rrc))
913 {
914 Log2(("VBoxHeadless: calling pfnRegisterFFmpegFB\n"));
915 rcc = pfnRegisterFFmpegFB(ulFrameWidth, ulFrameHeight, ulBitRate,
916 pszMPEGFile, &pFramebuffer);
917 if (rcc != S_OK)
918 LogError("Failed to initialise video capturing - make sure that the file format\n"
919 "you wish to use is supported on your system\n", rcc);
920 }
921 if (RT_SUCCESS(rrc) && (S_OK == rcc))
922 {
923 Log2(("VBoxHeadless: Registering framebuffer\n"));
924 pFramebuffer->AddRef();
925 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
926 }
927 if (!RT_SUCCESS(rrc) || (rcc != S_OK))
928 rc = E_FAIL;
929 }
930 if (rc != S_OK)
931 {
932 break;
933 }
934#endif /* defined(VBOX_FFMPEG) */
935
936 ULONG cMonitors = 1;
937 machine->COMGETTER(MonitorCount)(&cMonitors);
938
939#ifdef VBOX_WITH_VRDP
940 unsigned uScreenId;
941 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
942 {
943#ifdef VBOX_FFMPEG
944 if (fFFMPEG && uScreenId == 0)
945 {
946 /* Already registered. */
947 continue;
948 }
949#endif /* defined(VBOX_FFMPEG) */
950 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer ();
951 if (!pVRDPFramebuffer)
952 {
953 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
954 break;
955 }
956 pVRDPFramebuffer->AddRef();
957 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
958 }
959 if (uScreenId < cMonitors)
960 {
961 break;
962 }
963#endif
964
965 /* get the machine debugger (isn't necessarily available) */
966 ComPtr <IMachineDebugger> machineDebugger;
967 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
968 if (machineDebugger)
969 {
970 Log(("Machine debugger available!\n"));
971 }
972
973 if (fRawR0 != ~0U)
974 {
975 if (!machineDebugger)
976 {
977 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
978 break;
979 }
980 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
981 }
982 if (fRawR3 != ~0U)
983 {
984 if (!machineDebugger)
985 {
986 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR0 ? "" : "no");
987 break;
988 }
989 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
990 }
991 if (fPATM != ~0U)
992 {
993 if (!machineDebugger)
994 {
995 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fRawR0 ? "" : "no");
996 break;
997 }
998 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
999 }
1000 if (fCSAM != ~0U)
1001 {
1002 if (!machineDebugger)
1003 {
1004 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fRawR0 ? "" : "no");
1005 break;
1006 }
1007 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1008 }
1009
1010 /* initialize global references */
1011 gSession = session;
1012 gConsole = console;
1013 gEventQ = com::EventQueue::getMainEventQueue();
1014
1015 /* VirtualBox callback registration. */
1016 VirtualBoxCallback *vboxCallback = new VirtualBoxCallback();
1017 vboxCallback->AddRef();
1018 CHECK_ERROR(virtualBox, RegisterCallback(vboxCallback));
1019 vboxCallback->Release();
1020 if (FAILED (rc))
1021 break;
1022
1023 /* register a callback for machine events */
1024 {
1025 ConsoleCallback *callback = new ConsoleCallback ();
1026 callback->AddRef();
1027 CHECK_ERROR(console, RegisterCallback (callback));
1028 callback->Release();
1029 if (FAILED (rc))
1030 break;
1031 }
1032
1033#ifdef VBOX_WITH_VRDP
1034 /* default is to enable the RDP server (backward compatibility) */
1035 BOOL fVRDPEnable = true;
1036 BOOL fVRDPEnabled;
1037 ComPtr <IVRDPServer> vrdpServer;
1038 CHECK_ERROR_BREAK(machine, COMGETTER (VRDPServer) (vrdpServer.asOutParam()));
1039 CHECK_ERROR_BREAK(vrdpServer, COMGETTER(Enabled) (&fVRDPEnabled));
1040
1041 if (vrdpEnabled != NULL)
1042 {
1043 /* -vrdp on|off|config */
1044 if (!strcmp(vrdpEnabled, "off") || !strcmp(vrdpEnabled, "disable"))
1045 fVRDPEnable = false;
1046 else if (!strcmp(vrdpEnabled, "config"))
1047 {
1048 if (!fVRDPEnabled)
1049 fVRDPEnable = false;
1050 }
1051 else if (strcmp(vrdpEnabled, "on") && strcmp(vrdpEnabled, "enable"))
1052 {
1053 RTPrintf("-vrdp requires an argument (on|off|config)\n");
1054 break;
1055 }
1056 }
1057
1058 if (fVRDPEnable)
1059 {
1060 Log (("VBoxHeadless: Enabling VRDP server...\n"));
1061
1062 /* set VRDP port if requested by the user */
1063 if (vrdpPort != NULL)
1064 {
1065 Bstr bstr = vrdpPort;
1066 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Ports)(bstr));
1067 }
1068 /* set VRDP address if requested by the user */
1069 if (vrdpAddress != NULL)
1070 {
1071 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(NetAddress)(Bstr(vrdpAddress)));
1072 }
1073 /* enable VRDP server (only if currently disabled) */
1074 if (!fVRDPEnabled)
1075 {
1076 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (TRUE));
1077 }
1078 }
1079 else
1080 {
1081 /* disable VRDP server (only if currently enabled */
1082 if (fVRDPEnabled)
1083 {
1084 CHECK_ERROR_BREAK(vrdpServer, COMSETTER(Enabled) (FALSE));
1085 }
1086 }
1087#endif
1088 Log (("VBoxHeadless: Powering up the machine...\n"));
1089
1090 ComPtr <IProgress> progress;
1091 CHECK_ERROR_BREAK(console, PowerUp (progress.asOutParam()));
1092
1093 /* wait for result because there can be errors */
1094 if (SUCCEEDED(progress->WaitForCompletion (-1)))
1095 {
1096 LONG progressRc;
1097 progress->COMGETTER(ResultCode)(&progressRc);
1098 rc = progressRc;
1099 if (FAILED(progressRc))
1100 {
1101 com::ProgressErrorInfo info(progress);
1102 if (info.isBasicAvailable())
1103 {
1104 RTPrintf("Error: failed to start machine. Error message: %lS\n", info.getText().raw());
1105 }
1106 else
1107 {
1108 RTPrintf("Error: failed to start machine. No error message available!\n");
1109 }
1110 }
1111 }
1112
1113#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1114 signal(SIGINT, SaveState);
1115 signal(SIGTERM, SaveState);
1116#endif
1117
1118 Log (("VBoxHeadless: Waiting for PowerDown...\n"));
1119
1120 Event *e;
1121
1122 while (gEventQ->waitForEvent (&e) && e)
1123 gEventQ->handleEvent (e);
1124
1125 Log (("VBoxHeadless: event loop has terminated...\n"));
1126
1127#ifdef VBOX_FFMPEG
1128 if (pFramebuffer)
1129 {
1130 pFramebuffer->Release ();
1131 Log(("Released framebuffer\n"));
1132 pFramebuffer = NULL;
1133 }
1134#endif /* defined(VBOX_FFMPEG) */
1135
1136 /* we don't have to disable VRDP here because we don't save the settings of the VM */
1137 }
1138 while (0);
1139
1140 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1141 gConsole = NULL;
1142
1143 if (fSessionOpened)
1144 {
1145 /*
1146 * Close the session. This will also uninitialize the console and
1147 * unregister the callback we've registered before.
1148 */
1149 Log (("VBoxHeadless: Closing the session...\n"));
1150 session->Close();
1151 }
1152
1153 /* Must be before com::Shutdown */
1154 session.setNull();
1155 virtualBox.setNull();
1156
1157 com::Shutdown();
1158
1159 LogFlow (("VBoxHeadless FINISHED.\n"));
1160
1161 return rc;
1162}
1163
1164
1165#ifndef VBOX_WITH_HARDENING
1166/**
1167 * Main entry point.
1168 */
1169int main (int argc, char **argv, char **envp)
1170{
1171 // initialize VBox Runtime
1172 int rc = RTR3InitAndSUPLib();
1173 if (RT_FAILURE(rc))
1174 {
1175 RTPrintf("VBoxHeadless: Runtime Error:\n"
1176 " %Rrc -- %Rrf\n", rc, rc);
1177 switch (rc)
1178 {
1179 case VERR_VM_DRIVER_NOT_INSTALLED:
1180 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1181 "loaded successfully. Aborting ...\n");
1182 break;
1183 default:
1184 break;
1185 }
1186 return 1;
1187 }
1188
1189 return TrustedMain (argc, argv, envp);
1190}
1191#endif /* !VBOX_WITH_HARDENING */
1192
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