VirtualBox

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

Last change on this file since 47067 was 46649, checked in by vboxsync, 12 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1/* $Id: VBoxHeadless.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2/** @file
3 * VBoxHeadless - The VirtualBox Headless frontend for running VMs on servers.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
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
18#include <VBox/com/com.h>
19#include <VBox/com/string.h>
20#include <VBox/com/array.h>
21#include <VBox/com/Guid.h>
22#include <VBox/com/ErrorInfo.h>
23#include <VBox/com/errorprint.h>
24#include <VBox/com/NativeEventQueue.h>
25
26#include <VBox/com/VirtualBox.h>
27#include <VBox/com/listeners.h>
28
29using namespace com;
30
31#define LOG_GROUP LOG_GROUP_GUI
32
33#include <VBox/log.h>
34#include <VBox/version.h>
35#include <iprt/buildconfig.h>
36#include <iprt/ctype.h>
37#include <iprt/initterm.h>
38#include <iprt/stream.h>
39#include <iprt/ldr.h>
40#include <iprt/getopt.h>
41#include <iprt/env.h>
42#include <VBox/err.h>
43#include <VBox/VBoxVideo.h>
44
45#ifdef VBOX_WITH_VIDEO_REC
46#include <cstdlib>
47#include <cerrno>
48#include "VBoxHeadless.h"
49#include <iprt/env.h>
50#include <iprt/param.h>
51#include <iprt/process.h>
52#include <VBox/sup.h>
53#endif
54
55//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
56#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
57#include <signal.h>
58#endif
59
60#include "Framebuffer.h"
61
62#include "NullFramebuffer.h"
63
64////////////////////////////////////////////////////////////////////////////////
65
66#define LogError(m,rc) \
67 do { \
68 Log(("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
69 RTPrintf("%s\n", m); \
70 } while (0)
71
72////////////////////////////////////////////////////////////////////////////////
73
74/* global weak references (for event handlers) */
75static IConsole *gConsole = NULL;
76static NativeEventQueue *gEventQ = NULL;
77
78/* flag whether frontend should terminate */
79static volatile bool g_fTerminateFE = false;
80
81////////////////////////////////////////////////////////////////////////////////
82
83/**
84 * Handler for VirtualBoxClient events.
85 */
86class VirtualBoxClientEventListener
87{
88public:
89 VirtualBoxClientEventListener()
90 {
91 }
92
93 virtual ~VirtualBoxClientEventListener()
94 {
95 }
96
97 HRESULT init()
98 {
99 return S_OK;
100 }
101
102 void uninit()
103 {
104 }
105
106 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
107 {
108 switch (aType)
109 {
110 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
111 {
112 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
113 Assert(pVSACEv);
114 BOOL fAvailable = FALSE;
115 pVSACEv->COMGETTER(Available)(&fAvailable);
116 if (!fAvailable)
117 {
118 LogRel(("VBoxHeadless: VBoxSVC became unavailable, exiting.\n"));
119 RTPrintf("VBoxSVC became unavailable, exiting.\n");
120 /* Terminate the VM as cleanly as possible given that VBoxSVC
121 * is no longer present. */
122 g_fTerminateFE = true;
123 gEventQ->interruptEventQueueProcessing();
124 }
125 break;
126 }
127 default:
128 AssertFailed();
129 }
130
131 return S_OK;
132 }
133
134private:
135};
136
137/**
138 * Handler for global events.
139 */
140class VirtualBoxEventListener
141{
142public:
143 VirtualBoxEventListener()
144 {
145 mfNoLoggedInUsers = true;
146 }
147
148 virtual ~VirtualBoxEventListener()
149 {
150 }
151
152 HRESULT init()
153 {
154 return S_OK;
155 }
156
157 void uninit()
158 {
159 }
160
161 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
162 {
163 switch (aType)
164 {
165 case VBoxEventType_OnGuestPropertyChanged:
166 {
167 ComPtr<IGuestPropertyChangedEvent> pChangedEvent = aEvent;
168 Assert(pChangedEvent);
169
170 HRESULT hrc;
171
172 ComPtr <IMachine> pMachine;
173 if (gConsole)
174 {
175 hrc = gConsole->COMGETTER(Machine)(pMachine.asOutParam());
176 if (SUCCEEDED(hrc) && pMachine)
177 {
178 Bstr gpMachineId, machineId;
179 hrc = pMachine->COMGETTER(Id)(gpMachineId.asOutParam());
180 AssertComRC(hrc);
181 hrc = pChangedEvent->COMGETTER(MachineId)(machineId.asOutParam());
182 AssertComRC(hrc);
183 if (gpMachineId != machineId)
184 hrc = VBOX_E_OBJECT_NOT_FOUND;
185 }
186 }
187 else
188 hrc = VBOX_E_INVALID_VM_STATE;
189
190 if (SUCCEEDED(hrc))
191 {
192 Bstr strKey;
193 hrc = pChangedEvent->COMGETTER(Name)(strKey.asOutParam());
194 AssertComRC(hrc);
195
196 Bstr strValue;
197 hrc = pChangedEvent->COMGETTER(Value)(strValue.asOutParam());
198 AssertComRC(hrc);
199
200 Utf8Str utf8Key = strKey;
201 Utf8Str utf8Value = strValue;
202 LogRelFlow(("Guest property \"%s\" has been changed to \"%s\"\n",
203 utf8Key.c_str(), utf8Value.c_str()));
204
205 if (utf8Key.equals("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
206 {
207 LogRelFlow(("Guest indicates that there %s logged in users\n",
208 utf8Value.equals("true") ? "are no" : "are"));
209
210 /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
211 BOOL fProcessDisconnectOnGuestLogout = FALSE;
212
213 /* Does the machine handle VRDP disconnects? */
214 Bstr strDiscon;
215 hrc = pMachine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
216 strDiscon.asOutParam());
217 if (SUCCEEDED(hrc))
218 {
219 Utf8Str utf8Discon = strDiscon;
220 fProcessDisconnectOnGuestLogout = utf8Discon.equals("1")
221 ? TRUE : FALSE;
222 }
223
224 LogRelFlow(("VRDE: hrc=%Rhrc: Host %s disconnecting clients (current host state known: %s)\n",
225 hrc, fProcessDisconnectOnGuestLogout ? "will handle" : "does not handle",
226 mfNoLoggedInUsers ? "No users logged in" : "Users logged in"));
227
228 if (fProcessDisconnectOnGuestLogout)
229 {
230 bool fDropConnection = false;
231 if (!mfNoLoggedInUsers) /* Only if the property really changes. */
232 {
233 if ( utf8Value == "true"
234 /* Guest property got deleted due to reset,
235 * so it has no value anymore. */
236 || utf8Value.isEmpty())
237 {
238 mfNoLoggedInUsers = true;
239 fDropConnection = true;
240 }
241 }
242 else if (utf8Value == "false")
243 mfNoLoggedInUsers = false;
244 /* Guest property got deleted due to reset,
245 * take the shortcut without touching the mfNoLoggedInUsers
246 * state. */
247 else if (utf8Value.isEmpty())
248 fDropConnection = true;
249
250 LogRelFlow(("VRDE: szNoLoggedInUsers=%s, mfNoLoggedInUsers=%RTbool, fDropConnection=%RTbool\n",
251 utf8Value.c_str(), mfNoLoggedInUsers, fDropConnection));
252
253 if (fDropConnection)
254 {
255 /* If there is a connection, drop it. */
256 ComPtr<IVRDEServerInfo> info;
257 hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
258 if (SUCCEEDED(hrc) && info)
259 {
260 ULONG cClients = 0;
261 hrc = info->COMGETTER(NumberOfClients)(&cClients);
262
263 LogRelFlow(("VRDE: connected clients=%RU32\n", cClients));
264 if (SUCCEEDED(hrc) && cClients > 0)
265 {
266 ComPtr <IVRDEServer> vrdeServer;
267 hrc = pMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
268 if (SUCCEEDED(hrc) && vrdeServer)
269 {
270 LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n"));
271 hrc = vrdeServer->COMSETTER(Enabled)(FALSE);
272 AssertComRC(hrc);
273 HRESULT hrc2 = vrdeServer->COMSETTER(Enabled)(TRUE);
274 if (SUCCEEDED(hrc))
275 hrc = hrc2;
276 }
277 }
278 }
279 }
280 }
281 }
282
283 if (FAILED(hrc))
284 LogRelFlow(("VRDE: returned error=%Rhrc\n", hrc));
285 }
286
287 break;
288 }
289
290 default:
291 AssertFailed();
292 }
293
294 return S_OK;
295 }
296
297private:
298
299 bool mfNoLoggedInUsers;
300};
301
302/**
303 * Handler for machine events.
304 */
305class ConsoleEventListener
306{
307public:
308 ConsoleEventListener() :
309 mLastVRDEPort(-1),
310 m_fIgnorePowerOffEvents(false)
311 {
312 }
313
314 virtual ~ConsoleEventListener()
315 {
316 }
317
318 HRESULT init()
319 {
320 return S_OK;
321 }
322
323 void uninit()
324 {
325 }
326
327 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
328 {
329 switch (aType)
330 {
331 case VBoxEventType_OnMouseCapabilityChanged:
332 {
333
334 ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
335 Assert(mccev);
336
337 BOOL fSupportsAbsolute = false;
338 mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute);
339
340 /* Emit absolute mouse event to actually enable the host mouse cursor. */
341 if (fSupportsAbsolute && gConsole)
342 {
343 ComPtr<IMouse> mouse;
344 gConsole->COMGETTER(Mouse)(mouse.asOutParam());
345 if (mouse)
346 {
347 mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
348 }
349 }
350 break;
351 }
352 case VBoxEventType_OnStateChanged:
353 {
354 ComPtr<IStateChangedEvent> scev = aEvent;
355 Assert(scev);
356
357 MachineState_T machineState;
358 scev->COMGETTER(State)(&machineState);
359
360 /* Terminate any event wait operation if the machine has been
361 * PoweredDown/Saved/Aborted. */
362 if (machineState < MachineState_Running && !m_fIgnorePowerOffEvents)
363 {
364 g_fTerminateFE = true;
365 gEventQ->interruptEventQueueProcessing();
366 }
367
368 break;
369 }
370 case VBoxEventType_OnVRDEServerInfoChanged:
371 {
372 ComPtr<IVRDEServerInfoChangedEvent> rdicev = aEvent;
373 Assert(rdicev);
374
375 if (gConsole)
376 {
377 ComPtr<IVRDEServerInfo> info;
378 gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
379 if (info)
380 {
381 LONG port;
382 info->COMGETTER(Port)(&port);
383 if (port != mLastVRDEPort)
384 {
385 if (port == -1)
386 RTPrintf("VRDE server is inactive.\n");
387 else if (port == 0)
388 RTPrintf("VRDE server failed to start.\n");
389 else
390 RTPrintf("VRDE server is listening on port %d.\n", port);
391
392 mLastVRDEPort = port;
393 }
394 }
395 }
396 break;
397 }
398 case VBoxEventType_OnCanShowWindow:
399 {
400 ComPtr<ICanShowWindowEvent> cswev = aEvent;
401 Assert(cswev);
402 cswev->AddVeto(NULL);
403 break;
404 }
405 case VBoxEventType_OnShowWindow:
406 {
407 ComPtr<IShowWindowEvent> swev = aEvent;
408 Assert(swev);
409 swev->COMSETTER(WinId)(0);
410 break;
411 }
412 default:
413 AssertFailed();
414 }
415 return S_OK;
416 }
417
418 void ignorePowerOffEvents(bool fIgnore)
419 {
420 m_fIgnorePowerOffEvents = fIgnore;
421 }
422
423private:
424
425 long mLastVRDEPort;
426 bool m_fIgnorePowerOffEvents;
427};
428
429typedef ListenerImpl<VirtualBoxClientEventListener> VirtualBoxClientEventListenerImpl;
430typedef ListenerImpl<VirtualBoxEventListener> VirtualBoxEventListenerImpl;
431typedef ListenerImpl<ConsoleEventListener> ConsoleEventListenerImpl;
432
433VBOX_LISTENER_DECLARE(VirtualBoxClientEventListenerImpl)
434VBOX_LISTENER_DECLARE(VirtualBoxEventListenerImpl)
435VBOX_LISTENER_DECLARE(ConsoleEventListenerImpl)
436
437#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
438static void SaveState(int sig)
439{
440 ComPtr <IProgress> progress = NULL;
441
442/** @todo Deal with nested signals, multithreaded signal dispatching (esp. on windows),
443 * and multiple signals (both SIGINT and SIGTERM in some order).
444 * Consider processing the signal request asynchronously since there are lots of things
445 * which aren't safe (like RTPrintf and printf IIRC) in a signal context. */
446
447 RTPrintf("Signal received, saving state.\n");
448
449 HRESULT rc = gConsole->SaveState(progress.asOutParam());
450 if (FAILED(rc))
451 {
452 RTPrintf("Error saving state! rc = 0x%x\n", rc);
453 return;
454 }
455 Assert(progress);
456 LONG cPercent = 0;
457
458 RTPrintf("0%%");
459 RTStrmFlush(g_pStdOut);
460 for (;;)
461 {
462 BOOL fCompleted = false;
463 rc = progress->COMGETTER(Completed)(&fCompleted);
464 if (FAILED(rc) || fCompleted)
465 break;
466 ULONG cPercentNow;
467 rc = progress->COMGETTER(Percent)(&cPercentNow);
468 if (FAILED(rc))
469 break;
470 if ((cPercentNow / 10) != (cPercent / 10))
471 {
472 cPercent = cPercentNow;
473 RTPrintf("...%d%%", cPercentNow);
474 RTStrmFlush(g_pStdOut);
475 }
476
477 /* wait */
478 rc = progress->WaitForCompletion(100);
479 }
480
481 HRESULT lrc;
482 rc = progress->COMGETTER(ResultCode)(&lrc);
483 if (FAILED(rc))
484 lrc = ~0;
485 if (!lrc)
486 {
487 RTPrintf(" -- Saved the state successfully.\n");
488 RTThreadYield();
489 }
490 else
491 RTPrintf("-- Error saving state, lrc=%d (%#x)\n", lrc, lrc);
492
493}
494#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
495
496////////////////////////////////////////////////////////////////////////////////
497
498static void show_usage()
499{
500 RTPrintf("Usage:\n"
501 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
502 " -v, -vrde, --vrde on|off|config Enable (default) or disable the VRDE\n"
503 " server or don't change the setting\n"
504 " -e, -vrdeproperty, --vrdeproperty <name=[value]> Set a VRDE property:\n"
505 " \"TCP/Ports\" - comma-separated list of ports\n"
506 " the VRDE server can bind to. Use a dash between\n"
507 " two port numbers to specify a range\n"
508 " \"TCP/Address\" - interface IP the VRDE server\n"
509 " will bind to\n"
510 " --settingspw <pw> Specify the settings password\n"
511 " --settingspwfile <file> Specify a file containing the settings password\n"
512#ifdef VBOX_WITH_VIDEO_REC
513 " -c, -capture, --capture Record the VM screen output to a file\n"
514 " -w, --width Frame width when recording\n"
515 " -h, --height Frame height when recording\n"
516 " -r, --bitrate Recording bit rate when recording\n"
517 " -f, --filename File name when recording. The codec used\n"
518 " will be chosen based on the file extension\n"
519#endif
520 "\n");
521}
522
523#ifdef VBOX_WITH_VIDEO_REC
524/**
525 * Parse the environment for variables which can influence the VIDEOREC settings.
526 * purely for backwards compatibility.
527 * @param pulFrameWidth may be updated with a desired frame width
528 * @param pulFrameHeight may be updated with a desired frame height
529 * @param pulBitRate may be updated with a desired bit rate
530 * @param ppszFileName may be updated with a desired file name
531 */
532static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameHeight,
533 unsigned long *pulBitRate, const char **ppszFileName)
534{
535 const char *pszEnvTemp;
536
537 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0)
538 {
539 errno = 0;
540 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
541 if (errno != 0)
542 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREWIDTH environment variable", 0);
543 else
544 *pulFrameWidth = ulFrameWidth;
545 }
546 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREHEIGHT")) != 0)
547 {
548 errno = 0;
549 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
550 if (errno != 0)
551 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREHEIGHT environment variable", 0);
552 else
553 *pulFrameHeight = ulFrameHeight;
554 }
555 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREBITRATE")) != 0)
556 {
557 errno = 0;
558 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
559 if (errno != 0)
560 LogError("VBoxHeadless: ERROR: invalid VBOX_CAPTUREBITRATE environment variable", 0);
561 else
562 *pulBitRate = ulBitRate;
563 }
564 if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREFILE")) != 0)
565 *ppszFileName = pszEnvTemp;
566}
567#endif /* VBOX_WITH_VIDEO_REC defined */
568
569static RTEXITCODE readPasswordFile(const char *pszFilename, com::Utf8Str *pPasswd)
570{
571 size_t cbFile;
572 char szPasswd[512];
573 int vrc = VINF_SUCCESS;
574 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
575 bool fStdIn = !strcmp(pszFilename, "stdin");
576 PRTSTREAM pStrm;
577 if (!fStdIn)
578 vrc = RTStrmOpen(pszFilename, "r", &pStrm);
579 else
580 pStrm = g_pStdIn;
581 if (RT_SUCCESS(vrc))
582 {
583 vrc = RTStrmReadEx(pStrm, szPasswd, sizeof(szPasswd)-1, &cbFile);
584 if (RT_SUCCESS(vrc))
585 {
586 if (cbFile >= sizeof(szPasswd)-1)
587 {
588 RTPrintf("Provided password in file '%s' is too long\n", pszFilename);
589 rcExit = RTEXITCODE_FAILURE;
590 }
591 else
592 {
593 unsigned i;
594 for (i = 0; i < cbFile && !RT_C_IS_CNTRL(szPasswd[i]); i++)
595 ;
596 szPasswd[i] = '\0';
597 *pPasswd = szPasswd;
598 }
599 }
600 else
601 {
602 RTPrintf("Cannot read password from file '%s': %Rrc\n", pszFilename, vrc);
603 rcExit = RTEXITCODE_FAILURE;
604 }
605 if (!fStdIn)
606 RTStrmClose(pStrm);
607 }
608 else
609 {
610 RTPrintf("Cannot open password file '%s' (%Rrc)\n", pszFilename, vrc);
611 rcExit = RTEXITCODE_FAILURE;
612 }
613
614 return rcExit;
615}
616
617static RTEXITCODE settingsPasswordFile(ComPtr<IVirtualBox> virtualBox, const char *pszFilename)
618{
619 com::Utf8Str passwd;
620 RTEXITCODE rcExit = readPasswordFile(pszFilename, &passwd);
621 if (rcExit == RTEXITCODE_SUCCESS)
622 {
623 int rc;
624 CHECK_ERROR(virtualBox, SetSettingsSecret(com::Bstr(passwd).raw()));
625 if (FAILED(rc))
626 rcExit = RTEXITCODE_FAILURE;
627 }
628
629 return rcExit;
630}
631
632#ifdef RT_OS_WINDOWS
633// Required for ATL
634static CComModule _Module;
635#endif
636
637/**
638 * Entry point.
639 */
640extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
641{
642 const char *vrdePort = NULL;
643 const char *vrdeAddress = NULL;
644 const char *vrdeEnabled = NULL;
645 unsigned cVRDEProperties = 0;
646 const char *aVRDEProperties[16];
647 unsigned fRawR0 = ~0U;
648 unsigned fRawR3 = ~0U;
649 unsigned fPATM = ~0U;
650 unsigned fCSAM = ~0U;
651#ifdef VBOX_WITH_VIDEO_REC
652 unsigned fVIDEOREC = 0;
653 unsigned long ulFrameWidth = 800;
654 unsigned long ulFrameHeight = 600;
655 unsigned long ulBitRate = 300000;
656 char pszMPEGFile[RTPATH_MAX];
657 const char *pszFileNameParam = "VBox-%d.vob";
658#endif /* VBOX_WITH_VIDEO_REC */
659
660 LogFlow (("VBoxHeadless STARTED.\n"));
661 RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
662 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
663 "All rights reserved.\n\n");
664
665#ifdef VBOX_WITH_VIDEO_REC
666 /* Parse the environment */
667 parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
668#endif
669
670 enum eHeadlessOptions
671 {
672 OPT_RAW_R0 = 0x100,
673 OPT_NO_RAW_R0,
674 OPT_RAW_R3,
675 OPT_NO_RAW_R3,
676 OPT_PATM,
677 OPT_NO_PATM,
678 OPT_CSAM,
679 OPT_NO_CSAM,
680 OPT_SETTINGSPW,
681 OPT_SETTINGSPW_FILE,
682 OPT_COMMENT
683 };
684
685 static const RTGETOPTDEF s_aOptions[] =
686 {
687 { "-startvm", 's', RTGETOPT_REQ_STRING },
688 { "--startvm", 's', RTGETOPT_REQ_STRING },
689 { "-vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
690 { "--vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
691 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
692 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
693 { "-vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
694 { "--vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
695 { "-vrde", 'v', RTGETOPT_REQ_STRING },
696 { "--vrde", 'v', RTGETOPT_REQ_STRING },
697 { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
698 { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
699 { "-rawr0", OPT_RAW_R0, 0 },
700 { "--rawr0", OPT_RAW_R0, 0 },
701 { "-norawr0", OPT_NO_RAW_R0, 0 },
702 { "--norawr0", OPT_NO_RAW_R0, 0 },
703 { "-rawr3", OPT_RAW_R3, 0 },
704 { "--rawr3", OPT_RAW_R3, 0 },
705 { "-norawr3", OPT_NO_RAW_R3, 0 },
706 { "--norawr3", OPT_NO_RAW_R3, 0 },
707 { "-patm", OPT_PATM, 0 },
708 { "--patm", OPT_PATM, 0 },
709 { "-nopatm", OPT_NO_PATM, 0 },
710 { "--nopatm", OPT_NO_PATM, 0 },
711 { "-csam", OPT_CSAM, 0 },
712 { "--csam", OPT_CSAM, 0 },
713 { "-nocsam", OPT_NO_CSAM, 0 },
714 { "--nocsam", OPT_NO_CSAM, 0 },
715 { "--settingspw", OPT_SETTINGSPW, RTGETOPT_REQ_STRING },
716 { "--settingspwfile", OPT_SETTINGSPW_FILE, RTGETOPT_REQ_STRING },
717#ifdef VBOX_WITH_VIDEO_REC
718 { "-capture", 'c', 0 },
719 { "--capture", 'c', 0 },
720 { "--width", 'w', RTGETOPT_REQ_UINT32 },
721 { "--height", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
722 { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
723 { "--filename", 'f', RTGETOPT_REQ_STRING },
724#endif /* VBOX_WITH_VIDEO_REC defined */
725 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
726 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
727 };
728
729 const char *pcszNameOrUUID = NULL;
730
731 // parse the command line
732 int ch;
733 const char *pcszSettingsPw = NULL;
734 const char *pcszSettingsPwFile = NULL;
735 RTGETOPTUNION ValueUnion;
736 RTGETOPTSTATE GetState;
737 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
738 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
739 {
740 switch(ch)
741 {
742 case 's':
743 pcszNameOrUUID = ValueUnion.psz;
744 break;
745 case 'p':
746 RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
747 vrdePort = ValueUnion.psz;
748 break;
749 case 'a':
750 RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
751 vrdeAddress = ValueUnion.psz;
752 break;
753 case 'v':
754 vrdeEnabled = ValueUnion.psz;
755 break;
756 case 'e':
757 if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
758 aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
759 else
760 RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
761 break;
762 case OPT_RAW_R0:
763 fRawR0 = true;
764 break;
765 case OPT_NO_RAW_R0:
766 fRawR0 = false;
767 break;
768 case OPT_RAW_R3:
769 fRawR3 = true;
770 break;
771 case OPT_NO_RAW_R3:
772 fRawR3 = false;
773 break;
774 case OPT_PATM:
775 fPATM = true;
776 break;
777 case OPT_NO_PATM:
778 fPATM = false;
779 break;
780 case OPT_CSAM:
781 fCSAM = true;
782 break;
783 case OPT_NO_CSAM:
784 fCSAM = false;
785 break;
786 case OPT_SETTINGSPW:
787 pcszSettingsPw = ValueUnion.psz;
788 break;
789 case OPT_SETTINGSPW_FILE:
790 pcszSettingsPwFile = ValueUnion.psz;
791 break;
792#ifdef VBOX_WITH_VIDEO_REC
793 case 'c':
794 fVIDEOREC = true;
795 break;
796 case 'w':
797 ulFrameWidth = ValueUnion.u32;
798 break;
799 case 'r':
800 ulBitRate = ValueUnion.u32;
801 break;
802 case 'f':
803 pszFileNameParam = ValueUnion.psz;
804 break;
805#endif /* VBOX_WITH_VIDEO_REC defined */
806 case 'h':
807#ifdef VBOX_WITH_VIDEO_REC
808 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
809 {
810 ulFrameHeight = ValueUnion.u32;
811 break;
812 }
813#endif
814 show_usage();
815 return 0;
816 case OPT_COMMENT:
817 /* nothing to do */
818 break;
819 case 'V':
820 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
821 return 0;
822 default:
823 ch = RTGetOptPrintError(ch, &ValueUnion);
824 show_usage();
825 return ch;
826 }
827 }
828
829#ifdef VBOX_WITH_VIDEO_REC
830 if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
831 {
832 LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
833 return 1;
834 }
835 if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
836 {
837 LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
838 return 1;
839 }
840 if (ulBitRate < 300000 || ulBitRate > 1000000)
841 {
842 LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
843 return 1;
844 }
845 /* Make sure we only have %d or %u (or none) in the file name specified */
846 char *pcPercent = (char*)strchr(pszFileNameParam, '%');
847 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
848 {
849 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
850 return 1;
851 }
852 /* And no more than one % in the name */
853 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
854 {
855 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
856 return 1;
857 }
858 RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
859#endif /* defined VBOX_WITH_VIDEO_REC */
860
861 if (!pcszNameOrUUID)
862 {
863 show_usage();
864 return 1;
865 }
866
867 HRESULT rc;
868
869 rc = com::Initialize();
870#ifdef VBOX_WITH_XPCOM
871 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
872 {
873 char szHome[RTPATH_MAX] = "";
874 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
875 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
876 return 1;
877 }
878#endif
879 if (FAILED(rc))
880 {
881 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
882 return 1;
883 }
884
885 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
886 ComPtr<IVirtualBox> virtualBox;
887 ComPtr<ISession> session;
888 ComPtr<IMachine> machine;
889 bool fSessionOpened = false;
890 ComPtr<IEventListener> vboxClientListener;
891 ComPtr<IEventListener> vboxListener;
892 ComObjPtr<ConsoleEventListenerImpl> consoleListener;
893
894 do
895 {
896 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
897 if (FAILED(rc))
898 {
899 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBoxClient object!\n");
900 com::ErrorInfo info;
901 if (!info.isFullAvailable() && !info.isBasicAvailable())
902 {
903 com::GluePrintRCMessage(rc);
904 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
905 }
906 else
907 GluePrintErrorInfo(info);
908 break;
909 }
910
911 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
912 if (FAILED(rc))
913 {
914 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
915 break;
916 }
917 rc = pVirtualBoxClient->COMGETTER(Session)(session.asOutParam());
918 if (FAILED(rc))
919 {
920 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
921 break;
922 }
923
924 if (pcszSettingsPw)
925 {
926 CHECK_ERROR(virtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
927 if (FAILED(rc))
928 break;
929 }
930 else if (pcszSettingsPwFile)
931 {
932 int rcExit = settingsPasswordFile(virtualBox, pcszSettingsPwFile);
933 if (rcExit != RTEXITCODE_SUCCESS)
934 break;
935 }
936
937 ComPtr<IMachine> m;
938
939 rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
940 if (FAILED(rc))
941 {
942 LogError("Invalid machine name or UUID!\n", rc);
943 break;
944 }
945 Bstr id;
946 m->COMGETTER(Id)(id.asOutParam());
947 AssertComRC(rc);
948 if (FAILED(rc))
949 break;
950
951 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
952 Utf8Str(id).c_str()));
953
954 // open a session
955 CHECK_ERROR_BREAK(m, LockMachine(session, LockType_VM));
956 fSessionOpened = true;
957
958 /* get the console */
959 ComPtr<IConsole> console;
960 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
961
962 /* get the mutable machine */
963 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
964
965 ComPtr<IDisplay> display;
966 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
967
968#ifdef VBOX_WITH_VIDEO_REC
969 IFramebuffer *pFramebuffer = 0;
970 RTLDRMOD hLdrVideoRecFB;
971 PFNREGISTERVIDEORECFB pfnRegisterVideoRecFB;
972
973 if (fVIDEOREC)
974 {
975 HRESULT rcc = S_OK;
976 int rrc = VINF_SUCCESS;
977 RTERRINFOSTATIC ErrInfo;
978
979 Log2(("VBoxHeadless: loading VBoxVideoRecFB and libvpx shared library\n"));
980 RTErrInfoInitStatic(&ErrInfo);
981 rrc = SUPR3HardenedLdrLoadAppPriv("VBoxVideoRecFB", &hLdrVideoRecFB, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
982
983 if (RT_SUCCESS(rrc))
984 {
985 Log2(("VBoxHeadless: looking up symbol VBoxRegisterVideoRecFB\n"));
986 rrc = RTLdrGetSymbol(hLdrVideoRecFB, "VBoxRegisterVideoRecFB",
987 reinterpret_cast<void **>(&pfnRegisterVideoRecFB));
988 if (RT_FAILURE(rrc))
989 LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
990 }
991 else
992 LogError("Failed to load the video capture extension\n", rrc); /** @todo stupid function, no formatting options. */
993 if (RT_SUCCESS(rrc))
994 {
995 Log2(("VBoxHeadless: calling pfnRegisterVideoRecFB\n"));
996 rcc = pfnRegisterVideoRecFB(ulFrameWidth, ulFrameHeight, ulBitRate,
997 pszMPEGFile, &pFramebuffer);
998 if (rcc != S_OK)
999 LogError("Failed to initialise video capturing - make sure that the file format\n"
1000 "you wish to use is supported on your system\n", rcc);
1001 }
1002 if (RT_SUCCESS(rrc) && rcc == S_OK)
1003 {
1004 Log2(("VBoxHeadless: Registering framebuffer\n"));
1005 pFramebuffer->AddRef();
1006 display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
1007 }
1008 if (!RT_SUCCESS(rrc) || rcc != S_OK)
1009 rc = E_FAIL;
1010 }
1011 if (rc != S_OK)
1012 {
1013 break;
1014 }
1015#endif /* defined(VBOX_WITH_VIDEO_REC) */
1016 ULONG cMonitors = 1;
1017 machine->COMGETTER(MonitorCount)(&cMonitors);
1018
1019 unsigned uScreenId;
1020 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1021 {
1022# ifdef VBOX_WITH_VIDEO_REC
1023 if (fVIDEOREC && uScreenId == 0)
1024 {
1025 /* Already registered. */
1026 continue;
1027 }
1028# endif
1029 VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer();
1030 if (!pVRDPFramebuffer)
1031 {
1032 RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
1033 break;
1034 }
1035 pVRDPFramebuffer->AddRef();
1036 display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
1037 }
1038 if (uScreenId < cMonitors)
1039 {
1040 break;
1041 }
1042
1043 // fill in remaining slots with null framebuffers
1044 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1045 {
1046 ComPtr<IFramebuffer> fb;
1047 LONG xOrigin, yOrigin;
1048 HRESULT hrc2 = display->GetFramebuffer(uScreenId,
1049 fb.asOutParam(),
1050 &xOrigin, &yOrigin);
1051 if (hrc2 == S_OK && fb.isNull())
1052 {
1053 NullFB *pNullFB = new NullFB();
1054 pNullFB->AddRef();
1055 pNullFB->init();
1056 display->SetFramebuffer(uScreenId, pNullFB);
1057 }
1058 }
1059
1060 /* get the machine debugger (isn't necessarily available) */
1061 ComPtr <IMachineDebugger> machineDebugger;
1062 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
1063 if (machineDebugger)
1064 {
1065 Log(("Machine debugger available!\n"));
1066 }
1067
1068 if (fRawR0 != ~0U)
1069 {
1070 if (!machineDebugger)
1071 {
1072 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
1073 break;
1074 }
1075 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
1076 }
1077 if (fRawR3 != ~0U)
1078 {
1079 if (!machineDebugger)
1080 {
1081 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
1082 break;
1083 }
1084 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1085 }
1086 if (fPATM != ~0U)
1087 {
1088 if (!machineDebugger)
1089 {
1090 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
1091 break;
1092 }
1093 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1094 }
1095 if (fCSAM != ~0U)
1096 {
1097 if (!machineDebugger)
1098 {
1099 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
1100 break;
1101 }
1102 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1103 }
1104
1105 /* initialize global references */
1106 gConsole = console;
1107 gEventQ = com::NativeEventQueue::getMainEventQueue();
1108
1109 /* VirtualBoxClient events registration. */
1110 {
1111 ComPtr<IEventSource> pES;
1112 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1113 ComObjPtr<VirtualBoxClientEventListenerImpl> listener;
1114 listener.createObject();
1115 listener->init(new VirtualBoxClientEventListener());
1116 vboxClientListener = listener;
1117 com::SafeArray<VBoxEventType_T> eventTypes;
1118 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
1119 CHECK_ERROR(pES, RegisterListener(vboxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1120 }
1121
1122 /* Console events registration. */
1123 {
1124 ComPtr<IEventSource> es;
1125 CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
1126 consoleListener.createObject();
1127 consoleListener->init(new ConsoleEventListener());
1128 com::SafeArray<VBoxEventType_T> eventTypes;
1129 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1130 eventTypes.push_back(VBoxEventType_OnStateChanged);
1131 eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
1132 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1133 eventTypes.push_back(VBoxEventType_OnShowWindow);
1134 CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
1135 }
1136
1137 /* default is to enable the remote desktop server (backward compatibility) */
1138 BOOL fVRDEEnable = true;
1139 BOOL fVRDEEnabled;
1140 ComPtr <IVRDEServer> vrdeServer;
1141 CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
1142 CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));
1143
1144 if (vrdeEnabled != NULL)
1145 {
1146 /* -vrdeServer on|off|config */
1147 if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
1148 fVRDEEnable = false;
1149 else if (!strcmp(vrdeEnabled, "config"))
1150 {
1151 if (!fVRDEEnabled)
1152 fVRDEEnable = false;
1153 }
1154 else if (strcmp(vrdeEnabled, "on") && strcmp(vrdeEnabled, "enable"))
1155 {
1156 RTPrintf("-vrdeServer requires an argument (on|off|config)\n");
1157 break;
1158 }
1159 }
1160
1161 if (fVRDEEnable)
1162 {
1163 Log(("VBoxHeadless: Enabling VRDE server...\n"));
1164
1165 /* set VRDE port if requested by the user */
1166 if (vrdePort != NULL)
1167 {
1168 Bstr bstr = vrdePort;
1169 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
1170 }
1171 /* set VRDE address if requested by the user */
1172 if (vrdeAddress != NULL)
1173 {
1174 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
1175 }
1176
1177 /* Set VRDE properties. */
1178 if (cVRDEProperties > 0)
1179 {
1180 for (unsigned i = 0; i < cVRDEProperties; i++)
1181 {
1182 /* Parse 'name=value' */
1183 char *pszProperty = RTStrDup(aVRDEProperties[i]);
1184 if (pszProperty)
1185 {
1186 char *pDelimiter = strchr(pszProperty, '=');
1187 if (pDelimiter)
1188 {
1189 *pDelimiter = '\0';
1190
1191 Bstr bstrName = pszProperty;
1192 Bstr bstrValue = &pDelimiter[1];
1193 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1194 }
1195 else
1196 {
1197 RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
1198 RTStrFree(pszProperty);
1199 rc = E_INVALIDARG;
1200 break;
1201 }
1202 RTStrFree(pszProperty);
1203 }
1204 else
1205 {
1206 RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
1207 rc = E_OUTOFMEMORY;
1208 break;
1209 }
1210 }
1211 if (FAILED(rc))
1212 break;
1213 }
1214
1215 /* enable VRDE server (only if currently disabled) */
1216 if (!fVRDEEnabled)
1217 {
1218 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
1219 }
1220 }
1221 else
1222 {
1223 /* disable VRDE server (only if currently enabled */
1224 if (fVRDEEnabled)
1225 {
1226 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
1227 }
1228 }
1229
1230 /* Disable the host clipboard before powering up */
1231 console->COMSETTER(UseHostClipboard)(false);
1232
1233 Log(("VBoxHeadless: Powering up the machine...\n"));
1234
1235 ComPtr <IProgress> progress;
1236 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1237
1238 /*
1239 * Wait for the result because there can be errors.
1240 *
1241 * It's vital to process events while waiting (teleportation deadlocks),
1242 * so we'll poll for the completion instead of waiting on it.
1243 */
1244 for (;;)
1245 {
1246 BOOL fCompleted;
1247 rc = progress->COMGETTER(Completed)(&fCompleted);
1248 if (FAILED(rc) || fCompleted)
1249 break;
1250
1251 /* Process pending events, then wait for new ones. Note, this
1252 * processes NULL events signalling event loop termination. */
1253 gEventQ->processEventQueue(0);
1254 if (!g_fTerminateFE)
1255 gEventQ->processEventQueue(500);
1256 }
1257
1258 if (SUCCEEDED(progress->WaitForCompletion(-1)))
1259 {
1260 /* Figure out if the operation completed with a failed status
1261 * and print the error message. Terminate immediately, and let
1262 * the cleanup code take care of potentially pending events. */
1263 LONG progressRc;
1264 progress->COMGETTER(ResultCode)(&progressRc);
1265 rc = progressRc;
1266 if (FAILED(rc))
1267 {
1268 com::ProgressErrorInfo info(progress);
1269 if (info.isBasicAvailable())
1270 {
1271 RTPrintf("Error: failed to start machine. Error message: %ls\n", info.getText().raw());
1272 }
1273 else
1274 {
1275 RTPrintf("Error: failed to start machine. No error message available!\n");
1276 }
1277 break;
1278 }
1279 }
1280
1281 /* VirtualBox events registration. */
1282 {
1283 ComPtr<IEventSource> es;
1284 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1285 ComObjPtr<VirtualBoxEventListenerImpl> listener;
1286 listener.createObject();
1287 listener->init(new VirtualBoxEventListener());
1288 vboxListener = listener;
1289 com::SafeArray<VBoxEventType_T> eventTypes;
1290 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
1291
1292 /**
1293 * @todo Set the notification pattern to "/VirtualBox/GuestInfo/OS/ *Logged*"
1294 * to not cause too much load. The current API is broken as
1295 * IMachine::GuestPropertyNotificationPatterns() would change the
1296 * filter for _all_ clients. This is not what we want!
1297 */
1298 CHECK_ERROR(es, RegisterListener(vboxListener, ComSafeArrayAsInParam(eventTypes), true));
1299 }
1300
1301#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1302 signal(SIGINT, SaveState);
1303 signal(SIGTERM, SaveState);
1304#endif
1305
1306 Log(("VBoxHeadless: Waiting for PowerDown...\n"));
1307
1308 while ( !g_fTerminateFE
1309 && RT_SUCCESS(gEventQ->processEventQueue(RT_INDEFINITE_WAIT)))
1310 /* nothing */ ;
1311
1312 Log(("VBoxHeadless: event loop has terminated...\n"));
1313
1314#ifdef VBOX_WITH_VIDEO_REC
1315 if (pFramebuffer)
1316 {
1317 pFramebuffer->Release();
1318 Log(("Released framebuffer\n"));
1319 pFramebuffer = NULL;
1320 }
1321#endif /* defined(VBOX_WITH_VIDEO_REC) */
1322
1323 /* we don't have to disable VRDE here because we don't save the settings of the VM */
1324 }
1325 while (0);
1326
1327 /*
1328 * Get the machine state.
1329 */
1330 MachineState_T machineState = MachineState_Aborted;
1331 if (!machine.isNull())
1332 machine->COMGETTER(State)(&machineState);
1333
1334 /*
1335 * Turn off the VM if it's running
1336 */
1337 if ( gConsole
1338 && ( machineState == MachineState_Running
1339 || machineState == MachineState_Teleporting
1340 || machineState == MachineState_LiveSnapshotting
1341 /** @todo power off paused VMs too? */
1342 )
1343 )
1344 do
1345 {
1346 consoleListener->getWrapped()->ignorePowerOffEvents(true);
1347 ComPtr<IProgress> pProgress;
1348 CHECK_ERROR_BREAK(gConsole, PowerDown(pProgress.asOutParam()));
1349 CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
1350 BOOL completed;
1351 CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
1352 ASSERT(completed);
1353 LONG hrc;
1354 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc));
1355 if (FAILED(hrc))
1356 {
1357 RTPrintf("VBoxHeadless: ERROR: Failed to power down VM!");
1358 com::ErrorInfo info;
1359 if (!info.isFullAvailable() && !info.isBasicAvailable())
1360 com::GluePrintRCMessage(hrc);
1361 else
1362 GluePrintErrorInfo(info);
1363 break;
1364 }
1365 } while (0);
1366
1367 /* VirtualBox callback unregistration. */
1368 if (vboxListener)
1369 {
1370 ComPtr<IEventSource> es;
1371 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1372 if (!es.isNull())
1373 CHECK_ERROR(es, UnregisterListener(vboxListener));
1374 vboxListener.setNull();
1375 }
1376
1377 /* Console callback unregistration. */
1378 if (consoleListener)
1379 {
1380 ComPtr<IEventSource> es;
1381 CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
1382 if (!es.isNull())
1383 CHECK_ERROR(es, UnregisterListener(consoleListener));
1384 consoleListener.setNull();
1385 }
1386
1387 /* VirtualBoxClient callback unregistration. */
1388 if (vboxClientListener)
1389 {
1390 ComPtr<IEventSource> pES;
1391 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1392 if (!pES.isNull())
1393 CHECK_ERROR(pES, UnregisterListener(vboxClientListener));
1394 vboxClientListener.setNull();
1395 }
1396
1397 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1398 gConsole = NULL;
1399
1400 if (fSessionOpened)
1401 {
1402 /*
1403 * Close the session. This will also uninitialize the console and
1404 * unregister the callback we've registered before.
1405 */
1406 Log(("VBoxHeadless: Closing the session...\n"));
1407 session->UnlockMachine();
1408 }
1409
1410 /* Must be before com::Shutdown */
1411 session.setNull();
1412 virtualBox.setNull();
1413 pVirtualBoxClient.setNull();
1414 machine.setNull();
1415
1416 com::Shutdown();
1417
1418 LogFlow(("VBoxHeadless FINISHED.\n"));
1419
1420 return FAILED(rc) ? 1 : 0;
1421}
1422
1423
1424#ifndef VBOX_WITH_HARDENING
1425/**
1426 * Main entry point.
1427 */
1428int main(int argc, char **argv, char **envp)
1429{
1430 // initialize VBox Runtime
1431 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
1432 if (RT_FAILURE(rc))
1433 {
1434 RTPrintf("VBoxHeadless: Runtime Error:\n"
1435 " %Rrc -- %Rrf\n", rc, rc);
1436 switch (rc)
1437 {
1438 case VERR_VM_DRIVER_NOT_INSTALLED:
1439 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1440 "loaded successfully. Aborting ...\n");
1441 break;
1442 default:
1443 break;
1444 }
1445 return 1;
1446 }
1447
1448 return TrustedMain(argc, argv, envp);
1449}
1450#endif /* !VBOX_WITH_HARDENING */
1451
1452#ifdef VBOX_WITH_XPCOM
1453NS_DECL_CLASSINFO(NullFB)
1454NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NullFB, IFramebuffer)
1455#endif
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