SOAP, SRW codecs and HTTP transport for YAZ using libxml2.
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (c) 1995-2003, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * NT threaded server code by
7  *   Chas Woodfield, Fretwell Downing Informatics.
8  *
9  * $Id: statserv.c,v 1.91 2003-02-12 15:06:43 adam Exp $
10  */
11
12 #include <stdio.h>
13 #include <string.h>
14 #ifdef WIN32
15 #include <process.h>
16 #include <winsock.h>
17 #include <direct.h>
18 #include "service.h"
19 #else
20 #include <unistd.h>
21 #include <pwd.h>
22 #endif
23
24 #if YAZ_POSIX_THREADS
25 #include <pthread.h>
26 #elif YAZ_GNU_THREADS
27 #include <pth.h>
28 #endif
29
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <errno.h>
33
34 #include <yaz/comstack.h>
35 #include <yaz/tcpip.h>
36 #include <yaz/options.h>
37 #ifdef USE_XTIMOSI
38 #include <yaz/xmosi.h>
39 #endif
40 #include <yaz/log.h>
41 #include "eventl.h"
42 #include "session.h"
43 #include <yaz/statserv.h>
44
45 static IOCHAN pListener = NULL;
46
47 static char *me = "statserver";
48 /*
49  * default behavior.
50  */
51 int check_options(int argc, char **argv);
52 statserv_options_block control_block = {
53     1,                          /* dynamic mode */
54     0,                          /* threaded mode */
55     0,                          /* one shot (single session) */
56     LOG_DEFAULT_LEVEL,          /* log level */
57     "",                         /* no PDUs */
58     "",                         /* diagnostic output to stderr */
59     "tcp:@:9999",               /* default listener port */
60     PROTO_Z3950,                /* default application protocol */
61     60,                         /* idle timeout (minutes) */
62     1024*1024,                  /* maximum PDU size (approx.) to allow */
63     "default-config",           /* configuration name to pass to backend */
64     "",                         /* set user id */
65     0,                          /* bend_start handler */
66     0,                          /* bend_stop handler */
67     check_options,              /* Default routine, for checking the run-time arguments */
68     check_ip_tcpd,
69     "",
70     0,                          /* default value for inet deamon */
71     0,                          /* handle (for service, etc) */
72     0,                          /* bend_init handle */
73     0,                          /* bend_close handle */
74 #ifdef WIN32
75     "Z39.50 Server",            /* NT Service Name */
76     "Server",                   /* NT application Name */
77     "",                         /* NT Service Dependencies */
78     "Z39.50 Server",            /* NT Service Display Name */
79 #endif /* WIN32 */
80     0                           /* SOAP handlers */
81 };
82
83 /*
84  * handle incoming connect requests.
85  * The dynamic mode is a bit tricky mostly because we want to avoid
86  * doing all of the listening and accepting in the parent - it's
87  * safer that way.
88  */
89 #ifdef WIN32
90
91 typedef struct _ThreadList ThreadList;
92
93 struct _ThreadList
94 {
95     HANDLE hThread;
96     IOCHAN pIOChannel;
97     ThreadList *pNext;
98 };
99
100 static ThreadList *pFirstThread;
101 static CRITICAL_SECTION Thread_CritSect;
102 static BOOL bInitialized = FALSE;
103
104 static void ThreadList_Initialize()
105 {
106     /* Initialize the critical Sections */
107     InitializeCriticalSection(&Thread_CritSect);
108
109      /* Set the first thraed */
110     pFirstThread = NULL;
111
112     /* we have been initialized */
113     bInitialized = TRUE;
114 }
115
116 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
117 {
118     /* Only one thread can go through this section at a time */
119     EnterCriticalSection(&Thread_CritSect);
120
121     {
122         /* Lets create our new object */
123         ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
124         pNewThread->hThread = hThread;
125         pNewThread->pIOChannel = pIOChannel;
126         pNewThread->pNext = pFirstThread;
127         pFirstThread = pNewThread;
128
129         /* Lets let somebody else create a new object now */
130         LeaveCriticalSection(&Thread_CritSect);
131     }
132 }
133
134 void statserv_remove(IOCHAN pIOChannel)
135 {
136     /* Only one thread can go through this section at a time */
137     EnterCriticalSection(&Thread_CritSect);
138
139     {
140         ThreadList *pCurrentThread = pFirstThread;
141         ThreadList *pNextThread;
142         ThreadList *pPrevThread =NULL;
143
144         /* Step through alll the threads */
145         for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
146         {
147             /* We only need to compare on the IO Channel */
148             if (pCurrentThread->pIOChannel == pIOChannel)
149             {
150                 /* We have found the thread we want to delete */
151                 /* First of all reset the next pointers */
152                 if (pPrevThread == NULL)
153                     pFirstThread = pCurrentThread->pNext;
154                 else
155                     pPrevThread->pNext = pCurrentThread->pNext;
156
157                 /* All we need todo now is delete the memory */
158                 free(pCurrentThread);
159
160                 /* No need to look at any more threads */
161                 pNextThread = NULL;
162             }
163             else
164             {
165                 /* We need to look at another thread */
166                 pNextThread = pCurrentThread->pNext;
167                 pPrevThread = pCurrentThread;
168             }
169         }
170
171         /* Lets let somebody else remove an object now */
172         LeaveCriticalSection(&Thread_CritSect);
173     }
174 }
175
176 /* WIN32 statserv_closedown */
177 void statserv_closedown()
178 {
179     /* Shouldn't do anything if we are not initialized */
180     if (bInitialized)
181     {
182         int iHandles = 0;
183         HANDLE *pThreadHandles = NULL;
184
185         /* We need to stop threads adding and removing while we */
186         /* start the closedown process */
187         EnterCriticalSection(&Thread_CritSect);
188
189         {
190             /* We have exclusive access to the thread stuff now */
191             /* Y didn't i use a semaphore - Oh well never mind */
192             ThreadList *pCurrentThread = pFirstThread;
193
194             /* Before we do anything else, we need to shutdown the listener */
195             if (pListener != NULL)
196                 iochan_destroy(pListener);
197
198             for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
199             {
200                 /* Just destroy the IOCHAN, that should do the trick */
201                 iochan_destroy(pCurrentThread->pIOChannel);
202                 closesocket(pCurrentThread->pIOChannel->fd);
203
204                 /* Keep a running count of our handles */
205                 iHandles++;
206             }
207
208             if (iHandles > 0)
209             {
210                 HANDLE *pCurrentHandle ;
211
212                 /* Allocate the thread handle array */
213                 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
214                 pCurrentHandle = pThreadHandles; 
215
216                 for (pCurrentThread = pFirstThread;
217                      pCurrentThread != NULL;
218                      pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
219                 {
220                     /* Just the handle */
221                     *pCurrentHandle = pCurrentThread->hThread;
222                 }
223             }
224
225             /* We can now leave the critical section */
226             LeaveCriticalSection(&Thread_CritSect);
227         }
228
229         /* Now we can really do something */
230         if (iHandles > 0)
231         {
232             logf (LOG_LOG, "waiting for %d to die", iHandles);
233             /* This will now wait, until all the threads close */
234             WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
235
236             /* Free the memory we allocated for the handle array */
237             free(pThreadHandles);
238         }
239
240         if (control_block.bend_stop)
241             (*control_block.bend_stop)(&control_block);
242         /* No longer require the critical section, since all threads are dead */
243         DeleteCriticalSection(&Thread_CritSect);
244     }
245 }
246
247 void __cdecl event_loop_thread (IOCHAN iochan)
248 {
249     event_loop (&iochan);
250 }
251
252 /* WIN32 listener */
253 static void listener(IOCHAN h, int event)   
254 {
255     COMSTACK line = (COMSTACK) iochan_getdata(h);
256     association *newas;
257     int res;
258     HANDLE newHandle;
259
260     if (event == EVENT_INPUT)
261     {
262         if ((res = cs_listen(line, 0, 0)) < 0)
263         {
264             yaz_log(LOG_FATAL, "cs_listen failed");
265             return;
266         }
267         else if (res == 1)
268             return;
269         yaz_log(LOG_DEBUG, "listen ok");
270         iochan_setevent(h, EVENT_OUTPUT);
271         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
272     }
273     else if (event == EVENT_OUTPUT)
274     {
275         COMSTACK new_line = cs_accept(line);
276         IOCHAN new_chan;
277         char *a = NULL;
278
279         if (!new_line)
280         {
281             yaz_log(LOG_FATAL, "Accept failed.");
282             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
283             return;
284         }
285         yaz_log(LOG_DEBUG, "Accept ok");
286
287         if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
288                                        EVENT_INPUT)))
289         {
290             yaz_log(LOG_FATAL, "Failed to create iochan");
291             iochan_destroy(h);
292             return;
293         }
294
295         yaz_log(LOG_DEBUG, "Creating association");
296         if (!(newas = create_association(new_chan, new_line)))
297         {
298             yaz_log(LOG_FATAL, "Failed to create new assoc.");
299             iochan_destroy(h);
300             return;
301         }
302         newas->cs_get_mask = EVENT_INPUT;
303         newas->cs_put_mask = 0;
304         newas->cs_accept_mask = 0;
305
306         yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
307         iochan_setdata(new_chan, newas);
308         iochan_settimeout(new_chan, control_block.idle_timeout * 60);
309
310         /* Now what we need todo is create a new thread with this iochan as
311            the parameter */
312         newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
313         if (newHandle == (HANDLE) -1)
314         {
315             
316             yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread.");
317             iochan_destroy(h);
318             return;
319         }
320         /* We successfully created the thread, so add it to the list */
321         statserv_add(newHandle, new_chan);
322
323         yaz_log(LOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
324         iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
325     }
326     else
327     {
328         yaz_log(LOG_FATAL, "Bad event on listener.");
329         iochan_destroy(h);
330         return;
331     }
332 }
333
334 int statserv_must_terminate(void)
335 {
336     return 0;
337 }
338
339 #else /* ! WIN32 */
340
341 static int term_flag = 0;
342 /* To save having an #ifdef in event_loop we need to
343    define this empty function 
344 */
345 int statserv_must_terminate(void)
346 {
347     return term_flag;
348 }
349
350 void statserv_remove(IOCHAN pIOChannel)
351 {
352 }
353
354 void statserv_closedown()
355 {
356     IOCHAN p;
357
358     if (control_block.bend_stop)
359         (*control_block.bend_stop)(&control_block);
360     for (p = pListener; p; p = p->next)
361     {
362         iochan_destroy(p);
363     }
364 }
365
366 void sigterm(int sig)
367 {
368     term_flag = 1;
369 }
370
371 static void *new_session (void *vp);
372 static int no_sessions = 0;
373
374 /* UNIX listener */
375 static void listener(IOCHAN h, int event)
376 {
377     COMSTACK line = (COMSTACK) iochan_getdata(h);
378     static int hand[2];
379     static int child = 0;
380     int res;
381
382     if (event == EVENT_INPUT)
383     {
384         if (control_block.dynamic && !child) 
385         {
386             int res;
387
388             ++no_sessions;
389             if (pipe(hand) < 0)
390             {
391                 yaz_log(LOG_FATAL|LOG_ERRNO, "pipe");
392                 iochan_destroy(h);
393                 return;
394             }
395             if ((res = fork()) < 0)
396             {
397                 yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
398                 iochan_destroy(h);
399                 return;
400             }
401             else if (res == 0) /* child */
402             {
403                 char nbuf[100];
404                 IOCHAN pp;
405
406                 close(hand[0]);
407                 child = 1;
408                 for (pp = pListener; pp; pp = iochan_getnext(pp))
409                 {
410                     if (pp != h)
411                     {
412                         COMSTACK l = (COMSTACK)iochan_getdata(pp);
413                         cs_close(l);
414                         iochan_destroy(pp);
415                     }
416                 }
417                 sprintf(nbuf, "%s(%d)", me, getpid());
418                 yaz_log_init(control_block.loglevel, nbuf, 0);
419                 /* ensure that bend_stop is not called when each child exits -
420                    only for the main process .. 
421                 */
422                 control_block.bend_stop = 0;
423             }
424             else /* parent */
425             {
426                 close(hand[1]);
427                 /* wait for child to take the call */
428                 for (;;)
429                 {
430                     char dummy[1];
431                     int res;
432                     
433                     if ((res = read(hand[0], dummy, 1)) < 0 &&
434                                      yaz_errno() != EINTR)
435                     {
436                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
437                         return;
438                     }
439                     else if (res >= 0)
440                         break;
441                 }
442                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
443                 close(hand[0]);
444                 return;
445             }
446         }
447         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
448                                    control_block.daemon_name)) < 0)
449         {
450             yaz_log(LOG_WARN|LOG_ERRNO, "cs_listen failed");
451             return;
452         }
453         else if (res == 1)
454             return;
455         yaz_log(LOG_DEBUG, "listen ok");
456         iochan_setevent(h, EVENT_OUTPUT);
457         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
458     }
459     /* in dynamic mode, only the child ever comes down here */
460     else if (event == EVENT_OUTPUT)
461     {
462         COMSTACK new_line = cs_accept(line);
463
464         if (!new_line)
465         {
466             yaz_log(LOG_FATAL, "Accept failed.");
467             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
468             return;
469         }
470         yaz_log(LOG_DEBUG, "accept ok");
471         if (control_block.dynamic)
472         {
473             IOCHAN pp;
474             /* close our half of the listener socket */
475             for (pp = pListener; pp; pp = iochan_getnext(pp))
476             {
477                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
478                 cs_close(l);
479                 iochan_destroy(pp);
480             }
481             /* release dad */
482             yaz_log(LOG_DEBUG, "Releasing parent");
483             close(hand[1]);
484         }
485         else
486         {
487             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
488             ++no_sessions;
489         }
490 #if YAZ_POSIX_THREADS
491         if (control_block.threads)
492         {
493             pthread_t child_thread;
494             pthread_create (&child_thread, 0, new_session, new_line);
495             pthread_detach (child_thread);
496         }
497         else
498             new_session(new_line);
499 #elif YAZ_GNU_THREADS
500         if (control_block.threads)
501         {
502             pth_attr_t attr;
503             pth_t child_thread;
504
505             attr = pth_attr_new ();
506             pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
507             pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
508             pth_attr_set (attr, PTH_ATTR_NAME, "session");
509             yaz_log (LOG_LOG, "pth_spawn begin");
510             child_thread = pth_spawn (attr, new_session, new_line);
511             yaz_log (LOG_LOG, "pth_spawn finish");
512             pth_attr_destroy (attr);
513         }
514         else
515             new_session(new_line);
516 #else
517         new_session(new_line);
518 #endif
519     }
520     else if (event == EVENT_TIMEOUT)
521     {
522         yaz_log(LOG_LOG, "Shutting down listener.");
523         iochan_destroy(h);
524     }
525     else
526     {
527         yaz_log(LOG_FATAL, "Bad event on listener.");
528         iochan_destroy(h);
529     }
530 }
531
532 static void *new_session (void *vp)
533 {
534     char *a;
535     association *newas;
536     IOCHAN new_chan;
537     COMSTACK new_line = (COMSTACK) vp;
538
539     unsigned cs_get_mask, cs_accept_mask, mask =  
540         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
541         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
542
543     if (mask)    
544     {
545         cs_accept_mask = mask;  /* accept didn't complete */
546         cs_get_mask = 0;
547     }
548     else
549     {
550         cs_accept_mask = 0;     /* accept completed.  */
551         cs_get_mask = mask = EVENT_INPUT;
552     }
553
554     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
555     {
556         yaz_log(LOG_FATAL, "Failed to create iochan");
557         return 0;
558     }
559     if (!(newas = create_association(new_chan, new_line)))
560     {
561         yaz_log(LOG_FATAL, "Failed to create new assoc.");
562         return 0;
563     }
564     newas->cs_accept_mask = cs_accept_mask;
565     newas->cs_get_mask = cs_get_mask;
566
567     iochan_setdata(new_chan, newas);
568     iochan_settimeout(new_chan, control_block.idle_timeout * 60);
569     a = cs_addrstr(new_line);
570     yaz_log(LOG_LOG, "Starting session %d from %s",
571         no_sessions, a ? a : "[Unknown]");
572     if (control_block.threads)
573     {
574         event_loop(&new_chan);
575     }
576     else
577     {
578         new_chan->next = pListener;
579         pListener = new_chan;
580     }
581     return 0;
582 }
583
584 /* UNIX */
585 #endif
586
587 static void inetd_connection(int what)
588 {
589     COMSTACK line;
590     IOCHAN chan;
591     association *assoc;
592     char *addr;
593
594     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
595     {
596         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
597         {
598             if ((assoc = create_association(chan, line)))
599             {
600                 iochan_setdata(chan, assoc);
601                 iochan_settimeout(chan, control_block.idle_timeout * 60);
602                 addr = cs_addrstr(line);
603                 yaz_log(LOG_LOG, "Inetd association from %s",
604                         addr ? addr : "[UNKNOWN]");
605                 assoc->cs_get_mask = EVENT_INPUT;
606             }
607             else
608             {
609                 yaz_log(LOG_FATAL, "Failed to create association structure");
610             }
611             chan->next = pListener;
612             pListener = chan;
613         }
614         else
615         {
616             yaz_log(LOG_FATAL, "Failed to create iochan");
617         }
618     }
619     else
620     {
621         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
622     }
623 }
624
625 /*
626  * Set up a listening endpoint, and give it to the event-handler.
627  */
628 static int add_listener(char *where, int what)
629 {
630     COMSTACK l;
631     void *ap;
632     IOCHAN lst = NULL;
633     const char *mode;
634
635     if (control_block.dynamic)
636         mode = "dynamic";
637     else if (control_block.threads)
638         mode = "threaded";
639     else
640         mode = "static";
641
642     yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
643             what == PROTO_SR ? "SR" : "Z3950", where);
644
645     l = cs_create_host(where, 0, &ap);
646     if (!l)
647     {
648         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where);
649         return -1;
650     }
651     if (cs_bind(l, ap, CS_SERVER) < 0)
652     {
653         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
654         cs_close (l);
655         return -1;
656     }
657     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
658          EVENT_EXCEPT)))
659     {
660         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
661         cs_close (l);
662         return -1;
663     }
664     iochan_setdata(lst, l);
665
666     /* Ensure our listener chain is setup properly */
667     lst->next = pListener;
668     pListener = lst;
669     return 0; /* OK */
670 }
671
672 #ifndef WIN32
673 /* UNIX only (for windows we don't need to catch the signals) */
674 static void catchchld(int num)
675 {
676     while (waitpid(-1, 0, WNOHANG) > 0)
677         ;
678     signal(SIGCHLD, catchchld);
679 }
680 #endif
681
682 statserv_options_block *statserv_getcontrol(void)
683 {
684     static statserv_options_block cb;
685
686     memcpy(&cb, &control_block, sizeof(cb));
687     return &cb;
688 }
689
690 void statserv_setcontrol(statserv_options_block *block)
691 {
692     memcpy(&control_block, block, sizeof(*block));
693 }
694
695 void statserv_add_soap_handler(int (*h)(struct bend_soap_rr *rr),
696                                const char *ns)
697 {
698     struct bend_soap_handler *sh = xmalloc(sizeof(*sh));
699
700     sh->handler = h;
701     sh->ns = xstrdup(ns);
702     sh->next = control_block.soap_handlers;
703     control_block.soap_handlers = sh;
704     yaz_log(LOG_LOG, "soap handler added");
705 }
706
707 static void statserv_reset(void)
708 {
709     struct bend_soap_handler *sh = control_block.soap_handlers;
710
711     control_block.soap_handlers = 0;
712     while (sh)
713     {
714         struct bend_soap_handler *sh_next = sh->next;
715         xfree (sh->ns);
716         xfree (sh);
717         sh = sh_next;
718     }
719 }
720
721 int statserv_start(int argc, char **argv)
722 {
723     int ret;
724
725 #ifdef WIN32
726     /* We need to initialize the thread list */
727     ThreadList_Initialize();
728 /* WIN32 */
729 #endif
730     
731 #ifdef WIN32
732     if ((me = strrchr (argv[0], '\\')))
733         me++;
734     else
735         me = argv[0];
736 #else
737     me = argv[0];
738 #endif
739     if (control_block.options_func(argc, argv))
740         return(1);
741     
742     if (control_block.bend_start)
743         (*control_block.bend_start)(&control_block);
744 #ifdef WIN32
745     yaz_log (LOG_LOG, "Starting server %s", me);
746 #else
747 /* UNIX */
748     if (control_block.inetd)
749         inetd_connection(control_block.default_proto);
750     else
751     {
752         yaz_log (LOG_LOG, "Starting server %s pid=%d", me, getpid());
753 #if 0
754         sigset_t sigs_to_block;
755         
756         sigemptyset(&sigs_to_block);
757         sigaddset (&sigs_to_block, SIGTERM);
758         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
759         /* missing... */
760 #endif
761         if (control_block.dynamic)
762             signal(SIGCHLD, catchchld);
763     }
764     signal (SIGPIPE, SIG_IGN);
765     signal (SIGTERM, sigterm);
766     if (*control_block.setuid)
767     {
768         struct passwd *pw;
769         
770         if (!(pw = getpwnam(control_block.setuid)))
771         {
772             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
773             return(1);
774         }
775         if (setuid(pw->pw_uid) < 0)
776         {
777             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
778             exit(1);
779         }
780     }
781 /* UNIX */
782 #endif
783     
784     
785     if ((pListener == NULL) && *control_block.default_listen)
786         add_listener(control_block.default_listen,
787                      control_block.default_proto);
788         
789     if (pListener == NULL)
790         ret = 1;
791     else
792     {
793         yaz_log(LOG_LOG, "Entering event loop.");
794         ret = event_loop(&pListener);
795     }
796     return ret;
797 }
798
799 int check_options(int argc, char **argv)
800 {
801     int ret = 0, r;
802     char *arg;
803
804     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:", argv, argc, &arg)) != -2)
805     {
806         switch (ret)
807         {
808         case 0:
809             if (add_listener(arg, control_block.default_proto))
810                 return 1;  /* failed to create listener */
811             break;
812         case '1':        
813             control_block.one_shot = 1;
814             control_block.dynamic = 0;
815             break;
816         case 'z':
817             control_block.default_proto = PROTO_Z3950;
818             break;
819         case 's':
820             fprintf (stderr, "%s: SR protocol no longer supported\n", me);
821             exit (1);
822             break;
823         case 'S':
824             control_block.dynamic = 0;
825             break;
826         case 'T':
827 #if YAZ_POSIX_THREADS
828             control_block.dynamic = 0;
829             control_block.threads = 1;
830 #elif YAZ_GNU_THREADS
831             control_block.dynamic = 0;
832             control_block.threads = 1;
833 #else
834             fprintf(stderr, "%s: Threaded mode not available.\n", me);
835             return 1;
836 #endif
837             break;
838         case 'l':
839             strcpy(control_block.logfile, arg ? arg : "");
840             yaz_log_init(control_block.loglevel, me, control_block.logfile);
841             break;
842         case 'v':
843             control_block.loglevel = yaz_log_mask_str(arg);
844             yaz_log_init(control_block.loglevel, me, control_block.logfile);
845             break;
846         case 'a':
847             strcpy(control_block.apdufile, arg ? arg : "");
848             break;
849         case 'u':
850             strcpy(control_block.setuid, arg ? arg : "");
851             break;
852         case 'c':
853             strcpy(control_block.configname, arg ? arg : "");
854             break;
855         case 'd':
856             strcpy(control_block.daemon_name, arg ? arg : "");
857             break;
858         case 't':
859             if (!arg || !(r = atoi(arg)))
860             {
861                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
862                 return(1);
863             }
864             control_block.idle_timeout = r;
865             break;
866         case  'k':
867             if (!arg || !(r = atoi(arg)))
868             {
869                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
870                 return(1);
871             }
872             control_block.maxrecordsize = r * 1024;
873             break;
874         case 'i':
875             control_block.inetd = 1;
876             break;
877         case 'w':
878             if (chdir(arg))
879             {
880                 perror(arg);            
881                 return 1;
882             }
883             break;
884         default:
885             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
886                     " -l <logfile> -u <user> -c <config> -t <minutes>"
887                     " -k <kilobytes> -d <daemon>"
888                         " -ziST1 -w <directory> <listender-addr>... ]\n", me);
889             return 1;
890         }
891     }
892     return 0;
893 }
894
895 #ifdef WIN32
896 typedef struct _Args
897 {
898     char **argv;
899     int argc;
900 } Args; 
901
902 static Args ArgDetails;
903
904 /* name of the executable */
905 #define SZAPPNAME            "server"
906
907 /* list of service dependencies - "dep1\0dep2\0\0" */
908 #define SZDEPENDENCIES       ""
909
910 int statserv_main(int argc, char **argv,
911                   bend_initresult *(*bend_init)(bend_initrequest *r),
912                   void (*bend_close)(void *handle))
913 {
914     statserv_options_block *cb = statserv_getcontrol();
915     
916     cb->bend_init = bend_init;
917     cb->bend_close = bend_close;
918
919     statserv_setcontrol(cb);
920
921     /* Lets setup the Arg structure */
922     ArgDetails.argc = argc;
923     ArgDetails.argv = argv;
924     
925     /* Now setup the service with the service controller */
926     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
927                  cb->service_name, /* internal service name */
928                  cb->service_display_name, /* displayed name */
929                  SZDEPENDENCIES);
930     return 0;
931 }
932
933 int StartAppService(void *pHandle, int argc, char **argv)
934 {
935     /* Initializes the App */
936     return 1;
937 }
938
939 void RunAppService(void *pHandle)
940 {
941     Args *pArgs = (Args *)pHandle;
942     
943     /* Starts the app running */
944     statserv_start(pArgs->argc, pArgs->argv);
945 }
946
947 void StopAppService(void *pHandle)
948 {
949     /* Stops the app */
950     statserv_closedown();
951     statserv_reset();
952 }
953 /* WIN32 */
954 #else
955 /* UNIX */
956 int statserv_main(int argc, char **argv,
957                   bend_initresult *(*bend_init)(bend_initrequest *r),
958                   void (*bend_close)(void *handle))
959 {
960     int ret;
961     statserv_options_block *cb = statserv_getcontrol();
962     
963     cb->bend_init = bend_init;
964     cb->bend_close = bend_close;
965
966     statserv_setcontrol(cb);
967     ret = statserv_start (argc, argv);
968     statserv_closedown ();
969     statserv_reset();
970     return ret;
971 }
972 #endif