VirtualBox

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

Last change on this file since 61522 was 60622, checked in by vboxsync, 9 years ago

Guest Control: Added proper handling for (VBoxService) toolbox exit codes, resolving various copyto/copyfrom bugs.

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