VirtualBox

Ignore:
Timestamp:
Jan 30, 2025 10:01:07 AM (3 months ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
167248
Message:

Windows driver installation: Implemented APIs for controlling (driver SCM) services. bugref:10762

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/GuestHost/installation/VBoxWinDrvInst.cpp

    r107973 r107985  
    5151#include <iprt/string.h>
    5252#include <iprt/system.h>
     53#include <iprt/thread.h> /* For RTThreadSleep(). */
    5354#include <iprt/utf16.h>
    5455
     
    22842285}
    22852286
     2287/**
     2288 * Controls a Windows service, internal version.
     2289 *
     2290 * @returns VBox status code.
     2291 * @param   hDrvInst            Windows driver installer handle to use.
     2292 * @param   pszService          Name of service to control.
     2293 * @param   enmFn               Service control function to use.
     2294 *                              VBOXWINDRVSVCFN_RESTART is not implemented and must be composed of
     2295 *                              VBOXWINDRVSVCFN_START + VBOXWINDRVSVCFN_STOP by the caller.
     2296 * @param   fFlags              Service control flags (of type VBOXWINDRVSVCFN_F_XXX) to use.
     2297 * @param   msTimeout           Timeout (in ms) to use. Only being used if VBOXWINDRVSVCFN_F_WAIT is specified in \a fFlags.
     2298 */
     2299static int vbooxWinDrvInstControlServiceEx(PVBOXWINDRVINSTINTERNAL pCtx,
     2300                                           const char *pszService, VBOXWINDRVSVCFN enmFn, uint32_t fFlags, RTMSINTERVAL msTimeout)
     2301{
     2302    AssertPtrReturn(pszService, VERR_INVALID_POINTER);
     2303    AssertReturn(!(fFlags & ~VBOXWINDRVSVCFN_F_VALID_MASK), VERR_INVALID_PARAMETER);
     2304    AssertReturn(enmFn > VBOXWINDRVSVCFN_INVALID && enmFn < VBOXWINDRVSVCFN_END, VERR_INVALID_PARAMETER);
     2305    AssertReturn(msTimeout == RT_INDEFINITE_WAIT || msTimeout, VERR_INVALID_PARAMETER);
     2306
     2307    PRTUTF16 pwszService;
     2308    int rc = RTStrToUtf16(pszService, &pwszService);
     2309    if (RT_FAILURE(rc))
     2310        return rc;
     2311
     2312    SC_HANDLE hSvc = NULL;
     2313    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
     2314    if (hSCM != NULL)
     2315    {
     2316        hSvc = OpenServiceW(hSCM, pwszService, SERVICE_ALL_ACCESS | SERVICE_QUERY_STATUS);
     2317        if (hSvc == NULL)
     2318        {
     2319            rc = RTErrConvertFromWin32(GetLastError());
     2320            if (rc == VERR_NOT_FOUND)
     2321                vboxWinDrvInstLogError(pCtx, "Service '%s' not found", pszService);
     2322            else
     2323                rc = vboxWinDrvInstLogLastError(pCtx, "Opening service '%s' failed", pszService);
     2324        }
     2325    }
     2326    else
     2327        rc = vboxWinDrvInstLogLastError(pCtx, "Opening Service Control Manager (SCM) failed");
     2328
     2329    if (RT_FAILURE(rc))
     2330    {
     2331        RTUtf16Free(pwszService);
     2332        if (hSvc)
     2333            CloseServiceHandle(hSvc);
     2334        if (hSCM)
     2335            CloseServiceHandle(hSCM);
     2336        return rc;
     2337    }
     2338
     2339    SERVICE_STATUS_PROCESS enmSvcSts;
     2340
     2341    switch (enmFn)
     2342    {
     2343        case VBOXWINDRVSVCFN_START:
     2344        {
     2345            if (!StartService(hSvc, 0, NULL))
     2346            {
     2347                if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
     2348                    break;
     2349
     2350                /** @todo Also handle disabled services here? */
     2351
     2352                rc = vboxWinDrvInstLogLastError(pCtx, "Starting service '%s' failed", pszService);
     2353            }
     2354            else
     2355                vboxWinDrvInstLogInfo(pCtx, "Starting service '%s' ...", pszService);
     2356            break;
     2357        }
     2358
     2359        case VBOXWINDRVSVCFN_STOP:
     2360        {
     2361            if (!ControlService(hSvc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&enmSvcSts))
     2362            {
     2363                DWORD const dwErr = GetLastError();
     2364
     2365                /* A not active or disabled service is not an error, so just skip. */
     2366                if (   dwErr == ERROR_SERVICE_DISABLED
     2367                    || dwErr == ERROR_SERVICE_NOT_ACTIVE)
     2368                    break;
     2369
     2370                rc = vboxWinDrvInstLogLastError(pCtx, "Stopping service '%s' failed", pszService);
     2371            }
     2372            else
     2373                vboxWinDrvInstLogInfo(pCtx, "Stopping service '%s' ...", pszService);
     2374            break;
     2375        }
     2376
     2377        default:
     2378            AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
     2379            break;
     2380    }
     2381
     2382    if (   RT_SUCCESS(rc)
     2383        && (fFlags & VBOXWINDRVSVCFN_F_WAIT))
     2384    {
     2385        uint64_t const msStartTS = RTTimeMilliTS();
     2386
     2387        rc = VERR_NO_CHANGE; /* No change yet. */
     2388
     2389        vboxWinDrvInstLogInfo(pCtx, "Waiting for status change of service '%s' ...", pszService);
     2390        for (;;)
     2391        {
     2392            DWORD dwBytes;
     2393            if (!QueryServiceStatusEx(hSvc,
     2394                                      SC_STATUS_PROCESS_INFO,
     2395                                      (LPBYTE)&enmSvcSts,
     2396                                      sizeof(SERVICE_STATUS_PROCESS),
     2397                                      &dwBytes))
     2398            {
     2399                rc = vboxWinDrvInstLogLastError(pCtx, "Failed to query service status");
     2400                break;
     2401            }
     2402
     2403            if ((RTTimeMilliTS() - msStartTS) % RT_MS_1SEC == 0) /* Don't spam. */
     2404                vboxWinDrvInstLogVerbose(pCtx, 3, "Service '%s' status is %#x: %u",
     2405                                         pszService, enmSvcSts.dwCurrentState, (RTTimeMilliTS() - msStartTS) % 100 == 0);
     2406
     2407            switch (enmSvcSts.dwCurrentState)
     2408            {
     2409                case SERVICE_STOP_PENDING:
     2410                case SERVICE_START_PENDING:
     2411                    RTThreadSleep(100); /* Wait a bit before retrying. */
     2412                    break;
     2413
     2414                case SERVICE_RUNNING:
     2415                {
     2416                    if (enmFn == VBOXWINDRVSVCFN_START)
     2417                        rc = VINF_SUCCESS;
     2418                    break;
     2419                }
     2420
     2421                case SERVICE_STOPPED:
     2422                {
     2423                    if (enmFn == VBOXWINDRVSVCFN_START)
     2424                    {
     2425                        vboxWinDrvInstLogError(pCtx, "Service '%s' stopped unexpectedly", pszService);
     2426                        rc = VERR_INVALID_STATE;
     2427                    }
     2428                    else
     2429                        rc = VINF_SUCCESS;
     2430                    break;
     2431                }
     2432
     2433                default:
     2434                {
     2435                    vboxWinDrvInstLogError(pCtx, "Service '%s' reported an unexpected state (%#x)",
     2436                                           pszService, enmSvcSts.dwCurrentState);
     2437                    rc = VERR_INVALID_STATE;
     2438                }
     2439            }
     2440
     2441            if (   RT_FAILURE(rc)
     2442                && rc != VERR_NO_CHANGE)
     2443                break;
     2444
     2445            if (RT_SUCCESS(rc))
     2446                break;
     2447
     2448            if (RTTimeMilliTS() - msStartTS >= msTimeout)
     2449            {
     2450                vboxWinDrvInstLogError(pCtx, "Waiting for service '%s' timed out (%ums)", pszService, msTimeout);
     2451                rc = VERR_TIMEOUT;
     2452                break;
     2453            }
     2454        }
     2455
     2456        if (RT_SUCCESS(rc))
     2457            vboxWinDrvInstLogInfo(pCtx, "Service '%s' successfully %s",
     2458                                  pszService, enmFn == VBOXWINDRVSVCFN_START ? "started" : "stopped");
     2459    }
     2460    else
     2461        vboxWinDrvInstLogVerbose(pCtx, 1, "Service '%s' was %s asynchronously",
     2462                                 pszService, enmFn == VBOXWINDRVSVCFN_START ? "started" : "stopped");
     2463
     2464    RTUtf16Free(pwszService);
     2465    CloseServiceHandle(hSvc);
     2466    CloseServiceHandle(hSCM);
     2467    return rc;
     2468}
     2469
     2470/**
     2471 * Controls a Windows service, extended version.
     2472 *
     2473 * @returns VBox status code.
     2474 * @param   hDrvInst            Windows driver installer handle to use.
     2475 * @param   pszService          Name of service to control.
     2476 * @param   enmFn               Service control function to use.
     2477 * @param   fFlags              Service control flags (of type VBOXWINDRVSVCFN_F_XXX) to use.
     2478 * @param   msTimeout           Timeout (in ms) to use. Only being used if VBOXWINDRVSVCFN_F_WAIT is specified in \a fFlags.
     2479 */
     2480int VBooxWinDrvInstControlServiceEx(VBOXWINDRVINST hDrvInst,
     2481                                    const char *pszService, VBOXWINDRVSVCFN enmFn, uint32_t fFlags, RTMSINTERVAL msTimeout)
     2482{
     2483    PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
     2484    VBOXWINDRVINST_VALID_RETURN(pCtx);
     2485
     2486#define CONTROL_SERVICE(a_Fn) \
     2487    vbooxWinDrvInstControlServiceEx(pCtx, pszService, a_Fn, fFlags, msTimeout);
     2488
     2489    int rc;
     2490    if (enmFn == VBOXWINDRVSVCFN_RESTART)
     2491    {
     2492        rc = CONTROL_SERVICE(VBOXWINDRVSVCFN_STOP);
     2493        if (RT_SUCCESS(rc))
     2494            rc = CONTROL_SERVICE(VBOXWINDRVSVCFN_START);
     2495    }
     2496    else
     2497        rc = CONTROL_SERVICE(enmFn);
     2498
     2499#undef CONTROL_SERVICE
     2500    return rc;
     2501}
     2502
     2503/**
     2504 * Controls a Windows service.
     2505 *
     2506 * @returns VBox status code.
     2507 * @param   hDrvInst            Windows driver installer handle to use.
     2508 * @param   pszService          Name of service to control.
     2509 * @param   enmFn               Service control function to use.
     2510 *
     2511 * @note    Function waits 30s for the service to reach the desired control function.
     2512 *          Use VBooxWinDrvInstControlServiceEx() for more flexibility.
     2513 */
     2514int VBoxWinDrvInstControlService(VBOXWINDRVINST hDrvInst, const char *pszService, VBOXWINDRVSVCFN enmFn)
     2515{
     2516    PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
     2517    VBOXWINDRVINST_VALID_RETURN(pCtx);
     2518
     2519    return VBooxWinDrvInstControlServiceEx(pCtx, pszService, enmFn, VBOXWINDRVSVCFN_F_WAIT, RT_MS_30SEC);
     2520}
     2521
    22862522#ifdef TESTCASE
    22872523/**
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette