VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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