/** * Try adjust the time using adjtime or similar. * * @returns true on success, false on failure. * * @param pDrift The time adjustment. */ static void VBoxServiceTimeSyncSet(PCRTTIMESPEC pDrift) { /* * Query the current time, adjust it by adding the drift and set it. */ RTTIMESPEC NewGuestTime; int rc = RTTimeSet(RTTimeSpecAdd(RTTimeNow(&NewGuestTime), pDrift)); if (RT_SUCCESS(rc)) { /* Succeeded - reset the error count and log the change. */ g_cTimeSyncErrors = 0; if (g_cVerbosity >= 1) { char sz[64]; RTTIME Time; VBoxServiceVerbose(1, "time set to %s\n", RTTimeToString(RTTimeExplode(&Time, &NewGuestTime), sz, sizeof(sz))); #ifdef DEBUG RTTIMESPEC Tmp; if (g_cVerbosity >= 3) VBoxServiceVerbose(3, " now %s\n", RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz))); #endif } } else if (g_cTimeSyncErrors++ < 10) VBoxServiceError("VBoxServiceTimeSyncSet: RTTimeSet(%RDtimespec) failed: %Rrc\n", &NewGuestTime, rc); }
static void WINAPI vboxServiceWinMain(DWORD argc, LPTSTR *argv) { VBoxServiceVerbose(2, "Registering service control handler ...\n"); #ifdef TARGET_NT4 g_hWinServiceStatus = RegisterServiceCtrlHandler(VBOXSERVICE_NAME, vboxServiceWinCtrlHandler); #else g_hWinServiceStatus = RegisterServiceCtrlHandlerEx(VBOXSERVICE_NAME, vboxServiceWinCtrlHandler, NULL); #endif if (g_hWinServiceStatus != NULL) { VBoxServiceVerbose(2, "Service control handler registered.\n"); vboxServiceWinStart(); } else { DWORD dwErr = GetLastError(); switch (dwErr) { case ERROR_INVALID_NAME: VBoxServiceError("Invalid service name!\n"); break; case ERROR_SERVICE_DOES_NOT_EXIST: VBoxServiceError("Service does not exist!\n"); break; default: VBoxServiceError("Could not register service control handle! Error: %ld\n", dwErr); break; } } }
/** @copydoc VBOXSERVICE::pfnInit */ static DECLCALLBACK(int) VBoxServiceAutoMountInit(void) { VBoxServiceVerbose(3, "VBoxServiceAutoMountInit\n"); int rc = RTSemEventMultiCreate(&g_AutoMountEvent); AssertRCReturn(rc, rc); rc = VbglR3SharedFolderConnect(&g_SharedFoldersSvcClientID); if (RT_SUCCESS(rc)) { VBoxServiceVerbose(3, "VBoxServiceAutoMountInit: Service Client ID: %#x\n", g_SharedFoldersSvcClientID); } else { /* If the service was not found, we disable this service without causing VBoxService to fail. */ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */ { VBoxServiceVerbose(0, "VBoxServiceAutoMountInit: Shared Folders service is not available\n"); rc = VERR_SERVICE_DISABLED; } else VBoxServiceError("Control: Failed to connect to the Shared Folders service! Error: %Rrc\n", rc); RTSemEventMultiDestroy(g_AutoMountEvent); g_AutoMountEvent = NIL_RTSEMEVENTMULTI; } return rc; }
/** * Wrapper around VbglR3GuestPropWriteValue that does value formatting and * logging. * * @returns VBox status code. Errors will be logged. * * @param u32ClientId The HGCM client ID for the guest property session. * @param pszName The property name. * @param pszValueFormat The property format string. If this is NULL then * the property will be deleted (if possible). * @param ... Format arguments. */ int VBoxServiceWritePropF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...) { AssertPtr(pszName); int rc; if (pszValueFormat != NULL) { va_list va; va_start(va, pszValueFormat); VBoxServiceVerbose(3, "Writing guest property \"%s\" = \"%N\"\n", pszName, pszValueFormat, &va); va_end(va); va_start(va, pszValueFormat); rc = VbglR3GuestPropWriteValueV(u32ClientId, pszName, pszValueFormat, va); va_end(va); if (RT_FAILURE(rc)) VBoxServiceError("Error writing guest property \"%s\" (rc=%Rrc)\n", pszName, rc); } else { VBoxServiceVerbose(3, "Deleting guest property \"%s\"\n", pszName); rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL); if (RT_FAILURE(rc)) VBoxServiceError("Error deleting guest property \"%s\" (rc=%Rrc)\n", pszName, rc); } return rc; }
/** @copydoc VBOXSERVICE::pfnInit */ static DECLCALLBACK(int) VBoxServiceVMInfoInit(void) { /* * If not specified, find the right interval default. * Then create the event sem to block on. */ if (!g_cMsVMInfoInterval) g_cMsVMInfoInterval = g_DefaultInterval * 1000; if (!g_cMsVMInfoInterval) g_cMsVMInfoInterval = 10 * 1000; int rc = RTSemEventMultiCreate(&g_hVMInfoEvent); AssertRCReturn(rc, rc); VbglR3GetSessionId(&g_idVMInfoSession); /* The status code is ignored as this information is not available with VBox < 3.2.10. */ rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VMInfo: Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID); else { /* If the service was not found, we disable this service without causing VBoxService to fail. */ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */ { VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n"); rc = VERR_SERVICE_DISABLED; } else VBoxServiceError("VMInfo: Failed to connect to the guest property service! Error: %Rrc\n", rc); RTSemEventMultiDestroy(g_hVMInfoEvent); g_hVMInfoEvent = NIL_RTSEMEVENTMULTI; } if (RT_SUCCESS(rc)) { VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID); /* * Declare some guest properties with flags and reset values. */ VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */); VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0"); VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers", VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true"); VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */); } return rc; }
/** * Block the main thread until the service shuts down. */ void VBoxServiceMainWait(void) { int rc; VBoxServiceReportStatus(VBoxGuestFacilityStatus_Active); #ifdef RT_OS_WINDOWS /* * Wait for the semaphore to be signalled. */ VBoxServiceVerbose(1, "Waiting in main thread\n"); rc = RTSemEventCreate(&g_hEvtWindowsService); AssertRC(rc); while (!ASMAtomicReadBool(&g_fWindowsServiceShutdown)) { rc = RTSemEventWait(g_hEvtWindowsService, RT_INDEFINITE_WAIT); AssertRC(rc); } RTSemEventDestroy(g_hEvtWindowsService); g_hEvtWindowsService = NIL_RTSEMEVENT; #else /* * Wait explicitly for a HUP, INT, QUIT, ABRT or TERM signal, blocking * all important signals. * * The annoying EINTR/ERESTART loop is for the benefit of Solaris where * sigwait returns when we receive a SIGCHLD. Kind of makes sense since */ sigset_t signalMask; sigemptyset(&signalMask); sigaddset(&signalMask, SIGHUP); sigaddset(&signalMask, SIGINT); sigaddset(&signalMask, SIGQUIT); sigaddset(&signalMask, SIGABRT); sigaddset(&signalMask, SIGTERM); pthread_sigmask(SIG_BLOCK, &signalMask, NULL); int iSignal; do { iSignal = -1; rc = sigwait(&signalMask, &iSignal); } while ( rc == EINTR # ifdef ERESTART || rc == ERESTART # endif ); VBoxServiceVerbose(3, "VBoxServiceMainWait: Received signal %d (rc=%d)\n", iSignal, rc); #endif /* !RT_OS_WINDOWS */ }
/** * Handles VMMDevCpuEventType_Unplug. * * @param idCpuCore The CPU core ID. * @param idCpuPackage The CPU package ID. */ static void VBoxServiceCpuHotPlugHandleUnplugEvent(uint32_t idCpuCore, uint32_t idCpuPackage) { #ifdef RT_OS_LINUX char *pszCpuDevicePath = NULL; int rc = VBoxServiceCpuHotPlugGetACPIDevicePath(&pszCpuDevicePath, idCpuCore, idCpuPackage); if (RT_SUCCESS(rc)) { RTFILE hFileCpuEject; rc = RTFileOpenF(&hFileCpuEject, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, "%s/eject", pszCpuDevicePath); if (RT_SUCCESS(rc)) { /* Write a 1 to eject the CPU */ rc = RTFileWrite(hFileCpuEject, "1", 1, NULL); if (RT_SUCCESS(rc)) VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was ejected\n", idCpuPackage, idCpuCore); else VBoxServiceError("CpuHotPlug: Failed to eject CPU %u/%u rc=%Rrc\n", idCpuPackage, idCpuCore, rc); RTFileClose(hFileCpuEject); } else VBoxServiceError("CpuHotPlug: Failed to open \"%s/eject\" rc=%Rrc\n", pszCpuDevicePath, rc); RTStrFree(pszCpuDevicePath); } else VBoxServiceError("CpuHotPlug: Failed to get CPU device path rc=%Rrc\n", rc); #else # error "Port me" #endif }
static BOOL WINAPI VBoxServiceConsoleControlHandler(DWORD dwCtrlType) { int rc = VINF_SUCCESS; bool fEventHandled = FALSE; switch (dwCtrlType) { /* User pressed CTRL+C or CTRL+BREAK or an external event was sent * via GenerateConsoleCtrlEvent(). */ case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: case CTRL_C_EVENT: VBoxServiceVerbose(2, "ControlHandler: Received break/close event\n"); rc = VBoxServiceStopServices(); fEventHandled = TRUE; break; default: break; /** @todo Add other events here. */ } if (RT_FAILURE(rc)) VBoxServiceError("ControlHandler: Event %ld handled with error rc=%Rrc\n", dwCtrlType, rc); return fEventHandled; }
/** * Uninstalls the service. */ RTEXITCODE VBoxServiceWinUninstall(void) { VBoxServiceVerbose(1, "Uninstalling service ...\n"); SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (hSCManager == NULL) { VBoxServiceError("Could not open SCM! Error: %d\n", GetLastError()); return RTEXITCODE_FAILURE; } RTEXITCODE rcExit; SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS ); if (hService != NULL) { if (DeleteService(hService)) { /* * ??? */ HKEY hKey = NULL; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) { RegDeleteKey(hKey, VBOXSERVICE_NAME); RegCloseKey(hKey); } VBoxServiceVerbose(0, "Service successfully uninstalled!\n"); rcExit = RTEXITCODE_SUCCESS; } else rcExit = VBoxServiceError("Could not remove service! Error: %d\n", GetLastError()); CloseServiceHandle(hService); } else rcExit = VBoxServiceError("Could not open service! Error: %d\n", GetLastError()); CloseServiceHandle(hSCManager); return rcExit; }
/** @todo Integrate into RTFsQueryMountpoint(). */ static bool VBoxServiceAutoMountShareIsMounted(const char *pszShare, char *pszMountPoint, size_t cbMountPoint) { AssertPtrReturn(pszShare, VERR_INVALID_PARAMETER); AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER); AssertReturn(cbMountPoint, VERR_INVALID_PARAMETER); bool fMounted = false; /* @todo What to do if we have a relative path in mtab instead * of an absolute one ("temp" vs. "/media/temp")? * procfs contains the full path but not the actual share name ... * FILE *pFh = setmntent("/proc/mounts", "r+t"); */ #ifdef RT_OS_SOLARIS FILE *pFh = fopen(_PATH_MOUNTED, "r"); if (!pFh) VBoxServiceError("VBoxServiceAutoMountShareIsMounted: Could not open mount tab \"%s\"!\n", _PATH_MOUNTED); else { mnttab mntTab; while ((getmntent(pFh, &mntTab))) { if (!RTStrICmp(mntTab.mnt_special, pszShare)) { fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp) ? true : false; break; } } fclose(pFh); } #else FILE *pFh = setmntent(_PATH_MOUNTED, "r+t"); if (pFh == NULL) VBoxServiceError("VBoxServiceAutoMountShareIsMounted: Could not open mount tab \"%s\"!\n", _PATH_MOUNTED); else { mntent *pMntEnt; while ((pMntEnt = getmntent(pFh))) { if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare)) { fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir) ? true : false; break; } } endmntent(pFh); } #endif VBoxServiceVerbose(4, "VBoxServiceAutoMountShareIsMounted: Share \"%s\" at mount point \"%s\" = %s\n", pszShare, fMounted ? pszMountPoint : "<None>", fMounted ? "Yes" : "No"); return fMounted; }
/** @copydoc VBOXSERVICE::pfnInit */ static DECLCALLBACK(int) VBoxServiceVMStatsInit(void) { VBoxServiceVerbose(3, "VBoxServiceVMStatsInit\n"); int rc = RTSemEventMultiCreate(&g_VMStatEvent); AssertRCReturn(rc, rc); gCtx.cMsStatInterval = 0; /* default; update disabled */ RT_ZERO(gCtx.au64LastCpuLoad_Idle); RT_ZERO(gCtx.au64LastCpuLoad_Kernel); RT_ZERO(gCtx.au64LastCpuLoad_User); RT_ZERO(gCtx.au64LastCpuLoad_Nice); rc = VbglR3StatQueryInterval(&gCtx.cMsStatInterval); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsInit: New statistics interval %u seconds\n", gCtx.cMsStatInterval); else VBoxServiceVerbose(3, "VBoxStatsInit: DeviceIoControl failed with %d\n", rc); #ifdef RT_OS_WINDOWS /* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation. */ *(void **)&gCtx.pfnNtQuerySystemInformation = RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation"); if (gCtx.pfnNtQuerySystemInformation) VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation); else { VBoxServiceVerbose(3, "VBoxStatsInit: ntdll.NtQuerySystemInformation not found!\n"); return VERR_SERVICE_DISABLED; } /* GlobalMemoryStatus is win2k and up, so load it dynamically */ *(void **)&gCtx.pfnGlobalMemoryStatusEx = RTLdrGetSystemSymbol("kernel32.dll", "GlobalMemoryStatusEx"); if (gCtx.pfnGlobalMemoryStatusEx) VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx); else { /** @todo Now fails in NT4; do we care? */ VBoxServiceVerbose(3, "VBoxStatsInit: kernel32.GlobalMemoryStatusEx not found!\n"); return VERR_SERVICE_DISABLED; } /* GetPerformanceInfo is xp and up, so load it dynamically */ *(void **)&gCtx.pfnGetPerformanceInfo = RTLdrGetSystemSymbol("psapi.dll", "GetPerformanceInfo"); if (gCtx.pfnGetPerformanceInfo) VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo); #endif /* RT_OS_WINDOWS */ return VINF_SUCCESS; }
/** * Cancels any pending time adjustment. * * Called when we've caught up and before calls to VBoxServiceTimeSyncSet. */ static void VBoxServiceTimeSyncCancelAdjust(void) { #ifdef RT_OS_WINDOWS /** @todo r=bird: g_hTokenProcess cannot be NULL here. See argumentation in * VBoxServiceTimeSyncAdjust. */ if (g_hTokenProcess == NULL) /* No process token (anymore)? */ return; if (SetSystemTimeAdjustment(0, TRUE /* Periodic adjustments disabled. */)) VBoxServiceVerbose(3, "VBoxServiceTimeSyncCancelAdjust: Windows Time Adjustment is now disabled.\n"); else if (g_cTimeSyncErrors++ < 10) VBoxServiceError("VBoxServiceTimeSyncCancelAdjust: SetSystemTimeAdjustment(,disable) failed, error=%u\n", GetLastError()); #endif /* !RT_OS_WINDOWS */ }
/** @copydoc VBOXSERVICE::pfnTerm */ static DECLCALLBACK(void) VBoxServiceAutoMountTerm(void) { VBoxServiceVerbose(3, "VBoxServiceAutoMountTerm\n"); VbglR3SharedFolderDisconnect(g_SharedFoldersSvcClientID); g_SharedFoldersSvcClientID = 0; if (g_AutoMountEvent != NIL_RTSEMEVENTMULTI) { RTSemEventMultiDestroy(g_AutoMountEvent); g_AutoMountEvent = NIL_RTSEMEVENTMULTI; } return; }
/** * Save and noisy string copy. * * @param pwszDst Destination buffer. * @param cbDst Size in bytes - not WCHAR count! * @param pSrc Source string. * @param pszWhat What this is. For the log. */ static void VBoxServiceVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat) { Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst); size_t cbCopy = pSrc->Length; if (cbCopy + sizeof(WCHAR) > cbDst) { VBoxServiceVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n", pszWhat, cbCopy, cbDst); cbCopy = cbDst - sizeof(WCHAR); } if (cbCopy) memcpy(pwszDst, pSrc->Buffer, cbCopy); pwszDst[cbCopy / sizeof(WCHAR)] = '\0'; }
/** Reports our current status to the SCM. */ static BOOL vboxServiceWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint) { if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */ return FALSE; VBoxServiceVerbose(2, "Setting service status to: %ld\n", dwStatus); g_dwWinServiceLastStatus = dwStatus; SERVICE_STATUS ss; RT_ZERO(ss); ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = dwStatus; /* Don't accept controls when in start pending state. */ if (ss.dwCurrentState != SERVICE_START_PENDING) { ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; #ifndef TARGET_NT4 /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000. * This makes SCM angry. */ char szOSVersion[32]; int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion)); if (RT_SUCCESS(rc)) { if (RTStrVersionCompare(szOSVersion, "5.1") >= 0) ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE; } else VBoxServiceError("Error determining OS version, rc=%Rrc\n", rc); #endif } ss.dwWin32ExitCode = NO_ERROR; ss.dwServiceSpecificExitCode = 0; /* Not used */ ss.dwCheckPoint = dwCheckPoint; ss.dwWaitHint = 3000; BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss); if (!fStatusSet) VBoxServiceError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n", dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError()); return fStatusSet; }
/** * Reports the current VBoxService status to the host. * * This makes sure that the Failed state is sticky. * * @return IPRT status code. * @param enmStatus Status to report to the host. */ int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus) { /* * VBoxGuestFacilityStatus_Failed is sticky. */ static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive; VBoxServiceVerbose(4, "Setting VBoxService status to %u\n", enmStatus); if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed) { int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_VBoxService, enmStatus, 0 /* Flags */); if (RT_FAILURE(rc)) { VBoxServiceError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc); return rc; } s_enmLastStatus = enmStatus; } return VINF_SUCCESS; }
/** Reports our current status to the SCM. */ static BOOL vboxServiceWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint) { if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */ return FALSE; VBoxServiceVerbose(2, "Setting service status to: %ld\n", dwStatus); g_dwWinServiceLastStatus = dwStatus; SERVICE_STATUS ss; ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = dwStatus; ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; #ifndef TARGET_NT4 ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE; #endif ss.dwWin32ExitCode = NO_ERROR; ss.dwServiceSpecificExitCode = 0; /* Not used */ ss.dwCheckPoint = dwCheckPoint; ss.dwWaitHint = 3000; return SetServiceStatus(g_hWinServiceStatus, &ss); }
static int VBoxServiceAutoMountPrepareMountPoint(const char *pszMountPoint, const char *pszShareName, vbsf_mount_opts *pOpts) { AssertPtrReturn(pOpts, VERR_INVALID_PARAMETER); AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER); AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER); RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */ int rc = RTDirCreateFullPath(pszMountPoint, fMode); if (RT_SUCCESS(rc)) { rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, pOpts->gid, RTPATH_F_ON_LINK); if (RT_SUCCESS(rc)) { rc = RTPathSetMode(pszMountPoint, fMode); if (RT_FAILURE(rc)) { if (rc == VERR_WRITE_PROTECT) { VBoxServiceVerbose(3, "VBoxServiceAutoMountPrepareMountPoint: Mount directory \"%s\" already is used/mounted\n", pszMountPoint); rc = VINF_SUCCESS; } else VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not set mode %RTfmode for mount directory \"%s\", rc = %Rrc\n", fMode, pszMountPoint, rc); } } else VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not set permissions for mount directory \"%s\", rc = %Rrc\n", pszMountPoint, rc); } else VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not create mount directory \"%s\" with mode %RTfmode, rc = %Rrc\n", pszMountPoint, fMode, rc); return rc; }
/** * Reads a guest property. * * @returns VBox status code, fully bitched. * * @param u32ClientId The HGCM client ID for the guest property session. * @param pszPropName The property name. * @param ppszValue Where to return the value. This is always set * to NULL. Free it using RTStrFree(). * @param ppszFlags Where to return the value flags. Free it * using RTStrFree(). Optional. * @param puTimestamp Where to return the timestamp. This is only set * on success. Optional. */ int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) { AssertPtrReturn(pszPropName, VERR_INVALID_POINTER); AssertPtrReturn(ppszValue, VERR_INVALID_POINTER); uint32_t cbBuf = _1K; void *pvBuf = NULL; int rc; *ppszValue = NULL; for (unsigned cTries = 0; cTries < 10; cTries++) { /* * (Re-)Allocate the buffer and try read the property. */ RTMemFree(pvBuf); pvBuf = RTMemAlloc(cbBuf); if (!pvBuf) { VBoxServiceError("Guest Property: Failed to allocate %zu bytes\n", cbBuf); rc = VERR_NO_MEMORY; break; } char *pszValue; char *pszFlags; uint64_t uTimestamp; rc = VbglR3GuestPropRead(u32ClientId, pszPropName, pvBuf, cbBuf, &pszValue, &uTimestamp, &pszFlags, NULL); if (RT_FAILURE(rc)) { if (rc == VERR_BUFFER_OVERFLOW) { /* try again with a bigger buffer. */ cbBuf *= 2; continue; } if (rc == VERR_NOT_FOUND) VBoxServiceVerbose(2, "Guest Property: %s not found\n", pszPropName); else VBoxServiceError("Guest Property: Failed to query \"%s\": %Rrc\n", pszPropName, rc); break; } VBoxServiceVerbose(2, "Guest Property: Read \"%s\" = \"%s\", timestamp %RU64n\n", pszPropName, pszValue, uTimestamp); *ppszValue = RTStrDup(pszValue); if (!*ppszValue) { VBoxServiceError("Guest Property: RTStrDup failed for \"%s\"\n", pszValue); rc = VERR_NO_MEMORY; break; } if (puTimestamp) *puTimestamp = uTimestamp; if (ppszFlags) *ppszFlags = RTStrDup(pszFlags); break; /* done */ } if (pvBuf) RTMemFree(pvBuf); return rc; }
/** * Worker for VBoxServiceGetFileVersionString. * * @returns VBox status code. * @param pszFilename ASCII & ANSI & UTF-8 compliant name. */ static int VBoxServiceGetFileVersion(const char *pszFilename, PDWORD pdwMajor, PDWORD pdwMinor, PDWORD pdwBuildNumber, PDWORD pdwRevisionNumber) { int rc; *pdwMajor = *pdwMinor = *pdwBuildNumber = *pdwRevisionNumber = 0; /* * Get the file version info. */ DWORD dwHandleIgnored; DWORD cbVerData = GetFileVersionInfoSizeA(pszFilename, &dwHandleIgnored); if (cbVerData) { LPTSTR pVerData = (LPTSTR)RTMemTmpAllocZ(cbVerData); if (pVerData) { if (GetFileVersionInfoA(pszFilename, dwHandleIgnored, cbVerData, pVerData)) { /* * Try query and parse the FileVersion string our selves first * since this will give us the correct revision number when * it goes beyond the range of an uint16_t / WORD. */ if (VBoxServiceGetFileVersionOwn(pVerData, pdwMajor, pdwMinor, pdwBuildNumber, pdwRevisionNumber)) rc = VINF_SUCCESS; else { /* Fall back on VS_FIXEDFILEINFO */ UINT cbFileInfoIgnored = 0; VS_FIXEDFILEINFO *pFileInfo = NULL; if (VerQueryValue(pVerData, "\\", (LPVOID *)&pFileInfo, &cbFileInfoIgnored)) { *pdwMajor = HIWORD(pFileInfo->dwFileVersionMS); *pdwMinor = LOWORD(pFileInfo->dwFileVersionMS); *pdwBuildNumber = HIWORD(pFileInfo->dwFileVersionLS); *pdwRevisionNumber = LOWORD(pFileInfo->dwFileVersionLS); rc = VINF_SUCCESS; } else { rc = RTErrConvertFromWin32(GetLastError()); VBoxServiceVerbose(3, "No file version value for file \"%s\" available! (%d / rc=%Rrc)\n", pszFilename, GetLastError(), rc); } } } else { rc = RTErrConvertFromWin32(GetLastError()); VBoxServiceVerbose(0, "GetFileVersionInfo(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc); } RTMemTmpFree(pVerData); } else { VBoxServiceVerbose(0, "Failed to allocate %u byte for file version info for '%s'\n", cbVerData, pszFilename); rc = VERR_NO_TMP_MEMORY; } } else { rc = RTErrConvertFromWin32(GetLastError()); VBoxServiceVerbose(3, "GetFileVersionInfoSize(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc); } return rc; }
/** @copydoc VBOXSERVICE::pfnWorker */ DECLCALLBACK(int) VBoxServiceCpuHotPlugWorker(bool volatile *pfShutdown) { /* * Tell the control thread that it can continue spawning services. */ RTThreadUserSignal(RTThreadSelf()); /* * Enable the CPU hotplug notifier. */ int rc = VbglR3CpuHotPlugInit(); if (RT_FAILURE(rc)) return rc; /* * The Work Loop. */ for (;;) { /* Wait for CPU hot plugging event. */ uint32_t idCpuCore; uint32_t idCpuPackage; VMMDevCpuEventType enmEventType; rc = VbglR3CpuHotPlugWaitForEvent(&enmEventType, &idCpuCore, &idCpuPackage); if (RT_SUCCESS(rc)) { VBoxServiceVerbose(3, "CpuHotPlug: Event happened idCpuCore=%u idCpuPackage=%u enmEventType=%d\n", idCpuCore, idCpuPackage, enmEventType); switch (enmEventType) { case VMMDevCpuEventType_Plug: VBoxServiceCpuHotPlugHandlePlugEvent(idCpuCore, idCpuPackage); break; case VMMDevCpuEventType_Unplug: VBoxServiceCpuHotPlugHandleUnplugEvent(idCpuCore, idCpuPackage); break; default: { static uint32_t s_iErrors = 0; if (s_iErrors++ < 10) VBoxServiceError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n", idCpuCore, idCpuPackage, enmEventType); break; } } } else if (rc != VERR_INTERRUPTED && rc != VERR_TRY_AGAIN) { VBoxServiceError("CpuHotPlug: VbglR3CpuHotPlugWaitForEvent returned %Rrc\n", rc); break; } if (*pfShutdown) break; } VbglR3CpuHotPlugTerm(); return rc; }
/** @copydoc VBOXSERVICE::pfnWorker */ DECLCALLBACK(int) VBoxServiceVMStatsWorker(bool volatile *pfShutdown) { int rc = VINF_SUCCESS; /* Start monitoring of the stat event change event. */ rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0); if (RT_FAILURE(rc)) { VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc); return rc; } /* * Tell the control thread that it can continue * spawning services. */ RTThreadUserSignal(RTThreadSelf()); /* * Now enter the loop retrieving runtime data continuously. */ for (;;) { uint32_t fEvents = 0; RTMSINTERVAL cWaitMillies; /* Check if an update interval change is pending. */ rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents); if ( RT_SUCCESS(rc) && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST)) { VbglR3StatQueryInterval(&gCtx.cMsStatInterval); } if (gCtx.cMsStatInterval) { VBoxServiceVMStatsReport(); cWaitMillies = gCtx.cMsStatInterval; } else cWaitMillies = 3000; /* * Block for a while. * * The event semaphore takes care of ignoring interruptions and it * allows us to implement service wakeup later. */ if (*pfShutdown) break; int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies); if (*pfShutdown) break; if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2)) { VBoxServiceError("VBoxServiceVMStatsWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2); rc = rc2; break; } } /* Cancel monitoring of the stat event change event. */ rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST); if (RT_FAILURE(rc)) VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc); RTSemEventMultiDestroy(g_VMStatEvent); g_VMStatEvent = NIL_RTSEMEVENTMULTI; VBoxServiceVerbose(3, "VBoxStatsThread: finished statistics change request thread\n"); return 0; }
/** * Handles VMMDevCpuEventType_Plug. * * @param idCpuCore The CPU core ID. * @param idCpuPackage The CPU package ID. */ static void VBoxServiceCpuHotPlugHandlePlugEvent(uint32_t idCpuCore, uint32_t idCpuPackage) { #ifdef RT_OS_LINUX /* * The topology directory (containing the physical and core id properties) * is not available until the CPU is online. So we just iterate over all directories * and enable every CPU which is not online already. * Because the directory might not be available immediately we try a few times. * * @todo: Maybe use udev to monitor hot-add events from the kernel */ bool fCpuOnline = false; unsigned cTries = 5; do { PRTDIR pDirDevices = NULL; int rc = RTDirOpen(&pDirDevices, SYSFS_CPU_PATH); if (RT_SUCCESS(rc)) { RTDIRENTRY DirFolderContent; while (RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */ { /** @todo r-bird: This code is bringing all CPUs online; the idCpuCore and * idCpuPackage parameters are unused! * aeichner: These files are not available at this point unfortunately. (see comment above) * bird: Yes, but isn't that easily dealt with by doing: * if (matching_topology() || !have_topology_directory()) * bring_cpu_online() * That could save you the cpu0 and cpuidle checks to. */ /* * Check if this is a CPU object. * cpu0 is excluded because it is not possible to change the state * of the first CPU on Linux (it doesn't even have an online file) * and cpuidle is no CPU device. Prevents error messages later. */ if( !strncmp(DirFolderContent.szName, "cpu", 3) && strncmp(DirFolderContent.szName, "cpu0", 4) && strncmp(DirFolderContent.szName, "cpuidle", 7)) { /* Get the sysdev */ RTFILE hFileCpuOnline = NIL_RTFILE; rc = RTFileOpenF(&hFileCpuOnline, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName); if (RT_SUCCESS(rc)) { /* Write a 1 to online the CPU */ rc = RTFileWrite(hFileCpuOnline, "1", 1, NULL); RTFileClose(hFileCpuOnline); if (RT_SUCCESS(rc)) { VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was brought online\n", idCpuPackage, idCpuCore); fCpuOnline = true; break; } /* Error means CPU not present or online already */ } else VBoxServiceError("CpuHotPlug: Failed to open \"%s/%s/online\" rc=%Rrc\n", SYSFS_CPU_PATH, DirFolderContent.szName, rc); } } } else VBoxServiceError("CpuHotPlug: Failed to open path %s rc=%Rrc\n", SYSFS_CPU_PATH, rc); /* Sleep a bit */ if (!fCpuOnline) RTThreadSleep(10); } while ( !fCpuOnline && cTries-- > 0); #else # error "Port me" #endif }
/** * Returns the path of the ACPI CPU device with the given core and package ID. * * @returns VBox status code. * @param ppszPath Where to store the path. * @param idCpuCore The core ID of the CPU. * @param idCpuPackage The package ID of the CPU. */ static int VBoxServiceCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage) { int rc = VINF_SUCCESS; AssertPtrReturn(ppszPath, VERR_INVALID_PARAMETER); rc = VBoxServiceCpuHotPlugProbePath(); if (RT_SUCCESS(rc)) { /* Build the path from all components. */ bool fFound = false; unsigned iLvlCurr = 0; char *pszPath = NULL; char *pszPathDir = NULL; PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr]; /* Init everything. */ Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED); pszPath = RTPathJoinA(SYSFS_ACPI_CPU_PATH, pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId].pcszName); if (!pszPath) return VERR_NO_STR_MEMORY; pAcpiCpuPathLvl->pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH); if (!pAcpiCpuPathLvl->pszPath) { RTStrFree(pszPath); return VERR_NO_STR_MEMORY; } /* Open the directory */ rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->pDir, pszPath, RTDIRFILTER_WINNT, 0); if (RT_SUCCESS(rc)) { RTStrFree(pszPath); /* Search for CPU */ while (!fFound) { /* Get the next directory. */ RTDIRENTRY DirFolderContent; rc = RTDirRead(pAcpiCpuPathLvl->pDir, &DirFolderContent, NULL); if (RT_SUCCESS(rc)) { /* Create the new path. */ char *pszPathCurr = RTPathJoinA(pAcpiCpuPathLvl->pszPath, DirFolderContent.szName); if (!pszPathCurr) { rc = VERR_NO_STR_MEMORY; break; } /* If this is the last level check for the given core and package id. */ if (iLvlCurr == RT_ELEMENTS(g_aAcpiCpuPath) - 1) { /* Get the sysdev */ uint32_t idCore = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/core_id", pszPathCurr); uint32_t idPackage = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/physical_package_id", pszPathCurr); if ( idCore == idCpuCore && idPackage == idCpuPackage) { /* Return the path */ pszPath = pszPathCurr; fFound = true; VBoxServiceVerbose(3, "CPU found\n"); break; } else { /* Get the next directory. */ RTStrFree(pszPathCurr); VBoxServiceVerbose(3, "CPU doesn't match, next directory\n"); } } else { /* Go deeper */ iLvlCurr++; VBoxServiceVerbose(3, "Going deeper (iLvlCurr=%u)\n", iLvlCurr); pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr]; Assert(!pAcpiCpuPathLvl->pDir); Assert(!pAcpiCpuPathLvl->pszPath); pAcpiCpuPathLvl->pszPath = pszPathCurr; PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId]; Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED); pszPathDir = RTPathJoinA(pszPathCurr, pPathComponent->pcszName); if (!pszPathDir) { rc = VERR_NO_STR_MEMORY; break; } VBoxServiceVerbose(3, "New path %s\n", pszPathDir); /* Open the directory */ rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->pDir, pszPathDir, RTDIRFILTER_WINNT, 0); if (RT_FAILURE(rc)) break; } } else { /* Go back one level and try to get the next entry. */ Assert(iLvlCurr > 0); RTDirClose(pAcpiCpuPathLvl->pDir); RTStrFree(pAcpiCpuPathLvl->pszPath); pAcpiCpuPathLvl->pDir = NULL; pAcpiCpuPathLvl->pszPath = NULL; iLvlCurr--; pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr]; VBoxServiceVerbose(3, "Directory not found, going back (iLvlCurr=%u)\n", iLvlCurr); } } /* while not found */ } /* Successful init */ /* Cleanup */ for (unsigned i = 0; i < RT_ELEMENTS(g_aAcpiCpuPath); i++) { if (g_aAcpiCpuPath[i].pDir) RTDirClose(g_aAcpiCpuPath[i].pDir); if (g_aAcpiCpuPath[i].pszPath) RTStrFree(g_aAcpiCpuPath[i].pszPath); g_aAcpiCpuPath[i].pDir = NULL; g_aAcpiCpuPath[i].pszPath = NULL; } if (pszPathDir) RTStrFree(pszPathDir); if (RT_FAILURE(rc) && pszPath) RTStrFree(pszPath); if (RT_SUCCESS(rc)) *ppszPath = pszPath; } return rc; }
/** * Probes for the correct path to the ACPI CPU object in sysfs for the * various different kernel versions and distro's. * * @returns VBox status code. */ static int VBoxServiceCpuHotPlugProbePath(void) { int rc = VINF_SUCCESS; /* Probe for the correct path if we didn't already. */ if (RT_UNLIKELY(g_aAcpiCpuPath[0].uId == ACPI_CPU_PATH_NOT_PROBED)) { char *pszPath = NULL; /** < Current path, increasing while we dig deeper. */ pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH); if (!pszPath) return VERR_NO_MEMORY; /* * Simple algorithm to find the path. * Performance is not a real problem because it is * only executed once. */ for (unsigned iLvlCurr = 0; iLvlCurr < RT_ELEMENTS(g_aAcpiCpuPath); iLvlCurr++) { PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr]; for (unsigned iCompCurr = 0; iCompCurr < pAcpiCpuPathLvl->cComponents; iCompCurr++) { PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[iCompCurr]; /* Open the directory */ PRTDIR pDirCurr = NULL; char *pszPathTmp = RTPathJoinA(pszPath, pPathComponent->pcszName); if (pszPathTmp) { rc = RTDirOpenFiltered(&pDirCurr, pszPathTmp, RTDIRFILTER_WINNT, 0); RTStrFree(pszPathTmp); } else rc = VERR_NO_STR_MEMORY; if (RT_FAILURE(rc)) break; /* Search if the current directory contains one of the possible parts. */ size_t cchName = strlen(pPathComponent->pcszName); RTDIRENTRY DirFolderContent; bool fFound = false; /* Get rid of the * filter which is in the path component. */ if (pPathComponent->fNumberedSuffix) cchName--; while (RT_SUCCESS(RTDirRead(pDirCurr, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */ { if ( DirFolderContent.cbName >= cchName && !strncmp(DirFolderContent.szName, pPathComponent->pcszName, cchName)) { /* Found, use the complete name to dig deeper. */ fFound = true; pAcpiCpuPathLvl->uId = iCompCurr; char *pszPathLvl = RTPathJoinA(pszPath, DirFolderContent.szName); if (pszPathLvl) { RTStrFree(pszPath); pszPath = pszPathLvl; } else rc = VERR_NO_STR_MEMORY; break; } } RTDirClose(pDirCurr); if (fFound) break; } /* For every possible component. */ /* No matching component for this part, no need to continue */ if (RT_FAILURE(rc)) break; } /* For every level */ VBoxServiceVerbose(1, "Final path after probing %s rc=%Rrc\n", pszPath, rc); RTStrFree(pszPath); } return rc; }
int main(int argc, char **argv) { RTEXITCODE rcExit; /* * Init globals and such. */ int rc = RTR3InitExe(argc, &argv, 0); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); g_pszProgName = RTPathFilename(argv[0]); #ifdef DEBUG rc = RTCritSectInit(&g_csLog); AssertRC(rc); #endif #ifdef VBOXSERVICE_TOOLBOX /* * Run toolbox code before all other stuff since these things are simpler * shell/file/text utility like programs that just happens to be inside * VBoxService and shouldn't be subject to /dev/vboxguest, pid-files and * global mutex restrictions. */ if (VBoxServiceToolboxMain(argc, argv, &rcExit)) return rcExit; #endif /* * Connect to the kernel part before daemonizing so we can fail and * complain if there is some kind of problem. We need to initialize the * guest lib *before* we do the pre-init just in case one of services needs * do to some initial stuff with it. */ VBoxServiceVerbose(2, "Calling VbgR3Init()\n"); rc = VbglR3Init(); if (RT_FAILURE(rc)) { if (rc == VERR_ACCESS_DENIED) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Insufficient privileges to start %s! Please start with Administrator/root privileges!\n", g_pszProgName); return RTMsgErrorExit(RTEXITCODE_FAILURE, "VbglR3Init failed with rc=%Rrc\n", rc); } #ifdef RT_OS_WINDOWS /* * Check if we're the specially spawned VBoxService.exe process that * handles page fusion. This saves an extra executable. */ if ( argc == 2 && !strcmp(argv[1], "--pagefusionfork")) return VBoxServicePageSharingInitFork(); #endif char szLogFile[RTPATH_MAX + 128] = ""; /* * Parse the arguments. * * Note! This code predates RTGetOpt, thus the manual parsing. */ bool fDaemonize = true; bool fDaemonized = false; for (int i = 1; i < argc; i++) { const char *psz = argv[i]; if (*psz != '-') return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown argument '%s'\n", psz); psz++; /* translate long argument to short */ if (*psz == '-') { psz++; size_t cch = strlen(psz); #define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \ && !memcmp(psz, strconst, sizeof(strconst) - 1) ) if (MATCHES("foreground")) psz = "f"; else if (MATCHES("verbose")) psz = "v"; else if (MATCHES("version")) psz = "V"; else if (MATCHES("help")) psz = "h"; else if (MATCHES("interval")) psz = "i"; #ifdef RT_OS_WINDOWS else if (MATCHES("register")) psz = "r"; else if (MATCHES("unregister")) psz = "u"; #endif else if (MATCHES("logfile")) psz = "l"; else if (MATCHES("daemonized")) { fDaemonized = true; continue; } else { bool fFound = false; if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1)) for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++) if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName))) g_aServices[j].fEnabled = true; if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1)) for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++) if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName))) g_aServices[j].fEnabled = false; if (cch > sizeof("only-") && !memcmp(psz, "only-", sizeof("only-") - 1)) for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) { g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName); if (g_aServices[j].fEnabled) fFound = true; } if (!fFound) { rcExit = vboxServiceLazyPreInit(); if (rcExit != RTEXITCODE_SUCCESS) return rcExit; for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++) { rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i); fFound = rc == VINF_SUCCESS; if (fFound) break; if (rc != -1) return rc; } } if (!fFound) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%s'\n", argv[i]); continue; } #undef MATCHES } /* handle the string of short options. */ do { switch (*psz) { case 'i': rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i, &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1); if (rc) return rc; psz = NULL; break; case 'f': fDaemonize = false; break; case 'v': g_cVerbosity++; break; case 'V': RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); return RTEXITCODE_SUCCESS; case 'h': case '?': return vboxServiceUsage(); #ifdef RT_OS_WINDOWS case 'r': return VBoxServiceWinInstall(); case 'u': return VBoxServiceWinUninstall(); #endif case 'l': { rc = VBoxServiceArgString(argc, argv, psz + 1, &i, szLogFile, sizeof(szLogFile)); if (rc) return rc; psz = NULL; break; } default: { rcExit = vboxServiceLazyPreInit(); if (rcExit != RTEXITCODE_SUCCESS) return rcExit; bool fFound = false; for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) { rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i); fFound = rc == VINF_SUCCESS; if (fFound) break; if (rc != -1) return rc; } if (!fFound) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option '%c' (%s)\n", *psz, argv[i]); break; } } } while (psz && *++psz); } /* Check that at least one service is enabled. */ if (vboxServiceCountEnabledServices() == 0) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n"); rc = VBoxServiceLogCreate(strlen(szLogFile) ? szLogFile : NULL); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)", strlen(szLogFile) ? szLogFile : "<None>", rc); /* Call pre-init if we didn't do it already. */ rcExit = vboxServiceLazyPreInit(); if (rcExit != RTEXITCODE_SUCCESS) return rcExit; #ifdef RT_OS_WINDOWS /* * Make sure only one instance of VBoxService runs at a time. Create a * global mutex for that. * * Note! The \\Global\ namespace was introduced with Win2K, thus the * version check. * Note! If the mutex exists CreateMutex will open it and set last error to * ERROR_ALREADY_EXISTS. */ OSVERSIONINFOEX OSInfoEx; RT_ZERO(OSInfoEx); OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); SetLastError(NO_ERROR); HANDLE hMutexAppRunning; if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx) && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT && OSInfoEx.dwMajorVersion >= 5 /* NT 5.0 a.k.a W2K */) hMutexAppRunning = CreateMutex(NULL, FALSE, "Global\\" VBOXSERVICE_NAME); else hMutexAppRunning = CreateMutex(NULL, FALSE, VBOXSERVICE_NAME); if (hMutexAppRunning == NULL) { DWORD dwErr = GetLastError(); if ( dwErr == ERROR_ALREADY_EXISTS || dwErr == ERROR_ACCESS_DENIED) { VBoxServiceError("%s is already running! Terminating.", g_pszProgName); return RTEXITCODE_FAILURE; } VBoxServiceError("CreateMutex failed with last error %u! Terminating", GetLastError()); return RTEXITCODE_FAILURE; } #else /* !RT_OS_WINDOWS */ /** @todo Add PID file creation here? */ #endif /* !RT_OS_WINDOWS */ VBoxServiceVerbose(0, "%s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity); /* * Daemonize if requested. */ if (fDaemonize && !fDaemonized) { #ifdef RT_OS_WINDOWS VBoxServiceVerbose(2, "Starting service dispatcher ...\n"); rcExit = VBoxServiceWinEnterCtrlDispatcher(); #else VBoxServiceVerbose(1, "Daemonizing...\n"); rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */); if (RT_FAILURE(rc)) return VBoxServiceError("Daemon failed: %Rrc\n", rc); /* in-child */ #endif } #ifdef RT_OS_WINDOWS else #endif { /* * Windows: We're running the service as a console application now. Start the * services, enter the main thread's run loop and stop them again * when it returns. * * POSIX: This is used for both daemons and console runs. Start all services * and return immediately. */ #ifdef RT_OS_WINDOWS # ifndef RT_OS_NT4 /* Install console control handler. */ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)VBoxServiceConsoleControlHandler, TRUE /* Add handler */)) { VBoxServiceError("Unable to add console control handler, error=%ld\n", GetLastError()); /* Just skip this error, not critical. */ } # endif /* !RT_OS_NT4 */ #endif /* RT_OS_WINDOWS */ rc = VBoxServiceStartServices(); rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; if (RT_SUCCESS(rc)) VBoxServiceMainWait(); #ifdef RT_OS_WINDOWS # ifndef RT_OS_NT4 /* Uninstall console control handler. */ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */)) { VBoxServiceError("Unable to remove console control handler, error=%ld\n", GetLastError()); /* Just skip this error, not critical. */ } # endif /* !RT_OS_NT4 */ #else /* !RT_OS_WINDOWS */ /* On Windows - since we're running as a console application - we already stopped all services * through the console control handler. So only do the stopping of services here on other platforms * where the break/shutdown/whatever signal was just received. */ VBoxServiceStopServices(); #endif /* RT_OS_WINDOWS */ } VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminated); #ifdef RT_OS_WINDOWS /* * Cleanup mutex. */ CloseHandle(hMutexAppRunning); #endif VBoxServiceVerbose(0, "Ended.\n"); #ifdef DEBUG RTCritSectDelete(&g_csLog); //RTMemTrackerDumpAllToStdOut(); #endif VBoxServiceLogDestroy(); return rcExit; }
/** * Gathers VM statistics and reports them to the host. */ static void VBoxServiceVMStatsReport(void) { #if defined(RT_OS_WINDOWS) SYSTEM_INFO systemInfo; PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo; MEMORYSTATUSEX memStatus; uint32_t cbStruct; DWORD cbReturned; Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation); if ( !gCtx.pfnGlobalMemoryStatusEx || !gCtx.pfnNtQuerySystemInformation) return; /* Clear the report so we don't report garbage should NtQuerySystemInformation behave in an unexpected manner. */ VMMDevReportGuestStats req; RT_ZERO(req); /* Query and report guest statistics */ GetSystemInfo(&systemInfo); memStatus.dwLength = sizeof(memStatus); gCtx.pfnGlobalMemoryStatusEx(&memStatus); req.guestStats.u32PageSize = systemInfo.dwPageSize; req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / _4K); req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / _4K); /* The current size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. */ req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal; req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad; req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_PAGE_FILE_SIZE | VBOX_GUEST_STAT_MEMORY_LOAD; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif if (gCtx.pfnGetPerformanceInfo) { PERFORMANCE_INFORMATION perfInfo; if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo))) { req.guestStats.u32Processes = perfInfo.ProcessCount; req.guestStats.u32Threads = perfInfo.ThreadCount; req.guestStats.u32Handles = perfInfo.HandleCount; req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */ req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */ req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */ req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */ req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE; } else VBoxServiceVerbose(3, "VBoxServiceVMStatsReport: GetPerformanceInfo failed with %d\n", GetLastError()); } /* Query CPU load information */ cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct); if (!pProcInfo) return; /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */ NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned); if ( !rc && cbReturned == cbStruct) { if (gCtx.au64LastCpuLoad_Kernel == 0) { /* first time */ gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart; gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart; gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart; Sleep(250); rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned); Assert(!rc); } uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.au64LastCpuLoad_Idle[0]); uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.au64LastCpuLoad_Kernel[0]); uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.au64LastCpuLoad_User[0]); deltaKernel -= deltaIdle; /* idle time is added to kernel time */ uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser; if (ullTotalTime == 0) /* Prevent division through zero. */ ullTotalTime = 1; req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime); req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart; gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart; gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart; /** @todo SMP: report details for each CPU? */ } for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++) { req.guestStats.u32CpuId = i; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", i); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError()); } RTMemFree(pProcInfo); #elif defined(RT_OS_LINUX) VMMDevReportGuestStats req; RT_ZERO(req); PRTSTREAM pStrm; char szLine[256]; char *psz; int rc = RTStrmOpen("/proc/meminfo", "r", &pStrm); if (RT_SUCCESS(rc)) { uint64_t u64Kb; uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0; for (;;) { rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine)); if (RT_FAILURE(rc)) break; if (strstr(szLine, "MemTotal:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[9]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Total = u64Kb * _1K; } else if (strstr(szLine, "MemFree:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Free = u64Kb * _1K; } else if (strstr(szLine, "Buffers:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Buffers = u64Kb * _1K; } else if (strstr(szLine, "Cached:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[7]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Cached = u64Kb * _1K; } else if (strstr(szLine, "SwapTotal:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[10]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64PagedTotal = u64Kb * _1K; } } req.guestStats.u32PhysMemTotal = u64Total / _4K; req.guestStats.u32PhysMemAvail = (u64Free + u64Buffers + u64Cached) / _4K; req.guestStats.u32MemSystemCache = (u64Buffers + u64Cached) / _4K; req.guestStats.u32PageFileSize = u64PagedTotal / _4K; RTStrmClose(pStrm); } else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: memory info not available!\n"); req.guestStats.u32PageSize = getpagesize(); req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif /** @todo req.guestStats.u32Threads */ /** @todo req.guestStats.u32Processes */ /* req.guestStats.u32Handles doesn't make sense here. */ /** @todo req.guestStats.u32MemoryLoad */ /** @todo req.guestStats.u32MemCommitTotal */ /** @todo req.guestStats.u32MemKernelTotal */ /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */ /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */ bool fCpuInfoAvail = false; rc = RTStrmOpen("/proc/stat", "r", &pStrm); if (RT_SUCCESS(rc)) { for (;;) { rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine)); if (RT_FAILURE(rc)) break; if ( strstr(szLine, "cpu") == szLine && strlen(szLine) > 3 && RT_C_IS_DIGIT(szLine[3])) { uint32_t u32CpuId; rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId); if (u32CpuId < VMM_MAX_CPU_COUNT) { uint64_t u64User = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User); uint64_t u64Nice = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice); uint64_t u64System = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System); uint64_t u64Idle = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle); uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[u32CpuId]; uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[u32CpuId]; uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[u32CpuId]; uint64_t u64DeltaNice = u64Nice - gCtx.au64LastCpuLoad_Nice[u32CpuId]; uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser + u64DeltaNice; if (u64DeltaAll == 0) /* Prevent division through zero. */ u64DeltaAll = 1; gCtx.au64LastCpuLoad_Idle[u32CpuId] = u64Idle; gCtx.au64LastCpuLoad_Kernel[u32CpuId] = u64System; gCtx.au64LastCpuLoad_User[u32CpuId] = u64User; gCtx.au64LastCpuLoad_Nice[u32CpuId] = u64Nice; req.guestStats.u32CpuId = u32CpuId; req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser + u64DeltaNice) * 100 / u64DeltaAll); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; fCpuInfoAvail = true; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", u32CpuId); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: skipping information for CPU%u\n", u32CpuId); } } RTStrmClose(pStrm); } if (!fCpuInfoAvail) { VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n"); rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n"); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } #elif defined(RT_OS_SOLARIS) VMMDevReportGuestStats req; RT_ZERO(req); kstat_ctl_t *pStatKern = kstat_open(); if (pStatKern) { /* * Memory statistics. */ uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0; int rc = -1; kstat_t *pStatPages = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"system_pages"); if (pStatPages) { rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */); if (rc != -1) { kstat_named_t *pStat = NULL; pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"pagestotal"); if (pStat) u64Total = pStat->value.ul; pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"freemem"); if (pStat) u64Free = pStat->value.ul; } } kstat_t *pStatZFS = kstat_lookup(pStatKern, (char *)"zfs", 0 /* instance */, (char *)"arcstats"); if (pStatZFS) { rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */); if (rc != -1) { kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, (char *)"size"); if (pStat) u64Cached = pStat->value.ul; } } /* * The vminfo are accumulative counters updated every "N" ticks. Let's get the * number of stat updates so far and use that to divide the swap counter. */ kstat_t *pStatInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"sysinfo"); if (pStatInfo) { sysinfo_t SysInfo; rc = kstat_read(pStatKern, pStatInfo, &SysInfo); if (rc != -1) { kstat_t *pStatVMInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"vminfo"); if (pStatVMInfo) { vminfo_t VMInfo; rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo); if (rc != -1) { Assert(SysInfo.updates != 0); u64PagedTotal = VMInfo.swap_avail / SysInfo.updates; } } } } req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */ req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */ req.guestStats.u32MemSystemCache = u64Cached / _4K; req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */ /** @todo req.guestStats.u32Threads */ /** @todo req.guestStats.u32Processes */ /** @todo req.guestStats.u32Handles -- ??? */ /** @todo req.guestStats.u32MemoryLoad */ /** @todo req.guestStats.u32MemCommitTotal */ /** @todo req.guestStats.u32MemKernelTotal */ /** @todo req.guestStats.u32MemKernelPaged */ /** @todo req.guestStats.u32MemKernelNonPaged */ req.guestStats.u32PageSize = getpagesize(); req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif /* * CPU statistics. */ cpu_stat_t StatCPU; RT_ZERO(StatCPU); kstat_t *pStatNode = NULL; uint32_t cCPUs = 0; bool fCpuInfoAvail = false; for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next) { if (!strcmp(pStatNode->ks_module, "cpu_stat")) { rc = kstat_read(pStatKern, pStatNode, &StatCPU); if (rc == -1) break; uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE]; uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER]; uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL]; uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[cCPUs]; uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[cCPUs]; uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[cCPUs]; uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser; if (u64DeltaAll == 0) /* Prevent division through zero. */ u64DeltaAll = 1; gCtx.au64LastCpuLoad_Idle[cCPUs] = u64Idle; gCtx.au64LastCpuLoad_Kernel[cCPUs] = u64System; gCtx.au64LastCpuLoad_User[cCPUs] = u64User; req.guestStats.u32CpuId = cCPUs; req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; fCpuInfoAvail = true; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", cCPUs); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); cCPUs++; } } /* * Report whatever statistics were collected. */ if (!fCpuInfoAvail) { VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n"); rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n"); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } kstat_close(pStatKern); } #else /* todo: implement for other platforms. */ #endif }
/** * Starts the service. * * @returns VBox status code, errors are fully bitched. */ int VBoxServiceStartServices(void) { int rc; VBoxServiceReportStatus(VBoxGuestFacilityStatus_Init); /* * Initialize the services. */ VBoxServiceVerbose(2, "Initializing services ...\n"); for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) if (g_aServices[j].fEnabled) { rc = g_aServices[j].pDesc->pfnInit(); if (RT_FAILURE(rc)) { if (rc != VERR_SERVICE_DISABLED) { VBoxServiceError("Service '%s' failed to initialize: %Rrc\n", g_aServices[j].pDesc->pszName, rc); VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed); return rc; } g_aServices[j].fEnabled = false; VBoxServiceVerbose(0, "Service '%s' was disabled because of missing functionality\n", g_aServices[j].pDesc->pszName); } } /* * Start the service(s). */ VBoxServiceVerbose(2, "Starting services ...\n"); rc = VINF_SUCCESS; for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) { if (!g_aServices[j].fEnabled) continue; VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName); rc = RTThreadCreate(&g_aServices[j].Thread, vboxServiceThread, (void *)(uintptr_t)j, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName); if (RT_FAILURE(rc)) { VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc); break; } g_aServices[j].fStarted = true; /* Wait for the thread to initialize. */ /** @todo There is a race between waiting and checking * the fShutdown flag of a thread here and processing * the thread's actual worker loop. If the thread decides * to exit the loop before we skipped the fShutdown check * below the service will fail to start! */ RTThreadUserWait(g_aServices[j].Thread, 60 * 1000); if (g_aServices[j].fShutdown) { VBoxServiceError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName); rc = VERR_GENERAL_FAILURE; } } if (RT_SUCCESS(rc)) VBoxServiceVerbose(1, "All services started.\n"); else { VBoxServiceError("An error occcurred while the services!\n"); VBoxServiceReportStatus(VBoxGuestFacilityStatus_Failed); } return rc; }
/** @copydoc VBOXSERVICE::pfnTerm */ static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void) { VBoxServiceVerbose(3, "VBoxServiceVMStatsTerm\n"); return; }
/** * Stops and terminates the services. * * This should be called even when VBoxServiceStartServices fails so it can * clean up anything that we succeeded in starting. */ int VBoxServiceStopServices(void) { VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminating); /* * Signal all the services. */ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) ASMAtomicWriteBool(&g_aServices[j].fShutdown, true); /* * Do the pfnStop callback on all running services. */ for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) if (g_aServices[j].fStarted) { VBoxServiceVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName); g_aServices[j].pDesc->pfnStop(); } /* * Wait for all the service threads to complete. */ int rc = VINF_SUCCESS; for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) { if (!g_aServices[j].fEnabled) /* Only stop services which were started before. */ continue; if (g_aServices[j].Thread != NIL_RTTHREAD) { VBoxServiceVerbose(2, "Waiting for service '%s' to stop ...\n", g_aServices[j].pDesc->pszName); int rc2 = VINF_SUCCESS; for (int i = 0; i < 30; i++) /* Wait 30 seconds in total */ { rc2 = RTThreadWait(g_aServices[j].Thread, 1000 /* Wait 1 second */, NULL); if (RT_SUCCESS(rc2)) break; #ifdef RT_OS_WINDOWS /* Notify SCM that it takes a bit longer ... */ VBoxServiceWinSetStopPendingStatus(i + j*32); #endif } if (RT_FAILURE(rc2)) { VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2); rc = rc2; } } VBoxServiceVerbose(3, "Terminating service '%s' (%d) ...\n", g_aServices[j].pDesc->pszName, j); g_aServices[j].pDesc->pfnTerm(); } #ifdef RT_OS_WINDOWS /* * Wake up and tell the main() thread that we're shutting down (it's * sleeping in VBoxServiceMainWait). */ ASMAtomicWriteBool(&g_fWindowsServiceShutdown, true); if (g_hEvtWindowsService != NIL_RTSEMEVENT) { VBoxServiceVerbose(3, "Stopping the main thread...\n"); int rc2 = RTSemEventSignal(g_hEvtWindowsService); AssertRC(rc2); } #endif VBoxServiceVerbose(2, "Stopping services returning: %Rrc\n", rc); VBoxServiceReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed); return rc; }