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