VirtualBox

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

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

Main/Frontends: Also use facilities for guest features (seamless, graphics), added facility-state-to-name to VBoxManage, some renaming.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette