/* Tool to identify RMD blockers */
#define MAXDEVICE  200
#define MAXDEVNAMLEN  64
#define MAXDEVLOCKNAM 13
#define MAXFILNAMLEN 80
#define MAXPID 16
/*
** BLOCKING.C, Hein van den Heuvel, Digital, July 1995
**  
**   List all locks not granted (converting, waiting) for a specified PID.
**   For each, print blocking lock information, resource name and parent.
**   Usage: LOCK [pid] [device]
**                pid = process ID; 0 = all, 1 - 15 = RMS locks.
**   Have fun,
**	Hein van den Heuvel, July 1995.
*/

#include <prvdef>
#include <lkidef>
#include <lckdef>
#include <descrip>
#include <string>
#include <ctype>
#include <stdio>
#include <ssdef>
#include <rms>
#define terminator 0,0,0,0
#define EFN 1

static char devnam[MAXDEVICE][MAXDEVNAMLEN];	    /* counted string */
static char devlocknames[MAXDEVICE][MAXDEVLOCKNAM+1];
static int  device_count = 0;   

typedef struct { short len, cod; void *address; short *retlen; } item;
typedef struct { unsigned int len; void *address; } desc;

int	sys$getlkiw(), sys$getjpi(), sys$setprv(), sys$cmexec();
void	dump_data () ;
int	make_device_name_list(), print_filename_and_record() ;
char	*find_device_name();
char	*mode[]={"NL", "CR", "CW", "PR", "PW", "EX", "??"};

main(int argc, char *argv[])
{
    int	    stat, s, i, l, parent, grand_parent, lock_id, lock_pid;
    int	    wildcard=0, retlen=0, parent_retlen=0, pid=0, locks=0;
    char    *x;
    struct lkidef lkibuf[100], *lki;
    struct { unsigned all : 16, one : 15, too_small : 1 ;} lkilen;
    int	    privs[] = { PRV$M_WORLD | PRV$M_CMEXEC , 0};
    int	    *search_devnam=0;
    desc    search_devnam_desc;

#pragma nostandard /* Using address of variable where constant is standard */

    struct  {   char	rqmode, grmode, queue, fill;} lki_state;
    struct  {   int	rms; unsigned short fid_num, fid_seq, fid_rvn;
		char	devlocknam[22] ;} parent_resnam;
    struct  { unsigned int	id, vbn, fill[6];} resnam;

    item    getlki_items[] = { 4, LKI$_LOCKID,  &lock_id, 0, 
			       4, LKI$_PID,     &lock_pid, 0, 
			       3, LKI$_STATE,   &lki_state, 0, terminator};
    item    block_items[] = { 31, LKI$_RESNAM,  &resnam, &retlen,
			       4, LKI$_PARENT,  &parent, 0, 
		   sizeof lkibuf, LKI$_BLOCKING,&lkibuf, &lkilen, terminator};
    item    parent_items[] = {31, LKI$_RESNAM,  &parent_resnam, &parent_retlen,
			       4, LKI$_PARENT,  &grand_parent, 0, terminator};
    int
	getlki_args[] = {7, EFN, (int) &wildcard, (int) &getlki_items,0,0,0,0},
	parent_args[] = {7, EFN, (int) &parent, (int) &parent_items,0,0,0,0},
	block_args[] = {7, EFN, (int) &lock_id, (int) &block_items,0,0,0,0};

#pragma standard

    /*
    ** First get some temporary privs for the GETLKI in EXEC mode later on.
    */
    stat = sys$setprv ( 1, privs, 0, 0);
    if (stat != SS$_NORMAL) return (stat & -2);

    if (argc > 1) {
	/*
	** First argument, if present, specifies PID to look for. 0 = All.
	*/
	sscanf (argv[1], "%x", &pid);
	if (pid > MAXPID) {
	    printf (" Looking for non-granted locks for PID %8X\n\n", pid);
	    } else {
	    x = "";
	    if (pid > 0) x = "RMS ";
	    printf (" Looking for non-granted %slocks for all PIDs\n\n", x);
	    };
	};

    if (argc > 2) {
	/*
	** Second argument, if present, specifies wildcarded device name
	** to look for. For lock on matching devices, the RMS files name
	** and locked record is attempted to be displayed. 
	*/
        search_devnam_desc.len = strlen(argv[2]);
        search_devnam_desc.address = (int) argv[2];
        search_devnam = &search_devnam_desc;
        str$upcase (&search_devnam_desc, &search_devnam_desc);
	}

    (void) make_device_name_list(search_devnam);


    /*
    ** Main loop. Get a lock, any lock. 
    ** Find out wether it is held by specified process, and waiting.
    */
    stat = sys$cmexec (&sys$getlkiw, &getlki_args);
    while (stat & 1) {
	locks++;
	if ( (lki_state.queue != LKI$C_GRANTED) &&
	     ((pid < MAXPID) ||  (lock_pid == pid)) ) {
	    int rms = 0;

	    parent = 0;
	    /*
	    ** Have blocked lock for specified PID, request details.
	    */
	    stat = sys$cmexec (&sys$getlkiw, &block_args);
	    if (!(stat & 1)) break;
	    /*
	    ** Let's see if we stumbled into an rms record or bucket lock...
	    */
	    if (parent!=0) {
		s = sys$cmexec (&sys$getlkiw, &parent_args);
		if ((s & 1) && (grand_parent==0) && 
		    (parent_resnam.rms == 'RMS$')) rms = 1;
		}

	    /*
	    ** Print out all locks, or just RMS ?
	    */
	    if ((pid == 0) || (pid > MAXPID) || rms) {
		l = lkilen.all / lkilen.one;
	        printf ("Pid %08X Lock %08X, Rq=%s, Parent %08X, blocked by %d.\n", 
		    lock_pid, lock_id, mode[lki_state.rqmode], parent, l);
		dump_data (&resnam, retlen, "Resource Name ");
		lki = &lkibuf;
		for ( i = 0; i < l; i++) {
		    printf ("  PID=%8X, Gr=%s, Rq=%s, LockId=%08X, System=%08\n",
				lki->lki$l_pid, mode[lki->lki$b_grmode], 
				mode[lki->lki$b_rqmode],
				lki->lki$l_lkid, lki->lki$l_csid);
		    lki++;
		    }
		if (parent!=0) dump_data (&parent_resnam, 
			parent_retlen, "Parent Resource Name");
		printf ("\n");
		}

	    if (rms) {
		printf ("  RMS lock! VBN/ID %X/%X, File (%d,%d,%d) on %s\n", 
			resnam.vbn, resnam.id,
			parent_resnam.fid_num, parent_resnam.fid_seq,
			parent_resnam.fid_rvn, 
			&parent_resnam.devlocknam[1]);
		print_filename_and_record ( &parent_resnam.devlocknam[0],
			&parent_resnam.fid_num, resnam.vbn, resnam.id);
		}
	    }         /* PID   ? */
	if (stat &1) stat = sys$cmexec (&sys$getlkiw, &getlki_args);	
	} /* stat ? */
    if (stat == SS$_NOMORELOCK) stat = SS$_NORMAL;
    printf (" Done. Scanned %d locks.\n", locks);
    return stat;
}

void dump_data (void *p, short len, char *t)
{
    int	i, j, k, l;
    unsigned char (*x)[72];
    char out[80], *in, c;
    x = in = p;
    l = len;
    if (l > 72) l = 72; /* sizeof out */
    for (i=0; i<l; i++){
	c = *in++;
	if (!isalnum (c)) c = '.';
	out[i]=c;
	}
    out[i]=0;
    printf ("    %s (%d):\n    <%s>\n", t, len, &out[0]);
    j = 0;

    for (i=0; i<l;) {
	j += sprintf (&out[j], "%02X ", (*x)[i++]);
	if (!(i%24)) {
	    printf ("    %s\n",&out[0]);
	    j = 0;
	    }
	}
    if (j) printf ("    %s\n",&out[0]);
    return;
}

int print_filename_and_record ( char *devlocknam,
	unsigned short (*fid)[3], unsigned int vbn, unsigned int id)
{
    struct FAB fab;
    struct NAM nam;
    struct RAB rab;
    int  device_number, stat, i, l;
    char buf[512];
static	char filnam[MAXFILNAMLEN+1];
    desc devnam_desc, filnam_desc = {0, filnam};

    for (device_number=0; 
	 strcmp ((char *) devlocknames[device_number], devlocknam);
	 device_number++) if (device_number == device_count) return;

    devnam_desc.len = devnam[device_number][0];
    devnam_desc.address = &devnam[device_number][1];
    filnam_desc.len = MAXFILNAMLEN;
    stat = lib$fid_to_name(&devnam_desc, fid, &filnam_desc, &filnam_desc);
    filnam[filnam_desc.len] = 0;
    printf ( "  %s %s\n", &devnam[device_number][1], filnam);
/*
**  Now that we've printed out the file name, and previously the record
**  or bucket data, lets try get display the data being waited for.
**  I suppose this could end up becoming blocked for the very same lock!
**  (VBN lock code is not at all tested as RMS does not normally block there)
*/
    fab = cc$rms_fab;
    rab = cc$rms_rab;
    nam = cc$rms_nam;
    rab.rab$l_fab = &fab;
    fab.fab$l_nam = &nam;
    fab.fab$l_fop = FAB$M_NAM;		/* will use dev+id, not name */
    if (!vbn) fab.fab$b_fac = FAB$M_BIO;/* block IO if not an RFA */
    fab.fab$b_shr = FAB$M_SHRUPD;	/* allow writers */
    for (i=0; i<3; i++) nam.nam$w_fid[i] = (*fid)[i];
    for (i=0; i<devnam[device_number][0]; i++)  
		nam.nam$t_dvi[i] = devnam[device_number][i];
    nam.nam$t_dvi[0]--;			/* strip colon */
    rab.rab$l_ubf = buf;
    rab.rab$w_usz = sizeof buf;

    stat = sys$open ( &fab );
        if (!(stat&1)) return stat;
    stat = sys$connect ( &rab );
        if (!(stat&1)) return stat;
    if (vbn) {
	rab.rab$b_rac = RAB$C_RFA;
	rab.rab$v_rrl = 1;
	rab.rab$l_rfa0 = vbn;
	rab.rab$w_rfa4 = id;
	stat = sys$get ( &rab );                
	l = rab.rab$w_rsz;
	if (stat == RMS$_RTB) {
	    l = rab.rab$l_stv;
	    stat = 1;
	    }
	} else {
	rab.rab$l_bkt = id;
	stat = sys$read ( &rab );                
	}
	dump_data (rab.rab$l_rbf, l, "  Record ");
	return stat;
}

int make_device_name_list(desc *search_devnam)
    {

#include <dvidef>
#include <dcdef>
/* #include <dvsdef> does not exist, hand coded defines follow */
#define DVS$_DEVCLASS 1
#define DVS$_DEVTYPE  2
#define SS$_NOMOREDEV 2648

    int i, stat, context[] = {0,0}, devclass = DC$_DISK;
    short int retlen;
    desc devnam_desc;
    item getdvi_items[] = { MAXDEVLOCKNAM, DVI$_DEVLOCKNAM,0, 0, terminator};
    item device_items[] = { 4, DVS$_DEVCLASS,   &devclass, 0, terminator};
    /*
    ** Build two arrays with DISK device names and their lock names.
    */
    for (i=0;  i<MAXDEVICE;  i++) {
	devnam_desc.len = MAXDEVNAMLEN-1;
	devnam_desc.address = &devnam[i][1];
	stat = sys$device_scan (&devnam_desc, &devnam_desc, 
	    search_devnam, device_items, context);
	devnam[i][0]=devnam_desc.len;
	if (stat & 1) {
	    devnam[i][devnam_desc.len+1]=0;    /* Null terminate */
	    getdvi_items[0].address = devlocknames[i];
	    getdvi_items[0].retlen = &retlen;

	    stat = sys$getdvi (0,0,&devnam_desc,getdvi_items,0,0,0,0);
	    if (stat != SS$_NORMAL) return stat;
	    devlocknames[i][retlen] = 0;
	}
	else {
	    if (stat == SS$_NOMOREDEV) {
		device_count = i;
		i = MAXDEVICE;
	    }
	    break;
	}
    }
    return stat;
}
