#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include "screen.h"
#include "extern.h"
#include "logfile.h"
static void changed_logfile __P((struct logfile *));
static struct logfile *lookup_logfile __P((char *));
static int stolen_logfile __P((struct logfile *));
static struct logfile *logroot = NULL;
static void
changed_logfile(l)
struct logfile *l;
{
struct stat o, *s = l->st;
if (fstat(fileno(l->fp), &o) < 0)
return;
if (o.st_size > s->st_size)
{
s->st_size = o.st_size;
s->st_mtime = o.st_mtime;
}
}
int
lf_move_fd(fd, need_fd)
int need_fd, fd;
{
int r = -1;
if (fd == need_fd)
return fd;
if (fd >=0 && fd < need_fd)
r = lf_move_fd(dup(fd), need_fd);
close(fd);
return r;
}
static int
logfile_reopen(name, wantfd, l)
char *name;
int wantfd;
struct logfile *l;
{
int got_fd;
close(wantfd);
if (((got_fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
lf_move_fd(got_fd, wantfd) < 0)
{
logfclose(l);
debug1("logfile_reopen: failed for %s\n", name);
return -1;
}
changed_logfile(l);
debug2("logfile_reopen: %d = %s\n", wantfd, name);
return 0;
}
static int (* lf_reopen_fn)() = logfile_reopen;
void
logreopen_register(fn)
int (*fn) __P((char *, int, struct logfile *));
{
lf_reopen_fn = fn ? fn : logfile_reopen;
}
static int
stolen_logfile(l)
struct logfile *l;
{
struct stat o, *s = l->st;
o = *s;
if (fstat(fileno(l->fp), s) < 0)
s->st_ino = s->st_dev = 0;
ASSERT(s == l->st);
if (!o.st_dev && !o.st_ino)
return 0;
if ((!s->st_dev && !s->st_ino) ||
!s->st_nlink ||
(s->st_size < o.st_size) ||
(s->st_mtime != o.st_mtime) ||
((s->st_ctime != o.st_ctime) &&
!(s->st_mtime == s->st_ctime &&
o.st_ctime < s->st_ctime)))
{
debug1("stolen_logfile: %s stolen!\n", l->name);
debug3("st_dev %d, st_ino %d, st_nlink %d\n",
(int)s->st_dev, (int)s->st_ino, (int)s->st_nlink);
debug2("s->st_size %d, o.st_size %d\n", (int)s->st_size, (int)o.st_size);
debug2("s->st_mtime %d, o.st_mtime %d\n",
(int)s->st_mtime, (int)o.st_mtime);
debug2("s->st_ctime %d, o.st_ctime %d\n",
(int)s->st_ctime, (int)o.st_ctime);
return -1;
}
debug1("stolen_logfile: %s o.k.\n", l->name);
return 0;
}
static struct logfile *
lookup_logfile(name)
char *name;
{
struct logfile *l;
for (l = logroot; l; l = l->next)
if (!strcmp(name, l->name))
return l;
return NULL;
}
struct logfile *
logfopen(name, fp)
char *name;
FILE *fp;
{
struct logfile *l;
if (!fp)
{
if (!(l = lookup_logfile(name)))
return NULL;
l->opencount++;
return l;
}
if (!(l = (struct logfile *)malloc(sizeof(struct logfile))))
return NULL;
if (!(l->st = (struct stat *)malloc(sizeof(struct stat))))
{
free((char *)l);
return NULL;
}
if (!(l->name = SaveStr(name)))
{
free((char *)l->st);
free((char *)l);
return NULL;
}
l->fp = fp;
l->opencount = 1;
l->writecount = 0;
l->flushcount = 0;
changed_logfile(l);
l->next = logroot;
logroot = l;
return l;
}
int
islogfile(name)
char *name;
{
if (!name)
return logroot ? 1 : 0;
return lookup_logfile(name) ? 1 : 0;
}
int
logfclose(l)
struct logfile *l;
{
struct logfile **lp;
for (lp = &logroot; *lp; lp = &(*lp)->next)
if (*lp == l)
break;
if (!*lp)
return -1;
if ((--l->opencount) > 0)
return 0;
if (l->opencount < 0)
abort();
*lp = l->next;
fclose(l->fp);
free(l->name);
free((char *)l);
return 0;
}
int
logfwrite(l, buf, n)
struct logfile *l;
char *buf;
int n;
{
int r;
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r = fwrite(buf, n, 1, l->fp);
l->writecount += l->flushcount + 1;
l->flushcount = 0;
changed_logfile(l);
return r;
}
int
logfflush(l)
struct logfile *l;
{
int r = 0;
if (!l)
for (l = logroot; l; l = l->next)
{
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r |= fflush(l->fp);
l->flushcount++;
changed_logfile(l);
}
else
{
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r = fflush(l->fp);
l->flushcount++;
changed_logfile(l);
}
return r;
}