Index: sys/sys/syslog.h =================================================================== RCS file: /usr/share/cvs/freebsd/src/sys/sys/syslog.h,v retrieving revision 1.26 diff -u -r1.26 syslog.h --- sys/sys/syslog.h 7 Jan 2005 02:29:24 -0000 1.26 +++ sys/sys/syslog.h 7 Feb 2007 23:16:08 -0000 @@ -151,6 +151,20 @@ }; #endif +/* Used by reentrant functions */ + +struct syslog_data { + int log_file; + int connected; + int opened; + int log_stat; + const char *log_tag; + int log_fac; + int log_mask; +}; + +#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff} + #ifdef _KERNEL #define LOG_PRINTF -1 /* pseudo-priority to indicate use of printf */ #endif @@ -194,6 +208,12 @@ int setlogmask(int); void syslog(int, const char *, ...) __printflike(2, 3); void vsyslog(int, const char *, __va_list) __printflike(2, 0); +void closelog_r(struct syslog_data *); +void openlog_r(const char *, int, int, struct syslog_data *); +int setlogmask_r(int, struct syslog_data *); +void syslog_r(int, struct syslog_data *, const char *, ...) + __printflike(3, 4); +void vsyslog_r(int, struct syslog_data *, const char *, __va_list); __END_DECLS #endif /* !_KERNEL */ Index: lib/libc/gen/syslog.c =================================================================== RCS file: /usr/share/cvs/freebsd/src/lib/libc/gen/syslog.c,v retrieving revision 1.39 diff -u -r1.39 syslog.c --- lib/libc/gen/syslog.c 9 Jan 2007 00:27:55 -0000 1.39 +++ lib/libc/gen/syslog.c 7 Feb 2007 23:16:43 -0000 @@ -56,14 +56,7 @@ #include "libc_private.h" -static int LogFile = -1; /* fd for log */ -static int status; /* connection status */ -static int opened; /* have done openlog() */ -static int LogStat = 0; /* status bits, set by openlog() */ -static const char *LogTag = NULL; /* string to tag the entry with */ -static int LogFacility = LOG_USER; /* default facility code */ -static int LogMask = 0xff; /* mask of priorities to be logged */ -static pthread_mutex_t syslog_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct syslog_data sdata = SYSLOG_DATA_INIT; #define THREAD_LOCK() \ do { \ @@ -74,9 +67,8 @@ if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex); \ } while(0) -static void disconnectlog(void); /* disconnect from syslogd */ -static void connectlog(void); /* (re)connect to syslogd */ -static void openlog_unlocked(const char *, int, int); +static void connectlog_r(struct syslog_data *data); +static void disconnectlog_r(struct syslog_data *data); enum { NOCONN = 0, @@ -93,29 +85,6 @@ }; /* - * stdio write hook for writing to a static string buffer - * XXX: Maybe one day, dynamically allocate it so that the line length - * is `unlimited'. - */ -static int -writehook(void *cookie, const char *buf, int len) -{ - struct bufcookie *h; /* private `handle' */ - - h = (struct bufcookie *)cookie; - if (len > h->left) { - /* clip in case of wraparound */ - len = h->left; - } - if (len > 0) { - (void)memcpy(h->base, buf, len); /* `write' it. */ - h->base += len; - h->left -= len; - } - return len; -} - -/* * syslog, vsyslog -- * print message on log file; output is intended for syslogd(8). */ @@ -132,166 +101,197 @@ void vsyslog(int pri, const char *fmt, va_list ap) { + vsyslog_r(pri, &sdata, fmt, ap); +} + +void +openlog(const char *ident, int logstat, int logfac) +{ + openlog_r(ident, logstat, logfac, &sdata); +} + +void +closelog(void) +{ + closelog_r(&sdata); +} + +/* setlogmask -- set the log mask level */ +int +setlogmask(int pmask) +{ + return setlogmask_r(pmask, &sdata); +} + +/* Reentrant version of syslog, i.e. syslog_r() */ + +void +syslog_r(int pri, struct syslog_data *data, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog_r(pri, data, fmt, ap); + va_end(ap); +} + +void +vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap) +{ int cnt; - char ch, *p; + char ch, *p, *t; time_t now; - int fd, saved_errno; - char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64]; - FILE *fp, *fmt_fp; - struct bufcookie tbuf_cookie; - struct bufcookie fmt_cookie; + int fd, saved_errno, error; +#define TBUF_LEN 2048 +#define FMT_LEN 1024 + char *stdp, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; + int tbuf_left, fmt_left, prlen; #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID /* Check for invalid bits. */ if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { - syslog(INTERNALLOG, - "syslog: unknown facility/priority: %x", pri); + if (data == &sdata) { + syslog(INTERNALLOG, + "syslog: unknown facility/priority: %x", pri); + } else { + syslog_r(INTERNALLOG, data, + "syslog_r: unknown facility/priority: %x", pri); + } pri &= LOG_PRIMASK|LOG_FACMASK; } - saved_errno = errno; - - THREAD_LOCK(); - /* Check priority against setlogmask values. */ - if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) { - THREAD_UNLOCK(); + if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) return; - } + + saved_errno = errno; /* Set default facility if none specified. */ if ((pri & LOG_FACMASK) == 0) - pri |= LogFacility; + pri |= data->log_fac; - /* Create the primary stdio hook */ - tbuf_cookie.base = tbuf; - tbuf_cookie.left = sizeof(tbuf); - fp = fwopen(&tbuf_cookie, writehook); - if (fp == NULL) { - THREAD_UNLOCK(); - return; - } - - /* Build the message. */ - (void)time(&now); - (void)fprintf(fp, "<%d>", pri); - (void)fprintf(fp, "%.15s ", ctime_r(&now, timbuf) + 4); - if (LogStat & LOG_PERROR) { - /* Transfer to string buffer */ - (void)fflush(fp); - stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left); - } - if (LogTag == NULL) - LogTag = _getprogname(); - if (LogTag != NULL) - (void)fprintf(fp, "%s", LogTag); - if (LogStat & LOG_PID) - (void)fprintf(fp, "[%d]", getpid()); - if (LogTag != NULL) { - (void)fprintf(fp, ": "); + /* If we have been called through syslog(), no need for reentrancy. */ + if (data == &sdata) + (void)time(&now); + + p = tbuf; + tbuf_left = TBUF_LEN; + +#define DEC() \ + do { \ + if (prlen < 0) \ + prlen = 0; \ + if (prlen >= tbuf_left) \ + prlen = tbuf_left - 1; \ + p += prlen; \ + tbuf_left -= prlen; \ + } while (0) + + prlen = snprintf(p, tbuf_left, "<%d>", pri); + DEC(); + + /* + * syslogd will expand time automagically for reentrant case, and + * for normal case, just do like before + */ + if (data == &sdata) { + prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now)); + DEC(); + } + + if (data->log_stat & LOG_PERROR) + stdp = p; + if (data->log_tag == NULL) + data->log_tag = __progname; + if (data->log_tag != NULL) { + prlen = snprintf(p, tbuf_left, "%s", data->log_tag); + DEC(); + } + if (data->log_stat & LOG_PID) { + prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid()); + DEC(); + } + if (data->log_tag != NULL) { + if (tbuf_left > 1) { + *p++ = ':'; + tbuf_left--; + } + if (tbuf_left > 1) { + *p++ = ' '; + tbuf_left--; + } } - /* Check to see if we can skip expanding the %m */ - if (strstr(fmt, "%m")) { - - /* Create the second stdio hook */ - fmt_cookie.base = fmt_cpy; - fmt_cookie.left = sizeof(fmt_cpy) - 1; - fmt_fp = fwopen(&fmt_cookie, writehook); - if (fmt_fp == NULL) { - fclose(fp); - THREAD_UNLOCK(); - return; - } + /* strerror() is not reentrant */ - /* - * Substitute error message for %m. Be careful not to - * molest an escaped percent "%%m". We want to pass it - * on untouched as the format is later parsed by vfprintf. - */ - for ( ; (ch = *fmt); ++fmt) { - if (ch == '%' && fmt[1] == 'm') { - ++fmt; - strerror_r(saved_errno, errstr, sizeof(errstr)); - fputs(errstr, fmt_fp); - } else if (ch == '%' && fmt[1] == '%') { - ++fmt; - fputc(ch, fmt_fp); - fputc(ch, fmt_fp); + for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) { + if (ch == '%' && fmt[1] == 'm') { + ++fmt; + if (data == &sdata) { + prlen = snprintf(t, fmt_left, "%s", + strerror(saved_errno)); } else { - fputc(ch, fmt_fp); + prlen = snprintf(t, fmt_left, "Error %d", + saved_errno); + } + if (prlen < 0) + prlen = 0; + if (prlen >= fmt_left) + prlen = fmt_left - 1; + t += prlen; + fmt_left -= prlen; + } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { + *t++ = '%'; + *t++ = '%'; + fmt++; + fmt_left -= 2; + } else { + if (fmt_left > 1) { + *t++ = ch; + fmt_left--; } } - - /* Null terminate if room */ - fputc(0, fmt_fp); - fclose(fmt_fp); - - /* Guarantee null termination */ - fmt_cpy[sizeof(fmt_cpy) - 1] = '\0'; - - fmt = fmt_cpy; } + *t = '\0'; - (void)vfprintf(fp, fmt, ap); - (void)fclose(fp); - - cnt = sizeof(tbuf) - tbuf_cookie.left; - - /* Remove a trailing newline */ - if (tbuf[cnt - 1] == '\n') - cnt--; + prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); + DEC(); + cnt = p - tbuf; /* Output to stderr if requested. */ - if (LogStat & LOG_PERROR) { + if (data->log_stat & LOG_PERROR) { struct iovec iov[2]; - struct iovec *v = iov; - v->iov_base = stdp; - v->iov_len = cnt - (stdp - tbuf); - ++v; - v->iov_base = "\n"; - v->iov_len = 1; + iov[0].iov_base = stdp; + iov[0].iov_len = cnt - (stdp - tbuf); + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; (void)_writev(STDERR_FILENO, iov, 2); } /* Get connected, output the message to the local logger. */ - if (!opened) - openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0); - connectlog(); + if (!data->opened) + openlog_r(data->log_tag, data->log_stat, 0, data); + connectlog_r(data); /* - * If the send() failed, there are two likely scenarios: + * If the send() failed, there are two likely scenarios: * 1) syslogd was restarted - * 2) /var/run/log is out of socket buffer space, which - * in most cases means local DoS. - * We attempt to reconnect to /var/run/log to take care of + * 2) /dev/log is out of socket buffer space + * We attempt to reconnect to /dev/log to take care of * case #1 and keep send()ing data to cover case #2 * to give syslogd a chance to empty its socket buffer. - * - * If we are working with a priveleged socket, then take - * only one attempt, because we don't want to freeze a - * critical application like su(1) or sshd(8). - * */ - - if (send(LogFile, tbuf, cnt, 0) < 0) { + if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) { if (errno != ENOBUFS) { - disconnectlog(); - connectlog(); + disconnectlog_r(data); + connectlog_r(data); } do { _usleep(1); - if (send(LogFile, tbuf, cnt, 0) >= 0) { - THREAD_UNLOCK(); - return; - } - if (status == CONNPRIV) + if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0) break; } while (errno == ENOBUFS); - } else { - THREAD_UNLOCK(); - return; } /* @@ -299,137 +299,95 @@ * as a blocking console should not stop other processes. * Make sure the error reported is the one from the syslogd failure. */ - if (LogStat & LOG_CONS && + if (error == -1 && (data->log_stat & LOG_CONS) && (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { struct iovec iov[2]; - struct iovec *v = iov; - + p = strchr(tbuf, '>') + 1; - v->iov_base = p; - v->iov_len = cnt - (p - tbuf); - ++v; - v->iov_base = "\r\n"; - v->iov_len = 2; + iov[0].iov_base = p; + iov[0].iov_len = cnt - (p - tbuf); + iov[1].iov_base = "\r\n"; + iov[1].iov_len = 2; (void)_writev(fd, iov, 2); (void)_close(fd); } - THREAD_UNLOCK(); + if (data != &sdata) + closelog_r(data); } -/* Should be called with mutex acquired */ static void -disconnectlog(void) +disconnectlog_r(struct syslog_data *data) { - /* - * If the user closed the FD and opened another in the same slot, - * that's their problem. They should close it before calling on - * system services. - */ - if (LogFile != -1) { - _close(LogFile); - LogFile = -1; - } - status = NOCONN; /* retry connect */ + /* + * If the user closed the FD and opened another in the same slot, + * that's their problem. They should close it before calling on + * system services. + */ + if (data->log_file != -1) { + _close(data->log_file); + data->log_file = -1; + } + data->connected = 0; /* retry connect */ } -/* Should be called with mutex acquired */ static void -connectlog(void) +connectlog_r(struct syslog_data *data) { - struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ - - if (LogFile == -1) { - if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) - return; - (void)_fcntl(LogFile, F_SETFD, 1); - } - if (LogFile != -1 && status == NOCONN) { - SyslogAddr.sun_len = sizeof(SyslogAddr); - SyslogAddr.sun_family = AF_UNIX; - - /* - * First try priveleged socket. If no success, - * then try default socket. - */ - (void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV, - sizeof SyslogAddr.sun_path); - if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, - sizeof(SyslogAddr)) != -1) - status = CONNPRIV; - - if (status == NOCONN) { - (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, - sizeof SyslogAddr.sun_path); - if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, - sizeof(SyslogAddr)) != -1) - status = CONNDEF; - } - - if (status == NOCONN) { - /* - * Try the old "/dev/log" path, for backward - * compatibility. - */ - (void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG, - sizeof SyslogAddr.sun_path); - if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, - sizeof(SyslogAddr)) != -1) - status = CONNDEF; - } + struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ - if (status == NOCONN) { - (void)_close(LogFile); - LogFile = -1; - } - } + if (data->log_file == -1) { + if ((data->log_file = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) + return; + (void)_fcntl(data->log_file, F_SETFD, 1); + } + if (data->log_file != -1 && !data->connected) { + memset(&SyslogAddr, '\0', sizeof(SyslogAddr)); + SyslogAddr.sun_len = sizeof(SyslogAddr); + SyslogAddr.sun_family = AF_UNIX; + strlcpy(SyslogAddr.sun_path, _PATH_LOG, + sizeof(SyslogAddr.sun_path)); + if (_connect(data->log_file, (struct sockaddr *)&SyslogAddr, + sizeof(SyslogAddr)) == -1) { + (void)_close(data->log_file); + data->log_file = -1; + } else + data->connected = 1; + } } -static void -openlog_unlocked(const char *ident, int logstat, int logfac) +void +openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data) { - if (ident != NULL) - LogTag = ident; - LogStat = logstat; - if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) - LogFacility = logfac; + if (ident != NULL) + data->log_tag = ident; + data->log_stat = logstat; + if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) + data->log_fac = logfac; - if (LogStat & LOG_NDELAY) /* open immediately */ - connectlog(); + if (data->log_stat & LOG_NDELAY) /* open immediately */ + connectlog_r(data); - opened = 1; /* ident and facility has been set */ -} - -void -openlog(const char *ident, int logstat, int logfac) -{ - THREAD_LOCK(); - openlog_unlocked(ident, logstat, logfac); - THREAD_UNLOCK(); + data->opened = 1; /* ident and facility has been set */ } - void -closelog(void) +closelog_r(struct syslog_data *data) { - THREAD_LOCK(); - (void)_close(LogFile); - LogFile = -1; - LogTag = NULL; - status = NOCONN; - THREAD_UNLOCK(); + (void)_close(data->log_file); + data->log_file = -1; + data->connected = 0; + data->log_tag = NULL; } /* setlogmask -- set the log mask level */ int -setlogmask(int pmask) +setlogmask_r(int pmask, struct syslog_data *data) { - int omask; + int omask; - THREAD_LOCK(); - omask = LogMask; - if (pmask != 0) - LogMask = pmask; - THREAD_UNLOCK(); - return (omask); + omask = data->log_mask; + if (pmask != 0) + data->log_mask = pmask; + return (omask); }