Moved all threading code into threads.c
[citadel.git] / citadel / sysdep.c
1 /*
2  * $Id$
3  *
4  * Citadel "system dependent" stuff.
5  * See copyright.txt for copyright information.
6  *
7  * Here's where we (hopefully) have most parts of the Citadel server that
8  * would need to be altered to run the server in a non-POSIX environment.
9  * 
10  * If we ever port to a different platform and either have multiple
11  * variants of this file or simply load it up with #ifdefs.
12  *
13  */
14
15 #include "sysdep.h"
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <ctype.h>
21 #include <signal.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <sys/socket.h>
26 #include <syslog.h>
27 #include <sys/syslog.h>
28
29 #if TIME_WITH_SYS_TIME
30 # include <sys/time.h>
31 # include <time.h>
32 #else
33 # if HAVE_SYS_TIME_H
34 #  include <sys/time.h>
35 # else
36 #  include <time.h>
37 # endif
38 #endif
39
40 #include <limits.h>
41 #include <sys/resource.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <sys/un.h>
47 #include <string.h>
48 #include <pwd.h>
49 #include <errno.h>
50 #include <stdarg.h>
51 #include <grp.h>
52 #include <libcitadel.h>
53 #include "citadel.h"
54 #include "server.h"
55 #include "sysdep_decls.h"
56 #include "citserver.h"
57 #include "support.h"
58 #include "config.h"
59 #include "database.h"
60 #include "housekeeping.h"
61 #include "modules/crypto/serv_crypto.h" /* Needed for init_ssl, client_write_ssl, client_read_ssl, destruct_ssl */
62 #include "ecrash.h"
63
64 #ifdef HAVE_SYS_SELECT_H
65 #include <sys/select.h>
66 #endif
67
68 #ifndef HAVE_SNPRINTF
69 #include "snprintf.h"
70 #endif
71
72 #include "ctdl_module.h"
73 #include "threads.h"
74
75 #ifdef DEBUG_MEMORY_LEAKS
76 struct igheap {
77         struct igheap *next;
78         char file[32];
79         int line;
80         void *block;
81 };
82
83 struct igheap *igheap = NULL;
84 #endif
85
86
87 pthread_key_t MyConKey;                         /* TSD key for MyContext() */
88
89 int verbosity = DEFAULT_VERBOSITY;              /* Logging level */
90
91 struct CitContext masterCC;
92 time_t last_purge = 0;                          /* Last dead session purge */
93 int num_sessions = 0;                           /* Current number of sessions */
94
95 int syslog_facility = LOG_DAEMON;
96 int enable_syslog = 0;
97
98
99 /*
100  * Create an interface to lprintf that follows the coding convention.
101  * This is here until such time as we have replaced all calls to lprintf with CtdlLogPrintf
102  */
103  
104 void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...)
105 {
106         va_list arg_ptr;
107         va_start(arg_ptr, format);
108         vlprintf(loglevel, format, arg_ptr);
109         va_end(arg_ptr);
110 }
111
112
113 /*
114  * lprintf()  ...   Write logging information
115  */
116 void lprintf(enum LogLevel loglevel, const char *format, ...) {   
117         va_list arg_ptr;
118         va_start(arg_ptr, format);
119         vlprintf(loglevel, format, arg_ptr);
120         va_end(arg_ptr);
121 }
122
123 void vlprintf(enum LogLevel loglevel, const char *format, va_list arg_ptr)
124 {
125         char buf[SIZ], buf2[SIZ];
126
127         if (enable_syslog) {
128                 vsyslog((syslog_facility | loglevel), format, arg_ptr);
129         }
130
131         /* stderr output code */
132         if (enable_syslog || running_as_daemon) return;
133
134         /* if we run in forground and syslog is disabled, log to terminal */
135         if (loglevel <= verbosity) { 
136                 struct timeval tv;
137                 struct tm tim;
138                 time_t unixtime;
139
140                 gettimeofday(&tv, NULL);
141                 /* Promote to time_t; types differ on some OSes (like darwin) */
142                 unixtime = tv.tv_sec;
143                 localtime_r(&unixtime, &tim);
144                 if (CC->cs_pid != 0) {
145                         sprintf(buf,
146                                 "%04d/%02d/%02d %2d:%02d:%02d.%06ld [%3d] ",
147                                 tim.tm_year + 1900, tim.tm_mon + 1,
148                                 tim.tm_mday, tim.tm_hour, tim.tm_min,
149                                 tim.tm_sec, (long)tv.tv_usec,
150                                 CC->cs_pid);
151                 } else {
152                         sprintf(buf,
153                                 "%04d/%02d/%02d %2d:%02d:%02d.%06ld ",
154                                 tim.tm_year + 1900, tim.tm_mon + 1,
155                                 tim.tm_mday, tim.tm_hour, tim.tm_min,
156                                 tim.tm_sec, (long)tv.tv_usec);
157                 }
158                 vsprintf(buf2, format, arg_ptr);   
159
160                 fprintf(stderr, "%s%s", buf, buf2);
161                 fflush(stderr);
162         }
163 }   
164
165
166
167 /*
168  * Signal handler to shut down the server.
169  */
170
171 volatile int exit_signal = 0;
172 volatile int shutdown_and_halt = 0;
173 volatile int restart_server = 0;
174 volatile int running_as_daemon = 0;
175
176 static RETSIGTYPE signal_cleanup(int signum) {
177         CtdlLogPrintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum);
178 #ifdef THREADS_USESIGNALS
179         if (CT)
180         {
181                 CtdlLogPrintf(CTDL_DEBUG, "Thread \"%s\" caught signal %d.\n", CT->name, signum);
182                 CT->signal = signum;
183         }
184         else
185 #endif
186                 exit_signal = signum;
187 }
188
189
190
191 /*
192  * Some initialization stuff...
193  */
194 void init_sysdep(void) {
195         sigset_t set;
196
197         /* Avoid vulnerabilities related to FD_SETSIZE if we can. */
198 #ifdef FD_SETSIZE
199 #ifdef RLIMIT_NOFILE
200         struct rlimit rl;
201         getrlimit(RLIMIT_NOFILE, &rl);
202         rl.rlim_cur = FD_SETSIZE;
203         rl.rlim_max = FD_SETSIZE;
204         setrlimit(RLIMIT_NOFILE, &rl);
205 #endif
206 #endif
207
208         /* If we've got OpenSSL, we're going to use it. */
209 #ifdef HAVE_OPENSSL
210         init_ssl();
211 #endif
212
213         /*
214          * Set up a place to put thread-specific data.
215          * We only need a single pointer per thread - it points to the
216          * CitContext structure (in the ContextList linked list) of the
217          * session to which the calling thread is currently bound.
218          */
219         if (pthread_key_create(&MyConKey, NULL) != 0) {
220                 CtdlLogPrintf(CTDL_CRIT, "Can't create TSD key: %s\n",
221                         strerror(errno));
222         }
223
224         /*
225          * The action for unexpected signals and exceptions should be to
226          * call signal_cleanup() to gracefully shut down the server.
227          */
228         sigemptyset(&set);
229         sigaddset(&set, SIGINT);
230         sigaddset(&set, SIGQUIT);
231         sigaddset(&set, SIGHUP);
232         sigaddset(&set, SIGTERM);
233         // sigaddset(&set, SIGSEGV);    commented out because
234         // sigaddset(&set, SIGILL);     we want core dumps
235         // sigaddset(&set, SIGBUS);
236         sigprocmask(SIG_UNBLOCK, &set, NULL);
237
238         signal(SIGINT, signal_cleanup);
239         signal(SIGQUIT, signal_cleanup);
240         signal(SIGHUP, signal_cleanup);
241         signal(SIGTERM, signal_cleanup);
242         // signal(SIGSEGV, signal_cleanup);     commented out because
243         // signal(SIGILL, signal_cleanup);      we want core dumps
244         // signal(SIGBUS, signal_cleanup);
245
246         /*
247          * Do not shut down the server on broken pipe signals, otherwise the
248          * whole Citadel service would come down whenever a single client
249          * socket breaks.
250          */
251         signal(SIGPIPE, SIG_IGN);
252 }
253
254
255
256
257 /*
258  * This is a generic function to set up a master socket for listening on
259  * a TCP port.  The server shuts down if the bind fails.
260  *
261  */
262 int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormessage)
263 {
264         struct sockaddr_in sin;
265         int s, i;
266         int actual_queue_len;
267
268         actual_queue_len = queue_len;
269         if (actual_queue_len < 5) actual_queue_len = 5;
270
271         memset(&sin, 0, sizeof(sin));
272         sin.sin_family = AF_INET;
273         sin.sin_port = htons((u_short)port_number);
274         if (ip_addr == NULL) {
275                 sin.sin_addr.s_addr = INADDR_ANY;
276         }
277         else {
278                 sin.sin_addr.s_addr = inet_addr(ip_addr);
279         }
280                                                                                 
281         if (sin.sin_addr.s_addr == !INADDR_ANY) {
282                 sin.sin_addr.s_addr = INADDR_ANY;
283         }
284
285         s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
286
287         if (s < 0) {
288                 *errormessage = (char*) malloc(SIZ + 1);
289                 snprintf(*errormessage, SIZ, 
290                                  "citserver: Can't create a socket: %s",
291                                  strerror(errno));
292                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
293                 return(-1);
294         }
295
296         i = 1;
297         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
298
299         if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
300                 *errormessage = (char*) malloc(SIZ + 1);
301                 snprintf(*errormessage, SIZ, 
302                                  "citserver: Can't bind: %s",
303                                  strerror(errno));
304                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
305                 close(s);
306                 return(-1);
307         }
308
309         /* set to nonblock - we need this for some obscure situations */
310         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
311                 *errormessage = (char*) malloc(SIZ + 1);
312                 snprintf(*errormessage, SIZ, 
313                                  "citserver: Can't set socket to non-blocking: %s",
314                                  strerror(errno));
315                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
316                 close(s);
317                 return(-1);
318         }
319
320         if (listen(s, actual_queue_len) < 0) {
321                 *errormessage = (char*) malloc(SIZ + 1);
322                 snprintf(*errormessage, SIZ, 
323                                  "citserver: Can't listen: %s",
324                                  strerror(errno));
325                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
326                 close(s);
327                 return(-1);
328         }
329
330         return(s);
331 }
332
333
334
335 /*
336  * Create a Unix domain socket and listen on it
337  */
338 int ig_uds_server(char *sockpath, int queue_len, char **errormessage)
339 {
340         struct sockaddr_un addr;
341         int s;
342         int i;
343         int actual_queue_len;
344
345         actual_queue_len = queue_len;
346         if (actual_queue_len < 5) actual_queue_len = 5;
347
348         i = unlink(sockpath);
349         if (i != 0) if (errno != ENOENT) {
350                 *errormessage = (char*) malloc(SIZ + 1);
351                 snprintf(*errormessage, SIZ, "citserver: can't unlink %s: %s",
352                         sockpath, strerror(errno));
353                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
354                 return(-1);
355         }
356
357         memset(&addr, 0, sizeof(addr));
358         addr.sun_family = AF_UNIX;
359         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
360
361         s = socket(AF_UNIX, SOCK_STREAM, 0);
362         if (s < 0) {
363                 *errormessage = (char*) malloc(SIZ + 1);
364                 snprintf(*errormessage, SIZ, 
365                          "citserver: Can't create a socket: %s",
366                          strerror(errno));
367                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
368                 return(-1);
369         }
370
371         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
372                 *errormessage = (char*) malloc(SIZ + 1);
373                 snprintf(*errormessage, SIZ, 
374                          "citserver: Can't bind: %s",
375                          strerror(errno));
376                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
377                 return(-1);
378         }
379
380         /* set to nonblock - we need this for some obscure situations */
381         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
382                 *errormessage = (char*) malloc(SIZ + 1);
383                 snprintf(*errormessage, SIZ, 
384                          "citserver: Can't set socket to non-blocking: %s",
385                          strerror(errno));
386                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
387                 close(s);
388                 return(-1);
389         }
390
391         if (listen(s, actual_queue_len) < 0) {
392                 *errormessage = (char*) malloc(SIZ + 1);
393                 snprintf(*errormessage, SIZ, 
394                          "citserver: Can't listen: %s",
395                          strerror(errno));
396                 CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage);
397                 return(-1);
398         }
399
400         chmod(sockpath, S_ISGID|S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
401         return(s);
402 }
403
404
405
406 /*
407  * Return a pointer to the CitContext structure bound to the thread which
408  * called this function.  If there's no such binding (for example, if it's
409  * called by the housekeeper thread) then a generic 'master' CC is returned.
410  *
411  * This function is used *VERY* frequently and must be kept small.
412  */
413 struct CitContext *MyContext(void) {
414
415         register struct CitContext *c;
416
417         return ((c = (struct CitContext *) pthread_getspecific(MyConKey),
418                 c == NULL) ? &masterCC : c
419         );
420 }
421
422
423 /*
424  * Initialize a new context and place it in the list.  The session number
425  * used to be the PID (which is why it's called cs_pid), but that was when we
426  * had one process per session.  Now we just assign them sequentially, starting
427  * at 1 (don't change it to 0 because masterCC uses 0).
428  */
429 struct CitContext *CreateNewContext(void) {
430         struct CitContext *me;
431         static int next_pid = 0;
432
433         me = (struct CitContext *) malloc(sizeof(struct CitContext));
434         if (me == NULL) {
435                 CtdlLogPrintf(CTDL_ALERT, "citserver: can't allocate memory!!\n");
436                 return NULL;
437         }
438         memset(me, 0, sizeof(struct CitContext));
439
440         /* The new context will be created already in the CON_EXECUTING state
441          * in order to prevent another thread from grabbing it while it's
442          * being set up.
443          */
444         me->state = CON_EXECUTING;
445
446         /*
447          * Generate a unique session number and insert this context into
448          * the list.
449          */
450         begin_critical_section(S_SESSION_TABLE);
451         me->cs_pid = ++next_pid;
452         me->prev = NULL;
453         me->next = ContextList;
454         ContextList = me;
455         if (me->next != NULL) {
456                 me->next->prev = me;
457         }
458         ++num_sessions;
459         end_critical_section(S_SESSION_TABLE);
460         return(me);
461 }
462
463
464 /*
465  * The following functions implement output buffering. If the kernel supplies
466  * native TCP buffering (Linux & *BSD), use that; otherwise, emulate it with
467  * user-space buffering.
468  */
469 #ifndef HAVE_DARWIN
470 #ifdef TCP_CORK
471 #       define HAVE_TCP_BUFFERING
472 #else
473 #       ifdef TCP_NOPUSH
474 #               define HAVE_TCP_BUFFERING
475 #               define TCP_CORK TCP_NOPUSH
476 #       endif
477 #endif /* TCP_CORK */
478 #endif /* HAVE_DARWIN */
479
480 #ifdef HAVE_TCP_BUFFERING
481 static unsigned on = 1, off = 0;
482 void buffer_output(void) {
483         struct CitContext *ctx = MyContext();
484         setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
485         ctx->buffering = 1;
486 }
487
488 void unbuffer_output(void) {
489         struct CitContext *ctx = MyContext();
490         setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
491         ctx->buffering = 0;
492 }
493
494 void flush_output(void) {
495         struct CitContext *ctx = MyContext();
496         setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
497         setsockopt(ctx->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
498 }
499 #else 
500 #ifdef HAVE_DARWIN
501 /* Stub functions for Darwin/OS X where TCP buffering isn't liked at all */
502 void buffer_output(void) {
503         CC->buffering = 0;
504 }
505 void unbuffer_output(void) {
506         CC->buffering = 0;
507 }
508 void flush_output(void) {
509 }
510 #else
511 void buffer_output(void) {
512         if (CC->buffering == 0) {
513                 CC->buffering = 1;
514                 CC->buffer_len = 0;
515                 CC->output_buffer = malloc(SIZ);
516         }
517 }
518
519 void flush_output(void) {
520         if (CC->buffering == 1) {
521                 client_write(CC->output_buffer, CC->buffer_len);
522                 CC->buffer_len = 0;
523         }
524 }
525
526 void unbuffer_output(void) {
527         if (CC->buffering == 1) {
528                 CC->buffering = 0;
529                 /* We don't call flush_output because we can't. */
530                 client_write(CC->output_buffer, CC->buffer_len);
531                 CC->buffer_len = 0;
532                 free(CC->output_buffer);
533                 CC->output_buffer = NULL;
534         }
535 }
536 #endif /* HAVE_DARWIN */
537 #endif /* HAVE_TCP_BUFFERING */
538
539
540
541 /*
542  * client_write()   ...    Send binary data to the client.
543  */
544 void client_write(char *buf, int nbytes)
545 {
546         int bytes_written = 0;
547         int retval;
548 #ifndef HAVE_TCP_BUFFERING
549         int old_buffer_len = 0;
550 #endif
551         t_context *Ctx;
552
553         Ctx = CC;
554         if (Ctx->redirect_buffer != NULL) {
555                 if ((Ctx->redirect_len + nbytes + 2) >= Ctx->redirect_alloc) {
556                         Ctx->redirect_alloc = (Ctx->redirect_alloc * 2) + nbytes;
557                         Ctx->redirect_buffer = realloc(Ctx->redirect_buffer,
558                                                 Ctx->redirect_alloc);
559                 }
560                 memcpy(&Ctx->redirect_buffer[Ctx->redirect_len], buf, nbytes);
561                 Ctx->redirect_len += nbytes;
562                 Ctx->redirect_buffer[Ctx->redirect_len] = 0;
563                 return;
564         }
565
566 #ifndef HAVE_TCP_BUFFERING
567         /* If we're buffering for later, do that now. */
568         if (Ctx->buffering) {
569                 old_buffer_len = Ctx->buffer_len;
570                 Ctx->buffer_len += nbytes;
571                 Ctx->output_buffer = realloc(Ctx->output_buffer, Ctx->buffer_len);
572                 memcpy(&Ctx->output_buffer[old_buffer_len], buf, nbytes);
573                 return;
574         }
575 #endif
576
577         /* Ok, at this point we're not buffering.  Go ahead and write. */
578
579 #ifdef HAVE_OPENSSL
580         if (Ctx->redirect_ssl) {
581                 client_write_ssl(buf, nbytes);
582                 return;
583         }
584 #endif
585
586         while (bytes_written < nbytes) {
587                 retval = write(Ctx->client_socket, &buf[bytes_written],
588                         nbytes - bytes_written);
589                 if (retval < 1) {
590                         CtdlLogPrintf(CTDL_ERR,
591                                 "client_write(%d bytes) failed: %s (%d)\n",
592                                 nbytes - bytes_written,
593                                 strerror(errno), errno);
594                         cit_backtrace();
595                         // CtdlLogPrintf(CTDL_DEBUG, "Tried to send: %s",  &buf[bytes_written]);
596                         Ctx->kill_me = 1;
597                         return;
598                 }
599                 bytes_written = bytes_written + retval;
600         }
601 }
602
603
604 /*
605  * cprintf()  ...   Send formatted printable data to the client.   It is
606  *                implemented in terms of client_write() but remains in
607  *                sysdep.c in case we port to somewhere without va_args...
608  */
609 void cprintf(const char *format, ...) {   
610         va_list arg_ptr;   
611         char buf[1024];   
612    
613         va_start(arg_ptr, format);   
614         if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
615                 buf[sizeof buf - 2] = '\n';
616         client_write(buf, strlen(buf)); 
617         va_end(arg_ptr);
618 }   
619
620
621 /*
622  * Read data from the client socket.
623  * Return values are:
624  *      1       Requested number of bytes has been read.
625  *      0       Request timed out.
626  *      -1      The socket is broken.
627  * If the socket breaks, the session will be terminated.
628  */
629 int client_read_to(char *buf, int bytes, int timeout)
630 {
631         int len,rlen;
632         fd_set rfds;
633         int fd;
634         struct timeval tv;
635         int retval;
636
637 #ifdef HAVE_OPENSSL
638         if (CC->redirect_ssl) {
639                 return (client_read_ssl(buf, bytes, timeout));
640         }
641 #endif
642         len = 0;
643         fd = CC->client_socket;
644         while(len<bytes) {
645                 FD_ZERO(&rfds);
646                 FD_SET(fd, &rfds);
647                 tv.tv_sec = timeout;
648                 tv.tv_usec = 0;
649
650                 retval = select( (fd)+1, 
651                                  &rfds, NULL, NULL, &tv);
652
653                 if (FD_ISSET(fd, &rfds) == 0) {
654                         return(0);
655                 }
656
657                 rlen = read(fd, &buf[len], bytes-len);
658                 if (rlen<1) {
659                         /* The socket has been disconnected! */
660                         CC->kill_me = 1;
661                         return(-1);
662                 }
663                 len = len + rlen;
664         }
665         return(1);
666 }
667
668 /*
669  * Read data from the client socket with default timeout.
670  * (This is implemented in terms of client_read_to() and could be
671  * justifiably moved out of sysdep.c)
672  */
673 INLINE int client_read(char *buf, int bytes)
674 {
675         return(client_read_to(buf, bytes, config.c_sleeping));
676 }
677
678
679 /*
680  * client_getln()   ...   Get a LF-terminated line of text from the client.
681  * (This is implemented in terms of client_read() and could be
682  * justifiably moved out of sysdep.c)
683  */
684 int client_getln(char *buf, int bufsize)
685 {
686         int i, retval;
687
688         /* Read one character at a time.
689          */
690         for (i = 0;;i++) {
691                 retval = client_read(&buf[i], 1);
692                 if (retval != 1 || buf[i] == '\n' || i == (bufsize-1))
693                         break;
694         }
695
696         /* If we got a long line, discard characters until the newline.
697          */
698         if (i == (bufsize-1))
699                 while (buf[i] != '\n' && retval == 1)
700                         retval = client_read(&buf[i], 1);
701
702         /* Strip the trailing LF, and the trailing CR if present.
703          */
704         buf[i] = 0;
705         while ( (i > 0)
706                 && ( (buf[i - 1]==13)
707                      || ( buf[i - 1]==10)) ) {
708                 i--;
709                 buf[i] = 0;
710         }
711         if (retval < 0) safestrncpy(&buf[i], "000", bufsize - i);
712         return(retval);
713 }
714
715
716 /*
717  * Cleanup any contexts that are left lying around
718  */
719 void context_cleanup(void)
720 {
721         struct CitContext *ptr = NULL;
722         struct CitContext *rem = NULL;
723
724         /*
725          * Clean up the contexts.
726          * There are no threads so no critical_section stuff is needed.
727          */
728         ptr = ContextList;
729         
730         /* We need to update the ContextList because some modules may want to itterate it
731          * Question is should we NULL it before iterating here or should we just keep updating it
732          * as we remove items?
733          *
734          * Answer is to NULL it first to prevent modules from doing any actions on the list at all
735          */
736         ContextList=NULL;
737         while (ptr != NULL){
738                 /* Remove the session from the active list */
739                 rem = ptr->next;
740                 --num_sessions;
741                 
742                 lprintf(CTDL_DEBUG, "Purging session %d\n", ptr->cs_pid);
743                 RemoveContext(ptr);
744                 free (ptr);
745                 ptr = rem;
746         }
747 }
748
749
750 /*
751  * The system-dependent part of master_cleanup() - close the master socket.
752  */
753 void sysdep_master_cleanup(void) {
754         struct ServiceFunctionHook *serviceptr;
755         
756         /*
757          * close all protocol master sockets
758          */
759         for (serviceptr = ServiceHookTable; serviceptr != NULL;
760             serviceptr = serviceptr->next ) {
761
762                 if (serviceptr->tcp_port > 0)
763                         CtdlLogPrintf(CTDL_INFO, "Closing listener on port %d\n",
764                                 serviceptr->tcp_port);
765
766                 if (serviceptr->sockpath != NULL)
767                         CtdlLogPrintf(CTDL_INFO, "Closing listener on '%s'\n",
768                                 serviceptr->sockpath);
769
770                 close(serviceptr->msock);
771
772                 /* If it's a Unix domain socket, remove the file. */
773                 if (serviceptr->sockpath != NULL) {
774                         unlink(serviceptr->sockpath);
775                 }
776         }
777         
778         context_cleanup();
779         
780 #ifdef HAVE_OPENSSL
781         destruct_ssl();
782 #endif
783         CtdlDestroyProtoHooks();
784         CtdlDestroyDeleteHooks();
785         CtdlDestroyXmsgHooks();
786         CtdlDestroyNetprocHooks();
787         CtdlDestroyUserHooks();
788         CtdlDestroyMessageHook();
789         CtdlDestroyCleanupHooks();
790         CtdlDestroyFixedOutputHooks();  
791         CtdlDestroySessionHooks();
792         CtdlDestroyServiceHook();
793         CtdlDestroyRoomHooks();
794         CtdlDestroyDirectoryServiceFuncs();
795         #ifdef HAVE_BACKTRACE
796         eCrash_Uninit();
797         #endif
798 }
799
800
801
802 /*
803  * Terminate another session.
804  * (This could justifiably be moved out of sysdep.c because it
805  * no longer does anything that is system-dependent.)
806  */
807 void kill_session(int session_to_kill) {
808         struct CitContext *ptr;
809
810         begin_critical_section(S_SESSION_TABLE);
811         for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
812                 if (ptr->cs_pid == session_to_kill) {
813                         ptr->kill_me = 1;
814                 }
815         }
816         end_critical_section(S_SESSION_TABLE);
817 }
818
819 pid_t current_child;
820 void graceful_shutdown(int signum) {
821         kill(current_child, signum);
822         unlink(file_pid_file);
823         exit(0);
824 }
825
826
827 /*
828  * Start running as a daemon.
829  */
830 void start_daemon(int unused) {
831         int status = 0;
832         pid_t child = 0;
833         FILE *fp;
834         int do_restart = 0;
835
836         current_child = 0;
837
838         /* Close stdin/stdout/stderr and replace them with /dev/null.
839          * We don't just call close() because we don't want these fd's
840          * to be reused for other files.
841          */
842         chdir(ctdl_run_dir);
843
844         child = fork();
845         if (child != 0) {
846                 exit(0);
847         }
848         
849         signal(SIGHUP, SIG_IGN);
850         signal(SIGINT, SIG_IGN);
851         signal(SIGQUIT, SIG_IGN);
852
853         setsid();
854         umask(0);
855         freopen("/dev/null", "r", stdin);
856         freopen("/dev/null", "w", stdout);
857         freopen("/dev/null", "w", stderr);
858
859         do {
860                 current_child = fork();
861
862                 signal(SIGTERM, graceful_shutdown);
863         
864                 if (current_child < 0) {
865                         perror("fork");
866                         exit(errno);
867                 }
868         
869                 else if (current_child == 0) {
870                         return; /* continue starting citadel. */
871                 }
872         
873                 else {
874                         fp = fopen(file_pid_file, "w");
875                         if (fp != NULL) {
876                 /*
877                  * NB.. The pid file contains the pid of the actual server.
878                  * This is not the pid of the watcher process
879                  */
880                                 fprintf(fp, ""F_PID_T"\n", current_child);
881                                 fclose(fp);
882                         }
883                         waitpid(current_child, &status, 0);
884                 }
885
886                 do_restart = 0;
887
888                 /* Did the main process exit with an actual exit code? */
889                 if (WIFEXITED(status)) {
890
891                         /* Exit code 0 means the watcher should exit */
892                         if (WEXITSTATUS(status) == 0) {
893                                 do_restart = 0;
894                         }
895
896                         /* Exit code 101-109 means the watcher should exit */
897                         else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
898                                 do_restart = 0;
899                         }
900
901                         /* Any other exit code means we should restart. */
902                         else {
903                                 do_restart = 1;
904                         }
905                 }
906
907                 /* Any other type of termination (signals, etc.) should also restart. */
908                 else {
909                         do_restart = 1;
910                 }
911
912         } while (do_restart);
913
914         unlink(file_pid_file);
915         exit(WEXITSTATUS(status));
916 }
917
918
919
920 /*
921  * Generic routine to convert a login name to a full name (gecos)
922  * Returns nonzero if a conversion took place
923  */
924 int convert_login(char NameToConvert[]) {
925         struct passwd *pw;
926         int a;
927
928         pw = getpwnam(NameToConvert);
929         if (pw == NULL) {
930                 return(0);
931         }
932         else {
933                 strcpy(NameToConvert, pw->pw_gecos);
934                 for (a=0; a<strlen(NameToConvert); ++a) {
935                         if (NameToConvert[a] == ',') NameToConvert[a] = 0;
936                 }
937                 return(1);
938         }
939 }
940
941 /*
942  * Purge all sessions which have the 'kill_me' flag set.
943  * This function has code to prevent it from running more than once every
944  * few seconds, because running it after every single unbind would waste a lot
945  * of CPU time and keep the context list locked too much.  To force it to run
946  * anyway, set "force" to nonzero.
947  */
948 void dead_session_purge(int force) {
949         struct CitContext *ptr, *ptr2;          /* general-purpose utility pointer */
950         struct CitContext *rem = NULL;  /* list of sessions to be destroyed */
951         
952         if (force == 0) {
953                 if ( (time(NULL) - last_purge) < 5 ) {
954                         return; /* Too soon, go away */
955                 }
956         }
957         time(&last_purge);
958
959         if (try_critical_section(S_SESSION_TABLE))
960                 return;
961                 
962         ptr = ContextList;
963         while (ptr) {
964                 ptr2 = ptr;
965                 ptr = ptr->next;
966                 
967                 if ( (ptr2->state == CON_IDLE) && (ptr2->kill_me) ) {
968                         /* Remove the session from the active list */
969                         if (ptr2->prev) {
970                                 ptr2->prev->next = ptr2->next;
971                         }
972                         else {
973                                 ContextList = ptr2->next;
974                         }
975                         if (ptr2->next) {
976                                 ptr2->next->prev = ptr2->prev;
977                         }
978
979                         --num_sessions;
980                         /* And put it on our to-be-destroyed list */
981                         ptr2->next = rem;
982                         rem = ptr2;
983                 }
984         }
985         end_critical_section(S_SESSION_TABLE);
986
987         /* Now that we no longer have the session list locked, we can take
988          * our time and destroy any sessions on the to-be-killed list, which
989          * is allocated privately on this thread's stack.
990          */
991         while (rem != NULL) {
992                 CtdlLogPrintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid);
993                 RemoveContext(rem);
994                 ptr = rem;
995                 rem = rem->next;
996                 free(ptr);
997         }
998 }
999
1000
1001
1002
1003
1004 /*
1005  * masterCC is the context we use when not attached to a session.  This
1006  * function initializes it.
1007  */
1008 void InitializeMasterCC(void) {
1009         memset(&masterCC, 0, sizeof(struct CitContext));
1010         masterCC.internal_pgm = 1;
1011         masterCC.cs_pid = 0;
1012 }
1013
1014
1015
1016
1017
1018
1019 /*
1020  * Bind a thread to a context.  (It's inline merely to speed things up.)
1021  */
1022 INLINE void become_session(struct CitContext *which_con) {
1023         pthread_setspecific(MyConKey, (void *)which_con );
1024 }
1025
1026
1027
1028 /* 
1029  * This loop just keeps going and going and going...
1030  */     
1031 void *worker_thread(void *arg) {
1032         int i;
1033         int highest;
1034         struct CitContext *ptr;
1035         struct CitContext *bind_me = NULL;
1036         fd_set readfds;
1037         int retval = 0;
1038         struct CitContext *con= NULL;   /* Temporary context pointer */
1039         struct ServiceFunctionHook *serviceptr;
1040         int ssock;                      /* Descriptor for client socket */
1041         struct timeval tv;
1042         int force_purge = 0;
1043         int m;
1044         
1045
1046         while (!CtdlThreadCheckStop()) {
1047
1048                 /* make doubly sure we're not holding any stale db handles
1049                  * which might cause a deadlock.
1050                  */
1051                 cdb_check_handles();
1052 do_select:      force_purge = 0;
1053                 bind_me = NULL;         /* Which session shall we handle? */
1054
1055                 /* Initialize the fdset. */
1056                 FD_ZERO(&readfds);
1057                 highest = 0;
1058
1059                 begin_critical_section(S_SESSION_TABLE);
1060                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
1061                         if (ptr->state == CON_IDLE) {
1062                                 FD_SET(ptr->client_socket, &readfds);
1063                                 if (ptr->client_socket > highest)
1064                                         highest = ptr->client_socket;
1065                         }
1066                         if ((bind_me == NULL) && (ptr->state == CON_READY)) {
1067                                 bind_me = ptr;
1068                                 ptr->state = CON_EXECUTING;
1069                         }
1070                 }
1071                 end_critical_section(S_SESSION_TABLE);
1072
1073                 if (bind_me) {
1074                         goto SKIP_SELECT;
1075                 }
1076
1077                 /* If we got this far, it means that there are no sessions
1078                  * which a previous thread marked for attention, so we go
1079                  * ahead and get ready to select().
1080                  */
1081
1082                 /* First, add the various master sockets to the fdset. */
1083                 for (serviceptr = ServiceHookTable; serviceptr != NULL;
1084                 serviceptr = serviceptr->next ) {
1085                         m = serviceptr->msock;
1086                         FD_SET(m, &readfds);
1087                         if (m > highest) {
1088                                 highest = m;
1089                         }
1090                 }
1091
1092                 if (!CtdlThreadCheckStop()) {
1093                         tv.tv_sec = 1;          /* wake up every second if no input */
1094                         tv.tv_usec = 0;
1095                         retval = CtdlThreadSelect(highest + 1, &readfds, NULL, NULL, &tv);
1096                 }
1097
1098                 if (CtdlThreadCheckStop()) return(NULL);
1099
1100                 /* Now figure out who made this select() unblock.
1101                  * First, check for an error or exit condition.
1102                  */
1103                 if (retval < 0) {
1104                         if (errno == EBADF) {
1105                                 CtdlLogPrintf(CTDL_NOTICE, "select() failed: (%s)\n",
1106                                         strerror(errno));
1107                                 goto do_select;
1108                         }
1109                         if (errno != EINTR) {
1110                                 CtdlLogPrintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno));
1111                                 CtdlThreadStopAll();
1112                         } else if (!CtdlThreadCheckStop()) {
1113                                 CtdlLogPrintf(CTDL_DEBUG, "Un handled select failure.\n");
1114                                 goto do_select;
1115                         }
1116                 }
1117                 else if(retval == 0) {
1118                         goto SKIP_SELECT;
1119                 }
1120                 /* Next, check to see if it's a new client connecting
1121                  * on a master socket.
1122                  */
1123                 else for (serviceptr = ServiceHookTable; serviceptr != NULL;
1124                      serviceptr = serviceptr->next ) {
1125
1126                         if (FD_ISSET(serviceptr->msock, &readfds)) {
1127                                 ssock = accept(serviceptr->msock, NULL, 0);
1128                                 if (ssock >= 0) {
1129                                         CtdlLogPrintf(CTDL_DEBUG,
1130                                                 "New client socket %d\n",
1131                                                 ssock);
1132
1133                                         /* The master socket is non-blocking but the client
1134                                          * sockets need to be blocking, otherwise certain
1135                                          * operations barf on FreeBSD.  Not a fatal error.
1136                                          */
1137                                         if (fcntl(ssock, F_SETFL, 0) < 0) {
1138                                                 CtdlLogPrintf(CTDL_EMERG,
1139                                                         "citserver: Can't set socket to blocking: %s\n",
1140                                                         strerror(errno));
1141                                         }
1142
1143                                         /* New context will be created already
1144                                          * set up in the CON_EXECUTING state.
1145                                          */
1146                                         con = CreateNewContext();
1147
1148                                         /* Assign our new socket number to it. */
1149                                         con->client_socket = ssock;
1150                                         con->h_command_function =
1151                                                 serviceptr->h_command_function;
1152                                         con->h_async_function =
1153                                                 serviceptr->h_async_function;
1154                                         con->ServiceName =
1155                                                 serviceptr->ServiceName;
1156                                         
1157                                         /* Determine whether it's a local socket */
1158                                         if (serviceptr->sockpath != NULL)
1159                                                 con->is_local_socket = 1;
1160         
1161                                         /* Set the SO_REUSEADDR socket option */
1162                                         i = 1;
1163                                         setsockopt(ssock, SOL_SOCKET,
1164                                                 SO_REUSEADDR,
1165                                                 &i, sizeof(i));
1166
1167                                         become_session(con);
1168                                         begin_session(con);
1169                                         serviceptr->h_greeting_function();
1170                                         become_session(NULL);
1171                                         con->state = CON_IDLE;
1172                                         goto do_select;
1173                                 }
1174                         }
1175                 }
1176
1177                 /* It must be a client socket.  Find a context that has data
1178                  * waiting on its socket *and* is in the CON_IDLE state.  Any
1179                  * active sockets other than our chosen one are marked as
1180                  * CON_READY so the next thread that comes around can just bind
1181                  * to one without having to select() again.
1182                  */
1183                 begin_critical_section(S_SESSION_TABLE);
1184                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
1185                         if ( (FD_ISSET(ptr->client_socket, &readfds))
1186                            && (ptr->state != CON_EXECUTING) ) {
1187                                 ptr->input_waiting = 1;
1188                                 if (!bind_me) {
1189                                         bind_me = ptr;  /* I choose you! */
1190                                         bind_me->state = CON_EXECUTING;
1191                                 }
1192                                 else {
1193                                         ptr->state = CON_READY;
1194                                 }
1195                         }
1196                 }
1197                 end_critical_section(S_SESSION_TABLE);
1198
1199 SKIP_SELECT:
1200                 /* We're bound to a session */
1201                 if (bind_me != NULL) {
1202                         become_session(bind_me);
1203
1204                         /* If the client has sent a command, execute it. */
1205                         if (CC->input_waiting) {
1206                                 CC->h_command_function();
1207                                 CC->input_waiting = 0;
1208                         }
1209
1210                         /* If there are asynchronous messages waiting and the
1211                          * client supports it, do those now */
1212                         if ((CC->is_async) && (CC->async_waiting)
1213                            && (CC->h_async_function != NULL)) {
1214                                 CC->h_async_function();
1215                                 CC->async_waiting = 0;
1216                         }
1217                         
1218                         force_purge = CC->kill_me;
1219                         become_session(NULL);
1220                         bind_me->state = CON_IDLE;
1221                 }
1222
1223                 dead_session_purge(force_purge);
1224                 do_housekeeping();
1225         }
1226         /* If control reaches this point, the server is shutting down */        
1227         return(NULL);
1228 }
1229
1230
1231
1232
1233 /*
1234  * SyslogFacility()
1235  * Translate text facility name to syslog.h defined value.
1236  */
1237 int SyslogFacility(char *name)
1238 {
1239         int i;
1240         struct
1241         {
1242                 int facility;
1243                 char *name;
1244         }   facTbl[] =
1245         {
1246                 {   LOG_KERN,   "kern"          },
1247                 {   LOG_USER,   "user"          },
1248                 {   LOG_MAIL,   "mail"          },
1249                 {   LOG_DAEMON, "daemon"        },
1250                 {   LOG_AUTH,   "auth"          },
1251                 {   LOG_SYSLOG, "syslog"        },
1252                 {   LOG_LPR,    "lpr"           },
1253                 {   LOG_NEWS,   "news"          },
1254                 {   LOG_UUCP,   "uucp"          },
1255                 {   LOG_LOCAL0, "local0"        },
1256                 {   LOG_LOCAL1, "local1"        },
1257                 {   LOG_LOCAL2, "local2"        },
1258                 {   LOG_LOCAL3, "local3"        },
1259                 {   LOG_LOCAL4, "local4"        },
1260                 {   LOG_LOCAL5, "local5"        },
1261                 {   LOG_LOCAL6, "local6"        },
1262                 {   LOG_LOCAL7, "local7"        },
1263                 {   0,            NULL          }
1264         };
1265         for(i = 0; facTbl[i].name != NULL; i++) {
1266                 if(!strcasecmp(name, facTbl[i].name))
1267                         return facTbl[i].facility;
1268         }
1269         enable_syslog = 0;
1270         return LOG_DAEMON;
1271 }
1272
1273
1274 /********** MEM CHEQQER ***********/
1275
1276 #ifdef DEBUG_MEMORY_LEAKS
1277
1278 #undef malloc
1279 #undef realloc
1280 #undef strdup
1281 #undef free
1282
1283 void *tracked_malloc(size_t size, char *file, int line) {
1284         struct igheap *thisheap;
1285         void *block;
1286
1287         block = malloc(size);
1288         if (block == NULL) return(block);
1289
1290         thisheap = malloc(sizeof(struct igheap));
1291         if (thisheap == NULL) {
1292                 free(block);
1293                 return(NULL);
1294         }
1295
1296         thisheap->block = block;
1297         strcpy(thisheap->file, file);
1298         thisheap->line = line;
1299         
1300         begin_critical_section(S_DEBUGMEMLEAKS);
1301         thisheap->next = igheap;
1302         igheap = thisheap;
1303         end_critical_section(S_DEBUGMEMLEAKS);
1304
1305         return(block);
1306 }
1307
1308
1309 void *tracked_realloc(void *ptr, size_t size, char *file, int line) {
1310         struct igheap *thisheap;
1311         void *block;
1312
1313         block = realloc(ptr, size);
1314         if (block == NULL) return(block);
1315
1316         thisheap = malloc(sizeof(struct igheap));
1317         if (thisheap == NULL) {
1318                 free(block);
1319                 return(NULL);
1320         }
1321
1322         thisheap->block = block;
1323         strcpy(thisheap->file, file);
1324         thisheap->line = line;
1325         
1326         begin_critical_section(S_DEBUGMEMLEAKS);
1327         thisheap->next = igheap;
1328         igheap = thisheap;
1329         end_critical_section(S_DEBUGMEMLEAKS);
1330
1331         return(block);
1332 }
1333
1334
1335
1336 void tracked_free(void *ptr) {
1337         struct igheap *thisheap;
1338         struct igheap *trash;
1339
1340         free(ptr);
1341
1342         if (igheap == NULL) return;
1343         begin_critical_section(S_DEBUGMEMLEAKS);
1344         for (thisheap = igheap; thisheap != NULL; thisheap = thisheap->next) {
1345                 if (thisheap->next != NULL) {
1346                         if (thisheap->next->block == ptr) {
1347                                 trash = thisheap->next;
1348                                 thisheap->next = thisheap->next->next;
1349                                 free(trash);
1350                         }
1351                 }
1352         }
1353         if (igheap->block == ptr) {
1354                 trash = igheap;
1355                 igheap = igheap->next;
1356                 free(trash);
1357         }
1358         end_critical_section(S_DEBUGMEMLEAKS);
1359 }
1360
1361 char *tracked_strdup(const char *s, char *file, int line) {
1362         char *ptr;
1363
1364         if (s == NULL) return(NULL);
1365         ptr = tracked_malloc(strlen(s) + 1, file, line);
1366         if (ptr == NULL) return(NULL);
1367         strncpy(ptr, s, strlen(s));
1368         return(ptr);
1369 }
1370
1371 void dump_heap(void) {
1372         struct igheap *thisheap;
1373
1374         for (thisheap = igheap; thisheap != NULL; thisheap = thisheap->next) {
1375                 CtdlLogPrintf(CTDL_CRIT, "UNFREED: %30s : %d\n",
1376                         thisheap->file, thisheap->line);
1377         }
1378 }
1379
1380 #endif /*  DEBUG_MEMORY_LEAKS */