VirtualBox

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

Last change on this file since 95852 was 95852, checked in by vboxsync, 2 years ago

VBoxService: Dropped unused errno.h from main file. bugref:10261

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