1
0
Files
2022-09-29 17:59:04 +03:00

434 lines
12 KiB
C++

/*
* logger.c++ -- implementation of event daemon class that manages
* logging and the event log.
*
* $Revision: 1.5 $
*/
/*
* Copyright 1992 Silicon Graphics, Inc. All rights reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
extern "C" {
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <syslog.h>
#include <time.h>
};
#include <osfcn.h>
#include "event.h"
#include "daemonName.h"
#include "server.h"
#include "logger.h"
#include "cfg.h"
extern "C" {
#include "eventStrings.h"
void syslog (int p, const char *m, ...);
};
extern EV_cfg *cfg;
/*
* EV_logger::EV_logger - instantiate the event logger function of the server
*
* Arguments are the maximum size a log can grow to, the number of logs there
* can be, and the logfilename to use.
*
* WARNING if EV_MAX_NUM_LOGS ever goes beyond 10, this will need to be
* changed. SUFFIX_LEN and the routine for generating names must change to
* reflect double digit suffixes.
*/
EV_logger::EV_logger(int logSize, int numLogs, const char *logfilename) {
#define SUFFIX_LEN 2
char suffix [SUFFIX_LEN + 1];
suffix[0] = '.'; /* the separator */
suffix[2] = '\0'; /* NULL terminate */
log = 0; /* init FILE * */
currsize = 0; /* init size of log */
/*
* flushcnt is used to determine how often to force the log buffer to
* disk. The value 1 flushes each record. Buffering would be more
* efficient, but I don't know a good value yet.
*/
flushcnt = 1; // XXXX This will be a parameter in the future
/*
* sanity check the arguments
*/
if (logSize > 0 && logSize <= EV_MAX_LOG_SIZE) {
logsize = logSize;
}
else {
logsize = EV_LOG_SIZE;
}
if (numLogs > 0 && numLogs < EV_MAX_NUM_LOGS) {
numlogs = numLogs;
}
else {
numlogs = EV_NUM_LOGS;
}
if (!logfilename || (strlen (logfilename) > (MAXPATHLEN - SUFFIX_LEN))) {
logFileNames[0] = new char [strlen(EV_LOGFILE_NAME) + 1];
}
else {
logFileNames[0] = new char [strlen(logfilename) + 1];
strcpy(logFileNames[0], logfilename);
}
/*
* Logfile names are generated by adding suffixes of .<n> to the name
* passed in.
* Note that if we go to double digit suffixes, this will
* break.
*/
for (int i = 1; i < numlogs; i++) {
logFileNames[i] = new char [strlen(logfilename) +
SUFFIX_LEN + 1];
strcpy (logFileNames[i], logfilename);
suffix[1] = i + '0';
strcat (logFileNames[i], suffix);
}
}
EV_logger::~EV_logger(void) {
syslog(LOG_INFO, "EV_logger::~EV_logger");
}
/*
* EV_logger::openLog -- open the eventlog file
*
* returns EV_OK if successful, -1 otherwise. Note that the FILE *log
* is NULL if the open fails.
*
* This function only opens THE eventlog file. It handles existing files,
* full files, empty files, and non-existent files.
*/
EV_stat
EV_logger::openLog(void) {
struct stat fstats;
if (stat(logFileNames[0], &fstats) == 0) { /* does it exist */
// log exists. See if its full
/*
* the test is a guess since the st_size is in bytes.
* My idea of size is number of records. This is a conservative
* test since there will be few records of maximum size.
*/
if (fstats.st_size >= (EV_MAX_LOG_REC_SIZE * logsize)) {
cycleLogs();
}
else {
currsize = (int) (fstats.st_size / EV_MAX_LOG_REC_SIZE);
}
}
// Using stream I/O, append only access.
if (! (log = fopen(logFileNames[0], FILE_OPEN_TYPE_APPEND))) {
syslog(LOG_ERR, "EV_logger::EV_logger cannot open log file: %m");
return (-1);
}
else {
EV_event *ep = new EV_event(NV_LOG_START);
ep->srcApp = strdup (eventDaemon);
ep->pid = getpid();
ep->timeSent = (time ((time_t *)NULL));
ep->setOtherData("**********");
logit(ep);
delete (ep);
return(EV_OK);
}
}
/*
* EV_logger::cycleLogs -- called to move event logs to an older ones
*
* Treats the list of log files as a list of pointers in a circular
* buffer. It removes the oldest file and then renames all the other ones,
* "aging" them in the process. The rename function will delete an existing
* file. This is why it works.
*/
void
EV_logger::cycleLogs(void) {
for (int i = numlogs - 2 ; i >= 0; i--) {
if (rename(logFileNames[i], logFileNames[i+1])) {
// can't make the logfile logfile.old; simply truncate
syslog(LOG_ERR,
"EV_logger::EV_logger cannot move old to new: %s, %s:%m",
logFileNames[i], logFileNames[i+1]);
}
}
}
/*
* EV_logger::logit - put an event in the event log
*/
void
EV_logger::logit (EV_event *ep) {
char logbuf [EV_MAX_LOG_REC_SIZE + 1];
if (! ep) {
return;
}
if (currsize >= logsize) { /* test if log is full */
currsize = 0; /* size for new log file */
cycleLogs(); /* get the next log file */
if (openLog() == -1) {
syslog (LOG_ERR, "EV_logger::logit cannot open log file.");
return;
}
}
if (log != NULL) { /* always test here for valid FILE for the log*/
logrec.format (ep, logbuf); /* format the event */
if (fputs(logbuf, log) != EOF) { /* put it the stream */
currsize++; /* new logsize */
fputc('\n', log); /* add newline */
if (! (currsize % flushcnt) ) { /* to disk if time */
fflush(log);
}
}
else { /* unexpected error */
syslog(LOG_ERR, "EV_logger::logit: fputs: %m");
}
}
}
/*
* EV_logrec::format -- formats an eventlog record into a buffer from the
* event passed to it.
*
*/
void
EV_logrec::format (EV_event *ep, char *logbuf) {
/*
* XXXX
* In its final incarnation this function will use the EV_cfg info about the
* event format. Right now I'll do it manually.
*/
int buflen = 0; /* total length */
char *bufptr; /* where in buffer */
char itembuf[EV_MAX_LOG_REC_SIZE]; /* tmp buf for formatting */
int itemlen; /* length of this item */
char tbuf[28]; /* buffer for time string */
EV_timeStamp ts; /* the timestamp in event */
eventID etype; /* the even type in event */
/*
* This define adds an "item" to the eventlog record buffer.
* It first checks to see if a string format item is NULL.
* If it is it does not add it to the record. This keeps the log
* record smaller and keeps the string '(null)' from appearing which
* is what the sprintf function sticks in for a string that is a
* NULL pointer. Each item is first formatted into the item buffer to
* make sure that it will fit into the event log buffer. If it does not
* the item is not added to the buffer
*/
#define ADD_ITEM(item, format) \
if (!(item == NULL && format[1] == 's')) { \
itemlen = sprintf(itembuf, format, item); \
buflen += itemlen; \
if (buflen <= EV_MAX_LOG_REC_SIZE) { \
strcpy(bufptr, itembuf); \
bufptr += itemlen; } \
else { \
buflen -= itemlen;}}
bufptr = logbuf; /* init pos in buffer */
etype = ep->getType(); /* get the event type */
// format the timestamp into a time buffer
ts = ep->getTimeStamp();
cftime (tbuf, "%b %e %X", &ts);
// Now put in the generic info for all events.
ADD_ITEM (tbuf, "%s ");
ADD_ITEM (eventTypeStrings[etype], "%s "); /* string for the type */
ADD_ITEM (ep->getSrcApp(), "%s");
ADD_ITEM('[', "%c");
ADD_ITEM(ep->getPID(), "%d");
ADD_ITEM (']', "%c ");
ADD_ITEM (ep->getUserName(), "%s");
ADD_ITEM ('(', "%c");
ADD_ITEM (ep->getUID(), "%d");
ADD_ITEM (')', "%c ");
/*
* Don't put in the source host and its address for this release.
* Everything is local. Also it is not really that important where the
* application is running.
*
* ADD_ITEM (ep->getSrcHostName(), "%s ");
* ADD_ITEM (ep->getSrcHostAddr(), "%s ");
*/
ADD_ITEM (alarmStrings[cfg->getAlarmLevel(etype)], "%s ");
ADD_ITEM (ep->getInterfaceName(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getInterfaceAddr(), "%s");
ADD_ITEM (')', "%c ");
ADD_ITEM (ep->getFilter(), "%s ");
/*
* The previous stuff is common to all events, and for the set of events
* in the if statement below there is no other data except for the
* "otherData if supplied. Therefore this test determines whether I need
* to add a newline and tab to separate the event specific data from the
* generic information.
*
*/
if (!(etype == NV_OTHER || etype == NV_STARTUP ||
etype == NV_SHUTDOWN || etype == NV_START_SNOOP ||
etype == NV_STOP_SNOOP || etype == NV_LOG_START ||
etype == NV_LOG_END))
ADD_ITEM("\n\t", "%s");
/*
* Now for the event specific stuff. Note that for items that are
* EV_objIDs, that is those that have a name, network address, and
* MAC address, I only put in the MAC address if the address (and name)
* are missing. This is just to keep the record smaller and easier to
* read.
*/
switch (etype) {
case NV_NEW_NODE:
ADD_ITEM (ep->getNodeName(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getNodeAddr(), "%s");
ADD_ITEM (')', "%c ");
if (! ep->getNodeAddr())
ADD_ITEM (ep->getNodeMACAddr(), "%s ");
break;
case NV_NEW_NET:
ADD_ITEM (ep->getNetName(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getNetAddr(), "%s");
ADD_ITEM(')', "%c ");
break;
case NV_CONVERSE_START:
ADD_ITEM (ep->getEndPt1Name(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getEndPt1Addr(), "%s");
ADD_ITEM (')', "%c ");
if (!ep->getEndPt1Addr())
ADD_ITEM (ep->getEndPt1MACAddr(), "%s ");
ADD_ITEM (ep->getEndPt2Name(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getEndPt2Addr(), "%s");
ADD_ITEM(')', "%c ");
if (! ep->getEndPt2Addr())
ADD_ITEM (ep->getEndPt2MACAddr(), "%s ");
break;
case NV_CONVERSE_STOP:
ADD_ITEM (ep->getEndPt1Name(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getEndPt1Addr(), "%s");
ADD_ITEM (')', "%c ");
if (!ep->getEndPt1Addr())
ADD_ITEM (ep->getEndPt1MACAddr(), "%s ");
ADD_ITEM (ep->getEndPt2Name(), "%s");
ADD_ITEM('(', "%c");
ADD_ITEM (ep->getEndPt2Addr(), "%s");
ADD_ITEM(')', "%c ");
if (! ep->getEndPt2Addr())
ADD_ITEM (ep->getEndPt2MACAddr(), "%s ");
ADD_ITEM (ep->getBytes(), "%d bytes ");
ADD_ITEM (ep->getPkts(), "%d packets ");
break;
case NV_NEW_PROTO:
ADD_ITEM(ep->getProto(), "%s ");
break;
case NV_RATE_THRESH_HI_MET:
case NV_RATE_THRESH_HI_UN_MET:
case NV_RATE_THRESH_LO_MET:
case NV_RATE_THRESH_LO_UN_MET:
{
ADD_ITEM(ep->getRate(), "%.0f ");
rateBase base = ep->getRateBase();
switch (base) {
case BYTE_BASED:
case PACKET_BASED:
ADD_ITEM(rateStrings[base], "%s/sec ");
break;
case PERCENT_UTIL:
case PERCENT_BYTES:
case PERCENT_PKTS:
case PERCENT_N_BYTES:
case PERCENT_N_PKTS:
ADD_ITEM (rateStrings[base], "%s ");
if (base == PERCENT_N_BYTES) {
ADD_ITEM(ep->getRateOfBase(), "%.0f bytes. ");
} else if (base == PERCENT_N_PKTS) {
ADD_ITEM(ep->getRateOfBase(), "%.0f packets. ");
}
break;
}
ADD_ITEM(ep->getThreshRate(), "threshold: %.0f ");
}
break;
case NV_NEW_TOPN:
{
int i = 0;
objectList *topN = ep->getTopNodes();
while (topN[i]) {
ADD_ITEM(topN[i], "%s ");
i++;
}
}
break;
}
/*
* The "otherData" is treated specially, put on a line by itself
* indented like event specific data. The test is made so as not to add
* a line consisting of only a TAB.
*/
if (ep->getOtherData()) {
ADD_ITEM("\n\t", "%s");
ADD_ITEM(ep->getOtherData(), "%s");
}
}