VirtualBox

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

Last change on this file since 86178 was 85962, checked in by vboxsync, 4 years ago

bugref:9637. Starting VBoxDRMClient from VBoxService when some geust property is set.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.6 KB
Line 
1/* $Id: VBoxService.cpp 85962 2020-08-31 18:47:22Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
7 * Copyright (C) 2007-2020 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/** @page pg_vgsvc VBoxService
20 *
21 * VBoxService is a root daemon for implementing guest additions features.
22 *
23 * It is structured as one binary that contains many sub-services. The reason
24 * for this is partially historical and partially practical. The practical
25 * reason is that the VBoxService binary is typically statically linked, at
26 * least with IPRT and the guest library, so we save quite a lot of space having
27 * on single binary instead individual binaries for each sub-service and their
28 * helpers (currently up to 9 subservices and 8 helpers). The historical is
29 * simply that it started its life on OS/2 dreaming of conquring Windows next,
30 * so it kind of felt natural to have it all in one binary.
31 *
32 * Even if it's structured as a single binary, it is possible, by using command
33 * line options, to start each subservice as an individual process.
34 *
35 * Subservices:
36 * - @subpage pg_vgsvc_timesync "Time Synchronization"
37 * - @subpage pg_vgsvc_vminfo "VM Information"
38 * - @subpage pg_vgsvc_vmstats "VM Statistics"
39 * - @subpage pg_vgsvc_gstctrl "Guest Control"
40 * - @subpage pg_vgsvc_pagesharing "Page Sharing"
41 * - @subpage pg_vgsvc_memballoon "Memory Balooning"
42 * - @subpage pg_vgsvc_cpuhotplug "CPU Hot-Plugging"
43 * - @subpage pg_vgsvc_automount "Shared Folder Automounting"
44 * - @subpage pg_vgsvc_clipboard "Clipboard (OS/2 only)"
45 *
46 * Now, since the service predates a lot of stuff, including RTGetOpt, we're
47 * currently doing our own version of argument parsing here, which is kind of
48 * stupid. That will hopefully be cleaned up eventually.
49 */
50
51
52/*********************************************************************************************************************************
53* Header Files *
54*********************************************************************************************************************************/
55/** @todo LOG_GROUP*/
56#ifndef _MSC_VER
57# include <unistd.h>
58#endif
59#include <errno.h>
60#ifndef RT_OS_WINDOWS
61# include <signal.h>
62# ifdef RT_OS_OS2
63# define pthread_sigmask sigprocmask
64# endif
65#endif
66#ifdef RT_OS_FREEBSD
67# include <pthread.h>
68#endif
69
70#include <package-generated.h>
71#include "product-generated.h"
72
73#include <iprt/asm.h>
74#include <iprt/buildconfig.h>
75#include <iprt/initterm.h>
76#include <iprt/file.h>
77#ifdef DEBUG
78# include <iprt/memtracker.h>
79#endif
80#include <iprt/env.h>
81#include <iprt/message.h>
82#include <iprt/path.h>
83#include <iprt/process.h>
84#include <iprt/semaphore.h>
85#include <iprt/string.h>
86#include <iprt/stream.h>
87#include <iprt/system.h>
88#include <iprt/thread.h>
89
90#include <VBox/err.h>
91#include <VBox/log.h>
92
93#include "VBoxServiceInternal.h"
94#include "VBoxServiceUtils.h"
95#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
96# include "VBoxServiceControl.h"
97#endif
98#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
99# include "VBoxServiceToolBox.h"
100#endif
101
102
103/*********************************************************************************************************************************
104* Global Variables *
105*********************************************************************************************************************************/
106/** The program name (derived from argv[0]). */
107char *g_pszProgName = (char *)"";
108/** The current verbosity level. */
109unsigned g_cVerbosity = 0;
110char g_szLogFile[RTPATH_MAX + 128] = "";
111char g_szPidFile[RTPATH_MAX] = "";
112/** Logging parameters. */
113/** @todo Make this configurable later. */
114static PRTLOGGER g_pLoggerRelease = NULL;
115static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
116static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
117static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
118/** Critical section for (debug) logging. */
119#ifdef DEBUG
120 RTCRITSECT g_csLog;
121#endif
122/** The default service interval (the -i | --interval) option). */
123uint32_t g_DefaultInterval = 0;
124#ifdef RT_OS_WINDOWS
125/** Signal shutdown to the Windows service thread. */
126static bool volatile g_fWindowsServiceShutdown;
127/** Event the Windows service thread waits for shutdown. */
128static RTSEMEVENT g_hEvtWindowsService;
129#endif
130
131/**
132 * The details of the services that has been compiled in.
133 */
134static struct
135{
136 /** Pointer to the service descriptor. */
137 PCVBOXSERVICE pDesc;
138 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
139 RTTHREAD Thread;
140 /** Whether Pre-init was called. */
141 bool fPreInited;
142 /** Shutdown indicator. */
143 bool volatile fShutdown;
144 /** Indicator set by the service thread exiting. */
145 bool volatile fStopped;
146 /** Whether the service was started or not. */
147 bool fStarted;
148 /** Whether the service is enabled or not. */
149 bool fEnabled;
150} g_aServices[] =
151{
152#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
153 { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
154#endif
155#ifdef VBOX_WITH_VBOXSERVICE_TIMESYNC
156 { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
157#endif
158#ifdef VBOX_WITH_VBOXSERVICE_CLIPBOARD
159 { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
160#endif
161#ifdef VBOX_WITH_VBOXSERVICE_VMINFO
162 { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
163#endif
164#ifdef VBOX_WITH_VBOXSERVICE_CPUHOTPLUG
165 { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
166#endif
167#ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
168# ifdef VBOX_WITH_MEMBALLOON
169 { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
170# endif
171 { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
172#endif
173#if defined(VBOX_WITH_VBOXSERVICE_PAGE_SHARING)
174 { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
175#endif
176#ifdef VBOX_WITH_SHARED_FOLDERS
177 { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
178#endif
179};
180
181
182/*
183 * Default call-backs for services which do not need special behaviour.
184 */
185
186/**
187 * @interface_method_impl{VBOXSERVICE,pfnPreInit, Default Implementation}
188 */
189DECLCALLBACK(int) VGSvcDefaultPreInit(void)
190{
191 return VINF_SUCCESS;
192}
193
194
195/**
196 * @interface_method_impl{VBOXSERVICE,pfnOption, Default Implementation}
197 */
198DECLCALLBACK(int) VGSvcDefaultOption(const char **ppszShort, int argc,
199 char **argv, int *pi)
200{
201 NOREF(ppszShort);
202 NOREF(argc);
203 NOREF(argv);
204 NOREF(pi);
205
206 return -1;
207}
208
209
210/**
211 * @interface_method_impl{VBOXSERVICE,pfnInit, Default Implementation}
212 */
213DECLCALLBACK(int) VGSvcDefaultInit(void)
214{
215 return VINF_SUCCESS;
216}
217
218
219/**
220 * @interface_method_impl{VBOXSERVICE,pfnTerm, Default Implementation}
221 */
222DECLCALLBACK(void) VGSvcDefaultTerm(void)
223{
224 return;
225}
226
227
228/**
229 * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
230 */
231static DECLCALLBACK(void) vgsvcLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
232{
233 /* Some introductory information. */
234 static RTTIMESPEC s_TimeSpec;
235 char szTmp[256];
236 if (enmPhase == RTLOGPHASE_BEGIN)
237 RTTimeNow(&s_TimeSpec);
238 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
239
240 switch (enmPhase)
241 {
242 case RTLOGPHASE_BEGIN:
243 {
244 pfnLog(pLoggerRelease,
245 "VBoxService %s r%s (verbosity: %u) %s (%s %s) release log\n"
246 "Log opened %s\n",
247 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity, VBOX_BUILD_TARGET,
248 __DATE__, __TIME__, szTmp);
249
250 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
251 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
252 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
253 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
254 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
255 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
256 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
257 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
258 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
259 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
260 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
261 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
262
263 /* the package type is interesting for Linux distributions */
264 char szExecName[RTPATH_MAX];
265 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
266 pfnLog(pLoggerRelease,
267 "Executable: %s\n"
268 "Process ID: %u\n"
269 "Package type: %s"
270#ifdef VBOX_OSE
271 " (OSE)"
272#endif
273 "\n",
274 pszExecName ? pszExecName : "unknown",
275 RTProcSelf(),
276 VBOX_PACKAGE_STRING);
277 break;
278 }
279
280 case RTLOGPHASE_PREROTATE:
281 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
282 break;
283
284 case RTLOGPHASE_POSTROTATE:
285 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
286 break;
287
288 case RTLOGPHASE_END:
289 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
290 break;
291
292 default:
293 /* nothing */
294 break;
295 }
296}
297
298
299/**
300 * Creates the default release logger outputting to the specified file.
301 *
302 * Pass NULL to disabled logging.
303 *
304 * @return IPRT status code.
305 * @param pszLogFile Filename for log output. NULL disables logging
306 * (r=bird: No, it doesn't!).
307 */
308int VGSvcLogCreate(const char *pszLogFile)
309{
310 /* Create release logger (stdout + file). */
311 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
312 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME;
313#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
314 fFlags |= RTLOGFLAGS_USECRLF;
315#endif
316 int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
317#ifdef DEBUG
318 "VBOXSERVICE_LOG",
319#else
320 "VBOXSERVICE_RELEASE_LOG",
321#endif
322 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
323 RTLOGDEST_STDOUT | RTLOGDEST_USER,
324 vgsvcLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
325 NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
326 if (RT_SUCCESS(rc))
327 {
328 /* register this logger as the release logger */
329 RTLogRelSetDefaultInstance(g_pLoggerRelease);
330
331 /* Explicitly flush the log in case of VBOXSERVICE_RELEASE_LOG=buffered. */
332 RTLogFlush(g_pLoggerRelease);
333 }
334
335 return rc;
336}
337
338
339/**
340 * Logs a verbose message.
341 *
342 * @param pszFormat The message text.
343 * @param va Format arguments.
344 */
345void VGSvcLogV(const char *pszFormat, va_list va)
346{
347#ifdef DEBUG
348 int rc = RTCritSectEnter(&g_csLog);
349 if (RT_SUCCESS(rc))
350 {
351#endif
352 char *psz = NULL;
353 RTStrAPrintfV(&psz, pszFormat, va);
354
355 AssertPtr(psz);
356 LogRel(("%s", psz));
357
358 RTStrFree(psz);
359#ifdef DEBUG
360 RTCritSectLeave(&g_csLog);
361 }
362#endif
363}
364
365
366/**
367 * Destroys the currently active logging instance.
368 */
369void VGSvcLogDestroy(void)
370{
371 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
372}
373
374
375/**
376 * Displays the program usage message.
377 *
378 * @returns 1.
379 */
380static int vgsvcUsage(void)
381{
382 RTPrintf("Usage: %s [-f|--foreground] [-v|--verbose] [-l|--logfile <file>]\n"
383 " [-p|--pidfile <file>] [-i|--interval <seconds>]\n"
384 " [--disable-<service>] [--enable-<service>]\n"
385 " [--only-<service>] [-h|-?|--help]\n", g_pszProgName);
386#ifdef RT_OS_WINDOWS
387 RTPrintf(" [-r|--register] [-u|--unregister]\n");
388#endif
389 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
390 if (g_aServices[j].pDesc->pszUsage)
391 RTPrintf("%s\n", g_aServices[j].pDesc->pszUsage);
392 RTPrintf("\n"
393 "Options:\n"
394 " -i | --interval The default interval.\n"
395 " -f | --foreground Don't daemonize the program. For debugging.\n"
396 " -l | --logfile <file> Enables logging to a file.\n"
397 " -p | --pidfile <file> Write the process ID to a file.\n"
398 " -v | --verbose Increment the verbosity level. For debugging.\n"
399 " -V | --version Show version information.\n"
400 " -h | -? | --help Show this message and exit with status 1.\n"
401 );
402#ifdef RT_OS_WINDOWS
403 RTPrintf(" -r | --register Installs the service.\n"
404 " -u | --unregister Uninstall service.\n");
405#endif
406
407 RTPrintf("\n"
408 "Service-specific options:\n");
409 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
410 {
411 RTPrintf(" --enable-%-14s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
412 RTPrintf(" --disable-%-13s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
413 RTPrintf(" --only-%-16s Only enables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
414 if (g_aServices[j].pDesc->pszOptions)
415 RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
416 }
417 RTPrintf("\n"
418 " Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
419
420 return 1;
421}
422
423
424/**
425 * Displays an error message.
426 *
427 * @returns RTEXITCODE_FAILURE.
428 * @param pszFormat The message text.
429 * @param ... Format arguments.
430 */
431RTEXITCODE VGSvcError(const char *pszFormat, ...)
432{
433 va_list args;
434 va_start(args, pszFormat);
435 char *psz = NULL;
436 RTStrAPrintfV(&psz, pszFormat, args);
437 va_end(args);
438
439 AssertPtr(psz);
440 LogRel(("Error: %s", psz));
441
442 RTStrFree(psz);
443
444 return RTEXITCODE_FAILURE;
445}
446
447
448/**
449 * Displays a verbose message based on the currently
450 * set global verbosity level.
451 *
452 * @param iLevel Minimum log level required to display this message.
453 * @param pszFormat The message text.
454 * @param ... Format arguments.
455 */
456void VGSvcVerbose(unsigned iLevel, const char *pszFormat, ...)
457{
458 if (iLevel <= g_cVerbosity)
459 {
460 va_list va;
461 va_start(va, pszFormat);
462 VGSvcLogV(pszFormat, va);
463 va_end(va);
464 }
465}
466
467
468/**
469 * Reports the current VBoxService status to the host.
470 *
471 * This makes sure that the Failed state is sticky.
472 *
473 * @return IPRT status code.
474 * @param enmStatus Status to report to the host.
475 */
476int VGSvcReportStatus(VBoxGuestFacilityStatus enmStatus)
477{
478 /*
479 * VBoxGuestFacilityStatus_Failed is sticky.
480 */
481 static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
482 VGSvcVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
483 if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
484 {
485 int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService, enmStatus, 0 /* Flags */);
486 if (RT_FAILURE(rc))
487 {
488 VGSvcError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
489 return rc;
490 }
491 s_enmLastStatus = enmStatus;
492 }
493 return VINF_SUCCESS;
494}
495
496
497/**
498 * Gets a 32-bit value argument.
499 * @todo Get rid of this and VGSvcArgString() as soon as we have RTOpt handling.
500 *
501 * @returns 0 on success, non-zero exit code on error.
502 * @param argc The argument count.
503 * @param argv The argument vector
504 * @param psz Where in *pi to start looking for the value argument.
505 * @param pi Where to find and perhaps update the argument index.
506 * @param pu32 Where to store the 32-bit value.
507 * @param u32Min The minimum value.
508 * @param u32Max The maximum value.
509 */
510int VGSvcArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
511{
512 if (*psz == ':' || *psz == '=')
513 psz++;
514 if (!*psz)
515 {
516 if (*pi + 1 >= argc)
517 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing value for the '%s' argument\n", argv[*pi]);
518 psz = argv[++*pi];
519 }
520
521 char *pszNext;
522 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
523 if (RT_FAILURE(rc) || *pszNext)
524 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to convert interval '%s' to a number\n", psz);
525 if (*pu32 < u32Min || *pu32 > u32Max)
526 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The timesync interval of %RU32 seconds is out of range [%RU32..%RU32]\n",
527 *pu32, u32Min, u32Max);
528 return 0;
529}
530
531
532/** @todo Get rid of this and VGSvcArgUInt32() as soon as we have RTOpt handling. */
533static int vgsvcArgString(int argc, char **argv, const char *psz, int *pi, char *pszBuf, size_t cbBuf)
534{
535 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
536 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
537
538 if (*psz == ':' || *psz == '=')
539 psz++;
540 if (!*psz)
541 {
542 if (*pi + 1 >= argc)
543 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing string for the '%s' argument\n", argv[*pi]);
544 psz = argv[++*pi];
545 }
546
547 if (!RTStrPrintf(pszBuf, cbBuf, "%s", psz))
548 return RTMsgErrorExit(RTEXITCODE_FAILURE, "String for '%s' argument too big\n", argv[*pi]);
549 return 0;
550}
551
552
553/**
554 * The service thread.
555 *
556 * @returns Whatever the worker function returns.
557 * @param ThreadSelf My thread handle.
558 * @param pvUser The service index.
559 */
560static DECLCALLBACK(int) vgsvcThread(RTTHREAD ThreadSelf, void *pvUser)
561{
562 const unsigned i = (uintptr_t)pvUser;
563
564#ifndef RT_OS_WINDOWS
565 /*
566 * Block all signals for this thread. Only the main thread will handle signals.
567 */
568 sigset_t signalMask;
569 sigfillset(&signalMask);
570 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
571#endif
572
573 int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
574 ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
575 RTThreadUserSignal(ThreadSelf);
576 return rc;
577}
578
579
580/**
581 * Lazily calls the pfnPreInit method on each service.
582 *
583 * @returns VBox status code, error message displayed.
584 */
585static RTEXITCODE vgsvcLazyPreInit(void)
586{
587 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
588 if (!g_aServices[j].fPreInited)
589 {
590 int rc = g_aServices[j].pDesc->pfnPreInit();
591 if (RT_FAILURE(rc))
592 return VGSvcError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
593 g_aServices[j].fPreInited = true;
594 }
595 return RTEXITCODE_SUCCESS;
596}
597
598
599/**
600 * Count the number of enabled services.
601 */
602static unsigned vgsvcCountEnabledServices(void)
603{
604 unsigned cEnabled = 0;
605 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
606 cEnabled += g_aServices[i].fEnabled;
607 return cEnabled;
608}
609
610
611#ifdef RT_OS_WINDOWS
612/**
613 * Console control event callback.
614 *
615 * @returns TRUE if handled, FALSE if not.
616 * @param dwCtrlType The control event type.
617 *
618 * @remarks This is generally called on a new thread, so we're racing every
619 * other thread in the process.
620 */
621static BOOL WINAPI vgsvcWinConsoleControlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
622{
623 int rc = VINF_SUCCESS;
624 bool fEventHandled = FALSE;
625 switch (dwCtrlType)
626 {
627 /* User pressed CTRL+C or CTRL+BREAK or an external event was sent
628 * via GenerateConsoleCtrlEvent(). */
629 case CTRL_BREAK_EVENT:
630 case CTRL_CLOSE_EVENT:
631 case CTRL_C_EVENT:
632 VGSvcVerbose(2, "ControlHandler: Received break/close event\n");
633 rc = VGSvcStopServices();
634 fEventHandled = TRUE;
635 break;
636 default:
637 break;
638 /** @todo Add other events here. */
639 }
640
641 if (RT_FAILURE(rc))
642 VGSvcError("ControlHandler: Event %ld handled with error rc=%Rrc\n",
643 dwCtrlType, rc);
644 return fEventHandled;
645}
646#endif /* RT_OS_WINDOWS */
647
648
649/**
650 * Starts the service.
651 *
652 * @returns VBox status code, errors are fully bitched.
653 *
654 * @remarks Also called from VBoxService-win.cpp, thus not static.
655 */
656int VGSvcStartServices(void)
657{
658 int rc;
659
660 VGSvcReportStatus(VBoxGuestFacilityStatus_Init);
661
662 /*
663 * Initialize the services.
664 */
665 VGSvcVerbose(2, "Initializing services ...\n");
666 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
667 if (g_aServices[j].fEnabled)
668 {
669 rc = g_aServices[j].pDesc->pfnInit();
670 if (RT_FAILURE(rc))
671 {
672 if (rc != VERR_SERVICE_DISABLED)
673 {
674 VGSvcError("Service '%s' failed to initialize: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
675 VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
676 return rc;
677 }
678
679 g_aServices[j].fEnabled = false;
680 VGSvcVerbose(0, "Service '%s' was disabled because of missing functionality\n", g_aServices[j].pDesc->pszName);
681 }
682 }
683
684 /*
685 * Start the service(s).
686 */
687 VGSvcVerbose(2, "Starting services ...\n");
688 rc = VINF_SUCCESS;
689 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
690 {
691 if (!g_aServices[j].fEnabled)
692 continue;
693
694 VGSvcVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
695 rc = RTThreadCreate(&g_aServices[j].Thread, vgsvcThread, (void *)(uintptr_t)j, 0,
696 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
697 if (RT_FAILURE(rc))
698 {
699 VGSvcError("RTThreadCreate failed, rc=%Rrc\n", rc);
700 break;
701 }
702 g_aServices[j].fStarted = true;
703
704 /* Wait for the thread to initialize. */
705 /** @todo There is a race between waiting and checking
706 * the fShutdown flag of a thread here and processing
707 * the thread's actual worker loop. If the thread decides
708 * to exit the loop before we skipped the fShutdown check
709 * below the service will fail to start! */
710 /** @todo This presumably means either a one-shot service or that
711 * something has gone wrong. In the second case treating it as failure
712 * to start is probably right, so we need a way to signal the first
713 * rather than leaving the idle thread hanging around. A flag in the
714 * service description? */
715 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
716 if (g_aServices[j].fShutdown)
717 {
718 VGSvcError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
719 rc = VERR_GENERAL_FAILURE;
720 }
721 }
722
723 if (RT_SUCCESS(rc))
724 VGSvcVerbose(1, "All services started.\n");
725 else
726 {
727 VGSvcError("An error occcurred while the services!\n");
728 VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
729 }
730 return rc;
731}
732
733
734/**
735 * Stops and terminates the services.
736 *
737 * This should be called even when VBoxServiceStartServices fails so it can
738 * clean up anything that we succeeded in starting.
739 *
740 * @remarks Also called from VBoxService-win.cpp, thus not static.
741 */
742int VGSvcStopServices(void)
743{
744 VGSvcReportStatus(VBoxGuestFacilityStatus_Terminating);
745
746 /*
747 * Signal all the services.
748 */
749 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
750 ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
751
752 /*
753 * Do the pfnStop callback on all running services.
754 */
755 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
756 if (g_aServices[j].fStarted)
757 {
758 VGSvcVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
759 g_aServices[j].pDesc->pfnStop();
760 }
761
762 VGSvcVerbose(3, "All stop functions for services called\n");
763
764 /*
765 * Wait for all the service threads to complete.
766 */
767 int rc = VINF_SUCCESS;
768 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
769 {
770 if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
771 continue;
772 if (g_aServices[j].Thread != NIL_RTTHREAD)
773 {
774 VGSvcVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
775 int rc2 = VINF_SUCCESS;
776 for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
777 {
778 rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
779 if (RT_SUCCESS(rc2))
780 break;
781#ifdef RT_OS_WINDOWS
782 /* Notify SCM that it takes a bit longer ... */
783 VGSvcWinSetStopPendingStatus(i + j*32);
784#endif
785 }
786 if (RT_FAILURE(rc2))
787 {
788 VGSvcError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
789 rc = rc2;
790 }
791 }
792 VGSvcVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
793 g_aServices[j].pDesc->pfnTerm();
794 }
795
796#ifdef RT_OS_WINDOWS
797 /*
798 * Wake up and tell the main() thread that we're shutting down (it's
799 * sleeping in VBoxServiceMainWait).
800 */
801 ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
802 if (g_hEvtWindowsService != NIL_RTSEMEVENT)
803 {
804 VGSvcVerbose(3, "Stopping the main thread...\n");
805 int rc2 = RTSemEventSignal(g_hEvtWindowsService);
806 AssertRC(rc2);
807 }
808#endif
809
810 VGSvcVerbose(2, "Stopping services returning: %Rrc\n", rc);
811 VGSvcReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
812 return rc;
813}
814
815
816/**
817 * Block the main thread until the service shuts down.
818 *
819 * @remarks Also called from VBoxService-win.cpp, thus not static.
820 */
821void VGSvcMainWait(void)
822{
823 int rc;
824
825 VGSvcReportStatus(VBoxGuestFacilityStatus_Active);
826
827#ifdef RT_OS_WINDOWS
828 /*
829 * Wait for the semaphore to be signalled.
830 */
831 VGSvcVerbose(1, "Waiting in main thread\n");
832 rc = RTSemEventCreate(&g_hEvtWindowsService);
833 AssertRC(rc);
834 while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown))
835 {
836 rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT);
837 AssertRC(rc);
838 }
839 RTSemEventDestroy(g_hEvtWindowsService);
840 g_hEvtWindowsService = NIL_RTSEMEVENT;
841#else
842 /*
843 * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking
844 * all important signals.
845 *
846 * The annoying EINTR/ERESTART loop is for the benefit of Solaris where
847 * sigwait returns when we receive a SIGCHLD. Kind of makes sense since
848 * the signal has to be delivered... Anyway, darwin (10.9.5) has a much
849 * worse way of dealing with SIGCHLD, apparently it'll just return any
850 * of the signals we're waiting on when SIGCHLD becomes pending on this
851 * thread. So, we wait for SIGCHLD here and ignores it.
852 */
853 sigset_t signalMask;
854 sigemptyset(&signalMask);
855 sigaddset(&signalMask, SIGHUP);
856 sigaddset(&signalMask, SIGINT);
857 sigaddset(&signalMask, SIGQUIT);
858 sigaddset(&signalMask, SIGABRT);
859 sigaddset(&signalMask, SIGTERM);
860 sigaddset(&signalMask, SIGCHLD);
861 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
862
863 int iSignal;
864 do
865 {
866 iSignal = -1;
867 rc = sigwait(&signalMask, &iSignal);
868 }
869 while ( rc == EINTR
870# ifdef ERESTART
871 || rc == ERESTART
872# endif
873 || iSignal == SIGCHLD
874 );
875
876 VGSvcVerbose(3, "VGSvcMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
877#endif /* !RT_OS_WINDOWS */
878}
879
880
881/**
882 * Report VbglR3InitUser / VbglR3Init failure.
883 *
884 * @returns RTEXITCODE_FAILURE
885 * @param rcVbgl The failing status code.
886 */
887static RTEXITCODE vbglInitFailure(int rcVbgl)
888{
889 if (rcVbgl == VERR_ACCESS_DENIED)
890 return RTMsgErrorExit(RTEXITCODE_FAILURE,
891 "Insufficient privileges to start %s! Please start with Administrator/root privileges!\n",
892 g_pszProgName);
893 return RTMsgErrorExit(RTEXITCODE_FAILURE, "VbglR3Init failed with rc=%Rrc\n", rcVbgl);
894}
895
896#ifdef RT_OS_LINUX
897/**
898 * Check for a guest property and start VBoxDRMClient if it exists.
899 *
900 */
901static void startDRMResize(void)
902{
903 uint32_t uGuestPropSvcClientID;
904 int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
905 if (RT_SUCCESS(rc))
906 {
907 rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/DRMResize");
908 if (RT_SUCCESS(rc))
909 {
910 RTMsgInfo("Starting DRM resize service");
911 char szDRMClientPath[RTPATH_MAX];
912 RTPathExecDir(szDRMClientPath, RTPATH_MAX);
913 RTPathStripSuffix(szDRMClientPath);
914 RTPathAppend(szDRMClientPath, RTPATH_MAX, "VBoxDRMClient");
915 const char *apszArgs[1] = { NULL };
916 rc = RTProcCreate("VBoxDRMClient", apszArgs, RTENV_DEFAULT,
917 RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL);
918 if (rc == -1)
919 RTMsgError("Could not start DRM resize service");
920 }
921 }
922}
923#endif
924
925int main(int argc, char **argv)
926{
927 RTEXITCODE rcExit;
928
929 /*
930 * Init globals and such.
931 */
932 int rc = RTR3InitExe(argc, &argv, 0);
933 if (RT_FAILURE(rc))
934 return RTMsgInitFailure(rc);
935 g_pszProgName = RTPathFilename(argv[0]);
936#ifdef RT_OS_WINDOWS
937 VGSvcWinResolveApis();
938#endif
939#ifdef DEBUG
940 rc = RTCritSectInit(&g_csLog);
941 AssertRC(rc);
942#endif
943
944#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
945 /*
946 * Run toolbox code before all other stuff since these things are simpler
947 * shell/file/text utility like programs that just happens to be inside
948 * VBoxService and shouldn't be subject to /dev/vboxguest, pid-files and
949 * global mutex restrictions.
950 */
951 if (VGSvcToolboxMain(argc, argv, &rcExit))
952 return rcExit;
953#endif
954
955 bool fUserSession = false;
956#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
957 /*
958 * Check if we're the specially spawned VBoxService.exe process that
959 * handles a guest control session.
960 */
961 if ( argc >= 2
962 && !RTStrICmp(argv[1], VBOXSERVICECTRLSESSION_GETOPT_PREFIX))
963 fUserSession = true;
964#endif
965
966 /*
967 * Connect to the kernel part before daemonizing and *before* we do the sub-service
968 * pre-init just in case one of services needs do to some initial stuff with it.
969 *
970 * However, we do not fail till after we've parsed arguments, because that will
971 * prevent useful stuff like --help, --register, --unregister and --version from
972 * working when the driver hasn't been installed/loaded yet.
973 */
974 int const rcVbgl = fUserSession ? VbglR3InitUser() : VbglR3Init();
975
976#ifdef RT_OS_WINDOWS
977 /*
978 * Check if we're the specially spawned VBoxService.exe process that
979 * handles page fusion. This saves an extra statically linked executable.
980 */
981 if ( argc == 2
982 && !RTStrICmp(argv[1], "pagefusion"))
983 {
984 if (RT_SUCCESS(rcVbgl))
985 return VGSvcPageSharingWorkerChild();
986 return vbglInitFailure(rcVbgl);
987 }
988#endif
989
990#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
991 /*
992 * Check if we're the specially spawned VBoxService.exe process that
993 * handles a guest control session.
994 */
995 if (fUserSession)
996 {
997 if (RT_SUCCESS(rcVbgl))
998 return VGSvcGstCtrlSessionSpawnInit(argc, argv);
999 return vbglInitFailure(rcVbgl);
1000 }
1001#endif
1002
1003 /*
1004 * Parse the arguments.
1005 *
1006 * Note! This code predates RTGetOpt, thus the manual parsing.
1007 */
1008 bool fDaemonize = true;
1009 bool fDaemonized = false;
1010 for (int i = 1; i < argc; i++)
1011 {
1012 const char *psz = argv[i];
1013 if (*psz != '-')
1014 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'\n", psz);
1015 psz++;
1016
1017 /* translate long argument to short */
1018 if (*psz == '-')
1019 {
1020 psz++;
1021 size_t cch = strlen(psz);
1022#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
1023 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
1024 if (MATCHES("foreground"))
1025 psz = "f";
1026 else if (MATCHES("verbose"))
1027 psz = "v";
1028 else if (MATCHES("version"))
1029 psz = "V";
1030 else if (MATCHES("help"))
1031 psz = "h";
1032 else if (MATCHES("interval"))
1033 psz = "i";
1034#ifdef RT_OS_WINDOWS
1035 else if (MATCHES("register"))
1036 psz = "r";
1037 else if (MATCHES("unregister"))
1038 psz = "u";
1039#endif
1040 else if (MATCHES("logfile"))
1041 psz = "l";
1042 else if (MATCHES("pidfile"))
1043 psz = "p";
1044 else if (MATCHES("daemonized"))
1045 {
1046 fDaemonized = true;
1047 continue;
1048 }
1049 else
1050 {
1051 bool fFound = false;
1052
1053 if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-")))
1054 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1055 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
1056 g_aServices[j].fEnabled = true;
1057
1058 if (cch > sizeof("disable-") && !memcmp(psz, RT_STR_TUPLE("disable-")))
1059 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1060 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
1061 g_aServices[j].fEnabled = false;
1062
1063 if (cch > sizeof("only-") && !memcmp(psz, RT_STR_TUPLE("only-")))
1064 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
1065 {
1066 g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName);
1067 if (g_aServices[j].fEnabled)
1068 fFound = true;
1069 }
1070
1071 if (!fFound)
1072 {
1073 rcExit = vgsvcLazyPreInit();
1074 if (rcExit != RTEXITCODE_SUCCESS)
1075 return rcExit;
1076 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1077 {
1078 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
1079 fFound = rc == VINF_SUCCESS;
1080 if (fFound)
1081 break;
1082 if (rc != -1)
1083 return rc;
1084 }
1085 }
1086 if (!fFound)
1087 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%s'\n", argv[i]);
1088 continue;
1089 }
1090#undef MATCHES
1091 }
1092
1093 /* handle the string of short options. */
1094 do
1095 {
1096 switch (*psz)
1097 {
1098 case 'i':
1099 rc = VGSvcArgUInt32(argc, argv, psz + 1, &i, &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
1100 if (rc)
1101 return rc;
1102 psz = NULL;
1103 break;
1104
1105 case 'f':
1106 fDaemonize = false;
1107 break;
1108
1109 case 'v':
1110 g_cVerbosity++;
1111 break;
1112
1113 case 'V':
1114 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1115 return RTEXITCODE_SUCCESS;
1116
1117 case 'h':
1118 case '?':
1119 return vgsvcUsage();
1120
1121#ifdef RT_OS_WINDOWS
1122 case 'r':
1123 return VGSvcWinInstall();
1124
1125 case 'u':
1126 return VGSvcWinUninstall();
1127#endif
1128
1129 case 'l':
1130 {
1131 rc = vgsvcArgString(argc, argv, psz + 1, &i, g_szLogFile, sizeof(g_szLogFile));
1132 if (rc)
1133 return rc;
1134 psz = NULL;
1135 break;
1136 }
1137
1138 case 'p':
1139 {
1140 rc = vgsvcArgString(argc, argv, psz + 1, &i, g_szPidFile, sizeof(g_szPidFile));
1141 if (rc)
1142 return rc;
1143 psz = NULL;
1144 break;
1145 }
1146
1147 default:
1148 {
1149 rcExit = vgsvcLazyPreInit();
1150 if (rcExit != RTEXITCODE_SUCCESS)
1151 return rcExit;
1152
1153 bool fFound = false;
1154 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
1155 {
1156 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
1157 fFound = rc == VINF_SUCCESS;
1158 if (fFound)
1159 break;
1160 if (rc != -1)
1161 return rc;
1162 }
1163 if (!fFound)
1164 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%c' (%s)\n", *psz, argv[i]);
1165 break;
1166 }
1167 }
1168 } while (psz && *++psz);
1169 }
1170
1171 /* Now we can report the VBGL failure. */
1172 if (RT_FAILURE(rcVbgl))
1173 return vbglInitFailure(rcVbgl);
1174
1175 /* Check that at least one service is enabled. */
1176 if (vgsvcCountEnabledServices() == 0)
1177 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n");
1178
1179 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
1180 if (RT_FAILURE(rc))
1181 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log '%s', rc=%Rrc\n",
1182 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
1183
1184 /* Call pre-init if we didn't do it already. */
1185 rcExit = vgsvcLazyPreInit();
1186 if (rcExit != RTEXITCODE_SUCCESS)
1187 return rcExit;
1188
1189#ifdef RT_OS_LINUX
1190 startDRMResize();
1191#endif
1192
1193#ifdef RT_OS_WINDOWS
1194 /*
1195 * Make sure only one instance of VBoxService runs at a time. Create a
1196 * global mutex for that.
1197 *
1198 * Note! The \\Global\ namespace was introduced with Win2K, thus the
1199 * version check.
1200 * Note! If the mutex exists CreateMutex will open it and set last error to
1201 * ERROR_ALREADY_EXISTS.
1202 */
1203 OSVERSIONINFOEX OSInfoEx;
1204 RT_ZERO(OSInfoEx);
1205 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1206
1207 SetLastError(NO_ERROR);
1208 HANDLE hMutexAppRunning;
1209 if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
1210 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
1211 && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */)
1212 hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME);
1213 else
1214 hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME);
1215 if (hMutexAppRunning == NULL)
1216 {
1217 DWORD dwErr = GetLastError();
1218 if ( dwErr == ERROR_ALREADY_EXISTS
1219 || dwErr == ERROR_ACCESS_DENIED)
1220 {
1221 VGSvcError("%s is already running! Terminating.\n", g_pszProgName);
1222 return RTEXITCODE_FAILURE;
1223 }
1224
1225 VGSvcError("CreateMutex failed with last error %u! Terminating.\n", GetLastError());
1226 return RTEXITCODE_FAILURE;
1227 }
1228
1229#else /* !RT_OS_WINDOWS */
1230 /** @todo Add PID file creation here? */
1231#endif /* !RT_OS_WINDOWS */
1232
1233 VGSvcVerbose(0, "%s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
1234
1235 /*
1236 * Daemonize if requested.
1237 */
1238 if (fDaemonize && !fDaemonized)
1239 {
1240#ifdef RT_OS_WINDOWS
1241 VGSvcVerbose(2, "Starting service dispatcher ...\n");
1242 rcExit = VGSvcWinEnterCtrlDispatcher();
1243#else
1244 VGSvcVerbose(1, "Daemonizing...\n");
1245 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */,
1246 false /* fRespawn */, NULL /* pcRespawn */);
1247 if (RT_FAILURE(rc))
1248 return VGSvcError("Daemon failed: %Rrc\n", rc);
1249 /* in-child */
1250#endif
1251 }
1252#ifdef RT_OS_WINDOWS
1253 else
1254#endif
1255 {
1256 /*
1257 * Windows: We're running the service as a console application now. Start the
1258 * services, enter the main thread's run loop and stop them again
1259 * when it returns.
1260 *
1261 * POSIX: This is used for both daemons and console runs. Start all services
1262 * and return immediately.
1263 */
1264#ifdef RT_OS_WINDOWS
1265# ifndef RT_OS_NT4 /** @todo r=bird: What's RT_OS_NT4??? */
1266 /* Install console control handler. */
1267 if (!SetConsoleCtrlHandler(vgsvcWinConsoleControlHandler, TRUE /* Add handler */))
1268 {
1269 VGSvcError("Unable to add console control handler, error=%ld\n", GetLastError());
1270 /* Just skip this error, not critical. */
1271 }
1272# endif /* !RT_OS_NT4 */
1273#endif /* RT_OS_WINDOWS */
1274 rc = VGSvcStartServices();
1275 RTFILE hPidFile = NIL_RTFILE;
1276 if (RT_SUCCESS(rc))
1277 if (g_szPidFile[0])
1278 rc = VbglR3PidFile(g_szPidFile, &hPidFile);
1279 rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1280 if (RT_SUCCESS(rc))
1281 VGSvcMainWait();
1282 if (g_szPidFile[0] && hPidFile != NIL_RTFILE)
1283 VbglR3ClosePidFile(g_szPidFile, hPidFile);
1284#ifdef RT_OS_WINDOWS
1285# ifndef RT_OS_NT4
1286 /* Uninstall console control handler. */
1287 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
1288 {
1289 VGSvcError("Unable to remove console control handler, error=%ld\n", GetLastError());
1290 /* Just skip this error, not critical. */
1291 }
1292# endif /* !RT_OS_NT4 */
1293#else /* !RT_OS_WINDOWS */
1294 /* On Windows - since we're running as a console application - we already stopped all services
1295 * through the console control handler. So only do the stopping of services here on other platforms
1296 * where the break/shutdown/whatever signal was just received. */
1297 VGSvcStopServices();
1298#endif /* RT_OS_WINDOWS */
1299 }
1300 VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
1301
1302#ifdef RT_OS_WINDOWS
1303 /*
1304 * Cleanup mutex.
1305 */
1306 CloseHandle(hMutexAppRunning);
1307#endif
1308
1309 VGSvcVerbose(0, "Ended.\n");
1310
1311#ifdef DEBUG
1312 RTCritSectDelete(&g_csLog);
1313 //RTMemTrackerDumpAllToStdOut();
1314#endif
1315
1316 VGSvcLogDestroy();
1317
1318 return rcExit;
1319}
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