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