VirtualBox

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

Last change on this file since 45268 was 44158, checked in by vboxsync, 12 years ago

Fe/VBoxHeadless: Reduced guest property notification handling.

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