summaryrefslogtreecommitdiff
path: root/lib/libc/gen/syslog_r.c
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2009-11-18 07:43:23 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2009-11-18 07:43:23 +0000
commit7f261868429894b4a1c02e3ba5bab76733c86897 (patch)
tree810bc7943ba83f9c1892ab5600469e048d2cf4b1 /lib/libc/gen/syslog_r.c
parentb66e5c4431a1e293f203c0f530a6aa3bd62264c9 (diff)
More shrinkage, a bit for ramdisks but mostly for static binaries:
- wrap with #ifndef NO_LOG_BAD_DNS_RESPONSES libc code that uses p_class() and p_type() for diagnostics, then add that define to libstub to avoid pulling in res_debug_syms.o - split rcmd() and ruserok() into separate files, as nothing uses both - split readdir_r() to its own file - split syslog_r() from syslog(), as the latter needs localtime(); many binaries no longer need to pull in all the time code after this; switch from usleep() to nanosleep() while we're at it (The profit of analysis of -Wl,-M,--cref output) Chops 888kB from /bin and /sbin on i386 ok deraadt@, miod@
Diffstat (limited to 'lib/libc/gen/syslog_r.c')
-rw-r--r--lib/libc/gen/syslog_r.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/lib/libc/gen/syslog_r.c b/lib/libc/gen/syslog_r.c
new file mode 100644
index 00000000000..cfacae5a6d2
--- /dev/null
+++ b/lib/libc/gen/syslog_r.c
@@ -0,0 +1,322 @@
+/* $OpenBSD: syslog_r.c,v 1.1 2009/11/18 07:43:22 guenther Exp $ */
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+extern char *__progname; /* Program name, from crt0. */
+
+static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */
+static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */
+
+void __vsyslog_r(int pri, struct syslog_data *, size_t (*)(char *, size_t),
+ const char *, va_list);
+
+/* 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)
+{
+ const char *ident;
+
+ __vsyslog_r(pri, data, NULL, fmt, ap);
+
+ /* close the socket without losing log_tag */
+ ident = data->log_tag;
+ closelog_r(data);
+ data->log_tag = ident;
+}
+
+/*
+ * This is used by both syslog_r and syslog. The latter supplies
+ * a non-NULL gettime callback for filling in the date, but we also
+ * use the presence of that callback to decide whether it's safe
+ * to call strerror and what the name of the caller is
+ */
+void
+__vsyslog_r(int pri, struct syslog_data *data,
+ size_t (*gettime)(char *, size_t), const char *fmt, va_list ap)
+{
+ int cnt;
+ char ch, *p, *t;
+ time_t now;
+ 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_r(INTERNALLOG, data,
+ "syslog%s: unknown facility/priority: %x",
+ gettime != NULL ? "" : "_r", pri);
+ pri &= LOG_PRIMASK|LOG_FACMASK;
+ }
+
+ /* Check priority against setlogmask values. */
+ 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 |= data->log_fac;
+
+ 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, invoke the callback to do it just do like before
+ */
+ if (gettime != NULL) {
+ prlen = gettime(p, tbuf_left);
+ 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--;
+ }
+ }
+
+ /* strerror() is not reentrant */
+
+ for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
+ if (ch == '%' && fmt[1] == 'm') {
+ ++fmt;
+ if (gettime != NULL) {
+ prlen = snprintf(t, fmt_left, "%s",
+ strerror(saved_errno));
+ } else {
+ 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--;
+ }
+ }
+ }
+ *t = '\0';
+
+ prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
+ DEC();
+ cnt = p - tbuf;
+
+ /* Output to stderr if requested. */
+ if (data->log_stat & LOG_PERROR) {
+ struct iovec iov[2];
+
+ 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 (!data->opened)
+ openlog_r(data->log_tag, data->log_stat, 0, data);
+ connectlog_r(data);
+
+ /*
+ * If the send() failed, there are two likely scenarios:
+ * 1) syslogd was restarted
+ * 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 ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
+ if (errno != ENOBUFS) {
+ disconnectlog_r(data);
+ connectlog_r(data);
+ }
+ do {
+ struct timespec rqt = { 0, 1000 };
+
+ nanosleep(&rqt, NULL);
+ if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
+ break;
+ } while (errno == ENOBUFS);
+ }
+
+ /*
+ * Output the message to the console; try not to block
+ * as a blocking console should not stop other processes.
+ * Make sure the error reported is the one from the syslogd failure.
+ */
+ if (error == -1 && (data->log_stat & LOG_CONS) &&
+ (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
+ struct iovec iov[2];
+
+ p = strchr(tbuf, '>') + 1;
+ 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);
+ }
+}
+
+static 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 (data->log_file != -1) {
+ close(data->log_file);
+ data->log_file = -1;
+ }
+ data->connected = 0; /* retry connect */
+}
+
+static void
+connectlog_r(struct syslog_data *data)
+{
+ struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */
+
+ 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;
+ }
+}
+
+void
+openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
+{
+ if (ident != NULL)
+ data->log_tag = ident;
+ data->log_stat = logstat;
+ if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
+ data->log_fac = logfac;
+
+ if (data->log_stat & LOG_NDELAY) /* open immediately */
+ connectlog_r(data);
+
+ data->opened = 1; /* ident and facility has been set */
+}
+
+void
+closelog_r(struct syslog_data *data)
+{
+ (void)close(data->log_file);
+ data->log_file = -1;
+ data->connected = 0;
+ data->log_tag = NULL;
+}
+