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