aboutsummaryrefslogtreecommitdiff
path: root/lib/libpjdlog/pjdlog.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpjdlog/pjdlog.c')
-rw-r--r--lib/libpjdlog/pjdlog.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/lib/libpjdlog/pjdlog.c b/lib/libpjdlog/pjdlog.c
new file mode 100644
index 000000000000..84615faccd27
--- /dev/null
+++ b/lib/libpjdlog/pjdlog.c
@@ -0,0 +1,809 @@
+/*-
+ * Copyright (c) 2009-2010 The FreeBSD Foundation
+ * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <libutil.h>
+#include <limits.h>
+#include <printf.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef notyet
+#include <robustio.h>
+#endif
+
+#include "pjdlog.h"
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define PJDLOG_MAX_MSGSIZE 4096
+
+#define PJDLOG_PREFIX_STACK 4
+#define PJDLOG_PREFIX_MAXSIZE 128
+
+#define PJDLOG_NEVER_INITIALIZED 0
+#define PJDLOG_NOT_INITIALIZED 1
+#define PJDLOG_INITIALIZED 2
+
+static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
+static int pjdlog_mode, pjdlog_debug_level, pjdlog_sock;
+static int pjdlog_prefix_current;
+static char pjdlog_prefix[PJDLOG_PREFIX_STACK][PJDLOG_PREFIX_MAXSIZE];
+
+static int
+pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
+ size_t n, int *argt)
+{
+
+ assert(n >= 1);
+ argt[0] = PA_INT | PA_FLAG_INTMAX;
+ return (1);
+}
+
+static int
+pjdlog_printf_render_humanized_number(struct __printf_io *io,
+ const struct printf_info *pi, const void * const *arg)
+{
+ char buf[5];
+ intmax_t num;
+ int ret;
+
+ num = *(const intmax_t *)arg[0];
+ humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
+ HN_NOSPACE | HN_DECIMAL);
+ ret = __printf_out(io, pi, buf, strlen(buf));
+ __printf_flush(io);
+ return (ret);
+}
+
+static int
+pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
+ size_t n, int *argt)
+{
+
+ assert(n >= 1);
+ argt[0] = PA_POINTER;
+ return (1);
+}
+
+static int
+pjdlog_printf_render_sockaddr_ip(struct __printf_io *io,
+ const struct printf_info *pi, const void * const *arg)
+{
+ const struct sockaddr_storage *ss;
+ char addr[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+ int ret;
+
+ ss = *(const struct sockaddr_storage * const *)arg[0];
+ switch (ss->ss_family) {
+ case AF_INET:
+ {
+ const struct sockaddr_in *sin;
+
+ sin = (const struct sockaddr_in *)ss;
+ if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
+ sizeof(addr)) == NULL) {
+ PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
+ strerror(errno));
+ }
+ break;
+ }
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *sin;
+
+ sin = (const struct sockaddr_in6 *)ss;
+ if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
+ sizeof(addr)) == NULL) {
+ PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
+ strerror(errno));
+ }
+ break;
+ }
+ default:
+ snprintf(addr, sizeof(addr), "[unsupported family %hhu]",
+ ss->ss_family);
+ break;
+ }
+ ret = __printf_out(io, pi, addr, strlen(addr));
+ __printf_flush(io);
+ return (ret);
+}
+
+static int
+pjdlog_printf_render_sockaddr(struct __printf_io *io,
+ const struct printf_info *pi, const void * const *arg)
+{
+ const struct sockaddr_storage *ss;
+ char buf[PATH_MAX];
+ int ret;
+
+ ss = *(const struct sockaddr_storage * const *)arg[0];
+ switch (ss->ss_family) {
+ case AF_UNIX:
+ {
+ const struct sockaddr_un *sun;
+
+ sun = (const struct sockaddr_un *)ss;
+ if (sun->sun_path[0] == '\0')
+ snprintf(buf, sizeof(buf), "N/A");
+ else
+ snprintf(buf, sizeof(buf), "%s", sun->sun_path);
+ break;
+ }
+ case AF_INET:
+ {
+ char addr[INET_ADDRSTRLEN];
+ const struct sockaddr_in *sin;
+ unsigned int port;
+
+ sin = (const struct sockaddr_in *)ss;
+ port = ntohs(sin->sin_port);
+ if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
+ sizeof(addr)) == NULL) {
+ PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
+ strerror(errno));
+ }
+ snprintf(buf, sizeof(buf), "%s:%u", addr, port);
+ break;
+ }
+ case AF_INET6:
+ {
+ char addr[INET6_ADDRSTRLEN];
+ const struct sockaddr_in6 *sin;
+ unsigned int port;
+
+ sin = (const struct sockaddr_in6 *)ss;
+ port = ntohs(sin->sin6_port);
+ if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
+ sizeof(addr)) == NULL) {
+ PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
+ strerror(errno));
+ }
+ snprintf(buf, sizeof(buf), "[%s]:%u", addr, port);
+ break;
+ }
+ default:
+ snprintf(buf, sizeof(buf), "[unsupported family %hhu]",
+ ss->ss_family);
+ break;
+ }
+ ret = __printf_out(io, pi, buf, strlen(buf));
+ __printf_flush(io);
+ return (ret);
+}
+
+void
+pjdlog_init(int mode)
+{
+ int saved_errno;
+
+ assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
+ pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
+#ifdef notyet
+ assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG ||
+ mode == PJDLOG_MODE_SOCK);
+#else
+ assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
+#endif
+
+ saved_errno = errno;
+
+ if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
+ __use_xprintf = 1;
+ register_printf_render_std("T");
+ register_printf_render('N',
+ pjdlog_printf_render_humanized_number,
+ pjdlog_printf_arginfo_humanized_number);
+ register_printf_render('I',
+ pjdlog_printf_render_sockaddr_ip,
+ pjdlog_printf_arginfo_sockaddr);
+ register_printf_render('S',
+ pjdlog_printf_render_sockaddr,
+ pjdlog_printf_arginfo_sockaddr);
+ }
+
+ if (mode == PJDLOG_MODE_SYSLOG)
+ openlog(NULL, LOG_PID | LOG_NDELAY, LOG_LOCAL0);
+ pjdlog_mode = mode;
+ pjdlog_debug_level = 0;
+ pjdlog_prefix_current = 0;
+ pjdlog_prefix[0][0] = '\0';
+
+ pjdlog_initialized = PJDLOG_INITIALIZED;
+ pjdlog_sock = -1;
+
+ errno = saved_errno;
+}
+
+void
+pjdlog_fini(void)
+{
+ int saved_errno;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ saved_errno = errno;
+
+ if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
+ closelog();
+
+ pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
+ pjdlog_sock = -1;
+
+ errno = saved_errno;
+}
+
+/*
+ * Configure where the logs should go.
+ * By default they are send to stdout/stderr, but after going into background
+ * (eg. by calling daemon(3)) application is responsible for changing mode to
+ * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
+ */
+void
+pjdlog_mode_set(int mode)
+{
+ int saved_errno;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+#ifdef notyet
+ assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG ||
+ mode == PJDLOG_MODE_SOCK);
+#else
+ assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
+#endif
+
+ if (pjdlog_mode == mode)
+ return;
+
+ saved_errno = errno;
+
+ if (mode == PJDLOG_MODE_SYSLOG)
+ openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ else if (mode == PJDLOG_MODE_STD)
+ closelog();
+
+ if (mode != PJDLOG_MODE_SOCK)
+ pjdlog_sock = -1;
+
+ pjdlog_mode = mode;
+
+ errno = saved_errno;
+}
+
+
+/*
+ * Return current mode.
+ */
+int
+pjdlog_mode_get(void)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ return (pjdlog_mode);
+}
+
+#ifdef notyet
+/*
+ * Sets socket number to use for PJDLOG_MODE_SOCK mode.
+ */
+void
+pjdlog_sock_set(int sock)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(pjdlog_mode == PJDLOG_MODE_SOCK);
+ assert(sock >= 0);
+
+ pjdlog_sock = sock;
+}
+#endif
+
+#ifdef notyet
+/*
+ * Returns socket number used for PJDLOG_MODE_SOCK mode.
+ */
+int
+pjdlog_sock_get(void)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(pjdlog_mode == PJDLOG_MODE_SOCK);
+ assert(pjdlog_sock >= 0);
+
+ return (pjdlog_sock);
+}
+#endif
+
+/*
+ * Set debug level. All the logs above the level specified here will be
+ * ignored.
+ */
+void
+pjdlog_debug_set(int level)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(level >= 0);
+ assert(level <= 127);
+
+ pjdlog_debug_level = level;
+}
+
+/*
+ * Return current debug level.
+ */
+int
+pjdlog_debug_get(void)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ return (pjdlog_debug_level);
+}
+
+/*
+ * Set prefix that will be used before each log.
+ */
+void
+pjdlog_prefix_set(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ va_start(ap, fmt);
+ pjdlogv_prefix_set(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Set prefix that will be used before each log.
+ */
+void
+pjdlogv_prefix_set(const char *fmt, va_list ap)
+{
+ int saved_errno;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(fmt != NULL);
+
+ saved_errno = errno;
+
+ vsnprintf(pjdlog_prefix[pjdlog_prefix_current],
+ sizeof(pjdlog_prefix[pjdlog_prefix_current]), fmt, ap);
+
+ errno = saved_errno;
+}
+
+/*
+ * Get current prefix.
+ */
+const char *
+pjdlog_prefix_get(void)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ return (pjdlog_prefix[pjdlog_prefix_current]);
+}
+
+/*
+ * Set new prefix and put the current one on the stack.
+ */
+void
+pjdlog_prefix_push(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ va_start(ap, fmt);
+ pjdlogv_prefix_push(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Set new prefix and put the current one on the stack.
+ */
+void
+pjdlogv_prefix_push(const char *fmt, va_list ap)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(pjdlog_prefix_current < PJDLOG_PREFIX_STACK - 1);
+
+ pjdlog_prefix_current++;
+
+ pjdlogv_prefix_set(fmt, ap);
+}
+
+/*
+ * Removes current prefix and recovers previous one from the stack.
+ */
+void
+pjdlog_prefix_pop(void)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(pjdlog_prefix_current > 0);
+
+ pjdlog_prefix_current--;
+}
+
+/*
+ * Convert log level into string.
+ */
+static const char *
+pjdlog_level_to_string(int loglevel)
+{
+
+ switch (loglevel) {
+ case LOG_EMERG:
+ return ("EMERG");
+ case LOG_ALERT:
+ return ("ALERT");
+ case LOG_CRIT:
+ return ("CRIT");
+ case LOG_ERR:
+ return ("ERROR");
+ case LOG_WARNING:
+ return ("WARNING");
+ case LOG_NOTICE:
+ return ("NOTICE");
+ case LOG_INFO:
+ return ("INFO");
+ case LOG_DEBUG:
+ return ("DEBUG");
+ }
+ assert(!"Invalid log level.");
+ abort(); /* XXX: gcc */
+}
+
+static int
+vsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
+{
+ size_t len;
+
+ len = strlen(str);
+ assert(len < size);
+ return (vsnprintf(str + len, size - len, fmt, ap));
+}
+
+static int
+snprlcat(char *str, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = vsnprlcat(str, size, fmt, ap);
+ va_end(ap);
+ return (result);
+}
+
+static void
+pjdlogv_common_single_line(const char *func, const char *file, int line,
+ int loglevel, int debuglevel, int error, const char *msg)
+{
+ static char log[2 * PJDLOG_MAX_MSGSIZE];
+ char *logp;
+ size_t logs;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+#ifdef notyet
+ assert(pjdlog_mode == PJDLOG_MODE_STD ||
+ pjdlog_mode == PJDLOG_MODE_SYSLOG ||
+ pjdlog_mode == PJDLOG_MODE_SOCK);
+#else
+ assert(pjdlog_mode == PJDLOG_MODE_STD ||
+ pjdlog_mode == PJDLOG_MODE_SYSLOG);
+#endif
+ assert(pjdlog_mode != PJDLOG_MODE_SOCK || pjdlog_sock >= 0);
+ assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
+ loglevel == LOG_CRIT || loglevel == LOG_ERR ||
+ loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
+ loglevel == LOG_INFO || loglevel == LOG_DEBUG);
+ assert(loglevel != LOG_DEBUG || debuglevel > 0);
+ assert(loglevel != LOG_DEBUG || debuglevel <= pjdlog_debug_level);
+ assert(debuglevel <= 127);
+ assert(error >= -1);
+ assert((file != NULL && line > 0) ||
+ (func == NULL && file == NULL && line == 0));
+
+ switch (pjdlog_mode) {
+ case PJDLOG_MODE_STD:
+ case PJDLOG_MODE_SYSLOG:
+ logp = log;
+ logs = sizeof(log);
+ break;
+ case PJDLOG_MODE_SOCK:
+ logp = log + 4;
+ logs = sizeof(log) - 4;
+ break;
+ default:
+ assert(!"Invalid mode.");
+ }
+
+ *logp = '\0';
+
+ if (pjdlog_mode != PJDLOG_MODE_SOCK) {
+ if (loglevel == LOG_DEBUG) {
+ /* Attach debuglevel if this is debug log. */
+ snprlcat(logp, logs, "[%s%d] ",
+ pjdlog_level_to_string(loglevel), debuglevel);
+ } else {
+ snprlcat(logp, logs, "[%s] ",
+ pjdlog_level_to_string(loglevel));
+ }
+ if (pjdlog_mode != PJDLOG_MODE_SYSLOG &&
+ pjdlog_debug_level >= 1) {
+ snprlcat(logp, logs, "(pid=%d) ", getpid());
+ }
+ }
+ /* Attach file, func, line if debuglevel is 2 or more. */
+ if (pjdlog_debug_level >= 2 && file != NULL) {
+ if (func == NULL)
+ snprlcat(logp, logs, "(%s:%d) ", file, line);
+ else
+ snprlcat(logp, logs, "(%s:%d:%s) ", file, line, func);
+ }
+
+ if (pjdlog_mode != PJDLOG_MODE_SOCK) {
+ snprlcat(logp, logs, "%s",
+ pjdlog_prefix[pjdlog_prefix_current]);
+ }
+
+ strlcat(logp, msg, logs);
+
+ /* Attach error description. */
+ if (error != -1)
+ snprlcat(logp, logs, ": %s.", strerror(error));
+
+ switch (pjdlog_mode) {
+ case PJDLOG_MODE_STD:
+ fprintf(stderr, "%s\n", logp);
+ fflush(stderr);
+ break;
+ case PJDLOG_MODE_SYSLOG:
+ syslog(loglevel, "%s", logp);
+ break;
+#ifdef notyet
+ case PJDLOG_MODE_SOCK:
+ {
+ char ack[2];
+ uint16_t dlen;
+
+ log[2] = loglevel;
+ log[3] = debuglevel;
+ dlen = strlen(logp) + 3; /* +3 = loglevel, debuglevel and terminating \0 */
+ bcopy(&dlen, log, sizeof(dlen));
+ if (robust_send(pjdlog_sock, log, (size_t)dlen + 2) == -1) /* +2 for size */
+ assert(!"Unable to send log.");
+ if (robust_recv(pjdlog_sock, ack, sizeof(ack)) == -1)
+ assert(!"Unable to send log.");
+ break;
+ }
+#endif
+ default:
+ assert(!"Invalid mode.");
+ }
+}
+
+/*
+ * Common log routine, which can handle regular log level as well as debug
+ * level. We decide here where to send the logs (stdout/stderr or syslog).
+ */
+void
+_pjdlogv_common(const char *func, const char *file, int line, int loglevel,
+ int debuglevel, int error, const char *fmt, va_list ap)
+{
+ char log[PJDLOG_MAX_MSGSIZE];
+ char *logp, *curline;
+ const char *prvline;
+ int saved_errno;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+ assert(pjdlog_mode == PJDLOG_MODE_STD ||
+ pjdlog_mode == PJDLOG_MODE_SYSLOG ||
+ pjdlog_mode == PJDLOG_MODE_SOCK);
+ assert(pjdlog_mode != PJDLOG_MODE_SOCK || pjdlog_sock >= 0);
+ assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
+ loglevel == LOG_CRIT || loglevel == LOG_ERR ||
+ loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
+ loglevel == LOG_INFO || loglevel == LOG_DEBUG);
+ assert(loglevel != LOG_DEBUG || debuglevel > 0);
+ assert(debuglevel <= 127);
+ assert(error >= -1);
+
+ /* Ignore debug above configured level. */
+ if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
+ return;
+
+ saved_errno = errno;
+
+ vsnprintf(log, sizeof(log), fmt, ap);
+ logp = log;
+ prvline = NULL;
+
+ while ((curline = strsep(&logp, "\n")) != NULL) {
+ if (*curline == '\0')
+ continue;
+ if (prvline != NULL) {
+ pjdlogv_common_single_line(func, file, line, loglevel,
+ debuglevel, -1, prvline);
+ }
+ prvline = curline;
+ }
+ if (prvline == NULL)
+ prvline = "";
+ pjdlogv_common_single_line(func, file, line, loglevel, debuglevel,
+ error, prvline);
+
+ errno = saved_errno;
+}
+
+/*
+ * Common log routine.
+ */
+void
+_pjdlog_common(const char *func, const char *file, int line, int loglevel,
+ int debuglevel, int error, const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ va_start(ap, fmt);
+ _pjdlogv_common(func, file, line, loglevel, debuglevel, error, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Log error, errno and exit.
+ */
+void
+_pjdlogv_exit(const char *func, const char *file, int line, int exitcode,
+ int error, const char *fmt, va_list ap)
+{
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ _pjdlogv_common(func, file, line, exitcode == 0 ? LOG_INFO : LOG_ERR, 0,
+ error, fmt, ap);
+ exit(exitcode);
+ /* NOTREACHED */
+}
+
+/*
+ * Log error, errno and exit.
+ */
+void
+_pjdlog_exit(const char *func, const char *file, int line, int exitcode,
+ int error, const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ va_start(ap, fmt);
+ _pjdlogv_exit(func, file, line, exitcode, error, fmt, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+/*
+ * Log failure message and exit.
+ */
+void
+_pjdlog_abort(const char *func, const char *file, int line,
+ int error, const char *failedexpr, const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(pjdlog_initialized == PJDLOG_INITIALIZED);
+
+ /*
+ * Set pjdlog_debug_level to 2, so that file, line and func are
+ * included in log. This is fine as we will exit anyway.
+ */
+ if (pjdlog_debug_level < 2)
+ pjdlog_debug_level = 2;
+
+ /*
+ * When there is no message we pass __func__ as 'fmt'.
+ * It would be cleaner to pass NULL or "", but gcc generates a warning
+ * for both of those.
+ */
+ if (fmt != func) {
+ va_start(ap, fmt);
+ _pjdlogv_common(func, file, line, LOG_CRIT, 0, -1, fmt, ap);
+ va_end(ap);
+ }
+ if (failedexpr == NULL) {
+ _pjdlog_common(func, file, line, LOG_CRIT, 0, -1, "Aborted.");
+ } else {
+ _pjdlog_common(func, file, line, LOG_CRIT, 0, -1,
+ "Assertion failed: (%s).", failedexpr);
+ }
+ if (error != -1)
+ _pjdlog_common(func, file, line, LOG_CRIT, 0, error, "Errno");
+ abort();
+}
+
+#ifdef notyet
+/*
+ * Receive log from the given socket.
+ */
+int
+pjdlog_receive(int sock)
+{
+ char log[PJDLOG_MAX_MSGSIZE];
+ int loglevel, debuglevel;
+ uint16_t dlen;
+
+ if (robust_recv(sock, &dlen, sizeof(dlen)) == -1)
+ return (-1);
+
+ PJDLOG_ASSERT(dlen > 0);
+ PJDLOG_ASSERT(dlen <= PJDLOG_MAX_MSGSIZE - 3);
+
+ if (robust_recv(sock, log, (size_t)dlen) == -1)
+ return (-1);
+
+ log[dlen - 1] = '\0';
+ loglevel = log[0];
+ debuglevel = log[1];
+ _pjdlog_common(NULL, NULL, 0, loglevel, debuglevel, -1, "%s", log + 2);
+
+ if (robust_send(sock, "ok", 2) == -1)
+ return (-1);
+
+ return (0);
+}
+#endif