VirtualBox

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

Last change on this file since 29535 was 29438, checked in by vboxsync, 15 years ago

Guest Control/VBoxService: Added support for cancel pending (blocking) calls; added support for properly stopping + shutting down main thread.

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