- use dig for resolving host names - ascon.c: fix terminator parsing - property callback: change property before callback - logger.c:default for logger period must be the old value instead of 1 - add frappy type history writing - increase max. logreader line length - HIPNONE returns "null" with json protocol - encode strings properly in formatNameValue - fix memory leak in json2tcl - scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty - scriptcontext: remove args for action timestamp - scriptcontext: "que" function will replace an already queued action, e.g. for 'halt - introduced updatestatus script
496 lines
13 KiB
C
496 lines
13 KiB
C
/*---------------------------------------------------------------------------
|
|
logger.c
|
|
|
|
Markus Zolliker, Sept 2004
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <assert.h>
|
|
#include <fortify.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include "logger.h"
|
|
|
|
static char *dir = NULL;
|
|
static Logger *list;
|
|
static time_t lastLife = 0;
|
|
static time_t lastWritten = 0;
|
|
static time_t omitCheck = 0;
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
char *LoggerName(Logger * log)
|
|
{
|
|
return log->name;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void LoggerSetNumeric(Logger * log, int numeric)
|
|
{
|
|
if (log) {
|
|
log->numeric = numeric;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
Logger *LoggerFind(const char *name)
|
|
{
|
|
Logger *p;
|
|
p = list;
|
|
while (p != NULL) {
|
|
if (0 == strcasecmp(name, p->name)) {
|
|
return p;
|
|
}
|
|
p = p->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
Logger *LoggerList(void)
|
|
{
|
|
return list;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
#define LASTLOGTXT "#last logging entry at:\n"
|
|
|
|
time_t LoggerGetLastLife(char *dirarg)
|
|
{
|
|
char path[256], line[32];
|
|
FILE *fil;
|
|
time_t t = 0;
|
|
|
|
if (dirarg == NULL) {
|
|
return lastWritten;
|
|
}
|
|
snprintf(path, sizeof path, "%s/lastlife.dat", dirarg);
|
|
fil = fopen(path, "r");
|
|
if (fil) {
|
|
line[0]='\0';
|
|
fgets(line, sizeof line, fil);
|
|
if (strcmp(line, LASTLOGTXT) == 0) {
|
|
fgets(line, sizeof line, fil);
|
|
t = atol(line);
|
|
if (t < 1000000000) {
|
|
printf("bad lastLife %ld\n", (long) t);
|
|
}
|
|
}
|
|
fclose(fil);
|
|
} else {
|
|
/* printf("can not read %s\n", path); */
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
time_t LoggerSetDir(char *dirarg)
|
|
{
|
|
dir = dirarg;
|
|
lastLife = LoggerGetLastLife(dir);
|
|
return lastLife;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
char *LoggerGetDir(void)
|
|
{
|
|
char path[256];
|
|
FILE *fil;
|
|
static time_t last;
|
|
|
|
lastWritten = time(NULL);
|
|
if (lastWritten != last) { /* do not write more than once per second */
|
|
snprintf(path, sizeof path, "%s/lastlife.dat", dir);
|
|
fil = fopen(path, "w");
|
|
if (fil) {
|
|
fprintf(fil, "%s%ld\n", LASTLOGTXT, (long) lastWritten);
|
|
fclose(fil);
|
|
} else {
|
|
printf("can not write %s\n", path);
|
|
}
|
|
last = lastWritten;
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int LoggerVarPath(char *dir, char *path, int pathLen, char *name,
|
|
struct tm *t)
|
|
{
|
|
int l;
|
|
|
|
l = strlen(dir);
|
|
if (l + strlen(name) + 8 >= pathLen) {
|
|
path[0] = '\0';
|
|
return 0;
|
|
}
|
|
strcpy(path, dir);
|
|
strftime(path + l, pathLen - l, "/%Y/", t);
|
|
l += 6;
|
|
for (; *name != '\0'; name++, l++) {
|
|
path[l] = tolower(*name);
|
|
}
|
|
path[l] = '/';
|
|
l++;
|
|
path[l] = '\0';
|
|
return l;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int LoggerWrite0(Logger * log, time_t now, int period, char *value)
|
|
{
|
|
char path[256], stim[32], buf[32];
|
|
struct tm tm, lasttm;
|
|
int l, ext, writeInfo;
|
|
FILE *fil;
|
|
time_t beforenow;
|
|
char *nl;
|
|
|
|
if (log->name[0] == '\0')
|
|
return 0;
|
|
LoggerGetDir();
|
|
if (dir == NULL)
|
|
return 0;
|
|
if (now == 0) {
|
|
printf("now==0\n");
|
|
}
|
|
lasttm = *localtime(&log->last);
|
|
tm = *localtime(&now);
|
|
l = LoggerVarPath(dir, path, sizeof path, log->name, &tm);
|
|
|
|
strftime(path + l, sizeof path - l, "%m-%d.log", &tm);
|
|
strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", &tm);
|
|
|
|
if (period <= 0) {
|
|
if (log->period > 0) {
|
|
period = log->period;
|
|
} else {
|
|
period = 1;
|
|
}
|
|
}
|
|
writeInfo = (tm.tm_isdst != lasttm.tm_isdst ||
|
|
tm.tm_yday != lasttm.tm_yday ||
|
|
(period != log->period && log->numeric));
|
|
if (strcmp(value, log->old) != 0 || writeInfo) {
|
|
|
|
fil = fopen(path, "r+");
|
|
if (fil == NULL) { /* create new file */
|
|
fil = fopen(path, "w+");
|
|
if (fil == NULL)
|
|
return 0;
|
|
fprintf(fil, "%s isdst %d period %d exact %d\n", stim, tm.tm_isdst,
|
|
period, log->exact);
|
|
} else { /* check if file is from today */
|
|
fgets(buf, sizeof buf, fil);
|
|
if (0 != strncmp(buf, stim, 11)) {
|
|
fclose(fil); /* it was file from an earlier year */
|
|
fil = fopen(path, "w+"); /* overwrite old logfile */
|
|
if (fil == NULL)
|
|
return 0;
|
|
fprintf(fil, "%s isdst %d period %d exact %d\n", stim, tm.tm_isdst,
|
|
period, log->exact);
|
|
} else {
|
|
fseek(fil, 0, SEEK_END); /* set position to end */
|
|
if (writeInfo) {
|
|
fprintf(fil, "#isdst %d period %d exact %d\n", tm.tm_isdst,
|
|
period, log->exact);
|
|
}
|
|
if (log->lastWrite != 0 && now >= log->lastWrite + 2 * period) {
|
|
/* this is only useful for direct access of the log files */
|
|
beforenow = now - period;
|
|
lasttm = *localtime(&beforenow);
|
|
if (lasttm.tm_yday == tm.tm_yday) {
|
|
strftime(stim, sizeof stim, "%H:%M:%S", &lasttm);
|
|
} else {
|
|
snprintf(stim, sizeof stim, "00:00:00");
|
|
}
|
|
fprintf(fil, "%s\t%s\n", stim, log->old);
|
|
}
|
|
}
|
|
}
|
|
strftime(stim, sizeof stim, "%H:%M:%S", &tm);
|
|
nl = strchr(value, '\n');
|
|
if (nl == NULL) {
|
|
fprintf(fil, "%s\t%s\n", stim, value);
|
|
} else {
|
|
/* newline within string! do write only characters before nl */
|
|
fprintf(fil, "%s\t%.*s\n", stim, (int)(nl - value), value);
|
|
}
|
|
log->lastWrite = now;
|
|
fclose(fil);
|
|
|
|
}
|
|
log->period = period;
|
|
|
|
l = strlen(value);
|
|
if (l >= log->oldsize) { /* increase log->old size, optimized for linux/i386 */
|
|
ext = ((l - log->oldsize) / 16 + 1) * 16;
|
|
if (ext < log->oldsize / 4)
|
|
ext += (log->oldsize / 64) * 16;
|
|
log->oldsize += ext;
|
|
free(log->old);
|
|
log->old = calloc(1, log->oldsize);
|
|
}
|
|
assert(log->old);
|
|
assert(l < log->oldsize);
|
|
if (log->old != value) {
|
|
strcpy(log->old, value);
|
|
}
|
|
assert(log->old[l] == '\0');
|
|
log->last = now;
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int LoggerWrite(Logger * log, time_t now, int period, char *value)
|
|
{
|
|
Logger *p;
|
|
time_t h0;
|
|
static int yday = -1;
|
|
struct tm *tm;
|
|
char *valp;
|
|
char buf[32];
|
|
|
|
int l;
|
|
FILE *fil;
|
|
char path[256];
|
|
char tim[256];
|
|
|
|
if (log->name[0] == '\0')
|
|
return 0;
|
|
tm = localtime(&now);
|
|
if (tm->tm_yday != yday) { /* new day */
|
|
tm->tm_hour = 0;
|
|
tm->tm_min = 0;
|
|
tm->tm_sec = 0;
|
|
h0 = mktime(tm);
|
|
|
|
yday = tm->tm_yday;
|
|
|
|
/* -- debug logging if dir/debug exists */
|
|
l = LoggerVarPath(dir, path, sizeof path, "debug", tm);
|
|
strftime(path + l, sizeof path - l, "%m-%d.log", tm);
|
|
fil = fopen(path, "a");
|
|
if (fil) {
|
|
strftime(tim, sizeof tim, "h0 %m-%d %H:%M:%S\n", localtime(&h0));
|
|
fputs(tim, fil);
|
|
}
|
|
|
|
/* log old values (forced midnight log) */
|
|
p = list;
|
|
while (p != NULL) {
|
|
|
|
if (fil) { /* debug logging */
|
|
strftime(tim, sizeof tim, "last %m-%d %H:%M:%S, ",
|
|
localtime(&p->last));
|
|
fputs(tim, fil);
|
|
fprintf(fil, "period %d, name %s, old %s\n", p->period, p->name,
|
|
p->old);
|
|
}
|
|
|
|
if (p->last < h0 && p->last != 0 &&
|
|
p->period >= 0 && p->old[0] != '\0') {
|
|
LoggerWrite0(p, h0, p->period, p->old);
|
|
}
|
|
p = p->next;
|
|
}
|
|
if (fil)
|
|
fclose(fil);
|
|
}
|
|
if (now < log->last + log->period && strcmp(log->old, value) != 0) {
|
|
log->omitValue = strtod(value, &valp);
|
|
if (value != valp && valp[0] == '\0') { /* value was a plain number */
|
|
log->omitTime = now;
|
|
return 1;
|
|
}
|
|
} else {
|
|
log->omitTime = 0;
|
|
}
|
|
if (now > omitCheck + 5) { /* check for omitted values only every 5 seconds */
|
|
for (p = list; p != NULL; p = p->next) {
|
|
if (p->omitTime > 0 && now > p->omitTime + p->period) {
|
|
snprintf(buf, sizeof buf, "%.7g", p->omitValue);
|
|
LoggerWrite0(p, p->omitTime, p->period, buf);
|
|
p->omitTime = 0;
|
|
}
|
|
}
|
|
omitCheck = now;
|
|
}
|
|
return LoggerWrite0(log, now, period, value);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
time_t LoggerLastTime(Logger * log)
|
|
{
|
|
if (log->last != 0 && log->period > 0) {
|
|
return log->last;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int LoggerPeriod(Logger * log)
|
|
{
|
|
return log->period;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void LoggerChange(Logger * log, int period, char *newname)
|
|
{
|
|
Logger *other;
|
|
time_t t;
|
|
|
|
if (period < 0) {
|
|
period = log->period;
|
|
}
|
|
if (strcasecmp(newname, log->name) != 0) {
|
|
if (log->name[0] != '\0') { /* was not defunct */
|
|
LoggerWrite0(log, time(NULL), log->period, log->old);
|
|
LoggerWrite0(log, time(NULL), log->period, "");
|
|
}
|
|
|
|
other = LoggerFind(newname); /* look if logger with new name already exists */
|
|
if (other) {
|
|
/* make defunct */
|
|
LoggerWrite0(other, time(NULL), other->period, other->old);
|
|
LoggerWrite0(other, time(NULL), other->period, "");
|
|
other->name[0] = '\0';
|
|
}
|
|
free(log->name);
|
|
log->name = strdup(newname);
|
|
t = time(NULL) - 1;
|
|
if (lastLife != 0 && lastLife + period < t) {
|
|
t = lastLife + period;
|
|
}
|
|
LoggerWrite(log, t, period, ""); /* value was undefined since last life of server */
|
|
} else {
|
|
LoggerWrite0(log, time(NULL), period, log->old);
|
|
}
|
|
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void LoggerKill(Logger * log)
|
|
{
|
|
/* we do not really free the logger, it might be reused
|
|
for the same variable later. We set the value to undefined */
|
|
|
|
if (list != NULL) { /* LoggerFreeAll not yet called */
|
|
LoggerWrite(log, time(NULL), 0, "");
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int LoggerMakeDir(char *path)
|
|
{
|
|
static char buffer[4096];
|
|
struct stat st;
|
|
int i, lpath, l;
|
|
char *slash;
|
|
|
|
i = stat(path, &st);
|
|
if (i >= 0) {
|
|
if (((st.st_mode >> 12) & 15) != 4) { /* exists, but is no directory */
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
i = mkdir(path, S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH);
|
|
if (i < 0) {
|
|
if (errno != ENOENT)
|
|
return 0; /* mkdir failed */
|
|
snprintf(buffer, sizeof buffer, "%s", path);
|
|
lpath = strlen(buffer);
|
|
/* Discard any trailing slash characters */
|
|
while (lpath > 0 && buffer[lpath - 1] == '/')
|
|
buffer[--lpath] = '\0';
|
|
do {
|
|
slash = strrchr(buffer, '/');
|
|
if (!slash)
|
|
return 0;
|
|
*slash = '\0';
|
|
i = mkdir(buffer, S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH);
|
|
} while (i < 0 && errno == ENOENT);
|
|
l = strlen(buffer);
|
|
while (l < lpath) {
|
|
buffer[l] = '/';
|
|
i = mkdir(buffer, S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH);
|
|
if (i < 0)
|
|
return 0;
|
|
l = strlen(buffer);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
Logger *LoggerMake(char *name, int period, int exact)
|
|
{
|
|
Logger *log;
|
|
char path[256];
|
|
time_t t;
|
|
struct tm tm;
|
|
|
|
LoggerGetDir();
|
|
if (dir == NULL)
|
|
return NULL;
|
|
time(&t);
|
|
tm = *localtime(&t);
|
|
LoggerVarPath(dir, path, sizeof path, name, &tm);
|
|
if (LoggerMakeDir(path) == 0)
|
|
return NULL;
|
|
log = LoggerFind(name); /* look if logger already exists */
|
|
if (log == NULL) {
|
|
log = calloc(1, sizeof *log);
|
|
if (log == NULL)
|
|
return NULL;
|
|
log->name = strdup(name);
|
|
if (log->name == NULL) {
|
|
free(log);
|
|
return NULL;
|
|
}
|
|
log->period = -1;
|
|
log->exact = exact;
|
|
log->old = calloc(1, 12);
|
|
log->oldsize = 12;
|
|
log->last = 0;
|
|
log->lastWrite = 0;
|
|
log->numeric = 1;
|
|
log->next = list;
|
|
list = log;
|
|
t = time(NULL) - 1;
|
|
if (lastLife != 0 && lastLife + period < t) {
|
|
t = lastLife + period;
|
|
}
|
|
LoggerWrite(log, t, period, ""); /* value was undefined since last life of server */
|
|
} else {
|
|
LoggerWrite0(log, time(NULL), period, log->old);
|
|
}
|
|
return log;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void LoggerFreeAll(void)
|
|
{
|
|
Logger *p, *next;
|
|
|
|
p = list;
|
|
while (p != NULL) {
|
|
next = p->next;
|
|
if (p->name)
|
|
free(p->name);
|
|
if (p->old)
|
|
free(p->old);
|
|
if (p->secop_old)
|
|
free(p->secop_old);
|
|
if (p->secop_id)
|
|
free(p->secop_id);
|
|
free(p);
|
|
p = next;
|
|
}
|
|
list = NULL;
|
|
}
|