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