VirtualBox

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

Last change on this file since 30121 was 29817, checked in by vboxsync, 15 years ago

VBoxService: Some cleanup - started with reviewing r61961, but got carried away...

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