VirtualBox

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

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

FE/VBoxHeadless: If we check the status of an API call, we'd better
actually get it first instead of checking the status of a previous API
call. bugref:8161.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.0 KB
Line 
1/* $Id: VBoxHeadless.cpp 89221 2021-05-21 12:22:21Z 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 /* do not block windows session termination */
873 lResult = TRUE;
874 break;
875
876 case WM_ENDSESSION:
877 lResult = 0;
878 LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
879 lParam == 0 ? " shutdown" : "",
880 lParam & ENDSESSION_CRITICAL ? " critical" : "",
881 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
882 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
883 wParam == FALSE ? " cancelled" : "",
884 wParam ? "TRUE" : "FALSE",
885 (unsigned long)lParam));
886 if (wParam == FALSE)
887 break;
888
889 /* tell the user what we are doing */
890 ::ShutdownBlockReasonCreate(hwnd, L"Waiting for VM to terminate");
891
892 /* tell the VM to save state/power off */
893 g_fTerminateFE = true;
894 gEventQ->interruptEventQueueProcessing();
895
896 if (g_hCanQuit != NIL_RTSEMEVENT)
897 {
898 LogRel(("VBoxHeadless: WM_ENDSESSION: waiting for VM termination...\n"));
899
900 rc = RTSemEventWait(g_hCanQuit, RT_INDEFINITE_WAIT);
901 if (RT_SUCCESS(rc))
902 LogRel(("VBoxHeadless: WM_ENDSESSION: done\n"));
903 else
904 LogRel(("VBoxHeadless: WM_ENDSESSION: failed to wait for VM termination: %Rrc\n", rc));
905 }
906 else
907 {
908 LogRel(("VBoxHeadless: WM_ENDSESSION: cannot wait for VM termination\n"));
909 }
910 break;
911
912 default:
913 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
914 break;
915 }
916 return lResult;
917}
918
919
920static const char * const ctrl_event_names[] = {
921 "CTRL_C_EVENT",
922 "CTRL_BREAK_EVENT",
923 "CTRL_CLOSE_EVENT",
924 /* reserved, not used */
925 "<console control event 3>",
926 "<console control event 4>",
927 /* not sent to processes that load gdi32.dll or user32.dll */
928 "CTRL_LOGOFF_EVENT",
929 "CTRL_SHUTDOWN_EVENT",
930};
931
932
933BOOL WINAPI
934ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
935{
936 const char *signame;
937 char namebuf[48];
938 int rc;
939
940 if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
941 signame = ctrl_event_names[dwCtrlType];
942 else
943 {
944 /* should not happen, but be prepared */
945 RTStrPrintf(namebuf, sizeof(namebuf),
946 "<console control event %lu>", (unsigned long)dwCtrlType);
947 signame = namebuf;
948 }
949 LogRel(("VBoxHeadless: got %s\n", signame));
950 RTMsgInfo("Got %s\n", signame);
951 RTMsgInfo("");
952
953 /* tell the VM to save state/power off */
954 g_fTerminateFE = true;
955 gEventQ->interruptEventQueueProcessing();
956
957 /*
958 * We don't need to wait for Ctrl-C / Ctrl-Break, but we must wait
959 * for Close, or we will be killed before the VM is saved.
960 */
961 if (g_hCanQuit != NIL_RTSEMEVENT)
962 {
963 LogRel(("VBoxHeadless: waiting for VM termination...\n"));
964
965 rc = RTSemEventWait(g_hCanQuit, RT_INDEFINITE_WAIT);
966 if (RT_FAILURE(rc))
967 LogRel(("VBoxHeadless: Failed to wait for VM termination: %Rrc\n", rc));
968 }
969
970 /* tell the system we handled it */
971 LogRel(("VBoxHeadless: ConsoleCtrlHandler: return\n"));
972 return TRUE;
973}
974#endif /* RT_OS_WINDOWS */
975
976
977/*
978 * Simplified version of showProgress() borrowed from VBoxManage.
979 */
980HRESULT
981showProgress(const ComPtr<IProgress> &progress)
982{
983 BOOL fCompleted = FALSE;
984 ULONG ulLastPercent = 0;
985 ULONG ulCurrentPercent = 0;
986 HRESULT hrc;
987
988 com::Bstr bstrDescription;
989 hrc = progress->COMGETTER(Description(bstrDescription.asOutParam()));
990 if (FAILED(hrc))
991 {
992 RTStrmPrintf(g_pStdErr, "Failed to get progress description: %Rhrc\n", hrc);
993 return hrc;
994 }
995
996 RTStrmPrintf(g_pStdErr, "%ls: ", bstrDescription.raw());
997 RTStrmFlush(g_pStdErr);
998
999 hrc = progress->COMGETTER(Completed(&fCompleted));
1000 while (SUCCEEDED(hrc))
1001 {
1002 progress->COMGETTER(Percent(&ulCurrentPercent));
1003
1004 /* did we cross a 10% mark? */
1005 if (ulCurrentPercent / 10 > ulLastPercent / 10)
1006 {
1007 /* make sure to also print out missed steps */
1008 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
1009 {
1010 if (curVal < 100)
1011 {
1012 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
1013 RTStrmFlush(g_pStdErr);
1014 }
1015 }
1016 ulLastPercent = (ulCurrentPercent / 10) * 10;
1017 }
1018
1019 if (fCompleted)
1020 break;
1021
1022 gEventQ->processEventQueue(500);
1023 hrc = progress->COMGETTER(Completed(&fCompleted));
1024 }
1025
1026 /* complete the line. */
1027 LONG iRc = E_FAIL;
1028 hrc = progress->COMGETTER(ResultCode)(&iRc);
1029 if (SUCCEEDED(hrc))
1030 {
1031 if (SUCCEEDED(iRc))
1032 RTStrmPrintf(g_pStdErr, "100%%\n");
1033#if 0
1034 else if (g_fCanceled)
1035 RTStrmPrintf(g_pStdErr, "CANCELED\n");
1036#endif
1037 else
1038 {
1039 RTStrmPrintf(g_pStdErr, "\n");
1040 RTStrmPrintf(g_pStdErr, "Operation failed: %Rhrc\n", iRc);
1041 }
1042 hrc = iRc;
1043 }
1044 else
1045 {
1046 RTStrmPrintf(g_pStdErr, "\n");
1047 RTStrmPrintf(g_pStdErr, "Failed to obtain operation result: %Rhrc\n", hrc);
1048 }
1049 RTStrmFlush(g_pStdErr);
1050 return hrc;
1051}
1052
1053
1054/**
1055 * Entry point.
1056 */
1057extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
1058{
1059 RT_NOREF(envp);
1060 const char *vrdePort = NULL;
1061 const char *vrdeAddress = NULL;
1062 const char *vrdeEnabled = NULL;
1063 unsigned cVRDEProperties = 0;
1064 const char *aVRDEProperties[16];
1065 unsigned fRawR0 = ~0U;
1066 unsigned fRawR3 = ~0U;
1067 unsigned fPATM = ~0U;
1068 unsigned fCSAM = ~0U;
1069 unsigned fPaused = 0;
1070#ifdef VBOX_WITH_RECORDING
1071 bool fRecordEnabled = false;
1072 uint32_t ulRecordVideoWidth = 800;
1073 uint32_t ulRecordVideoHeight = 600;
1074 uint32_t ulRecordVideoRate = 300000;
1075 char szRecordFilename[RTPATH_MAX];
1076 const char *pszRecordFilenameTemplate = "VBox-%d.webm"; /* .webm container by default. */
1077#endif /* VBOX_WITH_RECORDING */
1078#ifdef RT_OS_WINDOWS
1079 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
1080#endif
1081
1082 LogFlow(("VBoxHeadless STARTED.\n"));
1083 RTPrintf(VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
1084 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
1085 "All rights reserved.\n\n");
1086
1087#ifdef VBOX_WITH_RECORDING
1088 /* Parse the environment */
1089 parse_environ(&ulRecordVideoWidth, &ulRecordVideoHeight, &ulRecordVideoRate, &pszRecordFilenameTemplate);
1090#endif
1091
1092 enum eHeadlessOptions
1093 {
1094 OPT_RAW_R0 = 0x100,
1095 OPT_NO_RAW_R0,
1096 OPT_RAW_R3,
1097 OPT_NO_RAW_R3,
1098 OPT_PATM,
1099 OPT_NO_PATM,
1100 OPT_CSAM,
1101 OPT_NO_CSAM,
1102 OPT_SETTINGSPW,
1103 OPT_SETTINGSPW_FILE,
1104 OPT_COMMENT,
1105 OPT_PAUSED
1106 };
1107
1108 static const RTGETOPTDEF s_aOptions[] =
1109 {
1110 { "-startvm", 's', RTGETOPT_REQ_STRING },
1111 { "--startvm", 's', RTGETOPT_REQ_STRING },
1112 { "-vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1113 { "--vrdpport", 'p', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1114 { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1115 { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1116 { "-vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1117 { "--vrdp", 'v', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
1118 { "-vrde", 'v', RTGETOPT_REQ_STRING },
1119 { "--vrde", 'v', RTGETOPT_REQ_STRING },
1120 { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
1121 { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
1122 { "-rawr0", OPT_RAW_R0, 0 },
1123 { "--rawr0", OPT_RAW_R0, 0 },
1124 { "-norawr0", OPT_NO_RAW_R0, 0 },
1125 { "--norawr0", OPT_NO_RAW_R0, 0 },
1126 { "-rawr3", OPT_RAW_R3, 0 },
1127 { "--rawr3", OPT_RAW_R3, 0 },
1128 { "-norawr3", OPT_NO_RAW_R3, 0 },
1129 { "--norawr3", OPT_NO_RAW_R3, 0 },
1130 { "-patm", OPT_PATM, 0 },
1131 { "--patm", OPT_PATM, 0 },
1132 { "-nopatm", OPT_NO_PATM, 0 },
1133 { "--nopatm", OPT_NO_PATM, 0 },
1134 { "-csam", OPT_CSAM, 0 },
1135 { "--csam", OPT_CSAM, 0 },
1136 { "-nocsam", OPT_NO_CSAM, 0 },
1137 { "--nocsam", OPT_NO_CSAM, 0 },
1138 { "--settingspw", OPT_SETTINGSPW, RTGETOPT_REQ_STRING },
1139 { "--settingspwfile", OPT_SETTINGSPW_FILE, RTGETOPT_REQ_STRING },
1140#ifdef VBOX_WITH_RECORDING
1141 { "-record", 'c', 0 },
1142 { "--record", 'c', 0 },
1143 { "--videowidth", 'w', RTGETOPT_REQ_UINT32 },
1144 { "--videoheight", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
1145 { "--videorate", 'r', RTGETOPT_REQ_UINT32 },
1146 { "--filename", 'f', RTGETOPT_REQ_STRING },
1147#endif /* VBOX_WITH_RECORDING defined */
1148 { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
1149 { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
1150 { "-start-paused", OPT_PAUSED, 0 },
1151 { "--start-paused", OPT_PAUSED, 0 }
1152 };
1153
1154 const char *pcszNameOrUUID = NULL;
1155
1156#ifdef RT_OS_DARWIN
1157 hideSetUidRootFromAppKit();
1158#endif
1159
1160 // parse the command line
1161 int ch;
1162 const char *pcszSettingsPw = NULL;
1163 const char *pcszSettingsPwFile = NULL;
1164 RTGETOPTUNION ValueUnion;
1165 RTGETOPTSTATE GetState;
1166 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
1167 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1168 {
1169 switch(ch)
1170 {
1171 case 's':
1172 pcszNameOrUUID = ValueUnion.psz;
1173 break;
1174 case 'p':
1175 RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
1176 vrdePort = ValueUnion.psz;
1177 break;
1178 case 'a':
1179 RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
1180 vrdeAddress = ValueUnion.psz;
1181 break;
1182 case 'v':
1183 vrdeEnabled = ValueUnion.psz;
1184 break;
1185 case 'e':
1186 if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
1187 aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
1188 else
1189 RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
1190 break;
1191 case OPT_RAW_R0:
1192 fRawR0 = true;
1193 break;
1194 case OPT_NO_RAW_R0:
1195 fRawR0 = false;
1196 break;
1197 case OPT_RAW_R3:
1198 fRawR3 = true;
1199 break;
1200 case OPT_NO_RAW_R3:
1201 fRawR3 = false;
1202 break;
1203 case OPT_PATM:
1204 fPATM = true;
1205 break;
1206 case OPT_NO_PATM:
1207 fPATM = false;
1208 break;
1209 case OPT_CSAM:
1210 fCSAM = true;
1211 break;
1212 case OPT_NO_CSAM:
1213 fCSAM = false;
1214 break;
1215 case OPT_SETTINGSPW:
1216 pcszSettingsPw = ValueUnion.psz;
1217 break;
1218 case OPT_SETTINGSPW_FILE:
1219 pcszSettingsPwFile = ValueUnion.psz;
1220 break;
1221 case OPT_PAUSED:
1222 fPaused = true;
1223 break;
1224#ifdef VBOX_WITH_RECORDING
1225 case 'c':
1226 fRecordEnabled = true;
1227 break;
1228 case 'w':
1229 ulRecordVideoWidth = ValueUnion.u32;
1230 break;
1231 case 'r':
1232 ulRecordVideoRate = ValueUnion.u32;
1233 break;
1234 case 'f':
1235 pszRecordFilenameTemplate = ValueUnion.psz;
1236 break;
1237#endif /* VBOX_WITH_RECORDING defined */
1238 case 'h':
1239#ifdef VBOX_WITH_RECORDING
1240 if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
1241 {
1242 ulRecordVideoHeight = ValueUnion.u32;
1243 break;
1244 }
1245#endif
1246 show_usage();
1247 return 0;
1248 case OPT_COMMENT:
1249 /* nothing to do */
1250 break;
1251 case 'V':
1252 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1253 return 0;
1254 default:
1255 ch = RTGetOptPrintError(ch, &ValueUnion);
1256 show_usage();
1257 return ch;
1258 }
1259 }
1260
1261#ifdef VBOX_WITH_RECORDING
1262 if (ulRecordVideoWidth < 512 || ulRecordVideoWidth > 2048 || ulRecordVideoWidth % 2)
1263 {
1264 LogError("VBoxHeadless: ERROR: please specify an even video frame width between 512 and 2048", 0);
1265 return 1;
1266 }
1267 if (ulRecordVideoHeight < 384 || ulRecordVideoHeight > 1536 || ulRecordVideoHeight % 2)
1268 {
1269 LogError("VBoxHeadless: ERROR: please specify an even video frame height between 384 and 1536", 0);
1270 return 1;
1271 }
1272 if (ulRecordVideoRate < 300000 || ulRecordVideoRate > 1000000)
1273 {
1274 LogError("VBoxHeadless: ERROR: please specify an even video bitrate between 300000 and 1000000", 0);
1275 return 1;
1276 }
1277 /* Make sure we only have %d or %u (or none) in the file name specified */
1278 char *pcPercent = (char*)strchr(pszRecordFilenameTemplate, '%');
1279 if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
1280 {
1281 LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the recording file name.", -1);
1282 return 1;
1283 }
1284 /* And no more than one % in the name */
1285 if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
1286 {
1287 LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the recording file name.", -1);
1288 return 1;
1289 }
1290 RTStrPrintf(&szRecordFilename[0], RTPATH_MAX, pszRecordFilenameTemplate, RTProcSelf());
1291#endif /* defined VBOX_WITH_RECORDING */
1292
1293 if (!pcszNameOrUUID)
1294 {
1295 show_usage();
1296 return 1;
1297 }
1298
1299 HRESULT rc;
1300 int irc;
1301
1302 rc = com::Initialize();
1303#ifdef VBOX_WITH_XPCOM
1304 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
1305 {
1306 char szHome[RTPATH_MAX] = "";
1307 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1308 RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1309 return 1;
1310 }
1311#endif
1312 if (FAILED(rc))
1313 {
1314 RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
1315 return 1;
1316 }
1317
1318 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
1319 ComPtr<IVirtualBox> virtualBox;
1320 ComPtr<ISession> session;
1321 ComPtr<IMachine> machine;
1322 bool fSessionOpened = false;
1323 ComPtr<IEventListener> vboxClientListener;
1324 ComPtr<IEventListener> vboxListener;
1325 ComObjPtr<ConsoleEventListenerImpl> consoleListener;
1326
1327 do
1328 {
1329 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
1330 if (FAILED(rc))
1331 {
1332 RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBoxClient object!\n");
1333 com::ErrorInfo info;
1334 if (!info.isFullAvailable() && !info.isBasicAvailable())
1335 {
1336 com::GluePrintRCMessage(rc);
1337 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
1338 }
1339 else
1340 GluePrintErrorInfo(info);
1341 break;
1342 }
1343
1344 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
1345 if (FAILED(rc))
1346 {
1347 RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
1348 break;
1349 }
1350 rc = pVirtualBoxClient->COMGETTER(Session)(session.asOutParam());
1351 if (FAILED(rc))
1352 {
1353 RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
1354 break;
1355 }
1356
1357 if (pcszSettingsPw)
1358 {
1359 CHECK_ERROR(virtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
1360 if (FAILED(rc))
1361 break;
1362 }
1363 else if (pcszSettingsPwFile)
1364 {
1365 int rcExit = settingsPasswordFile(virtualBox, pcszSettingsPwFile);
1366 if (rcExit != RTEXITCODE_SUCCESS)
1367 break;
1368 }
1369
1370 ComPtr<IMachine> m;
1371
1372 rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
1373 if (FAILED(rc))
1374 {
1375 LogError("Invalid machine name or UUID!\n", rc);
1376 break;
1377 }
1378
1379 Bstr id;
1380 rc = m->COMGETTER(Id)(id.asOutParam());
1381 AssertComRC(rc);
1382 if (FAILED(rc))
1383 break;
1384
1385 Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
1386 Utf8Str(id).c_str()));
1387
1388 // set session name
1389 CHECK_ERROR_BREAK(session, COMSETTER(Name)(Bstr("headless").raw()));
1390 // open a session
1391 CHECK_ERROR_BREAK(m, LockMachine(session, LockType_VM));
1392 fSessionOpened = true;
1393
1394 /* get the console */
1395 ComPtr<IConsole> console;
1396 CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
1397
1398 /* get the mutable machine */
1399 CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));
1400
1401 ComPtr<IDisplay> display;
1402 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
1403
1404#ifdef VBOX_WITH_RECORDING
1405 if (fRecordEnabled)
1406 {
1407 ComPtr<IRecordingSettings> recordingSettings;
1408 CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1409 CHECK_ERROR_BREAK(recordingSettings, COMSETTER(Enabled)(TRUE));
1410
1411 SafeIfaceArray <IRecordingScreenSettings> saRecordScreenScreens;
1412 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordScreenScreens)));
1413
1414 /* Note: For now all screens have the same configuration. */
1415 for (size_t i = 0; i < saRecordScreenScreens.size(); ++i)
1416 {
1417 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(Enabled)(TRUE));
1418 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(Filename)(Bstr(szRecordFilename).raw()));
1419 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoWidth)(ulRecordVideoWidth));
1420 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoHeight)(ulRecordVideoHeight));
1421 CHECK_ERROR_BREAK(saRecordScreenScreens[i], COMSETTER(VideoRate)(ulRecordVideoRate));
1422 }
1423 }
1424#endif /* defined(VBOX_WITH_RECORDING) */
1425
1426 /* get the machine debugger (isn't necessarily available) */
1427 ComPtr <IMachineDebugger> machineDebugger;
1428 console->COMGETTER(Debugger)(machineDebugger.asOutParam());
1429 if (machineDebugger)
1430 {
1431 Log(("Machine debugger available!\n"));
1432 }
1433
1434 if (fRawR0 != ~0U)
1435 {
1436 if (!machineDebugger)
1437 {
1438 RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
1439 break;
1440 }
1441 machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
1442 }
1443 if (fRawR3 != ~0U)
1444 {
1445 if (!machineDebugger)
1446 {
1447 RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
1448 break;
1449 }
1450 machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
1451 }
1452 if (fPATM != ~0U)
1453 {
1454 if (!machineDebugger)
1455 {
1456 RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
1457 break;
1458 }
1459 machineDebugger->COMSETTER(PATMEnabled)(fPATM);
1460 }
1461 if (fCSAM != ~0U)
1462 {
1463 if (!machineDebugger)
1464 {
1465 RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
1466 break;
1467 }
1468 machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
1469 }
1470
1471 /* initialize global references */
1472 gConsole = console;
1473 gEventQ = com::NativeEventQueue::getMainEventQueue();
1474
1475 /* VirtualBoxClient events registration. */
1476 {
1477 ComPtr<IEventSource> pES;
1478 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1479 ComObjPtr<VirtualBoxClientEventListenerImpl> listener;
1480 listener.createObject();
1481 listener->init(new VirtualBoxClientEventListener());
1482 vboxClientListener = listener;
1483 com::SafeArray<VBoxEventType_T> eventTypes;
1484 eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
1485 CHECK_ERROR(pES, RegisterListener(vboxClientListener, ComSafeArrayAsInParam(eventTypes), true));
1486 }
1487
1488 /* Console events registration. */
1489 {
1490 ComPtr<IEventSource> es;
1491 CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
1492 consoleListener.createObject();
1493 consoleListener->init(new ConsoleEventListener());
1494 com::SafeArray<VBoxEventType_T> eventTypes;
1495 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1496 eventTypes.push_back(VBoxEventType_OnStateChanged);
1497 eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
1498 eventTypes.push_back(VBoxEventType_OnCanShowWindow);
1499 eventTypes.push_back(VBoxEventType_OnShowWindow);
1500 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
1501 CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
1502 }
1503
1504 /* Default is to use the VM setting for the VRDE server. */
1505 enum VRDEOption
1506 {
1507 VRDEOption_Config,
1508 VRDEOption_Off,
1509 VRDEOption_On
1510 };
1511 VRDEOption enmVRDEOption = VRDEOption_Config;
1512 BOOL fVRDEEnabled;
1513 ComPtr <IVRDEServer> vrdeServer;
1514 CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
1515 CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));
1516
1517 if (vrdeEnabled != NULL)
1518 {
1519 /* -vrde on|off|config */
1520 if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
1521 enmVRDEOption = VRDEOption_Off;
1522 else if (!strcmp(vrdeEnabled, "on") || !strcmp(vrdeEnabled, "enable"))
1523 enmVRDEOption = VRDEOption_On;
1524 else if (strcmp(vrdeEnabled, "config"))
1525 {
1526 RTPrintf("-vrde requires an argument (on|off|config)\n");
1527 break;
1528 }
1529 }
1530
1531 Log(("VBoxHeadless: enmVRDE %d, fVRDEEnabled %d\n", enmVRDEOption, fVRDEEnabled));
1532
1533 if (enmVRDEOption != VRDEOption_Off)
1534 {
1535 /* Set other specified options. */
1536
1537 /* set VRDE port if requested by the user */
1538 if (vrdePort != NULL)
1539 {
1540 Bstr bstr = vrdePort;
1541 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
1542 }
1543 /* set VRDE address if requested by the user */
1544 if (vrdeAddress != NULL)
1545 {
1546 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
1547 }
1548
1549 /* Set VRDE properties. */
1550 if (cVRDEProperties > 0)
1551 {
1552 for (unsigned i = 0; i < cVRDEProperties; i++)
1553 {
1554 /* Parse 'name=value' */
1555 char *pszProperty = RTStrDup(aVRDEProperties[i]);
1556 if (pszProperty)
1557 {
1558 char *pDelimiter = strchr(pszProperty, '=');
1559 if (pDelimiter)
1560 {
1561 *pDelimiter = '\0';
1562
1563 Bstr bstrName = pszProperty;
1564 Bstr bstrValue = &pDelimiter[1];
1565 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1566 }
1567 else
1568 {
1569 RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
1570 RTStrFree(pszProperty);
1571 rc = E_INVALIDARG;
1572 break;
1573 }
1574 RTStrFree(pszProperty);
1575 }
1576 else
1577 {
1578 RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
1579 rc = E_OUTOFMEMORY;
1580 break;
1581 }
1582 }
1583 if (FAILED(rc))
1584 break;
1585 }
1586
1587 }
1588
1589 if (enmVRDEOption == VRDEOption_On)
1590 {
1591 /* enable VRDE server (only if currently disabled) */
1592 if (!fVRDEEnabled)
1593 {
1594 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
1595 }
1596 }
1597 else if (enmVRDEOption == VRDEOption_Off)
1598 {
1599 /* disable VRDE server (only if currently enabled */
1600 if (fVRDEEnabled)
1601 {
1602 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
1603 }
1604 }
1605
1606 /* Disable the host clipboard before powering up */
1607 console->COMSETTER(UseHostClipboard)(false);
1608
1609 Log(("VBoxHeadless: Powering up the machine...\n"));
1610
1611 ComPtr <IProgress> progress;
1612 if (!fPaused)
1613 CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));
1614 else
1615 CHECK_ERROR_BREAK(console, PowerUpPaused(progress.asOutParam()));
1616
1617 rc = showProgress(progress);
1618 if (FAILED(rc))
1619 {
1620 com::ProgressErrorInfo info(progress);
1621 if (info.isBasicAvailable())
1622 {
1623 RTPrintf("Error: failed to start machine. Error message: %ls\n", info.getText().raw());
1624 }
1625 else
1626 {
1627 RTPrintf("Error: failed to start machine. No error message available!\n");
1628 }
1629 break;
1630 }
1631
1632#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
1633 signal(SIGPIPE, SIG_IGN);
1634 signal(SIGTTOU, SIG_IGN);
1635
1636 struct sigaction sa;
1637 RT_ZERO(sa);
1638 sa.sa_handler = HandleSignal;
1639 sigaction(SIGHUP, &sa, NULL);
1640 sigaction(SIGINT, &sa, NULL);
1641 sigaction(SIGTERM, &sa, NULL);
1642 sigaction(SIGUSR1, &sa, NULL);
1643 sigaction(SIGUSR2, &sa, NULL);
1644#endif
1645
1646#ifdef RT_OS_WINDOWS
1647 /*
1648 * Register windows console signal handler to react to Ctrl-C,
1649 * Ctrl-Break, Close.
1650 */
1651 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1652
1653 /*
1654 * Spawn windows message pump to monitor session events.
1655 */
1656 RTTHREAD hThrMsg;
1657 irc = RTThreadCreate(&hThrMsg,
1658 windowsMessageMonitor, NULL,
1659 0, /* :cbStack */
1660 RTTHREADTYPE_MSG_PUMP, 0,
1661 "MSG");
1662 if (RT_FAILURE(irc)) /* not fatal */
1663 LogRel(("VBoxHeadless: failed to start windows message monitor: %Rrc\n", irc));
1664#endif /* RT_OS_WINDOWS */
1665
1666
1667 /*
1668 * Pump vbox events forever
1669 */
1670 LogRel(("VBoxHeadless: starting event loop\n"));
1671 for (;;)
1672 {
1673 irc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
1674
1675 /*
1676 * interruptEventQueueProcessing from another thread is
1677 * reported as VERR_INTERRUPTED, so check the flag first.
1678 */
1679 if (g_fTerminateFE)
1680 {
1681 LogRel(("VBoxHeadless: processEventQueue: %Rrc, termination requested\n", irc));
1682 break;
1683 }
1684
1685 if (RT_FAILURE(irc))
1686 {
1687 LogRel(("VBoxHeadless: processEventQueue: %Rrc\n", irc));
1688 RTMsgError("event loop: %Rrc", irc);
1689 break;
1690 }
1691 }
1692
1693 Log(("VBoxHeadless: event loop has terminated...\n"));
1694
1695#ifdef VBOX_WITH_RECORDING
1696 if (fRecordEnabled)
1697 {
1698 if (!machine.isNull())
1699 {
1700 ComPtr<IRecordingSettings> recordingSettings;
1701 CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1702 CHECK_ERROR_BREAK(recordingSettings, COMSETTER(Enabled)(FALSE));
1703 }
1704 }
1705#endif /* VBOX_WITH_RECORDING */
1706
1707 /* we don't have to disable VRDE here because we don't save the settings of the VM */
1708 }
1709 while (0);
1710
1711 /*
1712 * Get the machine state.
1713 */
1714 MachineState_T machineState = MachineState_Aborted;
1715 if (!machine.isNull())
1716 {
1717 rc = machine->COMGETTER(State)(&machineState);
1718 if (SUCCEEDED(rc))
1719 Log(("machine state = %RU32\n", machineState));
1720 else
1721 Log(("IMachine::getState: %Rhrc\n", rc));
1722 }
1723 else
1724 {
1725 Log(("machine == NULL\n"));
1726 }
1727
1728 /*
1729 * Turn off the VM if it's running
1730 */
1731 if ( gConsole
1732 && ( machineState == MachineState_Running
1733 || machineState == MachineState_Teleporting
1734 || machineState == MachineState_LiveSnapshotting
1735 /** @todo power off paused VMs too? */
1736 )
1737 )
1738 do
1739 {
1740 consoleListener->getWrapped()->ignorePowerOffEvents(true);
1741
1742 ComPtr<IProgress> pProgress;
1743 if (!machine.isNull())
1744 CHECK_ERROR_BREAK(machine, SaveState(pProgress.asOutParam()));
1745 else
1746 CHECK_ERROR_BREAK(gConsole, PowerDown(pProgress.asOutParam()));
1747
1748 rc = showProgress(pProgress);
1749 if (FAILED(rc))
1750 {
1751 com::ErrorInfo info;
1752 if (!info.isFullAvailable() && !info.isBasicAvailable())
1753 com::GluePrintRCMessage(rc);
1754 else
1755 com::GluePrintErrorInfo(info);
1756 break;
1757 }
1758 } while (0);
1759
1760 /* VirtualBox callback unregistration. */
1761 if (vboxListener)
1762 {
1763 ComPtr<IEventSource> es;
1764 CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
1765 if (!es.isNull())
1766 CHECK_ERROR(es, UnregisterListener(vboxListener));
1767 vboxListener.setNull();
1768 }
1769
1770 /* Console callback unregistration. */
1771 if (consoleListener)
1772 {
1773 ComPtr<IEventSource> es;
1774 CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
1775 if (!es.isNull())
1776 CHECK_ERROR(es, UnregisterListener(consoleListener));
1777 consoleListener.setNull();
1778 }
1779
1780 /* VirtualBoxClient callback unregistration. */
1781 if (vboxClientListener)
1782 {
1783 ComPtr<IEventSource> pES;
1784 CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
1785 if (!pES.isNull())
1786 CHECK_ERROR(pES, UnregisterListener(vboxClientListener));
1787 vboxClientListener.setNull();
1788 }
1789
1790 /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
1791 gConsole = NULL;
1792
1793 if (fSessionOpened)
1794 {
1795 /*
1796 * Close the session. This will also uninitialize the console and
1797 * unregister the callback we've registered before.
1798 */
1799 Log(("VBoxHeadless: Closing the session...\n"));
1800 session->UnlockMachine();
1801 }
1802
1803 /* Must be before com::Shutdown */
1804 session.setNull();
1805 virtualBox.setNull();
1806 pVirtualBoxClient.setNull();
1807 machine.setNull();
1808
1809 com::Shutdown();
1810
1811#ifdef RT_OS_WINDOWS
1812 /* tell the session monitor it can ack WM_ENDSESSION */
1813 if (g_hCanQuit != NIL_RTSEMEVENT)
1814 {
1815 RTSemEventSignal(g_hCanQuit);
1816 }
1817
1818 /* tell the session monitor to quit */
1819 if (g_hWindow != NULL)
1820 {
1821 ::PostMessage(g_hWindow, WM_QUIT, 0, 0);
1822 }
1823#endif
1824
1825 LogRel(("VBoxHeadless: exiting\n"));
1826 return FAILED(rc) ? 1 : 0;
1827}
1828
1829
1830#ifndef VBOX_WITH_HARDENING
1831/**
1832 * Main entry point.
1833 */
1834int main(int argc, char **argv, char **envp)
1835{
1836 // initialize VBox Runtime
1837 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
1838 if (RT_FAILURE(rc))
1839 {
1840 RTPrintf("VBoxHeadless: Runtime Error:\n"
1841 " %Rrc -- %Rrf\n", rc, rc);
1842 switch (rc)
1843 {
1844 case VERR_VM_DRIVER_NOT_INSTALLED:
1845 RTPrintf("Cannot access the kernel driver. Make sure the kernel module has been \n"
1846 "loaded successfully. Aborting ...\n");
1847 break;
1848 default:
1849 break;
1850 }
1851 return 1;
1852 }
1853
1854 return TrustedMain(argc, argv, envp);
1855}
1856#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