| /* |
| * snmpdelta.c - Monitor deltas of integer valued SNMP variables |
| * |
| */ |
| /********************************************************************** |
| * |
| * Copyright 1996 by Carnegie Mellon University |
| * |
| * All Rights Reserved |
| * |
| * Permission to use, copy, modify, and distribute this software and its |
| * documentation for any purpose and without fee is hereby granted, |
| * provided that the above copyright notice appear in all copies and that |
| * both that copyright notice and this permission notice appear in |
| * supporting documentation, and that the name of CMU not be |
| * used in advertising or publicity pertaining to distribution of the |
| * software without specific, written prior permission. |
| * |
| * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| * SOFTWARE. |
| * |
| **********************************************************************/ |
| |
| #include <config.h> |
| |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #include <stdio.h> |
| #include <ctype.h> |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_SYS_SELECT_H |
| #include <sys/select.h> |
| #endif |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if HAVE_NETDB_H |
| #include <netdb.h> |
| #endif |
| #if HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| |
| #include "asn1.h" |
| #include "snmp_api.h" |
| #include "snmp_impl.h" |
| #include "snmp_client.h" |
| #include "mib.h" |
| #include "snmp.h" |
| #include "system.h" |
| #include "int64.h" |
| #include "snmp_parse_args.h" |
| #include "snmp_logging.h" |
| #include "default_store.h" |
| |
| #define MAX_ARGS 256 |
| |
| const char *SumFile = "Sum"; |
| char *Argv[MAX_ARGS]; |
| int Argc; |
| |
| /* Information about the handled variables */ |
| struct varInfo { |
| char *name; |
| oid *info_oid; |
| int type; |
| size_t oidlen; |
| char descriptor[64]; |
| u_int value; |
| struct counter64 c64value; |
| float max; |
| time_t time; |
| int peak_count; |
| float peak; |
| float peak_average; |
| int spoiled; |
| }; |
| |
| |
| void usage (void) |
| { |
| fprintf(stderr, "Usage:\nsnmpdelta "); |
| snmp_parse_args_usage(stderr); |
| fprintf(stderr, "[-f commandFile]\n[-l] [-L SumFileName] [-s] [-k] [-t] [-S] [-v vars/pkt]\n [-p period] [-P peaks] oid [oid ...]\n"); |
| snmp_parse_args_descriptions(stderr); |
| fprintf(stderr, " -l\t\twrite configuration to file\n"); |
| fprintf(stderr, " -f config\tload configuration from file\n"); |
| fprintf(stderr, " -p period\tspecifies the poll period\n"); |
| fprintf(stderr, " -P peaks\treporting period in poll periods\n"); |
| fprintf(stderr, " -v vars/pkt\tnumber of variables per packet\n"); |
| fprintf(stderr, " -k\t\tkeep seconds in output time\n"); |
| fprintf(stderr, " -m\t\tshow max values\n"); |
| fprintf(stderr, " -S\t\tlog to a sum file\n"); |
| fprintf(stderr, " -s\t\tshow timestamps\n"); |
| fprintf(stderr, " -t\t\tget timing from agent\n"); |
| fprintf(stderr, " -T\t\tprint output in tabular form\n"); |
| fprintf(stderr, " -L sumfile\tspecifies the sum file name\n"); |
| } |
| |
| int wait_for_peak_start(int period, int peak) |
| { |
| struct timeval m_time, *tv = &m_time; |
| struct tm tm; |
| time_t SecondsAtNextHour; |
| int target = 0; |
| int seconds; |
| |
| seconds = period * peak; |
| |
| /* Find the current time */ |
| gettimeofday(tv, (struct timezone *)0); |
| |
| /* Create a tm struct from it */ |
| memcpy(&tm, localtime((time_t *)&tv->tv_sec), sizeof(tm)); |
| |
| /* Calculate the next hour */ |
| tm.tm_sec = 0; |
| tm.tm_min = 0; |
| tm.tm_hour++; |
| SecondsAtNextHour = mktime(&tm); |
| |
| /* Now figure out the amount of time to sleep */ |
| target = (SecondsAtNextHour - tv->tv_sec) % seconds; |
| |
| return target; |
| } |
| |
| void print_log(char *file, char *message) |
| { |
| FILE *fp; |
| |
| fp = fopen(file, "a"); |
| if (fp == NULL){ |
| fprintf(stderr, "Couldn't open %s\n", file); |
| return; |
| } |
| fprintf(fp, "%s\n", message); |
| fclose(fp); |
| } |
| |
| void sprint_descriptor(char *buffer, |
| struct varInfo *vip) |
| { |
| char buf[SPRINT_MAX_LEN], *cp; |
| |
| sprint_objid(buf, vip->info_oid, vip->oidlen); |
| for(cp = buf; *cp; cp++) |
| ; |
| while(cp >= buf){ |
| if (isalpha(*cp)) |
| break; |
| cp--; |
| } |
| while(cp >= buf){ |
| if (*cp == '.') |
| break; |
| cp--; |
| } |
| cp++; |
| if (cp < buf) |
| cp = buf; |
| strcpy(buffer, cp); |
| } |
| |
| void processFileArgs(char *fileName) |
| { |
| FILE *fp; |
| char buf[260], *cp; |
| int blank, linenumber = 0; |
| |
| fp = fopen(fileName, "r"); |
| if (fp == NULL) |
| return; |
| while (fgets(buf, sizeof(buf), fp)){ |
| linenumber++; |
| if (strlen(buf) > (sizeof(buf)-2)){ |
| fprintf(stderr, "Line too long on line %d of %s\n", |
| linenumber, fileName); |
| exit(1); |
| } |
| if (buf[0] == '#') |
| continue; |
| blank = TRUE; |
| for(cp = buf; *cp; cp++) |
| if (!isspace(*cp)){ |
| blank = FALSE; |
| break; |
| } |
| if (blank) |
| continue; |
| Argv[Argc] = (char *)malloc(strlen(buf)); /* ignore newline */ |
| buf[strlen(buf) - 1] = 0; |
| strcpy(Argv[Argc++], buf); |
| } |
| fclose(fp); |
| return; |
| } |
| |
| void wait_for_period(int period) |
| { |
| struct timeval m_time, *tv = &m_time; |
| struct tm tm; |
| int count; |
| static int target = 0; |
| time_t nexthour; |
| |
| gettimeofday(tv, (struct timezone *)0); |
| |
| if (target){ |
| target += period; |
| } else { |
| memcpy(&tm, localtime((time_t *)&tv->tv_sec), sizeof(tm)); |
| tm.tm_sec = 0; |
| tm.tm_min = 0; |
| tm.tm_hour++; |
| nexthour = mktime(&tm); |
| |
| target = (nexthour - tv->tv_sec) % period; |
| if (target == 0) |
| target = period; |
| target += tv->tv_sec; |
| } |
| |
| tv->tv_sec = target - tv->tv_sec; |
| if (tv->tv_usec != 0){ |
| tv->tv_sec--; |
| tv->tv_usec = 1000000 - tv->tv_usec; |
| } |
| if (tv->tv_sec < 0){ |
| /* ran out of time, schedule immediately */ |
| tv->tv_sec = 0; |
| tv->tv_usec = 0; |
| } |
| count = 1; |
| while(count != 0){ |
| count = select(0, 0, 0, 0, tv); |
| switch (count){ |
| case 0: |
| break; |
| case -1: |
| /* FALLTHRU */ |
| default: |
| snmp_log_perror("select"); |
| break; |
| } |
| } |
| } |
| |
| oid sysUpTimeOid[9] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 }; |
| size_t sysUpTimeLen = 9; |
| |
| int main(int argc, char *argv[]) |
| { |
| struct snmp_session session, *ss; |
| struct snmp_pdu *pdu, *response; |
| struct variable_list *vars; |
| int arg; |
| char *gateway; |
| |
| int count, current_name = 0; |
| struct varInfo varinfo[128], *vip; |
| u_int value=0; |
| struct counter64 c64value; |
| float printvalue; |
| int period = 1; |
| int deltat = 0, timestamp = 0, fileout = 0, dosum = 0, printmax = 0; |
| int keepSeconds = 0, peaks = 0; |
| int tableForm = 0; |
| time_t last_time = 0; |
| time_t this_time; |
| time_t delta_time; |
| int sum; /* what the heck is this for, its never used? */ |
| char filename[128]; |
| struct timeval tv; |
| struct tm tm; |
| char timestring[64], valueStr[64], maxStr[64], peakStr[64]; |
| char outstr[256]; |
| int status; |
| int begin, end, last_end; |
| int varbindsPerPacket = 60; |
| int print = 1; |
| int exit_code = 0; |
| |
| arg = snmp_parse_args(argc, argv, &session, NULL, NULL); |
| gateway = session.peername; |
| |
| /* |
| * usage: snmpdelta gateway-name community-name [-f commandFile] [-l] |
| * [-v vars/pkt] [-s] [-t] [-S] [-p period] [-P peaks] [-k] |
| * [-L SumFileName] variable list .. |
| */ |
| |
| Argc = 0; |
| for(; arg < argc; arg++){ |
| if (argv[arg][0] == '-'){ |
| switch(argv[arg][1]){ |
| case 'd': |
| snmp_set_dump_packet(1); |
| break; |
| case 'p': |
| period = atoi(argv[++arg]); |
| break; |
| case 'P': |
| peaks = atoi(argv[++arg]); |
| break; |
| case 'v': |
| varbindsPerPacket = atoi(argv[++arg]); |
| break; |
| case 't': |
| deltat = 1; |
| break; |
| case 's': |
| timestamp = 1; |
| break; |
| case 'S': |
| dosum = 1; |
| break; |
| case 'm': |
| printmax = 1; |
| break; |
| case 'f': |
| processFileArgs(argv[++arg]); |
| break; |
| case 'l': |
| fileout = 1; |
| break; |
| case 'L': |
| SumFile = argv[++arg]; |
| break; |
| case 'k': |
| keepSeconds = 1; |
| break; |
| case 'T': |
| tableForm = 1; |
| break; |
| default: |
| fprintf(stderr, "Invalid option: -%c\n", argv[arg][1]); |
| usage(); |
| exit(1); |
| break; |
| } |
| } else { |
| Argv[Argc++] = strdup(argv[arg]); |
| } |
| } |
| |
| for (arg = 0; arg < Argc; arg++) |
| varinfo[current_name++].name = Argv[arg]; |
| |
| if (current_name == 0){ |
| usage(); |
| exit(1); |
| } |
| |
| if (dosum){ |
| varinfo[current_name++].name = 0; |
| } |
| |
| SOCK_STARTUP; |
| |
| /* open an SNMP session */ |
| ss = snmp_open(&session); |
| if (ss == NULL){ |
| /* diagnose snmp_open errors with the input struct snmp_session pointer */ |
| snmp_sess_perror("snmpdelta", &session); |
| SOCK_CLEANUP; |
| exit(1); |
| } |
| |
| if (tableForm && timestamp) { |
| printf("%s", gateway); |
| } |
| for(count = 0; count < current_name; count++){ |
| vip = varinfo + count; |
| if (vip->name){ |
| vip->oidlen = MAX_OID_LEN; |
| vip->info_oid = (oid *)malloc(sizeof(oid) * vip->oidlen); |
| if (snmp_parse_oid(vip->name, vip->info_oid, &vip->oidlen) == NULL) { |
| snmp_perror(vip->name); |
| SOCK_CLEANUP; |
| exit(1); |
| } |
| sprint_descriptor(vip->descriptor, vip); |
| if (tableForm) printf("\t%s", vip->descriptor); |
| } else { |
| vip->oidlen = 0; |
| strcpy(vip->descriptor, SumFile); |
| } |
| vip->value = 0; |
| zeroU64(&vip->c64value); |
| vip->time = 0; |
| vip->max = 0; |
| if (peaks){ |
| vip->peak_count = -1; |
| vip->peak = 0; |
| vip->peak_average = 0; |
| } |
| } |
| |
| wait_for_period(period); |
| |
| end = current_name; |
| sum = 0; |
| while(1){ |
| pdu = snmp_pdu_create(SNMP_MSG_GET); |
| |
| if (deltat) |
| snmp_add_null_var(pdu, sysUpTimeOid, sysUpTimeLen); |
| |
| if (end == current_name) |
| count = 0; |
| else |
| count = end; |
| begin = count; |
| for(; count < current_name |
| && count < begin + varbindsPerPacket - deltat; count++){ |
| if (varinfo[count].oidlen) |
| snmp_add_null_var(pdu, varinfo[count].info_oid, varinfo[count].oidlen); |
| } |
| last_end = end; |
| end = count; |
| |
| retry: |
| status = snmp_synch_response(ss, pdu, &response); |
| if (status == STAT_SUCCESS){ |
| if (response->errstat == SNMP_ERR_NOERROR){ |
| if (timestamp){ |
| gettimeofday(&tv, (struct timezone *)0); |
| memcpy(&tm, localtime((time_t *) &tv.tv_sec), sizeof(tm)); |
| if (((period % 60) |
| && (!peaks || ((period * peaks) % 60))) |
| || keepSeconds) |
| sprintf(timestring, " [%02d:%02d:%02d %d/%d]", |
| tm.tm_hour, tm.tm_min, tm.tm_sec, |
| tm.tm_mon+1, tm.tm_mday); |
| else |
| sprintf(timestring, " [%02d:%02d %d/%d]", |
| tm.tm_hour, tm.tm_min, |
| tm.tm_mon+1, tm.tm_mday); |
| } |
| |
| vars = response->variables; |
| if (deltat){ |
| if (!vars){ |
| fprintf(stderr, "Missing variable in reply\n"); |
| continue; |
| } else { |
| this_time = *(vars->val.integer); |
| } |
| vars = vars->next_variable; |
| } else { |
| this_time = 1; |
| } |
| |
| for(count = begin; count < end; count++) { |
| vip = varinfo + count; |
| |
| if (vip->oidlen){ |
| if (!vars){ |
| fprintf(stderr, "Missing variable in reply\n"); |
| break; |
| } |
| vip->type = vars->type; |
| if (vars->type == ASN_COUNTER64) { |
| u64Subtract(vars->val.counter64, &vip->c64value, &c64value); |
| memcpy(&vip->c64value, &vars->val.counter64, |
| sizeof(struct counter64)); |
| } else { |
| value = *(vars->val.integer) - vip->value; |
| vip->value = *(vars->val.integer); |
| } |
| vars = vars->next_variable; |
| } else { |
| value = sum; |
| sum = 0; |
| } |
| delta_time = this_time - vip->time; |
| if (delta_time <= 0) |
| delta_time = 100; |
| last_time = vip->time; |
| vip->time = this_time; |
| if (last_time == 0) |
| continue; |
| |
| if (vip->oidlen && vip->type != ASN_COUNTER64) { |
| sum += value; |
| } |
| |
| if (tableForm) { |
| if (count == begin) sprintf(outstr, "%s", timestring+1); |
| else outstr[0] = 0; |
| } |
| else |
| sprintf(outstr, "%s %s", timestring, vip->descriptor); |
| |
| if (deltat || tableForm){ |
| if (vip->type == ASN_COUNTER64) { |
| fprintf(stderr, "time delta and table form not supported for counter64s\n"); |
| exit(1); |
| } else { |
| printvalue = ((float)value * 100) / delta_time; |
| if (tableForm) sprintf(valueStr, "\t%.2f", printvalue); |
| else sprintf(valueStr, " /sec: %.2f", printvalue); |
| } |
| } else { |
| printvalue = (float)value; |
| sprintf(valueStr, " /%d sec: ", period); |
| printU64(valueStr+strlen(valueStr), &c64value); |
| } |
| |
| if (!peaks){ |
| strcat(outstr, valueStr); |
| } else { |
| print = 0; |
| if (vip->peak_count == -1){ |
| if (wait_for_peak_start(period, peaks) == 0) |
| vip->peak_count = 0; |
| } else { |
| vip->peak_average += printvalue; |
| if (vip->peak < printvalue) |
| vip->peak = printvalue; |
| if (++vip->peak_count == peaks){ |
| if (deltat) |
| sprintf(peakStr, |
| " /sec: %.2f (%d sec Peak: %.2f)", |
| vip->peak_average/vip->peak_count, |
| period, vip->peak); |
| else |
| sprintf(peakStr, |
| " /%d sec: %.0f (%d sec Peak: %.0f)", |
| period, |
| vip->peak_average/vip->peak_count, |
| period, vip->peak); |
| vip->peak_average = 0; |
| vip->peak = 0; |
| vip->peak_count = 0; |
| print = 1; |
| strcat(outstr, peakStr); |
| } |
| } |
| } |
| |
| if (printmax){ |
| if (printvalue > vip->max){ |
| vip->max = printvalue; |
| } |
| if (deltat) |
| sprintf(maxStr, " (Max: %.2f)", vip->max); |
| else |
| sprintf(maxStr, " (Max: %.0f)", vip->max); |
| strcat(outstr, maxStr); |
| } |
| |
| if (print){ |
| if (fileout){ |
| sprintf(filename, "%s-%s", gateway, vip->descriptor); |
| print_log(filename, outstr + 1); |
| } else { |
| if (tableForm) printf("%s", outstr); |
| else printf("%s\n", outstr + 1); |
| fflush(stdout); |
| } |
| } |
| } |
| if (end == last_end && tableForm) printf("\n"); |
| } else { |
| if (response->errstat == SNMP_ERR_TOOBIG){ |
| if (response->errindex <= varbindsPerPacket |
| && response->errindex > 0){ |
| varbindsPerPacket = response->errindex - 1; |
| } else { |
| if (varbindsPerPacket > 30) |
| varbindsPerPacket -= 5; |
| else |
| varbindsPerPacket--; |
| } |
| if (varbindsPerPacket <= 0) |
| { |
| exit_code = 5; |
| break; |
| } |
| end = last_end; |
| continue; |
| } else if (response->errstat == SNMP_ERR_NOSUCHNAME){ |
| fprintf(stderr, "This object doesn't exist on %s: ", gateway); |
| for(count = 1, vars = response->variables; |
| vars && count != response->errindex; |
| vars = vars->next_variable, count++) |
| ; |
| if (vars) |
| fprint_objid(stderr, vars->name, vars->name_length); |
| fprintf(stderr, "\n"); |
| /* Don't exit when OIDs from file are not found on agent |
| exit_code = 1; |
| break; |
| */ |
| } else { |
| fprintf(stderr, "Error in packet: %s\n", |
| snmp_errstring(response->errstat)); |
| exit_code = 1; |
| break; |
| } |
| |
| /* retry if the errored variable was successfully removed */ |
| pdu = snmp_fix_pdu(response, SNMP_MSG_GET); |
| snmp_free_pdu(response); |
| response = NULL; |
| if (pdu != NULL) |
| goto retry; |
| } |
| |
| } else if (status == STAT_TIMEOUT){ |
| fprintf(stderr, "Timeout: No Response from %s\n", gateway); |
| response = 0; |
| break; |
| } else { /* status == STAT_ERROR */ |
| snmp_sess_perror("snmpdelta", ss); |
| response = 0; |
| break; |
| } |
| |
| if (response) |
| snmp_free_pdu(response); |
| if (end == current_name){ |
| wait_for_period(period); |
| } |
| } |
| snmp_close(ss); |
| SOCK_CLEANUP; |
| return(exit_code); |
| } |