/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* perf720 src/perf/perfagent/usr/samples/perfagent/server/SpmiHotlog.c 1.2 */
/*                                                                        */
/*                                                                        */
/*                                                                        */
/* OBJECT CODE ONLY SOURCE MATERIALS                                      */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1996,1998              */
/* 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
 *		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. 1996
 *   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/SpmiHotlog.c, perfagent, perf420 3/7/95 17:11:24";

/*
 * EXECUTION NOTES:  
 *   SpmiHotlog 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 contain the path names of peer sets of the metrics
 *   to be monitored.  The path names and descriptions of metrics can be
 *   acquired from the SpmiPeek program.  Note that the path names of peer sets
 *   lack the lowest level context name.  For example, the peer metrics "xfer"
 *   for a machine with three disks might be:
 *
 *         Disk/hdisk0/xfer
 *         Disk/hdisk1/xfer
 *         Disk/hdisk2/xfer
 *
 *   The corresponding peer set path name is:
 *
 *         Disk/xfer
 *
 *   Following the peer metrics path name, you can specify up-to 4 parameters
 *   that will control how this program monitors hot metrics.  The values
 *   and their defaults are:
 *
 *   Value No   Description                                   Default
 *   --------   -------------------------------------------   -------
 *       1      The maximum number of responses to return        2
 *
 *       2      The threshold that determines if response     Metric max value
 *              is generated; negative value means thet a     divided by 2;
 *              metric qualifies if its value is less than
 *              the numeric value of threshold.	
 *
 *       3      Determines how to generate data feeds:           1
 *                 0 = never
 *                 1 = according to threshold
 *                 2 = always
 *
 *       4      Determines if exceptions/traps generated.        0
 *                 0 = neither
 *                 1 = exceptions only
 *                 2 = traps only
 *                 3 = both exceptions and traps
 *
 *   The Usage statement is as follows:
 *       SpmiHotlog {-f infile|-m|-mf infile} [-i seconds] [-o outfile]
 *                  [-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 -b will start SpmiHotlog at the specified time.  The start time should
 *     be within 24 hours.  If the start time is incorrect then SpmiHotlog 
 *     will begin execution immediately.
 *   The -e will stop SpmiHotlog 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 SpmiHotlog 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 SpmiHotSet       *hotset;
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, 
                        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 (hotset)
       if (SpmiFreeHotSet(hotset))
       {
          printf("ERROR:  SpmiFreeHotSet 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);
}

/*=========================== 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 hotset to get the stat values and write    */
/* the values to the selected output file and format.                      */
/*=========================================================================*/

void get_stats()
{
    struct SpmiHotVals    *hotval;
    struct SpmiStat       *stat;
    int                   index; 
    float                 value;
    char                  *name;
    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 hotset is valid */
       if (SpmiGetHotSet(hotset, TRUE) != 0)
       {
          if (++errors > 10)
          {
             printf("ERROR:  SpmiGetHotSet Failed.\n");
             if (strlen(SpmiErrmsg))
                printf("%s", SpmiErrmsg);
             must_exit();
          }
       }

       /* output a timestamp */
       gettimeofday(&atid, NULL);
       format_time((char *)t1, (time_t *)&atid.tv_sec);

       /* traverse the hotset */
       hotval = SpmiNextHotItem(hotset, NULL, &index, &value, &name);
       while (hotval != NULL)
       {
          /* output the statistic values */
          stat = SpmiGetStat(hotval->stat);
          fprintf(out_file, "%s, %s/%s/%s=%1.2f, threshold=%d\n",
                  t1, hotval->path, name, stat->name, value, hotval->threshold);
          hotval = SpmiNextHotItem(hotset, hotval, &index, &value, &name);
       }

       ++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.                                       */
/*========================================================================*/

#ifdef _NO_PROTO
void add_stats(max, threshold, feed, excp)
int max;
int threshold;
int feed;
int excp;
#else
void add_stats(int max, int threshold, int feed, int excp)
#endif
{

    SpmiCxHdl            cxhdl;
    struct SpmiStat      *stat;
	 struct SpmiCxLink    *cxlink;
    struct SpmiCx        *cx;
    struct SpmiStatLink  *statlink;

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

    if (threshold == -1)
       threshold = stat->max / 2;
    if (max == -1)
       max = 2;
    if (SpmiAddSetHot(hotset, statistic, cxhdl, max, threshold, 10, feed, excp, 9, 9) == NULL)
    {
       printf("ERROR:  SpmiAddSetHot 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, max, threshold, feed, excp)
char *stat_inp;
int *max;
int *threshold;
int *feed;
int *excp;
#else
void parse_name(char *stat_inp, int *max, int *threshold, int *feed, int *excp)
#endif
{
   char   stat_work[256];
   char   *stat_out;
   char   *s;

   if (s = strtok(stat_inp, " ,\n"))
   {
      strcpy(stat_work, s);
      if (s = strtok(NULL, " ,\n"))
         *max = atoi(s);
      else
         *max = -1;
      if (s = strtok(NULL, " ,\n"))
         *threshold = atoi(s);
      else
         *threshold = -1;
      if (s = strtok(NULL, " ,\n"))
         *feed = atoi(s);
      else
         *feed = SiHotThreshold;
      if (s = strtok(NULL, " ,\n"))
         *excp = atoi(s);
      else
         *excp = SiHotNoException;
   }

   stat_out = strtok(stat_work, "/");
   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];
    int     max;
    int     threshold;
    int     feed;
    int     excp;

    while (fgets(stat_inp, sizeof(stat_inp), in_file) && stat_counter <= 28)
    {
       parse_name((char *)&stat_inp, &max, &threshold, &feed, &excp);
       add_stats(max, threshold, feed, excp);
    }
    fclose(in_file);
    return;
}

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

void read_input()
{
    char    stat_inp[128];
    int     max;
    int     threshold;
    int     feed;
    int     excp;

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

    printf("\nWhen the last peer set has been entered, key 99.");
    printf("\nEnter peer path name [max [threshold [feed [exception]]]]:  ");
    while (gets(stat_inp) && stat_counter <= 28)
    {
       if (!strcmp(stat_inp, "99"))
          break;
       parse_name((char *)&stat_inp, &max, &threshold, &feed, &excp);
       add_stats(max, threshold, feed, excp);
       if (stat_counter <= 28)
          printf("\nEnter peer path name [max [threshold [feed [exception]]]]:  ");
       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, SpmiHotlog 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 SpmiHotlog 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:  SpmiHotlog 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            explainflag = 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:x")) != 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 'x': 
                explainflag++;
                break;
      }
   }

   if(!fileflag && !manflag)
        errflag++;
   if (errflag && !explainflag) 
   {
      printf("Usage:  SpmiHotlog {-f infile|-m|-mf infile} [-i seconds] [-o outfile]\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("                -b  Start SpmiHotlog 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 SpmiHotlog 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");
      printf("                -x  Print explanation of input line format.\n");
      must_exit();
   }
   if (explainflag) 
   {
   	printf("The input file should contain the path names of peer sets of the\n");
   	printf("metrics to be monitored.  Note that the path names of peer sets\n");
   	printf("lack the lowest level context name.  For example, the peer metrics\n");
   	printf("\"xfer\" for a machine with three disks might be:\n");
   	printf("      Disk/hdisk0/xfer\n");
   	printf("      Disk/hdisk1/xfer\n");
   	printf("      Disk/hdisk2/xfer\n");
   	printf("The corresponding peer set path name is:\n");
   	printf("      Disk/xfer\n\n");
   	printf("Following the peer metrics path name, you can specify up-to 4\n");
   	printf("parameters that will control how this program monitors hot metrics:\n\n");
   	printf("Value No   Description                                   Default\n");
   	printf("--------   -------------------------------------------   -------\n");
   	printf("    1      The maximum number of responses to return        2\n\n");
   	printf("    2      The threshold that determines if response     Metric max value\n");
   	printf("           is generated; negative value means thet a     divided by 2;\n");
   	printf("           metric qualifies if its value is less than\n");
   	printf("           the numeric value of threshold.\n\n");
   	printf("    3      Determines how to generate data feeds:           1\n");
   	printf("              0 = never, 1 = according to threshold\n");
   	printf("              2 = always\n\n");
   	printf("    4      Determines if exceptions/traps generated.        0\n");
   	printf("              0 = neither,    1 = exceptions only\n");
   	printf("              2 = traps only, 3 = both exceptions and traps\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;

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

    uname(&uname_struct);
    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)
    {
       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();
}
