VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/main.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 21 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.4 KB
Line 
1/* $Id: main.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - The main() function.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QMessageBox>
31#ifdef VBOX_WS_X11
32# ifndef Q_OS_SOLARIS
33# include <QFontDatabase>
34# endif
35#endif
36
37/* GUI includes: */
38#include "UICommon.h"
39#include "UIStarter.h"
40#include "UIModalWindowManager.h"
41#ifdef VBOX_WS_MAC
42# include "VBoxUtils.h"
43# include "UICocoaApplication.h"
44#endif /* VBOX_WS_MAC */
45
46/* Other VBox includes: */
47#include <iprt/buildconfig.h>
48#include <iprt/stream.h>
49#include <VBox/err.h>
50#include <VBox/version.h>
51#include <VBox/sup.h>
52#if !defined(VBOX_WITH_HARDENING) || !defined(VBOX_RUNTIME_UI)
53# include <iprt/initterm.h>
54# ifdef VBOX_WS_MAC
55# include <iprt/asm.h>
56# endif
57#endif
58#ifdef VBOX_WS_X11
59# include <iprt/env.h>
60#endif
61#ifdef VBOX_WITH_HARDENING
62# include <iprt/ctype.h>
63#endif
64#if defined(VBOX_RUNTIME_UI) && defined(VBOX_WS_MAC)
65# include <iprt/path.h>
66#endif
67
68/* Other includes: */
69#ifdef VBOX_WS_MAC
70# include <dlfcn.h>
71# include <sys/mman.h>
72#endif /* VBOX_WS_MAC */
73#ifdef VBOX_WS_X11
74# include <dlfcn.h>
75# include <unistd.h>
76# include <X11/Xlib.h>
77# if defined(RT_OS_LINUX) && defined(DEBUG)
78# include <signal.h>
79# include <execinfo.h>
80# ifndef __USE_GNU
81# define __USE_GNU
82# endif /* !__USE_GNU */
83# include <ucontext.h>
84# ifdef RT_ARCH_AMD64
85# define REG_PC REG_RIP
86# else /* !RT_ARCH_AMD64 */
87# define REG_PC REG_EIP
88# endif /* !RT_ARCH_AMD64 */
89# endif /* RT_OS_LINUX && DEBUG */
90#endif /* VBOX_WS_X11 */
91
92
93/* XXX Temporarily. Don't rely on the user to hack the Makefile himself! */
94QString g_QStrHintLinuxNoMemory = QApplication::tr(
95 "This error means that the kernel driver was either not able to "
96 "allocate enough memory or that some mapping operation failed."
97 );
98
99QString g_QStrHintLinuxNoDriver = QApplication::tr(
100 "The VirtualBox Linux kernel driver is either not loaded or not set "
101 "up correctly. Please try setting it up again by executing<br/><br/>"
102 " <font color=blue>'/sbin/vboxconfig'</font><br/><br/>"
103 "as root.<br/><br/>"
104 "If your system has EFI Secure Boot enabled you may also need to sign "
105 "the kernel modules (vboxdrv, vboxnetflt, vboxnetadp, vboxpci) before "
106 "you can load them. Please see your Linux system's documentation for "
107 "more information."
108 );
109
110QString g_QStrHintOtherWrongDriverVersion = QApplication::tr(
111 "The VirtualBox kernel modules do not match this version of "
112 "VirtualBox. The installation of VirtualBox was apparently not "
113 "successful. Please try completely uninstalling and reinstalling "
114 "VirtualBox."
115 );
116
117QString g_QStrHintLinuxWrongDriverVersion = QApplication::tr(
118 "The VirtualBox kernel modules do not match this version of "
119 "VirtualBox. The installation of VirtualBox was apparently not "
120 "successful. Executing<br/><br/>"
121 " <font color=blue>'/sbin/vboxconfig'</font><br/><br/>"
122 "may correct this. Make sure that you are not mixing builds "
123 "of VirtualBox from different sources."
124 );
125
126QString g_QStrHintOtherNoDriver = QApplication::tr(
127 "Make sure the kernel module has been loaded successfully."
128 );
129
130/* I hope this isn't (C), (TM) or (R) Microsoft support ;-) */
131QString g_QStrHintReinstall = QApplication::tr(
132 "Please try reinstalling VirtualBox."
133 );
134
135
136#ifdef VBOX_WS_X11
137/** X11: For versions of Xlib which are aware of multi-threaded environments this function
138 * calls for XInitThreads() which initializes Xlib support for concurrent threads.
139 * @returns @c non-zero unless it is unsafe to make multi-threaded calls to Xlib.
140 * @remarks This is a workaround for a bug on old Xlib versions, fixed in commit
141 * 941f02e and released in Xlib version 1.1. We check for the symbol
142 * "xcb_connect" which was introduced in that version. */
143static Status MakeSureMultiThreadingIsSafe()
144{
145 /* Success by default: */
146 Status rc = 1;
147 /* Get a global handle to process symbols: */
148 void *pvProcess = dlopen(0, RTLD_GLOBAL | RTLD_LAZY);
149 /* Initialize multi-thread environment only if we can obtain
150 * an address of xcb_connect symbol in this process: */
151 if (pvProcess && dlsym(pvProcess, "xcb_connect"))
152 rc = XInitThreads();
153 /* Close the handle: */
154 if (pvProcess)
155 dlclose(pvProcess);
156 /* Return result: */
157 return rc;
158}
159
160# if defined(RT_OS_LINUX) && defined(DEBUG)
161/** X11, Linux, Debug: The signal handler that prints out a backtrace of the call stack.
162 * @remarks The code is taken from http://www.linuxjournal.com/article/6391. */
163static void BackTraceSignalHandler(int sig, siginfo_t *pInfo, void *pSecret)
164{
165 void *trace[16];
166 char **messages = (char **)0;
167 int i, iTraceSize = 0;
168 ucontext_t *uc = (ucontext_t *)pSecret;
169
170 /* Do something useful with siginfo_t: */
171 if (sig == SIGSEGV)
172 Log(("GUI: Got signal %d, faulty address is %p, from %p\n",
173 sig, pInfo->si_addr, uc->uc_mcontext.gregs[REG_PC]));
174 /* Or do nothing by default: */
175 else
176 Log(("GUI: Got signal %d\n", sig));
177
178 /* Acquire backtrace of 16 lvls depth: */
179 iTraceSize = backtrace(trace, 16);
180
181 /* Overwrite sigaction with caller's address: */
182 trace[1] = (void *)uc->uc_mcontext.gregs[REG_PC];
183
184 /* Translate the addresses into an array of messages: */
185 messages = backtrace_symbols(trace, iTraceSize);
186
187 /* Skip first stack frame (points here): */
188 Log(("GUI: [bt] Execution path:\n"));
189 for (i = 1; i < iTraceSize; ++i)
190 Log(("GUI: [bt] %s\n", messages[i]));
191
192 exit(0);
193}
194
195/** X11, Linux, Debug: Installs signal handler printing backtrace of the call stack. */
196static void InstallSignalHandler()
197{
198 struct sigaction sa;
199 sa.sa_sigaction = BackTraceSignalHandler;
200 sigemptyset(&sa.sa_mask);
201 sa.sa_flags = SA_RESTART | SA_SIGINFO;
202 sigaction(SIGSEGV, &sa, 0);
203 sigaction(SIGBUS, &sa, 0);
204 sigaction(SIGUSR1, &sa, 0);
205}
206# endif /* RT_OS_LINUX && DEBUG */
207#endif /* VBOX_WS_X11 */
208
209/** Qt5 message handler, function that prints out
210 * debug, warning, critical, fatal and system error messages.
211 * @param type Holds the type of the message.
212 * @param context Holds the message context.
213 * @param strMessage Holds the message body. */
214static void QtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &strMessage)
215{
216 NOREF(context);
217# ifndef VBOX_WS_X11
218 NOREF(strMessage);
219# endif
220 switch (type)
221 {
222 case QtDebugMsg:
223 Log(("Qt DEBUG: %s\n", strMessage.toUtf8().constData()));
224 break;
225 case QtWarningMsg:
226 Log(("Qt WARNING: %s\n", strMessage.toUtf8().constData()));
227# ifdef VBOX_WS_X11
228 /* Needed for instance for the message ``cannot connect to X server'': */
229 RTStrmPrintf(g_pStdErr, "Qt WARNING: %s\n", strMessage.toUtf8().constData());
230# endif
231 break;
232 case QtCriticalMsg:
233 Log(("Qt CRITICAL: %s\n", strMessage.toUtf8().constData()));
234# ifdef VBOX_WS_X11
235 /* Needed for instance for the message ``cannot connect to X server'': */
236 RTStrmPrintf(g_pStdErr, "Qt CRITICAL: %s\n", strMessage.toUtf8().constData());
237# endif
238 break;
239 case QtFatalMsg:
240 Log(("Qt FATAL: %s\n", strMessage.toUtf8().constData()));
241# ifdef VBOX_WS_X11
242 /* Needed for instance for the message ``cannot connect to X server'': */
243 RTStrmPrintf(g_pStdErr, "Qt FATAL: %s\n", strMessage.toUtf8().constData());
244# endif
245# if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
246 case QtInfoMsg:
247 /** @todo ignore? */
248 break;
249# endif
250 }
251}
252
253/** Shows all available command line parameters. */
254static void ShowHelp()
255{
256#ifndef VBOX_RUNTIME_UI
257 static const char s_szTitle[] = VBOX_PRODUCT " VM Selector";
258#else
259 static const char s_szTitle[] = VBOX_PRODUCT " VM Runner";
260#endif
261 static const char s_szUsage[] =
262#ifdef VBOX_RUNTIME_UI
263 "Options:\n"
264 " --startvm <vmname|UUID> start a VM by specifying its UUID or name\n"
265 " --separate start a separate VM process\n"
266 " --normal keep normal (windowed) mode during startup\n"
267 " --fullscreen switch to fullscreen mode during startup\n"
268 " --seamless switch to seamless mode during startup\n"
269 " --scale switch to scale mode during startup\n"
270 " --no-startvm-errormsgbox do not show a message box for VM start errors\n"
271 " --restore-current restore the current snapshot before starting\n"
272 " --no-aggressive-caching delays caching media info in VM processes\n"
273 " --fda <image|none> Mount the specified floppy image\n"
274 " --dvd <image|none> Mount the specified DVD image\n"
275# ifdef VBOX_GUI_WITH_PIDFILE
276 " --pidfile <file> create a pidfile file when a VM is up and running\n"
277# endif /* VBOX_GUI_WITH_PIDFILE */
278# ifdef VBOX_WITH_DEBUGGER_GUI
279 " --dbg enable the GUI debug menu\n"
280 " --debug like --dbg and show debug windows at VM startup\n"
281 " --debug-command-line like --dbg and show command line window at VM startup\n"
282 " --debug-statistics like --dbg and show statistics window at VM startup\n"
283 " --statistics-expand <pat> expand the matching statistics (can be repeated)\n"
284 " --statistics-filter <pat> statistics filter\n"
285 " --no-debug disable the GUI debug menu and debug windows\n"
286 " --start-paused start the VM in the paused state\n"
287 " --start-running start the VM running (for overriding --debug*)\n"
288# endif /* VBOX_WITH_DEBUGGER_GUI */
289 "\n"
290 "Expert options:\n"
291 " --execute-all-in-iem For debugging the interpreted execution mode.\n"
292 " --driverless Do not open the support driver (NEM or IEM mode).\n"
293 " --warp-pct <pct> time warp factor, 100%% (= 1.0) = normal speed\n"
294 "\n"
295# ifdef VBOX_WITH_DEBUGGER_GUI
296 "The following environment (and extra data) variables are evaluated:\n"
297 " VBOX_GUI_DBG_ENABLED (GUI/Dbg/Enabled)\n"
298 " enable the GUI debug menu if set\n"
299 " VBOX_GUI_DBG_AUTO_SHOW (GUI/Dbg/AutoShow)\n"
300 " show debug windows at VM startup\n"
301 " VBOX_GUI_NO_DEBUGGER\n"
302 " disable the GUI debug menu and debug windows\n"
303# endif /* VBOX_WITH_DEBUGGER_GUI */
304#else
305 "No special options.\n"
306 "\n"
307 "If you are looking for --startvm and related options, you need to use VirtualBoxVM.\n"
308#endif
309 ;
310
311 RTPrintf("%s v%s\n"
312 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
313 "\n"
314 "%s",
315 s_szTitle, RTBldCfgVersion(), s_szUsage);
316
317#ifdef RT_OS_WINDOWS
318 /* Show message box. Modify the option list a little
319 * to better make it fit in the upcoming dialog. */
320 char szTitleWithVersion[sizeof(s_szTitle) + 128];
321 char szMsg[sizeof(s_szUsage) + 128];
322 char *pszDst = szMsg;
323 size_t offSrc = 0;
324 while (offSrc < sizeof(s_szUsage) - 1U)
325 {
326 char ch;
327 if ( s_szUsage[offSrc] == ' '
328 && s_szUsage[offSrc + 1] == ' '
329 && ( (ch = s_szUsage[offSrc + 2]) == '-' /* option line */
330 || ch == 'V' /* env.var. line */))
331 {
332 /* Split option lines: */
333 if (ch == '-')
334 {
335 offSrc += 2;
336 size_t cchOption = 0;
337 while ( s_szUsage[offSrc + cchOption] != ' '
338 || s_szUsage[offSrc + cchOption + 1] != ' ')
339 ++cchOption;
340
341 memcpy(pszDst, &s_szUsage[offSrc], cchOption);
342 offSrc += cchOption + 2;
343 pszDst += cchOption;
344 }
345 /* Change environment variable indentation: */
346 else
347 {
348 offSrc += 2;
349 size_t cchLine = 0;
350 while ((ch = s_szUsage[offSrc + cchLine]) != '\n' && ch != '\0')
351 ++cchLine;
352
353 memcpy(pszDst, &s_szUsage[offSrc], cchLine);
354 offSrc += cchLine + 1;
355 pszDst += cchLine;
356 }
357 *pszDst++ = '\n';
358 *pszDst++ = '\t';
359
360 while (s_szUsage[offSrc] == ' ')
361 ++offSrc;
362 }
363
364 /* Copy up to and including end of line: */
365 while ((ch = s_szUsage[offSrc++]) != '\n' && ch != '\0')
366 *pszDst++ = ch;
367 *pszDst++ = ch;
368 }
369 *pszDst = '\0';
370
371 RTStrPrintf(szTitleWithVersion, sizeof(szTitleWithVersion), "%s v%s - Command Line Options", s_szTitle, RTBldCfgVersion());
372 MessageBoxExA(NULL /*hwndOwner*/, szMsg, szTitleWithVersion, MB_OK | MB_ICONINFORMATION,
373 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
374#endif
375}
376
377extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char ** /*envp*/)
378{
379#ifdef RT_OS_WINDOWS
380 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
381#endif
382
383 /* Failed result initially: */
384 int iResultCode = 1;
385
386 /* Start logging: */
387 LogFlowFuncEnter();
388
389 /* Simulate try-catch block: */
390 do
391 {
392#ifdef VBOX_WS_X11
393 /* Make sure multi-threaded environment is safe: */
394 if (!MakeSureMultiThreadingIsSafe())
395 break;
396 /* Force using Qt platform module 'xcb', we have X11 specific code: */
397 RTEnvSet("QT_QPA_PLATFORM", "xcb");
398#endif /* VBOX_WS_X11 */
399
400 /* Console help preprocessing: */
401 bool fHelpShown = false;
402 for (int i = 0; i < argc; ++i)
403 {
404 if ( !strcmp(argv[i], "-h")
405 || !strcmp(argv[i], "-?")
406 || !strcmp(argv[i], "-help")
407 || !strcmp(argv[i], "--help"))
408 {
409 fHelpShown = true;
410 ShowHelp();
411 break;
412 }
413 }
414 if (fHelpShown)
415 {
416 iResultCode = 0;
417 break;
418 }
419
420#ifdef VBOX_WITH_HARDENING
421 /* Make sure the image verification code works: */
422 SUPR3HardenedVerifyInit();
423#endif /* VBOX_WITH_HARDENING */
424
425#ifdef VBOX_WS_MAC
426 /* Apply font fixes (before QApplication get created and instantiated font-hints): */
427 switch (UICommon::determineOsRelease())
428 {
429 case MacOSXRelease_Mavericks: QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); break;
430 case MacOSXRelease_Yosemite: QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Helvetica Neue"); break;
431 case MacOSXRelease_ElCapitan: QFont::insertSubstitution(".SF NS Text", "Helvetica Neue"); break;
432 default: break;
433 }
434
435 /* Instantiate own NSApplication before QApplication do it for us: */
436 UICocoaApplication::instance();
437
438# ifdef VBOX_RUNTIME_UI
439 /* If we're a helper app inside Resources in the main application bundle,
440 we need to amend the library path so the platform plugin can be found.
441 Note! This builds on the initIprtForDarwinHelperApp() hack. */
442 {
443 char szExecDir[RTPATH_MAX];
444 int vrc = RTPathExecDir(szExecDir, sizeof(szExecDir));
445 AssertRC(vrc);
446 RTPathStripTrailingSlash(szExecDir); /* .../Contents/MacOS */
447 RTPathStripFilename(szExecDir); /* .../Contents */
448 RTPathAppend(szExecDir, sizeof(szExecDir), "plugins"); /* .../Contents/plugins */
449 QCoreApplication::addLibraryPath(QString::fromUtf8(szExecDir));
450 }
451# endif
452#endif /* VBOX_WS_MAC */
453
454#ifdef VBOX_WS_X11
455# if defined(RT_OS_LINUX) && defined(DEBUG)
456 /* Install signal handler to backtrace the call stack: */
457 InstallSignalHandler();
458# endif /* RT_OS_LINUX && DEBUG */
459#endif /* VBOX_WS_X11 */
460
461 /* Install Qt console message handler: */
462 qInstallMessageHandler(QtMessageOutput);
463
464 /* Enable HiDPI support: */
465 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
466#if (!defined(DEBUG_bird) || defined(RT_OS_DARWIN))
467# ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
468 /* This shouldn't be enabled for customer WM, since Qt has conflicts in that case. */
469 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
470# endif
471#endif
472
473 /* Create application: */
474 QApplication a(argc, argv);
475
476#ifdef VBOX_WS_WIN
477 /* Drag in the sound drivers and DLLs early to get rid of the delay taking
478 * place when the main menu bar (or any action from that menu bar) is
479 * activated for the first time. This delay is especially annoying if it
480 * happens when the VM is executing in real mode (which gives 100% CPU
481 * load and slows down the load process that happens on the main GUI
482 * thread to several seconds). */
483 PlaySound(NULL, NULL, 0);
484
485#endif /* VBOX_WS_WIN */
486
487#ifdef VBOX_WS_MAC
488 /* Disable menu icons on MacOS X host: */
489 ::darwinDisableIconsInMenus();
490
491#endif /* VBOX_WS_MAC */
492
493#ifdef VBOX_WS_X11
494 /* Make all widget native.
495 * We did it to avoid various Qt crashes while testing widget attributes or acquiring winIds.
496 * Yes, we aware of note that alien widgets faster to draw but the only widget we need to be fast
497 * is viewport of VM which was always native since we are using his id for 3D service needs. */
498 a.setAttribute(Qt::AA_NativeWindows);
499
500# ifdef Q_OS_SOLARIS
501 a.setStyle("fusion");
502# endif /* Q_OS_SOLARIS */
503
504# ifndef Q_OS_SOLARIS
505 /* Apply font fixes (after QApplication get created and instantiated font-family): */
506 QFontDatabase fontDataBase;
507 QString currentFamily(QApplication::font().family());
508 bool isCurrentScaleable = fontDataBase.isScalable(currentFamily);
509 QString subFamily(QFont::substitute(currentFamily));
510 bool isSubScaleable = fontDataBase.isScalable(subFamily);
511 if (isCurrentScaleable && !isSubScaleable)
512 QFont::removeSubstitutions(currentFamily);
513# endif /* !Q_OS_SOLARIS */
514
515 /* Qt version check (major.minor are sensitive, fix number is ignored): */
516 if (UICommon::qtRTVersion() < (UICommon::qtCTVersion() & 0xFFFF00))
517 {
518 QString strMsg = QApplication::tr("Executable <b>%1</b> requires Qt %2.x, found Qt %3.")
519 .arg(qAppName())
520 .arg(UICommon::qtCTVersionString().section('.', 0, 1))
521 .arg(UICommon::qtRTVersionString());
522 QMessageBox::critical(0, QApplication::tr("Incompatible Qt Library Error"),
523 strMsg, QMessageBox::Abort, 0);
524 qFatal("%s", strMsg.toUtf8().constData());
525 break;
526 }
527#endif /* VBOX_WS_X11 */
528
529 /* Create modal-window manager: */
530 UIModalWindowManager::create();
531
532 /* Create UI starter: */
533 UIStarter::create();
534#ifndef VBOX_RUNTIME_UI
535 /* Create global app instance for Selector UI: */
536 UICommon::create(UICommon::UIType_SelectorUI);
537#else
538 /* Create global app instance for Runtime UI: */
539 UICommon::create(UICommon::UIType_RuntimeUI);
540#endif
541
542 /* Simulate try-catch block: */
543 do
544 {
545 /* Exit if UICommon is not valid: */
546 if (!uiCommon().isValid())
547 break;
548
549 /* Init link between UI starter and global app instance: */
550 gStarter->init();
551
552 /* Exit if UICommon pre-processed arguments: */
553 if (uiCommon().processArgs())
554 break;
555
556 // WORKAROUND:
557 // Initially we wanted to make that workaround for Runtime UI only,
558 // because only there we had a strict handling for proper application quit
559 // procedure. But it appeared on X11 (as usually due to an async nature) there
560 // can happen situations that Qt application is checking whether at least one
561 // window is already shown and if not - exits prematurely _before_ it is actually
562 // shown. That can happen for example if window is not yet shown because blocked
563 // by startup error message-box which is not treated as real window by some
564 // reason. So we are making application exit manual everywhere.
565 qApp->setQuitOnLastWindowClosed(false);
566
567 /* Request to Start UI _after_ QApplication executed: */
568 QMetaObject::invokeMethod(gStarter, "sltStartUI", Qt::QueuedConnection);
569
570 /* Start application: */
571 iResultCode = a.exec();
572
573 /* Break link between UI starter and global app instance: */
574 gStarter->deinit();
575 }
576 while (0);
577
578 /* Destroy global app instance: */
579 UICommon::destroy();
580 /* Destroy UI starter: */
581 UIStarter::destroy();
582
583 /* Destroy modal-window manager: */
584 UIModalWindowManager::destroy();
585 }
586 while (0);
587
588 /* Finish logging: */
589 LogFlowFunc(("rc=%d\n", iResultCode));
590 LogFlowFuncLeave();
591
592 /* Return result: */
593 return iResultCode;
594}
595
596#if !defined(VBOX_WITH_HARDENING) || !defined(VBOX_RUNTIME_UI)
597
598# if defined(RT_OS_DARWIN) && defined(VBOX_RUNTIME_UI)
599
600extern "C" const char *_dyld_get_image_name(uint32_t);
601
602/** Init runtime with the executable path pointing into the
603 * VirtualBox.app/Contents/MacOS/ rather than
604 * VirtualBox.app/Contents/Resource/VirtualBoxVM.app/Contents/MacOS/.
605 *
606 * This is a HACK to make codesign and friends happy on OS X. The idea is to
607 * improve and eliminate this over time.
608 */
609DECL_NO_INLINE(static, int) initIprtForDarwinHelperApp(int cArgs, char ***ppapszArgs, uint32_t fFlags)
610{
611 const char *pszImageName = _dyld_get_image_name(0);
612 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
613
614 char szTmpPath[PATH_MAX + 1];
615 const char *psz = realpath(pszImageName, szTmpPath);
616 int rc;
617 if (psz)
618 {
619 char *pszFilename = RTPathFilename(szTmpPath);
620 if (pszFilename)
621 {
622 char const chSavedFilename0 = *pszFilename;
623 *pszFilename = '\0';
624 RTPathStripTrailingSlash(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS */
625 RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/ */
626 RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources/VirtualBoxVM.app */
627 RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents/Resources */
628 RTPathStripFilename(szTmpPath); /* VirtualBox.app/Contents */
629 char *pszDst = strchr(szTmpPath, '\0');
630 pszDst = (char *)memcpy(pszDst, RT_STR_TUPLE("/MacOS/")) + sizeof("/MacOS/") - 1; /** @todo where is mempcpy? */
631 *pszFilename = chSavedFilename0;
632 memmove(pszDst, pszFilename, strlen(pszFilename) + 1);
633
634 return RTR3InitEx(RTR3INIT_VER_CUR, fFlags, cArgs, ppapszArgs, szTmpPath);
635 }
636 rc = VERR_INVALID_NAME;
637 }
638 else
639 rc = RTErrConvertFromErrno(errno);
640 AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszImageName, strlen(pszImageName), pszImageName), rc);
641 return rc;
642}
643# endif
644
645
646int main(int argc, char **argv, char **envp)
647{
648# ifdef VBOX_WS_X11
649 /* Make sure multi-threaded environment is safe: */
650 if (!MakeSureMultiThreadingIsSafe())
651 return 1;
652# endif /* VBOX_WS_X11 */
653
654 /*
655 * Determin the IPRT/SUPLib initialization flags if runtime UI process.
656 * Only initialize SUPLib if about to start a VM in this process.
657 *
658 * Note! This must must match the corresponding parsing in hardenedmain.cpp
659 * and UICommon.cpp exactly, otherwise there will be weird error messages.
660 */
661 /** @todo r=bird: We should consider just postponing this stuff till VM
662 * creation, it shouldn't make too much of a difference GIP-wise. */
663 uint32_t fFlags = 0;
664# ifdef VBOX_RUNTIME_UI
665 unsigned cOptionsLeft = 4;
666 bool fStartVM = false;
667 bool fSeparateProcess = false;
668 bool fExecuteAllInIem = false;
669 bool fDriverless = false;
670 for (int i = 1; i < argc && cOptionsLeft > 0; ++i)
671 {
672 if ( !strcmp(argv[i], "--startvm")
673 || !strcmp(argv[i], "-startvm"))
674 {
675 cOptionsLeft -= fStartVM == false;
676 fStartVM = true;
677 i++;
678 }
679 else if ( !strcmp(argv[i], "--separate")
680 || !strcmp(argv[i], "-separate"))
681 {
682 cOptionsLeft -= fSeparateProcess == false;
683 fSeparateProcess = true;
684 }
685 else if (!strcmp(argv[i], "--execute-all-in-iem"))
686 {
687 cOptionsLeft -= fExecuteAllInIem == false;
688 fExecuteAllInIem = true;
689 }
690 else if (!strcmp(argv[i], "--driverless"))
691 {
692 cOptionsLeft -= fDriverless == false;
693 fDriverless = true;
694 }
695 }
696 if (fStartVM && !fSeparateProcess)
697 {
698 fFlags |= RTR3INIT_FLAGS_TRY_SUPLIB;
699 if (fExecuteAllInIem)
700 fFlags |= SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED << RTR3INIT_FLAGS_SUPLIB_SHIFT;
701 if (fDriverless)
702 fFlags |= SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT;
703 }
704# endif
705
706 /* Initialize VBox Runtime: */
707# if defined(RT_OS_DARWIN) && defined(VBOX_RUNTIME_UI)
708 int rc = initIprtForDarwinHelperApp(argc, &argv, fFlags);
709# else
710 int rc = RTR3InitExe(argc, &argv, fFlags);
711# endif
712 if (RT_FAILURE(rc))
713 {
714 /* Initialization failed: */
715
716 /* We have to create QApplication anyway
717 * just to show the only one error-message: */
718 QApplication a(argc, &argv[0]);
719 Q_UNUSED(a);
720
721# ifdef Q_OS_SOLARIS
722 a.setStyle("fusion");
723# endif
724
725 /* Prepare the error-message: */
726 QString strTitle = QApplication::tr("VirtualBox - Runtime Error");
727 QString strText = "<html>";
728 switch (rc)
729 {
730 case VERR_VM_DRIVER_NOT_INSTALLED:
731 case VERR_VM_DRIVER_LOAD_ERROR:
732 strText += QApplication::tr("<b>Cannot access the kernel driver!</b><br/><br/>");
733# ifdef RT_OS_LINUX
734 strText += g_QStrHintLinuxNoDriver;
735# else
736 strText += g_QStrHintOtherNoDriver;
737# endif
738 break;
739# ifdef RT_OS_LINUX
740 case VERR_NO_MEMORY:
741 strText += g_QStrHintLinuxNoMemory;
742 break;
743# endif
744 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
745 strText += QApplication::tr("Kernel driver not accessible");
746 break;
747 case VERR_VM_DRIVER_VERSION_MISMATCH:
748# ifdef RT_OS_LINUX
749 strText += g_QStrHintLinuxWrongDriverVersion;
750# else
751 strText += g_QStrHintOtherWrongDriverVersion;
752# endif
753 break;
754 default:
755 strText += QApplication::tr("Unknown error %2 during initialization of the Runtime").arg(rc);
756 break;
757 }
758 strText += "</html>";
759
760 /* Show the error-message: */
761 QMessageBox::critical(0 /* parent */, strTitle, strText,
762 QMessageBox::Abort /* 1st button */, 0 /* 2nd button */);
763
764 /* Default error-result: */
765 return 1;
766 }
767
768 /* Call to actual main function: */
769 return TrustedMain(argc, argv, envp);
770}
771
772#endif /* !VBOX_WITH_HARDENING || !VBOX_RUNTIME_UI */
773
774#ifdef VBOX_WITH_HARDENING
775
776/**
777 * Special entrypoint used by the hardening code when something goes south.
778 *
779 * Display an error dialog to the user.
780 *
781 * @param pszWhere Indicates where the error occured.
782 * @param enmWhat Indicates what init operation was going on at the time.
783 * @param rc The VBox status code corresponding to the error.
784 * @param pszMsgFmt The message format string.
785 * @param va Format arguments.
786 */
787extern "C" DECLEXPORT(void) TrustedError(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va)
788{
789 char szMsgBuf[_16K];
790
791 /*
792 * We have to create QApplication anyway just to show the only one error-message.
793 * This is a bit hackish as we don't have the argument vector handy.
794 */
795 int argc = 0;
796 char *argv[2] = { NULL, NULL };
797 QApplication a(argc, &argv[0]);
798
799 /*
800 * The details starts off a properly formatted rc and where/what, we use
801 * the szMsgBuf for this, thus this have to come before the actual message
802 * formatting.
803 */
804 RTStrPrintf(szMsgBuf, sizeof(szMsgBuf),
805 "<!--EOM-->"
806 "where: %s\n"
807 "what: %d\n"
808 "%Rra\n",
809 pszWhere, enmWhat, rc);
810 QString strDetails = szMsgBuf;
811
812 /*
813 * Format the error message. Take whatever comes after a double new line as
814 * something better off in the details section.
815 */
816 RTStrPrintfV(szMsgBuf, sizeof(szMsgBuf), pszMsgFmt, va);
817
818 char *pszDetails = strstr(szMsgBuf, "\n\n");
819 if (pszDetails)
820 {
821 while (RT_C_IS_SPACE(*pszDetails))
822 *pszDetails++ = '\0';
823 if (*pszDetails)
824 {
825 strDetails += "\n";
826 strDetails += pszDetails;
827 }
828 RTStrStripR(szMsgBuf);
829 }
830
831 QString strText = QApplication::tr("<html><b>%1 (rc=%2)</b><br/><br/>").arg(szMsgBuf).arg(rc);
832 strText.replace(QString("\n"), QString("<br>"));
833
834 /*
835 * Append possibly helpful hints to the error message.
836 */
837 switch (enmWhat)
838 {
839 case kSupInitOp_Driver:
840# ifdef RT_OS_LINUX
841 strText += g_QStrHintLinuxNoDriver;
842# else /* RT_OS_LINUX */
843 strText += g_QStrHintOtherNoDriver;
844# endif /* !RT_OS_LINUX */
845 break;
846 case kSupInitOp_IPRT:
847 case kSupInitOp_Misc:
848 if (rc == VERR_VM_DRIVER_VERSION_MISMATCH)
849# ifndef RT_OS_LINUX
850 strText += g_QStrHintOtherWrongDriverVersion;
851# else
852 strText += g_QStrHintLinuxWrongDriverVersion;
853 else if (rc == VERR_NO_MEMORY)
854 strText += g_QStrHintLinuxNoMemory;
855# endif
856 else
857 strText += g_QStrHintReinstall;
858 break;
859 case kSupInitOp_Integrity:
860 case kSupInitOp_RootCheck:
861 strText += g_QStrHintReinstall;
862 break;
863 default:
864 /* no hints here */
865 break;
866 }
867
868# ifdef VBOX_WS_X11
869 /* We have to to make sure that we display the error-message
870 * after the parent displayed its own message. */
871 sleep(2);
872# endif /* VBOX_WS_X11 */
873
874 /* Update strText with strDetails: */
875 if (!strDetails.isEmpty())
876 strText += QString("<br><br>%1").arg(strDetails);
877
878 /* Close the <html> scope: */
879 strText += "</html>";
880
881 /* Create and show the error message-box: */
882 QMessageBox::critical(0, QApplication::tr("VirtualBox - Error In %1").arg(pszWhere), strText);
883
884 qFatal("%s", strText.toUtf8().constData());
885}
886
887#endif /* VBOX_WITH_HARDENING */
Note: See TracBrowser for help on using the repository browser.

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