VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months 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.6 KB
RevLine 
[18712]1/* $Id: VBoxService.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
[3655]2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
[98103]7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
[3655]8 *
[96407]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
[3655]26 */
27
28
[58033]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 */
[3655]60
[62470]61
[57358]62/*********************************************************************************************************************************
63* Header Files *
64*********************************************************************************************************************************/
[18712]65/** @todo LOG_GROUP*/
[6029]66#ifndef _MSC_VER
67# include <unistd.h>
68#endif
[28599]69#ifndef RT_OS_WINDOWS
[95856]70# include <errno.h>
[28599]71# include <signal.h>
[33395]72# ifdef RT_OS_OS2
73# define pthread_sigmask sigprocmask
74# endif
[28599]75#endif
[33230]76#ifdef RT_OS_FREEBSD
77# include <pthread.h>
78#endif
[3655]79
[40129]80#include <package-generated.h>
[28599]81#include "product-generated.h"
[40129]82
[25479]83#include <iprt/asm.h>
84#include <iprt/buildconfig.h>
85#include <iprt/initterm.h>
[50051]86#include <iprt/file.h>
[39843]87#ifdef DEBUG
88# include <iprt/memtracker.h>
89#endif
[85962]90#include <iprt/env.h>
[36331]91#include <iprt/message.h>
[25479]92#include <iprt/path.h>
[40129]93#include <iprt/process.h>
[29762]94#include <iprt/semaphore.h>
[3655]95#include <iprt/string.h>
96#include <iprt/stream.h>
[40129]97#include <iprt/system.h>
[25479]98#include <iprt/thread.h>
99
[76419]100#include <VBox/err.h>
[18672]101#include <VBox/log.h>
[25479]102
[3655]103#include "VBoxServiceInternal.h"
[85962]104#include "VBoxServiceUtils.h"
[60583]105#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
[44863]106# include "VBoxServiceControl.h"
107#endif
[60622]108#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
109# include "VBoxServiceToolBox.h"
110#endif
[3655]111
[18712]112
[57358]113/*********************************************************************************************************************************
114* Global Variables *
115*********************************************************************************************************************************/
[3655]116/** The program name (derived from argv[0]). */
[40129]117char *g_pszProgName = (char *)"";
[3655]118/** The current verbosity level. */
[57966]119unsigned g_cVerbosity = 0;
[44863]120char g_szLogFile[RTPATH_MAX + 128] = "";
[57721]121char g_szPidFile[RTPATH_MAX] = "";
[40129]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. */
[38113]128/** Critical section for (debug) logging. */
129#ifdef DEBUG
[40129]130 RTCRITSECT g_csLog;
[38113]131#endif
[3655]132/** The default service interval (the -i | --interval) option). */
[40129]133uint32_t g_DefaultInterval = 0;
[29762]134#ifdef RT_OS_WINDOWS
135/** Signal shutdown to the Windows service thread. */
[29817]136static bool volatile g_fWindowsServiceShutdown;
[29762]137/** Event the Windows service thread waits for shutdown. */
[40129]138static RTSEMEVENT g_hEvtWindowsService;
[29762]139#endif
[3655]140
141/**
142 * The details of the services that has been compiled in.
143 */
[6029]144static struct
[3655]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;
[36331]150 /** Whether Pre-init was called. */
151 bool fPreInited;
[3655]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;
[6029]160} g_aServices[] =
[3655]161{
[60583]162#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
[36331]163 { &g_Control, NIL_RTTHREAD, false, false, false, false, true },
[6029]164#endif
[60583]165#ifdef VBOX_WITH_VBOXSERVICE_TIMESYNC
[36331]166 { &g_TimeSync, NIL_RTTHREAD, false, false, false, false, true },
[6029]167#endif
[60583]168#ifdef VBOX_WITH_VBOXSERVICE_CLIPBOARD
[36331]169 { &g_Clipboard, NIL_RTTHREAD, false, false, false, false, true },
[6029]170#endif
[60583]171#ifdef VBOX_WITH_VBOXSERVICE_VMINFO
[36331]172 { &g_VMInfo, NIL_RTTHREAD, false, false, false, false, true },
[19321]173#endif
[60583]174#ifdef VBOX_WITH_VBOXSERVICE_CPUHOTPLUG
[36331]175 { &g_CpuHotPlug, NIL_RTTHREAD, false, false, false, false, true },
[25975]176#endif
[60583]177#ifdef VBOX_WITH_VBOXSERVICE_MANAGEMENT
[29020]178# ifdef VBOX_WITH_MEMBALLOON
[36331]179 { &g_MemBalloon, NIL_RTTHREAD, false, false, false, false, true },
[29020]180# endif
[36331]181 { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true },
[26292]182#endif
[60583]183#if defined(VBOX_WITH_VBOXSERVICE_PAGE_SHARING)
[36331]184 { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true },
[29343]185#endif
[31202]186#ifdef VBOX_WITH_SHARED_FOLDERS
[36331]187 { &g_AutoMount, NIL_RTTHREAD, false, false, false, false, true },
[31202]188#endif
[3655]189};
190
191
[58029]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)
[51570]200{
201 return VINF_SUCCESS;
202}
203
[58029]204
205/**
206 * @interface_method_impl{VBOXSERVICE,pfnOption, Default Implementation}
207 */
208DECLCALLBACK(int) VGSvcDefaultOption(const char **ppszShort, int argc,
[51570]209 char **argv, int *pi)
210{
211 NOREF(ppszShort);
212 NOREF(argc);
213 NOREF(argv);
214 NOREF(pi);
215
216 return -1;
217}
218
[58029]219
220/**
221 * @interface_method_impl{VBOXSERVICE,pfnInit, Default Implementation}
222 */
223DECLCALLBACK(int) VGSvcDefaultInit(void)
[51570]224{
225 return VINF_SUCCESS;
226}
227
[58029]228
229/**
230 * @interface_method_impl{VBOXSERVICE,pfnTerm, Default Implementation}
231 */
232DECLCALLBACK(void) VGSvcDefaultTerm(void)
[51570]233{
234 return;
235}
236
[58029]237
[3655]238/**
[58029]239 * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
[40129]240 */
[58029]241static DECLCALLBACK(void) vgsvcLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
[40129]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,
[58029]255 "VBoxService %s r%s (verbosity: %u) %s (%s %s) release log\n"
[40129]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);
[58029]269 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
[40129]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:
[58029]303 /* nothing */
304 break;
[40129]305 }
306}
307
308
309/**
310 * Creates the default release logger outputting to the specified file.
311 *
[58029]312 * Pass NULL to disabled logging.
313 *
[40129]314 * @return IPRT status code.
[69779]315 * @param pszLogFile Filename for log output. NULL disables logging
316 * (r=bird: No, it doesn't!).
[40129]317 */
[58029]318int VGSvcLogCreate(const char *pszLogFile)
[40129]319{
320 /* Create release logger (stdout + file). */
321 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
[75721]322 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME;
[40129]323#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
324 fFlags |= RTLOGFLAGS_USECRLF;
325#endif
[90829]326 int rc = RTLogCreateEx(&g_pLoggerRelease, "VBOXSERVICE_RELEASE_LOG", fFlags, "all",
[77557]327 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
[90862]328 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT | RTLOGDEST_USER,
[58029]329 vgsvcLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
[94624]330 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
[69779]331 NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
[40129]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
[44863]344
[69987]345/**
346 * Logs a verbose message.
347 *
348 * @param pszFormat The message text.
[69997]349 * @param va Format arguments.
[69987]350 */
[69997]351void VGSvcLogV(const char *pszFormat, va_list va)
[69987]352{
353#ifdef DEBUG
354 int rc = RTCritSectEnter(&g_csLog);
355 if (RT_SUCCESS(rc))
356 {
357#endif
358 char *psz = NULL;
[69997]359 RTStrAPrintfV(&psz, pszFormat, va);
[69987]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 */
[58029]375void VGSvcLogDestroy(void)
[40129]376{
[40731]377 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
[40129]378}
379
380
381/**
[3655]382 * Displays the program usage message.
[6029]383 *
[3655]384 * @returns 1.
385 */
[58029]386static int vgsvcUsage(void)
[3655]387{
[83974]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);
[18712]392#ifdef RT_OS_WINDOWS
[83974]393 RTPrintf(" [-r|--register] [-u|--unregister]\n");
[18639]394#endif
[3655]395 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[29594]396 if (g_aServices[j].pDesc->pszUsage)
397 RTPrintf("%s\n", g_aServices[j].pDesc->pszUsage);
[3655]398 RTPrintf("\n"
399 "Options:\n"
[29594]400 " -i | --interval The default interval.\n"
[33550]401 " -f | --foreground Don't daemonize the program. For debugging.\n"
[40129]402 " -l | --logfile <file> Enables logging to a file.\n"
[57721]403 " -p | --pidfile <file> Write the process ID to a file.\n"
[29594]404 " -v | --verbose Increment the verbosity level. For debugging.\n"
[36183]405 " -V | --version Show version information.\n"
[29594]406 " -h | -? | --help Show this message and exit with status 1.\n"
[18712]407 );
408#ifdef RT_OS_WINDOWS
[29594]409 RTPrintf(" -r | --register Installs the service.\n"
410 " -u | --unregister Uninstall service.\n");
[18639]411#endif
412
413 RTPrintf("\n"
[29594]414 "Service-specific options:\n");
[3655]415 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
416 {
[29594]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);
[38223]419 RTPrintf(" --only-%-16s Only enables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
[28599]420 if (g_aServices[j].pDesc->pszOptions)
421 RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
[3655]422 }
423 RTPrintf("\n"
[28599]424 " Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
[3655]425
426 return 1;
427}
428
429
430/**
431 * Displays an error message.
[6029]432 *
[29817]433 * @returns RTEXITCODE_FAILURE.
[3655]434 * @param pszFormat The message text.
435 * @param ... Format arguments.
436 */
[58029]437RTEXITCODE VGSvcError(const char *pszFormat, ...)
[3655]438{
[40129]439 va_list args;
440 va_start(args, pszFormat);
441 char *psz = NULL;
442 RTStrAPrintfV(&psz, pszFormat, args);
443 va_end(args);
[3655]444
[40129]445 AssertPtr(psz);
446 LogRel(("Error: %s", psz));
[18670]447
[40129]448 RTStrFree(psz);
[3655]449
[29817]450 return RTEXITCODE_FAILURE;
[3655]451}
452
453
454/**
[69987]455 * Displays a verbose message based on the currently
456 * set global verbosity level.
[6029]457 *
[38633]458 * @param iLevel Minimum log level required to display this message.
[3655]459 * @param pszFormat The message text.
460 * @param ... Format arguments.
461 */
[58029]462void VGSvcVerbose(unsigned iLevel, const char *pszFormat, ...)
[3655]463{
[38633]464 if (iLevel <= g_cVerbosity)
465 {
[69997]466 va_list va;
467 va_start(va, pszFormat);
468 VGSvcLogV(pszFormat, va);
469 va_end(va);
[3655]470 }
471}
472
473
474/**
[35907]475 * Reports the current VBoxService status to the host.
476 *
[36331]477 * This makes sure that the Failed state is sticky.
478 *
[35907]479 * @return IPRT status code.
480 * @param enmStatus Status to report to the host.
481 */
[58029]482int VGSvcReportStatus(VBoxGuestFacilityStatus enmStatus)
[35907]483{
[36331]484 /*
485 * VBoxGuestFacilityStatus_Failed is sticky.
486 */
487 static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
[58029]488 VGSvcVerbose(4, "Setting VBoxService status to %u\n", enmStatus);
[36331]489 if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
490 {
[58031]491 int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService, enmStatus, 0 /* Flags */);
[36331]492 if (RT_FAILURE(rc))
493 {
[58029]494 VGSvcError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
[36331]495 return rc;
496 }
497 s_enmLastStatus = enmStatus;
498 }
499 return VINF_SUCCESS;
[35907]500}
501
502
503/**
[3655]504 * Gets a 32-bit value argument.
[58029]505 * @todo Get rid of this and VGSvcArgString() as soon as we have RTOpt handling.
[3655]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 */
[58029]516int VGSvcArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
[3655]517{
518 if (*psz == ':' || *psz == '=')
519 psz++;
520 if (!*psz)
521 {
522 if (*pi + 1 >= argc)
[40129]523 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing value for the '%s' argument\n", argv[*pi]);
[3655]524 psz = argv[++*pi];
525 }
526
527 char *pszNext;
528 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
529 if (RT_FAILURE(rc) || *pszNext)
[40129]530 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to convert interval '%s' to a number\n", psz);
[3655]531 if (*pu32 < u32Min || *pu32 > u32Max)
[40129]532 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The timesync interval of %RU32 seconds is out of range [%RU32..%RU32]\n",
533 *pu32, u32Min, u32Max);
[3655]534 return 0;
535}
536
[58029]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)
[40129]540{
541 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
[75713]542 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
[3655]543
[40129]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
[3655]559/**
560 * The service thread.
[6029]561 *
[3655]562 * @returns Whatever the worker function returns.
563 * @param ThreadSelf My thread handle.
564 * @param pvUser The service index.
565 */
[58029]566static DECLCALLBACK(int) vgsvcThread(RTTHREAD ThreadSelf, void *pvUser)
[3655]567{
568 const unsigned i = (uintptr_t)pvUser;
[28603]569
570#ifndef RT_OS_WINDOWS
[29020]571 /*
[28603]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
[3655]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
[18712]585
[29762]586/**
[36331]587 * Lazily calls the pfnPreInit method on each service.
588 *
589 * @returns VBox status code, error message displayed.
590 */
[58029]591static RTEXITCODE vgsvcLazyPreInit(void)
[36331]592{
593 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
594 if (!g_aServices[j].fPreInited)
595 {
[51564]596 int rc = g_aServices[j].pDesc->pfnPreInit();
597 if (RT_FAILURE(rc))
[58029]598 return VGSvcError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
[36331]599 g_aServices[j].fPreInited = true;
600 }
601 return RTEXITCODE_SUCCESS;
602}
603
604
605/**
[36328]606 * Count the number of enabled services.
[29762]607 */
[58029]608static unsigned vgsvcCountEnabledServices(void)
[19031]609{
[36328]610 unsigned cEnabled = 0;
611 for (unsigned i = 0; i < RT_ELEMENTS(g_aServices); i++)
612 cEnabled += g_aServices[i].fEnabled;
613 return cEnabled;
[19031]614}
615
[29020]616
[39202]617#ifdef RT_OS_WINDOWS
[58029]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 */
[85121]627static BOOL WINAPI vgsvcWinConsoleControlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
[39202]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:
[58029]638 VGSvcVerbose(2, "ControlHandler: Received break/close event\n");
639 rc = VGSvcStopServices();
[39202]640 fEventHandled = TRUE;
641 break;
642 default:
643 break;
644 /** @todo Add other events here. */
645 }
646
647 if (RT_FAILURE(rc))
[58029]648 VGSvcError("ControlHandler: Event %ld handled with error rc=%Rrc\n",
[39202]649 dwCtrlType, rc);
650 return fEventHandled;
651}
652#endif /* RT_OS_WINDOWS */
653
654
[18712]655/**
656 * Starts the service.
657 *
658 * @returns VBox status code, errors are fully bitched.
[58029]659 *
660 * @remarks Also called from VBoxService-win.cpp, thus not static.
[18712]661 */
[58029]662int VGSvcStartServices(void)
[18670]663{
[18712]664 int rc;
[29020]665
[58029]666 VGSvcReportStatus(VBoxGuestFacilityStatus_Init);
[35907]667
[18670]668 /*
669 * Initialize the services.
670 */
[58029]671 VGSvcVerbose(2, "Initializing services ...\n");
[18670]672 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[51564]673 if (g_aServices[j].fEnabled)
[18712]674 {
[21139]675 rc = g_aServices[j].pDesc->pfnInit();
676 if (RT_FAILURE(rc))
677 {
[29345]678 if (rc != VERR_SERVICE_DISABLED)
[29316]679 {
[58031]680 VGSvcError("Service '%s' failed to initialize: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
[58029]681 VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
[29202]682 return rc;
683 }
[70351]684
[29345]685 g_aServices[j].fEnabled = false;
[58029]686 VGSvcVerbose(0, "Service '%s' was disabled because of missing functionality\n", g_aServices[j].pDesc->pszName);
[21139]687 }
[18712]688 }
[29020]689
[18670]690 /*
691 * Start the service(s).
692 */
[58029]693 VGSvcVerbose(2, "Starting services ...\n");
[18712]694 rc = VINF_SUCCESS;
[18670]695 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
696 {
[29762]697 if (!g_aServices[j].fEnabled)
[18670]698 continue;
699
[58029]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,
[18670]702 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
703 if (RT_FAILURE(rc))
704 {
[58029]705 VGSvcError("RTThreadCreate failed, rc=%Rrc\n", rc);
[18670]706 break;
707 }
708 g_aServices[j].fStarted = true;
709
[36331]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! */
[51570]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? */
[18670]721 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
722 if (g_aServices[j].fShutdown)
[25159]723 {
[58029]724 VGSvcError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
[18670]725 rc = VERR_GENERAL_FAILURE;
[25159]726 }
[18670]727 }
[29762]728
[35027]729 if (RT_SUCCESS(rc))
[58029]730 VGSvcVerbose(1, "All services started.\n");
[35027]731 else
[35907]732 {
[58029]733 VGSvcError("An error occcurred while the services!\n");
734 VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
[35907]735 }
[18670]736 return rc;
737}
738
[18712]739
740/**
[18670]741 * Stops and terminates the services.
[18712]742 *
743 * This should be called even when VBoxServiceStartServices fails so it can
744 * clean up anything that we succeeded in starting.
[58029]745 *
746 * @remarks Also called from VBoxService-win.cpp, thus not static.
[18670]747 */
[58029]748int VGSvcStopServices(void)
[18670]749{
[58029]750 VGSvcReportStatus(VBoxGuestFacilityStatus_Terminating);
[35907]751
[29817]752 /*
753 * Signal all the services.
754 */
[18670]755 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[29817]756 ASMAtomicWriteBool(&g_aServices[j].fShutdown, true);
757
758 /*
759 * Do the pfnStop callback on all running services.
760 */
[18670]761 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[51564]762 if (g_aServices[j].fStarted)
[29438]763 {
[58029]764 VGSvcVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
[18670]765 g_aServices[j].pDesc->pfnStop();
[29438]766 }
[29817]767
[58029]768 VGSvcVerbose(3, "All stop functions for services called\n");
[44863]769
[29817]770 /*
771 * Wait for all the service threads to complete.
772 */
[36331]773 int rc = VINF_SUCCESS;
[18670]774 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[29762]775 {
776 if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */
[29438]777 continue;
[51564]778 if (g_aServices[j].Thread != NIL_RTTHREAD)
[29438]779 {
[58029]780 VGSvcVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName);
[36331]781 int rc2 = VINF_SUCCESS;
[29762]782 for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */
[21139]783 {
[36331]784 rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL);
785 if (RT_SUCCESS(rc2))
[29762]786 break;
[25159]787#ifdef RT_OS_WINDOWS
[29762]788 /* Notify SCM that it takes a bit longer ... */
[58029]789 VGSvcWinSetStopPendingStatus(i + j*32);
[25159]790#endif
[21139]791 }
[36331]792 if (RT_FAILURE(rc2))
793 {
[58029]794 VGSvcError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
[36331]795 rc = rc2;
796 }
[18670]797 }
[58029]798 VGSvcVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j);
[51564]799 g_aServices[j].pDesc->pfnTerm();
[29762]800 }
[19031]801
[29438]802#ifdef RT_OS_WINDOWS
[29633]803 /*
[29817]804 * Wake up and tell the main() thread that we're shutting down (it's
805 * sleeping in VBoxServiceMainWait).
[29438]806 */
[36331]807 ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true);
[29817]808 if (g_hEvtWindowsService != NIL_RTSEMEVENT)
[29438]809 {
[58029]810 VGSvcVerbose(3, "Stopping the main thread...\n");
[36331]811 int rc2 = RTSemEventSignal(g_hEvtWindowsService);
812 AssertRC(rc2);
[29438]813 }
814#endif
[29633]815
[58029]816 VGSvcVerbose(2, "Stopping services returning: %Rrc\n", rc);
817 VGSvcReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
[19031]818 return rc;
[18670]819}
820
[29020]821
[29775]822/**
[29817]823 * Block the main thread until the service shuts down.
[58029]824 *
825 * @remarks Also called from VBoxService-win.cpp, thus not static.
[28599]826 */
[58029]827void VGSvcMainWait(void)
[28599]828{
[29817]829 int rc;
830
[58029]831 VGSvcReportStatus(VBoxGuestFacilityStatus_Active);
[30758]832
[29817]833#ifdef RT_OS_WINDOWS
834 /*
835 * Wait for the semaphore to be signalled.
836 */
[58029]837 VGSvcVerbose(1, "Waiting in main thread\n");
[29817]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
[75726]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.
[29817]858 */
[28603]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);
[75726]866 sigaddset(&signalMask, SIGCHLD);
[28603]867 pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
[29633]868
[29817]869 int iSignal;
[29633]870 do
[29647]871 {
[29633]872 iSignal = -1;
[29647]873 rc = sigwait(&signalMask, &iSignal);
874 }
875 while ( rc == EINTR
[29776]876# ifdef ERESTART
[29775]877 || rc == ERESTART
[29776]878# endif
[75726]879 || iSignal == SIGCHLD
[29775]880 );
[29633]881
[58031]882 VGSvcVerbose(3, "VGSvcMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
[29817]883#endif /* !RT_OS_WINDOWS */
[28599]884}
[18712]885
[28599]886
[83974]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
[3655]903int main(int argc, char **argv)
904{
[36331]905 RTEXITCODE rcExit;
906
[3655]907 /*
908 * Init globals and such.
[92662]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.
[3655]912 */
[92662]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);
[36331]924 if (RT_FAILURE(rc))
925 return RTMsgInitFailure(rc);
[92662]926
[33854]927 g_pszProgName = RTPathFilename(argv[0]);
[70171]928#ifdef RT_OS_WINDOWS
929 VGSvcWinResolveApis();
930#endif
[38113]931#ifdef DEBUG
932 rc = RTCritSectInit(&g_csLog);
933 AssertRC(rc);
934#endif
[33854]935
[60583]936#ifdef VBOX_WITH_VBOXSERVICE_TOOLBOX
[36331]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 */
[58029]943 if (VGSvcToolboxMain(argc, argv, &rcExit))
[36331]944 return rcExit;
[33854]945#endif
946
[50051]947 bool fUserSession = false;
[60583]948#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
[25390]949 /*
[49531]950 * Check if we're the specially spawned VBoxService.exe process that
951 * handles a guest control session.
952 */
953 if ( argc >= 2
[83409]954 && !RTStrICmp(argv[1], VBOXSERVICECTRLSESSION_GETOPT_PREFIX))
[49531]955 fUserSession = true;
956#endif
957
958 /*
[83974]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.
[25390]965 */
[83974]966 int const rcVbgl = fUserSession ? VbglR3InitUser() : VbglR3Init();
[25390]967
[30560]968#ifdef RT_OS_WINDOWS
[30957]969 /*
970 * Check if we're the specially spawned VBoxService.exe process that
[58029]971 * handles page fusion. This saves an extra statically linked executable.
[30957]972 */
[49531]973 if ( argc == 2
974 && !RTStrICmp(argv[1], "pagefusion"))
[83974]975 {
976 if (RT_SUCCESS(rcVbgl))
977 return VGSvcPageSharingWorkerChild();
978 return vbglInitFailure(rcVbgl);
979 }
[30560]980#endif
981
[60583]982#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
[44863]983 /*
984 * Check if we're the specially spawned VBoxService.exe process that
985 * handles a guest control session.
986 */
[49531]987 if (fUserSession)
[83974]988 {
989 if (RT_SUCCESS(rcVbgl))
990 return VGSvcGstCtrlSessionSpawnInit(argc, argv);
991 return vbglInitFailure(rcVbgl);
992 }
[44872]993#endif
[40129]994
[33154]995 /*
[36331]996 * Parse the arguments.
997 *
[36328]998 * Note! This code predates RTGetOpt, thus the manual parsing.
[3655]999 */
1000 bool fDaemonize = true;
[23029]1001 bool fDaemonized = false;
[3655]1002 for (int i = 1; i < argc; i++)
1003 {
1004 const char *psz = argv[i];
[19532]1005 if (*psz != '-')
[40129]1006 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'\n", psz);
[19532]1007 psz++;
[3655]1008
[19532]1009 /* translate long argument to short */
1010 if (*psz == '-')
[3655]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";
[36183]1020 else if (MATCHES("version"))
1021 psz = "V";
[3655]1022 else if (MATCHES("help"))
1023 psz = "h";
1024 else if (MATCHES("interval"))
1025 psz = "i";
[18712]1026#ifdef RT_OS_WINDOWS
[18639]1027 else if (MATCHES("register"))
1028 psz = "r";
1029 else if (MATCHES("unregister"))
1030 psz = "u";
1031#endif
[40129]1032 else if (MATCHES("logfile"))
1033 psz = "l";
[57721]1034 else if (MATCHES("pidfile"))
1035 psz = "p";
[3655]1036 else if (MATCHES("daemonized"))
1037 {
[23029]1038 fDaemonized = true;
[3655]1039 continue;
1040 }
1041 else
1042 {
1043 bool fFound = false;
1044
[46326]1045 if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-")))
[3655]1046 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
[6137]1047 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
[3655]1048 g_aServices[j].fEnabled = true;
1049
[46326]1050 if (cch > sizeof("disable-") && !memcmp(psz, RT_STR_TUPLE("disable-")))
[3655]1051 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
[6137]1052 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
[3655]1053 g_aServices[j].fEnabled = false;
1054
[46326]1055 if (cch > sizeof("only-") && !memcmp(psz, RT_STR_TUPLE("only-")))
[38223]1056 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[40129]1057 {
[38223]1058 g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName);
[40129]1059 if (g_aServices[j].fEnabled)
1060 fFound = true;
1061 }
[38223]1062
[3655]1063 if (!fFound)
[36331]1064 {
[58029]1065 rcExit = vgsvcLazyPreInit();
[36331]1066 if (rcExit != RTEXITCODE_SUCCESS)
1067 return rcExit;
[3655]1068 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
1069 {
1070 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
[40129]1071 fFound = rc == VINF_SUCCESS;
[19526]1072 if (fFound)
1073 break;
1074 if (rc != -1)
[3655]1075 return rc;
1076 }
[36331]1077 }
[19532]1078 if (!fFound)
[40129]1079 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%s'\n", argv[i]);
[3655]1080 continue;
1081 }
1082#undef MATCHES
1083 }
[19532]1084
1085 /* handle the string of short options. */
1086 do
[3655]1087 {
[19532]1088 switch (*psz)
[3655]1089 {
[19532]1090 case 'i':
[83974]1091 rc = VGSvcArgUInt32(argc, argv, psz + 1, &i, &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
[19532]1092 if (rc)
1093 return rc;
1094 psz = NULL;
1095 break;
[3655]1096
[19532]1097 case 'f':
1098 fDaemonize = false;
1099 break;
[3655]1100
[19532]1101 case 'v':
1102 g_cVerbosity++;
1103 break;
[3655]1104
[36183]1105 case 'V':
1106 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1107 return RTEXITCODE_SUCCESS;
1108
[19532]1109 case 'h':
1110 case '?':
[58029]1111 return vgsvcUsage();
[18712]1112
[19532]1113#ifdef RT_OS_WINDOWS
1114 case 'r':
[58029]1115 return VGSvcWinInstall();
[18639]1116
[19532]1117 case 'u':
[58029]1118 return VGSvcWinUninstall();
[19532]1119#endif
[18639]1120
[40129]1121 case 'l':
1122 {
[83974]1123 rc = vgsvcArgString(argc, argv, psz + 1, &i, g_szLogFile, sizeof(g_szLogFile));
[40129]1124 if (rc)
1125 return rc;
1126 psz = NULL;
1127 break;
1128 }
1129
[57721]1130 case 'p':
1131 {
[83974]1132 rc = vgsvcArgString(argc, argv, psz + 1, &i, g_szPidFile, sizeof(g_szPidFile));
[57721]1133 if (rc)
1134 return rc;
1135 psz = NULL;
1136 break;
1137 }
1138
[19532]1139 default:
1140 {
[58029]1141 rcExit = vgsvcLazyPreInit();
[36331]1142 if (rcExit != RTEXITCODE_SUCCESS)
1143 return rcExit;
1144
[19532]1145 bool fFound = false;
1146 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
[3655]1147 {
[36331]1148 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
[36183]1149 fFound = rc == VINF_SUCCESS;
[19532]1150 if (fFound)
1151 break;
1152 if (rc != -1)
1153 return rc;
[3655]1154 }
[19532]1155 if (!fFound)
[40129]1156 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%c' (%s)\n", *psz, argv[i]);
[19532]1157 break;
[3655]1158 }
[19532]1159 }
1160 } while (psz && *++psz);
[3655]1161 }
[36182]1162
[83974]1163 /* Now we can report the VBGL failure. */
1164 if (RT_FAILURE(rcVbgl))
1165 return vbglInitFailure(rcVbgl);
1166
[36328]1167 /* Check that at least one service is enabled. */
[58029]1168 if (vgsvcCountEnabledServices() == 0)
[40129]1169 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n");
[36328]1170
[58029]1171 rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
[40129]1172 if (RT_FAILURE(rc))
[58029]1173 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log '%s', rc=%Rrc\n",
1174 g_szLogFile[0] ? g_szLogFile : "<None>", rc);
[40129]1175
[36331]1176 /* Call pre-init if we didn't do it already. */
[58029]1177 rcExit = vgsvcLazyPreInit();
[36331]1178 if (rcExit != RTEXITCODE_SUCCESS)
1179 return rcExit;
1180
[86823]1181#ifdef VBOX_WITH_VBOXSERVICE_DRMRESIZE
[93372]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 */
[86823]1187#endif /* VBOX_WITH_VBOXSERVICE_DRMRESIZE */
[85962]1188
[36182]1189#ifdef RT_OS_WINDOWS
[3655]1190 /*
[36182]1191 * Make sure only one instance of VBoxService runs at a time. Create a
1192 * global mutex for that.
[36328]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.
[36182]1198 */
1199 OSVERSIONINFOEX OSInfoEx;
1200 RT_ZERO(OSInfoEx);
1201 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1202
[96502]1203
[36328]1204 SetLastError(NO_ERROR);
[36182]1205 HANDLE hMutexAppRunning;
[96502]1206 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(5,0,0)) /* Windows 2000 */
[96503]1207 hMutexAppRunning = CreateMutexW(NULL, FALSE, L"Global\\" RT_CONCAT(L,VBOXSERVICE_NAME));
[36182]1208 else
[96502]1209 hMutexAppRunning = CreateMutexW(NULL, FALSE, RT_CONCAT(L,VBOXSERVICE_NAME));
[36328]1210 if (hMutexAppRunning == NULL)
[36182]1211 {
[40129]1212 DWORD dwErr = GetLastError();
1213 if ( dwErr == ERROR_ALREADY_EXISTS
1214 || dwErr == ERROR_ACCESS_DENIED)
1215 {
[58029]1216 VGSvcError("%s is already running! Terminating.\n", g_pszProgName);
[40129]1217 return RTEXITCODE_FAILURE;
1218 }
1219
[58029]1220 VGSvcError("CreateMutex failed with last error %u! Terminating.\n", GetLastError());
[36328]1221 return RTEXITCODE_FAILURE;
[36182]1222 }
[40129]1223
[36328]1224#else /* !RT_OS_WINDOWS */
[92695]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. */
[36328]1227#endif /* !RT_OS_WINDOWS */
[36182]1228
[58029]1229 VGSvcVerbose(0, "%s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);
[22530]1230
[3655]1231 /*
1232 * Daemonize if requested.
1233 */
[23029]1234 if (fDaemonize && !fDaemonized)
[3655]1235 {
[18712]1236#ifdef RT_OS_WINDOWS
[58029]1237 VGSvcVerbose(2, "Starting service dispatcher ...\n");
1238 rcExit = VGSvcWinEnterCtrlDispatcher();
[18712]1239#else
[58029]1240 VGSvcVerbose(1, "Daemonizing...\n");
[53421]1241 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */,
[54010]1242 false /* fRespawn */, NULL /* pcRespawn */);
[6445]1243 if (RT_FAILURE(rc))
[58029]1244 return VGSvcError("Daemon failed: %Rrc\n", rc);
[3655]1245 /* in-child */
[18642]1246#endif
[3655]1247 }
[19140]1248#ifdef RT_OS_WINDOWS
1249 else
[29020]1250#endif
[19140]1251 {
1252 /*
[29020]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.
[28603]1256 *
[29020]1257 * POSIX: This is used for both daemons and console runs. Start all services
1258 * and return immediately.
[19140]1259 */
[39202]1260#ifdef RT_OS_WINDOWS
1261 /* Install console control handler. */
[85121]1262 if (!SetConsoleCtrlHandler(vgsvcWinConsoleControlHandler, TRUE /* Add handler */))
[39202]1263 {
[58029]1264 VGSvcError("Unable to add console control handler, error=%ld\n", GetLastError());
[39202]1265 /* Just skip this error, not critical. */
1266 }
1267#endif /* RT_OS_WINDOWS */
[58029]1268 rc = VGSvcStartServices();
[57729]1269 RTFILE hPidFile = NIL_RTFILE;
[57721]1270 if (RT_SUCCESS(rc))
[57729]1271 if (g_szPidFile[0])
1272 rc = VbglR3PidFile(g_szPidFile, &hPidFile);
[29817]1273 rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[28604]1274 if (RT_SUCCESS(rc))
[58029]1275 VGSvcMainWait();
[57729]1276 if (g_szPidFile[0] && hPidFile != NIL_RTFILE)
1277 VbglR3ClosePidFile(g_szPidFile, hPidFile);
[39202]1278#ifdef RT_OS_WINDOWS
1279 /* Uninstall console control handler. */
1280 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
1281 {
[58029]1282 VGSvcError("Unable to remove console control handler, error=%ld\n", GetLastError());
[39202]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. */
[58029]1289 VGSvcStopServices();
[39202]1290#endif /* RT_OS_WINDOWS */
[19140]1291 }
[58029]1292 VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
[29020]1293
[18712]1294#ifdef RT_OS_WINDOWS
1295 /*
[36328]1296 * Cleanup mutex.
[18712]1297 */
[36328]1298 CloseHandle(hMutexAppRunning);
[18670]1299#endif
[18712]1300
[58029]1301 VGSvcVerbose(0, "Ended.\n");
[38113]1302
1303#ifdef DEBUG
1304 RTCritSectDelete(&g_csLog);
[39843]1305 //RTMemTrackerDumpAllToStdOut();
[38113]1306#endif
[40129]1307
[58029]1308 VGSvcLogDestroy();
[40129]1309
[29817]1310 return rcExit;
[3655]1311}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use