#include <config.h>
#include <stdio.h>
#include "cvs.h"
#include "buffer.h"
#if defined CLIENT_SUPPORT || defined SERVER_SUPPORT
struct log_buffer
{
struct buffer *buf;
FILE *log;
#ifdef PROXY_SUPPORT
bool fatal_errors;
char *back_fn;
bool disabled;
struct buffer *back_buf;
size_t max;
bool tofile;
#endif
};
#ifdef PROXY_SUPPORT
static inline void
log_buffer_force_file (struct log_buffer *lb)
{
lb->log = cvs_temp_file (&lb->back_fn);
if (!lb->log)
error (lb->fatal_errors, errno, "failed to open log file.");
}
#endif
static int log_buffer_input (void *, char *, size_t, size_t, size_t *);
static int log_buffer_output (void *, const char *, size_t, size_t *);
static int log_buffer_flush (void *);
static int log_buffer_block (void *, bool);
static int log_buffer_get_fd (void *);
static int log_buffer_shutdown (struct buffer *);
struct buffer *
log_buffer_initialize (struct buffer *buf, FILE *fp,
# ifdef PROXY_SUPPORT
bool fatal_errors,
size_t max,
# endif
bool input,
void (*memory) (struct buffer *))
{
struct log_buffer *lb = xmalloc (sizeof *lb);
struct buffer *retbuf;
lb->buf = buf;
lb->log = fp;
#ifdef PROXY_SUPPORT
lb->back_fn = NULL;
lb->fatal_errors = fatal_errors;
lb->disabled = false;
assert (size_in_bounds_p (max));
lb->max = max;
lb->tofile = false;
lb->back_buf = buf_nonio_initialize (memory);
#endif
retbuf = buf_initialize (input ? log_buffer_input : NULL,
input ? NULL : log_buffer_output,
input ? NULL : log_buffer_flush,
log_buffer_block, log_buffer_get_fd,
log_buffer_shutdown, memory, lb);
if (!buf_empty_p (buf))
{
struct buffer_data *data;
#ifdef PROXY_SUPPORT
size_t total = 0;
#endif
for (data = buf->data; data != NULL; data = data->next)
{
#ifdef PROXY_SUPPORT
if (!lb->tofile)
{
total = xsum (data->size, total);
if (total >= max)
lb->tofile = true;
}
if (lb->tofile)
{
if (!lb->log) log_buffer_force_file (lb);
if (lb->log)
{
#endif
if (fwrite (data->bufp, 1, data->size, lb->log)
!= (size_t) data->size)
error (
#ifdef PROXY_SUPPORT
fatal_errors,
#else
false,
#endif
errno, "writing to log file");
fflush (lb->log);
#ifdef PROXY_SUPPORT
}
}
else
buf_copy_data (lb->back_buf, data, data);
#endif
}
buf_append_buffer (retbuf, buf);
}
return retbuf;
}
static int
log_buffer_input (void *closure, char *data, size_t need, size_t size,
size_t *got)
{
struct log_buffer *lb = closure;
int status;
assert (lb->buf->input);
status = (*lb->buf->input) (lb->buf->closure, data, need, size, got);
if (status != 0)
return status;
if (
#ifdef PROXY_SUPPORT
!lb->disabled &&
#endif
*got > 0)
{
#ifdef PROXY_SUPPORT
if (!lb->tofile
&& xsum (*got, buf_count_mem (lb->back_buf)) >= lb->max)
lb->tofile = true;
if (lb->tofile)
{
if (!lb->log) log_buffer_force_file (lb);
if (lb->log)
{
#endif
if (fwrite (data, 1, *got, lb->log) != *got)
error (
#ifdef PROXY_SUPPORT
lb->fatal_errors,
#else
false,
#endif
errno, "writing to log file");
fflush (lb->log);
#ifdef PROXY_SUPPORT
}
}
else
buf_output (lb->back_buf, data, *got);
#endif
}
return 0;
}
static int
log_buffer_output (void *closure, const char *data, size_t have, size_t *wrote)
{
struct log_buffer *lb = closure;
int status;
assert (lb->buf->output);
status = (*lb->buf->output) (lb->buf->closure, data, have, wrote);
if (status != 0)
return status;
if (
#ifdef PROXY_SUPPORT
!lb->disabled &&
#endif
*wrote > 0)
{
#ifdef PROXY_SUPPORT
if (!lb->tofile
&& xsum (*wrote, buf_count_mem (lb->back_buf)) >= lb->max)
lb->tofile = true;
if (lb->tofile)
{
if (!lb->log) log_buffer_force_file (lb);
if (lb->log)
{
#endif
if (fwrite (data, 1, *wrote, lb->log) != *wrote)
error (
#ifdef PROXY_SUPPORT
lb->fatal_errors,
#else
false,
#endif
errno, "writing to log file");
fflush (lb->log);
#ifdef PROXY_SUPPORT
}
}
else
buf_output (lb->back_buf, data, *wrote);
#endif
}
return 0;
}
static int
log_buffer_flush (void *closure)
{
struct log_buffer *lb = closure;
assert (lb->buf->flush);
if (lb->log && (fflush (lb->log)))
error (0, errno, "flushing log file");
return (*lb->buf->flush) (lb->buf->closure);
}
static int
log_buffer_block (void *closure, bool block)
{
struct log_buffer *lb = closure;
if (block)
return set_block (lb->buf);
else
return set_nonblock (lb->buf);
}
#ifdef PROXY_SUPPORT
struct buffer *
log_buffer_rewind (struct buffer *buf)
{
struct log_buffer *lb = buf->closure;
struct buffer *retbuf;
int fd;
lb->disabled = true;
if (lb->log)
{
FILE *tmp = lb->log;
lb->log = NULL;
if (fflush (tmp) < 0)
error (0, errno, "flushing log file");
rewind (tmp);
fd = dup (fileno (tmp));
if (fclose (tmp) < 0)
error (0, errno, "closing log file");
}
else
fd = open (DEVNULL, O_RDONLY);
if (fd < 0)
{
error (lb->fatal_errors, errno, "failed to rewind log buf.");
return NULL;
}
retbuf = fd_buffer_initialize (fd, 0, NULL, true, buf->memory_error);
{
struct buffer *tmp;
buf_append_buffer (retbuf, lb->back_buf);
tmp = lb->back_buf;
lb->back_buf = NULL;
buf_free (tmp);
}
return retbuf;
}
#endif
#ifndef PROXY_SUPPORT
static
#endif
void
log_buffer_closelog (struct buffer *buf)
{
struct log_buffer *lb = buf->closure;
void *tmp;
#ifdef PROXY_SUPPORT
lb->disabled = true;
#endif
if (lb->log)
{
tmp = lb->log;
lb->log = NULL;
if (fclose (tmp) < 0)
error (0, errno, "closing log file");
}
#ifdef PROXY_SUPPORT
if (lb->back_fn)
{
tmp = lb->back_fn;
lb->back_fn = NULL;
if (CVS_UNLINK (tmp))
error (0, errno, "Failed to delete log file.");
free (tmp);
}
if (lb->back_buf)
{
tmp = lb->back_buf;
lb->back_buf = NULL;
buf_free (tmp);
}
#endif
}
static int
log_buffer_get_fd (void *closure)
{
struct log_buffer *lb = closure;
return buf_get_fd (lb->buf);
}
static int
log_buffer_shutdown (struct buffer *buf)
{
struct log_buffer *lb = buf->closure;
log_buffer_closelog (buf);
return buf_shutdown (lb->buf);
}
void
setup_logfiles (char *var, struct buffer **to_server_p,
struct buffer **from_server_p)
{
char *log = getenv (var);
if (log)
{
int len = strlen (log);
char *buf = xmalloc (len + 5);
char *p;
FILE *fp;
strcpy (buf, log);
p = buf + len;
strcpy (p, ".in");
fp = fopen (buf, "wb");
if (!fp)
error (0, errno, "opening to-server logfile %s", buf);
else
*to_server_p = log_buffer_initialize (*to_server_p, fp,
# ifdef PROXY_SUPPORT
false,
0,
# endif
false, NULL);
strcpy (p, ".out");
fp = fopen (buf, "wb");
if (!fp)
error (0, errno, "opening from-server logfile %s", buf);
else
*from_server_p = log_buffer_initialize (*from_server_p, fp,
# ifdef PROXY_SUPPORT
false,
0,
# endif
true, NULL);
free (buf);
}
}
#endif