* \defgroup Webserver This contains a simple multithreaded TCP server manager. It sits around
* waiting on the specified port for incoming HTTP connections. When a
* connection is established, it calls context_loop() from context_loop.c.
- *
+ * \ingroup WebcitHttpServer
*/
/*@{*/
+#include "webcit.h"
#include "webserver.h"
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
#ifndef HAVE_SNPRINTF
int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
#endif
int msock; /**< master listening socket */
int is_https = 0; /**< Nonzero if I am an HTTPS service */
int follow_xff = 0; /**< Follow X-Forwarded-For: header */
+int home_specified = 0; /**< did the user specify a homedir? */
extern void *context_loop(int);
extern void *housekeeping_loop(void);
extern pthread_mutex_t SessionListMutex;
extern pthread_key_t MyConKey;
+char socket_dir[PATH_MAX]; /**< where to talk to our citadel server */
+static const char editor_absolut_dir[PATH_MAX]=EDITORDIR; /**< nailed to what configure gives us. */
+static char static_dir[PATH_MAX]; /**< calculated on startup */
+char *static_dirs[]={ /**< needs same sort order as the web mapping */
+ (char*)static_dir, /** our templates on disk */
+ (char*)editor_absolut_dir /** the editor on disk */
+};
+int ndirs=2; //sizeof(static_content_dirs);//sizeof(char *);
+
+/**
+ * Subdirectories from which the client may request static content
+ */
+char *static_content_dirs[] = {
+ "static", /** static templates */
+ "tiny_mce" /** the JS editor */
+};
+
+
char *server_cookie = NULL; /**< our Cookie connection to the client */
+int http_port = PORT_NUM; /**< Port to listen on */
char *ctdlhost = DEFAULT_HOST; /**< our name */
char *ctdlport = DEFAULT_PORT; /**< our Port */
int setup_wizard = 0; /**< should we run the setup wizard? \todo */
char wizard_filename[PATH_MAX];/**< where's the setup wizard? */
+int running_as_daemon = 0; /**< should we deamonize on startup? */
+
/**
* \brief This is a generic function to set up a master socket for listening on
*/
ssize_t client_write(const void *buf, size_t count)
{
+ char *newptr;
+ size_t newalloc;
if (WC->burst != NULL) {
- WC->burst =
- realloc(WC->burst, (WC->burst_len + count + 2));
- memcpy(&WC->burst[WC->burst_len], buf, count);
- WC->burst_len += count;
- return (count);
+ if ((WC->burst_len + count) >= WC->burst_alloc) {
+ newalloc = (WC->burst_alloc * 2);
+ if ((WC->burst_len + count) >= newalloc) {
+ newalloc += count;
+ }
+ newptr = realloc(WC->burst, newalloc);
+ if (newptr != NULL) {
+ WC->burst = newptr;
+ WC->burst_alloc = newalloc;
+ }
+ }
+ if ((WC->burst_len + count) < WC->burst_alloc) {
+ memcpy(&WC->burst[WC->burst_len], buf, count);
+ WC->burst_len += count;
+ return (count);
+ }
+ else {
+ return(-1);
+ }
}
#ifdef HAVE_OPENSSL
if (is_https) {
WC->burst = NULL;
}
WC->burst_len = 0;
- WC->burst = malloc(SIZ);
+ WC->burst_alloc = 32768;
+ WC->burst = malloc(WC->burst_alloc);
}
the_data = WC->burst;
WC->burst_len = 0;
+ WC->burst_alloc = 0;
WC->burst = NULL;
#ifdef HAVE_ZLIB
retval = client_read(sock, &buf[i], 1);
if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
break;
+ if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
+ /** Non printable character recieved from client */
+ return(-1);
+ }
}
- /** If we got a long line, discard characters until the newline. */
+ /** If we got a long line, discard characters until the newline. */
if (i == (bufsiz-1))
while (buf[i] != '\n' && retval == 1)
retval = client_read(sock, &buf[i], 1);
return (retval);
}
+/**
+ * \brief shut us down the regular way.
+ * param signum the signal we want to forward
+ */
+pid_t current_child;
+void graceful_shutdown(int signum) {
+ kill(current_child, signum);
+ exit(0);
+}
+
/**
- * \brief Start running as a daemon.
- * param do_close_stdio Only close stdio if set.
+ * \brief Start running as a daemon.
+ *
+ * param do_close_stdio Only close stdio if set.
*/
-void start_daemon(int do_close_stdio)
+
+/*
+ * Start running as a daemon.
+ */
+void start_daemon(int do_close_stdio, char *pid_file)
{
- if (do_close_stdio) {
- /* close(0); */
- close(1);
- close(2);
+ int status = 0;
+ pid_t child = 0;
+ FILE *fp;
+ int do_restart = 0;
+
+ current_child = 0;
+
+ /* Close stdin/stdout/stderr and replace them with /dev/null.
+ * We don't just call close() because we don't want these fd's
+ * to be reused for other files.
+ */
+ chdir("/");
+
+ child = fork();
+ if (child != 0) {
+ fp = fopen(pid_file, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", child);
+ fclose(fp);
+ }
+ exit(0);
}
+
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
- if (fork() != 0)
- exit(0);
+
+ setsid();
+ umask(0);
+ if (do_close_stdio) {
+ freopen("/dev/null", "r", stdin);
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ }
+ do {
+ current_child = fork();
+
+ signal(SIGTERM, graceful_shutdown);
+
+ if (current_child < 0) {
+ perror("fork");
+ exit(errno);
+ }
+
+ else if (current_child == 0) {
+ return; /* continue starting citadel. */
+ }
+
+ else {
+ waitpid(current_child, &status, 0);
+ }
+
+ do_restart = 0;
+
+ /* Did the main process exit with an actual exit code? */
+ if (WIFEXITED(status)) {
+
+ /* Exit code 0 means the watcher should exit */
+ if (WEXITSTATUS(status) == 0) {
+ do_restart = 0;
+ }
+
+ /* Exit code 101-109 means the watcher should exit */
+ else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
+ do_restart = 0;
+ }
+
+ /* Any other exit code means we should restart. */
+ else {
+ do_restart = 1;
+ }
+ }
+
+ /* Any other type of termination (signals, etc.) should also restart. */
+ else {
+ do_restart = 1;
+ }
+
+ } while (do_restart);
+
+ unlink(pid_file);
+ exit(WEXITSTATUS(status));
}
+/**
+ * \brief Spawn an additional worker thread into the pool.
+ */
void spawn_another_worker_thread()
{
pthread_t SessThread; /**< Thread descriptor */
pthread_t SessThread; /**< Thread descriptor */
pthread_attr_t attr; /**< Thread attributes */
int a, i; /**< General-purpose variables */
- int port = PORT_NUM; /**< Port to listen on */
char tracefile[PATH_MAX];
char ip_addr[256];
- char *webcitdir = WEBCITDIR;
+ char dirbuffer[PATH_MAX]="";
+ int relh=0;
+ int home=0;
+ int home_specified=0;
+ char relhome[PATH_MAX]="";
+ char webcitdir[PATH_MAX] = DATADIR;
+ char pidfile[PATH_MAX] = "";
+ char *hdir;
+ const char *basedir;
#ifdef ENABLE_NLS
char *locale = NULL;
char *mo = NULL;
/** Parse command line */
#ifdef HAVE_OPENSSL
- while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF)
+ while ((a = getopt(argc, argv, "h:i:p:t:x:d:cfs")) != EOF)
#else
- while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF)
+ while ((a = getopt(argc, argv, "h:i:p:t:x:d:cf")) != EOF)
#endif
switch (a) {
case 'h':
- webcitdir = strdup(optarg);
+ hdir = strdup(optarg);
+ relh=hdir[0]!='/';
+ if (!relh) safestrncpy(webcitdir, hdir,
+ sizeof webcitdir);
+ else
+ safestrncpy(relhome, relhome,
+ sizeof relhome);
+ /* free(hdir); TODO: SHOULD WE DO THIS? */
+ home_specified = 1;
+ home=1;
+ break;
+ case 'd':
+ hdir = strdup(optarg);
+ safestrncpy(pidfile, hdir,sizeof pidfile);
+ running_as_daemon = 1;
break;
case 'i':
safestrncpy(ip_addr, optarg, sizeof ip_addr);
break;
case 'p':
- port = atoi(optarg);
- if (port == 0) {
+ http_port = atoi(optarg);
+ if (http_port == 0) {
safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
}
break;
fprintf(stderr, "usage: webserver "
"[-i ip_addr] [-p http_port] "
"[-t tracefile] [-c] [-f] "
+ "[-d] "
#ifdef HAVE_OPENSSL
"[-s] "
#endif
if (++optind < argc)
ctdlport = argv[optind];
}
+
+ /* daemonize, if we were asked to */
+ if (running_as_daemon) {
+ start_daemon(0, pidfile);
+ }
+
/** Tell 'em who's in da house */
lprintf(1, SERVER "\n");
- lprintf(1, "Copyright (C) 1996-2005 by the Citadel development team.\n"
+ lprintf(1, "Copyright (C) 1996-2006 by the Citadel development team.\n"
"This software is distributed under the terms of the "
"GNU General Public License.\n\n"
);
- lprintf(9, "Changing directory to %s\n", webcitdir);
- if (chdir(webcitdir) != 0) {
- perror("chdir");
- }
/** initialize the International Bright Young Thing */
#ifdef ENABLE_NLS
-
initialize_locales();
-
locale = setlocale(LC_ALL, "");
-
mo = malloc(strlen(webcitdir) + 20);
- sprintf(mo, "%s/locale", webcitdir);
- lprintf(9, "Message catalog directory: %s\n",
- bindtextdomain("webcit", mo)
- );
+ lprintf(9, "Message catalog directory: %s\n", bindtextdomain("webcit", LOCALEDIR));
free(mo);
- lprintf(9, "Text domain: %s\n",
- textdomain("webcit")
- );
- lprintf(9, "Text domain Charset: %s\n",
- bind_textdomain_codeset("webcit","UTF8")
- );
+ lprintf(9, "Text domain: %s\n", textdomain("webcit"));
+ lprintf(9, "Text domain Charset: %s\n", bind_textdomain_codeset("webcit","UTF8"));
#endif
+
+ /* calculate all our path on a central place */
+ /* where to keep our config */
+
+#define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
+ snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
+ (home&!relh)?webcitdir:basedir, \
+ ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \
+ ((basedir!=webcitdir)&(home&!relh))?"/":"", \
+ relhome, \
+ (relhome[0]!='\0')?"/":"",\
+ dirbuffer,\
+ (dirbuffer[0]!='\0')?"/":"");
+ basedir=RUNDIR;
+ COMPUTE_DIRECTORY(socket_dir);
+ basedir=DATADIR;
+ COMPUTE_DIRECTORY(static_dir);
+ /** we should go somewhere we can leave our coredump, if enabled... */
+ lprintf(9, "Changing directory to %s\n", socket_dir);
+ if (chdir(webcitdir) != 0) {
+ perror("chdir");
+ }
initialize_viewdefs();
initialize_axdefs();
msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
}
else {
- lprintf(2, "Attempting to bind to port %d...\n", port);
- msock = ig_tcp_server(ip_addr, port, LISTEN_QUEUE_LENGTH);
+ lprintf(2, "Attempting to bind to port %d...\n", http_port);
+ msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
}
lprintf(2, "Listening on socket %d\n", msock);
#endif
if (fail_this_transaction == 0) {
+
/** Perform an HTTP transaction... */
context_loop(ssock);
+
+ /** Shut down SSL/TLS if required... */
+#ifdef HAVE_OPENSSL
+ if (is_https) {
+ endtls();
+ }
+#endif
+
/** ...and close the socket. */
lingering_close(ssock);
}
}
+/**
+ * \brief print the actual stack frame.
+ */
+void wc_backtrace(void)
+{
+#ifdef HAVE_BACKTRACE
+ void *stack_frames[50];
+ size_t size, i;
+ char **strings;
+
+
+ size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
+ strings = backtrace_symbols(stack_frames, size);
+ for (i = 0; i < size; i++) {
+ if (strings != NULL)
+ lprintf(1, "%s\n", strings[i]);
+ else
+ lprintf(1, "%p\n", stack_frames[i]);
+ }
+ free(strings);
+#endif
+}
+
/*@}*/