VirtualBox

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

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

Logging: Missing parameter.

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