X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fsysdep.c;h=c09fd2a06292edca76dae0ebbad3997440b76818;hb=cbc2a603ed4c3995836e45fd400e44e12868f5f8;hp=fe372b76d337be8aa3f7b658876fd4d50effc037;hpb=83776a3a51b4cfb71f2ec19401b4ada9bd9ac0fd;p=citadel.git diff --git a/citadel/sysdep.c b/citadel/sysdep.c index fe372b76d..c09fd2a06 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -71,6 +71,9 @@ #include "ctdl_module.h" #include "threads.h" +#include "user_ops.h" +#include "control.h" + #ifdef DEBUG_MEMORY_LEAKS struct igheap { @@ -84,7 +87,7 @@ struct igheap *igheap = NULL; #endif -pthread_key_t MyConKey; /* TSD key for MyContext() */ +citthread_key_t MyConKey; /* TSD key for MyContext() */ int verbosity = DEFAULT_VERBOSITY; /* Logging level */ @@ -97,30 +100,16 @@ int enable_syslog = 0; /* - * Create an interface to lprintf that follows the coding convention. - * This is here until such time as we have replaced all calls to lprintf with CtdlLogPrintf - */ - -void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...) -{ - va_list arg_ptr; - va_start(arg_ptr, format); - vlprintf(loglevel, format, arg_ptr); - va_end(arg_ptr); -} - - -/* - * lprintf() ... Write logging information + * CtdlLogPrintf() ... Write logging information */ -void lprintf(enum LogLevel loglevel, const char *format, ...) { +void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...) { va_list arg_ptr; va_start(arg_ptr, format); - vlprintf(loglevel, format, arg_ptr); + vCtdlLogPrintf(loglevel, format, arg_ptr); va_end(arg_ptr); } -void vlprintf(enum LogLevel loglevel, const char *format, va_list arg_ptr) +void vCtdlLogPrintf(enum LogLevel loglevel, const char *format, va_list arg_ptr) { char buf[SIZ], buf2[SIZ]; @@ -155,7 +144,7 @@ void vlprintf(enum LogLevel loglevel, const char *format, va_list arg_ptr) tim.tm_mday, tim.tm_hour, tim.tm_min, tim.tm_sec, (long)tv.tv_usec); } - vsprintf(buf2, format, arg_ptr); + vsnprintf(buf2, SIZ, format, arg_ptr); fprintf(stderr, "%s%s", buf, buf2); fflush(stderr); @@ -174,16 +163,15 @@ volatile int restart_server = 0; volatile int running_as_daemon = 0; static RETSIGTYPE signal_cleanup(int signum) { - CtdlLogPrintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); #ifdef THREADS_USESIGNALS if (CT) - { - CtdlLogPrintf(CTDL_DEBUG, "Thread \"%s\" caught signal %d.\n", CT->name, signum); CT->signal = signum; - } else #endif + { + CtdlLogPrintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); exit_signal = signum; + } } @@ -216,7 +204,7 @@ void init_sysdep(void) { * CitContext structure (in the ContextList linked list) of the * session to which the calling thread is currently bound. */ - if (pthread_key_create(&MyConKey, NULL) != 0) { + if (citthread_key_create(&MyConKey, NULL) != 0) { CtdlLogPrintf(CTDL_CRIT, "Can't create TSD key: %s\n", strerror(errno)); } @@ -414,7 +402,7 @@ struct CitContext *MyContext(void) { register struct CitContext *c; - return ((c = (struct CitContext *) pthread_getspecific(MyConKey), + return ((c = (struct CitContext *) citthread_getspecific(MyConKey), c == NULL) ? &masterCC : c ); } @@ -435,14 +423,17 @@ struct CitContext *CreateNewContext(void) { CtdlLogPrintf(CTDL_ALERT, "citserver: can't allocate memory!!\n"); return NULL; } - memset(me, 0, sizeof(struct CitContext)); + 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. */ me->state = CON_EXECUTING; - /* * Generate a unique session number and insert this context into * the list. @@ -457,10 +448,59 @@ struct CitContext *CreateNewContext(void) { } ++num_sessions; end_critical_section(S_SESSION_TABLE); - return(me); + return (me); +} + + +struct CitContext *CtdlGetContextArray(int *count) +{ + int nContexts, i; + struct CitContext *nptr, *cptr; + + nContexts = num_sessions; + nptr = malloc(sizeof(struct CitContext) * nContexts); + if (!nptr) + return NULL; + begin_critical_section(S_SESSION_TABLE); + for (cptr = ContextList, i=0; cptr != NULL && i < nContexts; cptr = cptr->next, i++) + memcpy(&nptr[i], cptr, sizeof (struct CitContext)); + end_critical_section (S_SESSION_TABLE); + + *count = i; + return nptr; } + +/** + * This function fills in a context and its user field correctly + * Then creates/loads that user + */ +void CtdlFillSystemContext(struct CitContext *context, char *name) +{ + char sysname[USERNAME_SIZE]; + + memset(context, 0, sizeof(struct CitContext)); + context->internal_pgm = 1; + context->cs_pid = 0; + strcpy (sysname, "SYS_"); + strcat (sysname, name); + /* 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 @@ -541,14 +581,16 @@ void unbuffer_output(void) { /* * client_write() ... Send binary data to the client. */ -void client_write(char *buf, int nbytes) +int client_write(char *buf, int nbytes) { int bytes_written = 0; int retval; #ifndef HAVE_TCP_BUFFERING int old_buffer_len = 0; #endif + fd_set wset; t_context *Ctx; + int fdflags; Ctx = CC; if (Ctx->redirect_buffer != NULL) { @@ -560,7 +602,7 @@ void client_write(char *buf, int nbytes) memcpy(&Ctx->redirect_buffer[Ctx->redirect_len], buf, nbytes); Ctx->redirect_len += nbytes; Ctx->redirect_buffer[Ctx->redirect_len] = 0; - return; + return 0; } #ifndef HAVE_TCP_BUFFERING @@ -570,7 +612,7 @@ void client_write(char *buf, int nbytes) Ctx->buffer_len += nbytes; Ctx->output_buffer = realloc(Ctx->output_buffer, Ctx->buffer_len); memcpy(&Ctx->output_buffer[old_buffer_len], buf, nbytes); - return; + return 0; } #endif @@ -579,11 +621,27 @@ void client_write(char *buf, int nbytes) #ifdef HAVE_OPENSSL if (Ctx->redirect_ssl) { client_write_ssl(buf, nbytes); - return; + return 0; } #endif + fdflags = fcntl(Ctx->client_socket, F_GETFL); + while (bytes_written < nbytes) { + if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { + 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; + } + } + retval = write(Ctx->client_socket, &buf[bytes_written], nbytes - bytes_written); if (retval < 1) { @@ -594,10 +652,11 @@ void client_write(char *buf, int nbytes) cit_backtrace(); // CtdlLogPrintf(CTDL_DEBUG, "Tried to send: %s", &buf[bytes_written]); Ctx->kill_me = 1; - return; + return -1; } bytes_written = bytes_written + retval; } + return 0; } @@ -649,6 +708,15 @@ int client_read_to(char *buf, int bytes, int timeout) retval = select( (fd)+1, &rfds, NULL, NULL, &tv); + if (retval < 0) + { + if (errno == EINTR) + { + CtdlLogPrintf(CTDL_DEBUG, "Interrupted select().\n"); + CC->kill_me = 1; + return (-1); + } + } if (FD_ISSET(fd, &rfds) == 0) { return(0); @@ -739,7 +807,7 @@ void context_cleanup(void) rem = ptr->next; --num_sessions; - lprintf(CTDL_DEBUG, "Purging session %d\n", ptr->cs_pid); + CtdlLogPrintf(CTDL_DEBUG, "Purging session %d\n", ptr->cs_pid); RemoveContext(ptr); free (ptr); ptr = rem; @@ -747,10 +815,9 @@ void context_cleanup(void) } -/* - * The system-dependent part of master_cleanup() - close the master socket. - */ -void sysdep_master_cleanup(void) { + +void close_masters (void) +{ struct ServiceFunctionHook *serviceptr; /* @@ -760,20 +827,32 @@ void sysdep_master_cleanup(void) { serviceptr = serviceptr->next ) { if (serviceptr->tcp_port > 0) + { CtdlLogPrintf(CTDL_INFO, "Closing listener on port %d\n", serviceptr->tcp_port); - + serviceptr->tcp_port = 0; + } + if (serviceptr->sockpath != NULL) CtdlLogPrintf(CTDL_INFO, "Closing listener on '%s'\n", serviceptr->sockpath); close(serviceptr->msock); - /* If it's a Unix domain socket, remove the file. */ if (serviceptr->sockpath != NULL) { unlink(serviceptr->sockpath); + serviceptr->sockpath = NULL; } } +} + + +/* + * The system-dependent part of master_cleanup() - close the master socket. + */ +void sysdep_master_cleanup(void) { + + close_masters(); context_cleanup(); @@ -873,11 +952,7 @@ void start_daemon(int unused) { else { fp = fopen(file_pid_file, "w"); if (fp != NULL) { - /* - * NB.. The pid file contains the pid of the actual server. - * This is not the pid of the watcher process - */ - fprintf(fp, ""F_PID_T"\n", current_child); + fprintf(fp, ""F_PID_T"\n", getpid()); fclose(fp); } waitpid(current_child, &status, 0); @@ -1014,20 +1089,43 @@ void InitializeMasterCC(void) { - - /* * Bind a thread to a context. (It's inline merely to speed things up.) */ INLINE void become_session(struct CitContext *which_con) { - pthread_setspecific(MyConKey, (void *)which_con ); + citthread_setspecific(MyConKey, (void *)which_con ); } /* * This loop just keeps going and going and going... - */ + */ +/* + * FIXME: + * This current implimentation of worker_thread creates a bottle neck in several situations + * The first thing to remember is that a single thread can handle more than one connection at a time. + * More threads mean less memory for the system to run in. + * So for efficiency we want every thread to be doing something useful or waiting in the main loop for + * something to happen anywhere. + * This current implimentation requires worker threads to wait in other locations, after it has + * been committed to a single connection which is very wasteful. + * As an extreme case consider this: + * A slow client connects and this slow client sends only one character each second. + * With this current implimentation a single worker thread is dispatched to handle that connection + * until such times as the client timeout expires, an error occurs on the socket or the client + * completes its transmission. + * THIS IS VERY BAD since that thread could have handled a read from many more clients in each one + * second interval between chars. + * + * It is my intention to re-write this code and the associated client_getln, client_read functions + * to allow any thread to read data on behalf of any connection (context). + * To do this I intend to have this main loop read chars into a buffer stored in the context. + * Once the correct criteria for a full buffer is met then we will dispatch a thread to + * process it. + * This worker thread loop also needs to be able to handle binary data. + */ + void *worker_thread(void *arg) { int i; int highest; @@ -1094,8 +1192,8 @@ do_select: force_purge = 0; tv.tv_usec = 0; retval = CtdlThreadSelect(highest + 1, &readfds, NULL, NULL, &tv); } - - if (CtdlThreadCheckStop()) return(NULL); + else + return NULL; /* Now figure out who made this select() unblock. * First, check for an error or exit condition. @@ -1109,12 +1207,14 @@ do_select: force_purge = 0; if (errno != EINTR) { CtdlLogPrintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); CtdlThreadStopAll(); - } else if (!CtdlThreadCheckStop()) { - CtdlLogPrintf(CTDL_DEBUG, "Un handled select failure.\n"); + } else { + CtdlLogPrintf(CTDL_DEBUG, "Interrupted CtdlThreadSelect.\n"); + if (CtdlThreadCheckStop()) return(NULL); goto do_select; } } else if(retval == 0) { + if (CtdlThreadCheckStop()) return(NULL); goto SKIP_SELECT; } /* Next, check to see if it's a new client connecting