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