VirtualBox

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

Last change on this file since 39570 was 39202, checked in by vboxsync, 13 years ago

VBoxService/Windows: Added gentle shutdown when user does CTRL+C/CTRL+BREAK/close the console window.

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