/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"
#include <time.h>
#include "uucp.h"

#ifdef	V7
#define O_RDONLY	0
#endif
#define KILLMSG "the system administrator has killed job"
#define USAGE1	"[-q] | [-m] | [-k JOB [-n]] | [-r JOB [-n]] | [-p]"
#define USAGE2	"[-a] [-s SYSTEM [-j]] [-u USER] [-S STATE]"
#define USAGE3  "-t SYSTEM [-d number] [-c]"
#define LOCK "LCK.."
#define STST_MAX	132
#define MAXDATE		12
#define MINTIME		60
#define MINUTES		60
#define CHAR		"a"
#define MAXSTATE	4
/* #include "logs.h" */
struct m {
	char	mach[15];		/* machine name */
	char	locked;
	int	ccount, xcount;
	int	count, type;
	long	retrytime;
	time_t lasttime;
	short	c_age;			/* age of oldest C. file */
	short	x_age;			/* age of oldest X. file */
	char	stst[STST_MAX];
} M[UUSTAT_TBL+2];


struct userdate {
	char uhour[3];
	char umin[3];
	char lhour[3];
	char lmin[3];
};

struct userdate userformat;
struct userdate *friendlyptr = &userformat;	

extern long atol();
static int whattodo();
static int readperf();
static void queuetime();
static void xfertime();
static char * gmt();
static char * gmts();
static void errortn();
static void friendlytime();
static void complete();
static int state();
static int gnameflck();
static void kprocessC();
static int convert();
void uprocessC(), printit(), docalc(), procState();

static short State, Queued, Running, Complete, Interrupted;

static char mailmsg[BUFSIZ];
static char outbuf[BUFSIZ+1];
static int count;
static short jobcount;
static short execute;
static char lowerlimit[MAXDATE+1], upperlimit[MAXDATE+1];
static float totalque, totalxfer;
static long totaljob, totalbytes;
static long inputsecs;
#ifdef ATTSV
extern void qsort();		/* qsort(3) and comparison test */
#endif /* ATTSV */
int sortcnt = -1;
extern int machcmp();
extern int _age();		/* find the age of a file */
static long calcnum;	
extern char Jobid[];	/* jobid for status or kill option */
short Kill;		/*  == 1 if -k specified */
short Rejuvenate;	/*  == 1 for -r specified */
short Uopt;		/*  == 1 if -u option specified */
short Sysopt;		/*  == 1 if -s option specified */
static short Calctime;   /*  == 1 if -t parameter set */
short Summary;		/*  == 1 if -q or -m is specified */
short Queue;		/*  == 1 if -q option set - queue summary */
short Machines;		/*  == 1 if -m option set - machines summary */
short Psopt;		/*  == 1 if -p option set - output "ps" of LCK pids */
static short Window;    /*  == 1 if -d parameter set with -t option */
static short nonotf;    /*  == 1 if -n parameter set with -k option */
short avgqueue;		/*  == 1 if -c parameter set with -t option */
short avgxfer;		/*  will be set to 1 if -c not specified    */
short Jobcount;		/* == 1 if -j parameter set with -s option */
char f[NAMESIZE];

int
main(argc, argv, envp)
char *argv[];
char **envp;
{
	struct m *m, *machine();
	DIR *spooldir, *subdir, *machdir, *gradedir;
	char *str, *rindex();
	char subf[256], gradef[256];
	char *c, lckdir[BUFSIZ];
	char buf[BUFSIZ];
	char chkname[MAXFULLNAME];
	char *vec[7];
	int i, chkid;
	char fullpath[MAXFULLNAME];
	long temp;
	
	char arglist[MAXSTATE+1];

	/* Set locale environment variables local definitions */
	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	User[0] = '\0';
	Rmtname[0] = '\0';
	Jobid[0] = '\0';
	Psopt=Machines=Summary=Queue=Kill=Rejuvenate=Uopt=Sysopt=Jobcount=0;
	execute=avgqueue=avgxfer=Calctime=Window=0;
	jobcount=nonotf=0;

	/* set calcnum to default time in minutes */
	calcnum=MINTIME;

	(void) strcpy(Progname, "uustat");
	Uid = getuid();
	Euid = geteuid();
	guinfo(Uid, Loginuser);
	uucpname(Myname);
	while ((i = getopt(argc, argv, "acjk:mnpr:qs:u:x:t:d:S:")) != EOF) {
		switch(i){
		case 'a':
			Sysopt = 1;
			break;
		case 'c':
			avgqueue = 1;
			break;
		case 'd':
			Window = 1;
			calcnum = atoi(optarg);
			if (calcnum <= 0)
				calcnum = MINTIME;
			break;
		case 'k':
			(void) strncpy(Jobid, optarg, NAMESIZE);
			Jobid[NAMESIZE-1] = '\0';
			Kill = 1;
			break;
		case 'j':
			Jobcount = 1;
			break;
		case 'm':
			Machines = Summary = 1;
			break;
		case 'n':
			nonotf = 1;
			break;
		case 'p':
			Psopt = 1;
			break;
		case 'r':
			(void) strncpy(Jobid, optarg, NAMESIZE);
			Jobid[NAMESIZE-1] = '\0';
			Rejuvenate = 1;
			break;
		case 'q':
			Queue = Summary = 1;
			break;
		case 's':
			(void) strncpy(Rmtname, optarg, MAXBASENAME);

			Rmtname[MAXBASENAME] = '\0';
			if (versys(Rmtname)) {
				fprintf(stderr, gettext("Invalid system\n"));
				exit(1);
			}
			Sysopt = 1;
			break;
		case 't':
			Calctime = 1;
			(void) strncpy(Rmtname, optarg, MAXBASENAME);
			Rmtname[MAXBASENAME] = '\0';
			if (versys(Rmtname)) {
				fprintf(stderr, gettext("Invalid system\n"));
				exit(1);
			}
			break;
		case 'u':
			(void) strncpy(User, optarg, 8);
			User[8] = '\0';
			if(gninfo(User, &chkid, chkname)) {
				fprintf(stderr, gettext("Invalid user\n"));
				exit(1);
			}
			Uopt = 1;
			execute = 1;
			break;
		case 'x':
			Debug = atoi(optarg);
			if (Debug <= 0)
				Debug = 1;
			break;
		case 'S':
			if (strlen(optarg) > sizeof (arglist)) {
				errortn();
				exit(1);
			}
			State = 1;
			(void) strlcpy(arglist, optarg, sizeof (arglist));
			procState(arglist);
			break;
		default:
			errortn();		
			exit(1);
		}
	}

	if (argc != optind) {
		errortn();
		exit(1);
	}

	DEBUG(9, "Progname (%s): STARTED\n", Progname);
	DEBUG(9, "User=%s, ", User);
	DEBUG(9, "Loginuser=%s, ", Loginuser);
	DEBUG(9, "Jobid=%s, ", Jobid);
	DEBUG(9, "Rmtname=%s\n", Rmtname);

	/* -j only allowed with -s */
	if (Jobcount && !Sysopt)
		{
		errortn();
		exit(1);
		}
       if ((Calctime + Psopt + Machines + Queue + Kill + Rejuvenate + (Uopt|Sysopt |State)) >1) {
		/* only -u, -S and -s can be used together */
		errortn();
		exit(1);
	}
	if ((avgqueue | Window) & (!Calctime))
		{
		errortn();
		exit(1);
	}

	if (  !(Calctime | Kill | Rejuvenate | Uopt | Sysopt | Queue| Machines | State) ) {
		(void) strcpy(User, Loginuser);
		Uopt = 1;
	}

	if ( nonotf && !(Kill | Rejuvenate) ) {
		errortn();
		exit(1);
	}

	/*****************************************/
	/* PROCESS THE OPTIONS                   */
	/*****************************************/

	if (State && Complete)
		{
		   DEBUG(9, "calling complete %d\n",Complete);
		   complete();
		}
	
	if (Calctime) {
		count = readperf(calcnum);

		if (count != 0)
			docalc();
		
	}

	if (Psopt) {
		/* do "ps -flp" or pids in LCK files */
		lckpid();
		/* lckpid will not return */
	}

	if (Summary) {
	    /*   Gather data for Summary option report  */
	    if (chdir(STATDIR) || (spooldir = opendir(STATDIR)) == NULL)
		exit(101);		/* good old code 101 */
	    while (gnamef(spooldir, f) == TRUE) {
		if (freopen(f, "r", stdin) == NULL)
			continue;
		m = machine(f);
		if (fgets(buf, sizeof(buf), stdin) == NULL)
			continue;
		if (getargs(buf, vec, 5) < 5)
			continue;
		m->type = atoi(vec[0]);
		m->count = atoi(vec[1]);
		m->lasttime = atol(vec[2]);
		m->retrytime = atol(vec[3]);
		(void) strncpy(m->stst, vec[4], STST_MAX);
		str = rindex(m->stst, ' ');
		(void) machine(++str);	/* longer name? */
		*str = '\0';
			
	    }
	    closedir(spooldir);
	}


	if (Summary) {
	    /*  search for LCK machines  */
	    char flck[MAXNAMESIZE];

	    (void) strcpy(lckdir, LOCKPRE);
	    *strrchr(lckdir, '/') = '\0';
	    /* open lock directory */
	    if (chdir(lckdir) != 0 || (subdir = opendir(lckdir)) == NULL)
		exit(101);		/* good old code 101 */

	    while (gnameflck(subdir, flck) == TRUE) {
		/* XXX - this is disgusting... */
		if (EQUALSN("LCK..", flck, 5)) {
		    if (!EQUALSN(flck + 5, "cul", 3)
		     && !EQUALSN(flck + 5, "cua", 3)
		     && !EQUALSN(flck + 5, "tty", 3)
		     && !EQUALSN(flck + 5, "dtsw", 4)
		     && !EQUALSN(flck + 5, "vadic", 5)
		     && !EQUALSN(flck + 5, "micom", 5))
			machine(flck + 5)->locked++;
		}
	    }
	}

	if (chdir(SPOOL) != 0 || (spooldir = opendir(SPOOL)) == NULL)
		exit(101);		/* good old code 101 */

	while (gnamef(spooldir, f) == TRUE) {
	 /* at /var/spool/uucp directory */
	 /* f will contain remote machine names */
	   
          if (EQUALSN("LCK..", f, 5))
		continue;

          if (*Rmtname && !EQUALSN(Rmtname, f, MAXBASENAME))
		continue;

          if ( (Kill || Rejuvenate)
	      && (!EQUALSN(f, Jobid, strlen(Jobid)-5)) )
		    continue;

	  if (DIRECTORY(f))  {
		if (chdir(f) != 0)
			exit(101);
		(void) sprintf(fullpath, "%s/%s", SPOOL, f);
		machdir = opendir(fullpath);
		if (machdir == NULL)
			exit(101);
				
		m = machine(f);
		while (gnamef(machdir, gradef) == TRUE) {
			/* at /var/spool/uucp/remote_name */
			/* gradef will contain job_grade directory names */

	     		if (DIRECTORY(gradef) && (gradedir = opendir(gradef))) {
				/* at /var/spool/uucp/remote_name/job_grade */

		  		while (gnamef(gradedir, subf) == TRUE) {
				    /* subf will contain file names */
				    /* files can be C. or D. or A., etc.. */

				    if (subf[1] == '.') {
		  		      if (subf[0] == CMDPRE) {
					/* if file name is C. */
					m->ccount++;

					if (Kill || Rejuvenate)
					    kprocessC(gradef, subf);
					else if (Uopt | Sysopt | Queued | Running | Interrupted) 
				 	   /* go print out C. file info */
				  	   uprocessC(f ,gradef, subf);

					else 	/* get the age of the C. file */
				 	   if ( (i = _age(gradef, subf))>m->c_age)
						m->c_age = i;
					}
		    		    }
				}
				closedir(gradedir);
			}

			else if (gradef[0] == XQTPRE && gradef[1] == '.') {
			   m->xcount++;
			   if ( (i = _age(machdir, gradef)) > m->x_age)
				m->x_age = i;
			}
		}
		closedir(machdir);
	  }	
	  /* cd back to /var/spoool/uucp dir */
	  if (chdir(SPOOL) != 0)
		exit(101);	
	} /* while more files in spooldir */
	closedir(spooldir);

	if (Jobcount && (jobcount != 0))
		printf("job count = %d\n",jobcount);

	/* for Kill or Rejuvenate - will not get here unless it failed */
	if (Kill) {
	    printf(gettext("Can't find Job %s; Not killed\n"), Jobid);
	    exit(1);
	} else if (Rejuvenate) {
	    printf(gettext("Can't find Job %s; Not rejuvenated\n"), Jobid);
	    exit(1);
	}

	/* Make sure the overflow entry is null since it may be incorrect */
	M[UUSTAT_TBL].mach[0] = NULLCHAR;
	if (Summary) {
	    for((sortcnt = 0, m = &M[0]);*(m->mach) != NULL;(sortcnt++,m++))
			;
	    qsort((char *)M, (unsigned int)sortcnt, sizeof(struct m), machcmp);
	    for (m = M; m->mach[0] != NULLCHAR; m++)
		printit(m);
	}
	return (0);
}


/*
 * uprocessC - get information about C. file
 *
 */

void
uprocessC(machine, dir, file)
char   *machine, *dir, *file;
{
	struct stat s;
	struct tm *tp;
	char fullname[MAXFULLNAME], buf[BUFSIZ], user[9];
	char xfullname[MAXFULLNAME];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	short goodRecord = 0;
	FILE *fp, *xfp;
	short first = 1;
	int statefound = 0;
	extern long fsize();
	char format_tmp[BUFSIZ+1];
	fp=xfp=NULL;

	/*********************************************/
	/* initialize output buffer to blanks        */
	/*********************************************/

	if (Complete && !Queued && !Running && !Interrupted)
		return;
	outbuf[0] = NULLCHAR;

	DEBUG(9, "uprocessC(%s, ", dir);
	DEBUG(9, "%s);\n", file);

	if (Jobid[0] != '\0' && (!EQUALS(Jobid, &file[2])) ) {
		/* kill job - not this one */
		return;
	}

	(void) sprintf(fullname, "%s/%s", dir, file);
	if (stat(fullname, &s) != 0) {
	     /* error - can't stat */
	    DEBUG(4, "Can't stat file (%s),", fullname);
	    DEBUG(4, " errno (%d) -- skip it!\n", errno);
	}

	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(4, "Can't open file (%s), ", fullname);
		DEBUG(4, "errno=%d -- skip it!\n", errno);
		return;
	}
	tp = localtime(&s.st_mtime);

	if (s.st_size == 0 && User[0] == '\0') { /* dummy D. for polling */
	    sprintf(format_tmp,"%-12s  %2.2d/%2.2d-%2.2d:%2.2d:%2.2d  (POLL)\n",
		&file[2], tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		tp->tm_min, tp->tm_sec);
		(void) strcat(outbuf, format_tmp);
	}
	else while (fgets(buf, BUFSIZ, fp) != NULL) {
	    if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
	      user, opt, file3) <5) {
		DEBUG(4, "short line (%s)\n", buf);
		continue;
	    }
	    DEBUG(9, "type (%s), ", type);
	    DEBUG(9, "file1 (%s)", file1);
	    DEBUG(9, "file2 (%s)", file2);
	    DEBUG(9, "file3 (%s)", file3);
	    DEBUG(9, "user (%s)", user);

	    goodRecord = 0;

	    if (User[0] != '\0' && (!EQUALS(User, user)) ) 
		continue;


	    if (first)
	       {
	        sprintf(format_tmp,"%-12s", &file[2]);
		(void) strcat(outbuf, format_tmp);

		/* if the job state is requested call the
		   state function to determine this job's state */

		if (State)
		{ 
		   statefound = state(dir, file);		
	           DEBUG(9, "uprocessC: statefound value = %d\n", statefound);
 		   if ((whattodo(statefound) != TRUE))
			{
			   outbuf[0] = NULLCHAR;
			   return;
			}
		   else
			{
			   if (statefound == 1)
				(void) strcat(outbuf, "queued");
			   else if (statefound == 2)
				(void) strcat(outbuf, "running");
			   else if (statefound == 3)
				(void) strcat(outbuf, "interrupted");
			}
		}
		sprintf(format_tmp, " %2.2d/%2.2d-%2.2d:%2.2d ",
		    tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		    tp->tm_min);
		(void) strcat(outbuf, format_tmp);
	       }
	    else
	       {
		sprintf(format_tmp,"%-12s %2.2d/%2.2d-%2.2d:%2.2d ",
		    "", tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		    tp->tm_min);
		(void) strcat(outbuf, format_tmp);
		}
	    first = 0;

	    sprintf(format_tmp,"%s  %s  ", type, machine);
	    (void) strcat(outbuf, format_tmp);
	    if (*type == 'R')
	       {
	        sprintf(format_tmp,"%s  %s ", user, file1);
	        (void) strcat(outbuf, format_tmp);
	       }
	    else if (file2[0] != 'X')
		{
		 sprintf(format_tmp,"%s %ld %s ", user, fsize(dir, file3, file1), file1);
		 (void) strcat(outbuf, format_tmp);
		}
	    else if (*type == 'S' && file2[0] == 'X') {
		(void) sprintf(xfullname, "%s/%s", dir, file1);
		xfp = fopen(xfullname, "r");
		if (xfp == NULL) { /* program error */
		    DEBUG(4, "Can't read %s, ", xfullname);
		    DEBUG(4, "errno=%d -- skip it!\n", errno);
		    sprintf(format_tmp,"%s  ", user);
		    (void) strcat(outbuf, format_tmp);
		    (void) strcat(outbuf,"????\n");
		}
		else {
		    char command[BUFSIZ], uline_u[BUFSIZ], uline_m[BUFSIZ];
		    char retaddr[BUFSIZ], *username;

		    *retaddr = *uline_u = *uline_m = '\0';
		    while (fgets(buf, BUFSIZ, xfp) != NULL) {
			switch(buf[0]) {
			case 'C':
				strcpy(command, buf + 2);
				break;
			case 'U':
				sscanf(buf + 2, "%s%s", uline_u, uline_m);
				break;
			case 'R':
				sscanf(buf+2, "%s", retaddr);
				break;
			}
		    }
		    username = user;
		    if (*uline_u != '\0')
			    username = uline_u;
		    if (*retaddr != '\0')
			username = retaddr;
		    if (!EQUALS(uline_m, Myname))
			printf("%s!", uline_m);
		    sprintf(format_tmp,"%s  %s", username, command);
		    (void) strcat(outbuf, format_tmp);
		}
	    }
       	strcat(outbuf, "\n");
	fputs(outbuf, stdout);
        outbuf[0] = NULLCHAR;
	goodRecord = 1;
       } /* end of while more data in buffer */
	
       /* successful processing of a job, increment job count
	  counter */	
	if (goodRecord)
            jobcount++;

	if (xfp != NULL)
	    fclose(xfp);

	fclose(fp);
	return;
}
/*
 * whattodo - determine what to do with current C dot file
 *  	     depending on any combination (2**3 - 1) of input 
 *	     job states
 */
static int
whattodo(inputint)
int inputint;
{
	/* Maybe some commentary here will help explain this truth
	   table.

	Queued		|Running	|Interrupted
	-------------------------------------------------
		X	|		|
	-------------------------------------------------
			|	X	|
	-------------------------------------------------
			|		|      X
	-------------------------------------------------
		X	|	X	|
	-------------------------------------------------
			|	X	|      X
	-------------------------------------------------
		X	|		|      X
	-------------------------------------------------
		X	|	X	|      X
	-------------------------------------------------

	Now do you understand.  All  possible combinations have to
	be evaluated to determine whether or not to print the C dot
	information out or not! Well, all but 000, because if neither
	of these states are input by the user we would not be 
	examing the C dot file anyway!
	*/

	if (Queued && Running && Interrupted)
		return(TRUE);
	else if ((Queued && !Running && !Interrupted) && (inputint == 1))
		return(TRUE);
	else if ((Running && !Queued && !Interrupted) && (inputint == 2))				return(TRUE);
	else if ((Interrupted && !Queued && !Running) && (inputint == 3))				return(TRUE);
	else if ((Queued && Running && !Interrupted) && 
		(inputint == 1 || inputint == 2))
	  		return(TRUE);
	else if ((!Queued && Running && Interrupted) && 
		(inputint == 2 || inputint == 3))
			return(TRUE);
	else if ((Queued && !Running && Interrupted) && 
		(inputint ==1 || inputint == 3))
			return(TRUE);
	else return(FALSE);
}
/*
 * kprocessC - process kill or rejuvenate job
 */

static void
kprocessC(dir, file)
char *file, *dir;
{
	struct stat s;
	struct tm *tp;
	extern struct tm *localtime();
	char fullname[MAXFULLNAME], buf[BUFSIZ], user[9];
	char rfullname[MAXFULLNAME];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	FILE *fp, *xfp;
 	struct utimbuf times;
	short ret;
	short first = 1;

	DEBUG(9, "kprocessC(%s, ", dir);
	DEBUG(9, "%s);\n", file);

	if ((!EQUALS(Jobid, &file[2])) ) {
		/* kill job - not this one */
		return;
	}

	(void) sprintf(fullname, "%s/%s", dir, file);
	if (stat(fullname, &s) != 0) {
	     /* error - can't stat */
	    if(Kill) {
		fprintf(stderr,
		  gettext("Can't stat:%s, errno (%d)--can't kill it!\n"),
		  fullname, errno);
	    } else {
		fprintf(stderr,
		  gettext("Can't stat:%s, errno (%d)--can't rejuvenate it!\n"),
		  fullname, errno);
	    }
	    exit(1);
	}

	fp = fopen(fullname, "r");
	if (fp == NULL) {
	    if(Kill) {
		fprintf(stderr,
		  gettext("Can't read:%s, errno (%d)--can't kill it!\n"),
		  fullname, errno);
	    } else {
		fprintf(stderr,
		  gettext("Can't read:%s, errno (%d)--can't rejuvenate it!\n"),
		  fullname, errno);
	    }
	    exit(1);
	}

 	times.actime = times.modtime = time((time_t *)NULL);
 
	while (fgets(buf, BUFSIZ, fp) != NULL) {
	    if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
	      user, opt, file3) <6) {
		if(Kill) {
		    fprintf(stderr,
		      gettext("Bad format:%s, errno (%d)--can't kill it!\n"),
		      fullname, errno);
		} else {
		    fprintf(stderr,
		      gettext("Bad format:%s, errno (%d)--can't rejuvenate it!\n"),
		      fullname, errno);
		}
	        exit(1);
	    }

	    DEBUG(9, "buf in uprocessK = %s\n ", buf);
	    DEBUG(9, "fullname is %s\n",fullname);
	    DEBUG(9, "type (%s), ", type);
	    DEBUG(9, "file1 (%s)", file1);
	    DEBUG(9, "file2 (%s)", file2);
	    DEBUG(9, "file3 (%s)", file3);
	    DEBUG(9, "user (%s)", user);


	    if (first) {
	        if ((access(fullname, 02) != 0)
		    && !PREFIX(Loginuser, user)
		    && !PREFIX(user, Loginuser) ) {
			/* not allowed - not owner or root */
			if(Kill)
			    fprintf(stderr, gettext("Not owner,"
			      " uucp or root - can't kill job %s\n"), Jobid);
			else
			    fprintf(stderr, gettext("Not owner, uucp or root -"
			      " can't rejuvenate job %s\n"), Jobid);
		    exit(1);
		}
		first = 0;
	    }

	    /* remove D. file */
	    (void) sprintf(rfullname, "%s/%s", dir, file3);
	    DEBUG(4, "Remove %s\n", rfullname);
	    if (Kill) 
		ret = unlink(rfullname);
	    else /* Rejuvenate */
 		ret = utime(rfullname, &times);
	    if (ret != 0 && errno != ENOENT) {
		/* program error?? */
		if(Kill)
		    fprintf(stderr, gettext("Error: Can't kill,"
		      " File (%s), errno (%d)\n"), rfullname, errno);
		else
		    fprintf(stderr, gettext("Error: Can't rejuvenated,"
		      " File (%s), errno (%d)\n"), rfullname, errno);
		exit(1);
	    }
	}

	DEBUG(4, "Remove %s\n", fullname);
	if (Kill)
	    ret = unlink(fullname);
	else /* Rejuvenate */
		ret = utime(fullname, &times);
	
	if (ret != 0) {
	    /* program error?? */
	    if(Kill)
	        fprintf(stderr, gettext("Error1: Can't kill,"
	          " File (%s), errno (%d)\n"), fullname, errno);
	    else
	        fprintf(stderr, gettext("Error1: Can't rejuvenate,"
	          " File (%s), errno (%d)\n"), fullname, errno);
	    exit(1);
	}
	/* if kill done by SA then send user mail */
	else if (!EQUALS(Loginuser, user))
	   {
		sprintf(mailmsg, "%s %s", KILLMSG, Jobid);
		mailst(user, "job killed", mailmsg, "", ""); 
	   }
	fclose(fp);
	if (!nonotf) {
		if(Kill)
			printf(gettext("Job: %s successfully killed\n"), Jobid);
		else
			printf(gettext("Job: %s successfully rejuvenated\n"),
			    Jobid);
		}
	exit(0);
}

/*
 * fsize - return the size of f1 or f2 (if f1 does not exist)
 *	f1 is the local name
 *
 */

long
fsize(dir, f1, f2)
char *dir, *f1, *f2;
{
	struct stat s;
	char fullname[BUFSIZ];

	(void) sprintf(fullname, "%s/%s", dir, f1);
	if (stat(fullname, &s) == 0) {
	    return(s.st_size);
	}
	if (stat(f2, &s) == 0) {
	    return(s.st_size);
	}

	return(-99999);
}

void cleanup(){}
void logent(){}		/* to load ulockf.c */
void systat(){}		/* to load utility.c */

struct m	*
machine(name)
char	*name;
{
	struct m *m;
	size_t	namelen;

	DEBUG(9, "machine(%s), ", name);
	namelen = strlen(name);
	for (m = M; m->mach[0] != NULLCHAR; m++)
		/* match on overlap? */
		if (EQUALSN(name, m->mach, MAXBASENAME)) {
			/* use longest name */
			if (namelen > strlen(m->mach))
				(void) strcpy(m->mach, name);
			return(m);
		}

	/*
	 * The table is set up with 2 extra entries
	 * When we go over by one, output error to errors log
	 * When more than one over, just reuse the previous entry
	 */
	DEBUG(9, "m-M=%d\n", m-M);
	if (m-M >= UUSTAT_TBL) {
	    if (m-M == UUSTAT_TBL) {
		errent("MACHINE TABLE FULL", "", UUSTAT_TBL,
		__FILE__, __LINE__);
		(void) fprintf(stderr,
		    gettext("WARNING: Table Overflow--output not complete\n"));
	    }
	    else
		/* use the last entry - overwrite it */
		m = &M[UUSTAT_TBL];
	}

	(void) strcpy(m->mach, name);
	m->c_age= m->x_age= m->lasttime= m->locked= m->ccount= m->xcount= 0;
	m->stst[0] = '\0';
	return(m);
}

void
printit(m)
struct m *m;
{
	struct tm *tp;
	time_t	t;
	int	minimum;
	extern struct tm *localtime();

	if (m->ccount == 0
	 && m->xcount == 0
	 /*&& m->stst[0] == '\0'*/
	 && m->locked == 0
	 && Queue
	 && m->type == 0)
		return;
	printf("%-10s", m->mach);
	if (Queue) {
		if (m->ccount)
			printf("%3dC", m->ccount);
		else
			printf("    ");
		if (m->c_age)
			printf("(%d)", m->c_age);
		else
			printf("   ");
		if (m->xcount)
			printf("%3dX", m->xcount);
		else
			printf("    ");
		if (m->x_age)
			printf("(%d) ", m->x_age);
		else
			printf("    ");
	} else
		printf(" ");

	if (m->lasttime) {
	    tp = localtime(&m->lasttime);
	    printf("%2.2d/%2.2d-%2.2d:%2.2d ",
		tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		tp->tm_min);
	}
/*	if (m->locked && m->type != SS_INPROGRESS) */
	if (m->locked)
		printf("Locked ");
	if (m->stst[0] != '\0') {
		printf("%s", m->stst);
		switch (m->type) {
		case SS_SEQBAD:
		case SS_LOGIN_FAILED:
		case SS_DIAL_FAILED:
		case SS_BAD_LOG_MCH:
		case SS_BADSYSTEM:
		case SS_CANT_ACCESS_DEVICE:
		case SS_DEVICE_FAILED:
		case SS_WRONG_MCH:
		case SS_RLOCKED:
		case SS_RUNKNOWN:
		case SS_RLOGIN:
		case SS_UNKNOWN_RESPONSE:
		case SS_STARTUP:
		case SS_CHAT_FAILED:
			(void) time(&t);
			t = m->retrytime - (t - m->lasttime);
			if (t > 0) {
				minimum = (t + 59) / 60;
				printf("Retry: %d:%2.2d", minimum/60, minimum%60);
			}
			if (m->count > 1)
				printf(" Count: %d", m->count);
		}
	}
	putchar('\n');
	return;
}

#define MAXLOCKS 100	/* Maximum number of lock files this will handle */

int
lckpid()
{
    int i;
    int fd, ret;
    pid_t pid, list[MAXLOCKS];
    char alpid[SIZEOFPID+2];	/* +2 for '\n' and null */
    char buf[BUFSIZ], f[MAXNAMESIZE];
    char *c, lckdir[BUFSIZ];
    DIR *dir;

    DEBUG(9, "lckpid() - entered\n%s", "");
    for (i=0; i<MAXLOCKS; i++)
	list[i] = -1;
    (void) strcpy(lckdir, LOCKPRE);
    *strrchr(lckdir, '/') = '\0';
    DEBUG(9, "lockdir (%s)\n", lckdir);

    /* open lock directory */
    if (chdir(lckdir) != 0 || (dir = opendir(lckdir)) == NULL)
		exit(101);		/* good old code 101 */
    while (gnameflck(dir, f) == TRUE) {
	/* find all lock files */
	DEBUG(9, "f (%s)\n", f);
	if (EQUALSN("LCK.", f, 4) || EQUALSN("LK.", f, 3)) {
	    /* read LCK file */
	    fd = open(f, O_RDONLY);
	    printf("%s: ", f);
	    ret = read(fd, alpid, SIZEOFPID+2); /* +2 for '\n' and null */
	    pid = strtol(alpid, (char **) NULL, 10);
	    (void) close(fd);
	    if (ret != -1) {
		printf("%ld\n", (long) pid);
		for(i=0; i<MAXLOCKS; i++) {
		    if (list[i] == pid)
			break;
		    if (list[i] == -1) {
		        list[i] = pid;
		        break;
		    }
		}
	    }
	    else
		printf("????\n");
	}
    }
    fflush(stdout);
    *buf = NULLCHAR;
    for (i=0; i<MAXLOCKS; i++) {
	if( list[i] == -1)
		break;
	(void) sprintf(&buf[strlen(buf)], "%d ", list[i]);
    }

    if (i > 0)
#ifdef V7
	execl("/bin/ps", "uustat-ps", buf, (char *) 0);
#else
	execl("/usr/bin/ps", "ps", "-flp", buf, (char *) 0);
#endif
    exit(0);
}

/*
 * get next file name from lock directory
 *	p	 -> file description of directory file to read
 *	filename -> address of buffer to return filename in
 *		    must be of size NAMESIZE
 * returns:
 *	FALSE	-> end of directory read
 *	TRUE	-> returned name
 */
static int
gnameflck(p, filename)
char *filename;
DIR *p;
{
	struct dirent dentry;
	struct dirent *dp = &dentry;

	for (;;) {
		if ((dp = readdir(p)) == NULL)
			return(FALSE);
		if (dp->d_ino != 0 && dp->d_name[0] != '.')
			break;
	}

	(void) strncpy(filename, dp->d_name, MAXNAMESIZE-1);
	filename[MAXNAMESIZE-1] = '\0';
	return(TRUE);
}

int
machcmp(a,b)
char *a,*b;
{
	return(strcmp(((struct m *) a)->mach,((struct m *) b)->mach));
}

static long _sec_per_day = 86400L;

/*
 * _age - find the age of "file" in days
 * return:
 *	age of file
 *	0 - if stat fails
 */

int
_age(dir, file)
char * file;	/* the file name */
char * dir;	/* system spool directory */
{
	char fullname[MAXFULLNAME];
	static time_t ptime = 0;
	time_t time();
	struct stat stbuf;

	if (!ptime)
		(void) time(&ptime);
	(void) sprintf(fullname, "%s/%s", dir, file);
	if (stat(fullname, &stbuf) != -1) {
		return ((int)((ptime - stbuf.st_mtime)/_sec_per_day));
	}
	else
		return(0);
}
/* Function:  complete - find and print jobids of completed jobs for
 *		         user.
 *
 * Look thru the /var/uucp/.Admin/account file (if present)
 * for all jobs initiated by user and print.
 *
 * Parameters:	
 *
 *		Username - user that initiated uustat request
 *
 * Returns:
 *
 */
static void
complete()
{

	/* Function name: complete
	   Author:	  Roland T. Conwell
	   Date:	  July 31, 1986
	   Naration: This function will search through
		     /var/uucp/.Admin/account file
		     for all jobs submitted by User.  If User jobs are
		     found the state of 'completed' will be
		     printed on stdout. Module called by uustat main

	*/
char abuf[BUFSIZ];
FILE *fp;
char accno[15], jobid[15], system[15], loginame[15], time[20], dest[15];
char size[15];
char grade[2], jgrade[2];
char status[2];
int x;

fp = fopen(ACCOUNT, "r");
if (fp == NULL)
   {
	fprintf(stderr, gettext("Can't open account log\n"));
		return;
   }
while (fgets(abuf, BUFSIZ, fp) != NULL)
   {

	x = sscanf(abuf, "%s%s%s%s%s%s%s%s%s%s",
		accno,jobid, size, status, grade, jgrade, system, loginame,
		time, dest);
	if (x < 6)
		continue;

	if (!EQUALS(status, "C"))
		continue;

	DEBUG(9, "COMPLETE: accno = %s\n", accno);
	DEBUG(9, "COMPLETE: jobid = %s\n", jobid);
	DEBUG(9, "COMPLETE: size = %s\n", size);
	DEBUG(9, "COMPLETE: status = %s\n", status);
	DEBUG(9, "COMPLETE: grade = %s\n", grade);
	DEBUG(9, "COMPLETE: jgrade = %s\n", jgrade);
	DEBUG(9, "COMPLETE: system = %s\n", system);
	DEBUG(9, "COMPLETE: loginame = %s\n", loginame);
	DEBUG(9, "COMPLETE: time = %s\n", time);
	DEBUG(9, "COMPLETE: dest = %s\n", dest);

	if (*Rmtname && !EQUALS(Rmtname, dest))
		continue;
	if (*User && !EQUALS(User, loginame))
		continue;
	if (State && !Uopt)
	  {
	   if (EQUALS(Loginuser, loginame))
		{
			printf("%s completed\n",jobid);
			jobcount++;
		}
	  }
	else
	  {
		printf("%s completed\n", jobid);
		jobcount++;
	  }
   }
   fclose(fp);
   return;
}

/* Function: state - determine if Cdotfile is queued or running
 *
 * This function searches thru the directory jcdir for a Adotfile 
 * that matches the Cdotfile.  If found then look for a matching
 * lock file.  If a Adotfile and a lock file is found then the
 * job is in the running state.  If no Adotfile is found then the
 * job is in the queued state.  If a Adotfile is found and no
 * lock file is found then the job is queued.
 * 
 * Parameters:
 *
 *	jcdir    -   the job grade directory to search
 *	cdotfile -   the Cdotfile whose state is to be determined
 *
 * Returns: 
 *
 */
static int
state(jcdir, cdotfile)
char *jcdir, *cdotfile;
{
	short found, foundlck, CequalA;	
	char comparef[MAXBASENAME+1], afile[MAXBASENAME+1], cfile[MAXBASENAME+1];
	char lckfile[MAXBASENAME+1], lockname[MAXBASENAME+1];
	char lckdir[BUFSIZ+1];
	DIR *subjcdir, *sjcdir;
	int rtnstate = 0;
	foundlck = 0;
	CequalA = 0;
	sjcdir = opendir(jcdir);
	if (sjcdir == NULL)
		return (0);

	while (gnamef(sjcdir, comparef) == TRUE) {
	    if (comparef[0] == 'A') {

		(void) strcpy(afile, comparef);
		*strchr(afile, 'A') = ' ';
		(void) strcpy(cfile, cdotfile);
		*strchr(cfile, 'C') = ' ';

		if (EQUALS(cfile, afile)) {
	 	    /* now we have a C. and A. for same job */
	  	    /* check for LCK..machine.job_grade     */
		    /* if no LCK file at this point we will */
		    /* print the RUNNING state	        */
			CequalA = 1;

			(void) strcpy(lckdir, LOCKPRE);
			*strrchr(lckdir, '/') = '\0';
			/* open lock directory */
			
			subjcdir = opendir(lckdir);
			if (subjcdir == NULL)
			   exit(101); /* I know, I know! */
			(void) sprintf(lockname,"%s%s.%s",LOCK, f, jcdir);
			while (gnamef(subjcdir, lckfile) == TRUE)
			  {
			    DEBUG(9, "STATE: lockfile = %s\n",lckfile);
			    if (EQUALS(lockname, lckfile))
			          foundlck = 1;
			  }
			closedir(subjcdir);	

			}
		}	

	}

	closedir(sjcdir);
	/* got adot, cdot and lock file */

	if (Running && foundlck)
		rtnstate = 2;
	else if (Interrupted && CequalA && !foundlck)
		rtnstate = 3;
	else if (Queued && !CequalA && !foundlck)
		rtnstate = 1;
	DEBUG(9, "STATE: returning with value %d\n",rtnstate);
	return(rtnstate);

} /* end of state.c */



static int 
readperf(timerange)
long timerange;
{
	
	char proto[2], jc[2], role[2];
	char rectype[5],  time[MAXDATE+1], pid[10],wmachine[10];
	char remote[10],device[10], netid[20], jobid[20];
	static float queuetime, tat;
	static long size;
        struct tm tm_tmp;	
	time_t t_time, t_starttime, t_upperlimit;

	char options[10];
	static float rst, ust, kst, xferrate, utt, ktt;
	static float rtt, wfield, xfield, yfield;

	struct perfrec *recptr;
	static float tqt;
	static int jobs;
	char abuf[BUFSIZ];
	FILE *fp;
	static int x;
	char *strptr, *startime;
	int recordcnt;

	totalxfer=totalbytes=recordcnt=totaljob=totalque=0;
	lowerlimit[0] = '\0';
	upperlimit[0] = '\0';
	
 
	inputsecs = convert(timerange);
	startime = gmts();
	strncpy(lowerlimit, startime, MAXDATE);
	strncpy(upperlimit, gmt(), MAXDATE);

	/* convert lowerlimit and upperlimit to HH:MM format */
	friendlytime(lowerlimit, upperlimit);

	fp = fopen(PERFLOG, "r");
	if (fp == NULL)
 	  {
		(void) fprintf(stderr, gettext("Can't open performance log\n"));
			return(0);
	   }


	while (fgets(abuf, BUFSIZ, fp) != NULL)
	  {
	    DEBUG(9, "READPERF: abuf before = %s\n",abuf);

	    if (!EQUALSN(abuf, "xfer", 4))
		continue;

            /* convert all '|'s to blanks for sscanf */
	    for (strptr = abuf; *strptr != '\0'; strptr++)
		if (*strptr == '|')
		    *strptr = ' ';
	    DEBUG(9, "READPERF: abuf = %s\n",abuf);

	    x = sscanf(abuf, "%s%*s%s%s%s%s%s%s%*s%s%s%f%f%ld%s%f%f%f%f%f%f%f%f%f%*s", 
		rectype, time, pid, wmachine, role, remote, device, netid,
		jobid, &queuetime, &tat, &size, options, &rst,
		&ust, &kst, &xferrate, &utt, &ktt, &rtt, &wfield,
		&xfield);

		DEBUG(9, "READPERF: rectype = %s\n",rectype);
		DEBUG(9, "READPERF: time = %s\n",time);
		DEBUG(9, "READPERF: pid = %s\n",pid);
		DEBUG(9, "READPERF: remote = %s\n",remote);
		DEBUG(9, "READPERF: jobid = %s\n",jobid);
		DEBUG(9, "READPERF: queuetime = %f\n",queuetime);
		DEBUG(9, "READPERF: tat = %f\n",tat);
		DEBUG(9, "READPERF: xferrate = %f\n",xferrate);

		abuf[0] = '\0';
		
		if (!EQUALS(Rmtname, remote))
			continue;

		if (!EQUALS(role, "M"))
			continue;

		if (x < 18)
			continue;

		DEBUG(9, "READPERF: startime = %s\n", startime);
		DEBUG(9, "READPERF: lowerlimit = %s\n", lowerlimit);
		DEBUG(9, "READPERF: time = %s\n", time);
		DEBUG(9, "READPERF: upperlimit = %s\n", upperlimit);

		strptime(time, "%y %m %d %H %M %S", &tm_tmp);
		t_time = mktime(&tm_tmp);
		strptime(startime, "%y %m %d %H %M %S", &tm_tmp);
		t_starttime = mktime(&tm_tmp);
		strptime(upperlimit, "%y %m %d %H %M %S", &tm_tmp);
		t_upperlimit = mktime(&tm_tmp);

		DEBUG(9, "READPERF: t_time = %d\n", t_time);
		DEBUG(9, "READPERF: t_starttime = %d\n", t_starttime);
		DEBUG(9, "READPERF: t_upperlimit = %d\n", t_upperlimit);
		if (t_starttime <= t_time && t_upperlimit >= t_time)
		{
			totaljob++;	
			totalque = totalque + queuetime;
			totalxfer = totalxfer + xferrate;
			totalbytes = totalbytes + size;
			recordcnt = recordcnt + 1;
		DEBUG(9, "  processing recordcnt %d\n", recordcnt);
		}
	DEBUG(9, "END step 1 %d\n", recordcnt);
  	 } /* while */
	DEBUG(9, "END step 2 recordcnt %d\n", recordcnt);

	fclose(fp);
	return(recordcnt);


} /* end of readperf */

void
docalc()
{
   if (avgqueue)
	queuetime();
   else
	xfertime();
   return;
}

static int
convert(intime)
long intime;
{
	long outtime;

	outtime = intime * 60;
	return(outtime);

}
static void
queuetime()
{
 	static double avgqtime;

	avgqtime = totalque / totaljob;

	printf("average queue time to [%s] for last [%ld] minutes:  %6.2f seconds\n",Rmtname, calcnum, avgqtime);
 	 printf("data gathered from %s:%s to %s:%s GMT\n", friendlyptr->uhour, friendlyptr->umin, friendlyptr->lhour, friendlyptr->lmin);
	return;
}


static void
xfertime()
{
         static double avgxrate;

	avgxrate = totalbytes / totalxfer;

	printf("average transfer rate with [ %s ] for last [%ld] minutes: %6.2f bytes/sec\n", Rmtname, calcnum, avgxrate);
 	 printf("data gathered from %s:%s to %s:%s GMT\n", friendlyptr->uhour, friendlyptr->umin, friendlyptr->lhour, friendlyptr->lmin);
	return;
}

/*
 * Local Function:	gmts - Generate Start Time String
 *
 * This function returns the address to a string containing the start
 * time, or upperlimit, for searching the PERFLOG.
 * The start time is in GMT in the form YYMMDDhhmmss.
 *
 * Parameters:
 *
 *	none
 *
 * Return:
 *
 *	An address of a static character array containing the date.
 */

static char *
gmts()
{
	static char	date[] = "YYMMDDhhmmss";

	struct tm 		*td;
	time_t			now;	/* Current time. */
	time_t			temp;
	now = time((time_t *) 0);

	/* inputsecs is declared global to this file */
	DEBUG(9, "GMTS: now = %ld\n", now);
	DEBUG(9, "GMTS: inputsecs = %ld\n", inputsecs);

	temp = (now - inputsecs);
	td = gmtime(&temp);
	(void) sprintf(date, "%02d%02d%02d%02d%02d%02d",
				(td->tm_year % 100),
				td->tm_mon + 1,
				td->tm_mday,
				td->tm_hour,
				td->tm_min,
				td->tm_sec
		      );
	return date;
}

/*
 * Local Function:	gmt - Generate Current Time String
 *
 * This function returns the address to a string containing the current
 * GMT in the form YYMMDDhhmmss.
 *
 * Parameters:
 *
 *	none
 *
 * Return:
 *
 *	An address of a static character array containing the date.
 */

static char *
gmt()
{
	static char	date[] = "YYMMDDhhmmss";

	struct tm	*td;
	time_t			now;	/* Current time. */

	now = time((time_t *) 0);
	td = gmtime(&now);
	(void) sprintf(date, "%02d%02d%02d%02d%02d%02d",
				(td->tm_year % 100),
				td->tm_mon + 1,
				td->tm_mday,
				td->tm_hour,
				td->tm_min,
				td->tm_sec
		      );
	return date;
}

static void
friendlytime(uplimit, lolimit)
char *uplimit, *lolimit;
{

	char c;

	c = *(uplimit+6);
	friendlyptr->uhour[0] = *(uplimit+6);
	friendlyptr->uhour[1] = *(uplimit+7);
	friendlyptr->lhour[0] = *(lolimit+6);
	friendlyptr->lhour[1] = *(lolimit+7);
	friendlyptr->umin[0]  = *(uplimit+8);
	friendlyptr->umin[1]  = *(uplimit+9);
	friendlyptr->lmin[0]  = *(lolimit+8);
	friendlyptr->lmin[1]  = *(lolimit+9);

	friendlyptr->uhour[2] = '\0';
	friendlyptr->lhour[2] = '\0';
	friendlyptr->umin[2] = '\0';
	friendlyptr->lmin[2] = '\0';
	return;
}

void
procState(inputargs)
char * inputargs;
{
	if (strchr(inputargs, 'q') != NULL)
		Queued = 1;
	if (strchr(inputargs, 'r') != NULL)
		Running = 1;
	if (strchr(inputargs, 'i') != NULL)
		Interrupted = 1;
	if (strchr(inputargs, 'c') != NULL)
		Complete = 1;

	if ((size_t)(Queued + Running + Interrupted + Complete) < strlen(inputargs))
		{
			errortn();
			exit(1);
		}
	return;
}

static void
errortn()
{


	(void) fprintf(stderr, gettext("\tUsage: %s " USAGE1 "\n"),
	    Progname);
	(void) fprintf(stderr, gettext("or\n\tUsage: %s " USAGE2 "\n"),
	    Progname);
	(void) fprintf(stderr, gettext("or\n\tUsage: %s " USAGE3 "\n"),
	    Progname);
	return;
}