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