X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fsysdep.c;h=14676acfb552e038528181142399497c25121de7;hb=1e656d277fe91b7c4f5d73eab4a0dd0b7a173145;hp=71b1bd9b72ca4355c3ebffd84dd67231702ecda7;hpb=505aa03d294547c7e6f43dd433712dc7c3651d33;p=citadel.git diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 71b1bd9b7..14676acfb 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -12,10 +12,6 @@ * */ -#ifdef DLL_EXPORT -#define IN_LIBCIT -#endif - #include "sysdep.h" #include #include @@ -27,6 +23,7 @@ #include #include #include +#include #include #if TIME_WITH_SYS_TIME @@ -41,7 +38,10 @@ #endif #include +#include #include +#include +#include #include #include #include @@ -52,17 +52,17 @@ #ifdef HAVE_PTHREAD_H #include #endif +#include #include "citadel.h" #include "server.h" -#include "serv_extensions.h" #include "sysdep_decls.h" #include "citserver.h" #include "support.h" #include "config.h" #include "database.h" #include "housekeeping.h" -#include "tools.h" -#include "serv_crypto.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 @@ -95,122 +95,132 @@ time_t last_purge = 0; /* Last dead session purge */ static int num_threads = 0; /* Current number of threads */ int num_sessions = 0; /* Current number of sessions */ -pthread_t initial_thread; /* tid for main() thread */ +int syslog_facility = LOG_DAEMON; +int enable_syslog = 0; + +void DestroyWorkerList(void); + -int syslog_facility = (-1); +/* + * Create an interface to lprintf that follows the coding convention. + * This is here until such time as we have replaced all calls to lprintf with CtdlLogPrintf + */ + +void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, format); + vlprintf(loglevel, format, arg_ptr); + va_end(arg_ptr); +} /* * lprintf() ... Write logging information - * - * Note: the variable "buf" below needs to be large enough to handle any - * log data sent through this function. BE CAREFUL! */ void lprintf(enum LogLevel loglevel, const char *format, ...) { va_list arg_ptr; - char buf[SIZ]; - - va_start(arg_ptr, format); - vsnprintf(buf, sizeof(buf), format, arg_ptr); - va_end(arg_ptr); - - if (syslog_facility >= 0) { - if (loglevel <= verbosity) { - /* Hackery -IO */ - if (CC && CC->cs_pid) { - memmove(buf + 6, buf, sizeof(buf) - 6); - snprintf(buf, 6, "[%3d]", CC->cs_pid); - buf[5] = ' '; - } - syslog(loglevel, buf); - } + va_start(arg_ptr, format); + vlprintf(loglevel, format, arg_ptr); + va_end(arg_ptr); +} + +void vlprintf(enum LogLevel loglevel, const char *format, va_list arg_ptr) +{ + char buf[SIZ], buf2[SIZ]; + + if (enable_syslog) { + vsyslog((syslog_facility | loglevel), format, arg_ptr); } - else if (loglevel <= verbosity) { + + /* stderr output code */ + if (enable_syslog || running_as_daemon) return; + + /* if we run in forground and syslog is disabled, log to terminal */ + if (loglevel <= verbosity) { struct timeval tv; - struct tm *tim; + struct tm tim; time_t unixtime; gettimeofday(&tv, NULL); /* Promote to time_t; types differ on some OSes (like darwin) */ unixtime = tv.tv_sec; - tim = localtime(&unixtime); - /* - * Log provides millisecond accuracy. If you need - * microsecond accuracy and your OS supports it, change - * %03ld to %06ld and remove " / 1000" after tv.tv_usec. - */ - if (CC && CC->cs_pid) { -#if 0 - /* Millisecond display */ - fprintf(stderr, - "%04d/%02d/%02d %2d:%02d:%02d.%03ld [%3d] %s", - tim->tm_year + 1900, tim->tm_mon + 1, - tim->tm_mday, tim->tm_hour, tim->tm_min, - tim->tm_sec, (long)tv.tv_usec / 1000, - CC->cs_pid, buf); -#endif - /* Microsecond display */ - fprintf(stderr, - "%04d/%02d/%02d %2d:%02d:%02d.%06ld [%3d] %s", - tim->tm_year + 1900, tim->tm_mon + 1, - tim->tm_mday, tim->tm_hour, tim->tm_min, - tim->tm_sec, (long)tv.tv_usec, - CC->cs_pid, buf); + localtime_r(&unixtime, &tim); + if (CC->cs_pid != 0) { + sprintf(buf, + "%04d/%02d/%02d %2d:%02d:%02d.%06ld [%3d] ", + tim.tm_year + 1900, tim.tm_mon + 1, + tim.tm_mday, tim.tm_hour, tim.tm_min, + tim.tm_sec, (long)tv.tv_usec, + CC->cs_pid); } else { -#if 0 - /* Millisecond display */ - fprintf(stderr, - "%04d/%02d/%02d %2d:%02d:%02d.%03ld %s", - tim->tm_year + 1900, tim->tm_mon + 1, - tim->tm_mday, tim->tm_hour, tim->tm_min, - tim->tm_sec, (long)tv.tv_usec / 1000, buf); -#endif - /* Microsecond display */ - fprintf(stderr, - "%04d/%02d/%02d %2d:%02d:%02d.%06ld %s", - tim->tm_year + 1900, tim->tm_mon + 1, - tim->tm_mday, tim->tm_hour, tim->tm_min, - tim->tm_sec, (long)tv.tv_usec, buf); + sprintf(buf, + "%04d/%02d/%02d %2d:%02d:%02d.%06ld ", + tim.tm_year + 1900, tim.tm_mon + 1, + tim.tm_mday, tim.tm_hour, tim.tm_min, + tim.tm_sec, (long)tv.tv_usec); } + vsprintf(buf2, format, arg_ptr); + + fprintf(stderr, "%s%s", buf, buf2); fflush(stderr); } - - PerformLogHooks(loglevel, buf); } /* - * We used to use master_cleanup() as a signal handler to shut down the server. - * however, master_cleanup() and the functions it calls do some things that - * aren't such a good idea to do from a signal handler: acquiring mutexes, - * playing with signal masks on BSDI systems, etc. so instead we install the - * following signal handler to set a global variable to inform the main loop - * that it's time to call master_cleanup() and exit. + * Signal handler to shut down the server. */ volatile int time_to_die = 0; +volatile int shutdown_and_halt = 0; +volatile int restart_server = 0; +volatile int running_as_daemon = 0; static RETSIGTYPE signal_cleanup(int signum) { + lprintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); time_to_die = 1; + master_cleanup(signum); +} + + + + +void InitialiseSemaphores(void) +{ + int i; + + /* Set up a bunch of semaphores to be used for critical sections */ + for (i=0; istate = CON_EXECUTING; - /* * Generate a unique session number and insert this context into * the list. */ begin_critical_section(S_SESSION_TABLE); - - if (ContextList == NULL) { - ContextList = me; - me->cs_pid = 1; - me->next = NULL; - } - - else if (ContextList->cs_pid > 1) { - me->next = ContextList; - ContextList = me; - me->cs_pid = 1; - } - - else { - for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { - if (ptr->next == NULL) { - ptr->next = me; - me->cs_pid = ptr->cs_pid + 1; - me->next = NULL; - goto DONE; - } - else if (ptr->next->cs_pid > (ptr->cs_pid+1)) { - me->next = ptr->next; - ptr->next = me; - me->cs_pid = ptr->cs_pid + 1; - goto DONE; - } - } + me->cs_pid = ++next_pid; + me->prev = NULL; + me->next = ContextList; + ContextList = me; + if (me->next != NULL) { + me->next->prev = me; } - -DONE: ++num_sessions; + ++num_sessions; end_critical_section(S_SESSION_TABLE); return(me); } /* - * buffer_output() ... tell client_write to buffer all output until - * instructed to dump it all out later + * The following functions implement output buffering. If the kernel supplies + * native TCP buffering (Linux & *BSD), use that; otherwise, emulate it with + * user-space buffering. */ +#ifndef HAVE_DARWIN +#ifdef TCP_CORK +# define HAVE_TCP_BUFFERING +#else +# ifdef TCP_NOPUSH +# define HAVE_TCP_BUFFERING +# define TCP_CORK TCP_NOPUSH +# endif +#endif /* TCP_CORK */ +#endif /* HAVE_DARWIN */ + +#ifdef HAVE_TCP_BUFFERING +static unsigned on = 1, off = 0; +void buffer_output(void) { + struct CitContext *ctx = MyContext(); + setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4); + ctx->buffering = 1; +} + +void unbuffer_output(void) { + struct CitContext *ctx = MyContext(); + setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4); + ctx->buffering = 0; +} + +void flush_output(void) { + struct CitContext *ctx = MyContext(); + setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4); + setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4); +} +#else +#ifdef HAVE_DARWIN +/* Stub functions for Darwin/OS X where TCP buffering isn't liked at all */ +void buffer_output(void) { + CC->buffering = 0; +} +void unbuffer_output(void) { + CC->buffering = 0; +} +void flush_output(void) { +} +#else void buffer_output(void) { if (CC->buffering == 0) { CC->buffering = 1; @@ -469,9 +557,6 @@ void buffer_output(void) { } } -/* - * flush_output() ... dump out all that output we've been buffering. - */ void flush_output(void) { if (CC->buffering == 1) { client_write(CC->output_buffer, CC->buffer_len); @@ -479,9 +564,6 @@ void flush_output(void) { } } -/* - * unbuffer_output() ... stop buffering output. - */ void unbuffer_output(void) { if (CC->buffering == 1) { CC->buffering = 0; @@ -492,6 +574,8 @@ void unbuffer_output(void) { CC->output_buffer = NULL; } } +#endif /* HAVE_DARWIN */ +#endif /* HAVE_TCP_BUFFERING */ @@ -502,46 +586,55 @@ void client_write(char *buf, int nbytes) { int bytes_written = 0; int retval; - int sock; +#ifndef HAVE_TCP_BUFFERING int old_buffer_len = 0; - - if (CC->redirect_fp != NULL) { - fwrite(buf, nbytes, 1, CC->redirect_fp); +#endif + t_context *Ctx; + + Ctx = CC; + if (Ctx->redirect_buffer != NULL) { + if ((Ctx->redirect_len + nbytes + 2) >= Ctx->redirect_alloc) { + Ctx->redirect_alloc = (Ctx->redirect_alloc * 2) + nbytes; + Ctx->redirect_buffer = realloc(Ctx->redirect_buffer, + Ctx->redirect_alloc); + } + memcpy(&Ctx->redirect_buffer[Ctx->redirect_len], buf, nbytes); + Ctx->redirect_len += nbytes; + Ctx->redirect_buffer[Ctx->redirect_len] = 0; return; } - if (CC->redirect_sock > 0) { - sock = CC->redirect_sock; /* and continue below... */ - } - else { - sock = CC->client_socket; - } - +#ifndef HAVE_TCP_BUFFERING /* If we're buffering for later, do that now. */ - if (CC->buffering) { - old_buffer_len = CC->buffer_len; - CC->buffer_len += nbytes; - CC->output_buffer = realloc(CC->output_buffer, CC->buffer_len); - memcpy(&CC->output_buffer[old_buffer_len], buf, nbytes); + if (Ctx->buffering) { + old_buffer_len = Ctx->buffer_len; + Ctx->buffer_len += nbytes; + Ctx->output_buffer = realloc(Ctx->output_buffer, Ctx->buffer_len); + memcpy(&Ctx->output_buffer[old_buffer_len], buf, nbytes); return; } +#endif /* Ok, at this point we're not buffering. Go ahead and write. */ #ifdef HAVE_OPENSSL - if (CC->redirect_ssl) { + if (Ctx->redirect_ssl) { client_write_ssl(buf, nbytes); return; } #endif while (bytes_written < nbytes) { - retval = write(sock, &buf[bytes_written], + retval = write(Ctx->client_socket, &buf[bytes_written], nbytes - bytes_written); if (retval < 1) { - lprintf(CTDL_ERR, "client_write() failed: %s\n", - strerror(errno)); - if (sock == CC->client_socket) CC->kill_me = 1; + lprintf(CTDL_ERR, + "client_write(%d bytes) failed: %s (%d)\n", + nbytes - bytes_written, + strerror(errno), errno); + cit_backtrace(); + // lprintf(CTDL_DEBUG, "Tried to send: %s", &buf[bytes_written]); + Ctx->kill_me = 1; return; } bytes_written = bytes_written + retval; @@ -556,7 +649,7 @@ void client_write(char *buf, int nbytes) */ void cprintf(const char *format, ...) { va_list arg_ptr; - char buf[SIZ]; + char buf[1024]; va_start(arg_ptr, format); if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1) @@ -578,6 +671,7 @@ int client_read_to(char *buf, int bytes, int timeout) { int len,rlen; fd_set rfds; + int fd; struct timeval tv; int retval; @@ -587,23 +681,23 @@ int client_read_to(char *buf, int bytes, int timeout) } #endif len = 0; + fd = CC->client_socket; while(lenclient_socket, &rfds); + FD_SET(fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; - retval = select( (CC->client_socket)+1, - &rfds, NULL, NULL, &tv); + retval = select( (fd)+1, + &rfds, NULL, NULL, &tv); - if (FD_ISSET(CC->client_socket, &rfds) == 0) { + if (FD_ISSET(fd, &rfds) == 0) { return(0); } - rlen = read(CC->client_socket, &buf[len], bytes-len); + rlen = read(fd, &buf[len], bytes-len); if (rlen<1) { - lprintf(CTDL_ERR, "client_read() failed: %s\n", - strerror(errno)); + /* The socket has been disconnected! */ CC->kill_me = 1; return(-1); } @@ -624,11 +718,11 @@ INLINE int client_read(char *buf, int bytes) /* - * client_gets() ... Get a LF-terminated line of text from the client. + * client_getln() ... Get a LF-terminated line of text from the client. * (This is implemented in terms of client_read() and could be * justifiably moved out of sysdep.c) */ -int client_gets(char *buf) +int client_getln(char *buf, int bufsize) { int i, retval; @@ -636,22 +730,26 @@ int client_gets(char *buf) */ for (i = 0;;i++) { retval = client_read(&buf[i], 1); - if (retval != 1 || buf[i] == '\n' || i == (SIZ-1)) + if (retval != 1 || buf[i] == '\n' || i == (bufsize-1)) break; } /* If we got a long line, discard characters until the newline. */ - if (i == (SIZ-1)) + if (i == (bufsize-1)) while (buf[i] != '\n' && retval == 1) retval = client_read(&buf[i], 1); - /* Strip the trailing newline and any trailing nonprintables (cr's) + /* Strip the trailing LF, and the trailing CR if present. */ buf[i] = 0; - while ((strlen(buf)>0)&&(!isprint(buf[strlen(buf)-1]))) - buf[strlen(buf)-1] = 0; - if (retval < 0) strcpy(buf, "000"); + while ( (i > 0) + && ( (buf[i - 1]==13) + || ( buf[i - 1]==10)) ) { + i--; + buf[i] = 0; + } + if (retval < 0) safestrncpy(&buf[i], "000", bufsize - i); return(retval); } @@ -663,6 +761,7 @@ int client_gets(char *buf) void sysdep_master_cleanup(void) { struct ServiceFunctionHook *serviceptr; +///// DestroyWorkerList(); /* * close all protocol master sockets */ @@ -684,9 +783,28 @@ void sysdep_master_cleanup(void) { unlink(serviceptr->sockpath); } } +#ifdef HAVE_OPENSSL + destruct_ssl(); +#endif + serv_calendar_destroy(); + CtdlDestroyProtoHooks(); + CtdlDestroyDeleteHooks(); + CtdlDestroyXmsgHooks(); + CtdlDestroyNetprocHooks(); + CtdlDestroyUserHooks(); + CtdlDestroyMessageHook(); + CtdlDestroyCleanupHooks(); + CtdlDestroyFixedOutputHooks(); + CtdlDestroySessionHooks(); + CtdlDestroyServiceHook(); + #ifdef HAVE_BACKTRACE + eCrash_Uninit(); + #endif } + + /* * Terminate another session. * (This could justifiably be moved out of sysdep.c because it @@ -704,22 +822,103 @@ void kill_session(int session_to_kill) { end_critical_section(S_SESSION_TABLE); } - +pid_t current_child; +void graceful_shutdown(int signum) { + kill(current_child, signum); + unlink(file_pid_file); + exit(0); +} /* - * Start running as a daemon. Only close stdio if do_close_stdio is set. + * Start running as a daemon. */ -void start_daemon(int do_close_stdio) { - if (do_close_stdio) { - /* close(0); */ - close(1); - close(2); +void start_daemon(int unused) { + int status = 0; + pid_t child = 0; + FILE *fp; + int do_restart = 0; + + current_child = 0; + + /* Close stdin/stdout/stderr and replace them with /dev/null. + * We don't just call close() because we don't want these fd's + * to be reused for other files. + */ + chdir(ctdl_run_dir); + + child = fork(); + if (child != 0) { + exit(0); } - signal(SIGHUP,SIG_IGN); - signal(SIGINT,SIG_IGN); - signal(SIGQUIT,SIG_IGN); - if (fork()!=0) exit(0); + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + setsid(); + umask(0); + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + + do { + current_child = fork(); + + signal(SIGTERM, graceful_shutdown); + + if (current_child < 0) { + perror("fork"); + exit(errno); + } + + else if (current_child == 0) { + return; /* continue starting citadel. */ + } + + else { + fp = fopen(file_pid_file, "w"); + if (fp != NULL) { + /* + * NB.. The pid file contains the pid of the actual server. + * This is not the pid of the watcher process + */ + fprintf(fp, ""F_PID_T"\n", current_child); + fclose(fp); + } + waitpid(current_child, &status, 0); + } + + do_restart = 0; + + /* Did the main process exit with an actual exit code? */ + if (WIFEXITED(status)) { + + /* Exit code 0 means the watcher should exit */ + if (WEXITSTATUS(status) == 0) { + do_restart = 0; + } + + /* Exit code 101-109 means the watcher should exit */ + else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) { + do_restart = 0; + } + + /* Any other exit code means we should restart. */ + else { + do_restart = 1; + } + } + + /* Any other type of termination (signals, etc.) should also restart. */ + else { + do_restart = 1; + } + + } while (do_restart); + + unlink(file_pid_file); + exit(WEXITSTATUS(status)); } @@ -770,11 +969,15 @@ void create_worker(void) { return; } - /* we seem to need something bigger than FreeBSD's default 64k stack */ - - if ((ret = pthread_attr_setstacksize(&attr, 128 * 1024))) { - lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", strerror(ret)); + /* Our per-thread stacks need to be bigger than the default size, + * otherwise the MIME parser crashes on FreeBSD, and the IMAP service + * crashes on 64-bit Linux. + */ + if ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) { + lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", + strerror(ret)); time_to_die = -1; + pthread_attr_destroy(&attr); return; } @@ -787,6 +990,83 @@ void create_worker(void) { n->next = worker_list; worker_list = n; + pthread_attr_destroy(&attr); +} + +void DestroyWorkerList(void) +{ + struct CitContext *ptr; /* general-purpose utility pointer */ + struct CitContext *rem = NULL; /* list of sessions to be destroyed */ + + begin_critical_section(S_SESSION_TABLE); + ptr = ContextList; + while (ptr != NULL){ + /* Remove the session from the active list */ + rem = ptr->next; + --num_sessions; + + lprintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); + end_critical_section(S_SESSION_TABLE); + RemoveContext(ptr); + begin_critical_section(S_SESSION_TABLE); + free (ptr); + ptr = rem; + } + end_critical_section(S_SESSION_TABLE); + + struct worker_node *cur, *p; + cur = worker_list; + while (cur != NULL) + { + p = cur->next; + free (cur); + cur = p; + } + worker_list = NULL; +} + +/* + * Create the maintenance threads and begin their operation. + */ +void create_maintenance_threads(void) { + int ret; + pthread_attr_t attr; + + if ((ret = pthread_attr_init(&attr))) { + lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret)); + time_to_die = -1; + return; + } + + /* Our per-thread stacks need to be bigger than the default size, + * otherwise the MIME parser crashes on FreeBSD, and the IMAP service + * crashes on 64-bit Linux. + */ + if ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) { + lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", + strerror(ret)); + time_to_die = -1; + pthread_attr_destroy(&attr); + return; + } + + struct MaintenanceThreadHook *fcn; + + lprintf(CTDL_DEBUG, "Performing startup of maintenance thread hooks\n"); + + for (fcn = MaintenanceThreadHookTable; fcn != NULL; fcn = fcn->next) { + if ((ret = pthread_create(&(fcn->MaintenanceThread_tid), &attr, fcn->fcn_ptr, NULL) != 0)) { + lprintf(CTDL_ALERT, "Can't create thread: %s\n", strerror(ret)); + } + else + { + lprintf(CTDL_NOTICE, "Spawned a new maintenance thread \"%s\" (%ld). \n", fcn->name, + fcn->MaintenanceThread_tid); + } + } + + + pthread_attr_destroy(&attr); } @@ -803,7 +1083,8 @@ void create_worker(void) { * if such an action is appropriate. */ void dead_session_purge(int force) { - struct CitContext *ptr, *rem; + struct CitContext *ptr; /* general-purpose utility pointer */ + struct CitContext *rem = NULL; /* list of sessions to be destroyed */ if (force == 0) { if ( (time(NULL) - last_purge) < 5 ) { @@ -812,28 +1093,44 @@ void dead_session_purge(int force) { } time(&last_purge); - do { - rem = NULL; - begin_critical_section(S_SESSION_TABLE); - for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { - if ( (ptr->state == CON_IDLE) && (ptr->kill_me) ) { - rem = ptr; + begin_critical_section(S_SESSION_TABLE); + for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { + if ( (ptr->state == CON_IDLE) && (ptr->kill_me) ) { + + /* Remove the session from the active list */ + if (ptr->prev) { + ptr->prev->next = ptr->next; } - } - end_critical_section(S_SESSION_TABLE); + else { + ContextList = ptr->next; + } + if (ptr->next) { + ptr->next->prev = ptr->prev; + } + + --num_sessions; + + /* And put it on our to-be-destroyed list */ + ptr->next = rem; + rem = ptr; - /* RemoveContext() enters its own S_SESSION_TABLE critical - * section, so we have to do it like this. - */ - if (rem != NULL) { - lprintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); - RemoveContext(rem); } + } + end_critical_section(S_SESSION_TABLE); - } while (rem != NULL); + /* Now that we no longer have the session list locked, we can take + * our time and destroy any sessions on the to-be-killed list, which + * is allocated privately on this thread's stack. + */ + while (rem != NULL) { + lprintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); + RemoveContext(rem); + ptr = rem; + rem = rem->next; + free(ptr); + } /* Raise the size of the worker thread pool if necessary. */ - if ( (num_sessions > num_threads) && (num_threads < config.c_max_workers) ) { begin_critical_section(S_WORKER_LIST); @@ -846,22 +1143,6 @@ void dead_session_purge(int force) { -/* - * Redirect a session's output to a file or socket. - * This function may be called with a file handle *or* a socket (but not - * both). Call with neither to return output to its normal client socket. - */ -void CtdlRedirectOutput(FILE *fp, int sock) { - - if (fp != NULL) CC->redirect_fp = fp; - else CC->redirect_fp = NULL; - - if (sock > 0) CC->redirect_sock = sock; - else CC->redirect_sock = (-1); - -} - - /* * masterCC is the context we use when not attached to a session. This * function initializes it. @@ -895,7 +1176,7 @@ void *worker_thread(void *arg) { struct CitContext *ptr; struct CitContext *bind_me = NULL; fd_set readfds; - int retval; + int retval = 0; struct CitContext *con= NULL; /* Temporary context pointer */ struct ServiceFunctionHook *serviceptr; int ssock; /* Descriptor for client socket */ @@ -907,6 +1188,10 @@ void *worker_thread(void *arg) { cdb_allocate_tsd(); + // Register for tracing + #ifdef HAVE_BACKTRACE + eCrash_RegisterThread("WorkerThread", 0); + #endif while (!time_to_die) { /* make doubly sure we're not holding any stale db handles @@ -934,7 +1219,9 @@ do_select: force_purge = 0; } end_critical_section(S_SESSION_TABLE); - if (bind_me) goto SKIP_SELECT; + if (bind_me) { + goto SKIP_SELECT; + } /* If we got this far, it means that there are no sessions * which a previous thread marked for attention, so we go @@ -956,9 +1243,8 @@ do_select: force_purge = 0; tv.tv_usec = 0; retval = select(highest + 1, &readfds, NULL, NULL, &tv); } - else { - break; - } + + if (time_to_die) return(NULL); /* Now figure out who made this select() unblock. * First, check for an error or exit condition. @@ -984,29 +1270,36 @@ do_select: force_purge = 0; if (FD_ISSET(serviceptr->msock, &readfds)) { ssock = accept(serviceptr->msock, NULL, 0); - if (ssock < 0) { - lprintf(CTDL_CRIT, - "citserver: accept(): %s\n", - strerror(errno)); - } - else { + if (ssock >= 0) { lprintf(CTDL_DEBUG, "New client socket %d\n", ssock); + /* The master socket is non-blocking but the client + * sockets need to be blocking, otherwise certain + * operations barf on FreeBSD. Not a fatal error. + */ + if (fcntl(ssock, F_SETFL, 0) < 0) { + lprintf(CTDL_EMERG, + "citserver: Can't set socket to blocking: %s\n", + strerror(errno)); + } + /* New context will be created already - * set up in the CON_EXECUTING state. - */ + * set up in the CON_EXECUTING state. + */ con = CreateNewContext(); - /* Assign new socket number to it. */ + /* Assign our new socket number to it. */ con->client_socket = ssock; con->h_command_function = serviceptr->h_command_function; con->h_async_function = serviceptr->h_async_function; - - /* Determine whether local socket */ + con->ServiceName = + serviceptr->ServiceName; + + /* Determine whether it's a local socket */ if (serviceptr->sockpath != NULL) con->is_local_socket = 1; @@ -1026,10 +1319,6 @@ do_select: force_purge = 0; } } - if (time_to_die) { - break; - } - /* It must be a client socket. Find a context that has data * waiting on its socket *and* is in the CON_IDLE state. Any * active sockets other than our chosen one are marked as @@ -1080,10 +1369,12 @@ SKIP_SELECT: do_housekeeping(); check_sched_shutdown(); } - + if (con != NULL) free (con);//// TODO: could this harm other threads? /* If control reaches this point, the server is shutting down */ - --num_threads; - return NULL; + #ifdef HAVE_BACKTRACE + eCrash_UnregisterThread(); + #endif + return(NULL); } @@ -1125,7 +1416,8 @@ int SyslogFacility(char *name) if(!strcasecmp(name, facTbl[i].name)) return facTbl[i].facility; } - return -1; + enable_syslog = 0; + return LOG_DAEMON; }