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