/**
 * @file   SCFLMUSG.c
 * @brief  Sample program in C to evaluate the usage log on z/OS
 * @author limes datentechnik gmbh
 **********************************************************************/

/* defines code page for literals for IBM mainframes to German */
#ifdef __ZOS__
#  pragma convert(1141)
#endif

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

#include"FLCBYT.h"

#define GIB          1073741824
#define FLAG_OFFL    1
#define FLAG_ZIIP    2
#define FLAG_ZEDC    3
#define FLAG_CPAC    4

/**
 * This test program takes optional parameters containing the SUBSYS-options1 of SUBSYS-LOGR:
 * [FROM={({[yyyy/ddd][,hh:mm[:ss]]})  | OLDEST}]
 * [TO={({[yyyy/ddd][,hh:mm[:ss]]})  | YOUNGEST}]
 * [,DURATION=(nnnn,HOURS)]
 * [,VIEW={ACTIVE|ALL|INACTIVE}]
 * [,GMT|LOCAL].
 * It calculates a summary of all corresponding usage log entries per
 * component and print a summary to STDOUT (DD:SYSPRINT) all other
 * messages are printed to STDERR (DD:SYSOUT).
 *
 * @param argc must be 1 or 2
 * @param argv contains the optional SUBSYS-options1 for subsystem allocation
 * @return 8=parameter error, 6=open file error, 5=memory allocation, 4=read write error, 2=close error, 0=Success
 */

typedef struct MyRec{
   char                       acFirm[8];        /* first part of the IFA owner containing the company name */
   char                       acOwnr[8];        /* second part of the IFA owner containing the FLCL owner namer "limes" */
   char                       acProd[8];        /* first part of the IFA name containing the product (FLUC, FLAM, FLM4, FLIES, ...) */
   char                       acComp[8];        /* second part of the IFA name containing the component (FIOBLk, CNVGZP, FMTXML, ...) */
   char                       acVers[8];        /* product version (5.1.13) */
   char                       acQual[8];        /* additional qualifier (READ/WRITE separation) */
   char                       acFmId[8];        /* FMID of the product (FLAM501) */
   char                       acDelm[1];        /* Zero termination to simple print the elements before as one string in C */
   char                       acFlag[7];        /* Several control flags and bits for open function (if first byte set it indicates offload'able load, rest unsued) */
   unsigned long long         uiPerS;           /* Clocks per second (e.g. 4096*1000000 for TOD on z/OS) to interpret the times below correctly */
   unsigned long long         uiTotT;           /* Total runtime if available else 0 */
   unsigned long long         uiCpuT;           /* CPU time in clocks (in uiPerS)*/
   unsigned long long         uiOflT;           /* Offload time (SRB) in clocks (in uiPerS) */
   unsigned long long         uiEOfl;           /* eligible offload time (SRB) in clocks (in uiPerS) */
   unsigned long long         uiByte;           /* amount of byte processed */
} TsRec;

static int siCompare(const void* pvRec1, const void* pvRec2) {
   return(memcmp(pvRec1,pvRec2,64));
}

int main(int argc, char * argv[])
{
   void*                   pvRed;
   char                    acFile[128];
   char                    acParm[256];
   char                    acOpen[512];
   char                    acMode[128];
   unsigned char           acBuf[1024];
   TsRec*                  psRec=NULL;
   TsRec*                  psTab=NULL;
   unsigned int            uiLen,i,uiCnt=0;
   size_t                  uiInfSiz=0;
   char*                   pcInfBuf=NULL;
   char*                   pcLogSrm=NULL;
   unsigned long long      uiTotRec=0;
   unsigned long long      uiPrcRec=0;
   unsigned long long      uiTotT=0;
   unsigned long long      uiCpuT=0;
   unsigned long long      uiOflT=0;
   unsigned long long      uiEOfl=0;
   unsigned long long      uiZiip=0;
   unsigned long long      uiZedc=0;
   unsigned long long      uiCpac=0;
   unsigned long long      uiByte=0;
   unsigned long long      uiPerS=1000000;

 /* defines environment variable LANG to German EBCDIC for IBM mainframes */
#ifdef __ZOS__
   __setenv("LANG","de_DE.IBM-1141",1);
#endif

   /* determine the log stream name from info list */
   int r=fcbinfo("get.usage", FLMOUT_FORMAT_LST, &uiInfSiz, &pcInfBuf);
   if (r>0) { /* parse info list */
      char* pcEnd=pcInfBuf+r;
      char* pcHelp=strstr(pcInfBuf,"LOG-STREAM ");
      if (pcHelp!=NULL) { /* LOG-STREAM entry found */
         pcHelp+=11;
         while(pcHelp<pcEnd && *pcHelp && (isspace(*pcHelp) || *pcHelp==':')) pcHelp++;
         if (isalpha(*pcHelp)) { /* LOG-STREAM name found */
            pcLogSrm=pcHelp;
            for (i=0;pcHelp<pcEnd && i<sizeof(acFile)-1 && *pcHelp && !isspace(*pcHelp);pcHelp++,i++){
               acFile[i]=*pcHelp;
            }
            acFile[i]=0x00;
         }
      }
   }

   if (pcLogSrm==NULL || *pcLogSrm==0) {
      fprintf(stderr,"Determination of log stream name failed\n");
      if (r<0) {
         fprintf(stderr,"%s",fcberrtr());
         if (pcInfBuf!=NULL) free(pcInfBuf);
         exit(12);
      } else {
         fprintf(stderr,"%s",pcInfBuf);
         free(pcInfBuf);
         exit(10);
      }
   }
   free(pcInfBuf);

   if (argc==1) {
      snprintf(acParm,sizeof(acParm),"%s","");
   } else if (argc==2) {
      snprintf(acParm,sizeof(acParm),"IXGSEXIT,\"%s\"",argv[1]);
   } else {
      fprintf(stderr,"Parameter count (%d) not correct to run this program\n",argc);
      fprintf(stderr,"Given parameters:\n");
      for (i=0;i<argc;i++) fprintf(stderr,"--> P%2.2u(%s)\n",i,argv[i]);
      fprintf(stderr,"The program accept one optional parameter (no blanks):\n");
      fprintf(stderr,"   SUBSYS-options1 (separated by comma)\n");
      fprintf(stderr,"     FROM={({[yyyy/ddd][,hh:mm[:ss]]})  | OLDEST}\n");
      fprintf(stderr,"   [,TO={({[yyyy/ddd][,hh:mm[:ss]]})    | YOUNGEST}]\n");
      fprintf(stderr,"   | DURATION=(nnnn,HOURS)\n");
      fprintf(stderr,"   [,VIEW={ACTIVE|ALL|INACTIVE}]\n");
      fprintf(stderr,"   [,GMT|LOCAL]\n");
      exit(8);
   }
   snprintf(acOpen,sizeof(acOpen),"read.record(file='%s' SUBSYS(name='LOGR' parm='%s') RECF=VB RECL=%d)",acFile,acParm,(int)sizeof(acBuf));
   snprintf(acMode,sizeof(acMode),"format.record()");
   pvRed=fcbopen(acOpen,acMode);
   if (pvRed==NULL) {
      fprintf(stderr,"Open usage file for read operation failed (probably no log stream defined)\n");
      fprintf(stderr,"pvRed=fcbopen(\"%s\",\"%s\"\n\n",acOpen,acMode);
      fprintf(stderr,"%s\n\n",fcberrms(fcberrno));
      fprintf(stderr,"%s\n\n",fcberrtr());
      exit(6);
   }
   fprintf(stderr,"Open of FLAM usage log (%s) was successful\n",acOpen);

   for (uiLen=fcbread(acBuf,1,sizeof(acBuf),pvRed); fcberror(pvRed)==FLMRTC_OK;
        uiLen=fcbread(acBuf,1,sizeof(acBuf),pvRed)) { /* read all records */
      if (uiLen==sizeof(TsRec)) {
         psRec=(TsRec*)acBuf;
         for (i=0;i<uiCnt;i++) { /* search component */
            if (memcmp(psRec->acFirm,psTab[i].acFirm,8)==0 &&
                memcmp(psRec->acProd,psTab[i].acProd,8)==0 &&
                memcmp(psRec->acComp,psTab[i].acComp,8)==0 &&
                memcmp(psRec->acQual,psTab[i].acQual,8)==0) { /* if match then accumulate */
               psTab[i].uiTotT+=psRec->uiTotT;
               psTab[i].uiCpuT+=psRec->uiCpuT;
               psTab[i].uiOflT+=psRec->uiOflT;
               psTab[i].uiEOfl+=psRec->uiEOfl;
               psTab[i].uiByte+=psRec->uiByte;
               uiPrcRec++;
               break;
            }
         }
         if (i>=uiCnt && memcmp(psRec->acFirm,"LIMES   ",8)==0) { /* no match then new entry if firm is LIMES */
            TsRec* psHlp=(TsRec*)realloc(psTab,sizeof(TsRec)*(uiCnt+1));
            if (psHlp==NULL) {
               fprintf(stderr,"Allocation of summary table failed (CNT=%u SIZ=%u)\n",uiCnt+1,(unsigned int)(sizeof(TsRec)*(uiCnt+1)));
               if (psTab!=NULL) free(psTab);
               fcbclose(pvRed);
               exit(5);
            }
            psTab=psHlp;
            memcpy(psTab+uiCnt,psRec,64);
            psTab[uiCnt].uiPerS=psRec->uiPerS;
            psTab[uiCnt].uiTotT=psRec->uiTotT;
            psTab[uiCnt].uiCpuT=psRec->uiCpuT;
            psTab[uiCnt].uiOflT=psRec->uiOflT;
            psTab[uiCnt].uiEOfl=psRec->uiEOfl;
            psTab[uiCnt].uiByte=psRec->uiByte;
            fprintf(stderr,"Add new component (%s) to summary\n",psRec->acFirm);
            uiPrcRec++;
            uiCnt++;
         }
      }
      uiTotRec++;
   }
   if (fcberror(pvRed)!=FLMRTC_OK && fcberror(pvRed)!=FLMRTC_EOF) {
      fprintf(stderr,"Read data from usage file failed\n");
      fprintf(stderr,"uiLen(%u)=fcbread(acBuf,1,(%u)sizeof(acBuf),pvRed);\n\n",uiLen,(unsigned int)sizeof(acBuf));
      fprintf(stderr,"%s\n\n",fcberrms(fcberror(pvRed)));
      fprintf(stderr,"%s\n\n",fcberrtr());
      if (psTab!=NULL) free(psTab);
      fcbclose(pvRed);
      exit(4);
   }
   fprintf(stderr,"Summarize %llu/%llu records to %u components\n",uiPrcRec,uiTotRec,uiCnt);

   qsort(psTab, uiCnt, sizeof(TsRec), siCompare);

   fprintf(stdout,"# FLAM-USAGE-SUMMARY\n");
   fprintf(stdout,"#   LOG-STREAM       : %s\n"  ,acFile);
   if (acParm[0])
   fprintf(stdout,"#   PARMETER         : %s\n"  ,acParm);
   fprintf(stdout,"#   COMPONENTS USED  : %u\n"  ,uiCnt);
   fprintf(stdout,"#   RECORDS PROCESSED: %llu\n",uiPrcRec);
   fprintf(stdout,"#   TOTAL RECORDS    : %llu\n",uiTotRec);
   fprintf(stdout,"#FIRM   OWNER   PRODUCT COMP    VERSION FUNC    JOBNAME ;  CPUTIME IN SEC;  OFFLOAD IN SEC; ELIGIBLE IN SEC;  DATAVOL IN GIB\n");
   for (i=0;i<uiCnt;i++) { /* print summary */
      uiTotT+=psTab[i].uiTotT;
      uiCpuT+=psTab[i].uiCpuT;
      uiOflT+=psTab[i].uiOflT;
      uiEOfl+=psTab[i].uiEOfl;
      if (psTab[i].acFlag[0]==FLAG_ZIIP) {
         uiZiip+=psTab[i].uiCpuT;
      } else if (psTab[i].acFlag[0]==FLAG_ZEDC) {
         uiZedc+=psTab[i].uiCpuT;
      } else if (psTab[i].acFlag[0]==FLAG_CPAC) {
         uiCpac+=psTab[i].uiCpuT;
      }
      uiByte+=psTab[i].uiByte;
      uiPerS=(psTab[i].uiPerS)?psTab[i].uiPerS:1000000;
      fprintf(stdout,"%s;%16.3f;%16.3f;%16.3f;%16.3f\n",
              psTab[i].acFirm,((double)psTab[i].uiCpuT)/((double)uiPerS),
                              ((double)psTab[i].uiOflT)/((double)uiPerS),
                              ((double)psTab[i].uiEOfl)/((double)uiPerS),
                              ((double)psTab[i].uiByte)/((double)GIB));
   }
   fprintf(stdout,"#FIRM   OWNER   PRODUCT COMP    VERSION FUNC    JOBNAME ;  CPUTIME IN SEC;  OFFLOAD IN SEC; ELIGIBLE IN SEC;  DATAVOL IN GIB\n");
   fprintf(stdout,"#                                      TOTALTIME IN SEC ;-------------------------------------------------------------------\n");
   fprintf(stdout,"#                            SUMMARY :%18.3f;%16.3f;%16.3f;%16.3f;%16.3f\n",
         ((double)uiTotT)/((double)uiPerS),
         ((double)uiCpuT)/((double)uiPerS),
         ((double)uiOflT)/((double)uiPerS),
         ((double)uiEOfl)/((double)uiPerS),
         ((double)uiByte)/((double)GIB));
   fprintf(stdout,"#                               ZIIP :                   %16.3f (could be implemented as SRB routine)\n",
         ((double)uiZiip)/((double)uiPerS));
   fprintf(stdout,"#                               ZEDC :                   %16.3f (automatic used if available)\n",
         ((double)uiZedc)/((double)uiPerS));
   fprintf(stdout,"#                              CPACF :                   %16.3f (automatic used if available)\n",
         ((double)uiCpac)/((double)uiPerS));
   if (psTab!=NULL) free(psTab);

   fprintf(stderr,"FLAM usage reporting summary was successful printed\n");
   if (fcbclose(pvRed)) {
      fprintf(stderr,"Close usage file until read operation failed\n");
      fprintf(stderr,"fcbclose(pvRed);\n\n");
      fprintf(stderr,"%s\n\n",fcberrms(fcberrno));
      fprintf(stderr,"%s\n\n",fcberrtr());
      if (psTab!=NULL) free(psTab);
      exit(2);
   }
   fprintf(stderr,"Close of FLAM usage log (%s) was successful\n",acOpen);
   return(0);
}

/******************************************************************************/

