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