$! BUILD.COM
$!
$!  Builds DECW_ITM.EXE.
$!
$!  Once built, you should place DECW_ITM.EXE, DECW_ITM_START.COM, and
$!  DECW_STARTLOGIN.COM in a directory and define a system-wide logical
$!  name DECW_ITM_DIR to point to that directory.
$!
$ BLISS/LIBRARY DECW_ITM.R32
$ BLISS DECW_ITM
$ BLISS COLLECT
$ BLISS FORCE
$ BLISS DECW_DISPLAY
$ LINK DECW_ITM,COLLECT,FORCE,DECW_DISPLAY,SYS$SYSTEM:SYS.STB/SELECTIVE
$ EXIT



#ifndef __DALDEF_LOADED 
#define __DALDEF_LOADED 1
/*+-+
 * DAL - Device Allocation Lock (value block contents)
 *
 * This structure defines the contents of the lock value block for a
 * device allocation lock as defined in SYS$LIBRARY:LIB.REQ
 *-+-
 */
#define DAL$M_NOTFIRST_MNT 1
#define DAL$M_FOREIGN 2
#define DAL$M_GROUP 4
#define DAL$M_SYSTEM 8
#define DAL$M_WRITE 16
#define DAL$M_NOQUOTA 32
#define DAL$M_OVR_PROT 64
#define DAL$M_OVR_OWNUIC 128
#define DAL$M_NOINTERLOCK 256
#define DAL$M_SHADOW_MBR 512
#pragma nostandard
struct daldef
{
    variant_union 
    {
        unsigned short int dal$w_flags;         /* Device usage flags:      */
        variant_struct 
        {
            unsigned dal$v_notfirst_mnt : 1;    /* not first time mounted.  */
            unsigned dal$v_foreign : 1;         /* device mounted /FOREIGN  */
            unsigned dal$v_group : 1;           /* device mounted /GROUP    */
            unsigned dal$v_system : 1;          /* device mounted /SYSTEM   */
            unsigned dal$v_write : 1;           /* write access allowed     */
            unsigned dal$v_noquota : 1;         /* quota checking disabled  */
            unsigned dal$v_ovr_prot : 1;        /* override protection      */
            unsigned dal$v_ovr_ownuic : 1;      /* override volume ownership*/
            unsigned dal$v_nointerlock : 1;     /* NOT VAXcluster interlocked */
            unsigned dal$v_shadow_mbr : 1;      /* shadow set member        */
            unsigned dal$v_fill_2 : 6;
        } dal$r_fill_1;
    } dal$r_fill_0;
    unsigned short int dal$w_protection;        /* Volume protection        */
    unsigned long int dal$l_owner_uic;          /* Volume owner UIC         */
    unsigned long int dal$l_reserved[2];        /* reserved                 */
};
#pragma standard
#endif /* __DALDEF_LOADED */



/*
 *+-+
 *
 * MODULE: DEVLCK.C
 *
 * ABSTRACT:
 *      This program displays local and remote device information obtained
 *      by accessing the Device Allocation Lock. Information is obtained
 *      by calling the kernel mode subroutine DEVLCKINFO.
 *
 * AUTHOR: Eric M. LaFranchi,   CREATION DATE: 31-JUL-1991
 *
 *-*-
 */
#include <descrip.h>                    /* VMS descriptor macros              */
#include <dvidef.h>                     /* $GETDVI item code definitions      */
#include <lckdef.h>                     /* lock mode definitions              */
#include <libdef.h>                     /* VMS RTL status code definitions    */
#include <lkidef.h>                     /* $GETLKI item codes and definitions */
#include <ssdef.h>                      /* VMS system service definitions     */
#include <stdio.h>                      /* standard C-RTL I/O definitions     */
#include <string.h>                     /* system service failure and status  */
#include <stsdef.h>                     /* C RTL string function definitions  */
#include <syidef.h>                     /* $GETSYI item codes and definitions */

#include "daldef.h"                     /* device allocation lock definitions */

    /* item list descriptor structure
     */
typedef struct item_descriptor
{
    unsigned short int  buflen;
    unsigned short int  itmcod;
    void                *bufadr;
    unsigned short int  *retlen;
} ITEM_LIST;

    /* node name structure
     */
typedef struct { char name[16]; } NODE;

    /* Flags for device access type
     */
#define FLG_M_LOCAL     1
#define FLG_M_ALLOCATED 2
#define FLG_M_REMOTE    4

    /* Index constants for the following string table.
     */
#define FILES11         0
#define FOREIGN         1
#define ALLOCATED       2
#define SYSTEM          3
#define GROUP           4
#define PROCESS         5
#define ODS1            6
#define ODS2            7
#define NONE            8
#define YES             9
#define NO              10

extern unsigned long int devlckinfo( );



/*
 * FUNCTION: display_dal
 *
 * ABSTRACT:
 *      This function displays device information obtained from a device's
 *      Device Allocation Lock (DAL) resource.
 *
 * INPUTS:
 *      device -- pointer to device name
 *      local_csid -- csid of current local node
 *      lckcnt -- number of lki buffers to process
 *      lkibuf -- pointer to array of lki buffers
 *      valblk -- pointer to lock value block
 */
static unsigned long int
display_dal( const char *const device,
             const unsigned long int local_csid,
             const unsigned short int lckcnt,
             const struct lkidef *const lkibuf,
             const struct daldef *const valblk )
{
    static ITEM_LIST syiitmlst[] =
    {
        { sizeof( NODE ), SYI$_NODENAME, NULL, NULL },
        { 0, 0, NULL, NULL }
    };

    static const char *devcxt[] =
    {   "Mounted Files-11", "Mounted Foreign",  "Allocated",
        "System", "Group", "Process",
        "ODS-1", "ODS-2", "N/A",
        "Yes", "No"
    };

    static const char *ownership[] = { "S:", " O:", " G:", " W:" };
    static const char prot[] = "rwed";

    char devprt[28];
    char owneruic[12];
    unsigned long int csid, status;
    unsigned long access, structure, write, shadow, mntsts;
    unsigned short int retlen, iosb[4], nodecnt;

    register char *cp;
    register NODE *node;
    register i, tmp, tmp1, flags;

        /* allocate buffer for node names and set node count to zero
         */
    node = (NODE *)malloc( (lckcnt - 1) * sizeof ( NODE ) );
    if ( node == NULL ) return ( LIB$_INSVIRMEM );

        /* Scan the Lock Information buffers and compute remote activity
         */
    flags = 0;
    nodecnt = 0;
    syiitmlst[0].retlen = &retlen;
    for ( i = 0; i < lckcnt; i++ )
    {
        if ( lkibuf[i].lki$b_grmode != LCK$K_NLMODE )
        {
            csid = lkibuf[i].lki$l_csid;

                /* Determine if local or remote access
                 */
            if ( local_csid == csid )
                flags |= FLG_M_LOCAL;
            else
                flags |= FLG_M_REMOTE;

                /* The device is allocated if an exclusive mode lock
                 * is granted on the DAL resource
                 */
            if ( lkibuf[i].lki$b_grmode == LCK$K_EXMODE )
                flags |= FLG_M_ALLOCATED;

                /* Store the node name accessing the device and increment
                 * the access (node) count
                 */
            syiitmlst[0].bufadr = &node[nodecnt];
            status = SYS$GETSYIW( 1, &csid, NULL, syiitmlst,
                                  iosb, NULL, NULL );
            if ( status & 1 ) status = iosb[0];
            if ( !(status & 1) )
            {
                nodecnt = 0;
                break;
            }
            node[nodecnt].name[retlen] = '\0';
            nodecnt++;
        }
    }

        /* If the device is accessed, print the device name, the access
         * mechanism, the node names accessing the device, and whether or
         * not the device is accessed with the F11B-XQP
         */
    if ( nodecnt > 0 )
    {
        access = ALLOCATED;
        structure = mntsts = write = shadow = NONE;

            /* determine access type of device
             */
        if ( valblk->dal$v_notfirst_mnt )
        {
            if ( !valblk->dal$v_foreign )
            {
                    /* file structure level
                     */
                if ( valblk->dal$v_nointerlock )
                    structure = ODS1;
                else
                    structure = ODS2;
                access = FILES11;
            }
            else
                access = FOREIGN;

                /* The device is mounted, determine mount status
                 */
            if ( valblk->dal$v_system )
                mntsts = SYSTEM;
            else
                if ( valblk->dal$v_group )
                    mntsts = GROUP;
                else
                    mntsts = PROCESS;

            if ( valblk->dal$v_write )
                write = YES;
            else
                write = NO;
        }

            /* Is device a shadow set.
             */
        if ( valblk->dal$v_shadow_mbr )
            shadow = YES;
        else
            shadow = NO;

        printf( "\n Device %s is %s %son node%s\n", device,
                flags & FLG_M_ALLOCATED ? "allocated" : "accessed",
                (flags & FLG_M_REMOTE) && !(flags & FLG_M_LOCAL) ?
                "remotely " : "",
                nodecnt > 1 ? "s" : "" );
        for ( i = (nodecnt - 1); i >= 0; i-- )
            printf( "  %s\n", node[i].name );

        printf( "\n%s%s\n%s%s\n%s%s\n",
                " Access           Mount    File       ",
                "Write   Shadow  Owner      Device",
                " Type             Status   Structure  ",
                "Enable  Member  UIC        Protection",
                " ---------------- -------- ---------- ",
                "------- ------- ---------- -------------" );

        sprintf( owneruic, "[%o,%o]",
                 valblk->dal$l_owner_uic >> 16,
                 valblk->dal$l_owner_uic & 0xffff );

            /* build device protection string
             */
        memset( devprt, '\0', sizeof( devprt ) );
        tmp = valblk->dal$w_protection;
        for ( i = 0; i < 16; i++ )
        {
            tmp1 = i & 3;
            if ( !tmp1 ) strcat( devprt, ownership[i >> 2] );
            if ( !((1 << i) & tmp) )
                sprintf( devprt, "%s%c", devprt, prot[tmp1] );
        }

        cp = (char *)strchr( devprt, 'G' );
        *--cp = '\0';
        printf( " %- 17s%- 9s%- 11s%- 8s%- 8s%- 11s%s\n",
                devcxt[access], devcxt[mntsts], devcxt[structure],
                devcxt[write], devcxt[shadow], owneruic, devprt );
        printf( "%65s%s\n", "", ++cp );
    }

        /* Release memory associated with node name buffer
         */
    free( node );

    return ( status );
}



/*
 * FUNCTION: main
 *
 * ABSTRACT:
 *      This function allocates a buffer for lock information based on the
 *      number of nodes in the VAXcluster (plus four in case a new node joins
 *      the cluster or a show device command has been issued concurrently.)
 *      The devlockinfo kernel mode routine is called to gather lock
 *      the information to be processed by the display_dal function.
 *
 */
main( unsigned argc, char **argv )
{
    static ITEM_LIST syiitmlst[ ] =
    {
        { sizeof(unsigned long int), SYI$_NODE_CSID, NULL, NULL },
        { sizeof(unsigned short int), SYI$_CLUSTER_NODES, NULL, NULL },
        { 0, 0, NULL, NULL }
    };

    static ITEM_LIST lkiitmlst[ ] =
    {
        { 0, LKI$_LOCKS, NULL, NULL },
        { 0, 0, NULL, NULL }
    };

    void *arglst[4];
    char devbuf[132];
    struct daldef valblk;
    struct lkidef *lkibuf;
    struct dsc$descriptor_s devnam;
    unsigned short int iosb[4], nodecnt;
    unsigned long int lckcnt, local_csid, status;
    char devlst[80], *argvec[3];

    register tmp;
    register char *device;

        /* if no devices on command line, then we're done.
         */
    if ( argc == 1 )
    {
        argv = argvec;
        argvec[2] = NULL;
        fputs( "Enter device: ", stdout ); argvec[1] = gets( devlst );
    }

        /* Build item list and call $GETSYI system service
         */
    syiitmlst[0].bufadr = &local_csid;
    syiitmlst[1].bufadr = &nodecnt;
    status = SYS$GETSYIW( 1, NULL, NULL, syiitmlst, iosb, NULL, NULL );
    if ( status & 1 ) status = iosb[0];
    if ( !(status & 1) ) return ( status );

        /* Allocate and intialize $GETLKI system service item list.
         */
    tmp = (nodecnt + 4) * LKI$K_LENGTH;
    lkibuf = (struct lkidef *)malloc( tmp );
    if ( lkibuf == NULL ) return ( LIB$_INSVIRMEM );
    lkiitmlst[0].buflen = tmp;
    lkiitmlst[0].bufadr = (void *)lkibuf;
    lkiitmlst[0].retlen = (unsigned short int *)&lckcnt;

        /* Initialize argument list for kernel mode subroutine
         */
    arglst[0] = (void *)3;
    arglst[1] = (void *)&devnam;
    arglst[2] = (void *)&valblk;
    arglst[3] = (void *)lkiitmlst;

        /* process command line into contigous string, then parse off each
         * device name and process one at a time.
         */
    for ( strcpy( devbuf, *++argv ); *++argv; strcat( devbuf, *argv ) );
    for ( device = strtok( devbuf, "," ); device; device = strtok( NULL, "," ) )
    {
        devnam.dsc$a_pointer = device;
        devnam.dsc$w_length = strlen( device );
        status = SYS$CMKRNL( devlckinfo, arglst );
        if ( !(status & 1) ) break;

            /* Test for buffer overflow.
             */
        if ( (tmp = lckcnt) < 0 )
        {
            status = (SS$_BUFFEROVF & ~STS$M_SEVERITY) | STS$K_WARNING;
            break;
        }

        lckcnt = (tmp & 0xFFFF) / ((tmp >> 16) & 0x7FFF);
        status = display_dal( device, local_csid, lckcnt, lkibuf, &valblk );
    }

    free( lkibuf );

    return ( status );
}



        .title devlckinfo -- get device allocation lock information
        .ident  'V01.0'
;++
; FACILITY:
;       devlckinfo -- get device allocation lock information
;
; ABSTRACT:
;       This module provides a routine to access the Device Allocation Lock
;       (DAL) for a device. The lock value block and any other information
;       specified in the item list argument to the $GETLKI system service.
;
; AUTHOR: Eric M. LaFranchi,    CREATION DATE: 31-JUL-1991
;
;--

        .default displacement, byte
        .library 'sys$share:lib.mlb'
        .link   'sys$system:sys.stb' /selective_search

        $chfdef                         ; condition handling fac definitions
        $devdef                         ; device characteristic definitions
        $ipldef                         ; IPL definitions
        $lckdef                         ; lock manager definitions
        $lkidef                         ; lock information definitions
        $pcbdef                         ; process control block definitions
        $prdef                          ; processor register definitions
        $psldef                         ; access mode definitions
        $ssdef                          ; system service status definitions
        $stsdef                         ; system service failure definitions
        $ucbdef                         ; device unit control block definitions

       .page
        .sbttl  devlckinfo -- request device allocation lock information
        .psect  code nowrt, exe, shr, pic, novec, quad
;++
; Environment:
;       VAX/VMS kernel mode routine
;
; Abstract:
;       This routine probes user arguments, builds the Device Allocation
;       Lock (DAL) resource name, and enqueues a NULL mode lock on the
;       Device Allocation resource. The lock value block is returned if a
;       buffer is specified, and $GETLKI system service is requested with
;       the user specified item list.
;
; Calling Sequence:
;       $cmkrnl_s -
;               routin = devlckinfo,
;               arglst = arglst
;
; Inputs:
;       devnam -- address of device name descriptor
;       valblk -- user value block address
;       itmlst -- item list argument to $GETLKI system service
;
; Outputs:
;       value block -- returned to caller if specified
;       itmlst -- process by the $GETLKI system service
;
; Return Value:
;       r0 -- VMS status code
;
;--
flags = LCK$M_EXPEDITE!LCK$M_SYSTEM!LCK$M_VALBLK!LCK$M_NOQUEUE
devnam = 4
valblk = 8
itmlst = 12

        $offset 0, NEGATIVE, <-                 ; frame pointer (fp) offsets
                <resnam, 8>, -                  ; for local storage
                <alldevnam, 16>, -
                <prefix, 4>, -                  
                <lksb, 24>, -
                <lkid, 4>, -
                <efn, 4>, -
                stksiz>

        .entry  devlckinfo, ^m<r2, r3, r4, r5>  ; save registers
        movab   w^handler, (fp)                 ; establish condition handler
        movab   stksiz(sp), sp                  ; reserve work space
        movzwl  #ss$_accvio, r0                 ; assume access violation status
        ifnord  s^#16, (ap), 20$                ; check accessibility of arglst
        movl    devnam(ap), r1                  ; load device name descriptor
        ifnord  s^#8, (r1), 20$                 ; check descriptor readability
        movq    (r1), r2                        ; load descriptor contents
        ifnord  r2, (r3), 20$                   ; branch if string not readable
        jsb     g^sch$iolockr                   ; grab I/O database mutex
        jsb     g^ioc$searchdev                 ; search I/O DB for device
        blbc    r0, 10$                         ; branch if device not found
        movl    r1, r5                          ; place ucb address in r5
        movzwl  #ss$_ivdevnam, r0               ; assume invalid device status
        assume  dev$v_clu eq 0                  ; check DEV$V_CLU is bit zero
        blbc    ucb$l_devchar2(r5), 10$         ; branch not available
        movl    #^a'SYS$', prefix(fp)           ; store system lock prefix
        movl    r4, r3                          ; save pcb address
        movab   alldevnam(fp), r1               ; load address for device name
        movl    s^#16, r0                       ; load device name buffer length
        movl    s^#1, r4                        ; load flags for name type
        jsb     g^ioc$cvt_devnam                ; get device name
        movl    r3, r4                          ; restore pcb
        addl3   s^#4, r1, resnam(fp)            ; store DAL resource name length
10$:    movl    r0, r5                          ; save status
        jsb     g^sch$iounlock                  ; release I/O database mutex
        setipl  s^#0                            ; return IPL to zero
        blbs    r5, 25$                         ; check status -- branch if O.K.
        movl    r5, r0                          ; restore bad status
20$:    ret                                     ; return error status
25$:    pushal  efn(fp)                         ; push event flag address
        calls   s^#1, g^lib$get_ef              ; allocate event flag
        blbc    r0, 20$                         ; branch is no event flag
        movab   prefix(fp), <resnam+4>(fp)      ; initalize resource descriptor

        $enqw_s -                               ; Enqueue a NULL mode lock on
                efn = efn(fp), -                ; device allocation lock resourc
                lkmode = #LCK$K_NLMODE, -
                lksb = lksb(fp), -
                flags = #flags, -
                resnam = resnam(fp), -
                acmode = #PSL$C_KERNEL
        blbc    r0, 30$                         ; branch if error return status 
        movzwl  lksb(fp), r0                    ; load status from lksb
30$:    cmpw    r0, #ss$_valnotvalid            ; invalid value block?
        beql    40$                             ; branch invalid value blk O.K.
        blbc    r0, 70$                         ; branch error enqueueing lock
40$:    movl    valblk(ap), r1                  ; load user value block address
        beql    50$                             ; branch no value block address
        movzwl  #ss$_accvio, r2                 ; assume access violation status
        ifnowrt s^#16, (r1), 60$                ; branch buffer not writeable
        movc3   s^#16, <lksb+8>(fp), (r1)       ; copy value block to user buffe
        movl    <lksb+4>(fp), lkid(fp)          ; save/store lock id
        movzwl  #ss$_normal, r2                 ; load success if no item list
        movl    itmlst(ap), r3                  ; load item list address
        beql    60$                             ; branch no item list address

50$:    $getlkiw_s -                            ; get information for all locks
                efn = efn(fp), -                ; on the device allocation lock
                lkidadr = lkid(fp), -           ; resource
                itmlst = (r3), -
                iosb = lksb(fp)
        movl    r0, r2                          ; load return status into r2
        blbc    r0, 60$                         ; branch if an error occurred
        movzwl  lksb(fp), r2                    ; load completion status

60$:    $deq_s -                                ; dequeue the lock
                lkid = lkid(fp), -
                acmode = #PSL$C_KERNEL
        blbc    r2, 80$                         ; branch if previous bad status
70$:    movl    r0, r2                          ; save status
80$:    pushal  efn(fp)                         ; push event flag address
        calls   s^#1, g^lib$free_ef             ; give back event flag
        blbc    r0, 90$                         ; branch, bad event flag
        movl    r2, r0                          ; restore real status
        ret                                     ; return status to caller
90$:    blbs    r2, 99$                         ; return bad event flag status
        movl    r2, r0                          ; load previous bad status to r0
99$:    ret                                     ; return status to caller

       .page
        .sbttl  handler -- handler for kernel mode exceptions
;++
;
; abstract:
;       This handler releases the I/O database mutex if it has been aquired
;       by this process, then returns the exception status.
;
; establish sequence:
;       movab   handler, (fp)
;
;--
        .align  long
handler:
        .word   ^m<>
        movl    chf$l_sigarglst(ap), r1         ; load signal array address
        movl    chf$l_sig_name(r1), r0          ; load signal name
        cmpl    r0, #ss$_unwind                 ; are we unwinding?
        beql    20$                             ; branch if yes and resignal
        pushr   #^m<r0, r2, r3, r4>             ; save registers
        movl    g^ctl$gl_pcb, r4                ; load pcb address
        tstw    pcb$w_mtxcnt(r4)                ; holding I/O database mutex?
        beql    10$                             ; branch if not holding mutex
        jsb     g^sch$iounlock                  ; release I/O database mutex
        setipl  s^#0                            ; return IPL to zero
10$:    popr    #^m<r0, r2, r3, r4>             ; restore registers
        movl    chf$l_mcharglst(ap), r1         ; load mechanism array address
        movl    r0, chf$l_mch_savr0(r1)         ; set return status into r0
        $unwind_s                               ; return condition to caller
20$:    movl    #ss$_resignal, r0               ; continue unwinding
        ret
        .end

