
/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: collect.c,v 1.4 93/10/11 18:41:29 mogul Exp $";
 
/*
        collect.c
*/

/*
 *  CHANGES:
 *    27Jan89 Merit: Added check for max number of hosts
 *    13Oct89 Merit: Added new parameters:
 *
 *       -m mode
 *      Set file mode (octal) of log files on creation.  Handy if you don't
 *      want to mess with umask.
 *
 *       -u
 *      Write logfile times in Universal Time instead of local.
 *
 *      Rel 2.4:
 *   30Nov89 Merit/ISI: New parameter for terse mode.
 *       -t
 *      Decrease volume of log file.
 *        Rel 3.0:
 *   ISI: Cosmetic changes to clarify logic. 
 *   ISI: Check syntax of enum file.
 *        Rel 3.01:
 *   ISI: Fix bug that kept -p parm from working 
 *   ISI: Fix bug in clearing multiple objects -- found by Robert Elz. 
 *   Aug93/DECWRL: Alpha/OSF port
 *
 */
#include <stdio.h>
#include <rpc/rpc.h>
#include <errno.h>
#include <ctype.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/file.h>
#include <netdb.h>

#include "collect.h"

char *HELPSTR = "Usage:\n\
  collect object(s)\
\n\t [-e enumFile]\
\n\t [-h host1] .. [-h hostn]\
\n\t [-i min] \tPolling interval\
\n\t [-c min] \tCheckpoint interval\
\n\t [-p port] \tTCP port to connect to\
\n\t [-r min] \tClear interval\
\n\t [-m mode] \tSet log file mode\
\n\t [-u] \t\tFormat times in universal time\
\n\t [-d] \t\tTrace & log to stdout\
\n\t [-dl] \t\tTrace to stdout, log to files\
\n\t [-dx] \t\tTrace & hex dump to stdout\n";

int     interval,        /* Seconds between queries */
        chkpnt,          /* Seconds between checkpoints */
        clear;           /* Seconds between clears */
        
struct collector rspy[MAX_ADDRS];        

FILE *tracefp = NULL;                /* File to save trace */
boolean hexd = FALSE,
        needlog = TRUE;
extern short port;
extern boolean print_utime;
extern boolean tersemode;
extern int openmode;
char out[256];
boolean isrspy = FALSE;
time_t CurrTime;  /* Current time (seconds) */
#ifdef XPARSE
long CurrTimeMS;
#endif

void hex_dump();
void netclose();
void flush_xdr();
void closeconn();

XDR R_XDR, W_XDR;
XDR *readp = &R_XDR, *writep = &W_XDR;

#define error_out(x,y) {fprintf(stderr,"collect: %s %s\n",x,y); exit(1);}

void
Usage()
{
   printf(HELPSTR);
   exit(1);
}

char *argscan(argcp, argvp)
   int *argcp;
   char ***argvp;
{
   char *cp;
        
   if (*(cp = 2+**argvp)) 
      return(cp);
   else if  (--*argcp > 0)
      return(*++*argvp);
   Usage();
} /* argscan */

main(argc,argv)
    int argc;
    char *argv[];
{
   int     i, j, h, op, rc,
           host = 0;        /* index for collector structure */
   u_int32 temp;
   time_t nextread;
   char *cp, *number;
   FILE *fp;
   struct tm *tp;
   
   tracefp = NULL;

   if (argc == 1) 
      Usage();

   /* Initialization stuff */
      CurrTime = time(0);
      tp = localtime(&CurrTime);
      /* nextread = creation - (creation mod 60 min) */
      rspy[0].creation = CurrTime;
      nextread = CurrTime - ((tp->tm_min*60) + tp->tm_sec);

      while (--argc > 0 && *++argv) {
         if (!strncmp("-e", *argv, 2)) {
             cp = argscan(&argc, &argv);
             if ((fp = fopen(cp, "r")) != NULL) {
              while ((rc = Scan_Cmd(fp, NULL)) == CMD_OK);
              if (rc != CMD_EOF) {
                fprintf(stderr, "CONFIG FILE ERROR \n");
                fflush(stdout);
                exit(1);
              }
              fclose(fp);
                }
             else {
                perror("collect: Cannot open parm file") ;
                exit(1) ;
                }
             }
         else if (!strncmp("-h", *argv, 2)) {
            if (host >= MAX_ADDRS - 1) {
                perror("collect: Too many hosts");
                exit(1);
            }
            cp = argscan(&argc, &argv);
            if (resolve_name(cp, &rspy[host].inetaddr, 1) < 1) {
               error_out("Bad hostname:", cp);
               }
            else {
               strcpy(rspy[host++].hostname, cp);
               if (tracefp) {
                  temp = ntohl(rspy[host-1].inetaddr.s_addr);
                  fprintf(tracefp,"Resolved: %s = %d.%d.%d.%d\n", 
                     rspy[host-1].hostname,
                     (temp>>24), ((temp>>16)&0xff), 
                     ((temp>>8)&0xff), (temp&0xff));
                  }
               }
            }
         else if (!strcmp("-d", *argv)) {
             /* trace & log info to stdout */
             needlog = FALSE;
             tracefp = stdout;
             }
         else if (!strcmp("-dl", *argv)) {
             /* trace info to stdout, log info to files */
             tracefp = stdout;
             }
         else if (!strcmp("-dx", *argv)) {
             /* trace to stdout, log is hex dump to stdout */
             tracefp = stdout;
             hexd = TRUE;
             needlog = FALSE;
             }
         else if (!strncmp("-i", *argv, 2)) {
             number = argscan(&argc, &argv);
             for (cp = number; *cp; cp++) 
                if (! isdigit(*cp)) {
                   error_out("Bad polling interval:", number);
                   }
             interval = 60*atoi(number);
             }
         else if (!strncmp("-c", *argv, 2)) {
             number = argscan(&argc, &argv);
             for (cp = number; *cp; cp++) 
                if (! isdigit(*cp)) {
                   error_out("Bad checkpoint interval:", number);
                   }
             chkpnt = 60*atoi(number);
             }
         else if (!strncmp("-r", *argv, 2)) {
             number = argscan(&argc, &argv);
             for (cp = number; *cp; cp++) 
                if (! isdigit(*cp)) {
                   error_out("Bad clear interval:", number);
                   }
             clear = 60*atoi(number);
             }
         else if (!strncmp("-m", *argv, 2)) {
             /* set log file mode */
             number = argscan(&argc, &argv);
             if (sscanf(number, "%o", &openmode) != 1)
                error_out("Bad open mode:", number);
             }
         else if (!strcmp("-u", *argv)) {
             /* print times in universal time */
             print_utime = TRUE;
             }
     else if (!strcmp("-t", *argv)) {
              /* generate log in terse mode */
          tersemode = TRUE;
             }
         else if (!strncmp("-p", *argv, 2)) {
             number = argscan(&argc, &argv);
             if (!(port = get_port(number,"collect")))
                return;
             }
         else if (**argv == '-') {
            fprintf(stderr, "collect: Bad option: %s\n", *argv);
            Usage();
            }
         else { /* copy to first blank slot for all hosts */
            for (j=0; j<MAX_STATS; j++) 
               if (!rspy[0].s[j].query) {
                  for (h=0; h < MAX_ADDRS; h++) {
                     strncpy(rspy[h].s[j].sname, *argv, 
                                     sizeof(rspy[h].s[j].sname)-1);
                     rspy[h].s[j].query = TRUE;
                     }
                  break;
                  }
            }
         }
         
   if (!rspy[0].s[0].query) {
      fprintf(stderr, "collect: No object specified!\n");
      Usage();
      }
      
   if (! host) {
      /* Default host name */
      gethostname(rspy[0].hostname, sizeof(rspy[0].hostname));
      if (!resolve_name(rspy[0].hostname, &rspy[0].inetaddr, 1))
         error_out("Bad default host name:", rspy[0].hostname);
      }
      
   for (host = 0; strlen(rspy[host].hostname); host++) {
        rspy[host].creation = rspy[0].creation;
        rspy[host].nextclear = (clear)? nextread: 0;
       }
   if ((clear || chkpnt) && (!interval))
      interval = ((chkpnt<clear) && chkpnt)? chkpnt: clear;
    
   /*************************************************************
    *   MAIN LOOP.  Execute only once if interval == 0.         *
    *                                                           *
    *************************************************************/
    while (1) {  
      for (host = 0; strlen(rspy[host].hostname); host++) {
         if (tracefp) fprintf(tracefp,"\nStatspy host: %s\n",rspy[host].hostname);
         /* open tcp connection */
         if (openconn(&rspy[host].inetaddr)) {

            /* go through collection structures */
            CurrTime = time(0);
            for (i = 0; rspy[host].s[i].query ; i++) {
               signal(SIGALRM, netclose); /* reinit timer for each read */
               alarm(TIMEOUT);
               
                   /* Send READ/READCLEAR op */
               op = (clear && (CurrTime >= rspy[host].nextclear))? 
                                                 READCL_op: READ_op;
               if (!read_stats(op, rspy[host].s[i].sname)) break;
               
                  /* Fetch and process reply */
               if (hexd)
                  hex_dump(&R_XDR, tracefp, out, sizeof(out), 0, 
                                  DTYP_bits);
               else  {
                   logfp = NULL;
                   Get_reply(&R_XDR, &rspy[host]);
               }
            }  /* end of loop over collection structures for host */
          
            if (clear) {   /* Set next clear time */
               while (CurrTime >= rspy[host].nextclear)
                  rspy[host].nextclear += clear;
            }
            signal(SIGALRM, NULL);        /* clear timeout */
            alarm(0);
            closeconn();
         }
            
         if (tracefp) fflush(tracefp);
      } /* end loop over hosts */
         
      if (interval == 0) break ;
      
      CurrTime = time(0);
      while (CurrTime >= nextread) nextread += interval;
      sleep((nextread - CurrTime));
      } /* keep going */

         
} /* main */

/*
 *  Send op = READ/READCLEAR operator to remote host.
 *
 */
boolean
read_stats(op, objname)
   int op;
   char *objname;
{
   char *cp, temp[200];
   int i = op;

   sprintf(temp, "%s ;", objname);
   cp = temp;

   if (xdr_int(&W_XDR, &i) &&
       xdr_string(&W_XDR, &cp, strlen(temp))) {
      if (tracefp) 
         fprintf(tracefp,"%s '%s'\n", 
                   (op == READCL_op)? "Read+Clear": "Read", objname);
      flush_xdr();
      return(TRUE);
      }
   return(FALSE);
} /* read_stats */
