559 lines
15 KiB
C
559 lines
15 KiB
C
/*---------------------------------------------------------------------------
|
|
|
|
Implementation for the server log.
|
|
|
|
Is a bit tricky. Writes 1000 lines to a file, than increments
|
|
and opens a new one. Wraps arounf after 3. This is a compromise
|
|
between error tracking and use of diskspace.
|
|
|
|
|
|
|
|
Mark Koennecke, October 1996
|
|
|
|
Copyright:
|
|
|
|
Labor fuer Neutronenstreuung
|
|
Paul Scherrer Institut
|
|
CH-5423 Villigen-PSI
|
|
|
|
|
|
The authors hereby grant permission to use, copy, modify, distribute,
|
|
and license this software and its documentation for any purpose, provided
|
|
that existing copyright notices are retained in all copies and that this
|
|
notice is included verbatim in any distributions. No written agreement,
|
|
license, or royalty fee is required for any of the authorized uses.
|
|
Modifications to this software may be copyrighted by their authors
|
|
and need not follow the licensing terms described here, provided that
|
|
the new terms are clearly indicated on the first page of each file where
|
|
they apply.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
|
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
|
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
MODIFICATIONS.
|
|
|
|
Modified: Paul Hathaway, June 2004
|
|
SICSLogWrite
|
|
- no longer asserts existence of the log file, writing
|
|
to stderr and skipping further file writes.
|
|
- NETWrites log message (if enabled) before attempt to write to file
|
|
- uses OpenVerifyLogFile helper function (removed duplicate code)
|
|
-----------------------------------------------------------------------------*/
|
|
#include <stdarg.h>
|
|
#include "fortify.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <strlutil.h>
|
|
|
|
#include "ifile.h"
|
|
#include "servlog.h"
|
|
#include "network.h"
|
|
|
|
/* define this, if you do not want a server log
|
|
|
|
#define NOLOG 1
|
|
*/
|
|
/* -------------------------------------------------------------------------
|
|
The server log output can be captured by a client. In order to implement
|
|
this the following code is necessary.
|
|
*/
|
|
typedef struct __LogLog {
|
|
pSICSLogHook pFunc;
|
|
void *pData;
|
|
unsigned int code_bits;
|
|
struct __LogLog *pNext;
|
|
struct __LogLog *pPrevious;
|
|
} CaptureEntry, *pCaptureEntry;
|
|
|
|
static pCaptureEntry pCapture = NULL;
|
|
/*------------------------------------------------------------------------*/
|
|
#include "outcode.c" /* for pCode */
|
|
|
|
const char* OutCodeToTxt(OutCode eOut)
|
|
{
|
|
switch (eOut) {
|
|
case eInternal: return "Int"; /* internal */
|
|
case eCommand: return "Cmd"; /* reserved, not used */
|
|
case eHWError: return "ErH"; /* reserved, used only for SICSLog */
|
|
case eInError: return "ErI"; /* reserved, used as a mark in the handling of output codes */
|
|
case eStatus: return "Sta"; /* reserved, deprecated */
|
|
case eValue: return "Val"; /* value reponse: copied into Tcl */
|
|
case eStart: return "Beg"; /* start message */
|
|
case eFinish: return "End"; /* finish message */
|
|
case eEvent: return "Evt"; /* some callback messages */
|
|
case eWarning: return "Wrn"; /* warnings */
|
|
case eError: return "Err"; /* error: copied into Tcl */
|
|
case eHdbValue: return "HVa"; /* hipadaba value chnage */
|
|
case eHdbEvent: return "HEv"; /* Hipadaba event */
|
|
case eLog: return "Log"; /* log message: is always written to client */
|
|
case eLogError: return "ErL"; /* error message to log: is always written to client */
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
const char* OutCodeToText(OutCode eOut)
|
|
{
|
|
switch (eOut) {
|
|
case eInternal: return "Internal";
|
|
case eCommand: return "Command";
|
|
case eHWError: return "HWError";
|
|
case eInError: return "InError";
|
|
case eStatus: return "Status";
|
|
case eValue: return "Value";
|
|
case eStart: return "Start";
|
|
case eFinish: return "Finish";
|
|
case eEvent: return "Event";
|
|
case eWarning: return "Warning";
|
|
case eError: return "Error";
|
|
case eHdbValue: return "HdbValue";
|
|
case eHdbEvent: return "HdbEvent";
|
|
case eLog: return "Log";
|
|
case eLogError: return "LogError";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
int OutCodeFromText(const char *text, OutCode *outcode)
|
|
{
|
|
int i;
|
|
for (i = 0; i < iNoCodes; ++i) {
|
|
if (strcasecmp(text, pCode[i]) == 0) {
|
|
if (outcode)
|
|
*outcode = i;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static unsigned int find_code_bits(const char *p1, const char *p2) {
|
|
/* must be outcode, try find it */
|
|
int i;
|
|
size_t len = p2 - p1;
|
|
if (len == 3 && strncasecmp(p1, "all", 3))
|
|
return ~0;
|
|
for (i = 0; i < iNoCodes; ++i) {
|
|
if (pCode[i] != NULL && strlen(pCode[i]) == len) {
|
|
if (strncasecmp(p1, pCode[i], len) == 0) {
|
|
return 1 << i;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char *AddSICSLogHook(pSICSLogHook func, const char *pCodes, void *pData)
|
|
{
|
|
unsigned int code_bits = 0;
|
|
if (strcasecmp("all", pCodes) == 0)
|
|
code_bits = ~0;
|
|
else {
|
|
const char *p1, *p2;
|
|
p1 = pCodes;
|
|
while (NULL != (p2 = strchr(p1, ','))) {
|
|
/* TODO [p1:p2) */
|
|
code_bits |= find_code_bits(p1, p2);
|
|
p1 = p2 + 1;
|
|
}
|
|
/* p1 points at the last or only code */
|
|
/* TODO p1 */
|
|
code_bits |= find_code_bits(p1, p1 + strlen(p1));
|
|
}
|
|
if (code_bits != 0) {
|
|
pCaptureEntry pNew;
|
|
pNew = (pCaptureEntry) malloc(sizeof(CaptureEntry));
|
|
if (!pNew) {
|
|
SICSLogWrite("Out of memory in LogCapture", eInternal);
|
|
return NULL;
|
|
}
|
|
if (pCapture) {
|
|
pCapture->pPrevious = pNew;
|
|
}
|
|
pNew->pPrevious = NULL;
|
|
pNew->pNext = pCapture;
|
|
pCapture = pNew;
|
|
pNew->code_bits = code_bits;
|
|
pNew->pFunc = func;
|
|
pNew->pData = pData;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Remove any and all hooks with this pData */
|
|
char *RemSICSLogHook(void *pData)
|
|
{
|
|
pCaptureEntry pCurrent, pTemp;
|
|
|
|
/* find first */
|
|
pCurrent = pCapture;
|
|
while (pCurrent != NULL) {
|
|
if (pData == pCurrent->pData) {
|
|
/* relink */
|
|
if (pCurrent->pPrevious) {
|
|
pCurrent->pPrevious->pNext = pCurrent->pNext;
|
|
} else {
|
|
pCapture = pCurrent->pNext;
|
|
}
|
|
if (pCurrent->pNext) {
|
|
pCurrent->pNext->pPrevious = pCurrent->pPrevious;
|
|
}
|
|
pTemp = pCurrent->pNext;
|
|
/* get rid of pCurrent */
|
|
free(pCurrent);
|
|
pCurrent = pTemp;
|
|
} else {
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int HasLineFeed(char *pText)
|
|
{
|
|
int i;
|
|
|
|
for (i = strlen(pText); i > 0; i--) {
|
|
if (pText[i] == '\n') {
|
|
return 1;
|
|
}
|
|
if (isalpha(pText[i])) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static const char* timestamp(struct timeval *tp) {
|
|
static char ts[80];
|
|
int year, month, day;
|
|
int hour, min, sec, usec;
|
|
struct timeval tv;
|
|
struct tm *time;
|
|
if (tp)
|
|
tv = *tp;
|
|
else
|
|
gettimeofday(&tv, NULL);
|
|
time = localtime(&tv.tv_sec);
|
|
year = 1900 + time->tm_year;
|
|
month = time->tm_mon + 1;
|
|
day = time->tm_mday;
|
|
hour = time->tm_hour;
|
|
min = time->tm_min;
|
|
sec = time->tm_sec;
|
|
usec = (int) tv.tv_usec;
|
|
snprintf(ts, 80, "%04d-%02d-%02dT%02d:%02d:%02d.%06d",
|
|
year, month, day, hour, min, sec, usec);
|
|
return ts;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#define MAXLOG 100000
|
|
#define MAXFILES 100
|
|
|
|
static FILE *fLogFile = NULL;
|
|
static int iFile = 0;
|
|
static int iLineCount = 0;
|
|
static int iLogUsable = 1;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int OpenVerifyLogFile()
|
|
{
|
|
char pFile[256];
|
|
char filnam[512];
|
|
char *pChar = NULL;
|
|
char fPath[1024];
|
|
|
|
/* snprintf(fPath, 1023, "%s/", getenv("SICS_INIT_LOGPATH")); */
|
|
snprintf(fPath, 1023, "%s/", "../log");
|
|
|
|
pChar = IFindOption(pSICSOptions, "LogFileBaseName");
|
|
if (!pChar) { /* Try to write to file "server" in */
|
|
strcpy(pFile, "server");
|
|
} else {
|
|
strlcpy(pFile, pChar, 255);
|
|
}
|
|
|
|
snprintf(filnam, 511, "%s%s_%19.19s.%02d.log", fPath, pFile, timestamp(NULL), iFile);
|
|
|
|
fLogFile = fopen(filnam, "w");
|
|
if (!fLogFile) {
|
|
fprintf(stderr, "ERROR: Cannot open logfile %s for writing\n", pFile);
|
|
fLogFile = NULL;
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
void SICSLogEnable(int flag)
|
|
{
|
|
iLogUsable = flag;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void SICSLogWriteFile(char *pText, OutCode eOut, struct timeval *tp)
|
|
{
|
|
pCaptureEntry pCurrent;
|
|
int text_len;
|
|
|
|
#ifdef NOLOG
|
|
return;
|
|
#endif
|
|
|
|
text_len = strlen(pText);
|
|
/* do all captured */
|
|
pCurrent = pCapture;
|
|
while (pCurrent) {
|
|
if ((pCurrent->code_bits & (1 << eOut))) {
|
|
pCurrent->pFunc(pText, eOut, pCurrent->pData);
|
|
}
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
if (0 == iLogUsable)
|
|
return;
|
|
|
|
if (fLogFile == NULL) { /* first time of use */
|
|
/* no options: startup or serious trouble, print to stdout */
|
|
if (!pSICSOptions) {
|
|
printf("WARNING: Cannot log(%s)\n", pText);
|
|
return;
|
|
}
|
|
iLogUsable = OpenVerifyLogFile();
|
|
}
|
|
|
|
/* switch file if too many lines */
|
|
if (iLineCount >= MAXLOG) {
|
|
fprintf(fLogFile, "%s: <<<close logfile>>>\n", timestamp(NULL));
|
|
fclose(fLogFile);
|
|
fLogFile = NULL;
|
|
iFile++;
|
|
iLineCount = 0;
|
|
if (iFile >= MAXFILES) {
|
|
iFile = 0;
|
|
}
|
|
iLogUsable = OpenVerifyLogFile();
|
|
}
|
|
|
|
if (1 == iLogUsable) {
|
|
|
|
if (iLineCount == 0)
|
|
fprintf(fLogFile, "%s: <<<open logfile>>>\n", timestamp(NULL));
|
|
fprintf(fLogFile, "%s:%s:", timestamp(tp), OutCodeToTxt(eOut));
|
|
fprintf(fLogFile, "%s", pText);
|
|
if (text_len < 1 || pText[text_len - 1] != '\n')
|
|
fprintf(fLogFile, "\n");
|
|
fflush(fLogFile);
|
|
iLineCount++;
|
|
}
|
|
}
|
|
|
|
void SICSLogWriteTime(char *pText, OutCode eOut, struct timeval *tp)
|
|
{
|
|
char buf[200];
|
|
const char *cp = pText;
|
|
int idx = 0;
|
|
struct timeval tv;
|
|
|
|
if (tp == NULL) {
|
|
gettimeofday(&tv, NULL);
|
|
tp = &tv;
|
|
}
|
|
while (*cp) {
|
|
if (*cp == '\n') {
|
|
buf[idx++] = '\n';
|
|
buf[idx++] = '\0';
|
|
SICSLogWriteFile(buf, eOut, tp);
|
|
idx = 0;
|
|
buf[idx++] = '*';
|
|
buf[idx++] = ':';
|
|
} else if (*cp == '\r') {
|
|
buf[idx++] = '\\';
|
|
buf[idx++] = 'r';
|
|
} else if (*cp == '\t') {
|
|
buf[idx++] = '\\';
|
|
buf[idx++] = 't';
|
|
} else if (*cp < ' ' || *cp > '~') {
|
|
const char hex[] = "0123456789ABCDEF";
|
|
buf[idx++] = '<';
|
|
buf[idx++] = hex[(*cp >> 4) & 0xF];
|
|
buf[idx++] = hex[(*cp) & 0xF];
|
|
buf[idx++] = '>';
|
|
} else {
|
|
buf[idx++] = *cp;
|
|
}
|
|
cp++;
|
|
if (idx > 132) {
|
|
buf[idx++] = '\n';
|
|
buf[idx++] = '\0';
|
|
SICSLogWriteFile(buf, eOut, tp);
|
|
idx = 0;
|
|
buf[idx++] = '*';
|
|
buf[idx++] = ':';
|
|
}
|
|
}
|
|
if (idx > 0) {
|
|
buf[idx++] = '\n';
|
|
buf[idx++] = '\0';
|
|
SICSLogWriteFile(buf, eOut, tp);
|
|
}
|
|
}
|
|
|
|
void SICSLogWrite(char *pText, OutCode eOut)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
SICSLogWriteTime(pText, eOut, &tv);
|
|
}
|
|
|
|
void SICSLogWriteHexTime(const char *text, int count, OutCode eOut,
|
|
struct timeval *tp)
|
|
{
|
|
const char hex[] = "0123456789ABCDEF";
|
|
char addr[20], left[80], right[80];
|
|
char *lp = left;
|
|
char *rp = right;
|
|
int i;
|
|
int duplicates;
|
|
/* Limit the output */
|
|
if (count > 1024)
|
|
count = 1024;
|
|
duplicates = 0;
|
|
for (i = 0; i < count; ++i) {
|
|
if ((i & 0xF) == 0) {
|
|
if (i > 0) {
|
|
char line[132];
|
|
snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr,
|
|
left, right);
|
|
SICSLogWriteTime(line, eOut, tp);
|
|
}
|
|
snprintf(addr, sizeof(addr) - 1, "0x%04X", i);
|
|
while (i >= 16 && i + 16 < count) {
|
|
if (memcmp(&text[i - 16], &text[i], 16) != 0)
|
|
break;
|
|
++duplicates;
|
|
i += 16;
|
|
}
|
|
if (duplicates > 0) {
|
|
if (duplicates > 1) {
|
|
char line[132];
|
|
snprintf(line, sizeof(line) - 1, "%-6s: ... (%d duplicates)",
|
|
addr, duplicates);
|
|
SICSLogWriteTime(line, eOut, tp);
|
|
} else {
|
|
char line[132];
|
|
snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr,
|
|
left, right);
|
|
SICSLogWriteTime(line, eOut, tp);
|
|
}
|
|
duplicates = 0;
|
|
}
|
|
snprintf(addr, sizeof(addr) - 1, "0x%04X", i);
|
|
lp = left;
|
|
rp = right;
|
|
}
|
|
*lp++ = hex[(text[i] >> 4) & 0xF];
|
|
*lp++ = hex[(text[i]) & 0xF];
|
|
*lp++ = ' ';
|
|
if (text[i] >= ' ' && text[i] <= '~')
|
|
*rp++ = text[i];
|
|
else
|
|
*rp++ = '.';
|
|
/* if we just did slot 7, insert an extra space */
|
|
if ((i & 0xF) == 7) {
|
|
*lp++ = ' ';
|
|
*rp++ = ' ';
|
|
}
|
|
*lp = *rp = '\0';
|
|
}
|
|
if (i > 0) {
|
|
char line[132];
|
|
snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr, left,
|
|
right);
|
|
SICSLogWriteTime(line, eOut, tp);
|
|
}
|
|
}
|
|
|
|
void SICSLogWriteHex(const char *text, int count, OutCode eOut)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
SICSLogWriteHexTime(text, count, eOut, &tv);
|
|
}
|
|
|
|
void SICSLogTimePrintf(OutCode eOut, struct timeval *tp, const char *fmt,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
char buf[256];
|
|
char *dyn;
|
|
unsigned int l;
|
|
int res;
|
|
|
|
va_start(ap, fmt);
|
|
l = vsnprintf(buf, sizeof buf, fmt, ap);
|
|
va_end(ap);
|
|
if (l >= sizeof buf) {
|
|
/* we have probably a C99 conforming snprintf and
|
|
need a larger buffer
|
|
*/
|
|
dyn = malloc(l + 1);
|
|
if (dyn != NULL) {
|
|
va_start(ap, fmt);
|
|
vsnprintf(dyn, l + 1, fmt, ap);
|
|
va_end(ap);
|
|
SICSLogWriteTime(dyn, eOut, tp);
|
|
free(dyn);
|
|
return;
|
|
}
|
|
}
|
|
SICSLogWriteTime(buf, eOut, tp);
|
|
return;
|
|
}
|
|
|
|
void SICSLogPrintf(OutCode eOut, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char buf[256];
|
|
char *dyn;
|
|
unsigned int l;
|
|
int res;
|
|
|
|
va_start(ap, fmt);
|
|
l = vsnprintf(buf, sizeof buf, fmt, ap);
|
|
va_end(ap);
|
|
if (l >= sizeof buf) {
|
|
/* we have probably a C99 conforming snprintf and
|
|
need a larger buffer
|
|
*/
|
|
dyn = malloc(l + 1);
|
|
if (dyn != NULL) {
|
|
va_start(ap, fmt);
|
|
vsnprintf(dyn, l + 1, fmt, ap);
|
|
va_end(ap);
|
|
SICSLogWrite(dyn, eOut);
|
|
free(dyn);
|
|
return;
|
|
}
|
|
}
|
|
SICSLogWrite(buf, eOut);
|
|
return;
|
|
}
|