X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fsysdep.c;h=1413675ea9d07b4bf46745a7249763c7c2eb3fb6;hb=a2a06e5d6767d0e45eeccf4032153764e8a5520f;hp=1380be84eb9a3dc89ef9559a7c25b27fac87dacd;hpb=3abe95abb2d57c31287df5f648fa105873b51e39;p=citadel.git diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 1380be84e..1413675ea 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 @@ -57,7 +54,6 @@ #endif #include "citadel.h" #include "server.h" -#include "serv_extensions.h" #include "sysdep_decls.h" #include "citserver.h" #include "support.h" @@ -65,7 +61,7 @@ #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 */ #ifdef HAVE_SYS_SELECT_H #include @@ -98,9 +94,10 @@ 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 */ -int syslog_facility = (-1); +int syslog_facility = LOG_DAEMON; int enable_syslog = 0; -extern int running_as_daemon; + +void DestroyWorkerList(void); /* * lprintf() ... Write logging information @@ -110,7 +107,7 @@ void lprintf(enum LogLevel loglevel, const char *format, ...) { if (enable_syslog) { va_start(arg_ptr, format); - vsyslog(loglevel, format, arg_ptr); + vsyslog((syslog_facility | loglevel), format, arg_ptr); va_end(arg_ptr); } @@ -155,6 +152,9 @@ void lprintf(enum LogLevel loglevel, const char *format, ...) { */ 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); @@ -162,7 +162,6 @@ static RETSIGTYPE signal_cleanup(int signum) { master_cleanup(signum); } - /* * Some initialization stuff... */ @@ -248,6 +247,7 @@ void begin_critical_section(int which_one) #ifdef DEBUG_MEMORY_LEAKS && (which_one != S_DEBUGMEMLEAKS) #endif + && (which_one != S_RPLIST) ) { cdb_check_handles(); } @@ -269,7 +269,7 @@ void end_critical_section(int which_one) * a TCP port. The server shuts down if the bind fails. * */ -int ig_tcp_server(char *ip_addr, int port_number, int queue_len) +int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormessage) { struct sockaddr_in sin; int s, i; @@ -288,15 +288,18 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len) sin.sin_addr.s_addr = inet_addr(ip_addr); } - if (sin.sin_addr.s_addr == INADDR_NONE) { + if (sin.sin_addr.s_addr == !INADDR_ANY) { sin.sin_addr.s_addr = INADDR_ANY; } s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { - lprintf(CTDL_EMERG, "citserver: Can't create a socket: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't create a socket: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -304,24 +307,32 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - lprintf(CTDL_EMERG, "citserver: Can't bind: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't bind: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } /* set to nonblock - we need this for some obscure situations */ if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { - lprintf(CTDL_EMERG, - "citserver: Can't set socket to non-blocking: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't set socket to non-blocking: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } if (listen(s, actual_queue_len) < 0) { - lprintf(CTDL_EMERG, "citserver: Can't listen: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't listen: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } @@ -334,7 +345,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len) /* * Create a Unix domain socket and listen on it */ -int ig_uds_server(char *sockpath, int queue_len) +int ig_uds_server(char *sockpath, int queue_len, char **errormessage) { struct sockaddr_un addr; int s; @@ -346,8 +357,10 @@ int ig_uds_server(char *sockpath, int queue_len) i = unlink(sockpath); if (i != 0) if (errno != ENOENT) { - lprintf(CTDL_EMERG, "citserver: can't unlink %s: %s\n", + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, "citserver: can't unlink %s: %s", sockpath, strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -357,29 +370,40 @@ int ig_uds_server(char *sockpath, int queue_len) s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { - lprintf(CTDL_EMERG, "citserver: Can't create a socket: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't create a socket: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - lprintf(CTDL_EMERG, "citserver: Can't bind: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't bind: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } /* set to nonblock - we need this for some obscure situations */ if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { - lprintf(CTDL_EMERG, - "citserver: Can't set socket to non-blocking: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't set socket to non-blocking: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } if (listen(s, actual_queue_len) < 0) { - lprintf(CTDL_EMERG, "citserver: Can't listen: %s\n", - strerror(errno)); + *errormessage = (char*) malloc(SIZ + 1); + snprintf(*errormessage, SIZ, + "citserver: Can't listen: %s", + strerror(errno)); + lprintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -410,8 +434,7 @@ struct CitContext *MyContext(void) { * Initialize a new context and place it in the list. The session number * used to be the PID (which is why it's called cs_pid), but that was when we * had one process per session. Now we just assign them sequentially, starting - * at 1 (don't change it to 0 because masterCC uses 0) and re-using them when - * sessions terminate. + * at 1 (don't change it to 0 because masterCC uses 0). */ struct CitContext *CreateNewContext(void) { struct CitContext *me; @@ -453,6 +476,7 @@ struct CitContext *CreateNewContext(void) { * 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 @@ -460,8 +484,8 @@ struct CitContext *CreateNewContext(void) { # define HAVE_TCP_BUFFERING # define TCP_CORK TCP_NOPUSH # endif -#endif - +#endif /* TCP_CORK */ +#endif /* HAVE_DARWIN */ #ifdef HAVE_TCP_BUFFERING static unsigned on = 1, off = 0; @@ -482,6 +506,16 @@ void flush_output(void) { setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4); setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4); } +#elif 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) { @@ -535,11 +569,6 @@ void client_write(char *buf, int nbytes) return; } - if (CC->redirect_fp != NULL) { - fwrite(buf, (size_t)nbytes, (size_t)1, CC->redirect_fp); - return; - } - #ifndef HAVE_TCP_BUFFERING /* If we're buffering for later, do that now. */ if (CC->buffering) { @@ -564,8 +593,10 @@ void client_write(char *buf, int nbytes) retval = write(CC->client_socket, &buf[bytes_written], nbytes - bytes_written); if (retval < 1) { - lprintf(CTDL_ERR, "client_write() failed: %s\n", - strerror(errno)); + lprintf(CTDL_ERR, + "client_write(%d bytes) failed: %s (%d)\n", + nbytes - bytes_written, + strerror(errno), errno); CC->kill_me = 1; return; } @@ -581,7 +612,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) @@ -627,8 +658,7 @@ int client_read_to(char *buf, int bytes, int timeout) rlen = read(CC->client_socket, &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); } @@ -671,12 +701,13 @@ int client_getln(char *buf, int bufsize) 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]))) + while ( (!IsEmptyStr(buf)) && ((buf[strlen(buf)-1]==10) || (buf[strlen(buf)-1] == 13)) ) { buf[strlen(buf)-1] = 0; - if (retval < 0) strcpy(buf, "000"); + } + if (retval < 0) safestrncpy(buf, "000", bufsize); return(retval); } @@ -688,6 +719,7 @@ int client_getln(char *buf, int bufsize) void sysdep_master_cleanup(void) { struct ServiceFunctionHook *serviceptr; +///// DestroyWorkerList(); /* * close all protocol master sockets */ @@ -709,9 +741,25 @@ 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(); } + + /* * Terminate another session. * (This could justifiably be moved out of sysdep.c because it @@ -729,19 +777,99 @@ 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. */ void start_daemon(int unused) { - close(0); close(1); close(2); - if (fork()) exit(0); + 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); + setsid(); - signal(SIGHUP,SIG_IGN); - signal(SIGINT,SIG_IGN); - signal(SIGQUIT,SIG_IGN); + 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) { + fprintf(fp, ""F_PID_T"\n", 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)); } @@ -792,12 +920,13 @@ void create_worker(void) { 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. + /* 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, 1024 * 1024))) { - lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", strerror(ret)); + 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; @@ -815,6 +944,82 @@ void create_worker(void) { 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); +} + /* @@ -889,18 +1094,6 @@ void dead_session_purge(int force) { -/* - * Redirect a session's output to a file. - * This function may be called with a file handle. - * Call with NULL to return output to its normal client socket. - */ -void CtdlRedirectOutput(FILE *fp) -{ - if (fp != NULL) CC->redirect_fp = fp; - else CC->redirect_fp = NULL; -} - - /* * masterCC is the context we use when not attached to a session. This * function initializes it. @@ -1029,19 +1222,29 @@ do_select: force_purge = 0; "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 */ + /* Determine whether it's a local socket */ if (serviceptr->sockpath != NULL) con->is_local_socket = 1; @@ -1111,7 +1314,7 @@ 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 */ return(NULL); }