VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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