VirtualBox

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

Last change on this file since 79812 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • 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 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service.
4 */
5
6/*
7 * Copyright (C) 2012-2019 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 <iprt/errcore.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 Bstr bstrOperationDescription;
134
135 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
136
137 ULONG cOperations = 1;
138 HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations);
139 if (FAILED(hrc))
140 {
141 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
142 RTStrmFlush(g_pStdErr);
143 return hrc;
144 }
145
146 /*
147 * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
148 * to not get intermixed with other (raw) stdout data which might get
149 * written in the meanwhile.
150 */
151 RTStrmPrintf(g_pStdErr, "0%%...");
152 RTStrmFlush(g_pStdErr);
153
154 /* setup signal handling if cancelable */
155 bool fCanceledAlready = false;
156 BOOL fCancelable;
157 hrc = progress->COMGETTER(Cancelable)(&fCancelable);
158 if (FAILED(hrc))
159 fCancelable = FALSE;
160 if (fCancelable)
161 {
162 signal(SIGINT, showProgressSignalHandler);
163#ifdef SIGBREAK
164 signal(SIGBREAK, showProgressSignalHandler);
165#endif
166 }
167
168 hrc = progress->COMGETTER(Completed(&fCompleted));
169 while (SUCCEEDED(hrc))
170 {
171 progress->COMGETTER(Percent(&ulCurrentPercent));
172
173 /* did we cross a 10% mark? */
174 if (ulCurrentPercent / 10 > ulLastPercent / 10)
175 {
176 /* make sure to also print out missed steps */
177 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
178 {
179 if (curVal < 100)
180 {
181 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
182 RTStrmFlush(g_pStdErr);
183 }
184 }
185 ulLastPercent = (ulCurrentPercent / 10) * 10;
186 }
187
188 if (fCompleted)
189 break;
190
191 /* process async cancelation */
192 if (g_fCanceled && !fCanceledAlready)
193 {
194 hrc = progress->Cancel();
195 if (SUCCEEDED(hrc))
196 fCanceledAlready = true;
197 else
198 g_fCanceled = false;
199 }
200
201 /* make sure the loop is not too tight */
202 progress->WaitForCompletion(100);
203
204 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
205 hrc = progress->COMGETTER(Completed(&fCompleted));
206 }
207
208 /* undo signal handling */
209 if (fCancelable)
210 {
211 signal(SIGINT, SIG_DFL);
212#ifdef SIGBREAK
213 signal(SIGBREAK, SIG_DFL);
214#endif
215 }
216
217 /* complete the line. */
218 LONG iRc = E_FAIL;
219 hrc = progress->COMGETTER(ResultCode)(&iRc);
220 if (SUCCEEDED(hrc))
221 {
222 if (SUCCEEDED(iRc))
223 RTStrmPrintf(g_pStdErr, "100%%\n");
224 else if (g_fCanceled)
225 RTStrmPrintf(g_pStdErr, "CANCELED\n");
226 else
227 {
228 RTStrmPrintf(g_pStdErr, "\n");
229 RTStrmPrintf(g_pStdErr, "Progress state: %Rhrc\n", iRc);
230 }
231 hrc = iRc;
232 }
233 else
234 {
235 RTStrmPrintf(g_pStdErr, "\n");
236 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
237 }
238 RTStrmFlush(g_pStdErr);
239 return hrc;
240}
241
242DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType)
243{
244 if ( enmLogType == AUTOSTARTLOGTYPE_VERBOSE
245 && !g_fVerbose)
246 return;
247
248 LogRel(("%s", pszMsg));
249}
250
251static void displayHeader()
252{
253 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"
254 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
255 "All rights reserved.\n\n");
256}
257
258/**
259 * Displays the help.
260 *
261 * @param pszImage Name of program name (image).
262 */
263static void displayHelp(const char *pszImage)
264{
265 AssertPtrReturnVoid(pszImage);
266
267 displayHeader();
268
269 RTStrmPrintf(g_pStdErr,
270 "Usage:\n"
271 " %s [-v|--verbose] [-h|-?|--help]\n"
272 " [-F|--logfile=<file>] [-R|--logrotate=<num>] [-S|--logsize=<bytes>]\n"
273 " [-I|--loginterval=<seconds>]\n"
274 " [-c|--config=<config file>]\n", pszImage);
275
276 RTStrmPrintf(g_pStdErr, "\n"
277 "Options:\n");
278
279 for (unsigned i = 0;
280 i < RT_ELEMENTS(g_aOptions);
281 ++i)
282 {
283 std::string str(g_aOptions[i].pszLong);
284 if (g_aOptions[i].iShort < 1000) /* Don't show short options which are defined by an ID! */
285 {
286 str += ", -";
287 str += g_aOptions[i].iShort;
288 }
289 str += ":";
290
291 const char *pcszDescr = "";
292
293 switch (g_aOptions[i].iShort)
294 {
295 case 'h':
296 pcszDescr = "Print this help message and exit.";
297 break;
298
299#ifdef VBOXAUTOSTART_DAEMONIZE
300 case 'b':
301 pcszDescr = "Run in background (daemon mode).";
302 break;
303#endif
304
305 case 'F':
306 pcszDescr = "Name of file to write log to (no file).";
307 break;
308
309 case 'R':
310 pcszDescr = "Number of log files (0 disables log rotation).";
311 break;
312
313 case 'S':
314 pcszDescr = "Maximum size of a log file to trigger rotation (bytes).";
315 break;
316
317 case 'I':
318 pcszDescr = "Maximum time interval to trigger log rotation (seconds).";
319 break;
320
321 case 'c':
322 pcszDescr = "Name of the configuration file for the global overrides.";
323 break;
324 }
325
326 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
327 }
328
329 RTStrmPrintf(g_pStdErr, "\nUse environment variable VBOXAUTOSTART_RELEASE_LOG for logging options.\n");
330}
331
332int main(int argc, char *argv[])
333{
334 /*
335 * Before we do anything, init the runtime without loading
336 * the support driver.
337 */
338 int rc = RTR3InitExe(argc, &argv, 0);
339 if (RT_FAILURE(rc))
340 return RTMsgInitFailure(rc);
341
342 /*
343 * Parse the global options
344 */
345 int c;
346 const char *pszLogFile = NULL;
347 const char *pszConfigFile = NULL;
348 bool fQuiet = false;
349 bool fStart = false;
350 bool fStop = false;
351 RTGETOPTUNION ValueUnion;
352 RTGETOPTSTATE GetState;
353 RTGetOptInit(&GetState, argc, argv,
354 g_aOptions, RT_ELEMENTS(g_aOptions), 1 /* First */, 0 /*fFlags*/);
355 while ((c = RTGetOpt(&GetState, &ValueUnion)))
356 {
357 switch (c)
358 {
359 case 'h':
360 displayHelp(argv[0]);
361 return 0;
362
363 case 'v':
364 g_fVerbose = true;
365 break;
366
367#ifdef VBOXAUTOSTART_DAEMONIZE
368 case 'b':
369 g_fDaemonize = true;
370 break;
371#endif
372 case 'V':
373 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
374 return 0;
375
376 case 'F':
377 pszLogFile = ValueUnion.psz;
378 break;
379
380 case 'R':
381 g_cHistory = ValueUnion.u32;
382 break;
383
384 case 'S':
385 g_uHistoryFileSize = ValueUnion.u64;
386 break;
387
388 case 'I':
389 g_uHistoryFileTime = ValueUnion.u32;
390 break;
391
392 case 'Q':
393 fQuiet = true;
394 break;
395
396 case 'c':
397 pszConfigFile = ValueUnion.psz;
398 break;
399
400 case 's':
401 fStart = true;
402 break;
403
404 case 'd':
405 fStop = true;
406 break;
407
408 default:
409 return RTGetOptPrintError(c, &ValueUnion);
410 }
411 }
412
413 if (!fStart && !fStop)
414 {
415 displayHelp(argv[0]);
416 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present");
417 }
418 else if (fStart && fStop)
419 {
420 displayHelp(argv[0]);
421 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive");
422 }
423
424 if (!pszConfigFile)
425 {
426 displayHelp(argv[0]);
427 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
428 }
429
430 if (!fQuiet)
431 displayHeader();
432
433 PCFGAST pCfgAst = NULL;
434 char *pszUser = NULL;
435 PCFGAST pCfgAstUser = NULL;
436 PCFGAST pCfgAstPolicy = NULL;
437 bool fAllow = false;
438
439 rc = autostartParseConfig(pszConfigFile, &pCfgAst);
440 if (RT_FAILURE(rc))
441 return RTEXITCODE_FAILURE;
442
443 rc = RTProcQueryUsernameA(RTProcSelf(), &pszUser);
444 if (RT_FAILURE(rc))
445 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query username of the process");
446
447 pCfgAstUser = autostartConfigAstGetByName(pCfgAst, pszUser);
448 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
449
450 /* Check default policy. */
451 if (pCfgAstPolicy)
452 {
453 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
454 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
455 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
456 {
457 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
458 fAllow = true;
459 }
460 else
461 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'default_policy' must be either 'allow' or 'deny'");
462 }
463
464 if ( pCfgAstUser
465 && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
466 {
467 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
468 if (pCfgAstPolicy)
469 {
470 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
471 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
472 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
473 {
474 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true"))
475 fAllow = true;
476 else
477 fAllow = false;
478 }
479 else
480 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'allow' must be either 'true' or 'false'");
481 }
482 }
483 else if (pCfgAstUser)
484 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid config, user is not a compound node");
485
486 if (!fAllow)
487 return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
488
489 RTStrFree(pszUser);
490
491 /* Don't start if the VirtualBox settings directory does not exist. */
492 char szUserHomeDir[RTPATH_MAX];
493 rc = com::GetVBoxUserHomeDirectory(szUserHomeDir, sizeof(szUserHomeDir), false /* fCreateDir */);
494 if (RT_FAILURE(rc))
495 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory: %Rrc", rc);
496 else if (!RTDirExists(szUserHomeDir))
497 return RTEXITCODE_SUCCESS;
498
499 /* create release logger, to stdout */
500 RTERRINFOSTATIC ErrInfo;
501 rc = com::VBoxLogRelCreate("Autostart", g_fDaemonize ? NULL : pszLogFile,
502 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
503 "all", "VBOXAUTOSTART_RELEASE_LOG",
504 RTLOGDEST_STDOUT, UINT32_MAX /* cMaxEntriesPerGroup */,
505 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
506 RTErrInfoInitStatic(&ErrInfo));
507 if (RT_FAILURE(rc))
508 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, rc);
509
510#ifdef VBOXAUTOSTART_DAEMONIZE
511 if (g_fDaemonize)
512 {
513 /* prepare release logging */
514 char szLogFile[RTPATH_MAX];
515
516 if (!pszLogFile || !*pszLogFile)
517 {
518 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
519 if (RT_FAILURE(rc))
520 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not get base directory for logging: %Rrc", rc);
521 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vboxautostart.log");
522 if (RT_FAILURE(rc))
523 return RTMsgErrorExit(RTEXITCODE_FAILURE, "could not construct logging path: %Rrc", rc);
524 pszLogFile = szLogFile;
525 }
526
527 rc = RTProcDaemonizeUsingFork(false /* fNoChDir */, false /* fNoClose */, NULL);
528 if (RT_FAILURE(rc))
529 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to daemonize, rc=%Rrc. exiting.", rc);
530 /* create release logger, to file */
531 rc = com::VBoxLogRelCreate("Autostart", pszLogFile,
532 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
533 "all", "VBOXAUTOSTART_RELEASE_LOG",
534 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
535 g_cHistory, g_uHistoryFileTime, g_uHistoryFileSize,
536 RTErrInfoInitStatic(&ErrInfo));
537 if (RT_FAILURE(rc))
538 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, rc);
539 }
540#endif
541
542 /* Set up COM */
543 rc = autostartSetup();
544 if (RT_FAILURE(rc))
545 return RTEXITCODE_FAILURE;
546
547 RTEXITCODE rcExit;
548 if (fStart)
549 rcExit = autostartStartMain(pCfgAstUser);
550 else
551 {
552 Assert(fStop);
553 rcExit = autostartStopMain(pCfgAstUser);
554 }
555
556 autostartConfigAstDestroy(pCfgAst);
557 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
558
559 autostartShutdown();
560 return rcExit;
561}
562
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