VirtualBox

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

Last change on this file since 95105 was 95105, checked in by vboxsync, 3 years ago

FE/VBoxAutostart: Lots of small cleanups here and there, mostly in the logging and documentation area. Added more syntax help to make more clear how to actually use this binary. Also added support for logging verbosity levels and output those stdout (if available) [forgot to commit a file].

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