Z_GDU definitions in zgdu.h. Renamed PROTO_SRW to PROTO_HTTP.
[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.95 2003-02-20 15:15:04 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     15,                         /* 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, 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 static int max_sessions = 0;
374
375 /* UNIX listener */
376 static void listener(IOCHAN h, int event)
377 {
378     COMSTACK line = (COMSTACK) iochan_getdata(h);
379     static int hand[2];
380     static int child = 0;
381     int res;
382
383     if (event == EVENT_INPUT)
384     {
385         if (control_block.dynamic && !child) 
386         {
387             int res;
388
389             ++no_sessions;
390             if (pipe(hand) < 0)
391             {
392                 yaz_log(LOG_FATAL|LOG_ERRNO, "pipe");
393                 iochan_destroy(h);
394                 return;
395             }
396             if ((res = fork()) < 0)
397             {
398                 yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
399                 iochan_destroy(h);
400                 return;
401             }
402             else if (res == 0) /* child */
403             {
404                 char nbuf[100];
405                 IOCHAN pp;
406
407                 close(hand[0]);
408                 child = 1;
409                 for (pp = pListener; pp; pp = iochan_getnext(pp))
410                 {
411                     if (pp != h)
412                     {
413                         COMSTACK l = (COMSTACK)iochan_getdata(pp);
414                         cs_close(l);
415                         iochan_destroy(pp);
416                     }
417                 }
418                 sprintf(nbuf, "%s(%d)", me, getpid());
419                 yaz_log_init(control_block.loglevel, nbuf, 0);
420                 /* ensure that bend_stop is not called when each child exits -
421                    only for the main process .. 
422                 */
423                 control_block.bend_stop = 0;
424             }
425             else /* parent */
426             {
427                 close(hand[1]);
428                 /* wait for child to take the call */
429                 for (;;)
430                 {
431                     char dummy[1];
432                     int res;
433                     
434                     if ((res = read(hand[0], dummy, 1)) < 0 &&
435                                      yaz_errno() != EINTR)
436                     {
437                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
438                         return;
439                     }
440                     else if (res >= 0)
441                         break;
442                 }
443                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
444                 close(hand[0]);
445                 return;
446             }
447         }
448         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
449                                    control_block.daemon_name)) < 0)
450         {
451             yaz_log(LOG_WARN|LOG_ERRNO, "cs_listen failed");
452             return;
453         }
454         else if (res == 1)
455             return;
456         yaz_log(LOG_DEBUG, "listen ok");
457         iochan_setevent(h, EVENT_OUTPUT);
458         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
459     }
460     /* in dynamic mode, only the child ever comes down here */
461     else if (event == EVENT_OUTPUT)
462     {
463         COMSTACK new_line = cs_accept(line);
464
465         if (!new_line)
466         {
467             yaz_log(LOG_FATAL, "Accept failed.");
468             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
469             return;
470         }
471         yaz_log(LOG_DEBUG, "accept ok");
472         if (control_block.dynamic)
473         {
474             IOCHAN pp;
475             /* close our half of the listener socket */
476             for (pp = pListener; pp; pp = iochan_getnext(pp))
477             {
478                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
479                 cs_close(l);
480                 iochan_destroy(pp);
481             }
482             /* release dad */
483             yaz_log(LOG_DEBUG, "Releasing parent");
484             close(hand[1]);
485         }
486         else
487         {
488             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
489             ++no_sessions;
490         }
491 #if YAZ_POSIX_THREADS
492         if (control_block.threads)
493         {
494             pthread_t child_thread;
495             pthread_create (&child_thread, 0, new_session, new_line);
496             pthread_detach (child_thread);
497         }
498         else
499             new_session(new_line);
500 #elif YAZ_GNU_THREADS
501         if (control_block.threads)
502         {
503             pth_attr_t attr;
504             pth_t child_thread;
505
506             attr = pth_attr_new ();
507             pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
508             pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
509             pth_attr_set (attr, PTH_ATTR_NAME, "session");
510             yaz_log (LOG_LOG, "pth_spawn begin");
511             child_thread = pth_spawn (attr, new_session, new_line);
512             yaz_log (LOG_LOG, "pth_spawn finish");
513             pth_attr_destroy (attr);
514         }
515         else
516             new_session(new_line);
517 #else
518         new_session(new_line);
519 #endif
520     }
521     else if (event == EVENT_TIMEOUT)
522     {
523         yaz_log(LOG_LOG, "Shutting down listener.");
524         iochan_destroy(h);
525     }
526     else
527     {
528         yaz_log(LOG_FATAL, "Bad event on listener.");
529         iochan_destroy(h);
530     }
531 }
532
533 static void *new_session (void *vp)
534 {
535     char *a;
536     association *newas;
537     IOCHAN new_chan;
538     COMSTACK new_line = (COMSTACK) vp;
539
540     unsigned cs_get_mask, cs_accept_mask, mask =  
541         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
542         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
543
544     if (mask)    
545     {
546         cs_accept_mask = mask;  /* accept didn't complete */
547         cs_get_mask = 0;
548     }
549     else
550     {
551         cs_accept_mask = 0;     /* accept completed.  */
552         cs_get_mask = mask = EVENT_INPUT;
553     }
554
555     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
556     {
557         yaz_log(LOG_FATAL, "Failed to create iochan");
558         return 0;
559     }
560     if (!(newas = create_association(new_chan, new_line)))
561     {
562         yaz_log(LOG_FATAL, "Failed to create new assoc.");
563         return 0;
564     }
565     newas->cs_accept_mask = cs_accept_mask;
566     newas->cs_get_mask = cs_get_mask;
567
568     iochan_setdata(new_chan, newas);
569     iochan_settimeout(new_chan, 60);
570 #if 1
571     a = cs_addrstr(new_line);
572 #else
573     a = 0;
574 #endif
575     yaz_log(LOG_LOG, "Starting session %d from %s",
576         no_sessions, a ? a : "[Unknown]");
577     if (max_sessions && no_sessions == max_sessions)
578         control_block.one_shot = 1;
579     if (control_block.threads)
580     {
581         event_loop(&new_chan);
582     }
583     else
584     {
585         new_chan->next = pListener;
586         pListener = new_chan;
587     }
588     return 0;
589 }
590
591 /* UNIX */
592 #endif
593
594 static void inetd_connection(int what)
595 {
596     COMSTACK line;
597     IOCHAN chan;
598     association *assoc;
599     char *addr;
600
601     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
602     {
603         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
604         {
605             if ((assoc = create_association(chan, line)))
606             {
607                 iochan_setdata(chan, assoc);
608                 iochan_settimeout(chan, 60);
609                 addr = cs_addrstr(line);
610                 yaz_log(LOG_LOG, "Inetd association from %s",
611                         addr ? addr : "[UNKNOWN]");
612                 assoc->cs_get_mask = EVENT_INPUT;
613             }
614             else
615             {
616                 yaz_log(LOG_FATAL, "Failed to create association structure");
617             }
618             chan->next = pListener;
619             pListener = chan;
620         }
621         else
622         {
623             yaz_log(LOG_FATAL, "Failed to create iochan");
624         }
625     }
626     else
627     {
628         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
629     }
630 }
631
632 /*
633  * Set up a listening endpoint, and give it to the event-handler.
634  */
635 static int add_listener(char *where, int what)
636 {
637     COMSTACK l;
638     void *ap;
639     IOCHAN lst = NULL;
640     const char *mode;
641
642     if (control_block.dynamic)
643         mode = "dynamic";
644     else if (control_block.threads)
645         mode = "threaded";
646     else
647         mode = "static";
648
649     yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
650             what == PROTO_SR ? "SR" : "Z3950", where);
651
652     l = cs_create_host(where, 0, &ap);
653     if (!l)
654     {
655         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where);
656         return -1;
657     }
658     if (cs_bind(l, ap, CS_SERVER) < 0)
659     {
660         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
661         cs_close (l);
662         return -1;
663     }
664     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
665          EVENT_EXCEPT)))
666     {
667         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
668         cs_close (l);
669         return -1;
670     }
671     iochan_setdata(lst, l);
672
673     /* Ensure our listener chain is setup properly */
674     lst->next = pListener;
675     pListener = lst;
676     return 0; /* OK */
677 }
678
679 #ifndef WIN32
680 /* UNIX only (for windows we don't need to catch the signals) */
681 static void catchchld(int num)
682 {
683     while (waitpid(-1, 0, WNOHANG) > 0)
684         ;
685     signal(SIGCHLD, catchchld);
686 }
687 #endif
688
689 statserv_options_block *statserv_getcontrol(void)
690 {
691     static statserv_options_block cb;
692
693     memcpy(&cb, &control_block, sizeof(cb));
694     return &cb;
695 }
696
697 void statserv_setcontrol(statserv_options_block *block)
698 {
699     memcpy(&control_block, block, sizeof(*block));
700 }
701
702 static void statserv_reset(void)
703 {
704 }
705
706 int statserv_start(int argc, char **argv)
707 {
708     int ret;
709
710 #ifdef WIN32
711     /* We need to initialize the thread list */
712     ThreadList_Initialize();
713 /* WIN32 */
714 #endif
715     
716 #ifdef WIN32
717     if ((me = strrchr (argv[0], '\\')))
718         me++;
719     else
720         me = argv[0];
721 #else
722     me = argv[0];
723 #endif
724     if (control_block.options_func(argc, argv))
725         return(1);
726     
727     if (control_block.bend_start)
728         (*control_block.bend_start)(&control_block);
729 #ifdef WIN32
730     yaz_log (LOG_LOG, "Starting server %s", me);
731 #else
732 /* UNIX */
733     if (control_block.inetd)
734         inetd_connection(control_block.default_proto);
735     else
736     {
737         yaz_log (LOG_LOG, "Starting server %s pid=%d", me, getpid());
738 #if 0
739         sigset_t sigs_to_block;
740         
741         sigemptyset(&sigs_to_block);
742         sigaddset (&sigs_to_block, SIGTERM);
743         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
744         /* missing... */
745 #endif
746         if (control_block.dynamic)
747             signal(SIGCHLD, catchchld);
748     }
749     signal (SIGPIPE, SIG_IGN);
750     signal (SIGTERM, sigterm);
751     if (*control_block.setuid)
752     {
753         struct passwd *pw;
754         
755         if (!(pw = getpwnam(control_block.setuid)))
756         {
757             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
758             return(1);
759         }
760         if (setuid(pw->pw_uid) < 0)
761         {
762             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
763             exit(1);
764         }
765     }
766 /* UNIX */
767 #endif
768     
769     
770     if ((pListener == NULL) && *control_block.default_listen)
771         add_listener(control_block.default_listen,
772                      control_block.default_proto);
773         
774     if (pListener == NULL)
775         ret = 1;
776     else
777     {
778         yaz_log(LOG_LOG, "Entering event loop.");
779         ret = event_loop(&pListener);
780     }
781     return ret;
782 }
783
784 int check_options(int argc, char **argv)
785 {
786     int ret = 0, r;
787     char *arg;
788
789     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:D:", argv, argc, &arg)) != -2)
790     {
791         switch (ret)
792         {
793         case 0:
794             if (add_listener(arg, control_block.default_proto))
795                 return 1;  /* failed to create listener */
796             break;
797         case '1':        
798             control_block.one_shot = 1;
799             control_block.dynamic = 0;
800             break;
801         case 'z':
802             control_block.default_proto = PROTO_Z3950;
803             break;
804         case 's':
805             fprintf (stderr, "%s: SR protocol no longer supported\n", me);
806             exit (1);
807             break;
808         case 'S':
809             control_block.dynamic = 0;
810             break;
811         case 'T':
812 #if YAZ_POSIX_THREADS
813             control_block.dynamic = 0;
814             control_block.threads = 1;
815 #elif YAZ_GNU_THREADS
816             control_block.dynamic = 0;
817             control_block.threads = 1;
818 #else
819             fprintf(stderr, "%s: Threaded mode not available.\n", me);
820             return 1;
821 #endif
822             break;
823         case 'l':
824             strcpy(control_block.logfile, arg ? arg : "");
825             yaz_log_init(control_block.loglevel, me, control_block.logfile);
826             break;
827         case 'v':
828             control_block.loglevel = yaz_log_mask_str(arg);
829             yaz_log_init(control_block.loglevel, me, control_block.logfile);
830             break;
831         case 'a':
832             strcpy(control_block.apdufile, arg ? arg : "");
833             break;
834         case 'u':
835             strcpy(control_block.setuid, arg ? arg : "");
836             break;
837         case 'c':
838             strcpy(control_block.configname, arg ? arg : "");
839             break;
840         case 'd':
841             strcpy(control_block.daemon_name, arg ? arg : "");
842             break;
843         case 't':
844             if (!arg || !(r = atoi(arg)))
845             {
846                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
847                 return(1);
848             }
849             control_block.idle_timeout = r;
850             break;
851         case  'k':
852             if (!arg || !(r = atoi(arg)))
853             {
854                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
855                 return(1);
856             }
857             control_block.maxrecordsize = r * 1024;
858             break;
859         case 'i':
860             control_block.inetd = 1;
861             break;
862         case 'w':
863             if (chdir(arg))
864             {
865                 perror(arg);            
866                 return 1;
867             }
868             break;
869         case 'D':
870             max_sessions = atoi(arg);
871             break;
872         default:
873             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
874                     " -l <logfile> -u <user> -c <config> -t <minutes>"
875                     " -k <kilobytes> -d <daemon>"
876                         " -ziST1 -w <directory> <listender-addr>... ]\n", me);
877             return 1;
878         }
879     }
880     return 0;
881 }
882
883 #ifdef WIN32
884 typedef struct _Args
885 {
886     char **argv;
887     int argc;
888 } Args; 
889
890 static Args ArgDetails;
891
892 /* name of the executable */
893 #define SZAPPNAME            "server"
894
895 /* list of service dependencies - "dep1\0dep2\0\0" */
896 #define SZDEPENDENCIES       ""
897
898 int statserv_main(int argc, char **argv,
899                   bend_initresult *(*bend_init)(bend_initrequest *r),
900                   void (*bend_close)(void *handle))
901 {
902     statserv_options_block *cb = statserv_getcontrol();
903     
904     cb->bend_init = bend_init;
905     cb->bend_close = bend_close;
906
907     statserv_setcontrol(cb);
908
909     /* Lets setup the Arg structure */
910     ArgDetails.argc = argc;
911     ArgDetails.argv = argv;
912     
913     /* Now setup the service with the service controller */
914     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
915                  cb->service_name, /* internal service name */
916                  cb->service_display_name, /* displayed name */
917                  SZDEPENDENCIES);
918     return 0;
919 }
920
921 int StartAppService(void *pHandle, int argc, char **argv)
922 {
923     /* Initializes the App */
924     return 1;
925 }
926
927 void RunAppService(void *pHandle)
928 {
929     Args *pArgs = (Args *)pHandle;
930     
931     /* Starts the app running */
932     statserv_start(pArgs->argc, pArgs->argv);
933 }
934
935 void StopAppService(void *pHandle)
936 {
937     /* Stops the app */
938     statserv_closedown();
939     statserv_reset();
940 }
941 /* WIN32 */
942 #else
943 /* UNIX */
944 int statserv_main(int argc, char **argv,
945                   bend_initresult *(*bend_init)(bend_initrequest *r),
946                   void (*bend_close)(void *handle))
947 {
948     int ret;
949     statserv_options_block *cb = statserv_getcontrol();
950     
951     cb->bend_init = bend_init;
952     cb->bend_close = bend_close;
953
954     statserv_setcontrol(cb);
955     ret = statserv_start (argc, argv);
956     statserv_closedown ();
957     statserv_reset();
958     return ret;
959 }
960 #endif