VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp@ 43629

Last change on this file since 43629 was 43378, checked in by vboxsync, 12 years ago

Autostart: Implement stop feature for Linux hosts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/* $Id: VBoxAutostart.cpp 43378 2012-09-20 20:16:58Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service.
4 */
5
6/*
7 * Copyright (C) 2012 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
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <VBox/com/com.h>
23#include <VBox/com/string.h>
24#include <VBox/com/Guid.h>
25#include <VBox/com/array.h>
26#include <VBox/com/ErrorInfo.h>
27#include <VBox/com/errorprint.h>
28
29#include <VBox/com/EventQueue.h>
30#include <VBox/com/listeners.h>
31#include <VBox/com/VirtualBox.h>
32
33#include <VBox/err.h>
34#include <VBox/log.h>
35#include <VBox/version.h>
36
37#include <package-generated.h>
38
39#include <iprt/asm.h>
40#include <iprt/buildconfig.h>
41#include <iprt/critsect.h>
42#include <iprt/getopt.h>
43#include <iprt/initterm.h>
44#include <iprt/message.h>
45#include <iprt/path.h>
46#include <iprt/process.h>
47#include <iprt/semaphore.h>
48#include <iprt/stream.h>
49#include <iprt/string.h>
50#include <iprt/system.h>
51#include <iprt/time.h>
52#include <iprt/ctype.h>
53#include <iprt/dir.h>
54
55#include <algorithm>
56#include <list>
57#include <string>
58#include <signal.h>
59
60#include "VBoxAutostart.h"
61
62using namespace com;
63
64#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
65# define VBOXAUTOSTART_DAEMONIZE
66#endif
67
68ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
69bool g_fVerbose = false;
70ComPtr<IVirtualBox> g_pVirtualBox = NULL;
71ComPtr<ISession> g_pSession = NULL;
72
73/** Logging parameters. */
74static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
75static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
76static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
77
78/** Run in background. */
79static bool g_fDaemonize = false;
80
81/**
82 * Command line arguments.
83 */
84static const RTGETOPTDEF g_aOptions[] = {
85#ifdef VBOXAUTOSTART_DAEMONIZE
86 { "--background", 'b', RTGETOPT_REQ_NOTHING },
87#endif
88 /** For displayHelp(). */
89 { "--help", 'h', RTGETOPT_REQ_NOTHING },
90 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
91 { "--start", 's', RTGETOPT_REQ_NOTHING },
92 { "--stop", 'd', RTGETOPT_REQ_NOTHING },
93 { "--config", 'c', RTGETOPT_REQ_STRING },
94 { "--logfile", 'F', RTGETOPT_REQ_STRING },
95 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
96 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
97 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 },
98 { "--quiet", 'Q', RTGETOPT_REQ_NOTHING }
99};
100
101/** Set by the signal handler. */
102static volatile bool g_fCanceled = false;
103
104
105/**
106 * Signal handler that sets g_fCanceled.
107 *
108 * This can be executed on any thread in the process, on Windows it may even be
109 * a thread dedicated to delivering this signal. Do not doing anything
110 * unnecessary here.
111 */
112static void showProgressSignalHandler(int iSignal)
113{
114 NOREF(iSignal);
115 ASMAtomicWriteBool(&g_fCanceled, true);
116}
117
118/**
119 * Print out progress on the console.
120 *
121 * This runs the main event queue every now and then to prevent piling up
122 * unhandled things (which doesn't cause real problems, just makes things
123 * react a little slower than in the ideal case).
124 */
125DECLHIDDEN(HRESULT) showProgress(ComPtr<IProgress> progress)
126{
127 using namespace com;
128
129 BOOL fCompleted = FALSE;
130 ULONG ulCurrentPercent = 0;
131 ULONG ulLastPercent = 0;
132
133 ULONG ulLastOperationPercent = (ULONG)-1;
134
135 ULONG ulLastOperation = (ULONG)-1;
136 Bstr bstrOperationDescription;
137
138 EventQueue::getMainEventQueue()->processEventQueue(0);
139
140 ULONG cOperations = 1;
141 HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations);
142 if (FAILED(hrc))
143 {
144 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
145 RTStrmFlush(g_pStdErr);
146 return hrc;
147 }
148
149 /*
150 * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
151 * to not get intermixed with other (raw) stdout data which might get
152 * written in the meanwhile.
153 */
154 RTStrmPrintf(g_pStdErr, "0%%...");
155 RTStrmFlush(g_pStdErr);
156
157 /* setup signal handling if cancelable */
158 bool fCanceledAlready = false;
159 BOOL fCancelable;
160 hrc = progress->COMGETTER(Cancelable)(&fCancelable);
161 if (FAILED(hrc))
162 fCancelable = FALSE;
163 if (fCancelable)
164 {
165 signal(SIGINT, showProgressSignalHandler);
166#ifdef SIGBREAK
167 signal(SIGBREAK, showProgressSignalHandler);
168#endif
169 }
170
171 hrc = progress->COMGETTER(Completed(&fCompleted));
172 while (SUCCEEDED(hrc))
173 {
174 progress->COMGETTER(Percent(&ulCurrentPercent));
175
176 /* did we cross a 10% mark? */
177 if (ulCurrentPercent / 10 > ulLastPercent / 10)
178 {
179 /* make sure to also print out missed steps */
180 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
181 {
182 if (curVal < 100)
183 {
184 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
185 RTStrmFlush(g_pStdErr);
186 }
187 }
188 ulLastPercent = (ulCurrentPercent / 10) * 10;
189 }
190
191 if (fCompleted)
192 break;
193
194 /* process async cancelation */
195 if (g_fCanceled && !fCanceledAlready)
196 {
197 hrc = progress->Cancel();
198 if (SUCCEEDED(hrc))
199 fCanceledAlready = true;
200 else
201 g_fCanceled = false;
202 }
203
204 /* make sure the loop is not too tight */
205 progress->WaitForCompletion(100);
206
207 EventQueue::getMainEventQueue()->processEventQueue(0);
208 hrc = progress->COMGETTER(Completed(&fCompleted));
209 }
210
211 /* undo signal handling */
212 if (fCancelable)
213 {
214 signal(SIGINT, SIG_DFL);
215#ifdef SIGBREAK
216 signal(SIGBREAK, SIG_DFL);
217#endif
218 }
219
220 /* complete the line. */
221 LONG iRc = E_FAIL;
222 hrc = progress->COMGETTER(ResultCode)(&iRc);
223 if (SUCCEEDED(hrc))
224 {
225 if (SUCCEEDED(iRc))
226 RTStrmPrintf(g_pStdErr, "100%%\n");
227 else if (g_fCanceled)
228 RTStrmPrintf(g_pStdErr, "CANCELED\n");
229 else
230 {
231 RTStrmPrintf(g_pStdErr, "\n");
232 RTStrmPrintf(g_pStdErr, "Progress state: %Rhrc\n", iRc);
233 }
234 hrc = iRc;
235 }
236 else
237 {
238 RTStrmPrintf(g_pStdErr, "\n");
239 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
240 }
241 RTStrmFlush(g_pStdErr);
242 return hrc;
243}
244
245DECLHIDDEN(const char *) machineStateToName(MachineState_T machineState, bool fShort)
246{
247 switch (machineState)
248 {
249 case MachineState_PoweredOff:
250 return fShort ? "poweroff" : "powered off";
251 case MachineState_Saved:
252 return "saved";
253 case MachineState_Aborted:
254 return "aborted";
255 case MachineState_Teleported:
256 return "teleported";
257 case MachineState_Running:
258 return "running";
259 case MachineState_Paused:
260 return "paused";
261 case MachineState_Stuck:
262 return fShort ? "gurumeditation" : "guru meditation";
263 case MachineState_LiveSnapshotting:
264 return fShort ? "livesnapshotting" : "live snapshotting";
265 case MachineState_Teleporting:
266 return "teleporting";
267 case MachineState_Starting:
268 return "starting";
269 case MachineState_Stopping:
270 return "stopping";
271 case MachineState_Saving:
272 return "saving";
273 case MachineState_Restoring:
274 return "restoring";
275 case MachineState_TeleportingPausedVM:
276 return fShort ? "teleportingpausedvm" : "teleporting paused vm";
277 case MachineState_TeleportingIn:
278 return fShort ? "teleportingin" : "teleporting (incoming)";
279 case MachineState_RestoringSnapshot:
280 return fShort ? "restoringsnapshot" : "restoring snapshot";
281 case MachineState_DeletingSnapshot:
282 return fShort ? "deletingsnapshot" : "deleting snapshot";
283 case MachineState_DeletingSnapshotOnline:
284 return fShort ? "deletingsnapshotlive" : "deleting snapshot live";
285 case MachineState_DeletingSnapshotPaused:
286 return fShort ? "deletingsnapshotlivepaused" : "deleting snapshot live paused";
287 case MachineState_SettingUp:
288 return fShort ? "settingup" : "setting up";
289 default:
290 break;
291 }
292 return "unknown";
293}
294
295DECLHIDDEN(void) serviceLog(const char *pszFormat, ...)
296{
297 va_list args;
298 va_start(args, pszFormat);
299 char *psz = NULL;
300 RTStrAPrintfV(&psz, pszFormat, args);
301 va_end(args);
302
303 LogRel(("%s", psz));
304
305 RTStrFree(psz);
306}
307
308static void displayHeader()
309{
310 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
311 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
312 "All rights reserved.\n\n");
313}
314
315/**
316 * Displays the help.
317 *
318 * @param pszImage Name of program name (image).
319 */
320static void displayHelp(const char *pszImage)
321{
322 AssertPtrReturnVoid(pszImage);
323
324 displayHeader();
325
326 RTStrmPrintf(g_pStdErr,
327 "Usage:\n"
328 " %s [-v|--verbose] [-h|-?|--help]\n"
329 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
330 " [-I|--loginterval=<seconds>]\n"
331 " [-c|--config=<config file>]\n", pszImage);
332
333 RTStrmPrintf(g_pStdErr, "\n"
334 "Options:\n");
335
336 for (unsigned i = 0;
337 i < RT_ELEMENTS(g_aOptions);
338 ++i)
339 {
340 std::string str(g_aOptions[i].pszLong);
341 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
342 {
343 str += ", -";
344 str += g_aOptions[i].iShort;
345 }
346 str += ":";
347
348 const char *pcszDescr = "";
349
350 switch (g_aOptions[i].iShort)
351 {
352 case 'h':
353 pcszDescr = "Print this help message and exit.";
354 break;
355
356#ifdef VBOXAUTOSTART_DAEMONIZE
357 case 'b':
358 pcszDescr = "Run in background (daemon mode).";
359 break;
360#endif
361
362 case 'F':
363 pcszDescr = "Name of file to write log to (no file).";
364 break;
365
366 case 'R':
367 pcszDescr = "Number of log files (0 disables log rotation).";
368 break;
369
370 case 'S':
371 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
372 break;
373
374 case 'I':
375 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
376 break;
377
378 case 'c':
379 pcszDescr = "Name of the configuration file for the global overrides.";
380 break;
381 }
382
383 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
384 }
385
386 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
387}
388
389/**
390 * Creates all global COM objects.
391 *
392 * @return HRESULT
393 */
394static int autostartSetup()
395{
396 serviceLogVerbose(("Setting up ...\n"));
397
398 /*
399 * Setup VirtualBox + session interfaces.
400 */
401 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
402 if (SUCCEEDED(rc))
403 {
404 rc = g_pSession.createInprocObject(CLSID_Session);
405 if (FAILED(rc))
406 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
407 }
408 else
409 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
410
411 if (FAILED(rc))
412 return VERR_COM_OBJECT_NOT_FOUND;
413
414 return VINF_SUCCESS;
415}
416
417static void autostartShutdown()
418{
419 serviceLogVerbose(("Shutting down ...\n"));
420
421 g_pSession.setNull();
422 g_pVirtualBox.setNull();
423}
424
425int main(int argc, char *argv[])
426{
427 /*
428 * Before we do anything, init the runtime without loading
429 * the support driver.
430 */
431 int rc = RTR3InitExe(argc, &argv, 0);
432 if (RT_FAILURE(rc))
433 return RTMsgInitFailure(rc);
434
435 /*
436 * Parse the global options
437 */
438 int c;
439 const char *pszLogFile = NULL;
440 const char *pszConfigFile = NULL;
441 bool fQuiet = false;
442 bool fStart = false;
443 bool fStop = false;
444 RTGETOPTUNION ValueUnion;
445 RTGETOPTSTATE GetState;
446 RTGetOptInit(&GetState, argc, argv,
447 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
448 while ((c = RTGetOpt(&GetState, &ValueUnion)))
449 {
450 switch (c)
451 {
452 case 'h':
453 displayHelp(argv[0]);
454 return 0;
455
456 case 'v':
457 g_fVerbose = true;
458 break;
459
460#ifdef VBOXAUTOSTART_DAEMONIZE
461 case 'b':
462 g_fDaemonize = true;
463 break;
464#endif
465 case 'V':
466 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
467 return 0;
468
469 case 'F':
470 pszLogFile = ValueUnion.psz;
471 break;
472
473 case 'R':
474 g_cHistory = ValueUnion.u32;
475 break;
476
477 case 'S':
478 g_uHistoryFileSize = ValueUnion.u64;
479 break;
480
481 case 'I':
482 g_uHistoryFileTime = ValueUnion.u32;
483 break;
484
485 case 'Q':
486 fQuiet = true;
487 break;
488
489 case 'c':
490 pszConfigFile = ValueUnion.psz;
491 break;
492
493 case 's':
494 fStart = true;
495 break;
496
497 case 'd':
498 fStop = true;
499 break;
500
501 default:
502 return RTGetOptPrintError(c, &ValueUnion);
503 }
504 }
505
506 if (!fStart && !fStop)
507 {
508 displayHelp(argv[0]);
509 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present");
510 }
511 else if (fStart && fStop)
512 {
513 displayHelp(argv[0]);
514 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
515 }
516
517 if (!pszConfigFile)
518 {
519 displayHelp(argv[0]);
520 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
521 }
522
523 if (!fQuiet)
524 displayHeader();
525
526 PCFGAST pCfgAst = NULL;
527 char *pszUser = NULL;
528 PCFGAST pCfgAstUser = NULL;
529 PCFGAST pCfgAstPolicy = NULL;
530 bool fAllow = false;
531
532 rc = autostartParseConfig(pszConfigFile, &pCfgAst);
533 if (RT_FAILURE(rc))
534 return RTEXITCODE_FAILURE;
535
536 rc = RTProcQueryUsernameA(RTProcSelf(), &pszUser);
537 if (RT_FAILURE(rc))
538 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query username of the process");
539
540 pCfgAstUser = autostartConfigAstGetByName(pCfgAst, pszUser);
541 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
542
543 /* Check default policy. */
544 if (pCfgAstPolicy)
545 {
546 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
547 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
548 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
549 {
550 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
551 fAllow = true;
552 }
553 else
554 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'default_policy' must be either 'allow' or 'deny'");
555 }
556
557 if ( pCfgAstUser
558 && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
559 {
560 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
561 if (pCfgAstPolicy)
562 {
563 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
564 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
565 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
566 {
567 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true"))
568 fAllow = true;
569 else
570 fAllow = false;
571 }
572 else
573 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'allow' must be either 'true' or 'false'");
574 }
575 }
576 else if (pCfgAstUser)
577 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid config, user is not a compound node");
578
579 if (!fAllow)
580 return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
581
582 RTStrFree(pszUser);
583
584 /* Don't start if the VirtualBox settings directory does not exist. */
585 char szUserHomeDir[RTPATH_MAX];
586 rc = com::GetVBoxUserHomeDirectory(szUserHomeDir, sizeof(szUserHomeDir), false /* fCreateDir */);
587 if (RT_FAILURE(rc))
588 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory: %Rrc", rc);
589 else if (!RTDirExists(szUserHomeDir))
590 return RTEXITCODE_SUCCESS;
591
592 /* create release logger, to stdout */
593 char szError[RTPATH_MAX + 128];
594 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
595 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
596 "all", "VBOXAUTOSTART_RELEASE_LOG",
597 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
598 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
599 szError, sizeof(szError));
600 if (RT_FAILURE(rc))
601 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
602
603#ifdef VBOXAUTOSTART_DAEMONIZE
604 if (g_fDaemonize)
605 {
606 /* prepare release logging */
607 char szLogFile[RTPATH_MAX];
608
609 if (!pszLogFile || !*pszLogFile)
610 {
611 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
612 if (RT_FAILURE(rc))
613 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
614 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
615 if (RT_FAILURE(rc))
616 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
617 pszLogFile = szLogFile;
618 }
619
620 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, NULL);
621 if (RT_FAILURE(rc))
622 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
623 /* create release logger, to file */
624 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
625 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
626 "all", "VBOXAUTOSTART_RELEASE_LOG",
627 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
628 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
629 szError, sizeof(szError));
630 if (RT_FAILURE(rc))
631 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
632 }
633#endif
634
635 /*
636 * Initialize COM.
637 */
638 using namespace com;
639 HRESULT hrc = com::Initialize();
640# ifdef VBOX_WITH_XPCOM
641 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
642 {
643 char szHome[RTPATH_MAX] = "";
644 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
645 return RTMsgErrorExit(RTEXITCODE_FAILURE,
646 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
647 }
648# endif
649 if (FAILED(hrc))
650 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
651
652 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
653 if (FAILED(hrc))
654 {
655 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
656 com::ErrorInfo info;
657 if (!info.isFullAvailable() && !info.isBasicAvailable())
658 {
659 com::GluePrintRCMessage(hrc);
660 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
661 }
662 else
663 com::GluePrintErrorInfo(info);
664 return RTEXITCODE_FAILURE;
665 }
666
667 rc = autostartSetup();
668 if (RT_FAILURE(rc))
669 return RTEXITCODE_FAILURE;
670
671 RTEXITCODE rcExit;
672 if (fStart)
673 rcExit = autostartStartMain(pCfgAstUser);
674 else
675 {
676 Assert(fStop);
677 rcExit = autostartStopMain(pCfgAstUser);
678 }
679
680 autostartConfigAstDestroy(pCfgAst);
681 EventQueue::getMainEventQueue()->processEventQueue(0);
682
683 autostartShutdown();
684
685 g_pVirtualBoxClient.setNull();
686
687 com::Shutdown();
688
689 return rcExit;
690}
691
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