VirtualBox

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

Last change on this file since 44824 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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