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