/****************************************************************************
 * READ_KFDB.C
 *    David Schwab
 *    DEMAX Software, San Mateo, CA
 *    Demonstration Program for DECUS session on the Known File Database.
 *    FALL 1990 DECUS Symposium.
 *    This program will copy the KFDs and KFEs into user buffers and then
 *    print out the information contained in them.
 *
 *    Copyright 1990, DEMAX Software, San Mateo, CA.
 *
 *    David Schwab and DEMAX software make no warranty on this software.
 *    You are free to use it for any purpose except to include it in software
 *    for resale. Feel free to re-distribute this code, provided this
 *    informational header is left intact. 
 *    THIS CODE RUNS IN EXECUTIVE MODE. USE AT YOUR OWN RISK!!
 ****************************************************************************
 */

#include ssdef
#include lckdef
#include descrip
#include prvdef
#include psldef
#include nam

/*
 * You will need to create the following include files before running this
 * program. Extract $DYNDEF, $KFEDEF, and $KFDDEF from SYS$LIBRARY:LIB.MLB
 * and convert the MACRO equates to C defines.
 */
#include "dyndef.h"
#include "kfedef.h"
#include "kfddef.h"

/***************************************************************************
 ************************   TYPEDEFS ***************************************
 ********Define the Known File Database Structures**************************
 ***************************************************************************
 */

typedef struct kfe_struct  /* Structure of a Known File Entry */
      {
	void	              *kfe$l_hshlnk;
	struct kfe_struct     *kfe$l_kfelink;
	unsigned short         kfe$w_size;
	unsigned char          kfe$b_type;
	unsigned char          kfe$b_hshidx;
	struct kfd_struct     *kfe$l_kfd;
	unsigned short         kfe$w_flags;
	unsigned short         kfe$w_gblseccnt;
	unsigned int           kfe$l_usecnt;
	variant_union
          {
	    variant_struct
	      {
	        void          *kfe$l_wcb;
		void          *kfe$l_imghdr;
	      } a_struct;
	    variant_struct
	      {
		unsigned short kfe$w_fid_num;
		unsigned short kfe$w_fid_seq;
		unsigned short kfe$w_fid_rvn;
		short          kfe$w_unused;
	      } b_struct;
	  } a_union;
	int                    kfe$q_procpriv[2];
	unsigned char          kfe$b_matchctl;
	char                   kfe$b_unused;
	unsigned short         kfe$w_amecod;
	unsigned int           kfe$l_ident;
        unsigned int           kfe$l_orb;
	unsigned short         kfe$w_shrcnt;
	unsigned char          kfe$b_filnamlen;
	char	               kfe$t_filnam[39];
      } KFE;

typedef struct kfd_struct  /* Structure of a Known File Directory */
      {
	struct kfd_struct *kfd$l_link;
	struct kfe_struct *kfd$l_kfelist;
	unsigned short     kfd$w_size;
	unsigned char      kfd$b_type;
	char               kfd$b_spare;
	unsigned short     kfd$w_refcnt;
	unsigned char      kfd$b_devlen;
	unsigned char      kfd$b_dirlen;
	unsigned char      kfd$b_ddtstrlen;
	char	           kfd$t_ddtstr[NAM$C_MAXRSS];
      } KFD;

typedef struct   /* Structure of a Known File Pointer Block */
      {
	struct kfd_struct *kfpb$l_kfdlst;
	void              *kfpb$l_kfehshtab;
	unsigned short     kfpb$w_size;
	unsigned char      kfpb$b_type;
	char               kfpb$b_spare;
	unsigned short     kfpb$w_kfdlstcnt;
	unsigned short     kfpb$w_hshtablen;
      } KFPB;

/***************************************************************************
 **********************OTHER STRUCTURES*************************************
 ***************************************************************************
 */

struct 	/* The structure of a lock status block */
  {
    short status, reserved;
    int lkid;
  } lksb;

/***************************************************************************
 *****************References to be resolved from SYS.STB********************
 ***************************************************************************
 */
/*
 * We find the KFDB via EXE$GL_KNOWN_FILES which points to the KFPB
 */
globalref KFPB *EXE$GL_KNOWN_FILES;

/*
 * We will need to lock the KFDB for reading. The name of the lock used for
 * the lock name is at EXE$GQ_KFE_LCKNAM.
 */
globalref struct dsc$descriptor EXE$GQ_KFE_LCKNAM;

/***************************************************************************
 ******************Global Variables*****************************************
 ***************************************************************************
 */

KFPB kfpb_copy, kfpb_copy2;	/* Our copies of the KFPB */

char *buff; /* This points to the buffer we will use to copy the KFDB */

/****************************************************************************
 * COPY_KFPB
 *
 * This executive mode routine will lock the KFDB for reading, will copy
 * the KFPB into the global variable kfpb_copy, and will then dequeue the
 * lock on the KFDB.
 ****************************************************************************
 */
int copy_kfpb(void)
{
  int status;

  /*
   * This is the same lock that INSTALL uses when it needs to read the
   * KFDB.
   */
  status = sys$enqw(0, LCK$K_PRMODE, &lksb, LCK$M_SYSTEM, &EXE$GQ_KFE_LCKNAM,
		     0, 0, 0, 0, PSL$C_EXEC, 0);
  if (status & 1)
    status = lksb.status;
  if (!(status & 1))
    return(status);

  /*
   * Make a copy of the KFPB
   */
  if (EXE$GL_KNOWN_FILES)
    kfpb_copy = *EXE$GL_KNOWN_FILES;

  /*
   * Dequeue the lock
   */
  sys$deq(lksb.lkid, 0, PSL$C_EXEC, 0);

  return(SS$_NORMAL);
}

/*****************************************************************************
 * COPY_KFD
 *
 * This executive mode routine will, like the previous, lock the KFDB and
 * make a new copy of the KFPB. If the new copy has a larger KFDLSTCNT
 * field than the old one, then we return SS$_BUFFEROVF because we
 * based our buffer allocation on the contents KFDLSTCNT. Otherwise, for
 * each KFD in the list, we:
 *	1. copy the KFD into the buffer
 *	2. For each KFE in the KFD's KFE list, we copy the KFE into the
 *	   buffer.
 * and then return SS$_NORMAL.
 *	Note that we take a different approach than INSTALL/LIST. LIST creates
 * enough buffer space for the lines of output as they will appear on the
 * screen and the executive mode routine creates the output lines, but they
 * are not written until the entire KFDB has been scanned and all the output
 * lines have been created. This is the reason there is a noticeable pause
 * until output appears. Our approach is to make a copy of the KFDB. This
 * allows us to look at our copy in the debugger and format the output in
 * whatever way we wish without changing executive mode code.
 ***************************************************************************
 */
int copy_kfd()
{
  int status;
  KFD *kfd_link, *kfd_p = (KFD *) buff;
  KFE *kfe_p, *kfe_link;

  /*
   * Lock the KFDB
   */
  status = sys$enqw(0, LCK$K_PRMODE, &lksb, LCK$M_SYSTEM, &EXE$GQ_KFE_LCKNAM,
		     0, 0, 0, 0, PSL$C_EXEC, 0);
  if (status & 1)
    status = lksb.status;
  if (!(status & 1))
    return(status);

  /*
   * Make a new copy of the KFPB
   */
  if (EXE$GL_KNOWN_FILES)
    kfpb_copy2 = *EXE$GL_KNOWN_FILES;

  /*
   * Make sure that the list count hasn't increased
   */
  if (kfpb_copy.kfpb$w_kfdlstcnt < kfpb_copy2.kfpb$w_kfdlstcnt)
    return(SS$_BUFFEROVF);

  for (kfd_link = kfpb_copy2.kfpb$l_kfdlst;  /* For each KFD */
       kfd_link;
       kfd_link = kfd_link->kfd$l_link)
    {
      /*
       * Copy the KFD into the buffer
       */
      *kfd_p++ = *kfd_link;

      /*
       * For each KFE linked to the KFD
       */
      for (kfe_p = (KFE *)kfd_p, kfe_link = (KFE *)kfd_link->kfd$l_kfelist;
	   kfe_link;
	   kfe_p++, kfe_link = kfe_link->kfe$l_kfelink)
	memcpy(kfe_p, kfe_link, kfe_link->kfe$w_size);

      /*
       * Correct the pointer of where to copy the next KFD
       */
      kfd_p = (KFD *) kfe_p;
    }

  /*
   * Fake a zero-filled KFD so we know we have reached the end
   */
  memset(kfd_p, 0, sizeof(KFD));

  /*
   * Dequeue the lock
   */
  sys$deq(lksb.lkid, 0, PSL$C_EXEC, 0);
  return(SS$_NORMAL);
}

/****************************************************************************
 * PRINT_TYPE
 *
 * Used to print the 'type' field in a KFPB, KFE, or KFD. Will print the
 * symbolic "DYN$C_" name rather than the actual value.
 ****************************************************************************
 */
print_type(int n)
{
  switch (n)
    {
	case DYN$C_KFPB : printf("DYN$C_KFPB\n");    break;
	case DYN$C_KFE  : printf("DYN$C_KFE\n");     break;
	case DYN$C_KFD  : printf("DYN$C_KFD\n");     break;
	default		: printf("unknown (%d\n",n); break;
    }
}

/*****************************************************************************
 * PRINT_KFPB
 *
 * Prints the values of the fields in the KFPB
 *****************************************************************************
 */
void print_kfpb(void)
{
  printf("kfpb$l_kfdlst = %x\n",	kfpb_copy.kfpb$l_kfdlst);
  printf("kfpb$l_kfehshtab = %x\n",	kfpb_copy.kfpb$l_kfehshtab);
  printf("kfpb$w_size = %d\n",		kfpb_copy.kfpb$w_size);
  printf("kfpb$b_type = ");
  print_type(kfpb_copy.kfpb$b_type);
  printf("kfpb$b_spare = %d\n",		kfpb_copy.kfpb$b_spare);
  printf("kfpb$w_kfdlstcnt = %d\n",	kfpb_copy.kfpb$w_kfdlstcnt);
  printf("kfpb$w_hshtablen = %d\n\n",	kfpb_copy.kfpb$w_hshtablen);
}

/*****************************************************************************
 * PRINT_KFD
 *
 * Prints the values of the fields in a KFD
 *****************************************************************************
 */
void print_kfd(KFD *kfd_p)
{
  char filbuf[256];

  printf("\tkfd$l_link      = %x\n", 	kfd_p->kfd$l_link);
  printf("\tkfd$l_kfelist   = %x\n", 	kfd_p->kfd$l_kfelist);
  printf("\tkfd$w_size      = %d\n", 	kfd_p->kfd$w_size);
  printf("\tkfd$b_type      = ");
  print_type(kfd_p->kfd$b_type);
  printf("\tkfd$b_spare     = %d\n", 	kfd_p->kfd$b_spare);
  printf("\tkfd$w_refcnt    = %d\n", 	kfd_p->kfd$w_refcnt);
  printf("\tkfd$b_devlen    = %d\n", 	kfd_p->kfd$b_devlen);
  printf("\tkfd$b_dirlen    = %d\n", 	kfd_p->kfd$b_dirlen);
  printf("\tkfd$b_ddtstrlen = %d\n", 	kfd_p->kfd$b_ddtstrlen);
  /*
   * Copy the directory/type string to another buffer where we know we will
   * have room to add a terminating 0 for C I/O
   */
  strncpy(filbuf, kfd_p->kfd$t_ddtstr,	kfd_p->kfd$b_ddtstrlen);
  filbuf[kfd_p->kfd$b_ddtstrlen] = '\0';
  printf("\tkfd$t_ddtstr    = %s\n\n", 	filbuf);
}

/*****************************************************************************
 * PRINT_FLAGS
 *
 * Prints what the KFE flags mean.
 *****************************************************************************
 */
void print_flags(short flags)
{
  if (flags & KFE$M_PROTECT)   printf(" PROTECT");
  if (flags & KFE$M_LIM)       printf(" LIM");
  if (flags & KFE$M_PROCPRIV)  printf(" PROCPRIV");
  if (flags & KFE$M_OPEN)      printf(" OPEN");
  if (flags & KFE$M_HDRRES)    printf(" HDRRES");
  if (flags & KFE$M_SHARED)    printf(" SHARED");
  if (flags & KFE$M_SHMIDENT)  printf(" SHMIDENT");
  if (flags & KFE$M_COMPATMOD) printf(" COMPATMOD");
  if (flags & KFE$M_NOPURGE)   printf(" NOPURGE");
  if (flags & KFE$M_ACCOUNT)   printf(" ACCOUNT");
  if (flags & KFE$M_WRITEABLE) printf(" WRITEABLE");
  if (flags & KFE$M_EXEONLY)   printf(" EXEONLY");
  printf("\n");
}

/*****************************************************************************
 * PRINT_KFE
 *
 * Prints the values of the fields in a KFE
 *****************************************************************************
 */
void print_kfe(KFE *kfe_p)
{
  char filbuf[40];
  union prvdef *privs;

  printf("\t\tkfe$l_hshlnk      = %x\n",    kfe_p->kfe$l_hshlnk);
  printf("\t\tkfe$l_kfelink     = %x\n",    kfe_p->kfe$l_kfelink);
  printf("\t\tkfe$w_size        = %d\n",    kfe_p->kfe$w_size);
  printf("\t\tkfe$b_type        = ");
  print_type(kfe_p->kfe$b_type);
  printf("\t\tkfe$b_hshidx      = %d\n",    kfe_p->kfe$b_hshidx);
  printf("\t\tkfe$l_kfd         = %x\n",    kfe_p->kfe$l_kfd);
  printf("\t\tkfe$w_flags       = %x:",     kfe_p->kfe$w_flags);
  print_flags(kfe_p->kfe$w_flags);
  printf("\t\tkfe$w_gblseccnt   = %d\n",    kfe_p->kfe$w_gblseccnt);
  printf("\t\tkfe$l_usecnt      = %d\n",    kfe_p->kfe$l_usecnt);
  /*
   * Some of the fields are interpreted differently depending on whether
   * the file was installed open or not.
   */
  if (kfe_p->kfe$w_flags & (1 << KFE$V_OPEN))
    {
      printf("\t\tkfe$l_wcb         = %x\n",    kfe_p->kfe$l_wcb);
      printf("\t\tkfe$l_imghdr      = %x\n",    kfe_p->kfe$l_imghdr);
    }
  else
    {
      printf("\t\tkfe$w_fid_num     = %d\n",    kfe_p->kfe$w_fid_num);
      printf("\t\tkfe$w_fid_seq     = %d\n",    kfe_p->kfe$w_fid_seq);
      printf("\t\tkfe$w_fid_rvn     = %d\n",    kfe_p->kfe$w_fid_rvn);
    }
  /*
   * Print the installed privileges, interpreting the bit values.
   * First, cast the quadword privilege mask as a prvdef union so we can
   * address each bit by name.
   */
  printf("\t\tkfe$q_procpriv    = %x %x: ", kfe_p->kfe$q_procpriv[1],
	 				    kfe_p->kfe$q_procpriv[0]);
  privs = (union prvdef *) kfe_p->kfe$q_procpriv;
  if (privs->prv$v_cmkrnl)    printf("CMKRNL ");
  if (privs->prv$v_cmexec)    printf("CMEXEC ");
  if (privs->prv$v_sysnam)    printf("SYSNAM ");
  if (privs->prv$v_grpnam)    printf("GRPNAM ");
  if (privs->prv$v_allspool)  printf("ALLSPOOL ");
  if (privs->prv$v_detach)    printf("DETACH ");
  if (privs->prv$v_diagnose)  printf("DIAGNOSE ");
  if (privs->prv$v_log_io)    printf("LOG_IO ");
  if (privs->prv$v_group)     printf("GROUP ");
  if (privs->prv$v_noacnt)    printf("NOACNT ");
  if (privs->prv$v_prmceb)    printf("PRMCEP ");
  if (privs->prv$v_prmmbx)    printf("PRMMBX ");
  if (privs->prv$v_pswapm)    printf("PSWAPM ");
  if (privs->prv$v_setpri)    printf("SETPRI ");
  if (privs->prv$v_setprv)    printf("SETPRV ");
  if (privs->prv$v_tmpmbx)    printf("TMPMBX ");
  if (privs->prv$v_world)     printf("WORLD ");
  if (privs->prv$v_mount)     printf("MOUNT ");
  if (privs->prv$v_oper)      printf("OPER ");
  if (privs->prv$v_exquota)   printf("EXQUOTA ");
  if (privs->prv$v_netmbx)    printf("NETMBX ");
  if (privs->prv$v_volpro)    printf("VOLPRO ");
  if (privs->prv$v_phy_io)    printf("PHY_IO ");
  if (privs->prv$v_bugchk)    printf("BUGCHK ");
  if (privs->prv$v_prmgbl)    printf("PRMGBL ");
  if (privs->prv$v_sysgbl)    printf("SYSGBL ");
  if (privs->prv$v_pfnmap)    printf("PFNMAP ");
  if (privs->prv$v_shmem)     printf("SHMEM ");
  if (privs->prv$v_sysprv)    printf("SYSPRV ");
  if (privs->prv$v_bypass)    printf("BYPASS ");
  if (privs->prv$v_syslck)    printf("SYSLCK ");
  if (privs->prv$v_share)     printf("SHARE ");
  if (privs->prv$v_upgrade)   printf("UPGRADE ");
  if (privs->prv$v_downgrade) printf("DOWNGRADE ");
  if (privs->prv$v_grpprv)    printf("GRPPRV ");
  if (privs->prv$v_readall)   printf("READALL ");
  if (privs->prv$v_security)  printf("SECURITY ");
  printf("\n");

  printf("\t\tkfe$b_matchctl    = %d\n",    kfe_p->kfe$b_matchctl);
  printf("\t\tkfe$w_amecod      = %d\n",    kfe_p->kfe$w_amecod);
  printf("\t\tkfe$l_ident       = %x\n",    kfe_p->kfe$l_ident);
  printf("\t\tkfe$l_orb         = %x\n",    kfe_p->kfe$l_orb);
  printf("\t\tkfe$w_shrcnt      = %d\n",    kfe_p->kfe$w_shrcnt);
  printf("\t\tkfe$b_filnamlen   = %d\n",    kfe_p->kfe$b_filnamlen);
  /*
   * Before printing the file name, copy it to another buffer where we can
   * put a terminating 0 on it.
   */
  strncpy(filbuf, kfe_p->kfe$t_filnam, kfe_p->kfe$b_filnamlen);
  filbuf[kfe_p->kfe$b_filnamlen] = '\0';
  printf("\t\tkfe$t_filnam      = %s\n\n",  filbuf);
}

/***************************************************************************
 * MAIN ROUTINE
 ***************************************************************************
 */
main()
{
  KFD *kfd_p;
  KFE *kfe_p;
  int status;

  /*
   * Print the contents of EXE$GL_KNOWN_FILES - pointer to the KFPB
   */
  printf("EXE$GL_KNOWN_FILES = %x\n\n",EXE$GL_KNOWN_FILES);

  /*
   * Make a copy of the KFPB
   */
  status = sys$cmexec(copy_kfpb, 0);
  if (status != SS$_NORMAL)
    exit(status);

  if (!kfpb_copy.kfpb$b_type)    /* This is just a check. It wouldn't */
    {				 /* happen unless NOTHING is installed */
      printf("No kfpb\n");
      exit(1);
    }

  /*
   * Print the contents of the KFPB
   */
  print_kfpb();

  if (!kfpb_copy.kfpb$w_kfdlstcnt)   /* Shouldn't happen */
    {
      printf("No kfds\n");
      exit(1);
    }

  while (1)
    {
      /*
       * Allocate space for the KFEs and KFDs. Since the sizeof a KFD is
       * larger than a KFE, allocating for all with the size of a KFD will
       * give us plenty of room.
       */
      buff = malloc(sizeof(KFD) * (1 + kfpb_copy.kfpb$w_kfdlstcnt));
      if (!buff)
        {
          printf("Could not allocate enough buffer space\n");
          exit(1);
        }
      /*
       * Attempt to copy the KFDs and KFEs
       */
      status = sys$cmexec(copy_kfd, 0);
      if (status != SS$_NORMAL && status != SS$_BUFFEROVF)
        exit(status);
      if (status == SS$_BUFFEROVF)
        {
          printf("Count changed after allocation. Trying again\n\n");
          kfpb_copy = kfpb_copy2;
          free(buff);
	  continue;    /* Go through the while loop again */
        }
      else
	break;  /* We copied the KFDB, so break out of the while loop */
    }
  /*
   * Just another consistency check that shouldn't happen
   */
  if (!((KFD *)buff)->kfd$b_type)
    {
      printf("No kfds\n");
      exit(1);
    }
  /*
   * Now print all the KFDs and KFEs
   */
  for (kfd_p = (KFD *)buff; kfd_p; )
    {
      print_kfd(kfd_p);  /* Print the KFD */
      kfd_p++;
      if (kfd_p->kfd$b_type == DYN$C_KFD)  /* Loop again if next is KFD */
        continue;
      if (kfd_p->kfd$b_type != DYN$C_KFE)  /* Next is not KFE or KFD. Quit */
        break;
		 /*
                  * Next is KFE, so print each of the KFEs
		  */
      for (kfe_p = (KFE *) kfd_p; kfe_p->kfe$b_type == DYN$C_KFE; kfe_p++)
        print_kfe(kfe_p);

      kfd_p = (KFD *)kfe_p;
      if (kfd_p->kfd$b_type != DYN$C_KFD)  /* Next is not KFE or KFD. Quit */
        break;
    }

  free(buff);
}            
