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