lighttpd1.4/src/log.c

244 lines
5.6 KiB
C
Raw Normal View History

#include "first.h"
#include "base.h"
#include "log.h"
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h> /* malloc() free() */
#include <unistd.h>
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#endif
#ifndef HAVE_CLOCK_GETTIME
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h> /* gettimeofday() */
#endif
#endif
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
}
/* retry write on EINTR or when not all data was written */
ssize_t write_all(int fd, const void* buf, size_t count) {
ssize_t written = 0;
while (count > 0) {
ssize_t r = write(fd, buf, count);
if (r < 0) {
switch (errno) {
case EINTR:
/* try again */
break;
default:
/* fail - repeating probably won't help */
return -1;
}
} else if (0 == r) {
/* really shouldn't happen... */
errno = EIO;
return -1;
} else {
force_assert(r <= (ssize_t) count);
written += r;
buf = r + (char const*) buf;
count -= r;
}
}
return written;
}
/* lowercase: append space, uppercase: don't */
static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) {
for(; *fmt; fmt++) {
int d;
char *s;
buffer *b;
off_t o;
switch(*fmt) {
case 'S': /* string */
case 's': /* string */
s = va_arg(ap, char *);
buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0);
break;
case 'B': /* buffer */
case 'b': /* buffer */
b = va_arg(ap, buffer *);
buffer_append_string_c_escaped(out, CONST_BUF_LEN(b));
break;
case 'D': /* int */
case 'd': /* int */
d = va_arg(ap, int);
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(out, d);
break;
case 'O': /* off_t */
case 'o': /* off_t */
o = va_arg(ap, off_t);
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(out, o);
break;
case 'X': /* int (hex) */
case 'x': /* int (hex) */
d = va_arg(ap, int);
buffer_append_string_len(out, CONST_STR_LEN("0x"));
buffer_append_uint_hex(out, d);
break;
case '(':
case ')':
case '<':
case '>':
case ',':
case ' ':
buffer_append_string_len(out, fmt, 1);
break;
}
if (*fmt >= 'a') { /* 's' 'b' 'd' 'o' 'x' */
buffer_append_string_len(out, CONST_STR_LEN(" "));
}
}
}
static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
switch(errh->errorlog_mode) {
case ERRORLOG_PIPE:
case ERRORLOG_FILE:
case ERRORLOG_FD:
if (-1 == errh->errorlog_fd) return -1;
/* cache the generated timestamp */
if (*errh->last_ts != *errh->cur_ts) {
*errh->last_ts = *errh->cur_ts;
buffer_clear(errh->tb);
buffer_append_strftime(errh->tb, "%Y-%m-%d %H:%M:%S", localtime(errh->cur_ts));
}
buffer_copy_buffer(b, errh->tb);
buffer_append_string_len(b, CONST_STR_LEN(": ("));
break;
case ERRORLOG_SYSLOG:
/* syslog is generating its own timestamps */
buffer_copy_string_len(b, CONST_STR_LEN("("));
break;
}
buffer_append_string(b, filename);
buffer_append_string_len(b, CONST_STR_LEN("."));
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(b, line);
buffer_append_string_len(b, CONST_STR_LEN(") "));
return 0;
}
static void log_write(const log_error_st *errh, buffer *b) {
switch(errh->errorlog_mode) {
case ERRORLOG_PIPE:
case ERRORLOG_FILE:
case ERRORLOG_FD:
buffer_append_string_len(b, CONST_STR_LEN("\n"));
write_all(errh->errorlog_fd, CONST_BUF_LEN(b));
break;
case ERRORLOG_SYSLOG:
syslog(LOG_ERR, "%s", b->ptr);
break;
}
}
int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
const log_error_st *errh = srv->errh;
buffer *b = errh->b;
if (-1 == log_buffer_prepare(errh, filename, line, b)) return 0;
va_list ap;
va_start(ap, fmt);
log_buffer_append_printf(b, fmt, ap);
va_end(ap);
log_write(errh, b);
return 0;
}
int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) {
const log_error_st *errh = srv->errh;
buffer *b = errh->b;
va_list ap;
size_t prefix_len;
char *pos, *end, *current_line;
if (buffer_string_is_empty(multiline)) return 0;
if (-1 == log_buffer_prepare(errh, filename, line, b)) return 0;
va_start(ap, fmt);
log_buffer_append_printf(b, fmt, ap);
va_end(ap);
prefix_len = buffer_string_length(b);
current_line = pos = multiline->ptr;
end = multiline->ptr + buffer_string_length(multiline);
for ( ; pos <= end ; ++pos) {
switch (*pos) {
case '\n':
case '\r':
case '\0': /* handles end of string */
if (current_line < pos) {
/* truncate to prefix */
buffer_string_set_length(b, prefix_len);
buffer_append_string_len(b, current_line, pos - current_line);
log_write(errh, b);
}
current_line = pos + 1;
break;
default:
break;
}
}
return 0;
}
log_error_st *
log_error_st_init (time_t *cur_ts_ptr, time_t *last_ts_ptr)
{
log_error_st *errh = calloc(1, sizeof(log_error_st));
force_assert(errh);
errh->errorlog_fd = STDERR_FILENO;
errh->errorlog_mode = ERRORLOG_FD;
errh->b = buffer_init();
errh->tb = buffer_init();
errh->cur_ts = cur_ts_ptr;
errh->last_ts = last_ts_ptr;
return errh;
}
void
log_error_st_free (log_error_st *errh)
{
if (NULL == errh) return;
buffer_free(errh->tb);
buffer_free(errh->b);
free(errh);
}