]> code.citadel.org Git - citadel.git/blob - citadel/server/sysdep.c
Try to close the databases if we segfault
[citadel.git] / citadel / server / sysdep.c
1 // Citadel "system dependent" stuff.
2 //
3 // Here's where we (hopefully) have most parts of the Citadel server that
4 // might need tweaking when run on different operating system variants.
5 //
6 // Copyright (c) 1987-2023 by the citadel.org team
7 //
8 // This program is open source software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License, version 3.
10
11 #include "sysdep.h"
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <syslog.h>
19 #include <sys/syslog.h>
20 #include <execinfo.h>
21 #include <netdb.h>
22 #include <sys/un.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netinet/tcp.h>
28 #include <arpa/inet.h>
29 #define SHOW_ME_VAPPEND_PRINTF
30 #include <libcitadel.h>
31 #include "citserver.h"
32 #include "config.h"
33 #include "ctdl_module.h"
34 #include "sysdep_decls.h"
35 #include "modules/crypto/serv_crypto.h" // Needed for init_ssl, client_write_ssl, client_read_ssl
36 #include "housekeeping.h"
37 #include "context.h"
38
39 // Signal handler to shut down the server.
40 volatile int exit_signal = 0;
41 volatile int shutdown_and_halt = 0;
42 volatile int restart_server = 0;
43 volatile int running_as_daemon = 0;
44
45
46 static RETSIGTYPE signal_cleanup(int signum) {
47         syslog(LOG_DEBUG, "sysdep: caught signal %d", signum);
48         signal(signum, SIG_DFL);
49         exit_signal = signum;
50         server_shutting_down = 1;
51 }
52
53
54 // Some initialization stuff...
55 void init_sysdep(void) {
56         sigset_t set;
57
58         // Avoid vulnerabilities related to FD_SETSIZE if we can.
59 #ifdef FD_SETSIZE
60 #ifdef RLIMIT_NOFILE
61         struct rlimit rl;
62         getrlimit(RLIMIT_NOFILE, &rl);
63         rl.rlim_cur = FD_SETSIZE;
64         rl.rlim_max = FD_SETSIZE;
65         setrlimit(RLIMIT_NOFILE, &rl);
66 #endif
67 #endif
68
69         // If we've got OpenSSL, we're going to use it.
70 #ifdef HAVE_OPENSSL
71         init_ssl();
72 #endif
73
74         if (pthread_key_create(&ThreadKey, NULL) != 0) {                        // TSD for threads
75                 syslog(LOG_ERR, "pthread_key_create() : %m");
76                 exit(CTDLEXIT_THREAD);
77         }
78         
79         if (pthread_key_create(&MyConKey, NULL) != 0) {                         // TSD for sessions
80                 syslog(LOG_CRIT, "sysdep: can't create TSD key: %m");
81                 exit(CTDLEXIT_THREAD);
82         }
83
84         // Interript, hangup, and terminate signals should cause the server to shut down.
85         sigemptyset(&set);
86         sigaddset(&set, SIGINT);
87         sigaddset(&set, SIGHUP);
88         sigaddset(&set, SIGTERM);
89         sigaddset(&set, SIGSEGV);
90         sigprocmask(SIG_UNBLOCK, &set, NULL);
91
92         signal(SIGINT, signal_cleanup);
93         signal(SIGHUP, signal_cleanup);
94         signal(SIGTERM, signal_cleanup);
95         signal(SIGSEGV, signal_cleanup);
96
97         // Do not shut down the server on broken pipe signals, otherwise the
98         // whole Citadel service would come down whenever a single client
99         // socket breaks.
100         signal(SIGPIPE, SIG_IGN);
101 }
102
103
104 // This is a generic function to set up a master socket for listening on
105 // a TCP port.  The server shuts down if the bind fails.  (IPv4/IPv6 version)
106 //
107 // ip_addr      IP address to bind
108 // port_number  port number to bind
109 // queue_len    number of incoming connections to allow in the queue
110 int ctdl_tcp_server(char *ip_addr, int port_number, int queue_len) {
111         struct protoent *p;
112         struct sockaddr_in6 sin6;
113         struct sockaddr_in sin4;
114         int s, i, b;
115         int ip_version = 6;
116
117         memset(&sin6, 0, sizeof(sin6));
118         memset(&sin4, 0, sizeof(sin4));
119         sin6.sin6_family = AF_INET6;
120         sin4.sin_family = AF_INET;
121
122         if (    (ip_addr == NULL)                                                       // any IPv6
123                 || (IsEmptyStr(ip_addr))
124                 || (!strcmp(ip_addr, "*"))
125         ) {
126                 ip_version = 6;
127                 sin6.sin6_addr = in6addr_any;
128         }
129         else if (!strcmp(ip_addr, "0.0.0.0")) {                                         // any IPv4
130                 ip_version = 4;
131                 sin4.sin_addr.s_addr = INADDR_ANY;
132         }
133         else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) {                   // specific IPv4
134                 ip_version = 4;
135                 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
136                         syslog(LOG_ALERT, "tcpserver: inet_pton: %m");
137                         return (-1);
138                 }
139         }
140         else {                                                                          // specific IPv6
141                 ip_version = 6;
142                 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
143                         syslog(LOG_ALERT, "tcpserver: inet_pton: %m");
144                         return (-1);
145                 }
146         }
147
148         if (port_number == 0) {
149                 syslog(LOG_ALERT, "tcpserver: no port number was specified");
150                 return (-1);
151         }
152         sin6.sin6_port = htons((u_short) port_number);
153         sin4.sin_port = htons((u_short) port_number);
154
155         p = getprotobyname("tcp");
156         if (p == NULL) {
157                 syslog(LOG_ALERT, "tcpserver: getprotobyname: %m");
158                 return (-1);
159         }
160
161         s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
162         if (s < 0) {
163                 syslog(LOG_ALERT, "tcpserver: socket: %m");
164                 return (-1);
165         }
166         // Set some socket options that make sense.
167         i = 1;
168         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
169
170         if (ip_version == 6) {
171                 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
172         }
173         else {
174                 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
175         }
176
177         if (b < 0) {
178                 syslog(LOG_ALERT, "tcpserver: bind: %m");
179                 return (-1);
180         }
181
182         fcntl(s, F_SETFL, O_NONBLOCK);
183
184         if (listen(s, ((queue_len >= 5) ? queue_len : 5) ) < 0) {
185                 syslog(LOG_ALERT, "tcpserver: listen: %m");
186                 return (-1);
187         }
188         return (s);
189 }
190
191
192 // Create a Unix domain socket and listen on it
193 int ctdl_uds_server(char *sockpath, int queue_len) {
194         struct sockaddr_un addr;
195         int s;
196         int i;
197         int actual_queue_len;
198 #ifdef HAVE_STRUCT_UCRED
199         int passcred = 1;
200 #endif
201
202         actual_queue_len = queue_len;
203         if (actual_queue_len < 5) actual_queue_len = 5;
204
205         i = unlink(sockpath);
206         if ((i != 0) && (errno != ENOENT)) {
207                 syslog(LOG_ERR, "udsserver: %m");
208                 return(-1);
209         }
210
211         memset(&addr, 0, sizeof(addr));
212         addr.sun_family = AF_UNIX;
213         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
214
215         s = socket(AF_UNIX, SOCK_STREAM, 0);
216         if (s < 0) {
217                 syslog(LOG_ERR, "udsserver: socket: %m");
218                 return(-1);
219         }
220
221         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
222                 syslog(LOG_ERR, "udsserver: bind: %m");
223                 return(-1);
224         }
225
226         // set to nonblock - we need this for some obscure situations
227         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
228                 syslog(LOG_ERR, "udsserver: fcntl: %m");
229                 close(s);
230                 return(-1);
231         }
232
233         if (listen(s, actual_queue_len) < 0) {
234                 syslog(LOG_ERR, "udsserver: listen: %m");
235                 return(-1);
236         }
237
238 #ifdef HAVE_STRUCT_UCRED
239         setsockopt(s, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred));
240 #endif
241
242         chmod(sockpath, S_ISGID|S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
243         return(s);
244 }
245
246
247 // The following functions implement output buffering on operating systems which
248 // support it (such as Linux and various BSD flavors).
249 #ifndef HAVE_DARWIN
250 #ifdef TCP_CORK
251 #       define HAVE_TCP_BUFFERING
252 #else
253 #       ifdef TCP_NOPUSH
254 #               define HAVE_TCP_BUFFERING
255 #               define TCP_CORK TCP_NOPUSH
256 #       endif
257 #endif // TCP_CORK
258 #endif // HAVE_DARWIN
259
260 static unsigned on = 1, off = 0;
261
262 void buffer_output(void) {
263 #ifdef HAVE_TCP_BUFFERING
264 #ifdef HAVE_OPENSSL
265         if (!CC->redirect_ssl)
266 #endif
267                 setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
268 #endif
269 }
270
271 void unbuffer_output(void) {
272 #ifdef HAVE_TCP_BUFFERING
273 #ifdef HAVE_OPENSSL
274         if (!CC->redirect_ssl)
275 #endif
276                 setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
277 #endif
278 }
279
280 void flush_output(void) {
281 #ifdef HAVE_TCP_BUFFERING
282         setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
283         setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
284 #endif
285 }
286
287
288 // close the client socket
289 void client_close(void) {
290         if (!CC) return;
291         if (CC->client_socket <= 0) return;
292         syslog(LOG_DEBUG, "sysdep: closing socket %d", CC->client_socket);
293         close(CC->client_socket);
294         CC->client_socket = -1 ;
295 }
296
297
298 // Send binary data to the client.
299 int client_write(const char *buf, int nbytes) {
300         int bytes_written = 0;
301         int retval;
302 #ifndef HAVE_TCP_BUFFERING
303         int old_buffer_len = 0;
304 #endif
305         fd_set wset;
306         CitContext *Ctx;
307         int fdflags;
308
309         if (nbytes < 1) return(0);
310
311         Ctx = CC;
312         if (Ctx->redirect_buffer != NULL) {
313                 StrBufAppendBufPlain(Ctx->redirect_buffer, buf, nbytes, 0);
314                 return 0;
315         }
316
317 #ifdef HAVE_OPENSSL
318         if (Ctx->redirect_ssl) {
319                 client_write_ssl(buf, nbytes);
320                 return 0;
321         }
322 #endif
323         if (Ctx->client_socket == -1) return -1;
324
325         fdflags = fcntl(Ctx->client_socket, F_GETFL);
326
327         while ((bytes_written < nbytes) && (Ctx->client_socket != -1)){
328                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
329                         FD_ZERO(&wset);
330                         FD_SET(Ctx->client_socket, &wset);
331                         if (select(1, NULL, &wset, NULL, NULL) == -1) {
332                                 if (errno == EINTR) {
333                                         syslog(LOG_DEBUG, "sysdep: client_write(%d bytes) select() interrupted.", nbytes-bytes_written);
334                                         if (server_shutting_down) {
335                                                 CC->kill_me = KILLME_SELECT_INTERRUPTED;
336                                                 return (-1);
337                                         }
338                                         else {
339                                                 // can't trust fd's and stuff so we need to re-create them
340                                                 continue;
341                                         }
342                                 }
343                                 else {
344                                         syslog(LOG_ERR, "sysdep: client_write(%d bytes) select failed: %m", nbytes - bytes_written);
345                                         client_close();
346                                         Ctx->kill_me = KILLME_SELECT_FAILED;
347                                         return -1;
348                                 }
349                         }
350                 }
351
352                 retval = write(Ctx->client_socket, &buf[bytes_written], nbytes - bytes_written);
353                 if (retval < 1) {
354                         syslog(LOG_ERR, "sysdep: client_write(%d bytes) failed: %m", nbytes - bytes_written);
355                         client_close();
356                         Ctx->kill_me = KILLME_WRITE_FAILED;
357                         return -1;
358                 }
359                 bytes_written = bytes_written + retval;
360         }
361         return 0;
362 }
363
364
365 void cputbuf(const StrBuf *Buf) {   
366         client_write(ChrPtr(Buf), StrLength(Buf)); 
367 }   
368
369
370 // Send formatted printable data to the client.
371 // Implemented in terms of client_write() so it's technically not sysdep...
372 void cprintf(const char *format, ...) {   
373         va_list arg_ptr;   
374         char buf[1024];
375         int rc;
376    
377         va_start(arg_ptr, format);   
378         rc = vsnprintf(buf, sizeof buf, format, arg_ptr);
379         if (rc < 0) {
380                 syslog(LOG_ERR,"Console output error occured. Format: %s",format);
381         } else {
382                 if (rc >= sizeof buf) {
383                         // Output was truncated.
384                         // Chop at the end of the buffer
385                         buf[sizeof buf - 2] = '\n';
386                         buf[sizeof buf - 1] = 0;
387                 }
388                 client_write(buf, strlen(buf)); 
389         }
390         va_end(arg_ptr);
391 }
392
393
394 // Read data from the client socket.
395 //
396 // sock         socket fd to read from
397 // buf          buffer to read into 
398 // bytes        number of bytes to read
399 // timeout      Number of seconds to wait before timing out
400 //
401 // Possible return values:
402 //      1       Requested number of bytes has been read.
403 //      0       Request timed out.
404 //      -1      Connection is broken, or other error.
405 int client_read_blob(StrBuf *Target, int bytes, int timeout) {
406         const char *Error;
407         int retval = 0;
408
409 #ifdef HAVE_OPENSSL
410         if (CC->redirect_ssl) {
411                 retval = client_read_sslblob(Target, bytes, timeout);
412                 if (retval < 0) {
413                         syslog(LOG_ERR, "sysdep: client_read_blob() failed");
414                 }
415         }
416         else 
417 #endif
418         {
419                 retval = StrBufReadBLOBBuffered(Target, 
420                                                 CC->RecvBuf.Buf,
421                                                 &CC->RecvBuf.ReadWritePointer,
422                                                 &CC->client_socket,
423                                                 1, 
424                                                 bytes,
425                                                 O_TERM,
426                                                 &Error
427                 );
428                 if (retval < 0) {
429                         syslog(LOG_ERR, "sysdep: client_read_blob() failed: %s", Error);
430                         client_close();
431                         return retval;
432                 }
433         }
434         return retval;
435 }
436
437
438 // to make client_read_random_blob() more efficient, increase buffer size.
439 // just use in greeting function, else your buffer may be flushed
440 void client_set_inbound_buf(long N) {
441         FlushStrBuf(CC->RecvBuf.Buf);
442         ReAdjustEmptyBuf(CC->RecvBuf.Buf, N * SIZ, N * SIZ);
443 }
444
445
446 int client_read_random_blob(StrBuf *Target, int timeout) {
447         int rc;
448
449         rc =  client_read_blob(Target, 1, timeout);
450         if (rc > 0) {
451                 long len;
452                 const char *pch;
453                 
454                 len = StrLength(CC->RecvBuf.Buf);
455                 pch = ChrPtr(CC->RecvBuf.Buf);
456
457                 if (len > 0) {
458                         if (CC->RecvBuf.ReadWritePointer != NULL) {
459                                 len -= CC->RecvBuf.ReadWritePointer - pch;
460                                 pch = CC->RecvBuf.ReadWritePointer;
461                         }
462                         StrBufAppendBufPlain(Target, pch, len, 0);
463                         FlushStrBuf(CC->RecvBuf.Buf);
464                         CC->RecvBuf.ReadWritePointer = NULL;
465                         return StrLength(Target);
466                 }
467                 return rc;
468         }
469         else
470                 return rc;
471 }
472
473
474 int client_read_to(char *buf, int bytes, int timeout) {
475         int rc;
476
477         rc = client_read_blob(CC->MigrateBuf, bytes, timeout);
478         if (rc < 0) {
479                 *buf = '\0';
480                 return rc;
481         }
482         else {
483                 memcpy(buf, 
484                        ChrPtr(CC->MigrateBuf),
485                        StrLength(CC->MigrateBuf) + 1);
486                 FlushStrBuf(CC->MigrateBuf);
487                 return rc;
488         }
489 }
490
491
492 int HaveMoreLinesWaiting(CitContext *ctx) {
493         if (    (ctx->kill_me != 0)
494                 || ( (ctx->RecvBuf.ReadWritePointer == NULL)
495                 && (StrLength(ctx->RecvBuf.Buf) == 0)
496                 && (ctx->client_socket != -1))
497         )
498                 return 0;
499         else
500                 return 1;
501 }
502
503
504 // Read data from the client socket with default timeout.
505 // (This is implemented in terms of client_read_to() and could be
506 // justifiably moved out of sysdep.c)
507 INLINE int client_read(char *buf, int bytes) {
508         return(client_read_to(buf, bytes, CtdlGetConfigInt("c_sleeping")));
509 }
510
511
512 int CtdlClientGetLine(StrBuf *Target) {
513         const char *Error;
514         int rc;
515
516         FlushStrBuf(Target);
517 #ifdef HAVE_OPENSSL
518         if (CC->redirect_ssl) {
519                 rc = client_readline_sslbuffer(Target, CC->RecvBuf.Buf, &CC->RecvBuf.ReadWritePointer, 1);
520                 return rc;
521         }
522         else 
523 #endif
524         {
525                 rc = StrBufTCP_read_buffered_line_fast(Target, 
526                                                        CC->RecvBuf.Buf,
527                                                        &CC->RecvBuf.ReadWritePointer,
528                                                        &CC->client_socket,
529                                                        5,
530                                                        1,
531                                                        &Error
532                 );
533                 return rc;
534         }
535 }
536
537
538 // Get a LF-terminated line of text from the client.
539 // (This is implemented in terms of client_read() and could be
540 // justifiably moved out of sysdep.c)
541 int client_getln(char *buf, int bufsize) {
542         int i, retval;
543         const char *pCh;
544
545         retval = CtdlClientGetLine(CC->MigrateBuf);
546         if (retval < 0)
547           return(retval >= 0);
548
549
550         i = StrLength(CC->MigrateBuf);
551         pCh = ChrPtr(CC->MigrateBuf);
552         // Strip the trailing LF, and the trailing CR if present.
553         if (bufsize <= i)
554                 i = bufsize - 1;
555         while ( (i > 0)
556                 && ( (pCh[i - 1]==13)
557                      || ( pCh[i - 1]==10)) ) {
558                 i--;
559         }
560         memcpy(buf, pCh, i);
561         buf[i] = 0;
562
563         FlushStrBuf(CC->MigrateBuf);
564         if (retval < 0) {
565                 safestrncpy(&buf[i], "000", bufsize - i);
566         }
567         return(retval >= 0);
568 }
569
570
571 // The system-dependent part of master_cleanup() - close the master socket.
572 void sysdep_master_cleanup(void) {
573         context_cleanup();
574 }
575
576
577
578 pid_t current_child;
579 void graceful_shutdown(int signum) {
580         kill(current_child, signum);
581         unlink(file_pid_file);
582         exit(0);
583 }
584
585 int nFireUps = 0;
586 int nFireUpsNonRestart = 0;
587 pid_t ForkedPid = 1;
588
589 // Start running as a daemon.
590 void start_daemon(int unused) {
591         int status = 0;
592         pid_t child = 0;
593         FILE *fp;
594         int do_restart = 0;
595         current_child = 0;
596
597         // Close stdin/stdout/stderr and replace them with /dev/null.
598         // We don't just call close() because we don't want these fd's
599         // to be reused for other files.
600         child = fork();
601         if (child != 0) {
602                 exit(0);
603         }
604         
605         signal(SIGHUP, SIG_IGN);
606         signal(SIGINT, SIG_IGN);
607         signal(SIGQUIT, SIG_IGN);
608
609         setsid();
610         umask(0);
611         if (    (freopen("/dev/null", "r", stdin) != stdin) || 
612                 (freopen("/dev/null", "w", stdout) != stdout) || 
613                 (freopen("/dev/null", "w", stderr) != stderr)
614         ) {
615                 syslog(LOG_ERR, "sysdep: unable to reopen stdio: %m");
616         }
617
618         do {
619                 current_child = fork();
620                 signal(SIGTERM, graceful_shutdown);
621                 if (current_child < 0) {
622                         perror("fork");
623                         exit(errno);
624                 }
625                 else if (current_child == 0) {
626                         return; // continue starting citadel.
627                 }
628                 else {
629                         fp = fopen(file_pid_file, "w");
630                         if (fp != NULL) {
631                                 fprintf(fp, ""F_PID_T"\n", getpid());
632                                 fclose(fp);
633                         }
634                         waitpid(current_child, &status, 0);
635                 }
636                 nFireUpsNonRestart = nFireUps;
637                 
638                 // Exit code 0 means the watcher should exit
639                 if (WIFEXITED(status) && (WEXITSTATUS(status) == CTDLEXIT_SHUTDOWN)) {
640                         do_restart = 0;
641                 }
642
643                 // Exit code 101-109 means the watcher should exit
644                 else if (WIFEXITED(status) && (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109)) {
645                         do_restart = 0;
646                 }
647
648                 // Any other exit code, or no exit code, means we should restart.
649                 else {
650                         do_restart = 1;
651                         nFireUps++;
652                         ForkedPid = current_child;
653                 }
654
655         } while (do_restart);
656
657         unlink(file_pid_file);
658         exit(WEXITSTATUS(status));
659 }
660
661
662 void checkcrash(void) {
663         if (nFireUpsNonRestart != nFireUps) {
664                 StrBuf *CrashMail;
665                 CrashMail = NewStrBuf();
666                 syslog(LOG_ALERT, "sysdep: posting crash message");
667                 StrBufPrintf(CrashMail, 
668                         " \n"
669                         " The Citadel server process (citserver) terminated unexpectedly."
670                         "\n \n"
671                         " This could be the result of a bug in the server program, or some external "
672                         "factor.\n \n"
673                         " You can obtain more information about this by enabling core dumps.\n \n"
674                         " For more information, please see:\n \n"
675                         " http://citadel.org/doku.php?id=faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files"
676                         "\n \n"
677
678                         " If you have already done this, the core dump is likely to be found at %score.%d\n"
679                         ,
680                         ctdl_run_dir, ForkedPid);
681                 CtdlAideMessage(ChrPtr(CrashMail), "Citadel server process terminated unexpectedly");
682                 FreeStrBuf(&CrashMail);
683         }
684 }
685
686
687 // Generic routine to convert a login name to a full name (gecos)
688 // Returns nonzero if a conversion took place
689 int convert_login(char NameToConvert[]) {
690         struct passwd *pw;
691         unsigned int a;
692
693         pw = getpwnam(NameToConvert);
694         if (pw == NULL) {
695                 return(0);
696         }
697         else {
698                 strcpy(NameToConvert, pw->pw_gecos);
699                 for (a=0; a<strlen(NameToConvert); ++a) {
700                         if (NameToConvert[a] == ',') NameToConvert[a] = 0;
701                 }
702                 return(1);
703         }
704 }
705
706
707 void HuntBadSession(void) {
708         int highest;
709         CitContext *ptr;
710         fd_set readfds;
711         struct timeval tv;
712         struct ServiceFunctionHook *serviceptr;
713
714         // Next, add all of the client sockets
715         begin_critical_section(S_SESSION_TABLE);
716         for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
717                 if ((ptr->state == CON_SYS) && (ptr->client_socket == 0))
718                         continue;
719                 // Initialize the fdset.
720                 FD_ZERO(&readfds);
721                 highest = 0;
722                 tv.tv_sec = 0;          // wake up every second if no input
723                 tv.tv_usec = 0;
724
725                 // Don't select on dead sessions, only truly idle ones
726                 if (    (ptr->state == CON_IDLE)
727                         && (ptr->kill_me == 0)
728                         && (ptr->client_socket > 0)
729                 ) {
730                         FD_SET(ptr->client_socket, &readfds);
731                         if (ptr->client_socket > highest)
732                                 highest = ptr->client_socket;
733                         
734                         if ((select(highest + 1, &readfds, NULL, NULL, &tv) < 0) && (errno == EBADF)) {
735                                 // Gotcha!
736                                 syslog(LOG_ERR,
737                                        "sysdep: killing session CC[%d] bad FD: [%d] User[%s] Host[%s:%s]",
738                                         ptr->cs_pid,
739                                         ptr->client_socket,
740                                         ptr->curr_user,
741                                         ptr->cs_host,
742                                         ptr->cs_addr
743                                 );
744                                 ptr->kill_me = 1;
745                                 ptr->client_socket = -1;
746                                 break;
747                         }
748                 }
749         }
750         end_critical_section(S_SESSION_TABLE);
751
752         // First, add the various master sockets to the fdset.
753         for (serviceptr = ServiceHookTable; serviceptr != NULL; serviceptr = serviceptr->next ) {
754
755                 // Initialize the fdset.
756                 highest = 0;
757                 tv.tv_sec = 0;          // wake up every second if no input
758                 tv.tv_usec = 0;
759
760                 FD_SET(serviceptr->msock, &readfds);
761                 if (serviceptr->msock > highest) {
762                         highest = serviceptr->msock;
763                 }
764                 if ((select(highest + 1, &readfds, NULL, NULL, &tv) < 0) && (errno == EBADF)) {
765                         // Gotcha! server socket dead? commit suicide!
766                         syslog(LOG_ERR, "sysdep: found bad FD: %d and its a server socket! Shutting Down!", serviceptr->msock);
767                         server_shutting_down = 1;
768                         break;
769                 }
770         }
771 }
772
773
774 // Main server loop
775 // select() can wake up on any of the following conditions:
776 // 1. A new client connection on a master socket
777 // 2. Received data on a client socket
778 // 3. A timer event
779 // That's why we are blocking on select() and not accept().
780 void *worker_thread(void *blah) {
781         int highest;
782         CitContext *ptr;
783         CitContext *bind_me = NULL;
784         fd_set readfds;
785         int retval = 0;
786         struct timeval tv;
787         int force_purge = 0;
788         struct ServiceFunctionHook *serviceptr;
789         int ssock;                      // Descriptor for client socket
790         CitContext *con = NULL;         // Temporary context pointer
791         int i;
792
793         pthread_mutex_lock(&ThreadCountMutex);
794         ++num_workers;
795         pthread_mutex_unlock(&ThreadCountMutex);
796
797         while (!server_shutting_down) {
798
799                 // make doubly sure we're not holding any stale db handles which might cause a deadlock
800                 cdb_check_handles();
801 do_select:      force_purge = 0;
802                 bind_me = NULL;         // Which session shall we handle?
803
804                 // Initialize the fdset
805                 FD_ZERO(&readfds);
806                 highest = 0;
807
808                 // First, add the various master sockets to the fdset.
809                 for (serviceptr = ServiceHookTable; serviceptr != NULL; serviceptr = serviceptr->next ) {
810                         FD_SET(serviceptr->msock, &readfds);
811                         if (serviceptr->msock > highest) {
812                                 highest = serviceptr->msock;
813                         }
814                 }
815
816                 // Next, add all of the client sockets.
817                 begin_critical_section(S_SESSION_TABLE);
818                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
819                         if ((ptr->state == CON_SYS) && (ptr->client_socket == 0))
820                             continue;
821
822                         // Don't select on dead sessions, only truly idle ones
823                         if (    (ptr->state == CON_IDLE)
824                                 && (ptr->kill_me == 0)
825                                 && (ptr->client_socket > 0)
826                         ) {
827                                 FD_SET(ptr->client_socket, &readfds);
828                                 if (ptr->client_socket > highest)
829                                         highest = ptr->client_socket;
830                         }
831                         if ((bind_me == NULL) && (ptr->state == CON_READY)) {
832                                 bind_me = ptr;
833                                 ptr->state = CON_EXECUTING;
834                                 break;
835                         }
836                         if ((bind_me == NULL) && (ptr->state == CON_GREETING)) {
837                                 bind_me = ptr;
838                                 ptr->state = CON_STARTING;
839                                 break;
840                         }
841                 }
842                 end_critical_section(S_SESSION_TABLE);
843
844                 if (bind_me) {
845                         goto SKIP_SELECT;
846                 }
847
848                 // If we got this far, it means that there are no sessions
849                 // which a previous thread marked for attention, so we go
850                 // ahead and get ready to select().
851
852                 if (!server_shutting_down) {
853                         tv.tv_sec = 1;          // wake up every second if no input
854                         tv.tv_usec = 0;
855                         retval = select(highest + 1, &readfds, NULL, NULL, &tv);
856                 }
857                 else {
858                         --num_workers;
859                         return NULL;
860                 }
861
862                 // Now figure out who made this select() unblock.
863                 // First, check for an error or exit condition.
864                 if (retval < 0) {
865                         if (errno == EBADF) {
866                                 syslog(LOG_ERR, "sysdep: select() failed: %m");
867                                 HuntBadSession();
868                                 goto do_select;
869                         }
870                         if (errno != EINTR) {
871                                 syslog(LOG_ERR, "sysdep: exiting: %m");
872                                 server_shutting_down = 1;
873                                 continue;
874                         } else {
875                                 if (server_shutting_down) {
876                                         --num_workers;
877                                         return(NULL);
878                                 }
879                                 goto do_select;
880                         }
881                 }
882                 else if (retval == 0) {
883                         if (server_shutting_down) {
884                                 --num_workers;
885                                 return(NULL);
886                         }
887                 }
888
889                 // Next, check to see if it's a new client connecting on a master socket.
890
891                 else if ((retval > 0) && (!server_shutting_down)) for (serviceptr = ServiceHookTable; serviceptr != NULL; serviceptr = serviceptr->next) {
892
893                         if (FD_ISSET(serviceptr->msock, &readfds)) {
894                                 ssock = accept(serviceptr->msock, NULL, 0);
895                                 if (ssock >= 0) {
896                                         syslog(LOG_DEBUG, "sysdep: new client socket %d", ssock);
897
898                                         // The master socket is non-blocking but the client
899                                         // sockets need to be blocking, otherwise certain
900                                         // operations barf on FreeBSD.  Not a fatal error.
901                                         if (fcntl(ssock, F_SETFL, 0) < 0) {
902                                                 syslog(LOG_ERR, "sysdep: Can't set socket to blocking: %m");
903                                         }
904
905                                         // New context will be created already
906                                         // set up in the CON_EXECUTING state.
907                                         con = CreateNewContext();
908
909                                         // Assign our new socket number to it.
910                                         con->tcp_port = serviceptr->tcp_port;
911                                         con->client_socket = ssock;
912                                         con->h_command_function = serviceptr->h_command_function;
913                                         con->h_async_function = serviceptr->h_async_function;
914                                         con->h_greeting_function = serviceptr->h_greeting_function;
915                                         con->ServiceName = serviceptr->ServiceName;
916                                         
917                                         // Connections on a local client are always from the same host
918                                         if (serviceptr->sockpath != NULL) {
919                                                 con->is_local_client = 1;
920                                         }
921         
922                                         // Set the SO_REUSEADDR socket option
923                                         i = 1;
924                                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
925                                         con->state = CON_GREETING;
926                                         retval--;
927                                         if (retval == 0)
928                                                 break;
929                                 }
930                         }
931                 }
932
933                 // It must be a client socket.  Find a context that has data
934                 // waiting on its socket *and* is in the CON_IDLE state.  Any
935                 // active sockets other than our chosen one are marked as
936                 // CON_READY so the next thread that comes around can just bind
937                 // to one without having to select() again.
938                 begin_critical_section(S_SESSION_TABLE);
939                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
940                         int checkfd = ptr->client_socket;
941                         if ((checkfd != -1) && (ptr->state == CON_IDLE) ){
942                                 if (FD_ISSET(checkfd, &readfds)) {
943                                         ptr->input_waiting = 1;
944                                         if (!bind_me) {
945                                                 bind_me = ptr;  // I choose you!
946                                                 bind_me->state = CON_EXECUTING;
947                                         }
948                                         else {
949                                                 ptr->state = CON_READY;
950                                         }
951                                 } else if ((ptr->is_async) && (ptr->async_waiting) && (ptr->h_async_function)) {
952                                         if (!bind_me) {
953                                                 bind_me = ptr;  // I choose you!
954                                                 bind_me->state = CON_EXECUTING;
955                                         }
956                                         else {
957                                                 ptr->state = CON_READY;
958                                         }
959                                 }
960                         }
961                 }
962                 end_critical_section(S_SESSION_TABLE);
963
964 SKIP_SELECT:
965                 // We're bound to a session
966                 pthread_mutex_lock(&ThreadCountMutex);
967                 ++active_workers;
968                 pthread_mutex_unlock(&ThreadCountMutex);
969
970                 if (bind_me != NULL) {
971                         become_session(bind_me);
972
973                         if (bind_me->state == CON_STARTING) {
974                                 bind_me->state = CON_EXECUTING;
975                                 begin_session(bind_me);
976                                 bind_me->h_greeting_function();
977                         }
978                         // If the client has sent a command, execute it.
979                         if (CC->input_waiting) {
980                                 CC->h_command_function();
981
982                                 while (HaveMoreLinesWaiting(CC))
983                                        CC->h_command_function();
984
985                                 CC->input_waiting = 0;
986                         }
987
988                         // If there are asynchronous messages waiting and the client supports it, do those now
989                         if ((CC->is_async) && (CC->async_waiting) && (CC->h_async_function != NULL)) {
990                                 CC->h_async_function();
991                                 CC->async_waiting = 0;
992                         }
993
994                         force_purge = CC->kill_me;
995                         become_session(NULL);
996                         bind_me->state = CON_IDLE;
997                 }
998
999                 dead_session_purge(force_purge);
1000                 do_housekeeping();
1001
1002                 pthread_mutex_lock(&ThreadCountMutex);
1003                 --active_workers;
1004                 if ((active_workers + CtdlGetConfigInt("c_min_workers") < num_workers) &&
1005                     (num_workers > CtdlGetConfigInt("c_min_workers")))
1006                 {
1007                         num_workers--;
1008                         pthread_mutex_unlock(&ThreadCountMutex);
1009                         return (NULL);
1010                 }
1011                 pthread_mutex_unlock(&ThreadCountMutex);
1012         }
1013
1014         // If control reaches this point, the server is shutting down
1015         pthread_mutex_lock(&ThreadCountMutex);
1016         --num_workers;
1017         pthread_mutex_unlock(&ThreadCountMutex);
1018         return(NULL);
1019 }
1020
1021
1022 // SyslogFacility()
1023 // Translate text facility name to syslog.h defined value.
1024 int SyslogFacility(char *name)
1025 {
1026         int i;
1027         struct
1028         {
1029                 int facility;
1030                 char *name;
1031         }   facTbl[] =
1032         {
1033                 {   LOG_KERN,   "kern"          },
1034                 {   LOG_USER,   "user"          },
1035                 {   LOG_MAIL,   "mail"          },
1036                 {   LOG_DAEMON, "daemon"        },
1037                 {   LOG_AUTH,   "auth"          },
1038                 {   LOG_SYSLOG, "syslog"        },
1039                 {   LOG_LPR,    "lpr"           },
1040                 {   LOG_NEWS,   "news"          },
1041                 {   LOG_UUCP,   "uucp"          },
1042                 {   LOG_LOCAL0, "local0"        },
1043                 {   LOG_LOCAL1, "local1"        },
1044                 {   LOG_LOCAL2, "local2"        },
1045                 {   LOG_LOCAL3, "local3"        },
1046                 {   LOG_LOCAL4, "local4"        },
1047                 {   LOG_LOCAL5, "local5"        },
1048                 {   LOG_LOCAL6, "local6"        },
1049                 {   LOG_LOCAL7, "local7"        },
1050                 {   0,            NULL          }
1051         };
1052         for(i = 0; facTbl[i].name != NULL; i++) {
1053                 if(!strcasecmp(name, facTbl[i].name))
1054                         return facTbl[i].facility;
1055         }
1056         return LOG_DAEMON;
1057 }