home *** CD-ROM | disk | FTP | other *** search
- //****************************************************************
- //*** UnHang.cmm - CEnvi program to monitor PM queues and kill ***
- //*** ver.1 or otherwise handle session that have hung ***
- //*** queues to allow the system to continue. ***
- //****************************************************************
-
- #include <OptParms.lib>
-
- main(argc,argv)
- {
- ParseInputArgs(argc,argv);
- if ( 1 != argc ) {
- // All the args weren't parsed, and so invalid input.
- // show how to use this program.
- Instructions();
- } else {
-
- if ( defined(LineCount) )
- system("mode 80,%d",LineCount);
- if ( HighPriority )
- RunAtHighPriority();
- if ( Hide )
- HideMyself();
- UnhangForever();
- }
- }
-
- #define DEFAULT_MAX_HANGTIME 60 // how many seconds, max (approximately)
- // can a program hang before we kill it
- #define DEFAULT_CYCLES_PER_MAX_HANGTIME 3 // break test down to this many times
- // maximum hang time, more cycles gives
- // each program a better chance to not
- // be hung, but takes up more time
- #define DEFAULT_MINIMUM_SUSPEND_FACTOR 0 // don't suspend less than this long
-
-
- Instructions()
- {
- printf("\a\n");
- printf("UnHang.cmm - Prevent the workplace shell from hanging due to a bad app.\n");
- printf("\n");
- printf("SYNTAX: CEnvi UnHang.cmm [Options]\n");
- printf("\n");
- printf("Options: /LINES=linecount - how many lines to display in the output\n");
- printf(" /ONTOP - keep this screen on top of other windows\n");
- printf(" /HIDE - Hide this window\n");
- printf(" /LOG=FileSpec - Filename to log hangs and attempts to unhang\n");
- printf(" /HIGH - Run at high priority\n");
- printf(" /TIMEOUT=# - how many seconds (max) for complete test; default %d\n", DEFAULT_MAX_HANGTIME);
- printf(" /CYCLES=# - how many mini-test cycles per timeout; default %d\n", DEFAULT_CYCLES_PER_MAX_HANGTIME);
- printf(" /SUSPEND=# - minimum time to suspend between checks; this keeps from\n");
- printf(" \"hogging\" the cpu; 0 is bad if /HIGH; default %d\n",DEFAULT_MINIMUM_SUSPEND_FACTOR);
- printf(" /COMMAND=cmd - will call this command if an application seems hung,\n");
- printf(" sending Process ID as first parameter and process name\n");
- printf(" as second parameter. Default is to kill the app.\n");
- printf("\n");
- }
-
- LineCount;
- OnTop;
- Hide;
- LogFileName;
- HighPriority;
- MaxHangtime = DEFAULT_MAX_HANGTIME;
- CyclesPerMaxHangtime = DEFAULT_CYCLES_PER_MAX_HANGTIME;
- MinimumSuspendFactor = DEFAULT_MINIMUM_SUSPEND_FACTOR;
- HangCommand;
-
- ParseInputArgs(argc,argv)
- {
- if ( OptionalParameter(argc,argv,"LINES",lTemp)
- && (LineCount=atoi(lTemp)) < 1 )
- printf("Invalid LINES parameter\a\n"), abort();
- OnTop = OptionalParameter(argc,argv,"ONTOP");
- Hide = OptionalParameter(argc,argv,"HIDE");
- OptionalParameter(argc,argv,"LOG",LogFileName);
- HighPriority = OptionalParameter(argc,argv,"HIGH");
- if ( OptionalParameter(argc,argv,"TIMEOUT",lTemp)
- && (MaxHangtime=atoi(lTemp)) < 1 )
- printf("Invalid TIMEOUT parameter\a\n"), abort();
- if ( OptionalParameter(argc,argv,"CYCLES",lTemp)
- && (CyclesPerMaxHangtime=atoi(lTemp)) < 1 )
- printf("Invalid CYCLES parameter\a\n"), abort();
- if ( OptionalParameter(argc,argv,"SUSPEND",lTemp)
- && (MinimumSuspendFactor=atoi(lTemp)) < 0 )
- printf("Invalid SUSPEND parameter\a\n"), abort();
- OptionalParameter(argc,argv,"COMMAND",HangCommand);
- }
-
-
- /////////////////////////////////////////////////////////////////////////
-
- #define HWND_DESKTOP 1
- #define HWND_OBJECT 2
-
- SuspendFactor;
-
- UnhangForever()
- {
- // initialize suspend factor - 25 sounds like a good number to start with
- SuspendFactor = max(25,MinimumSuspendFactor);
-
- // initialize timers to know loops are run frequently enough
- TimeBetweenLoops = MaxHangtime / CyclesPerMaxHangtime ;
- PMShellProblemTime = 0; // PMSHELL gets treated special; we'll let it have
- // a little extra hung time to allow some other
- // process to hang first
- PMShellTimeout = MaxHangtime * (CyclesPerMaxHangtime + 2) / CyclesPerMaxHangtime;
-
- for( ; ; ) { // FOREVER
-
- LoopStartTime = time();
-
- if ( OnTop )
- PutMyselfOnTop();
-
- // build an array of all the queue from the desktop on down
- QueueList = BuildQueueList();
- PList = ProcessList();
-
- // Since it does no good to kill the very first queue, then don't test it
- //QueueList++;
-
- // For each queue, see if it is hung
- printf("\n\nProcess ID Queue Size Status\n");
- printf("---------- ------ ----- ------ ------");
- PMShellProblem = False;
- for ( q = GetArraySpan(QueueList); 0 <= q; q-- ) {
-
- hmq = QueueList[q];
- if SuspendFactor suspend(SuspendFactor);
-
- if ( GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize) ) {
- printf("\n%-10.10s %-5d %04X %-6d ",ProcessName,ProcessID,hmq,QueueSize);
- if ( HungQueue(hmq) ) {
- printf("HUNG");
-
- // If this is PMSHELL, then give it a little extra time
- if ( !strcmpi("PMSHELL",ProcessName) ) {
- PMShellProblem = True;
- if ( 0 == PMShellProblemTime )
- PMShellProblemTime = time();
- if ( difftime(time(),PMShellProblemTime) < PMShellTimeout )
- continue;
- }
- LogFile("Process %s, id = %d, queue = %04X, size = %d, hung\n",
- ProcessName,ProcessID,hmq,QueueSize);
- AttendToHungProcess(ProcessID,ProcessName);
- printf(" AND KILLED!\a\a\a");
- } else {
- printf("OK");
- }
- }
-
- }
- if ( !PMShellProblem )
- PMShellProblemTime = 0;
-
- // Adjust SuspendFactor to try to even-out processor time used by UNHANG.
- // If extra time is available then increase SuspendFactor. If not
- // enough time is available then decrease it.
- ElapsedTestTime = difftime(time(),LoopStartTime);
- ExcessTime = TimeBetweenLoops - ElapsedTestTime;
- SuspendFactor = max(MinimumSuspendFactor,SuspendFactor + ExcessTime/CyclesPerMaxHangtime);
- if ( 0 < ExcessTime ) {
- // a little time left over, so wait until time is up
- while ( difftime(time(),LoopStartTime) < TimeBetweenLoops )
- suspend(SuspendFactor);
- }
- printf("\n New SuspendFactor = %d (from excess %d)\n",SuspendFactor,ExcessTime);
- }
- }
-
-
- LogFile(Format)
- {
- if ( defined(LogFileName) ) {
- if ( fp = fopen(LogFileName,"a") ) {
- fprintf(fp,"\n");
- fprintf(fp,ctime(time()));
- va_start(valist,Format);
- vfprintf(fp,Format,valist);
- va_end(valist);
- fclose(fp);
- }
- }
- }
-
- BuildQueueList() // build array of all desktop window handles
- {
- AddQueueForWindowAndChildren(QList,HWND_DESKTOP);
- //AddQueueForWindowAndChildren(QList,HWND_OBJECT);
- return(QList);
- }
-
- AddQueueForWindowAndChildren(QList,ParentHandle)
- {
- // Get the queue for this parent handle
- Queue = GetWindowQueue(ParentHandle);
-
- // If this queue is not already in the queue list, then add it
- if ( !defined(QList) ) {
- QList[0] = Queue;
- } else {
- for ( i = GetArraySpan(QList); 0 <= i; i-- ) {
- if ( Queue == QList[i] )
- break;
- }
- if ( i < 0 )
- QList[1+GetArraySpan(QList)] = Queue;
- }
-
- // recursively call this routine for all its children
- EnumHandle = WinBeginEnumWindows(ParentHandle);
- while ( NULL != (WinHandle = WinGetNextWindow(EnumHandle)) ) {
- if SuspendFactor suspend(SuspendFactor);
- AddQueueForWindowAndChildren(QList,WinHandle);
- }
- WinEndEnumWindows(EnumHandle);
- if SuspendFactor suspend(SuspendFactor);
- }
-
-
- HungQueue(hmq) // return True if this queue is hung
- {
- if ( !GetQueueInfo(hmq,MsgCount,ProcessID) )
- // this no longer appears to be a queue; and so never mind
- return(False);
-
- // determine if queue is hung by sending lots of NULL message to it
- // then wait a second and if sending null message fails then it is hung
- MsgCountPerLoop = MsgCount / CyclesPerMaxHangtime ;
- for ( i = MsgCountPerLoop; 0 < i--; ) {
- if SuspendFactor suspend(SuspendFactor);
- if ( !PostNullMsgToQueue(hmq) ) {
- // there appears to have been an error posting a null message.
- // and so the queue must be full (or no longer valid). Wait
- // some seconds and give it one more chance
- suspend( 5 * 1000 );
- return( !PostNullMsgToQueue(hmq)
- && GetQueueInfo(hmq,MsgCount,ProcessID) );
- }
- }
- return(False);
- }
-
- WinBeginEnumWindows(pHwndParent)
- {
- #define ORD_WIN32BEGINENUMWINDOWS 702
- return DynamicLink("PMWIN",ORD_WIN32BEGINENUMWINDOWS,BIT32,CDECL,pHwndParent)
- }
-
- WinGetNextWindow(pHEnum)
- {
- #define ORD_WIN32GETNEXTWINDOW 756
- return DynamicLink("PMWIN",ORD_WIN32GETNEXTWINDOW,BIT32,CDECL,pHEnum)
- }
-
- WinEndEnumWindows(pHEnum)
- {
- #define ORD_WIN32ENDENUMWINDOWS 737
- return DynamicLink("PMWIN",ORD_WIN32ENDENUMWINDOWS,BIT32,CDECL,pHEnum)
- }
-
- GetWindowQueue(hwnd)
- {
- #define ORD_WIN32QUERYWINDOWULONG 843
- #define QWL_HMQ (-4)
- return DynamicLink("PMWIN",ORD_WIN32QUERYWINDOWULONG,BIT32,CDECL,
- hwnd,QWL_HMQ)
- }
-
- GetQueueInfo(hmq,count,pid)
- {
- #define ORD_WIN32QUERYQUEUEINFO 824
- BLOBSize(qinfo,4 * 5);
- success = DynamicLink("PMWIN",ORD_WIN32QUERYQUEUEINFO,BIT32,CDECL,
- hmq,qinfo,BLObSize(qinfo));
- if ( success ) {
- pid = BLObGet(qinfo,4 * 1,UWORD32);
- count = BLObGet(qinfo,4 * 3,UWORD32);
- }
- return(success);
- }
-
- GetQueueData(hmq,PList,ProcessName,ProcessID,QueueSize)
- // return data about this queue and who owns it; False if cannot
- {
- if ( !GetQueueInfo(hmq,QueueSize,ProcessID) )
- return(False);
-
- // to find process name, match processID to id in PList
- for ( p = GetArraySpan(PList); 0 <= p; p-- ) {
- if ( ProcessID == PList[p].id ) {
- ProcessName = SplitFileName(PList[p].name).name;
- return(True);
- }
- }
- // if got here, then all OK except for process name
- ProcessName = "?????";
- return(True);
- }
-
- PostNullMsgToQueue(hmq)
- {
- #define ORD_WIN32POSTQUEUEMSG 902
- #define WM_NULL 0
- return DynamicLink("PMWIN",ORD_WIN32POSTQUEUEMSG,BIT32,CDECL,
- hmq,WM_NULL,MessageID,0,0)
- }
-
- DeadList; // keep a list of processes already killed
- AttendToHungProcess(id,name)
- {
- // determine if this process already (attempted) killed
- if ( !defined(DeadList) ) {
- DeadList[0] = id;
- } else {
- for ( _d = GetArraySpan(DeadList); 0 <= _d; _d-- ) {
- if ( DeadList[_d] == id ) {
- AttendToHungProcessParent(id);
- }
- }
- if ( _d < 0 ) {
- // this id hasn't been killed before; add to list
- DeadList[GetArraySpan(DeadList)+1] = id;
- }
- }
-
- if ( !defined(HangCommand) ) {
- #define DKP_PROCESSTREE 0 // kill process and all descendents, if created by this process
- #define DKP_PROCESS 1 // kill any process even if not created by this process
- #define ORD_DOS32KILLPROCESS 235
- DynamicLink("doscalls",ORD_DOS32KILLPROCESS,BIT32,CDECL,DKP_PROCESS,id);
- } else {
- system("%s %d %s",HangCommand,id,name);
- }
- }
-
- AttendToHungProcessParent(id)
- {
- // get parent of this id, and then KILL IT
- list = ProcessList();
- // find entry for this process ID
- for ( proc = GetArraySpan(list); 0 <= proc; proc-- ) {
- if ( list[proc].id == id ) {
- parent = list[proc].parent;
- for ( _i = GetArraySpan(list); 0 <= _i; _i-- ) {
- if ( list[_i].id == parent ) {
- LogFile("Kill Parent Process %s, id = %d\n",
- list[_i].name,parent);
- AttendToHungProcess(parent,list[_i].name);
- }
- }
- }
- }
- }
-
- RunAtHighPriority() // make this a high priority process
- {
- #define ORD_DOS32SETPRIORITY 236
- #define PRTYC_TIMECRITICAL 3
- DynamicLink("doscalls",ORD_DOS32SETPRIORITY,BIT32,CDECL,0,PRTYC_TIMECRITICAL,0,0)
- }
-
- HideMyself()
- {
- #define SWP_HIDE 0x0010
- #define ORD_WIN32SETWINDOWPOS 875
- DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
- Info().WinHandle,0,0,0,0,0,SWP_HIDE);
- }
-
- PutMyselfOnTop()
- {
- #define HWND_TOP 3
- #define SWP_ZORDER 4
- #define ORD_WIN32SETWINDOWPOS 875
- DynamicLink("PMWIN",ORD_WIN32SETWINDOWPOS,BIT32,CDECL,
- Info().WinHandle,HWND_TOP,0,0,0,0,SWP_ZORDER);
- }
-