summaryrefslogtreecommitdiff
path: root/usr.sbin/syslogd
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2007-02-20 11:24:33 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2007-02-20 11:24:33 +0000
commit3e901f274e66b8947db67d97ef73db5fcd107e0f (patch)
treed0ff1faed0a521d51d85e0712f136c23e7f3d58a /usr.sbin/syslogd
parent50a35661237dcd2063503611b5a771df988aab1b (diff)
implement logging to other program's stdin.
if the target is like "| /path/to/program", syslogd forks and execs program and sends the selected log messages to program's stdin. uses a socketpair, grows the receive buffer on the reader side and has the socket nonblocking on syslog'd side to prevent syslogd blocking. I'm using that here to feed logsurfer from ports for automated log analysis, werks beautifully. lots of input & help mpf, ok mpf djm "no objections" millert
Diffstat (limited to 'usr.sbin/syslogd')
-rw-r--r--usr.sbin/syslogd/privsep.c110
-rw-r--r--usr.sbin/syslogd/syslogd.c46
2 files changed, 139 insertions, 17 deletions
diff --git a/usr.sbin/syslogd/privsep.c b/usr.sbin/syslogd/privsep.c
index 64e22cbc17c..01f3d1d9c1b 100644
--- a/usr.sbin/syslogd/privsep.c
+++ b/usr.sbin/syslogd/privsep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: privsep.c,v 1.28 2006/07/09 14:42:27 millert Exp $ */
+/* $OpenBSD: privsep.c,v 1.29 2007/02/20 11:24:32 henning Exp $ */
/*
* Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
@@ -63,6 +63,7 @@ enum priv_state {
enum cmd_types {
PRIV_OPEN_TTY, /* open terminal or console device */
PRIV_OPEN_LOG, /* open logfile for appending */
+ PRIV_OPEN_PIPE, /* fork & exec child that gets logs on stdin */
PRIV_OPEN_UTMP, /* open utmp for reading only */
PRIV_OPEN_CONFIG, /* open config file for reading only */
PRIV_CONFIG_MODIFIED, /* check if config file has been modified */
@@ -86,6 +87,8 @@ struct logname {
static TAILQ_HEAD(, logname) lognames;
static void check_log_name(char *, size_t);
+static int open_file(char *);
+static int open_pipe(char *);
static void check_tty_name(char *, size_t);
static void increase_state(int);
static void sig_pass_to_chld(int);
@@ -220,7 +223,9 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
break;
case PRIV_OPEN_LOG:
- dprintf("[priv]: msg PRIV_OPEN_LOG received\n");
+ case PRIV_OPEN_PIPE:
+ dprintf("[priv]: msg PRIV_OPEN_%s received\n",
+ cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
/* Expecting: length, path */
must_read(socks[0], &path_len, sizeof(size_t));
if (path_len == 0 || path_len > sizeof(path))
@@ -228,7 +233,14 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
must_read(socks[0], &path, path_len);
path[path_len - 1] = '\0';
check_log_name(path, path_len);
- fd = open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0);
+
+ if (cmd == PRIV_OPEN_LOG)
+ fd = open_file(path);
+ else if (cmd == PRIV_OPEN_PIPE)
+ fd = open_pipe(path);
+ else
+ errx(1, "invalid cmd");
+
send_fd(socks[0], fd);
if (fd < 0)
warnx("priv_open_log failed");
@@ -353,6 +365,84 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
_exit(1);
}
+static int
+open_file(char *path)
+{
+ /* must not start with | */
+ if (path[0] == '|')
+ return (-1);
+
+ return (open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0));
+}
+
+static int
+open_pipe(char *cmd)
+{
+ char *argp[] = {"sh", "-c", NULL, NULL};
+ struct passwd *pw;
+ int fd[2];
+ int bsize, flags;
+ pid_t pid;
+
+ /* skip over leading | and whitespace */
+ if (cmd[0] != '|')
+ return (-1);
+ for(cmd++; *cmd && *cmd == ' '; cmd++)
+ ; /* nothing */
+ if (!*cmd)
+ return (-1);
+
+ argp[2] = cmd;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) {
+ logerror("open_pipe");
+ return (-1);
+ }
+
+ /* make the fd on syslogd's side nonblocking */
+ if ((flags = fcntl(fd[1], F_GETFL, 0)) == -1) {
+ logerror("fcntl");
+ return (-1);
+ }
+ flags |= O_NONBLOCK;
+ if ((flags = fcntl(fd[1], F_SETFL, flags)) == -1) {
+ logerror("fcntl");
+ return (-1);
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ logerror("fork error");
+ return (-1);
+ case 0:
+ break;
+ default:
+ close(fd[0]);
+ return (fd[1]);
+ }
+
+ close(fd[1]);
+
+ /* grow receive buffer */
+ bsize = 65535;
+ while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF,
+ &bsize, sizeof(bsize)) == -1)
+ bsize /= 2;
+
+ if ((pw = getpwnam("_syslogd")) == NULL)
+ errx(1, "unknown user _syslogd");
+ if (setgroups(1, &pw->pw_gid) == -1 ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+ err(1, "failure dropping privs");
+ endpwent();
+
+ if (dup2(fd[0], STDIN_FILENO) == -1)
+ err(1, "dup2 failed");
+ if (execv("/bin/sh", argp) == -1)
+ err(1, "execv %s", cmd);
+}
+
/* Check that the terminal device is ok, and if not, rewrite to /dev/null.
* Either /dev/console or /dev/tty* are allowed.
*/
@@ -468,7 +558,10 @@ priv_open_log(const char *lognam)
return -1;
path_len = strlen(path) + 1;
- cmd = PRIV_OPEN_LOG;
+ if (lognam[0] == '|')
+ cmd = PRIV_OPEN_PIPE;
+ else
+ cmd = PRIV_OPEN_LOG;
must_write(priv_fd, &cmd, sizeof(int));
must_write(priv_fd, &path_len, sizeof(size_t));
must_write(priv_fd, path, path_len);
@@ -654,7 +747,14 @@ sig_pass_to_chld(int sig)
static void
sig_got_chld(int sig)
{
- if (cur_state < STATE_QUIT)
+ pid_t pid;
+
+ do {
+ pid = waitpid(WAIT_ANY, NULL, WNOHANG);
+ } while (pid == -1 && errno == EINTR);
+
+ if (pid == child_pid &&
+ cur_state < STATE_QUIT)
cur_state = STATE_QUIT;
}
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
index 5ccd8c68c75..fac3bb08849 100644
--- a/usr.sbin/syslogd/syslogd.c
+++ b/usr.sbin/syslogd/syslogd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: syslogd.c,v 1.94 2007/01/03 13:25:20 mpf Exp $ */
+/* $OpenBSD: syslogd.c,v 1.95 2007/02/20 11:24:32 henning 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.94 2007/01/03 13:25:20 mpf Exp $";
+static const char rcsid[] = "$OpenBSD: syslogd.c,v 1.95 2007/02/20 11:24:32 henning Exp $";
#endif
#endif /* not lint */
@@ -183,10 +183,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_MEMBUF 7 /* memory buffer */
+#define F_PIPE 8 /* pipe to external program */
-char *TypeNames[8] = {
+char *TypeNames[9] = {
"UNUSED", "FILE", "TTY", "CONSOLE",
- "FORW", "USERS", "WALL", "MEMBUF"
+ "FORW", "USERS", "WALL", "MEMBUF",
+ "PIPE"
};
struct filed *Files;
@@ -897,8 +899,9 @@ fprintlog(struct filed *f, int flags, char *msg)
case F_TTY:
case F_FILE:
+ case F_PIPE:
dprintf(" %s\n", f->f_un.f_fname);
- if (f->f_type != F_FILE) {
+ if (f->f_type != F_FILE && f->f_type != F_PIPE) {
v->iov_base = "\r\n";
v->iov_len = 2;
} else {
@@ -920,7 +923,8 @@ fprintlog(struct filed *f, int flags, char *msg)
*/
break;
} else if ((e == EIO || e == EBADF) &&
- f->f_type != F_FILE && !retryonce) {
+ f->f_type != F_FILE && f->f_type != F_PIPE &&
+ !retryonce) {
f->f_file = priv_open_tty(f->f_un.f_fname);
retryonce = 1;
if (f->f_file < 0) {
@@ -928,6 +932,15 @@ fprintlog(struct filed *f, int flags, char *msg)
logerror(f->f_un.f_fname);
} else
goto again;
+ } else if ((e == EPIPE || e == EBADF) &&
+ f->f_type == F_PIPE && !retryonce) {
+ f->f_file = priv_open_log(f->f_un.f_fname);
+ retryonce = 1;
+ if (f->f_file < 0) {
+ f->f_type = F_UNUSED;
+ logerror(f->f_un.f_fname);
+ } else
+ goto again;
} else {
f->f_type = F_UNUSED;
f->f_file = -1;
@@ -1163,6 +1176,7 @@ init(void)
case F_FILE:
case F_TTY:
case F_CONSOLE:
+ case F_PIPE:
(void)close(f->f_file);
break;
case F_FORW:
@@ -1250,6 +1264,7 @@ init(void)
case F_FILE:
case F_TTY:
case F_CONSOLE:
+ case F_PIPE:
printf("%s", f->f_un.f_fname);
break;
@@ -1282,7 +1297,7 @@ init(void)
(p1 == p2 || (p1 != NULL && p2 != NULL && strcmp(p1, p2) == 0))
/*
- * Spot a line with a duplicate file, console, tty, or membuf target.
+ * Spot a line with a duplicate file, pipe, console, tty, or membuf target.
*/
struct filed *
find_dup(struct filed *f)
@@ -1296,6 +1311,7 @@ find_dup(struct filed *f)
case F_FILE:
case F_TTY:
case F_CONSOLE:
+ case F_PIPE:
if (strcmp(list->f_un.f_fname, f->f_un.f_fname) == 0 &&
progmatches(list->f_program, f->f_program))
return (list);
@@ -1441,6 +1457,7 @@ cfline(char *line, char *prog)
break;
case '/':
+ case '|':
(void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
d = find_dup(f);
if (d != NULL) {
@@ -1465,11 +1482,16 @@ cfline(char *line, char *prog)
else
f->f_type = F_TTY;
} else {
- f->f_type = F_FILE;
- /* Clear O_NONBLOCK flag on f->f_file */
- if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) {
- i &= ~O_NONBLOCK;
- fcntl(f->f_file, F_SETFL, i);
+ if (*p == '|')
+ f->f_type = F_PIPE;
+ else {
+ f->f_type = F_FILE;
+
+ /* Clear O_NONBLOCK flag on f->f_file */
+ if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) {
+ i &= ~O_NONBLOCK;
+ fcntl(f->f_file, F_SETFL, i);
+ }
}
}
break;