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