VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-posix.cpp@ 56743

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

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: VBoxAutostart-posix.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service.
4 */
5
6/*
7 * Copyright (C) 2012-2013 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/NativeEventQueue.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 NativeEventQueue::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 NativeEventQueue::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(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType)
246{
247 va_list args;
248
249 if ( enmLogType == AUTOSTARTLOGTYPE_VERBOSE
250 && !g_fVerbose)
251 return;
252
253 LogRel(("%s", pszMsg));
254}
255
256static void displayHeader()
257{
258 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
259 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
260 "All rights reserved.\n\n");
261}
262
263/**
264 * Displays the help.
265 *
266 * @param pszImage Name of program name (image).
267 */
268static void displayHelp(const char *pszImage)
269{
270 AssertPtrReturnVoid(pszImage);
271
272 displayHeader();
273
274 RTStrmPrintf(g_pStdErr,
275 "Usage:\n"
276 " %s [-v|--verbose] [-h|-?|--help]\n"
277 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
278 " [-I|--loginterval=<seconds>]\n"
279 " [-c|--config=<config file>]\n", pszImage);
280
281 RTStrmPrintf(g_pStdErr, "\n"
282 "Options:\n");
283
284 for (unsigned i = 0;
285 i < RT_ELEMENTS(g_aOptions);
286 ++i)
287 {
288 std::string str(g_aOptions[i].pszLong);
289 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
290 {
291 str += ", -";
292 str += g_aOptions[i].iShort;
293 }
294 str += ":";
295
296 const char *pcszDescr = "";
297
298 switch (g_aOptions[i].iShort)
299 {
300 case 'h':
301 pcszDescr = "Print this help message and exit.";
302 break;
303
304#ifdef VBOXAUTOSTART_DAEMONIZE
305 case 'b':
306 pcszDescr = "Run in background (daemon mode).";
307 break;
308#endif
309
310 case 'F':
311 pcszDescr = "Name of file to write log to (no file).";
312 break;
313
314 case 'R':
315 pcszDescr = "Number of log files (0 disables log rotation).";
316 break;
317
318 case 'S':
319 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
320 break;
321
322 case 'I':
323 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
324 break;
325
326 case 'c':
327 pcszDescr = "Name of the configuration file for the global overrides.";
328 break;
329 }
330
331 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
332 }
333
334 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
335}
336
337int main(int argc, char *argv[])
338{
339 /*
340 * Before we do anything, init the runtime without loading
341 * the support driver.
342 */
343 int rc = RTR3InitExe(argc, &argv, 0);
344 if (RT_FAILURE(rc))
345 return RTMsgInitFailure(rc);
346
347 /*
348 * Parse the global options
349 */
350 int c;
351 const char *pszLogFile = NULL;
352 const char *pszConfigFile = NULL;
353 bool fQuiet = false;
354 bool fStart = false;
355 bool fStop = false;
356 RTGETOPTUNION ValueUnion;
357 RTGETOPTSTATE GetState;
358 RTGetOptInit(&GetState, argc, argv,
359 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
360 while ((c = RTGetOpt(&GetState, &ValueUnion)))
361 {
362 switch (c)
363 {
364 case 'h':
365 displayHelp(argv[0]);
366 return 0;
367
368 case 'v':
369 g_fVerbose = true;
370 break;
371
372#ifdef VBOXAUTOSTART_DAEMONIZE
373 case 'b':
374 g_fDaemonize = true;
375 break;
376#endif
377 case 'V':
378 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
379 return 0;
380
381 case 'F':
382 pszLogFile = ValueUnion.psz;
383 break;
384
385 case 'R':
386 g_cHistory = ValueUnion.u32;
387 break;
388
389 case 'S':
390 g_uHistoryFileSize = ValueUnion.u64;
391 break;
392
393 case 'I':
394 g_uHistoryFileTime = ValueUnion.u32;
395 break;
396
397 case 'Q':
398 fQuiet = true;
399 break;
400
401 case 'c':
402 pszConfigFile = ValueUnion.psz;
403 break;
404
405 case 's':
406 fStart = true;
407 break;
408
409 case 'd':
410 fStop = true;
411 break;
412
413 default:
414 return RTGetOptPrintError(c, &ValueUnion);
415 }
416 }
417
418 if (!fStart && !fStop)
419 {
420 displayHelp(argv[0]);
421 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present");
422 }
423 else if (fStart && fStop)
424 {
425 displayHelp(argv[0]);
426 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
427 }
428
429 if (!pszConfigFile)
430 {
431 displayHelp(argv[0]);
432 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
433 }
434
435 if (!fQuiet)
436 displayHeader();
437
438 PCFGAST pCfgAst = NULL;
439 char *pszUser = NULL;
440 PCFGAST pCfgAstUser = NULL;
441 PCFGAST pCfgAstPolicy = NULL;
442 bool fAllow = false;
443
444 rc = autostartParseConfig(pszConfigFile, &pCfgAst);
445 if (RT_FAILURE(rc))
446 return RTEXITCODE_FAILURE;
447
448 rc = RTProcQueryUsernameA(RTProcSelf(), &pszUser);
449 if (RT_FAILURE(rc))
450 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query username of the process");
451
452 pCfgAstUser = autostartConfigAstGetByName(pCfgAst, pszUser);
453 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
454
455 /* Check default policy. */
456 if (pCfgAstPolicy)
457 {
458 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
459 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
460 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
461 {
462 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
463 fAllow = true;
464 }
465 else
466 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'default_policy' must be either 'allow' or 'deny'");
467 }
468
469 if ( pCfgAstUser
470 && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
471 {
472 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
473 if (pCfgAstPolicy)
474 {
475 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
476 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
477 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
478 {
479 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true"))
480 fAllow = true;
481 else
482 fAllow = false;
483 }
484 else
485 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'allow' must be either 'true' or 'false'");
486 }
487 }
488 else if (pCfgAstUser)
489 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid config, user is not a compound node");
490
491 if (!fAllow)
492 return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
493
494 RTStrFree(pszUser);
495
496 /* Don't start if the VirtualBox settings directory does not exist. */
497 char szUserHomeDir[RTPATH_MAX];
498 rc = com::GetVBoxUserHomeDirectory(szUserHomeDir, sizeof(szUserHomeDir), false /* fCreateDir */);
499 if (RT_FAILURE(rc))
500 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory: %Rrc", rc);
501 else if (!RTDirExists(szUserHomeDir))
502 return RTEXITCODE_SUCCESS;
503
504 /* create release logger, to stdout */
505 char szError[RTPATH_MAX + 128];
506 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
507 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
508 "all", "VBOXAUTOSTART_RELEASE_LOG",
509 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
510 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
511 szError, sizeof(szError));
512 if (RT_FAILURE(rc))
513 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
514
515#ifdef VBOXAUTOSTART_DAEMONIZE
516 if (g_fDaemonize)
517 {
518 /* prepare release logging */
519 char szLogFile[RTPATH_MAX];
520
521 if (!pszLogFile || !*pszLogFile)
522 {
523 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
524 if (RT_FAILURE(rc))
525 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
526 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
527 if (RT_FAILURE(rc))
528 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
529 pszLogFile = szLogFile;
530 }
531
532 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, NULL);
533 if (RT_FAILURE(rc))
534 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
535 /* create release logger, to file */
536 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
537 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
538 "all", "VBOXAUTOSTART_RELEASE_LOG",
539 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
540 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
541 szError, sizeof(szError));
542 if (RT_FAILURE(rc))
543 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, rc);
544 }
545#endif
546
547 /* Set up COM */
548 rc = autostartSetup();
549 if (RT_FAILURE(rc))
550 return RTEXITCODE_FAILURE;
551
552 RTEXITCODE rcExit;
553 if (fStart)
554 rcExit = autostartStartMain(pCfgAstUser);
555 else
556 {
557 Assert(fStop);
558 rcExit = autostartStopMain(pCfgAstUser);
559 }
560
561 autostartConfigAstDestroy(pCfgAst);
562 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
563
564 autostartShutdown();
565 return rcExit;
566}
567
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