CPPFLAGS=@CPPFLAGS@ -I. -I ./include/
DATABASE=@DATABASE@
DEFS=@DEFS@
-LDFLAGS=@LDFLAGS@
+LDFLAGS=@LDFLAGS@ -rdynamic
LIBS=@LIBS@
LIBOBJS=@LIBOBJS@
INSTALL=@INSTALL@
INSTALL_DATA=@INSTALL_DATA@
RESOLV=@RESOLV@
SHELL=/bin/sh
-SERVER_LDFLAGS=@SERVER_LDFLAGS@
+SERVER_LDFLAGS=@SERVER_LDFLAGS@ -rdynamic
SERVER_LIBS=@SERVER_LIBS@
SETUP_LIBS=@SETUP_LIBS@
YACC=@YACC@
clientsocket.c commands.c config.c control.c $(DATABASE) \
domain.c serv_extensions.c file_ops.c genstamp.c getutline.c \
housekeeping.c html.c ical_dezonify.c \
- internet_addressing.c \
+ internet_addressing.c ecrash.c \
ipc_c_tcp.c locate_host.c md5.c messages.c \
modules/autocompletion/serv_autocompletion.c \
mime_parser.c msgbase.c msgform.c parsedate.c policy.c \
routines2.o screen.o tools.o citadel_dirs.o $(LIBOBJS)
$(CC) citadel.o citadel_ipc.o client_chat.o client_passwords.o \
commands.o html.o ipc_c_tcp.o md5.o messages.o rooms.o routines.o \
- routines2.o screen.o tools.o citadel_dirs.o $(LIBOBJS) \
+ routines2.o screen.o tools.o citadel_dirs.o ecrash.o $(LIBOBJS) \
$(LDFLAGS) -o citadel $(LIBS)
.y.c:
file_ops.o msgbase.o euidindex.o \
locate_host.o housekeeping.o mime_parser.o html.o \
internet_addressing.o journaling.o \
- parsedate.o genstamp.o \
+ parsedate.o genstamp.o ecrash.o \
clientsocket.o modules_init.o $(AUTH) $(SERV_MODULES)
citserver: $(SERV_OBJS)
#include "screen.h"
#include "citadel_dirs.h"
+#include "ecrash.h"
#include "md5.h"
#define IFEXPERT if (userflags&US_EXPERT)
CtdlIPC *ipc_for_signal_handlers; /* KLUDGE cover your eyes */
+
+/*
+ * lprintf() ... Write logging information;
+ * simple here to have the same
+ * symbols in the client.
+ */
+enum LogLevel {CTDL_EMERG};
+
+void lprintf(enum LogLevel loglevel, const char *format, ...) {
+ va_list arg_ptr;
+
+ va_start(arg_ptr, format);
+ vfprintf(stderr, format, arg_ptr);
+ va_end(arg_ptr);
+ fflush(stderr);
+}
+
/*
* here is our 'clean up gracefully and exit' routine
*/
char relhome[PATH_MAX]="";
char ctdldir[PATH_MAX]=CTDLDIR;
int lp;
+ eCrashParameters params;
+// eCrashSymbolTable symbol_table;
calc_dirs_n_files(relh, home, relhome, ctdldir);
+
+ bzero(¶ms, sizeof(params));
+ params.filename = file_pid_paniclog;
+// panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
+ params.filep = fopen(file_pid_paniclog, "a+");
+ params.debugLevel = ECRASH_DEBUG_VERBOSE;
+ params.dumpAllThreads = TRUE;
+ params.useBacktraceSymbols = 1;
+/// BuildSymbolTable(&symbol_table);
+// params.symbolTable = &symbol_table;
+ params.signals[0]=SIGSEGV;
+ params.signals[1]=SIGILL;
+ params.signals[2]=SIGBUS;
+ params.signals[3]=SIGABRT;
+
+ eCrash_Init(¶ms);
setIPCDeathHook(screen_delete);
setIPCErrorPrintf(err_printf);
char file_citadel_socket[PATH_MAX]="";
char file_mail_aliases[PATH_MAX]="";
char file_pid_file[PATH_MAX]="";
+char file_pid_paniclog[PATH_MAX]="";
char file_crpt_file_key[PATH_MAX]="";
char file_crpt_file_csr[PATH_MAX]="";
char file_crpt_file_cer[PATH_MAX]="";
"%scitadel.socket",
ctdl_run_dir);
snprintf(file_pid_file,
- sizeof file_pid_file,
- "%scitadel.pid",
- ctdl_run_dir);
-
+ sizeof file_pid_file,
+ "%scitadel.pid",
+ ctdl_run_dir);
+ snprintf(file_pid_paniclog,
+ sizeof file_pid_paniclog,
+ "%spanic.log",
+ ctdl_home_directory);
snprintf(file_crpt_file_key,
sizeof file_crpt_file_key,
"%s/citadel.key",
extern char file_citadel_socket[PATH_MAX];
extern char file_mail_aliases[PATH_MAX];
extern char file_pid_file[PATH_MAX];
+extern char file_pid_paniclog[PATH_MAX];
extern char file_crpt_file_key[PATH_MAX];
extern char file_crpt_file_csr[PATH_MAX];
extern char file_crpt_file_cer[PATH_MAX];
int ScheduledShutdown = 0;
int do_defrag = 0;
time_t server_startup_time;
+int panic_fd;
/**
* \brief print the actual stack frame.
free(strings);
#endif
}
+
+/**
+ * \brief print the actual stack frame.
+ */
+void cit_panic_backtrace(int SigNum)
+{
+#ifdef HAVE_BACKTRACE
+ void *stack_frames[10];
+ size_t size, i;
+ char **strings;
+
+ printf("caught signal 11\n");
+ size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
+ strings = backtrace_symbols(stack_frames, size);
+ for (i = 0; i < size; i++) {
+ if (strings != NULL)
+ lprintf(1, "%s\n", strings[i]);
+ else
+ lprintf(1, "%p\n", stack_frames[i]);
+ }
+ free(strings);
+#endif
+ exit(-1);
+}
+
/*
* Various things that need to be initialized at startup
*/
};
void cit_backtrace(void);
+void cit_panic_backtrace(int SigNum);
void master_startup (void);
void master_cleanup (int exitcode);
void RemoveContext (struct CitContext *);
struct CitContext *viewed,
struct CitContext *viewer);
extern int do_defrag;
+extern int panic_fd;
char CtdlCheckExpress(void);
int CtdlAccessCheck(int);
#include "snprintf.h"
#endif
#include "screen.h"
+#include "ecrash.h"
struct citcmd {
struct citcmd *next;
static void *ka_thread(void *arg)
{
+ char threadName[256];
+
+ // Set up our name
+ sprintf(threadName, "ka_Thread n");
+
+ // Register for tracing
+ eCrash_RegisterThread(threadName, 0);
+
really_do_keepalive();
pthread_detach(ka_thr_handle);
ka_thr_active = 0;
+
+ eCrash_UnregisterThread();
return NULL;
}
CFLAGS = -Wall -g
-#ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -O0 -ggdb -pg
-#else
-# CFLAGS += -O2
-#endif
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0 -ggdb -rdynamic -D_GNU_SOURCE -MD -MP
+ LDFLAGS += -pg
+ SERVER_LDFLAGS += -lm
+# SERVER_LDFLAGS += -pg
+else
+ CFLAGS += -O2
+endif
configure: configure-stamp
configure-stamp:
--with-zlib \
--with-ldap \
--with-libical \
- --with-libsieve
+ --with-libsieve \
+ --enable-debug
touch configure-stamp
--- /dev/null
+/*
+ * File: eCrash.c
+ * @author David Frascone
+ *
+ * eCrash Implementation
+ *
+ * eCrash will allow you to capture stack traces in the
+ * event of a crash, and write those traces to disk, stdout,
+ * or any other file handle.
+ *
+ * modified to integrate closer into citadel by Wilfried Goesgens
+ *
+ * vim: ts=4
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <execinfo.h>
+#include <pthread.h>
+#include "sysdep_decls.h"
+#include "ecrash.h"
+
+#define NIY() printf("%s: Not Implemented Yet!\n", __FUNCTION__)
+
+static eCrashParameters gbl_params;
+static int gbl_fd=-1;
+
+static int gbl_backtraceEntries;
+static void **gbl_backtraceBuffer;
+static char **gbl_backtraceSymbols;
+static int gbl_backtraceDoneFlag = 0;
+
+/*
+ * Private structures for our thread list
+ */
+typedef struct thread_list_node{
+ char *threadName;
+ pthread_t thread;
+ int backtraceSignal;
+ sighandler_t oldHandler;
+ struct thread_list_node *Next;
+} ThreadListNode;
+
+static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
+static ThreadListNode *ThreadList = NULL;
+
+/*********************************************************************
+ *********************************************************************
+ ** P R I V A T E F U N C T I O N S
+ *********************************************************************
+ ********************************************************************/
+
+
+/*!
+ * Insert a node into our threadList
+ *
+ * @param name Text string indicating our thread
+ * @param thread Our Thread Id
+ * @param signo Signal to create backtrace with
+ * @param old_handler Our old handler for signo
+ *
+ * @returns zero on success
+ */
+static int addThreadToList(char *name, pthread_t thread,int signo,
+ sighandler_t old_handler)
+{
+ ThreadListNode *node;
+
+ node = malloc(sizeof(ThreadListNode));
+ if (!node) return -1;
+
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
+ node->threadName = strdup(name);
+ node->thread = thread;
+ node->backtraceSignal = signo;
+ node->oldHandler = old_handler;
+
+ /* And, add it to the list */
+ pthread_mutex_lock(&ThreadListMutex);
+ node->Next = ThreadList;
+ ThreadList = node;
+ pthread_mutex_unlock(&ThreadListMutex);
+
+ return 0;
+
+} // addThreadToList
+
+/*!
+ * Remove a node from our threadList
+ *
+ * @param thread Our Thread Id
+ *
+ * @returns zero on success
+ */
+static int removeThreadFromList(pthread_t thread)
+{
+ ThreadListNode *Probe, *Prev=NULL;
+ ThreadListNode *Removed = NULL;
+
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
+ pthread_mutex_lock(&ThreadListMutex);
+ for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
+ if (Probe->thread == thread) {
+ // We found it! Unlink it and move on!
+ Removed = Probe;
+ if (Prev == NULL) { // head of list
+ ThreadList = Probe->Next;
+ } else {
+ // Prev != null, so we need to link around ourselves.
+ Prev->Next = Probe->Next;
+ }
+ Removed->Next = NULL;
+ break;
+ }
+
+ Prev = Probe;
+ }
+ pthread_mutex_unlock(&ThreadListMutex);
+
+ // Now, if something is in Removed, free it, and return success
+ if (Removed) {
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ " Found %s -- removing\n", Removed->threadName);
+ // Reset the signal handler
+ signal(Removed->backtraceSignal, Removed->oldHandler);
+
+ // And free the allocated memory
+ free (Removed->threadName);
+ free (Removed);
+
+ return 0;
+ } else {
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ " Not Found\n");
+ return -1; // Not Found
+ }
+} // removeThreadFromList
+
+/*!
+ * Output text to a fd, looping to avoid being interrupted.
+ *
+ * @param str String to output
+ * @param bytes String length
+ * @param fd File descriptor to write to
+ *
+ * @returns bytes written, or error on failure.
+ */
+static int blockingWrite(char *str, int bytes, int fd)
+{
+ int offset=0;
+ int bytesWritten;
+ int totalWritten = 0;
+
+ while (bytes > 0) {
+ bytesWritten = write(fd, &str[offset], bytes);
+ if (bytesWritten < 1) break;
+ totalWritten += bytesWritten;
+ bytes -= bytesWritten;
+ }
+
+ return totalWritten;
+
+} // blockingWrite
+/*!
+ * Print out a line of output to all our destinations
+ *
+ * One by one, output a line of text to all of our output destinations.
+ *
+ * Return failure if we fail to output to any of them.
+ *
+ * @param format Normal printf style vararg format
+ *
+ * @returns nothing// bytes written, or error on failure.
+ */
+static void outputPrintf(char *format, ...)
+{
+ // Our output line of text
+ static char outputLine[MAX_LINE_LEN];
+ //int bytesInLine;
+ va_list ap;
+ //int return_value=0;
+
+ va_start(ap, format);
+
+ return lprintf(CTDL_EMERG, format, ap);
+
+/*
+ bytesInLine = vsnprintf(outputLine, MAX_LINE_LEN-1, format, ap);
+ if (bytesInLine > -1 && bytesInLine < (MAX_LINE_LEN-1)) {
+ // We're a happy camper -- start printing
+ if (gbl_params.filename) {
+ // append to our file -- hopefully it's been opened
+ if (gbl_fd != -1) {
+ if (blockingWrite(outputLine, bytesInLine, gbl_fd)) {
+ return_value=-2;
+ }
+ }
+ }
+
+ // Write to our file pointer
+ if (gbl_params.filep != NULL) {
+ if (fwrite(outputLine, bytesInLine, 1, gbl_params.filep) != 1) {
+ return_value=-3;
+ }
+ fflush(gbl_params.filep);
+ }
+
+ // Write to our fd
+ if (gbl_params.fd != -1) {
+ if (blockingWrite(outputLine, bytesInLine, gbl_params.fd)) {
+ return_value=-4;
+ }
+ }
+ } else {
+ // We overran our string.
+ return_value=-1;
+ }
+*/
+} // outputPrintf
+
+/*!
+ * Initialize our output (open files, etc)
+ *
+ * This file initializes all output streams, since we're about
+ * to have output.
+ *
+ */
+static void outputInit( void )
+{
+ if (gbl_params.filename) {
+ /* First try append */
+ gbl_fd = open(gbl_params.filename, O_WRONLY|O_APPEND);
+ if (gbl_fd < 0) {
+ gbl_fd = open(gbl_params.filename, O_RDWR|O_CREAT,
+ S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); // 0644
+ if (gbl_fd < 0) {
+ gbl_fd = -1;
+ }
+ }
+ }
+} // outputInit
+
+
+/*!
+ * Finalize our output (close files, etc)
+ *
+ * This file closes all output streams.
+ *
+ */
+static void outputFini( void )
+{
+/* -> these seem to run into mutexes in the libc...
+ if (gbl_fd > -1)
+ close(gbl_fd);
+
+ if (gbl_params.filep != NULL)
+ fclose(gbl_params.filep);
+
+ if (gbl_params.fd > -1)
+ close(gbl_params.fd);
+*/
+ // Just in case someone tries to call outputPrintf after outputFini
+ gbl_fd = gbl_params.fd = -1;
+ gbl_params.filep = NULL;
+
+ sync();
+
+} // outputFini
+
+static void *lookupClosestSymbol(eCrashSymbolTable *table,
+ void *address)
+{
+ int addr;
+ eCrashSymbol *last=NULL;
+
+ // For now, use a linear lookup.
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "Looking for %p in %d symbols\n", address, table->numSymbols);
+ for (addr=0; addr < table->numSymbols; addr++) {
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ " Examining [%d] %p\n", addr,
+ table->symbols[addr].address);
+ if (table->symbols[addr].address > address) {
+ break;
+ }
+ last = &table->symbols[addr];
+ }
+
+ // last will either be NULL, or the last address less than the
+ // one we're looking for.
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "Returning %s (%p)\n", last?last->function:"(nil)",
+ last?last->address:0);
+ return last;
+
+} // lookupClosestSymbol
+
+/*!
+ * Dump our backtrace into a global location
+ *
+ * This function will dump out our backtrace into our
+ * global holding area.
+ *
+ */
+static void createGlobalBacktrace( void )
+{
+
+ gbl_backtraceEntries = backtrace(gbl_backtraceBuffer,
+ gbl_params.maxStackDepth);
+
+ /* This is NOT signal safe -- it calls malloc. We need to
+ let the caller pass in a pointer to a symbol table inside of
+ our params. TODO */
+
+ if (!gbl_params.symbolTable) {
+ if (gbl_params.useBacktraceSymbols != FALSE) {
+ gbl_backtraceSymbols = backtrace_symbols(gbl_backtraceBuffer,
+ gbl_backtraceEntries);
+ }
+ }
+
+} /* createGlobalBacktrace */
+
+/*!
+ * Print out (to all the fds, etc), or global backtrace
+ */
+static void outputGlobalBacktrace ( void )
+{
+ int i;
+
+ for (i=0; i < gbl_backtraceEntries; i++) {
+ if (gbl_params.symbolTable) {
+ eCrashSymbol *symbol;
+
+ symbol = lookupClosestSymbol(gbl_params.symbolTable,
+ gbl_backtraceBuffer[i]);
+
+ if (symbol) {
+ outputPrintf("* Frame %02d: %s+%u\n",
+ i, symbol->function,
+ gbl_backtraceBuffer[i] - symbol->address);
+ } else {
+ outputPrintf("* Frame %02d: %p\n", i,
+ gbl_backtraceBuffer[i]);
+ }
+ } else {
+ if (gbl_backtraceSymbols != FALSE) {
+ outputPrintf("* Frame %02d: %s\n",
+ i, gbl_backtraceSymbols[i]);
+ } else {
+ outputPrintf("* Frame %02d: %p\n", i,
+ gbl_backtraceBuffer[i]);
+ }
+ } // symbolTable
+ }
+} // outputGlobalBacktrace
+
+/*!
+ * Output our current stack's backtrace
+ */
+static void outputBacktrace( void )
+{
+ createGlobalBacktrace();
+ outputGlobalBacktrace();
+} /* outputBacktrace */
+
+static void outputBacktraceThreads( void )
+{
+ ThreadListNode *probe;
+ int i;
+
+ // When we're backtracing, don't worry about the mutex . . hopefully
+ // we're in a safe place.
+
+ for (probe=ThreadList; probe; probe=probe->Next) {
+ gbl_backtraceDoneFlag = 0;
+ pthread_kill(probe->thread, probe->backtraceSignal);
+ for (i=0; i < gbl_params.threadWaitTime; i++) {
+ if (gbl_backtraceDoneFlag)
+ break;
+ sleep(1);
+ }
+ if (gbl_backtraceDoneFlag) {
+ outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
+ probe->threadName, (unsigned int)probe->thread);
+ outputGlobalBacktrace();
+ } else {
+ outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
+ probe->threadName, (unsigned int)probe->thread);
+ }
+ outputPrintf("*\n");
+ }
+} // outputBacktraceThreads
+
+
+/*!
+ * Handle signals (crash signals)
+ *
+ * This function will catch all crash signals, and will output the
+ * crash dump.
+ *
+ * It will physically write (and sync) the current thread's information
+ * before it attempts to send signals to other threads.
+ *
+ * @param signum Signal received.
+ */
+static void crash_handler(int signo)
+{
+ outputInit();
+ outputPrintf("*********************************************************\n");
+ outputPrintf("* eCrash Crash Handler\n");
+ outputPrintf("*********************************************************\n");
+ outputPrintf("*\n");
+ outputPrintf("* Got a crash! signo=%d\n", signo);
+ outputPrintf("*\n");
+ outputPrintf("* Offending Thread's Backtrace:\n");
+ outputPrintf("*\n");
+ outputBacktrace();
+ outputPrintf("*\n");
+
+ if (gbl_params.dumpAllThreads != FALSE) {
+ outputBacktraceThreads();
+ }
+
+ outputPrintf("*\n");
+ outputPrintf("*********************************************************\n");
+ outputPrintf("* eCrash Crash Handler\n");
+ outputPrintf("*********************************************************\n");
+
+ outputFini();
+
+ exit(signo);
+} // crash_handler
+
+/*!
+ * Handle signals (bt signals)
+ *
+ * This function shoudl be called to generate a crashdump into our
+ * global area. Once the dump has been completed, this function will
+ * return after tickling a global. Since mutexes are not async
+ * signal safe, the main thread, after signaling us to generate our
+ * own backtrace, will sleep for a few seconds waiting for us to complete.
+ *
+ * @param signum Signal received.
+ */
+static void bt_handler(int signo)
+{
+ createGlobalBacktrace();
+ gbl_backtraceDoneFlag=1;
+} // bt_handler
+
+/*!
+ * Validate a passed-in symbol table
+ *
+ * For now, just print it out (if verbose), and make sure it's
+ * sorted and none of the pointers are zero.
+ */
+static int ValidateSymbolTable( void )
+{
+ int i;
+ int rc=0;
+ unsigned long lastAddress =0;
+
+ // Get out of here if the table is empty
+ if (!gbl_params.symbolTable) return 0;
+
+ // Dump it in verbose mode
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "Symbol Table Provided with %d symbols\n",
+ gbl_params.symbolTable->numSymbols);
+ for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
+ // Dump it in verbose mode
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "%-30s %p\n",
+ gbl_params.symbolTable->symbols[i].function,
+ gbl_params.symbolTable->symbols[i].address);
+ if (lastAddress >
+ (unsigned long)gbl_params.symbolTable->symbols[i].address) {
+ DPRINTF(ECRASH_DEBUG_ERROR,
+ "Error: symbol table is not sorted (last=%p, current=%p)\n",
+ (void *)lastAddress,
+ gbl_params.symbolTable->symbols[i].address);
+ rc = -1;
+ }
+
+ } // for
+
+ return rc;
+
+} // ValidateSymbolTable
+
+/*********************************************************************
+ *********************************************************************
+ ** P U B L I C F U N C T I O N S
+ *********************************************************************
+ ********************************************************************/
+
+/*!
+ * Initialize eCrash.
+ *
+ * This function must be called before calling any other eCrash
+ * functions. It sets up the global behavior of the system, and
+ * registers the calling thread for crash dumps.
+ *
+ * @param params Our input parameters. The passed in structure will be copied.
+ *
+ * @return Zero on success.
+ */
+int eCrash_Init(eCrashParameters *params)
+{
+ int sigIndex;
+ int ret = 0;
+#ifdef DO_SIGNALS_RIGHT
+ sigset_t blocked;
+ struct sigaction act;
+#endif
+
+ DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
+
+ // Allocate our backtrace area
+ gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
+
+#ifdef DO_SIGNALS_RIGHT
+ sigemptyset(&blocked);
+ act.sa_sigaction = crash_handler;
+ act.sa_mask = blocked;
+ act.sa_flags = SA_SIGINFO;
+#endif
+
+ if (params != NULL) {
+ // Make ourselves a global copy of params.
+ gbl_params = *params;
+ gbl_params.filename = strdup(params->filename);
+
+ // Set our defaults, if they weren't specified
+ if (gbl_params.maxStackDepth == 0 )
+ gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
+
+ if (gbl_params.defaultBacktraceSignal == 0 )
+ gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
+
+ if (gbl_params.threadWaitTime == 0 )
+ gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
+
+ if (gbl_params.debugLevel == 0 )
+ gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
+
+ // Copy our symbol table
+ if (gbl_params.symbolTable) {
+ DPRINTF(ECRASH_DEBUG_VERBOSE,
+ "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
+ gbl_params.symbolTable->numSymbols);
+ // Make a copy of our symbol table
+ gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
+ memcpy(gbl_params.symbolTable, params->symbolTable,
+ sizeof(eCrashSymbolTable));
+
+ // Now allocate / copy the actual table.
+ gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
+ gbl_params.symbolTable->numSymbols);
+ memcpy(gbl_params.symbolTable->symbols,
+ params->symbolTable->symbols,
+ sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
+
+ ValidateSymbolTable();
+ }
+
+ // And, finally, register for our signals
+ for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
+ DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
+ " Catching signal[%d] %d\n", sigIndex,
+ gbl_params.signals[sigIndex]);
+
+ // I know there's a better way to catch signals with pthreads.
+ // I'll do it later TODO
+ signal(gbl_params.signals[sigIndex], crash_handler);
+ }
+ } else {
+ DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
+ ret = -1;
+ }
+ DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
+ return ret;
+} /* eCrash_Init */
+
+/*!
+ * UnInitialize eCrash.
+ *
+ * This function may be called to de-activate eCrash, release the
+ * signal handlers, and free any memory allocated by eCrash.
+ *
+ * @return Zero on success.
+ */
+int eCrash_Uninit( void )
+{
+ NIY();
+
+ return 0;
+} /* eCrash_Uninit */
+
+/*!
+ * Register a thread for backtracing on crash.
+ *
+ * This function must be called by any thread wanting it's stack
+ * dumped in the event of a crash. The thread my specify what
+ * signal should be used, or the default, SIGUSR1 will be used.
+ *
+ * @param signo Signal to use to generate dump (default: SIGUSR1)
+ *
+ * @return Zero on success.
+ */
+int eCrash_RegisterThread(char *name, int signo)
+{
+ sighandler_t old_handler;
+
+ // Register for our signal
+ if (signo == 0) {
+ signo = gbl_params.defaultBacktraceSignal;
+ }
+
+ old_handler = signal(signo, bt_handler);
+ return addThreadToList(name, pthread_self(), signo, old_handler);
+
+} /* eCrash_RegisterThread */
+
+/*!
+ * Un-register a thread for stack dumps.
+ *
+ * This function may be called to un-register any previously
+ * registered thread.
+ *
+ * @return Zero on success.
+ */
+int eCrash_UnregisterThread( void )
+{
+ return removeThreadFromList(pthread_self());
+} /* eCrash_UnregisterThread */
+
--- /dev/null
+/**
+ * \file eCrash.h
+ * @author David Frascone
+ *
+ * eCrash types and prototypes.
+ *
+ * vim: ts=4
+ */
+
+#ifndef _ECRASH_H_
+#define _ECRASH_H_
+
+#include <stdio.h>
+#include <signal.h>
+
+typedef void (*sighandler_t)(int);
+
+typedef long BOOL;
+
+#define MAX_LINE_LEN 256
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#define BOOL int
+#endif
+
+#define ECRASH_DEFAULT_STACK_DEPTH 10
+#define ECRASH_DEFAULT_BACKTRACE_SIGNAL SIGUSR2
+#define ECRASH_DEFAULT_THREAD_WAIT_TIME 10
+#define ECRASH_MAX_NUM_SIGNALS 30
+
+/** \struct eCrashSymbol
+ * \brief Function Name / Address pair
+ *
+ * This is used in conjunction with eCrashSymbolTable to
+ * provide an alternative to backtrace_symbols.
+ */
+typedef struct {
+ char *function;
+ void *address;
+} eCrashSymbol;
+/** \struct eCrashSymbolTable
+ * \brief Symbol table used to avoid backtrace_symbols()
+ *
+ * This structure is used to provide a alternative to
+ * backtrace_symbols which is not async signal safe.
+ *
+ * The symbol table should be sorted by address (it will
+ * be either binary or linearly searched)
+ */
+typedef struct {
+ int numSymbols;
+ eCrashSymbol *symbols;
+} eCrashSymbolTable;
+
+
+
+#define ECRASH_DEBUG_ENABLE /* undef to turn off debug */
+
+#ifdef ECRASH_DEBUG_ENABLE
+# define ECRASH_DEBUG_VERY_VERBOSE 1
+# define ECRASH_DEBUG_VERBOSE 2
+# define ECRASH_DEBUG_INFO 3
+# define ECRASH_DEBUG_WARN 4
+# define ECRASH_DEBUG_ERROR 5
+# define ECRASH_DEBUG_OFF 6
+# define ECRASH_DEBUG_DEFAULT (ECRASH_DEBUG_ERROR)
+# define DPRINTF(level, fmt...) \
+ if (level >= gbl_params.debugLevel) { printf(fmt); fflush(stdout); }
+#else /* ECRASH_DEBUG_ENABLE */
+# define DPRINTF(level, fmt...)
+#endif /* ECRASH_DEBUG_ENABLE */
+
+
+/** \struct eCrashParameters
+ * \brief eCrash Initialization Parameters
+ *
+ * This structure contains all the global initialization functions
+ * for eCrash.
+ *
+ * @see eCrash_Init
+ */
+typedef struct {
+
+
+ /* OUTPUT OPTIONS */
+ /** Filename to output to, or NULL */
+ char *filename;
+ /** FILE * to output to or NULL */
+ FILE *filep;
+ /** fd to output to or -1 */
+ int fd;
+
+ int debugLevel;
+
+ /** If true, all registered threads will
+ * be dumped
+ */
+ BOOL dumpAllThreads;
+
+ /** How far to backtrace each stack */
+ unsigned int maxStackDepth;
+
+ /** Default signal to use to tell a thread to drop it's
+ * stack.
+ */
+ int defaultBacktraceSignal;
+
+ /** How long to wait for a threads
+ * dump
+ */
+ unsigned int threadWaitTime;
+
+ /** If this is non-zero, the dangerous function, backtrace_symbols
+ * will be used. That function does a malloc(), so is not async
+ * signal safe, and could cause deadlocks.
+ */
+ BOOL useBacktraceSymbols;
+
+ /** To avoid the issues with backtrace_symbols (see comments above)
+ * the caller can supply it's own symbol table, containing function
+ * names and start addresses. This table can be created using
+ * a script, or a static table.
+ *
+ * If this variable is not null, it will be used, instead of
+ * backtrace_symbols, reguardless of the setting
+ * of useBacktraceSymbols.
+ */
+ eCrashSymbolTable *symbolTable;
+
+ /** Array of crash signals to catch,
+ * ending in 0 I would have left it a [] array, but I
+ * do a static copy, so I needed a set size.
+ */
+ int signals[ECRASH_MAX_NUM_SIGNALS];
+
+} eCrashParameters;
+
+/*!
+ * Initialize eCrash.
+ *
+ * This function must be called before calling any other eCrash
+ * functions. It sets up the global behavior of the system.
+ *
+ * @param params Our input parameters. The passed in structure will be copied.
+ *
+ * @return Zero on success.
+ */
+int eCrash_Init(eCrashParameters *params);
+/*!
+ * UnInitialize eCrash.
+ *
+ * This function may be called to de-activate eCrash, release the
+ * signal handlers, and free any memory allocated by eCrash.
+ *
+ * @return Zero on success.
+ */
+int eCrash_Uninit( void );
+
+/*!
+ * Register a thread for backtracing on crash.
+ *
+ * This function must be called by any thread wanting it's stack
+ * dumped in the event of a crash. The thread my specify what
+ * signal should be used, or the default, SIGUSR1 will be used.
+ *
+ * @param name String used to refer to us in crash dumps
+ * @param signo Signal to use to generate dump (default: SIGUSR1)
+ *
+ * @return Zero on success.
+ */
+int eCrash_RegisterThread(char *name, int signo);
+
+/*!
+ * Un-register a thread for stack dumps.
+ *
+ * This function may be called to un-register any previously
+ * registered thread.
+ *
+ * @return Zero on success.
+ */
+int eCrash_UnregisterThread( void );
+
+#endif /* _E_CRASH_H_ */
void CtdlUnregisterProtoHook(void (*handler)(char *), char *cmd);
void CtdlRegisterServiceHook(int tcp_port,
- char *sockpath,
- void (*h_greeting_function) (void),
- void (*h_command_function) (void),
- void (*h_async_function) (void)
+ char *sockpath,
+ void (*h_greeting_function) (void),
+ void (*h_command_function) (void),
+ void (*h_async_function) (void),
+ const char *ServiceName
);
void CtdlUnregisterServiceHook(int tcp_port,
char *sockpath,
}
+const char *CitadelServiceIMAP="IMAP";
+const char *CitadelServiceIMAPS="IMAPS";
+
/*
* This function is called to register the IMAP extension with Citadel.
*/
CTDL_MODULE_INIT(imap)
{
CtdlRegisterServiceHook(config.c_imap_port,
- NULL, imap_greeting, imap_command_loop, NULL);
+ NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP);
#ifdef HAVE_OPENSSL
CtdlRegisterServiceHook(config.c_imaps_port,
- NULL, imaps_greeting, imap_command_loop, NULL);
+ NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS);
#endif
CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP);
#endif /* HAVE_LIBSIEVE */
-
+const char* CitadelServiceManageSieve = "ManageSieve";
CTDL_MODULE_INIT(managesieve)
{
NULL,
managesieve_greeting,
managesieve_command_loop,
- NULL);
+ NULL,
+ CitadelServiceManageSieve);
#else /* HAVE_LIBSIEVE */
}
+const char *CitadelServicePop3="POP3";
+const char *CitadelServicePop3S="POP3S";
CTDL_MODULE_INIT(pop3)
NULL,
pop3_greeting,
pop3_command_loop,
- NULL);
+ NULL,
+ CitadelServicePop3);
#ifdef HAVE_OPENSSL
CtdlRegisterServiceHook(config.c_pop3s_port,
NULL,
pop3s_greeting,
pop3_command_loop,
- NULL);
+ NULL,
+ CitadelServicePop3S);
#endif
CtdlRegisterSessionHook(pop3_cleanup_function, EVT_STOP);
-
+const char *CitadelServiceSMTP_MTA="SMTP-MTA";
+const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
+const char *CitadelServiceSMTP_MSA="SMTP-MSA";
+const char *CitadelServiceSMTP_LMTP="LMTP";
+const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
CTDL_MODULE_INIT(smtp)
{
NULL,
smtp_mta_greeting,
smtp_command_loop,
- NULL);
+ NULL,
+ CitadelServiceSMTP_MTA);
#ifdef HAVE_OPENSSL
CtdlRegisterServiceHook(config.c_smtps_port,
NULL,
smtps_greeting,
smtp_command_loop,
- NULL);
+ NULL,
+ CitadelServiceSMTPS_MTA);
#endif
CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
NULL,
smtp_msa_greeting,
smtp_command_loop,
- NULL);
+ NULL,
+ CitadelServiceSMTP_MSA);
CtdlRegisterServiceHook(0, /* local LMTP */
file_lmtp_socket,
lmtp_greeting,
smtp_command_loop,
- NULL);
+ NULL,
+ CitadelServiceSMTP_LMTP);
CtdlRegisterServiceHook(0, /* local LMTP */
file_lmtp_unfiltered_socket,
lmtp_unfiltered_greeting,
smtp_command_loop,
- NULL);
+ NULL,
+ CitadelServiceSMTP_LMTP_UNF);
smtp_init_spoolout();
CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
iterations = 0;
}
else {
- cprintf(".");
+ cprintf(".");
if (iterations % 64 == 0)
cprintf("\n");
}
-
+const char *CitadelServiceDICT_TCP="DICT_TCP";
CTDL_MODULE_INIT(vcard)
{
NULL,
check_get_greeting,
check_get,
- NULL);
+ NULL,
+ CitadelServiceDICT_TCP);
/* return our Subversion id for the Log */
return "$Id$";
void CtdlRegisterServiceHook(int tcp_port,
- char *sockpath,
- void (*h_greeting_function) (void),
- void (*h_command_function) (void),
- void (*h_async_function) (void)
- )
+ char *sockpath,
+ void (*h_greeting_function) (void),
+ void (*h_command_function) (void),
+ void (*h_async_function) (void),
+ const char *ServiceName)
{
struct ServiceFunctionHook *newfcn;
char *message;
newfcn->h_greeting_function = h_greeting_function;
newfcn->h_command_function = h_command_function;
newfcn->h_async_function = h_async_function;
+ newfcn->ServiceName = ServiceName;
if (sockpath != NULL) {
newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions, &error);
snprintf(message, SIZ, "Unix domain socket '%s': ", sockpath);
}
else if (tcp_port <= 0) { /* port -1 to disable */
- lprintf(CTDL_INFO, "Service has been manually disabled, skipping\n");
+ lprintf(CTDL_INFO, "Service %s has been manually disabled, skipping\n", ServiceName);
free (message);
free(newfcn);
return;
tcp_port,
config.c_maxsessions,
&error);
- snprintf(message, SIZ, "TCP port %s:%d: ",
- config.c_ip_addr, tcp_port);
+ snprintf(message, SIZ, "TCP port %s:%d: (%s) ",
+ config.c_ip_addr, tcp_port, ServiceName);
}
if (newfcn->msock > 0) {
struct citmgsve *MGSVE; /**< Managesieve Session struct */
struct cit_ical *CIT_ICAL; /* calendaring data */
struct ma_info *ma; /* multipart/alternative data */
+ const char* ServiceName; /**< whats our actual purpose? */
};
typedef struct CitContext t_context;
void (*h_command_function) (void) ;
void (*h_async_function) (void) ;
int msock;
+ const char* ServiceName; /* this is just for debugging and logging purposes. */
};
extern struct ServiceFunctionHook *ServiceHookTable;
#include "citadel_dirs.c"
#include "modules_init.h"
-
+#include "ecrash.h"
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#ifndef HAVE_SNPRINTF
#include "snprintf.h"
#endif
+const char *CitadelServiceUDS="citadel-UDS";
+const char *CitadelServiceTCP="citadel-TCP";
/*
* Here's where it all begins.
#ifdef HAVE_RUN_DIR
struct stat filestats;
#endif
+ eCrashParameters params;
+// eCrashSymbolTable symbol_table;
/* initialise semaphores here. Patch by Matt and davew
* its called here as they are needed by lprintf for thread safety
}
calc_dirs_n_files(relh, home, relhome, ctdldir);
-
/* daemonize, if we were asked to */
if (running_as_daemon) {
start_daemon(0);
drop_root_perms = 1;
}
+ bzero(¶ms, sizeof(params));
+ params.filename = file_pid_paniclog;
+ panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
+ params.filep = fopen(file_pid_paniclog, "a+");
+ params.debugLevel = ECRASH_DEBUG_VERBOSE;
+ params.dumpAllThreads = TRUE;
+ params.useBacktraceSymbols = 1;
+/// BuildSymbolTable(&symbol_table);
+// params.symbolTable = &symbol_table;
+ params.signals[0]=SIGSEGV;
+ params.signals[1]=SIGILL;
+ params.signals[2]=SIGBUS;
+ params.signals[3]=SIGABRT;
+
+ eCrash_Init(¶ms);
+
+ eCrash_RegisterThread("MasterThread", 0);
+
+/// signal(SIGSEGV, cit_panic_backtrace);
/* Initialize the syslogger. Yes, we are really using 0 as the
* facility, because we are going to bitwise-OR the facility to
* the severity of each message, allowing us to write to other
* Bind the server to a Unix-domain socket.
*/
CtdlRegisterServiceHook(0,
- file_citadel_socket,
- citproto_begin_session,
- do_command_loop,
- do_async_loop);
+ file_citadel_socket,
+ citproto_begin_session,
+ do_command_loop,
+ do_async_loop,
+ CitadelServiceUDS);
/*
* Bind the server to our favorite TCP port (usually 504).
NULL,
citproto_begin_session,
do_command_loop,
- do_async_loop);
+ do_async_loop,
+ CitadelServiceTCP);
/*
* Load any server-side extensions available here.
#include "housekeeping.h"
#include "tools.h"
#include "modules/crypto/serv_crypto.h" /* Needed for init_ssl, client_write_ssl, client_read_ssl, destruct_ssl */
+#include "ecrash.h"
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
* lprintf() ... Write logging information
*/
void lprintf(enum LogLevel loglevel, const char *format, ...) {
- char *buf;
va_list arg_ptr;
+ char buf[SIZ], buf2[SIZ];
if (enable_syslog) {
va_start(arg_ptr, format);
}
/* stderr output code */
- if (enable_syslog || running_as_daemon) {
- return;
- }
+ if (enable_syslog || running_as_daemon) return;
/* if we run in forground and syslog is disabled, log to terminal */
if (loglevel <= verbosity) {
/* Promote to time_t; types differ on some OSes (like darwin) */
unixtime = tv.tv_sec;
localtime_r(&unixtime, &tim);
-// begin_critical_section(S_LOG);
-
- buf = malloc(SIZ+strlen(format));
-
if (CC->cs_pid != 0) {
sprintf(buf,
"%04d/%02d/%02d %2d:%02d:%02d.%06ld [%3d] ",
tim.tm_mday, tim.tm_hour, tim.tm_min,
tim.tm_sec, (long)tv.tv_usec);
}
- strcat(buf, format);
- va_start(arg_ptr, buf);
- vfprintf(stderr, buf, arg_ptr);
+ va_start(arg_ptr, format);
+ vsprintf(buf2, format, arg_ptr);
va_end(arg_ptr);
+
+ fprintf(stderr, "%s%s", buf, buf2);
fflush(stderr);
- free (buf);
-// end_critical_section(S_LOG);
}
}
int s;
int i;
int actual_queue_len;
- long ret, ret2;
+
actual_queue_len = queue_len;
if (actual_queue_len < 5) actual_queue_len = 5;
nbytes - bytes_written,
strerror(errno), errno);
cit_backtrace();
+ lprintf(CTDL_DEBUG, "Tried to send: %s", &buf[bytes_written]);
CC->kill_me = 1;
return;
}
CtdlDestroyFixedOutputHooks();
CtdlDestroySessionHooks();
CtdlDestroyServiceHook();
+ eCrash_Uninit();
}
cdb_allocate_tsd();
+ // Register for tracing
+ eCrash_RegisterThread("WorkerThread", 0);
+
while (!time_to_die) {
/* make doubly sure we're not holding any stale db handles
serviceptr->h_command_function;
con->h_async_function =
serviceptr->h_async_function;
-
+ con->ServiceName =
+ serviceptr->ServiceName;
+
/* Determine whether it's a local socket */
if (serviceptr->sockpath != NULL)
con->is_local_socket = 1;
}
if (con != NULL) free (con);//// TODO: could this harm other threads?
/* If control reaches this point, the server is shutting down */
+ eCrash_UnregisterThread();
return(NULL);
}