diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h --- a/usr.sbin/syslogd/syslogd.h +++ b/usr.sbin/syslogd/syslogd.h @@ -161,6 +161,7 @@ struct { char f_pname[MAXPATHLEN]; int f_procdesc; + struct deadq_entry *f_dq; }; /* F_PIPE */ }; diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -329,6 +329,7 @@ static bool needdofsync = true; /* Are any file(s) waiting to be fsynced? */ static struct pidfh *pfh; static bool RFC3164OutputFormat = true; /* Use legacy format by default. */ +static int kq; /* kqueue(2) descriptor. */ struct iovlist; @@ -337,7 +338,7 @@ static void addsock(const char *, const char *, mode_t); static nvlist_t *cfline(const char *, const char *, const char *, const char *); static const char *cvthname(struct sockaddr *); -static void deadq_enter(int); +static struct deadq_entry *deadq_enter(int); static void deadq_remove(struct deadq_entry *); static int decode(const char *, const CODE *); static void die(int) __dead2; @@ -349,6 +350,7 @@ static void init(bool); static void logmsg(int, const struct logtime *, const char *, const char *, const char *, const char *, const char *, const char *, int); +static void log_deadchild(int, int, const struct filed *); static void markit(void); static struct socklist *socksetup(struct addrinfo *, const char *, mode_t); static int socklist_recv_file(struct socklist *); @@ -386,9 +388,21 @@ f->f_type = F_UNUSED; break; case F_PIPE: - if (f->f_procdesc >= 0) { - deadq_enter(f->f_procdesc); + if (f->f_procdesc != -1) { + /* + * Close the procdesc, killing the underlying + * process (if it is still alive). + */ + (void)close(f->f_procdesc); f->f_procdesc = -1; + /* + * The pipe process is guaranteed to be dead now, + * so remove it from the deadq. + */ + if (f->f_dq != NULL) { + deadq_remove(f->f_dq); + f->f_dq = NULL; + } } break; default: @@ -492,7 +506,7 @@ struct kevent ev; struct socklist *sl; pid_t spid; - int ch, kq, ppipe_w = -1, s; + int ch, ppipe_w = -1, s; char *p; bool bflag = false, pflag = false, Sflag = false; @@ -794,6 +808,12 @@ break; } break; + case EVFILT_PROCDESC: + if ((ev.fflags & NOTE_EXIT) != 0) { + log_deadchild(ev.ident, ev.data, ev.udata); + close_filed(ev.udata); + } + break; } } } @@ -1833,6 +1853,7 @@ dprintf(" %s\n", f->f_pname); iovlist_append(il, "\n"); if (f->f_procdesc == -1) { + struct kevent ev; struct filed *f_in_list; int i = 0; @@ -1847,10 +1868,16 @@ logerror(f->f_pname); break; } + EV_SET(&ev, f->f_procdesc, EVFILT_PROCDESC, EV_ADD, + NOTE_EXIT, 0, f); + if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { + logerror("failed to add procdesc kevent"); + exit(1); + } } if (writev(f->f_file, il->iov, il->iovcnt) < 0) { logerror(f->f_pname); - close_filed(f); + f->f_dq = deadq_enter(f->f_procdesc); } break; @@ -2218,7 +2245,7 @@ /* flush any pending output */ if (f->f_prevcount) fprintlog_successive(f, 0); - /* close our end of the pipe */ + /* terminate existing pipe processes */ if (f->f_type == F_PIPE) close_filed(f); } @@ -2459,7 +2486,23 @@ case F_FORW: case F_CONSOLE: case F_TTY: + close_filed(f); + break; case F_PIPE: + if (f->f_procdesc != -1) { + struct kevent ev; + /* + * This filed is going to be freed. + * Delete procdesc kevents that reference it. + */ + EV_SET(&ev, f->f_procdesc, EVFILT_PROCDESC, + EV_DELETE, NOTE_EXIT, 0, f); + if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { + logerror("failed to delete procdesc" + "kevent"); + exit(1); + } + } close_filed(f); break; default: @@ -3125,15 +3168,11 @@ case 0: /* Already signalled once, try harder now. */ (void)pdkill(dq->dq_procdesc, SIGKILL); - (void)deadq_remove(dq); break; case 1: - if (pdkill(dq->dq_procdesc, SIGTERM) != 0) - (void)deadq_remove(dq); - else - dq->dq_timeout--; - break; + (void)pdkill(dq->dq_procdesc, SIGTERM); + /* FALLTHROUGH. */ default: dq->dq_timeout--; } @@ -3546,13 +3585,13 @@ return (pfd[1]); } -static void +static struct deadq_entry * deadq_enter(int pd) { struct deadq_entry *dq; if (pd == -1) - return; + return (NULL); dq = malloc(sizeof(*dq)); if (dq == NULL) { @@ -3563,16 +3602,42 @@ dq->dq_procdesc = pd; dq->dq_timeout = DQ_TIMO_INIT; TAILQ_INSERT_TAIL(&deadq_head, dq, dq_entries); + return (dq); } static void deadq_remove(struct deadq_entry *dq) { TAILQ_REMOVE(&deadq_head, dq, dq_entries); - close(dq->dq_procdesc); free(dq); } +static void +log_deadchild(int pd, int status, const struct filed *f) +{ + pid_t pid; + int code; + char buf[256]; + const char *reason; + + errno = 0; /* Keep strerror() stuff out of logerror messages. */ + if (WIFSIGNALED(status)) { + reason = "due to signal"; + code = WTERMSIG(status); + } else { + reason = "with status"; + code = WEXITSTATUS(status); + if (code == 0) + return; + } + if (pdgetpid(pd, &pid) == -1) + err(1, "pdgetpid"); + (void)snprintf(buf, sizeof(buf), + "Logging subprocess %d (%s) exited %s %d.", + pid, f->f_pname, reason, code); + logerror(buf); +} + static struct socklist * socksetup(struct addrinfo *ai, const char *name, mode_t mode) {