VirtualBox

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

Last change on this file since 89162 was 89108, checked in by vboxsync, 4 years ago

FE/VBoxHeadless: On Windows also listen to console control events
(Ctrl-C, Ctrl-Break, Close) and terminate the VM. By default try to
save state of the VM when we need to terminate it, don't just yank the
virtual power cord. bugref:8161.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette