#define SCCSID  "@(#)qstatec.c  6.5 92/06/17"

/*
 *
 *      This file implements the Dos32QuerySysState API.
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 */


/*
 *      TODO
 *      Convert all structure pointers to offsets
 *      Grab kernel semaphores for getting system semaphore information
 *      Test and error if loader or semaphore semaphore not available
 *      Sanity check info in case of inconsistent data
 *      Notify app of inconsistent/missing data if necessary:
 *      handle too small, partially full buffer return
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 *        05/17/95 wdma 121979 - avoid sys hang due to sem contention
 *        12/14/95 ChgTeam          145311  DosQProcStatus hangs system.  Add a
 *                                          check to the return of the  call to
 *                                          TKPidToPTDA.
 *        12/13/95 Scott Garfinkle  145345  Fix qsProcFS so it doesn't bomb
 *                                          if QS_FILE processing is enabled.
 *                                          Chg Team - Tag 145345
 */

#pragma code_seg("QUERYSWAP_TEXT", "CODE")

#if RAS
#pragma data_seg("CSWAP_DATA", "CSWAPDATA");
#endif

#define FNO_NUM FNO_QSTATEC_C

#define INCL_TYPES
#define INCL_ERROR_H
#define INCL_ERROR2_H

#include        <fnoperf.h>
#include <basedef.h>
#include <osconst.h>
#include <ostypes.h>
#include <basemac.h>
#include <osmac.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <mi.h>
#include <infoseg.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <proc.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <ldrxport.h>
#include <kmode.h>
#define FOR_EXEHDR      1       /* this, newexe, exe386 needed for ldrvars */
#include <newexe.h>
#include <exe386.h>
#include <ldrvars.h>
#include <seldesc.h>
#include <bsetib.h>
#include <tklocal.h>
#include <vmowner.h>
#include <selxport.h>
#include <semxport.h>           /* for 32 bit SEMS */
#include <semlocal.h>           /* for 32 bit SEMS */
#include <mft.h>
#include <sft.h>
#include <ptree.h>
#include <bseerr.h>
#include <string.h>
#include <qsexport.h>


// External Function Prototypes

extern ulong_t  qsSys16Sem(void);
extern ulong_t  qsMemory(ulong_t, ulong_t);
extern ulong_t  qs16ShrMem(uchar_t *pCurCache, ushort_t ptdahandle);
extern ulong_t  LDRSysMteInfo(ulong_t);
extern ulong_t  LDRProcMteInfo(qsPrec_t *, ldrdld_t *);

extern ulong_t  *vmNamShrSem;

// Internal Function Prototypes

ulong_t QSQuerySysState(ulong_t, ulong_t, ulong_t, ulong_t, uchar_t *, ulong_t);
ulong_t qsGlobal(void);
ulong_t qsProcess(ulong_t, ulong_t);
ulong_t qsSem(ulong_t, ulong_t);
ulong_t qsLib(ulong_t, ulong_t);
ulong_t qsMem(ulong_t, ulong_t);
ulong_t qsDisk(ulong_t, ulong_t);
ulong_t qsFile(ulong_t, ulong_t);
ulong_t qsHWConfig(ulong_t, ulong_t);
ulong_t qsNamedPipe(ulong_t, ulong_t);
ulong_t qsNotSupported(ulong_t, ulong_t);

ulong_t qsReadPtda(ptda_t *, void *);
ulong_t qsReadMFT(HPTREE, HPTKEY);
ulong_t qsProcSem(qsPrec_t *, ptda_t *);
ulong_t qsProcThrd(qsPrec_t *, ptda_t *);
ulong_t qsProcLib(qsPrec_t *, ptda_t *);
ulong_t qsProcShrMem(qsPrec_t *, ushort_t);
ulong_t qsProcFS(qsPrec_t *, ptda_t *);

ulong_t qsInitCache(void);
ulong_t qsFreeCache(void);

/* Note that the order of the function pointers in the qsFunc array
 * parallels the values assigned to the corresponding QS_* defines in
 * qsexport.h. Do not change one without modifying the other!
 */
typedef ulong_t (*PFUNC)(ulong_t, ulong_t);
PFUNC qsFunc[] = {
        qsProcess,
        qsSem,
        qsLib,
        qsFile,
        qsMem,
        qsDisk,
        qsHWConfig,
        qsNamedPipe,
        qsNotSupported,
        qsNotSupported,
};
/*
        Cacheing and buffer management related declarations
 */

uchar_t *pCurStateBuf;  /* tracks current location for writes to user buffer */
uchar_t *pRecStart;     /* tracks current location for next record type */
uchar_t *pEndStateBuf;  /* BUG NOT USEDpoints to end of user buffer */
uchar_t *pBeginCache;   /* points to beginning of system cache */
uchar_t *pEndCache;     /* points to end of system cache */
uchar_t *pCurCache;     /* points to current position in system cache */

qsPtrRec_t ResPtrRec;   /* header structure of pointers to record lists */
qsPtrRec_t *pPtrRec = &ResPtrRec;
uchar_t *pResPtrRec;    /* ptr to place in user buffer this will be written */

ulong_t qsElist;        /* List of entities for which info is requested. */
                        /* List of entities to simulate DOSQPROCSTAT behavior */
ulong_t qsPSList = QS_PROCESS | QS_SEMAPHORE | QS_MTE | QS_SHMEMORY;

KSEMMTX QsSem;          /* QuerySysState semaphore for exclusive runs */

extern  HPTREE  MFT_hPTree; /* handle to MFT PTree */

/***LP  qsAssert - cause internal error if assertion fails
 *
 *      Given a boolean, this procedure causes an internal error if
 *      the boolean is false.  Typically, the value of this boolean is
 *      derived from some test expression.
 *
 *      qsAssert (f)
 *
 *      ENTRY   f               - boolean value (int)
 *      RETURN  NONE
 *
 *      CALLS   _assert
 *
 *      WARNING This procedure is implemented as a macro.
 *
 *      EFFECTS NONE
 */
#ifdef  MISCSTRICT
#  define qsAssert(e) _assert(e)
#else
#  define qsAssert(e)
#endif

/***LP  VR32QuerySysState - Query System state information (API entry)
 *
 *      ENTRY
 *              sef     System Entry Frame
 *              elist           Designates what information is sought.
 *              elevel          Designates the level of informtion sought.
 *              pid             Process ID; 0 = all processes.
 *              tid             Thread ID; 0 = all threads.
 *              pulStateBuf     Pointer to user buffer
 *              cchStateBuf     Size of buffer in bytes
 *
 *      RETURNS
 *              NO_ERROR        Sucess
 *              ERROR_INVALID_PARAMETER
 *              ERROR_INVALID_LEVEL
 *              ERROR_BUFFER_OVERFLOW
 *              ERROR_PROTECTION_VIOLATION
 *
 *      Calls QSQuerySysState to do the work.
 */

ulong_t cdecl VR32QuerySysState(sef, elist, elevel, pid, tid, pulStateBuf, cchStateBuf)
THUNKSEF        sef;            // System Entry Frame
ulong_t         elist;          // List of system resources on which to report
ulong_t         elevel;         // Level of reporting per resource type
ulong_t         pid;            // Process to report on.
ulong_t         tid;            // Thread to report on.
uchar_t         *pulStateBuf;   // Buffer in which to return information
ulong_t         cchStateBuf;    // Size of Buffer in bytes
{
    return(QSQuerySysState(elist, elevel, pid, tid, pulStateBuf, cchStateBuf));
}

/***LP  QSQuerySysState - Query System state information (API entry)
 *
 *      This is the worker for the Dos32QuerySysState API
 *
 *      ENTRY
 *              elist           Designates what informtion is sought.
 *              elevel          Designates the level of informtion sought.
 *              pid             Process ID; 0 = all processes.
 *              tid             Thread ID; 0 = all threads.
 *              pulStateBuf     Pointer to user buffer
 *              cchStateBuf     Size of buffer in bytes
 *
 *      RETURNS
 *              NO_ERROR        Sucess
 *              ERROR_INVALID_PARAMETER
 *              ERROR_INVALID_LEVEL
 *              ERROR_BUFFER_OVERFLOW
 *              ERROR_PROTECTION_VIOLATION
 *
 *      QuerySysState loads system data structure information into a
 *      user supplied buffer based on the list of system entites for
 *      which data is requested.
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 */
ulong_t QSQuerySysState(elist, elevel, pid, tid, pulStateBuf, cchStateBuf)
ulong_t         elist;          // List of system resources on which to report
ulong_t         elevel;         // Level of reporting per resource type
ulong_t         pid;            // Process to report on.
ulong_t         tid;            // Thread to report on.
uchar_t         *pulStateBuf;   // Buffer in which to return information
ulong_t         cchStateBuf;    // Size of Buffer in bytes
{
        register int    i;
        ulong_t         rc = NO_ERROR;

        /*
         * Mask off any unsupported system resource entities and
         * return an error if the list is then empty.
         */
        if ( (elist &= QS_SUPPORTED) == 0)
                return (ERROR_INVALID_PARAMETER);

        /*
         * Serialize access to this code.
         */
        rc = KSEMRequestMutex((HKSEM)&QsSem, -1); /*75959 capture return code*/
        qsAssert(rc == 0);                        /*75959 panic if lock not gotten*/

#ifndef  MISCSTRICT                             //  121979
         if(rc != NO_ERROR)                     //  121979
                  return rc;                    //  121979
#endif                                          //  121979

        pEndStateBuf = pulStateBuf + cchStateBuf;
        pRecStart = pCurStateBuf = pulStateBuf;
        if ( (rc = qsInitCache()) != NO_ERROR)
                //return(rc);                       121979
                goto errsem2;                   //  121979


        /* Obtain global information first */
        rc = qsGlobal();
        if(rc != NO_ERROR) //103741
                 goto errsem;   //103741

        qsElist = elist;        /* save original elist value */

        /*
         * Take system sempahores where appropriate
         */
        if ( qsElist & QS_MTE )  {
                rc = LDRGetSem();      /*  75959 Check if lock gotten*/
                if (rc!=0) goto errsem;/*  75959  if not goto errsem*/
        }
        if ( qsElist & QS_SHMEMORY )  {
             while (KSEMRequestExclusive(vmNamShrSem, -1) == ERROR_INTERRUPT);
        }
        /*
         * Check each bit in elist and call through the function
         * table to obtain information about entities for which
         * bits are set. The values for the entity defines (QS_*)
         * are ordered so that the most commonly used have lower
         * values.
         */
        for (i = 0; elist && rc == NO_ERROR; elist >>= 1, i++) {
            if (elist & 0x01)
                rc = (*qsFunc[i]) (elevel, pid);
        }

        /*
         * Clear system sempahores where appropriate
         */
        if ( qsElist & QS_SHMEMORY )
                KSEMRelease(vmNamShrSem);
        if ( qsElist & QS_MTE )
                LDRClearSem();


errsem:;                                    /*75959 come here if ldrgetsem    */
                                            /*75959 couldn't get lock         */
        qsFreeCache();
errsem2:                                   // qsFreeCache() not needed; 121979
        KSEMReleaseMutex((HKSEM)&QsSem);       /* (24768) free cache before */
        return(rc);                            /*    you release semaphore  */
}

/***LP  qsGlobal - Obtain global system information.
 *
 *      TODO if there's no more to this routine, cut it.
 *      void qsGlobal ()
 *
 *      ENTRY   NONE
 *
 *      EXIT
 *
 *      MODIFIES        pCurCache
 *
 */
ulong_t qsGlobal()
{
        register qsGrec_t *     pGlobalRec;
        ulong_t                 rc;

        /*
         * Make sure there's room in the buffer for this record
         */
        if ( (rc = qsCheckCache(sizeof(qsGrec_t))) != NO_ERROR)
                return(rc);
        pGlobalRec = (qsGrec_t *) pCurCache;
        /*
         * Set the header structure pointer to this record type
         */
        pPtrRec->pGlobalRec = (qsGrec_t *) pRecStart;

        pGlobalRec->cThrds = ctcbTKInUse;
        pGlobalRec->c32SSem = ulShTotUsed;
        pGlobalRec->cMFTNodes = MFT_hPTree->pth_cNodes;

        qsUpdatePtrs((uchar_t *) ++pGlobalRec);

        return(NO_ERROR);
}

/***LP  qsProcess - Obtain per process information
 *
 *      ulong_t qsProcess (level, pid)
 *
 *      ENTRY   level   - Level of information requested
 *
 *      EXIT    ulong_t - Standard error code
 *
 */
ulong_t qsProcess(level, pid)
ulong_t level;
ulong_t pid;
{
        ptda_t  *pptda;
        ulong_t rc;

        /* Set the header structure pointer to this record type */
        pPtrRec->pProcRec = (qsPrec_t *) pRecStart;

        if (pid == 0) {         /* Get info for all processes */

            /* Get Sysint's process information separately; TKScanTasks will
             * skip it since it is marked as DYING
             */
            /* This was commented out to allow the first process
             * information from being repeated twice
             */
/*vlr*/ /*  if ( (rc = qsReadPtda(pPTDAFirst, (void *) NULL)) != NO_ERROR)
                return(rc); */

            return(TKScanTasks(TK_TG_ALL|TK_SF_WORKER|TK_SF_WRETCODE, 0,
                                qsReadPtda,(void *)NULL));
        }
        /* Information is requested only for the given PID */
/*        TKPidToPTDA((ushort_t) pid, SSToDS(&pptda));  */           /*145311*/

         if ( (rc = TKPidToPTDA((ushort_t) pid, SSToDS(&pptda)) ) != NO_ERROR) /*145311*/
            return (rc);                                                       /*145311*/

        return(qsReadPtda(pptda, (void *) NULL));
}

/***LP  qsReadPtda - Worker routine for TKScanTasks
 *
 *      ulong_t qsReadPtda (pPTDA, pArg)
 *
 *      Copies all per process information into cache.
 *      WATCH time involved. Can't really yield.
 *
 *      ENTRY   pPTDA   - Pointer to ptda
 *              pArg    - Unused
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pRecStart, pCurCache
 *
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 *
 */

ulong_t qsReadPtda(pPTDA, pArg)
ptda_t  *pPTDA;
void    *pArg;
{
        qsPrec_t ResProcRec;    /* process info structure */
        uchar_t *pResProcRec;   /* ptr to space reserved for struct in buffer */
        register qsPrec_t *pProcRec = SSToDS(&ResProcRec);
        ulong_t         rc;

        if ( (rc = qsReserveCache(SSToDS(&pResProcRec), sizeof(qsPrec_t)))
                        != NO_ERROR)
                return(rc);
        memset(pProcRec, 0, sizeof(qsPrec_t));

        /*
         * Fill query api's process structure from ptda
         */
        pProcRec->RecType = QS_PROCESS;
        pProcRec->pid = pPTDA->ptda_Pid;
        pProcRec->ppid = pPTDA->ptda_Ppid;
        pProcRec->type = pPTDA->ptda_ProcType;
        pProcRec->stat = pPTDA->ptda_ProcStatus;
        pProcRec->sgid = pPTDA->ptda_CurScrnGrp;
        pProcRec->hMte = pPTDA->ptda_module;
        pProcRec->cTCB = pPTDA->ptda_cTCB;
        pProcRec->cFH = pPTDA->JFN_Length;
        pProcRec->c32PSem = pPTDA->ulPrTotUsed;

        /*
         *      Store per process semaphore information in buffer
         *      if information is requested for semaphores.
         */
        if ( qsElist & QS_SEMAPHORE )
                if ( (rc = qsProcSem(pProcRec, pPTDA)) != NO_ERROR)
                        return(rc);

        /*
         *      Store per process module information in buffer
         *      if information is requested for library modules.
         */
        if ( qsElist & QS_MTE )
                if ( (rc = qsProcLib(pProcRec, pPTDA)) != NO_ERROR)
                        return(rc);
        /*
         *      Store per process shared memory information in buffer
         *      if information is requested for shared memory.
         */
        if ( qsElist & QS_SHMEMORY )
                if ( (rc = qsProcShrMem(pProcRec, pPTDA->ptda_handle) )
                                != NO_ERROR)
                        return(rc);

        /*
         *      Store per process file information in buffer
         *      if information is requested for file systems.
         */
        if ( qsElist & QS_FILESYS )
                if ( (rc = qsProcFS(pProcRec, pPTDA)) != NO_ERROR)
                        return(rc);

        /*
         *      Store thread information in buffer
         */
        rc = qsProcThrd(pProcRec, pPTDA);

        /*
         *      Flush process record structure out to user buffer
         */
       if(rc == NO_ERROR)   // 103741
           rc =             // 103741
           qsFlushResCache(pResProcRec,(uchar_t *)pProcRec,sizeof(qsPrec_t));

       return(rc);
}

/***LP  qsProcSem - Copy per process semaphore information into buffer
 *
 *      void qsProcSem (pProcRec, pPTDA)
 *
 *      NOTE: Grabbing 16 bit sems would be more efficient if a count were kept.
 *      Julie is investigating this.
 *
 *      ENTRY   pProcRec  - Pointer to beginning of process record in buffer.
 *              pPTDA     - Pointer to ptda
 *
 *      EXIT
 *
 *      MODIFIES        pRecStart, pCurCache
 *
 *
 */
ulong_t qsProcSem(pProcRec, pPTDA)
qsPrec_t        *pProcRec;
ptda_t          *pPTDA;
{
        register PPRUN     p32SemRec;
        register ushort_t *p16SemRec;
        register PSEM      pSem;        /* ptr to a 32 bit semaphore record */
        ulong_t            ulTblCt = 0; /* for 32 bit semaphore info dump */
        ulong_t            count, TotUsed;
        ulong_t            rc;
        ushort_t           i;


        /* Copy 32 bit semaphore info out to buffer */
        /* TODO Reinsert code when fully tested */
        pProcRec->p32SemRec = p32SemRec = NULL; /*BUG TMP TEST */

        /* Copy 16 bit semaphore indices out to buffer
         * NOTE that the conversion of index to SysSemPtr is not
         * done here to save time in the kernel. The conversion
         * is done in stateview as follows:
         *      ( index * sizeof(SysSemTblStruc) ) + &SysSemDataTable
         */
                    /* put sem index array following ProcRec structure */
        if (p32SemRec != NULL)
                qsUpdatePtrs((uchar_t *) p32SemRec);
        pProcRec->p16SemRec = (ushort_t *) pRecStart;
        if ( (rc = qsCheckCache(SYSSEM_MAXIMUM * sizeof(ushort_t))) != NO_ERROR)
                return(rc);
        p16SemRec = (ushort_t *)pCurCache;

        for (i = 0, count = 0; i < SYSSEM_MAXIMUM; i++) {
                                            /* if valid handle */
            if (pPTDA->SysSemPTDATbl.OpenSemTable[i] != 0) {
                    *p16SemRec++ = i;
                    count++;
            }
        }
        pProcRec->c16Sem = (ushort_t) count;
        if (count == 0)
                pProcRec->p16SemRec = (ushort_t *) NULL;
        else
                qsUpdatePtrs((uchar_t *) p16SemRec);

        return(NO_ERROR);
}

/***LP  qsProcLib - Copy per process list of runtime libraries into buffer
 *
 *      ulong_t qsProcLib (pProcRec, pPTDA)
 *
 *      ENTRY   pProcRec  - Pointer to beginning of process record in buffer.
 *              pPTDA     - Pointer to ptda
 *
 *      EXIT    ulong_t - Standard error code
 *
 */
ulong_t qsProcLib(pProcRec, pPTDA)
qsPrec_t        *pProcRec;
ptda_t          *pPTDA;
{

        /* Set the process structure pointer to start of per process lib info */
        pProcRec->pLibRec = (ushort_t *) pRecStart;

        return (LDRProcMteInfo(pProcRec, pPTDA->ptda_pldrdldHead));

}

/***LP  qsProcShrMem - Copy per process list of shared memory handles
 *                      into buffer
 *
 *      ulong_t qsProcShrMem (pProcRec, pptda_handle)
 *
 *      ENTRY   pProcRec  - Pointer to beginning of process record in buffer.
 *              ptdahandle - pPTDA->ptda_handle
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pCurCache
 *
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 */
ulong_t qsProcShrMem(pProcRec, ptdahandle)
qsPrec_t        *pProcRec;
ushort_t        ptdahandle;
{
        ulong_t rc;

        /* Copy shared memory info to buffer */
        /* Set the process struc pointer to start of per process shmem info */
        pProcRec->pShrMemRec = (ushort_t *) pRecStart;

        rc = qs16ShrMem(pCurCache, ptdahandle);

        /* cLib was passed back in the buffer at updated pCurCache */
        if(rc == NO_ERROR)   //103741
            pProcRec->cShrMem = *pCurCache;

        return(rc);
}

/***LP  qsProcFS - Copy per process list of file handles into buffer
 *
 *      void qsProcFS (pProcRec, pPTDA)
 *
 *      ENTRY   pProcRec  - Pointer to beginning of process record in buffer.
 *              pPTDA     - Pointer to ptda
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pCurCache
 *
 */
ulong_t qsProcFS(pProcRec, pPTDA)
qsPrec_t        *pProcRec;
ptda_t          *pPTDA;
{
        register ushort_t       *pFSRec;
        register ushort_t       *pJFN_Table;
        register int            handle;
        ulong_t                 rc;
        ulong_t                 tmp;

        /*
         * Make sure there's room for this process' file info in cache
         */
        if ( (rc = qsCheckCache(pPTDA->JFN_Length * sizeof(ushort_t)))
                                != NO_ERROR)
                return(rc);

        pFSRec = (ushort_t *)pCurCache;

#if 0           // comment old code for 145345 changes
        /* Dump the JFN table if the pointer to it is valid */

        if ( (tmp = SELConvertToLaddr(pPTDA->ThisPTDA)) != -1)
        {
                pProcRec->pFSRec = (ushort_t *) pRecStart;
                pJFN_Table = (ushort_t *) ((uchar_t *) pPTDA +
                                (OFFSETOF16(pPTDA->JFN_pTable) - TKOPTDA));

                /* Copy SFNs for each handle out to user buffer */
                for (handle = 0; handle < pPTDA->JFN_Length; handle++)
                        *pFSRec++ = *pJFN_Table++;
                qsUpdatePtrs((uchar_t *) pFSRec);
        }
#else           // new code for 145345

       if (pPTDA->JFN_Length == FILPERPROC )
            pJFN_Table = (ushort_t *) ((uchar_t *) pPTDA +
                            (OFFSETOF16(pPTDA->JFN_pTable) - TKOPTDA));
       else
       {
           pJFN_Table =  SELConvertToLinear(
                                (ULONG)(SEGMENTOF16(pPTDA->JFN_pTable)),pPTDA);
           if (pJFN_Table == (ulong_t)-1)   // error return
           return ERROR_INVALID_SELECTOR;
        }

        // Copy SFNs for each handle out and get counter data block
        //
        for (handle = pPTDA->JFN_Length; handle > 0; handle--, pJFN_Table++)
        {
            if (SFN_NULL != (short)*pJFN_Table )
                *pFSRec++ = *pJFN_Table;
        }

        qsUpdatePtrs((uchar_t *) pFSRec);
#endif          // new code for 145345

        return(NO_ERROR);
}

/***LP  qsProcThrd - Copy per process thread information into buffer
 *
 *      void qsProcThrd (pPTDA)
 *
 *      ENTRY   pProcRec  - Pointer to beginning of process record in buffer.
 *              pPTDA     - Pointer to ptda
 *
 *      EXIT
 *
 *      MODIFIES        pCurCache
 *
 *
 */
ulong_t qsProcThrd(pProcRec, pPTDA)
qsPrec_t        *pProcRec;
ptda_t          *pPTDA;
{
        register qsTrec_t *pThrdRec;
        register tcb_t    *ptcb;        /* ptr to thread control block */
        ushort_t           slot;
        ulong_t            rc = NO_ERROR;

        /* Set the header structure pointer to this record type */
        pProcRec->pThrdRec = (qsTrec_t *) pRecStart;

        /*
         * For each thread of this process, copy info to cache
         */
        for (ptcb = pPTDA->ptda_pTCBHead;ptcb != NULL;ptcb = ptcb->TCBpTCBNext){
                /* Check cache and flush if necessary */
                if ( (rc = qsCheckCache(sizeof(qsTrec_t))) != NO_ERROR)
                        return(rc);
                pThrdRec = (qsTrec_t *) pCurCache;

                pThrdRec->RecType = QS_THREAD;
                pThrdRec->tid = ptcb->TCBOrdinal;
                pThrdRec->slot = slot = ptcb->TCBNumber;
                pThrdRec->sleepid = (ulong_t) ptcb->TCBSleepID;
                pThrdRec->priority = TKGetPriority(ptcb);
                pThrdRec->systime = ptcb->TCBSysTime;
                pThrdRec->usertime = ptcb->TCBUserTime;
                pThrdRec->state = ptcb->TCBState;

                qsUpdatePtrs((uchar_t *) ++pThrdRec);
        }
        return(rc);
}

/***LP  qsSem   - Copy 16 and 32 bit system semaphore information into buffer
 *
 *      ulong_t qsSem (level, pid)
 *
 *      ENTRY   level   - Level of information requested
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pCurCache
 *
 */
ulong_t qsSem(level, pid)
ulong_t level;
ulong_t pid;
{
        register PSHUN       p32SysSem; /* pointer to system semaphore*/
        register ulong_t     ulTblCt;   /* counter to go thru tables */
        register ulong_t     ulUsedCt;  /* counter for # used spots in table */
        register qsS32rec_t *p32SemRec; /* pointer to cache */
        uchar_t             *pResSemRec;/* points to reserved buffer location */
        qsS32rec_t           ResSemRec;
        PMUXQ                pSysMuxQ;
        POPENQ               pSysOpenQ;
        PSEMRECORD           pSysSemRec;
        uchar_t             *pSysName;
        uchar_t             *pSemRec;

        ulong_t          rc;

        /*
         * Make sure there's room in the buffer for the bare minimum for
         * 16 bit semaphore record type
         */
        if ( (rc = qsCheckCache(sizeof(qsS16Headrec_t))) != NO_ERROR)
                return(rc);

        /* Set the header structure pointer to this record type */
        pPtrRec->p16SemRec = (qsS16Headrec_t *) pRecStart;

        /* dump 16-bit semaphore info into cache */
        rc = qsSys16Sem();

        if (rc != NO_ERROR)
                return(rc);

        /* dump 32-bit semaphore info into cache */

        /* TODO Reinsert code when fully tested */
        pPtrRec->p32SemRec = (qsS32rec_t *)NULL; /* BUG BUG TMP TEST */

        return(rc);
}

/***LP  qsLib   - System wide MTE information
 *
 *      ulong_t qsLib (level, pid)
 *
 *      NOTE LDRSysMteInfo checks for buffer overflow. Change if use macro
 *
 *      ENTRY   level   - Level of information requested. If the level bit
                         is set, dump per object information.
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pRecStart, pCurCache
 *
 */
ulong_t qsLib(level, pid)
ulong_t level;
ulong_t pid;
{
        ulong_t rc;

        /* Set the header structure pointer to this record type */
        pPtrRec->pLibRec = (qsLrec_t *) pRecStart;

        rc = LDRSysMteInfo(level);

        return(rc);
}

/***LP  qsMem   - System wide Shared Memory information
 *
 *      ulong_t qsMem (level, pid)
 *
 *      ENTRY   level   - Level of information requested.
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pRecStart, pCurCache
 *
 */
ulong_t qsMem (level, pid)
ulong_t level;
ulong_t pid;
{
        ulong_t rc;

        /* Set the header structure pointer to this record type */
        pPtrRec->pMemRec = (qsMrec_t *) pRecStart;

        rc = qsMemory(level,pid);

        return(rc);
}

/*
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 */
ulong_t qsFile(level, pid)
ulong_t level;
ulong_t pid;
{
        ulong_t         *pEndFSRec;
        ulong_t         rc;

        /* Set the header structure pointer to this record type */
        pPtrRec->pFSRec = (qsFrec_t *)pRecStart;

        rc = PTWalk(MFT_hPTree, qsReadMFT);
        if(rc != NO_ERROR)      //103741
            return rc;     //103741

        /* Mark the end of the list of FS records */
        pEndFSRec = (ulong_t *) pCurCache;
        *pEndFSRec++ = QS_END;
        qsUpdatePtrs( (uchar_t *) pEndFSRec);

        return(rc);

}

/***LP  qsReadMFT - Worker routine for PTWalk
 *
 *      ulong_t qsReadMFT (pPTNode)
 *
 *      Copies all per file information into cache.
 *
 *      ENTRY   pPTNode
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES        pRecStart, pCurCache
 *
 *
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 */
ulong_t qsReadMFT(hPTree, pPTNode)
HPTREE  hPTree;
HPTKEY pPTNode;
{
        qsFrec_t ResFSRec;      /* file system info structure */
        uchar_t *pResFSRec;     /* ptr to space reserved for struct in buffer */
        register qsFrec_t       *pFSRec = SSToDS(&ResFSRec);
        register sf_entry       *pSFT;
        register uchar_t        *pFSName, *pMFTName;
        qsSft_t                 *pFSSft;
        ulong_t                 ctSft = 0;
        ulong_t                 tmp;
        ulong_t                 rc;

        if ( (rc = qsReserveCache(SSToDS(&pResFSRec), sizeof(qsFrec_t)))
                        != NO_ERROR)
                return(rc);
        memset(pFSRec, 0, sizeof(qsFrec_t));

        pFSRec->RecType = QS_FILESYS;

        /* Check cache and flush if necessary */
        pMFTName = (uchar_t *) &((MFT_Entry *)pPTNode)->mft_name;
        if ( (rc = qsCheckCache(strlen(pMFTName))) != NO_ERROR)
                return(rc);

        pFSName = pCurCache;
        while (*pFSName++ = *pMFTName++)
                ;
        qsUpdatePtrs(pFSName);
        pFSRec->pSft = (qsSft_t *) pRecStart;

        pSFT = (sf_entry *) GET032OF1616(((MFT_Entry *)pPTNode)->mft_sptr, tmp);
        while ( pSFT != (sf_entry *) -1 ) {
                /* Check cache and flush if necessary */
                if ( (rc = qsCheckCache(sizeof(qsSft_t))) != NO_ERROR)
                        return(rc);
                pFSSft = (qsSft_t *) pCurCache;
                ctSft++;

                pFSSft->sfn = pSFT->sf_fsi.sfi_selfsfn;
                pFSSft->refcnt = pSFT->sf_ref_count;
                pFSSft->flags = pSFT->sf_flags;
                pFSSft->flags2 = pSFT->sf_flags2;
                pFSSft->mode = pSFT->sf_fsi.sfi_mode;
                pFSSft->mode2 = pSFT->sf_fsi.sfi_mode2;
                pFSSft->size = pSFT->sf_fsi.sfi_size;
                pFSSft->hVPB = pSFT->sf_fsi.sfi_hVPB;
                pFSSft->attr = (ushort_t) pSFT->sf_fsi.sfi_DOSattr;

                pSFT = (sf_entry *) GET032OF1616(pSFT->sf_chain, tmp);
        }

        qsUpdatePtrs((uchar_t *) ++pFSSft);

        pFSRec->ctSft = ctSft;
        pFSRec->pNextRec = pRecStart;

        /*
         *      Flush file system record structure out to user buffer
         */
        if(rc == NO_ERROR) //103741
            rc =           //103741
            qsFlushResCache(pResFSRec,(uchar_t *)pFSRec,sizeof(qsFrec_t));

        return(rc);
}

ulong_t qsDisk(level, pid)
ulong_t level;
ulong_t pid;
{
        return(ERROR_INVALID_PARAMETER);
}

ulong_t qsHWConfig(level, pid)
ulong_t level;
ulong_t pid;
{
        return(ERROR_INVALID_PARAMETER);
}


ulong_t qsNamedPipe(level, pid)
ulong_t level;
ulong_t pid;
{
        return(ERROR_INVALID_PARAMETER);
}


ulong_t qsNotSupported(level, pid)
ulong_t level;
ulong_t pid;
{
        return(ERROR_INVALID_PARAMETER);
}

/***LP  qsInitCache - Initialize buffer cache and pointers
 *
 *
 *      ENTRY   None
 *
 *      EXIT    ulong_t                 - return code
 *
 *      EFFECTS                         - pBeginCache, pEndCache, pCurCache
 *                                        pResPtrRec, pRecStart
 */
ulong_t qsInitCache()
{
        struct vmac_s ac;
        ulong_t rc;

/* 75959 Get cache from vmallocmem call rather than from kernel heap         */

// 75959  if ( (rc = allocKRHB(CACHESIZE, QSCACHETMPOWNER, &pBeginCache))
// 75959                  != NO_ERROR)
          rc = VMAllocMem(0x2000,
                          0x1000,
                          VMAC_ALIGNPAGE | PG_RESIDENT | PG_R | PG_W,
                          HOBNULL,
                          QSCACHETMPOWNER,
                          HOBNULL,
                          0,
                          0,
                          SSToDS(&ac));

          if (rc != 0) return(rc);

          pBeginCache = ac.ac_va;

        memset(pBeginCache, 0, CACHESIZE);
        pEndCache = pBeginCache + CACHESIZE;
        pCurCache = pBeginCache;
        /*
         * pPtrRec points to a structure that will be the first structure
         * in the user buffer containing pointers to the heads of the record
         * lists to follow. Reserve space (pointed to by pResPtrRec) in
         * the user buffer for this structure.
         */
        if ( (rc = qsReserveCache(&pResPtrRec, sizeof(qsPtrRec_t)))
                        != NO_ERROR)
                return(rc);
        memset(pPtrRec, 0, sizeof(qsPtrRec_t));
        return(NO_ERROR);
}

/***LP  qsFreeCache - Free cache
 *
 *      qsFreeCache flushes the cache and the reserved cache for the first
 *      structure in the buffer (the header of pointers) and then frees the
 *      memory associated with the cache.
 *
 *      ENTRY   NONE
 *
 *      EXIT    ulong_t                 - return code
 *
 *
 *      History:
 *        12/05/94 Scott Garfinkle  103741  Check for return codes from
 *                 (Debra Hargett)          function calls
 */
ulong_t qsFreeCache()
{
        ulong_t rc;

        rc =               //103741
        qsFlushResCache(pResPtrRec, (uchar_t *) pPtrRec, sizeof(qsPtrRec_t));
        if(rc != NO_ERROR) //103741
                 return rc;     //103741
        rc = qsFlushCache();
        if(rc != NO_ERROR) //103741 (better to have some exposure for a
            return rc;     //103741 memory leak than for heap corruption)
/*  Return memory back to system rather than from kernel heap     */
//      freeKRHB(pBeginCache);
        VMFreeMem(pBeginCache,0,0);
        return(rc);
}

/***LP  qsCheckCache - Check for cache overflow
 *
 *      qsCheckCache checks to see if the amount of data about to be written
 *      to the cache will fit into the cache. If not, the cache will be flushed
 *      and pointers reinitialized.
 *      qsCheckCache checks to see if the amount of data about to be written
 *      to the user buffer will fit in it.  If not, a buffer overflow occurs and
 *      pstat reallocates the buffer to a larger size and tries again.(B736831)
 *
 *      ENTRY   size                    - size in bytes to be written to cache
 *
 *      EXIT    ulong_t                 - return code
 *
 */
ulong_t qsCheckCache(size)
ulong_t size;
{
   ulong_t size2;          /* size in bytes to be written to user buffer */
                           /* potential information for cache (B736831)  */

        qsAssert(size <= CACHESIZE);
        if ( (pCurCache + size) >= pEndCache)
                return(qsFlushCache());
        else {                                                 /* B736831 */
           size2 = pCurCache - pBeginCache;                    /* B736831 */
           if ( (pCurStateBuf + size + size2) > pEndStateBuf)  /* B736831 */
                return(ERROR_BUFFER_OVERFLOW);                 /* B736831 */
        }                                                      /* B736831 */
        return(NO_ERROR);

}

/***LP  qsFlushCache - Flush cache to user buffer and reset pointers
 *
 *      ENTRY   NONE
 *
 *      EXIT    ulong_t                 - return code from TKSuBuff
 *
 */
ulong_t qsFlushCache()
{
        ulong_t rc;
        ulong_t size;

        size = pCurCache - pBeginCache;
        if ( (pCurStateBuf + size) > pEndStateBuf)
                return(ERROR_BUFFER_OVERFLOW);

        rc = TKSuBuff(pCurStateBuf, pBeginCache, size, 0);
        pCurCache = pBeginCache;
        pCurStateBuf += size;

        return(rc);
}

/***LP  qsReserveCache - Reserve space in user buffer for information
 *
 *      qsReserveCache updates the cache pointers so that space will be
 *      reserved properly in the user buffer for information that needs
 *      to be written out in the future.
 *
 *      ENTRY   ppResBuf                - address of ptr to user buffer
 *                                        where info will eventually be written
 *              size                    - size in bytes to be written to cache
 *
 *      EXIT    ulong_t                 - return code
 *
 */
ulong_t qsReserveCache(ppResBuf, size)
uchar_t **ppResBuf;
ulong_t size;
{
        ulong_t rc;

        if ( (rc = qsCheckCache(size)) == NO_ERROR)
        {
                *ppResBuf = pRecStart;
                pCurCache += size;
                pRecStart += size;
        }
        return(rc);
}

/***LP  qsFlushResCache - Write reserved cache to user buffer
 *
 *      ENTRY   pResBuf                 - place in user buffer to write info
 *              pInfo                   - data struct holding delay write info
 *              size                    - size of data in bytes
 *
 *      EXIT    ulong_t                 - return code from TKSuBuff
 *
 *      If the reserved space is still valid in the cache (i.e. the cache has
 *      not been flushed since the space was reserved) just fill in the
 *      cache. Otherwise write the info directly to the user buffer.
 */
void qsFlushResCache(pResBuf, pInfo, size)
uchar_t *pResBuf;
uchar_t *pInfo;
ulong_t size;
{
        uchar_t *pCache;

        if (pResBuf >= pCurStateBuf) {  /* Cache location still valid */
                pCache = pBeginCache + (pResBuf- pCurStateBuf);
                while ( size-- )
                        *pCache++ = *pInfo++;
        }
        else                            /* Write out to user buffer */
                TKSuBuff(pResBuf, pInfo, size, 0);
}

/***LP  qsUpdatePtrs - Update Cache and User buffer pointers
 *
 *      qsUpdatePtrs updates pRecStart - which points to the beginning of the
 *      next record location in the user buffer. pNewCache points to the end of
 *      a record just placed into the cache.
 *
 *      ENTRY   pNewCache               - New location in cache
 *
 *      EXIT    NONE
 *
 *      MODIFIES                pCurCache, pRecStart
 */
void qsUpdatePtrs(pNewCache)
uchar_t *pNewCache;
{
        pRecStart = pRecStart + (pNewCache - pCurCache);
        pCurCache = pNewCache;
}
