blob: 83b4e4aa753d2ad288f905b3a3eac34523eeab51 [file] [log] [blame]
/* Portions of this file are subject to the following copyrights. See
* the Net-SNMP's COPYING file for more details and other copyrights
* that may apply:
*/
/*
* Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms specified in the COPYING file
* distributed with the Net-SNMP package.
*/
#include <net-snmp/net-snmp-config.h>
#include "logmatch.h"
#ifdef HAVE_REGEX_H
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <regex.h>
#include <time.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "util_funcs/header_generic.h"
#include "util_funcs/header_simple_table.h"
/*
* ------------------------------------------------
* This function checks if the filename pattern
* contains the % character indicating a variable
* filename (i.e. it uses date/time format control
* codes, see 'man date') then expands those control
* codes based on current time and sets the
* filename field in the struct.
* Returns 1 if the filename changed, 0 otherwise
* -------------------------------------------------
*/
static int
logmatch_update_filename(const char * pattern, char * currentFilename)
{
time_t t;
struct tm *tmp;
char newFilename[256];
/*
* -------------------------------------------------------------------
* if the filename pattern doesn't have the "%" character just return,
* since there is no need for further processing
* -------------------------------------------------------------------
*/
if (strchr(pattern, '%') == NULL) {
return 0;
}
t = time(NULL);
tmp = localtime(&t);
if (tmp == NULL) {
perror("localtime");
return 0;
}
/* result of expansion must fit into newFilename, otherwise returning */
if (strftime(newFilename, sizeof(newFilename), pattern, tmp) == 0) {
return 0;
}
/* if same as current filename, just return */
if (strcmp(currentFilename, newFilename) == 0) {
return 0;
} else {
/* otherwise update currentFilename and return 1 */
strcpy(currentFilename, newFilename);
return 1;
}
}
struct logmatchstat {
char filenamePattern[256];
char filename[256];
char regEx[256];
char name[256];
FILE *logfile;
long currentFilePosition;
unsigned long globalMatchCounter;
unsigned long currentMatchCounter;
unsigned long matchCounter;
regex_t regexBuffer;
int myRegexError;
int virgin;
int thisIndex;
int frequency;
};
#define MAXLOGMATCH 250
static struct logmatchstat logmatchTable[MAXLOGMATCH];
static int logmatchCount = 0;
/***************************************************************
* *
* updateLogmatch *
* this function is called back by snmpd alarms *
* *
***************************************************************/
static void
updateLogmatch(int iindex)
{
int matchResultCode;
char inbuf[1024];
char perfilename[1024];
FILE *perfile;
unsigned long pos, ccounter, counter;
int result;
int toobig;
int anyChanges = FALSE;
struct stat sb;
char lastFilename[256];
/*
* ------------------------------------
* we can never be sure if this is the
* last time we are being called here,
* so we always update a persistent
* data file with our current file
* position
* ------------------------------------
*/
snprintf(perfilename, sizeof(perfilename), "%s/snmpd_logmatch_%s.pos",
get_persistent_directory(), logmatchTable[iindex].name);
if (logmatchTable[iindex].virgin) {
/*
* ------------------------------------
* this is the first time we are being
* called; let's try to find an old
* file position stored in a persistent
* data file and restore it
* ------------------------------------
*/
if ((perfile = fopen(perfilename, "r"))) {
/*
* ------------------------------------
* the persistent data file exists so
* let's read it out
* ------------------------------------
*/
pos = counter = ccounter = 0;
if (fscanf(perfile, "%lu %lu %lu %s",
&pos, &ccounter, &counter, lastFilename)) {
/*
* ------------------------------------
* the data could be read; now let's
* try to open the logfile to be
* scanned
* ------------------------------------
*/
if (logmatch_update_filename(logmatchTable[iindex].filenamePattern,
lastFilename) == 0) {
/*
* ---------------------------------
* the filename is still the same as
* the one stored in the persistent
* data file.
* ---------------------------------
*/
if ((logmatchTable[iindex].logfile =
fopen(logmatchTable[iindex].filename, "r"))) {
/*
* ------------------------------------
* the log file could be opened; now
* let's try to set the pointer
* ------------------------------------
*/
if (!fseek
(logmatchTable[iindex].logfile, pos, SEEK_SET)) {
/*
* ------------------------------------
* the pointer could be set - this is
* the most that we can do: if the
* pointer is smaller than the file
* size we must assume that the pointer
* still points to where it read the
* file last time; let's restore the
* data
* ------------------------------------
*/
logmatchTable[iindex].currentFilePosition = pos;
logmatchTable[iindex].currentMatchCounter =
ccounter;
}
fclose(logmatchTable[iindex].logfile);
}
}
logmatchTable[iindex].globalMatchCounter = counter;
}
fclose(perfile);
}
logmatchTable[iindex].virgin = FALSE;
}
/*
* -------------------------------------------
* check if a new input file needs to be opened
* if yes, reset counter and position
* -------------------------------------------
*/
if (logmatch_update_filename(logmatchTable[iindex].filenamePattern,
logmatchTable[iindex].filename) == 1) {
logmatchTable[iindex].currentFilePosition = 0;
logmatchTable[iindex].currentMatchCounter = 0;
}
/*
* ------------------------------------
* now the pointer and the counter are
* set either zero or reset to old
* value; now let's try to read some
* data
* ------------------------------------
*/
if (stat(logmatchTable[iindex].filename, &sb) == 0) {
if (logmatchTable[iindex].currentFilePosition > sb.st_size) {
toobig = TRUE;
} else {
toobig = FALSE;
}
if ((logmatchTable[iindex].logfile =
fopen(logmatchTable[iindex].filename, "r"))) {
result =
fseek(logmatchTable[iindex].logfile,
logmatchTable[iindex].currentFilePosition, SEEK_SET);
if (result || toobig || (errno == EINVAL)
|| feof(logmatchTable[iindex].logfile)) {
/*
* ------------------------------------
* when we are here that means we
* could't set the file position maybe
* the file was rotated; let's reset
* the filepointer, but not the counter
* ------------------------------------
*/
logmatchTable[iindex].currentFilePosition = 0;
logmatchTable[iindex].currentMatchCounter = 0;
fseek(logmatchTable[iindex].logfile, 0, SEEK_SET);
anyChanges = TRUE;
}
while (fgets
(inbuf, sizeof(inbuf), logmatchTable[iindex].logfile)) {
matchResultCode =
regexec(&(logmatchTable[iindex].regexBuffer),
inbuf, 0, NULL, REG_NOTEOL);
if (matchResultCode == 0) {
logmatchTable[iindex].globalMatchCounter++;
logmatchTable[iindex].currentMatchCounter++;
logmatchTable[iindex].matchCounter++;
anyChanges = TRUE;
}
}
logmatchTable[iindex].currentFilePosition =
ftell(logmatchTable[iindex].logfile);
fclose(logmatchTable[iindex].logfile);
}
}
/*
* ------------------------------------
* at this point we can be safe that
* our current file position is
* straightened out o.k. - we never
* know if this is the last time we are
* being called so save the position
* in a file
* ------------------------------------
*/
if (anyChanges && (perfile = fopen(perfilename, "w"))) {
/*
* ------------------------------------
* o.k. lets write out our variable
* ------------------------------------
*/
fprintf(perfile, "%lu %lu %lu %s\n",
logmatchTable[iindex].currentFilePosition,
logmatchTable[iindex].currentMatchCounter,
logmatchTable[iindex].globalMatchCounter,
logmatchTable[iindex].filename);
fclose(perfile);
}
}
static void
updateLogmatch_Scheduled(unsigned int registrationNumber,
struct logmatchstat *logmatchtable)
{
updateLogmatch(logmatchtable->thisIndex);
}
/***************************************************************
* *
* logmatch_parse_config *
* parse one line from snmpd.conf *
* *
***************************************************************/
static void
logmatch_parse_config(const char *token, char *cptr)
{
char space_name;
char space_path;
if (logmatchCount < MAXLOGMATCH) {
logmatchTable[logmatchCount].frequency = 30;
logmatchTable[logmatchCount].thisIndex = logmatchCount;
/*
* ------------------------------------
* be careful this counter needs to be
* reset from persistent storage
* ------------------------------------
*/
logmatchTable[logmatchCount].globalMatchCounter = 0;
logmatchTable[logmatchCount].currentMatchCounter = 0;
logmatchTable[logmatchCount].matchCounter = 0;
logmatchTable[logmatchCount].virgin = TRUE;
logmatchTable[logmatchCount].currentFilePosition = 0;
/*
* ------------------------------------
* be careful: the flag 255 must fit to
* the size of regEx as definded in
* logmatch.h
* ------------------------------------
*/
sscanf(cptr, "%255s%c%255s%c %d %255c\n",
logmatchTable[logmatchCount].name,
&space_name,
logmatchTable[logmatchCount].filenamePattern,
&space_path,
&(logmatchTable[logmatchCount].frequency),
logmatchTable[logmatchCount].regEx);
/* fill in filename with initial data */
strcpy(logmatchTable[logmatchCount].filename,
logmatchTable[logmatchCount].filenamePattern);
logmatch_update_filename(logmatchTable[logmatchCount].filenamePattern,
logmatchTable[logmatchCount].filename);
/*
* Log an error then return if any of the strings scanned in were
* larger then they should have been.
*/
if (space_name != ' ') {
snmp_log(LOG_ERR, "logmatch_parse_config: the name scanned " \
"in from line %s is too large. logmatchCount = %d\n",
cptr, logmatchCount);
return;
} else if (space_path != ' ') {
snmp_log(LOG_ERR, "logmatch_parse_config: the file name " \
"scanned in from line %s is too large. logmatchCount = %d\n",
cptr, logmatchCount);
return;
}
/*
* ------------------------------------
* just to be safe "NULL" the end of
* the arary regEx as sscanf won't do
* it with the %c modifier
* ------------------------------------
*/
logmatchTable[logmatchCount].regEx[255] = '\0';
/*
* ------------------------------------
* now compile the regular expression
* ------------------------------------
*/
logmatchTable[logmatchCount].myRegexError =
regcomp(&logmatchTable[logmatchCount].regexBuffer,
logmatchTable[logmatchCount].regEx,
REG_EXTENDED | REG_NOSUB);
if (logmatchTable[logmatchCount].myRegexError) {
char regexErrorString[100];
regerror(logmatchTable[logmatchCount].myRegexError,
&logmatchTable[logmatchCount].regexBuffer,
regexErrorString, 100);
snmp_log(LOG_ERR, "Could not process the logmatch regex - %s," \
"\n since regcomp() failed with - %s\n",
logmatchTable[logmatchCount].regEx, regexErrorString);
}
else if (logmatchTable[logmatchCount].frequency > 0) {
snmp_alarm_register(logmatchTable[logmatchCount].frequency,
SA_REPEAT,
(SNMPAlarmCallback *) updateLogmatch_Scheduled,
&logmatchTable[logmatchCount]);
}
logmatchCount++;
}
}
/***************************************************************
* *
* logmatch_free_config *
* free memory allocated by this mib module *
* *
***************************************************************/
static void
logmatch_free_config(void)
{
int i;
/*
* ------------------------------------
* the only memory we have allocated
* is the memory allocated by regcomp
* ------------------------------------
*/
for (i = 0; i < logmatchCount; i++) {
if (logmatchTable[i].myRegexError == 0)
regfree(&logmatchTable[i].regexBuffer);
}
logmatchCount = 0;
}
#define LOGMATCH_INFO 0
#define LOGMATCH_INDEX 1
#define LOGMATCH_NAME 2
#define LOGMATCH_FILENAME 3
#define LOGMATCH_REGEX 4
#define LOGMATCH_GLOBALCTR 5
#define LOGMATCH_GLOBALCNT 6
#define LOGMATCH_CURRENTCTR 7
#define LOGMATCH_CURRENTCNT 8
#define LOGMATCH_COUNTER 9
#define LOGMATCH_COUNT 10
#define LOGMATCH_FREQ 11
#define LOGMATCH_ERROR 100
#define LOGMATCH_MSG 101
/*
* OID functions
*/
static u_char *
var_logmatch_table(struct variable *vp,
oid * name,
size_t * length,
int exact,
size_t * var_len, WriteMethod ** write_method)
{
static long long_ret;
static char message[1024];
int iindex;
struct logmatchstat *logmatch;
if (vp->magic == LOGMATCH_INFO) {
if (header_generic(vp, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return (NULL);
} else {
if (header_simple_table
(vp, name, length, exact, var_len, write_method,
logmatchCount))
return (NULL);
iindex = name[*length - 1] - 1;
logmatch = &logmatchTable[iindex];
if (logmatch->myRegexError == 0)
updateLogmatch(iindex);
}
switch (vp->magic) {
case LOGMATCH_INFO:
long_ret = MAXLOGMATCH;
return (u_char *) & long_ret;
case LOGMATCH_INDEX:
long_ret = iindex + 1;
return (u_char *) & long_ret;
case LOGMATCH_NAME:
*var_len = strlen(logmatch->name);
return (u_char *) logmatch->name;
case LOGMATCH_FILENAME:
*var_len = strlen(logmatch->filename);
return (u_char *) logmatch->filename;
case LOGMATCH_REGEX:
*var_len = strlen(logmatch->regEx);
return (u_char *) logmatch->regEx;
case LOGMATCH_GLOBALCTR:
case LOGMATCH_GLOBALCNT:
long_ret = (logmatch->globalMatchCounter);
return (u_char *) & long_ret;
case LOGMATCH_CURRENTCTR:
case LOGMATCH_CURRENTCNT:
long_ret = (logmatch->currentMatchCounter);
return (u_char *) & long_ret;
case LOGMATCH_COUNTER:
case LOGMATCH_COUNT:
long_ret = (logmatch->matchCounter);
logmatch->matchCounter = 0;
return (u_char *) & long_ret;
case LOGMATCH_FREQ:
long_ret = logmatch->frequency;
return (u_char *) & long_ret;
case LOGMATCH_ERROR:
if (logmatch->frequency >= 0 && logmatch->myRegexError == 0)
long_ret = 0;
else
long_ret = 1;
return (u_char *) & long_ret;
case LOGMATCH_MSG:
regerror(logmatch->myRegexError, &(logmatch->regexBuffer), message,
sizeof(message));
*var_len = strlen(message);
return (u_char *) message;
default:
DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_logmatch_table\n",
vp->magic));
}
return NULL;
}
void
init_logmatch(void)
{
struct variable2 logmatch_info[] = {
{LOGMATCH_INFO, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 0}
};
struct variable2 logmatch_table[] = {
{LOGMATCH_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {1}},
{LOGMATCH_NAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {2}},
{LOGMATCH_FILENAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {3}},
{LOGMATCH_REGEX, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {4}},
{LOGMATCH_GLOBALCTR, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {5}},
{LOGMATCH_GLOBALCNT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {6}},
{LOGMATCH_CURRENTCTR, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {7}},
{LOGMATCH_CURRENTCNT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {8}},
{LOGMATCH_COUNTER, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {9}},
{LOGMATCH_COUNT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {10}},
{LOGMATCH_FREQ, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {11}},
{LOGMATCH_ERROR, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {100}},
{LOGMATCH_MSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_logmatch_table, 1, {101}}
};
/*
* Define the OID pointer to the top of the mib tree that we're
* registering underneath
*/
oid logmatch_info_oid[] = { NETSNMP_UCDAVIS_MIB, 16, 1 };
oid logmatch_variables_oid[] = { NETSNMP_UCDAVIS_MIB, 16, 2, 1 };
/*
* register ourselves with the agent to handle our mib tree
*/
REGISTER_MIB("ucd-snmp/logmatch", logmatch_info, variable2,
logmatch_info_oid);
REGISTER_MIB("ucd-snmp/logmatch", logmatch_table, variable2,
logmatch_variables_oid);
snmpd_register_config_handler("logmatch", logmatch_parse_config,
logmatch_free_config,
"logmatch name path cycletime regex");
}
#endif /* HAVE_REGEX_H */