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