Renamed Z_SRW_searchRetrieve to Z_SRW_PDU
[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.96 2003-02-23 14:26:58 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",
661                 where, errno);
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> <listender-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