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