threads are using signals and the GC code is #ifdef if available.
[citadel.git] / citadel / sysdep.c
index 0d1b058c6dd9c64e002f4c2040c349f8002563b2..13c5c908928ba9b9948154f1c86e3de2be41a829 100644 (file)
@@ -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.