* move the instant death to USR2 as dave questioned
[citadel.git] / citadel / sysdep.c
index a0ba89b2c71e3144ee2c5e59279f224bc48e7011..cb5b3a650458d22013992b9e1ee374a9711cdf41 100644 (file)
@@ -169,6 +169,10 @@ static RETSIGTYPE signal_cleanup(int signum) {
        }
 }
 
+static RETSIGTYPE signal_exit(int signum) {
+       exit(1);
+}
+
 
 
 /*
@@ -222,6 +226,7 @@ void init_sysdep(void) {
        signal(SIGQUIT, signal_cleanup);
        signal(SIGHUP, signal_cleanup);
        signal(SIGTERM, signal_cleanup);
+       signal(SIGUSR2, signal_exit);
        // signal(SIGSEGV, signal_cleanup);     commented out because
        // signal(SIGILL, signal_cleanup);      we want core dumps
        // signal(SIGBUS, signal_cleanup);
@@ -436,7 +441,16 @@ void flush_output(void) {
 #endif
 }
 
+/*
+static void flush_client_inbuf(void)
+{
+       CitContext *CCC=CC;
+
+       FlushStrBuf(CCC->ReadBuf);
+       CCC->Pos = NULL;
 
+}
+*/
 
 /*
  * client_write()   ...    Send binary data to the client.
@@ -452,16 +466,13 @@ int client_write(const char *buf, int nbytes)
        CitContext *Ctx;
        int fdflags;
 
+       if (nbytes < 1) return(0);
+
+//     flush_client_inbuf();
        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;
+               StrBufAppendBufPlain(Ctx->redirect_buffer,
+                                    buf, nbytes, 0);
                return 0;
        }
 
@@ -471,10 +482,11 @@ int client_write(const char *buf, int nbytes)
                return 0;
        }
 #endif
+       if (Ctx->client_socket == -1) return -1;
 
        fdflags = fcntl(Ctx->client_socket, F_GETFL);
 
-       while (bytes_written < nbytes) {
+       while ((bytes_written < nbytes) && (Ctx->client_socket != -1)){
                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
                        FD_ZERO(&wset);
                        FD_SET(Ctx->client_socket, &wset);
@@ -541,70 +553,101 @@ void cprintf(const char *format, ...) {
 
 /*
  * Read data from the client socket.
- * Return values are:
- *     1       Requested number of bytes has been read.
- *     0       Request timed out.
- *     -1      The socket is broken.
- * If the socket breaks, the session will be terminated.
+ *
+ * sock                socket fd to read from
+ * buf         buffer to read into 
+ * bytes       number of bytes to read
+ * timeout     Number of seconds to wait before timing out
+ *
+ * Possible return values:
+ *      1       Requested number of bytes has been read.
+ *      0       Request timed out.
+ *     -1      Connection is broken, or other error.
  */
-int client_read_to(char *buf, int bytes, int timeout)
+int client_read_blob(StrBuf *Target, int bytes, int timeout)
 {
-       int len,rlen;
-       fd_set rfds;
-       int fd;
-       struct timeval tv;
-       int retval;
+       CitContext *CCC=CC;
+       const char *Error;
+       int retval = 0;
 
 #ifdef HAVE_OPENSSL
-       if (CC->redirect_ssl) {
-               return (client_read_ssl(buf, bytes, timeout));
+       if (CCC->redirect_ssl) {
+               retval = client_read_sslblob(Target, bytes, timeout);
        }
+       else 
 #endif
-       len = 0;
-       fd = CC->client_socket;
-       while(len<bytes) {
-               FD_ZERO(&rfds);
-               FD_SET(fd, &rfds);
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               retval = select( (fd)+1, 
-                                &rfds, NULL, NULL, &tv);
-               if (retval < 0)
-               {
-                       if (errno == EINTR)
-                       {
-                               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);
-                       }
-               }
 
-               if (FD_ISSET(fd, &rfds) == 0) {
-                       return(0);
-               }
+               retval = StrBufReadBLOBBuffered(Target, 
+                                               CCC->ReadBuf,
+                                               &CCC->Pos,
+                                               &CCC->client_socket,
+                                               1, 
+                                               bytes,
+                                               O_TERM,
+                                               &Error);
+       if (retval < 0) {
+               CtdlLogPrintf(CTDL_CRIT, 
+                             "%s failed: %s\n",
+                             __FUNCTION__,
+                             Error);
+       }
+       else
+       {
+#ifdef BIGBAD_IODBG
+               int rv = 0;
+               char fn [SIZ];
+               FILE *fd;
+
+               snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
+
+               fd = fopen(fn, "a+");
+                fprintf(fd, "Read: BufSize: %d BufContent: [",
+                        StrLength(Target));
+               rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
+                fprintf(fd, "]\n");
+               
+                       
+                fclose(fd);
+#endif
 
-               rlen = read(fd, &buf[len], bytes-len);
-               if (rlen<1) {
-                       /* The socket has been disconnected! */
-                       CC->kill_me = 1;
-                       return(-1);
-               }
-               len = len + rlen;
        }
-       return(1);
+       return retval == bytes;
+}
+
+int client_read_to(char *buf, int bytes, int timeout)
+{
+       CitContext *CCC=CC;
+       int rc;
+
+       rc = client_read_blob(CCC->MigrateBuf, bytes, timeout);
+       if (rc < 0)
+       {
+               *buf = '\0';
+               return rc;
+       }
+       else
+       {
+               memcpy(buf, 
+                      ChrPtr(CCC->MigrateBuf),
+                      StrLength(CCC->MigrateBuf) + 1);
+               FlushStrBuf(CCC->MigrateBuf);
+               return rc;
+       }
 }
 
+
+int HaveMoreLinesWaiting(CitContext *CCC)
+{
+       if ((CCC->kill_me == 1) || (
+           (CCC->Pos == NULL) && 
+           (StrLength(CCC->ReadBuf) == 0) && 
+           (CCC->client_socket != -1)) )
+               return 0;
+       else
+               return 1;
+}
+
+
 /*
  * Read data from the client socket with default timeout.
  * (This is implemented in terms of client_read_to() and could be
@@ -615,6 +658,127 @@ INLINE int client_read(char *buf, int bytes)
        return(client_read_to(buf, bytes, config.c_sleeping));
 }
 
+int CtdlClientGetLine(StrBuf *Target)
+{
+       CitContext *CCC=CC;
+       const char *Error;
+       int rc;
+
+       FlushStrBuf(Target);
+#ifdef HAVE_OPENSSL
+       if (CCC->redirect_ssl) {
+#ifdef BIGBAD_IODBG
+               char fn [SIZ];
+               FILE *fd;
+               int len, rlen, nlen, nrlen;
+               const char *pch;
+
+               snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
+
+               fd = fopen(fn, "a+");
+               pch = ChrPtr(CCC->ReadBuf);
+               len = StrLength(CCC->ReadBuf);
+               if (CCC->Pos != NULL)
+                       rlen = CC->Pos - pch;
+               else
+                       rlen = 0;
+
+/*             fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \nBufContent: [%s]\n\n_____________________\n",
+                       len, rlen, pch);
+*/
+               fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \n_____________________\n",
+                       len, rlen);
+#endif
+               rc = client_readline_sslbuffer(Target,
+                                              CCC->ReadBuf,
+                                              1);
+#ifdef BIGBAD_IODBG
+                pch = ChrPtr(CCC->ReadBuf);
+                nlen = StrLength(CCC->ReadBuf);
+                if (CCC->Pos != NULL)
+                        nrlen = CC->Pos - pch;
+                else
+                        nrlen = 0;
+/*
+                fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \nBufContent: [%s]\n\n_____________________\n",
+                        len, nlen, rlen, nrlen, pch);
+*/
+                fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \n",
+                        len, nlen, rlen, nrlen);
+
+                fprintf(fd, "Read: BufSize: %d BufContent: [%s]\n\n*************\n",
+                        StrLength(Target), ChrPtr(Target));
+                fclose(fd);
+
+               if ((rc < 0) && (Error != NULL))
+                       CtdlLogPrintf(CTDL_CRIT, 
+                                     "%s failed: %s\n",
+                                     __FUNCTION__,
+                                     Error);
+#endif
+               return rc;
+       }
+       else 
+#endif
+       {
+#ifdef BIGBAD_IODBG
+               char fn [SIZ];
+               FILE *fd;
+               int len, rlen, nlen, nrlen;
+               const char *pch;
+
+               snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
+
+               fd = fopen(fn, "a+");
+               pch = ChrPtr(CCC->ReadBuf);
+               len = StrLength(CCC->ReadBuf);
+               if (CCC->Pos != NULL)
+                       rlen = CC->Pos - pch;
+               else
+                       rlen = 0;
+
+/*             fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \nBufContent: [%s]\n\n_____________________\n",
+                       len, rlen, pch);
+*/
+               fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \n_____________________\n",
+                       len, rlen);
+#endif
+               rc = StrBufTCP_read_buffered_line_fast(Target, 
+                                                      CCC->ReadBuf,
+                                                      &CCC->Pos,
+                                                      &CCC->client_socket,
+                                                      5,
+                                                      1,
+                                                      &Error);
+
+#ifdef BIGBAD_IODBG
+                pch = ChrPtr(CCC->ReadBuf);
+                nlen = StrLength(CCC->ReadBuf);
+                if (CCC->Pos != NULL)
+                        nrlen = CC->Pos - pch;
+                else
+                        nrlen = 0;
+/*
+                fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \nBufContent: [%s]\n\n_____________________\n",
+                        len, nlen, rlen, nrlen, pch);
+*/
+                fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \n",
+                        len, nlen, rlen, nrlen);
+
+                fprintf(fd, "Read: BufSize: %d BufContent: [%s]\n\n*************\n",
+                        StrLength(Target), ChrPtr(Target));
+                fclose(fd);
+
+               if ((rc < 0) && (Error != NULL))
+                       CtdlLogPrintf(CTDL_CRIT, 
+                                     "%s failed: %s\n",
+                                     __FUNCTION__,
+                                     Error);
+#endif
+               return rc;
+       }
+}
+
 
 /*
  * client_getln()   ...   Get a LF-terminated line of text from the client.
@@ -624,32 +788,33 @@ INLINE int client_read(char *buf, int bytes)
 int client_getln(char *buf, int bufsize)
 {
        int i, retval;
+       CitContext *CCC=CC;
+       const char *pCh;
 
-       /* Read one character at a time.
-        */
-       for (i = 0;;i++) {
-               retval = client_read(&buf[i], 1);
-               if (retval != 1 || buf[i] == '\n' || i == (bufsize-1))
-                       break;
-       }
+       retval = CtdlClientGetLine(CCC->MigrateBuf);
+       if (retval < 0)
+         return(retval >= 0);
 
-       /* If we got a long line, discard characters until the newline.
-        */
-       if (i == (bufsize-1))
-               while (buf[i] != '\n' && retval == 1)
-                       retval = client_read(&buf[i], 1);
 
+       i = StrLength(CCC->MigrateBuf);
+       pCh = ChrPtr(CCC->MigrateBuf);
        /* Strip the trailing LF, and the trailing CR if present.
         */
-       buf[i] = 0;
+       if (bufsize <= i)
+               i = bufsize - 1;
        while ( (i > 0)
-               && ( (buf[i - 1]==13)
-                    || ( buf[i - 1]==10)) ) {
+               && ( (pCh[i - 1]==13)
+                    || ( pCh[i - 1]==10)) ) {
                i--;
-               buf[i] = 0;
        }
-       if (retval < 0) safestrncpy(&buf[i], "000", bufsize - i);
-       return(retval);
+       memcpy(buf, pCh, i);
+       buf[i] = 0;
+
+       FlushStrBuf(CCC->MigrateBuf);
+       if (retval < 0) {
+               safestrncpy(&buf[i], "000", bufsize - i);
+       }
+       return(retval >= 0);
 }
 
 
@@ -713,7 +878,7 @@ void sysdep_master_cleanup(void) {
        CtdlDestroyServiceHook();
        CtdlDestroyRoomHooks();
        #ifdef HAVE_BACKTRACE
-       eCrash_Uninit();
+///    eCrash_Uninit();
        #endif
 }
 
@@ -745,7 +910,10 @@ void start_daemon(int unused) {
         * We don't just call close() because we don't want these fd's
         * to be reused for other files.
         */
-       chdir(ctdl_run_dir);
+       if (chdir(ctdl_run_dir) != 0)
+               CtdlLogPrintf(CTDL_EMERG, 
+                             "unable to change into directory [%s]: %s", 
+                             ctdl_run_dir, strerror(errno));
 
        child = fork();
        if (child != 0) {
@@ -758,9 +926,13 @@ void start_daemon(int unused) {
 
        setsid();
        umask(0);
-        freopen("/dev/null", "r", stdin);
-        freopen("/dev/null", "w", stdout);
-        freopen("/dev/null", "w", stderr);
+        if ((freopen("/dev/null", "r", stdin) != stdin) || 
+           (freopen("/dev/null", "w", stdout) != stdout) || 
+           (freopen("/dev/null", "w", stderr) != stderr))
+               CtdlLogPrintf(CTDL_EMERG, 
+                             "unable to reopen stdin/out/err %s", 
+                             strerror(errno));
+               
 
        do {
                current_child = fork();
@@ -830,7 +1002,6 @@ void checkcrash(void)
                        " For more information, please see:\n \n"
                        " http://citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files"
                        "\n \n"
-"http://www.4players.de/4players.php/walkthrough/Wii/9298/24733/Die_Chroniken_von_Narnia_Prinz_Kaspian_von_Narnia.html"
 
                        " If you have already done this, the core dump is likely to be found at %score.%d\n"
                        ,
@@ -917,19 +1088,25 @@ do_select:       force_purge = 0;
 
                begin_critical_section(S_SESSION_TABLE);
                for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
+                       int client_socket;
+                       client_socket = ptr->client_socket;
                        /* Dont select on dead sessions only truly idle ones */
-                       if ((ptr->state == CON_IDLE)) {
-                               FD_SET(ptr->client_socket, &readfds);
-                               if (ptr->client_socket > highest)
-                                       highest = ptr->client_socket;
+                       if ((ptr->state == CON_IDLE) && 
+                           (CC->kill_me == 0) &&
+                           (client_socket != -1))
+                       {
+                               FD_SET(client_socket, &readfds);
+                               if (client_socket > highest)
+                                       highest = client_socket;
                        }
                        if ((bind_me == NULL) && (ptr->state == CON_READY)) {
                                bind_me = ptr;
                                ptr->state = CON_EXECUTING;
                                break;
                        }
-                       if ((bind_me == NULL) && (ptr->state == CON_STARTING)) {
+                       if ((bind_me == NULL) && (ptr->state == CON_GREETING)) {
                                bind_me = ptr;
+                               ptr->state = CON_STARTING;
                                break;
                        }
                }
@@ -983,15 +1160,25 @@ do_select:       force_purge = 0;
                 */
                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;
+                       int checkfd = ptr->client_socket;
+                       if ((checkfd != -1) && (ptr->state == CON_IDLE) ){
+                               if (FD_ISSET(checkfd, &readfds)) {
+                                       ptr->input_waiting = 1;
+                                       if (!bind_me) {
+                                               bind_me = ptr;  /* I choose you! */
+                                               bind_me->state = CON_EXECUTING;
+                                       }
+                                       else {
+                                               ptr->state = CON_READY;
+                                       }
+                               } else if ((ptr->is_async) && (ptr->async_waiting) && (ptr->h_async_function)) {
+                                       if (!bind_me) {
+                                               bind_me = ptr;  /* I choose you! */
+                                               bind_me->state = CON_EXECUTING;
+                                       }
+                                       else {
+                                               ptr->state = CON_READY;
+                                       }
                                }
                        }
                }
@@ -1010,6 +1197,10 @@ SKIP_SELECT:
                        /* If the client has sent a command, execute it. */
                        if (CC->input_waiting) {
                                CC->h_command_function();
+
+                               while (HaveMoreLinesWaiting(CC))
+                                      CC->h_command_function();
+
                                CC->input_waiting = 0;
                        }
 
@@ -1146,7 +1337,7 @@ void *select_on_master (void *arg)
                                                SO_REUSEADDR,
                                                &i, sizeof(i));
 
-                                       con->state = CON_STARTING;
+                                       con->state = CON_GREETING;
 
                                        retval--;
                                        if (retval == 0)