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