Пример #1
0
/**
 * Try adjust the time using adjtime or similar.
 *
 * @returns true on success, false on failure.
 *
 * @param   pDrift              The time adjustment.
 */
static void vgsvcTimeSyncSet(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;
            VGSvcVerbose(1, "time set to %s\n", RTTimeToString(RTTimeExplode(&Time, &NewGuestTime), sz, sizeof(sz)));
#ifdef DEBUG
            RTTIMESPEC  Tmp;
            if (g_cVerbosity >= 3)
                VGSvcVerbose(3, "        now %s\n", RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
#endif
        }
    }
    else if (g_cTimeSyncErrors++ < 10)
        VGSvcError("vgsvcTimeSyncSet: RTTimeSet(%RDtimespec) failed: %Rrc\n", &NewGuestTime, rc);
}
Пример #2
0
static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv)
{
    RT_NOREF2(argc, argv);
    VGSvcVerbose(2, "Registering service control handler ...\n");
    if (g_pfnRegisterServiceCtrlHandlerExA)
        g_hWinServiceStatus = g_pfnRegisterServiceCtrlHandlerExA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt5Plus, NULL);
    else
        g_hWinServiceStatus = RegisterServiceCtrlHandlerA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt4);
    if (g_hWinServiceStatus != NULL)
    {
        VGSvcVerbose(2, "Service control handler registered.\n");
        vgsvcWinStart();
    }
    else
    {
        DWORD dwErr = GetLastError();
        switch (dwErr)
        {
            case ERROR_INVALID_NAME:
                VGSvcError("Invalid service name!\n");
                break;
            case ERROR_SERVICE_DOES_NOT_EXIST:
                VGSvcError("Service does not exist!\n");
                break;
            default:
                VGSvcError("Could not register service control handle! Error: %ld\n", dwErr);
                break;
        }
    }
}
Пример #3
0
/**
 * Callback registered by RegisterServiceCtrlHandler on NT5 and later.
 */
static DWORD WINAPI vgsvcWinCtrlHandlerNt5Plus(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
    VGSvcVerbose(2, "Control handler: dwControl=%#x, dwEventType=%#x\n", dwControl, dwEventType);
    RT_NOREF1(lpContext);

    switch (dwControl)
    {
        default:
            return vgsvcWinCtrlHandlerCommon(dwControl);

        case SERVICE_CONTROL_SESSIONCHANGE:  /* Only Windows 2000 and up. */
        {
            AssertPtr(lpEventData);
            PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
            Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));

            VGSvcVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
                         vgsvcWTSStateToString(dwEventType), pNotify->dwSessionId, dwEventType);

            /* Handle all events, regardless of dwEventType. */
            int rc2 = VGSvcVMInfoSignal();
            AssertRC(rc2);

            return NO_ERROR;
        }
    }
}
Пример #4
0
/**
 * Block the main thread until the service shuts down.
 *
 * @remarks Also called from VBoxService-win.cpp, thus not static.
 */
void VGSvcMainWait(void)
{
    int rc;

    VGSvcReportStatus(VBoxGuestFacilityStatus_Active);

#ifdef RT_OS_WINDOWS
    /*
     * Wait for the semaphore to be signalled.
     */
    VGSvcVerbose(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
          );

    VGSvcVerbose(3, "VGSvcMainWait: Received signal %d (rc=%d)\n", iSignal, rc);
#endif /* !RT_OS_WINDOWS */
}
Пример #5
0
/**
 * Console control event callback.
 *
 * @returns TRUE if handled, FALSE if not.
 * @param   dwCtrlType      The control event type.
 *
 * @remarks This is generally called on a new thread, so we're racing every
 *          other thread in the process.
 */
static BOOL WINAPI vgsvcWinConsoleControlHandler(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:
            VGSvcVerbose(2, "ControlHandler: Received break/close event\n");
            rc = VGSvcStopServices();
            fEventHandled = TRUE;
            break;
        default:
            break;
        /** @todo Add other events here. */
    }

    if (RT_FAILURE(rc))
        VGSvcError("ControlHandler: Event %ld handled with error rc=%Rrc\n",
                         dwCtrlType, rc);
    return fEventHandled;
}
Пример #6
0
/**
 * Common control handler.
 *
 * @returns Return code for NT5+.
 * @param   dwControl           The control code.
 */
static DWORD vgsvcWinCtrlHandlerCommon(DWORD dwControl)
{
    DWORD rcRet = NO_ERROR;
    switch (dwControl)
    {
        case SERVICE_CONTROL_INTERROGATE:
            vgsvcWinSetStatus(g_dwWinServiceLastStatus, 0);
            break;

        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
        {
            vgsvcWinSetStatus(SERVICE_STOP_PENDING, 0);

            int rc2 = VGSvcStopServices();
            if (RT_FAILURE(rc2))
                rcRet = ERROR_GEN_FAILURE;
            else
            {
                rc2 = VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
                AssertRC(rc2);
            }

            vgsvcWinSetStatus(SERVICE_STOPPED, 0);
            break;
        }

        default:
            VGSvcVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
            rcRet = ERROR_CALL_NOT_IMPLEMENTED;
            break;
    }

    return rcRet;
}
Пример #7
0
/**
 * Uninstalls the service.
 */
RTEXITCODE VGSvcWinUninstall(void)
{
    VGSvcVerbose(1, "Uninstalling service ...\n");

    SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (hSCManager == NULL)
    {
        VGSvcError("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);
            }

            VGSvcVerbose(0, "Service successfully uninstalled!\n");
            rcExit = RTEXITCODE_SUCCESS;
        }
        else
            rcExit = VGSvcError("Could not remove service! Error: %d\n", GetLastError());
        CloseServiceHandle(hService);
    }
    else
        rcExit = VGSvcError("Could not open service! Error: %d\n", GetLastError());
    CloseServiceHandle(hSCManager);

    return rcExit;
}
Пример #8
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnInit}
 */
static DECLCALLBACK(int) vgsvcVMStatsInit(void)
{
    VGSvcVerbose(3, "vgsvcVMStatsInit\n");

    int rc = RTSemEventMultiCreate(&g_VMStatEvent);
    AssertRCReturn(rc, rc);

    g_VMStat.cMsStatInterval        = 0;     /* default; update disabled */
    RT_ZERO(g_VMStat.au64LastCpuLoad_Idle);
    RT_ZERO(g_VMStat.au64LastCpuLoad_Kernel);
    RT_ZERO(g_VMStat.au64LastCpuLoad_User);
    RT_ZERO(g_VMStat.au64LastCpuLoad_Nice);

    rc = VbglR3StatQueryInterval(&g_VMStat.cMsStatInterval);
    if (RT_SUCCESS(rc))
        VGSvcVerbose(3, "vgsvcVMStatsInit: New statistics interval %u seconds\n", g_VMStat.cMsStatInterval);
    else
        VGSvcVerbose(3, "vgsvcVMStatsInit: 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 **)&g_VMStat.pfnNtQuerySystemInformation = RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation");
    if (g_VMStat.pfnNtQuerySystemInformation)
        VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnNtQuerySystemInformation = %x\n", g_VMStat.pfnNtQuerySystemInformation);
    else
    {
        VGSvcVerbose(3, "vgsvcVMStatsInit: ntdll.NtQuerySystemInformation not found!\n");
        return VERR_SERVICE_DISABLED;
    }

    /* GlobalMemoryStatus is win2k and up, so load it dynamically */
    *(void **)&g_VMStat.pfnGlobalMemoryStatusEx = RTLdrGetSystemSymbol("kernel32.dll", "GlobalMemoryStatusEx");
    if (g_VMStat.pfnGlobalMemoryStatusEx)
        VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.GlobalMemoryStatusEx = %x\n", g_VMStat.pfnGlobalMemoryStatusEx);
    else
    {
        /** @todo Now fails in NT4; do we care? */
        VGSvcVerbose(3, "vgsvcVMStatsInit: kernel32.GlobalMemoryStatusEx not found!\n");
        return VERR_SERVICE_DISABLED;
    }

    /* GetPerformanceInfo is xp and up, so load it dynamically */
    *(void **)&g_VMStat.pfnGetPerformanceInfo = RTLdrGetSystemSymbol("psapi.dll", "GetPerformanceInfo");
    if (g_VMStat.pfnGetPerformanceInfo)
        VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnGetPerformanceInfo= %x\n", g_VMStat.pfnGetPerformanceInfo);
#endif /* RT_OS_WINDOWS */

    return VINF_SUCCESS;
}
Пример #9
0
/**
 * Cancels any pending time adjustment.
 *
 * Called when we've caught up and before calls to vgsvcTimeSyncSet.
 */
static void vgsvcTimeSyncCancelAdjust(void)
{
#ifdef RT_OS_WINDOWS
/** @todo r=bird: g_hTokenProcess cannot be NULL here.  See argumentation in
 *        vgsvcTimeSyncAdjust.  */
    if (g_hTokenProcess == NULL) /* No process token (anymore)? */
        return;
    if (SetSystemTimeAdjustment(0, TRUE /* Periodic adjustments disabled. */))
        VGSvcVerbose(3, "vgsvcTimeSyncCancelAdjust: Windows Time Adjustment is now disabled.\n");
    else if (g_cTimeSyncErrors++ < 10)
        VGSvcError("vgsvcTimeSyncCancelAdjust: SetSystemTimeAdjustment(,disable) failed, error=%u\n", GetLastError());
#endif /* !RT_OS_WINDOWS */
}
Пример #10
0
/**
 * 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 VGSvcReportStatus(VBoxGuestFacilityStatus enmStatus)
{
    /*
     * VBoxGuestFacilityStatus_Failed is sticky.
     */
    static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
    VGSvcVerbose(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))
        {
            VGSvcError("Could not report VBoxService status (%u), rc=%Rrc\n", enmStatus, rc);
            return rc;
        }
        s_enmLastStatus = enmStatus;
    }
    return VINF_SUCCESS;
}
Пример #11
0
/** Reports our current status to the SCM. */
static BOOL vgsvcWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint)
{
    if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */
        return FALSE;

    VGSvcVerbose(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;

        /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000 or earlier.  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
            VGSvcError("Error determining OS version, rc=%Rrc\n", rc);
    }

    ss.dwWin32ExitCode            = NO_ERROR;
    ss.dwServiceSpecificExitCode  = 0; /* Not used */
    ss.dwCheckPoint               = dwCheckPoint;
    ss.dwWaitHint                 = 3000;

    BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss);
    if (!fStatusSet)
        VGSvcError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n",
                   dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError());
    return fStatusSet;
}
Пример #12
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnWorker}
 */
DECLCALLBACK(int) vgsvcTimeSyncWorker(bool volatile *pfShutdown)
{
    RTTIME Time;
    char sz[64];
    int rc = VINF_SUCCESS;

    /*
     * Tell the control thread that it can continue spawning services.
     */
    RTThreadUserSignal(RTThreadSelf());

    /*
     * The Work Loop.
     */
    for (;;)
    {
        /*
         * Try get a reliable time reading.
         */
        int cTries = 3;
        do
        {
            /* query it. */
            RTTIMESPEC GuestNow0, GuestNow, HostNow;
            RTTimeNow(&GuestNow0);
            int rc2 = VbglR3GetHostTime(&HostNow);
            if (RT_FAILURE(rc2))
            {
                if (g_cTimeSyncErrors++ < 10)
                    VGSvcError("vgsvcTimeSyncWorker: VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
                break;
            }
            RTTimeNow(&GuestNow);

            /* calc latency and check if it's ok. */
            RTTIMESPEC GuestElapsed = GuestNow;
            RTTimeSpecSub(&GuestElapsed, &GuestNow0);
            if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency)
            {
                /*
                 * Set the time once after we were restored.
                 * (Of course only if the drift is bigger than MinAdjust)
                 */
                uint32_t TimeSyncSetThreshold = g_TimeSyncSetThreshold;
                if (g_fTimeSyncSetOnRestore)
                {
                    uint64_t idNewSession = g_idTimeSyncSession;
                    VbglR3GetSessionId(&idNewSession);
                    if (idNewSession != g_idTimeSyncSession)
                    {
                        VGSvcVerbose(3, "vgsvcTimeSyncWorker: The VM session ID changed, forcing resync.\n");
                        TimeSyncSetThreshold = 0;
                        g_idTimeSyncSession  = idNewSession;
                    }
                }

                /*
                 * Calculate the adjustment threshold and the current drift.
                 */
                uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
                if (MinAdjust < g_TimeSyncMinAdjust)
                    MinAdjust = g_TimeSyncMinAdjust;

                RTTIMESPEC Drift = HostNow;
                RTTimeSpecSub(&Drift, &GuestNow);
                if (RTTimeSpecGetMilli(&Drift) < 0)
                    MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */

                RTTIMESPEC AbsDrift = Drift;
                RTTimeSpecAbsolute(&AbsDrift);
                if (g_cVerbosity >= 3)
                {
                    VGSvcVerbose(3, "vgsvcTimeSyncWorker: Host:    %s    (MinAdjust: %RU32 ms)\n",
                                 RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust);
                    VGSvcVerbose(3, "vgsvcTimeSyncWorker: Guest: - %s => %RDtimespec drift\n",
                                 RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)), &Drift);
                }

                uint32_t AbsDriftMilli = RTTimeSpecGetMilli(&AbsDrift);
                if (AbsDriftMilli > MinAdjust)
                {
                    /*
                     * Ok, the drift is above the threshold.
                     *
                     * Try a gradual adjustment first, if that fails or the drift is
                     * too big, fall back on just setting the time.
                     */

                    if (    AbsDriftMilli > TimeSyncSetThreshold
                        ||  g_fTimeSyncSetNext
                        ||  !vgsvcTimeSyncAdjust(&Drift))
                    {
                        vgsvcTimeSyncCancelAdjust();
                        vgsvcTimeSyncSet(&Drift);
                    }
                }
                else
                    vgsvcTimeSyncCancelAdjust();
                break;
            }
            VGSvcVerbose(3, "vgsvcTimeSyncWorker: %RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed);
            RTThreadSleep(1000);
        } while (--cTries > 0);

        /* Clear the set-next/set-start flag. */
        g_fTimeSyncSetNext = false;

        /*
         * 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_TimeSyncEvent, g_TimeSyncInterval);
        if (*pfShutdown)
            break;
        if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
        {
            VGSvcError("vgsvcTimeSyncWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
            rc = rc2;
            break;
        }
    }

    vgsvcTimeSyncCancelAdjust();
    RTSemEventMultiDestroy(g_TimeSyncEvent);
    g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
    return rc;
}
Пример #13
0
/**
 * Starts the service.
 *
 * @returns VBox status code, errors are fully bitched.
 *
 * @remarks Also called from VBoxService-win.cpp, thus not static.
 */
int VGSvcStartServices(void)
{
    int rc;

    VGSvcReportStatus(VBoxGuestFacilityStatus_Init);

    /*
     * Initialize the services.
     */
    VGSvcVerbose(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)
                {
                    VGSvcError("Service '%s' failed to initialize: %Rrc\n", g_aServices[j].pDesc->pszName, rc);
                    VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
                    return rc;
                }

                g_aServices[j].fEnabled = false;
                VGSvcVerbose(0, "Service '%s' was disabled because of missing functionality\n", g_aServices[j].pDesc->pszName);
            }
        }

    /*
     * Start the service(s).
     */
    VGSvcVerbose(2, "Starting services ...\n");
    rc = VINF_SUCCESS;
    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
    {
        if (!g_aServices[j].fEnabled)
            continue;

        VGSvcVerbose(2, "Starting service     '%s' ...\n", g_aServices[j].pDesc->pszName);
        rc = RTThreadCreate(&g_aServices[j].Thread, vgsvcThread, (void *)(uintptr_t)j, 0,
                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
        if (RT_FAILURE(rc))
        {
            VGSvcError("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! */
        /** @todo This presumably means either a one-shot service or that
         * something has gone wrong.  In the second case treating it as failure
         * to start is probably right, so we need a way to signal the first
         * rather than leaving the idle thread hanging around.  A flag in the
         * service description? */
        RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
        if (g_aServices[j].fShutdown)
        {
            VGSvcError("Service '%s' failed to start!\n", g_aServices[j].pDesc->pszName);
            rc = VERR_GENERAL_FAILURE;
        }
    }

    if (RT_SUCCESS(rc))
        VGSvcVerbose(1, "All services started.\n");
    else
    {
        VGSvcError("An error occcurred while the services!\n");
        VGSvcReportStatus(VBoxGuestFacilityStatus_Failed);
    }
    return rc;
}
Пример #14
0
/**
 * Stops and terminates the services.
 *
 * This should be called even when VBoxServiceStartServices fails so it can
 * clean up anything that we succeeded in starting.
 *
 * @remarks Also called from VBoxService-win.cpp, thus not static.
 */
int VGSvcStopServices(void)
{
    VGSvcReportStatus(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)
        {
            VGSvcVerbose(3, "Calling stop function for service '%s' ...\n", g_aServices[j].pDesc->pszName);
            g_aServices[j].pDesc->pfnStop();
        }

    VGSvcVerbose(3, "All stop functions for services called\n");

    /*
     * 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)
        {
            VGSvcVerbose(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 ... */
                VGSvcWinSetStopPendingStatus(i + j*32);
#endif
            }
            if (RT_FAILURE(rc2))
            {
                VGSvcError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc2);
                rc = rc2;
            }
        }
        VGSvcVerbose(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)
    {
        VGSvcVerbose(3, "Stopping the main thread...\n");
        int rc2 = RTSemEventSignal(g_hEvtWindowsService);
        AssertRC(rc2);
    }
#endif

    VGSvcVerbose(2, "Stopping services returning: %Rrc\n", rc);
    VGSvcReportStatus(RT_SUCCESS(rc) ? VBoxGuestFacilityStatus_Paused : VBoxGuestFacilityStatus_Failed);
    return rc;
}
Пример #15
0
/**
 * Try adjust the time using adjtime or similar.
 *
 * @returns true on success, false on failure.
 *
 * @param   pDrift          The time adjustment.
 */
static bool vgsvcTimeSyncAdjust(PCRTTIMESPEC pDrift)
{
#ifdef RT_OS_WINDOWS
/** @todo r=bird: g_hTokenProcess cannot be NULL here.
 *        vgsvcTimeSyncInit will fail and the service will not be started with
 *        it being NULL.  vgsvcTimeSyncInit OTOH will *NOT* be called until the
 *        service thread has terminated.  If anything
 *        else is the case, there is buggy code somewhere.*/
    if (g_hTokenProcess == NULL) /* Is the token already closed when shutting down? */
        return false;

    DWORD dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwWinTimeIncrement;
    BOOL  fWinTimeAdjustmentDisabled;
    if (GetSystemTimeAdjustment(&dwWinTimeAdjustment, &dwWinTimeIncrement, &fWinTimeAdjustmentDisabled))
    {
        DWORD dwDiffMax = g_dwWinTimeAdjustment * 0.50;
        DWORD dwDiffNew =   dwWinTimeAdjustment * 0.10;

        if (RTTimeSpecGetMilli(pDrift) > 0)
        {
            dwWinNewTimeAdjustment = dwWinTimeAdjustment + dwDiffNew;
            if (dwWinNewTimeAdjustment > (g_dwWinTimeAdjustment + dwDiffMax))
            {
                dwWinNewTimeAdjustment = g_dwWinTimeAdjustment + dwDiffMax;
                dwDiffNew = dwDiffMax;
            }
        }
        else
        {
            dwWinNewTimeAdjustment = dwWinTimeAdjustment - dwDiffNew;
            if (dwWinNewTimeAdjustment < (g_dwWinTimeAdjustment - dwDiffMax))
            {
                dwWinNewTimeAdjustment = g_dwWinTimeAdjustment - dwDiffMax;
                dwDiffNew = dwDiffMax;
            }
        }

        VGSvcVerbose(3, "vgsvcTimeSyncAdjust: Drift=%lldms\n", RTTimeSpecGetMilli(pDrift));
        VGSvcVerbose(3, "vgsvcTimeSyncAdjust: OrgTA=%ld, CurTA=%ld, NewTA=%ld, DiffNew=%ld, DiffMax=%ld\n",
                     g_dwWinTimeAdjustment, dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwDiffNew, dwDiffMax);
        if (SetSystemTimeAdjustment(dwWinNewTimeAdjustment, FALSE /* Periodic adjustments enabled. */))
        {
            g_cTimeSyncErrors = 0;
            return true;
        }

        if (g_cTimeSyncErrors++ < 10)
             VGSvcError("vgsvcTimeSyncAdjust: SetSystemTimeAdjustment failed, error=%u\n", GetLastError());
    }
    else if (g_cTimeSyncErrors++ < 10)
        VGSvcError("vgsvcTimeSyncAdjust: GetSystemTimeAdjustment failed, error=%ld\n", GetLastError());

#elif defined(RT_OS_OS2) || defined(RT_OS_HAIKU)
    /* No API for doing gradual time adjustments. */

#else /* PORTME */
    /*
     * Try use adjtime(), most unix-like systems have this.
     */
    struct timeval tv;
    RTTimeSpecGetTimeval(pDrift, &tv);
    if (adjtime(&tv, NULL) == 0)
    {
        if (g_cVerbosity >= 1)
            VGSvcVerbose(1, "vgsvcTimeSyncAdjust: adjtime by %RDtimespec\n", pDrift);
        g_cTimeSyncErrors = 0;
        return true;
    }
#endif

    /* failed */
    return false;
}
Пример #16
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnInit}
 */
static DECLCALLBACK(int) vgsvcTimeSyncInit(void)
{
    /*
     * If not specified, find the right interval default.
     * Then create the event sem to block on.
     */
    if (!g_TimeSyncInterval)
        g_TimeSyncInterval = g_DefaultInterval * 1000;
    if (!g_TimeSyncInterval)
        g_TimeSyncInterval = 10 * 1000;

    VbglR3GetSessionId(&g_idTimeSyncSession);
    /* The status code is ignored as this information is not available with VBox < 3.2.10. */

    int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
    AssertRC(rc);
#ifdef RT_OS_WINDOWS
    if (RT_SUCCESS(rc))
    {
        /*
         * Adjust privileges of this process so we can make system time adjustments.
         */
        if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &g_hTokenProcess))
        {
            TOKEN_PRIVILEGES tkPriv;
            RT_ZERO(tkPriv);
            tkPriv.PrivilegeCount = 1;
            tkPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            if (LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkPriv.Privileges[0].Luid))
            {
                DWORD cbRet = sizeof(g_TkOldPrivileges);
                if (AdjustTokenPrivileges(g_hTokenProcess, FALSE, &tkPriv, sizeof(TOKEN_PRIVILEGES), &g_TkOldPrivileges, &cbRet))
                    rc = VINF_SUCCESS;
                else
                {
                    DWORD dwErr = GetLastError();
                    rc = RTErrConvertFromWin32(dwErr);
                    VGSvcError("vgsvcTimeSyncInit: Adjusting token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
                               dwErr, rc);
                }
            }
            else
            {
                DWORD dwErr = GetLastError();
                rc = RTErrConvertFromWin32(dwErr);
                VGSvcError("vgsvcTimeSyncInit: Looking up token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
                           dwErr, rc);
            }
            if (RT_FAILURE(rc))
            {
                CloseHandle(g_hTokenProcess);
                g_hTokenProcess = NULL;
            }
        }
        else
        {
            DWORD dwErr = GetLastError();
            rc = RTErrConvertFromWin32(dwErr);
            VGSvcError("vgsvcTimeSyncInit: Opening process token (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
                       dwErr, rc);
            g_hTokenProcess = NULL;
        }
    }

    if (GetSystemTimeAdjustment(&g_dwWinTimeAdjustment, &g_dwWinTimeIncrement, &g_bWinTimeAdjustmentDisabled))
        VGSvcVerbose(3, "vgsvcTimeSyncInit: Initially %ld (100ns) units per %ld (100 ns) units interval, disabled=%d\n",
                     g_dwWinTimeAdjustment, g_dwWinTimeIncrement, g_bWinTimeAdjustmentDisabled ? 1 : 0);
    else
    {
        DWORD dwErr = GetLastError();
        rc = RTErrConvertFromWin32(dwErr);
        VGSvcError("vgsvcTimeSyncInit: Could not get time adjustment values! Last error: %ld!\n", dwErr);
    }
#endif /* RT_OS_WINDOWS */

    return rc;
}
Пример #17
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnPreInit}
 */
static DECLCALLBACK(int) vgsvcTimeSyncPreInit(void)
{
#ifdef VBOX_WITH_GUEST_PROPS
    /** @todo Merge this function with vgsvcTimeSyncOption() to generalize
     *        the "command line args override guest property values" behavior. */

    /*
     * Read the service options from the VM's guest properties.
     * Note that these options can be overridden by the command line options later.
     */
    uint32_t uGuestPropSvcClientID;
    int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
    if (RT_FAILURE(rc))
    {
        if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
        {
            VGSvcVerbose(0, "VMInfo: Guest property service is not available, skipping\n");
            rc = VINF_SUCCESS;
        }
        else
            VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
    }
    else
    {
        rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-interval",
                                 &g_TimeSyncInterval, 50, UINT32_MAX - 1);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-min-adjust",
                                     &g_TimeSyncMinAdjust, 0, 3600000);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-latency-factor",
                                     &g_TimeSyncLatencyFactor, 1, 1024);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-max-latency",
                                     &g_TimeSyncMaxLatency, 1, 3600000);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
                                     &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000 /* a week */);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
        {
            char *pszValue;
            rc = VGSvcReadProp(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-start",
                               &pszValue, NULL /* ppszFlags */, NULL /* puTimestamp */);
            if (RT_SUCCESS(rc))
            {
                g_fTimeSyncSetNext = true;
                RTStrFree(pszValue);
            }
        }
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
        {
            uint32_t value;
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore",
                                     &value, 1, 1);
            if (RT_SUCCESS(rc))
                g_fTimeSyncSetOnRestore = !!value;
        }

        VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
    }

    if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
        rc = VINF_SUCCESS;
    return rc;
#else
    /* Nothing to do here yet. */
    return VINF_SUCCESS;
#endif
}
Пример #18
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnWorker}
 */
DECLCALLBACK(int) vgsvcVMStatsWorker(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))
    {
        VGSvcVerbose(3, "vgsvcVMStatsWorker: 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(&g_VMStat.cMsStatInterval);

        if (g_VMStat.cMsStatInterval)
        {
            vgsvcVMStatsReport();
            cWaitMillies = g_VMStat.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))
        {
            VGSvcError("vgsvcVMStatsWorker: 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))
        VGSvcVerbose(3, "vgsvcVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);

    VGSvcVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
    return 0;
}
Пример #19
0
/**
 * Gathers VM statistics and reports them to the host.
 */
static void vgsvcVMStatsReport(void)
{
#if defined(RT_OS_WINDOWS)
    Assert(g_VMStat.pfnGlobalMemoryStatusEx && g_VMStat.pfnNtQuerySystemInformation);
    if (   !g_VMStat.pfnGlobalMemoryStatusEx
        || !g_VMStat.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 */
    SYSTEM_INFO systemInfo;
    GetSystemInfo(&systemInfo);

    MEMORYSTATUSEX memStatus;
    memStatus.dwLength = sizeof(memStatus);
    g_VMStat.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    = VGSvcBalloonQueryPages(_4K);
    req.guestStats.u32StatCaps         |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
# else
    req.guestStats.u32PhysMemBalloon    = 0;
# endif

    if (g_VMStat.pfnGetPerformanceInfo)
    {
        PERFORMANCE_INFORMATION perfInfo;

        if (g_VMStat.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
            VGSvcVerbose(3, "vgsvcVMStatsReport: GetPerformanceInfo failed with %d\n", GetLastError());
    }

    /* Query CPU load information */
    uint32_t cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
    PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
    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 */
    bool  fCpuInfoAvail = false;
    DWORD cbReturned;
    NTSTATUS rcNt = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
    if (    !rcNt
        &&  cbReturned == cbStruct)
    {
        for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++)
        {
            if (i >= VMM_MAX_CPU_COUNT)
            {
                VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPUs %u..%u\n", i, systemInfo.dwNumberOfProcessors);
                break;
            }

            if (g_VMStat.au64LastCpuLoad_Kernel[i] == 0)
            {
                /* first time */
                g_VMStat.au64LastCpuLoad_Idle[i]    = pProcInfo[i].IdleTime.QuadPart;
                g_VMStat.au64LastCpuLoad_Kernel[i]  = pProcInfo[i].KernelTime.QuadPart;
                g_VMStat.au64LastCpuLoad_User[i]    = pProcInfo[i].UserTime.QuadPart;

                Sleep(250);

                rcNt = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
                Assert(!rcNt);
            }

            uint64_t deltaIdle    = (pProcInfo[i].IdleTime.QuadPart   - g_VMStat.au64LastCpuLoad_Idle[i]);
            uint64_t deltaKernel  = (pProcInfo[i].KernelTime.QuadPart - g_VMStat.au64LastCpuLoad_Kernel[i]);
            uint64_t deltaUser    = (pProcInfo[i].UserTime.QuadPart   - g_VMStat.au64LastCpuLoad_User[i]);
            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;
            req.guestStats.u32CpuId          = i;
            fCpuInfoAvail = true;
            int rc = VbglR3StatReport(&req);
            if (RT_SUCCESS(rc))
                VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", i);
            else
                VGSvcVerbose(3, "vgsvcVMStatsReport: VbglR3StatReport failed with rc=%Rrc\n", rc);

            g_VMStat.au64LastCpuLoad_Idle[i]    = pProcInfo[i].IdleTime.QuadPart;
            g_VMStat.au64LastCpuLoad_Kernel[i]  = pProcInfo[i].KernelTime.QuadPart;
            g_VMStat.au64LastCpuLoad_User[i]    = pProcInfo[i].UserTime.QuadPart;
        }
    }
    RTMemFree(pProcInfo);

    if (!fCpuInfoAvail)
    {
        VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
        int rc = VbglR3StatReport(&req);
        if (RT_SUCCESS(rc))
            VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
        else
            VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
    }

#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
        VGSvcVerbose(3, "vgsvcVMStatsReport: 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  = VGSvcBalloonQueryPages(_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   - g_VMStat.au64LastCpuLoad_Idle[u32CpuId];
                    uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[u32CpuId];
                    uint64_t u64DeltaUser   = u64User   - g_VMStat.au64LastCpuLoad_User[u32CpuId];
                    uint64_t u64DeltaNice   = u64Nice   - g_VMStat.au64LastCpuLoad_Nice[u32CpuId];

                    uint64_t u64DeltaAll    = u64DeltaIdle
                                            + u64DeltaSystem
                                            + u64DeltaUser
                                            + u64DeltaNice;
                    if (u64DeltaAll == 0) /* Prevent division through zero. */
                        u64DeltaAll = 1;

                    g_VMStat.au64LastCpuLoad_Idle[u32CpuId]   = u64Idle;
                    g_VMStat.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
                    g_VMStat.au64LastCpuLoad_User[u32CpuId]   = u64User;
                    g_VMStat.au64LastCpuLoad_Nice[u32CpuId]   = u64Nice;

                    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;
                    req.guestStats.u32CpuId = u32CpuId;
                    fCpuInfoAvail = true;
                    rc = VbglR3StatReport(&req);
                    if (RT_SUCCESS(rc))
                        VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", u32CpuId);
                    else
                        VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
                }
                else
                    VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", u32CpuId);
            }
        }
        RTStrmClose(pStrm);
    }
    if (!fCpuInfoAvail)
    {
        VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
        rc = VbglR3StatReport(&req);
        if (RT_SUCCESS(rc))
            VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
        else
            VGSvcVerbose(3, "vgsvcVMStatsReport: 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  = VGSvcBalloonQueryPages(_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;

                if (cCPUs < VMM_MAX_CPU_COUNT)
                {
                    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   - g_VMStat.au64LastCpuLoad_Idle[cCPUs];
                    uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[cCPUs];
                    uint64_t u64DeltaUser   = u64User   - g_VMStat.au64LastCpuLoad_User[cCPUs];

                    uint64_t u64DeltaAll    = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
                    if (u64DeltaAll == 0) /* Prevent division through zero. */
                        u64DeltaAll = 1;

                    g_VMStat.au64LastCpuLoad_Idle[cCPUs]   = u64Idle;
                    g_VMStat.au64LastCpuLoad_Kernel[cCPUs] = u64System;
                    g_VMStat.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))
                        VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", cCPUs);
                    else
                        VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
                    cCPUs++;
                }
                else
                    VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", cCPUs);
            }
        }

        /*
         * Report whatever statistics were collected.
         */
        if (!fCpuInfoAvail)
        {
            VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
            rc = VbglR3StatReport(&req);
            if (RT_SUCCESS(rc))
                VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
            else
                VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
        }

        kstat_close(pStatKern);
    }

#else
    /** @todo implement for other platforms. */

#endif
}
Пример #20
0
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 RT_OS_WINDOWS
    VGSvcWinResolveApis();
#endif
#ifdef DEBUG
    rc = RTCritSectInit(&g_csLog);
    AssertRC(rc);
#endif

#ifdef VBOX_WITH_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 (VGSvcToolboxMain(argc, argv, &rcExit))
        return rcExit;
#endif

    bool fUserSession = false;
#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
    /*
     * Check if we're the specially spawned VBoxService.exe process that
     * handles a guest control session.
     */
    if (   argc >= 2
        && !RTStrICmp(argv[1], "guestsession"))
        fUserSession = true;
#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.
     */
    if (fUserSession)
        rc = VbglR3InitUser();
    else
        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 statically linked executable.
     */
    if (   argc == 2
        && !RTStrICmp(argv[1], "pagefusion"))
        return VGSvcPageSharingWorkerChild();
#endif

#ifdef VBOX_WITH_VBOXSERVICE_CONTROL
    /*
     * Check if we're the specially spawned VBoxService.exe process that
     * handles a guest control session.
     */
    if (fUserSession)
        return VGSvcGstCtrlSessionSpawnInit(argc, argv);
#endif

    /*
     * 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("pidfile"))
                psz = "p";
            else if (MATCHES("daemonized"))
            {
                fDaemonized = true;
                continue;
            }
            else
            {
                bool fFound = false;

                if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-")))
                    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, RT_STR_TUPLE("disable-")))
                    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, RT_STR_TUPLE("only-")))
                    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 = vgsvcLazyPreInit();
                    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 = VGSvcArgUInt32(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 vgsvcUsage();

#ifdef RT_OS_WINDOWS
                case 'r':
                    return VGSvcWinInstall();

                case 'u':
                    return VGSvcWinUninstall();
#endif

                case 'l':
                {
                    rc = vgsvcArgString(argc, argv, psz + 1, &i,
                                              g_szLogFile, sizeof(g_szLogFile));
                    if (rc)
                        return rc;
                    psz = NULL;
                    break;
                }

                case 'p':
                {
                    rc = vgsvcArgString(argc, argv, psz + 1, &i,
                                              g_szPidFile, sizeof(g_szPidFile));
                    if (rc)
                        return rc;
                    psz = NULL;
                    break;
                }

                default:
                {
                    rcExit = vgsvcLazyPreInit();
                    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 (vgsvcCountEnabledServices() == 0)
        return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n");

    rc = VGSvcLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
    if (RT_FAILURE(rc))
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log '%s', rc=%Rrc\n",
                              g_szLogFile[0] ? g_szLogFile : "<None>", rc);

    /* Call pre-init if we didn't do it already. */
    rcExit = vgsvcLazyPreInit();
    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)
        {
            VGSvcError("%s is already running! Terminating.\n", g_pszProgName);
            return RTEXITCODE_FAILURE;
        }

        VGSvcError("CreateMutex failed with last error %u! Terminating.\n", GetLastError());
        return RTEXITCODE_FAILURE;
    }

#else  /* !RT_OS_WINDOWS */
    /** @todo Add PID file creation here? */
#endif /* !RT_OS_WINDOWS */

    VGSvcVerbose(0, "%s r%s started. Verbose level = %d\n", RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity);

    /*
     * Daemonize if requested.
     */
    if (fDaemonize && !fDaemonized)
    {
#ifdef RT_OS_WINDOWS
        VGSvcVerbose(2, "Starting service dispatcher ...\n");
        rcExit = VGSvcWinEnterCtrlDispatcher();
#else
        VGSvcVerbose(1, "Daemonizing...\n");
        rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */,
                             false /* fRespawn */, NULL /* pcRespawn */);
        if (RT_FAILURE(rc))
            return VGSvcError("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 /** @todo r=bird: What's RT_OS_NT4??? */
        /* Install console control handler. */
        if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)vgsvcWinConsoleControlHandler, TRUE /* Add handler */))
        {
            VGSvcError("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 = VGSvcStartServices();
        RTFILE hPidFile = NIL_RTFILE;
        if (RT_SUCCESS(rc))
            if (g_szPidFile[0])
                rc = VbglR3PidFile(g_szPidFile, &hPidFile);
        rcExit = RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
        if (RT_SUCCESS(rc))
            VGSvcMainWait();
        if (g_szPidFile[0] && hPidFile != NIL_RTFILE)
            VbglR3ClosePidFile(g_szPidFile, hPidFile);
#ifdef RT_OS_WINDOWS
# ifndef RT_OS_NT4
        /* Uninstall console control handler. */
        if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
        {
            VGSvcError("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. */
        VGSvcStopServices();
#endif /* RT_OS_WINDOWS */
    }
    VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);

#ifdef RT_OS_WINDOWS
    /*
     * Cleanup mutex.
     */
    CloseHandle(hMutexAppRunning);
#endif

    VGSvcVerbose(0, "Ended.\n");

#ifdef DEBUG
    RTCritSectDelete(&g_csLog);
    //RTMemTrackerDumpAllToStdOut();
#endif

    VGSvcLogDestroy();

    return rcExit;
}
Пример #21
0
/**
 * Installs the service.
 */
RTEXITCODE VGSvcWinInstall(void)
{
    VGSvcVerbose(1, "Installing service ...\n");

    TCHAR imagePath[MAX_PATH] = { 0 };
    GetModuleFileName(NULL, imagePath, sizeof(imagePath));

    SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCManager == NULL)
    {
        VGSvcError("Could not open SCM! Error: %ld\n", GetLastError());
        return RTEXITCODE_FAILURE;
    }

    RTEXITCODE rc       = RTEXITCODE_SUCCESS;
    SC_HANDLE  hService = CreateService(hSCManager,
                                        VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
                                        SERVICE_ALL_ACCESS,
                                        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
                                        SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
                                        imagePath, NULL, NULL, NULL, NULL, NULL);
    if (hService != NULL)
        VGSvcVerbose(0, "Service successfully installed!\n");
    else
    {
        DWORD dwErr = GetLastError();
        switch (dwErr)
        {
            case ERROR_SERVICE_EXISTS:
                VGSvcVerbose(1, "Service already exists, just updating the service config.\n");
                hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
                if (hService)
                {
                    if (ChangeServiceConfig (hService,
                                             SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
                                             SERVICE_DEMAND_START,
                                             SERVICE_ERROR_NORMAL,
                                             imagePath,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             VBOXSERVICE_FRIENDLY_NAME))
                        VGSvcVerbose(1, "The service config has been successfully updated.\n");
                    else
                        rc = VGSvcError("Could not change service config! Error: %ld\n", GetLastError());
                }
                else
                    rc = VGSvcError("Could not open service! Error: %ld\n", GetLastError());
                break;

            default:
                rc = VGSvcError("Could not create service! Error: %ld\n", dwErr);
                break;
        }
    }

    if (rc == RTEXITCODE_SUCCESS)
        rc = vgsvcWinSetDesc(hService);

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return rc;
}
Пример #22
0
/**
 * Callback registered by RegisterServiceCtrlHandler on NT4 and earlier.
 */
static VOID WINAPI vgsvcWinCtrlHandlerNt4(DWORD dwControl)
{
    VGSvcVerbose(2, "Control handler (NT4): dwControl=%#x\n", dwControl);
    vgsvcWinCtrlHandlerCommon(dwControl);
}