--- sysklogd-1.4.2rh/sysklogd.8.dispatcher 2007-06-07 10:40:15.000000000 +0200 +++ sysklogd-1.4.2rh/sysklogd.8 2007-06-07 10:40:15.000000000 +0200 @@ -30,6 +30,9 @@ .RB [ " \-s " .I domainlist ] +.RB [ " \-D " +.I dispatcher +] .RB [ " \-v " ] .RB [ " \-x " ] .LP @@ -178,6 +181,14 @@ once, the names of the facility and priority are logged with each locally-written message. .TP +.BI "\-D " "dispatcher" +The syslogd daemon will start up a dispatcher program and open +standard input to the socketpair used to pass events. So, the +dispatcher only needs to read stdin. Because IPC communication +buffers have a limited size, it must read the events as fast +as possible. The syslogd send SIGTERM to indicate that the +dispatcher should exit. +.TP .B "\-v" Print version and exit. .TP @@ -444,6 +455,19 @@ kern.=debug |/usr/adm/debug .fi .LP +.SH OUTPUT TO DISPATCHER +If you run syslogd with the +.B \-D +option, you can route your messages +to dispatcher process. To specify messages, which go to dispatcher, +you use "!dispatcher" directive. +.IP +Following configuration routes info messages to a dispatcher process: +.IP +.nf + *.info !dispatcher +.fi +.LP .SH INSTALLATION CONCERNS There is probably one important consideration when installing this version of syslogd. This version of syslogd is dependent on proper --- /dev/null 2007-06-04 11:07:53.480224378 +0200 +++ sysklogd-1.4.2rh/sysklogd-dispatch.h 2007-06-07 10:40:15.000000000 +0200 @@ -0,0 +1,64 @@ +/* sysklogd-dispatch.h -- + * Copyright 2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb <sgrubb@redhat.com> + * Peter Vrabec <pvrabec@redhat.com> + * + */ + +#ifndef SYSKLOGD_DISPATCH_H +#define SYSKLOGD_DISPATCH_H + + +typedef enum { QOS_NON_BLOCKING, QOS_BLOCKING } qos_t; + +struct daemon_conf +{ + char * dispatcher; + qos_t qos; /* use blocking/non-blocking sockets */ +}; + +/* Sysklogd protocol #1 contains only one type of reply message + and that is standart message. */ +#define SYSKLOGD_PROTOCOL_VER 1 +#define MAXLINE 1024 /* Should be same MAXLINE as in syslogd.c */ +typedef enum { STD_MESS } mess_t; + +struct sysklogd_reply { + mess_t type; + int len; + union { + const char *message; + }; +}; + +struct sysklogd_dispatcher_header { + int ver; /* The version of this protocol */ + int hlen; /* Header length */ + mess_t type; /* Message type */ + int size; /* Size of data following the header */ +}; + + +int init_dispatcher(const struct daemon_conf *config); +void shutdown_dispatcher(void); +void reconfigure_dispatcher(void); +void dispatch_event(const struct sysklogd_reply *rep); + +#endif --- sysklogd-1.4.2rh/Makefile.dispatcher 2007-02-26 10:46:08.000000000 +0100 +++ sysklogd-1.4.2rh/Makefile 2007-06-07 10:40:15.000000000 +0200 @@ -10,6 +10,7 @@ INSTALL = /usr/bin/install BINDIR = $(TOPDIR)/sbin MANDIR = $(TOPDIR)/usr/man +INCLUDEDIR = $(TOPDIR)/usr/include # There is one report that under an all ELF system there may be a need to # explicilty link with libresolv.a. If linking syslogd fails you may wish @@ -57,10 +58,10 @@ test: syslog_tst ksym oops_test tsyslogd -install: install_man install_exec +install: install_man install_exec install_include -syslogd: syslogd.o pidfile.o - ${CC} ${LDFLAGS} -o syslogd syslogd.o pidfile.o ${LIBS} +syslogd: syslogd.o pidfile.o sysklogd-dispatch.o + ${CC} ${LDFLAGS} -o syslogd syslogd.o pidfile.o sysklogd-dispatch.o ${LIBS} klogd: klogd.o syslog.o pidfile.o ksym.o ksym_mod.o ${CC} ${LDFLAGS} -o klogd klogd.o syslog.o pidfile.o ksym.o \ @@ -121,6 +122,9 @@ ${INSTALL} -m 644 syslog.conf.5 ${MANDIR}/man5/syslog.conf.5 ${INSTALL} -m 644 klogd.8 ${MANDIR}/man8/klogd.8 +install_include: + ${INSTALL} -m 644 sysklogd-dispatch.h ${INCLUDEDIR}/sysklogd/sysklogd-dispatch.h + ## Red Hat specific additions --- /dev/null 2007-06-04 11:07:53.480224378 +0200 +++ sysklogd-1.4.2rh/dispatcher_example.c 2007-06-07 10:38:28.000000000 +0200 @@ -0,0 +1,205 @@ +/* dispatcher.c -- + * + * This is a sample program that you can customize to create your own syslog + * event handler. It will be started by syslog via the dispatcher option on + * command line. This program can be built as follows: + * + * gcc dispatcher_example.c -o dispatcher + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <locale.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/select.h> +#include <sys/un.h> + +#include "sysklogd/sysklogd-dispatch.h" + + +/* Local data */ +static volatile int signaled = 0; +static int pipe_fd; +static struct sockaddr_un my_addr; +static int fd; + +/* Local functions */ +static int event_loop(void); + +/* SIGTERM handler */ +static void term_handler( int sig ) +{ + signaled = 1; +} + + +/* + * main is started by sysklogd. See dispatcher in man sysklogd. + */ +int main(int argc, char *argv[]) +{ + struct sigaction sa; + struct stat stat_buf; + + setlocale (LC_ALL, ""); + fprintf(stderr, "starting dispatcher...\n"); + +#ifndef DEBUG + /* Make sure we are root */ + if (getuid() != 0) { + fprintf(stderr, "You must be root to run this program."); + exit(1); + } +#endif + + /* register sighandlers */ + sa.sa_flags = 0 ; + sa.sa_handler = term_handler; + sigemptyset( &sa.sa_mask ) ; + sigaction( SIGTERM, &sa, NULL ); + /* sa.sa_handler = term_handler; + sigemptyset( &sa.sa_mask ) ; + sigaction( SIGCHLD, &sa, NULL ); */ + sa.sa_handler = SIG_IGN; + sigaction( SIGHUP, &sa, NULL ); + sigaction( SIGPIPE, &sa, NULL ); + (void)chdir("/"); + + /* change over to pipe_fd */ + pipe_fd = dup(0); + close(0); + fcntl(pipe_fd, F_SETFD, FD_CLOEXEC); + + /* Setup server */ + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket()"); + exit(1); + } + fcntl(fd, F_SETFD, O_NONBLOCK); + my_addr.sun_family=AF_UNIX; + strncpy(my_addr.sun_path, "/tmp/dispatcher.sock", sizeof(my_addr.sun_path)); + unlink(my_addr.sun_path); /* not necessary */ + if (bind(fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) == -1) { + perror("bind()"); + exit(1); + } + stat(my_addr.sun_path, &stat_buf); + chmod(my_addr.sun_path, stat_buf.st_mode | S_IWOTH); + if (listen(fd, 1) == -1) { + perror("listen()"); + exit(1); + } + + /* Start the program */ + return event_loop(); +} + +static int event_loop(void) +{ + void* data; + struct iovec vec[2]; + struct sysklogd_dispatcher_header hdr; + pid_t pid; + static socklen_t addrlen; + int new_fd = -1; + + + /* Allocate data structures */ + data = malloc(MAXLINE); + if (data == NULL) { + fprintf(stderr, "Cannot allocate buffer\n"); + return 1; + } + + do { + int rc; + struct timeval tv; + fd_set fdset; + + + /* if there isn't anybody connected to server, try accept */ + if (new_fd == -1 ) { + addrlen = sizeof(my_addr); + if ((new_fd = accept( fd, (struct sockaddr *) &my_addr, &addrlen))==-1) { + perror("accept()"); + exit(1); + } + } + + memset(data, 0, MAXLINE); + memset(&hdr, 0, sizeof(hdr)); + + tv.tv_sec = 1; + tv.tv_usec = 0; + FD_ZERO(&fdset); + FD_SET(pipe_fd, &fdset); + rc = select(pipe_fd+1, &fdset, NULL, NULL, &tv); + if (rc == 0) + continue; + else if (rc == -1) + break; + + /* Get header first. it is fixed size */ + vec[0].iov_base = (void*)&hdr; + vec[0].iov_len = sizeof(hdr); + rc = readv(pipe_fd, vec, 1); + if (rc == 0 || rc == -1) { + fprintf(stderr, "rc == %d(%s)\n", rc, strerror(errno)); + break; + } + + /* Analyze header */ + /*nothing to do at the momemnt */ + + /* Next payload */ + vec[1].iov_base = data; + vec[1].iov_len = MAXLINE; + rc = readv(pipe_fd, &vec[1], 1); + if (rc == 0 || rc == -1) { + fprintf(stderr, "rc == %d(%s)\n", rc, strerror(errno)); + break; + } + + /* handle some events here. */ + if (new_fd != -1) { + rc = write(new_fd, (char *)data, strlen(data)); + if (rc <= 0) { + if (errno == EPIPE) { + close(new_fd); + new_fd=-1; + } else { + perror("writev()"); + new_fd=-1; + } + } + } +/* + if( strstr((char *)data, "oops") ) { + pid = fork(); + if( !pid ) { + execl("/usr/bin/zenity","/usr/bin/zenity","--warning", + "--text", (char *)data, NULL); + fprintf(stderr, "exec() failed\n"); + } + } +*/ +/* + fprintf(stderr,"type=%d, payload size=%d\n", + hdr.type, hdr.size); + fprintf(stderr,"data=\"%.*s\"\n", hdr.size, + (char *)data); +*/ + } while(!signaled); + + return 0; +} + --- /dev/null 2007-06-04 11:07:53.480224378 +0200 +++ sysklogd-1.4.2rh/sysklogd-dispatch.c 2007-06-07 10:40:15.000000000 +0200 @@ -0,0 +1,190 @@ +/* sysklogd-dispatch.c -- + * Copyright 2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb <sgrubb@redhat.com> + * Junji Kanemaru <junji.kanemaru@linuon.com> + * Peter Vrabec <pvrabec@redhat.com> + */ + +#include <unistd.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "sysklogd-dispatch.h" + +/* This is the communications channel between sysklogd & the dispatcher */ +static int disp_pipe[2] = {-1, -1}; +static pid_t pid = 0; +static int n_errs = 0; +#define REPORT_LIMIT 10 + +/* set_flags: to set flags to file desc */ +static int set_flags(int fn, int flags) +{ + int fl; + + if ((fl = fcntl(fn, F_GETFL, 0)) < 0) { + fprintf(stderr, "fcntl failed. Cannot get flags (%s)\n", + strerror(errno)); + return fl; + } + + fl |= flags; + + return fcntl(fn, F_SETFL, fl); +} + +/* This function returns 1 on error & 0 on success */ +int init_dispatcher(const struct daemon_conf *config) +{ + if (config->dispatcher == NULL) + return 0; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, disp_pipe)) { + fprintf(stderr, "Failed creating disp_pipe\n"); + return 1; + } + + /* Make both disp_pipe non-blocking */ + if (config->qos == QOS_NON_BLOCKING) { + if (set_flags(disp_pipe[0], O_NONBLOCK) < 0 || + set_flags(disp_pipe[1], O_NONBLOCK) < 0) { + fprintf(stderr, "Failed to set O_NONBLOCK flag\n"); + return 1; + } + } + + // do the fork + pid = fork(); + switch(pid) { + case 0: // child + /* in case stdin was closed before sockerpair(), + disp_pipe[0] --eq 0. It's usual because syslogd + run as daemon. */ + if( disp_pipe[0] != 0 ) { + dup2(disp_pipe[0], 0); + close(disp_pipe[0]); + } + close(disp_pipe[1]); + setsid(); + execl(config->dispatcher, config->dispatcher, NULL); + fprintf(stderr, "exec() failed\n"); + exit(1); + break; + case -1: // error + return 1; + break; + default: // parent + close(disp_pipe[0]); + disp_pipe[0] = -1; + /* Avoid leaking this */ + if (fcntl(disp_pipe[1], F_SETFD, FD_CLOEXEC) < 0) { + fprintf(stderr, + "Failed to set FD_CLOEXEC flag\n"); + return 1; + } + fprintf(stderr, "Started dispatcher: %s pid: %u\n", + config->dispatcher, pid); + break; + } + + return 0; +} + +void shutdown_dispatcher(void) +{ + // kill child + if (pid) + kill(pid, SIGTERM); + // wait for term + // if not in time, send sigkill + pid = 0; + + // cleanup comm pipe + if (disp_pipe[0] >= 0) { + close(disp_pipe[0]); + disp_pipe[0] = -1; + } + if (disp_pipe[1] >= 0) { + close(disp_pipe[1]); + disp_pipe[1] = -1; + } +} + +void reconfigure_dispatcher(void) +{ + // signal child + if (pid) + kill(pid, SIGHUP); +} + +void dispatch_event(const struct sysklogd_reply *rep) +{ + int rc, count = 0; + struct iovec vec[2]; + struct sysklogd_dispatcher_header hdr; + + if (disp_pipe[1] == -1) + return; + + hdr.ver = SYSKLOGD_PROTOCOL_VER; /* Hard-coded to current protocol */ + hdr.hlen = sizeof(struct sysklogd_dispatcher_header); + hdr.type = rep->type; + hdr.size = rep->len; + + vec[0].iov_base = (void*)&hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = (void*)rep->message; + vec[1].iov_len = rep->len; + + do { + rc = writev(disp_pipe[1], vec, 2); + } while (rc < 0 && errno == EAGAIN && count++ < 10); + + // close pipe if no child or peer has been lost + if (rc <= 0) { + if (errno == EPIPE) { + shutdown_dispatcher(); + n_errs = 0; + } else { + if (n_errs <= REPORT_LIMIT) { + fprintf(stderr, + "dispatch err (%s) event lost\n", + errno == EAGAIN ? "pipe full" : + strerror(errno)); + n_errs++; + } + if (n_errs == REPORT_LIMIT) { + fprintf(stderr, + "dispatch error reporting limit\n" + " reached - ending report" + " notification."); + n_errs++; + } + } + } else + n_errs = 0; +} --- sysklogd-1.4.2rh/syslogd.c.dispatcher 2007-06-07 10:40:15.000000000 +0200 +++ sysklogd-1.4.2rh/syslogd.c 2007-06-07 10:40:15.000000000 +0200 @@ -507,6 +507,8 @@ #endif #include "version.h" +#include "sysklogd-dispatch.h" + #if defined(__linux__) #include <paths.h> #endif @@ -572,6 +574,8 @@ static int alarm_signal = 0; static int exit_on_signal = 0; +struct daemon_conf disp = { NULL, QOS_NON_BLOCKING }; + #define MAXFUNIX 20 int nfunix = 1; @@ -674,10 +678,11 @@ #define F_FORW_SUSP 7 /* suspended host forwarding */ #define F_FORW_UNKN 8 /* unknown host forwarding */ #define F_PIPE 9 /* named pipe */ +#define F_DISP 10 /* named pipe */ char *TypeNames[] = { "UNUSED", "FILE", "TTY", "CONSOLE", "FORW", "USERS", "WALL", "FORW(SUSPENDED)", - "FORW(UNKNOWN)", "PIPE" + "FORW(UNKNOWN)", "PIPE", "DISP" }; struct filed *Files = (struct filed *) 0; @@ -859,7 +864,7 @@ funix[i] = -1; } - while ((ch = getopt(argc, argv, "46Aa:dhf:l:m:np:rSs:vx")) != EOF) + while ((ch = getopt(argc, argv, "46Aa:dhf:D:l:m:np:rSs:vx")) != EOF) switch((char)ch) { case '4': family = PF_INET; @@ -881,6 +886,9 @@ case 'd': /* debug */ Debug = 1; break; + case 'D': /* dispatcher */ + disp.dispatcher = optarg; + break; case 'f': /* configuration file */ ConfFile = optarg; break; @@ -1067,6 +1075,15 @@ parts[i] = (char *) 0; dprintf("Starting.\n"); + + if( disp.dispatcher != NULL ) { + dprintf("Dispatcher initialisation.\n"); + if( init_dispatcher(&disp) == 1) { + dprintf("Dispatcher initialisation FAIL.\n"); + die(0); + } + } + init(); #ifndef TESTING if ( Debug ) @@ -1249,7 +1266,7 @@ int usage() { fprintf(stderr, "usage: syslogd [-46AdiIrvxh] [-l hostlist] [-m markinterval] [-n] [-p path]\n" \ - " [-s domainlist] [-f conffile]\n"); + " [-s domainlist] [-D dispatcher] [-f conffile]\n"); exit(1); } @@ -1767,6 +1784,7 @@ time_t fwd_suspend; struct addrinfo *saddr; #endif + struct sysklogd_reply reply; dprintf("Called fprintlog, "); @@ -2012,7 +2030,19 @@ } else if (f->f_flags & SYNC_FILE) (void) fsync(f->f_file); break; - + case F_DISP: + f->f_time = now; + dprintf("\n"); + v->iov_base = ""; + v->iov_len = 0; + (void) snprintf(line, sizeof(line), "%s%s%s%s%s%s%s\n", (char *) iov[0].iov_base, + (char *) iov[1].iov_base, (char *) iov[2].iov_base, (char *) iov[3].iov_base, + (char *) iov[4].iov_base, (char *) iov[5].iov_base, (char *) iov[6].iov_base); + reply.type = STD_MESS; + reply.message = line; + reply.len = strlen(reply.message); + dispatch_event(&reply); + break; case F_USERS: case F_WALL: f->f_time = now; @@ -2338,6 +2368,12 @@ logmsg(LOG_SYSLOG|LOG_INFO, buf, LocalHostName, ADDDATE); } + /* Terminate dispatcher */ + if (disp.dispatcher != NULL) { + shutdown_dispatcher(); + disp.dispatcher = NULL; + } + /* Close the UNIX sockets. */ for (i = 0; i < nfunix; i++) if (funix[i] != -1) @@ -2868,7 +2904,15 @@ if (strcmp(p, ctty) == 0) f->f_type = F_CONSOLE; break; - + case '!': + if( !strncmp(++p, "dispatcher", 10) ) { + dprintf ("sending to: %s\n", disp.dispatcher); + f->f_type = F_DISP; + } + else { + dprintf ("\"!\" is not followed by \"dispatcher\"\n"); + } + break; case '*': dprintf ("write-all\n"); f->f_type = F_WALL;