VirtualBox

Ignore:
Timestamp:
Dec 9, 2018 7:25:00 PM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
127323
Message:

VBoxSDS: Implemented (disabled) monitoring client (VBoxSVC) process termination to bypass the 5 min denalty for abended clients. Really handy for development. bugref:3300

Location:
trunk/src/VBox/Main/src-global/win
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-global/win/VBoxSDS.cpp

    r76067 r76082  
    486486    /** Part of the shutdown monitoring logic. */
    487487    bool volatile m_fActivity;
     488#ifdef WITH_WATCHER
     489    /** Part of the shutdown monitoring logic. */
     490    bool volatile m_fHasClients;
     491#endif
    488492    /** Auto reset event for communicating with the shutdown thread.
    489493     * This is created by startMonitor(). */
     
    497501     * Can be decreased at system shutdown phase. */
    498502    volatile uint32_t m_cMsShutdownTimeOut;
     503
     504    /** The service module instance. */
     505    static CComServiceModule * volatile s_pInstance;
    499506
    500507public:
     
    517524        , m_fComInitialized(false)
    518525        , m_fActivity(false)
     526#ifdef WITH_WATCHER
     527        , m_fHasClients(false)
     528#endif
    519529        , m_cMsShutdownTimeOut(cMsShutdownTimeout)
    520530        , m_hEventShutdown(INVALID_HANDLE_VALUE)
     
    532542        if (SUCCEEDED(hrc))
    533543        {
    534 
    535544            // copy service name
    536545            int rc = ::RTUtf16Copy(m_wszServiceName, sizeof(m_wszServiceName), p_wszServiceName);
     
    556565        if (cLocks == 0)
    557566        {
    558             m_fActivity = true;
     567            ::ASMAtomicWriteBool(&m_fActivity, true);
    559568            ::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero
    560569        }
     
    562571    }
    563572
     573    /**
     574     * Overload CAtlModule::Lock to untrigger automatic shutdown.
     575     */
     576    virtual LONG Lock() throw()
     577    {
     578        LONG cLocks = ATL::CComModule::Lock();
     579        LogFunc(("Lock() called. Ref=%d\n", cLocks));
     580#ifdef WITH_WATCHER
     581        ::ASMAtomicWriteBool(&m_fActivity, true);
     582        ::SetEvent(m_hEventShutdown); /* reset the timeout interval */
     583#endif
     584        return cLocks;
     585    }
     586
     587#ifdef WITH_WATCHER
     588
     589    /** Called to start the automatic shutdown behaviour based on client count
     590     *  rather than lock count.. */
     591    void notifyZeroClientConnections()
     592    {
     593        m_fHasClients = false;
     594        ::ASMAtomicWriteBool(&m_fActivity, true);
     595        ::SetEvent(m_hEventShutdown);
     596    }
     597
     598    /** Called to make sure automatic shutdown is cancelled. */
     599    void notifyHasClientConnections()
     600    {
     601        m_fHasClients = true;
     602        ::ASMAtomicWriteBool(&m_fActivity, true);
     603    }
     604
     605#endif /* WITH_WATCHER */
     606
    564607protected:
    565608
    566609    bool hasActiveConnection()
    567610    {
     611#ifdef WITH_WATCHER
     612        return m_fActivity || (m_fHasClients && GetLockCount() > 0);
     613#else
    568614        return m_fActivity || GetLockCount() > 0;
     615#endif
    569616    }
    570617
     
    584631            if (!hasActiveConnection()) /* if no activity let's really bail */
    585632            {
    586                 /*
    587                  * Disable log rotation at this point, worst case a log file
    588                  * becomes slightly bigger than it should. Avoids quirks with
    589                  * log rotation: there might be another API service process
    590                  * running at this point which would rotate the logs concurrently,
    591                  * creating a mess.
    592                  */
     633                ::CoSuspendClassObjects();
     634
     635                /* Disable log rotation at this point, worst case a log file becomes slightly
     636                   bigger than it should.  Avoids quirks with log rotation:  There might be
     637                   another API service process running at this point which would rotate the
     638                   logs concurrently, creating a mess. */
    593639                PRTLOGGER pReleaseLogger = ::RTLogRelGetDefaultInstance();
    594640                if (pReleaseLogger)
     
    606652                    }
    607653                }
    608                 ::CoSuspendClassObjects();
     654
    609655                if (!hasActiveConnection())
    610656                    break;
     
    686732    }
    687733};
     734
     735/*static*/ CComServiceModule * volatile CComServiceModule::s_pInstance = NULL;
     736
     737
     738#ifdef WITH_WATCHER
     739/**
     740 * Go-between for CComServiceModule and VirtualBoxSDS.
     741 */
     742void VBoxSDSNotifyClientCount(uint32_t cClients)
     743{
     744    CComServiceModule *pInstance = CComServiceModule::s_pInstance;
     745    if (pInstance)
     746    {
     747        if (cClients == 0)
     748            pInstance->notifyZeroClientConnections();
     749        else
     750            pInstance->notifyHasClientConnections();
     751    }
     752}
     753#endif
    688754
    689755
     
    9491015                     * Run service.
    9501016                     */
     1017                    CComServiceModule::s_pInstance = pServiceModule;
    9511018                    hrcExit = pServiceModule->startService(nShowCmd);
    9521019                    LogRelFunc(("VBoxSDS: Calling _ServiceModule.RevokeClassObjects()...\n"));
     1020                    CComServiceModule::s_pInstance = NULL;
    9531021                    pServiceModule->RevokeClassObjects();
    9541022                }
  • trunk/src/VBox/Main/src-global/win/VirtualBoxSDSImpl.cpp

    r76072 r76082  
    2929#include <iprt/asm.h>
    3030#include <iprt/critsect.h>
     31#include <iprt/mem.h>
     32#include <iprt/system.h>
    3133
    3234#include <rpcasync.h>
     
    5355     * This is NULL if not set. */
    5456    ComPtr<IVBoxSVCRegistration>    m_ptrTheChosenOne;
     57    /** The PID of the chosen one. */
     58    RTPROCESS                       m_pidTheChosenOne;
     59    /** The current watcher thread index, UINT32_MAX if not watched. */
     60    uint32_t                        m_iWatcher;
     61    /** The chosen one revision number.
     62     * This is used to detect races while waiting for a full watcher queue.  */
     63    uint32_t volatile               m_iTheChosenOneRevision;
    5564private:
    5665    /** Reference count to make destruction safe wrt hung callers.
     
    6372public:
    6473    VBoxSDSPerUserData(com::Utf8Str const &a_rStrUserSid, com::Utf8Str const &a_rStrUsername)
    65         : m_strUserSid(a_rStrUserSid), m_strUsername(a_rStrUsername), m_cRefs(1)
     74        : m_strUserSid(a_rStrUserSid)
     75        , m_strUsername(a_rStrUsername)
     76#ifdef WITH_WATCHER
     77        , m_iWatcher(UINT32_MAX)
     78        , m_iTheChosenOneRevision(0)
     79#endif
     80        , m_pidTheChosenOne(NIL_RTPROCESS)
     81        , m_cRefs(1)
    6682    {
    6783        RTCritSectInit(&m_Lock);
     
    7187    {
    7288        RTCritSectDelete(&m_Lock);
     89        i_unchooseTheOne(true /*fIrregular*/);
    7390    }
    7491
     
    98115        RTCritSectLeave(&m_Lock);
    99116    }
     117
     118    /** Reset the chosen one. */
     119    void i_unchooseTheOne(bool fIrregular)
     120    {
     121        if (m_ptrTheChosenOne.isNotNull())
     122        {
     123            if (!fIrregular)
     124                m_ptrTheChosenOne.setNull();
     125            else
     126            {
     127                LogRel(("i_unchooseTheOne: Irregular release ... (pid=%d (%#x) user=%s sid=%s)\n",
     128                        m_pidTheChosenOne, m_pidTheChosenOne, m_strUsername.c_str(), m_strUserSid.c_str()));
     129                m_ptrTheChosenOne.setNull();
     130                LogRel(("i_unchooseTheOne: ... done.\n"));
     131            }
     132        }
     133        m_pidTheChosenOne = NIL_RTPROCESS;
     134    }
     135
    100136};
    101137
    102138
    103139
    104 
    105 // constructor / destructor
    106 /////////////////////////////////////////////////////////////////////////////
    107 
    108 DEFINE_EMPTY_CTOR_DTOR(VirtualBoxSDS)
     140/*********************************************************************************************************************************
     141*   VirtualBoxSDS - constructor / destructor                                                                                     *
     142*********************************************************************************************************************************/
     143
     144VirtualBoxSDS::VirtualBoxSDS()
     145    : m_cVBoxSvcProcesses(0)
     146#ifdef WITH_WATCHER
     147    , m_cWatchers(0)
     148    , m_papWatchers(NULL)
     149#endif
     150{
     151}
     152
     153
     154VirtualBoxSDS::~VirtualBoxSDS()
     155{
     156#ifdef WITH_WATCHER
     157    i_shutdownAllWatchers();
     158    RTMemFree(m_papWatchers);
     159    m_papWatchers = NULL;
     160    m_cWatchers   = 0;
     161#endif
     162}
     163
    109164
    110165HRESULT VirtualBoxSDS::FinalConstruct()
     
    115170    AssertLogRelRCReturn(vrc, E_FAIL);
    116171
     172#ifdef WITH_WATCHER
     173    vrc = RTCritSectInit(&m_WatcherCritSect);
     174    AssertLogRelRCReturn(vrc, E_FAIL);
     175#endif
     176
    117177    LogRelFlowThisFuncLeave();
    118178    return S_OK;
     
    124184    LogRelFlowThisFuncEnter();
    125185
     186#ifdef WITH_WATCHER
     187    i_shutdownAllWatchers();
     188    RTCritSectDelete(&m_WatcherCritSect);
     189#endif
     190
    126191    RTCritSectRwDelete(&m_MapCritSect);
    127192
     
    141206
    142207
    143 // IVirtualBoxSDS methods
    144 /////////////////////////////////////////////////////////////////////////////
    145 
     208/*********************************************************************************************************************************
     209*   VirtualBoxSDS - IVirtualBoxSDS methods                                                                                       *
     210*********************************************************************************************************************************/
    146211
    147212/* SDS plan B interfaces: */
    148213STDMETHODIMP VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
    149214{
    150     LogRel(("VirtualBoxSDS::registerVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
    151 #ifdef DEBUG_bird
    152     RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL};
    153     RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
    154     LogRel(("RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
    155             rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
    156             CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
    157 #endif
     215    LogRel(("registerVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
     216
     217    /*
     218     * Get the caller PID so we can validate the aPid parameter with the other two.
     219     * The V2 structure requires Vista or later, so fake it if older.
     220     */
     221    RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
     222    RPC_STATUS rcRpc;
     223    if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
     224        rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
     225    else
     226    {
     227        CallAttribs.ClientPID = (HANDLE)(intptr_t)aPid;
     228        rcRpc = RPC_S_OK;
     229    }
    158230
    159231    HRESULT hrc;
    160232    if (   RT_VALID_PTR(aVBoxSVC)
    161         && RT_VALID_PTR(aExistingVirtualBox))
     233        && RT_VALID_PTR(aExistingVirtualBox)
     234        && rcRpc == RPC_S_OK
     235        && (intptr_t)CallAttribs.ClientPID == aPid)
    162236    {
    163237        *aExistingVirtualBox = NULL;
    164238
    165         /* Get the client user SID and name. */
     239        /*
     240         * Get the client user SID and name.
     241         */
    166242        com::Utf8Str strSid;
    167243        com::Utf8Str strUsername;
    168244        if (i_getClientUserSid(&strSid, &strUsername))
    169245        {
    170             VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername);
     246            VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername); /* (returns holding the lock) */
    171247            if (pUserData)
    172248            {
     
    184260                    catch (...)
    185261                    {
    186                         LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception calling GetVirtualBox.\n"));
     262                        LogRel(("registerVBoxSVC: Unexpected exception calling GetVirtualBox!!\n"));
    187263                        hrc = E_FAIL;
    188264                    }
    189265                    if (FAILED_DEAD_INTERFACE(hrc))
    190266                    {
    191                         LogRel(("VirtualBoxSDS::registerVBoxSVC: Seems VBoxSVC instance died.  Dropping it and letting caller take over. (hrc=%Rhrc)\n", hrc));
    192                         pUserData->m_ptrTheChosenOne.setNull();
     267                        LogRel(("registerVBoxSVC: Seems VBoxSVC instance died.  Dropping it and letting caller take over. (hrc=%Rhrc)\n", hrc));
     268#ifdef WITH_WATCHER
     269                        i_stopWatching(pUserData, pUserData->m_pidTheChosenOne);
     270#endif
     271                        pUserData->i_unchooseTheOne(true /*fIrregular*/);
    193272                    }
    194273                }
     
    197276
    198277                /*
    199                  * Is the caller the chosen one?
     278                 * No chosen one?  Make the caller the new chosen one!
    200279                 */
    201280                if (pUserData->m_ptrTheChosenOne.isNull())
    202281                {
    203                     LogRel(("VirtualBoxSDS::registerVBoxSVC: Making aPid=%u (%#x) the chosen one for user %s (%s)!\n",
     282                    LogRel(("registerVBoxSVC: Making aPid=%u (%#x) the chosen one for user %s (%s)!\n",
    204283                            aPid, aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
    205                     pUserData->m_ptrTheChosenOne = aVBoxSVC;
     284#ifdef WITH_WATCHER
     285                    /* Open the process so we can watch it. */
     286                    HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE /*fInherit*/, aPid);
     287                    if (hProcess == NULL)
     288                        hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE /*fInherit*/, aPid);
     289                    if (hProcess == NULL)
     290                        hProcess = OpenProcess(SYNCHRONIZE, FALSE /*fInherit*/, aPid);
     291                    if (hProcess != NULL)
     292                    {
     293                        if (i_watchIt(pUserData, hProcess, aPid))
     294#endif
     295                        {
     296                            /* Make it official... */
     297                            pUserData->m_ptrTheChosenOne = aVBoxSVC;
     298                            pUserData->m_pidTheChosenOne = aPid;
     299                            hrc = S_OK;
     300                        }
     301#ifdef WITH_WATCHER
     302                        else
     303                        {
     304
     305                            LogRel(("registerVBoxSVC: i_watchIt failed!\n"));
     306                            hrc = RPC_E_OUT_OF_RESOURCES;
     307                        }
     308                    }
     309                    else
     310                    {
     311                        LogRel(("registerVBoxSVC: OpenProcess failed: %u\n", GetLastError()));
     312                        hrc = E_ACCESSDENIED;
     313                    }
     314#endif
    206315                }
    207316
     
    215324            hrc = E_FAIL;
    216325    }
     326    else if (   !RT_VALID_PTR(aVBoxSVC)
     327             || !RT_VALID_PTR(aExistingVirtualBox))
     328        hrc = E_INVALIDARG;
     329    else if (rcRpc != RPC_S_OK)
     330    {
     331        LogRel(("registerVBoxSVC: rcRpc=%d (%#x)!\n", rcRpc, rcRpc));
     332        hrc = E_UNEXPECTED;
     333    }
    217334    else
     335    {
     336        LogRel(("registerVBoxSVC: Client PID mismatch: aPid=%d (%#x), RPC ClientPID=%zd (%#zx)\n",
     337                aPid, aPid, CallAttribs.ClientPID, CallAttribs.ClientPID));
    218338        hrc = E_INVALIDARG;
     339    }
    219340    LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
    220341    return hrc;
     
    224345STDMETHODIMP VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
    225346{
    226     LogRel(("VirtualBoxSDS::deregisterVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
     347    LogRel(("deregisterVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
    227348    HRESULT hrc;
    228349    if (RT_VALID_PTR(aVBoxSVC))
     
    238359                if (aVBoxSVC == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
    239360                {
    240                     LogRel(("VirtualBoxSDS::deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
     361                    LogRel(("deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
    241362                            pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
    242                     pUserData->m_ptrTheChosenOne.setNull();
     363#ifdef WITH_WATCHER
     364                    i_stopWatching(pUserData, pUserData->m_pidTheChosenOne);
     365#endif
     366                    pUserData->i_unchooseTheOne(false /*fIrregular*/);
    243367                }
    244368                else
    245                     LogRel(("VirtualBoxSDS::deregisterVBoxSVC: not the choosen one (%p != %p)\n",
     369                    LogRel(("deregisterVBoxSVC: not the choosen one (%p != %p)\n",
    246370                            (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
    247371                pUserData->i_unlock();
     
    252376            else
    253377            {
    254                 LogRel(("VirtualBoxSDS::deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
     378                LogRel(("deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
    255379                        strSid.c_str(), strUsername.c_str(), aPid));
    256380                hrc = S_OK;
     
    268392
    269393/*********************************************************************************************************************************
    270 *   Internal Methods                                                                                                             *
     394*   VirtualBoxSDS - Internal Methods                                                                                             *
    271395*********************************************************************************************************************************/
    272396
     
    307431                    catch (std::bad_alloc &)
    308432                    {
    309                         LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
     433                        LogRel(("i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
    310434                    }
    311435                    LocalFree((HLOCAL)pwszString);
     
    334458                            catch (std::bad_alloc &)
    335459                            {
    336                                 LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
     460                                LogRel(("i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
    337461                                a_pStrUsername->setNull();
    338462                            }
    339463                        }
    340464                        else
    341                             LogRel(("VirtualBoxSDS::i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
     465                            LogRel(("i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
    342466                                   GetLastError(), cwcUsername, cwcDomain));
    343467                    }
    344468                }
    345469                else
    346                     LogRel(("VirtualBoxSDS::i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
     470                    LogRel(("i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
    347471            }
    348472            else
    349                 LogRel(("VirtualBoxSDS::i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
     473                LogRel(("i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
    350474            CloseHandle(hToken);
    351475        }
     
    353477        {
    354478            CoRevertToSelf();
    355             LogRel(("VirtualBoxSDS::i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
     479            LogRel(("i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
    356480        }
    357481    }
    358482    else
    359         LogRel(("VirtualBoxSDS::i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
     483        LogRel(("i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
    360484    CoUninitialize();
    361485    return fRet;
     
    453577
    454578                if (pUserData)
    455                     LogRel(("VirtualBoxSDS::i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
     579                    LogRel(("i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
    456580                            pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
    457581                else
     
    467591}
    468592
     593#ifdef WITH_WATCHER
     594/**
     595 * Data about what's being watched.
     596 */
     597typedef struct VBoxSDSWatcherData
     598{
     599    /** The per-user data (referenced). */
     600    VBoxSDSPerUserData *pUserData;
     601    /** The chosen one revision number (for handling an almost impossible race
     602     * where a client terminates while making a deregistration call). */
     603    uint32_t            iRevision;
     604    /** The PID we're watching. */
     605    RTPROCESS           pid;
     606
     607    /** Sets the members to NULL values. */
     608    void setNull()
     609    {
     610        pUserData = NULL;
     611        iRevision = UINT32_MAX;
     612        pid       = NIL_RTPROCESS;
     613    }
     614} VBoxSDSWatcherData;
     615
     616/**
     617 * Per watcher data.
     618 */
     619typedef struct VBoxSDSWatcher
     620{
     621    /** Pointer to the VBoxSDS instance. */
     622    VirtualBoxSDS      *pVBoxSDS;
     623    /** The thread handle. */
     624    RTTHREAD            hThread;
     625    /** Number of references to this structure. */
     626    uint32_t volatile   cRefs;
     627    /** Set if the thread should shut down. */
     628    bool volatile       fShutdown;
     629    /** Number of pending items in the todo array. */
     630    uint32_t            cTodos;
     631    /** The watcher number. */
     632    uint32_t            iWatcher;
     633    /** The number of handles once TODOs have been taken into account. */
     634    uint32_t            cHandlesEffective;
     635    /** Number of handles / user data items being monitored. */
     636    uint32_t            cHandles;
     637    /** Array of handles.
     638     * The zero'th entry is the event semaphore use to signal the thread. */
     639    HANDLE              aHandles[MAXIMUM_WAIT_OBJECTS];
     640    /** Array the runs parallel to aHandles with the VBoxSVC data. */
     641    VBoxSDSWatcherData  aData[MAXIMUM_WAIT_OBJECTS];
     642    /** Todo items. */
     643    struct
     644    {
     645        /** If NULL the data is being removed, otherwise it's being added and
     646         * this is the process handle to watch for termination. */
     647        HANDLE              hProcess;
     648        /** The data about what's being watched. */
     649        VBoxSDSWatcherData  Data;
     650    }                   aTodos[MAXIMUM_WAIT_OBJECTS * 4];
     651
     652
     653    /** Helper for removing a handle & data table entry. */
     654    uint32_t removeHandle(uint32_t iEntry, uint32_t cHandles)
     655    {
     656        uint32_t cToShift = cHandles - iEntry - 1;
     657        if (cToShift > 0)
     658        {
     659            memmove(&aData[iEntry], &aData[iEntry + 1], sizeof(aData[0]) * cToShift);
     660            memmove(&aHandles[iEntry], &aHandles[iEntry + 1], sizeof(aHandles[0]) * cToShift);
     661        }
     662        cHandles--;
     663        aHandles[cHandles] = NULL;
     664        aData[cHandles].setNull();
     665
     666        return cHandles;
     667    }
     668} VBoxSDSWatcher;
     669
     670
     671
     672/**
     673 * Watcher thread.
     674 */
     675/*static*/ DECLCALLBACK(int) VirtualBoxSDS::i_watcherThreadProc(RTTHREAD hSelf, void *pvUser)
     676{
     677    VBoxSDSWatcher *pThis    = (VBoxSDSWatcher *)pvUser;
     678    VirtualBoxSDS  *pVBoxSDS = pThis->pVBoxSDS;
     679    RT_NOREF(hSelf);
     680
     681    /*
     682     * This thread may release references to IVBoxSVCRegistration objects.
     683     */
     684    CoInitializeEx(NULL, COINIT_MULTITHREADED);
     685
     686    /*
     687     * The loop.
     688     */
     689    RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
     690    while (!pThis->fShutdown)
     691    {
     692        /*
     693         * Deal with the todo list.
     694         */
     695        uint32_t cHandles = pThis->cHandles;
     696        uint32_t cTodos   = pThis->cTodos;
     697
     698        for (uint32_t i = 0; i < cTodos; i++)
     699        {
     700            VBoxSDSPerUserData *pUserData = pThis->aTodos[i].Data.pUserData;
     701            AssertContinue(pUserData);
     702            if (pThis->aTodos[i].hProcess != NULL)
     703            {
     704                /* Add: */
     705                AssertLogRelMsgBreakStmt(cHandles < RT_ELEMENTS(pThis->aHandles),
     706                                         ("cHandles=%u cTodos=%u i=%u iWatcher=%u\n", cHandles, cTodos, i, pThis->iWatcher),
     707                                         pThis->fShutdown = true);
     708                pThis->aHandles[cHandles] = pThis->aTodos[i].hProcess;
     709                pThis->aData[cHandles]    = pThis->aTodos[i].Data;
     710                cHandles++;
     711            }
     712            else
     713            {
     714                /* Remove: */
     715                uint32_t cRemoved = 0;
     716                uint32_t j        = cHandles;
     717                while (j-- > 1)
     718                    if (pThis->aData[j].pUserData == pUserData)
     719                    {
     720                        cHandles = pThis->removeHandle(j, cHandles);
     721                        pUserData->i_release();
     722                        cRemoved++;
     723                    }
     724                if (cRemoved != 1)
     725                    LogRel(("i_watcherThreadProc/#%u: Warning! cRemoved=%u pUserData=%p\n", pThis->iWatcher, cRemoved, pUserData));
     726            }
     727            /* Zap the entry in case we assert and leave further up. */
     728            pThis->aTodos[i].Data.setNull();
     729            pThis->aTodos[i].hProcess = NULL;
     730        }
     731
     732        Assert(cHandles > 0 && cHandles <= RT_ELEMENTS(pThis->aHandles));
     733        pThis->cHandles          = cHandles;
     734        pThis->cHandlesEffective = cHandles;
     735        pThis->cTodos            = 0;
     736
     737        if (pThis->fShutdown)
     738            break;
     739
     740        /*
     741         * Wait.
     742         */
     743        RTCritSectLeave(&pVBoxSDS->m_WatcherCritSect);
     744
     745        LogRel(("i_watcherThreadProc/#%u: Waiting on %u handles...\n", pThis->iWatcher, cHandles));
     746        DWORD const dwWait = WaitForMultipleObjects(cHandles, pThis->aHandles, FALSE /*fWaitAll*/, INFINITE);
     747        LogRel(("i_watcherThreadProc/#%u: ... wait returned: %#x (%d)\n", pThis->iWatcher, dwWait, dwWait));
     748
     749        uint32_t const iHandle = dwWait - WAIT_OBJECT_0;
     750        if (iHandle < cHandles && iHandle > 0)
     751        {
     752            /*
     753             * A VBoxSVC process has terminated.
     754             *
     755             * Note! We need to take the user data lock before the watcher one here.
     756             */
     757            VBoxSDSPerUserData * const pUserData = pThis->aData[iHandle].pUserData;
     758            uint32_t const             iRevision = pThis->aData[iHandle].iRevision;
     759            RTPROCESS const            pid       = pThis->aData[iHandle].pid;
     760
     761            pUserData->i_lock();
     762            RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
     763
     764            DWORD dwExit = 0;
     765            GetExitCodeProcess(pThis->aHandles[iHandle], &dwExit);
     766            LogRel(("i_watcherThreadProc/#%u: %p/%s: PID %u/%#x termination detected: %d (%#x)  [iRev=%u, cur %u]\n",
     767                    pThis->iWatcher, pUserData, pUserData->m_strUsername.c_str(), pid, pid, dwExit, dwExit,
     768                    iRevision, pUserData->m_iTheChosenOneRevision));
     769
     770            /* Remove it from the handle array. */
     771            CloseHandle(pThis->aHandles[iHandle]);
     772            pThis->cHandles = cHandles = pThis->removeHandle(iHandle, cHandles);
     773            pThis->cHandlesEffective -= 1;
     774
     775            /* If the process we were watching is still the current chosen one,
     776               unchoose it and decrement the client count.  Otherwise we were subject
     777               to a deregistration/termination race (unlikely). */
     778            if (pUserData->m_iTheChosenOneRevision == iRevision)
     779            {
     780                pUserData->i_unchooseTheOne(true /*fIrregular*/);
     781                pUserData->i_unlock();
     782                pVBoxSDS->i_decrementClientCount();
     783            }
     784            else
     785                pUserData->i_unlock();
     786            pUserData->i_release();
     787        }
     788        else
     789        {
     790            RTCritSectEnter(&pThis->pVBoxSDS->m_WatcherCritSect);
     791            AssertLogRelMsgBreak(iHandle == 0 || dwWait == WAIT_TIMEOUT,
     792                                 ("dwWait=%u (%#x) cHandles=%u\n", dwWait, dwWait, cHandles));
     793        }
     794    }
     795
     796    RTCritSectLeave(&pThis->pVBoxSDS->m_WatcherCritSect);
     797
     798    /*
     799     * In case we quit w/o being told, signal i_watchIt that we're out of action.
     800     */
     801    pThis->fShutdown = true;
     802
     803    /*
     804     * Release all our data on the way out.
     805     */
     806    uint32_t i = pThis->cHandles;
     807    while (i-- > 1)
     808    {
     809        if (pThis->aData[i].pUserData)
     810        {
     811            pThis->aData[i].pUserData->i_release();
     812            pThis->aData[i].pUserData = NULL;
     813        }
     814        if (pThis->aHandles[i])
     815        {
     816            CloseHandle(pThis->aHandles[i]);
     817            pThis->aHandles[i] = NULL;
     818        }
     819    }
     820    if (pThis->aHandles[0])
     821    {
     822        CloseHandle(pThis->aHandles[0]);
     823        pThis->aHandles[0] = NULL;
     824    }
     825
     826    i = pThis->cTodos;
     827    pThis->cTodos = 0;
     828    while (i-- > 0)
     829    {
     830        if (pThis->aTodos[i].Data.pUserData)
     831        {
     832            pThis->aTodos[i].Data.pUserData->i_release();
     833            pThis->aTodos[i].Data.pUserData = NULL;
     834        }
     835        if (pThis->aTodos[i].hProcess)
     836        {
     837            CloseHandle(pThis->aTodos[i].hProcess);
     838            pThis->aTodos[i].hProcess = NULL;
     839        }
     840    }
     841
     842    if (ASMAtomicDecU32(&pThis->cRefs) == 0)
     843        RTMemFree(pThis);
     844
     845    return VINF_SUCCESS;
     846}
     847
     848
     849/**
     850 * Starts monitoring a VBoxSVC process.
     851 *
     852 * @param   pUserData   The user which chosen VBoxSVC should be watched.
     853 * @param   hProcess    Handle to the VBoxSVC process.  Consumed.
     854 * @param   pid         The VBoxSVC PID.
     855 * @returns Success indicator.
     856 */
     857bool VirtualBoxSDS::i_watchIt(VBoxSDSPerUserData *pUserData, HANDLE hProcess, RTPROCESS pid)
     858{
     859    RTCritSectEnter(&m_WatcherCritSect);
     860
     861    /*
     862     * Find a watcher with capacity left over (we save 8 entries for removals).
     863     */
     864    for (uint32_t i = 0; i < m_cWatchers; i++)
     865    {
     866        VBoxSDSWatcher *pWatcher = m_papWatchers[i];
     867        if (   pWatcher->cHandlesEffective < RT_ELEMENTS(pWatcher->aHandles)
     868            && !pWatcher->fShutdown)
     869        {
     870            uint32_t iTodo = pWatcher->cTodos;
     871            if (iTodo + 8 < RT_ELEMENTS(pWatcher->aTodos))
     872            {
     873                pWatcher->aTodos[iTodo].hProcess       = hProcess;
     874                pWatcher->aTodos[iTodo].Data.pUserData = pUserData;
     875                pWatcher->aTodos[iTodo].Data.iRevision = ++pUserData->m_iTheChosenOneRevision;
     876                pWatcher->aTodos[iTodo].Data.pid       = pid;
     877                pWatcher->cTodos = iTodo + 1;
     878
     879                pUserData->m_iWatcher = pWatcher->iWatcher;
     880                pUserData->i_retain();
     881
     882                BOOL fRc = SetEvent(pWatcher->aHandles[0]);
     883                AssertLogRelMsg(fRc, ("SetEvent(%p) failed: %u\n", pWatcher->aHandles[0], GetLastError()));
     884                LogRel(("i_watchIt: Added %p/%p to watcher #%u: %RTbool\n", pUserData, hProcess, pWatcher->iWatcher, fRc));
     885
     886                i_incrementClientCount();
     887                RTCritSectLeave(&m_WatcherCritSect);
     888                RTThreadYield();
     889                return true;
     890            }
     891        }
     892    }
     893
     894    /*
     895     * No watcher with capacity was found, so create a new one with
     896     * the user/handle prequeued.
     897     */
     898    void *pvNew = RTMemRealloc(m_papWatchers, sizeof(m_papWatchers[0]) * (m_cWatchers + 1));
     899    if (pvNew)
     900    {
     901        m_papWatchers = (VBoxSDSWatcher **)pvNew;
     902        VBoxSDSWatcher *pWatcher = (VBoxSDSWatcher *)RTMemAllocZ(sizeof(*pWatcher));
     903        if (pWatcher)
     904        {
     905            for (uint32_t i = 0; i < RT_ELEMENTS(pWatcher->aData); i++)
     906                pWatcher->aData[i].setNull();
     907            for (uint32_t i = 0; i < RT_ELEMENTS(pWatcher->aTodos); i++)
     908                pWatcher->aTodos[i].Data.setNull();
     909
     910            pWatcher->pVBoxSDS          = this;
     911            pWatcher->iWatcher          = m_cWatchers;
     912            pWatcher->cRefs             = 2;
     913            pWatcher->cHandlesEffective = 2;
     914            pWatcher->cHandles          = 2;
     915            pWatcher->aHandles[0]       = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/,  NULL);
     916            if (pWatcher->aHandles[0])
     917            {
     918                /* Add incoming VBoxSVC process in slot #1: */
     919                pWatcher->aHandles[1]        = hProcess;
     920                pWatcher->aData[1].pid       = pid;
     921                pWatcher->aData[1].pUserData = pUserData;
     922                pWatcher->aData[1].iRevision = ++pUserData->m_iTheChosenOneRevision;
     923                pUserData->i_retain();
     924                pUserData->m_iWatcher = pWatcher->iWatcher;
     925
     926                /* Start the thread and we're good. */
     927                m_papWatchers[m_cWatchers++] = pWatcher;
     928                int rc = RTThreadCreateF(&pWatcher->hThread, i_watcherThreadProc, pWatcher, 0, RTTHREADTYPE_MAIN_WORKER,
     929                                         RTTHREADFLAGS_WAITABLE, "watcher%u", pWatcher->iWatcher);
     930                if (RT_SUCCESS(rc))
     931                {
     932                    LogRel(("i_watchIt: Created new watcher #%u for %p/%p\n", m_cWatchers, pUserData, hProcess));
     933
     934                    i_incrementClientCount();
     935                    RTCritSectLeave(&m_WatcherCritSect);
     936                    return true;
     937                }
     938
     939                LogRel(("i_watchIt: Error starting watcher thread: %Rrc\n", rc));
     940                m_papWatchers[--m_cWatchers] = NULL;
     941
     942                pUserData->m_iWatcher = UINT32_MAX;
     943                pUserData->i_release();
     944                CloseHandle(pWatcher->aHandles[0]);
     945            }
     946            else
     947                LogRel(("i_watchIt: CreateEventW failed: %u\n", GetLastError()));
     948            RTMemFree(pWatcher);
     949        }
     950        else
     951            LogRel(("i_watchIt: failed to allocate watcher structure!\n"));
     952    }
     953    else
     954        LogRel(("i_watchIt: Failed to grow watcher array to %u entries!\n", m_cWatchers + 1));
     955
     956    RTCritSectLeave(&m_WatcherCritSect);
     957    CloseHandle(hProcess);
     958    return false;
     959}
     960
     961
     962/**
     963 * Stops monitoring a VBoxSVC process.
     964 *
     965 * @param   pUserData   The user which chosen VBoxSVC should be watched.
     966 * @param   pid         The VBoxSVC PID.
     967 */
     968void VirtualBoxSDS::i_stopWatching(VBoxSDSPerUserData *pUserData, RTPROCESS pid)
     969{
     970    /*
     971     * Add a remove order in the watcher's todo queue.
     972     */
     973    RTCritSectEnter(&m_WatcherCritSect);
     974    for (uint32_t iRound = 0; ; iRound++)
     975    {
     976        uint32_t const iWatcher = pUserData->m_iWatcher;
     977        if (iWatcher < m_cWatchers)
     978        {
     979            VBoxSDSWatcher *pWatcher = m_papWatchers[pUserData->m_iWatcher];
     980            if (!pWatcher->fShutdown)
     981            {
     982                /*
     983                 * Remove duplicate todo entries.
     984                 */
     985                bool fAddIt = true;
     986                uint32_t iTodo = pWatcher->cTodos;
     987                while (iTodo-- > 0)
     988                    if (pWatcher->aTodos[iTodo].Data.pUserData == pUserData)
     989                    {
     990                        if (pWatcher->aTodos[iTodo].hProcess == NULL)
     991                            fAddIt = true;
     992                        else
     993                        {
     994                            fAddIt = false;
     995                            CloseHandle(pWatcher->aTodos[iTodo].hProcess);
     996                        }
     997                        uint32_t const cTodos = --pWatcher->cTodos;
     998                        uint32_t const cToShift = cTodos - iTodo;
     999                        if (cToShift > 0)
     1000                            memmove(&pWatcher->aTodos[iTodo], &pWatcher->aTodos[iTodo + 1], sizeof(pWatcher->aTodos[0]) * cToShift);
     1001                        pWatcher->aTodos[cTodos].hProcess = NULL;
     1002                        pWatcher->aTodos[cTodos].Data.setNull();
     1003                    }
     1004
     1005                /*
     1006                 * Did we just eliminated the add and cancel out this operation?
     1007                 */
     1008                if (!fAddIt)
     1009                {
     1010                    pUserData->m_iWatcher = UINT32_MAX;
     1011                    pUserData->m_iTheChosenOneRevision++;
     1012                    i_decrementClientCount();
     1013
     1014                    RTCritSectLeave(&m_WatcherCritSect);
     1015                    RTThreadYield();
     1016                    return;
     1017                }
     1018
     1019                /*
     1020                 * No we didn't.  So, try append a removal item.
     1021                 */
     1022                iTodo = pWatcher->cTodos;
     1023                if (iTodo < RT_ELEMENTS(pWatcher->aTodos))
     1024                {
     1025                    pWatcher->aTodos[iTodo].hProcess       = NULL;
     1026                    pWatcher->aTodos[iTodo].Data.pUserData = pUserData;
     1027                    pWatcher->aTodos[iTodo].Data.pid       = pid;
     1028                    pWatcher->aTodos[iTodo].Data.iRevision = pUserData->m_iTheChosenOneRevision++;
     1029                    pWatcher->cTodos = iTodo + 1;
     1030                    SetEvent(pWatcher->aHandles[0]);
     1031
     1032                    pUserData->m_iWatcher = UINT32_MAX;
     1033                    i_decrementClientCount();
     1034
     1035                    RTCritSectLeave(&m_WatcherCritSect);
     1036                    RTThreadYield();
     1037                    return;
     1038                }
     1039            }
     1040            else
     1041            {
     1042                LogRel(("i_stopWatching: Watcher #%u has shut down.\n", iWatcher));
     1043                break;
     1044            }
     1045
     1046            /*
     1047             * Todo queue is full.  Sleep a little and let the watcher process it.
     1048             */
     1049            LogRel(("i_stopWatching: Watcher #%u todo queue is full! (round #%u)\n", iWatcher, iRound));
     1050
     1051            uint32_t const iTheChosenOneRevision = pUserData->m_iTheChosenOneRevision;
     1052            SetEvent(pWatcher->aHandles[0]);
     1053
     1054            RTCritSectLeave(&m_WatcherCritSect);
     1055            RTThreadSleep(1 + (iRound & 127));
     1056            RTCritSectEnter(&m_WatcherCritSect);
     1057
     1058            AssertLogRelMsgBreak(pUserData->m_iTheChosenOneRevision == iTheChosenOneRevision,
     1059                                 ("Impossible! m_iTheChosenOneRevision changed %#x -> %#x!\n",
     1060                                  iTheChosenOneRevision, pUserData->m_iTheChosenOneRevision));
     1061        }
     1062        else
     1063        {
     1064            AssertLogRelMsg(pUserData->m_iWatcher == UINT32_MAX,
     1065                            ("Impossible! iWatcher=%d m_cWatcher=%u\n", iWatcher, m_cWatchers));
     1066            break;
     1067        }
     1068    }
     1069    RTCritSectLeave(&m_WatcherCritSect);
     1070}
     1071
     1072
     1073/**
     1074 * Shutdowns all the watchers.
     1075 */
     1076void VirtualBoxSDS::i_shutdownAllWatchers(void)
     1077{
     1078    LogRel(("i_shutdownAllWatchers: %u watchers\n", m_cWatchers));
     1079
     1080    /* Notify them all. */
     1081    uint32_t i = m_cWatchers;
     1082    while (i-- > 0)
     1083    {
     1084        ASMAtomicWriteBool(&m_papWatchers[i]->fShutdown, true);
     1085        SetEvent(m_papWatchers[i]->aHandles[0]);
     1086    }
     1087
     1088    /* Wait for them to complete and destroy their data. */
     1089    i = m_cWatchers;
     1090    m_cWatchers = 0;
     1091    while (i-- > 0)
     1092    {
     1093        VBoxSDSWatcher *pWatcher = m_papWatchers[i];
     1094        if (pWatcher)
     1095        {
     1096            m_papWatchers[i] = NULL;
     1097
     1098            int rc = RTThreadWait(pWatcher->hThread, RT_MS_1MIN / 2, NULL);
     1099            if (RT_SUCCESS(rc))
     1100                pWatcher->hThread = NIL_RTTHREAD;
     1101            else
     1102                LogRel(("i_shutdownAllWatchers: RTThreadWait failed on #%u: %Rrc\n", i, rc));
     1103
     1104            if (ASMAtomicDecU32(&pWatcher->cRefs) == 0)
     1105                RTMemFree(pWatcher);
     1106        }
     1107    }
     1108}
     1109
     1110
     1111/**
     1112 * Increments the VBoxSVC client count.
     1113 */
     1114void VirtualBoxSDS::i_incrementClientCount()
     1115{
     1116    Assert(RTCritSectIsOwner(&m_WatcherCritSect));
     1117    uint32_t cClients = ++m_cVBoxSvcProcesses;
     1118    Assert(cClients < 4096);
     1119    VBoxSDSNotifyClientCount(cClients);
     1120}
     1121
     1122
     1123/**
     1124 * Decrements the VBoxSVC client count.
     1125 */
     1126void VirtualBoxSDS::i_decrementClientCount()
     1127{
     1128    Assert(RTCritSectIsOwner(&m_WatcherCritSect));
     1129    uint32_t cClients = --m_cVBoxSvcProcesses;
     1130    Assert(cClients < 4096);
     1131    VBoxSDSNotifyClientCount(cClients);
     1132}
     1133
     1134
     1135#endif /* WITH_WATCHER */
     1136
     1137
    4691138/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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