/*
**++
**
**  INTPROC$TIMEOUT.C
**
**  Abstract:
**
**	Finds inactive process trees and after a time period has
**	elapsed, aborts them.
**
**  Calls:
**
**	GET_PROC_INFO
**	ADD_BY_MPID
**	WARN_PUNT_USER
**
**  Authors:
**
**	Duane Chandler
**
**  Creation date:  5-FEB-1987
**
**  Modification history:
**
**	9-FEB-1987, by Duane Chandler
**	    Added capability to adjust number of warnings given before a
**	    process tree is aborted.
**	    Added checking of returns from the call to SYS$TRNLNM to see
**	    if input logical name parameters were translated successfully
**	    and, if not, to use hard-wired defaults.
**
**
**--
**/

#include stdio
#include ssdef
#include jpidef
#include descrip
#include lnmdef
#include brkdef

#include "intproc$timeout.h"

unsigned int	get_proc_info(),
		add_by_mpid(),
		sort_proc_mpid(),
		warn_punt_user();


static	struct  process *proc_old[MAXPROC], /* arrays for all processes */
			*proc_new[MAXPROC];


main()
    {
    unsigned int    bintim[2],			/* quadword for system time   */
		    rllnm = 0,			/* return length of log name  */
		    warnings = 0,		/* number of warnings to give */
		    idle_passes = 0,		/* idle passes a process may  */
						/* have before it is deleted  */
		    status = 0,			/* return for system services */
		    count = 0, i = 0, j = 0,    /* counters */
		    old_num_mpid = 0,		/*  ditto   */
		    new_num_mpid = 0,		/*  ditto   */
		    sensitivity = 3,		/* cputim sensitivity control */
		    carcon = 16842752,		/* bit mask %x0101000 for     */
						/* $BRKTHRU carriage cont.    */
		    proc_size;			/* size of the process	      */
						/* array for LIB$MOVC3	      */

    char    eqvstr[LNMLEN],			/* return buffer for $TRNLNM  */
	    interval[LNMLEN],			/* interval between passes    */
	    str_warnings[LNMLEN],		/* string number of warnings  */
	    str_idle_passes[LNMLEN],		/* string number of idle      */
						/* passes to be converted     */
						/* to integer		      */
	    message[MSGLEN],			/* message buffer for $BRKTHRU*/
	    opcon[TERMLEN] = "OPA0:";		/* operator console	      */



    /* descriptors for messages to the operator's console */
    $DESCRIPTOR( conbuf, opcon );
    $DESCRIPTOR( msgbuf, message );

    /* descriptors for translating logical names and shipping them  */
    /* in for use as parameters at run time			    */
    $DESCRIPTOR( timbuf, "0000 00:00:00.00" );
    $DESCRIPTOR( lnm_tabnam, "LNM$SYSTEM_TABLE" );
    $DESCRIPTOR( lnm_interval, "IPT$_INTERVAL" );
    $DESCRIPTOR( lnm_warnings, "IPT$_WARNINGS" );
    $DESCRIPTOR( lnm_idle_passes, "IPT$_IDLE_PASSES" );


    struct itmlst		  /* array of structures to pass to TRNLNM as */
	{			  /* an itemlist. Last structure is zeroed to */
	unsigned short buflen;	  /* provide a terminating longword of zero   */
	unsigned short itemcode;
	unsigned int   *bufadr;
	unsigned int   *lenadr;
	} itmlst[] =
             { LNMLEN, LNM$_STRING, &eqvstr, &rllnm,
	      0,0,0,0
	     };



    /* Translate logicals for parameters    */


    /* First, translate for interval	    */

    status = SYS$TRNLNM( 0, &lnm_tabnam, &lnm_interval, 0, &itmlst );
    if( status == SS$_NORMAL ) 
        strncpy( interval, eqvstr, rllnm );
    else
	strncpy( interval, "0000 00:02:00.00", sizeof( "0000 00:02:00.00" ) );

    /* Next, translate for number of idle passes to allow   */

    status = SYS$TRNLNM( 0, &lnm_tabnam, &lnm_idle_passes, 0, &itmlst );
    if( status == SS$_NORMAL ) 
	{
        strncpy( str_idle_passes, eqvstr, rllnm );
        idle_passes = atoi( str_idle_passes );
	}
    else
	idle_passes = 10;

    /* Next, translate for number of warnings to give	*/

    status = SYS$TRNLNM( 0, &lnm_tabnam, &lnm_warnings, 0, &itmlst );
    if( status == SS$_NORMAL ) 
	{
        strncpy( str_warnings, eqvstr, rllnm );
        warnings = atoi( str_warnings );
	}
    else
	warnings = 2;


    /* stuff the interval into the time buffer so $SCHDWK can see it	*/

    strcpy( timbuf.dsc$a_pointer, interval );
    timbuf.dsc$w_length = strlen( timbuf.dsc$a_pointer );


    /* Let ther humans know I've initialized myself */

    sprintf( message,
	    "%%IPT-I-INIT, interval %s, idle passes %d, warnings %d",
	    interval, idle_passes, warnings );

    msgbuf.dsc$w_length = strlen( message );
    status=SYS$BRKTHRU(0,&msgbuf,&conbuf,BRK$C_DEVICE,0,
		       carcon,0,BRK$C_USER1,0,0,0);


    /* Get some space to hold process information */

    for ( i = 0; i < MAXPROC; i++ )
    proc_old[i] = malloc( sizeof(struct process) );

    for ( i = 0; i < MAXPROC; i++ )
    proc_new[i] = malloc( sizeof(struct process) );


    /* Get the size of the process structure for later */

    proc_size = sizeof( struct process );


    /* Make first collection pass */

    count = get_proc_info( proc_old, MAXPROC, sensitivity );

    old_num_mpid = add_by_mpid( proc_old, count );

    /* Set my alarm clock at timbuf length intervals */

    SYS$BINTIM( &timbuf, &bintim );
    SYS$SCHDWK( 0, 0, bintim, bintim );


    FOREVER
	{
	SYS$HIBER();

        count = get_proc_info( proc_new, MAXPROC, sensitivity );

	new_num_mpid = add_by_mpid( proc_new, count );


	/* Get a record from the old array, whip through the new array	  */
	/* looking for a record which has the same master pid. When one	  */
	/* is found, check its cputime. If the cputime matches, increment */
	/* the timeout pass counter of the new array record and start     */
	/* looking at the next record from the old array and repeat until */
	/* there are no more old processes to look at			  */

	for( i = 0 ; i <= old_num_mpid ; i++ )
	    for( j = 0 ; j <= new_num_mpid ; j++ )
		if( proc_new[j]->mpid == proc_old[i]->mpid )
		    {
		    if( proc_new[j]->cputim == proc_old[i]->cputim )
			proc_new[j]->tmocnt = proc_old[i]->tmocnt + 1;
		    break;
		    }


	/* now that all the necessary information has been recorded, */
	/* the save copy of the array can now be updated by walking  */
	/* on the former copy and updating the number of elements    */
	/* counter to reflect the current number of processes.       */

	for( i = 0 ; i < MAXPROC ; i++ )
	    LIB$MOVC3( &proc_size, proc_new[i], proc_old[i] );

	old_num_mpid = new_num_mpid;


	/* Now the fun part.  If the number of idle passes for a given	    */
	/* master pid is one less than the limit, issue a warning message.  */
	/* If the number of idle passes for a given master pid is equal to  */
	/* the limit, punt it off the system; this also automatically	    */
	/* punts all its children off the system as well.		    */

        for( i=0; i < old_num_mpid ; i++ )
	    {
	    if( ( proc_old[i]->tmocnt < idle_passes ) &&
		  proc_old[i]->tmocnt >= idle_passes - warnings )
		warn_punt_user( proc_old[i], WARN );
	    if( proc_old[i]->tmocnt >= idle_passes )
		warn_punt_user( proc_old[i], PUNT );
	    }

	/* Go back to sleep */
	}
    }
