VirtualBox

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

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

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

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