/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: lck.c,v 1.8.1.2 92/11/18 15:37:59 marker Exp $"


/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/*
 * This file contains code for the crash function lck.
 */

#include "crash.h"
#include "sys/fcntl.h"
#include "sys/flock.h"

struct procid {                 /* effective and sys ids */
        pid_t epid;
        long sysid;
        int valid;
};

struct procid *procptr;		/* pointer to procid structure */
extern char *malloc();

static struct syment *Inode, *Flckinfo, *Sleeplcks;
static int lock_inited = 0;
static int proc_inited = 0;
struct flckinfo infobuf;

/*
 * Get effective and sys ids into table
 */
int
getprocid()
{
	int i;
	struct proc prbuf, *prp;
	struct pid pid;
	proc_t *slot_to_proc();

	if (proc_inited == 0) {
		procptr = (struct procid *)malloc((unsigned)
			(sizeof (struct procid) * vbuf.v_proc));
		proc_inited = 1;
	}
	for(i = 0; i < vbuf.v_proc; i++) {
		prp = slot_to_proc(i);
		if (prp == NULL)
			procptr[i].valid = 0;
		else {
			readmem((long)prp,1,-1,(char *)&prbuf,sizeof (proc_t), 					"proc table");
			readmem((long)prbuf.p_pidp,1,-1,
				(char *)&pid,sizeof(struct pid), "pid table");
			procptr[i].epid = prbuf.p_epid;
			procptr[i].sysid = prbuf.p_sysid;
			procptr[i].valid = 1;
		}
	}
}

/* find process with same id and sys id */
int
findproc(pid,sysid)
short pid,sysid;
{
	int slot;

	for (slot = 0; slot < vbuf.v_proc; slot++) 
		if ((procptr[slot].epid == pid)
		    && (procptr[slot].sysid == sysid))
			break;
	if (slot == vbuf.v_proc)
		slot = -1;
	return(slot);
}

/*
 * Get the command line arguments and enumerate the inode table to find
 * the vnode and then see if the vnode has file locks hung off it.
 *
 * lck [-wfilename] [virtual_address[es]]
 */
int
getlcks()
{
	int c, err, phys, vlist;
	long addr, arg1, arg2;

	vlist = phys = 0;
	addr = arg1 = arg2 = -1;
	optind = 1;

	while((c = getopt(argcnt,args,"w:")) !=EOF) {
		switch(c) {
			case 'w' :	redirect();
					break;
			default  :	longjmp(syn,0);
		}
	}
	getprocid();
	lockinit();

	readmem((long)Flckinfo->n_value,1,-1,(char *)&infobuf,
		sizeof infobuf,"flckinfo table");

	if (args[optind]) {
		fprintf(fp,
	       "TYP W    START       LEN     EPID SYSID   STAT   PREV NEXT\n");

		do {
			getargs(GETARGS_NO_SLOT,&arg1,&arg2);
			if(arg1 == -1) 
				continue;
			if (arg2 != -1) {
				for (addr = arg1; addr <= arg2; 
				    addr = (long)(((struct filock *)addr)+1)) {

					err = pr_filelocknode(addr, phys);
					if (err) {
						fprintf(fp,
					 "Invalid virtual address %X\n", addr);
						break;
					}
					addr += sizeof(struct filock);
				}
			} else {
				addr = arg1;
				err = pr_filelocknode(addr, phys);
				if (err) {
					fprintf(fp,
				 "Invalid virtual address %X\n", addr);
					break;
				}
			}
			addr = arg1 = arg2 = -1;
		} while(args[++optind]);
	} else { /* print all existing file locks via exhaustive vnode enum */
		pr_allfilelocks();
	}
}

/*
 * Initialize symbols required for file locking
 */
int
lockinit()
{
	if (lock_inited)
		return 0;

        if (!Inode) {
                if (! (Inode = symsrch("inode"))) {
                        error("inode not found in symbol table\n");
                        return 1;
		}
	}
        if (!Flckinfo) {
                if (! (Flckinfo = symsrch("flckinfo"))) {
                        error("flckinfo not found in symbol table\n");
                        return 1;
		}
	}
        if (!Sleeplcks) {
                if (! (Sleeplcks = symsrch("sleeplcks"))) {
                        error("sleeplcks not found in symbol table\n");
                        return 1;
		}
	}
	lock_inited = 1;
	return 0; /* success */
}

/*
 * Print all file locks via exhaustive enumeration of all
 * open vnodes AND all sleep waiting locks.
 */
int
pr_allfilelocks()
{
	struct filock *actptr, *slptr, *fptr, *freeptr, fibuf;
	struct flckinfo info;
	struct inode ibuf;
	int active, free, sleep, next, prev, slot;

	active = free = sleep = 0;

	fprintf(fp,"ACTIVE LOCKS:\n");
	fprintf(fp,
         "INO TYP W    START       LEN    PROC  EPID SYSID WAIT PREV NEXT\n");

	/*
	 * Enumerate the active inodes to find the open vnodes which can
	 * then be examined to find a list of file/record locks for that file.
	 */
	for(slot = 0; slot < vbuf.v_inode; slot++) {

		readmem((long)(Inode->n_value+slot*sizeof ibuf),1,-1,
			(char *)&ibuf,sizeof ibuf,"inode table");

		actptr = (struct filock *)(ibuf.i_vnode.v_filocks);

		while (actptr) {
			readmem((long)actptr,1,-1,(char *)&fibuf,
				sizeof fibuf,"filock information");
			++active;
			fprintf(fp,"%3d ",slot);
			if(fibuf.set.l_type == F_RDLCK) 
				fprintf(fp," R  ");
			else if(fibuf.set.l_type == F_WRLCK) 
				fprintf(fp," W  ");
			else fprintf(fp," ?  ");
			fprintf(fp,"%d %10ld %10ld %4d %5hd  %4x %4d",
				fibuf.set.l_whence,
				fibuf.set.l_start,
				fibuf.set.l_len,
				findproc(fibuf.set.l_pid,fibuf.set.l_sysid),
				fibuf.set.l_pid,
				fibuf.set.l_sysid,
				fibuf.stat.wakeflg);

			prev = (long)fibuf.prev;
			fprintf(fp," %4x",prev);

			next = (long)fibuf.next;
			fprintf(fp," %4x\n",next);
			actptr = fibuf.next;
		}
	}

	fprintf(fp,"\nSLEEP  LOCKS:\n");
	fprintf(fp,"TYP W    START       LEN    LPRC  EPID SYSID BPRC  EPID SYSID PREV NEXT\n");
	readmem((long)Sleeplcks->n_value,1,-1,(char *)&slptr,
		sizeof slptr,"sleep lock information table");
	while (slptr) {
		readmem((long)slptr,1,-1,(char *)&fibuf,sizeof fibuf,
			"sleep lock information table slot");
		++sleep;
		if(fibuf.set.l_type == F_RDLCK) 
			fprintf(fp," R  ");
		else if(fibuf.set.l_type == F_WRLCK) 
			fprintf(fp," W  ");
		else fprintf(fp," ?  ");
		fprintf(fp,"%1d %10ld %10ld %4d %5hd  %4x %4d %5hd  %4x",
			fibuf.set.l_whence,
			fibuf.set.l_start,
			fibuf.set.l_len,
			findproc(fibuf.set.l_pid,fibuf.set.l_sysid),
			fibuf.set.l_pid,
			fibuf.set.l_sysid,
			findproc(fibuf.stat.blk.pid,fibuf.stat.blk.sysid),
			fibuf.stat.blk.pid,
			fibuf.stat.blk.sysid);
		prev = (long)fibuf.prev;
		fprintf(fp," %4x",prev);

		next = (long)fibuf.next;
		fprintf(fp," %4x\n",next);
		slptr = fibuf.next;
	}

	readmem((long)Flckinfo->n_value,1,-1,(char *)&info,
		sizeof info,"file lock information table");

	fprintf(fp,"\nCONFIGURED RECORD LOCKS:\n");
	fprintf(fp,"RECS  RECCNT  RECOVF  RECTOT\n");
	fprintf(fp,"%4d   %4d    %4d    %4d\n",
		info.recs, info.reccnt, info.recovf, info.rectot);

	fprintf(fp,"\nACTUAL RECORD LOCKS:\n");
	fprintf(fp,"TOTAL  ACTIVE  FREE  SLEEP\n");
	fprintf(fp," %4d   %4d   %4d   %4d\n",
		active+sleep+free, active, free, sleep);
}    

/*
 * Print linked list of file locks hung off the vnode
 * Here 'addr' is the address of the file lock node and 'phys' is non-zero
 * if the address is physical rather than virtual. The default is phys==0
 * indicating VM addresses.
 */
int
pr_filelocknode(addr, phys)
	long addr;
	int phys;
{
	struct filock fibuf;
	int err, prev, next;

	err = readbuf(addr,0,phys,-1,(char *)&fibuf,
		      sizeof fibuf,"lock table");

	if (err)
		return err;

	fprintf(fp," %c%c%c",
		(fibuf.set.l_type == F_RDLCK) ? 'r' : ' ',
		(fibuf.set.l_type == F_WRLCK) ? 'w' : ' ',
		(fibuf.set.l_type == F_UNLCK) ? 'u' : ' ');

	fprintf(fp," %1d %10ld %10ld %5hd  %4x %8x",
		fibuf.set.l_whence, fibuf.set.l_start, fibuf.set.l_len,
		fibuf.set.l_pid, fibuf.set.l_sysid, fibuf.stat.wakeflg);

	prev = (long)fibuf.prev;
	fprintf(fp," %4x",prev);

	next = (long)fibuf.next;
	fprintf(fp," %4x\n",next);
	return 0;
}
