PJOBOBJECT_BASIC_PROCESS_ID_LIST CJobObject::CreatePidList() const{ DWORD dwMaxEntries = 8; JOBOBJECT_BASIC_PROCESS_ID_LIST *Structure; DestroyPidList(); do { BOOL bRet; if (m_dwBuffer) { dwMaxEntries *= 2; delete [] m_dwBuffer; m_dwBuffer = NULL; } m_dwBuffer = new DWORD[dwMaxEntries + 2]; Structure = (JOBOBJECT_BASIC_PROCESS_ID_LIST*)m_dwBuffer; memset(m_dwBuffer, 0, sizeof(m_dwBuffer)); bRet = QueryInformationJobObject(GetHandle(), JobObjectBasicProcessIdList, m_dwBuffer, sizeof(DWORD) * (dwMaxEntries + 2), NULL); if (bRet == 0 && GetLastError() != ERROR_MORE_DATA) { delete [] m_dwBuffer; throw CCodineException(CError::GetErrorMessage(GetLastError()), __FILE__, __LINE__); } } while (Structure->NumberOfAssignedProcesses > Structure->NumberOfProcessIdsInList); return Structure; }
LONGLONG Job::infoMemoryUsagePeak() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; tryApi(_T("QueryInformationJobObject"), QueryInformationJobObject(hJob,JobObjectExtendedLimitInformation,&info,sizeof info,NULL) != 0); return info.PeakJobMemoryUsed; }
bool WindowsJob::queryInformation(JOBOBJECTINFOCLASS informationClass, LPVOID lpInformation, DWORD cbInformationLength, LPDWORD lpReturnLength) { return !!QueryInformationJobObject(jobHandle_, informationClass, lpInformation, cbInformationLength, lpReturnLength); }
const JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION& CJobObject::GetExtendedUsageInfo() const{ BOOL bRet; bRet = QueryInformationJobObject(GetHandle(), JobObjectBasicAndIoAccountingInformation, (PVOID) &m_UsageInfo, sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION), NULL); if (bRet == 0) { throw CCodineException(CError::GetErrorMessage(GetLastError()), __FILE__, __LINE__); } return m_UsageInfo; }
const JOBOBJECT_EXTENDED_LIMIT_INFORMATION& CJobObject::GetExtendedLimit() const { BOOL bRet; bRet = QueryInformationJobObject(GetHandle(), JobObjectExtendedLimitInformation, (PVOID) &m_Limit, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), NULL); if (bRet == 0) { throw CCodineException(CError::GetErrorMessage(GetLastError()), __FILE__, __LINE__); } return m_Limit; }
DWORD JobbedProcessManager::ShockerProc() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION extLimit; LARGE_INTEGER qpc; DWORD result; do { QueryPerformanceCounter(&qpc); execution_time = (qpc.QuadPart - liStart.QuadPart) * qpc_freq; if (time_limit && execution_time > time_limit) { TerminateProcess(hProcess, 0xDEADBEEF); tle_ = true; WaitForSingleObject(hProcess, INFINITE); } QueryInformationJobObject(hJob, JobObjectExtendedLimitInformation, &extLimit, sizeof extLimit, nullptr); memory_ = extLimit.PeakJobMemoryUsed; mle_ |= memory_limit && memory_ > memory_limit; Sleep(100); result = WaitForSingleObject(hProcess, 0); } while (!terminate_shocker && result == WAIT_TIMEOUT); return 0; }
BOOL ProcessJob() { BOOL foundProblem = FALSE; DWORD jobProcessStructSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + sizeof(ULONG_PTR) * 1024; JOBOBJECT_BASIC_PROCESS_ID_LIST* jobProcessIdList = static_cast<JOBOBJECT_BASIC_PROCESS_ID_LIST*>(malloc(jobProcessStructSize)); if (jobProcessIdList) { SecureZeroMemory(jobProcessIdList, jobProcessStructSize); jobProcessIdList->NumberOfProcessIdsInList = 1024; if (QueryInformationJobObject(NULL, JobObjectBasicProcessIdList, jobProcessIdList, jobProcessStructSize, NULL)) { int ok_processes = 0; for (DWORD i = 0; i < jobProcessIdList->NumberOfAssignedProcesses; i++) { ULONG_PTR processId = jobProcessIdList->ProcessIdList[i]; // is this the current process? if so that's ok if (processId == (ULONG_PTR)GetCurrentProcessId()) { ok_processes++; } else { // find the process name for this job process HANDLE hJobProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)processId); if (hJobProcess != NULL) { const int processNameBufferSize = 4096; LPTSTR processName = static_cast<LPTSTR>(malloc(sizeof(TCHAR) * processNameBufferSize)); if (processName) { SecureZeroMemory(processName, sizeof(TCHAR) * processNameBufferSize); if (GetProcessImageFileName(hJobProcess, processName, processNameBufferSize) > 0) { String pnStr(processName); // ignore conhost.exe (this hosts the al-khaser executable in a console) if (pnStr.find(String(L"\\Windows\\System32\\conhost.exe")) != std::string::npos) { ok_processes++; } } free(processName); } CloseHandle(hJobProcess); } } } // if we found other processes in the job other than the current process and conhost, report a problem foundProblem = ok_processes != jobProcessIdList->NumberOfAssignedProcesses; } free(jobProcessIdList); } return foundProblem; }
/** * @brief * Internal session memory usage decoding routine. * Accepts a job pointer. Returns the sum of all memory * consumed for all tasks executed by the job, in kilo bytes. * * NOTE: To retrieve a handle to any process in the system, * the calling process should have a privilege "SeDebugPrivilege". * A Win32 API OpenProcess() can be used in a calling process * to obtain any desired process handle in the system. * * In PBS, ena_privilege() function can be used to enable * SeDebugPrivilege for calling process. For pbs_mom process, * ena_privilege() has been used in it's main_thread() function. * * @param[in] pjob - pointer to job * * @return u_Long * @retval 0 - failure * @retval memory usage of all the processes of a job - failure */ static u_Long mem_sum(job *pjob) { u_Long mem = 0; int nps = 0; DWORD i; HANDLE hProcess; DWORD pidlistsize; DWORD nwspages; SYSTEM_INFO si; PJOBOBJECT_BASIC_PROCESS_ID_LIST pProcessList; JOBOBJECT_BASIC_ACCOUNTING_INFORMATION ji; pbs_task *ptask = NULL; BOOL is_process_in_job = FALSE; /* Get the system info */ GetSystemInfo(&si); /* Get the number of processes embedded in the job */ if (pjob->ji_hJob != NULL && QueryInformationJobObject(pjob->ji_hJob, JobObjectBasicAccountingInformation, &ji, sizeof(ji), NULL)) { nps = ji.TotalProcesses; } if (nps == 0) { pjob->ji_flags |= MOM_NO_PROC; return 0; } /* Compute the size of pid list */ pidlistsize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (nps-1) * sizeof(DWORD); pProcessList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) malloc(pidlistsize); if (pProcessList == NULL) { log_err(-1, "mem_sum:", "memory allocation failed"); return (0); } pProcessList->NumberOfAssignedProcesses = nps; pProcessList->NumberOfProcessIdsInList = 0; /* Get the pid list */ if (pjob->ji_hJob != NULL) QueryInformationJobObject(pjob->ji_hJob, JobObjectBasicProcessIdList, pProcessList, pidlistsize, NULL); /* * Traverse through each process and find the * memory used by that process during its execution. */ for (i = 0; i < (pProcessList->NumberOfProcessIdsInList); i++) { hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pProcessList->ProcessIdList[i]); if (hProcess != NULL) { (void)QueryWorkingSet(hProcess, &nwspages, sizeof(nwspages)); mem += nwspages * (si.dwPageSize >> 10); CloseHandle(hProcess); } }
/** * * @brief * Is the process part of Windows job object. * * @param[in] hProc - handle to process * @param[in] hJob - handle to job * @param[out] p_is_process_in_job - pointer to store a bool value indicating * whether the process is part of the Windows job object * * @return void **/ void IsProcessInJob(HANDLE hProc, HANDLE hJob, BOOL *p_is_process_in_job) { int nps = 0; DWORD i = 0; DWORD pidlistsize = 0; DWORD pid = 0; PJOBOBJECT_BASIC_PROCESS_ID_LIST pProcessList; JOBOBJECT_BASIC_ACCOUNTING_INFORMATION ji; if (hJob == INVALID_HANDLE_VALUE || hJob == NULL || hProc == INVALID_HANDLE_VALUE || hProc == NULL) { *p_is_process_in_job = FALSE; return; } /* Get the number of processes embedded in the job */ if (QueryInformationJobObject(hJob, JobObjectBasicAccountingInformation, &ji, sizeof(ji), NULL)) { nps = ji.TotalProcesses; } if (nps == 0) { *p_is_process_in_job = FALSE; return; } /* Compute the size of pid list */ pidlistsize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (nps-1) * sizeof(DWORD); pProcessList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) malloc(pidlistsize); if (pProcessList == NULL) { *p_is_process_in_job = FALSE; return; } pProcessList->NumberOfAssignedProcesses = nps; pProcessList->NumberOfProcessIdsInList = 0; /* Get the pid list */ if (FALSE == QueryInformationJobObject(hJob, JobObjectBasicProcessIdList, pProcessList, pidlistsize, NULL)) return; /* * Traverse through each process and find the * memory used by that process during its execution. */ pid = GetProcessId(hProc); for (i = 0; i < (pProcessList->NumberOfProcessIdsInList); i++) { if (pProcessList->ProcessIdList[i] == pid) { free(pProcessList); *p_is_process_in_job = TRUE; return; } } free(pProcessList); *p_is_process_in_job = FALSE; return; }
HRESULT CNodeApplicationManager::Initialize(IHttpContext* context) { HRESULT hr; BOOL isInJob, createJob; JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo; if (this->initialized) { return S_OK; } ErrorIf(NULL == (this->eventProvider = new CNodeEventProvider()), ERROR_NOT_ENOUGH_MEMORY); CheckError(this->eventProvider->Initialize()); ErrorIf(NULL != this->asyncManager, ERROR_INVALID_OPERATION); ErrorIf(NULL == (this->asyncManager = new CAsyncManager()), ERROR_NOT_ENOUGH_MEMORY); CheckError(this->asyncManager->Initialize(context)); ErrorIf(NULL == (this->fileWatcher = new CFileWatcher()), ERROR_NOT_ENOUGH_MEMORY); CheckError(this->fileWatcher->Initialize(context)); // determine whether node processes should be created in a new job object // or whether current job object is adequate; the goal is to kill node processes when // the IIS worker process is killed while preserving current job limits, if any ErrorIf(!IsProcessInJob(GetCurrentProcess(), NULL, &isInJob), HRESULT_FROM_WIN32(GetLastError())); if (!isInJob) { createJob = TRUE; } else { ErrorIf(!QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &jobInfo, sizeof jobInfo, NULL), HRESULT_FROM_WIN32(GetLastError())); if (jobInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ) { createJob = TRUE; } else if(jobInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ) { createJob = FALSE; } else if(jobInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK ) { createJob = TRUE; this->breakAwayFromJobObject = TRUE; } else { createJob = TRUE; } } if (createJob) { ErrorIf(NULL == (this->jobObject = CreateJobObject(NULL, NULL)), HRESULT_FROM_WIN32(GetLastError())); RtlZeroMemory(&jobInfo, sizeof jobInfo); jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; ErrorIf(!SetInformationJobObject(this->jobObject, JobObjectExtendedLimitInformation, &jobInfo, sizeof jobInfo), HRESULT_FROM_WIN32(GetLastError())); } this->initialized = TRUE; this->GetEventProvider()->Log(L"iisnode initialized the application manager", WINEVENT_LEVEL_INFO); return S_OK; Error: this->GetEventProvider()->Log(L"iisnode failed to initialize the application manager", WINEVENT_LEVEL_ERROR); if (NULL != this->asyncManager) { delete this->asyncManager; this->asyncManager = NULL; } if (NULL != this->jobObject) { CloseHandle(this->jobObject); this->jobObject = NULL; } if (NULL != this->fileWatcher) { delete this->fileWatcher; this->fileWatcher = NULL; } return hr; }
static void run_child(wchar_t * cmdline) { HANDLE job; JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; DWORD rc; BOOL ok; STARTUPINFOW si; PROCESS_INFORMATION pi; #if defined(_WINDOWS) // When explorer launches a Windows (GUI) application, it displays // the "app starting" (the "pointer + hourglass") cursor for a number // of seconds, or until the app does something UI-ish (eg, creating a // window, or fetching a message). As this launcher doesn't do this // directly, that cursor remains even after the child process does these // things. We avoid that by doing a simple post+get message. // See http://bugs.python.org/issue17290 and // https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running MSG msg; PostMessage(0, 0, 0, 0); GetMessage(&msg, 0, 0, 0); #endif job = CreateJobObject(NULL, NULL); ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation, &info, sizeof(info), &rc); if (!ok || (rc != sizeof(info)) || !job) error(RC_CREATE_PROCESS, L"Job information querying failed"); info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, sizeof(info)); if (!ok) error(RC_CREATE_PROCESS, L"Job information setting failed"); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); ok = safe_duplicate_handle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput); if (!ok) error(RC_NO_STD_HANDLES, L"stdin duplication failed"); ok = safe_duplicate_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput); if (!ok) error(RC_NO_STD_HANDLES, L"stdout duplication failed"); ok = safe_duplicate_handle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError); if (!ok) error(RC_NO_STD_HANDLES, L"stderr duplication failed"); ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE); if (!ok) error(RC_CREATE_PROCESS, L"control handler setting failed"); si.dwFlags = STARTF_USESTDHANDLES; ok = CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); if (!ok) error(RC_CREATE_PROCESS, L"Unable to create process using '%ls'", cmdline); AssignProcessToJobObject(job, pi.hProcess); CloseHandle(pi.hThread); WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE); ok = GetExitCodeProcess(pi.hProcess, &rc); if (!ok) error(RC_CREATE_PROCESS, L"Failed to get exit code of process"); exit(rc); }
/*! Iterates over all the threads associated with this job (as determined by * mJobHandle), and runs the given callback function on them. The callback * function need not close the handle to the thread. * * \param callback The callback function to run on each thread. * * \par A few notes on the necessity and implementation of IterateThreads: * Before going to far into this function, you must realize that as much as one * may hope to the contrary, windows has no concept of mass process management. * If you want to do something to multiple processes or threads, windows * provides a few mediocre iteration methods, but beyond that, you're on your * own to find the processes and/or threads and work with them one at a time. * The following function was written for that exact purpose, it searches * through the list of all threads present in the entire system, finds the ones * whose parent process is part of the job object and runs the given callback * function on the thread. * * \par * The reason this is written on the thread level as opposed to the process * level (which would have been much easier), is because windows has no concept * of suspending a process, it can only suspend threads. Since the ability to * suspend and resume jobs was something that I wanted in GamessQ, it has to be * done on the tread level. When it comes to terminating processes, simply * terminating all their threads works just as well as terminating the process * via windows TerminatProcess function. * * \note * As clumsy as this method may seem, and as much as you may think and hope * that there MUST be a better way to do it, there really isn't. So far as I * have been able to find (and I searched for a while) this is the cleanest, * most efficient way to handle windows processes and threads in mass. * * \sa mJobHandle */ bool WindowsJob::IterateThreads(bool (*callback)(HANDLE)) { // We'll need this for later bool retVal = true; // get all the process ids for the job object JOBOBJECT_BASIC_PROCESS_ID_LIST *info; int num = mNumProcessors + 15; // make sure there's enough space int size = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + num * sizeof(DWORD); info = (JOBOBJECT_BASIC_PROCESS_ID_LIST *)malloc(size); ZeroMemory(info, size); info->NumberOfAssignedProcesses = num; if (! QueryInformationJobObject(mJobHandle, JobObjectBasicProcessIdList, info, size, NULL)) { LOG_ERROR("QueryInformationJobObject"); return false; } num = info->NumberOfProcessIdsInList; // get a thread snapshot HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); THREADENTRY32 entry; ZeroMemory(&entry, sizeof(entry)); entry.dwSize = sizeof(entry); if (! Thread32First(snapshot, &entry)) { LOG_ERROR("Thread32First"); CloseHandle(snapshot); return false; } // iterate through all the threads in the system. bool more = true; while (more) { // search through the job ids for (int i = 0; i < num; i ++) { // if this thread's parent is in the list, run the callback on it if (entry.th32OwnerProcessID == info->ProcessIdList[i]) { HANDLE threadHandle = OpenThread(THREAD_TERMINATE | THREAD_SUSPEND_RESUME , false, entry.th32ThreadID); if (! threadHandle) { LOG_ERROR("OpenThread"); } else { retVal = retVal && callback(threadHandle); } CloseHandle(threadHandle); break; } } // get the next thread, and make sure it's valid ZeroMemory(&entry, sizeof(entry)); entry.dwSize = sizeof(entry); if (! Thread32Next(snapshot, &entry)) { if (GetLastError() == ERROR_NO_MORE_FILES) { more = false; } else { LOG_ERROR("Thread32Next"); free(info); CloseHandle(snapshot); return false; } } } // clean up free(info); CloseHandle(snapshot); return retVal; }