Bring in Jeff Wheelhouse's CLOG / circular log file support for syslogd,
authorMatthew Dillon <dillon@dragonflybsd.org>
Sat, 30 Oct 2004 20:26:48 +0000 (20:26 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sat, 30 Oct 2004 20:26:48 +0000 (20:26 +0000)
With some formatting changes.

Side note: CLOG's use of mmap within syslogd is very dangerous and could
lead to syslogd seg-faulting out, especially since the control structure
appears to reside at the end of the physical log file.  Users of circular
log files must take care.

Requested-by: The MOB!
usr.sbin/Makefile
usr.sbin/clog/Makefile [new file with mode: 0644]
usr.sbin/clog/clog.8 [new file with mode: 0644]
usr.sbin/clog/clog.c [new file with mode: 0644]
usr.sbin/clog/clog.h [new file with mode: 0644]
usr.sbin/syslogd/syslog.conf.5
usr.sbin/syslogd/syslogd.c

index f1a0ab0..f83d8ce 100644 (file)
@@ -1,6 +1,6 @@
 #      From: @(#)Makefile      5.20 (Berkeley) 6/12/93
 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $
-# $DragonFly: src/usr.sbin/Makefile,v 1.14 2004/09/23 06:38:29 simokawa Exp $
+# $DragonFly: src/usr.sbin/Makefile,v 1.15 2004/10/30 20:26:44 dillon Exp $
 
 # XXX MISSING:         mkproto
 SUBDIR=        IPXrouted \
@@ -21,6 +21,7 @@ SUBDIR=       IPXrouted \
        chown \
        chroot \
        ckdist \
+       clog \
        config \
        cron \
        crunch \
diff --git a/usr.sbin/clog/Makefile b/usr.sbin/clog/Makefile
new file mode 100644 (file)
index 0000000..415b569
--- /dev/null
@@ -0,0 +1,10 @@
+#      @(#)Makefile    8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.2 2001/10/02 06:03:21 jdw Exp $
+# $DragonFly: src/usr.sbin/clog/Makefile,v 1.1 2004/10/30 20:26:46 dillon Exp $
+
+PROG=  clog
+SRCS=  clog.c 
+MAN8=  clog.8
+COPTS+=        -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/clog/clog.8 b/usr.sbin/clog/clog.8
new file mode 100644 (file)
index 0000000..9d23d72
--- /dev/null
@@ -0,0 +1,99 @@
+.\" Copyright (c) 2001
+.\"    Jeffrey D. Wheelhouse.  All rights reserved.
+.\"
+.\" This code was originally developed by Jeff Wheelhouse (jdw@wwwi.com).
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY JEFF WHEELHOUSE ``AS IS'' AND ANY EXPRESS OR 
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+.\" NO EVENT SHALL JEFF WHEELHOUSE BE LIABLE FOR ANY DIRECT, INDIRECT, 
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: clog.8,v 1.2 2001/10/02 04:41:21 jdw Exp $
+.\" $DragonFly: src/usr.sbin/clog/clog.8,v 1.1 2004/10/30 20:26:46 dillon Exp $
+.\"
+.Dd October 1, 2001
+.Dt CLOG 8
+.Os BSD 4
+.Sh NAME
+.Nm clog
+.Nd "display or initialize a circular system log"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f
+.Op Fl i Fl s Ar size
+.Ar logfile
+.Sh DESCRIPTION
+.Nm Clog
+displays or initializes a circular log file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f
+Display the contents of the circular logfile
+.Ar logfile ,
+then go into a loop waiting for
+new material to arrive.  This is essentially the same as using the
+.Fl f
+option of the 
+.Xr tail 1
+command on a standard syslog file.
+.It Fl i
+Initialize 
+.Ar logfile
+rather than reading it.  This option requires the 
+.Fl s
+option.  If 
+.Ar logfile
+already exists, it will be truncated and recreated by this command.
+.It Fl s
+This option specifies the size in bytes of the circular logfile that should 
+be created.  This option requires the
+.Fl i
+option.
+.El
+.Sh ABOUT CIRCULAR LOGFILES
+The
+.Nm
+command supports circular logfiles for 
+.Xr syslogd 8 .
+A circular logfile differs from a standard syslog file in that is has a fixed
+size.  It does not grow, and does not need to be rotated.  When 
+.Xr syslogd 8 
+reaches the end of a circular logfile, it simply begins again at the beginning,
+overwriting the oldest data.  The circular logfile also contains information 
+allowing 
+.Nm
+to establish what parts of the file are valid, and in what order they should
+be displayed.
+.Pp
+Circular logfiles are primarily useful for their ability to control the amount
+of storage devoted to logfiles.  This may be valuable when storage space is 
+at a premium or when the consequences of running out of storage space are 
+unacceptable.  Circular logfiles can safely be used on a memory disk (see
+.Xr md 4 ).
+.Pp
+Circular logfiles are also useful to catch messages that are generated rapidly
+but soon lose relevance, such as messages logged at debug priority.
+
+.Sh SEE ALSO
+.Xr syslogd 8 ,
+.Xr syslog.conf 5
+.Sh HISTORY
+The
+.Nm
+command was written for FreeBSD 4.3 but is not yet part of a BSD distribution.
diff --git a/usr.sbin/clog/clog.c b/usr.sbin/clog/clog.c
new file mode 100644 (file)
index 0000000..87fb6c1
--- /dev/null
@@ -0,0 +1,226 @@
+/*-
+ * Copyright (c) 2001
+ *     Jeff Wheelhouse (jdw@wwwi.com)
+ *
+ * This code was originally developed by Jeff Wheelhouse (jdw@wwwi.com).
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistribution of source code must retail the above copyright 
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JEFF WHEELHOUSE ``AS IS'' AND ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN 
+ * NO EVENT SHALL JEFF WHEELHOUSE BE LIABLE FOR ANY DIRECT, INDIRECT, 
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: clog.c,v 1.3 2001/10/02 18:51:26 jdw Exp $
+ * $DragonFly: src/usr.sbin/clog/clog.c,v 1.1 2004/10/30 20:26:46 dillon Exp $
+ */
+
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+
+#include "clog.h"
+
+
+/*
+ *  The BUFFER_SIZE value is just used to allocate a buffer full of NULLs 
+ *  so that a new logfile can be extended to its full size.
+ *
+ *  Compiling with -pedantic complains when the buffer array is declared
+ *  if I declare this as a const instead of a #define.
+ */
+#define BUFFER_SIZE 16384
+
+void init_log __P((const char *lname, size_t size));
+void read_log __P((const char *lname, int optf));
+void usage __P((void));
+
+const char *pname;
+
+int main(int argc, char **argv) {
+       int ch;
+       int init = 0;
+       int size = 0;
+       int optf = 0;
+
+       pname = argv[0];
+
+       while ((ch = getopt(argc, argv, "fis:")) != -1)
+               switch(ch) {
+               case 'i':
+                       init = 1;
+                       break;
+               case 's':
+                       size = atol(optarg);
+                       if (size==0) usage();
+                       break;
+               case 'f':
+                       optf = 1;
+               }
+
+       if ((size>0)&&(init==0)) {
+               fprintf(stderr,"%s: WARNING: -s argument ignored without -i.\n",pname);
+               size = 0;
+       }
+       if (argv[optind]==NULL) {
+               fprintf(stderr,"%s: ERROR: log_file argument must be specified.\n",pname);
+               usage();
+       }
+       if ((init==1)&&(size==0)) {
+               fprintf(stderr,"%s: ERROR: -i argument requires -s.\n",pname);
+               usage();
+       }
+       if ((init==1)&&(optf==1)) {
+               fprintf(stderr,"%s: ERROR: flags -f and -i are incompatible.\n",pname);
+               usage();
+       }
+
+       if (init==1) init_log(argv[optind],size);
+       /* if (optf==1) follow_log(artv[optind]); */
+       read_log(argv[optind],optf);
+               
+       return 0;
+}
+
+
+void usage() {
+  fprintf(stderr,"usage: %s [-i -s log_size] [ -f ] log_file\n",pname);
+  exit(1);
+}
+
+
+void read_log(const char *lname, int optf) {
+       int fd;
+       struct stat sb;
+       struct clog_footer *pcf;
+       char *pbuffer;
+       struct iovec iov[2];
+       int iovcnt = 0;
+       uint32_t start = 0;
+       uint32_t next;
+       struct pollfd pfd;
+
+       pfd.fd = -1;
+
+       fd = open(lname,O_RDONLY);
+       if (fd==-1) {
+               fprintf(stderr,"%s: ERROR: could not open %s (%s)\n",pname,lname,strerror(errno));
+               exit(11);
+       }
+
+       if (fstat(fd,&sb)==-1) {
+               fprintf(stderr,"%s: ERROR: could not stat %s (%s)\n",pname,lname,strerror(errno));
+               exit(13);
+       }
+       pbuffer = mmap(NULL,sb.st_size,PROT_READ,MAP_SHARED,fd,0);
+       if (pbuffer==NULL) {
+               fprintf(stderr,"%s: ERROR: could not mmap %s body (%s)\n",pname,lname,strerror(errno));
+               exit(14);
+       }
+       pcf = (struct clog_footer*)(pbuffer + sb.st_size - sizeof(struct clog_footer));
+
+       if (pcf->cf_wrap==1) start = pcf->cf_next + 1;
+       while(1) {
+               while(pcf->cf_lock==1) sched_yield();
+               next = pcf->cf_next;
+               iovcnt = 0;
+               if (start>next) {
+                       iov[iovcnt].iov_base = pbuffer + start;
+                       iov[iovcnt++].iov_len = pcf->cf_max - start;
+                       start = 0;
+               }
+               iov[iovcnt].iov_base = pbuffer + start;
+               iov[iovcnt++].iov_len = next - start;
+               if (writev(1,iov,iovcnt)==-1) {
+                       fprintf(stderr,"%s: ERROR: could not write output (%s)\n",pname,strerror(errno));
+                       exit(15);
+               }
+               start = next;
+               if (optf==0) break;
+               if (poll(&pfd,1,50)==-1) {
+                       fprintf(stderr,"%s: ERROR: could not poll (%s)\n",pname,strerror(errno));
+                       exit(16);
+               }
+       }
+       
+       (void)munmap(pbuffer,sb.st_size);
+       (void)close(fd);
+
+       exit(0);
+}
+
+
+void init_log(const char *lname, size_t size) {
+       int fd;
+       size_t fill = size;
+       char buffer[BUFFER_SIZE];
+       struct clog_footer cf;
+
+       memcpy(&cf.cf_magic,MAGIC_CONST,4);
+       cf.cf_max = size - sizeof(struct clog_footer);
+
+       (void)memset(buffer,0,BUFFER_SIZE);
+
+       fd = open(lname,O_RDWR|O_CREAT,0666); 
+       if (fd==-1) {
+               fprintf(stderr,"%s: ERROR: could not open %s (%s)\n",pname,lname,strerror(errno));
+               exit(2);
+       }
+       if (ftruncate(fd,(off_t)0)==-1) {
+               fprintf(stderr,"%s: ERROR: could not truncate %s (%s)\n",pname,lname,strerror(errno));
+               exit(3);
+       }
+       
+       while(fill>BUFFER_SIZE) {
+               if (write(fd,buffer,BUFFER_SIZE)==-1){
+                       fprintf(stderr,"%s: ERROR: could not write %s (%s)\n",pname,lname,strerror(errno));
+                       exit(4);
+               }
+               fill -= BUFFER_SIZE;
+       }
+       assert(fill<=BUFFER_SIZE);
+       if (fill>0) {
+               if (write(fd,buffer,fill)==-1) {
+                       fprintf(stderr,"%s: ERROR: could not write %s (%s)\n",pname,lname,strerror(errno));
+                       exit(5);
+               }
+       }
+       if (lseek(fd,-(off_t)(sizeof(struct clog_footer)),SEEK_END)==-1) {
+               fprintf(stderr,"%s: ERROR: could not seek in %s (%s)\n",pname,lname,strerror(errno));
+               exit(6);
+       }
+       if (write(fd,&cf,sizeof(cf))==-1) {
+               fprintf(stderr,"%s: ERROR: could not write magic in %s (%s)\n",pname,lname,strerror(errno));
+               exit(7);
+       }
+       (void)close(fd);
+       exit(0);
+}
+
+
diff --git a/usr.sbin/clog/clog.h b/usr.sbin/clog/clog.h
new file mode 100644 (file)
index 0000000..14189c3
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2001
+ *     Jeff Wheelhouse (jdw@wwwi.com)
+ *
+ * This code was originally developed by Jeff Wheelhouse (jdw@wwwi.com).
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistribution of source code must retail the above copyright 
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JEFF WHEELHOUSE ``AS IS'' AND ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN 
+ * NO EVENT SHALL JEFF WHEELHOUSE BE LIABLE FOR ANY DIRECT, INDIRECT, 
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: clog.h,v 1.2 2001/10/02 04:43:52 jdw Exp $
+ * $DragonFly: src/usr.sbin/clog/clog.h,v 1.1 2004/10/30 20:26:46 dillon Exp $
+ */
+
+
+#ifndef _CLOG_H_
+#define _CLOG_H_
+
+/*
+ *  This magic constant is used to identify a valid circular log file.
+ *  syslogd will ignore any circular log file that doesn't have this constant.
+ */
+
+const char MAGIC_CONST[4] = "CLOG";
+
+
+struct clog_footer {
+       uint32_t cf_magic;
+       uint32_t cf_wrap;
+       uint32_t cf_next;
+       uint32_t cf_max;
+       uint32_t cf_lock;
+};
+
+
+#endif  /* _CLOG_H_ */
+       
+
index 4bba154..cf12b31 100644 (file)
@@ -31,7 +31,7 @@
 .\"
 .\"     @(#)syslog.conf.5      8.1 (Berkeley) 6/9/93
 .\" $FreeBSD: src/usr.sbin/syslogd/syslog.conf.5,v 1.35 2004/07/03 18:35:53 ru Exp $
-.\" $DragonFly: src/usr.sbin/syslogd/syslog.conf.5,v 1.3 2004/08/09 20:11:19 dillon Exp $
+.\" $DragonFly: src/usr.sbin/syslogd/syslog.conf.5,v 1.4 2004/10/30 20:26:48 dillon Exp $
 .\"
 .Dd June 9, 1993
 .Dt SYSLOG.CONF 5
@@ -350,6 +350,12 @@ if they are logged in.
 An asterisk.
 Selected messages are written to all logged-in users.
 .It
+A percent sign
+.Pq Dq \&% ,
+followed by a pathname (beginning with a leading slash).  Selected messages
+are written to a circular log file.  See clog(8) for a discussion of
+circular log files.
+.It
 A vertical bar
 .Pq Dq \&| ,
 followed by a command to pipe the selected
index c5de032..50b242f 100644 (file)
@@ -33,7 +33,7 @@
  * @(#) Copyright (c) 1983, 1988, 1993, 1994 The Regents of the University of California.  All rights reserved.
  * @(#)syslogd.c       8.3 (Berkeley) 4/4/94
  * $FreeBSD: src/usr.sbin/syslogd/syslogd.c,v 1.130 2004/07/04 19:52:48 cperciva Exp $
- * $DragonFly: src/usr.sbin/syslogd/syslogd.c,v 1.3 2004/08/09 20:11:19 dillon Exp $
+ * $DragonFly: src/usr.sbin/syslogd/syslogd.c,v 1.4 2004/10/30 20:26:48 dillon Exp $
  */
 
 /*
@@ -60,6 +60,7 @@
  *   by Peter da Silva.
  * -u and -v by Harlan Stenn.
  * Priority comparison code by Harlan Stenn.
+ * Ring buffer code by Jeff Wheelhouse.
  */
 
 #define        MAXLINE         1024            /* maximum line length */
@@ -80,6 +81,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/syslimits.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 
 #include <netinet/in.h>
 
 #include "pathnames.h"
 #include "ttymsg.h"
+#include "../clog/clog.h"
 
 #define SYSLOG_NAMES
 #include <sys/syslog.h>
@@ -116,6 +119,7 @@ static const int withscopeid;
 const char     *ConfFile = _PATH_LOGCONF;
 const char     *PidFile = _PATH_LOGPID;
 const char     ctty[] = _PATH_CONSOLE;
+const char     ring_magic[] = "CLOG";
 
 #define        dprintf         if (Debug) printf
 
@@ -168,6 +172,11 @@ struct filed {
                        char    f_pname[MAXPATHLEN];
                        pid_t   f_pid;
                } f_pipe;
+               struct {
+                       char    f_rname[MAXPATHLEN];
+                       struct clog_footer *f_footer;
+                       size_t  f_size;
+               } f_ring;
        } f_un;
        char    f_prevline[MAXSVLINE];          /* last message logged */
        char    f_lasttime[16];                 /* time of last occurrence */
@@ -246,10 +255,12 @@ int       repeatinterval[] = { 30, 120, 600 };    /* # of secs before flush */
 #define F_USERS                5               /* list of users */
 #define F_WALL         6               /* everyone logged on */
 #define F_PIPE         7               /* pipe to program */
+#define F_RING         8               /* ring buffer (circular log) */
 
-const char *TypeNames[8] = {
+const char *TypeNames[] = {
        "UNUSED",       "FILE",         "TTY",          "CONSOLE",
-       "FORW",         "USERS",        "WALL",         "PIPE"
+       "FORW",         "USERS",        "WALL",         "PIPE",
+       "RING"
 };
 
 static struct filed *Files;    /* Log files that we write to */
@@ -309,6 +320,8 @@ static int  skip_message(const char *, const char *, int);
 static void    printline(const char *, char *);
 static void    printsys(char *);
 static int     p_open(const char *, pid_t *);
+ssize_t                rbwrite(struct filed *, char *, size_t);
+ssize_t                rbwritev(struct filed *, struct iovec *, int);
 static void    readklog(void);
 static void    reapchild(int);
 static void    usage(void);
@@ -1169,6 +1182,21 @@ fprintlog(struct filed *f, int flags, const char *msg)
                }
                break;
 
+       case F_RING:
+               dprintf(" %s\n", f->f_un.f_ring.f_rname);
+               v->iov_base = "\n";
+               v->iov_len = 1;
+               if (rbwritev(f, iov, 7) == -1) {
+                       int e = errno;
+                       munmap(f->f_un.f_ring.f_footer,
+                               sizeof(struct clog_footer));
+                       close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       errno = e;
+                       logerror(f->f_un.f_fname);
+               }
+               break;
+
        case F_PIPE:
                dprintf(" %s\n", f->f_un.f_pipe.f_pname);
                v->iov_base = lf;
@@ -1482,6 +1510,11 @@ init(int signo)
                        }
                        f->f_un.f_pipe.f_pid = 0;
                        break;
+               case F_RING:
+                       munmap(f->f_un.f_ring.f_footer,
+                               sizeof(struct clog_footer));
+                       close(f->f_file);
+                       break;
                }
                next = f->f_next;
                if (f->f_program) free(f->f_program);
@@ -1604,6 +1637,10 @@ init(int signo)
                                printf("%s", f->f_un.f_forw.f_hname);
                                break;
 
+                       case F_RING:
+                               printf("%s", f->f_un.f_ring.f_rname);
+                               break;
+
                        case F_PIPE:
                                printf("%s", f->f_un.f_pipe.f_pname);
                                break;
@@ -1654,6 +1691,7 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host)
        const char *p, *q;
        char *bp;
        char buf[MAXLINE], ebuf[100];
+       struct stat sb;
 
        dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
 
@@ -1842,6 +1880,42 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host)
                }
                break;
 
+       case '%':
+               if ((f->f_file = open(p + 1, O_RDWR, 0 )) < 0) {
+                       f->f_type = F_UNUSED;
+                       logerror(p + 1);
+                       break;
+               }
+               if (fstat(f->f_file,&sb) < 0) {
+                       close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       logerror(p+1);
+                       break;
+               }
+               f->f_un.f_ring.f_footer = 
+                       mmap(NULL, sizeof(struct clog_footer), 
+                            PROT_READ|PROT_WRITE, MAP_SHARED, 
+                            f->f_file, sb.st_size-sizeof(struct clog_footer));
+               if (f->f_un.f_ring.f_footer == NULL) {
+                       close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       logerror(p + 1);
+                       break;
+               }
+               if (memcmp(&(f->f_un.f_ring.f_footer->cf_magic), MAGIC_CONST, 4) != 0) {
+                       munmap(f->f_un.f_ring.f_footer,
+                               sizeof(struct clog_footer));
+                       close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       errno = ENODEV;
+                       logerror(p + 1);
+                       break;
+               }
+               f->f_un.f_ring.f_size = sb.st_size;
+               strcpy(f->f_un.f_ring.f_rname, p + 1);
+               f->f_type = F_RING;
+               break;
+
        case '|':
                f->f_un.f_pipe.f_pid = 0;
                (void)strlcpy(f->f_un.f_fname, p + 1, sizeof(f->f_un.f_fname));
@@ -2530,3 +2604,59 @@ socksetup(int af, const char *bindhostname)
 
        return (socks);
 }
+
+ssize_t
+rbwritev(struct filed *f, struct iovec *iov, int iovcnt) 
+{
+       int i;
+       ssize_t out = 0;
+       ssize_t error;
+
+       for (i = 0; i < iovcnt; i++) {
+               error = rbwrite(f, iov[i].iov_base, iov[i].iov_len);
+               if (error == -1)
+                       return (-1);
+               out += error;
+       }
+       return (out);
+}
+
+
+ssize_t
+rbwrite(struct filed *f, char *buf, size_t nbytes) 
+{
+       size_t maxwrite;
+       ssize_t error;
+       ssize_t out;
+
+       maxwrite = f->f_un.f_ring.f_footer->cf_max - 
+                  f->f_un.f_ring.f_footer->cf_next;
+       out = 0;
+
+       f->f_un.f_ring.f_footer->cf_lock = 1;
+       while (nbytes > 0) {
+               maxwrite = f->f_un.f_ring.f_footer->cf_max - 
+                          f->f_un.f_ring.f_footer->cf_next;
+               if (maxwrite > nbytes)
+                       maxwrite = nbytes;
+               error = pwrite(f->f_file, buf, maxwrite,
+                              f->f_un.f_ring.f_footer->cf_next);
+               if (error == -1) {
+                       f->f_un.f_ring.f_footer->cf_lock = 0;
+                       return (-1);
+               }
+               nbytes -= error;
+               out += error;
+               buf += error;
+               f->f_un.f_ring.f_footer->cf_next += error;
+               if (f->f_un.f_ring.f_footer->cf_next == 
+                   f->f_un.f_ring.f_footer->cf_max) {
+                       f->f_un.f_ring.f_footer->cf_next = 0;
+                       f->f_un.f_ring.f_footer->cf_wrap = 1;
+               }
+               
+       }
+                       
+       f->f_un.f_ring.f_footer->cf_lock = 0;
+       return (out);
+}