summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2004-01-04 08:28:50 +0000
committerDamien Miller <djm@cvs.openbsd.org>2004-01-04 08:28:50 +0000
commitc1606a8b4a93bb266cfcf1699e631fd5a316aaa6 (patch)
tree21dd93ca259055fc14af3b1fbfdc2066a803de1b
parentf2546e76535bf0bf40a4b51c451e6354435df578 (diff)
Buffered logging for syslogd. Logs may be stored in memory buffers and
extracted using a small client. Useful for diskless systems. much feedback from deraadt@, canacar@, jmc@, jakob@ ; ok deraadt@
-rw-r--r--usr.sbin/syslogd/Makefile4
-rw-r--r--usr.sbin/syslogd/privsep.c10
-rw-r--r--usr.sbin/syslogd/ringbuf.c150
-rw-r--r--usr.sbin/syslogd/syslog.conf.518
-rw-r--r--usr.sbin/syslogd/syslogd.811
-rw-r--r--usr.sbin/syslogd/syslogd.c429
-rw-r--r--usr.sbin/syslogd/syslogd.h17
7 files changed, 586 insertions, 53 deletions
diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile
index 2cefbabc26a..82a60fe421a 100644
--- a/usr.sbin/syslogd/Makefile
+++ b/usr.sbin/syslogd/Makefile
@@ -1,7 +1,7 @@
-# $OpenBSD: Makefile,v 1.4 2003/07/31 18:20:07 avsm Exp $
+# $OpenBSD: Makefile,v 1.5 2004/01/04 08:28:49 djm Exp $
PROG= syslogd
-SRCS= syslogd.c ttymsg.c privsep.c privsep_fdpass.c
+SRCS= syslogd.c ttymsg.c privsep.c privsep_fdpass.c ringbuf.c
MAN= syslogd.8 syslog.conf.5
.include <bsd.prog.mk>
diff --git a/usr.sbin/syslogd/privsep.c b/usr.sbin/syslogd/privsep.c
index 3e74e7ffbb6..aa06ed96a51 100644
--- a/usr.sbin/syslogd/privsep.c
+++ b/usr.sbin/syslogd/privsep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: privsep.c,v 1.12 2003/12/29 22:09:36 deraadt Exp $ */
+/* $OpenBSD: privsep.c,v 1.13 2004/01/04 08:28:49 djm Exp $ */
/*
* Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
@@ -168,6 +168,10 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
close(pfd[PFD_UNIX_0 + i].fd);
if (pfd[PFD_INET].fd != -1)
close(pfd[PFD_INET].fd);
+ if (pfd[PFD_CTLSOCK].fd != -1)
+ close(pfd[PFD_CTLSOCK].fd);
+ if (pfd[PFD_CTLCONN].fd != -1)
+ close(pfd[PFD_CTLCONN].fd);
if (pfd[PFD_KLOG].fd)
close(pfd[PFD_KLOG].fd);
@@ -309,8 +313,10 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
/* Unlink any domain sockets that have been opened */
for (i = 0; i < nfunix; i++)
- if (funixn[i] && pfd[PFD_UNIX_0 + i].fd != -1)
+ if (funixn[i] != NULL && pfd[PFD_UNIX_0 + i].fd != -1)
(void)unlink(funixn[i]);
+ if (ctlsock_path != NULL && pfd[PFD_CTLSOCK].fd != -1)
+ (void)unlink(ctlsock_path);
if (restart) {
int r;
diff --git a/usr.sbin/syslogd/ringbuf.c b/usr.sbin/syslogd/ringbuf.c
new file mode 100644
index 00000000000..7aba3f6a302
--- /dev/null
+++ b/usr.sbin/syslogd/ringbuf.c
@@ -0,0 +1,150 @@
+/* $OpenBSD: ringbuf.c,v 1.1 2004/01/04 08:28:49 djm Exp $ */
+
+/*
+ * Copyright (c) 2004 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Simple ringbuffer for lines of text.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "syslogd.h"
+
+/* Initialise a ring buffer */
+struct ringbuf *
+ringbuf_init(size_t len)
+{
+ struct ringbuf *ret;
+
+ if (len == 0 || (ret = malloc(sizeof(*ret))) == NULL)
+ return (NULL);
+
+ if ((ret->buf = malloc(len)) == NULL) {
+ free(ret);
+ return (NULL);
+ }
+
+ ret->len = len;
+ ret->start = ret->end = 0;
+
+ return (ret);
+}
+
+/* Clear a ring buffer */
+void
+ringbuf_clear(struct ringbuf *rb)
+{
+ rb->start = rb->end = 0;
+}
+
+/* Return the number of bytes used in a ringbuffer */
+size_t
+ringbuf_used(struct ringbuf *rb)
+{
+ return ((rb->len + rb->end - rb->start) % rb->len);
+}
+
+/*
+ * Append a line to a ring buffer, will delete lines from start
+ * of buffer as necessary
+ */
+int
+ringbuf_append_line(struct ringbuf *rb, char *line)
+{
+ size_t llen, used, copy_len;
+
+ if (rb == NULL || line == NULL)
+ return (-1);
+
+ llen = strlen(line);
+ if (line[llen - 1] != '\n')
+ llen++; /* one extra for appended '\n' */
+
+ if (rb == NULL || llen == 0 || llen >= rb->len)
+ return (-1);
+
+ /*
+ * If necessary, advance start pointer to make room for appended
+ * string. Ensure that start pointer is at the beginning of a line
+ * once we are done (i.e move to after '\n').
+ */
+ used = ringbuf_used(rb);
+ if (used + llen >= rb->len) {
+ rb->start = (rb->start + used + llen - rb->len) % rb->len;
+
+ /* Find next '\n' */
+ while (rb->buf[rb->start] != '\n')
+ rb->start = (rb->start + 1) % rb->len;
+ /* Skip it */
+ rb->start = (rb->start + 1) % rb->len;
+ }
+
+ /*
+ * Now append string, staring from last pointer and wrapping if
+ * necessary
+ */
+ if (rb->end + llen > rb->len) {
+ copy_len = rb->len - rb->end;
+ memcpy(rb->buf + rb->end, line, copy_len);
+ memcpy(rb->buf, line + copy_len, llen - copy_len - 1);
+ rb->buf[llen - copy_len - 1] = '\n';
+ } else {
+ memcpy(rb->buf + rb->end, line, llen - 1);
+ rb->buf[rb->end + llen - 1] = '\n';
+ }
+
+ rb->end = (rb->end + llen) % rb->len;
+
+ return (0);
+}
+
+/*
+ * Copy and nul-terminate a ringbuffer to a string.
+ */
+ssize_t
+ringbuf_to_string(char *buf, size_t len, struct ringbuf *rb)
+{
+ ssize_t copy_len, n;
+
+ if (buf == NULL || rb == NULL)
+ return (-1);
+
+ copy_len = MIN(len - 1, ringbuf_used(rb));
+
+ if (copy_len <= 0)
+ return (copy_len);
+
+ if (rb->start < rb->end)
+ memcpy(buf, rb->buf + rb->start, copy_len);
+ else {
+ /* If the buffer is wrapped, copy each hunk separately */
+ n = rb->len - rb->start;
+ memcpy(buf, rb->buf + rb->start, MIN(n, copy_len));
+ if (copy_len - n > 0)
+ memcpy(buf + n, rb->buf, MIN(rb->end, copy_len - n));
+ }
+ buf[copy_len] = '\0';
+
+ return (ringbuf_used(rb));
+}
diff --git a/usr.sbin/syslogd/syslog.conf.5 b/usr.sbin/syslogd/syslog.conf.5
index 02bf4725df7..b1bfc0812b7 100644
--- a/usr.sbin/syslogd/syslog.conf.5
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -26,7 +26,7 @@
.\" SUCH DAMAGE.
.\"
.\" from: @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
-.\" $OpenBSD: syslog.conf.5,v 1.13 2003/10/01 08:17:55 jmc Exp $
+.\" $OpenBSD: syslog.conf.5,v 1.14 2004/01/04 08:28:49 djm Exp $
.\" $NetBSD: syslog.conf.5,v 1.4 1996/01/02 17:41:46 perry Exp $
.\"
.Dd June 9, 1993
@@ -203,6 +203,15 @@ if they are logged in.
.It
An asterisk.
Selected messages are written to all logged-in users.
+.It
+A colon, followed by a memory buffer size
+.Pq in kilobytes
+another colon, followed by a buffer name.
+Selected messages are written to an in-memory buffer that may be read using
+.Xr syslogc 8 .
+Memory buffered logging is useful to provide access to log data on devices
+that lack local storage (e.g. diskless workstations or routers).
+The largest allowed buffer size is 256kb.
.El
.Pp
Blank lines and lines whose first non-blank character is a hash
@@ -249,9 +258,16 @@ mail,news.err /var/log/spoolerr
# Save ftpd transactions along with mail and news
!ftpd
*.* /var/log/spoolerr
+
+# Keep a copy of all logging in a 32k memory buffer named "debug"
+*.debug :32:debug
+
+# Store notices and authpriv messages in a 64k buffer named "important"
+*.notice,authpriv.* :64:important
.Ed
.Sh SEE ALSO
.Xr syslog 3 ,
+.Xr syslogc 8 ,
.Xr syslogd 8
.Sh HISTORY
The
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8
index efc96763182..f37aa8a1705 100644
--- a/usr.sbin/syslogd/syslogd.8
+++ b/usr.sbin/syslogd/syslogd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: syslogd.8,v 1.18 2003/06/12 12:59:53 jmc Exp $
+.\" $OpenBSD: syslogd.8,v 1.19 2004/01/04 08:28:49 djm Exp $
.\"
.\" Copyright (c) 1983, 1986, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -44,6 +44,7 @@
.Op Fl m Ar mark_interval
.Op Fl a Ar path
.Op Fl p Ar log_socket
+.Op Fl s Ar reporting_socket
.Ek
.Sh DESCRIPTION
.Nm
@@ -90,6 +91,11 @@ Up to about 20 additional logging sockets can be specified.
The primary use for this is to place additional log sockets in
.Pa /dev/log
of various chroot filespaces.
+.It Fl s Ar reporting_socket
+Specify path to an
+.Dv AF_LOCAL
+socket for use in reporting logs stored in memory buffers using
+.Xr syslogc 8 .
.El
.Pp
.Nm
@@ -160,7 +166,8 @@ kernel log device
.Xr syslog 3 ,
.Xr services 5 ,
.Xr syslog.conf 5 ,
-.Xr newsyslog 8
+.Xr newsyslog 8 ,
+.Xr syslogc 8
.Sh HISTORY
The
.Nm
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
index 3a0720ab6ef..bf1b7bfff57 100644
--- a/usr.sbin/syslogd/syslogd.c
+++ b/usr.sbin/syslogd/syslogd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: syslogd.c,v 1.69 2003/12/29 22:08:44 deraadt Exp $ */
+/* $OpenBSD: syslogd.c,v 1.70 2004/01/04 08:28:49 djm Exp $ */
/*
* Copyright (c) 1983, 1988, 1993, 1994
@@ -39,7 +39,7 @@ static const char copyright[] =
#if 0
static const char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
#else
-static const char rcsid[] = "$OpenBSD: syslogd.c,v 1.69 2003/12/29 22:08:44 deraadt Exp $";
+static const char rcsid[] = "$OpenBSD: syslogd.c,v 1.70 2004/01/04 08:28:49 djm Exp $";
#endif
#endif /* not lint */
@@ -63,9 +63,13 @@ static const char rcsid[] = "$OpenBSD: syslogd.c,v 1.69 2003/12/29 22:08:44 dera
* Author: Eric Allman
* extensive changes by Ralph Campbell
* more extensive changes by Eric Allman (again)
+ * memory buffer logging by Damien Miller
*/
#define MAXLINE 1024 /* maximum line length */
+#define MIN_MEMBUF (MAXLINE * 4) /* Minimum memory buffer size */
+#define MAX_MEMBUF (256 * 1024) /* Maximum memory buffer size */
+#define MAX_MEMBUF_NAME 64 /* Max length of membuf log name */
#define MAXSVLINE 120 /* maximum saved line length */
#define DEFUPRI (LOG_USER|LOG_NOTICE)
#define DEFSPRI (LOG_KERN|LOG_CRIT)
@@ -141,6 +145,10 @@ struct filed {
struct sockaddr_in f_addr;
} f_forw; /* forwarding address */
char f_fname[MAXPATHLEN];
+ struct {
+ char f_mname[MAX_MEMBUF_NAME];
+ struct ringbuf *f_rb;
+ } f_mb; /* Memory buffer */
} f_un;
char f_prevline[MAXSVLINE]; /* last message logged */
char f_lasttime[16]; /* time of last occurrence */
@@ -171,10 +179,11 @@ int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
#define F_FORW 4 /* remote machine */
#define F_USERS 5 /* list of users */
#define F_WALL 6 /* everyone logged on */
+#define F_MEMBUF 7 /* memory buffer */
-char *TypeNames[7] = {
+char *TypeNames[8] = {
"UNUSED", "FILE", "TTY", "CONSOLE",
- "FORW", "USERS", "WALL"
+ "FORW", "USERS", "WALL", "MEMBUF"
};
struct filed *Files;
@@ -195,6 +204,26 @@ int MarkSeq = 0; /* mark sequence number */
int SecureMode = 1; /* when true, speak only unix domain socks */
int NoDNS = 0; /* when true, will refrain from doing DNS lookups */
+char *ctlsock_path = NULL; /* Path to control socket */
+
+#define CTL_READING_CMD 1
+#define CTL_WRITING_REPLY 2
+int ctl_state = 0; /* What the control socket is up to */
+
+size_t ctl_cmd_bytes = 0; /* number of bytes of ctl_cmd read */
+struct {
+#define CMD_READ 1 /* Read out log */
+#define CMD_READ_CLEAR 2 /* Read and clear log */
+#define CMD_CLEAR 3 /* Clear log */
+#define CMD_LIST 4 /* List available logs */
+ int cmd;
+ char logname[MAX_MEMBUF_NAME];
+} ctl_cmd;
+
+char *ctl_reply = NULL; /* Buffer for control connection reply */
+size_t ctl_reply_size = 0; /* Number of bytes used in reply */
+size_t ctl_reply_offset = 0; /* Number of bytes of reply written so far */
+
struct pollfd pfd[N_PFD];
volatile sig_atomic_t MarkSet;
@@ -220,20 +249,25 @@ char *ttymsg(struct iovec *, int, char *, int);
void usage(void);
void wallmsg(struct filed *, struct iovec *);
int getmsgbufsize(void);
+int unix_socket(char *, int, mode_t);
+void double_rbuf(int);
+void ctlsock_accept_handler(void);
+void ctlconn_read_handler(void);
+void ctlconn_write_handler(void);
int
main(int argc, char *argv[])
{
int ch, i, linesize, fd;
- struct sockaddr_un sunx, fromunix;
+ struct sockaddr_un fromunix;
struct sockaddr_in sin, frominet;
- socklen_t slen, len;
+ socklen_t len;
char *p, *line;
char resolve[MAXHOSTNAMELEN];
int lockpipe[2], nullfd = -1;
FILE *fp;
- while ((ch = getopt(argc, argv, "dnuf:m:p:a:")) != -1)
+ while ((ch = getopt(argc, argv, "dnuf:m:p:a:s:")) != -1)
switch (ch) {
case 'd': /* debug */
Debug++;
@@ -248,12 +282,7 @@ main(int argc, char *argv[])
NoDNS = 1;
break;
case 'p': /* path */
- if (strlen(optarg) >= sizeof(sunx.sun_path)) {
- fprintf(stderr,
- "syslogd: socket path too long, exiting\n");
- exit(1);
- } else
- funixn[0] = optarg;
+ funixn[0] = optarg;
break;
case 'u': /* allow udp input port */
SecureMode = 0;
@@ -263,13 +292,12 @@ main(int argc, char *argv[])
fprintf(stderr, "syslogd: "
"out of descriptors, ignoring %s\n",
optarg);
- else if (strlen(optarg) >= sizeof(sunx.sun_path))
- fprintf(stderr,
- "syslogd: path too long, ignoring %s\n",
- optarg);
else
funixn[nfunix++] = optarg;
break;
+ case 's':
+ ctlsock_path = optarg;
+ break;
default:
usage();
}
@@ -305,29 +333,14 @@ main(int argc, char *argv[])
#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
#endif
for (i = 0; i < nfunix; i++) {
- (void)unlink(funixn[i]);
- memset(&sunx, 0, sizeof(sunx));
- sunx.sun_family = AF_UNIX;
- (void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
- if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1 ||
- bind(fd, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) == -1 ||
- chmod(funixn[i], 0666) < 0) {
- (void)snprintf(line, linesize, "cannot create %s",
- funixn[i]);
- logerror(line);
- dprintf("cannot create %s (%d)\n", funixn[i], errno);
+ if ((fd = unix_socket(funixn[i], SOCK_DGRAM, 0666)) == -1) {
if (i == 0)
die(0);
- } else {
- /* double socket receive buffer size */
- if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len,
- &slen) == 0) {
- len *= 2;
- (void)setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, slen);
- }
- pfd[PFD_UNIX_0 + i].fd = fd;
- pfd[PFD_UNIX_0 + i].events = POLLIN;
+ continue;
}
+ double_rbuf(fd);
+ pfd[PFD_UNIX_0 + i].fd = fd;
+ pfd[PFD_UNIX_0 + i].events = POLLIN;
}
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) != -1) {
@@ -350,17 +363,23 @@ main(int argc, char *argv[])
die(0);
} else {
InetInuse = 1;
- /* double socket receive buffer size */
- if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len,
- &slen) == 0) {
- len *= 2;
- (void)setsockopt(fd, SOL_SOCKET,
- SO_RCVBUF, &len, slen);
- }
+ double_rbuf(fd);
pfd[PFD_INET].fd = fd;
pfd[PFD_INET].events = POLLIN;
}
}
+
+ if (ctlsock_path != NULL) {
+ if ((fd = unix_socket(ctlsock_path, SOCK_STREAM, 0600)) == -1)
+ die(0);
+ if (listen(fd, 16) == -1) {
+ logerror("ctlsock listen");
+ die(0);
+ }
+ pfd[PFD_CTLSOCK].fd = fd;
+ pfd[PFD_CTLSOCK].events = POLLIN;
+ }
+
if ((fd = open(_PATH_KLOG, O_RDONLY, 0)) == -1) {
dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
} else {
@@ -412,6 +431,13 @@ main(int argc, char *argv[])
Startup = 0;
+ /* Allocate ctl socket reply buffer if we have a ctl socket */
+ if (pfd[PFD_CTLSOCK].fd != -1 &&
+ (ctl_reply = malloc(MAX_MEMBUF)) == NULL) {
+ logerror("Couldn't allocate ctlsock reply buffer");
+ die(0);
+ }
+
if (!Debug) {
dup2(nullfd, STDIN_FILENO);
dup2(nullfd, STDOUT_FILENO);
@@ -433,6 +459,7 @@ main(int argc, char *argv[])
(void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
(void)signal(SIGCHLD, reapchild);
(void)signal(SIGALRM, domark);
+ (void)signal(SIGPIPE, SIG_IGN);
(void)alarm(TIMERINTVL);
for (;;) {
@@ -454,6 +481,7 @@ main(int argc, char *argv[])
logerror("poll");
continue;
}
+
if ((pfd[PFD_KLOG].revents & POLLIN) != 0) {
i = read(pfd[PFD_KLOG].fd, line, linesize - 1);
if (i > 0) {
@@ -482,6 +510,12 @@ main(int argc, char *argv[])
logerror("recvfrom inet");
}
}
+ if ((pfd[PFD_CTLSOCK].revents & POLLIN) != 0)
+ ctlsock_accept_handler();
+ if ((pfd[PFD_CTLCONN].revents & POLLIN) != 0)
+ ctlconn_read_handler();
+ if ((pfd[PFD_CTLCONN].revents & POLLOUT) != 0)
+ ctlconn_write_handler();
for (i = 0; i < nfunix; i++) {
if ((pfd[PFD_UNIX_0 + i].revents & POLLIN) != 0) {
@@ -848,6 +882,13 @@ fprintlog(struct filed *f, int flags, char *msg)
v->iov_len = 2;
wallmsg(f, iov);
break;
+
+ case F_MEMBUF:
+ dprintf("\n");
+ snprintf(line, sizeof(line), "%.15s %s",
+ (char *)iov[0].iov_base, (char *)iov[4].iov_base);
+ ringbuf_append_line(f->f_un.f_mb.f_rb, line);
+ break;
}
f->f_prevcount = 0;
}
@@ -1148,6 +1189,11 @@ init(void)
for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
printf("%s, ", f->f_un.f_uname[i]);
break;
+
+ case F_MEMBUF:
+ printf("%s", f->f_un.f_mb.f_mname);
+ break;
+
}
if (f->f_program)
printf(" (%s)", f->f_program);
@@ -1166,10 +1212,11 @@ init(void)
void
cfline(char *line, struct filed *f, char *prog)
{
- int i, pri, addr_len;
+ int i, pri, addr_len, rb_len;
char *bp, *p, *q;
char buf[MAXLINE], ebuf[100];
char addr[MAXHOSTNAMELEN];
+ struct filed *xf;
dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
@@ -1305,6 +1352,52 @@ cfline(char *line, struct filed *f, char *prog)
f->f_type = F_WALL;
break;
+ case ':':
+ f->f_type = F_MEMBUF;
+
+ /* Parse buffer size (in kb) */
+ errno = 0;
+ rb_len = strtoul(++p, &q, 0);
+ if (*p == '\0' || (errno == ERANGE && rb_len == ULONG_MAX) ||
+ *q != ':' || rb_len == 0) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+ q++;
+ rb_len *= 1024;
+
+ /* Copy buffer name */
+ for(i = 0; i < sizeof(f->f_un.f_mb.f_mname) - 1; i++) {
+ if (!isalnum(q[i]))
+ break;
+ f->f_un.f_mb.f_mname[i] = q[i];
+ }
+
+ /* Make sure buffer name is unique */
+ for (xf = Files; i != 0 && xf != f; xf = xf->f_next) {
+ if (xf->f_type == F_MEMBUF &&
+ strcmp(xf->f_un.f_mb.f_mname,
+ f->f_un.f_mb.f_mname) == 0)
+ break;
+ }
+
+ /* Error on missing or non-unique name, or bad buffer length */
+ if (i == 0 || rb_len > MAX_MEMBUF || xf != f) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+
+ /* Allocate buffer */
+ rb_len = MAX(rb_len, MIN_MEMBUF);
+ if ((f->f_un.f_mb.f_rb = ringbuf_init(rb_len)) == NULL) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+ break;
+
default:
for (i = 0; i < MAXUNAMES && *p; i++) {
for (q = p; *q && *q != ','; )
@@ -1395,3 +1488,249 @@ markit(void)
(void)alarm(TIMERINTVL);
}
+int
+unix_socket(char *path, int type, mode_t mode)
+{
+ struct sockaddr_un s_un;
+ char errbuf[512];
+ int fd;
+ mode_t old_umask;
+
+ memset(&s_un, 0, sizeof(s_un));
+ s_un.sun_family = AF_UNIX;
+ if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >
+ sizeof(s_un.sun_path)) {
+ snprintf(errbuf, sizeof(errbuf), "socket path too long: %s",
+ path);
+ logerror(errbuf);
+ die(0);
+ }
+
+ if ((fd = socket(AF_UNIX, type, 0)) == -1) {
+ logerror("socket");
+ return (-1);
+ }
+
+ old_umask = umask(0177);
+
+ unlink(path);
+ if (bind(fd, (struct sockaddr *)&s_un, SUN_LEN(&s_un)) == -1) {
+ snprintf(errbuf, sizeof(errbuf), "cannot bind %s", path);
+ logerror(errbuf);
+ umask(old_umask);
+ close(fd);
+ return (-1);
+ }
+
+ umask(old_umask);
+
+ if (chmod(path, mode) == -1) {
+ snprintf(errbuf, sizeof(errbuf), "cannot chmod %s", path);
+ logerror(errbuf);
+ close(fd);
+ unlink(path);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+void
+double_rbuf(int fd)
+{
+ socklen_t slen, len;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, &slen) == 0) {
+ len *= 2;
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, slen);
+ }
+}
+
+static void
+ctlconn_cleanup(void)
+{
+ if (pfd[PFD_CTLCONN].fd != -1)
+ close(pfd[PFD_CTLCONN].fd);
+
+ pfd[PFD_CTLCONN].fd = -1;
+ pfd[PFD_CTLCONN].events = pfd[PFD_CTLCONN].revents = 0;
+
+ pfd[PFD_CTLSOCK].events = POLLIN;
+
+ ctl_state = ctl_cmd_bytes = ctl_reply_offset = ctl_reply_size = 0;
+}
+
+void
+ctlsock_accept_handler(void)
+{
+ int fd, flags;
+
+ dprintf("Accepting control connection\n");
+ fd = accept(pfd[PFD_CTLSOCK].fd, NULL, NULL);
+ if (fd == -1) {
+ if (errno != EINTR && errno != ECONNABORTED)
+ logerror("accept ctlsock");
+ return;
+ }
+
+ ctlconn_cleanup();
+
+ /* Only one connection at a time */
+ pfd[PFD_CTLSOCK].events = pfd[PFD_CTLSOCK].revents = 0;
+
+ if ((flags = fcntl(fd, F_GETFL)) == -1 ||
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ logerror("fcntl ctlconn");
+ close(fd);
+ return;
+ }
+
+ pfd[PFD_CTLCONN].fd = fd;
+ pfd[PFD_CTLCONN].events = POLLIN;
+ ctl_state = CTL_READING_CMD;
+ ctl_cmd_bytes = 0;
+}
+
+static struct filed
+*find_membuf_log(const char *name)
+{
+ struct filed *f;
+
+ for (f = Files; f != NULL; f = f->f_next) {
+ if (f->f_type == F_MEMBUF &&
+ strcmp(f->f_un.f_mb.f_mname, name) == 0)
+ break;
+ }
+ return (f);
+}
+
+void
+ctlconn_read_handler(void)
+{
+ ssize_t n;
+ struct filed *f;
+
+ if (ctl_state != CTL_READING_CMD) {
+ /* Shouldn't be here! */
+ logerror("ctlconn_read with bad ctl_state");
+ ctlconn_cleanup();
+ return;
+ }
+ retry:
+ n = read(pfd[PFD_CTLCONN].fd, (char*)&ctl_cmd + ctl_cmd_bytes,
+ sizeof(ctl_cmd) - ctl_cmd_bytes);
+ switch (n) {
+ case -1:
+ if (errno == EINTR)
+ goto retry;
+ logerror("ctlconn read");
+ /* FALLTHROUGH */
+ case 0:
+ ctlconn_cleanup();
+ return;
+ default:
+ ctl_cmd_bytes += n;
+ }
+
+ if (ctl_cmd_bytes < sizeof(ctl_cmd))
+ return;
+
+ /* Ensure that logname is \0 terminated */
+ if (memchr(ctl_cmd.logname, '\0', sizeof(ctl_cmd.logname)) == NULL) {
+ logerror("Corrupt ctlsock command");
+ ctlconn_cleanup();
+ return;
+ }
+
+ ctl_reply_size = ctl_reply_offset = 0;
+ *ctl_reply = '\0';
+
+ dprintf("ctlcmd %x logname \"%s\"\n", ctl_cmd.cmd, ctl_cmd.logname);
+
+ switch (ctl_cmd.cmd) {
+ case CMD_READ:
+ case CMD_READ_CLEAR:
+ f = find_membuf_log(ctl_cmd.logname);
+ if (f == NULL) {
+ strlcpy(ctl_reply, "No such log\n", MAX_MEMBUF);
+ } else {
+ ringbuf_to_string(ctl_reply, MAX_MEMBUF,
+ f->f_un.f_mb.f_rb);
+ }
+ ctl_reply_size = strlen(ctl_reply);
+
+ if (ctl_cmd.cmd == CMD_READ_CLEAR)
+ ringbuf_clear(f->f_un.f_mb.f_rb);
+ break;
+ case CMD_CLEAR:
+ f = find_membuf_log(ctl_cmd.logname);
+ if (f == NULL) {
+ strlcpy(ctl_reply, "No such log\n", MAX_MEMBUF);
+ } else {
+ ringbuf_clear(f->f_un.f_mb.f_rb);
+ strlcpy(ctl_reply, "Log cleared\n", MAX_MEMBUF);
+ }
+ ctl_reply_size = strlen(ctl_reply);
+ break;
+ case CMD_LIST:
+ for (f = Files; f != NULL; f = f->f_next) {
+ if (f->f_type == F_MEMBUF) {
+ strlcat(ctl_reply, f->f_un.f_mb.f_mname,
+ MAX_MEMBUF);
+ strlcat(ctl_reply, " ", MAX_MEMBUF);
+ }
+ }
+ strlcat(ctl_reply, "\n", MAX_MEMBUF);
+ ctl_reply_size = strlen(ctl_reply);
+ break;
+ default:
+ logerror("Unsupported ctlsock command");
+ ctlconn_cleanup();
+ return;
+ }
+
+ dprintf("ctlcmd reply length %d\n", ctl_reply_size);
+
+ /* If there is no reply, close the connection now */
+ if (ctl_reply_size == 0) {
+ ctlconn_cleanup();
+ return;
+ }
+
+ /* Otherwise, set up to write out reply */
+ ctl_state = CTL_WRITING_REPLY;
+ pfd[PFD_CTLCONN].events = POLLOUT;
+ pfd[PFD_CTLCONN].revents = 0;
+}
+
+void
+ctlconn_write_handler(void)
+{
+ ssize_t n;
+
+ if (ctl_state != CTL_WRITING_REPLY) {
+ /* Shouldn't be here! */
+ logerror("ctlconn_write with bad ctl_state");
+ ctlconn_cleanup();
+ return;
+ }
+ retry:
+ n = write(pfd[PFD_CTLCONN].fd, ctl_reply + ctl_reply_offset,
+ ctl_reply_size - ctl_reply_offset);
+ switch (n) {
+ case -1:
+ if (errno == EINTR)
+ goto retry;
+ if (errno != EPIPE)
+ logerror("ctlconn write");
+ /* FALLTHROUGH */
+ case 0:
+ ctlconn_cleanup();
+ return;
+ default:
+ ctl_reply_offset += n;
+ }
+
+ if (ctl_reply_offset >= ctl_reply_size)
+ ctlconn_cleanup();
+}
diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h
index 49b9aa4ee12..ed58afbb981 100644
--- a/usr.sbin/syslogd/syslogd.h
+++ b/usr.sbin/syslogd/syslogd.h
@@ -36,6 +36,8 @@ int receive_fd(int);
#define MAXFUNIX 21
extern int nfunix;
extern char *funixn[MAXFUNIX];
+extern char *ctlsock_path;
+
#define dprintf if (Debug) printf
extern int Debug;
extern int Startup;
@@ -43,6 +45,19 @@ extern int Startup;
/* fds to poll */
#define PFD_KLOG 0 /* Offset of /dev/klog entry */
#define PFD_INET 1 /* Offset of inet socket entry */
-#define PFD_UNIX_0 2 /* Start of Unix socket entries */
+#define PFD_CTLSOCK 2 /* Offset of control socket entry */
+#define PFD_CTLCONN 3 /* Offset of control connection entry */
+#define PFD_UNIX_0 4 /* Start of Unix socket entries */
#define N_PFD (PFD_UNIX_0 + MAXFUNIX) /* # of pollfd entries */
extern struct pollfd pfd[N_PFD];
+
+struct ringbuf {
+ char *buf;
+ size_t len, start, end;
+};
+
+struct ringbuf *ringbuf_init(size_t);
+void ringbuf_clear(struct ringbuf *);
+size_t ringbuf_used(struct ringbuf *);
+int ringbuf_append_line(struct ringbuf *, char *);
+ssize_t ringbuf_to_string(char *, size_t, struct ringbuf *);