1789 lines
89 KiB
C++
1789 lines
89 KiB
C++
|
/*======================================================================================//
|
||
|
| //
|
||
|
|Copyright (c) 1998, 1999 Sequent Computer Systems, Incorporated //
|
||
|
| //
|
||
|
|Description: //
|
||
|
| //
|
||
|
|---------------------------------------------------------------------------------------//
|
||
|
| This file implements the CProcConMgr class methods defined in ProcConSvc.h //
|
||
|
|---------------------------------------------------------------------------------------//
|
||
|
| //
|
||
|
|Created: //
|
||
|
| //
|
||
|
| Jarl McDonald 07-98 //
|
||
|
| //
|
||
|
|Revision History: //
|
||
|
| //
|
||
|
|=======================================================================================*/
|
||
|
#include "ProcConSvc.h"
|
||
|
#include <psapi.h>
|
||
|
|
||
|
// Constructor
|
||
|
// Note: this function runs as part of service start so keep it quick!
|
||
|
CProcConMgr::CProcConMgr( PCContext *ctxt ) :
|
||
|
m_mediatorEvent( ctxt->mediatorEvent ),
|
||
|
m_mediatorTable( ctxt->mediatorTable ),
|
||
|
m_cPC( *ctxt->cPC ), m_cDB( *ctxt->cDB ),
|
||
|
m_rawProcList( NULL ), m_rawProcCount( 0 ),
|
||
|
m_procManagedCount( 0 ), m_jobManagedCount( 0 ),
|
||
|
m_procAnchor( NULL ), m_jobAnchor( NULL ),
|
||
|
m_reportThread( NULL ),
|
||
|
m_systemMask( 1 ), m_sequencer( 1 )
|
||
|
{
|
||
|
ULONG_PTR mask;
|
||
|
|
||
|
InitializeCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
PCBuildAdminSecAttr( m_secAttr );
|
||
|
|
||
|
GetProcessAffinityMask( GetCurrentProcess(), &mask, &m_systemMask );
|
||
|
|
||
|
m_assocPort.CompletionPort = ctxt->completionPort;
|
||
|
if ( m_assocPort.CompletionPort ) {
|
||
|
m_reportThread = CreateThread( NULL, 10000, &JobReporter, this, 0, NULL );
|
||
|
if ( !m_reportThread ) {
|
||
|
PCLogUnExError( TEXT("JobReporter"), TEXT("CreateThread") );
|
||
|
CloseHandle( m_assocPort.CompletionPort );
|
||
|
m_assocPort.CompletionPort = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Destructor
|
||
|
CProcConMgr::~CProcConMgr( void )
|
||
|
{
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
if ( m_rawProcList ) delete [] m_rawProcList;
|
||
|
for ( ManagedProc *nextProc, *proc = m_procAnchor; proc; proc = nextProc ) {
|
||
|
nextProc = proc->next;
|
||
|
delete proc;
|
||
|
}
|
||
|
for ( ManagedJob *nextJob, *job = m_jobAnchor; job; job = nextJob ) {
|
||
|
nextJob = job->next;
|
||
|
delete job;
|
||
|
}
|
||
|
m_jobAnchor = NULL;
|
||
|
m_procAnchor = NULL;
|
||
|
m_jobManagedCount = m_procManagedCount = 0;
|
||
|
|
||
|
PCFreeSecAttr( m_secAttr );
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
DeleteCriticalSection( &m_mgCSMgrLists );
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// Function to determine if all CProcConMgr initial conditions have been met //
|
||
|
// Input: None //
|
||
|
// Returns: TRUE if ready, FALSE if not //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
BOOL CProcConMgr::ReadyToRun( void )
|
||
|
{
|
||
|
return m_secAttr.lpSecurityDescriptor != NULL;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// The Process Management thread //
|
||
|
// Input: nothing //
|
||
|
// Returns: 0 if successful, 1 if not //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
PCULONG32 CProcConMgr::Run( void )
|
||
|
{
|
||
|
|
||
|
HANDLE objList[] = { m_cPC.GetShutEvent(), m_cDB.GetDbEvent() };
|
||
|
|
||
|
// Trigger database load...
|
||
|
if ( m_cDB.LoadRules( m_cDB.LOADFLAG_ALL_RULES ) != ERROR_SUCCESS )
|
||
|
return 1;
|
||
|
|
||
|
// ProcCon main process loop...
|
||
|
for ( ;; ) {
|
||
|
|
||
|
// Discover all running processes/jobs...
|
||
|
Discover();
|
||
|
|
||
|
// Apply management rules...
|
||
|
Manage();
|
||
|
|
||
|
PCULONG32 event = WaitForMultipleObjects( ENTRY_COUNT(objList), objList, FALSE, m_cDB.GetPollDelay() );
|
||
|
if ( event == WAIT_FAILED ) {
|
||
|
PCLogUnExError( TEXT("PCManager"), TEXT("Wait") );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// if wait ended due to db update or timeout we loop, for shutdown we stop looping...
|
||
|
if ( event - WAIT_OBJECT_0 == 0 ) { // we got shutdown event
|
||
|
if ( m_assocPort.CompletionPort )
|
||
|
PostQueuedCompletionStatus( m_assocPort.CompletionPort, 0, 0, NULL );
|
||
|
Sleep( 1000 );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} // end for
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// Job Object Completion Port: function to listen to the job completion port and handle msgs. //
|
||
|
// Input: pointer to CProcConMgr class (function is static) //
|
||
|
// Returns: nothing -- runs until shutdown requested //
|
||
|
// Note: this is a static member function and thus does not have a 'this' context. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
PCULONG32 __stdcall CProcConMgr::JobReporter( void *inPtr ) {
|
||
|
|
||
|
CProcConMgr &cMgr = *((CProcConMgr *) inPtr);
|
||
|
|
||
|
OVERLAPPED *data;
|
||
|
DWORD msgId;
|
||
|
ULONG_PTR compKey;
|
||
|
|
||
|
while ( GetQueuedCompletionStatus( cMgr.GetComplPort(), &msgId, &compKey, &data, INFINITE ) ) {
|
||
|
|
||
|
if ( !compKey ) break; // shutdown requested
|
||
|
|
||
|
EnterCriticalSection( cMgr.GetListCSPtr() );
|
||
|
|
||
|
for ( ManagedJob *job = cMgr.GetJobAnchor(); job && compKey != job->compKey; job = job->next ) ;
|
||
|
|
||
|
// If we found the job, proceed to handle the event...
|
||
|
if ( job ) {
|
||
|
TCHAR value[24], pid[24];
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job->fullJobName, value, pid };
|
||
|
|
||
|
// Make sure we have the latest job definition...
|
||
|
PCJobDef *jobDef;
|
||
|
if ( cMgr.m_cDB.GetJobMgmtDefs( &jobDef, &job->jName ) ) { // Returns 0 if job definition not found
|
||
|
cMgr.UpdateJobEntry( *job, jobDef );
|
||
|
delete [] jobDef;
|
||
|
}
|
||
|
|
||
|
// Make sure we have the latest job stats...
|
||
|
cMgr.UpdateJobObjInfo( *job );
|
||
|
|
||
|
switch ( msgId ) {
|
||
|
//******** job time limit hit, data is NULL
|
||
|
case JOB_OBJECT_MSG_END_OF_JOB_TIME: {
|
||
|
_i64tot( PCLargeIntToInt64( job->JOExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit ) / 10000, value, 10 );
|
||
|
// Check for termination or just post...
|
||
|
DWORD rc = WaitForSingleObject( job->jobHandle, 0 );
|
||
|
// If we're only posting the limit exceeded, issue msg, suppress future msgs...
|
||
|
if ( rc == WAIT_TIMEOUT ) {
|
||
|
PCLogMessage( PC_SERVICE_JOB_HIT_TIME_LIMIT_NOTERM, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
job->timeExceededReported = TRUE; // suppress additional reports
|
||
|
}
|
||
|
// If we're posting the limit exceeded with all procs terminated, issue msg, clear state...
|
||
|
else {
|
||
|
PCLogMessage( PC_SERVICE_JOB_HIT_TIME_LIMIT_TERMINATED, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
job->curJobTimeLimitCNS = 0; // clear limit to force update
|
||
|
cMgr.ApplyJobMgmt( *job ); // go re-apply time limit to reset
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
//******** proc time limit hit, proc already being terminated, data = PID
|
||
|
case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
|
||
|
_i64tot( (ULONG_PTR) data, pid, 10 );
|
||
|
_i64tot( PCLargeIntToInt64( job->JOExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit ) / 10000, value, 10 );
|
||
|
PCLogMessage( PC_SERVICE_PROC_HIT_TIME_LIMIT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
break;
|
||
|
//******** proc count limit hit, data = NULL
|
||
|
case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
|
||
|
_ltot( job->JOExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit, value, 10 );
|
||
|
PCLogMessage( PC_SERVICE_JOB_HIT_COUNT_LIMIT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
break;
|
||
|
//******** proc count hit 0, data = NULL
|
||
|
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
|
||
|
if ( job->jobParms.mFlags & PCMFLAG_END_JOB_WHEN_EMPTY )
|
||
|
cMgr.JobIsEmpty( job ); // job should be deleted after this call!
|
||
|
break;
|
||
|
//******** process created in job or added to job, data = PID
|
||
|
case JOB_OBJECT_MSG_NEW_PROCESS: {
|
||
|
PID_VALUE newPid = (ULONG_PTR) data;
|
||
|
// See if the proc is one of our managed procs (will be if entry is for adding, not creating)...
|
||
|
for ( ManagedProc *Proc = cMgr.GetProcAnchor();
|
||
|
Proc && Proc->pStats.pid != newPid;
|
||
|
Proc = Proc->next ) ;
|
||
|
// if not found, not in a job, or not in this job report on its creation...
|
||
|
if ( !Proc || !Proc->reportAdd ) {
|
||
|
TCHAR procName[PROC_NAME_LEN + 1], imageName[IMAGE_NAME_LEN + 1], pid[32];
|
||
|
const TCHAR *pmsgs[] = { PROCCON_SVC_DISP_NAME, job->fullJobName, procName, pid, imageName };
|
||
|
_i64tot( (ULONG_PTR) data, pid, 10 );
|
||
|
// Make sure new proc is in our raw proc list...
|
||
|
cMgr.Discover();
|
||
|
// Now locate the proc by PID...
|
||
|
for ( PCULONG32 i = 0;
|
||
|
i < cMgr.GetRawProcCount() && newPid != cMgr.GetRawProcEntry( i ).pId;
|
||
|
++i ) ;
|
||
|
// If found, extract process and image names...
|
||
|
if ( i < cMgr.GetRawProcCount() ) {
|
||
|
_tcscpy( procName, cMgr.GetRawProcEntry( i ).pName );
|
||
|
_tcscpy( imageName, cMgr.GetRawProcEntry( i ).imageName );
|
||
|
}
|
||
|
// If not found (process that terminated already), use "unknown" (not localized)...
|
||
|
else {
|
||
|
_tcscpy( procName, PROCCON_UNKNOWN_PROCESS );
|
||
|
_tcscpy( imageName, PROCCON_UNKNOWN_PROCESS );
|
||
|
}
|
||
|
// Now report that this proc was created in ths job...
|
||
|
PCLogMessage( PC_SERVICE_ADD_NONPC_PROC_TO_JOB, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(pmsgs), pmsgs );
|
||
|
}
|
||
|
else if ( Proc->reportAdd ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, Proc->pName, Proc->pidAsString,
|
||
|
Proc->imageName, job->fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_ADD_PROC_TO_JOB, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
//******** process exiting job, data = PID of exiting process
|
||
|
case JOB_OBJECT_MSG_EXIT_PROCESS:
|
||
|
break;
|
||
|
//******** process exiting job, data = PID of exiting process
|
||
|
case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
|
||
|
break;
|
||
|
//******** proc memory limit hit, data = PID
|
||
|
case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:
|
||
|
if ( cMgr.NotTooSoon( job, MEM_REJECT_REPORT_LIMIT ) ) {
|
||
|
_i64tot( (ULONG_PTR) data, pid, 10 );
|
||
|
_i64tot( job->JOExtendedLimitInfo.ProcessMemoryLimit, value, 10 );
|
||
|
PCLogMessage( PC_SERVICE_PROC_HIT_MEMORY_LIMIT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
break;
|
||
|
//******** job memory limit hit, data = PID of process that attempted to exceed limit
|
||
|
case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:
|
||
|
if ( cMgr.NotTooSoon( job, MEM_REJECT_REPORT_LIMIT ) ) {
|
||
|
_i64tot( (ULONG_PTR) data, pid, 10 );
|
||
|
_i64tot( job->JOExtendedLimitInfo.JobMemoryLimit, value, 10 );
|
||
|
PCLogMessage( PC_SERVICE_JOB_HIT_MEMORY_LIMIT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
} // end switch
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection( cMgr.GetListCSPtr() );
|
||
|
}
|
||
|
|
||
|
if ( !cMgr.m_cPC.GotShutdown() )
|
||
|
PCLogUnExError( TEXT("JobReporter"), TEXT("GetQueuedCompletionStatus") );
|
||
|
CloseHandle( cMgr.GetComplPort() );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to test memory exceeded reporting inteval is within limit seconds //
|
||
|
// Input: pointer to managed job structure, limit seconds //
|
||
|
// Returns: TRUE if not too soon (interval exceeds limit), FALSE otherwise //
|
||
|
// Note: if the tick ctr has wrapped, we simply treat it is as 'not too soon' and unwrap //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
BOOL CProcConMgr::NotTooSoon( ManagedJob *job, PCULONG32 limit ) {
|
||
|
PCULONG32 now = GetTickCount();
|
||
|
BOOL rc = FALSE;
|
||
|
if ( now < job->memRejectReportTime || ((now - job->memRejectReportTime) / 1000 >= limit) ) {
|
||
|
rc = TRUE;
|
||
|
job->memRejectReportTime = now;
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to close a job object when no longer in use //
|
||
|
// Input: pointer to managed job structure //
|
||
|
// Returns: nothing //
|
||
|
// Note: Caller must hold manager list critical section. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::JobIsEmpty( ManagedJob *job ) {
|
||
|
UpdateJobObjInfo( *job ); // Update stats incl proc count
|
||
|
if ( job->JOBasicAndIoAcctInfo.BasicInfo.ActiveProcesses ) return; // done - no longer empty!
|
||
|
m_mediatorTable->SvcCloseEntry( job->fullJobName );
|
||
|
ManagedJob *lastJob, *thisJob;
|
||
|
for ( lastJob = thisJob = m_jobAnchor; thisJob; lastJob = thisJob, thisJob = thisJob->next ) {
|
||
|
if ( thisJob == job ) {
|
||
|
if ( lastJob == m_jobAnchor ) m_jobAnchor = thisJob->next;
|
||
|
else lastJob->next = thisJob->next;
|
||
|
delete job;
|
||
|
--m_jobManagedCount;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define HIGHEST_SYSTEM_PID 31
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to discover all running processes and build a 'raw' list of them //
|
||
|
// Input: none //
|
||
|
// Returns: nothing -- the current raw process list is deleted and rebuilt //
|
||
|
// Note: A 'raw' list simply consists of an entry per process containing at least the PID //
|
||
|
// and the ProcCon process name. This list is used by the management fcns. //
|
||
|
// Note 2: There are several choices for discovering what processes are running: //
|
||
|
// Performance data registry interface (NT 3 and beyond), //
|
||
|
// Performance data helper (NT 3 and beyond), //
|
||
|
// PSAPI (NT 4 and beyond), //
|
||
|
// Tool help library (NT 5). //
|
||
|
// //
|
||
|
// This function uses the PSAPI. Originally toolhelp32 was used but was too buggy. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::Discover( void ) {
|
||
|
|
||
|
DWORD bytesListed, count, listCount = 512; // first pass is double this count
|
||
|
DWORD *procList = NULL; // must track type used in EnumProcesses
|
||
|
|
||
|
// Loop allocating (increasingly larger) buffer and enumerating processes until we see them all...
|
||
|
do {
|
||
|
// Free previous buffer, if any
|
||
|
if ( procList ) delete [] procList;
|
||
|
|
||
|
// Get a buffer we hope will be big enough (double previous size)...
|
||
|
listCount *= 2;
|
||
|
procList = new DWORD[listCount];
|
||
|
|
||
|
// If complete failure, skip process enumeration...
|
||
|
if ( !procList ) {
|
||
|
PCLogNoMemory( TEXT("AllocTempRawProcList"), listCount * sizeof(*procList) ); // 7/28/2000 bugfix to report correct size
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Snapshot the process space...
|
||
|
if ( !EnumProcesses( procList, listCount * sizeof(*procList), &bytesListed ) ) {
|
||
|
PCLogUnExError( TEXT("PCDiscover"), TEXT("EnumProcesses") );
|
||
|
delete [] procList;
|
||
|
return;
|
||
|
}
|
||
|
count = bytesListed / sizeof(*procList);
|
||
|
|
||
|
} while ( count == listCount );
|
||
|
|
||
|
// Reset old process info...
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
if ( m_rawProcList ) delete [] m_rawProcList;
|
||
|
m_rawProcCount = 0;
|
||
|
|
||
|
// Allocate new raw process info list...
|
||
|
m_rawProcList = new RawProcList[count];
|
||
|
if ( !m_rawProcList ) {
|
||
|
PCLogNoMemory( _T("AllocRawProcList"), sizeof(RawProcList) * count );
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
delete [] procList;
|
||
|
return;
|
||
|
}
|
||
|
memset( m_rawProcList, 0, sizeof(RawProcList) * count );
|
||
|
|
||
|
// Walk the snapshot to extract data and determine process names from path names...
|
||
|
TCHAR pathAndFile[MAX_PATH] = _T("");
|
||
|
for ( DWORD proc = 0; proc < count; ++proc ) {
|
||
|
|
||
|
HANDLE hProc = NULL;
|
||
|
if (procList[proc] == 0) {
|
||
|
_tcscpy( pathAndFile, PROCCON_SYSTEM_IDLE );
|
||
|
}
|
||
|
else if (procList[proc] <= HIGHEST_SYSTEM_PID) {
|
||
|
_tcscpy( pathAndFile, PROCCON_SYSTEM_PROCESS );
|
||
|
}
|
||
|
else {
|
||
|
hProc = OpenProcess( PROCESS_QUERY_INFORMATION // to get counters, aff, prio, etc.
|
||
|
+ PROCESS_VM_READ, // to get module information
|
||
|
FALSE, procList[proc] );
|
||
|
if ( hProc ) {
|
||
|
if ( !GetModuleFileNameEx( hProc, NULL, pathAndFile, ENTRY_COUNT( pathAndFile ) ) &&
|
||
|
!GetModuleBaseName( hProc, NULL, pathAndFile, ENTRY_COUNT( pathAndFile ) ) ) {
|
||
|
PCLogUnExError( procList[proc], _T("GetModuleFile/BaseName") );
|
||
|
}
|
||
|
}
|
||
|
else continue;
|
||
|
}
|
||
|
|
||
|
// Save process data and assign process name (alias)...
|
||
|
RawProcList &le = m_rawProcList[m_rawProcCount];
|
||
|
le.pId = procList[proc];
|
||
|
m_cDB.AssignProcName( pathAndFile, &le.pName, &le.imageName );
|
||
|
|
||
|
// If name assigned (not hidden or unretrievable) include this in result...
|
||
|
if ( *le.pName ) ++m_rawProcCount;
|
||
|
|
||
|
// If we have an open handle, retrieve additional information, then close
|
||
|
if (hProc) {
|
||
|
GetProcessTimes( hProc, &le.createTime, &le.exitTime, &le.kernelTime, &le.userTime );
|
||
|
le.actualPriority = PCMapPriorityToPC( GetPriorityClass( hProc ) );
|
||
|
ULONG_PTR aff;
|
||
|
if ( !GetProcessAffinityMask( hProc, &aff, &m_systemMask ) )
|
||
|
PCLogUnExError( procList[proc], TEXT("GetAffinityMask") );
|
||
|
le.actualAffinity = aff;
|
||
|
CloseHandle( hProc );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] procList;
|
||
|
|
||
|
// Sort the raw process list...
|
||
|
qsort( m_rawProcList, m_rawProcCount, sizeof(RawProcList), CompareRawProcList );
|
||
|
|
||
|
// Update job information...
|
||
|
for ( ManagedJob *job = m_jobAnchor; job; job = job->next )
|
||
|
UpdateJobObjInfo( *job );
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to apply ProcCon management rules to the system //
|
||
|
// Input: nothing //
|
||
|
// Returns: nothing -- process rules are applied as appropriate //
|
||
|
// Note: entry conditions: //
|
||
|
// o Running processes are listed in m_rawProcList (unless NULL), sorted by PID. //
|
||
|
// o Managed processes from last pass are listed in m_procManagedList, unless NULL. //
|
||
|
// o Managed jobs from last pass are listed in m_jobManagedList, unless NULL. //
|
||
|
// o Defined jobs and processes are available from the DB component. //
|
||
|
// o Counts of entries are in m_rawProcCount, m_procManagedCount, m_jobManagedCount. //
|
||
|
// o No critical sections are held. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::Manage( void ) {
|
||
|
|
||
|
// Step 1 -- get the current management definitions we are to work from...
|
||
|
PCJobDef *jobDefs = NULL, *jHit;
|
||
|
PCProcDef *procDefs = NULL, *pHit;
|
||
|
PCULONG32 numJobDefs = m_cDB.GetJobMgmtDefs( &jobDefs );
|
||
|
PCULONG32 numProcDefs = m_cDB.GetProcMgmtDefs( &procDefs );
|
||
|
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
// Step 2 -- allocate max possible space for a list of procs to be managed...
|
||
|
ManagedProcItem *doProc = new ManagedProcItem[m_rawProcCount];
|
||
|
PCULONG32 numProc = 0;
|
||
|
if ( !doProc ) {
|
||
|
PCLogNoMemory( TEXT("AllocManagedProcList"), sizeof(ManagedProcItem) * m_rawProcCount );
|
||
|
delete [] jobDefs;
|
||
|
delete [] procDefs;
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Step 3 -- locate processes in the current proc list needing management, place in managed list...
|
||
|
PCULONG32 proc;
|
||
|
if ( numProcDefs ) {
|
||
|
for ( proc = 0; proc < m_rawProcCount; ++proc ) {
|
||
|
// see if we have a definition for this proc name...
|
||
|
pHit = (PCProcDef *) bsearch( m_rawProcList[proc].pName,
|
||
|
procDefs,
|
||
|
numProcDefs,
|
||
|
sizeof(PCProcDef),
|
||
|
CompareProcName );
|
||
|
// A definition exists so save in our list...
|
||
|
if ( pHit ) {
|
||
|
doProc[numProc].pStats.pid = m_rawProcList[proc].pId;
|
||
|
doProc[numProc].pStats.createTime = PCFileTimeToInt64( m_rawProcList[proc].createTime );
|
||
|
doProc[numProc].pStats.TotalUserTime = PCFileTimeToInt64( m_rawProcList[proc].userTime );
|
||
|
doProc[numProc].pStats.TotalKernelTime = PCFileTimeToInt64( m_rawProcList[proc].kernelTime );
|
||
|
doProc[numProc].pDef = pHit;
|
||
|
memcpy( doProc[numProc].imageName, m_rawProcList[proc].imageName, sizeof(doProc[0].imageName) );
|
||
|
++numProc;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Step 4 -- For each proc to be managed:
|
||
|
// a. Open process handle,
|
||
|
// b. Get process create time to determine if process is 'new' or 'old',
|
||
|
// c. If 'old', locate existing ManagedProc and re-apply management if needed.
|
||
|
// d. If 'new', create ManagedProc/ManagedJob apply management rules.
|
||
|
// e. Close handle(s).
|
||
|
//
|
||
|
// Note: At this point we have snapshots of all data we need and hold no critical sections.
|
||
|
// This will permit API calls, etc. to proceed without delay.
|
||
|
|
||
|
// Bump sequencer so we can tell which entries have gone away...
|
||
|
++m_sequencer;
|
||
|
|
||
|
// For each proc to manage -- do it...
|
||
|
HANDLE hProc = NULL;
|
||
|
for ( proc = 0; proc < numProc; ++proc ) {
|
||
|
BOOL isManaged = FALSE;
|
||
|
|
||
|
if ( hProc ) CloseHandle( hProc );
|
||
|
|
||
|
PID_VALUE pid = doProc[proc].pStats.pid;
|
||
|
PCProcDef *def = doProc[proc].pDef;
|
||
|
|
||
|
// Open process so we can manipulate it...
|
||
|
hProc = OpenProcess( PROCESS_SET_QUOTA | PROCESS_TERMINATE | PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION,
|
||
|
TRUE, (DWORD) pid ); // OpenProcess always uses 32-bit PID, even in Win64. May change!
|
||
|
// If we can't open it, report error unless process is simply gone, then ignore entry...
|
||
|
if ( !hProc ) {
|
||
|
if ( GetLastError() != ERROR_INVALID_PARAMETER )
|
||
|
PCLogUnExError( pid, TEXT("OpenProcess") );
|
||
|
continue;
|
||
|
}
|
||
|
// We were able to open the process -- find or allocate a tracking entry for it.
|
||
|
// The process is the same only if the pid and create time are the same...
|
||
|
ManagedProc *pMProc = FindProcEntry( pid, doProc[proc].pStats.createTime );
|
||
|
BOOL newProc = pMProc == NULL;
|
||
|
if ( newProc ) {
|
||
|
pMProc = new ManagedProc( def->procName, pid, doProc[proc].pStats.createTime );
|
||
|
if ( !pMProc ) {
|
||
|
PCLogNoMemory( TEXT("AllocManagedProc"), sizeof(ManagedProc) );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
pMProc->pStats.TotalUserTime = doProc[proc].pStats.TotalUserTime;
|
||
|
pMProc->pStats.TotalKernelTime = doProc[proc].pStats.TotalKernelTime;
|
||
|
|
||
|
// Get actual priority, affinity, and image name...
|
||
|
pMProc->actualPriority = PCMapPriorityToPC( GetPriorityClass( hProc ) );
|
||
|
ULONG_PTR aff;
|
||
|
GetProcessAffinityMask( hProc, &aff, &m_systemMask );
|
||
|
pMProc->actualAffinity = aff;
|
||
|
memcpy( pMProc->imageName, doProc[proc].imageName, sizeof(pMProc->imageName) );
|
||
|
|
||
|
// Update our tracking entry for this process...
|
||
|
UpdateProcEntry( *pMProc, *def, newProc );
|
||
|
|
||
|
// If process is to be part of a job...
|
||
|
TCHAR *job = def->memberOfJob;
|
||
|
if ( def->mFlags & PCMFLAG_APPLY_JOB_MEMBERSHIP && *job ) {
|
||
|
|
||
|
// Locate job definition...
|
||
|
jHit = (PCJobDef *) bsearch( job, jobDefs, numJobDefs, sizeof(PCJobDef), CompareJobName );
|
||
|
|
||
|
// Locate job tracking entry if it exists...
|
||
|
ManagedJob *pMJob = FindJobEntry( job );
|
||
|
BOOL newJob = pMJob == NULL;
|
||
|
|
||
|
// If no entry found and we have a job definition, create new entry...
|
||
|
if ( newJob && jHit ) {
|
||
|
pMJob = new ManagedJob( job, m_mediatorTable );
|
||
|
if ( !pMJob )
|
||
|
PCLogNoMemory( TEXT("AllocManagedJob"), sizeof(ManagedJob) );
|
||
|
}
|
||
|
// If we now have a job entry, proceed to manage the process via the job......
|
||
|
if ( pMJob ) {
|
||
|
// If Proc is already in a job...
|
||
|
if ( pMProc->isInJob ) {
|
||
|
// if not in this job, say we can't move it (but only say it once)...
|
||
|
if ( pMJob != pMProc->pMJob ) {
|
||
|
if ( pMProc->pMJob && CompareJobName(pMJob->fullJobName, pMProc->lastAlreadyInJobErr) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME,
|
||
|
pMProc->pName, pMProc->pidAsString, pMProc->imageName,
|
||
|
pMJob->fullJobName, pMProc->pMJob->fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_ALREADY_IN_JOB, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
memcpy( pMProc->lastAlreadyInJobErr, pMJob->fullJobName, sizeof(pMProc->lastAlreadyInJobErr) );
|
||
|
}
|
||
|
}
|
||
|
// if in this job and job not updated this pass, just (re)set mgmt behavior...
|
||
|
else if ( pMJob->sequence != m_sequencer ) { // this just prevents multiple calls for the same job
|
||
|
UpdateJobEntry( *pMJob, jHit, newJob );
|
||
|
UpdateJobObjInfo( *pMJob );
|
||
|
ApplyJobMgmt( *pMJob );
|
||
|
}
|
||
|
isManaged = TRUE;
|
||
|
}
|
||
|
// Proc not already in any job...
|
||
|
else {
|
||
|
// If job object does not exist -- create it...
|
||
|
if ( !pMJob->jobHandle ) {
|
||
|
pMJob->jobHandle = CreateJobObject( &m_secAttr, pMJob->fullJobName );
|
||
|
if ( pMJob->jobHandle && GetLastError() != ERROR_ALREADY_EXISTS ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, pMJob->fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_CREATE_JOB, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
m_mediatorTable->SvcAddEntry( pMJob->fullJobName, pMJob->compKey, m_secAttr ); // adds or updates
|
||
|
|
||
|
}
|
||
|
if ( !pMJob->jobHandle )
|
||
|
PCLogUnExError( pMJob->fullJobName, TEXT("CreateJobObj") );
|
||
|
else {
|
||
|
// Update management parms with definition if any, apply mgmt to job obj, put proc in job...
|
||
|
UpdateJobEntry( *pMJob, jHit, newJob );
|
||
|
UpdateJobObjInfo( *pMJob );
|
||
|
ApplyJobMgmt( *pMJob );
|
||
|
if ( AssignProcessToJobObject( pMJob->jobHandle, hProc ) ) {
|
||
|
pMProc->pMJob = pMJob;
|
||
|
pMProc->isInJob = TRUE;
|
||
|
pMProc->reportAdd = TRUE;
|
||
|
isManaged = TRUE;
|
||
|
}
|
||
|
else if ( GetLastError() == ERROR_ACCESS_DENIED ) { // failed due to already in some other job
|
||
|
FULL_JOB_NAME errName = TEXT("-unknown-");
|
||
|
pMProc->pMJob = NULL; // set the job as 'unknown'
|
||
|
pMProc->isInJob = TRUE; // we assume it is in some job
|
||
|
// See if this proc is in a job of ours by matching pids...
|
||
|
for ( ManagedJob *tstJob = m_jobAnchor; tstJob; tstJob = tstJob->next ) {
|
||
|
if ( GetProcListForJob( *tstJob ) ) {
|
||
|
for ( PCUINT32 pndx = 0;
|
||
|
pndx < tstJob->JOProcListInfo->NumberOfProcessIdsInList;
|
||
|
++pndx ) {
|
||
|
if ( pMProc->pStats.pid == (PID_VALUE) tstJob->JOProcListInfo->ProcessIdList[pndx] ) {
|
||
|
memcpy( errName, tstJob->fullJobName, sizeof(errName) );
|
||
|
pMProc->pMJob = tstJob;
|
||
|
isManaged = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
delete [] ((UCHAR *) tstJob->JOProcListInfo);
|
||
|
tstJob->JOProcListInfo = NULL;
|
||
|
}
|
||
|
if ( isManaged ) break;
|
||
|
}
|
||
|
// If the job was not found or is not the job we expect, then report...
|
||
|
if ( pMProc->pMJob != pMJob &&
|
||
|
CompareJobName( pMJob->fullJobName, pMProc->lastAlreadyInJobErr ) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME,
|
||
|
pMProc->pName, pMProc->pidAsString, pMProc->imageName,
|
||
|
pMJob->fullJobName, errName };
|
||
|
PCLogMessage( PC_SERVICE_ALREADY_IN_JOB, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
memcpy( pMProc->lastAlreadyInJobErr, pMJob->fullJobName, sizeof(pMProc->lastAlreadyInJobErr) );
|
||
|
}
|
||
|
}
|
||
|
else if ( GetLastError() != ERROR_NOT_ENOUGH_QUOTA ) // this will be reported via Compl Port
|
||
|
PCLogUnExError( pid, TEXT("AssignProcToJobObj") );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If not managed as a job member for any reason, apply proc management rules...
|
||
|
if ( !isManaged )
|
||
|
ApplyProcMgmt( *pMProc, hProc );
|
||
|
}
|
||
|
|
||
|
// Clean up any open handles and delete jobs and procs that no longer exist...
|
||
|
if ( hProc ) CloseHandle( hProc );
|
||
|
DeleteOrphanProcEntries();
|
||
|
DeleteOrphanJobEntries();
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
delete [] doProc;
|
||
|
delete [] jobDefs;
|
||
|
delete [] procDefs;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to find a process entry in our list of currently managed processes //
|
||
|
// Input: PID and create time of process to find //
|
||
|
// Returns: pointer to found process entry or NULL if not found //
|
||
|
// Note: A process muct match PID and create time to be a match. //
|
||
|
// If just the PID matches, the PID has been reused and the old proc is gone. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
CProcConMgr::ManagedProc *CProcConMgr::FindProcEntry( PID_VALUE pid, __int64 &createTime ) {
|
||
|
for ( ManagedProc *p = m_procAnchor;
|
||
|
p && p->pStats.pid != pid;
|
||
|
p = p->next ) ;
|
||
|
if ( p && memcmp( &p->pStats.createTime, &createTime, sizeof(createTime) ) ) p = NULL;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to find a job entry in our list of currently managed jobs //
|
||
|
// Input: job name of job to find //
|
||
|
// Returns: pointer to found job entry or NULL if not found //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
CProcConMgr::ManagedJob *CProcConMgr::FindJobEntry( TCHAR *job, ManagedJob **plast ) {
|
||
|
for ( ManagedJob *p = m_jobAnchor, *lst = m_jobAnchor;
|
||
|
p && CompareJobName( p->jName, job );
|
||
|
lst = p, p = p->next ) ;
|
||
|
if ( plast ) *plast = lst;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to update a process entry in our list of currently managed processes //
|
||
|
// Input: pointer to list entry being updated, pointer to definition data, new flag //
|
||
|
// Returns: nothing -- cannot fail //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::UpdateProcEntry( ManagedProc &proc, PCProcDef &item, BOOL newProc ) {
|
||
|
if ( newProc ) {
|
||
|
proc.next = m_procAnchor;
|
||
|
m_procAnchor = &proc;
|
||
|
++m_procManagedCount;
|
||
|
}
|
||
|
if ( memcmp( &proc.procParms, &item, sizeof(proc.procParms) ) ) {
|
||
|
memcpy( &proc.procParms, &item, sizeof(proc.procParms) );
|
||
|
proc.passSkipFlags = 0;
|
||
|
}
|
||
|
proc.sequence = m_sequencer;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to update a job entry in our list of currently managed jobs //
|
||
|
// Input: pointer to list entry being updated, pointer to definition data, new flag //
|
||
|
// Returns: nothing -- cannot fail //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::UpdateJobEntry( ManagedJob &job, PCJobDef *item, BOOL newJob ) {
|
||
|
if ( newJob ) {
|
||
|
job.next = m_jobAnchor;
|
||
|
m_jobAnchor = &job;
|
||
|
++m_jobManagedCount;
|
||
|
}
|
||
|
if ( item && memcmp( &job.jobParms, item, sizeof(job.jobParms) ) ) {
|
||
|
memcpy( &job.jobParms, item, sizeof(job.jobParms) );
|
||
|
job.dataErrorFlags = job.timeExceededReported = 0;
|
||
|
}
|
||
|
job.sequence = m_sequencer;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to allocate and build a job object process list and hang it on the ManagedJob //
|
||
|
// Input: ptr to managed job //
|
||
|
// Returns: TRUE if successful, FALSE if not (error issued) //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
BOOL CProcConMgr::GetProcListForJob( ManagedJob &job ) {
|
||
|
|
||
|
DWORD actual, size, entryCount = 16;
|
||
|
|
||
|
for ( ;; ) {
|
||
|
delete [] ((UCHAR *) job.JOProcListInfo);
|
||
|
size = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (entryCount - 1) * sizeof(job.JOProcListInfo->ProcessIdList[0]);
|
||
|
job.JOProcListInfo = (JOBOBJECT_BASIC_PROCESS_ID_LIST *) new UCHAR[size];
|
||
|
if ( !job.JOProcListInfo ) {
|
||
|
PCLogNoMemory( TEXT("AllocJobProcList"), size );
|
||
|
job.lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( !QueryInformationJobObject( job.jobHandle, JobObjectBasicProcessIdList,
|
||
|
job.JOProcListInfo, size, &actual )
|
||
|
&& GetLastError() != ERROR_MORE_DATA ) {
|
||
|
job.lastError = GetLastError();
|
||
|
PCLogUnExError( job.fullJobName, TEXT("GetJobObjProcListInfo") );
|
||
|
memset( job.JOProcListInfo, 0, sizeof(*job.JOProcListInfo) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( job.JOProcListInfo->NumberOfAssignedProcesses > job.JOProcListInfo->NumberOfProcessIdsInList )
|
||
|
entryCount = job.JOProcListInfo->NumberOfAssignedProcesses + 2;
|
||
|
else break;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to populate a managed job structure with job object information //
|
||
|
// Input: ptr to managed job //
|
||
|
// Returns: nothing //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::UpdateJobObjInfo( ManagedJob &job ) {
|
||
|
PCULONG32 actual;
|
||
|
if ( !QueryInformationJobObject( job.jobHandle, JobObjectExtendedLimitInformation,
|
||
|
&job.JOExtendedLimitInfo, sizeof(job.JOExtendedLimitInfo), &actual ) ) {
|
||
|
job.lastError = GetLastError();
|
||
|
PCLogUnExError( job.fullJobName, TEXT("GetJobObjExtLimitInfo") );
|
||
|
memset( &job.JOExtendedLimitInfo, 0, sizeof(job.JOExtendedLimitInfo) );
|
||
|
}
|
||
|
else if ( !QueryInformationJobObject( job.jobHandle, JobObjectBasicAndIoAccountingInformation,
|
||
|
&job.JOBasicAndIoAcctInfo, sizeof(job.JOBasicAndIoAcctInfo), &actual ) ) {
|
||
|
job.lastError = GetLastError();
|
||
|
PCLogUnExError( job.fullJobName, TEXT("GetBasicAndIoAcctInfo") );
|
||
|
memset( &job.JOBasicAndIoAcctInfo, 0, sizeof(job.JOBasicAndIoAcctInfo) );
|
||
|
}
|
||
|
else if ( GetProcListForJob( job ) ) {
|
||
|
for ( PCULONG32 i = 0; i < job.JOProcListInfo->NumberOfProcessIdsInList; ++i ) {
|
||
|
for ( PCULONG32 p = 0; p < m_rawProcCount; ++p ) {
|
||
|
if ( m_rawProcList[p].pId == job.JOProcListInfo->ProcessIdList[i] ) {
|
||
|
m_rawProcList[p].pMJob = &job;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] ((UCHAR *) job.JOProcListInfo);
|
||
|
job.JOProcListInfo = NULL;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to delete process entries orphaned in our list of currently managed processes //
|
||
|
// Input: none //
|
||
|
// Returns: nothing //
|
||
|
// Note: Caller must hold the CS that protects the list hanging off m_procAnchor //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::DeleteOrphanProcEntries( void ) {
|
||
|
for ( BOOL done = FALSE; !done; ) {
|
||
|
done = TRUE;
|
||
|
for ( ManagedProc *p = m_procAnchor, *plast = NULL; p; plast = p, p = p->next ) {
|
||
|
if ( p->sequence != m_sequencer ) {
|
||
|
if ( p == m_procAnchor ) m_procAnchor = p->next;
|
||
|
else plast->next = p->next;
|
||
|
delete p;
|
||
|
--m_procManagedCount;
|
||
|
done = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to delete job entries that have gone empty when 'close on empty' is set //
|
||
|
// Input: none //
|
||
|
// Returns: nothing //
|
||
|
// Note: Caller must hold the CS that protects the list hanging off m_jobAnchor //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
void CProcConMgr::DeleteOrphanJobEntries( void ) {
|
||
|
for ( BOOL done = FALSE; !done; ) {
|
||
|
done = TRUE;
|
||
|
for ( ManagedJob *p = m_jobAnchor; p; p = p->next ) {
|
||
|
if ( p->sequence != m_sequencer ) { // job was not visited during last proc scan
|
||
|
PCJobDef *jobDef;
|
||
|
if ( m_cDB.GetJobMgmtDefs( &jobDef, &p->jName ) ) {// Returns 0 if job definition not found
|
||
|
UpdateJobEntry( *p, jobDef );
|
||
|
delete [] jobDef;
|
||
|
}
|
||
|
if ( p->jobParms.mFlags & PCMFLAG_END_JOB_WHEN_EMPTY ) {
|
||
|
JobIsEmpty( p ); // job should be deleted after this call!
|
||
|
done = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to apply requested management behavior to the job in question //
|
||
|
// Input: reference to managed job definition //
|
||
|
// Returns: TRUE if requested behavior was assigned to the job, else FALSE //
|
||
|
// Note: Assigning a process to a job cannot be undone. Thus, when this function returns //
|
||
|
// TRUE the process is always associated with the job until the process ends. //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
BOOL CProcConMgr::ApplyJobMgmt( ManagedJob &job ) {
|
||
|
|
||
|
BOOL applied = FALSE, change = FALSE, wantON, isON;
|
||
|
|
||
|
// Establish flags to show what changes were made this pass...
|
||
|
int doAff = 0, doPri = 0, doSch = 0, doWS = 0, doProcTime = 0, doJobTime = 0, doProcCount = 0,
|
||
|
doProcMemory = 0, doJobMemory = 0, brkAwayOKAct = 0, silentBrkAwayAct = 0, dieUHExcept = 0;
|
||
|
|
||
|
// Next prepare any updates needed to get where we want...
|
||
|
// for all PC management flags,
|
||
|
// o if management is requested and is either not in place or a different limit is requested:
|
||
|
// turn on job object management flag and set limit value (if applicable)
|
||
|
// o if management is not requested but is currently in place:
|
||
|
// turn off job object management flag (leave limit alone)
|
||
|
|
||
|
// Process Breakaway flag (if requested by CreateProcess, process is outside job if flag is set)
|
||
|
// (if flag is not set, CreateProcess will fail)...
|
||
|
brkAwayOKAct = PCTestSetUnset( job.jobParms.mFlags, PCMFLAG_SET_PROC_BREAKAWAY_OK,
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags,
|
||
|
JOB_OBJECT_LIMIT_BREAKAWAY_OK );
|
||
|
if ( brkAwayOKAct ) change = TRUE;
|
||
|
if ( brkAwayOKAct > 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
|
||
|
else if ( brkAwayOKAct < 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_BREAKAWAY_OK;
|
||
|
|
||
|
// Process Silent Breakaway flag (flag on means processes created in job are always outside job)...
|
||
|
silentBrkAwayAct = PCTestSetUnset( job.jobParms.mFlags, PCMFLAG_SET_SILENT_BREAKAWAY,
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags,
|
||
|
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK );
|
||
|
if ( silentBrkAwayAct ) change = TRUE;
|
||
|
if ( silentBrkAwayAct > 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
|
||
|
else if ( silentBrkAwayAct < 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
|
||
|
|
||
|
// Process die on UH Execption flag (flag on means suppress GPF message box)...
|
||
|
dieUHExcept = PCTestSetUnset( job.jobParms.mFlags, PCMFLAG_SET_DIE_ON_UH_EXCEPTION,
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags,
|
||
|
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION );
|
||
|
if ( dieUHExcept ) change = TRUE;
|
||
|
if ( dieUHExcept > 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
|
||
|
else if ( dieUHExcept < 0 )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
|
||
|
|
||
|
// Process process memory limit flag...
|
||
|
PCUINT32 pageSize = m_cPC.GetPageSize();
|
||
|
MEMORY_VALUE oldProcMemory = job.JOExtendedLimitInfo.ProcessMemoryLimit,
|
||
|
newProcMemory = ((job.jobParms.procMemoryLimit + pageSize - 1) / pageSize) * pageSize;
|
||
|
|
||
|
// Ensure that process memory limit is not larger than API can handle...
|
||
|
if ( sizeof(SIZE_T) == 4 && newProcMemory > ((MAXDWORD / pageSize) * pageSize) ) newProcMemory = ((MAXDWORD / pageSize) * pageSize);
|
||
|
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_PROC_MEMORY_LIMIT;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY;
|
||
|
if ( wantON && (!isON || newProcMemory != oldProcMemory) ) {
|
||
|
if ( !newProcMemory ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_PROC_MEMORY_LIMIT) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_MEMORY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_PROC_MEMORY_LIMIT;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY;
|
||
|
job.JOExtendedLimitInfo.ProcessMemoryLimit = (SIZE_T) newProcMemory;
|
||
|
doProcMemory = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_MEMORY;
|
||
|
doProcMemory = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process job memory limit flag...
|
||
|
MEMORY_VALUE oldJobMemory = job.JOExtendedLimitInfo.JobMemoryLimit,
|
||
|
newJobMemory = ((job.jobParms.jobMemoryLimit + pageSize - 1) / pageSize) * pageSize;
|
||
|
|
||
|
// Ensure that requested job memory limit is not larger than API can handle...
|
||
|
if ( sizeof(SIZE_T) == 4 && newJobMemory > ((MAXDWORD / pageSize) * pageSize) ) newJobMemory = ((MAXDWORD / pageSize) * pageSize);
|
||
|
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_JOB_MEMORY_LIMIT;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY;
|
||
|
if ( wantON && (!isON || newJobMemory != oldJobMemory) ) {
|
||
|
if ( !newJobMemory ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_JOB_MEMORY_LIMIT) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_MEMORY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_JOB_MEMORY_LIMIT;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY;
|
||
|
job.JOExtendedLimitInfo.JobMemoryLimit = (SIZE_T) newJobMemory;
|
||
|
doJobMemory = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_MEMORY;
|
||
|
doJobMemory = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process affinity flag...
|
||
|
AFFINITY oldAff = job.JOExtendedLimitInfo.BasicLimitInformation.Affinity,
|
||
|
newAff = job.jobParms.affinity & m_systemMask;
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_AFFINITY;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_AFFINITY;
|
||
|
if ( wantON && (!isON || newAff != oldAff) ) {
|
||
|
if ( !newAff ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_AFFINITY) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_AFFINITY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.Affinity = (ULONG_PTR) newAff;
|
||
|
doAff = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_AFFINITY;
|
||
|
doAff = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process priority flag...
|
||
|
PRIORITY oldPri = job.JOExtendedLimitInfo.BasicLimitInformation.PriorityClass,
|
||
|
newPri = PCMapPriorityToNT( job.jobParms.priority );
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_PRIORITY;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS;
|
||
|
if ( wantON && (!isON || newPri != oldPri) ) {
|
||
|
if ( !newPri ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_PRIORITY) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_PRIORITY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.PriorityClass = newPri;
|
||
|
doPri = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_PRIORITY_CLASS;
|
||
|
doPri = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process scheduling class flag...
|
||
|
SCHEDULING_CLASS oldSch = job.JOExtendedLimitInfo.BasicLimitInformation.SchedulingClass,
|
||
|
newSch = job.jobParms.schedClass;
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_SCHEDULING_CLASS;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
|
||
|
if ( wantON && (!isON || newSch != oldSch) ) {
|
||
|
if ( newSch > 9 ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_SCHEDULING_CLASS) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_SCHEDULING_CLASS_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_SCHEDULING_CLASS;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.SchedulingClass = newSch;
|
||
|
doSch = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_SCHEDULING_CLASS;
|
||
|
doSch = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process working set flag...
|
||
|
MEMORY_VALUE oldMinWS = job.JOExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize,
|
||
|
oldMaxWS = job.JOExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize;
|
||
|
MEMORY_VALUE newMinWS = (job.jobParms.minWS + pageSize - 1) / pageSize * pageSize,
|
||
|
newMaxWS = (job.jobParms.maxWS + pageSize - 1) / pageSize * pageSize;
|
||
|
|
||
|
// Ensure that requested working set limits are not larger than API can handle...
|
||
|
if ( sizeof(SIZE_T) == 4 && newMaxWS > ((MAXDWORD / pageSize) * pageSize) ) { // exceeds largest 32 bit value that is page size multiple
|
||
|
newMaxWS = ((MAXDWORD / pageSize) * pageSize);
|
||
|
if ( newMinWS >= newMaxWS )
|
||
|
newMinWS = newMaxWS - 4 * pageSize;
|
||
|
}
|
||
|
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_WS_MINMAX;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET;
|
||
|
if ( wantON && (!isON || oldMinWS != newMinWS || oldMaxWS != newMaxWS) ) {
|
||
|
if ( !newMinWS || newMaxWS <= newMinWS ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_WS_MINMAX) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_WORKING_SET_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_WORKINGSET;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.MinimumWorkingSetSize = (SIZE_T) newMinWS;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.MaximumWorkingSetSize = (SIZE_T) newMaxWS;
|
||
|
doWS = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_WORKINGSET;
|
||
|
doWS = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process process count limit flag...
|
||
|
PCULONG32 oldProcCount = job.JOExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit,
|
||
|
newProcCount = job.jobParms.procCountLimit;
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_PROC_COUNT_LIMIT;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
|
||
|
if ( wantON && (!isON || newProcCount != oldProcCount) ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.ActiveProcessLimit = newProcCount;
|
||
|
doProcCount = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
|
||
|
doProcCount = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process process time limit flag...
|
||
|
TIME_VALUE oldProcTime = PCLargeIntToInt64( job.JOExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit ),
|
||
|
newProcTime = job.jobParms.procTimeLimitCNS;
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_PROC_TIME_LIMIT;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_TIME;
|
||
|
if ( wantON && (!isON || newProcTime != oldProcTime) ) {
|
||
|
if ( newProcTime < PC_MIN_TIME_LIMIT ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_PROC_TIME_LIMIT) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_TIME_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_PROC_TIME_LIMIT;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = newProcTime;
|
||
|
doProcTime = 1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_TIME;
|
||
|
doProcTime = -1;
|
||
|
change = TRUE;
|
||
|
}
|
||
|
|
||
|
// Process job time limit flag...
|
||
|
TIME_VALUE oldJobTime = job.curJobTimeLimitCNS,
|
||
|
newJobTime = job.jobParms.jobTimeLimitCNS;
|
||
|
wantON = job.jobParms.mFlags & PCMFLAG_APPLY_JOB_TIME_LIMIT;
|
||
|
isON = job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME;
|
||
|
if ( wantON && ( (!isON && !job.timeExceededReported) || newJobTime != oldJobTime ) ) {
|
||
|
if ( newJobTime < PC_MIN_TIME_LIMIT ) {
|
||
|
if ( !(job.dataErrorFlags & PCMFLAG_APPLY_JOB_TIME_LIMIT) ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_JOB_TIME_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
job.dataErrorFlags |= PCMFLAG_APPLY_JOB_TIME_LIMIT;
|
||
|
}
|
||
|
else {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME;
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = newJobTime;
|
||
|
doJobTime = 1;
|
||
|
change = TRUE;
|
||
|
job.curJobTimeLimitCNS = newJobTime;
|
||
|
}
|
||
|
}
|
||
|
else if ( !wantON && isON ) {
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME;
|
||
|
doJobTime = -1;
|
||
|
change = TRUE;
|
||
|
job.curJobTimeLimitCNS = 0;
|
||
|
}
|
||
|
|
||
|
// If we are about to change things other than job timing, set preserve job time...
|
||
|
if ( change && !doJobTime )
|
||
|
job.JOExtendedLimitInfo.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME;
|
||
|
|
||
|
// Now apply the updates needed and report if unsuccessful...
|
||
|
if ( change && !SetInformationJobObject( job.jobHandle, JobObjectExtendedLimitInformation,
|
||
|
&job.JOExtendedLimitInfo, sizeof(job.JOExtendedLimitInfo) ) )
|
||
|
PCLogUnExError( job.fullJobName, TEXT("SetJobObjLimitInfo") );
|
||
|
|
||
|
// The job object has been updated. Now do any completion port processing needed and report on changes.
|
||
|
else {
|
||
|
|
||
|
// First determine if we need to associate a completion port...
|
||
|
if ( !job.hasComplPort && m_assocPort.CompletionPort ) {
|
||
|
m_assocPort.CompletionKey = (void *) job.compKey;
|
||
|
if ( !SetInformationJobObject( job.jobHandle, JobObjectAssociateCompletionPortInformation,
|
||
|
&m_assocPort, sizeof(m_assocPort) ) && GetLastError() != ERROR_INVALID_PARAMETER )
|
||
|
PCLogUnExError( job.fullJobName, TEXT("SetJobObjComplPortInfo") );
|
||
|
else job.hasComplPort = TRUE;
|
||
|
}
|
||
|
|
||
|
// Next determine if we need to set end-of-job time time handling...
|
||
|
if ( job.hasComplPort ) {
|
||
|
JOBOBJECT_END_OF_JOB_TIME_INFORMATION eojData;
|
||
|
eojData.EndOfJobTimeAction = job.jobParms.mFlags & PCMFLAG_MSG_ON_JOB_TIME_LIMIT_HIT?
|
||
|
JOB_OBJECT_POST_AT_END_OF_JOB : JOB_OBJECT_TERMINATE_AT_END_OF_JOB;
|
||
|
if ( eojData.EndOfJobTimeAction != job.lastEojAction &&
|
||
|
!SetInformationJobObject( job.jobHandle, JobObjectEndOfJobTimeInformation,
|
||
|
&eojData, sizeof(eojData) ) )
|
||
|
PCLogUnExError( job.fullJobName, TEXT("SetJobObjEndOfJobInfo") );
|
||
|
else job.lastEojAction = eojData.EndOfJobTimeAction;
|
||
|
}
|
||
|
|
||
|
// Report on breakaway OK flag change, if any...
|
||
|
if ( brkAwayOKAct ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
if ( brkAwayOKAct > 0 )
|
||
|
PCLogMessage( PC_SERVICE_SET_BREAKAWAY_ALLOWED, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_UNSET_BREAKAWAY_ALLOWED, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on silent breakaway flag change, if any...
|
||
|
if ( silentBrkAwayAct ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
if ( silentBrkAwayAct > 0 )
|
||
|
PCLogMessage( PC_SERVICE_SET_SILENT_BREAKAWAY_ENABLED, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_UNSET_SILENT_BREAKAWAY_ENABLED, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on unhandled exception flag change, if any...
|
||
|
if ( dieUHExcept ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName };
|
||
|
if ( dieUHExcept > 0 )
|
||
|
PCLogMessage( PC_SERVICE_SET_LIMIT_DIE_ON_UNHANDLED_EXCEPTION, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_UNSET_LIMIT_DIE_ON_UNHANDLED_EXCEPTION, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on process memory limit change, if any...
|
||
|
if ( doProcMemory ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_i64tot( oldProcMemory, from, 10 );
|
||
|
_i64tot( newProcMemory, to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doProcMemory > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_PROCESS_MEMORY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_PROCESS_MEMORY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on job memory limit change, if any...
|
||
|
if ( doJobMemory ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_i64tot( oldJobMemory, from, 10 );
|
||
|
_i64tot( newJobMemory, to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doJobMemory > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_JOB_MEMORY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_JOB_MEMORY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on process time limit change, if any...
|
||
|
if ( doProcTime ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_i64tot( oldProcTime / 10000, from, 10 ); // display in milliseconds
|
||
|
_i64tot( newProcTime / 10000, to, 10 ); // display in milliseconds
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doProcTime > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_PROCESS_TIME, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_PROCESS_TIME, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on job time limit change, if any...
|
||
|
if ( doJobTime ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_i64tot( oldJobTime / 10000, from, 10 ); // display in milliseconds
|
||
|
_i64tot( newJobTime / 10000, to, 10 ); // display in milliseconds
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doJobTime > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_JOB_TIME, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_JOB_TIME, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on process count limit change, if any...
|
||
|
if ( doProcCount ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_ltot( oldProcCount, from, 10 );
|
||
|
_ltot( newProcCount, to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doProcCount > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_PROCESS_COUNT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_PROCESS_COUNT, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on priority class limit change, if any...
|
||
|
if ( doPri ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_ltot( PCMapPriorityToPC( oldPri ), from, 10 );
|
||
|
_ltot( PCMapPriorityToPC( newPri ), to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doPri > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_PRIORITY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_PRIORITY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on affinity limit change, if any...
|
||
|
if ( doAff ) {
|
||
|
TCHAR from[32] = TEXT("0x"), to[32] = TEXT("0x");
|
||
|
_i64tot( oldAff, &from[2], 16 );
|
||
|
_i64tot( newAff, &to[2], 16 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doAff > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_AFFINITY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_AFFINITY, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on scheduling class limit change, if any...
|
||
|
if ( doSch ) {
|
||
|
TCHAR from[32], to[32];
|
||
|
_ltot( oldSch, from, 10 );
|
||
|
_ltot( newSch, to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, to };
|
||
|
if ( doSch > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_SCHEDULING_CLASS, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_SCHEDULING_CLASS, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
// Report on working set limit change, if any...
|
||
|
if ( doWS ) {
|
||
|
TCHAR from[32], to[32], from2[32], to2[32];
|
||
|
_i64tot( oldMinWS, from, 10 );
|
||
|
_i64tot( oldMaxWS, from2, 10 );
|
||
|
_i64tot( newMinWS, to, 10 );
|
||
|
_i64tot( newMaxWS, to2, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, job.fullJobName, from, from2, to, to2 };
|
||
|
if ( doWS > 0 )
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_JOB_WORKING_SET, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
else
|
||
|
PCLogMessage( PC_SERVICE_REMOVE_JOB_WORKING_SET, EVENTLOG_INFORMATION_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
applied = TRUE;
|
||
|
}
|
||
|
|
||
|
return applied;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to apply requested management behavior to the process in question //
|
||
|
// Input: managed process definition, process handle //
|
||
|
// Returns: TRUE if any requested behavior is successfully applied, else FALSE //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
BOOL CProcConMgr::ApplyProcMgmt( ManagedProc &proc, HANDLE hProc ) {
|
||
|
BOOL setProc = FALSE;
|
||
|
ULONG_PTR oldAff, newAff;
|
||
|
PRIORITY oldPri, newPri;
|
||
|
SIZE_T oldMinWS, oldMaxWS;
|
||
|
|
||
|
// Handle request to set or unset process affinity...
|
||
|
|
||
|
// Check to see if affinity is to be applied...
|
||
|
if ( proc.procParms.mFlags & PCMFLAG_APPLY_AFFINITY ) {
|
||
|
// Get current affinity and save as original if not yet saved...
|
||
|
GetProcessAffinityMask( hProc, &oldAff, &m_systemMask );
|
||
|
if ( !(proc.originalParms.mFlags & PCMFLAG_APPLY_AFFINITY) ) {
|
||
|
proc.originalParms.mFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
proc.originalParms.affinity = oldAff;
|
||
|
}
|
||
|
// Get new affinity and complain if invalid and not reported...
|
||
|
newAff = (ULONG_PTR) proc.procParms.affinity & m_systemMask;
|
||
|
if ( !newAff && !(proc.passSkipFlags & PCMFLAG_APPLY_AFFINITY) ) {
|
||
|
// Flag to skip affinity test on next pass so we don't report error repeatedly...
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_AFFINITY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
// If new affinity differs from old, attempt change and report on success or failure...
|
||
|
else if ( newAff != oldAff ) {
|
||
|
PCULONG32 rc = SetProcessAffinityMask( hProc, newAff );
|
||
|
if ( rc ) {
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_AFFINITY;
|
||
|
proc.isAppliedFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
TCHAR from[32] = TEXT("0x"), to[32] = TEXT("0x");
|
||
|
_i64tot( oldAff, &from[2], 16 );
|
||
|
_i64tot( newAff, &to[2], 16 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName, from, to };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_AFFINITY, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_AFFINITY) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_AFFINITY_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Not applying affinity -- check to see if affinity is to be un-applied...
|
||
|
else if ( !(proc.procParms.mFlags & PCMFLAG_APPLY_AFFINITY) && proc.isAppliedFlags & PCMFLAG_APPLY_AFFINITY ) {
|
||
|
// Get current affinity to verify we are making a change...
|
||
|
GetProcessAffinityMask( hProc, &oldAff, &m_systemMask );
|
||
|
newAff = (ULONG_PTR) proc.originalParms.affinity;
|
||
|
if ( newAff != oldAff ) {
|
||
|
PCULONG32 rc = SetProcessAffinityMask( hProc, newAff );
|
||
|
// We reverted to original value, reset flags and report...
|
||
|
if ( rc ) {
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_AFFINITY;
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_AFFINITY;
|
||
|
TCHAR from[32] = TEXT("0x"), to[32] = TEXT("0x");
|
||
|
_i64tot( oldAff, &from[2], 16 );
|
||
|
_i64tot( newAff, &to[2], 16 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName, from, to };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_AFFINITY, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_AFFINITY) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_AFFINITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_AFFINITY_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_AFFINITY;
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_AFFINITY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle request to set or unset process priority...
|
||
|
|
||
|
// Check to see if priority is to be applied now...
|
||
|
if ( proc.procParms.mFlags & PCMFLAG_APPLY_PRIORITY ) {
|
||
|
// Get current priority and save as original if not yet saved...
|
||
|
oldPri = GetPriorityClass( hProc );
|
||
|
if ( !(proc.originalParms.mFlags & PCMFLAG_APPLY_PRIORITY) ) {
|
||
|
proc.originalParms.mFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
proc.originalParms.priority = PCMapPriorityToPC( oldPri );
|
||
|
}
|
||
|
// Get new priority and complain if zero...
|
||
|
newPri = PCMapPriorityToNT( proc.procParms.priority );
|
||
|
if ( !newPri && !(proc.passSkipFlags & PCMFLAG_APPLY_PRIORITY) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_PRIORITY_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
// If new priority differs from old, attempt change and report on success or failure...
|
||
|
else if ( newPri != oldPri ) {
|
||
|
PCULONG32 rc = SetPriorityClass( hProc, newPri );
|
||
|
if ( rc ) {
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_PRIORITY;
|
||
|
proc.isAppliedFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
TCHAR from[32], to[32];
|
||
|
_ltot( PCMapPriorityToPC( oldPri ), from, 10 );
|
||
|
_ltot( PCMapPriorityToPC( newPri ), to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName, from, to };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_PRIORITY, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_PRIORITY) ) {
|
||
|
// Flag to skip priority test on next pass so we don't report error repeatedly...
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_PRIORITY_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Not applying priority -- check to see if priority is to be un-applied...
|
||
|
else if ( !(proc.procParms.mFlags & PCMFLAG_APPLY_PRIORITY) && proc.isAppliedFlags & PCMFLAG_APPLY_PRIORITY ) {
|
||
|
// Get current priority to verify we are making a change...
|
||
|
oldPri = GetPriorityClass( hProc );
|
||
|
newPri = PCMapPriorityToNT( proc.originalParms.priority );
|
||
|
if ( newPri != oldPri) {
|
||
|
PCULONG32 rc = SetPriorityClass( hProc, newPri );
|
||
|
// We reverted to original value, reset all flags and report...
|
||
|
if ( rc ) {
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_PRIORITY;
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_PRIORITY;
|
||
|
TCHAR from[32], to[32];
|
||
|
_ltot( PCMapPriorityToPC( oldPri ), from, 10 );
|
||
|
_ltot( PCMapPriorityToPC( newPri ), to, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName, from, to };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_PRIORITY, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_PRIORITY) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_PRIORITY;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_PRIORITY_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_PRIORITY;
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_PRIORITY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle request to set or unset process working set...
|
||
|
|
||
|
// Check to see if working set is to be applied now...
|
||
|
if ( proc.procParms.mFlags & PCMFLAG_APPLY_WS_MINMAX ) {
|
||
|
|
||
|
// Ensure that requested working set limits are not larger than API can handle...
|
||
|
PCUINT32 pageSize = m_cPC.GetPageSize();
|
||
|
if ( sizeof(SIZE_T) == 4 && proc.procParms.maxWS > ((MAXDWORD / pageSize) * pageSize) ) { // exceeds largest 32 bit value that is page size multiple
|
||
|
proc.procParms.maxWS = ((MAXDWORD / pageSize) * pageSize);
|
||
|
if ( proc.procParms.minWS >= proc.procParms.maxWS )
|
||
|
proc.procParms.minWS = proc.procParms.maxWS - 4 * pageSize;
|
||
|
}
|
||
|
|
||
|
// Get current working set and save as original if not yet saved...
|
||
|
GetProcessWorkingSetSize( hProc, &oldMinWS, &oldMaxWS );
|
||
|
if ( !(proc.originalParms.mFlags & PCMFLAG_APPLY_WS_MINMAX) ) {
|
||
|
proc.originalParms.mFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
proc.originalParms.minWS = oldMinWS;
|
||
|
proc.originalParms.maxWS = oldMaxWS;
|
||
|
}
|
||
|
// Verify new working set and complain if bad...
|
||
|
if ( (!proc.procParms.minWS || proc.procParms.minWS >= proc.procParms.maxWS) &&
|
||
|
!(proc.passSkipFlags & PCMFLAG_APPLY_WS_MINMAX) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_WORKING_SET_REJECT, EVENTLOG_ERROR_TYPE,
|
||
|
ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
// If new working set differs from old, attempt change and report on success or failure...
|
||
|
else if ( proc.procParms.minWS != oldMinWS || proc.procParms.maxWS != oldMaxWS ) {
|
||
|
PCULONG32 rc = SetProcessWorkingSetSize( hProc, (SIZE_T) proc.procParms.minWS,
|
||
|
(SIZE_T) proc.procParms.maxWS );
|
||
|
if ( rc ) {
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_WS_MINMAX;
|
||
|
proc.isAppliedFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
TCHAR from[32], to[32], from2[32], to2[32];
|
||
|
_i64tot( (__int64) oldMinWS, from, 10 );
|
||
|
_i64tot( (__int64) oldMaxWS, from2, 10 );
|
||
|
_i64tot( proc.procParms.minWS, to, 10 );
|
||
|
_i64tot( proc.procParms.maxWS, to2, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName,
|
||
|
from, from2, to, to2 };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_WORKING_SET, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_WS_MINMAX) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_WORKINGSET_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Not applying working set -- check to see if working set is to be un-applied...
|
||
|
else if ( !(proc.procParms.mFlags & PCMFLAG_APPLY_WS_MINMAX) && proc.isAppliedFlags & PCMFLAG_APPLY_WS_MINMAX ) {
|
||
|
// Get current working set to verify we are making a change...
|
||
|
GetProcessWorkingSetSize( hProc, &oldMinWS, &oldMaxWS );
|
||
|
if ( proc.originalParms.minWS != oldMinWS || proc.originalParms.maxWS != oldMaxWS ) {
|
||
|
PCULONG32 rc = SetProcessWorkingSetSize( hProc, (SIZE_T) proc.originalParms.minWS,
|
||
|
(SIZE_T) proc.originalParms.maxWS );
|
||
|
// We reverted to original values, reset all flags and report...
|
||
|
if ( rc ) {
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_WS_MINMAX;
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_WS_MINMAX;
|
||
|
TCHAR from[32], to[32], from2[32], to2[32];
|
||
|
_i64tot( (__int64) oldMinWS, from, 10 );
|
||
|
_i64tot( (__int64) oldMaxWS, from2, 10 );
|
||
|
_i64tot( proc.originalParms.minWS, to, 10 );
|
||
|
_i64tot( proc.originalParms.maxWS, to2, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName,
|
||
|
from, from2, to, to2 };
|
||
|
PCLogMessage( PC_SERVICE_CHANGE_PROC_WORKING_SET, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
setProc = TRUE;
|
||
|
}
|
||
|
else if ( !(proc.passSkipFlags & PCMFLAG_APPLY_WS_MINMAX) ) {
|
||
|
proc.passSkipFlags |= PCMFLAG_APPLY_WS_MINMAX;
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, proc.pName, proc.pidAsString, proc.imageName };
|
||
|
PCLogMessage( PC_SERVICE_APPLY_PROC_WORKINGSET_ERROR, EVENTLOG_ERROR_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
proc.isAppliedFlags &= ~PCMFLAG_APPLY_WS_MINMAX;
|
||
|
proc.passSkipFlags &= ~PCMFLAG_APPLY_WS_MINMAX;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return setProc;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to build copy of active process list for exporting (to API) //
|
||
|
// Input: location to store allocated buffer (call frees it) //
|
||
|
// Returns: number of entries in the list //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
PCULONG32 CProcConMgr::ExportActiveProcList( PCProcListItem **list ) {
|
||
|
|
||
|
Discover(); // Go build or refresh process and job lists
|
||
|
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
PCULONG32 count = m_rawProcCount;
|
||
|
*list = NULL;
|
||
|
|
||
|
if ( m_rawProcList ) {
|
||
|
*list = new PCProcListItem[m_rawProcCount];
|
||
|
if ( !*list ) {
|
||
|
PCLogNoMemory( TEXT("AllocExportProcList"), sizeof(PCProcListItem) * m_rawProcCount );
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
return 0;
|
||
|
}
|
||
|
else memset( *list, 0, sizeof(PCProcListItem) * m_rawProcCount );
|
||
|
}
|
||
|
|
||
|
for ( PCULONG32 i = 0; i < count; ++i ) {
|
||
|
PCProcListItem &target = (*list)[i];
|
||
|
memcpy( target.procName, m_rawProcList[i].pName, sizeof(target.procName) );
|
||
|
memcpy( target.imageName, m_rawProcList[i].imageName, sizeof(target.imageName) );
|
||
|
target.procStats.pid = m_rawProcList[i].pId;
|
||
|
target.procStats.createTime = PCFileTimeToInt64( m_rawProcList[i].createTime );
|
||
|
target.procStats.TotalUserTime = PCFileTimeToInt64( m_rawProcList[i].userTime );
|
||
|
target.procStats.TotalKernelTime = PCFileTimeToInt64( m_rawProcList[i].kernelTime );
|
||
|
target.actualPriority = m_rawProcList[i].actualPriority;
|
||
|
target.actualAffinity = m_rawProcList[i].actualAffinity;
|
||
|
target.lFlags = PCLFLAG_IS_RUNNING;
|
||
|
if ( m_rawProcList[i].pMJob ) {
|
||
|
target.lFlags |= PCLFLAG_IS_IN_A_JOB;
|
||
|
memcpy(target.jobName, m_rawProcList[i].pMJob->jName, sizeof( target.jobName ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// function to build copy of active job list for exporting (to API) //
|
||
|
// Input: location to store allocated buffer (call frees it) //
|
||
|
// Returns: number of entries in the list //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
PCULONG32 CProcConMgr::ExportActiveJobList( PCJobListItem **list ) {
|
||
|
|
||
|
Discover(); // Go build or refresh process and job lists
|
||
|
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
PCULONG32 count = m_jobManagedCount;
|
||
|
|
||
|
// Allocate buffer for job list (caller will free)...
|
||
|
*list = new PCJobListItem[count];
|
||
|
if ( !*list ) {
|
||
|
PCLogNoMemory( TEXT("AllocJobList"), sizeof(PCJobListItem) * count );
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
return 0;
|
||
|
}
|
||
|
memset( *list, 0, sizeof(PCJobListItem) * count );
|
||
|
|
||
|
// Copy job names to list and set flag(s)...
|
||
|
PCULONG32 i = 0;
|
||
|
for ( ManagedJob *curJob = m_jobAnchor; curJob; curJob = curJob->next, ++i ) {
|
||
|
PCJobListItem &target = (*list)[i];
|
||
|
memcpy( target.jobName, curJob->jName, sizeof(curJob->jName) );
|
||
|
|
||
|
target.jobStats.TotalUserTime =
|
||
|
PCLargeIntToInt64( curJob->JOBasicAndIoAcctInfo.BasicInfo.TotalUserTime );
|
||
|
target.jobStats.TotalKernelTime =
|
||
|
PCLargeIntToInt64( curJob->JOBasicAndIoAcctInfo.BasicInfo.TotalKernelTime );
|
||
|
target.jobStats.ThisPeriodTotalUserTime =
|
||
|
PCLargeIntToInt64( curJob->JOBasicAndIoAcctInfo.BasicInfo.ThisPeriodTotalUserTime );
|
||
|
target.jobStats.ThisPeriodTotalKernelTime =
|
||
|
PCLargeIntToInt64( curJob->JOBasicAndIoAcctInfo.BasicInfo.ThisPeriodTotalKernelTime );
|
||
|
target.jobStats.TotalPageFaultCount = curJob->JOBasicAndIoAcctInfo.BasicInfo.TotalPageFaultCount;
|
||
|
target.jobStats.TotalProcesses = curJob->JOBasicAndIoAcctInfo.BasicInfo.TotalProcesses;
|
||
|
target.jobStats.ActiveProcesses = curJob->JOBasicAndIoAcctInfo.BasicInfo.ActiveProcesses;
|
||
|
target.jobStats.TotalTerminatedProcesses = curJob->JOBasicAndIoAcctInfo.BasicInfo.TotalTerminatedProcesses;
|
||
|
target.jobStats.ReadOperationCount = curJob->JOBasicAndIoAcctInfo.IoInfo.ReadOperationCount;
|
||
|
target.jobStats.WriteOperationCount = curJob->JOBasicAndIoAcctInfo.IoInfo.WriteOperationCount;
|
||
|
target.jobStats.OtherOperationCount = curJob->JOBasicAndIoAcctInfo.IoInfo.OtherOperationCount;
|
||
|
target.jobStats.ReadTransferCount = curJob->JOBasicAndIoAcctInfo.IoInfo.ReadTransferCount;
|
||
|
target.jobStats.WriteTransferCount = curJob->JOBasicAndIoAcctInfo.IoInfo.WriteTransferCount;
|
||
|
target.jobStats.OtherTransferCount = curJob->JOBasicAndIoAcctInfo.IoInfo.OtherTransferCount;
|
||
|
target.jobStats.PeakProcessMemoryUsed = curJob->JOExtendedLimitInfo.PeakProcessMemoryUsed;
|
||
|
target.jobStats.PeakJobMemoryUsed = curJob->JOExtendedLimitInfo.PeakJobMemoryUsed;
|
||
|
|
||
|
target.actualPriority =
|
||
|
PCMapPriorityToPC( curJob->JOExtendedLimitInfo.BasicLimitInformation.PriorityClass );
|
||
|
target.actualAffinity = curJob->JOExtendedLimitInfo.BasicLimitInformation.Affinity;
|
||
|
target.actualSchedClass = curJob->JOExtendedLimitInfo.BasicLimitInformation.SchedulingClass;
|
||
|
target.lFlags = PCLFLAG_IS_RUNNING;
|
||
|
}
|
||
|
|
||
|
// Sort the list by name...
|
||
|
qsort( *list, count, sizeof(PCJobListItem), CompareJobName );
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// functions to kill a job (only ProcCon created jobs can be killed) //
|
||
|
// Input: job name //
|
||
|
// Returns: PCERROR_SUCCESS on success, else error code //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
INT32 CProcConMgr::KillJob( JOB_NAME &name ) {
|
||
|
|
||
|
// Determine if user has right to kill jobs...
|
||
|
if ( m_cDB.TestAccess( PROCCON_REG_KILLJOB_ACCTEST ) != ERROR_SUCCESS )
|
||
|
return GetLastError();
|
||
|
|
||
|
INT32 err = PCERROR_DOES_NOT_EXIST; // prime error code for subsequent logic
|
||
|
|
||
|
// Locate the job in our job list to get handle. If found, simply terminate the job.
|
||
|
// Note that terminating the job simply means terminate all contained processes.
|
||
|
// If the close on empty option is set, the job will be closed in the notify routine.
|
||
|
EnterCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
for ( ManagedJob *prev = NULL, *job = m_jobAnchor; job; prev = job, job = job->next ) {
|
||
|
if ( !CompareJobName( &name, &job->jName ) ) {
|
||
|
if ( !TerminateJobObject( job->jobHandle, ERROR_PROCESS_ABORTED ) )
|
||
|
err = GetLastError();
|
||
|
else
|
||
|
err = PCERROR_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection( &m_mgCSMgrLists );
|
||
|
|
||
|
// If successful, log a message...
|
||
|
if ( err == PCERROR_SUCCESS ) {
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, name };
|
||
|
PCLogMessage( PC_SERVICE_KILLED_JOB, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
// functions to kill a process (any Windows process can be killed) //
|
||
|
// Input: process pid and process create time //
|
||
|
// Returns: PCERROR_SUCCESS on success, else error code //
|
||
|
//--------------------------------------------------------------------------------------------//
|
||
|
INT32 CProcConMgr::KillProcess( ULONG_PTR pid, TIME_VALUE created ) {
|
||
|
|
||
|
// Determine if user has right to kill processes...
|
||
|
if ( m_cDB.TestAccess( PROCCON_REG_KILLPROC_ACCTEST ) != ERROR_SUCCESS )
|
||
|
return GetLastError();
|
||
|
|
||
|
INT32 err = ERROR_SUCCESS;
|
||
|
|
||
|
// Attempt to get a handle to the process...
|
||
|
HANDLE hProc = OpenProcess( PROCESS_QUERY_INFORMATION // to get time data
|
||
|
+ PROCESS_TERMINATE, // to terminate it
|
||
|
FALSE, (DWORD) pid ); // Win64 OpenProcess still uses DWORD PID == warning
|
||
|
// If we found it, determine if its the same one (by create time) and terminate it...
|
||
|
if ( hProc ) {
|
||
|
FILETIME create, exit, kernel, user;
|
||
|
GetProcessTimes( hProc, &create, &exit, &kernel, &user );
|
||
|
if ( created != 0x777deadfeeb1e777 && PCFileTimeToInt64( create ) != created )
|
||
|
err = PCERROR_DOES_NOT_EXIST;
|
||
|
else if ( !TerminateProcess( hProc, ERROR_PROCESS_ABORTED ) )
|
||
|
err = GetLastError();
|
||
|
CloseHandle( hProc );
|
||
|
}
|
||
|
else {
|
||
|
err = GetLastError();
|
||
|
if ( err == ERROR_INVALID_PARAMETER )
|
||
|
err = PCERROR_DOES_NOT_EXIST;
|
||
|
}
|
||
|
|
||
|
// If successful, log a message...
|
||
|
if ( err == ERROR_SUCCESS ) {
|
||
|
TCHAR pidAsString[32];
|
||
|
_i64tot( pid, pidAsString, 10 );
|
||
|
const TCHAR *msgs[] = { PROCCON_SVC_DISP_NAME, pidAsString };
|
||
|
PCLogMessage( PC_SERVICE_KILLED_PROCESS, EVENTLOG_INFORMATION_TYPE, ENTRY_COUNT(msgs), msgs );
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
// End of CProcConMgr.cpp
|
||
|
//============================================================================J McDonald fecit====//
|
||
|
|