VirtualBox

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

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

VBoxService: Clarification for access rights error (VERR_ACCESS_DENIED).

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