VirtualBox

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

Last change on this file since 96503 was 96503, checked in by vboxsync, 21 months ago

Add/VBoxService: Replaced GetVersionEx calls with RTSystemGetNtVersion, so we don't need the IPRT fallback code for ancient NT versions that doesn't have that API. Checked out the KB970910 description against what we're doing with the API, and found that it shouldn't affect us, so RDP detection can run on all systems that have a WTSQuerySessionInformation API. Corrected QueryFullProcessImageNameW/GetModuleFileNameExW calls. [build fix] bugref:10162

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

© 2023 Oracle
ContactPrivacy policyTerms of Use