1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 16:42:56 -08:00

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
This commit is contained in:
henning 2007-02-20 11:24:32 +00:00
parent 558399e88c
commit 47607485d9
2 changed files with 139 additions and 17 deletions

View File

@ -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;
}

View File

@ -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;