VirtualBox

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

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

More autostart service updates, work in progress

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: VBoxAutostart.cpp 41999 2012-07-03 12:50: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
53
54#include <algorithm>
55#include <list>
56#include <string>
57#include <signal.h>
58
59using namespace com;
60
61/**
62 * VM list entry.
63 */
64typedef struct AUTOSTARTVM
65{
66 Bstr strId;
67 ULONG uStartupDelay;
68} AUTOSTARTVM;
69
70static ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
71static bool g_fVerbose = false;
72static ComPtr<IVirtualBox> g_pVirtualBox = NULL;
73static ComPtr<ISession> g_pSession = NULL;
74
75/** Logging parameters. */
76static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
77static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
78static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
79
80/** Run in background. */
81static bool g_fDaemonize = false;
82
83/**
84 * Command line arguments.
85 */
86static const RTGETOPTDEF g_aOptions[] = {
87#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
88 { "--background", 'b', RTGETOPT_REQ_NOTHING },
89#endif
90 /** For displayHelp(). */
91 { "--help", 'h', RTGETOPT_REQ_NOTHING },
92 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
93 { "--start", 's', RTGETOPT_REQ_NOTHING },
94 { "--stop", 'd', RTGETOPT_REQ_NOTHING },
95 { "--config", 'c', RTGETOPT_REQ_STRING },
96 { "--logfile", 'F', RTGETOPT_REQ_STRING },
97 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
98 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
99 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 },
100 { "--quiet", 'Q', RTGETOPT_REQ_NOTHING }
101};
102
103
104static void serviceLog(const char *pszFormat, ...)
105{
106 va_list args;
107 va_start(args, pszFormat);
108 char *psz = NULL;
109 RTStrAPrintfV(&psz, pszFormat, args);
110 va_end(args);
111
112 LogRel(("%s", psz));
113
114 RTStrFree(psz);
115}
116
117#define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
118
119static DECLCALLBACK(bool) autostartVMCmp(const AUTOSTARTVM &vm1, const AUTOSTARTVM &vm2)
120{
121 return vm1.uStartupDelay <= vm2.uStartupDelay;
122}
123
124/**
125 * Main routine for the autostart daemon.
126 *
127 * @returns exit status code.
128 */
129static RTEXITCODE autostartMain(void)
130{
131 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
132 std::list<AUTOSTARTVM> listVM;
133
134 /*
135 * Build a list of all VMs we need to autostart first, apply the overrides
136 * from the configuration and start the VMs afterwards.
137 */
138 com::SafeIfaceArray<IMachine> machines;
139 HRESULT rc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
140 if (SUCCEEDED(rc))
141 {
142 /*
143 * Iterate through the collection
144 */
145 for (size_t i = 0; i < machines.size(); ++i)
146 {
147 if (machines[i])
148 {
149 BOOL fAccessible;
150 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
151 if (!fAccessible)
152 continue;
153
154 BOOL fAutostart;
155 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartEnabled)(&fAutostart));
156 if (fAutostart)
157 {
158 AUTOSTARTVM autostartVM;
159
160 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostartVM.strId.asOutParam()));
161 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartDelay)(&autostartVM.uStartupDelay));
162
163 listVM.push_back(autostartVM);
164 }
165 }
166 }
167
168 if ( SUCCEEDED(rc)
169 && listVM.size())
170 {
171 /* Sort by startup delay and apply base override. */
172 listVM.sort(autostartVMCmp);
173
174 std::list<AUTOSTARTVM>::iterator it;
175 for (it = listVM.begin(); it != listVM.end(); it++)
176 {
177 ComPtr<IMachine> machine;
178 ComPtr<IProgress> progress;
179
180 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
181 machine.asOutParam()));
182
183 CHECK_ERROR_BREAK(machine, LaunchVMProcess(g_pSession, Bstr("headless").raw(),
184 Bstr("").raw(), progress.asOutParam()));
185 if (SUCCEEDED(rc) && !progress.isNull())
186 {
187 RTPrintf("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw());
188 CHECK_ERROR(progress, WaitForCompletion(-1));
189 if (SUCCEEDED(rc))
190 {
191 BOOL completed = true;
192 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
193 if (SUCCEEDED(rc))
194 {
195 ASSERT(completed);
196
197 LONG iRc;
198 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
199 if (SUCCEEDED(rc))
200 {
201 if (FAILED(iRc))
202 {
203 ProgressErrorInfo info(progress);
204 com::GluePrintErrorInfo(info);
205 }
206 else
207 {
208 RTPrintf("VM \"%ls\" has been successfully started.\n", (*it).strId.raw());
209 }
210 }
211 }
212 }
213 }
214 g_pSession->UnlockMachine();
215 }
216 }
217 }
218
219 return rcExit;
220}
221
222static void displayHeader()
223{
224 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
225 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
226 "All rights reserved.\n\n");
227}
228
229/**
230 * Displays the help.
231 *
232 * @param pszImage Name of program name (image).
233 */
234static void displayHelp(const char *pszImage)
235{
236 AssertPtrReturnVoid(pszImage);
237
238 displayHeader();
239
240 RTStrmPrintf(g_pStdErr,
241 "Usage:\n"
242 " %s [-v|--verbose] [-h|-?|--help]\n"
243 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
244 " [-I|--loginterval=<seconds>]\n"
245 " [-c|--config=<config file>]\n", pszImage);
246
247 RTStrmPrintf(g_pStdErr, "\n"
248 "Options:\n");
249
250 for (unsigned i = 0;
251 i < RT_ELEMENTS(g_aOptions);
252 ++i)
253 {
254 std::string str(g_aOptions[i].pszLong);
255 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
256 {
257 str += ", -";
258 str += g_aOptions[i].iShort;
259 }
260 str += ":";
261
262 const char *pcszDescr = "";
263
264 switch (g_aOptions[i].iShort)
265 {
266 case 'h':
267 pcszDescr = "Print this help message and exit.";
268 break;
269
270#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
271 case 'b':
272 pcszDescr = "Run in background (daemon mode).";
273 break;
274#endif
275 case 'P':
276 pcszDescr = "Name of the PID file which is created when the daemon was started.";
277 break;
278
279 case 'F':
280 pcszDescr = "Name of file to write log to (no file).";
281 break;
282
283 case 'R':
284 pcszDescr = "Number of log files (0 disables log rotation).";
285 break;
286
287 case 'S':
288 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
289 break;
290
291 case 'I':
292 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
293 break;
294
295 case 'c':
296 pcszDescr = "Name of the configuration file for the global overrides.";
297 break;
298 }
299
300 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
301 }
302
303 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
304}
305
306/**
307 * Creates all global COM objects.
308 *
309 * @return HRESULT
310 */
311static int autostartSetup()
312{
313 serviceLogVerbose(("Setting up ...\n"));
314
315 /*
316 * Setup VirtualBox + session interfaces.
317 */
318 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
319 if (SUCCEEDED(rc))
320 {
321 rc = g_pSession.createInprocObject(CLSID_Session);
322 if (FAILED(rc))
323 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
324 }
325 else
326 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
327
328 if (FAILED(rc))
329 return VERR_COM_OBJECT_NOT_FOUND;
330
331 return VINF_SUCCESS;
332}
333
334static void autostartShutdown()
335{
336 serviceLogVerbose(("Shutting down ...\n"));
337
338 g_pSession.setNull();
339 g_pVirtualBox.setNull();
340}
341
342int main(int argc, char *argv[])
343{
344 /*
345 * Before we do anything, init the runtime without loading
346 * the support driver.
347 */
348 int rc = RTR3InitExe(argc, &argv, 0);
349 if (RT_FAILURE(rc))
350 return RTMsgInitFailure(rc);
351
352 /*
353 * Parse the global options
354 */
355 int c;
356 const char *pszLogFile = NULL;
357 const char *pszConfigFile = NULL;
358 bool fQuiet = false;
359 bool fStart = false;
360 bool fStop = false;
361 RTGETOPTUNION ValueUnion;
362 RTGETOPTSTATE GetState;
363 RTGetOptInit(&GetState, argc, argv,
364 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
365 while ((c = RTGetOpt(&GetState, &ValueUnion)))
366 {
367 switch (c)
368 {
369 case 'h':
370 displayHelp(argv[0]);
371 return 0;
372
373 case 'v':
374 g_fVerbose = true;
375 break;
376
377#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
378 case 'b':
379 g_fDaemonize = true;
380 break;
381#endif
382 case 'V':
383 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
384 return 0;
385
386 case 'P':
387 pszPidFile = ValueUnion.psz;
388 break;
389
390 case 'F':
391 pszLogFile = ValueUnion.psz;
392 break;
393
394 case 'R':
395 g_cHistory = ValueUnion.u32;
396 break;
397
398 case 'S':
399 g_uHistoryFileSize = ValueUnion.u64;
400 break;
401
402 case 'I':
403 g_uHistoryFileTime = ValueUnion.u32;
404 break;
405
406 case 'Q':
407 fQuiet = true;
408 break;
409
410 case 'c':
411 pszConfigFile = ValueUnion.psz;
412 break;
413
414 case 's':
415 fStart = true;
416 break;
417
418 case 'd':
419 fStop = true;
420 break;
421
422 default:
423 return RTGetOptPrintError(c, &ValueUnion);
424 }
425 }
426
427 if (fStart == fStop)
428 {
429 displayHelp(argv[0]);
430 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
431 }
432
433 if (!fQuiet)
434 displayHeader();
435
436 /* create release logger, to stdout */
437 char szError[RTPATH_MAX + 128];
438 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
439 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
440 "all", "VBOXAUTOSTART_RELEASE_LOG",
441 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
442 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
443 szError, sizeof(szError));
444 if (RT_FAILURE(rc))
445 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
446
447#if defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
448 if (g_fDaemonize)
449 {
450 /* prepare release logging */
451 char szLogFile[RTPATH_MAX];
452
453 if (!pszLogFile || !*pszLogFile)
454 {
455 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
456 if (RT_FAILURE(rc))
457 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
458 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
459 if (RT_FAILURE(rc))
460 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
461 pszLogFile = szLogFile;
462 }
463
464 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, pszPidFile);
465 if (RT_FAILURE(rc))
466 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
467 /* create release logger, to file */
468 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
469 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
470 "all", "VBOXAUTOSTART_RELEASE_LOG",
471 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
472 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
473 szError, sizeof(szError));
474 if (RT_FAILURE(rc))
475 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
476 }
477#endif
478
479 /*
480 * Initialize COM.
481 */
482 using namespace com;
483 HRESULT hrc = com::Initialize();
484# ifdef VBOX_WITH_XPCOM
485 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
486 {
487 char szHome[RTPATH_MAX] = "";
488 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
489 return RTMsgErrorExit(RTEXITCODE_FAILURE,
490 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
491 }
492# endif
493 if (FAILED(hrc))
494 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
495
496 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
497 if (FAILED(hrc))
498 {
499 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
500 com::ErrorInfo info;
501 if (!info.isFullAvailable() && !info.isBasicAvailable())
502 {
503 com::GluePrintRCMessage(hrc);
504 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
505 }
506 else
507 com::GluePrintErrorInfo(info);
508 return RTEXITCODE_FAILURE;
509 }
510
511 rc = autostartSetup();
512 if (RT_FAILURE(rc))
513 return RTEXITCODE_FAILURE;
514
515 RTEXITCODE rcExit = autostartMain();
516
517 EventQueue::getMainEventQueue()->processEventQueue(0);
518
519 autostartShutdown();
520
521 g_pVirtualBoxClient.setNull();
522
523 com::Shutdown();
524
525 return rcExit;
526}
527
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