]> code.citadel.org Git - citadel.git/blobdiff - citadel/sysdep.c
* Finished removing all the "dynamic session data" stuff in order to
[citadel.git] / citadel / sysdep.c
index 61f7c363b1fe8273489f077347276637ef8846ec..b571b8b478674dbc558c7c41d32f78a997f4d56e 100644 (file)
@@ -43,6 +43,7 @@
 #include <limits.h>
 #include <sys/resource.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <sys/un.h>
@@ -102,30 +103,23 @@ int syslog_facility = (-1);
 
 /*
  * 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!
  */
+extern int running_as_daemon;
+static int enable_syslog = 1;
 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->cs_pid != 0) {
-                               memmove(&buf[6], buf, sizeof(buf) - 6);
-                               snprintf(buf, 6, "[%3d]", CC->cs_pid);
-                               buf[5] = ' ';
-                       }
-                       syslog(loglevel, "%s", buf);
-               }
+       if (enable_syslog) {
+               va_start(arg_ptr, format);
+                       vsyslog(loglevel, format, arg_ptr);
+               va_end(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;
                time_t unixtime;
@@ -136,39 +130,37 @@ void lprintf(enum LogLevel loglevel, const char *format, ...) {
                localtime_r(&unixtime, &tim);
                if (CC->cs_pid != 0) {
                        fprintf(stderr,
-                               "%04d/%02d/%02d %2d:%02d:%02d.%06ld [%3d] %s",
+                               "%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, buf);
+                               CC->cs_pid);
                } else {
                        fprintf(stderr,
-                               "%04d/%02d/%02d %2d:%02d:%02d.%06ld %s",
+                               "%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, buf);
+                               tim.tm_sec, (long)tv.tv_usec);
                }
+               va_start(arg_ptr, format);   
+                       vfprintf(stderr, format, arg_ptr);   
+               va_end(arg_ptr);   
                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;
 
 static RETSIGTYPE signal_cleanup(int signum) {
+       lprintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum);
        time_to_die = 1;
+       master_cleanup(signum);
 }
 
 
@@ -177,6 +169,7 @@ static RETSIGTYPE signal_cleanup(int signum) {
  */
 void init_sysdep(void) {
        int i;
+       sigset_t set;
 
        /* Avoid vulnerabilities related to FD_SETSIZE if we can. */
 #ifdef FD_SETSIZE
@@ -213,10 +206,23 @@ void init_sysdep(void) {
         * The action for unexpected signals and exceptions should be to
         * call signal_cleanup() to gracefully shut down the server.
         */
+       sigemptyset(&set);
+       sigaddset(&set, SIGINT);
+       sigaddset(&set, SIGQUIT);
+       sigaddset(&set, SIGHUP);
+       sigaddset(&set, SIGTERM);
+       // sigaddset(&set, SIGSEGV);    commented out because
+       // sigaddset(&set, SIGILL);     we want core dumps
+       // sigaddset(&set, SIGBUS);
+       sigprocmask(SIG_UNBLOCK, &set, NULL);
+
        signal(SIGINT, signal_cleanup);
        signal(SIGQUIT, signal_cleanup);
        signal(SIGHUP, signal_cleanup);
        signal(SIGTERM, signal_cleanup);
+       // signal(SIGSEGV, signal_cleanup);     commented out because
+       // signal(SIGILL, signal_cleanup);      we want core dumps
+       // signal(SIGBUS, signal_cleanup);
 
        /*
         * Do not shut down the server on broken pipe signals, otherwise the
@@ -304,6 +310,14 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
                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));
+               close(s);
+               return(-1);
+       }
+
        if (listen(s, actual_queue_len) < 0) {
                lprintf(CTDL_EMERG, "citserver: Can't listen: %s\n", strerror(errno));
                close(s);
@@ -352,6 +366,14 @@ int ig_uds_server(char *sockpath, int queue_len)
                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));
+               close(s);
+               return(-1);
+       }
+
        if (listen(s, actual_queue_len) < 0) {
                lprintf(CTDL_EMERG, "citserver: Can't listen: %s\n", strerror(errno));
                return(-1);
@@ -388,7 +410,8 @@ struct CitContext *MyContext(void) {
  * sessions terminate.
  */
 struct CitContext *CreateNewContext(void) {
-       struct CitContext *me, *ptr;
+       struct CitContext *me;
+       static int next_pid = 0;
 
        me = (struct CitContext *) malloc(sizeof(struct CitContext));
        if (me == NULL) {
@@ -409,46 +432,54 @@ struct CitContext *CreateNewContext(void) {
         * 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.
  */
+#ifdef TCP_CORK
+#      define HAVE_TCP_BUFFERING
+#else
+#      ifdef TCP_NOPUSH
+#              define HAVE_TCP_BUFFERING
+#              define TCP_CORK TCP_NOPUSH
+#      endif
+#endif
+
+
+#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
 void buffer_output(void) {
        if (CC->buffering == 0) {
                CC->buffering = 1;
@@ -457,9 +488,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);
@@ -467,9 +495,6 @@ void flush_output(void) {
        }
 }
 
-/*
- * unbuffer_output()  ...  stop buffering output.
- */
 void unbuffer_output(void) {
        if (CC->buffering == 1) {
                CC->buffering = 0;
@@ -480,6 +505,7 @@ void unbuffer_output(void) {
                CC->output_buffer = NULL;
        }
 }
+#endif
 
 
 
@@ -490,21 +516,28 @@ void client_write(char *buf, int nbytes)
 {
        int bytes_written = 0;
        int retval;
-       int sock;
+#ifndef HAVE_TCP_BUFFERING
        int old_buffer_len = 0;
+#endif
 
-       if (CC->redirect_fp != NULL) {
-               fwrite(buf, (size_t)nbytes, (size_t)1, CC->redirect_fp);
+       if (CC->redirect_buffer != NULL) {
+               if ((CC->redirect_len + nbytes + 2) >= CC->redirect_alloc) {
+                       CC->redirect_alloc = (CC->redirect_alloc * 2) + nbytes;
+                       CC->redirect_buffer = realloc(CC->redirect_buffer,
+                                               CC->redirect_alloc);
+               }
+               memcpy(&CC->redirect_buffer[CC->redirect_len], buf, nbytes);
+               CC->redirect_len += nbytes;
+               CC->redirect_buffer[CC->redirect_len] = 0;
                return;
        }
 
-       if (CC->redirect_sock > 0) {
-               sock = CC->redirect_sock;       /* and continue below... */
-       }
-       else {
-               sock = CC->client_socket;
+       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) {
                old_buffer_len = CC->buffer_len;
@@ -513,6 +546,7 @@ void client_write(char *buf, int nbytes)
                memcpy(&CC->output_buffer[old_buffer_len], buf, nbytes);
                return;
        }
+#endif
 
        /* Ok, at this point we're not buffering.  Go ahead and write. */
 
@@ -524,12 +558,12 @@ void client_write(char *buf, int nbytes)
 #endif
 
        while (bytes_written < nbytes) {
-               retval = write(sock, &buf[bytes_written],
+               retval = write(CC->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;
+                       CC->kill_me = 1;
                        return;
                }
                bytes_written = bytes_written + retval;
@@ -696,18 +730,15 @@ void kill_session(int session_to_kill) {
 
 
 /*
- * 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) {
+       close(0); close(1); close(2);
+       if (fork()) exit(0);
+       setsid();
        signal(SIGHUP,SIG_IGN);
        signal(SIGINT,SIG_IGN);
        signal(SIGQUIT,SIG_IGN);
-       if (fork()!=0) exit(0);
 }
 
 
@@ -839,18 +870,14 @@ 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.
+ * 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, int sock) {
-
+void CtdlRedirectOutput(FILE *fp)
+{
        if (fp != NULL) CC->redirect_fp = fp;
        else CC->redirect_fp = NULL;
-
-       if (sock > 0) CC->redirect_sock = sock;
-       else CC->redirect_sock = (-1);
-
 }
 
 
@@ -926,7 +953,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
@@ -975,12 +1004,7 @@ 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);
@@ -1111,7 +1135,8 @@ int SyslogFacility(char *name)
                if(!strcasecmp(name, facTbl[i].name))
                        return facTbl[i].facility;
        }
-       return -1;
+       enable_syslog = 0;
+       return LOG_DAEMON;
 }