/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.30  JRR     22-Jan-1992	Use killpg for RS6000 and not kill.
*       V01.40  JRR     22-Jan-1992	Add messages to diagnose RS6000 problem.
*       V01.5 	JRR     02-Mar-1992	Added Cosmic V2 code.
*       V01.6   JRR     08-Apr-1992     Added CERN enhancments
*	V01.7	JRR	17-Jun-1992	Added header.
*	V01.8	JRR	04-Nov-1992	Write status message before delete.
*	V01.9	JRR	01-Feb-1993	Added prototypes.
*	V01.10	JRR	11-Mar-1993	Fixed compile errors with get_node.
*	V01.11	JRR	19-Aug-1993	Trying kill rather than killpg.
*	V01.12	JRR	24-Feb-1994	Put the Irix 5.n fixes back in.
*	V01.13	JRR	28-Feb-1994	Added support for SOLARIS.
*/
/*++ nqs_delreq.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_delreq.c,v $
 *
 * DESCRIPTION:
 *
 *	Delete an NQS request.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.13 $ $Date: 1994/03/30 20:36:32 $ $State: Exp $)
 * $Log: nqs_delreq.c,v $
 * Revision 1.13  1994/03/30  20:36:32  jrroma
 * Version 3.35.6
 *
 * Revision 1.12  94/02/25  15:55:02  jrroma
 * Version 3.35.3
 * 
 * Revision 1.11  93/09/10  13:57:12  jrroma
 * Version 3.35
 * 
 * Revision 1.10  93/07/13  21:33:48  jrroma
 * Version 3.34
 * 
 * Revision 1.9  93/02/05  23:16:45  jrroma
 * Version 3.31
 * 
 * Revision 1.8  92/12/22  15:39:50  jrroma
 * Version 3.30
 * 
 * Revision 1.7  92/06/18  17:30:57  jrroma
 * Added gnu header
 * 
 * Revision 1.6  92/05/06  10:39:29  jrroma
 *  Version 3.20
 * 
 * Revision 1.5  92/03/02  13:34:33  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.4  92/01/22  16:21:44  jrroma
 * Added messages to diagnose RS6000 problem.
 * 
 * Revision 1.3  92/01/22  13:43:16  jrroma
 * Use killpg for RS6000 and not kill.
 * 
 * Revision 1.2  92/01/17  10:49:55  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  91/12/05  16:53:07  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <errno.h>
#include "transactcc.h"			/* Transaction completion codes */
#include <signal.h>
#include "nqsxvars.h"			/* NQS external vars and dirs  */
#include "nqsacct.h"

#if	SGI	
#include <sys/syssgi.h>
#include <sys/user.h>
#include <stdlib.h>
#include <time.h>
#include <sys/sysmp.h>
#include <sys/proc.h>
#include <sys/var.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#if   IRIX5
#include <sys/fault.h>
#include <sys/syscall.h>
#include <sys/procfs.h>
#endif
int	memfd;
int	procsize;
int	do_count;
int	total_time;	/* total cpu time */
time_t	start_time;
#endif
#if	IBMRS
#define	    MAX_PROC	1000
#include <nlist.h>
#include <procinfo.h>	
#include <stdlib.h>
#include <time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int	memfd;
struct nlist nlist;
int	do_count;
int	total_time;	/* total cpu time */
time_t	start_time;
#endif

#ifndef __CEXTRACT__
#if __STDC__

#if	SGI
static int get_node ( struct proc *pointer, int pid_oi );
#else
static int get_node ( struct proc *pointer, int pid_oi );
#endif
static int summary ( int pid_oi );
static int write_acct_record ( struct request *req );
#if	IBMRS
static get_node_finduser( long pid,  struct user *user );
#endif

#else /* __STDC__ */

static int get_node (/* struct proc *pointer, int pid_oi */);
static int summary (/* int pid_oi */);
static int write_acct_record (/* struct request *req */);
#if	IBMRS
static get_node_finduser(/* long pid,  struct user *user */);
#endif

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */



/*** nqs_delreq
 *
 *
 *	long nqs_delreq():
 *	Delete an NQS request.
 *
 *	Returns:
 *		TCML_NOSUCHREQ:	 if the specified request does not
 *				 exist on this machine.
 *		TCML_NOSUCHSIG:  if the specified request was running
 *				 or in transit, and a signal trans-
 *				 mission was specified for a signal
 *				 not recognized by the supporting
 *				 UNIX implementation.
 *		TCML_NOTREQOWN:	 if the mapped user-id does not match
 *				 the current mapped user-id of the
 *				 request owner.
 *		TCML_PEERDEPART: if the specified request is presently
 *				 being routed by a pipe queue.
 *		TCML_REQDELETE:	 if the specified request was deleted.
 *		TCML_REQRUNNING: if the specified request is running,
 *				 and no signal was specified.
 *		TCML_REQSIGNAL:	 if the request was signalled (or will
 *				 be signalled immediately upon receipt
 *				 of the process-family group.
 *
 */
long nqs_delreq (mapped_uid, orig_seqno, orig_mid, target_mid, sig, state)
uid_t mapped_uid;			/* Mapped owner user-id */
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
Mid_t target_mid;			/* Target Machine-id of request */
int sig;				/* Signal to send to processes */
					/* in a running request.  Sig = 0 */
					/* if no signal should be sent to */
					/* the request processes if running */
int state;				/* Request queue state: RQS_ */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	short reqindex;			/* Request index in Runvars if */
					/* the request is running, or */
					/* index in Runvars if subrequest */
					/* is transporting parent request */
					/* to its destination via a pipe/ */
					/* network queue pair */

	if (Debug > 2) {
	    printf("D$nqs_delreq: mapped_uid = %d.\n", mapped_uid);
	    printf("D$nqs_delreq: orig_seqno = %d.\n", orig_seqno);
	    printf("D$nqs_delreq: orig_mid = %d.\n", orig_mid);
	    printf("D$nqs_delreq: target_mid = %d.\n", target_mid);
	    printf("D$nqs_delreq: sig = %d.\n", sig);
	    printf("D$nqs_delreq: state = %d.\n", state);
	    fflush(stdout);	
	}
	/*
	 *  Search for the request.
	 */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
		/*
		 *  The request was not found in any of the local queues.
		 */
		return (TCML_NOSUCHREQ);
	}
        if (req->v1.req.uid != mapped_uid && mapped_uid != 0) {
		/*
		 *  This request cannot be affected by the client.
		 */
		return (TCML_NOTREQOWN);
	}
	if (state == RQS_DEPARTING) {
		/*
		 *  MOREHERE someday.  The batch request is returning
		 *  output files to the proper locations.  Send SIGTERM.
		 *
		 *	-- OR --
		 *
		 *  A request may be in the very process of transiting
		 *  to another machine.  Send SIGINT.
		 *
		 *  But for now, this ****TRIAGE**** implementation simply
		 *  returns peer-depart.
 		 */
		return (TCML_PEERDEPART);
	}
	if (state == RQS_RUNNING) {	/* The request is running, or */
					/* is being routed by a pipe queue */
		if (req->queue->q.type == QUE_PIPE ||
		   (req->status & RQF_PREDEPART)) {	/* ****TRIAGE**** */
			/*
			 *  The request is presently being routed by a
			 *  pipe queue.
			 *
			 *  This ****TRIAGE**** implementation does not
			 *  allow the deletion of requests presently
			 *  being routed by a pipe queue.
	 		 */
			return (TCML_PEERDEPART);
		}
		if (sig) {
			/*
			 *  The request is presently running, and is NOT
			 *  being routed by a pipe queue.
			 */
#if	BSD43 | ULTRIX | DECOSF | SOLARIS | LINUX
			if (sig < 1 || sig > SIGPROF) {
#else
#if	HPUX | SGI | SYS52 | IBMRS 
			if (sig < 1 || sig > SIGPWR) {
#else
BAD SYSTEM TYPE
#endif
#endif
				/*
				 *  No such signal.
				 */
				return (TCML_NOSUCHSIG);
			}
			/*
			 * Write out the accounting record in case ...
			 */
			write_acct_record(req);
			/*
			 *  Send the signal.  The signal may not necessarily
			 *  kill the request (unless it is of the SIGKILL (9)
			 *  variety).
			 */
			reqindex = req->reqindex;
			if ( (Runvars+reqindex)->process_family == 0) {
				/*
				 *  The process group/family of the server
				 *  has not yet been reported in....  Queue
				 *  the signal so that it can be sent later
				 *  upon receipt of the process group/family
				 *  packet for this request.
				 */
				if ((req->status & RQF_SIGQUEUED) == 0) {
					/*
					 *  We are NOT allowed to overwrite
					 *  any previously pending signal!
					 *  For example, nqs_aboque() may
					 *  have queued a signal for this
					 *  request.  Nqs_aboque() always wins.
					 */
					req->status |= RQF_SIGQUEUED;
					(Runvars+reqindex)->queued_signal = sig;
				}
			}
			else {
				/*
				 *  The process group/family of the server
				 *  is known.  Send the signal.
				 */
				kill (-(Runvars+reqindex)->process_family, sig);
				if (Debug > 2) {
				    printf("D$nqs_delreq: \
sent %d signal to %d process family.\n", sig, Runvars[reqindex].process_family);
				    fflush (stdout);
				}
			}
			return(TCML_REQSIGNAL);	/* Req has been signalled, */
						/* or req will be signalled*/
						/* immediately upon receipt*/
						/* of the process-family */
		}
		return (TCML_REQRUNNING);	/* Request not deleted */
	}
	/*
	 *  The request is not running.
	 *  Simply delete the request.
	 */
	nqs_deque (req);		/* Remove the request from the queue */
					/* setting the QUE_UPDATE bit */
	nqs_disreq (req, 1);		/* Dispose of request */
	udb_qorder (req->queue);	/* Update queue image in database */
	return (TCML_REQDELETE);	/* Request deleted */
}
/*
 * Write out the accounting record -- in case the shepard drops the ball!
 */
static write_acct_record(req)
struct request *req;
{
        int fd_acct;                    /* Accounting file descriptor */
        struct nqsacct_fin1 acct_fin1;  /* Accounting structure to report */
                                        /* Cpu usage                      */

        fd_acct = open(NQSACCT_FILE,
                        O_WRONLY|O_APPEND|O_CREAT, 0644);
        if (fd_acct < 0) {
            printf ("E$Error opening NQS account file;  Errno = %d\n", errno);
            fflush (stdout);
        } else {
            bytezero((char *)&acct_fin1, sizeof(acct_fin1));
            acct_fin1.h.type = NQSACCT_STAT;
            acct_fin1.h.length = sizeof(acct_fin1);
            acct_fin1.h.jobid = (Runvars + req->reqindex)->process_family;
            strncpy(acct_fin1.user,getusenam(req->v1.req.uid),
                                sizeof(acct_fin1.user));
            strncpy(acct_fin1.queue, req->queue->q.namev.name,
                                sizeof(acct_fin1.queue));
#if     SGI | IBMRS
	    summary((Runvars + req->reqindex)->process_family);
            acct_fin1.tms_stime = 0;
            acct_fin1.tms_utime = total_time;
#else
#if     HPUX | SYS52 | SOLARIS | LINUX
	    /*
	     * No way to check these,  so just give as zero.
	     */
            acct_fin1.tms_stime = 0;
            acct_fin1.tms_utime = 0;
#else
#if     BSD43 | ULTRIX | DECOSF
            acct_fin1.s_sec = 0;
            acct_fin1.s_usec = 0;
            acct_fin1.u_sec = 0;
            acct_fin1.u_usec = 0;
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
             acct_fin1.orig_mid = req->v1.req.orig_mid;
	     acct_fin1.seqno = req->v1.req.orig_seqno;
	     acct_fin1.fin_time = time ((time_t *) 0);
        }
        write(fd_acct, &acct_fin1, sizeof(acct_fin1));
        close(fd_acct);
  }
#if	SGI

static summary(pid_oi)
int	pid_oi;		/* pid of interest */
{

	struct	tm  *start_tm;
	int	status;
	struct proc	*poffset;
	struct	proc   proc_buff;

	total_time = 0;
	start_time = 0;
	/* printf( "pid is %d\n", pid_oi); */
	procsize = sysmp(MP_KERNADDR,  MPKA_PROCSIZE);
	/*printf ("Procsize is %d\n", procsize); */
	poffset = (struct proc *) sysmp(MP_KERNADDR,  MPKA_PROC);
	/*printf ("pointer is %x (or %d)\n", poffset, poffset); */
	memfd = open ("/dev/kmem", O_RDONLY);
	if (memfd == -1) {
	    printf("E$nqs_delreq: Error opening /dev/kmem, errno: %d\n", errno);
	    fflush(stdout);
	}
	poffset =  (struct proc *) ((int) poffset & ~0x80000000);
	/* printf ("pointer is %x (or %d)\n", poffset, poffset); */
	status = lseek(memfd, (int) poffset, SEEK_SET);
	if (status != (int) poffset) {
	    printf("E$nqs_delreq: Error in lseek, errno: %d\n", errno);
	    fflush(stdout);
	}
	status = read (memfd, &proc_buff, procsize);
	if (status != procsize) {
	    printf("E$nqs_delreq: Error in read, errno: %d\n", errno);
	    fflush(stdout);
	}
	poffset =  proc_buff.p_child;
	do_count = 0;
	get_node(poffset, pid_oi);
}
static get_node(pointer, pid_oi)
struct proc	*pointer;
int	pid_oi;
{
	int	status;
	struct  user  *uptr;
	struct user   auser;
	struct	proc   proc_buff;
#if     IRIX5
        int     fd;
        int     retval;
        prstatus_t prstatus;
        char char_pid[16];
#endif


	if (pointer == 0) return(0);
        pointer =  (struct proc *) ( (int )pointer & ~0x80000000);
        status = lseek(memfd, (int) pointer, SEEK_SET);
        if (status != (int) pointer) {
	    printf("E$nqs_delreq: Error in lseek, errno: %d\n", errno);
	    fflush(stdout);
	    return (0);
        }
        status = read (memfd, &proc_buff, procsize);
        if (status != procsize) {
	    printf("E$nqs_delreq: Error in lseek, errno: %d\n", errno);
	    fflush(stdout);
	    return (0);
        }
	if (proc_buff.p_pid == pid_oi) do_count++;
        /* if (do_count) printf(">>>>>>>>Pid is %d\n", proc_buff.p_pid); */
	uptr = &auser;
	status = syssgi (SGI_RDUBLK, proc_buff.p_pid, uptr, sizeof(auser) );
#if   IRIX5
        if (do_count) {
            sprintf(char_pid, "/proc/%05d", proc_buff.p_pid);
            fd = open (char_pid, O_RDONLY);
            if (fd != -1) {
                retval = ioctl(fd, PIOCSTATUS, &prstatus);
                total_time += 100 * (prstatus.pr_cutime.tv_sec +
                                prstatus.pr_cstime.tv_sec +
                                prstatus.pr_utime.tv_sec +
                                prstatus.pr_stime.tv_sec);
                close (fd);
            }
        }
#else

	if (do_count) total_time += uptr->u_utime+uptr->u_stime
			+uptr->u_cutime+uptr->u_cstime;
#endif
	if (do_count) {
	    if (start_time == 0) start_time = uptr->u_start;
	    else if (start_time > uptr->u_start) start_time = uptr->u_start;
	}
        status = get_node(proc_buff.p_child,pid_oi);
	if (proc_buff.p_pid == pid_oi) do_count = 0;
	status = get_node(proc_buff.p_sibling, pid_oi);
}
#endif
#if	IBMRS

static summary(pid_oi)
int	pid_oi;		/* pid of interest */
{

	struct	tm  *start_tm;
	int	status;
	int	poffset;
	ulong   procstart;
	struct	proc   myproc;
	
	total_time = 0;
#if	DEBUG_QSTAT
	printf( "pid is %d\n", pid_oi);
#endif
	memfd = open ("/dev/kmem", O_RDONLY);
	if (memfd == -1) {
	    printf("E$nqs_delreq: Error in lseek, errno: %d\n", errno);
	    fflush(stdout);
	    return;
	}
	nlist.n_name = "proc";
	knlist(&nlist, 1, sizeof(struct nlist));
	procstart = nlist.n_value;
#if	DEBUG_QSTAT
	printf ("pointer is %x (or %d)\n", procstart, procstart); 
#endif
	status = findproc( procstart,  &myproc);
	poffset = (int) myproc.p_child;
	do_count = 0;
	get_node(poffset, pid_oi);

}
static get_node(pointer, pid_oi)
int	pointer;
int	pid_oi;
{
	int	status;
	struct  user  myuser;
	struct	proc   myproc;

	if (pointer == 0) return(0);
	status = findproc(pointer,  &myproc);
	if (myproc.p_pid == pid_oi) do_count++;
#if	DEBUG_QSTAT
        if (do_count) printf(">>>>>>>>Pid is %d\n", myproc.p_pid);
#endif
	status = get_node_finduser(myproc.p_pid,  &myuser);
	if (do_count) total_time += myuser.u_utime+myuser.u_stime
			+myuser.u_cutime+myuser.u_cstime;
#if	DEBUG_QSTAT
	printf("Total time is %d\n",  total_time);
#endif
	if (do_count) {
	    if (start_time == 0) start_time = myuser.u_start;
	    else if  (start_time > myuser.u_start) start_time = myuser.u_start;
	}
        status = get_node(myproc.p_child,pid_oi);
	if (myproc.p_pid == pid_oi) do_count = 0;
	status = get_node(myproc.p_siblings, pid_oi);
}
static findproc(address,  myproc)
ulong address;
struct proc *myproc;
{
    int	    rtn;
    ulong   offset;
    offset = address & 0x7fffffff;
    rtn = lseek(memfd,  offset,  0);
    if (rtn == -1) return (0);
    rtn = readx(memfd, myproc, sizeof(struct proc),  1);
    if (rtn == -1) return (0);
    return(1);
}
get_node_finduser(pid,  user)
long	pid;
struct user *user;
{
    struct procinfo pinfo[MAX_PROC];
    int	proc_no,  nproc;
    int status;
    
    nproc = getproc(&pinfo[0],  MAX_PROC,  sizeof(struct procinfo) );
#if DEBUG_QSTAT
    printf("Nproc = %d\n",  nproc);
#endif
    for (proc_no = 0; proc_no < nproc; proc_no++) {
	if (pid == pinfo[proc_no].pi_pid) break;
    }
    status = getuser(&pinfo[proc_no],  sizeof(struct procinfo),  user, 
		    sizeof(struct user) );
#if DEBUG_QSTAT
    printf("Getuser status is %d\n",  status);
#endif
    return (status);
}
#endif
