VirtualBox

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

Last change on this file since 89697 was 89222, checked in by vboxsync, 4 years ago

FE/VBoxHeadless: Give the VM name in the shutdown block reason and
make the message more succint and less scary ("saving" instead of
technically correct but less informative "terminating"). bugref:8161.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.4 KB
Line 
1/* $Id: VBoxHeadless.cpp 89222 2021-05-21 12:50:09Z 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/message.h>
39#include <iprt/semaphore.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/ldr.h>
43#include <iprt/getopt.h>
44#include <iprt/env.h>
45#include <VBox/err.h>
46#include <VBoxVideo.h>
47
48#ifdef VBOX_WITH_RECORDING
49# include <cstdlib>
50# include <cerrno>
51# include <iprt/process.h>
52#endif
53
54#ifdef RT_OS_DARWIN
55# include <iprt/asm.h>
56# include <dlfcn.h>
57# include <sys/mman.h>
58#endif
59
60//#define VBOX_WITH_SAVESTATE_ON_SIGNAL
61#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
62#include <signal.h>
63static void HandleSignal(int sig);
64#endif
65
66#include "PasswordInput.h"
67
68////////////////////////////////////////////////////////////////////////////////
69
70#define LogError(m,rc) \
71 do { \
72 Log(("VBoxHeadless: ERROR: " m " [rc=0x%08X]\n", rc)); \
73 RTPrintf("%s\n", m); \
74 } while (0)
75
76////////////////////////////////////////////////////////////////////////////////
77
78/* global weak references (for event handlers) */
79static IConsole *gConsole = NULL;
80static NativeEventQueue *gEventQ = NULL;
81
82/* keep this handy for messages */
83static com::Utf8Str g_strVMName;
84static com::Utf8Str g_strVMUUID;
85
86/* flag whether frontend should terminate */
87static volatile bool g_fTerminateFE = false;
88
89////////////////////////////////////////////////////////////////////////////////
90
91/**
92 * Handler for VirtualBoxClient events.
93 */
94class VirtualBoxClientEventListener
95{
96public:
97 VirtualBoxClientEventListener()
98 {
99 }
100
101 virtual ~VirtualBoxClientEventListener()
102 {
103 }
104
105 HRESULT init()
106 {
107 return S_OK;
108 }
109
110 void uninit()
111 {
112 }
113
114 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
115 {
116 switch (aType)
117 {
118 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
119 {
120 ComPtr<IVBoxSVCAvailabilityChangedEvent> pVSACEv = aEvent;
121 Assert(pVSACEv);
122 BOOL fAvailable = FALSE;
123 pVSACEv->COMGETTER(Available)(&fAvailable);
124 if (!fAvailable)
125 {
126 LogRel(("VBoxHeadless: VBoxSVC became unavailable, exiting.\n"));
127 RTPrintf("VBoxSVC became unavailable, exiting.\n");
128 /* Terminate the VM as cleanly as possible given that VBoxSVC
129 * is no longer present. */
130 g_fTerminateFE = true;
131 gEventQ->interruptEventQueueProcessing();
132 }
133 break;
134 }
135 default:
136 AssertFailed();
137 }
138
139 return S_OK;
140 }
141
142private:
143};
144
145/**
146 * Handler for machine events.
147 */
148class ConsoleEventListener
149{
150public:
151 ConsoleEventListener() :
152 mLastVRDEPort(-1),
153 m_fIgnorePowerOffEvents(false),
154 m_fNoLoggedInUsers(true)
155 {
156 }
157
158 virtual ~ConsoleEventListener()
159 {
160 }
161
162 HRESULT init()
163 {
164 return S_OK;
165 }
166
167 void uninit()
168 {
169 }
170
171 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
172 {
173 switch (aType)
174 {
175 case VBoxEventType_OnMouseCapabilityChanged:
176 {
177
178 ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
179 Assert(!mccev.isNull());
180
181 BOOL fSupportsAbsolute = false;
182 mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute);
183
184 /* Emit absolute mouse event to actually enable the host mouse cursor. */
185 if (fSupportsAbsolute && gConsole)
186 {
187 ComPtr<IMouse> mouse;
188 gConsole->COMGETTER(Mouse)(mouse.asOutParam());
189 if (mouse)
190 {
191 mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
192 }
193 }
194 break;
195 }
196 case VBoxEventType_OnStateChanged:
197 {
198 ComPtr<IStateChangedEvent> scev = aEvent;
199 Assert(scev);
200
201 MachineState_T machineState;
202 scev->COMGETTER(State)(&machineState);
203
204 /* Terminate any event wait operation if the machine has been
205 * PoweredDown/Saved/Aborted. */
206 if (machineState < MachineState_Running && !m_fIgnorePowerOffEvents)
207 {
208 g_fTerminateFE = true;
209 gEventQ->interruptEventQueueProcessing();
210 }
211
212 break;
213 }
214 case VBoxEventType_OnVRDEServerInfoChanged:
215 {
216 ComPtr<IVRDEServerInfoChangedEvent> rdicev = aEvent;
217 Assert(rdicev);
218
219 if (gConsole)
220 {
221 ComPtr<IVRDEServerInfo> info;
222 gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
223 if (info)
224 {
225 LONG port;
226 info->COMGETTER(Port)(&port);
227 if (port != mLastVRDEPort)
228 {
229 if (port == -1)
230 RTPrintf("VRDE server is inactive.\n");
231 else if (port == 0)
232 RTPrintf("VRDE server failed to start.\n");
233 else
234 RTPrintf("VRDE server is listening on port %d.\n", port);
235
236 mLastVRDEPort = port;
237 }
238 }
239 }
240 break;
241 }
242 case VBoxEventType_OnCanShowWindow:
243 {
244 ComPtr<ICanShowWindowEvent> cswev = aEvent;
245 Assert(cswev);
246 cswev->AddVeto(NULL);
247 break;
248 }
249 case VBoxEventType_OnShowWindow:
250 {
251 ComPtr<IShowWindowEvent> swev = aEvent;
252 Assert(swev);
253 /* Ignore the event, WinId is either still zero or some other listener assigned it. */
254 NOREF(swev); /* swev->COMSETTER(WinId)(0); */
255 break;
256 }
257 case VBoxEventType_OnGuestPropertyChanged:
258 {
259 ComPtr<IGuestPropertyChangedEvent> pChangedEvent = aEvent;
260 Assert(pChangedEvent);
261
262 HRESULT hrc;
263
264 ComPtr <IMachine> pMachine;
265 if (gConsole)
266 {
267 hrc = gConsole->COMGETTER(Machine)(pMachine.asOutParam());
268 if (FAILED(hrc) || !pMachine)
269 hrc = VBOX_E_OBJECT_NOT_FOUND;
270 }
271 else
272 hrc = VBOX_E_INVALID_VM_STATE;
273
274 if (SUCCEEDED(hrc))
275 {
276 Bstr strKey;
277 hrc = pChangedEvent->COMGETTER(Name)(strKey.asOutParam());
278 AssertComRC(hrc);
279
280 Bstr strValue;
281 hrc = pChangedEvent->COMGETTER(Value)(strValue.asOutParam());
282 AssertComRC(hrc);
283
284 Utf8Str utf8Key = strKey;
285 Utf8Str utf8Value = strValue;
286 LogRelFlow(("Guest property \"%s\" has been changed to \"%s\"\n",
287 utf8Key.c_str(), utf8Value.c_str()));
288
289 if (utf8Key.equals("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
290 {
291 LogRelFlow(("Guest indicates that there %s logged in users\n",
292 utf8Value.equals("true") ? "are no" : "are"));
293
294 /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
295 BOOL fProcessDisconnectOnGuestLogout = FALSE;
296
297 /* Does the machine handle VRDP disconnects? */
298 Bstr strDiscon;
299 hrc = pMachine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
300 strDiscon.asOutParam());
301 if (SUCCEEDED(hrc))
302 {
303 Utf8Str utf8Discon = strDiscon;
304 fProcessDisconnectOnGuestLogout = utf8Discon.equals("1")
305 ? TRUE : FALSE;
306 }
307
308 LogRelFlow(("VRDE: hrc=%Rhrc: Host %s disconnecting clients (current host state known: %s)\n",
309 hrc, fProcessDisconnectOnGuestLogout ? "will handle" : "does not handle",
310 m_fNoLoggedInUsers ? "No users logged in" : "Users logged in"));
311
312 if (fProcessDisconnectOnGuestLogout)
313 {
314 bool fDropConnection = false;
315 if (!m_fNoLoggedInUsers) /* Only if the property really changes. */
316 {
317 if ( utf8Value == "true"
318 /* Guest property got deleted due to reset,
319 * so it has no value anymore. */
320 || utf8Value.isEmpty())
321 {
322 m_fNoLoggedInUsers = true;
323 fDropConnection = true;
324 }
325 }
326 else if (utf8Value == "false")
327 m_fNoLoggedInUsers = false;
328 /* Guest property got deleted due to reset,
329 * take the shortcut without touching the m_fNoLoggedInUsers
330 * state. */
331 else if (utf8Value.isEmpty())
332 fDropConnection = true;
333
334 LogRelFlow(("VRDE: szNoLoggedInUsers=%s, m_fNoLoggedInUsers=%RTbool, fDropConnection=%RTbool\n",
335 utf8Value.c_str(), m_fNoLoggedInUsers, fDropConnection));
336
337 if (fDropConnection)
338 {
339 /* If there is a connection, drop it. */
340 ComPtr<IVRDEServerInfo> info;
341 hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
342 if (SUCCEEDED(hrc) && info)
343 {
344 ULONG cClients = 0;
345 hrc = info->COMGETTER(NumberOfClients)(&cClients);
346
347 LogRelFlow(("VRDE: connected clients=%RU32\n", cClients));
348 if (SUCCEEDED(hrc) && cClients > 0)
349 {
350 ComPtr <IVRDEServer> vrdeServer;
351 hrc = pMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
352 if (SUCCEEDED(hrc) && vrdeServer)
353 {
354 LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n"));
355 hrc = vrdeServer->COMSETTER(Enabled)(FALSE);
356 AssertComRC(hrc);
357 HRESULT hrc2 = vrdeServer->COMSETTER(Enabled)(TRUE);
358 if (SUCCEEDED(hrc))
359 hrc = hrc2;
360 }
361 }
362 }
363 }
364 }
365 }
366
367 if (FAILED(hrc))
368 LogRelFlow(("VRDE: returned error=%Rhrc\n", hrc));
369 }
370
371 break;
372 }
373
374 default:
375 AssertFailed();
376 }
377 return S_OK;
378 }
379
380 void ignorePowerOffEvents(bool fIgnore)
381 {
382 m_fIgnorePowerOffEvents = fIgnore;
383 }
384
385private:
386
387 long mLastVRDEPort;
388 bool m_fIgnorePowerOffEvents;
389 bool m_fNoLoggedInUsers;
390};
391
392typedef ListenerImpl<VirtualBoxClientEventListener> VirtualBoxClientEventListenerImpl;
393typedef ListenerImpl<ConsoleEventListener> ConsoleEventListenerImpl;
394
395VBOX_LISTENER_DECLARE(VirtualBoxClientEventListenerImpl)
396VBOX_LISTENER_DECLARE(ConsoleEventListenerImpl)
397
398#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
399static void
400HandleSignal(int sig)
401{
402 RT_NOREF(sig);
403 LogRel(("VBoxHeadless: received singal %d\n", sig));
404 g_fTerminateFE = true;
405}
406#endif /* VBOX_WITH_SAVESTATE_ON_SIGNAL */
407
408////////////////////////////////////////////////////////////////////////////////
409
410static void show_usage()
411{
412 RTPrintf("Usage:\n"
413 " -s, -startvm, --startvm <name|uuid> Start given VM (required argument)\n"
414 " -v, -vrde, --vrde on|off|config Enable or disable the VRDE server\n"
415 " or don't change the setting (default)\n"
416 " -e, -vrdeproperty, --vrdeproperty <name=[value]> Set a VRDE property:\n"
417 " \"TCP/Ports\" - comma-separated list of\n"
418 " ports the VRDE server can bind to; dash\n"
419 " between two port numbers specifies range\n"
420 " \"TCP/Address\" - interface IP the VRDE\n"
421 " server will bind to\n"
422 " --settingspw <pw> Specify the settings password\n"
423 " --settingspwfile <file> Specify a file containing the\n"
424 " settings password\n"
425 " -start-paused, --start-paused Start the VM in paused state\n"
426#ifdef VBOX_WITH_RECORDING
427 " -c, -record, --record Record the VM screen output to a file\n"
428 " -w, --videowidth Video frame width when recording\n"
429 " -h, --videoheight Video frame height when recording\n"
430 " -r, --videobitrate Recording bit rate when recording\n"
431 " -f, --filename File name when recording. The codec used\n"
432 " will be chosen based on file extension\n"
433#endif
434 "\n");
435}
436
437#ifdef VBOX_WITH_RECORDING
438/**
439 * Parse the environment for variables which can influence the VIDEOREC settings.
440 * purely for backwards compatibility.
441 * @param pulFrameWidth may be updated with a desired frame width
442 * @param pulFrameHeight may be updated with a desired frame height
443 * @param pulBitRate may be updated with a desired bit rate
444 * @param ppszFilename may be updated with a desired file name
445 */
446static void parse_environ(uint32_t *pulFrameWidth, uint32_t *pulFrameHeight,
447 uint32_t *pulBitRate, const char **ppszFilename)
448{
449 const char *pszEnvTemp;
450/** @todo r=bird: This isn't up to scratch. The life time of an RTEnvGet
451 * return value is only up to the next RTEnv*, *getenv, *putenv,
452 * setenv call in _any_ process in the system and the it has known and
453 * documented code page issues.
454 *
455 * Use RTEnvGetEx instead! */
456 if ((pszEnvTemp = RTEnvGet("VBOX_RECORDWIDTH")) != 0)
457 {
458 errno = 0;
459 unsigned long ulFrameWidth = strtoul(pszEnvTemp, 0, 10);
460 if (errno != 0)
461 LogError("VBoxHeadless: ERROR: invalid VBOX_RECORDWIDTH environment variable", 0);
462 else
463 *pulFrameWidth = ulFrameWidth;
464 }
465 if ((pszEnvTemp = RTEnvGet("VBOX_RECORDHEIGHT")) != 0)
466 {
467 errno = 0;
468 unsigned long ulFrameHeight = strtoul(pszEnvTemp, 0, 10);
469 if (errno != 0)
470 LogError("VBoxHeadless: ERROR: invalid VBOX_RECORDHEIGHT environment variable", 0);
471 else
472 *pulFrameHeight = ulFrameHeight;
473 }
474 if ((pszEnvTemp = RTEnvGet("VBOX_RECORDBITRATE")) != 0)
475 {
476 errno = 0;
477 unsigned long ulBitRate = strtoul(pszEnvTemp, 0, 10);
478 if (errno != 0)
479 LogError("VBoxHeadless: ERROR: invalid VBOX_RECORDBITRATE environment variable", 0);
480 else
481 *pulBitRate = ulBitRate;
482 }
483 if ((pszEnvTemp = RTEnvGet("VBOX_RECORDFILE")) != 0)
484 *ppszFilename = pszEnvTemp;
485}
486#endif /* VBOX_WITH_RECORDING defined */
487
488#ifdef RT_OS_DARWIN
489
490# include <unistd.h>
491# include <stdio.h>
492# include <dlfcn.h>
493# include <iprt/formats/mach-o.h>
494
495/**
496 * Override this one to try hide the fact that we're setuid to root
497 * orginially.
498 */
499int issetugid_for_AppKit(void)
500{
501 Dl_info Info = {0};
502 char szMsg[512];
503 size_t cchMsg;
504 const void * uCaller = __builtin_return_address(0);
505 if (dladdr(uCaller, &Info))
506 cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p %s::%s+%p (via %p)\n",
507 uCaller, Info.dli_fname, Info.dli_sname, (void *)((uintptr_t)uCaller - (uintptr_t)Info.dli_saddr), __builtin_return_address(1));
508 else
509 cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p (via %p)\n", uCaller, __builtin_return_address(1));
510 write(2, szMsg, cchMsg);
511 return 0;
512}
513
514static bool patchExtSym(mach_header_64_t *pHdr, const char *pszSymbol, uintptr_t uNewValue)
515{
516 /*
517 * First do some basic header checks and the scan the load
518 * commands for the symbol table info.
519 */
520 AssertLogRelMsgReturn(pHdr->magic == (ARCH_BITS == 64 ? MH_MAGIC_64 : MH_MAGIC),
521 ("%p: magic=%#x\n", pHdr, pHdr->magic), false);
522 uint32_t const cCmds = pHdr->ncmds;
523 uint32_t const cbCmds = pHdr->sizeofcmds;
524 AssertLogRelMsgReturn(cCmds < 16384 && cbCmds < _2M, ("%p: ncmds=%u sizeofcmds=%u\n", pHdr, cCmds, cbCmds), false);
525
526 /*
527 * First command pass: Locate the symbol table and dynamic symbol table info
528 * commands, also calc the slide (load addr - link addr).
529 */
530 dysymtab_command_t const *pDySymTab = NULL;
531 symtab_command_t const *pSymTab = NULL;
532 segment_command_64_t const *pFirstSeg = NULL;
533 uintptr_t offSlide = 0;
534 uint32_t offCmd = 0;
535 for (uint32_t iCmd = 0; iCmd < cCmds; iCmd++)
536 {
537 AssertLogRelMsgReturn(offCmd + sizeof(load_command_t) <= cbCmds,
538 ("%p: iCmd=%u offCmd=%#x cbCmds=%#x\n", pHdr, iCmd, offCmd, cbCmds), false);
539 load_command_t const * const pCmd = (load_command_t const *)((uintptr_t)(pHdr + 1) + offCmd);
540 uint32_t const cbCurCmd = pCmd->cmdsize;
541 AssertLogRelMsgReturn(offCmd + cbCurCmd <= cbCmds && cbCurCmd <= cbCmds,
542 ("%p: iCmd=%u offCmd=%#x cbCurCmd=%#x cbCmds=%#x\n", pHdr, iCmd, offCmd, cbCurCmd, cbCmds), false);
543 offCmd += cbCurCmd;
544
545 if (pCmd->cmd == LC_SYMTAB)
546 {
547 AssertLogRelMsgReturn(!pSymTab, ("%p: pSymTab=%p pCmd=%p\n", pHdr, pSymTab, pCmd), false);
548 pSymTab = (symtab_command_t const *)pCmd;
549 AssertLogRelMsgReturn(cbCurCmd == sizeof(*pSymTab), ("%p: pSymTab=%p cbCurCmd=%#x\n", pHdr, pCmd, cbCurCmd), false);
550
551 }
552 else if (pCmd->cmd == LC_DYSYMTAB)
553 {
554 AssertLogRelMsgReturn(!pDySymTab, ("%p: pDySymTab=%p pCmd=%p\n", pHdr, pDySymTab, pCmd), false);
555 pDySymTab = (dysymtab_command_t const *)pCmd;
556 AssertLogRelMsgReturn(cbCurCmd == sizeof(*pDySymTab), ("%p: pDySymTab=%p cbCurCmd=%#x\n", pHdr, pCmd, cbCurCmd),
557 false);
558 }
559 else if (pCmd->cmd == LC_SEGMENT_64 && !pFirstSeg) /* ASSUMES the first seg is the one with the header and stuff. */
560 {
561 /* Note! the fileoff and vmaddr seems to be modified. */
562 pFirstSeg = (segment_command_64_t const *)pCmd;
563 AssertLogRelMsgReturn(cbCurCmd >= sizeof(*pFirstSeg), ("%p: iCmd=%u cbCurCmd=%#x\n", pHdr, iCmd, cbCurCmd), false);
564 AssertLogRelMsgReturn(/*pFirstSeg->fileoff == 0 && */ pFirstSeg->vmsize >= sizeof(*pHdr) + cbCmds,
565 ("%p: iCmd=%u fileoff=%llx vmsize=%#llx cbCmds=%#x name=%.16s\n",
566 pHdr, iCmd, pFirstSeg->fileoff, pFirstSeg->vmsize, cbCmds, pFirstSeg->segname), false);
567 offSlide = (uintptr_t)pHdr - pFirstSeg->vmaddr;
568 }
569 }
570 AssertLogRelMsgReturn(pSymTab, ("%p: no LC_SYMTAB\n", pHdr), false);
571 AssertLogRelMsgReturn(pDySymTab, ("%p: no LC_DYSYMTAB\n", pHdr), false);
572 AssertLogRelMsgReturn(pFirstSeg, ("%p: no LC_SEGMENT_64\n", pHdr), false);
573
574 /*
575 * Second command pass: Locate the memory locations of the symbol table, string
576 * table and the indirect symbol table by checking LC_SEGMENT_xx.
577 */
578 macho_nlist_64_t const *paSymbols = NULL;
579 uint32_t const offSymbols = pSymTab->symoff;
580 uint32_t const cSymbols = pSymTab->nsyms;
581 AssertLogRelMsgReturn(cSymbols > 0 && offSymbols >= sizeof(pHdr) + cbCmds,
582 ("%p: cSymbols=%#x offSymbols=%#x\n", pHdr, cSymbols, offSymbols), false);
583
584 const char *pchStrTab = NULL;
585 uint32_t const offStrTab = pSymTab->stroff;
586 uint32_t const cbStrTab = pSymTab->strsize;
587 AssertLogRelMsgReturn(cbStrTab > 0 && offStrTab >= sizeof(pHdr) + cbCmds,
588 ("%p: cbStrTab=%#x offStrTab=%#x\n", pHdr, cbStrTab, offStrTab), false);
589
590 uint32_t const *paidxIndirSymbols = NULL;
591 uint32_t const offIndirSymbols = pDySymTab->indirectsymboff;
592 uint32_t const cIndirSymbols = pDySymTab->nindirectsymb;
593 AssertLogRelMsgReturn(cIndirSymbols > 0 && offIndirSymbols >= sizeof(pHdr) + cbCmds,
594 ("%p: cIndirSymbols=%#x offIndirSymbols=%#x\n", pHdr, cIndirSymbols, offIndirSymbols), false);
595
596 offCmd = 0;
597 for (uint32_t iCmd = 0; iCmd < cCmds; iCmd++)
598 {
599 load_command_t const * const pCmd = (load_command_t const *)((uintptr_t)(pHdr + 1) + offCmd);
600 uint32_t const cbCurCmd = pCmd->cmdsize;
601 AssertLogRelMsgReturn(offCmd + cbCurCmd <= cbCmds && cbCurCmd <= cbCmds,
602 ("%p: iCmd=%u offCmd=%#x cbCurCmd=%#x cbCmds=%#x\n", pHdr, iCmd, offCmd, cbCurCmd, cbCmds), false);
603 offCmd += cbCurCmd;
604
605 if (pCmd->cmd == LC_SEGMENT_64)
606 {
607 segment_command_64_t const *pSeg = (segment_command_64_t const *)pCmd;
608 AssertLogRelMsgReturn(cbCurCmd >= sizeof(*pSeg), ("%p: iCmd=%u cbCurCmd=%#x\n", pHdr, iCmd, cbCurCmd), false);
609 uintptr_t const uPtrSeg = pSeg->vmaddr + offSlide;
610 uint64_t const cbSeg = pSeg->vmsize;
611 uint64_t const offFile = pSeg->fileoff;
612
613 uint64_t offSeg = offSymbols - offFile;
614 if (offSeg < cbSeg)
615 {
616 AssertLogRelMsgReturn(!paSymbols, ("%p: paSymbols=%p uPtrSeg=%p off=%#llx\n", pHdr, paSymbols, uPtrSeg, offSeg),
617 false);
618 AssertLogRelMsgReturn(offSeg + cSymbols * sizeof(paSymbols[0]) <= cbSeg,
619 ("%p: offSeg=%#llx cSymbols=%#x cbSeg=%llx\n", pHdr, offSeg, cSymbols, cbSeg), false);
620 paSymbols = (macho_nlist_64_t const *)(uPtrSeg + offSeg);
621 }
622
623 offSeg = offStrTab - offFile;
624 if (offSeg < cbSeg)
625 {
626 AssertLogRelMsgReturn(!pchStrTab, ("%p: paSymbols=%p uPtrSeg=%p\n", pHdr, pchStrTab, uPtrSeg), false);
627 AssertLogRelMsgReturn(offSeg + cbStrTab <= cbSeg,
628 ("%p: offSeg=%#llx cbStrTab=%#x cbSeg=%llx\n", pHdr, offSeg, cbStrTab, cbSeg), false);
629 pchStrTab = (const char *)(uPtrSeg + offSeg);
630 }
631
632 offSeg = offIndirSymbols - offFile;
633 if (offSeg < cbSeg)
634 {
635 AssertLogRelMsgReturn(!paidxIndirSymbols,
636 ("%p: paidxIndirSymbols=%p uPtrSeg=%p\n", pHdr, paidxIndirSymbols, uPtrSeg), false);
637 AssertLogRelMsgReturn(offSeg + cIndirSymbols * sizeof(paidxIndirSymbols[0]) <= cbSeg,
638 ("%p: offSeg=%#llx cIndirSymbols=%#x cbSeg=%llx\n", pHdr, offSeg, cIndirSymbols, cbSeg),
639 false);
640 paidxIndirSymbols = (uint32_t const *)(uPtrSeg + offSeg);
641 }
642 }
643 }
644
645 AssertLogRelMsgReturn(paSymbols, ("%p: offSymbols=%#x\n", pHdr, offSymbols), false);
646 AssertLogRelMsgReturn(pchStrTab, ("%p: offStrTab=%#x\n", pHdr, offStrTab), false);
647 AssertLogRelMsgReturn(paidxIndirSymbols, ("%p: offIndirSymbols=%#x\n", pHdr, offIndirSymbols), false);
648
649 /*
650 * Third command pass: Process sections of types S_NON_LAZY_SYMBOL_POINTERS
651 * and S_LAZY_SYMBOL_POINTERS
652 */
653 bool fFound = false;
654 offCmd = 0;
655 for (uint32_t iCmd = 0; iCmd < cCmds; iCmd++)
656 {
657 load_command_t const * const pCmd = (load_command_t const *)((uintptr_t)(pHdr + 1) + offCmd);
658 uint32_t const cbCurCmd = pCmd->cmdsize;
659 AssertLogRelMsgReturn(offCmd + cbCurCmd <= cbCmds && cbCurCmd <= cbCmds,
660 ("%p: iCmd=%u offCmd=%#x cbCurCmd=%#x cbCmds=%#x\n", pHdr, iCmd, offCmd, cbCurCmd, cbCmds), false);
661 offCmd += cbCurCmd;
662 if (pCmd->cmd == LC_SEGMENT_64)
663 {
664 segment_command_64_t const *pSeg = (segment_command_64_t const *)pCmd;
665 AssertLogRelMsgReturn(cbCurCmd >= sizeof(*pSeg), ("%p: iCmd=%u cbCurCmd=%#x\n", pHdr, iCmd, cbCurCmd), false);
666 uint64_t const uSegAddr = pSeg->vmaddr;
667 uint64_t const cbSeg = pSeg->vmsize;
668
669 uint32_t const cSections = pSeg->nsects;
670 section_64_t const * const paSections = (section_64_t const *)(pSeg + 1);
671 AssertLogRelMsgReturn(cSections < _256K && sizeof(*pSeg) + cSections * sizeof(paSections[0]) <= cbCurCmd,
672 ("%p: iCmd=%u cSections=%#x cbCurCmd=%#x\n", pHdr, iCmd, cSections, cbCurCmd), false);
673 for (uint32_t iSection = 0; iSection < cSections; iSection++)
674 {
675 if ( paSections[iSection].flags == S_NON_LAZY_SYMBOL_POINTERS
676 || paSections[iSection].flags == S_LAZY_SYMBOL_POINTERS)
677 {
678 uint32_t const idxIndirBase = paSections[iSection].reserved1;
679 uint32_t const cEntries = paSections[iSection].size / sizeof(uintptr_t);
680 AssertLogRelMsgReturn(idxIndirBase <= cIndirSymbols && idxIndirBase + cEntries <= cIndirSymbols,
681 ("%p: idxIndirBase=%#x cEntries=%#x cIndirSymbols=%#x\n",
682 pHdr, idxIndirBase, cEntries, cIndirSymbols), false);
683 uint64_t const uSecAddr = paSections[iSection].addr;
684 uint64_t const offInSeg = uSecAddr - uSegAddr;
685 AssertLogRelMsgReturn(offInSeg < cbSeg && offInSeg + cEntries * sizeof(uintptr_t) <= cbSeg,
686 ("%p: offInSeg=%#llx cEntries=%#x cbSeg=%#llx\n", pHdr, offInSeg, cEntries, cbSeg),
687 false);
688 uintptr_t *pauPtrs = (uintptr_t *)(uSecAddr + offSlide);
689 for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++)
690 {
691 uint32_t const idxSym = paidxIndirSymbols[idxIndirBase + iEntry];
692 if (idxSym < cSymbols)
693 {
694 macho_nlist_64_t const * const pSym = &paSymbols[idxSym];
695 const char * const pszName = pSym->n_un.n_strx < cbStrTab
696 ? &pchStrTab[pSym->n_un.n_strx] : "!invalid symtab offset!";
697 if (strcmp(pszName, pszSymbol) == 0)
698 {
699 pauPtrs[iEntry] = uNewValue;
700 fFound = true;
701 break;
702 }
703 }
704 else
705 AssertMsg(idxSym == INDIRECT_SYMBOL_LOCAL || idxSym == INDIRECT_SYMBOL_ABS, ("%#x\n", idxSym));
706 }
707 }
708 }
709 }
710 }
711 AssertLogRel(fFound);
712 return fFound;
713}
714
715/**
716 * Mac OS X: Really ugly hack to bypass a set-uid check in AppKit.
717 *
718 * This will modify the issetugid() function to always return zero. This must
719 * be done _before_ AppKit is initialized, otherwise it will refuse to play ball
720 * with us as it distrusts set-uid processes since Snow Leopard. We, however,
721 * have carefully dropped all root privileges at this point and there should be
722 * no reason for any security concern here.
723 */
724static void hideSetUidRootFromAppKit()
725{
726 void *pvAddr;
727 /* Find issetguid() and make it always return 0 by modifying the code: */
728# if 0
729 pvAddr = dlsym(RTLD_DEFAULT, "issetugid");
730 int rc = mprotect((void *)((uintptr_t)pvAddr & ~(uintptr_t)0xfff), 0x2000, PROT_WRITE | PROT_READ | PROT_EXEC);
731 if (!rc)
732 ASMAtomicWriteU32((volatile uint32_t *)pvAddr, 0xccc3c031); /* xor eax, eax; ret; int3 */
733 else
734# endif
735 {
736 /* Failing that, find AppKit and patch its import table: */
737 void *pvAppKit = dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", RTLD_NOLOAD);
738 pvAddr = dlsym(pvAppKit, "NSApplicationMain");
739 Dl_info Info = {0};
740 if ( dladdr(pvAddr, &Info)
741 && Info.dli_fbase != NULL)
742 {
743 if (!patchExtSym((mach_header_64_t *)Info.dli_fbase, "_issetugid", (uintptr_t)&issetugid_for_AppKit))
744 write(2, RT_STR_TUPLE("WARNING: Failed to patch issetugid in AppKit! (patchExtSym)\n"));
745# ifdef DEBUG
746 else
747 write(2, RT_STR_TUPLE("INFO: Successfully patched _issetugid import for AppKit!\n"));
748# endif
749 }
750 else
751 write(2, RT_STR_TUPLE("WARNING: Failed to patch issetugid in AppKit! (dladdr)\n"));
752 }
753
754}
755
756#endif /* RT_OS_DARWIN */
757
758
759#ifdef RT_OS_WINDOWS
760
761#define MAIN_WND_CLASS L"VirtualBox Headless Interface"
762
763HINSTANCE g_hInstance = NULL;
764HWND g_hWindow = NULL;
765RTSEMEVENT g_hCanQuit;
766
767static DECLCALLBACK(int) windowsMessageMonitor(RTTHREAD ThreadSelf, void *pvUser);
768static int createWindow();
769static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
770static void destroyWindow();
771
772
773static DECLCALLBACK(int)
774windowsMessageMonitor(RTTHREAD ThreadSelf, void *pvUser)
775{
776 RT_NOREF(ThreadSelf, pvUser);
777 int rc;
778
779 rc = createWindow();
780 if (RT_FAILURE(rc))
781 return rc;
782
783 RTSemEventCreate(&g_hCanQuit);
784
785 MSG msg;
786 BOOL b;
787 while ((b = ::GetMessage(&msg, 0, 0, 0)) > 0)
788 {
789 ::TranslateMessage(&msg);
790 ::DispatchMessage(&msg);
791 }
792
793 if (b < 0)
794 LogRel(("VBoxHeadless: GetMessage failed\n"));
795 LogRel(("VBoxHeadless: stopping windows message loop\n"));
796
797 destroyWindow();
798 return VINF_SUCCESS;
799}
800
801
802static int
803createWindow()
804{
805 /* program instance handle */
806 g_hInstance = (HINSTANCE)::GetModuleHandle(NULL);
807 if (g_hInstance == NULL)
808 {
809 LogRel(("VBoxHeadless: failed to obtain module handle\n"));
810 return VERR_GENERAL_FAILURE;
811 }
812
813 /* window class */
814 WNDCLASS wc;
815 RT_ZERO(wc);
816
817 wc.style = CS_NOCLOSE;
818 wc.lpfnWndProc = WinMainWndProc;
819 wc.hInstance = g_hInstance;
820 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
821 wc.lpszClassName = MAIN_WND_CLASS;
822
823 ATOM atomWindowClass = ::RegisterClass(&wc);
824 if (atomWindowClass == 0)
825 {
826 LogRel(("VBoxHeadless: failed to register window class\n"));
827 return VERR_GENERAL_FAILURE;
828 }
829
830 /* secret window, secret garden */
831 g_hWindow = ::CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0,
832 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
833 if (g_hWindow == NULL)
834 {
835 LogRel(("VBoxHeadless: failed to create window\n"));
836 return VERR_GENERAL_FAILURE;
837 }
838
839 return VINF_SUCCESS;
840}
841
842
843static void
844destroyWindow()
845{
846 if (g_hWindow == NULL)
847 return;
848
849 ::DestroyWindow(g_hWindow);
850 g_hWindow = NULL;
851
852 if (g_hInstance == NULL)
853 return;
854
855 ::UnregisterClass(MAIN_WND_CLASS, g_hInstance);
856 g_hInstance = NULL;
857}
858
859
860static LRESULT CALLBACK
861WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
862{
863 int rc;
864
865 LRESULT lResult = 0;
866 switch (msg)
867 {
868 case WM_QUERYENDSESSION:
869 LogRel(("VBoxHeadless: WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n",
870 lParam == 0 ? " shutdown" : "",
871 lParam & ENDSESSION_CRITICAL ? " critical" : "",
872 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
873 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
874 (unsigned long)lParam));
875
876 /* do not block windows session termination */
877 lResult = TRUE;
878 break;
879
880 case WM_ENDSESSION:
881 lResult = 0;
882 LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
883 lParam == 0 ? " shutdown" : "",
884 lParam & ENDSESSION_CRITICAL ? " critical" : "",
885 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
886 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
887 wParam == FALSE ? " cancelled" : "",
888 wParam ? "TRUE" : "FALSE",
889 (unsigned long)lParam));
890 if (wParam == FALSE)
891 break;
892
893 /* tell the user what we are doing */
894 ::ShutdownBlockReasonCreate(hwnd,
895 com::BstrFmt("%s saving state",
896 g_strVMName.c_str()).raw());
897
898 /* tell the VM to save state/power off */
899 g_fTerminateFE = true;
900 gEventQ->interruptEventQueueProcessing();
901
902 if (g_hCanQuit != NIL_RTSEMEVENT)
903 {
904 LogRel(("VBoxHeadless: WM_ENDSESSION: waiting for VM termination...\n"));
905
906 rc = RTSemEventWait(g_hCanQuit, RT_INDEFINITE_WAIT);
907 if (RT_SUCCESS(rc))
908 LogRel(("VBoxHeadless: WM_ENDSESSION: done\n"));
909 else
910 LogRel(("VBoxHeadless: WM_ENDSESSION: failed to wait for VM termination: %Rrc\n", rc));
911 }
912 else
913 {
914 LogRel(("VBoxHeadless: WM_ENDSESSION: cannot wait for VM termination\n"));
915 }
916 break;
917
918 default:
919 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
920 break;
921 }
922 return lResult;
923}
924
925
926static const char * const ctrl_event_names[] = {
927 "CTRL_C_EVENT",
928 "CTRL_BREAK_EVENT",
929 "CTRL_CLOSE_EVENT",
930 /* reserved, not used */
931 "<console control event 3>",
932 "<console control event 4>",
933 /* not sent to processes that load gdi32.dll or user32.dll */
934 "CTRL_LOGOFF_EVENT",
935 "CTRL_SHUTDOWN_EVENT",
936};
937
938
939BOOL WINAPI
940ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
941{
942 const char *signame;
943 char namebuf[48];
944 int rc;
945
946 if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
947 signame = ctrl_event_names[dwCtrlType];
948 else
949 {
950 /* should not happen, but be prepared */
951 RTStrPrintf(namebuf, sizeof(namebuf),
952 "<console control event %lu>", (unsigned long)dwCtrlType);
953 signame = namebuf;
954 }
955 LogRel(("VBoxHeadless: got %s\n", signame));
956 RTMsgInfo("Got %s\n", signame);
957 RTMsgInfo("");
958
959 /* tell the VM to save state/power off */
960 g_fTerminateFE = true;
961 gEventQ->interruptEventQueueProcessing();
962
963 /*
964 * We don't need to wait for Ctrl-C / Ctrl-Break, but we must wait
965 * for Close, or we will be killed before the VM is saved.
966 */
967 if (g_hCanQuit != NIL_RTSEMEVENT)
968 {
969 LogRel(("VBoxHeadless: waiting for VM termination...\n"));
970
971 rc = RTSemEventWait(g_hCanQuit, RT_INDEFINITE_WAIT);
972 if (RT_FAILURE(rc))
973 LogRel(("VBoxHeadless: Failed to wait for VM termination: %Rrc\n", rc));
974 }
975
976 /* tell the system we handled it */
977 LogRel(("VBoxHeadless: ConsoleCtrlHandler: return\n"));
978 return TRUE;
979}
980#endif /* RT_OS_WINDOWS */
981
982
983/*
984 * Simplified version of showProgress() borrowed from VBoxManage.
985 */
986HRESULT
987showProgress(const ComPtr<IProgress> &progress)
988{
989 BOOL fCompleted = FALSE;
990 ULONG ulLastPercent = 0;
991 ULONG ulCurrentPercent = 0;
992 HRESULT hrc;
993
994 com::Bstr bstrDescription;
995 hrc = progress->COMGETTER(Description(bstrDescription.asOutParam()));
996 if (FAILED(hrc))
997 {
998 RTStrmPrintf(g_pStdErr, "Failed to get progress description: %Rhrc\n", hrc);
999 return hrc;
1000 }
1001
1002 RTStrmPrintf(g_pStdErr, "%ls: ", bstrDescription.raw());
1003 RTStrmFlush(g_pStdErr);
1004
1005 hrc = progress->COMGETTER(Completed(&fCompleted));
1006 while (SUCCEEDED(hrc))
1007 {
1008 progress->COMGETTER(Percent(&ulCurrentPercent));
1009
1010 /* did we cross a 10% mark? */
1011 if (ulCurrentPercent / 10 > ulLastPercent / 10)
1012 {
1013 /* make sure to also print out missed steps */
1014 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
1015 {
1016 if (curVal < 100)
1017 {
1018 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
1019 RTStrmFlush(g_pStdErr);
1020 }
1021 }
1022 ulLastPercent = (ulCurrentPercent / 10) * 10;
1023 }
1024
1025 if (fCompleted)
1026 break;
1027
1028 gEventQ->processEventQueue(500);
1029 hrc = progress->COMGETTER(Completed(&fCompleted));
1030 }
1031
1032 /* complete the line. */
1033 LONG iRc = E_FAIL;
1034 hrc = progress->COMGETTER(ResultCode)(&iRc);
1035 if (SUCCEEDED(hrc))
1036 {
1037 if (SUCCEEDED(iRc))
1038 RTStrmPrintf(g_pStdErr, "100%%\n");
1039#if 0
1040 else if (g_fCanceled)
1041 RTStrmPrintf(g_pStdErr, "CANCELED\n");
1042#endif
1043 else
1044 {
1045 RTStrmPrintf(g_pStdErr, "\n");
1046 RTStrmPrintf(g_pStdErr, "Operation failed: %Rhrc\n", iRc);
1047 }
1048 hrc = iRc;
1049 }
1050 else
1051 {
1052 RTStrmPrintf(g_pStdErr, "\n");
1053 RTStrmPrintf(g_pStdErr, "Failed to obtain operation result: %Rhrc\n", hrc);
1054 }
1055 RTStrmFlush(g_pStdErr);
1056 return hrc;
1057}
1058
1059
1060/**
1061 * Entry point.
1062 */
1063extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
1064{
1065 RT_NOREF(envp);
1066 const char *vrdePort = NULL;
1067 const char *vrdeAddress = NULL;
1068 const char *vrdeEnabled = NULL;
1069 unsigned cVRDEProperties = 0;
1070 const char *aVRDEProperties[16];
1071 unsigned fRawR0 = ~0U;
1072 unsigned fRawR3 = ~0U;
1073 unsigned fPATM = ~0U;
1074 unsigned fCSAM = ~0U;
1075 unsigned fPaused = 0;
1076#ifdef VBOX_WITH_RECORDING
1077 bool fRecordEnabled = false;
1078 uint32_t ulRecordVideoWidth = 800;
1079 uint32_t ulRecordVideoHeight = 600;
1080 uint32_t ulRecordVideoRate = 300000;
1081 char szRecordFilename[RTPATH_MAX];
1082 const char *pszRecordFilenameTemplate = "VBox-%d.webm"; /* .webm container by default. */
1083#endif /* VBOX_WITH_RECORDING */
1084#ifdef RT_OS_WINDOWS
1085 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
1086#endif
1087
1088 LogFlow(("VBoxHeadless STARTED.\n"));
1089 RTPrintf(VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
1090 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
1091 "All rights reserved.\n\n");
1092
1093#ifdef VBOX_WITH_RECORDING
1094 /* Parse the environment */
1095 parse_environ(&ulRecordVideoWidth, &ulRecordVideoHeight, &ulRecordVideoRate, &pszRecordFilenameTemplate);
1096#endif
1097
1098 enum eHeadlessOptions
1099 {
1100 OPT_RAW_R0 = 0x100,
1101 OPT_NO_RAW_R0,
1102 OPT_RAW_R3,
1103 OPT_NO_RAW_R3,
1104 OPT_PATM,
1105 OPT_NO_PATM,
1106 OPT_CSAM,
1107 OPT_NO_CSAM,
1108 OPT_SETTINGSPW,
1109 OPT_SETTINGSPW_FILE,
1110 OPT_COMMENT,
1111 OPT_PAUSED
1112 };
1113
1114 static const RTGETOPTDEF s_aOptions[] =
1115 {
1116 { "-startvm", 's', RTGETOPT_REQ_STRING },
1117 { "--startvm", 's', RTGETOPT_REQ_STRING },
1118 { "-vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1119 { "--vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1120 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1121 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1122 { "-vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1123 { "--vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1124 { "-vrde", 'v', RTGETOPT_REQ_STRING },
1125 { "--vrde", 'v', RTGETOPT_REQ_STRING },
1126 { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
1127 { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
1128 { "-rawr0", OPT_RAW_R0, 0 },
1129 { "--rawr0", OPT_RAW_R0, 0 },
1130 { "-norawr0", OPT_NO_RAW_R0, 0 },
1131 { "--norawr0", OPT_NO_RAW_R0, 0 },
1132 { "-rawr3", OPT_RAW_R3, 0 },
1133 { "--rawr3", OPT_RAW_R3, 0 },
1134 { "-norawr3", OPT_NO_RAW_R3, 0 },
1135 { "--norawr3", OPT_NO_RAW_R3, 0 },
1136 { "-patm", OPT_PATM, 0 },
1137 { "--patm", OPT_PATM, 0 },
1138 { "-nopatm", OPT_NO_PATM, 0 },
1139 { "--nopatm", OPT_NO_PATM, 0 },
1140 { "-csam", OPT_CSAM, 0 },
1141 { "--csam", OPT_CSAM, 0 },
1142 { "-nocsam", OPT_NO_CSAM, 0 },
1143 { "--nocsam", OPT_NO_CSAM, 0 },
1144 { "--settingspw", OPT_SETTINGSPW, RTGETOPT_REQ_STRING },
1145 { "--settingspwfile", OPT_SETTINGSPW_FILE, RTGETOPT_REQ_STRING },
1146#ifdef VBOX_WITH_RECORDING
1147 { "-record", 'c', 0 },
1148 { "--record", 'c', 0 },
1149 { "--videowidth", 'w', RTGETOPT_REQ_UINT32 },
1150 { "--videoheight", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
1151 { "--videorate", 'r', RTGETOPT_REQ_UINT32 },
1152 { "--filename", 'f', RTGETOPT_REQ_STRING },
1153#endif /* VBOX_WITH_RECORDING defined */
1154 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
1155 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
1156 { "-start-paused", OPT_PAUSED, 0 },
1157 { "--start-paused", OPT_PAUSED, 0 }
1158 };
1159
1160 const char *pcszNameOrUUID = NULL;
1161
1162#ifdef RT_OS_DARWIN
1163 hideSetUidRootFromAppKit();
1164#endif
1165
1166 // parse the command line
1167 int ch;
1168 const char *pcszSettingsPw = NULL;
1169 const char *pcszSettingsPwFile = NULL;
1170 RTGETOPTUNION ValueUnion;
1171 RTGETOPTSTATE GetState;
1172 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
1173 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1174 {
1175 switch(ch)
1176 {
1177 case 's':
1178 pcszNameOrUUID = ValueUnion.psz;
1179 break;
1180 case 'p':
1181 RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
1182 vrdePort = ValueUnion.psz;
1183 break;
1184 case 'a':
1185 RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
1186 vrdeAddress = ValueUnion.psz;
1187 break;
1188 case 'v':
1189 vrdeEnabled = ValueUnion.psz;
1190 break;
1191 case 'e':
1192 if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
1193 aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
1194 else
1195 RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
1196 break;
1197 case OPT_RAW_R0:
1198 fRawR0 = true;
1199 break;
1200 case OPT_NO_RAW_R0:
1201 fRawR0 = false;
1202 break;
1203 case OPT_RAW_R3:
1204 fRawR3 = true;
1205 break;
1206 case OPT_NO_RAW_R3:
1207 fRawR3 = false;
1208 break;
1209 case OPT_PATM:
1210 fPATM = true;
1211 break;
1212 case OPT_NO_PATM:
1213 fPATM = false;
1214 break;
1215 case OPT_CSAM:
1216 fCSAM = true;
1217 break;
1218 case OPT_NO_CSAM:
1219 fCSAM = false;
1220 break;
1221 case OPT_SETTINGSPW:
1222 pcszSettingsPw = ValueUnion.psz;
1223 break;
1224 case OPT_SETTINGSPW_FILE:
1225 pcszSettingsPwFile = ValueUnion.psz;
1226 break;
1227 case OPT_PAUSED:
1228 fPaused = true;
1229 break;
1230#ifdef VBOX_WITH_RECORDING
1231 case 'c':
1232 fRecordEnabled = true;
1233 break;
1234 case 'w':
1235 ulRecordVideoWidth = ValueUnion.u32;
1236 break;
1237 case 'r':
1238 ulRecordVideoRate = ValueUnion.u32;
1239 break;
1240 case 'f':
1241 pszRecordFilenameTemplate = ValueUnion.psz;
1242 break;
1243#endif /* VBOX_WITH_RECORDING defined */
1244 case 'h':
1245#ifdef VBOX_WITH_RECORDING
1246 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
1247 {
1248 ulRecordVideoHeight = ValueUnion.u32;
1249 break;
1250 }
1251#endif
1252 show_usage();
1253 return 0;
1254 case OPT_COMMENT:
1255 /* nothing to do */
1256 break;
1257 case 'V':
1258 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1259 return 0;
1260 default:
1261 ch = RTGetOptPrintError(ch, &ValueUnion);
1262 show_usage();
1263 return ch;
1264 }
1265 }
1266
1267#ifdef VBOX_WITH_RECORDING
1268 if (ulRecordVideoWidth < 512 || ulRecordVideoWidth > 2048 || ulRecordVideoWidth % 2)
1269 {
1270 LogError("VBoxHeadless: ERROR: please specify an even video frame width between 512 and 2048", 0);
1271 return 1;
1272 }
1273 if (ulRecordVideoHeight < 384 || ulRecordVideoHeight > 1536 || ulRecordVideoHeight % 2)
1274 {
1275 LogError("VBoxHeadless: ERROR: please specify an even video frame height between 384 and 1536", 0);
1276 return 1;
1277 }
1278 if (ulRecordVideoRate < 300000 || ulRecordVideoRate > 1000000)
1279 {
1280 LogError("VBoxHeadless: ERROR: please specify an even video bitrate between 300000 and 1000000", 0);
1281 return 1;
1282 }
1283 /* Make sure we only have %d or %u (or none) in the file name specified */
1284 char *pcPercent = (char*)strchr(pszRecordFilenameTemplate, '%');
1285 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
1286 {
1287 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the recording file name.", -1);
1288 return 1;
1289 }
1290 /* And no more than one % in the name */
1291 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
1292 {
1293 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the recording file name.", -1);
1294 return 1;
1295 }
1296 RTStrPrintf(&szRecordFilename[0], RTPATH_MAX, pszRecordFilenameTemplate, RTProcSelf());
1297#endif /* defined VBOX_WITH_RECORDING */
1298
1299 if (!pcszNameOrUUID)
1300 {
1301 show_usage();
1302 return 1;
1303 }
1304
1305 HRESULT rc;
1306 int irc;
1307
1308 rc = com::Initialize();
1309#ifdef VBOX_WITH_XPCOM
1310 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
1311 {
1312 char szHome[RTPATH_MAX] = "";
1313 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1314 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1315 return 1;
1316 }
1317#endif
1318 if (FAILED(rc))
1319 {
1320 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
1321 return 1;
1322 }
1323
1324 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
1325 ComPtr<IVirtualBox> virtualBox;
1326 ComPtr<ISession> session;
1327 ComPtr<IMachine> machine;
1328 bool fSessionOpened = false;
1329 ComPtr<IEventListener> vboxClientListener;
1330 ComPtr<IEventListener> vboxListener;
1331 ComObjPtr<ConsoleEventListenerImpl> consoleListener;
1332
1333 do
1334 {
1335 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1336 if (FAILED(rc))
1337 {
1338 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBoxClient object!\n");
1339 com::ErrorInfo info;
1340 if (!info.isFullAvailable() && !info.isBasicAvailable())
1341 {
1342 com::GluePrintRCMessage(rc);
1343 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
1344 }
1345 else
1346 GluePrintErrorInfo(info);
1347 break;
1348 }
1349
1350 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
1351 if (FAILED(rc))
1352 {
1353 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
1354 break;
1355 }
1356 rc = pVirtualBoxClient->COMGETTER(Session)(session.asOutParam());
1357 if (FAILED(rc))
1358 {
1359 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
1360 break;
1361 }
1362
1363 if (pcszSettingsPw)
1364 {
1365 CHECK_ERROR(virtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
1366 if (FAILED(rc))
1367 break;
1368 }
1369 else if (pcszSettingsPwFile)
1370 {
1371 int rcExit = settingsPasswordFile(virtualBox, pcszSettingsPwFile);
1372 if (rcExit != RTEXITCODE_SUCCESS)
1373 break;
1374 }
1375
1376 ComPtr<IMachine> m;
1377
1378 rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
1379 if (FAILED(rc))
1380 {
1381 LogError("Invalid machine name or UUID!\n", rc);
1382 break;
1383 }
1384
1385 Bstr bstrVMId;
1386 rc = m->COMGETTER(Id)(bstrVMId.asOutParam());
1387 AssertComRC(rc);
1388 if (FAILED(rc))
1389 break;
1390 g_strVMUUID = bstrVMId;
1391
1392 Bstr bstrVMName;
1393 rc = m->COMGETTER(Name)(bstrVMName.asOutParam());
1394 AssertComRC(rc);
1395 if (FAILED(rc))
1396 break;
1397 g_strVMName = bstrVMName;
1398
1399 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
1400 g_strVMUUID.c_str()));
1401
1402 // set session name
1403 CHECK_ERROR_BREAK(session, COMSETTER(Name)(Bstr("headless").raw()));
1404 // open a session
1405 CHECK_ERROR_BREAK(m, LockMachine(session, LockType_VM));
1406 fSessionOpened = true;
1407
1408 /* get the console */
1409 ComPtr<IConsole> console;
1410 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
1411
1412 /* get the mutable machine */
1413 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
1414
1415 ComPtr<IDisplay> display;
1416 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
1417
1418#ifdef VBOX_WITH_RECORDING
1419 if (fRecordEnabled)
1420 {
1421 ComPtr<IRecordingSettings> recordingSettings;
1422 CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1423 CHECK_ERROR_BREAK(recordingSettings, COMSETTER(Enabled)(TRUE));
1424
1425 SafeIfaceArray <IRecordingScreenSettings> saRecordScreenScreens;
1426 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordScreenScreens)));
1427
1428 /* Note: For now all screens have the same configuration. */
1429 for (size_t i = 0; i < saRecordScreenScreens.size(); ++i)
1430 {
1431 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(Enabled)(TRUE));
1432 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(Filename)(Bstr(szRecordFilename).raw()));
1433 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoWidth)(ulRecordVideoWidth));
1434 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoHeight)(ulRecordVideoHeight));
1435 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoRate)(ulRecordVideoRate));
1436 }
1437 }
1438#endif /* defined(VBOX_WITH_RECORDING) */
1439
1440 /* get the machine debugger (isn't necessarily available) */
1441 ComPtr <IMachineDebugger> machineDebugger;
1442 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
1443 if (machineDebugger)
1444 {
1445 Log(("Machine debugger available!\n"));
1446 }
1447
1448 if (fRawR0 != ~0U)
1449 {
1450 if (!machineDebugger)
1451 {
1452 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
1453 break;
1454 }
1455 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
1456 }
1457 if (fRawR3 != ~0U)
1458 {
1459 if (!machineDebugger)
1460 {
1461 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
1462 break;
1463 }
1464 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1465 }
1466 if (fPATM != ~0U)
1467 {
1468 if (!machineDebugger)
1469 {
1470 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
1471 break;
1472 }
1473 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1474 }
1475 if (fCSAM != ~0U)
1476 {
1477 if (!machineDebugger)
1478 {
1479 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
1480 break;
1481 }
1482 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1483 }
1484
1485 /* initialize global references */
1486 gConsole = console;
1487 gEventQ = com::NativeEventQueue::getMainEventQueue();
1488
1489 /* VirtualBoxClient events registration. */
1490 {
1491 ComPtr<IEventSource> pES;
1492 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1493 ComObjPtr<VirtualBoxClientEventListenerImpl> listener;
1494 listener.createObject();
1495 listener->init(new VirtualBoxClientEventListener());
1496 vboxClientListener = listener;
1497 com::SafeArray<VBoxEventType_T> eventTypes;
1498 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
1499 CHECK_ERROR(pES, RegisterListener(vboxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1500 }
1501
1502 /* Console events registration. */
1503 {
1504 ComPtr<IEventSource> es;
1505 CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
1506 consoleListener.createObject();
1507 consoleListener->init(new ConsoleEventListener());
1508 com::SafeArray<VBoxEventType_T> eventTypes;
1509 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1510 eventTypes.push_back(VBoxEventType_OnStateChanged);
1511 eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
1512 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1513 eventTypes.push_back(VBoxEventType_OnShowWindow);
1514 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
1515 CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
1516 }
1517
1518 /* Default is to use the VM setting for the VRDE server. */
1519 enum VRDEOption
1520 {
1521 VRDEOption_Config,
1522 VRDEOption_Off,
1523 VRDEOption_On
1524 };
1525 VRDEOption enmVRDEOption = VRDEOption_Config;
1526 BOOL fVRDEEnabled;
1527 ComPtr <IVRDEServer> vrdeServer;
1528 CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
1529 CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));
1530
1531 if (vrdeEnabled != NULL)
1532 {
1533 /* -vrde on|off|config */
1534 if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
1535 enmVRDEOption = VRDEOption_Off;
1536 else if (!strcmp(vrdeEnabled, "on") || !strcmp(vrdeEnabled, "enable"))
1537 enmVRDEOption = VRDEOption_On;
1538 else if (strcmp(vrdeEnabled, "config"))
1539 {
1540 RTPrintf("-vrde requires an argument (on|off|config)\n");
1541 break;
1542 }
1543 }
1544
1545 Log(("VBoxHeadless: enmVRDE %d, fVRDEEnabled %d\n", enmVRDEOption, fVRDEEnabled));
1546
1547 if (enmVRDEOption != VRDEOption_Off)
1548 {
1549 /* Set other specified options. */
1550
1551 /* set VRDE port if requested by the user */
1552 if (vrdePort != NULL)
1553 {
1554 Bstr bstr = vrdePort;
1555 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
1556 }
1557 /* set VRDE address if requested by the user */
1558 if (vrdeAddress != NULL)
1559 {
1560 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
1561 }
1562
1563 /* Set VRDE properties. */
1564 if (cVRDEProperties > 0)
1565 {
1566 for (unsigned i = 0; i < cVRDEProperties; i++)
1567 {
1568 /* Parse 'name=value' */
1569 char *pszProperty = RTStrDup(aVRDEProperties[i]);
1570 if (pszProperty)
1571 {
1572 char *pDelimiter = strchr(pszProperty, '=');
1573 if (pDelimiter)
1574 {
1575 *pDelimiter = '\0';
1576
1577 Bstr bstrName = pszProperty;
1578 Bstr bstrValue = &pDelimiter[1];
1579 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1580 }
1581 else
1582 {
1583 RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
1584 RTStrFree(pszProperty);
1585 rc = E_INVALIDARG;
1586 break;
1587 }
1588 RTStrFree(pszProperty);
1589 }
1590 else
1591 {
1592 RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
1593 rc = E_OUTOFMEMORY;
1594 break;
1595 }
1596 }
1597 if (FAILED(rc))
1598 break;
1599 }
1600
1601 }
1602
1603 if (enmVRDEOption == VRDEOption_On)
1604 {
1605 /* enable VRDE server (only if currently disabled) */
1606 if (!fVRDEEnabled)
1607 {
1608 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
1609 }
1610 }
1611 else if (enmVRDEOption == VRDEOption_Off)
1612 {
1613 /* disable VRDE server (only if currently enabled */
1614 if (fVRDEEnabled)
1615 {
1616 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
1617 }
1618 }
1619
1620 /* Disable the host clipboard before powering up */
1621 console->COMSETTER(UseHostClipboard)(false);
1622
1623 Log(("VBoxHeadless: Powering up the machine...\n"));
1624
1625 ComPtr <IProgress> progress;
1626 if (!fPaused)
1627 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1628 else
1629 CHECK_ERROR_BREAK(console, PowerUpPaused(progress.asOutParam()));
1630
1631 rc = showProgress(progress);
1632 if (FAILED(rc))
1633 {
1634 com::ProgressErrorInfo info(progress);
1635 if (info.isBasicAvailable())
1636 {
1637 RTPrintf("Error: failed to start machine. Error message: %ls\n", info.getText().raw());
1638 }
1639 else
1640 {
1641 RTPrintf("Error: failed to start machine. No error message available!\n");
1642 }
1643 break;
1644 }
1645
1646#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1647 signal(SIGPIPE, SIG_IGN);
1648 signal(SIGTTOU, SIG_IGN);
1649
1650 struct sigaction sa;
1651 RT_ZERO(sa);
1652 sa.sa_handler = HandleSignal;
1653 sigaction(SIGHUP, &sa, NULL);
1654 sigaction(SIGINT, &sa, NULL);
1655 sigaction(SIGTERM, &sa, NULL);
1656 sigaction(SIGUSR1, &sa, NULL);
1657 sigaction(SIGUSR2, &sa, NULL);
1658#endif
1659
1660#ifdef RT_OS_WINDOWS
1661 /*
1662 * Register windows console signal handler to react to Ctrl-C,
1663 * Ctrl-Break, Close.
1664 */
1665 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1666
1667 /*
1668 * Spawn windows message pump to monitor session events.
1669 */
1670 RTTHREAD hThrMsg;
1671 irc = RTThreadCreate(&hThrMsg,
1672 windowsMessageMonitor, NULL,
1673 0, /* :cbStack */
1674 RTTHREADTYPE_MSG_PUMP, 0,
1675 "MSG");
1676 if (RT_FAILURE(irc)) /* not fatal */
1677 LogRel(("VBoxHeadless: failed to start windows message monitor: %Rrc\n", irc));
1678#endif /* RT_OS_WINDOWS */
1679
1680
1681 /*
1682 * Pump vbox events forever
1683 */
1684 LogRel(("VBoxHeadless: starting event loop\n"));
1685 for (;;)
1686 {
1687 irc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
1688
1689 /*
1690 * interruptEventQueueProcessing from another thread is
1691 * reported as VERR_INTERRUPTED, so check the flag first.
1692 */
1693 if (g_fTerminateFE)
1694 {
1695 LogRel(("VBoxHeadless: processEventQueue: %Rrc, termination requested\n", irc));
1696 break;
1697 }
1698
1699 if (RT_FAILURE(irc))
1700 {
1701 LogRel(("VBoxHeadless: processEventQueue: %Rrc\n", irc));
1702 RTMsgError("event loop: %Rrc", irc);
1703 break;
1704 }
1705 }
1706
1707 Log(("VBoxHeadless: event loop has terminated...\n"));
1708
1709#ifdef VBOX_WITH_RECORDING
1710 if (fRecordEnabled)
1711 {
1712 if (!machine.isNull())
1713 {
1714 ComPtr<IRecordingSettings> recordingSettings;
1715 CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1716 CHECK_ERROR_BREAK(recordingSettings, COMSETTER(Enabled)(FALSE));
1717 }
1718 }
1719#endif /* VBOX_WITH_RECORDING */
1720
1721 /* we don't have to disable VRDE here because we don't save the settings of the VM */
1722 }
1723 while (0);
1724
1725 /*
1726 * Get the machine state.
1727 */
1728 MachineState_T machineState = MachineState_Aborted;
1729 if (!machine.isNull())
1730 {
1731 rc = machine->COMGETTER(State)(&machineState);
1732 if (SUCCEEDED(rc))
1733 Log(("machine state = %RU32\n", machineState));
1734 else
1735 Log(("IMachine::getState: %Rhrc\n", rc));
1736 }
1737 else
1738 {
1739 Log(("machine == NULL\n"));
1740 }
1741
1742 /*
1743 * Turn off the VM if it's running
1744 */
1745 if ( gConsole
1746 && ( machineState == MachineState_Running
1747 || machineState == MachineState_Teleporting
1748 || machineState == MachineState_LiveSnapshotting
1749 /** @todo power off paused VMs too? */
1750 )
1751 )
1752 do
1753 {
1754 consoleListener->getWrapped()->ignorePowerOffEvents(true);
1755
1756 ComPtr<IProgress> pProgress;
1757 if (!machine.isNull())
1758 CHECK_ERROR_BREAK(machine, SaveState(pProgress.asOutParam()));
1759 else
1760 CHECK_ERROR_BREAK(gConsole, PowerDown(pProgress.asOutParam()));
1761
1762 rc = showProgress(pProgress);
1763 if (FAILED(rc))
1764 {
1765 com::ErrorInfo info;
1766 if (!info.isFullAvailable() && !info.isBasicAvailable())
1767 com::GluePrintRCMessage(rc);
1768 else
1769 com::GluePrintErrorInfo(info);
1770 break;
1771 }
1772 } while (0);
1773
1774 /* VirtualBox callback unregistration. */
1775 if (vboxListener)
1776 {
1777 ComPtr<IEventSource> es;
1778 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1779 if (!es.isNull())
1780 CHECK_ERROR(es, UnregisterListener(vboxListener));
1781 vboxListener.setNull();
1782 }
1783
1784 /* Console callback unregistration. */
1785 if (consoleListener)
1786 {
1787 ComPtr<IEventSource> es;
1788 CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
1789 if (!es.isNull())
1790 CHECK_ERROR(es, UnregisterListener(consoleListener));
1791 consoleListener.setNull();
1792 }
1793
1794 /* VirtualBoxClient callback unregistration. */
1795 if (vboxClientListener)
1796 {
1797 ComPtr<IEventSource> pES;
1798 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1799 if (!pES.isNull())
1800 CHECK_ERROR(pES, UnregisterListener(vboxClientListener));
1801 vboxClientListener.setNull();
1802 }
1803
1804 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1805 gConsole = NULL;
1806
1807 if (fSessionOpened)
1808 {
1809 /*
1810 * Close the session. This will also uninitialize the console and
1811 * unregister the callback we've registered before.
1812 */
1813 Log(("VBoxHeadless: Closing the session...\n"));
1814 session->UnlockMachine();
1815 }
1816
1817 /* Must be before com::Shutdown */
1818 session.setNull();
1819 virtualBox.setNull();
1820 pVirtualBoxClient.setNull();
1821 machine.setNull();
1822
1823 com::Shutdown();
1824
1825#ifdef RT_OS_WINDOWS
1826 /* tell the session monitor it can ack WM_ENDSESSION */
1827 if (g_hCanQuit != NIL_RTSEMEVENT)
1828 {
1829 RTSemEventSignal(g_hCanQuit);
1830 }
1831
1832 /* tell the session monitor to quit */
1833 if (g_hWindow != NULL)
1834 {
1835 ::PostMessage(g_hWindow, WM_QUIT, 0, 0);
1836 }
1837#endif
1838
1839 LogRel(("VBoxHeadless: exiting\n"));
1840 return FAILED(rc) ? 1 : 0;
1841}
1842
1843
1844#ifndef VBOX_WITH_HARDENING
1845/**
1846 * Main entry point.
1847 */
1848int main(int argc, char **argv, char **envp)
1849{
1850 // initialize VBox Runtime
1851 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
1852 if (RT_FAILURE(rc))
1853 {
1854 RTPrintf("VBoxHeadless: Runtime Error:\n"
1855 " %Rrc -- %Rrf\n", rc, rc);
1856 switch (rc)
1857 {
1858 case VERR_VM_DRIVER_NOT_INSTALLED:
1859 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1860 "loaded successfully. Aborting ...\n");
1861 break;
1862 default:
1863 break;
1864 }
1865 return 1;
1866 }
1867
1868 return TrustedMain(argc, argv, envp);
1869}
1870#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