VirtualBox

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

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

Autostart: Don't try to start VMs if the settings directory doesn't exist

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/* $Id: VBoxAutostart.cpp 42386 2012-07-25 11:53:18Z 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
60using namespace com;
61
62/**
63 * Tokenizer instance data for the config data.
64 */
65typedef struct CFGTOKENIZER
66{
67 /** Config file handle. */
68 PRTSTREAM hStrmConfig;
69 /** String buffer for the current line we are operating in. */
70 char *pszLine;
71 /** Size of the string buffer. */
72 size_t cbLine;
73 /** Current position in the line. */
74 char *pszLineCurr;
75 /** Current line in the config file. */
76 unsigned iLine;
77} CFGTOKENIZER, *PCFGTOKENIZER;
78
79/**
80 * VM list entry.
81 */
82typedef struct AUTOSTARTVM
83{
84 /** ID of the VM to start. */
85 Bstr strId;
86 /** Startup delay of the VM. */
87 ULONG uStartupDelay;
88} AUTOSTARTVM;
89
90static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
91static bool g_fVerbose = false;
92static ComPtr<IVirtualBox> g_pVirtualBox = NULL;
93static ComPtr<ISession> g_pSession = NULL;
94
95/** Logging parameters. */
96static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
97static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
98static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
99
100/** Run in background. */
101static bool g_fDaemonize = false;
102
103/**
104 * Command line arguments.
105 */
106static const RTGETOPTDEF g_aOptions[] = {
107#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
108 { "--background", 'b', RTGETOPT_REQ_NOTHING },
109#endif
110 /** For displayHelp(). */
111 { "--help", 'h', RTGETOPT_REQ_NOTHING },
112 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
113 { "--start", 's', RTGETOPT_REQ_NOTHING },
114 { "--stop", 'd', RTGETOPT_REQ_NOTHING },
115 { "--config", 'c', RTGETOPT_REQ_STRING },
116 { "--logfile", 'F', RTGETOPT_REQ_STRING },
117 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
118 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
119 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 },
120 { "--quiet", 'Q', RTGETOPT_REQ_NOTHING }
121};
122
123
124static void serviceLog(const char *pszFormat, ...)
125{
126 va_list args;
127 va_start(args, pszFormat);
128 char *psz = NULL;
129 RTStrAPrintfV(&psz, pszFormat, args);
130 va_end(args);
131
132 LogRel(("%s", psz));
133
134 RTStrFree(psz);
135}
136
137#define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
138
139/**
140 * Reads the next line from the config stream.
141 *
142 * @returns VBox status code.
143 * @param pCfgTokenizer The config tokenizer.
144 */
145static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
146{
147 int rc = VINF_SUCCESS;
148
149 do
150 {
151 rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
152 pCfgTokenizer->cbLine);
153 if (rc == VERR_BUFFER_OVERFLOW)
154 {
155 char *pszTmp;
156
157 pCfgTokenizer->cbLine += 128;
158 pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
159 if (pszTmp)
160 pCfgTokenizer->pszLine = pszTmp;
161 else
162 rc = VERR_NO_MEMORY;
163 }
164 } while (rc == VERR_BUFFER_OVERFLOW);
165
166 if (RT_SUCCESS(rc))
167 {
168 pCfgTokenizer->iLine++;
169 pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
170 }
171
172 return rc;
173}
174
175/**
176 * Creates the config tokenizer from the given filename.
177 *
178 * @returns VBox status code.
179 * @param pszFilename Config filename.
180 * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
181 * success.
182 */
183static int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
184{
185 int rc = VINF_SUCCESS;
186 PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
187
188 if (pCfgTokenizer)
189 {
190 pCfgTokenizer->iLine = 1;
191 pCfgTokenizer->cbLine = 128;
192 pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
193 if (pCfgTokenizer->pszLine)
194 {
195 rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
196 if (RT_SUCCESS(rc))
197 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
198 }
199 else
200 rc = VERR_NO_MEMORY;
201 }
202 else
203 rc = VERR_NO_MEMORY;
204
205 if (RT_SUCCESS(rc))
206 *ppCfgTokenizer = pCfgTokenizer;
207 else if ( RT_FAILURE(rc)
208 && pCfgTokenizer)
209 {
210 if (pCfgTokenizer->pszLine)
211 RTMemFree(pCfgTokenizer->pszLine);
212 if (pCfgTokenizer->hStrmConfig)
213 RTStrmClose(pCfgTokenizer->hStrmConfig);
214 RTMemFree(pCfgTokenizer);
215 }
216
217 return rc;
218}
219
220/**
221 * Destroys the given config tokenizer.
222 *
223 * @returns nothing.
224 * @param pCfgTokenizer The config tokenizer to destroy.
225 */
226static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
227{
228 if (pCfgTokenizer->pszLine)
229 RTMemFree(pCfgTokenizer->pszLine);
230 if (pCfgTokenizer->hStrmConfig)
231 RTStrmClose(pCfgTokenizer->hStrmConfig);
232 RTMemFree(pCfgTokenizer);
233}
234
235/**
236 * Read the next token from the config file.
237 *
238 * @returns VBox status code.
239 * @param pCfgTokenizer The config tokenizer data.
240 * @param ppszToken Where to store the start to the next token on success.
241 * @param pcchToken Where to store the number of characters of the next token
242 * excluding the \0 terminator on success.
243 */
244static int autostartConfigTokenizerReadNext(PCFGTOKENIZER pCfgTokenizer, const char **ppszToken,
245 size_t *pcchToken)
246{
247 if (!pCfgTokenizer->pszLineCurr)
248 return VERR_EOF;
249
250 int rc = VINF_SUCCESS;
251
252 for (;;)
253 {
254 char *pszTok = pCfgTokenizer->pszLineCurr;
255
256 /* Skip all spaces. */
257 while (RT_C_IS_BLANK(*pszTok))
258 pszTok++;
259
260 /* Check if we have to read a new line. */
261 if ( *pszTok == '\0'
262 || *pszTok == '#')
263 {
264 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
265 if (RT_FAILURE(rc))
266 break;
267 /* start from the beginning. */
268 }
269 else if ( *pszTok == '='
270 || *pszTok == ',')
271 {
272 *ppszToken = pszTok;
273 *pcchToken = 1;
274 pCfgTokenizer->pszLineCurr = pszTok + 1;
275 break;
276 }
277 else
278 {
279 /* Get the complete token. */
280 size_t cchToken = 1;
281 char *pszTmp = pszTok + 1;
282
283 while ( RT_C_IS_ALNUM(*pszTmp)
284 || *pszTmp == '_')
285 {
286 pszTmp++;
287 cchToken++;
288 }
289
290 *ppszToken = pszTok;
291 *pcchToken = cchToken;
292 pCfgTokenizer->pszLineCurr = pszTmp;
293 break;
294 }
295 }
296
297 return rc;
298}
299
300static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, const char *pszTokCheck)
301{
302 int rc = VINF_SUCCESS;
303 const char *pszToken = NULL;
304 size_t cchToken = 0;
305
306 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken, &cchToken);
307 if (RT_SUCCESS(rc))
308 {
309 if (RTStrNCmp(pszToken, pszTokCheck, cchToken))
310 {
311 RTMsgError("Unexpected token at line %d, expected '%s'",
312 pCfgTokenizer->iLine, pszTokCheck);
313 rc = VERR_INVALID_PARAMETER;
314 }
315 }
316 return rc;
317}
318
319/**
320 * Returns the start of the next token without consuming it.
321 *
322 * @returns VBox status code.
323 * @param pCfgTokenizer Tokenizer instance data.
324 * @param ppszTok Where to store the start of the next token on success.
325 */
326static int autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer, const char **ppszTok)
327{
328 int rc = VINF_SUCCESS;
329
330 for (;;)
331 {
332 char *pszTok = pCfgTokenizer->pszLineCurr;
333
334 /* Skip all spaces. */
335 while (RT_C_IS_BLANK(*pszTok))
336 pszTok++;
337
338 /* Check if we have to read a new line. */
339 if ( *pszTok == '\0'
340 || *pszTok == '#')
341 {
342 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
343 if (RT_FAILURE(rc))
344 break;
345 /* start from the beginning. */
346 }
347 else
348 {
349 *ppszTok = pszTok;
350 break;
351 }
352 }
353
354 return rc;
355}
356
357/**
358 * Check whether the given token is a reserved token.
359 *
360 * @returns true if the token is reserved or false otherwise.
361 * @param pszToken The token to check.
362 * @param cchToken Size of the token in characters.
363 */
364static bool autostartConfigTokenizerIsReservedToken(const char *pszToken, size_t cchToken)
365{
366 if ( cchToken == 1
367 && ( *pszToken == ','
368 || *pszToken == '='))
369 return true;
370 else if ( cchToken > 1
371 && ( !RTStrNCmp(pszToken, "default_policy", cchToken)
372 || !RTStrNCmp(pszToken, "exception_list", cchToken)))
373 return true;
374
375 return false;
376}
377
378/**
379 * Parse the given configuration file and return the interesting config parameters.
380 *
381 * @returns VBox status code.
382 * @param pszFilename The config file to parse.
383 * @param pfAllowed Where to store the flag whether the user of this process
384 * is allowed to start VMs automatically during system startup.
385 * @param puStartupDelay Where to store the startup delay for the user.
386 */
387static int autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay)
388{
389 int rc = VINF_SUCCESS;
390 char *pszUserProcess = NULL;
391 bool fDefaultAllow = false;
392 bool fInExceptionList = false;
393
394 AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
395 AssertPtrReturn(puStartupDelay, VERR_INVALID_POINTER);
396
397 *pfAllowed = false;
398 *puStartupDelay = 0;
399
400 rc = RTProcQueryUsernameA(RTProcSelf(), &pszUserProcess);
401 if (RT_SUCCESS(rc))
402 {
403 PCFGTOKENIZER pCfgTokenizer = NULL;
404
405 rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
406 if (RT_SUCCESS(rc))
407 {
408 do
409 {
410 size_t cchToken = 0;
411 const char *pszToken = NULL;
412
413 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
414 &cchToken);
415 if (RT_SUCCESS(rc))
416 {
417 if (!RTStrNCmp(pszToken, "default_policy", strlen("default_policy")))
418 {
419 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
420 if (RT_SUCCESS(rc))
421 {
422 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
423 &cchToken);
424 if (RT_SUCCESS(rc))
425 {
426 if (!RTStrNCmp(pszToken, "allow", strlen("allow")))
427 fDefaultAllow = true;
428 else if (!RTStrNCmp(pszToken, "deny", strlen("deny")))
429 fDefaultAllow = false;
430 else
431 {
432 RTMsgError("Unexpected token at line %d, expected either 'allow' or 'deny'",
433 pCfgTokenizer->iLine);
434 rc = VERR_INVALID_PARAMETER;
435 break;
436 }
437 }
438 }
439 }
440 else if (!RTStrNCmp(pszToken, "exception_list", strlen("exception_list")))
441 {
442 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
443 if (RT_SUCCESS(rc))
444 {
445 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
446 &cchToken);
447
448 while (RT_SUCCESS(rc))
449 {
450 if (autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
451 {
452 RTMsgError("Unexpected token at line %d, expected a username",
453 pCfgTokenizer->iLine);
454 rc = VERR_INVALID_PARAMETER;
455 break;
456 }
457 else if (!RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
458 fInExceptionList = true;
459
460 /* Skip , */
461 rc = autostartConfigTokenizerPeek(pCfgTokenizer, &pszToken);
462 if ( RT_SUCCESS(rc)
463 && *pszToken == ',')
464 {
465 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, ",");
466 AssertRC(rc);
467 }
468 else if (RT_SUCCESS(rc))
469 break;
470
471 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
472 &cchToken);
473 }
474
475 if (rc == VERR_EOF)
476 rc = VINF_SUCCESS;
477 }
478 }
479 else if (!autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
480 {
481 /* Treat as 'username = <base delay in seconds>. */
482 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
483 if (RT_SUCCESS(rc))
484 {
485 size_t cchDelay = 0;
486 const char *pszDelay = NULL;
487
488 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszDelay,
489 &cchDelay);
490 if (RT_SUCCESS(rc))
491 {
492 uint32_t uDelay = 0;
493
494 rc = RTStrToUInt32Ex(pszDelay, NULL, 10, &uDelay);
495 if (rc == VWRN_TRAILING_SPACES)
496 rc = VINF_SUCCESS;
497
498 if ( RT_SUCCESS(rc)
499 && !RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
500 *puStartupDelay = uDelay;
501
502 if (RT_FAILURE(rc))
503 RTMsgError("Unexpected token at line %d, expected a number",
504 pCfgTokenizer->iLine);
505 }
506 }
507 }
508 else
509 {
510 RTMsgError("Unexpected token at line %d, expected a username",
511 pCfgTokenizer->iLine);
512 rc = VERR_INVALID_PARAMETER;
513 }
514 }
515 else if (rc == VERR_EOF)
516 {
517 rc = VINF_SUCCESS;
518 break;
519 }
520 } while (RT_SUCCESS(rc));
521
522 if ( RT_SUCCESS(rc)
523 && ( (fDefaultAllow && !fInExceptionList)
524 || (!fDefaultAllow && fInExceptionList)))
525 *pfAllowed= true;
526
527 autostartConfigTokenizerDestroy(pCfgTokenizer);
528 }
529
530 RTStrFree(pszUserProcess);
531 }
532
533 return rc;
534}
535
536static DECLCALLBACK(bool) autostartVMCmp(const AUTOSTARTVM &vm1, const AUTOSTARTVM &vm2)
537{
538 return vm1.uStartupDelay <= vm2.uStartupDelay;
539}
540
541/**
542 * Main routine for the autostart daemon.
543 *
544 * @returns exit status code.
545 */
546static RTEXITCODE autostartMain(uint32_t uStartupDelay)
547{
548 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
549 int vrc = VINF_SUCCESS;
550 std::list<AUTOSTARTVM> listVM;
551
552 if (uStartupDelay)
553 {
554 serviceLogVerbose(("Delay starting for %d seconds ...\n", uStartupDelay));
555 vrc = RTThreadSleep(uStartupDelay * 1000);
556 }
557
558 if (vrc == VERR_INTERRUPTED)
559 return RTEXITCODE_SUCCESS;
560
561 /*
562 * Build a list of all VMs we need to autostart first, apply the overrides
563 * from the configuration and start the VMs afterwards.
564 */
565 com::SafeIfaceArray<IMachine> machines;
566 HRESULT rc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
567 if (SUCCEEDED(rc))
568 {
569 /*
570 * Iterate through the collection
571 */
572 for (size_t i = 0; i < machines.size(); ++i)
573 {
574 if (machines[i])
575 {
576 BOOL fAccessible;
577 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
578 if (!fAccessible)
579 continue;
580
581 BOOL fAutostart;
582 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartEnabled)(&fAutostart));
583 if (fAutostart)
584 {
585 AUTOSTARTVM autostartVM;
586
587 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostartVM.strId.asOutParam()));
588 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartDelay)(&autostartVM.uStartupDelay));
589
590 listVM.push_back(autostartVM);
591 }
592 }
593 }
594
595 if ( SUCCEEDED(rc)
596 && listVM.size())
597 {
598 ULONG uDelayCurr = 0;
599
600 /* Sort by startup delay and apply base override. */
601 listVM.sort(autostartVMCmp);
602
603 std::list<AUTOSTARTVM>::iterator it;
604 for (it = listVM.begin(); it != listVM.end(); it++)
605 {
606 ComPtr<IMachine> machine;
607 ComPtr<IProgress> progress;
608
609 if ((*it).uStartupDelay > uDelayCurr)
610 {
611 serviceLogVerbose(("Delay starting of the next VMs for %d seconds ...\n",
612 (*it).uStartupDelay - uDelayCurr));
613 RTThreadSleep(((*it).uStartupDelay - uDelayCurr) * 1000);
614 uDelayCurr = (*it).uStartupDelay;
615 }
616
617 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
618 machine.asOutParam()));
619
620 CHECK_ERROR_BREAK(machine, LaunchVMProcess(g_pSession, Bstr("headless").raw(),
621 Bstr("").raw(), progress.asOutParam()));
622 if (SUCCEEDED(rc) && !progress.isNull())
623 {
624 serviceLogVerbose(("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw()));
625 CHECK_ERROR(progress, WaitForCompletion(-1));
626 if (SUCCEEDED(rc))
627 {
628 BOOL completed = true;
629 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
630 if (SUCCEEDED(rc))
631 {
632 ASSERT(completed);
633
634 LONG iRc;
635 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
636 if (SUCCEEDED(rc))
637 {
638 if (FAILED(iRc))
639 {
640 ProgressErrorInfo info(progress);
641 com::GluePrintErrorInfo(info);
642 }
643 else
644 serviceLogVerbose(("VM \"%ls\" has been successfully started.\n", (*it).strId.raw()));
645 }
646 }
647 }
648 }
649 g_pSession->UnlockMachine();
650 }
651 }
652 }
653
654 return rcExit;
655}
656
657static void displayHeader()
658{
659 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
660 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
661 "All rights reserved.\n\n");
662}
663
664/**
665 * Displays the help.
666 *
667 * @param pszImage Name of program name (image).
668 */
669static void displayHelp(const char *pszImage)
670{
671 AssertPtrReturnVoid(pszImage);
672
673 displayHeader();
674
675 RTStrmPrintf(g_pStdErr,
676 "Usage:\n"
677 " %s [-v|--verbose] [-h|-?|--help]\n"
678 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
679 " [-I|--loginterval=<seconds>]\n"
680 " [-c|--config=<config file>]\n", pszImage);
681
682 RTStrmPrintf(g_pStdErr, "\n"
683 "Options:\n");
684
685 for (unsigned i = 0;
686 i < RT_ELEMENTS(g_aOptions);
687 ++i)
688 {
689 std::string str(g_aOptions[i].pszLong);
690 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
691 {
692 str += ", -";
693 str += g_aOptions[i].iShort;
694 }
695 str += ":";
696
697 const char *pcszDescr = "";
698
699 switch (g_aOptions[i].iShort)
700 {
701 case 'h':
702 pcszDescr = "Print this help message and exit.";
703 break;
704
705#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
706 case 'b':
707 pcszDescr = "Run in background (daemon mode).";
708 break;
709#endif
710
711 case 'F':
712 pcszDescr = "Name of file to write log to (no file).";
713 break;
714
715 case 'R':
716 pcszDescr = "Number of log files (0 disables log rotation).";
717 break;
718
719 case 'S':
720 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
721 break;
722
723 case 'I':
724 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
725 break;
726
727 case 'c':
728 pcszDescr = "Name of the configuration file for the global overrides.";
729 break;
730 }
731
732 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
733 }
734
735 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
736}
737
738/**
739 * Creates all global COM objects.
740 *
741 * @return HRESULT
742 */
743static int autostartSetup()
744{
745 serviceLogVerbose(("Setting up ...\n"));
746
747 /*
748 * Setup VirtualBox + session interfaces.
749 */
750 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
751 if (SUCCEEDED(rc))
752 {
753 rc = g_pSession.createInprocObject(CLSID_Session);
754 if (FAILED(rc))
755 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
756 }
757 else
758 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
759
760 if (FAILED(rc))
761 return VERR_COM_OBJECT_NOT_FOUND;
762
763 return VINF_SUCCESS;
764}
765
766static void autostartShutdown()
767{
768 serviceLogVerbose(("Shutting down ...\n"));
769
770 g_pSession.setNull();
771 g_pVirtualBox.setNull();
772}
773
774int main(int argc, char *argv[])
775{
776 /*
777 * Before we do anything, init the runtime without loading
778 * the support driver.
779 */
780 int rc = RTR3InitExe(argc, &argv, 0);
781 if (RT_FAILURE(rc))
782 return RTMsgInitFailure(rc);
783
784 /*
785 * Parse the global options
786 */
787 int c;
788 const char *pszLogFile = NULL;
789 const char *pszConfigFile = NULL;
790 bool fQuiet = false;
791 bool fStart = false;
792 bool fStop = false;
793 RTGETOPTUNION ValueUnion;
794 RTGETOPTSTATE GetState;
795 RTGetOptInit(&GetState, argc, argv,
796 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
797 while ((c = RTGetOpt(&GetState, &ValueUnion)))
798 {
799 switch (c)
800 {
801 case 'h':
802 displayHelp(argv[0]);
803 return 0;
804
805 case 'v':
806 g_fVerbose = true;
807 break;
808
809#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
810 case 'b':
811 g_fDaemonize = true;
812 break;
813#endif
814 case 'V':
815 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
816 return 0;
817
818 case 'F':
819 pszLogFile = ValueUnion.psz;
820 break;
821
822 case 'R':
823 g_cHistory = ValueUnion.u32;
824 break;
825
826 case 'S':
827 g_uHistoryFileSize = ValueUnion.u64;
828 break;
829
830 case 'I':
831 g_uHistoryFileTime = ValueUnion.u32;
832 break;
833
834 case 'Q':
835 fQuiet = true;
836 break;
837
838 case 'c':
839 pszConfigFile = ValueUnion.psz;
840 break;
841
842 case 's':
843 fStart = true;
844 break;
845
846 case 'd':
847 fStop = true;
848 break;
849
850 default:
851 return RTGetOptPrintError(c, &ValueUnion);
852 }
853 }
854
855 if (!fStart && !fStop)
856 {
857 displayHelp(argv[0]);
858 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present");
859 }
860 else if (fStart && fStop)
861 {
862 displayHelp(argv[0]);
863 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
864 }
865
866 if (!pszConfigFile)
867 {
868 displayHelp(argv[0]);
869 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
870 }
871
872 if (!fQuiet)
873 displayHeader();
874
875 bool fAllowed = false;
876 uint32_t uStartupDelay = 0;
877 rc = autostartParseConfig(pszConfigFile, &fAllowed, &uStartupDelay);
878 if (RT_FAILURE(rc))
879 return RTEXITCODE_FAILURE;
880
881 if (!fAllowed)
882 return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
883
884 /* Don't start if the VirtualBox settings directory does not exist. */
885 char szUserHomeDir[RTPATH_MAX];
886 rc = com::GetVBoxUserHomeDirectory(szUserHomeDir, sizeof(szUserHomeDir), false /* fCreateDir */);
887 if (RT_FAILURE(rc))
888 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory: %Rrc", rc);
889 else if (!RTDirExists(szUserHomeDir))
890 return RTEXITCODE_SUCCESS;
891
892 /* create release logger, to stdout */
893 char szError[RTPATH_MAX + 128];
894 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
895 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
896 "all", "VBOXAUTOSTART_RELEASE_LOG",
897 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
898 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
899 szError, sizeof(szError));
900 if (RT_FAILURE(rc))
901 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
902
903#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)
904 if (g_fDaemonize)
905 {
906 /* prepare release logging */
907 char szLogFile[RTPATH_MAX];
908
909 if (!pszLogFile || !*pszLogFile)
910 {
911 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
912 if (RT_FAILURE(rc))
913 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
914 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
915 if (RT_FAILURE(rc))
916 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
917 pszLogFile = szLogFile;
918 }
919
920 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, NULL);
921 if (RT_FAILURE(rc))
922 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
923 /* create release logger, to file */
924 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
925 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
926 "all", "VBOXAUTOSTART_RELEASE_LOG",
927 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
928 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
929 szError, sizeof(szError));
930 if (RT_FAILURE(rc))
931 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
932 }
933#endif
934
935 /*
936 * Initialize COM.
937 */
938 using namespace com;
939 HRESULT hrc = com::Initialize();
940# ifdef VBOX_WITH_XPCOM
941 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
942 {
943 char szHome[RTPATH_MAX] = "";
944 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
945 return RTMsgErrorExit(RTEXITCODE_FAILURE,
946 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
947 }
948# endif
949 if (FAILED(hrc))
950 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
951
952 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
953 if (FAILED(hrc))
954 {
955 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
956 com::ErrorInfo info;
957 if (!info.isFullAvailable() && !info.isBasicAvailable())
958 {
959 com::GluePrintRCMessage(hrc);
960 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
961 }
962 else
963 com::GluePrintErrorInfo(info);
964 return RTEXITCODE_FAILURE;
965 }
966
967 rc = autostartSetup();
968 if (RT_FAILURE(rc))
969 return RTEXITCODE_FAILURE;
970
971 RTEXITCODE rcExit = autostartMain(uStartupDelay);
972
973 EventQueue::getMainEventQueue()->processEventQueue(0);
974
975 autostartShutdown();
976
977 g_pVirtualBoxClient.setNull();
978
979 com::Shutdown();
980
981 return rcExit;
982}
983
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