transfused: add start-up log, self-log trigger, and run-time log flags

During start-up, logs are sent to syslog and stderr and the console if
syslog is not available. -l logfile will also send the logs to logfile but
only prior to any log trigger events. -m mount_trigger lets the user
specify the mount point to watch for to begin sending logs to
-t triggered_log file which may be on the newly mounted file system. Log
concatenation and rollover are not implemented, yet. Logging functionality
has been separated into transfused_log.c. Locks have been placed around
shared fds. The writer thread is now symmetric to the reader thread instead
of inheriting the parent's thread in order to trigger log switchover after
the file system proxy has started. The coredump size rlimit has been lifted.

Signed-off-by: David Sheets <david.sheets@docker.com>
This commit is contained in:
David Sheets 2016-03-18 11:21:42 +00:00
parent 58ded96b29
commit 16101e66c3
6 changed files with 381 additions and 86 deletions

View File

@ -1,6 +1,9 @@
.PHONY: all
DEPS=transfused.c
HDR=transfused.h transfused_log.h
SRC=transfused.c transfused_log.c
DEPS=$(HDR) $(SRC)
all: Dockerfile $(DEPS)
docker build -t transfused:build .
@ -8,7 +11,7 @@ all: Dockerfile $(DEPS)
chmod 755 transfused
transfused: $(DEPS)
gcc -g -static -Wall -Werror -o transfused transfused.c
gcc -g -static -Wall -Werror -o transfused $(SRC)
clean:
rm -f transfused

View File

@ -13,16 +13,28 @@ start()
ebegin "Starting FUSE socket passthrough"
PIDFILE=/var/run/transfused.pid
LOGFILE=/var/log/transfused.log
if cat /proc/cmdline | grep -q 'com.docker.driverDir'
then
DRIVERDIR="/Mac$(cat /proc/cmdline | sed -e 's/.*com.docker.driverDir="//' -e 's/".*//')"
RUNTIME_LOGFILE=${DRIVERDIR}/log/transfused.log
else
ID=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | head -c 8)
RUNTIME_LOGFILE="/Mac/private/tmp/transfused_${ID}.log"
fi
PIDFILE=/var/run/transfused.pid
STARTUP_LOGFILE=/var/transfused_start.log
MOUNT_TRIGGER=/Mac
start-stop-daemon --start --quiet \
--background \
--exec /sbin/transfused \
--pidfile ${PIDFILE} \
--stderr "${LOGFILE}" \
--stdout "${LOGFILE}" \
-- -p "${PIDFILE}"
-- \
-p "${PIDFILE}" \
-l "${STARTUP_LOGFILE}" \
-m "${MOUNT_TRIGGER}" \
-t "${RUNTIME_LOGFILE}"
eend $? "Failed to start transfused"
}

View File

@ -3,7 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
@ -17,6 +17,11 @@
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "transfused_log.h"
#define COPY_BUFSZ 65536
#define DEFAULT_FUSERMOUNT "/bin/fusermount"
@ -33,23 +38,16 @@ char * default_fusermount = DEFAULT_FUSERMOUNT;
char * default_socket9p_root = DEFAULT_SOCKET9P_ROOT;
char * usage =
"usage: transfused [-p pidfile] [-9 socket9p_root] [-f fusermount]\n"
" [-l logfile] [-m mount_trigger] [-t triggerlog]\n"
" -p pidfile\tthe path at which to write the pid of the process\n"
" -9 " DEFAULT_SOCKET9P_ROOT "\tthe root of the 9p socket file system\n"
" -f " DEFAULT_FUSERMOUNT "\tthe fusermount executable to use\n";
" -f " DEFAULT_FUSERMOUNT "\tthe fusermount executable to use\n"
" -l logfile\tthe log file to use before the mount trigger\n"
" -m mount_trigger\tthe mountpoint to use to trigger log switchover\n"
" -t triggerlog\tthe file to use after the trigger\n";
int debug = 0;
typedef struct {
char * socket9p_root;
char * fusermount;
char * pidfile;
} parameters;
typedef struct {
parameters * params;
long id;
} connection_state;
typedef struct {
char * descr;
long connection;
@ -64,15 +62,6 @@ pid_t gettid() {
return syscall(SYS_gettid);
}
void die(int exit_code, const char * perror_arg, const char * fmt, ...) {
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
if (perror_arg != NULL) perror(perror_arg);
exit(exit_code);
}
void * must_malloc(char *const descr, size_t size) {
void * ptr;
@ -82,7 +71,29 @@ void * must_malloc(char *const descr, size_t size) {
return ptr;
}
char ** read_opts(connection_state * connection, char * buf) {
void cond_init(char *const descr, pthread_cond_t * cond,
const pthread_condattr_t *restrict attr) {
if ((errno = pthread_cond_init(cond, attr)))
die(1, "", "cond init %s: ", descr);
}
void lock_init(char *const descr, pthread_mutex_t * mutex,
const pthread_mutexattr_t *restrict attr) {
if ((errno = pthread_mutex_init(mutex, attr)))
die(1, "", "lock init %s: ", descr);
}
void lock(char *const descr, pthread_mutex_t * mutex) {
if ((errno = pthread_mutex_lock(mutex)))
die(1, "", "lock %s: ", descr);
}
void unlock(char *const descr, pthread_mutex_t * mutex) {
if ((errno = pthread_mutex_unlock(mutex)))
die(1, "", "unlock %s: ", descr);
}
char ** read_opts(connection_t * connection, char * buf) {
int read_fd;
char * read_path;
int read_count;
@ -159,7 +170,7 @@ void copy(copy_thread_state * copy_state) {
die(1, "", "copy %s trace: error writing %s: ", descr, trace_path);
if (write_count != read_count)
die(1, NULL, "copy %s trace: read %d but only wrote %d\n",
die(1, NULL, "copy %s trace: read %d but only wrote %d",
descr, read_count, write_count);
close(trace_fd);
@ -170,7 +181,7 @@ void copy(copy_thread_state * copy_state) {
if (write_count == -1) die(1, "", "copy %s: error writing: ", descr);
if (write_count != read_count)
die(1, NULL, "copy %s: read %d but only wrote %d\n",
die(1, NULL, "copy %s: read %d but only wrote %d",
descr, read_count, write_count);
}
@ -203,6 +214,10 @@ void * copy_clean_to(copy_thread_state * copy_state) {
return NULL;
}
void * copy_clean_to_thread(void * copy_state) {
return (copy_clean_to((copy_thread_state *) copy_state));
}
int recv_fd(int sock) {
int ret;
int fd = -1;
@ -239,8 +254,7 @@ int recv_fd(int sock) {
}
// optv must be null-terminated
int get_fuse_sock(char * fusermount, char *const optv[]) {
int optc;
int get_fuse_sock(connection_t * conn, int optc, char *const optv[]) {
char ** argv;
char * envp[2];
pid_t fusermount_pid;
@ -249,13 +263,17 @@ int get_fuse_sock(char * fusermount, char *const optv[]) {
int fd;
// prepare argv from optv
for (optc = 0; optv[optc] != NULL; optc++) {}
argv = (char **) must_malloc("fusermount argv", (optc + 2) * sizeof(char *));
argv[0] = fusermount;
argv[0] = conn->params->fusermount;
memcpy(&argv[1], optv, (optc + 1) * sizeof(char *));
lock("get_fuse_sock fd_lock", &conn->params->fd_lock);
log_time_locked(conn,"mount ");
for (int i = 0; argv[i]; i++) log_continue_locked(conn, "%s ",argv[i]);
log_continue_locked(conn, "\n");
unlock("get_fuse_sock fd_lock", &conn->params->fd_lock);
// make the socket over which we'll be sent the FUSE socket fd
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fuse_socks))
die(1, "Couldn't create FUSE socketpair", "");
@ -269,7 +287,7 @@ int get_fuse_sock(char * fusermount, char *const optv[]) {
// fork and exec fusermount
fusermount_pid = fork();
if (!fusermount_pid) // child
if (execve(fusermount, argv, envp))
if (execve(argv[0], argv, envp))
die(1, "Failed to execute fusermount", "");
// parent
@ -282,16 +300,16 @@ int get_fuse_sock(char * fusermount, char *const optv[]) {
// wait for fusermount to return
waitpid(fusermount_pid, &status, 0);
if (!WIFEXITED(status))
die(1, NULL, "fusermount terminated abnormally\n");
die(1, NULL, "fusermount terminated abnormally");
if (WEXITSTATUS(status))
die(1, NULL, "fusermount exited with code %d\n", WEXITSTATUS(status));
die(1, NULL, "fusermount exited with code %d", WEXITSTATUS(status));
if (debug) fprintf(stderr, "about to recv_fd from fusermount\n");
if (debug) log_time(conn, "about to recv_fd from fusermount\n");
fd = recv_fd(fuse_socks[1]);
if (fd == -1)
die(1, NULL, "Couldn't receive fd over FUSE socket\n");
die(1, NULL, "Couldn't receive fd over FUSE socket");
// close the read end of the socket
close(fuse_socks[1]);
@ -299,7 +317,7 @@ int get_fuse_sock(char * fusermount, char *const optv[]) {
return fd;
}
void start_reader(connection_state * connection, int fuse) {
void start_reader(connection_t * connection, int fuse) {
int read_fd;
char * read_path;
pthread_t child;
@ -326,13 +344,14 @@ void start_reader(connection_state * connection, int fuse) {
connection->id);
if ((errno = pthread_detach(child)))
die (1, "", "couldn't detach read copy thread for connection '%ld': ",
connection->id);
die(1, "", "couldn't detach read copy thread for connection %ld: ",
connection->id);
}
void do_write(connection_state * connection, int fuse) {
void start_writer(connection_t * connection, int fuse) {
int write_fd;
char * write_path;
pthread_t child;
copy_thread_state * copy_state;
if (asprintf(&write_path, "%s/connections/%ld/write",
@ -350,33 +369,76 @@ void do_write(connection_state * connection, int fuse) {
copy_state->tag = "write";
copy_state->from = fuse;
copy_state->to = write_fd;
copy_clean_to(copy_state);
if ((errno = pthread_create(&child, NULL,
copy_clean_to_thread, copy_state)))
die(1, "", "Couldn't create write copy thread for connection %ld: ",
connection->id);
if ((errno = pthread_detach(child)))
die(1, "", "couldn't detach write copy thread for connection %ld: ",
connection->id);
}
void * mount_connection(connection_state * connection) {
void * mount_connection(connection_t * conn) {
int optc;
char ** optv;
int fuse;
char * buf;
pthread_mutex_t copy_lock;
pthread_cond_t copy_halt;
int should_halt = 0;
buf = (char *) must_malloc("read_opts packet malloc", COPY_BUFSZ);
optv = read_opts(connection, buf);
fuse = get_fuse_sock(connection->params->fusermount, optv);
free(optv);
optv = read_opts(conn, buf);
for (optc = 0; optv[optc] != NULL; optc++) {}
fuse = get_fuse_sock(conn, optc, optv);
free(buf);
start_reader(connection, fuse);
do_write(connection, fuse);
free(connection);
lock_init("copy_lock", &copy_lock, NULL);
cond_init("copy_halt", &copy_halt, NULL);
start_reader(conn, fuse);
start_writer(conn, fuse);
// trigger?
// TODO: strcmp scares me
// TODO: append logfile to trigger_log
if (conn->params->mount_trigger != NULL
&& conn->params->trigger_log != NULL
&& 0 == strcmp(optv[optc - 1], conn->params->mount_trigger)) {
lock("trigger mount fd_lock", &conn->params->fd_lock);
log_time_locked(conn, "Log mount trigger fired on %s, logging to %s\n",
conn->params->mount_trigger, conn->params->trigger_log);
conn->params->trigger_fd = open(conn->params->trigger_log,
O_WRONLY | O_CREAT, 0600);
if (conn->params->trigger_fd == -1)
die(1, "", "Couldn't open trigger log %s: ", conn->params->trigger_log);
unlock("trigger mount fd_lock", &conn->params->fd_lock);
}
free(optv);
lock("copy lock", &copy_lock);
while (!should_halt)
if ((errno = pthread_cond_wait(&copy_halt, &copy_lock)))
die(1, "", "Couldn't wait for copy halt for connection %ld: ",
conn->id);
unlock("copy lock", &copy_lock);
free(conn);
return NULL;
}
void * mount_thread(void * connection) {
return mount_connection((connection_state *) connection);
return mount_connection((connection_t *) connection);
}
void write_pid(connection_state * connection) {
void write_pid(connection_t * connection) {
int write_fd;
char * write_path;
pid_t pid = gettid();
@ -401,7 +463,7 @@ void write_pid(connection_state * connection) {
die(1, "Error writing pid", "");
if (write_count != pid_s_len)
die(1, NULL, "Error writing pid %s to socket: only wrote %d bytes\n",
die(1, NULL, "Error writing pid %s to socket: only wrote %d bytes",
pid_s, write_count);
close(write_fd);
@ -409,7 +471,7 @@ void write_pid(connection_state * connection) {
free(write_path);
}
void perform_syscall(uint8_t syscall, char path[]) {
void perform_syscall(connection_t * conn, uint8_t syscall, char path[]) {
char * name;
int r = 0;
@ -444,7 +506,7 @@ void perform_syscall(uint8_t syscall, char path[]) {
die(1, NULL, "Unknown event syscall %" PRIu8, syscall);
}
if (r != 0) fprintf(stderr, "Event %s error: %s", name, strerror(errno));
if (r != 0) log_time(conn, "Event %s error: %s\n", name, strerror(errno));
}
void * event_thread(void * connection_ptr) {
@ -452,7 +514,7 @@ void * event_thread(void * connection_ptr) {
int read_fd;
int read_count, event_len, path_len;
void * buf;
connection_state * connection = connection_ptr;
connection_t * connection = connection_ptr;
char * path;
uint8_t syscall;
@ -476,19 +538,19 @@ void * event_thread(void * connection_ptr) {
event_len = (int) ntohs(*((uint16_t *) buf));
if (debug) fprintf(stderr, "read %d bytes from connection %ld\n",
read_count, connection->id);
if (debug) log_time(connection, "read %d bytes from connection %ld\n",
read_count, connection->id);
if (read_count != event_len) {
fprintf(stderr, "event thread: only read %d of %d\n",
read_count, event_len);
log_time(connection, "event thread: only read %d of %d\n",
read_count, event_len);
msg = must_malloc("event hex", read_count * 2 + 1);
for (int i = 0; i < read_count; i++) {
sprintf(((char *) msg) + (i * 2),"%02x",(int) (((char *) buf)[i]));
}
((char *) msg)[read_count * 2] = 0;
fprintf(stderr, "message: %s\n", (char *) msg);
log_time(connection, "message: %s\n", (char *) msg);
free(msg);
continue;
@ -501,7 +563,7 @@ void * event_thread(void * connection_ptr) {
syscall = *(((uint8_t *) buf) + 4 + path_len);
// TODO: should this be in another thread?
perform_syscall(syscall, path);
perform_syscall(connection, syscall, path);
}
close(read_fd);
@ -529,8 +591,14 @@ void parse_parameters(int argc, char * argv[], parameters * params) {
params->pidfile = NULL;
params->socket9p_root = NULL;
params->fusermount = NULL;
params->logfile = NULL;
params->logfile_fd = 0;
params->mount_trigger = NULL;
params->trigger_log = NULL;
params->trigger_fd = 0;
lock_init("fd_lock", &params->fd_lock, NULL);
while ((c = getopt(argc, argv, ":p:9:f:")) != -1) {
while ((c = getopt(argc, argv, ":p:9:f:l:m:t:")) != -1) {
switch(c) {
case 'p':
@ -545,6 +613,18 @@ void parse_parameters(int argc, char * argv[], parameters * params) {
params->fusermount = optarg;
break;
case 'l':
params->logfile = optarg;
break;
case 'm':
params->mount_trigger = optarg;
break;
case 't':
params->trigger_log = optarg;
break;
case ':':
fprintf(stderr, "Option -%c requires a path argument\n", optopt);
errflg++;
@ -567,20 +647,47 @@ void parse_parameters(int argc, char * argv[], parameters * params) {
}
if (params->pidfile != NULL && access(params->pidfile, W_OK))
if (errno != ENOENT)
die(2, "", "-p %s path to pidfile must be writable: ", params->pidfile);
if (errno != ENOENT) {
fprintf(stderr, "-p %s path to pidfile must be writable: ",
params->pidfile);
perror("");
exit(2);
}
if (params->fusermount == NULL)
params->fusermount = default_fusermount;
if (access(params->fusermount, X_OK))
die(2, "", "-f %s path to fusermount must be executable: ",
params->fusermount);
if (access(params->fusermount, X_OK)) {
fprintf(stderr, "-f %s path to fusermount must be executable: ",
params->fusermount);
perror("");
exit(2);
}
if (params->socket9p_root == NULL)
params->socket9p_root = default_socket9p_root;
if (access(params->socket9p_root, X_OK))
die(2, "", "-9 %s path to socket 9p root directory must be executable: ",
params->socket9p_root);
if (access(params->socket9p_root, X_OK)) {
fprintf(stderr,
"-9 %s path to socket 9p root directory must be executable: ",
params->socket9p_root);
perror("");
exit(2);
}
if (params->logfile != NULL && access(params->logfile, W_OK))
if (errno != ENOENT) {
fprintf(stderr, "-l %s path to logfile must be writable: ",
params->logfile);
perror("");
exit(2);
}
if (params->mount_trigger != NULL
&& access(params->mount_trigger, F_OK)) {
fprintf(stderr, "-m %s path to mount point must exist: ",
params->mount_trigger);
perror("");
exit(2);
}
}
void write_pidfile(char * pidfile) {
@ -603,7 +710,7 @@ void write_pidfile(char * pidfile) {
die(1, "", "Error writing pidfile %s: ", pidfile);
if (write_count != pid_s_len)
die(1, NULL, "Error writing %s to pidfile %s: only wrote %d bytes\n",
die(1, NULL, "Error writing %s to pidfile %s: only wrote %d bytes",
pid_s, pidfile, write_count);
close(fd);
@ -617,11 +724,16 @@ void process_events(char * events_path, int events, parameters * params) {
int read_count;
long conn_id;
pthread_t child;
connection_state * conn;
connection_t * conn;
char * connection_type;
void * (*connection_handler_thread)(void *);
while (1) {
conn = (connection_t *) must_malloc("connection state",
sizeof(connection_t));
conn->params = params;
conn->id = 0;
read_count = read(events, buf, ID_LEN - 1);
if (read_count == -1) {
die(1, "", "Error reading events path %s: ", events_path);
@ -629,7 +741,7 @@ void process_events(char * events_path, int events, parameters * params) {
// TODO: this is probably the 9p server's fault due to
// not dropping the read 0 to force short read if
// the real read is flushed
fprintf(stderr, "read 0 from event stream %s\n", events_path);
log_time(conn, "read 0 from event stream %s\n", events_path);
continue;
}
@ -643,12 +755,9 @@ void process_events(char * events_path, int events, parameters * params) {
conn_id = strtol(buf + 1, NULL, 10);
if (errno) die(1, "failed", "Connection id of string '%s'", buf);
if (debug) fprintf(stderr, "handle connection %ld\n", conn_id);
conn = (connection_state *) must_malloc("connection state",
sizeof(connection_state));
conn->id = conn_id;
conn->params = params;
if (debug) log_time(conn, "handle connection %ld\n", conn_id);
switch (buf[0]) {
case 'm':
@ -672,7 +781,7 @@ void process_events(char * events_path, int events, parameters * params) {
die(1, "", "Couldn't detach thread for %s connection '%ld': ",
connection_type, conn_id);
if (debug) fprintf(stderr, "thread spawned\n");
if (debug) log_time(conn, "thread spawned\n");
}
}
@ -680,20 +789,37 @@ int main(int argc, char * argv[]) {
int events;
parameters params;
char * events_path;
struct rlimit core_limit;
core_limit.rlim_cur = RLIM_INFINITY;
core_limit.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &core_limit))
die(1, "", "Couldn't set RLIMIT_CORE to RLIM_INFINITY");
openlog(argv[0], LOG_CONS | LOG_PERROR | LOG_NDELAY, LOG_DAEMON);
parse_parameters(argc, argv, &params);
setup_debug();
if (params.pidfile != NULL) write_pidfile(params.pidfile);
if (params.logfile != NULL) {
params.logfile_fd = open(params.logfile, O_WRONLY | O_APPEND | O_CREAT);
if (params.logfile_fd == -1)
die(1, "", "Couldn't open log file %s: ", params.logfile);
}
if (asprintf(&events_path, "%s/events", params.socket9p_root) == -1)
die(1, "Couldn't allocate events path", "");
events = open(events_path, O_RDONLY | O_CLOEXEC);
if (events != -1) process_events(events_path, events, &params);
fprintf(stderr, "Failed to open events path %s: %s\n",
events_path, strerror(errno));
connection_t top;
top.params = &params;
top.id = 0;
log_time(&top, "Failed to open events path %s: %s\n",
events_path, strerror(errno));
free(events_path);
return 1;
}

View File

@ -0,0 +1,22 @@
#include <pthread.h>
typedef struct {
char * socket9p_root;
char * fusermount;
char * pidfile;
char * logfile;
char * mount_trigger;
char * trigger_log;
pthread_mutex_t fd_lock;
int logfile_fd;
int trigger_fd;
} parameters;
typedef struct {
parameters * params;
long id;
} connection_t;
void lock(char *const descr, pthread_mutex_t * mutex);
void unlock(char *const descr, pthread_mutex_t * mutex);

View File

@ -0,0 +1,117 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <syslog.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include "transfused.h"
void log_timestamp(int fd) {
char timestamp[26];
int msec;
struct tm* tm_info;
struct timeval tv;
gettimeofday(&tv, NULL);
msec = lrint(tv.tv_usec/1000.0);
if (msec >= 1000) {
msec -= 1000;
tv.tv_sec++;
}
tm_info = localtime(&tv.tv_sec);
strftime(timestamp, 26, "%Y-%m-%d %H:%M:%S", tm_info);
dprintf(fd, "%s.%03d ", timestamp, msec);
}
void die(int exit_code, const char * perror_arg, const char * fmt, ...) {
va_list argp;
int in_errno = errno;
va_start(argp, fmt);
vsyslog(LOG_CRIT, fmt, argp);
va_end(argp);
if (perror_arg != NULL) {
if (*perror_arg != 0)
syslog(LOG_CRIT, "%s: %s", perror_arg, strerror(in_errno));
else
syslog(LOG_CRIT, "%s", strerror(in_errno));
}
exit(exit_code);
}
void vlog_locked(connection_t * conn, const char * fmt, va_list args) {
int fd = conn->params->trigger_fd;
if (fd != 0) {
vdprintf(fd, fmt, args);
} else {
vsyslog(LOG_INFO, fmt, args);
fd = conn->params->logfile_fd;
if (fd != 0) {
vdprintf(fd, fmt, args);
}
}
}
void vlog_time_locked(connection_t * conn, const char * fmt, va_list args) {
int fd = conn->params->trigger_fd;
if (fd != 0) log_timestamp(fd);
else {
fd = conn->params->logfile_fd;
if (fd != 0) log_timestamp(fd);
}
vlog_locked(conn, fmt, args);
}
void log_time_locked(connection_t * connection, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vlog_time_locked(connection, fmt, args);
va_end(args);
}
void log_time(connection_t * connection, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
lock("log_time fd_lock", &connection->params->fd_lock);
vlog_time_locked(connection, fmt, args);
unlock("log_time fd_lock", &connection->params->fd_lock);
va_end(args);
}
void log_continue_locked(connection_t * connection, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
vlog_locked(connection, fmt, args);
va_end(args);
}
void log_continue(connection_t * connection, const char * fmt, ...) {
va_list args;
va_start(args, fmt);
lock("log_continue fd_lock", &connection->params->fd_lock);
vlog_locked(connection, fmt, args);
unlock("log_continue fd_lock", &connection->params->fd_lock);
va_end(args);
}

View File

@ -0,0 +1,15 @@
#include "transfused.h"
void die(int exit_code, const char * perror_arg, const char * fmt, ...);
void vlog_locked(connection_t * conn, const char * fmt, va_list args);
void vlog_time_locked(connection_t * conn, const char * fmt, va_list args);
void log_time_locked(connection_t * connection, const char * fmt, ...);
void log_time(connection_t * connection, const char * fmt, ...);
void log_continue_locked(connection_t * connection, const char * fmt, ...);
void log_continue(connection_t * connection, const char * fmt, ...);