/*--------------------------------------------------------------------------- logger.c Markus Zolliker, Sept 2004 ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include "logger.h" struct Logger { char *name; char *old; int oldsize; int period; time_t last; int lastLn; /* 0 or the number chars counted from the second last newline character. */ int overwrite; /* overwrite last line when value has not changed */ int oldValue; /* old value to be overwritten */ int numeric; int exact; Logger *next; }; static char *dir = NULL; static Logger *list; static time_t lastLife = 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" void LoggerSetDir(char *dirarg) { char path[256], line[32]; FILE *fil; dir = dirarg; snprintf(path, sizeof path, "%s/lastlife.dat", dir); fil = fopen(path, "r"); if (fil) { fgets(line, sizeof line, fil); if (strcmp(line, LASTLOGTXT) == 0) { fgets(line, sizeof line, fil); lastLife = atol(line); if (lastLife < 1000000000) { printf("bad lastLife %ld\n", (long)lastLife); } } fclose(fil); } else { printf("can not read %s\n", path); } } /*--------------------------------------------------------------------------*/ char *LoggerGetDir(void) { char path[256]; FILE *fil; time_t now; static time_t last; now = time(NULL); if (now != last) { snprintf(path, sizeof path, "%s/lastlife.dat", dir); fil = fopen(path, "w"); if (fil) { fprintf(fil, "%s%ld\n", LASTLOGTXT, (long)now); fclose(fil); } else { printf("can not write %s\n", path); } last = now; } return dir; } /*--------------------------------------------------------------------------*/ int LoggerVarPath(char *dir, char *path, int pathLen, char *name) { int l; l = strlen(dir); if (l + strlen(name) + 2 >= pathLen) { path[0]='\0'; return 0; } strcpy(path, dir); path[l] = '/'; l++; 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], chr; struct tm *tm; int yday; int isdst; int l, ext, skip, changed; FILE *fil; long endPos, pos1, p; char info[80]; LoggerGetDir(); if (dir == NULL) return 0; if (now == 0) { printf("now==0\n"); } tm = localtime(&log->last); isdst = tm->tm_isdst; yday = tm->tm_yday; tm = localtime(&now); if (tm->tm_yday != yday) { log->period = -1; } l = LoggerVarPath(dir, path, sizeof path, log->name); strftime(path + l, sizeof path - l, "%m-%d.log", tm); strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", tm); fil = fopen(path, "r+"); skip = 0; 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); log->period = period; endPos = ftell(fil); fseek(fil, -1, SEEK_CUR); } else { /* check if file is from today */ fgets(buf, sizeof buf, fil); if (0 != strncmp(buf, stim, 11)) { fclose(fil); fil=fopen(path, "w+"); /* overwrite old logfile */ if (fil == NULL) return 0; fputs(stim, fil); endPos = ftell(fil); fseek(fil, -1, SEEK_CUR); log->period = -1; } else { fseek(fil, -log->lastLn, SEEK_END); /* set position to last line */ endPos = ftell(fil) + log->lastLn; if (endPos > 0 && log->lastLn == 0) { /* find last newline in newly opened file */ fseek(fil, -1, SEEK_CUR); chr = fgetc(fil); while (chr != '\n' && ftell(fil) >= 2) { fseek(fil, -2, SEEK_CUR); chr = fgetc(fil); } fseek(fil, -1, SEEK_CUR); } else if (now != log->last) { skip = 1; } } } changed = (0 != strcmp(value, log->old)); info[0]='\0'; if (period != log->period) { log->period = period; changed = 1; if (log->numeric) { snprintf(info, sizeof info, "period %d exact %d ", period, log->exact); } else { snprintf(info, sizeof info, "period 0 exact 1 "); } } else { buf[0]='\0'; if (log->overwrite && !changed) { skip = 0; } } pos1 = 0; chr = fgetc(fil); while (chr != EOF) { if (chr == '\n') { skip--; if (skip < 0) break; } chr = fgetc(fil); } if (chr == EOF) { if (pos1 == 0) { fputs("\n#no newline found\n", fil); } else { fseek(fil, pos1, SEEK_SET); } } if (tm->tm_isdst != isdst || info[0] != '\0') { fprintf(fil, "#%sisdst %d\n", info, tm->tm_isdst); } pos1 = ftell(fil); strftime(stim, sizeof stim,"%H:%M:%S", tm); fprintf(fil, "%s\t%s\n", stim, value); for (p = ftell(fil); p < endPos; p++) { /* overwrite dirt after last line */ fprintf(fil, " "); } endPos = ftell(fil); fclose(fil); log->lastLn = (endPos - pos1) + 1; /* overwrite next time when value has not changed */ log->overwrite = ! changed; l = strlen(value); if (l >= log->oldsize) { 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); } strcpy(log->old, value); 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; int l; FILE *fil; char path[256]; char tim[256]; tm = localtime(&now); if (tm->tm_yday != yday) { tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; h0 = mktime(tm); yday = tm->tm_yday; l = LoggerVarPath(dir, path, sizeof path, "debug"); 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) { 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->overwrite = 0; } p = p->next; } if (fil) fclose(fil); } return LoggerWrite0(log, now, period, value); } /*--------------------------------------------------------------------------*/ time_t LoggerLastTime(Logger *log) { if (log->last != 0 && log->period > 0) { return log->last; } return 0; } /*--------------------------------------------------------------------------*/ 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, ""); } } /*--------------------------------------------------------------------------*/ Logger *LoggerMake(char *name, int period, int exact) { Logger *log; char path[256]; struct stat st; int i; time_t t; LoggerGetDir(); if (dir == NULL) return NULL; LoggerVarPath(dir, path, sizeof path, name); i = stat(path, &st); if (i >= 0) { if (((st.st_mode >> 12) & 15) != 4) { /* exists, but is no directory */ return NULL; } } else { i = mkdir(path, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); if (i < 0) return NULL; /* mkdir failed */ } 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->lastLn = 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 */ } 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; }