flog-1.8/0000755000000000000000000000000010636073446011020 5ustar rootrootflog-1.8/README0000644000076400007640000000365610636072534012521 0ustar hobbithobbit WHAT IS IT? =========== flog (file logger) is a program that reads input from STDIN and writes to a file. if a SIGHUP is received, the file will be reopened, allowing for log rotation [see logrotate(8) on RH.] The log file will only be reopened if flog detects that rotation has occurred (ie, old file gone or inode changed). flog is very small (less than 500 bytes memory footprint.) USAGE ===== Typically flog is used to log STDERR output from a daemon. It's also very useful with Apache. This way you never have to HUP apache itself: ErrorLog "|/bin/flog /httpd/logs/error_log" Typical example: (mydaemon >stdout.file) |& flog [-t] stderr.file this will redirect daemon's STDOUT to a file called "stdout.file" and STDERR (thru flog) to "stderr.file". (example uses zsh: the best shell! like bash but better) The -t option causes a timestamp to be prepended to each line. The -T option causes a timestamp to be prepended to each line with a user specified time format. See strftime(3) for how to specify the format. FEATURES ======== if flog fills up a device (write returns ENOSPC), the log file will be truncated back to 0 and restarted. (this is an emergency avoidance feature only! always use logrotate to recycle logs) send as many HUPs as you want to flog, when a HUP is caught, the LOG will only be reopened if flog detects a logrotation. (ie, inode changed, or file disappeard) CHANGE LOG ========== version 1.4 2005-01-17 Added large-file support (-D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1) O_LARGEFILE version 1.5 2005-02-04 Re-arm signal handler (needed for solaris. thanks Claude) version 1.6 2005-06-02 Now compiles on OpenBSD version 1.7 2005-09-01 Added -l option to limit file length (patch by dsong@teramail.com) version 1.8 2007-06-20 Added -p to write pid file (patch by manon@manon.de) AUTHOR ====== Copyright (c) 2001 Fredrik Sjoholm License: GPL - The GNU General Public License flog-1.8/Makefile0000644000076400007640000000062610636073411013266 0ustar hobbithobbit VERSION = 1.8 DIST = flog-$(VERSION) #DEBUG = -g CFLAGS = -O2 $(DEBUG) -Wall -pedantic -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 CC = gcc flog: flog.c $(CC) $(CFLAGS) -o $@ $< strip $@ clean: rm -rf *.o flog $(DIST) $(DIST).tar.gz *~ dist: $(DIST).tar.gz $(DIST): README Makefile flog.c mkdir $(DIST) tar c $^ | (cd $(DIST); tar x) $(DIST).tar.gz: $(DIST) tar czf $@ $^ rm -rf $(DIST) flog-1.8/flog.c0000644000076400007640000001362610636072373012733 0ustar hobbithobbit/* * * 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 Conf { char timestamp; // { 0 | 1 } char pidfile; // { 0 | 1 } char * time_format; char * pidfile_name; int max_len; int pid; } 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 unlinkpidfile (struct Conf* conf); 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; FILE *pid_fd; // 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], "-p")) { opt++; if (opt < argc) { conf.pidfile = 1; conf.pid = getpid(); conf.pidfile_name = 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" " -p pid file\n", argv[0]); exit(1); } if (conf.pidfile) { pid_fd = fopen(conf.pidfile_name, "w"); if ((pid_fd = fopen(conf.pidfile_name, "w")) == NULL) { fprintf (stderr, "Could not open pidile: %s", conf.pidfile_name); exit(1); }else if (pid_fd) { fprintf(pid_fd, "%lu\n", (unsigned long)getpid()); fclose(pid_fd); } } 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)); unlinkpidfile(&conf); 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)); unlinkpidfile(&conf); 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)); unlinkpidfile(&conf); 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); } } unlinkpidfile(&conf); 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|O_LARGEFILE, 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); } void unlinkpidfile (struct Conf* conf) { if (conf->pidfile) { unlink(conf->pidfile_name); } }