2019-03-21 08:05:21 +00:00
|
|
|
/* _XOPEN_SOURCE >= 500 for vsnprintf() */
|
|
|
|
#ifndef _XOPEN_SOURCE
|
|
|
|
#define _XOPEN_SOURCE 700
|
|
|
|
#endif
|
|
|
|
|
2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "log.h"
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2019-03-21 08:05:21 +00:00
|
|
|
#include <stdio.h> /* vsnprintf() */
|
2019-03-21 08:03:07 +00:00
|
|
|
#include <stdlib.h> /* malloc() free() */
|
2017-06-22 01:41:59 +00:00
|
|
|
#include <unistd.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_SYSLOG_H
|
2009-10-11 14:31:42 +00:00
|
|
|
# include <syslog.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-18 18:24:39 +00:00
|
|
|
#ifndef HAVE_CLOCK_GETTIME
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
# include <sys/time.h> /* gettimeofday() */
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2019-12-04 06:35:27 +00:00
|
|
|
time_t log_epoch_secs = 0;
|
|
|
|
|
2016-07-18 18:24:39 +00:00
|
|
|
int log_clock_gettime_realtime (struct timespec *ts) {
|
|
|
|
#ifdef HAVE_CLOCK_GETTIME
|
|
|
|
return clock_gettime(CLOCK_REALTIME, ts);
|
|
|
|
#else
|
|
|
|
/* Mac OSX does not provide clock_gettime()
|
|
|
|
* e.g. defined(__APPLE__) && defined(__MACH__) */
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
ts->tv_sec = tv.tv_sec;
|
|
|
|
ts->tv_nsec = tv.tv_usec * 1000;
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-08-22 16:01:08 +00:00
|
|
|
/* retry write on EINTR or when not all data was written */
|
2019-12-08 01:50:42 +00:00
|
|
|
ssize_t write_all(int fd, const void * const buf, size_t count) {
|
|
|
|
ssize_t written = 0;
|
2015-08-22 16:01:08 +00:00
|
|
|
|
2019-12-08 01:50:42 +00:00
|
|
|
for (ssize_t wr; count > 0; count -= wr, written += wr) {
|
|
|
|
wr = write(fd, (const char *)buf + written, count);
|
|
|
|
if (wr > 0) continue;
|
2015-08-22 16:01:08 +00:00
|
|
|
|
2019-12-08 01:50:42 +00:00
|
|
|
if (wr < 0 && errno == EINTR) { wr = 0; continue; } /* try again */
|
|
|
|
if (0 == wr) errno = EIO; /* really shouldn't happen... */
|
|
|
|
return -1; /* fail - repeating probably won't help */
|
|
|
|
}
|
|
|
|
|
|
|
|
return written;
|
2015-08-22 16:01:08 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 08:03:07 +00:00
|
|
|
static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
|
2019-12-04 06:35:27 +00:00
|
|
|
static time_t tlast;
|
|
|
|
static char tstr[20]; /* 20-chars needed for "%Y-%m-%d %H:%M:%S" */
|
|
|
|
static size_t tlen;
|
2019-03-21 08:03:07 +00:00
|
|
|
switch(errh->errorlog_mode) {
|
2009-04-10 10:50:51 +00:00
|
|
|
case ERRORLOG_PIPE:
|
2005-07-26 08:26:28 +00:00
|
|
|
case ERRORLOG_FILE:
|
2009-06-21 17:25:39 +00:00
|
|
|
case ERRORLOG_FD:
|
2019-03-21 08:03:07 +00:00
|
|
|
if (-1 == errh->errorlog_fd) return -1;
|
2013-03-25 17:22:32 +00:00
|
|
|
/* cache the generated timestamp */
|
2019-12-04 06:35:27 +00:00
|
|
|
if (tlast != log_epoch_secs) {
|
|
|
|
tlast = log_epoch_secs;
|
2019-12-04 06:35:27 +00:00
|
|
|
tlen = strftime(tstr, sizeof(tstr),
|
|
|
|
"%Y-%m-%d %H:%M:%S", localtime(&tlast));
|
2013-03-25 17:22:32 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 06:35:27 +00:00
|
|
|
buffer_copy_string_len(b, tstr, tlen);
|
2013-03-25 17:22:32 +00:00
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(": ("));
|
2005-07-26 08:26:28 +00:00
|
|
|
break;
|
|
|
|
case ERRORLOG_SYSLOG:
|
2013-03-25 17:22:32 +00:00
|
|
|
/* syslog is generating its own timestamps */
|
|
|
|
buffer_copy_string_len(b, CONST_STR_LEN("("));
|
2005-07-26 08:26:28 +00:00
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2013-03-25 17:22:32 +00:00
|
|
|
buffer_append_string(b, filename);
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN("."));
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_append_int(b, line);
|
2013-03-25 17:22:32 +00:00
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(") "));
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-21 08:03:07 +00:00
|
|
|
static void log_write(const log_error_st *errh, buffer *b) {
|
|
|
|
switch(errh->errorlog_mode) {
|
2013-03-25 17:22:32 +00:00
|
|
|
case ERRORLOG_PIPE:
|
|
|
|
case ERRORLOG_FILE:
|
|
|
|
case ERRORLOG_FD:
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN("\n"));
|
2019-03-21 08:03:07 +00:00
|
|
|
write_all(errh->errorlog_fd, CONST_BUF_LEN(b));
|
2013-03-25 17:22:32 +00:00
|
|
|
break;
|
|
|
|
case ERRORLOG_SYSLOG:
|
|
|
|
syslog(LOG_ERR, "%s", b->ptr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-01 20:08:14 +00:00
|
|
|
static void
|
|
|
|
log_buffer_append_encoded (buffer * const b,
|
|
|
|
const char * const s, const size_t n)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < n && ' ' <= s[i] && s[i] <= '~'; ++i) ;/*(ASCII isprint())*/
|
|
|
|
if (i == n)
|
|
|
|
buffer_append_string_len(b, s, n); /* common case; nothing to encode */
|
|
|
|
else
|
|
|
|
buffer_append_string_c_escaped(b, s, n);
|
2013-03-25 17:22:32 +00:00
|
|
|
}
|
2019-03-21 08:03:07 +00:00
|
|
|
|
|
|
|
|
2019-03-21 08:05:21 +00:00
|
|
|
static void
|
|
|
|
log_buffer_vprintf (buffer * const b,
|
|
|
|
const char * const fmt, va_list ap)
|
|
|
|
{
|
|
|
|
/* NOTE: log_buffer_prepare() ensures 0 != b->used */
|
|
|
|
/*assert(0 != b->used);*//*(only because code calcs below assume this)*/
|
|
|
|
/*assert(0 != b->size);*//*(errh->b should not have 0 size here)*/
|
|
|
|
size_t blen = buffer_string_length(b);
|
|
|
|
size_t bsp = buffer_string_space(b)+1;
|
|
|
|
char *s = b->ptr + blen;
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
va_list aptry;
|
|
|
|
va_copy(aptry, ap);
|
|
|
|
n = (size_t)vsnprintf(s, bsp, fmt, aptry);
|
|
|
|
va_end(aptry);
|
|
|
|
|
|
|
|
if (n >= bsp) {
|
|
|
|
buffer_string_prepare_append(b, n); /*(must re-read s after realloc)*/
|
|
|
|
vsnprintf((s = b->ptr + blen), buffer_string_space(b)+1, fmt, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < n && ' ' <= s[i] && s[i] <= '~'; ++i) ;/*(ASCII isprint())*/
|
|
|
|
if (i == n) {
|
|
|
|
buffer_string_set_length(b, blen + n);
|
|
|
|
return; /* common case; nothing to encode */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* need to encode log line
|
|
|
|
* copy original line fragment, append encoded line to buffer, free copy */
|
|
|
|
char * const src = (char *)malloc(n);
|
|
|
|
memcpy(src, s, n); /*(note: not '\0'-terminated)*/
|
|
|
|
buffer_append_string_c_escaped(b, src, n);
|
|
|
|
free(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-12-08 04:48:24 +00:00
|
|
|
log_error_va_list_impl (log_error_st * const errh,
|
2019-03-21 08:05:21 +00:00
|
|
|
const char * const filename,
|
|
|
|
const unsigned int line,
|
|
|
|
const char * const fmt, va_list ap,
|
|
|
|
const int perr)
|
|
|
|
{
|
|
|
|
const int errnum = errno;
|
2019-12-08 04:48:24 +00:00
|
|
|
buffer * const b = &errh->b;
|
2019-03-21 08:05:21 +00:00
|
|
|
if (-1 == log_buffer_prepare(errh, filename, line, b)) return;
|
|
|
|
log_buffer_vprintf(b, fmt, ap);
|
|
|
|
if (perr) {
|
|
|
|
buffer_append_string_len(b, CONST_STR_LEN(": "));
|
|
|
|
buffer_append_string(b, strerror(errnum));
|
|
|
|
}
|
|
|
|
log_write(errh, b);
|
|
|
|
errno = errnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2019-12-08 04:48:24 +00:00
|
|
|
log_error(log_error_st * const errh,
|
2019-03-21 08:05:21 +00:00
|
|
|
const char * const filename, const unsigned int line,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
log_error_va_list_impl(errh, filename, line, fmt, ap, 0);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2019-12-08 04:48:24 +00:00
|
|
|
log_perror (log_error_st * const errh,
|
2019-03-21 08:05:21 +00:00
|
|
|
const char * const filename, const unsigned int line,
|
|
|
|
const char * const fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
log_error_va_list_impl(errh, filename, line, fmt, ap, 1);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-01 20:08:14 +00:00
|
|
|
void
|
2019-12-08 04:48:24 +00:00
|
|
|
log_error_multiline_buffer (log_error_st * const restrict errh,
|
2019-12-01 20:08:14 +00:00
|
|
|
const char * const restrict filename,
|
|
|
|
const unsigned int line,
|
|
|
|
const buffer * const restrict multiline,
|
|
|
|
const char * const restrict fmt, ...)
|
|
|
|
{
|
|
|
|
if (multiline->used < 2) return;
|
|
|
|
|
|
|
|
const int errnum = errno;
|
2019-12-08 04:48:24 +00:00
|
|
|
buffer * const b = &errh->b;
|
2019-12-01 20:08:14 +00:00
|
|
|
if (-1 == log_buffer_prepare(errh, filename, line, b)) return;
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
log_buffer_vprintf(b, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
const size_t prefix_len = buffer_string_length(b);
|
|
|
|
const char * const end = multiline->ptr + multiline->used - 2;
|
|
|
|
const char *pos = multiline->ptr-1, *current_line;
|
|
|
|
do {
|
|
|
|
pos = strchr(current_line = pos+1, '\n');
|
|
|
|
if (!pos)
|
|
|
|
pos = end;
|
|
|
|
buffer_string_set_length(b, prefix_len); /* truncate to prefix */
|
|
|
|
log_buffer_append_encoded(b, current_line, pos - current_line);
|
|
|
|
log_write(errh, b);
|
|
|
|
} while (pos < end);
|
|
|
|
|
|
|
|
errno = errnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-21 08:03:07 +00:00
|
|
|
log_error_st *
|
2019-12-04 06:35:27 +00:00
|
|
|
log_error_st_init (void)
|
2019-03-21 08:03:07 +00:00
|
|
|
{
|
|
|
|
log_error_st *errh = calloc(1, sizeof(log_error_st));
|
|
|
|
force_assert(errh);
|
|
|
|
errh->errorlog_fd = STDERR_FILENO;
|
|
|
|
errh->errorlog_mode = ERRORLOG_FD;
|
|
|
|
return errh;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
log_error_st_free (log_error_st *errh)
|
|
|
|
{
|
|
|
|
if (NULL == errh) return;
|
2019-12-08 04:48:24 +00:00
|
|
|
free(errh->b.ptr);
|
2019-03-21 08:03:07 +00:00
|
|
|
free(errh);
|
|
|
|
}
|