Hi,
If writing to a tty blocks, syslogd forks and tries to write again
in a background process. A potential fork(2) at every message is
bad, so replace this with an event. As a bonus the syslogd child
process does not need to pledge "proc" anymore. Also limit the
number of delayed write events.
ok?
bluhm
Index: usr.sbin/syslogd/syslogd.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.c,v
retrieving revision 1.199
diff -u -p -r1.199 syslogd.c
--- usr.sbin/syslogd/syslogd.c 21 Oct 2015 14:03:07 -0000 1.199
+++ usr.sbin/syslogd/syslogd.c 21 Oct 2015 14:26:49 -0000
@@ -53,7 +53,6 @@
* IPv6, libevent, syslog over TCP and TLS by Alexander Bluhm
*/
-#define MAXLINE 8192 /* maximum line length */
#define MAX_UDPMSG 1180 /* maximum UDP send size */
#define MIN_MEMBUF (MAXLINE * 4) /* Minimum memory buffer size */
#define MAX_MEMBUF (256 * 1024) /* Maximum memory buffer size */
@@ -701,7 +700,7 @@ main(int argc, char *argv[])
if (priv_init(ConfFile, NoDNS, lockpipe[1], nullfd, argv) < 0)
errx(1, "unable to privsep");
- if (pledge("stdio rpath unix inet proc recvfd", NULL) == -1)
+ if (pledge("stdio rpath unix inet recvfd", NULL) == -1)
err(1, "pledge");
/* Process is now unprivileged and inside a chroot */
@@ -1952,8 +1951,7 @@ wallmsg(struct filed *f, struct iovec *i
break;
if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
UT_NAMESIZE)) {
- if ((p = ttymsg(iov, 6, utline))
- != NULL)
+ if ((p = ttymsg(iov, 6, utline)) != NULL)
logerrorx(p);
break;
}
Index: usr.sbin/syslogd/syslogd.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.h,v
retrieving revision 1.22
diff -u -p -r1.22 syslogd.h
--- usr.sbin/syslogd/syslogd.h 21 Oct 2015 14:03:07 -0000 1.22
+++ usr.sbin/syslogd/syslogd.h 23 Oct 2015 14:55:20 -0000
@@ -33,6 +33,7 @@ int priv_getnameinfo(struct sockaddr *
/* Terminal message */
#define TTYMSGTIME 1 /* timeout used by ttymsg */
+#define TTYMAXDELAY 256 /* max events in ttymsg */
char *ttymsg(struct iovec *, int, char *);
/* File descriptor send/recv */
@@ -47,6 +48,7 @@ extern char *path_ctlsock;
extern int fd_ctlsock, fd_ctlconn, fd_klog, fd_sendsys;
extern int fd_udp, fd_udp6, fd_bind, fd_listen, fd_tls, fd_unix[MAXUNIX];
+#define MAXLINE 8192 /* maximum line length */
#define ERRBUFSIZE 256
void logdebug(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
extern int Debug;
Index: usr.sbin/syslogd/ttymsg.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/ttymsg.c,v
retrieving revision 1.8
diff -u -p -r1.8 ttymsg.c
--- usr.sbin/syslogd/ttymsg.c 21 Oct 2015 14:03:07 -0000 1.8
+++ usr.sbin/syslogd/ttymsg.c 23 Oct 2015 14:28:22 -0000
@@ -35,6 +35,7 @@
#include <dirent.h>
#include <errno.h>
+#include <event.h>
#include <fcntl.h>
#include <paths.h>
#include <signal.h>
@@ -45,9 +46,17 @@
#include "syslogd.h"
+struct tty_delay {
+ struct event td_event;
+ size_t td_length;
+ char td_line[MAXLINE];
+};
+int tty_delayed = 0;
+void ttycb(int, short, void *);
+
/*
* Display the contents of a uio structure on a terminal.
- * Forks and finishes in child if write would block, waiting up to TTYMSGTIME
+ * Schedules an event if write would block, waiting up to TTYMSGTIME
* seconds. Returns pointer to error string on unexpected error;
* string is not newline-terminated. Various "normal" errors are ignored
* (exclusive-use, lack of permission, etc.).
@@ -61,8 +70,6 @@ ttymsg(struct iovec *iov, int iovcnt, ch
size_t left;
ssize_t wret;
struct iovec localiov[6];
- int forked = 0;
- sigset_t mask;
if (iovcnt < 0 || (size_t)iovcnt > nitems(localiov))
return ("too many iov's (change code in syslogd/ttymsg.c)");
@@ -122,34 +129,44 @@ ttymsg(struct iovec *iov, int iovcnt, ch
continue;
}
if (errno == EWOULDBLOCK) {
- int off = 0;
- pid_t cpid;
+ struct tty_delay *td;
+ struct timeval to;
- if (forked) {
- (void) close(fd);
- _exit(1);
+ if (tty_delayed >= TTYMAXDELAY) {
+ (void) snprintf(ebuf, sizeof(ebuf),
+ "%s: too many delayed writes", device);
+ return (ebuf);
}
logdebug("ttymsg delayed write\n");
- cpid = fork();
- if (cpid < 0) {
+ if (iov != localiov) {
+ bcopy(iov, localiov,
+ iovcnt * sizeof(struct iovec));
+ iov = localiov;
+ }
+ if ((td = malloc(sizeof(*td))) == NULL) {
(void) snprintf(ebuf, sizeof(ebuf),
- "fork: %s", strerror(errno));
- (void) close(fd);
+ "%s: malloc: %s", device, strerror(errno));
return (ebuf);
}
- if (cpid) { /* parent */
- (void) close(fd);
- return (NULL);
- }
- forked++;
- /* wait at most TTYMSGTIME seconds */
- (void) signal(SIGALRM, SIG_DFL);
- (void) signal(SIGTERM, SIG_DFL); /* XXX */
- (void) sigemptyset(&mask);
- (void) sigprocmask(SIG_SETMASK, &mask, NULL);
- (void) alarm((u_int)TTYMSGTIME);
- (void) fcntl(fd, O_NONBLOCK, &off);
- continue;
+ td->td_length = 0;
+ if (left > MAXLINE)
+ left = MAXLINE;
+ while (iovcnt && left) {
+ if (iov->iov_len > left)
+ iov->iov_len = left;
+ memcpy(td->td_line + td->td_length,
+ iov->iov_base, iov->iov_len);
+ td->td_length += iov->iov_len;
+ left -= iov->iov_len;
+ ++iov;
+ --iovcnt;
+ }
+ tty_delayed++;
+ event_set(&td->td_event, fd, EV_WRITE, ttycb, td);
+ to.tv_sec = TTYMSGTIME;
+ to.tv_usec = 0;
+ event_add(&td->td_event, &to);
+ return (NULL);
}
/*
* We get ENODEV on a slip line if we're running as root,
@@ -158,15 +175,41 @@ ttymsg(struct iovec *iov, int iovcnt, ch
if (errno == ENODEV || errno == EIO)
break;
(void) close(fd);
- if (forked)
- _exit(1);
(void) snprintf(ebuf, sizeof(ebuf),
"%s: %s", device, strerror(errno));
return (ebuf);
}
(void) close(fd);
- if (forked)
- _exit(0);
return (NULL);
+}
+
+void
+ttycb(int fd, short event, void *arg)
+{
+ struct tty_delay *td = arg;
+ struct timeval to;
+ ssize_t wret;
+
+ if (event != EV_WRITE)
+ goto done;
+
+ wret = write(fd, td->td_line, td->td_length);
+ if (wret < 0 && errno != EINTR && errno != EWOULDBLOCK)
+ goto done;
+ if (wret > 0) {
+ td->td_length -= wret;
+ if (td->td_length == 0)
+ goto done;
+ memmove(td->td_line, td->td_line + wret, td->td_length);
+ }
+ to.tv_sec = TTYMSGTIME;
+ to.tv_usec = 0;
+ event_add(&td->td_event, &to);
+ return;
+
+ done:
+ tty_delayed--;
+ close(fd);
+ free(td);
}