* THIS IS 7.44 (BETA RELEASE ONLY)
[citadel.git] / citadel / sysdep.c
index e3b5b16b7feb655b503a71ae78be294d3cb4bd17..46bdf021a405abb4d2568a674a868714cd9e8f36 100644 (file)
@@ -2,7 +2,7 @@
  * $Id$
  *
  * Citadel "system dependent" stuff.
- * See copyright.txt for copyright information.
+ * See COPYING for copyright information.
  *
  * Here's where we (hopefully) have most parts of the Citadel server that
  * would need to be altered to run the server in a non-POSIX environment.
@@ -72,6 +72,7 @@
 #include "ctdl_module.h"
 #include "threads.h"
 #include "user_ops.h"
+#include "control.h"
 
 
 #ifdef DEBUG_MEMORY_LEAKS
@@ -98,6 +99,52 @@ int syslog_facility = LOG_DAEMON;
 int enable_syslog = 0;
 
 
+/* Flag for single user mode */
+static int want_single_user = 0;
+
+/* Try to go single user */
+
+int CtdlTrySingleUser(void)
+{
+       int can_do = 0;
+       
+       begin_critical_section(S_SINGLE_USER);
+       if (want_single_user)
+               can_do = 0;
+       else
+       {
+               can_do = 1;
+               want_single_user = 1;
+       }
+       end_critical_section(S_SINGLE_USER);
+       return can_do;
+}
+
+void CtdlEndSingleUser(void)
+{
+       begin_critical_section(S_SINGLE_USER);
+       want_single_user = 0;
+       end_critical_section(S_SINGLE_USER);
+}
+
+
+int CtdlWantSingleUser(void)
+{
+       return want_single_user;
+}
+
+int CtdlIsSingleUser(void)
+{
+       if (want_single_user)
+       {
+               /* check for only one context here */
+               if (num_sessions == 1)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+
 /*
  * CtdlLogPrintf()  ...   Write logging information
  */
@@ -333,7 +380,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage)
        if (actual_queue_len < 5) actual_queue_len = 5;
 
        i = unlink(sockpath);
-       if (i != 0) if (errno != ENOENT) {
+       if ((i != 0) && (errno != ENOENT)) {
                *errormessage = (char*) malloc(SIZ + 1);
                snprintf(*errormessage, SIZ, "citserver: can't unlink %s: %s",
                        sockpath, strerror(errno));
@@ -423,13 +470,11 @@ struct CitContext *CreateNewContext(void) {
                return NULL;
        }
 
-       /* We can fill this as though its a private context but we must clear the internal_pgm flag
-        * to keep things correct. Watch out for the cs_pid field, that gets set further down,
-        * make sure you don't try to do it first.
-        */
-       CtdlFillPrivateContext(me, "notauth");
-       me->internal_pgm = 0;
-
+       memset(me, 0, sizeof(struct CitContext));
+       
+       /* Give the contaxt a name. Hopefully makes it easier to track */
+       strcpy (me->user.fullname, "SYS_notauth");
+       
        /* The new context will be created already in the CON_EXECUTING state
         * in order to prevent another thread from grabbing it while it's
         * being set up.
@@ -474,9 +519,10 @@ struct CitContext *CtdlGetContextArray(int *count)
 
 
 /**
- * This function fille in a context and its user field correctly
+ * This function fills in a context and its user field correctly
+ * Then creates/loads that user
  */
-void CtdlFillPrivateContext(struct CitContext *context, char *name)
+void CtdlFillSystemContext(struct CitContext *context, char *name)
 {
        char sysname[USERNAME_SIZE];
 
@@ -485,17 +531,25 @@ void CtdlFillPrivateContext(struct CitContext *context, char *name)
        context->cs_pid = 0;
        strcpy (sysname, "SYS_");
        strcat (sysname, name);
-       if (getuser(&(context->user), sysname))
-       {
-               strcpy(context->user.fullname, sysname);
+       /* internal_create_user has the side effect of loading the user regardless of wether they
+        * already existed or needed to be created
+        */
+       internal_create_user (sysname, &(context->user), -1) ;
+       
+       /* Check to see if the system user needs upgrading */
+       if (context->user.usernum == 0)
+       {       /* old system user with number 0, upgrade it */
+               context->user.usernum = get_new_user_number();
+               CtdlLogPrintf(CTDL_DEBUG, "Upgrading system user \"%s\" from user number 0 to user number %d\n", context->user.fullname, context->user.usernum);
+               /* add user to the database */
                putuser(&(context->user));
+               cdb_store(CDB_USERSBYNUMBER, &(context->user.usernum), sizeof(long), context->user.fullname, strlen(context->user.fullname)+1);
        }
 }
 
 /*
- * The following functions implement output buffering. If the kernel supplies
- * native TCP buffering (Linux & *BSD), use that; otherwise, emulate it with
- * user-space buffering.
+ * The following functions implement output buffering on operating systems which
+ * support it (such as Linux and various BSD flavors).
  */
 #ifndef HAVE_DARWIN
 #ifdef TCP_CORK
@@ -508,64 +562,31 @@ void CtdlFillPrivateContext(struct CitContext *context, char *name)
 #endif /* TCP_CORK */
 #endif /* HAVE_DARWIN */
 
-#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 
-#ifdef 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) {
-               CC->buffering = 1;
-               CC->buffer_len = 0;
-               CC->output_buffer = malloc(SIZ);
+#ifdef HAVE_TCP_BUFFERING
+       if (!CC->redirect_ssl) {
+               setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
        }
+#endif
 }
 
-void flush_output(void) {
-       if (CC->buffering == 1) {
-               client_write(CC->output_buffer, CC->buffer_len);
-               CC->buffer_len = 0;
+void unbuffer_output(void) {
+#ifdef HAVE_TCP_BUFFERING
+       if (!CC->redirect_ssl) {
+               setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
        }
+#endif
 }
 
-void unbuffer_output(void) {
-       if (CC->buffering == 1) {
-               CC->buffering = 0;
-               /* We don't call flush_output because we can't. */
-               client_write(CC->output_buffer, CC->buffer_len);
-               CC->buffer_len = 0;
-               free(CC->output_buffer);
-               CC->output_buffer = NULL;
-       }
+void flush_output(void) {
+#ifdef HAVE_TCP_BUFFERING
+       struct CitContext *CCC = CC;
+       setsockopt(CCC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
+       setsockopt(CCC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
+#endif
 }
-#endif /* HAVE_DARWIN */
-#endif /* HAVE_TCP_BUFFERING */
 
 
 
@@ -596,19 +617,6 @@ int client_write(char *buf, int nbytes)
                return 0;
        }
 
-#ifndef HAVE_TCP_BUFFERING
-       /* If we're buffering for later, do that now. */
-       if (Ctx->buffering) {
-               old_buffer_len = Ctx->buffer_len;
-               Ctx->buffer_len += nbytes;
-               Ctx->output_buffer = realloc(Ctx->output_buffer, Ctx->buffer_len);
-               memcpy(&Ctx->output_buffer[old_buffer_len], buf, nbytes);
-               return 0;
-       }
-#endif
-
-       /* Ok, at this point we're not buffering.  Go ahead and write. */
-
 #ifdef HAVE_OPENSSL
        if (Ctx->redirect_ssl) {
                client_write_ssl(buf, nbytes);
@@ -652,13 +660,12 @@ int client_write(char *buf, int nbytes)
 
 
 /*
- * cprintf()  ...   Send formatted printable data to the client.   It is
- *               implemented in terms of client_write() but remains in
- *               sysdep.c in case we port to somewhere without va_args...
+ * cprintf()   Send formatted printable data to the client.
+ *             Implemented in terms of client_write() so it's technically not sysdep...
  */
 void cprintf(const char *format, ...) {   
        va_list arg_ptr;   
-       char buf[1024];   
+       char buf[1024];
    
        va_start(arg_ptr, format);   
        if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
@@ -893,6 +900,9 @@ void graceful_shutdown(int signum) {
        exit(0);
 }
 
+int nFireUps = 0;
+int nFireUpsNonRestart = 0;
+pid_t ForkedPid = 1;
 
 /*
  * Start running as a daemon.
@@ -948,9 +958,8 @@ void start_daemon(int unused) {
                        }
                        waitpid(current_child, &status, 0);
                }
-
                do_restart = 0;
-
+               nFireUpsNonRestart = nFireUps;
                /* Did the main process exit with an actual exit code? */
                if (WIFEXITED(status)) {
 
@@ -967,12 +976,16 @@ void start_daemon(int unused) {
                        /* Any other exit code means we should restart. */
                        else {
                                do_restart = 1;
+                               nFireUps++;
+                               ForkedPid = current_child;
                        }
                }
 
                /* Any other type of termination (signals, etc.) should also restart. */
                else {
                        do_restart = 1;
+                       nFireUps++;
+                       ForkedPid = current_child;
                }
 
        } while (do_restart);
@@ -983,6 +996,28 @@ void start_daemon(int unused) {
 
 
 
+void checkcrash(void)
+{
+       if (nFireUpsNonRestart != nFireUps)
+       {
+               StrBuf *CrashMail;
+
+               CrashMail = NewStrBuf();
+               CtdlLogPrintf (CTDL_ALERT, "----------------sending crash mail\n");
+               StrBufPrintf(CrashMail, 
+                            "Your CitServer is just recovering from an unexpected termination.\n"
+                            " this maybe the result of an error in citserver or an external influence.\n"
+                            " You can get more information on this by enabling coredumping; for more information see\n"
+                            " http://citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
+                            " If you already did, the file you're looking for most probably is %score.%d\n"
+                            " Yours faithfully...",
+                            ctdl_run_dir, ForkedPid);
+               aide_message(ChrPtr(CrashMail), "Citadel server crashed.");
+               FreeStrBuf(&CrashMail);
+       }
+}
+
+
 /*
  * Generic routine to convert a login name to a full name (gecos)
  * Returns nonzero if a conversion took place