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