VirtualBox

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

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

VBoxAutostart: Rewritten config parser for more flexibility in the future, changes config format

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: VBoxAutostart.cpp 42732 2012-08-09 22:32:48Z 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
102DECLHIDDEN(void) serviceLog(const char *pszFormat, ...)
103{
104 va_list args;
105 va_start(args, pszFormat);
106 char *psz = NULL;
107 RTStrAPrintfV(&psz, pszFormat, args);
108 va_end(args);
109
110 LogRel(("%s", psz));
111
112 RTStrFree(psz);
113}
114
115static void displayHeader()
116{
117 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
118 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
119 "All rights reserved.\n\n");
120}
121
122/**
123 * Displays the help.
124 *
125 * @param pszImage Name of program name (image).
126 */
127static void displayHelp(const char *pszImage)
128{
129 AssertPtrReturnVoid(pszImage);
130
131 displayHeader();
132
133 RTStrmPrintf(g_pStdErr,
134 "Usage:\n"
135 " %s [-v|--verbose] [-h|-?|--help]\n"
136 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
137 " [-I|--loginterval=<seconds>]\n"
138 " [-c|--config=<config file>]\n", pszImage);
139
140 RTStrmPrintf(g_pStdErr, "\n"
141 "Options:\n");
142
143 for (unsigned i = 0;
144 i < RT_ELEMENTS(g_aOptions);
145 ++i)
146 {
147 std::string str(g_aOptions[i].pszLong);
148 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
149 {
150 str += ", -";
151 str += g_aOptions[i].iShort;
152 }
153 str += ":";
154
155 const char *pcszDescr = "";
156
157 switch (g_aOptions[i].iShort)
158 {
159 case 'h':
160 pcszDescr = "Print this help message and exit.";
161 break;
162
163#ifdef VBOXAUTOSTART_DAEMONIZE
164 case 'b':
165 pcszDescr = "Run in background (daemon mode).";
166 break;
167#endif
168
169 case 'F':
170 pcszDescr = "Name of file to write log to (no file).";
171 break;
172
173 case 'R':
174 pcszDescr = "Number of log files (0 disables log rotation).";
175 break;
176
177 case 'S':
178 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
179 break;
180
181 case 'I':
182 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
183 break;
184
185 case 'c':
186 pcszDescr = "Name of the configuration file for the global overrides.";
187 break;
188 }
189
190 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
191 }
192
193 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
194}
195
196/**
197 * Creates all global COM objects.
198 *
199 * @return HRESULT
200 */
201static int autostartSetup()
202{
203 serviceLogVerbose(("Setting up ...\n"));
204
205 /*
206 * Setup VirtualBox + session interfaces.
207 */
208 HRESULT rc = g_pVirtualBoxClient->COMGETTER(VirtualBox)(g_pVirtualBox.asOutParam());
209 if (SUCCEEDED(rc))
210 {
211 rc = g_pSession.createInprocObject(CLSID_Session);
212 if (FAILED(rc))
213 RTMsgError("Failed to create a session object (rc=%Rhrc)!", rc);
214 }
215 else
216 RTMsgError("Failed to get VirtualBox object (rc=%Rhrc)!", rc);
217
218 if (FAILED(rc))
219 return VERR_COM_OBJECT_NOT_FOUND;
220
221 return VINF_SUCCESS;
222}
223
224static void autostartShutdown()
225{
226 serviceLogVerbose(("Shutting down ...\n"));
227
228 g_pSession.setNull();
229 g_pVirtualBox.setNull();
230}
231
232int main(int argc, char *argv[])
233{
234 /*
235 * Before we do anything, init the runtime without loading
236 * the support driver.
237 */
238 int rc = RTR3InitExe(argc, &argv, 0);
239 if (RT_FAILURE(rc))
240 return RTMsgInitFailure(rc);
241
242 /*
243 * Parse the global options
244 */
245 int c;
246 const char *pszLogFile = NULL;
247 const char *pszConfigFile = NULL;
248 bool fQuiet = false;
249 bool fStart = false;
250 bool fStop = false;
251 RTGETOPTUNION ValueUnion;
252 RTGETOPTSTATE GetState;
253 RTGetOptInit(&GetState, argc, argv,
254 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
255 while ((c = RTGetOpt(&GetState, &ValueUnion)))
256 {
257 switch (c)
258 {
259 case 'h':
260 displayHelp(argv[0]);
261 return 0;
262
263 case 'v':
264 g_fVerbose = true;
265 break;
266
267#ifdef VBOXAUTOSTART_DAEMONIZE
268 case 'b':
269 g_fDaemonize = true;
270 break;
271#endif
272 case 'V':
273 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
274 return 0;
275
276 case 'F':
277 pszLogFile = ValueUnion.psz;
278 break;
279
280 case 'R':
281 g_cHistory = ValueUnion.u32;
282 break;
283
284 case 'S':
285 g_uHistoryFileSize = ValueUnion.u64;
286 break;
287
288 case 'I':
289 g_uHistoryFileTime = ValueUnion.u32;
290 break;
291
292 case 'Q':
293 fQuiet = true;
294 break;
295
296 case 'c':
297 pszConfigFile = ValueUnion.psz;
298 break;
299
300 case 's':
301 fStart = true;
302 break;
303
304 case 'd':
305 fStop = true;
306 break;
307
308 default:
309 return RTGetOptPrintError(c, &ValueUnion);
310 }
311 }
312
313 if (!fStart && !fStop)
314 {
315 displayHelp(argv[0]);
316 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present");
317 }
318 else if (fStart && fStop)
319 {
320 displayHelp(argv[0]);
321 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
322 }
323
324 if (!pszConfigFile)
325 {
326 displayHelp(argv[0]);
327 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
328 }
329
330 if (!fQuiet)
331 displayHeader();
332
333 PCFGAST pCfgAst = NULL;
334 char *pszUser = NULL;
335 PCFGAST pCfgAstUser = NULL;
336 PCFGAST pCfgAstPolicy = NULL;
337 bool fAllow = false;
338
339 rc = autostartParseConfig(pszConfigFile, &pCfgAst);
340 if (RT_FAILURE(rc))
341 return RTEXITCODE_FAILURE;
342
343 rc = RTProcQueryUsernameA(RTProcSelf(), &pszUser);
344 if (RT_FAILURE(rc))
345 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query username of the process");
346
347 pCfgAstUser = autostartConfigAstGetByName(pCfgAst, pszUser);
348 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
349
350 /* Check default policy. */
351 if (pCfgAstPolicy)
352 {
353 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
354 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
355 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
356 {
357 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
358 fAllow = true;
359 }
360 else
361 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'default_policy' must be either 'allow' or 'deny'");
362 }
363
364 if ( pCfgAstUser
365 && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
366 {
367 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
368 if (pCfgAstPolicy)
369 {
370 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
371 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
372 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
373 {
374 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true"))
375 fAllow = true;
376 else
377 fAllow = false;
378 }
379 else
380 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'allow' must be either 'true' or 'false'");
381 }
382 }
383 else if (pCfgAstUser)
384 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid config, user is not a compound node");
385
386 if (!fAllow)
387 return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
388
389 RTStrFree(pszUser);
390
391 /* Don't start if the VirtualBox settings directory does not exist. */
392 char szUserHomeDir[RTPATH_MAX];
393 rc = com::GetVBoxUserHomeDirectory(szUserHomeDir, sizeof(szUserHomeDir), false /* fCreateDir */);
394 if (RT_FAILURE(rc))
395 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory: %Rrc", rc);
396 else if (!RTDirExists(szUserHomeDir))
397 return RTEXITCODE_SUCCESS;
398
399 /* create release logger, to stdout */
400 char szError[RTPATH_MAX + 128];
401 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
402 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
403 "all", "VBOXAUTOSTART_RELEASE_LOG",
404 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
405 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
406 szError, sizeof(szError));
407 if (RT_FAILURE(rc))
408 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
409
410#ifdef VBOXAUTOSTART_DAEMONIZE
411 if (g_fDaemonize)
412 {
413 /* prepare release logging */
414 char szLogFile[RTPATH_MAX];
415
416 if (!pszLogFile || !*pszLogFile)
417 {
418 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
419 if (RT_FAILURE(rc))
420 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
421 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
422 if (RT_FAILURE(rc))
423 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
424 pszLogFile = szLogFile;
425 }
426
427 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, NULL);
428 if (RT_FAILURE(rc))
429 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
430 /* create release logger, to file */
431 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
432 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
433 "all", "VBOXAUTOSTART_RELEASE_LOG",
434 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
435 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
436 szError, sizeof(szError));
437 if (RT_FAILURE(rc))
438 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
439 }
440#endif
441
442 /*
443 * Initialize COM.
444 */
445 using namespace com;
446 HRESULT hrc = com::Initialize();
447# ifdef VBOX_WITH_XPCOM
448 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
449 {
450 char szHome[RTPATH_MAX] = "";
451 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
452 return RTMsgErrorExit(RTEXITCODE_FAILURE,
453 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
454 }
455# endif
456 if (FAILED(hrc))
457 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
458
459 hrc = g_pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
460 if (FAILED(hrc))
461 {
462 RTMsgError("Failed to create the VirtualBoxClient object (%Rhrc)!", hrc);
463 com::ErrorInfo info;
464 if (!info.isFullAvailable() && !info.isBasicAvailable())
465 {
466 com::GluePrintRCMessage(hrc);
467 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
468 }
469 else
470 com::GluePrintErrorInfo(info);
471 return RTEXITCODE_FAILURE;
472 }
473
474 rc = autostartSetup();
475 if (RT_FAILURE(rc))
476 return RTEXITCODE_FAILURE;
477
478 RTEXITCODE rcExit;
479 if (fStart)
480 rcExit = autostartStartMain(pCfgAstUser);
481 else
482 {
483 Assert(fStop);
484 rcExit = autostartStopMain(pCfgAstUser);
485 }
486
487 autostartConfigAstDestroy(pCfgAst);
488 EventQueue::getMainEventQueue()->processEventQueue(0);
489
490 autostartShutdown();
491
492 g_pVirtualBoxClient.setNull();
493
494 com::Shutdown();
495
496 return rcExit;
497}
498
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