- Timestamp:
- Aug 2, 2012 11:01:56 AM (13 years ago)
- svn:sync-xref-src-repo-rev:
- 79697
- Location:
- trunk/src/VBox/Frontends/VBoxAutostart
- Files:
-
- 4 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxAutostart/Makefile.kmk
r41999 r42527 22 22 VBoxAutostart_TEMPLATE = VBOXMAINCLIENTEXE 23 23 VBoxAutostart_SOURCES = \ 24 VBoxAutostart.cpp 24 VBoxAutostart.cpp \ 25 VBoxAutostartCfg.cpp \ 26 VBoxAutostartStart.cpp \ 27 VBoxAutostartStop.cpp 25 28 26 29 include $(FILE_KBUILD_SUB_FOOTER) -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp
r42386 r42527 58 58 #include <signal.h> 59 59 60 #include "VBoxAutostart.h" 61 60 62 using namespace com; 61 63 62 /** 63 * Tokenizer instance data for the config data. 64 */ 65 typedef 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 */ 82 typedef 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 90 static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL; 91 static bool g_fVerbose = false; 92 static ComPtr<IVirtualBox> g_pVirtualBox = NULL; 93 static ComPtr<ISession> g_pSession = NULL; 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 68 ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL; 69 bool g_fVerbose = false; 70 ComPtr<IVirtualBox> g_pVirtualBox = NULL; 71 ComPtr<ISession> g_pSession = NULL; 94 72 95 73 /** Logging parameters. */ … … 105 83 */ 106 84 static const RTGETOPTDEF g_aOptions[] = { 107 #if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)85 #ifdef VBOXAUTOSTART_DAEMONIZE 108 86 { "--background", 'b', RTGETOPT_REQ_NOTHING }, 109 87 #endif … … 122 100 123 101 124 static voidserviceLog(const char *pszFormat, ...)102 DECLHIDDEN(void) serviceLog(const char *pszFormat, ...) 125 103 { 126 104 va_list args; … … 133 111 134 112 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 */145 static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)146 {147 int rc = VINF_SUCCESS;148 149 do150 {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 else162 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 on181 * success.182 */183 static 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 else200 rc = VERR_NO_MEMORY;201 }202 else203 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 */226 static 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 token242 * excluding the \0 terminator on success.243 */244 static 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 else278 {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 300 static 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 */326 static 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 else348 {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 */364 static bool autostartConfigTokenizerIsReservedToken(const char *pszToken, size_t cchToken)365 {366 if ( cchToken == 1367 && ( *pszToken == ','368 || *pszToken == '='))369 return true;370 else if ( cchToken > 1371 && ( !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 process384 * is allowed to start VMs automatically during system startup.385 * @param puStartupDelay Where to store the startup delay for the user.386 */387 static 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 do409 {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 else431 {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 else509 {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 536 static 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 */546 static 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 overrides563 * 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 collection571 */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 else644 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 113 } 656 114 … … 703 161 break; 704 162 705 #if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)163 #ifdef VBOXAUTOSTART_DAEMONIZE 706 164 case 'b': 707 165 pcszDescr = "Run in background (daemon mode)."; … … 807 265 break; 808 266 809 #if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)267 #ifdef VBOXAUTOSTART_DAEMONIZE 810 268 case 'b': 811 269 g_fDaemonize = true; … … 901 359 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc); 902 360 903 #if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_DARWIN)361 #ifdef VBOXAUTOSTART_DAEMONIZE 904 362 if (g_fDaemonize) 905 363 { … … 969 427 return RTEXITCODE_FAILURE; 970 428 971 RTEXITCODE rcExit = autostartMain(uStartupDelay); 429 RTEXITCODE rcExit; 430 if (fStart) 431 rcExit = autostartStartMain(uStartupDelay); 432 else 433 { 434 Assert(fStop); 435 rcExit = autostartStopMain(uStartupDelay); 436 } 972 437 973 438 EventQueue::getMainEventQueue()->processEventQueue(0);
Note:
See TracChangeset
for help on using the changeset viewer.