Files
sics/logger.c
Douglas Clowes 86e1f7b7cf SICS-819 Fix SICS crash in logsetup
Directory creation involving path creation failed because the path has
a trailing slash. After creating the directory of the full path, SICS
tried to create the path again with the trailing slash - it failed.

When logger creation failed, SICS registered the callback with NULL. This
was dereferenced in the hipadaba callback.

Suppress trailing slashes in directory path creation. Do not register
the callback if the creation failed.
2015-01-23 10:09:12 +11:00

485 lines
12 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"
struct Logger {
char *name;
char *old;
int oldsize;
int period;
time_t last, lastWrite, omitTime;
int numeric;
float omitValue;
int exact;
Logger *next;
};
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;
}
/*--------------------------------------------------------------------------*/
#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;
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)
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);
fprintf(fil, "%s\t%s\n", stim, 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, "");
}
}
/*--------------------------------------------------------------------------*/
static 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);
free(p);
p = next;
}
list = NULL;
}