2012-04-13 12:47:30 +00:00
|
|
|
/*
|
2015-02-24 18:19:20 +00:00
|
|
|
* SSLsplit - transparent SSL/TLS interception
|
2016-03-25 11:19:23 +00:00
|
|
|
* Copyright (c) 2009-2016, Daniel Roethlisberger <daniel@roe.ch>
|
2012-04-13 12:47:30 +00:00
|
|
|
* All rights reserved.
|
|
|
|
* http://www.roe.ch/SSLsplit
|
|
|
|
*
|
|
|
|
* 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
|
2015-02-24 18:19:20 +00:00
|
|
|
* notice, this list of conditions, and the following disclaimer.
|
2012-04-13 12:47:30 +00:00
|
|
|
* 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 AUTHOR ``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 AUTHOR 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 "logger.h"
|
|
|
|
|
|
|
|
#include "thrqueue.h"
|
|
|
|
#include "logbuf.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Logger for multithreaded environments. Disk writes are executed in a
|
|
|
|
* writer thread. Logging threads submit buffers to be logged by adding
|
|
|
|
* them to the thrqueue. Logging threads may block on the pthread mutex
|
|
|
|
* of the thrqueue, but not on disk writes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct logger {
|
|
|
|
pthread_t thr;
|
2014-11-25 22:45:40 +00:00
|
|
|
logger_reopen_func_t reopen;
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_open_func_t open;
|
|
|
|
logger_close_func_t close;
|
|
|
|
logger_prep_func_t prep;
|
2012-04-13 12:47:30 +00:00
|
|
|
logger_write_func_t write;
|
2016-03-25 14:56:42 +00:00
|
|
|
logger_except_func_t except;
|
2012-04-13 12:47:30 +00:00
|
|
|
thrqueue_t *queue;
|
|
|
|
};
|
|
|
|
|
2014-11-25 22:45:40 +00:00
|
|
|
#define LBFLAG_REOPEN (1 << 0)
|
|
|
|
#define LBFLAG_OPEN (1 << 1)
|
|
|
|
#define LBFLAG_CLOSE (1 << 2)
|
2014-11-21 15:10:37 +00:00
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
static void
|
|
|
|
logger_clear(logger_t *logger)
|
|
|
|
{
|
|
|
|
memset(logger, 0, sizeof(logger_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create new logger with a specific write function callback.
|
|
|
|
* The callback will be executed in the logger's writer thread,
|
|
|
|
* not in the thread calling logger_submit().
|
|
|
|
*/
|
|
|
|
logger_t *
|
2014-11-25 22:45:40 +00:00
|
|
|
logger_new(logger_reopen_func_t reopenfunc, logger_open_func_t openfunc,
|
|
|
|
logger_close_func_t closefunc, logger_write_func_t writefunc,
|
2016-03-25 14:56:42 +00:00
|
|
|
logger_prep_func_t prepfunc, logger_except_func_t exceptfunc)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
logger_t *logger;
|
|
|
|
|
|
|
|
logger = malloc(sizeof(logger_t));
|
|
|
|
if (!logger)
|
|
|
|
return NULL;
|
|
|
|
logger_clear(logger);
|
2014-11-25 22:45:40 +00:00
|
|
|
logger->reopen = reopenfunc;
|
2014-11-21 15:10:37 +00:00
|
|
|
logger->open = openfunc;
|
|
|
|
logger->close = closefunc;
|
2012-04-13 12:47:30 +00:00
|
|
|
logger->write = writefunc;
|
2014-11-21 15:10:37 +00:00
|
|
|
logger->prep = prepfunc;
|
2016-03-25 14:56:42 +00:00
|
|
|
logger->except = exceptfunc;
|
2012-10-16 21:38:48 +00:00
|
|
|
logger->queue = NULL;
|
2012-04-13 12:47:30 +00:00
|
|
|
return logger;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the logger data structures. Caller must call logger_stop()
|
|
|
|
* or logger_leave() and logger_join() prior to freeing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
logger_free(logger_t *logger) {
|
2012-10-16 21:38:48 +00:00
|
|
|
if (logger->queue) {
|
|
|
|
thrqueue_free(logger->queue);
|
|
|
|
}
|
2012-04-13 12:47:30 +00:00
|
|
|
free(logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Submit a buffer to be logged by the logger thread.
|
2014-11-21 15:10:37 +00:00
|
|
|
* Calls the prep callback from within the calling tread before submission.
|
2014-12-13 22:52:17 +00:00
|
|
|
* Buffer guaranteed to be freed after logging completes or on failure.
|
2017-07-25 13:07:39 +00:00
|
|
|
* Returns -1 on error, 0 on success (including logging a NULL logbuf, which
|
|
|
|
* is a no-op).
|
2012-04-13 12:47:30 +00:00
|
|
|
*/
|
|
|
|
int
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_submit(logger_t *logger, void *fh, unsigned long prepflags,
|
|
|
|
logbuf_t *lb)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
2014-11-21 15:10:37 +00:00
|
|
|
if (logger->prep)
|
|
|
|
lb = logger->prep(fh, prepflags, lb);
|
|
|
|
if (!lb)
|
2017-07-25 13:07:39 +00:00
|
|
|
return 0;
|
2014-11-21 15:10:37 +00:00
|
|
|
lb->fh = fh;
|
|
|
|
logbuf_ctl_clear(lb);
|
2014-12-13 22:52:17 +00:00
|
|
|
if (thrqueue_enqueue(logger->queue, lb)) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
logbuf_free(lb);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-21 15:10:37 +00:00
|
|
|
}
|
|
|
|
|
2014-11-25 22:45:40 +00:00
|
|
|
/*
|
|
|
|
* Submit a log reopen event to the logger thread.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
logger_reopen(logger_t *logger)
|
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
|
|
|
if (!logger->reopen)
|
|
|
|
return 0;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
lb = logbuf_new(0, NULL, 0, NULL, NULL);
|
2014-11-25 22:45:40 +00:00
|
|
|
logbuf_ctl_set(lb, LBFLAG_REOPEN);
|
|
|
|
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2014-11-21 15:10:37 +00:00
|
|
|
/*
|
|
|
|
* Submit a file open event to the logger thread.
|
|
|
|
* fh is the file handle; an opaque unique address identifying the new file.
|
|
|
|
* If no open callback is configured, returns successfully.
|
|
|
|
* Returns 0 on success, -1 on failure.
|
|
|
|
*/
|
2014-11-25 22:45:40 +00:00
|
|
|
int
|
|
|
|
logger_open(logger_t *logger, void *fh)
|
2014-11-21 15:10:37 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
|
|
|
if (!logger->open)
|
|
|
|
return 0;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
lb = logbuf_new(0, NULL, 0, NULL, NULL);
|
2014-11-21 15:10:37 +00:00
|
|
|
lb->fh = fh;
|
|
|
|
logbuf_ctl_set(lb, LBFLAG_OPEN);
|
|
|
|
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Submit a file close event to the logger thread.
|
|
|
|
* If no close callback is configured, returns successfully.
|
|
|
|
* Returns 0 on success, -1 on failure.
|
|
|
|
*/
|
2014-11-25 22:45:40 +00:00
|
|
|
int
|
|
|
|
logger_close(logger_t *logger, void *fh)
|
2014-11-21 15:10:37 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
|
|
|
if (!logger->close)
|
|
|
|
return 0;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
lb = logbuf_new(0, NULL, 0, NULL, NULL);
|
2014-11-21 15:10:37 +00:00
|
|
|
lb->fh = fh;
|
|
|
|
logbuf_ctl_set(lb, LBFLAG_CLOSE);
|
2012-04-22 22:35:17 +00:00
|
|
|
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Logger thread main function.
|
|
|
|
*/
|
|
|
|
static void *
|
|
|
|
logger_thread(void *arg)
|
|
|
|
{
|
|
|
|
logger_t *logger = arg;
|
|
|
|
logbuf_t *lb;
|
2016-03-25 14:56:42 +00:00
|
|
|
int e = 0;
|
2012-04-13 12:47:30 +00:00
|
|
|
|
|
|
|
while ((lb = thrqueue_dequeue(logger->queue))) {
|
2014-11-25 22:45:40 +00:00
|
|
|
if (logbuf_ctl_isset(lb, LBFLAG_REOPEN)) {
|
2016-03-25 14:56:42 +00:00
|
|
|
if (logger->reopen() != 0)
|
|
|
|
e = 1;
|
2014-11-25 22:45:40 +00:00
|
|
|
} else if (logbuf_ctl_isset(lb, LBFLAG_OPEN)) {
|
2016-03-25 14:56:42 +00:00
|
|
|
if (logger->open(lb->fh) != 0)
|
|
|
|
e = 1;
|
2014-11-21 15:10:37 +00:00
|
|
|
} else if (logbuf_ctl_isset(lb, LBFLAG_CLOSE)) {
|
|
|
|
logger->close(lb->fh);
|
|
|
|
} else {
|
2016-03-25 14:56:42 +00:00
|
|
|
if (logbuf_write_free(lb, logger->write) < 0)
|
|
|
|
e = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e && logger->except) {
|
|
|
|
logger->except();
|
2014-11-21 15:10:37 +00:00
|
|
|
}
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start the logger's write thread.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
logger_start(logger_t *logger) {
|
|
|
|
int rv;
|
|
|
|
|
2012-10-16 21:38:48 +00:00
|
|
|
if (logger->queue) {
|
|
|
|
thrqueue_free(logger->queue);
|
|
|
|
}
|
|
|
|
logger->queue = thrqueue_new(1024);
|
|
|
|
|
2012-04-13 12:47:30 +00:00
|
|
|
rv = pthread_create(&logger->thr, NULL, logger_thread, logger);
|
|
|
|
if (rv)
|
|
|
|
return -1;
|
2012-10-23 20:52:54 +00:00
|
|
|
sched_yield();
|
2012-04-13 12:47:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the logger's write thread to write all pending write requests
|
|
|
|
* and then exit. Don't wait for the logger to exit.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
logger_leave(logger_t *logger) {
|
|
|
|
thrqueue_unblock_dequeue(logger->queue);
|
2012-10-23 20:52:54 +00:00
|
|
|
sched_yield();
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for the logger to exit.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
logger_join(logger_t *logger) {
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = pthread_join(logger->thr, NULL);
|
|
|
|
if (rv)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the logger's write thread to write all pending write requests
|
|
|
|
* and then exit; wait for the logger to exit.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
logger_stop(logger_t *logger) {
|
|
|
|
logger_leave(logger);
|
|
|
|
return logger_join(logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic print to a logger. These functions should be called by the
|
|
|
|
* actual worker thread(s) doing network I/O.
|
|
|
|
*
|
|
|
|
* _printf(), _print() and _write() copy the input buffers.
|
|
|
|
* _ncprint() and _ncwrite() will free() the input buffers.
|
|
|
|
*
|
|
|
|
* The file descriptor argument is a virtual or real system file descriptor
|
|
|
|
* used for multiplexing write requests to several files over the same
|
|
|
|
* logger. This argument is passed to the write handler as-is and is not
|
|
|
|
* interpreted or used by the logger itself in any way.
|
|
|
|
*
|
|
|
|
* All of the functions return 0 on succes, -1 on failure.
|
|
|
|
*/
|
|
|
|
int
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_printf(logger_t *logger, void *fh, unsigned long prepflags,
|
|
|
|
const char *fmt, ...)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
lb = logbuf_new(0, NULL, 0, fh, NULL);
|
2012-04-13 12:47:30 +00:00
|
|
|
if (!lb)
|
|
|
|
return -1;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
lb->sz = vasprintf((char**)&lb->buf, fmt, ap);
|
|
|
|
va_end(ap);
|
2014-11-17 18:11:27 +00:00
|
|
|
if (lb->sz < 0) {
|
2012-04-13 12:47:30 +00:00
|
|
|
logbuf_free(lb);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-21 15:10:37 +00:00
|
|
|
return logger_submit(logger, fh, prepflags, lb);
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
int
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_write(logger_t *logger, void *fh, unsigned long prepflags,
|
|
|
|
const void *buf, size_t sz)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
2014-11-21 15:10:37 +00:00
|
|
|
if (!(lb = logbuf_new_copy(buf, sz, fh, NULL)))
|
2012-04-13 12:47:30 +00:00
|
|
|
return -1;
|
2014-11-21 15:10:37 +00:00
|
|
|
return logger_submit(logger, fh, prepflags, lb);
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
int
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_print(logger_t *logger, void *fh, unsigned long prepflags,
|
|
|
|
const char *s)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
2017-07-25 13:07:39 +00:00
|
|
|
if (!(lb = logbuf_new_copy(s, strlen(s), fh, NULL)))
|
2012-04-13 12:47:30 +00:00
|
|
|
return -1;
|
2014-11-21 15:10:37 +00:00
|
|
|
return logger_submit(logger, fh, prepflags, lb);
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
int
|
2017-10-14 22:39:30 +00:00
|
|
|
logger_write_freebuf(logger_t *logger, int level, void *fh, unsigned long prepflags,
|
2014-11-21 15:10:37 +00:00
|
|
|
void *buf, size_t sz)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
if (!(lb = logbuf_new(level, buf, sz, fh, NULL)))
|
2012-04-13 12:47:30 +00:00
|
|
|
return -1;
|
2014-11-21 15:10:37 +00:00
|
|
|
return logger_submit(logger, fh, prepflags, lb);
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
int
|
2014-11-21 15:10:37 +00:00
|
|
|
logger_print_freebuf(logger_t *logger, void *fh, unsigned long prepflags,
|
|
|
|
char *s)
|
2012-04-13 12:47:30 +00:00
|
|
|
{
|
|
|
|
logbuf_t *lb;
|
|
|
|
|
2017-10-14 22:39:30 +00:00
|
|
|
if (!(lb = logbuf_new(0, s, strlen(s), fh, NULL)))
|
2012-04-13 12:47:30 +00:00
|
|
|
return -1;
|
2014-11-21 15:10:37 +00:00
|
|
|
return logger_submit(logger, fh, prepflags, lb);
|
2012-04-13 12:47:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vim: set noet ft=c: */
|