VirtualBox

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

Last change on this file since 85416 was 83799, checked in by vboxsync, 5 years ago

VBoxAutostart: VC++ 14.1 adjustments. Removed some unnecessary stdc++ headers and eliminated one silly std::string usage. [adjustments] bugref:8489

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