810 lines
18 KiB
C
810 lines
18 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* 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>
|
|
#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
|