78ee06f3153c58a9c6c385b2f95630e36e8fb28c
[citadel.git] / webcit / webserver.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup Webserver This contains a simple multithreaded TCP server manager.  It sits around
6  * waiting on the specified port for incoming HTTP connections.  When a
7  * connection is established, it calls context_loop() from context_loop.c.
8  * \ingroup WebcitHttpServer
9  */
10
11 /*@{*/
12 #include "webcit.h"
13 #include "webserver.h"
14
15 #if HAVE_BACKTRACE
16 #include <execinfo.h>
17 #endif
18
19 #ifndef HAVE_SNPRINTF
20 int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
21 #endif
22
23 int verbosity = 9;              /**< Logging level */
24 int msock;                          /**< master listening socket */
25 int is_https = 0;               /**< Nonzero if I am an HTTPS service */
26 int follow_xff = 0;             /**< Follow X-Forwarded-For: header */
27 int home_specified = 0; /**< did the user specify a homedir? */
28 extern void *context_loop(int);
29 extern void *housekeeping_loop(void);
30 extern pthread_mutex_t SessionListMutex;
31 extern pthread_key_t MyConKey;
32
33 char socket_dir[PATH_MAX];      /**< where to talk to our citadel server */
34 static const char editor_absolut_dir[PATH_MAX]=EDITORDIR; /**< nailed to what configure gives us. */
35 static char static_dir[PATH_MAX]; /**< calculated on startup */
36 static char static_local_dir[PATH_MAX]; /**< calculated on startup */
37 char  *static_dirs[]={ /**< needs same sort order as the web mapping */
38         (char*)static_dir,                  /** our templates on disk */
39         (char*)static_local_dir,            /** user provided templates disk */
40         (char*)editor_absolut_dir           /** the editor on disk */
41 };
42
43 /**
44  * Subdirectories from which the client may request static content
45  *
46  * (If you add more, remember to increment 'ndirs' below)
47  */
48 char *static_content_dirs[] = {
49         "static",                     /** static templates */
50         "static.local",               /** site local static templates */
51         "tiny_mce"                    /** the JS editor */
52 };
53
54 int ndirs=3;
55
56
57 char *server_cookie = NULL; /**< our Cookie connection to the client */
58
59 int http_port = PORT_NUM;       /**< Port to listen on */
60
61 char *ctdlhost = DEFAULT_HOST; /**< our name */
62 char *ctdlport = DEFAULT_PORT; /**< our Port */
63 int setup_wizard = 0;          /**< should we run the setup wizard? \todo */
64 char wizard_filename[PATH_MAX];/**< where's the setup wizard? */
65 int running_as_daemon = 0;     /**< should we deamonize on startup? */
66
67
68 /** 
69  * \brief This is a generic function to set up a master socket for listening on
70  * a TCP port.  The server shuts down if the bind fails.
71  * \param ip_addr ip to bind to
72  * \param port_number the port to bind to 
73  * \param queue_len the size of the input queue ????
74  */
75 int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
76 {
77         struct sockaddr_in sin;
78         int s, i;
79
80         memset(&sin, 0, sizeof(sin));
81         sin.sin_family = AF_INET;
82         if (ip_addr == NULL) {
83                 sin.sin_addr.s_addr = INADDR_ANY;
84         } else {
85                 sin.sin_addr.s_addr = inet_addr(ip_addr);
86         }
87
88         if (sin.sin_addr.s_addr == INADDR_NONE) {
89                 sin.sin_addr.s_addr = INADDR_ANY;
90         }
91
92         if (port_number == 0) {
93                 lprintf(1, "Cannot start: no port number specified.\n");
94                 exit(WC_EXIT_BIND);
95         }
96         sin.sin_port = htons((u_short) port_number);
97
98         s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
99         if (s < 0) {
100                 lprintf(1, "Can't create a socket: %s\n", strerror(errno));
101                 exit(WC_EXIT_BIND);
102         }
103         /** Set some socket options that make sense. */
104         i = 1;
105         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
106
107         if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
108                 lprintf(1, "Can't bind: %s\n", strerror(errno));
109                 exit(WC_EXIT_BIND);
110         }
111         if (listen(s, queue_len) < 0) {
112                 lprintf(1, "Can't listen: %s\n", strerror(errno));
113                 exit(WC_EXIT_BIND);
114         }
115         return (s);
116 }
117
118
119
120 /**
121  * \brief Create a Unix domain socket and listen on it
122  * \param sockpath file name of the unix domain socket
123  * \param queue_len queue size of the kernel fifo????
124  */
125 int ig_uds_server(char *sockpath, int queue_len)
126 {
127         struct sockaddr_un addr;
128         int s;
129         int i;
130         int actual_queue_len;
131
132         actual_queue_len = queue_len;
133         if (actual_queue_len < 5) actual_queue_len = 5;
134
135         i = unlink(sockpath);
136         if (i != 0) if (errno != ENOENT) {
137                 lprintf(1, "citserver: can't unlink %s: %s\n",
138                         sockpath, strerror(errno));
139                 exit(WC_EXIT_BIND);
140         }
141
142         memset(&addr, 0, sizeof(addr));
143         addr.sun_family = AF_UNIX;
144         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
145
146         s = socket(AF_UNIX, SOCK_STREAM, 0);
147         if (s < 0) {
148                 lprintf(1, "citserver: Can't create a socket: %s\n",
149                         strerror(errno));
150                 exit(WC_EXIT_BIND);
151         }
152
153         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
154                 lprintf(1, "citserver: Can't bind: %s\n",
155                         strerror(errno));
156                 exit(WC_EXIT_BIND);
157         }
158
159         if (listen(s, actual_queue_len) < 0) {
160                 lprintf(1, "citserver: Can't listen: %s\n",
161                         strerror(errno));
162                 exit(WC_EXIT_BIND);
163         }
164
165         chmod(sockpath, 0777);
166         return(s);
167 }
168
169
170
171
172 /**
173  * \brief Read data from the client socket.
174  * \param sock socket fd to read from ???
175  * \param buf buffer to read into 
176  * \param bytes how large is the read buffer?
177  * \param timeout how long should we wait for input?
178  * \return values are\
179  *      1       Requested number of bytes has been read.\
180  *      0       Request timed out.\
181  *         -1           Connection is broken, or other error.
182  */
183 int client_read_to(int sock, char *buf, int bytes, int timeout)
184 {
185         int len, rlen;
186         fd_set rfds;
187         struct timeval tv;
188         int retval;
189
190
191 #ifdef HAVE_OPENSSL
192         if (is_https) {
193                 return (client_read_ssl(buf, bytes, timeout));
194         }
195 #endif
196
197         len = 0;
198         while (len < bytes) {
199                 FD_ZERO(&rfds);
200                 FD_SET(sock, &rfds);
201                 tv.tv_sec = timeout;
202                 tv.tv_usec = 0;
203
204                 retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
205                 if (FD_ISSET(sock, &rfds) == 0) {
206                         return (0);
207                 }
208
209                 rlen = read(sock, &buf[len], bytes - len);
210
211                 if (rlen < 1) {
212                         lprintf(2, "client_read() failed: %s\n",
213                                 strerror(errno));
214                         return (-1);
215                 }
216                 len = len + rlen;
217         }
218
219 #ifdef HTTP_TRACING
220         write(2, "\033[32m", 5);
221         write(2, buf, bytes);
222         write(2, "\033[30m", 5);
223 #endif
224         return (1);
225 }
226
227 /**
228  * \brief write data to the client
229  * \param buf data to write to the client
230  * \param count size of buffer
231  */
232 ssize_t client_write(const void *buf, size_t count)
233 {
234         char *newptr;
235         size_t newalloc;
236
237         if (WC->burst != NULL) {
238                 if ((WC->burst_len + count) >= WC->burst_alloc) {
239                         newalloc = (WC->burst_alloc * 2);
240                         if ((WC->burst_len + count) >= newalloc) {
241                                 newalloc += count;
242                         }
243                         newptr = realloc(WC->burst, newalloc);
244                         if (newptr != NULL) {
245                                 WC->burst = newptr;
246                                 WC->burst_alloc = newalloc;
247                         }
248                 }
249                 if ((WC->burst_len + count) < WC->burst_alloc) {
250                         memcpy(&WC->burst[WC->burst_len], buf, count);
251                         WC->burst_len += count;
252                         return (count);
253                 }
254                 else {
255                         return(-1);
256                 }
257         }
258 #ifdef HAVE_OPENSSL
259         if (is_https) {
260                 client_write_ssl((char *) buf, count);
261                 return (count);
262         }
263 #endif
264 #ifdef HTTP_TRACING
265         write(2, "\033[34m", 5);
266         write(2, buf, count);
267         write(2, "\033[30m", 5);
268 #endif
269         return (write(WC->http_sock, buf, count));
270 }
271
272 /**
273  * \brief what burst???
274  */
275 void begin_burst(void)
276 {
277         if (WC->burst != NULL) {
278                 free(WC->burst);
279                 WC->burst = NULL;
280         }
281         WC->burst_len = 0;
282         WC->burst_alloc = 32768;
283         WC->burst = malloc(WC->burst_alloc);
284 }
285
286
287 /**
288  * \brief uses the same calling syntax as compress2(), but it
289  * creates a stream compatible with HTTP "Content-encoding: gzip"
290  */
291 #ifdef HAVE_ZLIB
292 #define DEF_MEM_LEVEL 8 /**< memlevel??? */
293 #define OS_CODE 0x03    /**< unix */
294 int ZEXPORT compress_gzip(Bytef * dest,         /**< compressed buffer*/
295                                                   uLongf * destLen,     /**< length of the compresed data */
296                                                   const Bytef * source, /**< source to encode */
297                                                   uLong sourceLen,      /**< length of the source to encode */
298                                                   int level)            /**< what level??? */
299 {
300         const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */
301
302         /** write gzip header */
303         sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
304                 gz_magic[0], gz_magic[1], Z_DEFLATED,
305                 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ ,
306                 OS_CODE);
307
308         /* normal deflate */
309         z_stream stream;
310         int err;
311         stream.next_in = (Bytef *) source;
312         stream.avail_in = (uInt) sourceLen;
313         stream.next_out = dest + 10L;   // after header
314         stream.avail_out = (uInt) * destLen;
315         if ((uLong) stream.avail_out != *destLen)
316                 return Z_BUF_ERROR;
317
318         stream.zalloc = (alloc_func) 0;
319         stream.zfree = (free_func) 0;
320         stream.opaque = (voidpf) 0;
321
322         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
323                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
324         if (err != Z_OK)
325                 return err;
326
327         err = deflate(&stream, Z_FINISH);
328         if (err != Z_STREAM_END) {
329                 deflateEnd(&stream);
330                 return err == Z_OK ? Z_BUF_ERROR : err;
331         }
332         *destLen = stream.total_out + 10L;
333
334         /* write CRC and Length */
335         uLong crc = crc32(0L, source, sourceLen);
336         int n;
337         for (n = 0; n < 4; ++n, ++*destLen) {
338                 dest[*destLen] = (int) (crc & 0xff);
339                 crc >>= 8;
340         }
341         uLong len = stream.total_in;
342         for (n = 0; n < 4; ++n, ++*destLen) {
343                 dest[*destLen] = (int) (len & 0xff);
344                 len >>= 8;
345         }
346         err = deflateEnd(&stream);
347         return err;
348 }
349 #endif
350
351 /**
352  * \brief what burst???
353  */
354 void end_burst(void)
355 {
356         size_t the_len;
357         char *the_data;
358
359         if (WC->burst == NULL)
360                 return;
361
362         the_len = WC->burst_len;
363         the_data = WC->burst;
364
365         WC->burst_len = 0;
366         WC->burst_alloc = 0;
367         WC->burst = NULL;
368
369 #ifdef HAVE_ZLIB
370         /* Handle gzip compression */
371         if (WC->gzip_ok) {
372                 char *compressed_data = NULL;
373                 uLongf compressed_len;
374
375                 compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
376                 compressed_data = malloc(compressed_len);
377
378                 if (compress_gzip((Bytef *) compressed_data,
379                                   &compressed_len,
380                                   (Bytef *) the_data,
381                                   (uLongf) the_len, Z_BEST_SPEED) == Z_OK) {
382                         wprintf("Content-encoding: gzip\r\n");
383                         free(the_data);
384                         the_data = compressed_data;
385                         the_len = compressed_len;
386                 } else {
387                         free(compressed_data);
388                 }
389         }
390 #endif                          /* HAVE_ZLIB */
391
392         wprintf("Content-length: %d\r\n\r\n", the_len);
393         client_write(the_data, the_len);
394         free(the_data);
395         return;
396 }
397
398
399
400 /**
401  * \brief Read data from the client socket with default timeout.
402  * (This is implemented in terms of client_read_to() and could be
403  * justifiably moved out of sysdep.c)
404  * \param sock the socket fd to read from???
405  * \param buf the buffer to write to
406  * \param bytes how large is the buffer
407  */
408 int client_read(int sock, char *buf, int bytes)
409 {
410         return (client_read_to(sock, buf, bytes, SLEEPING));
411 }
412
413
414 /**
415  * \brief Get a LF-terminated line of text from the client.
416  * (This is implemented in terms of client_read() and could be
417  * justifiably moved out of sysdep.c)
418  * \param sock socket fd to get client line from???
419  * \param buf buffer to write read data to
420  * \param bufsiz how many bytes to read
421  * \return  numer of bytes read???
422  */
423 int client_getln(int sock, char *buf, int bufsiz)
424 {
425         int i, retval;
426
427         /** Read one character at a time.*/
428         for (i = 0;; i++) {
429                 retval = client_read(sock, &buf[i], 1);
430                 if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
431                         break;
432                 if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
433                         /** Non printable character recieved from client */
434                         return(-1);
435                 }
436         }
437
438         /** If we got a long line, discard characters until the newline. */
439         if (i == (bufsiz-1))
440                 while (buf[i] != '\n' && retval == 1)
441                         retval = client_read(sock, &buf[i], 1);
442
443         /**
444          * Strip any trailing non-printable characters.
445          */
446         buf[i] = 0;
447         while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) {
448                 buf[strlen(buf) - 1] = 0;
449         }
450         return (retval);
451 }
452
453 /**
454  * \brief shut us down the regular way.
455  * param signum the signal we want to forward
456  */
457 pid_t current_child;
458 void graceful_shutdown(int signum) {
459         kill(current_child, signum);
460         exit(0);
461 }
462
463
464 /**
465  * \brief       Start running as a daemon.  
466  *
467  * param        do_close_stdio          Only close stdio if set.
468  */
469
470 /*
471  * Start running as a daemon.
472  */
473 void start_daemon(char *pid_file) 
474 {
475         int status = 0;
476         pid_t child = 0;
477         FILE *fp;
478         int do_restart = 0;
479
480         current_child = 0;
481
482         /* Close stdin/stdout/stderr and replace them with /dev/null.
483          * We don't just call close() because we don't want these fd's
484          * to be reused for other files.
485          */
486         chdir("/");
487
488         signal(SIGHUP, SIG_IGN);
489         signal(SIGINT, SIG_IGN);
490         signal(SIGQUIT, SIG_IGN);
491
492         child = fork();
493         if (child != 0) {
494                 exit(0);
495         }
496
497         setsid();
498         umask(0);
499         freopen("/dev/null", "r", stdin);
500         freopen("/dev/null", "w", stdout);
501         freopen("/dev/null", "w", stderr);
502
503         do {
504                 current_child = fork();
505
506         
507                 if (current_child < 0) {
508                         perror("fork");
509                         exit(errno);
510                 }
511         
512                 else if (current_child == 0) {
513                         signal(SIGTERM, graceful_shutdown);
514                         return; /* continue starting citadel. */
515                 }
516         
517                 else {
518                         signal(SIGTERM, SIG_IGN);
519                         if (pid_file) {
520                                 fp = fopen(pid_file, "w");
521                                 if (fp != NULL) {
522                                         fprintf(fp, "%d\n", current_child);
523                                         fclose(fp);
524                                 }
525                         }
526                         waitpid(current_child, &status, 0);
527                 }
528
529                 do_restart = 0;
530
531                 /* Did the main process exit with an actual exit code? */
532                 if (WIFEXITED(status)) {
533
534                         /* Exit code 0 means the watcher should exit */
535                         if (WEXITSTATUS(status) == 0) {
536                                 do_restart = 0;
537                         }
538
539                         /* Exit code 101-109 means the watcher should exit */
540                         else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
541                                 do_restart = 0;
542                         }
543
544                         /* Any other exit code means we should restart. */
545                         else {
546                                 do_restart = 1;
547                         }
548                 }
549
550                 /* Any other type of termination (signals, etc.) should also restart. */
551                 else {
552                         do_restart = 1;
553                 }
554
555         } while (do_restart);
556
557         if (pid_file) {
558                 unlink(pid_file);
559         }
560         exit(WEXITSTATUS(status));
561 }
562
563 /**
564  * \brief       Spawn an additional worker thread into the pool.
565  */
566 void spawn_another_worker_thread()
567 {
568         pthread_t SessThread;   /**< Thread descriptor */
569         pthread_attr_t attr;    /**< Thread attributes */
570         int ret;
571
572         lprintf(3, "Creating a new thread\n");
573
574         /** set attributes for the new thread */
575         pthread_attr_init(&attr);
576         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
577
578         /**
579          * Our per-thread stacks need to be bigger than the default size, otherwise
580          * the MIME parser crashes on FreeBSD, and the IMAP service crashes on
581          * 64-bit Linux.
582          */
583         if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
584                 lprintf(1, "pthread_attr_setstacksize: %s\n",
585                         strerror(ret));
586                 pthread_attr_destroy(&attr);
587         }
588
589         /** now create the thread */
590         if (pthread_create(&SessThread, &attr,
591                            (void *(*)(void *)) worker_entry, NULL)
592             != 0) {
593                 lprintf(1, "Can't create thread: %s\n", strerror(errno));
594         }
595
596         /** free up the attributes */
597         pthread_attr_destroy(&attr);
598 }
599
600 /**
601  * \brief Here's where it all begins.
602  * \param argc number of commandline args
603  * \param argv the commandline arguments
604  */
605 int main(int argc, char **argv)
606 {
607         pthread_t SessThread;   /**< Thread descriptor */
608         pthread_attr_t attr;    /**< Thread attributes */
609         int a, i;                       /**< General-purpose variables */
610         char tracefile[PATH_MAX];
611         char ip_addr[256];
612         char dirbuffer[PATH_MAX]="";
613         int relh=0;
614         int home=0;
615         int home_specified=0;
616         char relhome[PATH_MAX]="";
617         char webcitdir[PATH_MAX] = DATADIR;
618         char *pidfile = NULL;
619         char *hdir;
620         const char *basedir;
621 #ifdef ENABLE_NLS
622         char *locale = NULL;
623         char *mo = NULL;
624 #endif /* ENABLE_NLS */
625         char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */
626
627         strcpy(uds_listen_path, "");
628
629         /** Parse command line */
630 #ifdef HAVE_OPENSSL
631         while ((a = getopt(argc, argv, "h:i:p:t:x:dD:cfs")) != EOF)
632 #else
633         while ((a = getopt(argc, argv, "h:i:p:t:x:dD:cf")) != EOF)
634 #endif
635                 switch (a) {
636                 case 'h':
637                         hdir = strdup(optarg);
638                         relh=hdir[0]!='/';
639                         if (!relh) safestrncpy(webcitdir, hdir,
640                                                                    sizeof webcitdir);
641                         else
642                                 safestrncpy(relhome, relhome,
643                                                         sizeof relhome);
644                         /* free(hdir); TODO: SHOULD WE DO THIS? */
645                         home_specified = 1;
646                         home=1;
647                         break;
648                 case 'd':
649                         running_as_daemon = 1;
650                         break;
651                 case 'D':
652                         pidfile = strdup(optarg);
653                         running_as_daemon = 1;
654                         break;
655                 case 'i':
656                         safestrncpy(ip_addr, optarg, sizeof ip_addr);
657                         break;
658                 case 'p':
659                         http_port = atoi(optarg);
660                         if (http_port == 0) {
661                                 safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
662                         }
663                         break;
664                 case 't':
665                         safestrncpy(tracefile, optarg, sizeof tracefile);
666                         freopen(tracefile, "w", stdout);
667                         freopen(tracefile, "w", stderr);
668                         freopen(tracefile, "r", stdin);
669                         break;
670                 case 'x':
671                         verbosity = atoi(optarg);
672                         break;
673                 case 'f':
674                         follow_xff = 1;
675                         break;
676                 case 'c':
677                         server_cookie = malloc(256);
678                         if (server_cookie != NULL) {
679                                 safestrncpy(server_cookie,
680                                        "Set-cookie: wcserver=",
681                                         256);
682                                 if (gethostname
683                                     (&server_cookie[strlen(server_cookie)],
684                                      200) != 0) {
685                                         lprintf(2, "gethostname: %s\n",
686                                                 strerror(errno));
687                                         free(server_cookie);
688                                 }
689                         }
690                         break;
691                 case 's':
692                         is_https = 1;
693                         break;
694                 default:
695                         fprintf(stderr, "usage: webserver "
696                                 "[-i ip_addr] [-p http_port] "
697                                 "[-t tracefile] [-c] [-f] "
698                                 "[-d] "
699 #ifdef HAVE_OPENSSL
700                                 "[-s] "
701 #endif
702                                 "[remotehost [remoteport]]\n");
703                         return 1;
704                 }
705
706         if (optind < argc) {
707                 ctdlhost = argv[optind];
708                 if (++optind < argc)
709                         ctdlport = argv[optind];
710         }
711
712         /* daemonize, if we were asked to */
713         if (running_as_daemon) {
714                 start_daemon(pidfile);
715         }
716
717         /** Tell 'em who's in da house */
718         lprintf(1, SERVER "\n");
719         lprintf(1, "Copyright (C) 1996-2007 by the Citadel development team.\n"
720                 "This software is distributed under the terms of the "
721                 "GNU General Public License.\n\n"
722         );
723
724
725         /** initialize the International Bright Young Thing */
726 #ifdef ENABLE_NLS
727         initialize_locales();
728         locale = setlocale(LC_ALL, "");
729         mo = malloc(strlen(webcitdir) + 20);
730         lprintf(9, "Message catalog directory: %s\n", bindtextdomain("webcit", LOCALEDIR"/locale"));
731         free(mo);
732         lprintf(9, "Text domain: %s\n", textdomain("webcit"));
733         lprintf(9, "Text domain Charset: %s\n", bind_textdomain_codeset("webcit","UTF8"));
734 #endif
735
736
737         /* calculate all our path on a central place */
738     /* where to keep our config */
739         
740 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
741         snprintf(SUBDIR,sizeof SUBDIR,  "%s%s%s%s%s%s%s", \
742                          (home&!relh)?webcitdir:basedir, \
743              ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \
744              ((basedir!=webcitdir)&(home&!relh))?"/":"", \
745                          relhome, \
746              (relhome[0]!='\0')?"/":"",\
747                          dirbuffer,\
748                          (dirbuffer[0]!='\0')?"/":"");
749         basedir=RUNDIR;
750         COMPUTE_DIRECTORY(socket_dir);
751         basedir=DATADIR "/static";
752         COMPUTE_DIRECTORY(static_dir);
753         basedir=DATADIR "/static.local";
754         COMPUTE_DIRECTORY(static_local_dir);
755         /** we should go somewhere we can leave our coredump, if enabled... */
756         lprintf(9, "Changing directory to %s\n", socket_dir);
757         if (chdir(webcitdir) != 0) {
758                 perror("chdir");
759         }
760         initialize_viewdefs();
761         initialize_axdefs();
762
763         /**
764          * Set up a place to put thread-specific data.
765          * We only need a single pointer per thread - it points to the
766          * wcsession struct to which the thread is currently bound.
767          */
768         if (pthread_key_create(&MyConKey, NULL) != 0) {
769                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
770         }
771
772         /**
773          * Set up a place to put thread-specific SSL data.
774          * We don't stick this in the wcsession struct because SSL starts
775          * up before the session is bound, and it gets torn down between
776          * transactions.
777          */
778 #ifdef HAVE_OPENSSL
779         if (pthread_key_create(&ThreadSSL, NULL) != 0) {
780                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
781         }
782 #endif
783
784         /**
785          * Bind the server to our favorite port.
786          * There is no need to check for errors, because ig_tcp_server()
787          * exits if it doesn't succeed.
788          */
789
790         if (strlen(uds_listen_path) > 0) {
791                 lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
792                 msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
793         }
794         else {
795                 lprintf(2, "Attempting to bind to port %d...\n", http_port);
796                 msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
797         }
798
799         lprintf(2, "Listening on socket %d\n", msock);
800         signal(SIGPIPE, SIG_IGN);
801
802         pthread_mutex_init(&SessionListMutex, NULL);
803
804         /**
805          * Start up the housekeeping thread
806          */
807         pthread_attr_init(&attr);
808         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
809         pthread_create(&SessThread, &attr,
810                        (void *(*)(void *)) housekeeping_loop, NULL);
811
812
813         /**
814          * If this is an HTTPS server, fire up SSL
815          */
816 #ifdef HAVE_OPENSSL
817         if (is_https) {
818                 init_ssl();
819         }
820 #endif
821
822         /** Start a few initial worker threads */
823         for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
824                 spawn_another_worker_thread();
825         }
826
827         /* now the original thread becomes another worker */
828         worker_entry();
829         return 0;
830 }
831
832
833 /**
834  * Entry point for worker threads
835  */
836 void worker_entry(void)
837 {
838         int ssock;
839         int i = 0;
840         int time_to_die = 0;
841         int fail_this_transaction = 0;
842
843         do {
844                 /** Only one thread can accept at a time */
845                 fail_this_transaction = 0;
846                 ssock = accept(msock, NULL, 0);
847                 if (ssock < 0) {
848                         lprintf(2, "accept() failed: %s\n",
849                                 strerror(errno));
850                 } else {
851                         /** Set the SO_REUSEADDR socket option */
852                         i = 1;
853                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
854                                    &i, sizeof(i));
855
856                         /** If we are an HTTPS server, go crypto now. */
857 #ifdef HAVE_OPENSSL
858                         if (is_https) {
859                                 if (starttls(ssock) != 0) {
860                                         fail_this_transaction = 1;
861                                         close(ssock);
862                                 }
863                         }
864 #endif
865
866                         if (fail_this_transaction == 0) {
867
868                                 /** Perform an HTTP transaction... */
869                                 context_loop(ssock);
870
871                                 /** Shut down SSL/TLS if required... */
872 #ifdef HAVE_OPENSSL
873                                 if (is_https) {
874                                         endtls();
875                                 }
876 #endif
877
878                                 /** ...and close the socket. */
879                                 lingering_close(ssock);
880                         }
881
882                 }
883
884         } while (!time_to_die);
885
886         pthread_exit(NULL);
887 }
888
889 /**
890  * \brief logprintf. log messages 
891  * logs to stderr if loglevel is lower than the verbosity set at startup
892  * \param loglevel level of the message
893  * \param format the printf like format string
894  * \param ... the strings to put into format
895  */
896 int lprintf(int loglevel, const char *format, ...)
897 {
898         va_list ap;
899
900         if (loglevel <= verbosity) {
901                 va_start(ap, format);
902                 vfprintf(stderr, format, ap);
903                 va_end(ap);
904                 fflush(stderr);
905         }
906         return 1;
907 }
908
909
910 /**
911  * \brief print the actual stack frame.
912  */
913 void wc_backtrace(void)
914 {
915 #ifdef HAVE_BACKTRACE
916         void *stack_frames[50];
917         size_t size, i;
918         char **strings;
919
920
921         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
922         strings = backtrace_symbols(stack_frames, size);
923         for (i = 0; i < size; i++) {
924                 if (strings != NULL)
925                         lprintf(1, "%s\n", strings[i]);
926                 else
927                         lprintf(1, "%p\n", stack_frames[i]);
928         }
929         free(strings);
930 #endif
931 }
932
933 /*@}*/