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