/*
*
* Copyright (c) 2001 Fredrik Sjoholm <fredrik@sjoholm.com>
* All rights reserved.
* License: GPL - The GNU General Public License
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <string.h>
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 <format> prepend each line with specified strftime(3) format\n\n"
" -l <number> 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);
}
syntax highlighted by Code2HTML, v. 0.9.1