New windows NT/95 port using MSV5.0. The test server 'ztest' was
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (c) 1995, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: statserv.c,v $
7  * Revision 1.37  1997-09-01 08:53:01  adam
8  * New windows NT/95 port using MSV5.0. The test server 'ztest' was
9  * moved a separate directory. MSV5.0 project server.dsp created.
10  * As an option, the server can now operate as an NT service.
11  *
12  * Revision 1.36  1996/07/06 19:58:36  quinn
13  * System headerfiles gathered in yconfig
14  *
15  * Revision 1.35  1996/05/29  10:03:28  quinn
16  * Options work
17  *
18  * Revision 1.34  1996/02/21  13:12:07  quinn
19  * *** empty log message ***
20  *
21  * Revision 1.33  1996/02/10  12:23:49  quinn
22  * Enable inetd operations fro TCP/IP stack
23  *
24  * Revision 1.32  1996/01/19  15:41:52  quinn
25  * *** empty log message ***
26  *
27  * Revision 1.31  1995/11/17  11:09:39  adam
28  * Added new option '-c' to specify configuration name in control block.
29  *
30  * Revision 1.30  1995/11/01  13:54:59  quinn
31  * Minor adjustments
32  *
33  * Revision 1.29  1995/10/30  12:41:29  quinn
34  * Added hostname lookup for server.
35  *
36  * Revision 1.28  1995/09/29  17:12:30  quinn
37  * Smallish
38  *
39  * Revision 1.27  1995/09/27  15:03:02  quinn
40  * Modified function heads & prototypes.
41  *
42  * Revision 1.26  1995/08/29  14:44:51  quinn
43  * Reset timeouts.
44  *
45  * Revision 1.25  1995/08/29  11:18:02  quinn
46  * Added code to receive close
47  *
48  * Revision 1.24  1995/06/16  10:31:39  quinn
49  * Added session timeout.
50  *
51  * Revision 1.23  1995/06/15  12:30:48  quinn
52  * Setuid-facility.
53  *
54  * Revision 1.22  1995/06/15  07:45:17  quinn
55  * Moving to v3.
56  *
57  * Revision 1.21  1995/06/06  08:15:40  quinn
58  * Cosmetic.
59  *
60  * Revision 1.20  1995/05/29  08:12:09  quinn
61  * Moved oid to util
62  *
63  * Revision 1.19  1995/05/16  09:37:27  quinn
64  * Fixed bug
65  *
66  * Revision 1.18  1995/05/16  08:51:09  quinn
67  * License, documentation, and memory fixes
68  *
69  * Revision 1.17  1995/05/15  11:56:42  quinn
70  * Asynchronous facilities. Restructuring of seshigh code.
71  *
72  * Revision 1.16  1995/04/10  10:23:40  quinn
73  * Some work to add scan and other things.
74  *
75  * Revision 1.15  1995/03/31  10:16:51  quinn
76  * Fixed logging.
77  *
78  * Revision 1.14  1995/03/31  09:18:58  quinn
79  * Added logging.
80  *
81  * Revision 1.13  1995/03/30  16:08:39  quinn
82  * Little mods.
83  *
84  * Revision 1.12  1995/03/30  13:29:02  quinn
85  * Smallish
86  *
87  * Revision 1.11  1995/03/30  12:18:17  quinn
88  * Fixed bug.
89  *
90  * Revision 1.10  1995/03/29  15:40:16  quinn
91  * Ongoing work. Statserv is now dynamic by default
92  *
93  * Revision 1.9  1995/03/27  08:34:30  quinn
94  * Added dynamic server functionality.
95  * Released bindings to session.c (is now redundant)
96  *
97  * Revision 1.8  1995/03/20  09:46:26  quinn
98  * Added osi support.
99  *
100  * Revision 1.7  1995/03/16  13:29:04  quinn
101  * Partitioned server.
102  *
103  * Revision 1.6  1995/03/15  15:18:52  quinn
104  * Little changes to better support nonblocking I/O
105  * Added backend.h
106  *
107  * Revision 1.5  1995/03/15  08:37:45  quinn
108  * Now we're pretty much set for nonblocking I/O.
109  *
110  * Revision 1.4  1995/03/14  16:59:48  quinn
111  * Bug-fixes
112  *
113  * Revision 1.3  1995/03/14  11:30:15  quinn
114  * Works better now.
115  *
116  * Revision 1.2  1995/03/14  10:28:03  quinn
117  * More work on demo server.
118  *
119  * Revision 1.1  1995/03/10  18:22:45  quinn
120  * The rudiments of an asynchronous server.
121  *
122  */
123
124 /*
125  * Simple, static server. I wouldn't advise a static server unless you
126  * really have to, but it's great for debugging memory management.  :)
127  */
128
129 #include <yconfig.h>
130 #include <stdio.h>
131 #ifdef WINDOWS
132 #include <process.h>
133 #include <winsock.h>
134 #include <direct.h>
135 #else
136 #include <unistd.h>
137 #include <pwd.h>
138 #endif
139 #include <fcntl.h>
140 #include <signal.h>
141 #include <errno.h>
142
143 #include <options.h>
144 #include "eventl.h"
145 #include "session.h"
146 #include <comstack.h>
147 #include <tcpip.h>
148 #ifdef USE_XTIMOSI
149 #include <xmosi.h>
150 #endif
151 #include <log.h>
152 #include <statserv.h>
153
154 static IOCHAN pListener;
155
156 static char *me = "statserver";
157 /*
158  * default behavior.
159  */
160 static statserv_options_block control_block = {
161     1,                          /* dynamic mode */
162     LOG_DEFAULT_LEVEL,          /* log level */
163     "",                         /* no PDUs */
164     "",                         /* diagnostic output to stderr */
165     "tcp:@:9999",               /* default listener port */
166     PROTO_Z3950,                /* default application protocol */
167     60,                         /* idle timeout (minutes) */
168     1024*1024,                  /* maximum PDU size (approx.) to allow */
169     "default-config",           /* configuration name to pass to backend */
170     ""                          /* set user id */
171 };
172
173 /*
174  * handle incoming connect requests.
175  * The dynamic mode is a bit tricky mostly because we want to avoid
176  * doing all of the listening and accepting in the parent - it's
177  * safer that way.
178  */
179 #ifdef WINDOWS
180
181 typedef struct _ThreadList ThreadList;
182
183 typedef struct _ThreadList
184 {
185     HANDLE hThread;
186     IOCHAN pIOChannel;
187     ThreadList *pNext;
188 } ThreadList;
189
190 static ThreadList *pFirstThread;
191 static CRITICAL_SECTION Thread_CritSect;
192 static BOOL bInitialized = FALSE;
193
194 static void ThreadList_Initialize()
195 {
196     /* Initialize the critical Sections */
197      InitializeCriticalSection(&Thread_CritSect);
198
199      /* Set the first thraed */
200      pFirstThread = NULL;
201
202      /* we have been initialized */
203      bInitialized = TRUE;
204 }
205
206 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
207 {
208     /* Only one thread can go through this section at a time */
209     EnterCriticalSection(&Thread_CritSect);
210
211     {
212         /* Lets create our new object */
213         ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
214         pNewThread->hThread = hThread;
215         pNewThread->pIOChannel = pIOChannel;
216         pNewThread->pNext = pFirstThread;
217         pFirstThread = pNewThread;
218
219         /* Lets let somebody else create a new object now */
220         LeaveCriticalSection(&Thread_CritSect);
221     }
222 }
223
224 void statserv_remove(IOCHAN pIOChannel)
225 {
226     /* Only one thread can go through this section at a time */
227     EnterCriticalSection(&Thread_CritSect);
228
229     {
230         ThreadList *pCurrentThread = pFirstThread;
231         ThreadList *pNextThread;
232         ThreadList *pPrevThread =NULL;
233
234         /* Step through alll the threads */
235         for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
236         {
237             /* We only need to compare on the IO Channel */
238             if (pCurrentThread->pIOChannel == pIOChannel)
239             {
240                 /* We have found the thread we want to delete */
241                 /* First of all reset the next pointers */
242                 if (pPrevThread == NULL)
243                     pFirstThread = pCurrentThread->pNext;
244                 else
245                     pPrevThread->pNext = pCurrentThread->pNext;
246
247                 /* All we need todo now is delete the memory */
248                 free(pCurrentThread);
249
250                 /* No need to look at any more threads */
251                 pNextThread = NULL;
252             }
253             else
254             {
255                 /* We need to look at another thread */
256                 pNextThread = pCurrentThread->pNext;
257             }
258         }
259
260         /* Lets let somebody else remove an object now */
261         LeaveCriticalSection(&Thread_CritSect);
262     }
263 }
264
265 void statserv_closedown()
266 {
267     /* Shouldn't do anything if we are not initialized */
268     if (bInitialized)
269     {
270         int iHandles = 0;
271         HANDLE *pThreadHandles = NULL;
272
273         /* We need to stop threads adding and removing while we start the closedown process */
274         EnterCriticalSection(&Thread_CritSect);
275
276         {
277             /* We have exclusive access to the thread stuff now */
278             /* Y didn't i use a semaphore - Oh well never mind */
279             ThreadList *pCurrentThread = pFirstThread;
280
281             /* Before we do anything else, we need to shutdown the listener */
282             if (pListener != NULL)
283                 iochan_destroy(pListener);
284
285             for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
286             {
287                 /* Just destroy the IOCHAN, that should do the trick */
288                 iochan_destroy(pCurrentThread->pIOChannel);
289
290                 /* Keep a running count of our handles */
291                 iHandles++;
292             }
293
294             if (iHandles > 0)
295             {
296                 HANDLE *pCurrentHandle ;
297
298                 /* Allocate the thread handle array */
299                 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
300                 pCurrentHandle = pThreadHandles; 
301
302                 for (pCurrentThread = pFirstThread;
303                      pCurrentThread != NULL;
304                      pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
305                 {
306                     /* Just the handle */
307                     *pCurrentHandle = pCurrentThread->hThread;
308                 }
309             }
310
311             /* We can now leave the critical section */
312             LeaveCriticalSection(&Thread_CritSect);
313         }
314
315         /* Now we can really do something */
316         if (iHandles > 0)
317         {
318             /* This will now wait, until all the threads close */
319             WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
320
321             /* Free the memory we allocated for the handle array */
322             free(pThreadHandles);
323         }
324
325         /* No longer require the critical section, since all threads are dead */
326         DeleteCriticalSection(&Thread_CritSect);
327     }
328 }
329
330 static void listener(IOCHAN h, int event)
331 {
332     COMSTACK line = (COMSTACK) iochan_getdata(h);
333     association *newas;
334     int res;
335     HANDLE NewHandle;
336
337     if (event == EVENT_INPUT)
338     {
339         if ((res = cs_listen(line, 0, 0)) < 0)
340         {
341                 logf(LOG_FATAL, "cs_listen failed.");
342             return;
343         }
344         else if (res == 1)
345                 return;
346         logf(LOG_DEBUG, "listen ok");
347         iochan_setevent(h, EVENT_OUTPUT);
348         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
349     }
350     else if (event == EVENT_OUTPUT)
351     {
352         COMSTACK new_line;
353         IOCHAN new_chan;
354             char *a;
355         DWORD ThreadId;
356
357             if (!(new_line = cs_accept(line)))
358             {
359                 logf(LOG_FATAL, "Accept failed.");
360                 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
361                 return;
362             }
363             logf(LOG_DEBUG, "accept ok");
364
365             if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, EVENT_INPUT)))
366             {
367                 logf(LOG_FATAL, "Failed to create iochan");
368             iochan_destroy(h);
369             return;
370             }
371             if (!(newas = create_association(new_chan, new_line)))
372             {
373                 logf(LOG_FATAL, "Failed to create new assoc.");
374             iochan_destroy(h);
375             return;
376             }
377             iochan_setdata(new_chan, newas);
378             iochan_settimeout(new_chan, control_block.idle_timeout * 60);
379             a = cs_addrstr(new_line);
380             logf(LOG_LOG, "Accepted connection from %s", a ? a : "[Unknown]");
381
382         /* Now what we need todo is create a new thread with this iochan as the parameter */
383 /*        if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)event_loop, new_chan, 0, &ThreadId) == NULL)
384 */
385         /* Somehow, somewhere we need to store this thread id, otherwise we won't be able to close cleanly */
386         NewHandle = (HANDLE)_beginthreadex(NULL, 0, event_loop, new_chan, 0, &ThreadId);
387         if (NewHandle == (HANDLE)-1)
388             {
389                 logf(LOG_FATAL, "Failed to create new thread.");
390             iochan_destroy(h);
391             return;
392             }
393
394         /* We successfully created the thread, so add it to the list */
395         statserv_add(NewHandle, new_chan);
396
397         logf(LOG_DEBUG, "Created new thread, iochan %p", new_chan);
398         iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
399     }
400     else
401     {
402         logf(LOG_FATAL, "Bad event on listener.");
403         iochan_destroy(h);
404         return;
405     }
406 }
407
408 #else /* WINDOWS */
409
410 /* To save having an #ifdef in event_loop we need to define this empty function */
411 void statserv_remove(IOCHAN pIOChannel)
412 {
413 }
414
415 void statserv_closedown()
416 {
417     /* We don't need todoanything here - or do we */
418     if (pListener != NULL)
419         iochan_destroy(pListener);
420 }
421
422 static void listener(IOCHAN h, int event)
423 {
424     COMSTACK line = (COMSTACK) iochan_getdata(h);
425     association *newas;
426     static int hand[2];
427     static int child = 0;
428     int res;
429
430     if (event == EVENT_INPUT)
431     {
432         if (control_block.dynamic && !child) 
433         {
434             int res;
435
436             if (pipe(hand) < 0)
437             {
438                 logf(LOG_FATAL|LOG_ERRNO, "pipe");
439                 iochan_destroy(h);
440                 return;
441             }
442             if ((res = fork()) < 0)
443             {
444                 logf(LOG_FATAL|LOG_ERRNO, "fork");
445                 iochan_destroy(h);
446                 return;
447             }
448             else if (res == 0) /* child */
449             {
450                 char nbuf[100];
451                 IOCHAN pp;
452
453                 close(hand[0]);
454                 child = 1;
455                 for (pp = iochan_getchan(); pp; pp = iochan_getnext(pp))
456                 {
457                     if (pp != h)
458                     {
459                         COMSTACK l = iochan_getdata(pp);
460                         cs_close(l);
461                         iochan_destroy(pp);
462                     }
463                 }
464                 sprintf(nbuf, "%s(%d)", me, getpid());
465                 log_init(control_block.loglevel, nbuf, 0);
466             }
467             else /* parent */
468             {
469                 close(hand[1]);
470                 /* wait for child to take the call */
471                 for (;;)
472                 {
473                     char dummy[1];
474                     int res;
475                     
476                     if ((res = read(hand[0], dummy, 1)) < 0 && errno != EINTR)
477                     {
478                         logf(LOG_FATAL|LOG_ERRNO, "handshake read");
479                         return;
480                     }
481                     else if (res >= 0)
482                         break;
483                 }
484                 logf(LOG_DEBUG, "P: Child has taken the call");
485                 close(hand[0]);
486                 return;
487             }
488         }
489         if ((res = cs_listen(line, 0, 0)) < 0)
490         {
491             logf(LOG_FATAL, "cs_listen failed.");
492             return;
493         }
494         else if (res == 1)
495             return;
496         logf(LOG_DEBUG, "listen ok");
497         iochan_setevent(h, EVENT_OUTPUT);
498         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
499     }
500     /* in dynamic mode, only the child ever comes down here */
501     else if (event == EVENT_OUTPUT)
502     {
503         COMSTACK new_line;
504         IOCHAN new_chan;
505         char *a;
506
507         if (!(new_line = cs_accept(line)))
508         {
509             logf(LOG_FATAL, "Accept failed.");
510             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
511             return;
512         }
513         logf(LOG_DEBUG, "accept ok");
514         if (control_block.dynamic)
515         {
516             IOCHAN pp;
517             /* close our half of the listener socket */
518             for (pp = iochan_getchan(); pp; pp = iochan_getnext(pp))
519             {
520                 COMSTACK l = iochan_getdata(pp);
521                 cs_close(l);
522                 iochan_destroy(pp);
523             }
524             /* release dad */
525             logf(LOG_DEBUG, "Releasing parent");
526             close(hand[1]);
527         }
528         else
529             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
530
531         if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
532             EVENT_INPUT)))
533         {
534             logf(LOG_FATAL, "Failed to create iochan");
535             iochan_destroy(h);
536             return;
537         }
538         if (!(newas = create_association(new_chan, new_line)))
539         {
540             logf(LOG_FATAL, "Failed to create new assoc.");
541             iochan_destroy(h);
542             return;
543         }
544         iochan_setdata(new_chan, newas);
545         iochan_settimeout(new_chan, control_block.idle_timeout * 60);
546         a = cs_addrstr(new_line);
547         logf(LOG_LOG, "Accepted connection from %s", a ? a : "[Unknown]");
548     }
549     else
550     {
551         logf(LOG_FATAL, "Bad event on listener.");
552         iochan_destroy(h);
553         return;
554     }
555 }
556
557 #endif /* WINDOWS */
558
559 static void inetd_connection(int what)
560 {
561     COMSTACK line;
562     IOCHAN chan;
563     association *assoc;
564     char *addr;
565
566     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
567     {
568         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
569         {
570             if ((assoc = create_association(chan, line)))
571             {
572                 iochan_setdata(chan, assoc);
573                 iochan_settimeout(chan, control_block.idle_timeout * 60);
574                 addr = cs_addrstr(line);
575                 logf(LOG_LOG, "Inetd association from %s", addr ? addr : "[UNKNOWN]");
576             }
577             else
578             {
579                 logf(LOG_FATAL, "Failed to create association structure");
580             }
581         }
582         else
583         {
584             logf(LOG_FATAL, "Failed to create iochan");
585         }
586     }
587     else
588     {
589         logf(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
590     }
591 }
592
593 /*
594  * Set up a listening endpoint, and give it to the event-handler.
595  */
596 static void add_listener(char *where, int what)
597 {
598     COMSTACK l;
599     CS_TYPE type;
600     char mode[100], addr[100];
601     void *ap;
602     IOCHAN lst = NULL;
603
604     if (!where || sscanf(where, "%[^:]:%s", mode, addr) != 2)
605     {
606         fprintf(stderr, "%s: Address format: ('tcp'|'osi')':'<address>.\n",
607             me);
608     }
609     if (!strcmp(mode, "tcp"))
610     {
611         if (!(ap = tcpip_strtoaddr(addr)))
612         {
613             fprintf(stderr, "Address resolution failed for TCP.\n");
614         }
615         type = tcpip_type;
616     }
617     else if (!strcmp(mode, "osi"))
618     {
619 #ifdef USE_XTIMOSI
620         if (!(ap = mosi_strtoaddr(addr)))
621         {
622             fprintf(stderr, "Address resolution failed for TCP.\n");
623         }
624         type = mosi_type;
625 #else
626         fprintf(stderr, "OSI Transport not allowed by configuration.\n");
627 #endif
628     }
629     else
630     {
631         fprintf(stderr, "You must specify either 'osi:' or 'tcp:'.\n");
632     }
633     logf(LOG_LOG, "Adding %s %s listener on %s",
634         control_block.dynamic ? "dynamic" : "static",
635         what == PROTO_SR ? "SR" : "Z3950", where);
636     if (!(l = cs_create(type, 0, what)))
637     {
638         logf(LOG_FATAL|LOG_ERRNO, "Failed to create listener");
639     }
640     if (cs_bind(l, ap, CS_SERVER) < 0)
641     {
642         logf(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
643     }
644     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
645          EVENT_EXCEPT)))
646     {
647         logf(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
648     }
649     iochan_setdata(lst, l);
650
651     /* Ensure our listener chain is setup properly */
652     lst->next = pListener;
653     pListener = lst;
654 }
655
656 #ifndef WINDOWS
657 /* For windows we don't need to catch the signals */
658 static void catchchld(int num)
659 {
660     while (waitpid(-1, 0, WNOHANG) > 0)
661         ;
662     signal(SIGCHLD, catchchld);
663 }
664 #endif /* WINDOWS */
665
666 statserv_options_block *statserv_getcontrol(void)
667 {
668     static statserv_options_block cb;
669
670     memcpy(&cb, &control_block, sizeof(cb));
671     return &cb;
672 }
673
674 void statserv_setcontrol(statserv_options_block *block)
675 {
676     memcpy(&control_block, block, sizeof(*block));
677 }
678
679 int statserv_main(int argc, char **argv)
680 {
681     int ret, listeners = 0, inetd = 0, r;
682     char *arg;
683     int protocol = control_block.default_proto;
684
685 #ifdef WINDOWS
686     /* We need to initialize the thread list */
687     ThreadList_Initialize();
688 #endif /* WINDOWS */
689
690     me = argv[0];
691     while ((ret = options("a:iszSl:v:u:c:w:t:k:", argv, argc, &arg)) != -2)
692     {
693         switch (ret)
694         {
695             case 0:
696                     add_listener(arg, protocol);
697                     listeners++;
698                 break;
699             case 'z': protocol = PROTO_Z3950; break;
700             case 's': protocol = PROTO_SR; break;
701             case 'S': control_block.dynamic = 0; break;
702             case 'l':
703                 strcpy(control_block.logfile, arg ? arg : "");
704                 log_init(control_block.loglevel, me, control_block.logfile);
705                 break;
706             case 'v':
707                 control_block.loglevel = log_mask_str(arg);
708                 log_init(control_block.loglevel, me, control_block.logfile);
709                 break;
710             case 'a':
711                 strcpy(control_block.apdufile, arg ? arg : ""); break;
712             case 'u':
713                 strcpy(control_block.setuid, arg ? arg : ""); break;
714             case 'c':
715                 strcpy(control_block.configname, arg ? arg : ""); break;
716             case 't':
717                 if (!arg || !(r = atoi(arg)))
718                 {
719                     fprintf(stderr, "%s: Specify positive timeout for -t.\n",
720                         me);
721                     return(1);
722                 }
723                 control_block.idle_timeout = r;
724                 break;
725             case  'k':
726                 if (!arg || !(r = atoi(arg)))
727                 {
728                     fprintf(stderr, "%s: Specify positive timeout for -t.\n",
729                         me);
730                     return(1);
731                 }
732                 control_block.maxrecordsize = r * 1024;
733                 break;
734             case 'i':
735                 inetd = 1; break;
736             case 'w':
737                 if (chdir(arg))
738                 {
739                     perror(arg);
740
741                     return(1);
742                 }
743                 break;
744             default:
745                 fprintf(stderr, "Usage: %s [ -i -a <pdufile> -v <loglevel>"
746                         " -l <logfile> -u <user> -c <config> -t <minutes>"
747                         " -k <kilobytes>"
748                         " -zsS <listener-addr> -w <directory> ... ]\n", me);
749                 return(1);
750             }
751     }
752
753 #ifdef WINDOWS
754     log_init(control_block.loglevel, NULL, control_block.logfile);
755 #endif /* WINDOWS */
756
757     if ((pListener == NULL) && *control_block.default_listen)
758             add_listener(control_block.default_listen, protocol);
759
760 #ifndef WINDOWS
761     if (inetd)
762         inetd_connection(protocol);
763     else
764     {
765         if (control_block.dynamic)
766             signal(SIGCHLD, catchchld);
767     }
768     if (*control_block.setuid)
769     {
770         struct passwd *pw;
771         
772         if (!(pw = getpwnam(control_block.setuid)))
773         {
774             logf(LOG_FATAL, "%s: Unknown user", control_block.setuid);
775             return(1);
776         }
777         if (setuid(pw->pw_uid) < 0)
778         {
779             logf(LOG_FATAL|LOG_ERRNO, "setuid");
780             exit(1);
781         }
782     }
783 #endif /* WINDOWS */
784
785     logf(LOG_LOG, "Entering event loop.");
786         
787     if (pListener == NULL)
788         return(1);
789     else
790         return event_loop(pListener);
791 }