VirtualBox

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

Last change on this file since 85416 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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