/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* perf720 src/perf/perfagent/usr/samples/perfagent/server/SpmiLogger.c 1.12 */
/*                                                                        */
/*                                                                        */
/*                                                                        */
/* OBJECT CODE ONLY SOURCE MATERIALS                                      */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1992,1994              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* The source code for this program is not published or otherwise         */
/* divested of its trade secrets, irrespective of what has been           */
/* deposited with the U.S. Copyright Office.                              */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/*
 *   COMPONENT_NAME: PERFAGENT
 *
 *   FUNCTIONS: add_stats
 *		create_headers
 *		get_stats
 *		main
 *		must_exit
 *		parse_name
 *		read_file
 *		read_input
 *              set_up_start
 *              set_up_end
 *              format_time
 *
 *   ORIGINS: 30
 *
 *
 *   (C) COPYRIGHT International Business Machines Corp. 1992,1993,1994
 *   All Rights Reserved
 *   Licensed Materials - Property of IBM
 *   US Government Users Restricted Rights - Use, duplication or
 *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 *       NOTICE TO USERS OF THE SOURCE CODE EXAMPLES
 *
 * THE SOURCE CODE EXAMPLES PROVIDED BY IBM ARE ONLY INTENDED TO ASSIST IN THE
 * DEVELOPMENT OF A WORKING SOFTWARE PROGRAM.  THE SOURCE CODE EXAMPLES DO NOT
 * FUNCTION AS WRITTEN:  ADDITIONAL CODE IS REQUIRED.  IN ADDITION, THE SOURCE
 * CODE EXAMPLES MAY NOT COMPILE AND/OR BIND SUCCESSFULLY AS WRITTEN.
 * 
 * INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THE SOURCE CODE
 * EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, "AS IS" WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
 * LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
 * OF THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS,
 * IS WITH YOU.  SHOULD ANY PART OF THE SOURCE CODE EXAMPLES PROVE
 * DEFECTIVE, YOU (AND NOT IBM OR AN AUTHORIZED RISC System/6000* WORKSTATION
 * DEALER) ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR
 * CORRECTION.
 * 
 * IBM does not warrant that the contents of the source code examples, whether
 * individually or as one or more groups, will meet your requirements or that
 * the source code examples are error-free.
 * 
 * IBM may make improvements and/or changes in the source code examples at
 * any time.
 * 
 * Changes may be made periodically to the information in the source code
 * examples; these changes may be reported, for the sample device drivers
 * included herein, in new editions of the examples.
 * 
 * References in the source code examples to IBM products, programs, or
 * services do not imply that IBM intends to make these available in all
 * countries in which IBM operates.  Any reference to an IBM licensed
 * program in the source code examples is not intended to state or imply
 * that only IBM's licensed program may be used.  Any functionally equivalent
 * program may be used.
 * 
 * RISC System/6000 is a trademark of International Business Machines
 * Corporation.
 *
 */

static char *Sccs_id = "@(#)17  1.10  src/perf/perfagent/usr/samples/perfagent/server/SpmiLogger.c, perfagent, perf420 3/7/95 17:11:24";

/*
 * EXECUTION NOTES:  
 *   SpmiLogger is a program which will display a set of metrics that is 
 *   input to the program either through a file or manually.  The output can 
 *   also be saved to an output file instead of displaying to the screen.  
 *   The output can also be saved in a Lotus type import format.
 *   
 *   The input file should just contain the path names of the metrics
 *   of interest.  The path names and descriptions of metrics can be
 *   acquired from the SpmiPeek program.  The following is an example of what 
 *   the metrics input file should look like if the user were interested in 
 *   the CPU's kernel info, the size of real memory, and the number of defined 
 *   statistic sets:
 *
 *   CPU/cpu0/kern
 *   Mem/Real/size
 *   Spmi/statsets
 *
 *   The Usage statement is as follows:
 *       SpmiLogger {-f infile|-m|-mf infile} [-i seconds] [-o outfile [-s|-t]]
 *                  [-b hhmm] [-e hh.mm]
 *   The -f indicates the input file which contains a list of metrics 
 *           you wish to monitor.
 *   The -m allows you to input metrics manually.  You will be prompted
 *           for the metrics you wish to monitor.
 *   The -i allows you to input a delay in seconds between metrics readings.
 *           The default is two seconds.
 *   The -o indicates the output file.
 *   The -s will convert the output data into a Spreadsheet format.
 *   The -t will convert the output data into a tab separated format.
 *   The -b will start SpmiLogger at the specified time.  The start time should
 *     be within 24 hours.  If the start time is incorrect then SpmiLogger 
 *     will begin execution immediately.
 *   The -e will stop SpmiLogger after the specified interval.  Anything after 
 *     the decimal point is considered minutes and everything before the
 *     decimal point is considered hours.  You could conceivably end after
 *     120 minutes which could be input either as 0.120, or 2.0
 *     There is a maximum of 24 hours.
 *   If no begin and end times are specified, then SpmiLogger will run for
 *   the next 12 hours.
 * 
 */

#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/utsname.h>
#include <sys/Spmidef.h>

#define PLAIN 	0
#define TAB   	1
#define SPREAD 	2

extern char             SpmiErrmsg[];
extern int              SpmiErrno;

struct SpmiStatSet      *statset;
char                    context[600], 
                        statistic[300],
                        header_line[50],
                        *blanks = "          ",
                        *ofile, 
                        *ifile;
FILE                    *in_file, 
                        *out_file;
int                     stat_counter = 0,
                        manflag = 0, 
                        fileflag = 0, 
                        outflag = 0, 
                        formatflag = PLAIN,
                        delay = 2,
                        run_time = 43200,  /* default of 12 hours */
                        start_log = 0,
   	                  sleep_secs;


/*============================== must_exit() ==============================*/
/* This function is used to clean up memory after the program receives a   */
/* signal to terminate execution.                                          */
/*=========================================================================*/

void must_exit()
{
    if (statset)
       if (SpmiFreeStatSet(statset))
       {
          printf("ERROR:  SpmiFreeStatSet Failed.\n");
          if (strlen(SpmiErrmsg))
             printf("%s", SpmiErrmsg);
       }

    SpmiExit();
    if (SpmiErrno)
    {
       printf("ERROR:  SpmiExit Failed.\n");
       if (strlen(SpmiErrmsg))
           printf("%s", SpmiErrmsg);
    }

    if (outflag)
       fclose(out_file);
    exit(0);
}


/*============================ create_headers() ===========================*/
/* This function traverses the statset to make the headers which display   */
/* the names of the metrics in the set.  This is the standard output       */
/* format.                                                                 */
/*=========================================================================*/

void create_headers()
{
    struct SpmiStatVals   *statval1;
    struct SpmiStat       *spmistat;
    char                  tmp_array[100];
    int                   pathlen, statlen, length;

    /* determine if the statset is valid */
    if (SpmiGetStatSet(statset, TRUE) != 0)
    {
       printf("ERROR:  SpmiGetStatSet Failed.\n");
       if (strlen(SpmiErrmsg))
          printf("%s", SpmiErrmsg);
       must_exit();
    }

    switch (formatflag)
    {
        case SPREAD:
          strcpy(context, "\"Timestamp\"  ");
          break;
        case TAB:
          strcpy(context, "Timestamp\t");
          break;
        case PLAIN:
        default:
          strcpy(context, "Timestamp         ");
          strcpy(statistic, "                  ");
          break;
    }

    /* traverse the stat set to create a header which displays */
    /* the names of the metrics. */
    statval1 = SpmiFirstVals(statset);
    while (statval1 != NULL)
    {
       sprintf(tmp_array, "%s", SpmiStatGetPath(statval1->context, 
                                                statval1->stat, 0));
       if (strlen(SpmiStatGetPath(statval1->context,statval1->stat, 0)) == 0)
       {
          printf("ERROR:  SpmiStatGetPath Failed.\n");
          if (strlen(SpmiErrmsg))
             printf("%s", SpmiErrmsg);
          must_exit();
       }

       switch (formatflag)
       {
           case TAB:
	      strcat(context, tmp_array);
	      strcat(context, "\t");
              break;
           case SPREAD:
              strcat(context, "\"");
              strcat(context, tmp_array);
              strcat(context, "\"  ");
              break;
           case PLAIN:
           default:
              spmistat = SpmiGetStat(statval1->stat);
              if (spmistat == NULL)
              {
                 printf("ERROR:  SpmiGetStat Failed.\n");
                 if (strlen(SpmiErrmsg))
                    printf("%s", SpmiErrmsg);
                 must_exit();
              }

              pathlen = strlen(tmp_array);
              statlen = strlen(spmistat->name);
              length = pathlen - statlen;
   
              /* format the arrays so only the first 10 chars of the name is */
              /* shown, this is done to conserve screen space */
              if (length > 10)
              {
                 strncat(context, blanks, 1);
                 strncat(context, tmp_array, 10);
              }
              else 
              {
                 strncat(context, blanks, (11 - length));
                 strncat(context, tmp_array, length);
              }
   
              if (statlen > 10)
              {
                 strncat(statistic, blanks, 1);
                 strncat(statistic, spmistat->name, 10);
              }
              else
              {
                 strncat(statistic, blanks, 11 - statlen);
                 strcat(statistic, spmistat->name);
              }
              break;
       }

       statval1 = SpmiNextVals(statset, statval1);
    }

    switch (formatflag)
    {
       case TAB:
       case SPREAD:
        fprintf(out_file, "%s\n", header_line);
        fprintf(out_file, "%s\n", context);
        break;
    }
    return;
}

/*=========================== format_time() ===============================*/
/* This function takes an input of the time of the reading and then formats*/
/* the time for output to the out file.                                    */
/*=========================================================================*/
#ifdef _NO_PROTO
void format_time(t1, seconds)
char *t1;
time_t *seconds;
#else
void format_time(char *t1, time_t *seconds)
#endif
{
    struct tm  *t2;
    char       month[3], day[3], year[5];
    char       hour[3], min[3], sec[3];

    t2 = localtime(seconds);

    if ((t2->tm_mon + 1) < 10)
       sprintf(month, "0%d", (t2->tm_mon + 1));
    else
       sprintf(month, "%d", (t2->tm_mon + 1));
    if (t2->tm_mday < 10)
       sprintf(day, "0%d", t2->tm_mday);
    else
       sprintf(day, "%d", t2->tm_mday);

    sprintf(year, "%d", 1900 + t2->tm_year);

    if (t2->tm_hour < 10)
       sprintf(hour, "0%d", t2->tm_hour);
    else
       sprintf(hour, "%d", t2->tm_hour);
    if (t2->tm_min < 10)
       sprintf(min, "0%d", t2->tm_min);
    else
       sprintf(min, "%d", t2->tm_min);
    if (t2->tm_sec < 10)
       sprintf(sec, "0%d", t2->tm_sec);
    else
       sprintf(sec, "%d", t2->tm_sec);
    sprintf(t1, "%s/%s/%s %s:%s:%s", year, month, day, hour, min, sec);
    return;
}

/*============================= get_stats() ===============================*/
/* This function traverses the statset to get the stat values and write    */
/* the values to the selected output file and format.                      */
/*=========================================================================*/

void get_stats()
{
    struct SpmiStatVals   *statval1;
    float                 spmivalue;
    int                   counter = 20;
    char                  t1[25];
    struct timeval        atid;
    int                   errors = 0;

    /* execute this loop until a terminate signal is received */
    while (1)  
    {
       /* determine if the statset is valid */
       if (SpmiGetStatSet(statset, TRUE) != 0)
       {
          if (++errors > 10)
          {
             printf("ERROR:  SpmiGetStatSet Failed.\n");
             if (strlen(SpmiErrmsg))
                printf("%s", SpmiErrmsg);
             must_exit();
          }
       }

       /* output the headers every 20 lines */
       if ((counter == 20) && !formatflag)
       {
          fprintf(out_file, "\n\n");
          fprintf(out_file, "%s\n", context);
          fprintf(out_file, "%s\n", statistic);
          counter = 1;
       }

       /* output a timestamp */
       gettimeofday(&atid, NULL);
       format_time((char *)t1, (time_t *)&atid.tv_sec);
       switch(formatflag)
       {
          case TAB:     
             fprintf(out_file, "%s\t", t1);
             break;
          case SPREAD:
             fprintf(out_file, "\"%s\"", t1);
             break;
          case PLAIN:
          default:
             fprintf(out_file, "%s", t1);
             break;
       }

       /* traverse the stat set */
       statval1 = SpmiFirstVals(statset);
       while (statval1 != NULL)
       {
          spmivalue = SpmiGetValue(statset, statval1);
          if (spmivalue < 0.0)
          {
             /* maybe we just lost a connection */
             switch (formatflag)
             {
                 case TAB:
                   fprintf(out_file, "         -\t");
                   break;
                 case SPREAD:
                   fprintf(out_file, "\"        -\"");
                   break;
                 case PLAIN:
                 default:
                   fprintf(out_file, "         -");
                   break;
             }
          }
          else 
          {
             /* output the statistic values */
             switch(formatflag)
             { 
	        case TAB:
            	   fprintf(out_file, "%10.2f\t", spmivalue);
                   break;
	        case SPREAD:
                case PLAIN:
                default:
            	   fprintf(out_file, "%10.2f ", spmivalue);
                   break;
             }
          }
	
          statval1 = SpmiNextVals(statset, statval1);
       }
       fprintf(out_file, "\n");

       ++counter;

       /* sleep until next statistic read */
       sleep(delay);
       --run_time;
       if (!run_time)
	   must_exit();
    }

    return;
}

/*============================ add_stats() ===============================*/
/* Add the metrics to the stat set.                                       */
/*========================================================================*/

void add_stats()
{

    SpmiCxHdl   cxhdl;

    if ((cxhdl = SpmiPathGetCx(context, NULL)) == NULL)
    {
       printf("ERROR:  SpmiPathGetCx Failed.\n");
       if (strlen(SpmiErrmsg))
          printf("%s", SpmiErrmsg);
       must_exit();
    }

    if (SpmiPathAddSetStat(statset, statistic, cxhdl) == NULL)
    {
       printf("ERROR:  SpmiPathAddSetStat for %s Failed.\n", statistic);
       if (strlen(SpmiErrmsg))
          printf("%s\n", SpmiErrmsg);
       must_exit();
    }

    ++stat_counter;
    return;
}

/*============================= parse_name() =============================*/
/* This function is used to traverse the name of the input statistic to   */
/* separate the context from the statistic.                               */
/*========================================================================*/

#ifdef _NO_PROTO
void parse_name(stat_inp)
char *stat_inp;
#else
void parse_name(char *stat_inp)
#endif
{
   char   *stat_out;

   stat_out = strtok(stat_inp, "/");
   strcpy(context, stat_out);
   statistic[0] = '\0';
   while (stat_out)
   {
      stat_out = strtok(NULL, "/");
      if (stat_out)
      {
         if (statistic[0])
         {
            strcat(context, "/");
            strcat(context, statistic);
         }
         strcpy(statistic, stat_out);	
      }
   }
   return;
}

/*============================= read_file() ==============================*/
/* Read the statistic names from the input file.                          */
/*========================================================================*/

void read_file()
{
    char    stat_inp[128];

    while ((fscanf(in_file, "%s", &stat_inp)!=EOF) && (stat_counter <= 28))
    {
       parse_name((char *)&stat_inp);
       add_stats();
    }
    fclose(in_file);
    return;
}

/*============================ read_input() ==============================*/
/* Read statistic names from user input.                                  */
/*========================================================================*/

void read_input()
{
    char    stat_inp[128];

    if (stat_counter == 28) {
          printf("\nMaximum of 28 Statistics May Be Entered.\n");
          return;
    }

    printf("\nEnter Full Path Name of Stat, 99 to end:  ");
    scanf("%s", &stat_inp);
    while(strcmp(stat_inp, "99") && (stat_counter <= 28))
    {
       parse_name((char *)&stat_inp);
       add_stats();
       if (stat_counter <= 28)
       {
          printf("\nEnter Full Path Name of Stat:  ");
          scanf("%s", &stat_inp);
       }
       else
          printf("\nMaximum of 28 Statistics May Be Entered.\n");
    }  /* end while loop */
    return;

}

/*=========================== set_up_start() =============================*/
/* This function determines the number of seconds until the user's        */
/* desired time.  If an invalid time is input, SpmiLogger will begin      */
/* collecting data immediately.                                           */
/*========================================================================*/

void set_up_start()
{
	char                *start_time;
	char                hr[3], minute[3];
	int                 hour, min;
	struct timeval      timer;
	struct timezone     tzone;
	struct tm           *current_time;
	extern char				*optarg;

	start_time = optarg;
	hr[0] = *start_time;
	start_time++;
	if (*start_time == NULL)
		return;
	hr[1] = *start_time;
	start_time++;
	if (*start_time == NULL)
		return;
	minute[0] = *start_time;
	start_time++;
	if (*start_time == NULL)
		return;
	minute[1] = *start_time;

	hour = atoi(hr);
	min = atoi(minute);
        if ((hour < 0) || (hour > 23) || (min < 0) || (min > 59))
		return;

	gettimeofday(&timer, &tzone);
	current_time = localtime((time_t *)&timer.tv_sec);
	
	start_log++;
	/* special condition */
	if ((current_time->tm_hour == hour) && (current_time->tm_min > min))
	{
		sleep_secs = (24 * 3600);
		sleep_secs -= ((current_time->tm_min - min) * 60);
		return;
	}

	if (current_time->tm_hour > hour)           /* tomorrow */
	   sleep_secs = (24 * 3600) - ((current_time->tm_hour - hour) * 3600);
        else
	   sleep_secs = (hour - current_time->tm_hour) * 3600;

        if (current_time->tm_min > min)
	   sleep_secs -= ((current_time->tm_min - min) * 60);
        else
	   sleep_secs += ((min - current_time->tm_min) * 60);
	return;
} /* end set_up_start */

/*=========================== set_up_end() ===============================*/
/* This function determines the number of seconds SpmiLogger will execute.*/
/*========================================================================*/

int set_up_end()
{
        char   *end_time;
        char   *fracp, *intp;
        int    frac_part, int_part;

	end_time = optarg;
	if (end_time[0] == '.') {
	  intp = "0";
          fracp = strtok(end_time, ".");
       }
       else {
          intp = strtok(end_time, ".");
          fracp = strtok(NULL, ".");
       }
       int_part = atoi(intp);
       frac_part = atoi(fracp);
       if ((int_part < 0.0) || (frac_part < 0.0))
            return(1);
       else 
            run_time = (int_part * 3600) + (frac_part * 60);
       if (run_time > 86400)    /* 24 hours max */
       {
            printf("ERROR:  SpmiLogger can only execute for a\n");
            printf("        maximum of 24 hours.\n");
            must_exit();
       }
       return(0);
} /* set_up_end */

/*================================  main()  ============================*/

#ifdef _NO_PROTO
main(argc, argv)
int argc;
char **argv;
#else
main(int argc, char **argv)
#endif
{
    extern char    *optarg;
    int            errflag = 0;
    int            spmierr=0;
    int            c;
    struct utsname uname_struct;

    /* Initialize SPMI interface */
    if ((spmierr = SpmiInit(15)) != 0)
    {
       printf("ERROR:  Unable to initialize SPMI interface.\n");
       printf("%s", SpmiErrmsg);
       exit(-98);
    }

    /* setup interrupt signals */
#ifdef _NO_PROTO
    signal(SIGINT,  (void(*)())must_exit);
    signal(SIGTERM, (void(*)())must_exit);
    signal(SIGSEGV, (void(*)())must_exit);
    signal(SIGQUIT, (void(*)())must_exit);
#else
    signal(SIGINT,  (void(*)(int))must_exit);
    signal(SIGTERM, (void(*)(int))must_exit);
    signal(SIGSEGV, (void(*)(int))must_exit);
    signal(SIGQUIT, (void(*)(int))must_exit);
#endif /* _NO_PROTO */
	
   while ((c = getopt(argc, argv, "b:e:f:i:mo:st")) != EOF)
   {
      switch (c)
      {
	 case 'b':                   /* start time */
		set_up_start();
		break;
	 case 'e':                 /* end time */
		errflag += set_up_end();
		break;
         case 'f':                /* input file */
                fileflag++;
                ifile = optarg;
                break;
         case 'i':                  /* delay */
                delay = atoi(optarg);
		if (delay <= 0)
		{
			printf("ERROR:  Interval cannot be zero or \n");
			printf("        less than zero.\n");
			must_exit();
		}
                break;
         case 'm':               /* manual input */
                manflag++;
                break;
         case 'o':               /* output file */
                outflag++;
                ofile = optarg;
                break;
         case 's':               /* Spreadsheet output */
                if (formatflag)
                    errflag++;
                formatflag = SPREAD;
                break;
         case 't':               /* tab separated output */
                if (formatflag)
                    errflag++;
                formatflag = TAB;
                break;
         case '?': 
                errflag++;
                break;
      }
   }

   if( (formatflag && !outflag) ||
       (!fileflag && !manflag) )
        errflag++;
   if (errflag) 
   {
      printf("Usage:  SpmiLogger {-f infile|-m|-mf infile} [-i seconds] [-o outfile [-s|-t]]\n");
      printf("                   [-b hhmm] [-e hh.mm]\n");
      printf("        Flags:  -f  Input file which contains metrics list.\n");
      printf("                -m  Manual input of metrics.\n");
      printf("                -i  Delay between metrics readings.\n");
      printf("                    Default = two seconds\n");
      printf("                -o  Output file.\n");
      printf("                -s  Spreadsheet format.\n");
      printf("                -t  Tab separated format.\n");
      printf("                -b  Start SpmiLogger at a specified time.\n");
      printf("                    hh = Hour in 24 hour time\n");
      printf("                    mm = Minutes\n");
      printf("                    Default = Current time\n");
      printf("                -e  Stop SpmiLogger after the specified interval.\n");
      printf("                    hh = Number of hours to execute\n");
      printf("                    mm = Number of minutes to execute\n");
      printf("                    Default = 12 hours,  Max = 24 hours\n");
      must_exit();
   }

    out_file = NULL;
    if (outflag) 
    {
       out_file = fopen(ofile, "w");
       if (out_file == NULL)
       {
             printf("ERROR:  Unable to open output file.\n");
             printf("        Exiting program.\n");
             must_exit();
       }
    }
    else
	out_file = stdout;

    if (fileflag)
    {
          in_file = fopen(ifile, "r");
          if (in_file == NULL)
          {
             printf("ERROR:  Unable to open input file.\n");
             printf("        Exiting program.\n");
             must_exit();
          }
    }

    /* set up time counter */
    run_time = run_time / delay;

    statset = SpmiCreateStatSet();
    if (statset == NULL)
    {
       printf("ERROR:  SpmiCreateStatSet Failed.\n");
       if (strlen(SpmiErrmsg))
          printf("%s", SpmiErrmsg);
       must_exit();
    }

    uname(&uname_struct);
    if (formatflag == SPREAD)
       sprintf(header_line, "\"Metrics Provided By Hostname:  %s\"", uname_struct.nodename);
    else
       sprintf(header_line, "Metrics Provided By Hostname:  %s", uname_struct.nodename);

    if (manflag)
       read_input();

    if (fileflag)
       read_file();

    /* Display stats info */
    if (stat_counter != 0)
    {
       create_headers();
       if (start_log)
	   sleep(sleep_secs);
       get_stats();              /* access metrics */
    }
    else
    {
       printf("ERROR:  No metrics were input.\n");
       printf("        Exiting program.\n");
    }
 
    /* Exit Spmi Interface */
    must_exit();
}
