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