VirtualBox

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

Last change on this file since 59131 was 58033, checked in by vboxsync, 9 years ago

VBoxService: Started adding pages with some details for each component.

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