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