/* * * Copyright (c) 2001 Fredrik Sjoholm * All rights reserved. * License: GPL - The GNU General Public License * */ #include #include #include #include #include #include #include #include #include #include struct { char* data; int used; int size; } buf; struct FileInfo { char *name; int fd; struct stat stat; // remember info about file so we can know if it's been rotated } out; struct { char timestamp; // { 0 | 1 } char * time_format; int max_len; } conf; #define DEFAULT_TIME_FORMAT "%Y%m%d;%T: " volatile int gotHUP; void growbuf(int size); int openfile (struct FileInfo* file); int filechanged (struct FileInfo* file); void catchHUP (int sig); void handleHUP (); void writetime (int fd); int main(int argc, char** argv) { char *eol, *pos; int done = 0; int opt = 0; int totalwn = 0; // look for switches conf.time_format = NULL; while (++opt < argc) { if (!strcmp(argv[opt], "-t")) { conf.timestamp = 1; conf.time_format = DEFAULT_TIME_FORMAT; } else if (!strcmp(argv[opt], "-T")) { opt++; if (opt < argc) { conf.timestamp = 1; conf.time_format = argv[opt]; } } else if (!strcmp(argv[opt], "-l")) { opt++; if (opt < argc) { conf.max_len = atoi (argv[opt]); } } else { break; } } if (opt >= argc) { fprintf (stderr, "Usage: pipeline| %s [options] {logfile|-} # SIGHUP will reopen logfile\n" " -t prepend each line with \"YYYYMMDD;HH:MM:SS: \"\n" " -T prepend each line with specified strftime(3) format\n\n" " -l log file length limit (force truncation)\n", argv[0]); exit(1); } out.name = argv[opt]; openfile (&out); signal (SIGHUP, catchHUP); growbuf (4096); while (!done) { int size; if (buf.used == buf.size) growbuf (buf.size*2); size = read (0, buf.data+buf.used, buf.size-buf.used); if (size > 0) { buf.used += size; } else if (size == 0) { done = 1; } else { } for (;;) { if (gotHUP) { handleHUP(); signal (SIGHUP, catchHUP); gotHUP = 0; } pos = buf.data; eol = (char*) memchr(buf.data, '\n', buf.used); if (eol == 0) break; eol ++; if (conf.timestamp) writetime (out.fd); while (pos < eol) { int wn = write(out.fd, pos, eol-pos); if (wn > 0) { pos += wn; totalwn += wn; } else if (errno == ENOSPC) { char str[256]; if (ftruncate(out.fd,0)) { fprintf(stderr, "truncating %s: %d %s\n", out.name, errno, strerror(errno)); exit(1); } write (out.fd, str, snprintf(str, sizeof(str), "Device full: truncating %s\n\n", out.name)); } else { fprintf(stderr, "write %s: %d %s\n", out.name, errno, strerror(errno)); exit(1); } if (conf.max_len && (totalwn > conf.max_len)) { char str[256]; if (ftruncate(out.fd,0)) { fprintf(stderr, "truncating %s: %d %s\n", out.name, errno, strerror(errno)); exit(1); } write (out.fd, str, snprintf(str, sizeof(str), "File size limit: truncating %s\n\n", out.name)); totalwn = 0; } } buf.used = buf.data + buf.used - eol; memmove (buf.data, eol, buf.used); } } exit(0); } void growbuf (int size) { if (size > buf.size) { buf.data = (char*) realloc(buf.data, size); buf.size = size; } } int openfile (struct FileInfo* file) { int fd; if (strcmp(file->name,"-") != 0) { #ifdef O_LARGEFILE fd = open (file->name, O_CREAT|O_APPEND|O_WRONLY, 0666); #else fd = open (file->name, O_CREAT|O_APPEND|O_WRONLY, 0666); #endif } else { fd = 2; } if (fd < 0) { fprintf (stderr, "open write %s: %s\n", file->name, strerror(errno)); exit(1); } file->fd = fd; stat (file->name, &file->stat); // update stat buffer return fd; } int reopenfile (struct FileInfo* file) { if (strcmp(file->name,"-") != 0) { close (file->fd); return openfile (file); } else { return file->fd; } } int filechanged (struct FileInfo* file) { struct stat stat2; if (strcmp(file->name,"-") == 0) { return 0; } if (stat (file->name, &stat2) < 0) { return 1; // file removed or something } if (stat2.st_ino != file->stat.st_ino || stat2.st_dev != file->stat.st_dev) { // file changed or was moved to a different device return 1; } return 0; } void catchHUP (int sig) { gotHUP = 1; } void handleHUP () { char str[256]; if (filechanged(&out)) { // only reopen file and print msg if the file on disk was changed or removed write (out.fd, str, snprintf (str, sizeof(str), "Caught SIGHUP: Reopening %s (closed)\n\n", out.name)); reopenfile (&out); write (out.fd, str, snprintf (str, sizeof(str), "Caught SIGHUP: Reopening %s (opened)\n\n", out.name)); } } // format: "YYYYMMDD;HH.MM.SS: " void writetime (int fd) { time_t now; struct tm *t; char buf[32]; int len; time (&now); t = localtime (&now); len = strftime(buf, sizeof(buf), conf.time_format, t); write (fd, &buf, len); }