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