VirtualBox

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

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

VBoxService: Removed automatic Windows system preparation in favor of the more generic guest control feature.

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