/*	@(#)uustat.c	1.14
*/
#include "uucp.h"
VERSION(@(#)uustat.c	1.14)

#ifdef	V7
#define O_RDONLY	0
#endif

#define USAGE1	"[-a] [-q] or [-m] or [-kJOB] or [-rJOB] or [-p]"
#define USAGE2	"[-sSYSTEM] [-uUSER]"

#define STST_MAX	132
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];

extern long atol();
extern void qsort();		/* qsort(3) and comparison test */
int sortcnt = -1;
extern int machcmp();
extern int _age();		/* find the age of a file */

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 */
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 */

main(argc, argv, envp)
char *argv[];
char **envp;
{
	struct m *m, *machine();
	DIR *spooldir, *subdir;
	char *str, *strrchr();
	char f[256], subf[256];
	char *c, lckdir[BUFSIZ];
	char buf[BUFSIZ];
	char chkname[MAXFULLNAME];
	char *vec[7];
	int i, chkid;


	User[0] = '\0';
	Rmtname[0] = '\0';
	Jobid[0] = '\0';
	Psopt=Machines=Summary=Queue=Kill=Rejuvenate=Uopt=Sysopt=0;
	(void) strcpy(Progname, "uustat");
	Uid = getuid();
	Euid = geteuid();
	guinfo(Uid, Loginuser);
	uucpname(Myname);
	while ((i = getopt(argc, argv, "ak:mpr:qs:u:x:")) != EOF) {
		switch(i){
		case 'a':
			Sysopt = 1;
			break;
		case 'k':
			(void) strncpy(Jobid, optarg, NAMESIZE);
			Jobid[NAMESIZE] = '\0';
			Kill = 1;
			break;
		case 'm':
			Machines = Summary = 1;
			break;
		case 'p':
			Psopt = 1;
			break;
		case 'r':
			(void) strncpy(Jobid, optarg, NAMESIZE);
			Jobid[NAMESIZE] = '\0';
			Rejuvenate = 1;
			break;
		case 'q':
			Queue = Summary = 1;
			break;
		case 's':
			(void) strncpy(Rmtname, optarg, MAXBASENAME);
			Rmtname[MAXBASENAME] = '\0';
#if NOTDEF
			if (versys(Rmtname, 0)) {
				fprintf(stderr, "Invalid system\n");
				exit(1);
			}
#endif
			Sysopt = 1;
			break;
		case 'u':
			(void) strncpy(User, optarg, 8);
			User[8] = '\0';
			if(gninfo(User, &chkid, chkname)) {
				fprintf(stderr, "Invalid user\n");
				exit(1);
			}
			Uopt = 1;
			break;
		case 'x':
			Debug = atoi(optarg);
			if (Debug <= 0)
				Debug = 1;
			break;
		default:
			(void) fprintf(stderr, "\tusage: %s %s\n",
			    Progname, USAGE1);
			(void) fprintf(stderr, "or\n\tusage: %s %s\n",
			    Progname, USAGE2);
			exit(1);
		}
	}

	if (argc != optind) {
		(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE1);
		(void) fprintf(stderr, "or\n\tusage: %s %s\n",
		    Progname, USAGE2);
		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);

	if ((Psopt + Machines + Queue + Kill + Rejuvenate + (Uopt|Sysopt)) >1) {
		/* only -u and -s can be used together */
		printf("\tusage: %s %s\n", Progname, USAGE1);
		printf("or\n\tusage: %s %s\n", Progname, USAGE2);
		exit(1);
	}
	if (  !(Kill | Rejuvenate | Uopt | Sysopt | Queue | Machines) ) {
		(void) strcpy(User, Loginuser);
		Uopt = 1;
	}

	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 ((str = strchr(buf, '\n')) != NULL)
			*str = 0;
		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 = strrchr(m->stst, ' ');
		(void) machine(++str);	/* longer name? */
		*str = '\0';
			
	    }
	    closedir(spooldir);
	}


	if (Summary) {
	    /*  search for LCK machines  */

	    (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 (gnamef(subdir, f) == TRUE) {
		if (EQUALSN("LCK..", f, 5)) {
		    if (!EQUALSN(f + 5, "cul", 3)
		     && !EQUALSN(f + 5, "tty", 3)
		     && !EQUALSN(f + 5, "dtsw", 4)
		     && !EQUALSN(f + 5, "vadic", 5)
		     && !EQUALSN(f + 5, "micom", 5))
			machine(f + 5)->locked++;
		}
	    }
	}

	if (chdir(SPOOL) != 0 || (spooldir = opendir(SPOOL)) == NULL)
		exit(101);		/* good old code 101 */
	while (gnamef(spooldir, f) == TRUE) {
	    if (EQUALSN("LCK..", f, 5))
		continue;

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

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

	    if (DIRECTORY(f) && (subdir = opendir(f))) {
		m = machine(f);
	        while (gnamef(subdir, subf) == TRUE)
		    if (subf[1] == '.') {
		        if (subf[0] == CMDPRE) {
				m->ccount++;
				if (Kill || Rejuvenate)
				    kprocessC(f, subf);
				else if (Uopt | Sysopt)
				    uprocessC(f, subf);
				else 	/* get the age of the C. file */
				    if ( (i = _age(f, subf))>m->c_age)
					m->c_age = i;
			}

			else if (subf[0] == XQTPRE) {
			        m->xcount++;
				if ( (i = _age(f, subf)) > m->x_age)
					m->x_age = i;
			}

		    }
		closedir(subdir);
	    }
	}
	/* for Kill or Rejuvenate - will not get here unless it failed */
	if (Kill || Rejuvenate) {
	    printf("Can't find Job %s; Not %s\n", Jobid,
		Kill ? "killed" : "rejuvenated");
	    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);
	}
	exit(0);
}


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

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

	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 */
	    printf("%-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);
	}
	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);

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

/*
	    if ( (*file2 != 'X')
	      && (*file1 != '/' && *file1 != '~')
	      && (*file2 != '/' && *file2!= '~') )
		continue;
*/

	    if (first)
	        printf("%-12s  %2.2d/%2.2d-%2.2d:%2.2d  ",
		    &file[2], tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		    tp->tm_min);
	    else
		printf("%-12s  %2.2d/%2.2d-%2.2d:%2.2d  ",
		    "", tp->tm_mon + 1, tp->tm_mday, tp->tm_hour,
		    tp->tm_min);
	    first = 0;

	    printf("%s  %s  ", type, dir);
	    if (*type == 'R')
	        printf("%s  %s\n", user, file1);
	    else if (file2[0] != 'X')
		printf("%s %ld %s\n", user, fsize(dir, file3, file1), file1);
	    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);
		    printf("%s  %s  %s  ", type, dir, user);
		    printf("????\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);
		    printf("%s  %s", username, command);
		}
		if (xfp != NULL)
		    fclose(xfp);
	    }
	}

	fclose(fp);
	return;
}


/*
 * kprocessC - process kill or rejuvenate job
 */

kprocessC(dir, file)
char *file, *dir;
{
	struct stat s;
	register struct tm *tp;
	extern struct tm *localtime();
	extern int errno;
	char fullname[MAXFULLNAME], buf[BUFSIZ], user[9];
	char rfullname[MAXFULLNAME];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	FILE *fp, *xfp;
 	time_t times[2];
	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 */
	    fprintf(stderr, "Can't stat:%s, errno (%d)--can't %s it!\n",
		fullname, errno, Kill ? "kill" : "rejuvenate");
	    exit(1);
	}

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

 	times[0] = times[1] = 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) {
		fprintf(stderr, "Bad format:%s, errno (%d)--can't %s it!\n",
		    fullname, errno, Kill ? "kill" : "rejuvenate");
	        exit(1);
	    }
	    DEBUG(9, "type (%s) ", type);
	    DEBUG(9, "file1 (%s) ", file1);
	    DEBUG(9, "file2 (%s) ", file2);
	    DEBUG(9, "file3 (%s) ", file3);
	    DEBUG(9, "user (%s)\n", user);


	    if (first) {
	        if (Uid != 0
		    && !PREFIX(Loginuser, user)
		    && !PREFIX(user, Loginuser) ) {
			/* not allowed - not owner or root */
			fprintf(stderr,
			    "Not owner or root - can't %s job %s\n",
    			    Kill ? "kill" : "rejuvenate", 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);
	/*
	 * who cares if we can't?
	 * the C. is the file that matters,
	 * and the D. is sometimes a dummy
	 * we should check specifically but i'm lazy
	 */
#if NOTDEF
	    if (ret != 0 && errno != ENOENT) {
		/* program error?? */
		fprintf(stderr, "Error: Can't %s, File (%s), errno (%d)\n",
		    Kill ? "kill" : "rejuvenate", rfullname, errno);
		exit(1);
	    }
#endif
	}

	DEBUG(4, "Remove %s\n", fullname);
	if (Kill)
	    ret = unlink(fullname);
	else /* Rejuvenate */
		ret = utime(fullname, times);
	
	if (ret != 0) {
	    /* program error?? */
		fprintf(stderr, "Error: Can't %s, File ( %s), errno (%d)\n",
		    Kill ? "kill" : "rejuvenate", fullname, errno);
	    exit(1);
	}
	fclose(fp);
	printf("Job: %s successfully %s\n", Jobid,
	    Kill ? "killed" : "rejuvenated");
	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);
}

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

struct m	*
machine(name)
char	*name;
{
	struct m *m;
	int	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, SYSNSIZE)) {
			/* 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,
		sccsid, __FILE__, __LINE__);
		(void) fprintf(stderr,
		    "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);
}

printit(m)
struct m *m;
{
	register struct tm *tp;
	time_t	t;
	int	min;
	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 (m->ccount)
		printf("%3dC", m->ccount);
	else
		printf("    ");
	if (m->c_age)
		printf("(%d)", m->c_age);
	else
		printf("   ");
	if (m->xcount)
		printf("%4dX", m->xcount);
	else
		printf("     ");
	if (m->x_age)
		printf("(%d) ", m->x_age);
	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:
		case SS_CONVERSATION:
			(void) time(&t);
			t = m->retrytime - (t - m->lasttime);
			if (t > 0) {
				min = (t + 59) / 60;
				printf("Retry: %d:%2.2d", min/60, min%60);
			}
			if (m->count > 1)
				printf(" Count: %d", m->count);
		}
	}
	putchar('\n');
}

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

lckpid()
{
    register i;
    long pid, ret, fd;
#ifdef ASCIILOCKS
    char alpid[SIZEOFPID+2];	/* +2 for '\n' and null */
#endif
    long list[MAXLOCKS];
    char buf[BUFSIZ], f[MAXBASENAME];
    char *c, lckdir[BUFSIZ];
    DIR *dir;

    DEBUG(9, "lckpid() - entered\n", "");
    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 (gnamef(dir, f) == TRUE) {
	/* find all lock files */
	DEBUG(9, "f (%s)\n", f);
	if (EQUALSN("LCK.", f, 4)) {
	    /* read LCK file */
	    fd = open(f, O_RDONLY);
	    printf("%s: ", f);
#ifdef ASCIILOCKS
	    ret = read(fd, alpid, SIZEOFPID+2); /* +2 for '\n' and null */
	    pid = atoi(alpid);
#else
	    ret = read(fd, &pid, sizeof (int));
#endif
	    (void) close(fd);
	    if (ret != -1) {
		printf("%d\n", 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
	execlp(UUPS, "uustat-ps", buf, 0);
#else
	execl("/bin/ps", "ps", "-flp", buf, 0);
#endif
    exit(0);
}

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);
}
