VirtualBox

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

Last change on this file since 38113 was 38113, checked in by vboxsync, 14 years ago

VBoxService/GuestCtrl: Fixed stability issues due to PID recycling, enhanced and unified debug logging along with thread names to stdout.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.4 KB
Line 
1/* $Id: VBoxService.cpp 38113 2011-07-22 13:57:35Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
7 * Copyright (C) 2007-2011 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 "product-generated.h"
39#include <iprt/asm.h>
40#include <iprt/buildconfig.h>
41#include <iprt/initterm.h>
42#include <iprt/message.h>
43#include <iprt/path.h>
44#include <iprt/semaphore.h>
45#include <iprt/string.h>
46#include <iprt/stream.h>
47#include <iprt/thread.h>
48
49#include <VBox/log.h>
50
51#include "VBoxServiceInternal.h"
52
53
54/*******************************************************************************
55* Global Variables *
56*******************************************************************************/
57/** The program name (derived from argv[0]). */
58char *g_pszProgName = (char *)"";
59/** The current verbosity level. */
60int g_cVerbosity = 0;
61/** Critical section for (debug) logging. */
62#ifdef DEBUG
63 RTCRITSECT g_csLog;
64#endif
65/** The default service interval (the -i | --interval) option). */
66uint32_t g_DefaultInterval = 0;
67#ifdef RT_OS_WINDOWS
68/** Signal shutdown to the Windows service thread. */
69static bool volatile g_fWindowsServiceShutdown;
70/** Event the Windows service thread waits for shutdown. */
71static RTSEMEVENT g_hEvtWindowsService;
72#endif
73
74/**
75 * The details of the services that has been compiled in.
76 */
77static struct
78{
79 /** Pointer to the service descriptor. */
80 PCVBOXSERVICE pDesc;
81 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
82 RTTHREAD Thread;
83 /** Whether Pre-init was called. */
84 bool fPreInited;
85 /** Shutdown indicator. */
86 bool volatile fShutdown;
87 /** Indicator set by the service thread exiting. */
88 bool volatile fStopped;
89 /** Whether the service was started or not. */
90 bool fStarted;
91 /** Whether the service is enabled or not. */
92 bool fEnabled;
93} g_aServices[] =
94{
95#ifdef VBOXSERVICE_CONTROL
96 { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
97#endif
98#ifdef VBOXSERVICE_TIMESYNC
99 { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
100#endif
101#ifdef VBOXSERVICE_CLIPBOARD
102 { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
103#endif
104#ifdef VBOXSERVICE_VMINFO
105 { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
106#endif
107#ifdef VBOXSERVICE_CPUHOTPLUG
108 { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
109#endif
110#ifdef VBOXSERVICE_MANAGEMENT
111# ifdef VBOX_WITH_MEMBALLOON
112 { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
113# endif
114 { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
115#endif
116#if defined(VBOX_WITH_PAGE_SHARING) && defined(RT_OS_WINDOWS)
117 { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
118#endif
119#ifdef VBOX_WITH_SHARED_FOLDERS
120 { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
121#endif
122};
123
124
125/**
126 * Displays the program usage message.
127 *
128 * @returns 1.
129 */
130static int vboxServiceUsage(void)
131{
132 RTPrintf("Usage:\n"
133 " %-12s [-f|--foreground] [-v|--verbose] [-i|--interval <seconds>]\n"
134 " [--disable-<service>] [--enable-<service>] [-h|-?|--help]\n", g_pszProgName);
135#ifdef RT_OS_WINDOWS
136 RTPrintf(" [-r|--register] [-u|--unregister]\n");
137#endif
138 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
139 if (g_aServices[j].pDesc->pszUsage)
140 RTPrintf("%s\n", g_aServices[j].pDesc->pszUsage);
141 RTPrintf("\n"
142 "Options:\n"
143 " -i | --interval The default interval.\n"
144 " -f | --foreground Don't daemonize the program. For debugging.\n"
145 " -v | --verbose Increment the verbosity level. For debugging.\n"
146 " -V | --version Show version information.\n"
147 " -h | -? | --help Show this message and exit with status 1.\n"
148 );
149#ifdef RT_OS_WINDOWS
150 RTPrintf(" -r | --register Installs the service.\n"
151 " -u | --unregister Uninstall service.\n");
152#endif
153
154 RTPrintf("\n"
155 "Service-specific options:\n");
156 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
157 {
158 RTPrintf(" --enable-%-14s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
159 RTPrintf(" --disable-%-13s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
160 if (g_aServices[j].pDesc->pszOptions)
161 RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
162 }
163 RTPrintf("\n"
164 " Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
165
166 return 1;
167}
168
169
170/**
171 * Displays a syntax error message.
172 *
173 * @returns RTEXITCODE_SYNTAX.
174 * @param pszFormat The message text.
175 * @param ... Format arguments.
176 */
177RTEXITCODE VBoxServiceSyntax(const char *pszFormat, ...)
178{
179 RTStrmPrintf(g_pStdErr, "%s: syntax error: ", g_pszProgName);
180
181 va_list va;
182 va_start(va, pszFormat);
183 RTStrmPrintfV(g_pStdErr, pszFormat, va);
184 va_end(va);
185
186 return RTEXITCODE_SYNTAX;
187}
188
189
190/**
191 * Displays an error message.
192 *
193 * @returns RTEXITCODE_FAILURE.
194 * @param pszFormat The message text.
195 * @param ... Format arguments.
196 */
197RTEXITCODE VBoxServiceError(const char *pszFormat, ...)
198{
199 RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
200
201 va_list va;
202 va_start(va, pszFormat);
203 RTStrmPrintfV(g_pStdErr, pszFormat, va);
204 va_end(va);
205
206 va_start(va, pszFormat);
207 LogRel(("%s: Error: %N", g_pszProgName, pszFormat, &va));
208 va_end(va);
209
210 return RTEXITCODE_FAILURE;
211}
212
213
214/**
215 * Displays a verbose message.
216 *
217 * @returns 1
218 * @param pszFormat The message text.
219 * @param ... Format arguments.
220 */
221void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
222{
223#ifdef DEBUG
224 int rc = RTCritSectEnter(&g_csLog);
225 if (RT_SUCCESS(rc))
226 {
227#endif
228 if (iLevel <= g_cVerbosity)
229 {
230#ifdef DEBUG
231 const char *pszThreadName = RTThreadSelfName();
232 AssertPtr(pszThreadName);
233 RTStrmPrintf(g_pStdOut, "%s [%s]: ",
234 g_pszProgName, pszThreadName);
235#else
236 RTStrmPrintf(g_pStdOut, "%s: ", g_pszProgName);
237#endif
238 va_list va;
239 va_start(va, pszFormat);
240 RTStrmPrintfV(g_pStdOut, pszFormat, va);
241 va_end(va);
242 va_start(va, pszFormat);
243 LogRel(("%s: %N", g_pszProgName, pszFormat, &va));
244 va_end(va);
245 }
246#ifdef DEBUG
247 int rc2 = RTCritSectLeave(&g_csLog);
248 if (RT_SUCCESS(rc))
249 rc = rc2;
250 }
251#endif
252}
253
254
255/**
256 * Reports the current VBoxService status to the host.
257 *
258 * This makes sure that the Failed state is sticky.
259 *
260 * @return IPRT status code.
261 * @param enmStatus Status to report to the host.
262 */
263int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus)
264{
265 /*
266 * VBoxGuestFacilityStatus_Failed is sticky.
267 */
268 static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
269 VBoxServiceVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
270 if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
271 {
272 int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService,
273 enmStatus, 0 /* Flags */);
274 if (RT_FAILURE(rc))
275 {
276 VBoxServiceError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
277 return rc;
278 }
279 s_enmLastStatus = enmStatus;
280 }
281 return VINF_SUCCESS;
282}
283
284
285/**
286 * Gets a 32-bit value argument.
287 *
288 * @returns 0 on success, non-zero exit code on error.
289 * @param argc The argument count.
290 * @param argv The argument vector
291 * @param psz Where in *pi to start looking for the value argument.
292 * @param pi Where to find and perhaps update the argument index.
293 * @param pu32 Where to store the 32-bit value.
294 * @param u32Min The minimum value.
295 * @param u32Max The maximum value.
296 */
297int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
298{
299 if (*psz == ':' || *psz == '=')
300 psz++;
301 if (!*psz)
302 {
303 if (*pi + 1 >= argc)
304 return VBoxServiceSyntax("Missing value for the '%s' argument\n", argv[*pi]);
305 psz = argv[++*pi];
306 }
307
308 char *pszNext;
309 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
310 if (RT_FAILURE(rc) || *pszNext)
311 return VBoxServiceSyntax("Failed to convert interval '%s' to a number.\n", psz);
312 if (*pu32 < u32Min || *pu32 > u32Max)
313 return VBoxServiceSyntax("The timesync interval of %RU32 seconds is out of range [%RU32..%RU32].\n",
314 *pu32, u32Min, u32Max);
315 return 0;
316}
317
318
319/**
320 * The service thread.
321 *
322 * @returns Whatever the worker function returns.
323 * @param ThreadSelf My thread handle.
324 * @param pvUser The service index.
325 */
326static DECLCALLBACK(int) vboxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
327{
328 const unsigned i = (uintptr_t)pvUser;
329
330#ifndef RT_OS_WINDOWS
331 /*
332 * Block all signals for this thread. Only the main thread will handle signals.
333 */
334 sigset_t signalMask;
335 sigfillset(&signalMask);
336 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
337#endif
338
339 int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
340 ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
341 RTThreadUserSignal(ThreadSelf);
342 return rc;
343}
344
345
346/**
347 * Lazily calls the pfnPreInit method on each service.
348 *
349 * @returns VBox status code, error message displayed.
350 */
351static RTEXITCODE vboxServiceLazyPreInit(void)
352{
353 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
354 if (!g_aServices[j].fPreInited)
355 {
356 int rc = g_aServices[j].pDesc->pfnPreInit();
357 if (RT_FAILURE(rc))
358 return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
359 g_aServices[j].fPreInited = true;
360 }
361 return RTEXITCODE_SUCCESS;
362}
363
364
365/**
366 * Count the number of enabled services.
367 */
368static unsigned vboxServiceCountEnabledServices(void)
369{
370 unsigned cEnabled = 0;
371 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
372 cEnabled += g_aServices[i].fEnabled;
373 return cEnabled;
374}
375
376
377/**
378 * Starts the service.
379 *
380 * @returns VBox status code, errors are fully bitched.
381 */
382int VBoxServiceStartServices(void)
383{
384 int rc;
385
386 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Init);
387
388 /*
389 * Initialize the services.
390 */
391 VBoxServiceVerbose(2, "Initializing services ...\n");
392 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
393 if (g_aServices[j].fEnabled)
394 {
395 rc = g_aServices[j].pDesc->pfnInit();
396 if (RT_FAILURE(rc))
397 {
398 if (rc != VERR_SERVICE_DISABLED)
399 {
400 VBoxServiceError("Service '%s' failed to initialize: %Rrc\n",
401 g_aServices[j].pDesc->pszName, rc);
402 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
403 return rc;
404 }
405 g_aServices[j].fEnabled = false;
406 VBoxServiceVerbose(0, "Service '%s' was disabled because of missing functionality\n",
407 g_aServices[j].pDesc->pszName);
408
409 }
410 }
411
412 /*
413 * Start the service(s).
414 */
415 VBoxServiceVerbose(2, "Starting services ...\n");
416 rc = VINF_SUCCESS;
417 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
418 {
419 if (!g_aServices[j].fEnabled)
420 continue;
421
422 VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
423 rc = RTThreadCreate(&g_aServices[j].Thread, vboxServiceThread, (void *)(uintptr_t)j, 0,
424 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
425 if (RT_FAILURE(rc))
426 {
427 VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc);
428 break;
429 }
430 g_aServices[j].fStarted = true;
431
432 /* Wait for the thread to initialize. */
433 /** @todo There is a race between waiting and checking
434 * the fShutdown flag of a thread here and processing
435 * the thread's actual worker loop. If the thread decides
436 * to exit the loop before we skipped the fShutdown check
437 * below the service will fail to start! */
438 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
439 if (g_aServices[j].fShutdown)
440 {
441 VBoxServiceError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
442 rc = VERR_GENERAL_FAILURE;
443 }
444 }
445
446 if (RT_SUCCESS(rc))
447 VBoxServiceVerbose(1, "All services started.\n");
448 else
449 {
450 VBoxServiceError("An error occcurred while the services!\n");
451 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed);
452 }
453 return rc;
454}
455
456
457/**
458 * Stops and terminates the services.
459 *
460 * This should be called even when VBoxServiceStartServices fails so it can
461 * clean up anything that we succeeded in starting.
462 */
463int VBoxServiceStopServices(void)
464{
465 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminating);
466
467 /*
468 * Signal all the services.
469 */
470 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
471 ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
472
473 /*
474 * Do the pfnStop callback on all running services.
475 */
476 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
477 if (g_aServices[j].fStarted)
478 {
479 VBoxServiceVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
480 g_aServices[j].pDesc->pfnStop();
481 }
482
483 /*
484 * Wait for all the service threads to complete.
485 */
486 int rc = VINF_SUCCESS;
487 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
488 {
489 if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
490 continue;
491 if (g_aServices[j].Thread != NIL_RTTHREAD)
492 {
493 VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
494 int rc2 = VINF_SUCCESS;
495 for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
496 {
497 rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
498 if (RT_SUCCESS(rc2))
499 break;
500#ifdef RT_OS_WINDOWS
501 /* Notify SCM that it takes a bit longer ... */
502 VBoxServiceWinSetStopPendingStatus(i + j*32);
503#endif
504 }
505 if (RT_FAILURE(rc2))
506 {
507 VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
508 rc = rc2;
509 }
510 }
511 VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
512 g_aServices[j].pDesc->pfnTerm();
513 }
514
515#ifdef RT_OS_WINDOWS
516 /*
517 * Wake up and tell the main() thread that we're shutting down (it's
518 * sleeping in VBoxServiceMainWait).
519 */
520 ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
521 if (g_hEvtWindowsService != NIL_RTSEMEVENT)
522 {
523 VBoxServiceVerbose(3, "Stopping the main thread...\n");
524 int rc2 = RTSemEventSignal(g_hEvtWindowsService);
525 AssertRC(rc2);
526 }
527#endif
528
529 VBoxServiceVerbose(2, "Stopping services returning: %Rrc\n", rc);
530 VBoxServiceReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
531 return rc;
532}
533
534
535/**
536 * Block the main thread until the service shuts down.
537 */
538void VBoxServiceMainWait(void)
539{
540 int rc;
541
542 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Active);
543
544#ifdef RT_OS_WINDOWS
545 /*
546 * Wait for the semaphore to be signalled.
547 */
548 VBoxServiceVerbose(1, "Waiting in main thread\n");
549 rc = RTSemEventCreate(&g_hEvtWindowsService);
550 AssertRC(rc);
551 while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown))
552 {
553 rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT);
554 AssertRC(rc);
555 }
556 RTSemEventDestroy(g_hEvtWindowsService);
557 g_hEvtWindowsService = NIL_RTSEMEVENT;
558
559#else
560 /*
561 * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking
562 * all important signals.
563 *
564 * The annoying EINTR/ERESTART loop is for the benefit of Solaris where
565 * sigwait returns when we receive a SIGCHLD. Kind of makes sense since
566 */
567 sigset_t signalMask;
568 sigemptyset(&signalMask);
569 sigaddset(&signalMask, SIGHUP);
570 sigaddset(&signalMask, SIGINT);
571 sigaddset(&signalMask, SIGQUIT);
572 sigaddset(&signalMask, SIGABRT);
573 sigaddset(&signalMask, SIGTERM);
574 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
575
576 int iSignal;
577 do
578 {
579 iSignal = -1;
580 rc = sigwait(&signalMask, &iSignal);
581 }
582 while ( rc == EINTR
583# ifdef ERESTART
584 || rc == ERESTART
585# endif
586 );
587
588 VBoxServiceVerbose(3, "VBoxServiceMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
589#endif /* !RT_OS_WINDOWS */
590}
591
592
593int main(int argc, char **argv)
594{
595 RTEXITCODE rcExit;
596
597 /*
598 * Init globals and such.
599 */
600 int rc = RTR3Init();
601 if (RT_FAILURE(rc))
602 return RTMsgInitFailure(rc);
603 g_pszProgName = RTPathFilename(argv[0]);
604#ifdef DEBUG
605 rc = RTCritSectInit(&g_csLog);
606 AssertRC(rc);
607#endif
608
609#ifdef VBOXSERVICE_TOOLBOX
610 /*
611 * Run toolbox code before all other stuff since these things are simpler
612 * shell/file/text utility like programs that just happens to be inside
613 * VBoxService and shouldn't be subject to /dev/vboxguest, pid-files and
614 * global mutex restrictions.
615 */
616 if (VBoxServiceToolboxMain(argc, argv, &rcExit))
617 return rcExit;
618#endif
619
620 /*
621 * Connect to the kernel part before daemonizing so we can fail and
622 * complain if there is some kind of problem. We need to initialize the
623 * guest lib *before* we do the pre-init just in case one of services needs
624 * do to some initial stuff with it.
625 */
626 VBoxServiceVerbose(2, "Calling VbgR3Init()\n");
627 rc = VbglR3Init();
628 if (RT_FAILURE(rc))
629 {
630 if (rc == VERR_ACCESS_DENIED)
631 return VBoxServiceError("Insufficient privileges to start %s! Please start with Administrator/root privileges!\n",
632 g_pszProgName);
633 return VBoxServiceError("VbglR3Init failed with rc=%Rrc.\n", rc);
634 }
635
636#ifdef RT_OS_WINDOWS
637 /*
638 * Check if we're the specially spawned VBoxService.exe process that
639 * handles page fusion. This saves an extra executable.
640 */
641 if ( argc == 2
642 && !strcmp(argv[1], "--pagefusionfork"))
643 return VBoxServicePageSharingInitFork();
644#endif
645
646 /*
647 * Parse the arguments.
648 *
649 * Note! This code predates RTGetOpt, thus the manual parsing.
650 */
651 bool fDaemonize = true;
652 bool fDaemonized = false;
653 for (int i = 1; i < argc; i++)
654 {
655 const char *psz = argv[i];
656 if (*psz != '-')
657 return VBoxServiceSyntax("Unknown argument '%s'\n", psz);
658 psz++;
659
660 /* translate long argument to short */
661 if (*psz == '-')
662 {
663 psz++;
664 size_t cch = strlen(psz);
665#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
666 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
667 if (MATCHES("foreground"))
668 psz = "f";
669 else if (MATCHES("verbose"))
670 psz = "v";
671 else if (MATCHES("version"))
672 psz = "V";
673 else if (MATCHES("help"))
674 psz = "h";
675 else if (MATCHES("interval"))
676 psz = "i";
677#ifdef RT_OS_WINDOWS
678 else if (MATCHES("register"))
679 psz = "r";
680 else if (MATCHES("unregister"))
681 psz = "u";
682#endif
683 else if (MATCHES("daemonized"))
684 {
685 fDaemonized = true;
686 continue;
687 }
688 else
689 {
690 bool fFound = false;
691
692 if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1))
693 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
694 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
695 g_aServices[j].fEnabled = true;
696
697 if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1))
698 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
699 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
700 g_aServices[j].fEnabled = false;
701
702 if (!fFound)
703 {
704 rcExit = vboxServiceLazyPreInit();
705 if (rcExit != RTEXITCODE_SUCCESS)
706 return rcExit;
707
708 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
709 {
710 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
711 fFound = rc == 0;
712 if (fFound)
713 break;
714 if (rc != -1)
715 return rc;
716 }
717 }
718 if (!fFound)
719 return VBoxServiceSyntax("Unknown option '%s'\n", argv[i]);
720 continue;
721 }
722#undef MATCHES
723 }
724
725 /* handle the string of short options. */
726 do
727 {
728 switch (*psz)
729 {
730 case 'i':
731 rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i,
732 &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
733 if (rc)
734 return rc;
735 psz = NULL;
736 break;
737
738 case 'f':
739 fDaemonize = false;
740 break;
741
742 case 'v':
743 g_cVerbosity++;
744 break;
745
746 case 'V':
747 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
748 return RTEXITCODE_SUCCESS;
749
750 case 'h':
751 case '?':
752 return vboxServiceUsage();
753
754#ifdef RT_OS_WINDOWS
755 case 'r':
756 return VBoxServiceWinInstall();
757
758 case 'u':
759 return VBoxServiceWinUninstall();
760#endif
761
762 default:
763 {
764 rcExit = vboxServiceLazyPreInit();
765 if (rcExit != RTEXITCODE_SUCCESS)
766 return rcExit;
767
768 bool fFound = false;
769 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
770 {
771 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
772 fFound = rc == VINF_SUCCESS;
773 if (fFound)
774 break;
775 if (rc != -1)
776 return rc;
777 }
778 if (!fFound)
779 return VBoxServiceSyntax("Unknown option '%c' (%s)\n", *psz, argv[i]);
780 break;
781 }
782 }
783 } while (psz && *++psz);
784 }
785
786 /* Check that at least one service is enabled. */
787 if (vboxServiceCountEnabledServices() == 0)
788 return VBoxServiceSyntax("At least one service must be enabled.\n");
789
790 /* Call pre-init if we didn't do it already. */
791 rcExit = vboxServiceLazyPreInit();
792 if (rcExit != RTEXITCODE_SUCCESS)
793 return rcExit;
794
795#ifdef RT_OS_WINDOWS
796 /*
797 * Make sure only one instance of VBoxService runs at a time. Create a
798 * global mutex for that.
799 *
800 * Note! The \\Global\ namespace was introduced with Win2K, thus the
801 * version check.
802 * Note! If the mutex exists CreateMutex will open it and set last error to
803 * ERROR_ALREADY_EXISTS.
804 */
805 OSVERSIONINFOEX OSInfoEx;
806 RT_ZERO(OSInfoEx);
807 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
808
809 SetLastError(NO_ERROR);
810 HANDLE hMutexAppRunning;
811 if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
812 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
813 && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */)
814 hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME);
815 else
816 hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
817 if (hMutexAppRunning == NULL)
818 {
819 VBoxServiceError("CreateMutex failed with last error %u! Terminating", GetLastError());
820 return RTEXITCODE_FAILURE;
821 }
822 if (GetLastError() == ERROR_ALREADY_EXISTS)
823 {
824 VBoxServiceError("%s is already running! Terminating.", g_pszProgName);
825 CloseHandle(hMutexAppRunning);
826 return RTEXITCODE_FAILURE;
827 }
828#else /* !RT_OS_WINDOWS */
829 /** @todo Add PID file creation here? */
830#endif /* !RT_OS_WINDOWS */
831
832 VBoxServiceVerbose(0, "%s r%s started. Verbose level = %d\n",
833 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
834
835 /*
836 * Daemonize if requested.
837 */
838 if (fDaemonize && !fDaemonized)
839 {
840#ifdef RT_OS_WINDOWS
841 VBoxServiceVerbose(2, "Starting service dispatcher ...\n");
842 rcExit = VBoxServiceWinEnterCtrlDispatcher();
843#else
844 VBoxServiceVerbose(1, "Daemonizing...\n");
845 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */);
846 if (RT_FAILURE(rc))
847 return VBoxServiceError("Daemon failed: %Rrc\n", rc);
848 /* in-child */
849#endif
850 }
851#ifdef RT_OS_WINDOWS
852 else
853#endif
854 {
855 /*
856 * Windows: We're running the service as a console application now. Start the
857 * services, enter the main thread's run loop and stop them again
858 * when it returns.
859 *
860 * POSIX: This is used for both daemons and console runs. Start all services
861 * and return immediately.
862 */
863 rc = VBoxServiceStartServices();
864 rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
865 if (RT_SUCCESS(rc))
866 VBoxServiceMainWait();
867 VBoxServiceStopServices();
868 }
869 VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminated);
870
871#ifdef RT_OS_WINDOWS
872 /*
873 * Cleanup mutex.
874 */
875 CloseHandle(hMutexAppRunning);
876#endif
877
878 VBoxServiceVerbose(0, "Ended.\n");
879
880#ifdef DEBUG
881 RTCritSectDelete(&g_csLog);
882#endif
883 return rcExit;
884}
885
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