/*static char *SCCSID = "@(#)logcalls.c 6.6 91/07/25";*/
/*static char *SCCSID = "@(#)logcalls.c 6.6 91/07/25";*/
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS **********************
*
*   SOURCE FILE NAME:  LogCalls.c
*
*   DESCRIPTIVE NAME:  LogCalls Dynalink Library
*
*   STATUS: RELEASE 1 LEVEL 0
*
*   FUNCTION: Provides a two way communication between the logging
*             facility and sub-systems.
*
*   ENTRY POINTS: DosLogRegister
*                 DosLogRead
*
*   INTERNAL REFERENCES:
*      DATA:            Log_Pipe_Nm = '\PIPE\LOGDATA\JGRENBLF'
*
*   EXTERNAL REFERENCES:
*      ROUTINES:        DosOpen
*                       DosWaitNmPipe
*                       DosOpenSem
*                       DosWrite
*                       DosRead
*                       DosSetNmPipeSem
*                       DosCloseSem
*                       DosClose
*                       DosPeekNmPipe
*                       EnterDLL
*                       ExitDLL
*
**************************** END OF SPECIFICATIONS **********************/

#define INCL_DOSERRORS
#define INCL_NOXLATE_DOS16
#define INCL_DOSNMPIPES
#define INCL_DOSMODULEMGR
#define INCL_DOSSEMAPHORES
#define INCL_DOSDEVICES                                     /* CP20 DCR 1152 */
#ifndef INCL_OS2STD
#define INCL_OS2STD                    /* need to include defs for NULL, etc */
#endif
#include <os2.h>

#include <infoseg.h>
#include <dc1mmph.h>

#include <sysbits.h>                              /* added for CP20 DCR 1152 */

/*   ERR_TEMPLATE_NOT_PRESENT  -  templ_off > buf length       */
/*   ERR_IN_TEMPL_CNT          -  num string > max allowed     */
/*   ERR_STRING_CNT_EQ_ZERO    -  num strings == 0             */
/*   ERR_STRING_CNT_MISMATCH   -  string cnt != num strings    */
/*   ERR_REC_LEN_TO_LARGE      -  input record to long         */
/*   ERR_LF_BAD_SEMAPHORE      -  DosOpenSem failed            */
/*   ERR_LF_BAD_RESV_AREA      -  resv area not zeros          */

#define ERR_TEMPLATE_NOT_PRESENT     ERROR_LF_INVALID_TEMPL
#define ERR_IN_TEMPL_CNT             ERROR_LF_INVALID_TEMPL
#define ERR_STRING_CNT_EQ_ZERO       ERROR_LF_INVALID_TEMPL
#define ERR_STRING_CNT_MISMATCH      ERROR_LF_INVALID_TEMPL
#define ERR_REC_LEN_TO_LARGE         ERROR_LF_GENERAL_FAILURE
#define ERR_LF_BAD_SEMAPHORE         ERROR_LF_GENERAL_FAILURE
#define ERR_LF_BAD_RESV_AREA         ERROR_LF_GENERAL_FAILURE

#define NOERROR                        0    /* 0 return code                */
#define MAX_NUM_FORMAT_STRINGS        16    /* max number of format strings */
#define INDEFINITE_WAIT               -1    /* for DosWaitNmPipe            */
#define KEY_HANDLE                    28    /* for DosSetNmPipeSem          */
#define MAX_RECORD_LENGTH           1024    /* max length in LogRegList     */

extern  long pascal ENTER_DLL(void);   /* PTR B708481 changed from far call */
extern  pascal EXIT_DLL(unsigned int); /* PTR B708481 changed from far call */

/***************************************************/
/* Logging Defines Added for CP20 DCR 1152         */
/***************************************************/
#define ERRLOG_SERVICE        1L
#define ERRLOG_VERSION        1
#define IOCTL_LOGGING             0x000C
#define LOGGING_REGISTER          0x004B


/************************** START OF SPECIFICATIONS **********************
*
*   SUBROUTINE NAME:   DosLogRegister
*
*   DESCRIPTIVE NAME:  Register with OS/2 Log Facility
*
*   FUNCTION: The DosLogRegister function provides the interface for
*             subsystems to register with the system logging facility,
*             placing that subsystem on a notification list.  Also, log
*             record layouts can be passed to the logging facility to
*             allow formatting of unique records by the SYSLOG command.
*             Subsystem notification is accomplished via system semaphores
*             with the pertinent data available through a separate API
*             (see DosLogRead).
*
*   ENTRY POINT:  DosLogRegister
*
*   PARAMETERS:
*      INPUT:  PUSH@   WORD    LogHandle       ; log handle (returned)
*              PUSH@   OTHER   LogRegList      ; address of registry list
*              PUSH@   WORD    RequestID       ; address of requested ID
*
*      Where:
*
*         LogHandle:  is the address of the word that the system
*                     will return a log handle in.
*
*         LogRegList: is the address of the log registry buffer.
*
*         RequestID:  is the address of the word that the system fill
*                     in with a record ID generated by the Logging
*                     facility.  This is only used if the REC_ID in the
*                     LogRegList is a -1.
*
*         The format of the LogRegList is as follows:
*
*          LENGTH      DW     ?       ;length of data (including itself)
*          RESV        DW     0       ;reserved
*          REC_ID      DW     ?       ;record ID
*          TEMPL_OFF   DW     ?       ;offset of template
*          SEM_NAME    DB     ?       ;semaphore name string  (ASCIIZ)
*
*      1. REC_ID contains the ID to be registered for.  If this field
*          is set to a -1 (0FFFFH),  a REC_ID is generated by the Logging
*          Facility and returned in the word pointed to by REQUEST_ID.
*
*      2. The system semaphore is used by the logging facility to clear
*          the subsystems thread  when a log entry is detected that the
*          subsystem has been registered for.  If the value of  SEM_NAME
*          is NULL, no notification will be performed.
*
*      3. The offset to the templates is the offset from the beginning
*          of data passed.
*
*   EXIT-NORMAL:    AX = 0       No error.
*
*   EXIT-ERROR:     AX = Error Code
*
*           Possible errors:    Facility unavailable
*                               Registration succeeded, template area full
*                               Packet (record) ID in use
*                               Invalid template detected
*                               Registration failed (general failure)
*                               No more IDs available
*                               Invalid ID
*                               ERROR_TOO_MANY_OPEN_FILES
*                               ERROR_TOO_MANY_SEMAPHORES
*                               ERROR_SEM_NOT_FOUND
*                               ERROR_SEM_USER_LIMIT
*
*   EFFECTS:   none
*
*   INTERNAL REFERENCES:
*      DATA:            Log_Pipe_Nm = '\PIPE\LOGDATA\JGRENBLF'
*
*   EXTERNAL REFERENCES:
*      ROUTINES:        DosOpen
*                       DosWaitNmPipe
*                       DosOpenSem
*                       DosWrite
*                       DosRead
*                       DosSetNmPipeSem
*                       DosCloseSem
*                       DosClose
*                       EnterDLL
*                       ExitDLL
*
**************************** END OF SPECIFICATIONS **********************/

const char Log_Pipe_Nm[] = "\\PIPE\\LOGDATA\\JGRENBLF";

USHORT far pascal DOSLOGREGISTER(USHORT far *LogHandle,
                                 char far   *LogRegList,
                                 USHORT far *RequestID)
{

  USHORT rc;            /* return code                                  */
  HFILE  hLogDD ;       /* (DosOpen) handle to LOG$ device driver(2.0)  */
  HPIPE  hPipe ;        /* (DosOpen) named pipe handle                  */
  USHORT action_taken;  /* (DosOpen) action taken                       */
  USHORT bytes_read;    /* (DosRead) number bytes actual read           */
  USHORT fpState ;      /* Pipe state                                   */

  LONG   sem_handle;        /* semaphore handle                         */
  ULONG  Parmlist[3] ;      /* DosDevIOCtl parmlist for LOG$            */
  char far *pTmp ;          /* temp ptr for semaphore name              */


  struct {                    /* structure returned by the logdaem        */
     USHORT   r_rc;
     USHORT   requestid;
  } rtn_data;

  struct log_list_struc {     /* cast pointer for LogRegList              */
     USHORT length;
     USHORT resv;
     USHORT rec_id;
     USHORT templ_off;
     char     data;
  }  far *l_ptr;

  ENTER_DLL();

/******************   code starts here   *******************/

  MMPHPreDOSLogRegister()                      /* Performance Hook (1C08) */

  l_ptr = (struct log_list_struc far *) LogRegList;

  *RequestID = 0;     /* touch callers parms: we do not want to fault     */
  *LogHandle = 0;     /*    after opening the pipe                        */

  sem_handle = NULL ; /* init semaphore handle to no notification         */

  rc = NOERROR ;
  /*
   *   Test Resv for being 0
   */
  if (l_ptr->resv !=  0)
  {
     rc = ERR_LF_BAD_RESV_AREA;
  }
  else
   /*
    *   Test caller's buffer length against the maximum record length
    */
   if (l_ptr->length > MAX_RECORD_LENGTH)
   {
      rc = ERR_REC_LEN_TO_LARGE;
   }
   else
     /*
      *   Test Rec_id for being 0, invalid if it is
      */
     if (l_ptr->rec_id == 0)
     {
        rc = ERROR_LF_INVALID_ID;
     }
     else
       /*
        *  Test semaphore name: valid values are NULL or an already
        *      created semaphore name
        */
       if (l_ptr->data != NULL)
       {
          rc = DosOpenSem((PHSEM) &sem_handle, (char far *) LogRegList + 8);
       }


  if ( (rc == NOERROR) && (sem_handle != NULL) )
  {
    /**
     ** Tell logging facility that someone wants to register for notification
     **/
    rc = DosOpen  ( "LOG$"       ,                              /* file name */
                    &hLogDD      ,                            /* file handle */
                    &action_taken,                           /* action taken */
                    0L           ,                              /* file size */
                    FILE_NORMAL   ,                        /* file attribute */
                    FILE_OPEN     ,                            /* open flags */
                    OPEN_ACCESS_READWRITE |                     /* open mode */
                    OPEN_SHARE_DENYNONE   |
                    OPEN_FLAGS_FAIL_ON_ERROR ,
                    0L           );                              /* reserved */
    if (rc != NO_ERROR)
      rc = ERROR_LF_NOT_AVAIL;
    else
    {
      Parmlist[0] = ERRLOG_SERVICE;                      /* Logging class */
      pTmp        = &l_ptr->data ;                      /* Semaphore name */
      Parmlist[1] = (ULONG)pTmp ;                       /* Semaphore name */
      Parmlist[2] = l_ptr->rec_id ;                      /* Registered ID */
      rc =  DosDevIOCtl( NULL,                           /* Data ptr      */
                         &Parmlist ,                     /* Parmlist      */
                         LOGGING_REGISTER,               /* function code */
                         IOCTL_LOGGING,                  /* category      */
                         hLogDD ) ;                      /* DD filehandle */
      if (rc != NO_ERROR)
        rc = ERROR_LF_NOT_AVAIL;
      DosClose(hLogDD) ;                        /* Close connection to LOG$ */
    }
    if (rc == NO_ERROR)
    {
      /*  Open pipe: if rc = Error_Pipe_Busy : wait for instance
       *                   = Error_Bad_Pipe  : logging not active, return error
       *                   =   : (Need handles)
       *                ERROR_TOO_MANY_OPEN_FILES
       */

       rc = ERROR_PIPE_BUSY;

       while (rc == ERROR_PIPE_BUSY)
       {
         rc = DosWaitNmPipe((char far *) Log_Pipe_Nm     ,
                            (long)       INDEFINITE_WAIT);
         if (rc == 0)
         {
             rc = DosOpen (
                            (PSZ)     Log_Pipe_Nm   ,    /* pipe name        */
                            (PHFILE)  &hPipe        ,    /* pipe handle      */
                                      &action_taken ,    /* action taken     */
                            (ULONG)   1024          ,    /* file size        */
                                      FILE_NORMAL   ,    /* file attribute   */
                                      FILE_OPEN     ,    /* open flags       */
                             OPEN_ACCESS_READWRITE |     /* open mode        */
                             OPEN_SHARE_DENYNONE   |
                             OPEN_FLAGS_FAIL_ON_ERROR ,
                                      0L            );   /* reserved         */
         }
       } /* end while pipe_busy */

       if (rc != NO_ERROR)
       {
          if (rc == ERROR_PATH_NOT_FOUND)
             /* Logging system is not active */
             rc = ERROR_LF_NOT_AVAIL;
       }
    }
    if (rc == NO_ERROR)
    {
      /**
       ** LogDaem should have written the record ID to use for
       **  this caller into the pipe.
       ** Read the information to verify pipe instance.
       **/
      rc = DosRead ((HFILE)          hPipe        ,      /* pipe handle */
                    (char far *)    &rtn_data     ,      /* buffer area */
                    (USHORT)         4            ,      /* buffer len  */
                    (PUSHORT)       &bytes_read  );      /* bytes read  */
      if (rc)
         rc = ERROR_LF_NOT_AVAIL;
      else
      {
        /**
         ** Reset pipe to NO_WAIT mode.  If DosRead called when no data
         **  written, it will return 0 for number bytes read.
         **/
        rc = DosQNmPHandState ((HPIPE) hPipe,               /* Pipe hdl     */
                               &fpState ) ;                 /* Pipe state   */
        if (rc == 0)
        {
          rc = DosSetNmPHandState ((HPIPE) hPipe,           /* Pipe hdl   */
                                   (fpState | NP_NOWAIT));  /* Pipe mode  */
        }
        rc = 0 ; /* @PEN if unable to set Pipe state, return OK ****/
        *RequestID = rtn_data.requestid;
      }
    }
  }

  if ( (rc == NOERROR) && (sem_handle == NULL) )
  {
     /**
      ** If caller wanted to get a record ID or register a template
      **   just give him back default record ID(-2)
      **/
     if (l_ptr->rec_id == -1)
       *RequestID = -2 ;
  }
  if ( sem_handle != NULL )
  {
    if (rc)
       DosClose ( hPipe );
    else
    {
       rc = DosSetNmPipeSem((HPIPE)          hPipe    ,
                            (HSEM)           sem_handle ,
                            (unsigned short) KEY_HANDLE );
       if (rc)
       {
          DosClose ( hPipe );
          rc = ERROR_LF_NOT_AVAIL;
       }
       else
       {
         /* return pipe handle */
         *LogHandle = hPipe ;
       }
    }
  }

  MMPHPostDOSLogRegister()                     /* Performance Hook (1C88) */
  return(EXIT_DLL(rc));
}

/************************** START OF SPECIFICATIONS **********************
*
*   SUBROUTINE NAME:   DosLogRead
*
*   DESCRIPTIVE NAME:  Read log entry
*
*   FUNCTION: The DosLogRead function provides the interface for
*             subsystems to obtain the log data that they have been
*             registered for This is a system call to be issued by a
*             subsystems thread after the semaphore it provided has been
*             cleared by the logging facility.
*
*   ENTRY POINT:  DosLogRead
*
*   PARAMETERS: PUSH   WORD   LogHandle   ; log handle
*               PUSH   WORD   Length      ; length
*               PUSH@  OTHER  LogBuffer   ; address of log record buffer
*               PUSH@  WORD   ReadSize    ; number of bytes read (returned)
*
*       Where:
*
*          LogHandle: is the word handle returned by the
*                     call to DosLogRegister.
*
*          Length:   is the word length of the log buffer.
*
*         LogBuffer: is the address of the log buffer where the log
*                     data is to be returned by the logging facility.
*
*          ReadSize:  is the word that the logging facility fills in
*                     with the number of bytes returned.  A zero returned
*                     here indicates there are no more log packets to be
*                     processed.
*
*       Only  a  single log packet will be returned  for each call.  Since
*       it is possible  for more  than  one  packet to be waiting  for the
*       subsystem to  process, the subsystem  should  continue to read log
*       entries  until  there  are  no more before going back into a  wait
*       state  on  the  semaphore  used  by the the logging  facility  for
*       wake-up purposes.
*
*   EXIT-NORMAL:    AX = 0       No error.
*
*   EXIT-ERROR:     AX = Error Code
*
*           Possible errors:  Buffer too small
*                             Invalid handle
*
*   EFFECTS:   none
*
*   INTERNAL REFERENCES:
*      DATA:            Log_Pipe_Nm = '\PIPE\LOGDATA\JGRENBLF'
*
*   EXTERNAL REFERENCES:
*      ROUTINES:        DosPeekNmPipe
*                       DosRead
*                       EnterDLL
*                       ExitDLL
*
**************************** END OF SPECIFICATIONS **********************/

USHORT far pascal DOSLOGREAD (USHORT       LogHandle,
                              USHORT       Length,
                              char far    *LogBuffer,
                              USHORT  far *ReadSize)
{
AVAILDATA bytes_avail;          /* returned by DosPeekNmPipe                */
USHORT pipe_state       = 0;    /* returned by DosPeekNmPipe                */
USHORT packet_len;              /* length of packet tobe given to user      */
USHORT rc;                      /* return code                              */

    bytes_avail.cbpipe = 0;
    bytes_avail.cbmessage = 0;
  ENTER_DLL();   /* set up registers for c-code */

    MMPHPreDOSLogRead()                          /* Performance Hook (1C07) */

    /***
    *   test pipe for data and get the packet length
    */
    rc = DosPeekNmPipe((USHORT)  LogHandle    ,
                  (char far *)  &packet_len   , /* buffer area */
                  (USHORT)       2            , /* buffer len  */
                  (USHORT far *) ReadSize     , /* bytes read  */
                                &bytes_avail  , /* bytes avail */
                  (USHORT far *) &pipe_state ); /* pipe state  */

    /***
    *   if pipe connected (rc =0) and data present (readsize = 2)
    */
    if ( rc == 0  &&  *ReadSize == 2 )
    {
      /***
      *   test callers buffer for enough space for packet
      */
      if ( Length >= bytes_avail.cbmessage)
      {
        rc = DosRead ((USHORT)         LogHandle ,     /* file handle   */
                      (char far *)     LogBuffer ,     /* buffer area   */
                      (USHORT)         Length,         /* buffer len    */
                      (PUSHORT)        ReadSize );     /* bytes read    */
      }
      else
      {
        rc = ERROR_LF_BUF_TOO_SMALL;         /* user buffer to small    */
        *ReadSize = 0;                       /* user read 0 bytes       */
      }
    }
    else
    {
      if (rc != 0)
        rc = ERROR_LF_INVALID_HANDLE;  /* system unavailable   */
      else
        rc = 0;                        /* no data in pipe      */
    }

    MMPHPostDOSLogRead()                         /* Performance Hook (1C87) */
   return(EXIT_DLL(rc));
}


