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