diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2007-02-20 11:24:33 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2007-02-20 11:24:33 +0000 |
commit | 3e901f274e66b8947db67d97ef73db5fcd107e0f (patch) | |
tree | d0ff1faed0a521d51d85e0712f136c23e7f3d58a /usr.sbin | |
parent | 50a35661237dcd2063503611b5a771df988aab1b (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')
-rw-r--r-- | usr.sbin/syslogd/privsep.c | 110 | ||||
-rw-r--r-- | usr.sbin/syslogd/syslogd.c | 46 |
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; |