X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fsysdep.c;h=13c5c908928ba9b9948154f1c86e3de2be41a829;hb=12a091b625c28d94e895901c900f4fdbd0776f7f;hp=0d1b058c6dd9c64e002f4c2040c349f8002563b2;hpb=ec9d4d9a12991ce69b71fd48c9f88c272073ab80;p=citadel.git diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 0d1b058c6..13c5c9089 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -159,11 +159,10 @@ volatile int restart_server = 0; volatile int running_as_daemon = 0; static RETSIGTYPE signal_cleanup(int signum) { -#ifdef THREADS_USESIGNALS + if (CT) CT->signal = signum; else -#endif { CtdlLogPrintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); exit_signal = signum; @@ -480,13 +479,25 @@ int client_write(char *buf, int nbytes) FD_ZERO(&wset); FD_SET(Ctx->client_socket, &wset); if (select(1, NULL, &wset, NULL, NULL) == -1) { - CtdlLogPrintf(CTDL_ERR, - "client_write(%d bytes) select failed: %s (%d)\n", - nbytes - bytes_written, - strerror(errno), errno); - cit_backtrace(); - Ctx->kill_me = 1; - return -1; + if (errno == EINTR) + { + CtdlLogPrintf(CTDL_DEBUG, "client_write(%d bytes) select() interrupted.\n", nbytes-bytes_written); + if (CtdlThreadCheckStop()) { + CC->kill_me = 1; + return (-1); + } else { + /* can't trust fd's and stuff so we need to re-create them */ + continue; + } + } else { + CtdlLogPrintf(CTDL_ERR, + "client_write(%d bytes) select failed: %s (%d)\n", + nbytes - bytes_written, + strerror(errno), errno); + cit_backtrace(); + Ctx->kill_me = 1; + return -1; + } } } @@ -559,7 +570,17 @@ int client_read_to(char *buf, int bytes, int timeout) { if (errno == EINTR) { - CtdlLogPrintf(CTDL_DEBUG, "Interrupted select().\n"); + CtdlLogPrintf(CTDL_DEBUG, "Interrupted select() in client_read_to().\n"); + if (CtdlThreadCheckStop()) { + CC->kill_me = 1; + return (-1); + } else { + /* can't trust fd's and stuff so we need to re-create them */ + continue; + } + } + else { + CtdlLogPrintf(CTDL_DEBUG, "Failed select() in client_read_to().\n"); CC->kill_me = 1; return (-1); } @@ -866,18 +887,13 @@ int convert_login(char NameToConvert[]) { */ void *worker_thread(void *arg) { - int i; int highest; CitContext *ptr; CitContext *bind_me = NULL; fd_set readfds; int retval = 0; - CitContext *con= NULL; /* Temporary context pointer */ - struct ServiceFunctionHook *serviceptr; - int ssock; /* Descriptor for client socket */ struct timeval tv; int force_purge = 0; - int m; while (!CtdlThreadCheckStop()) { @@ -904,6 +920,11 @@ do_select: force_purge = 0; if ((bind_me == NULL) && (ptr->state == CON_READY)) { bind_me = ptr; ptr->state = CON_EXECUTING; + break; + } + if ((bind_me == NULL) && (ptr->state == CON_STARTING)) { + bind_me = ptr; + break; } } end_critical_section(S_SESSION_TABLE); @@ -917,20 +938,134 @@ do_select: force_purge = 0; * ahead and get ready to select(). */ + if (!CtdlThreadCheckStop()) { + tv.tv_sec = 1; /* wake up every second if no input */ + tv.tv_usec = 0; + retval = CtdlThreadSelect(highest + 1, &readfds, NULL, NULL, &tv); + } + else + return NULL; + + /* Now figure out who made this select() unblock. + * First, check for an error or exit condition. + */ + if (retval < 0) { + if (errno == EBADF) { + CtdlLogPrintf(CTDL_NOTICE, "select() failed: (%s)\n", + strerror(errno)); + goto do_select; + } + if (errno != EINTR) { + CtdlLogPrintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); + CtdlThreadStopAll(); + continue; + } else { + CtdlLogPrintf(CTDL_DEBUG, "Interrupted CtdlThreadSelect.\n"); + if (CtdlThreadCheckStop()) return(NULL); + goto do_select; + } + } + else if(retval == 0) { + if (CtdlThreadCheckStop()) return(NULL); + } + + /* 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 + * CON_READY so the next thread that comes around can just bind + * to one without having to select() again. + */ + begin_critical_section(S_SESSION_TABLE); + for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { + if ( (FD_ISSET(ptr->client_socket, &readfds)) + && (ptr->state != CON_EXECUTING) ) { + ptr->input_waiting = 1; + if (!bind_me) { + bind_me = ptr; /* I choose you! */ + bind_me->state = CON_EXECUTING; + } + else { + ptr->state = CON_READY; + } + } + } + end_critical_section(S_SESSION_TABLE); + +SKIP_SELECT: + /* We're bound to a session */ + if (bind_me != NULL) { + become_session(bind_me); + + if (bind_me->state == CON_STARTING) { + bind_me->state = CON_EXECUTING; + begin_session(bind_me); + bind_me->h_greeting_function(); + } + /* If the client has sent a command, execute it. */ + if (CC->input_waiting) { + CC->h_command_function(); + CC->input_waiting = 0; + } + + /* If there are asynchronous messages waiting and the + * client supports it, do those now */ + if ((CC->is_async) && (CC->async_waiting) + && (CC->h_async_function != NULL)) { + CC->h_async_function(); + CC->async_waiting = 0; + } + + force_purge = CC->kill_me; + become_session(NULL); + bind_me->state = CON_IDLE; + } + + dead_session_purge(force_purge); + do_housekeeping(); + } + /* If control reaches this point, the server is shutting down */ + return(NULL); +} + + + + +/* + * A function to handle selecting on master sockets. + * In other words it handles new connections. + * It is a thread. + */ +void *select_on_master (void *arg) +{ + struct ServiceFunctionHook *serviceptr; + fd_set master_fds; + int highest; + struct timeval tv; + int ssock; /* Descriptor for client socket */ + CitContext *con= NULL; /* Temporary context pointer */ + int m; + int i; + int retval; + + while (!CtdlThreadCheckStop()) { + /* Initialize the fdset. */ + FD_ZERO(&master_fds); + highest = 0; + /* First, add the various master sockets to the fdset. */ for (serviceptr = ServiceHookTable; serviceptr != NULL; serviceptr = serviceptr->next ) { m = serviceptr->msock; - FD_SET(m, &readfds); + FD_SET(m, &master_fds); if (m > highest) { highest = m; } } if (!CtdlThreadCheckStop()) { - tv.tv_sec = 1; /* wake up every second if no input */ + tv.tv_sec = 60; /* wake up every second if no input */ tv.tv_usec = 0; - retval = CtdlThreadSelect(highest + 1, &readfds, NULL, NULL, &tv); + retval = CtdlThreadSelect(highest + 1, &master_fds, NULL, NULL, &tv); } else return NULL; @@ -942,7 +1077,7 @@ do_select: force_purge = 0; if (errno == EBADF) { CtdlLogPrintf(CTDL_NOTICE, "select() failed: (%s)\n", strerror(errno)); - goto do_select; + continue; } if (errno != EINTR) { CtdlLogPrintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); @@ -950,12 +1085,12 @@ do_select: force_purge = 0; } else { CtdlLogPrintf(CTDL_DEBUG, "Interrupted CtdlThreadSelect.\n"); if (CtdlThreadCheckStop()) return(NULL); - goto do_select; + continue; } } else if(retval == 0) { if (CtdlThreadCheckStop()) return(NULL); - goto SKIP_SELECT; + continue; } /* Next, check to see if it's a new client connecting * on a master socket. @@ -963,7 +1098,7 @@ do_select: force_purge = 0; else for (serviceptr = ServiceHookTable; serviceptr != NULL; serviceptr = serviceptr->next ) { - if (FD_ISSET(serviceptr->msock, &readfds)) { + if (FD_ISSET(serviceptr->msock, &master_fds)) { ssock = accept(serviceptr->msock, NULL, 0); if (ssock >= 0) { CtdlLogPrintf(CTDL_DEBUG, @@ -991,6 +1126,7 @@ do_select: force_purge = 0; serviceptr->h_command_function; con->h_async_function = serviceptr->h_async_function; + con->h_greeting_function = serviceptr->h_greeting_function; con->ServiceName = serviceptr->ServiceName; @@ -1004,75 +1140,20 @@ do_select: force_purge = 0; SO_REUSEADDR, &i, sizeof(i)); - become_session(con); - begin_session(con); - serviceptr->h_greeting_function(); - become_session(NULL); - con->state = CON_IDLE; - retval--; - if (retval) - CtdlLogPrintf (CTDL_DEBUG, "Select said more than 1 fd to handle but we only handle one\n"); - goto do_select; - } - } - } + con->state = CON_STARTING; - /* 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 - * CON_READY so the next thread that comes around can just bind - * to one without having to select() again. - */ - begin_critical_section(S_SESSION_TABLE); - for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { - if ( (FD_ISSET(ptr->client_socket, &readfds)) - && (ptr->state != CON_EXECUTING) ) { - ptr->input_waiting = 1; - if (!bind_me) { - bind_me = ptr; /* I choose you! */ - bind_me->state = CON_EXECUTING; - } - else { - ptr->state = CON_READY; + retval--; + if (retval == 0) + break; } } } - end_critical_section(S_SESSION_TABLE); - -SKIP_SELECT: - /* We're bound to a session */ - if (bind_me != NULL) { - become_session(bind_me); - - /* If the client has sent a command, execute it. */ - if (CC->input_waiting) { - CC->h_command_function(); - CC->input_waiting = 0; - } - - /* If there are asynchronous messages waiting and the - * client supports it, do those now */ - if ((CC->is_async) && (CC->async_waiting) - && (CC->h_async_function != NULL)) { - CC->h_async_function(); - CC->async_waiting = 0; - } - - force_purge = CC->kill_me; - become_session(NULL); - bind_me->state = CON_IDLE; - } - - dead_session_purge(force_purge); - do_housekeeping(); } - /* If control reaches this point, the server is shutting down */ - return(NULL); + return NULL; } - /* * SyslogFacility() * Translate text facility name to syslog.h defined value.