/****************************************************************** Copyright (c) 2000-2002 Microsoft Corporation, All Rights Reserved JOProcess.CPP -- WMI provider class implementation ******************************************************************/ #include "precomp.h" #if NTONLY >= 5 #include "JOProcess.h" #include /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ CJOProcess MyCJOProcess( PROVIDER_NAME_WIN32NAMEDJOBOBJECTPROCESS, IDS_CimWin32Namespace); /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ CJOProcess::CJOProcess( LPCWSTR setName, LPCWSTR pszNameSpace /*=NULL*/) : Provider(setName, pszNameSpace) { } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ CJOProcess::~CJOProcess() { } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ HRESULT CJOProcess::GetObject( CInstance* pInstance, long lFlags /*= 0L*/ ) { return FindSingleInstance(pInstance); } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ HRESULT CJOProcess::EnumerateInstances( MethodContext* pMethodContext, long lFlags) { HRESULT hr = WBEM_S_NO_ERROR; hr = Enumerate(pMethodContext); return hr; } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ // We will only optimize for the // case where a specific job was // specified. HRESULT CJOProcess::ExecQuery ( MethodContext *pMethodContext, CFrameworkQuery& Query, long lFlags ) { HRESULT hr = WBEM_S_NO_ERROR; std::vector<_bstr_t> rgJOs; DWORD dwJOsCount = 0L; Query.GetValuesForProp(L"Collection", rgJOs); dwJOsCount = rgJOs.size(); if(dwJOsCount > 0) { CInstancePtr pInstJO; CHString chstrPath; for(DWORD x = 0; x < dwJOsCount; x++) { // First we see if the specified JO exists. chstrPath.Format( L"\\\\.\\%s:%s", IDS_CimWin32Namespace, (LPCWSTR)rgJOs[x]); hr = CWbemProviderGlue::GetInstanceKeysByPath( chstrPath, &pInstJO, pMethodContext); if (SUCCEEDED(hr)) { // Ok, the JO exists. Enumerate its processes... // rgJOs[x] contains something like // Win32_NamedJobObject.CollectionID="myjob", // from which I want just the myjob part. CHString chstrInstKey; if(GetInstKey( CHString((LPCWSTR)rgJOs[x]), chstrInstKey)) { hr = EnumerateProcsInJob( chstrInstKey, pMethodContext); if (FAILED(hr)) { break; } } else { return WBEM_E_INVALID_PARAMETER; } } } } else { hr = Enumerate(pMethodContext); } return hr; } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ HRESULT CJOProcess::PutInstance ( const CInstance &Instance, long lFlags ) { HRESULT hr = WBEM_E_INVALID_PARAMETER; MethodContext *pMethodContext = Instance.GetMethodContext(); long lCreateFlags = lFlags & 3; // Decide how to proceed based on the value of lFlags: // (we want to support PutInstance in all cases except // where the client asked to update the instance). if(lCreateFlags != WBEM_FLAG_UPDATE_ONLY) { // The caller only wants to create an instance that doesn't exist. if((hr = FindSingleInstance( &Instance)) == WBEM_E_NOT_FOUND) { // The association instance does not already exist, so create it... // First see that the job object instance exists... CHString chstrJOPath; if ( ! Instance.GetCHString(L"Collection", chstrJOPath) ) { return WBEM_E_FAILED ; } CInstancePtr pJOInst; hr = CWbemProviderGlue::GetInstanceKeysByPath( chstrJOPath, &pJOInst, pMethodContext); if (SUCCEEDED(hr)) { // Confirm that the process exists... CHString chstrProcPath; if ( ! Instance.GetCHString(L"Member", chstrProcPath) ) { return WBEM_E_FAILED ; } CInstancePtr pProcInst; hr = CWbemProviderGlue::GetInstanceKeysByPath( chstrProcPath, &pProcInst, pMethodContext); if(SUCCEEDED(hr)) { hr = Create(pJOInst, pProcInst); } } } } return hr; } /***************************************************************************** * * FUNCTION : CJOProcess::FindSingleInstance * * DESCRIPTION : Internal helper function used to locate a single job * object. * * INPUTS : A pointer to a CInstance containing the instance we are * attempting to locate and fill values for. * * RETURNS : A valid HRESULT * *****************************************************************************/ HRESULT CJOProcess::FindSingleInstance ( const CInstance* pInstance ) { HRESULT hr = WBEM_S_NO_ERROR; if(!pInstance) { return WBEM_E_INVALID_PARAMETER; } MethodContext* pMethodContext = pInstance->GetMethodContext(); CHString chstrCollection; CHString chstrMember; // Find out which JO and which process are specified... if ( ! pInstance->GetCHString(L"Collection", chstrCollection) ) { return WBEM_E_FAILED ; } if ( ! pInstance->GetCHString(L"Member", chstrMember) ) { return WBEM_E_FAILED ; } CHString chstrCollectionID; CHString chstrProcessHandle; CInstancePtr cinstJO; CInstancePtr cinstProcess; if(GetInstKey(chstrCollection, chstrCollectionID) && GetInstKey(chstrMember, chstrProcessHandle)) { // See if the specified job exists... hr = CWbemProviderGlue::GetInstanceKeysByPath( chstrCollection, &cinstJO, pMethodContext); if(SUCCEEDED(hr)) { // See if the specified process exists... hr = CWbemProviderGlue::GetInstanceKeysByPath( chstrMember, &cinstProcess, pMethodContext); } if(SUCCEEDED(hr)) { // The endpoints exist. Is the specified // process part of the specified job though? CHString chstrProcessID; DWORD dwProcessID; if(cinstProcess->GetCHString(L"Handle", chstrProcessID)) { dwProcessID = _wtoi(chstrProcessID); SmartCloseHandle hJob; CHString chstrUndecoratedJOName; UndecorateJOName( chstrCollectionID, chstrUndecoratedJOName); hJob = ::OpenJobObject( MAXIMUM_ALLOWED, FALSE, chstrUndecoratedJOName); if(hJob != NULL) { long lSize = 0L; bool fGotProcList = false; DWORD dwLen = 0L; lSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (5 * sizeof(ULONG_PTR)); wmilib::auto_buffer < BYTE > pbBuff ( new BYTE [ lSize ], lSize ) ; if ( pbBuff.get () ) { fGotProcList = ::QueryInformationJobObject( hJob, JobObjectBasicProcessIdList, pbBuff.get (), lSize, &dwLen); if(!fGotProcList) { // need to grow the buffer... lSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (((JOBOBJECT_BASIC_PROCESS_ID_LIST*)pbBuff.get ())->NumberOfAssignedProcesses - 1)*sizeof(ULONG_PTR); pbBuff.reset ( new BYTE [ lSize ] ) ; if ( pbBuff.get () ) { fGotProcList = ::QueryInformationJobObject( hJob, JobObjectBasicProcessIdList, pbBuff.get (), lSize, &dwLen); } else { hr = WBEM_E_OUT_OF_MEMORY; } } if(fGotProcList) { PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpl = static_cast(static_cast(pbBuff.get ())); bool fExists = false; for(long m = 0; m < pjobpl->NumberOfProcessIdsInList && !fExists; m++) { if(dwProcessID == pjobpl->ProcessIdList[m]) { fExists = true; } } // If the process wasn't in the job, // we didn't find an instance of the // requested association, even though // the endpoints may have existed. if(!fExists) { hr = WBEM_E_NOT_FOUND; } } else { hr = WinErrorToWBEMhResult(::GetLastError()); } } else { hr = WBEM_E_OUT_OF_MEMORY; } } else { hr = WinErrorToWBEMhResult(::GetLastError()); } } else { hr = WBEM_E_INVALID_PARAMETER; } } } else { hr = WBEM_E_FAILED; } return hr; } /***************************************************************************** * * FUNCTION : CJOProcess::Create * * DESCRIPTION : Internal helper function used to add a process to a job. * * INPUTS : A pointer to the JO instance to add the proc to, and * a pointer to the proc instance to add * * RETURNS : A valid HRESULT. * *****************************************************************************/ HRESULT CJOProcess::Create ( const CInstance &JOInstance, const CInstance &ProcInstance ) { HRESULT hr = WBEM_S_NO_ERROR; CHString chstrJOID; CHString chstrProcHandle; // JO to add the proc to... if ( ! JOInstance.GetCHString(L"CollectionID", chstrJOID) ) { return WBEM_E_FAILED ; } // Proc to add to the job... if ( ! ProcInstance.GetCHString(L"Handle", chstrProcHandle) ) { return WBEM_E_FAILED ; } DWORD dwProcID = _wtol(chstrProcHandle); // Do the add SmartCloseHandle hJob; CHString chstrUndecoratedJOName; UndecorateJOName( chstrJOID, chstrUndecoratedJOName); hJob = ::OpenJobObject( MAXIMUM_ALLOWED, FALSE, chstrUndecoratedJOName); if(hJob != NULL) { SmartCloseHandle hProc; hProc = ::OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcID); if(hProc != NULL) { if(!::AssignProcessToJobObject( hJob, hProc)) { hr = MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); } } } else { hr = WinErrorToWBEMhResult(::GetLastError()); } return hr; } /***************************************************************************** * * FUNCTION : CJOProcess::Enumerate * * DESCRIPTION : Internal helper function used to enumerate instances of * this class. All instances are enumerated, but only the * properties specified are obtained. * * INPUTS : A pointer to a the MethodContext for the call. * A DWORD specifying which properties are requested. * * RETURNS : A valid HRESULT * *****************************************************************************/ HRESULT CJOProcess::Enumerate ( MethodContext *pMethodContext ) { HRESULT hr = WBEM_S_NO_ERROR; TRefPointerCollection JOList; hr = CWbemProviderGlue::GetInstancesByQuery( L"SELECT CollectionID FROM Win32_NamedJobObject", &JOList, pMethodContext, IDS_CimWin32Namespace); if(SUCCEEDED(hr)) { REFPTRCOLLECTION_POSITION pos; // Initialize the enum if(JOList.BeginEnum(pos)) { // Set some vars CHString chstrJOID; CInstancePtr pJOInst; for (pJOInst.Attach(JOList.GetNext(pos)) ; (pJOInst != NULL) ; pJOInst.Attach(JOList.GetNext(pos)) ) { bool t_Status = pJOInst->GetCHString ( L"CollectionID", chstrJOID ) ; if ( t_Status ) { hr = EnumerateProcsInJob ( chstrJOID, pMethodContext ); } else { hr = WBEM_E_FAILED ; } } } } return hr; } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ HRESULT CJOProcess::EnumerateProcsInJob ( LPCWSTR wstrJobID, MethodContext *pMethodContext ) { HRESULT hr = WBEM_S_NO_ERROR; SmartCloseHandle hJob; CHString chstrUndecoratedJOName; UndecorateJOName( wstrJobID, chstrUndecoratedJOName); hJob = ::OpenJobObject( MAXIMUM_ALLOWED, FALSE, chstrUndecoratedJOName); if(hJob != NULL) { long lSize = 0L; bool fGotProcList = false; DWORD dwLen = 0L; lSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (5 * sizeof(ULONG_PTR)); wmilib::auto_buffer < BYTE > pbBuff ( new BYTE [ lSize ], lSize ) ; if ( pbBuff.get () ) { fGotProcList = ::QueryInformationJobObject( hJob, JobObjectBasicProcessIdList, pbBuff.get (), lSize, &dwLen); if(!fGotProcList) { // need to grow the buffer... lSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (((JOBOBJECT_BASIC_PROCESS_ID_LIST*)pbBuff.get ())->NumberOfAssignedProcesses - 1)*sizeof(ULONG_PTR); pbBuff.reset ( new BYTE [ lSize ] ) ; if ( pbBuff.get () ) { fGotProcList = ::QueryInformationJobObject( hJob, JobObjectBasicProcessIdList, pbBuff.get (), lSize, &dwLen); } else { hr = WBEM_E_OUT_OF_MEMORY; } } if(fGotProcList) { PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpl = static_cast(static_cast(pbBuff.get ())); for(long m = 0; m < pjobpl->NumberOfProcessIdsInList; m++) { // Create association inst for each // proc in the job... CInstancePtr pInstance(CreateNewInstance(pMethodContext), false); // Set the endpoints... CHString chstrEscaped; DecorateJOName(chstrUndecoratedJOName, chstrEscaped); EscapeBackslashes(chstrEscaped, chstrEscaped); EscapeQuotes(chstrEscaped, chstrEscaped); CHString chstrTmp; chstrTmp.Format(L"\\\\.\\%s:Win32_NamedJobObject.CollectionID=\"%s\"", IDS_CimWin32Namespace, (LPCWSTR)chstrEscaped); pInstance->SetCHString(L"Collection", chstrTmp); CHString chstrHandle; chstrHandle.Format(L"%d", pjobpl->ProcessIdList[m]); chstrTmp.Format(L"\\\\.\\%s:Win32_Process.Handle=\"%s\"", IDS_CimWin32Namespace, (LPCWSTR) chstrHandle); pInstance->SetCHString(L"Member", chstrTmp); if (SUCCEEDED(hr)) { hr = pInstance->Commit(); } } } else { hr = WinErrorToWBEMhResult(::GetLastError()); } } else { hr = WBEM_E_OUT_OF_MEMORY; } } else { hr = WinErrorToWBEMhResult(::GetLastError()); } return hr; } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ bool CJOProcess::GetInstKey ( CHString& chstrInstPath, CHString& chstrKeyName ) { // the object path is specified in // the first arg. It should at a // minimum always contain an '=' sign, // after which, in quotes is the // object key. bool fRet = false; CHString chstrTmp; LONG lPos = chstrInstPath.Find(L'='); if(lPos != -1) { chstrTmp = chstrInstPath.Mid(lPos + 1); // Remove quotes... if(chstrTmp.Left(1) == L"\"") { chstrTmp = chstrTmp.Mid(1); if(chstrTmp.Right(1) == L"\"") { chstrTmp = chstrTmp.Left(chstrTmp.GetLength() - 1); chstrKeyName = chstrTmp; fRet = true; } } } return fRet; } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ // Takes a decorated job object name and // undecorates it. Decorated job object names // have a backslash preceeding any character // that should be uppercase once undecorated. // // Due to the way CIMOM handles backslashes, // we will get capital letters preceeded by // two, not just one, backslashes. Hence, we // must strip them both. // // According to the decoration scheme, the // following are both lower case: 'A' and 'a', // while the following are both upper case: // '\a' and '\A'. // void CJOProcess::UndecorateJOName ( LPCWSTR wstrDecoratedName, CHString& chstrUndecoratedJOName ) { if(wstrDecoratedName != NULL && *wstrDecoratedName != L'\0') { LPWSTR wstrDecoratedNameLower = NULL; try { wstrDecoratedNameLower = new WCHAR[wcslen(wstrDecoratedName)+1]; if(wstrDecoratedNameLower) { wcscpy(wstrDecoratedNameLower, wstrDecoratedName); _wcslwr(wstrDecoratedNameLower); WCHAR* p3 = chstrUndecoratedJOName.GetBuffer( wcslen(wstrDecoratedNameLower) + 1); const WCHAR* p1 = wstrDecoratedNameLower; const WCHAR* p2 = p1 + 1; while(*p1 != L'\0') { if(*p1 == L'\\') { if(*p2 != NULL) { // Might have any number of // backslashes back to back, // which we will treat as // being the same as one // backslash - i.e., we will // skip over the backslash(s) // and copy over the following // letter. while(*p2 == L'\\') { p2++; }; *p3 = towupper(*p2); p3++; p1 = p2 + 1; if(*p1 != L'\0') { p2 = p1 + 1; } } else { p1++; } } else { *p3 = *p1; p3++; p1 = p2; if(*p1 != L'\0') { p2 = p1 + 1; } } } *p3 = NULL; chstrUndecoratedJOName.ReleaseBuffer(); delete wstrDecoratedNameLower; wstrDecoratedNameLower = NULL; } } catch(...) { if(wstrDecoratedNameLower) { delete wstrDecoratedNameLower; wstrDecoratedNameLower = NULL; } throw; } } } /****************************************************************************** * * Name: * * * Description: * * *****************************************************************************/ // Does the inverse of the above function. // However, here, we only need to put in one // backslash before each uppercase letter. // CIMOM will add the second backslash. void CJOProcess::DecorateJOName ( LPCWSTR wstrUndecoratedName, CHString& chstrDecoratedJOName ) { if(wstrUndecoratedName != NULL && *wstrUndecoratedName != L'\0') { // Worst case is that we will have // a decorated string twice as long // as the undecorated string (happens // is every character in the undecorated // string is a capital letter). WCHAR* p3 = chstrDecoratedJOName.GetBuffer( 2 * (wcslen(wstrUndecoratedName) + 1)); const WCHAR* p1 = wstrUndecoratedName; while(*p1 != L'\0') { if(iswupper(*p1)) { // Add in a backslash... *p3 = L'\\'; p3++; // Add in the character... *p3 = *p1; p3++; p1++; } else { // Add in the character... *p3 = *p1; p3++; p1++; } } *p3 = NULL; chstrDecoratedJOName.ReleaseBuffer(); // What if we had a job called Job, // and someone specified it in the // object path as "Job" instead of // "\Job"? We DON'T want to find it // in such a case, because this would // appear to not be adhering to our // own convention. Hence, we // lowercase the incoming string. chstrDecoratedJOName.MakeLower(); } } #endif // #if NTONLY >= 5