VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxService.cpp@ 54010

Last change on this file since 54010 was 54010, checked in by vboxsync, 10 years ago

Additions/common/VBoxGuestLib: add respawn counter to Daemonize().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.6 KB
Line 
1/* $Id: VBoxService.cpp 54010 2015-01-28 12:21:54Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
7 * Copyright (C) 2007-2014 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/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23/** @todo LOG_GROUP*/
24#ifndef _MSC_VER
25# include <unistd.h>
26#endif
27#include <errno.h>
28#ifndef RT_OS_WINDOWS
29# include <signal.h>
30# ifdef RT_OS_OS2
31# define pthread_sigmask sigprocmask
32# endif
33#endif
34#ifdef RT_OS_FREEBSD
35# include <pthread.h>
36#endif
37
38#include <package-generated.h>
39#include "product-generated.h"
40
41#include <iprt/asm.h>
42#include <iprt/buildconfig.h>
43#include <iprt/initterm.h>
44#include <iprt/file.h>
45#ifdef DEBUG
46# include <iprt/memtracker.h>
47#endif
48#include <iprt/message.h>
49#include <iprt/path.h>
50#include <iprt/process.h>
51#include <iprt/semaphore.h>
52#include <iprt/string.h>
53#include <iprt/stream.h>
54#include <iprt/system.h>
55#include <iprt/thread.h>
56
57#include <VBox/log.h>
58
59#include "VBoxServiceInternal.h"
60#ifdef VBOX_WITH_GUEST_CONTROL
61# include "VBoxServiceControl.h"
62#endif
63
64
65/*******************************************************************************
66* Global Variables *
67*******************************************************************************/
68/** The program name (derived from argv[0]). */
69char *g_pszProgName = (char *)"";
70/** The current verbosity level. */
71int g_cVerbosity = 0;
72char g_szLogFile[RTPATH_MAX + 128] = "";
73/** Logging parameters. */
74/** @todo Make this configurable later. */
75static PRTLOGGER g_pLoggerRelease = NULL;
76static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
77static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
78static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
79/** Critical section for (debug) logging. */
80#ifdef DEBUG
81 RTCRITSECT g_csLog;
82#endif
83/** The default service interval (the -i | --interval) option). */
84uint32_t g_DefaultInterval = 0;
85#ifdef RT_OS_WINDOWS
86/** Signal shutdown to the Windows service thread. */
87static bool volatile g_fWindowsServiceShutdown;
88/** Event the Windows service thread waits for shutdown. */
89static RTSEMEVENT g_hEvtWindowsService;
90#endif
91
92/**
93 * The details of the services that has been compiled in.
94 */
95static struct
96{
97 /** Pointer to the service descriptor. */
98 PCVBOXSERVICE pDesc;
99 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
100 RTTHREAD Thread;
101 /** Whether Pre-init was called. */
102 bool fPreInited;
103 /** Shutdown indicator. */
104 bool volatile fShutdown;
105 /** Indicator set by the service thread exiting. */
106 bool volatile fStopped;
107 /** Whether the service was started or not. */
108 bool fStarted;
109 /** Whether the service is enabled or not. */
110 bool fEnabled;
111} g_aServices[] =
112{
113#ifdef VBOXSERVICE_CONTROL
114 { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
115#endif
116#ifdef VBOXSERVICE_TIMESYNC
117 { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
118#endif
119#ifdef VBOXSERVICE_CLIPBOARD
120 { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
121#endif
122#ifdef VBOXSERVICE_VMINFO
123 { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
124#endif
125#ifdef VBOXSERVICE_CPUHOTPLUG
126 { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
127#endif
128#ifdef VBOXSERVICE_MANAGEMENT
129# ifdef VBOX_WITH_MEMBALLOON
130 { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
131# endif
132 { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
133#endif
134#if defined(VBOXSERVICE_PAGE_SHARING)
135 { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
136#endif
137#ifdef VBOX_WITH_SHARED_FOLDERS
138 { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
139#endif
140#ifdef VBOXSERVICE_WITH_DISPLAY
141 { &g_Display, NIL_RTTHREAD, false, false, false, false, true },
142#endif
143};
144
145/* Default call-backs for services which do not need special behaviour. */
146
147/** @copydoc VBOXSERVICE::pfnPreInit */
148DECLCALLBACK(int) VBoxServiceDefaultPreInit(void)
149{
150 return VINF_SUCCESS;
151}
152
153/** @copydoc VBOXSERVICE::pfnOption */
154DECLCALLBACK(int) VBoxServiceDefaultOption(const char **ppszShort, int argc,
155 char **argv, int *pi)
156{
157 NOREF(ppszShort);
158 NOREF(argc);
159 NOREF(argv);
160 NOREF(pi);
161
162 return -1;
163}
164
165/** @copydoc VBOXSERVICE::pfnInit */
166DECLCALLBACK(int) VBoxServiceDefaultInit(void)
167{
168 return VINF_SUCCESS;
169}
170
171/** @copydoc VBOXSERVICE::pfnTerm */
172DECLCALLBACK(void) VBoxServiceDefaultTerm(void)
173{
174 return;
175}
176
177/**
178 * Release logger callback.
179 *
180 * @return IPRT status code.
181 * @param pLoggerRelease
182 * @param enmPhase
183 * @param pfnLog
184 */
185static void VBoxServiceLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
186{
187 /* Some introductory information. */
188 static RTTIMESPEC s_TimeSpec;
189 char szTmp[256];
190 if (enmPhase == RTLOGPHASE_BEGIN)
191 RTTimeNow(&s_TimeSpec);
192 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
193
194 switch (enmPhase)
195 {
196 case RTLOGPHASE_BEGIN:
197 {
198 pfnLog(pLoggerRelease,
199 "VBoxService %s r%s (verbosity: %d) %s (%s %s) release log\n"
200 "Log opened %s\n",
201 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity, VBOX_BUILD_TARGET,
202 __DATE__, __TIME__, szTmp);
203
204 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
205 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
206 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
207 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
208 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
209 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
210 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
211 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
212 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
213 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
214 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
215
216 /* the package type is interesting for Linux distributions */
217 char szExecName[RTPATH_MAX];
218 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
219 pfnLog(pLoggerRelease,
220 "Executable: %s\n"
221 "Process ID: %u\n"
222 "Package type: %s"
223#ifdef VBOX_OSE
224 " (OSE)"
225#endif
226 "\n",
227 pszExecName ? pszExecName : "unknown",
228 RTProcSelf(),
229 VBOX_PACKAGE_STRING);
230 break;
231 }
232
233 case RTLOGPHASE_PREROTATE:
234 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
235 break;
236
237 case RTLOGPHASE_POSTROTATE:
238 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
239 break;
240
241 case RTLOGPHASE_END:
242 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
243 break;
244
245 default:
246 /* nothing */;
247 }
248}
249
250
251/**
252 * Creates the default release logger outputting to the specified file.
253 * Pass NULL for disabled logging.
254 *
255 * @return IPRT status code.
256 * @param pszLogFile Filename for log output. Optional.
257 */
258int VBoxServiceLogCreate(const char *pszLogFile)
259{
260 /* Create release logger (stdout + file). */
261 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
262 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
263#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
264 fFlags |= RTLOGFLAGS_USECRLF;
265#endif
266 char szError[RTPATH_MAX + 128] = "";
267 int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
268#ifdef DEBUG
269 "VBOXSERVICE_LOG",
270#else
271 "VBOXSERVICE_RELEASE_LOG",
272#endif
273 RT_ELEMENTS(s_apszGroups), s_apszGroups,
274 RTLOGDEST_STDOUT | RTLOGDEST_USER,
275 VBoxServiceLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
276 szError, sizeof(szError), pszLogFile);
277 if (RT_SUCCESS(rc))
278 {
279 /* register this logger as the release logger */
280 RTLogRelSetDefaultInstance(g_pLoggerRelease);
281
282 /* Explicitly flush the log in case of VBOXSERVICE_RELEASE_LOG=buffered. */
283 RTLogFlush(g_pLoggerRelease);
284 }
285
286 return rc;
287}
288
289
290void VBoxServiceLogDestroy(void)
291{
292 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
293}
294
295
296/**
297 * Displays the program usage message.
298 *
299 * @returns 1.
300 */
301static int vboxServiceUsage(void)
302{
303 RTPrintf("Usage:\n"
304 " %-12s [-f|--foreground] [-v|--verbose] [-l|--logfile <file>]\n"
305 " [-i|--interval <seconds>]\n"
306 " [--disable-<service>] [--enable-<service>]\n"
307 " [--only-<service>] [-h|-?|--help]\n", g_pszProgName);
308#ifdef RT_OS_WINDOWS
309 RTPrintf(" [-r|--register] [-u|--unregister]\n");
310#endif
311 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
312 if (g_aServices[j].pDesc->pszUsage)
313 RTPrintf("%s\n", g_aServices[j].pDesc->pszUsage);
314 RTPrintf("\n"
315 "Options:\n"
316 " -i | --interval The default interval.\n"
317 " -f | --foreground Don't daemonize the program. For debugging.\n"
318 " -l | --logfile <file> Enables logging to a file.\n"
319 " -v | --verbose Increment the verbosity level. For debugging.\n"
320 " -V | --version Show version information.\n"
321 " -h | -? | --help Show this message and exit with status 1.\n"
322 );
323#ifdef RT_OS_WINDOWS
324 RTPrintf(" -r | --register Installs the service.\n"
325 " -u | --unregister Uninstall service.\n");
326#endif
327
328 RTPrintf("\n"
329 "Service-specific options:\n");
330 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
331 {
332 RTPrintf(" --enable-%-14s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
333 RTPrintf(" --disable-%-13s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
334 RTPrintf(" --only-%-16s Only enables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
335 if (g_aServices[j].pDesc->pszOptions)
336 RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
337 }
338 RTPrintf("\n"
339 " Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
340
341 return 1;
342}
343
344
345/**
346 * Displays an error message.
347 *
348 * @returns RTEXITCODE_FAILURE.
349 * @param pszFormat The message text.
350 * @param ... Format arguments.
351 */
352RTEXITCODE VBoxServiceError(const char *pszFormat, ...)
353{
354 va_list args;
355 va_start(args, pszFormat);
356 char *psz = NULL;
357 RTStrAPrintfV(&psz, pszFormat, args);
358 va_end(args);
359
360 AssertPtr(psz);
361 LogRel(("Error: %s", psz));
362
363 RTStrFree(psz);
364
365 return RTEXITCODE_FAILURE;
366}
367
368
369/**
370 * Displays a verbose message.
371 *
372 * @param iLevel Minimum log level required to display this message.
373 * @param pszFormat The message text.
374 * @param ... Format arguments.
375 */
376void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
377{
378 if (iLevel <= g_cVerbosity)
379 {
380#ifdef DEBUG
381 int rc = RTCritSectEnter(&g_csLog);
382 if (RT_SUCCESS(rc))
383 {
384#endif
385 va_list args;
386 va_start(args, pszFormat);
387 char *psz = NULL;
388 RTStrAPrintfV(&psz, pszFormat, args);
389 va_end(args);
390
391 AssertPtr(psz);
392 LogRel(("%s", psz));
393
394 RTStrFree(psz);
395#ifdef DEBUG
396 RTCritSectLeave(&g_csLog);
397 }
398#endif
399 }
400}
401
402
403/**
404 * Reports the current VBoxService status to the host.
405 *
406 * This makes sure that the Failed state is sticky.
407 *
408 * @return IPRT status code.
409 * @param enmStatus Status to report to the host.
410 */
411int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus)
412{
413 /*
414 * VBoxGuestFacilityStatus_Failed is sticky.
415 */
416 static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
417 VBoxServiceVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
418 if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
419 {
420 int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService,
421 enmStatus, 0 /* Flags */);
422 if (RT_FAILURE(rc))
423 {
424 VBoxServiceError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
425 return rc;
426 }
427 s_enmLastStatus = enmStatus;
428 }
429 return VINF_SUCCESS;
430}
431
432
433/**
434 * Gets a 32-bit value argument.
435 * @todo Get rid of this and VBoxServiceArgString() as soon as we have RTOpt handling.
436 *
437 * @returns 0 on success, non-zero exit code on error.
438 * @param argc The argument count.
439 * @param argv The argument vector
440 * @param psz Where in *pi to start looking for the value argument.
441 * @param pi Where to find and perhaps update the argument index.
442 * @param pu32 Where to store the 32-bit value.
443 * @param u32Min The minimum value.
444 * @param u32Max The maximum value.
445 */
446int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
447{
448 if (*psz == ':' || *psz == '=')
449 psz++;
450 if (!*psz)
451 {
452 if (*pi + 1 >= argc)
453 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing value for the '%s' argument\n", argv[*pi]);
454 psz = argv[++*pi];
455 }
456
457 char *pszNext;
458 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
459 if (RT_FAILURE(rc) || *pszNext)
460 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to convert interval '%s' to a number\n", psz);
461 if (*pu32 < u32Min || *pu32 > u32Max)
462 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The timesync interval of %RU32 seconds is out of range [%RU32..%RU32]\n",
463 *pu32, u32Min, u32Max);
464 return 0;
465}
466
467/** @todo Get rid of this and VBoxServiceArgUInt32() as soon as we have RTOpt handling. */
468int VBoxServiceArgString(int argc, char **argv, const char *psz, int *pi, char *pszBuf, size_t cbBuf)
469{
470 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
471 AssertPtrReturn(cbBuf, VERR_INVALID_PARAMETER);
472
473 if (*psz == ':' || *psz == '=')
474 psz++;
475 if (!*psz)
476 {
477 if (*pi + 1 >= argc)
478 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing string for the '%s' argument\n", argv[*pi]);
479 psz = argv[++*pi];
480 }
481
482 if (!RTStrPrintf(pszBuf, cbBuf, "%s", psz))
483 return RTMsgErrorExit(RTEXITCODE_FAILURE, "String for '%s' argument too big\n", argv[*pi]);
484 return 0;
485}
486
487
488
489/**
490 * The service thread.
491 *
492 * @returns Whatever the worker function returns.
493 * @param ThreadSelf My thread handle.
494 * @param pvUser The service index.
495 */
496static DECLCALLBACK(int) vboxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
497{
498 const unsigned i = (uintptr_t)pvUser;
499
500#ifndef RT_OS_WINDOWS
501 /*
502 * Block all signals for this thread. Only the main thread will handle signals.
503 */
504 sigset_t signalMask;
505 sigfillset(&signalMask);
506 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
507#endif
508
509 int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
510 ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
511 RTThreadUserSignal(ThreadSelf);
512 return rc;
513}
514
515
516/**
517 * Lazily calls the pfnPreInit method on each service.
518 *
519 * @returns VBox status code, error message displayed.
520 */
521static RTEXITCODE vboxServiceLazyPreInit(void)
522{
523 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
524 if (!g_aServices[j].fPreInited)
525 {
526 int rc = g_aServices[j].pDesc->pfnPreInit();
527 if (RT_FAILURE(rc))
528 return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
529 g_aServices[j].fPreInited = true;
530 }
531 return RTEXITCODE_SUCCESS;
532}
533
534
535/**
536 * Count the number of enabled services.
537 */
538static unsigned vboxServiceCountEnabledServices(void)
539{
540 unsigned cEnabled = 0;
541 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
542 cEnabled += g_aServices[i].fEnabled;
543 return cEnabled;
544}
545
546
547#ifdef RT_OS_WINDOWS
548static BOOL WINAPI VBoxServiceConsoleControlHandler(DWORD dwCtrlType)
549{
550 int rc = VINF_SUCCESS;
551 bool fEventHandled = FALSE;
552 switch (dwCtrlType)
553 {
554 /* User pressed CTRL+C or CTRL+BREAK or an external event was sent
555 * via GenerateConsoleCtrlEvent(). */
556 case CTRL_BREAK_EVENT:
557 case CTRL_CLOSE_EVENT:
558 case CTRL_C_EVENT:
559 VBoxServiceVerbose(2, "ControlHandler: Received break/close event\n");
560 rc = VBoxServiceStopServices();
561 fEventHandled = TRUE;
562 break;
563 default:
564 break;
565 /** @todo Add other events here. */
566 }
567
568 if (RT_FAILURE(rc))
569 VBoxServiceError("ControlHandler: Event %ld handled with error rc=%Rrc\n",
570 dwCtrlType, rc);
571 return fEventHandled;
572}
573#endif /* RT_OS_WINDOWS */
574
575
576/**
577 * Starts the service.
578 *
579 * @returns VBox status code, errors are fully bitched.
580 */
581int VBoxServiceStartServices(void)
582{
583 int rc;
584
585 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Init);
586
587 /*
588 * Initialize the services.
589 */
590 VBoxServiceVerbose(2, "Initializing services ...\n");
591 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
592 if (g_aServices[j].fEnabled)
593 {
594 rc = g_aServices[j].pDesc->pfnInit();
595 if (RT_FAILURE(rc))
596 {
597 if (rc != VERR_SERVICE_DISABLED)
598 {
599 VBoxServiceError("Service '%s' failed to initialize: %Rrc\n",
600 g_aServices[j].pDesc->pszName, rc);
601 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
602 return rc;
603 }
604 g_aServices[j].fEnabled = false;
605 VBoxServiceVerbose(0, "Service '%s' was disabled because of missing functionality\n",
606 g_aServices[j].pDesc->pszName);
607
608 }
609 }
610
611 /*
612 * Start the service(s).
613 */
614 VBoxServiceVerbose(2, "Starting services ...\n");
615 rc = VINF_SUCCESS;
616 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
617 {
618 if (!g_aServices[j].fEnabled)
619 continue;
620
621 VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
622 rc = RTThreadCreate(&g_aServices[j].Thread, vboxServiceThread, (void *)(uintptr_t)j, 0,
623 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
624 if (RT_FAILURE(rc))
625 {
626 VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc);
627 break;
628 }
629 g_aServices[j].fStarted = true;
630
631 /* Wait for the thread to initialize. */
632 /** @todo There is a race between waiting and checking
633 * the fShutdown flag of a thread here and processing
634 * the thread's actual worker loop. If the thread decides
635 * to exit the loop before we skipped the fShutdown check
636 * below the service will fail to start! */
637 /** @todo This presumably means either a one-shot service or that
638 * something has gone wrong. In the second case treating it as failure
639 * to start is probably right, so we need a way to signal the first
640 * rather than leaving the idle thread hanging around. A flag in the
641 * service description? */
642 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
643 if (g_aServices[j].fShutdown)
644 {
645 VBoxServiceError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
646 rc = VERR_GENERAL_FAILURE;
647 }
648 }
649
650 if (RT_SUCCESS(rc))
651 VBoxServiceVerbose(1, "All services started.\n");
652 else
653 {
654 VBoxServiceError("An error occcurred while the services!\n");
655 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
656 }
657 return rc;
658}
659
660
661/**
662 * Stops and terminates the services.
663 *
664 * This should be called even when VBoxServiceStartServices fails so it can
665 * clean up anything that we succeeded in starting.
666 */
667int VBoxServiceStopServices(void)
668{
669 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminating);
670
671 /*
672 * Signal all the services.
673 */
674 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
675 ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
676
677 /*
678 * Do the pfnStop callback on all running services.
679 */
680 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
681 if (g_aServices[j].fStarted)
682 {
683 VBoxServiceVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
684 g_aServices[j].pDesc->pfnStop();
685 }
686
687 VBoxServiceVerbose(3, "All stop functions for services called\n");
688
689 /*
690 * Wait for all the service threads to complete.
691 */
692 int rc = VINF_SUCCESS;
693 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
694 {
695 if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
696 continue;
697 if (g_aServices[j].Thread != NIL_RTTHREAD)
698 {
699 VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
700 int rc2 = VINF_SUCCESS;
701 for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
702 {
703 rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
704 if (RT_SUCCESS(rc2))
705 break;
706#ifdef RT_OS_WINDOWS
707 /* Notify SCM that it takes a bit longer ... */
708 VBoxServiceWinSetStopPendingStatus(i + j*32);
709#endif
710 }
711 if (RT_FAILURE(rc2))
712 {
713 VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
714 rc = rc2;
715 }
716 }
717 VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
718 g_aServices[j].pDesc->pfnTerm();
719 }
720
721#ifdef RT_OS_WINDOWS
722 /*
723 * Wake up and tell the main() thread that we're shutting down (it's
724 * sleeping in VBoxServiceMainWait).
725 */
726 ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
727 if (g_hEvtWindowsService != NIL_RTSEMEVENT)
728 {
729 VBoxServiceVerbose(3, "Stopping the main thread...\n");
730 int rc2 = RTSemEventSignal(g_hEvtWindowsService);
731 AssertRC(rc2);
732 }
733#endif
734
735 VBoxServiceVerbose(2, "Stopping services returning: %Rrc\n", rc);
736 VBoxServiceReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
737 return rc;
738}
739
740
741/**
742 * Block the main thread until the service shuts down.
743 */
744void VBoxServiceMainWait(void)
745{
746 int rc;
747
748 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Active);
749
750#ifdef RT_OS_WINDOWS
751 /*
752 * Wait for the semaphore to be signalled.
753 */
754 VBoxServiceVerbose(1, "Waiting in main thread\n");
755 rc = RTSemEventCreate(&g_hEvtWindowsService);
756 AssertRC(rc);
757 while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown))
758 {
759 rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT);
760 AssertRC(rc);
761 }
762 RTSemEventDestroy(g_hEvtWindowsService);
763 g_hEvtWindowsService = NIL_RTSEMEVENT;
764#else
765 /*
766 * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking
767 * all important signals.
768 *
769 * The annoying EINTR/ERESTART loop is for the benefit of Solaris where
770 * sigwait returns when we receive a SIGCHLD. Kind of makes sense since
771 */
772 sigset_t signalMask;
773 sigemptyset(&signalMask);
774 sigaddset(&signalMask, SIGHUP);
775 sigaddset(&signalMask, SIGINT);
776 sigaddset(&signalMask, SIGQUIT);
777 sigaddset(&signalMask, SIGABRT);
778 sigaddset(&signalMask, SIGTERM);
779 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
780
781 int iSignal;
782 do
783 {
784 iSignal = -1;
785 rc = sigwait(&signalMask, &iSignal);
786 }
787 while ( rc == EINTR
788# ifdef ERESTART
789 || rc == ERESTART
790# endif
791 );
792
793 VBoxServiceVerbose(3, "VBoxServiceMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
794#endif /* !RT_OS_WINDOWS */
795}
796
797
798int main(int argc, char **argv)
799{
800 RTEXITCODE rcExit;
801
802 /*
803 * Init globals and such.
804 */
805 int rc = RTR3InitExe(argc, &argv, 0);
806 if (RT_FAILURE(rc))
807 return RTMsgInitFailure(rc);
808 g_pszProgName = RTPathFilename(argv[0]);
809#ifdef DEBUG
810 rc = RTCritSectInit(&g_csLog);
811 AssertRC(rc);
812#endif
813
814#ifdef VBOXSERVICE_TOOLBOX
815 /*
816 * Run toolbox code before all other stuff since these things are simpler
817 * shell/file/text utility like programs that just happens to be inside
818 * VBoxService and shouldn't be subject to /dev/vboxguest, pid-files and
819 * global mutex restrictions.
820 */
821 if (VBoxServiceToolboxMain(argc, argv, &rcExit))
822 return rcExit;
823#endif
824
825 bool fUserSession = false;
826#ifdef VBOX_WITH_GUEST_CONTROL
827 /*
828 * Check if we're the specially spawned VBoxService.exe process that
829 * handles a guest control session.
830 */
831 if ( argc >= 2
832 && !RTStrICmp(argv[1], "guestsession"))
833 fUserSession = true;
834#endif
835
836 /*
837 * Connect to the kernel part before daemonizing so we can fail and
838 * complain if there is some kind of problem. We need to initialize the
839 * guest lib *before* we do the pre-init just in case one of services needs
840 * do to some initial stuff with it.
841 */
842 if (fUserSession)
843 rc = VbglR3InitUser();
844 else
845 rc = VbglR3Init();
846
847 if (RT_FAILURE(rc))
848 {
849 if (rc == VERR_ACCESS_DENIED)
850 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Insufficient privileges to start %s! Please start with Administrator/root privileges!\n",
851 g_pszProgName);
852 return RTMsgErrorExit(RTEXITCODE_FAILURE, "VbglR3Init failed with rc=%Rrc\n", rc);
853 }
854
855#ifdef RT_OS_WINDOWS
856 /*
857 * Check if we're the specially spawned VBoxService.exe process that
858 * handles page fusion. This saves an extra executable.
859 */
860 if ( argc == 2
861 && !RTStrICmp(argv[1], "pagefusion"))
862 return VBoxServicePageSharingInitFork();
863#endif
864
865#ifdef VBOX_WITH_GUEST_CONTROL
866 /*
867 * Check if we're the specially spawned VBoxService.exe process that
868 * handles a guest control session.
869 */
870 if (fUserSession)
871 return VBoxServiceControlSessionForkInit(argc, argv);
872#endif
873
874 /*
875 * Parse the arguments.
876 *
877 * Note! This code predates RTGetOpt, thus the manual parsing.
878 */
879 bool fDaemonize = true;
880 bool fDaemonized = false;
881 for (int i = 1; i < argc; i++)
882 {
883 const char *psz = argv[i];
884 if (*psz != '-')
885 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'\n", psz);
886 psz++;
887
888 /* translate long argument to short */
889 if (*psz == '-')
890 {
891 psz++;
892 size_t cch = strlen(psz);
893#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
894 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
895 if (MATCHES("foreground"))
896 psz = "f";
897 else if (MATCHES("verbose"))
898 psz = "v";
899 else if (MATCHES("version"))
900 psz = "V";
901 else if (MATCHES("help"))
902 psz = "h";
903 else if (MATCHES("interval"))
904 psz = "i";
905#ifdef RT_OS_WINDOWS
906 else if (MATCHES("register"))
907 psz = "r";
908 else if (MATCHES("unregister"))
909 psz = "u";
910#endif
911 else if (MATCHES("logfile"))
912 psz = "l";
913 else if (MATCHES("daemonized"))
914 {
915 fDaemonized = true;
916 continue;
917 }
918 else
919 {
920 bool fFound = false;
921
922 if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-")))
923 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
924 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
925 g_aServices[j].fEnabled = true;
926
927 if (cch > sizeof("disable-") && !memcmp(psz, RT_STR_TUPLE("disable-")))
928 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
929 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
930 g_aServices[j].fEnabled = false;
931
932 if (cch > sizeof("only-") && !memcmp(psz, RT_STR_TUPLE("only-")))
933 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
934 {
935 g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName);
936 if (g_aServices[j].fEnabled)
937 fFound = true;
938 }
939
940 if (!fFound)
941 {
942 rcExit = vboxServiceLazyPreInit();
943 if (rcExit != RTEXITCODE_SUCCESS)
944 return rcExit;
945 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
946 {
947 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
948 fFound = rc == VINF_SUCCESS;
949 if (fFound)
950 break;
951 if (rc != -1)
952 return rc;
953 }
954 }
955 if (!fFound)
956 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%s'\n", argv[i]);
957 continue;
958 }
959#undef MATCHES
960 }
961
962 /* handle the string of short options. */
963 do
964 {
965 switch (*psz)
966 {
967 case 'i':
968 rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i,
969 &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
970 if (rc)
971 return rc;
972 psz = NULL;
973 break;
974
975 case 'f':
976 fDaemonize = false;
977 break;
978
979 case 'v':
980 g_cVerbosity++;
981 break;
982
983 case 'V':
984 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
985 return RTEXITCODE_SUCCESS;
986
987 case 'h':
988 case '?':
989 return vboxServiceUsage();
990
991#ifdef RT_OS_WINDOWS
992 case 'r':
993 return VBoxServiceWinInstall();
994
995 case 'u':
996 return VBoxServiceWinUninstall();
997#endif
998
999 case 'l':
1000 {
1001 rc = VBoxServiceArgString(argc, argv, psz + 1, &i,
1002 g_szLogFile, sizeof(g_szLogFile));
1003 if (rc)
1004 return rc;
1005 psz = NULL;
1006 break;
1007 }
1008
1009 default:
1010 {
1011 rcExit = vboxServiceLazyPreInit();
1012 if (rcExit != RTEXITCODE_SUCCESS)
1013 return rcExit;
1014
1015 bool fFound = false;
1016 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
1017 {
1018 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
1019 fFound = rc == VINF_SUCCESS;
1020 if (fFound)
1021 break;
1022 if (rc != -1)
1023 return rc;
1024 }
1025 if (!fFound)
1026 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%c' (%s)\n", *psz, argv[i]);
1027 break;
1028 }
1029 }
1030 } while (psz && *++psz);
1031 }
1032
1033 /* Check that at least one service is enabled. */
1034 if (vboxServiceCountEnabledServices() == 0)
1035 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n");
1036
1037 rc = VBoxServiceLogCreate(strlen(g_szLogFile) ? g_szLogFile : NULL);
1038 if (RT_FAILURE(rc))
1039 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log \"%s\", rc=%Rrc\n",
1040 strlen(g_szLogFile) ? g_szLogFile : "<None>", rc);
1041
1042 /* Call pre-init if we didn't do it already. */
1043 rcExit = vboxServiceLazyPreInit();
1044 if (rcExit != RTEXITCODE_SUCCESS)
1045 return rcExit;
1046
1047#ifdef RT_OS_WINDOWS
1048 /*
1049 * Make sure only one instance of VBoxService runs at a time. Create a
1050 * global mutex for that.
1051 *
1052 * Note! The \\Global\ namespace was introduced with Win2K, thus the
1053 * version check.
1054 * Note! If the mutex exists CreateMutex will open it and set last error to
1055 * ERROR_ALREADY_EXISTS.
1056 */
1057 OSVERSIONINFOEX OSInfoEx;
1058 RT_ZERO(OSInfoEx);
1059 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1060
1061 SetLastError(NO_ERROR);
1062 HANDLE hMutexAppRunning;
1063 if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
1064 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1065 && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */)
1066 hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME);
1067 else
1068 hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
1069 if (hMutexAppRunning == NULL)
1070 {
1071 DWORD dwErr = GetLastError();
1072 if ( dwErr == ERROR_ALREADY_EXISTS
1073 || dwErr == ERROR_ACCESS_DENIED)
1074 {
1075 VBoxServiceError("%s is already running! Terminating.\n", g_pszProgName);
1076 return RTEXITCODE_FAILURE;
1077 }
1078
1079 VBoxServiceError("CreateMutex failed with last error %u! Terminating.\n", GetLastError());
1080 return RTEXITCODE_FAILURE;
1081 }
1082
1083#else /* !RT_OS_WINDOWS */
1084 /** @todo Add PID file creation here? */
1085#endif /* !RT_OS_WINDOWS */
1086
1087 VBoxServiceVerbose(0, "%s r%s started. Verbose level = %d\n",
1088 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
1089
1090 /*
1091 * Daemonize if requested.
1092 */
1093 if (fDaemonize && !fDaemonized)
1094 {
1095#ifdef RT_OS_WINDOWS
1096 VBoxServiceVerbose(2, "Starting service dispatcher ...\n");
1097 rcExit = VBoxServiceWinEnterCtrlDispatcher();
1098#else
1099 VBoxServiceVerbose(1, "Daemonizing...\n");
1100 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */,
1101 false /* fRespawn */, NULL /* pcRespawn */);
1102 if (RT_FAILURE(rc))
1103 return VBoxServiceError("Daemon failed: %Rrc\n", rc);
1104 /* in-child */
1105#endif
1106 }
1107#ifdef RT_OS_WINDOWS
1108 else
1109#endif
1110 {
1111 /*
1112 * Windows: We're running the service as a console application now. Start the
1113 * services, enter the main thread's run loop and stop them again
1114 * when it returns.
1115 *
1116 * POSIX: This is used for both daemons and console runs. Start all services
1117 * and return immediately.
1118 */
1119#ifdef RT_OS_WINDOWS
1120# ifndef RT_OS_NT4
1121 /* Install console control handler. */
1122 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)VBoxServiceConsoleControlHandler, TRUE /* Add handler */))
1123 {
1124 VBoxServiceError("Unable to add console control handler, error=%ld\n", GetLastError());
1125 /* Just skip this error, not critical. */
1126 }
1127# endif /* !RT_OS_NT4 */
1128#endif /* RT_OS_WINDOWS */
1129 rc = VBoxServiceStartServices();
1130 rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1131 if (RT_SUCCESS(rc))
1132 VBoxServiceMainWait();
1133#ifdef RT_OS_WINDOWS
1134# ifndef RT_OS_NT4
1135 /* Uninstall console control handler. */
1136 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
1137 {
1138 VBoxServiceError("Unable to remove console control handler, error=%ld\n", GetLastError());
1139 /* Just skip this error, not critical. */
1140 }
1141# endif /* !RT_OS_NT4 */
1142#else /* !RT_OS_WINDOWS */
1143 /* On Windows - since we're running as a console application - we already stopped all services
1144 * through the console control handler. So only do the stopping of services here on other platforms
1145 * where the break/shutdown/whatever signal was just received. */
1146 VBoxServiceStopServices();
1147#endif /* RT_OS_WINDOWS */
1148 }
1149 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminated);
1150
1151#ifdef RT_OS_WINDOWS
1152 /*
1153 * Cleanup mutex.
1154 */
1155 CloseHandle(hMutexAppRunning);
1156#endif
1157
1158 VBoxServiceVerbose(0, "Ended.\n");
1159
1160#ifdef DEBUG
1161 RTCritSectDelete(&g_csLog);
1162 //RTMemTrackerDumpAllToStdOut();
1163#endif
1164
1165 VBoxServiceLogDestroy();
1166
1167 return rcExit;
1168}
1169
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