diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2004-01-04 08:28:50 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2004-01-04 08:28:50 +0000 |
commit | c1606a8b4a93bb266cfcf1699e631fd5a316aaa6 (patch) | |
tree | 21dd93ca259055fc14af3b1fbfdc2066a803de1b | |
parent | f2546e76535bf0bf40a4b51c451e6354435df578 (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/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/syslogd/privsep.c | 10 | ||||
-rw-r--r-- | usr.sbin/syslogd/ringbuf.c | 150 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslog.conf.5 | 18 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslogd.8 | 11 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslogd.c | 429 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslogd.h | 17 |
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 *); |