]> code.citadel.org Git - citadel.git/blob - citadel/sysdep.c
5108dba0375d7800875feda4355ea3be11bd4a57
[citadel.git] / citadel / sysdep.c
1 /*
2  * Citadel "system dependent" stuff.
3  *
4  * Here's where we (hopefully) have most parts of the Citadel server that
5  * would need to be altered to run the server in a non-POSIX environment.
6  * 
7  * If we ever port to a different platform and either have multiple
8  * variants of this file or simply load it up with #ifdefs.
9  *
10  * Copyright (c) 1987-2011 by the citadel.org team
11  *
12  * This program is open source software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  */
26
27 #include "sysdep.h"
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <signal.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <sys/socket.h>
38 #include <syslog.h>
39 #include <sys/syslog.h>
40
41 #if TIME_WITH_SYS_TIME
42 # include <sys/time.h>
43 # include <time.h>
44 #else
45 # if HAVE_SYS_TIME_H
46 #  include <sys/time.h>
47 # else
48 #  include <time.h>
49 # endif
50 #endif
51
52 #include <limits.h>
53 #include <sys/resource.h>
54 #include <netinet/in.h>
55 #include <netinet/tcp.h>
56 #include <arpa/inet.h>
57 #include <netdb.h>
58 #include <sys/un.h>
59 #include <string.h>
60 #include <pwd.h>
61 #include <errno.h>
62 #include <stdarg.h>
63 #include <grp.h>
64 #define SHOW_ME_VAPPEND_PRINTF
65 #include <libcitadel.h>
66 #include "citadel.h"
67 #include "server.h"
68 #include "sysdep_decls.h"
69 #include "citserver.h"
70 #include "support.h"
71 #include "config.h"
72 #include "database.h"
73 #include "housekeeping.h"
74 #include "modules/crypto/serv_crypto.h" /* Needed for init_ssl, client_write_ssl, client_read_ssl, destruct_ssl */
75 #include "ecrash.h"
76 #include "context.h"
77
78 #ifdef HAVE_SYS_SELECT_H
79 #include <sys/select.h>
80 #endif
81
82 #ifndef HAVE_SNPRINTF
83 #include "snprintf.h"
84 #endif
85
86 #include "ctdl_module.h"
87 #include "threads.h"
88 #include "user_ops.h"
89 #include "control.h"
90
91
92 /*
93  * Signal handler to shut down the server.
94  */
95
96 volatile int exit_signal = 0;
97 volatile int shutdown_and_halt = 0;
98 volatile int restart_server = 0;
99 volatile int running_as_daemon = 0;
100
101 static RETSIGTYPE signal_cleanup(int signum) {
102
103         if (CT)
104                 CT->signal = signum;
105         else
106         {
107                 syslog(LOG_DEBUG, "Caught signal %d; shutting down.\n", signum);
108                 exit_signal = signum;
109         }
110 }
111
112 static RETSIGTYPE signal_exit(int signum) {
113         exit(1);
114 }
115
116
117
118 /*
119  * Some initialization stuff...
120  */
121 void init_sysdep(void) {
122         sigset_t set;
123
124         /* Avoid vulnerabilities related to FD_SETSIZE if we can. */
125 #ifdef FD_SETSIZE
126 #ifdef RLIMIT_NOFILE
127         struct rlimit rl;
128         getrlimit(RLIMIT_NOFILE, &rl);
129         rl.rlim_cur = FD_SETSIZE;
130         rl.rlim_max = FD_SETSIZE;
131         setrlimit(RLIMIT_NOFILE, &rl);
132 #endif
133 #endif
134
135         /* If we've got OpenSSL, we're going to use it. */
136 #ifdef HAVE_OPENSSL
137         init_ssl();
138 #endif
139
140         /*
141          * Set up a place to put thread-specific data.
142          * We only need a single pointer per thread - it points to the
143          * CitContext structure (in the ContextList linked list) of the
144          * session to which the calling thread is currently bound.
145          */
146         if (citthread_key_create(&MyConKey, NULL) != 0) {
147                 syslog(LOG_CRIT, "Can't create TSD key: %s\n",
148                         strerror(errno));
149         }
150
151         /*
152          * The action for unexpected signals and exceptions should be to
153          * call signal_cleanup() to gracefully shut down the server.
154          */
155         sigemptyset(&set);
156         sigaddset(&set, SIGINT);                // intr = shutdown
157         sigaddset(&set, SIGHUP);
158         sigaddset(&set, SIGTERM);
159         sigprocmask(SIG_UNBLOCK, &set, NULL);
160
161         signal(SIGINT, signal_cleanup);         // intr = shutdown
162         signal(SIGHUP, signal_cleanup);
163         signal(SIGTERM, signal_cleanup);
164         signal(SIGUSR2, signal_exit);
165
166         /*
167          * Do not shut down the server on broken pipe signals, otherwise the
168          * whole Citadel service would come down whenever a single client
169          * socket breaks.
170          */
171         signal(SIGPIPE, SIG_IGN);
172 }
173
174
175 /* 
176  * This is a generic function to set up a master socket for listening on
177  * a TCP port.  The server shuts down if the bind fails.  (IPv4/IPv6 version)
178  *
179  * ip_addr      IP address to bind
180  * port_number  port number to bind
181  * queue_len    number of incoming connections to allow in the queue
182  */
183 int ctdl_tcp_server(char *ip_addr, int port_number, int queue_len, char *errormessage)
184 {
185         struct protoent *p;
186         struct sockaddr_in6 sin6;
187         struct sockaddr_in sin4;
188         int s, i, b;
189         int ip_version = 6;
190
191         memset(&sin6, 0, sizeof(sin6));
192         memset(&sin4, 0, sizeof(sin4));
193         sin6.sin6_family = AF_INET6;
194         sin4.sin_family = AF_INET;
195
196         if (    (ip_addr == NULL)                                                       /* any IPv6 */
197                 || (IsEmptyStr(ip_addr))
198                 || (!strcmp(ip_addr, "*"))
199         ) {
200                 ip_version = 6;
201                 sin6.sin6_addr = in6addr_any;
202         }
203         else if (!strcmp(ip_addr, "0.0.0.0"))                                           /* any IPv4 */
204         {
205                 ip_version = 4;
206                 sin4.sin_addr.s_addr = INADDR_ANY;
207         }
208         else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':')))                     /* specific IPv4 */
209         {
210                 ip_version = 4;
211                 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
212                         snprintf(errormessage, SIZ,
213                                  "Error binding to [%s] : %s", ip_addr, strerror(errno)
214                         );
215                         syslog(LOG_ALERT, "%s\n", errormessage);
216                         return (-1);
217                 }
218         }
219         else                                                                            /* specific IPv6 */
220         {
221                 ip_version = 6;
222                 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
223                         snprintf(errormessage, SIZ,
224                                  "Error binding to [%s] : %s", ip_addr, strerror(errno)
225                         );
226                         syslog(LOG_ALERT, "%s\n", errormessage);
227                         return (-1);
228                 }
229         }
230
231         if (port_number == 0) {
232                 snprintf(errormessage, SIZ,
233                          "Can't start: no port number specified."
234                 );
235                 syslog(LOG_ALERT, "%s\n", errormessage);
236                 return (-1);
237         }
238         sin6.sin6_port = htons((u_short) port_number);
239         sin4.sin_port = htons((u_short) port_number);
240
241         p = getprotobyname("tcp");
242
243         s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
244         if (s < 0) {
245                 snprintf(errormessage, SIZ,
246                          "Can't create a listening socket: %s", strerror(errno)
247                 );
248                 syslog(LOG_ALERT, "%s\n", errormessage);
249                 return (-1);
250         }
251         /* Set some socket options that make sense. */
252         i = 1;
253         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
254
255         if (ip_version == 6) {
256                 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
257         }
258         else {
259                 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
260         }
261
262         if (b < 0) {
263                 snprintf(errormessage, SIZ,
264                          "Can't bind: %s", strerror(errno)
265                 );
266                 syslog(LOG_ALERT, "%s\n", errormessage);
267                 return (-1);
268         }
269
270         fcntl(s, F_SETFL, O_NONBLOCK);
271
272         if (listen(s, ((queue_len >= 5) ? queue_len : 5) ) < 0) {
273                 snprintf(errormessage, SIZ,
274                          "Can't listen: %s", strerror(errno)
275                 );
276                 syslog(LOG_ALERT, "%s\n", errormessage);
277                 return (-1);
278         }
279         return (s);
280 }
281
282
283
284
285
286 /*
287  * Create a Unix domain socket and listen on it
288  */
289 int ctdl_uds_server(char *sockpath, int queue_len, char *errormessage)
290 {
291         struct sockaddr_un addr;
292         int s;
293         int i;
294         int actual_queue_len;
295 #ifdef HAVE_STRUCT_UCRED
296         int passcred = 1;
297 #endif
298
299         actual_queue_len = queue_len;
300         if (actual_queue_len < 5) actual_queue_len = 5;
301
302         i = unlink(sockpath);
303         if ((i != 0) && (errno != ENOENT)) {
304                 snprintf(errormessage, SIZ, "citserver: can't unlink %s: %s",
305                         sockpath, strerror(errno)
306                 );
307                 syslog(LOG_EMERG, "%s\n", errormessage);
308                 return(-1);
309         }
310
311         memset(&addr, 0, sizeof(addr));
312         addr.sun_family = AF_UNIX;
313         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
314
315         s = socket(AF_UNIX, SOCK_STREAM, 0);
316         if (s < 0) {
317                 snprintf(errormessage, SIZ, 
318                          "citserver: Can't create a socket: %s",
319                          strerror(errno));
320                 syslog(LOG_EMERG, "%s\n", errormessage);
321                 return(-1);
322         }
323
324         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
325                 snprintf(errormessage, SIZ, 
326                          "citserver: Can't bind: %s",
327                          strerror(errno));
328                 syslog(LOG_EMERG, "%s\n", errormessage);
329                 return(-1);
330         }
331
332         /* set to nonblock - we need this for some obscure situations */
333         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
334                 snprintf(errormessage, SIZ, 
335                          "citserver: Can't set socket to non-blocking: %s",
336                          strerror(errno));
337                 syslog(LOG_EMERG, "%s\n", errormessage);
338                 close(s);
339                 return(-1);
340         }
341
342         if (listen(s, actual_queue_len) < 0) {
343                 snprintf(errormessage, SIZ, 
344                          "citserver: Can't listen: %s",
345                          strerror(errno));
346                 syslog(LOG_EMERG, "%s\n", errormessage);
347                 return(-1);
348         }
349
350 #ifdef HAVE_STRUCT_UCRED
351         setsockopt(s, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred));
352 #endif
353
354         chmod(sockpath, S_ISGID|S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
355         return(s);
356 }
357
358
359
360 /*
361  * The following functions implement output buffering on operating systems which
362  * support it (such as Linux and various BSD flavors).
363  */
364 #ifndef HAVE_DARWIN
365 #ifdef TCP_CORK
366 #       define HAVE_TCP_BUFFERING
367 #else
368 #       ifdef TCP_NOPUSH
369 #               define HAVE_TCP_BUFFERING
370 #               define TCP_CORK TCP_NOPUSH
371 #       endif
372 #endif /* TCP_CORK */
373 #endif /* HAVE_DARWIN */
374
375 static unsigned on = 1, off = 0;
376
377 void buffer_output(void) {
378 #ifdef HAVE_TCP_BUFFERING
379 #ifdef HAVE_OPENSSL
380         if (!CC->redirect_ssl)
381 #endif
382                 setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
383 #endif
384 }
385
386 void unbuffer_output(void) {
387 #ifdef HAVE_TCP_BUFFERING
388 #ifdef HAVE_OPENSSL
389         if (!CC->redirect_ssl)
390 #endif
391                 setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
392 #endif
393 }
394
395 void flush_output(void) {
396 #ifdef HAVE_TCP_BUFFERING
397         struct CitContext *CCC = CC;
398         setsockopt(CCC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
399         setsockopt(CCC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
400 #endif
401 }
402
403 /*
404 static void flush_client_inbuf(void)
405 {
406         CitContext *CCC=CC;
407
408         FlushStrBuf(CCC->ReadBuf);
409         CCC->Pos = NULL;
410
411 }
412 */
413
414
415 /*
416  * client_close()       ...     close the client socket
417  */
418 void client_close(void) {
419         CitContext *CCC = CC;
420         int r;
421
422         if (!CCC) return;
423         if (CCC->client_socket < 0) return;
424         syslog(LOG_DEBUG, "Closing socket %d\n", CCC->client_socket);
425         r = close(CCC->client_socket);
426         if (!r) syslog(LOG_INFO, "close() failed: %s\n", strerror(errno));
427         CCC->client_socket = -1 ;
428 }
429
430
431
432
433 /*
434  * client_write()   ...    Send binary data to the client.
435  */
436 int client_write(const char *buf, int nbytes)
437 {
438         int bytes_written = 0;
439         int retval;
440 #ifndef HAVE_TCP_BUFFERING
441         int old_buffer_len = 0;
442 #endif
443         fd_set wset;
444         CitContext *Ctx;
445         int fdflags;
446
447         if (nbytes < 1) return(0);
448
449         Ctx = CC;
450
451 #ifdef BIGBAD_IODBG
452         {
453                 int rv = 0;
454                 char fn [SIZ];
455                 FILE *fd;
456                 
457                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", Ctx->ServiceName, Ctx->cs_pid);
458                 
459                 fd = fopen(fn, "a+");
460                 fprintf(fd, "Sending: BufSize: %d BufContent: [",
461                         nbytes);
462                 rv = fwrite(buf, nbytes, 1, fd);
463                 fprintf(fd, "]\n");
464                 
465                         
466                 fclose(fd);
467         }
468 #endif
469 //      flush_client_inbuf();
470         if (Ctx->redirect_buffer != NULL) {
471                 StrBufAppendBufPlain(Ctx->redirect_buffer,
472                                      buf, nbytes, 0);
473                 return 0;
474         }
475
476 #ifdef HAVE_OPENSSL
477         if (Ctx->redirect_ssl) {
478                 client_write_ssl(buf, nbytes);
479                 return 0;
480         }
481 #endif
482         if (Ctx->client_socket == -1) return -1;
483
484         fdflags = fcntl(Ctx->client_socket, F_GETFL);
485
486         while ((bytes_written < nbytes) && (Ctx->client_socket != -1)){
487                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
488                         FD_ZERO(&wset);
489                         FD_SET(Ctx->client_socket, &wset);
490                         if (select(1, NULL, &wset, NULL, NULL) == -1) {
491                                 if (errno == EINTR)
492                                 {
493                                         syslog(LOG_DEBUG, "client_write(%d bytes) select() interrupted.\n", nbytes-bytes_written);
494                                         if (CtdlThreadCheckStop()) {
495                                                 CC->kill_me = KILLME_SELECT_INTERRUPTED;
496                                                 return (-1);
497                                         } else {
498                                                 /* can't trust fd's and stuff so we need to re-create them */
499                                                 continue;
500                                         }
501                                 } else {
502                                         syslog(LOG_ERR,
503                                                 "client_write(%d bytes) select failed: %s (%d)\n",
504                                                 nbytes - bytes_written,
505                                                 strerror(errno), errno
506                                         );
507                                         cit_backtrace();
508                                         client_close();
509                                         Ctx->kill_me = KILLME_SELECT_FAILED;
510                                         return -1;
511                                 }
512                         }
513                 }
514
515                 retval = write(Ctx->client_socket, &buf[bytes_written], nbytes - bytes_written);
516                 if (retval < 1) {
517                         syslog(LOG_ERR,
518                                 "client_write(%d bytes) failed: %s (%d)\n",
519                                 nbytes - bytes_written,
520                                 strerror(errno), errno
521                         );
522                         cit_backtrace();
523                         client_close();
524                         // syslog(LOG_DEBUG, "Tried to send: %s",  &buf[bytes_written]);
525                         Ctx->kill_me = KILLME_WRITE_FAILED;
526                         return -1;
527                 }
528                 bytes_written = bytes_written + retval;
529         }
530         return 0;
531 }
532
533 void cputbuf(const StrBuf *Buf) {   
534         client_write(ChrPtr(Buf), StrLength(Buf)); 
535 }   
536
537
538 /*
539  * cprintf()    Send formatted printable data to the client.
540  *              Implemented in terms of client_write() so it's technically not sysdep...
541  */
542 void cprintf(const char *format, ...) {   
543         va_list arg_ptr;   
544         char buf[1024];
545    
546         va_start(arg_ptr, format);   
547         if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
548                 buf[sizeof buf - 2] = '\n';
549         client_write(buf, strlen(buf)); 
550         va_end(arg_ptr);
551 }   
552
553
554 /*
555  * Read data from the client socket.
556  *
557  * sock         socket fd to read from
558  * buf          buffer to read into 
559  * bytes        number of bytes to read
560  * timeout      Number of seconds to wait before timing out
561  *
562  * Possible return values:
563  *      1       Requested number of bytes has been read.
564  *      0       Request timed out.
565  *      -1      Connection is broken, or other error.
566  */
567 int client_read_blob(StrBuf *Target, int bytes, int timeout)
568 {
569         CitContext *CCC=CC;
570         const char *Error;
571         int retval = 0;
572
573 #ifdef HAVE_OPENSSL
574         if (CCC->redirect_ssl) {
575 #ifdef BIGBAD_IODBG
576                 int rv = 0;
577                 char fn [SIZ];
578                 FILE *fd;
579                 
580                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
581                         
582                 fd = fopen(fn, "a+");
583                 fprintf(fd, "Reading BLOB: BufSize: %d ",
584                         bytes);
585                 rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
586                 fprintf(fd, "]\n");
587                 
588                         
589                 fclose(fd);
590 #endif
591                 retval = client_read_sslblob(Target, bytes, timeout);
592                 if (retval < 0) {
593                         syslog(LOG_CRIT, 
594                                       "%s failed\n",
595                                       __FUNCTION__);
596                 }
597 #ifdef BIGBAD_IODBG
598                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
599                 
600                 fd = fopen(fn, "a+");
601                 fprintf(fd, "Read: %d BufContent: [",
602                         StrLength(Target));
603                 rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
604                 fprintf(fd, "]\n");
605                 
606                 
607                 fclose(fd);
608 #endif
609         }
610         else 
611 #endif
612         {
613 #ifdef BIGBAD_IODBG
614                 int rv = 0;
615                 char fn [SIZ];
616                 FILE *fd;
617                 
618                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
619                         
620                 fd = fopen(fn, "a+");
621                 fprintf(fd, "Reading BLOB: BufSize: %d ",
622                         bytes);
623                 rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
624                 fprintf(fd, "]\n");
625                 
626                         
627                 fclose(fd);
628 #endif
629                 retval = StrBufReadBLOBBuffered(Target, 
630                                                 CCC->ReadBuf,
631                                                 &CCC->Pos,
632                                                 &CCC->client_socket,
633                                                 1, 
634                                                 bytes,
635                                                 O_TERM,
636                                                 &Error);
637                 if (retval < 0) {
638                         syslog(LOG_CRIT, "%s failed: %s\n", __FUNCTION__, Error);
639                         client_close();
640                         return retval;
641                 }
642 #ifdef BIGBAD_IODBG
643                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
644                 
645                 fd = fopen(fn, "a+");
646                 fprintf(fd, "Read: %d BufContent: [",
647                         StrLength(Target));
648                 rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
649                 fprintf(fd, "]\n");
650                 fclose(fd);
651 #endif
652         }
653         return retval;
654 }
655
656
657 /*
658  * to make client_read_random_blob() more efficient, increase buffer size.
659  * just use in greeting function, else your buffer may be flushed
660  */
661 void client_set_inbound_buf(long N)
662 {
663         FlushStrBuf(CC->ReadBuf);
664         ReAdjustEmptyBuf(CC->ReadBuf, N * SIZ, N * SIZ);
665 }
666
667 int client_read_random_blob(StrBuf *Target, int timeout)
668 {
669         CitContext *CCC=CC;
670         int rc;
671
672         rc =  client_read_blob(Target, 1, timeout);
673         if (rc > 0)
674         {
675                 long len;
676                 const char *pch;
677                 
678                 len = StrLength(CCC->ReadBuf);
679                 pch = ChrPtr(CCC->ReadBuf);
680
681                 if (len > 0)
682                 {
683                         if (CCC->Pos != NULL) {
684                                 len -= CCC->Pos - pch;
685                                 pch = CCC->Pos;
686                         }
687                         StrBufAppendBufPlain(Target, pch, len, 0);
688                         FlushStrBuf(CCC->ReadBuf);
689                         CCC->Pos = NULL;
690 #ifdef BIGBAD_IODBG
691                         {
692                                 int rv = 0;
693                                 char fn [SIZ];
694                                 FILE *fd;
695                         
696                                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
697                         
698                                 fd = fopen(fn, "a+");
699                                 fprintf(fd, "Read: BufSize: %d BufContent: [",
700                                         StrLength(Target));
701                                 rv = fwrite(ChrPtr(Target), StrLength(Target), 1, fd);
702                                 fprintf(fd, "]\n");
703                         
704                         
705                                 fclose(fd);
706                         }
707 #endif
708         
709                         return StrLength(Target);
710                 }
711                 return rc;
712         }
713         else
714                 return rc;
715 }
716
717 int client_read_to(char *buf, int bytes, int timeout)
718 {
719         CitContext *CCC=CC;
720         int rc;
721
722         rc = client_read_blob(CCC->MigrateBuf, bytes, timeout);
723         if (rc < 0)
724         {
725                 *buf = '\0';
726                 return rc;
727         }
728         else
729         {
730                 memcpy(buf, 
731                        ChrPtr(CCC->MigrateBuf),
732                        StrLength(CCC->MigrateBuf) + 1);
733                 FlushStrBuf(CCC->MigrateBuf);
734                 return rc;
735         }
736 }
737
738
739 int HaveMoreLinesWaiting(CitContext *CCC)
740 {
741         if ((CCC->kill_me != 0) || (
742             (CCC->Pos == NULL) && 
743             (StrLength(CCC->ReadBuf) == 0) && 
744             (CCC->client_socket != -1)) )
745                 return 0;
746         else
747                 return 1;
748 }
749
750
751 /*
752  * Read data from the client socket with default timeout.
753  * (This is implemented in terms of client_read_to() and could be
754  * justifiably moved out of sysdep.c)
755  */
756 INLINE int client_read(char *buf, int bytes)
757 {
758         return(client_read_to(buf, bytes, config.c_sleeping));
759 }
760
761 int CtdlClientGetLine(StrBuf *Target)
762 {
763         CitContext *CCC=CC;
764         const char *Error;
765         int rc;
766
767         FlushStrBuf(Target);
768 #ifdef HAVE_OPENSSL
769         if (CCC->redirect_ssl) {
770 #ifdef BIGBAD_IODBG
771                 char fn [SIZ];
772                 FILE *fd;
773                 int len = 0;
774                 int rlen = 0;
775                 int  nlen = 0;
776                 int nrlen = 0;
777                 const char *pch;
778
779                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
780
781                 fd = fopen(fn, "a+");
782                 pch = ChrPtr(CCC->ReadBuf);
783                 len = StrLength(CCC->ReadBuf);
784                 if (CCC->Pos != NULL)
785                         rlen = CC->Pos - pch;
786                 else
787                         rlen = 0;
788
789 /*              fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \nBufContent: [%s]\n\n_____________________\n",
790                         len, rlen, pch);
791 */
792                 fprintf(fd, "\n\n\nSSL1: BufSize: %d BufPos: %d \n_____________________\n",
793                         len, rlen);
794 #endif
795                 rc = client_readline_sslbuffer(Target,
796                                                CCC->ReadBuf,
797                                                &CCC->Pos,
798                                                1);
799 #ifdef BIGBAD_IODBG
800                 pch = ChrPtr(CCC->ReadBuf);
801                 nlen = StrLength(CCC->ReadBuf);
802                 if (CCC->Pos != NULL)
803                         nrlen = CC->Pos - pch;
804                 else
805                         nrlen = 0;
806 /*
807                 fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \nBufContent: [%s]\n\n_____________________\n",
808                         len, nlen, rlen, nrlen, pch);
809 */
810                 fprintf(fd, "\n\n\nSSL2: BufSize: was: %d is: %d BufPos: was: %d is: %d \n",
811                         len, nlen, rlen, nrlen);
812
813                 fprintf(fd, "SSL3: Read: BufSize: %d BufContent: [%s]\n\n*************\n",
814                         StrLength(Target), ChrPtr(Target));
815                 fclose(fd);
816
817                 if (rc < 0)
818                         syslog(LOG_CRIT, 
819                                       "%s failed\n",
820                                       __FUNCTION__);
821 #endif
822                 return rc;
823         }
824         else 
825 #endif
826         {
827 #ifdef BIGBAD_IODBG
828                 char fn [SIZ];
829                 FILE *fd;
830                 int len, rlen, nlen, nrlen;
831                 const char *pch;
832
833                 snprintf(fn, SIZ, "/tmp/foolog_%s.%d", CCC->ServiceName, CCC->cs_pid);
834
835                 fd = fopen(fn, "a+");
836                 pch = ChrPtr(CCC->ReadBuf);
837                 len = StrLength(CCC->ReadBuf);
838                 if (CCC->Pos != NULL)
839                         rlen = CC->Pos - pch;
840                 else
841                         rlen = 0;
842
843 /*              fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \nBufContent: [%s]\n\n_____________________\n",
844                         len, rlen, pch);
845 */
846                 fprintf(fd, "\n\n\nBufSize: %d BufPos: %d \n_____________________\n",
847                         len, rlen);
848 #endif
849                 rc = StrBufTCP_read_buffered_line_fast(Target, 
850                                                        CCC->ReadBuf,
851                                                        &CCC->Pos,
852                                                        &CCC->client_socket,
853                                                        5,
854                                                        1,
855                                                        &Error);
856
857 #ifdef BIGBAD_IODBG
858                 pch = ChrPtr(CCC->ReadBuf);
859                 nlen = StrLength(CCC->ReadBuf);
860                 if (CCC->Pos != NULL)
861                         nrlen = CC->Pos - pch;
862                 else
863                         nrlen = 0;
864 /*
865                 fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \nBufContent: [%s]\n\n_____________________\n",
866                         len, nlen, rlen, nrlen, pch);
867 */
868                 fprintf(fd, "\n\n\nBufSize: was: %d is: %d BufPos: was: %d is: %d \n",
869                         len, nlen, rlen, nrlen);
870
871                 fprintf(fd, "Read: BufSize: %d BufContent: [%s]\n\n*************\n",
872                         StrLength(Target), ChrPtr(Target));
873                 fclose(fd);
874
875                 if ((rc < 0) && (Error != NULL))
876                         syslog(LOG_CRIT, 
877                                       "%s failed: %s\n",
878                                       __FUNCTION__,
879                                       Error);
880 #endif
881                 return rc;
882         }
883 }
884
885
886 /*
887  * client_getln()   ...   Get a LF-terminated line of text from the client.
888  * (This is implemented in terms of client_read() and could be
889  * justifiably moved out of sysdep.c)
890  */
891 int client_getln(char *buf, int bufsize)
892 {
893         int i, retval;
894         CitContext *CCC=CC;
895         const char *pCh;
896
897         retval = CtdlClientGetLine(CCC->MigrateBuf);
898         if (retval < 0)
899           return(retval >= 0);
900
901
902         i = StrLength(CCC->MigrateBuf);
903         pCh = ChrPtr(CCC->MigrateBuf);
904         /* Strip the trailing LF, and the trailing CR if present.
905          */
906         if (bufsize <= i)
907                 i = bufsize - 1;
908         while ( (i > 0)
909                 && ( (pCh[i - 1]==13)
910                      || ( pCh[i - 1]==10)) ) {
911                 i--;
912         }
913         memcpy(buf, pCh, i);
914         buf[i] = 0;
915
916         FlushStrBuf(CCC->MigrateBuf);
917         if (retval < 0) {
918                 safestrncpy(&buf[i], "000", bufsize - i);
919         }
920         return(retval >= 0);
921 }
922
923
924 /*
925  * Cleanup any contexts that are left lying around
926  */
927
928
929 void close_masters (void)
930 {
931         struct ServiceFunctionHook *serviceptr;
932         
933         /*
934          * close all protocol master sockets
935          */
936         for (serviceptr = ServiceHookTable; serviceptr != NULL;
937             serviceptr = serviceptr->next ) {
938
939                 if (serviceptr->tcp_port > 0)
940                 {
941                         syslog(LOG_INFO, "Closing listener on port %d\n",
942                                 serviceptr->tcp_port);
943                         serviceptr->tcp_port = 0;
944                 }
945                 
946                 if (serviceptr->sockpath != NULL)
947                         syslog(LOG_INFO, "Closing listener on '%s'\n",
948                                 serviceptr->sockpath);
949
950                 close(serviceptr->msock);
951                 /* If it's a Unix domain socket, remove the file. */
952                 if (serviceptr->sockpath != NULL) {
953                         unlink(serviceptr->sockpath);
954                         serviceptr->sockpath = NULL;
955                 }
956         }
957 }
958
959
960 /*
961  * The system-dependent part of master_cleanup() - close the master socket.
962  */
963 void sysdep_master_cleanup(void) {
964         
965         close_masters();
966         
967         context_cleanup();
968         
969 #ifdef HAVE_OPENSSL
970         destruct_ssl();
971 #endif
972         CtdlDestroyProtoHooks();
973         CtdlDestroyDeleteHooks();
974         CtdlDestroyXmsgHooks();
975         CtdlDestroyNetprocHooks();
976         CtdlDestroyUserHooks();
977         CtdlDestroyMessageHook();
978         CtdlDestroyCleanupHooks();
979         CtdlDestroyFixedOutputHooks();  
980         CtdlDestroySessionHooks();
981         CtdlDestroyServiceHook();
982         CtdlDestroyRoomHooks();
983         CtdlDestroySearchHooks();
984         #ifdef HAVE_BACKTRACE
985 ///     eCrash_Uninit();
986         #endif
987 }
988
989
990
991 pid_t current_child;
992 void graceful_shutdown(int signum) {
993         kill(current_child, signum);
994         unlink(file_pid_file);
995         exit(0);
996 }
997
998 int nFireUps = 0;
999 int nFireUpsNonRestart = 0;
1000 pid_t ForkedPid = 1;
1001
1002 /*
1003  * Start running as a daemon.
1004  */
1005 void start_daemon(int unused) {
1006         int status = 0;
1007         pid_t child = 0;
1008         FILE *fp;
1009         int do_restart = 0;
1010
1011         current_child = 0;
1012
1013         /* Close stdin/stdout/stderr and replace them with /dev/null.
1014          * We don't just call close() because we don't want these fd's
1015          * to be reused for other files.
1016          */
1017         if (chdir(ctdl_run_dir) != 0)
1018                 syslog(LOG_EMERG, 
1019                               "unable to change into directory [%s]: %s", 
1020                               ctdl_run_dir, strerror(errno));
1021
1022         child = fork();
1023         if (child != 0) {
1024                 exit(0);
1025         }
1026         
1027         signal(SIGHUP, SIG_IGN);
1028         signal(SIGINT, SIG_IGN);
1029         signal(SIGQUIT, SIG_IGN);
1030
1031         setsid();
1032         umask(0);
1033         if ((freopen("/dev/null", "r", stdin) != stdin) || 
1034             (freopen("/dev/null", "w", stdout) != stdout) || 
1035             (freopen("/dev/null", "w", stderr) != stderr))
1036                 syslog(LOG_EMERG, 
1037                               "unable to reopen stdin/out/err %s", 
1038                               strerror(errno));
1039                 
1040
1041         do {
1042                 current_child = fork();
1043
1044                 signal(SIGTERM, graceful_shutdown);
1045         
1046                 if (current_child < 0) {
1047                         perror("fork");
1048                         exit(errno);
1049                 }
1050         
1051                 else if (current_child == 0) {
1052                         return; /* continue starting citadel. */
1053                 }
1054         
1055                 else {
1056                         fp = fopen(file_pid_file, "w");
1057                         if (fp != NULL) {
1058                                 fprintf(fp, ""F_PID_T"\n", getpid());
1059                                 fclose(fp);
1060                         }
1061                         waitpid(current_child, &status, 0);
1062                 }
1063                 do_restart = 0;
1064                 nFireUpsNonRestart = nFireUps;
1065                 
1066                 /* Exit code 0 means the watcher should exit */
1067                 if (WIFEXITED(status) && (WEXITSTATUS(status) == CTDLEXIT_SHUTDOWN)) {
1068                         do_restart = 0;
1069                 }
1070
1071                 /* Exit code 101-109 means the watcher should exit */
1072                 else if (WIFEXITED(status) && (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109)) {
1073                         do_restart = 0;
1074                 }
1075
1076                 /* Any other exit code, or no exit code, means we should restart. */
1077                 else {
1078                         do_restart = 1;
1079                         nFireUps++;
1080                         ForkedPid = current_child;
1081                 }
1082
1083         } while (do_restart);
1084
1085         unlink(file_pid_file);
1086         exit(WEXITSTATUS(status));
1087 }
1088
1089
1090
1091 void checkcrash(void)
1092 {
1093         if (nFireUpsNonRestart != nFireUps)
1094         {
1095                 StrBuf *CrashMail;
1096
1097                 CrashMail = NewStrBuf();
1098                 syslog(LOG_ALERT, "Posting crash message\n");
1099                 StrBufPrintf(CrashMail, 
1100                         " \n"
1101                         " The Citadel server process (citserver) terminated unexpectedly."
1102                         "\n \n"
1103                         " This could be the result of a bug in the server program, or some external "
1104                         "factor.\n \n"
1105                         " You can obtain more information about this by enabling core dumps.\n \n"
1106                         " For more information, please see:\n \n"
1107                         " http://citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files"
1108                         "\n \n"
1109
1110                         " If you have already done this, the core dump is likely to be found at %score.%d\n"
1111                         ,
1112                         ctdl_run_dir, ForkedPid);
1113                 CtdlAideMessage(ChrPtr(CrashMail), "Citadel server process terminated unexpectedly");
1114                 FreeStrBuf(&CrashMail);
1115         }
1116 }
1117
1118
1119 /*
1120  * Generic routine to convert a login name to a full name (gecos)
1121  * Returns nonzero if a conversion took place
1122  */
1123 int convert_login(char NameToConvert[]) {
1124         struct passwd *pw;
1125         int a;
1126
1127         pw = getpwnam(NameToConvert);
1128         if (pw == NULL) {
1129                 return(0);
1130         }
1131         else {
1132                 strcpy(NameToConvert, pw->pw_gecos);
1133                 for (a=0; a<strlen(NameToConvert); ++a) {
1134                         if (NameToConvert[a] == ',') NameToConvert[a] = 0;
1135                 }
1136                 return(1);
1137         }
1138 }
1139
1140
1141
1142 /* 
1143  * This loop just keeps going and going and going...
1144  */
1145 void *worker_thread(void *arg) {
1146         int highest;
1147         CitContext *ptr;
1148         CitContext *bind_me = NULL;
1149         fd_set readfds;
1150         int retval = 0;
1151         struct timeval tv;
1152         int force_purge = 0;
1153         
1154
1155         while (!CtdlThreadCheckStop()) {
1156
1157                 /* make doubly sure we're not holding any stale db handles
1158                  * which might cause a deadlock.
1159                  */
1160                 cdb_check_handles();
1161 do_select:      force_purge = 0;
1162                 bind_me = NULL;         /* Which session shall we handle? */
1163
1164                 /* Initialize the fdset. */
1165                 FD_ZERO(&readfds);
1166                 highest = 0;
1167
1168                 begin_critical_section(S_SESSION_TABLE);
1169                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
1170                         /* Don't select on dead sessions, only truly idle ones */
1171                         if (    (ptr->state == CON_IDLE)
1172                                 && (ptr->kill_me == 0)
1173                                 && (ptr->client_socket != -1)
1174                         ) {
1175                                 FD_SET(ptr->client_socket, &readfds);
1176                                 if (ptr->client_socket > highest)
1177                                         highest = ptr->client_socket;
1178                         }
1179                         if ((bind_me == NULL) && (ptr->state == CON_READY)) {
1180                                 bind_me = ptr;
1181                                 ptr->state = CON_EXECUTING;
1182                                 break;
1183                         }
1184                         if ((bind_me == NULL) && (ptr->state == CON_GREETING)) {
1185                                 bind_me = ptr;
1186                                 ptr->state = CON_STARTING;
1187                                 break;
1188                         }
1189                 }
1190                 end_critical_section(S_SESSION_TABLE);
1191
1192                 if (bind_me) {
1193                         goto SKIP_SELECT;
1194                 }
1195
1196                 /* If we got this far, it means that there are no sessions
1197                  * which a previous thread marked for attention, so we go
1198                  * ahead and get ready to select().
1199                  */
1200
1201                 if (!CtdlThreadCheckStop()) {
1202                         tv.tv_sec = 1;          /* wake up every second if no input */
1203                         tv.tv_usec = 0;
1204                         retval = CtdlThreadSelect(highest + 1, &readfds, NULL, NULL, &tv);
1205                 }
1206                 else
1207                         return NULL;
1208
1209                 /* Now figure out who made this select() unblock.
1210                  * First, check for an error or exit condition.
1211                  */
1212                 if (retval < 0) {
1213                         if (errno == EBADF) {
1214                                 syslog(LOG_NOTICE, "select() failed: (%s)\n",
1215                                         strerror(errno));
1216                                 goto do_select;
1217                         }
1218                         if (errno != EINTR) {
1219                                 syslog(LOG_EMERG, "Exiting (%s)\n", strerror(errno));
1220                                 CtdlThreadStopAll();
1221                                 continue;
1222                         } else {
1223                                 syslog(LOG_DEBUG, "Interrupted CtdlThreadSelect.\n");
1224                                 if (CtdlThreadCheckStop()) return(NULL);
1225                                 goto do_select;
1226                         }
1227                 }
1228                 else if(retval == 0) {
1229                         if (CtdlThreadCheckStop()) return(NULL);
1230                 }
1231
1232                 /* It must be a client socket.  Find a context that has data
1233                  * waiting on its socket *and* is in the CON_IDLE state.  Any
1234                  * active sockets other than our chosen one are marked as
1235                  * CON_READY so the next thread that comes around can just bind
1236                  * to one without having to select() again.
1237                  */
1238                 begin_critical_section(S_SESSION_TABLE);
1239                 for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
1240                         int checkfd = ptr->client_socket;
1241                         if ((checkfd != -1) && (ptr->state == CON_IDLE) ){
1242                                 if (FD_ISSET(checkfd, &readfds)) {
1243                                         ptr->input_waiting = 1;
1244                                         if (!bind_me) {
1245                                                 bind_me = ptr;  /* I choose you! */
1246                                                 bind_me->state = CON_EXECUTING;
1247                                         }
1248                                         else {
1249                                                 ptr->state = CON_READY;
1250                                         }
1251                                 } else if ((ptr->is_async) && (ptr->async_waiting) && (ptr->h_async_function)) {
1252                                         if (!bind_me) {
1253                                                 bind_me = ptr;  /* I choose you! */
1254                                                 bind_me->state = CON_EXECUTING;
1255                                         }
1256                                         else {
1257                                                 ptr->state = CON_READY;
1258                                         }
1259                                 }
1260                         }
1261                 }
1262                 end_critical_section(S_SESSION_TABLE);
1263
1264 SKIP_SELECT:
1265                 /* We're bound to a session */
1266                 if (bind_me != NULL) {
1267                         become_session(bind_me);
1268
1269                         if (bind_me->state == CON_STARTING) {
1270                                 bind_me->state = CON_EXECUTING;
1271                                 begin_session(bind_me);
1272                                 bind_me->h_greeting_function();
1273                         }
1274                         /* If the client has sent a command, execute it. */
1275                         if (CC->input_waiting) {
1276                                 CC->h_command_function();
1277
1278                                 while (HaveMoreLinesWaiting(CC))
1279                                        CC->h_command_function();
1280
1281                                 CC->input_waiting = 0;
1282                         }
1283
1284                         /* If there are asynchronous messages waiting and the
1285                          * client supports it, do those now */
1286                         if ((CC->is_async) && (CC->async_waiting)
1287                            && (CC->h_async_function != NULL)) {
1288                                 CC->h_async_function();
1289                                 CC->async_waiting = 0;
1290                         }
1291                         
1292                         force_purge = CC->kill_me;
1293                         become_session(NULL);
1294                         bind_me->state = CON_IDLE;
1295                 }
1296
1297                 dead_session_purge(force_purge);
1298                 do_housekeeping();
1299         }
1300         /* If control reaches this point, the server is shutting down */        
1301         return(NULL);
1302 }
1303
1304
1305
1306
1307 /*
1308  * A function to handle selecting on master sockets.
1309  * In other words it handles new connections.
1310  * It is a thread.
1311  */
1312 void *select_on_master (void *arg)
1313 {
1314         struct ServiceFunctionHook *serviceptr;
1315         fd_set master_fds;
1316         int highest;
1317         struct timeval tv;
1318         int ssock;                      /* Descriptor for client socket */
1319         CitContext *con= NULL;  /* Temporary context pointer */
1320         int m;
1321         int i;
1322         int retval;
1323         struct CitContext select_on_master_CC;
1324
1325         CtdlFillSystemContext(&select_on_master_CC, "select_on_master");
1326         citthread_setspecific(MyConKey, (void *)&select_on_master_CC);
1327
1328         while (!CtdlThreadCheckStop()) {
1329                 /* Initialize the fdset. */
1330                 FD_ZERO(&master_fds);
1331                 highest = 0;
1332
1333                 /* First, add the various master sockets to the fdset. */
1334                 for (serviceptr = ServiceHookTable; serviceptr != NULL;
1335                 serviceptr = serviceptr->next ) {
1336                         m = serviceptr->msock;
1337                         FD_SET(m, &master_fds);
1338                         if (m > highest) {
1339                                 highest = m;
1340                         }
1341                 }
1342
1343                 if (!CtdlThreadCheckStop()) {
1344                         tv.tv_sec = 60;         /* wake up every second if no input */
1345                         tv.tv_usec = 0;
1346                         retval = CtdlThreadSelect(highest + 1, &master_fds, NULL, NULL, &tv);
1347                 }
1348                 else
1349                         return NULL;
1350
1351                 /* Now figure out who made this select() unblock.
1352                  * First, check for an error or exit condition.
1353                  */
1354                 if (retval < 0) {
1355                         if (errno == EBADF) {
1356                                 syslog(LOG_NOTICE, "select() failed: (%s)\n",
1357                                         strerror(errno));
1358                                 continue;
1359                         }
1360                         if (errno != EINTR) {
1361                                 syslog(LOG_EMERG, "Exiting (%s)\n", strerror(errno));
1362                                 CtdlThreadStopAll();
1363                         } else {
1364                                 syslog(LOG_DEBUG, "Interrupted CtdlThreadSelect.\n");
1365                                 if (CtdlThreadCheckStop()) return(NULL);
1366                                 continue;
1367                         }
1368                 }
1369                 else if(retval == 0) {
1370                         if (CtdlThreadCheckStop()) return(NULL);
1371                         continue;
1372                 }
1373                 /* Next, check to see if it's a new client connecting
1374                  * on a master socket.
1375                  */
1376                 else for (serviceptr = ServiceHookTable; serviceptr != NULL;
1377                      serviceptr = serviceptr->next ) {
1378
1379                         if (FD_ISSET(serviceptr->msock, &master_fds)) {
1380                                 ssock = accept(serviceptr->msock, NULL, 0);
1381                                 if (ssock >= 0) {
1382                                         syslog(LOG_DEBUG, "New client socket %d\n", ssock);
1383
1384                                         /* The master socket is non-blocking but the client
1385                                          * sockets need to be blocking, otherwise certain
1386                                          * operations barf on FreeBSD.  Not a fatal error.
1387                                          */
1388                                         if (fcntl(ssock, F_SETFL, 0) < 0) {
1389                                                 syslog(LOG_EMERG,
1390                                                         "citserver: Can't set socket to blocking: %s\n",
1391                                                         strerror(errno));
1392                                         }
1393
1394                                         /* New context will be created already
1395                                          * set up in the CON_EXECUTING state.
1396                                          */
1397                                         con = CreateNewContext();
1398
1399                                         /* Assign our new socket number to it. */
1400                                         con->client_socket = ssock;
1401                                         con->h_command_function = serviceptr->h_command_function;
1402                                         con->h_async_function = serviceptr->h_async_function;
1403                                         con->h_greeting_function = serviceptr->h_greeting_function;
1404                                         con->ServiceName = serviceptr->ServiceName;
1405                                         
1406                                         /* Determine whether it's a local socket */
1407                                         if (serviceptr->sockpath != NULL) {
1408                                                 con->is_local_socket = 1;
1409                                         }
1410         
1411                                         /* Set the SO_REUSEADDR socket option */
1412                                         i = 1;
1413                                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
1414                                         con->state = CON_GREETING;
1415                                         retval--;
1416                                         if (retval == 0)
1417                                                 break;
1418                                 }
1419                         }
1420                 }
1421         }
1422         CtdlClearSystemContext();
1423
1424         return NULL;
1425 }
1426
1427
1428
1429 /*
1430  * SyslogFacility()
1431  * Translate text facility name to syslog.h defined value.
1432  */
1433 int SyslogFacility(char *name)
1434 {
1435         int i;
1436         struct
1437         {
1438                 int facility;
1439                 char *name;
1440         }   facTbl[] =
1441         {
1442                 {   LOG_KERN,   "kern"          },
1443                 {   LOG_USER,   "user"          },
1444                 {   LOG_MAIL,   "mail"          },
1445                 {   LOG_DAEMON, "daemon"        },
1446                 {   LOG_AUTH,   "auth"          },
1447                 {   LOG_SYSLOG, "syslog"        },
1448                 {   LOG_LPR,    "lpr"           },
1449                 {   LOG_NEWS,   "news"          },
1450                 {   LOG_UUCP,   "uucp"          },
1451                 {   LOG_LOCAL0, "local0"        },
1452                 {   LOG_LOCAL1, "local1"        },
1453                 {   LOG_LOCAL2, "local2"        },
1454                 {   LOG_LOCAL3, "local3"        },
1455                 {   LOG_LOCAL4, "local4"        },
1456                 {   LOG_LOCAL5, "local5"        },
1457                 {   LOG_LOCAL6, "local6"        },
1458                 {   LOG_LOCAL7, "local7"        },
1459                 {   0,            NULL          }
1460         };
1461         for(i = 0; facTbl[i].name != NULL; i++) {
1462                 if(!strcasecmp(name, facTbl[i].name))
1463                         return facTbl[i].facility;
1464         }
1465         return LOG_DAEMON;
1466 }