yaz_log_time_format sets the format of the timestamp. See man 3 strftime
[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.10 2004-11-02 11:37:21 heikki 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                 control_block.bend_stop = 0;
431             }
432             else /* parent */
433             {
434                 close(hand[1]);
435                 /* wait for child to take the call */
436                 for (;;)
437                 {
438                     char dummy[1];
439                     int res;
440                     
441                     if ((res = read(hand[0], dummy, 1)) < 0 &&
442                                      yaz_errno() != EINTR)
443                     {
444                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
445                         return;
446                     }
447                     else if (res >= 0)
448                         break;
449                 }
450                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
451                 close(hand[0]);
452                 return;
453             }
454         }
455         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
456                                    control_block.daemon_name)) < 0)
457         {
458             yaz_log(LOG_WARN|LOG_ERRNO, "cs_listen failed");
459             return;
460         }
461         else if (res == 1)
462             return;
463         yaz_log(LOG_DEBUG, "listen ok");
464         iochan_setevent(h, EVENT_OUTPUT);
465         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
466     }
467     /* in dynamic mode, only the child ever comes down here */
468     else if (event == EVENT_OUTPUT)
469     {
470         COMSTACK new_line = cs_accept(line);
471
472         if (!new_line)
473         {
474             yaz_log(LOG_FATAL, "Accept failed.");
475             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
476             return;
477         }
478         yaz_log(LOG_DEBUG, "accept ok");
479         if (control_block.dynamic)
480         {
481             IOCHAN pp;
482             /* close our half of the listener socket */
483             for (pp = pListener; pp; pp = iochan_getnext(pp))
484             {
485                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
486                 cs_close(l);
487                 iochan_destroy(pp);
488             }
489             /* release dad */
490             yaz_log(LOG_DEBUG, "Releasing parent");
491             close(hand[1]);
492         }
493         else
494         {
495             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
496             ++no_sessions;
497         }
498 #if YAZ_POSIX_THREADS
499         if (control_block.threads)
500         {
501             pthread_t child_thread;
502             pthread_create (&child_thread, 0, new_session, new_line);
503             pthread_detach (child_thread);
504         }
505         else
506             new_session(new_line);
507 #elif YAZ_GNU_THREADS
508         if (control_block.threads)
509         {
510             pth_attr_t attr;
511             pth_t child_thread;
512
513             attr = pth_attr_new ();
514             pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
515             pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
516             pth_attr_set (attr, PTH_ATTR_NAME, "session");
517             yaz_log (LOG_LOG, "pth_spawn begin");
518             child_thread = pth_spawn (attr, new_session, new_line);
519             yaz_log (LOG_LOG, "pth_spawn finish");
520             pth_attr_destroy (attr);
521         }
522         else
523             new_session(new_line);
524 #else
525         new_session(new_line);
526 #endif
527     }
528     else if (event == EVENT_TIMEOUT)
529     {
530         yaz_log(LOG_LOG, "Shutting down listener.");
531         iochan_destroy(h);
532     }
533     else
534     {
535         yaz_log(LOG_FATAL, "Bad event on listener.");
536         iochan_destroy(h);
537     }
538 }
539
540 static void *new_session (void *vp)
541 {
542     char *a;
543     association *newas;
544     IOCHAN new_chan;
545     COMSTACK new_line = (COMSTACK) vp;
546
547     unsigned cs_get_mask, cs_accept_mask, mask =  
548         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
549         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
550
551     if (mask)    
552     {
553         cs_accept_mask = mask;  /* accept didn't complete */
554         cs_get_mask = 0;
555     }
556     else
557     {
558         cs_accept_mask = 0;     /* accept completed.  */
559         cs_get_mask = mask = EVENT_INPUT;
560     }
561
562     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
563     {
564         yaz_log(LOG_FATAL, "Failed to create iochan");
565         return 0;
566     }
567     if (!(newas = create_association(new_chan, new_line)))
568     {
569         yaz_log(LOG_FATAL, "Failed to create new assoc.");
570         return 0;
571     }
572     newas->cs_accept_mask = cs_accept_mask;
573     newas->cs_get_mask = cs_get_mask;
574
575     iochan_setdata(new_chan, newas);
576     iochan_settimeout(new_chan, 60);
577 #if 1
578     a = cs_addrstr(new_line);
579 #else
580     a = 0;
581 #endif
582     yaz_log(LOG_LOG, "Starting session %d from %s",
583         no_sessions, a ? a : "[Unknown]");
584     if (max_sessions && no_sessions == max_sessions)
585         control_block.one_shot = 1;
586     if (control_block.threads)
587     {
588         event_loop(&new_chan);
589     }
590     else
591     {
592         new_chan->next = pListener;
593         pListener = new_chan;
594     }
595     return 0;
596 }
597
598 /* UNIX */
599 #endif
600
601 static void inetd_connection(int what)
602 {
603     COMSTACK line;
604     IOCHAN chan;
605     association *assoc;
606     char *addr;
607
608     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
609     {
610         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
611         {
612             if ((assoc = create_association(chan, line)))
613             {
614                 iochan_setdata(chan, assoc);
615                 iochan_settimeout(chan, 60);
616                 addr = cs_addrstr(line);
617                 yaz_log(LOG_LOG, "Inetd association from %s",
618                         addr ? addr : "[UNKNOWN]");
619                 assoc->cs_get_mask = EVENT_INPUT;
620             }
621             else
622             {
623                 yaz_log(LOG_FATAL, "Failed to create association structure");
624             }
625             chan->next = pListener;
626             pListener = chan;
627         }
628         else
629         {
630             yaz_log(LOG_FATAL, "Failed to create iochan");
631         }
632     }
633     else
634     {
635         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
636     }
637 }
638
639 /*
640  * Set up a listening endpoint, and give it to the event-handler.
641  */
642 static int add_listener(char *where, int what)
643 {
644     COMSTACK l;
645     void *ap;
646     IOCHAN lst = NULL;
647     const char *mode;
648
649     if (control_block.dynamic)
650         mode = "dynamic";
651     else if (control_block.threads)
652         mode = "threaded";
653     else
654         mode = "static";
655
656     yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
657             what == PROTO_SR ? "SR" : "Z3950", where);
658
659     l = cs_create_host(where, 2, &ap);
660     if (!l)
661     {
662         yaz_log(LOG_FATAL, "Failed to listen on %s", where);
663         return -1;
664     }
665     if (*control_block.cert_fname)
666         cs_set_ssl_certificate_file(l, control_block.cert_fname);
667
668     if (cs_bind(l, ap, CS_SERVER) < 0)
669     {
670         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
671         cs_close (l);
672         return -1;
673     }
674     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
675          EVENT_EXCEPT)))
676     {
677         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
678         cs_close (l);
679         return -1;
680     }
681     iochan_setdata(lst, l);
682
683     /* Ensure our listener chain is setup properly */
684     lst->next = pListener;
685     pListener = lst;
686     return 0; /* OK */
687 }
688
689 #ifndef WIN32
690 /* UNIX only (for windows we don't need to catch the signals) */
691 static void catchchld(int num)
692 {
693     while (waitpid(-1, 0, WNOHANG) > 0)
694         ;
695     signal(SIGCHLD, catchchld);
696 }
697 #endif
698
699 statserv_options_block *statserv_getcontrol(void)
700 {
701     static statserv_options_block cb;
702
703     memcpy(&cb, &control_block, sizeof(cb));
704     return &cb;
705 }
706
707 void statserv_setcontrol(statserv_options_block *block)
708 {
709     memcpy(&control_block, block, sizeof(*block));
710 }
711
712 static void statserv_reset(void)
713 {
714 }
715
716 int statserv_start(int argc, char **argv)
717 {
718     int ret = 0;
719
720 #ifdef WIN32
721     /* We need to initialize the thread list */
722     ThreadList_Initialize();
723 /* WIN32 */
724 #endif
725     
726 #ifdef WIN32
727     if ((me = strrchr (argv[0], '\\')))
728         me++;
729     else
730         me = argv[0];
731 #else
732     me = argv[0];
733 #endif
734     if (control_block.options_func(argc, argv))
735         return(1);
736     
737     if (control_block.bend_start)
738         (*control_block.bend_start)(&control_block);
739 #ifdef WIN32
740     yaz_log (LOG_LOG, "Starting server %s", me);
741     if (!pListener && *control_block.default_listen)
742         add_listener(control_block.default_listen,
743                      control_block.default_proto);
744     
745     if (!pListener)
746         return 1;
747 #else
748 /* UNIX */
749     if (control_block.inetd)
750         inetd_connection(control_block.default_proto);
751     else
752     {
753         if (control_block.background)
754         {
755             switch (fork())
756             {
757             case 0: 
758                 break;
759             case -1:
760                 return 1;
761             default: 
762             _exit(0);
763             }
764             
765             if (setsid() < 0)
766                 return 1;
767             
768             close(0);
769             close(1);
770             close(2);
771             open("/dev/null",O_RDWR);
772             dup(0); dup(0);
773         }
774         if (!pListener && *control_block.default_listen)
775             add_listener(control_block.default_listen,
776                          control_block.default_proto);
777         
778         if (!pListener)
779             return 1;
780
781         if (*control_block.pid_fname)
782         {
783             FILE *f = fopen(control_block.pid_fname, "w");
784             if (!f)
785             {
786                 yaz_log(LOG_FATAL|LOG_ERRNO, "Couldn't create %s", 
787                         control_block.pid_fname);
788                 exit(0);
789             }
790             fprintf(f, "%ld", (long) getpid());
791             fclose(f);
792         }
793
794         yaz_log (LOG_LOG, "Starting server %s pid=%d", me, getpid());
795 #if 0
796         sigset_t sigs_to_block;
797         
798         sigemptyset(&sigs_to_block);
799         sigaddset (&sigs_to_block, SIGTERM);
800         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
801         /* missing... */
802 #endif
803         if (control_block.dynamic)
804             signal(SIGCHLD, catchchld);
805     }
806     signal (SIGPIPE, SIG_IGN);
807     signal (SIGTERM, sigterm);
808     if (*control_block.setuid)
809     {
810         struct passwd *pw;
811         
812         if (!(pw = getpwnam(control_block.setuid)))
813         {
814             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
815             return(1);
816         }
817         if (setuid(pw->pw_uid) < 0)
818         {
819             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
820             exit(1);
821         }
822     }
823 /* UNIX */
824 #endif
825     if ((pListener == NULL) && *control_block.default_listen)
826         add_listener(control_block.default_listen,
827                      control_block.default_proto);
828         
829     if (pListener == NULL)
830         ret = 1;
831     else
832     {
833         yaz_log(LOG_LOG, "Entering event loop.");
834         ret = event_loop(&pListener);
835     }
836     return ret;
837 }
838
839 int check_options(int argc, char **argv)
840 {
841     int ret = 0, r;
842     char *arg;
843
844     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:DC:",
845                           argv, argc, &arg)) != -2)
846     {
847         switch (ret)
848         {
849         case 0:
850             if (add_listener(arg, control_block.default_proto))
851                 return 1;  /* failed to create listener */
852             break;
853         case '1':        
854             control_block.one_shot = 1;
855             control_block.dynamic = 0;
856             break;
857         case 'z':
858             control_block.default_proto = PROTO_Z3950;
859             break;
860         case 's':
861             fprintf (stderr, "%s: SR protocol no longer supported\n", me);
862             exit (1);
863             break;
864         case 'S':
865             control_block.dynamic = 0;
866             break;
867         case 'T':
868 #if YAZ_POSIX_THREADS
869             control_block.dynamic = 0;
870             control_block.threads = 1;
871 #elif YAZ_GNU_THREADS
872             control_block.dynamic = 0;
873             control_block.threads = 1;
874 #else
875             fprintf(stderr, "%s: Threaded mode not available.\n", me);
876             return 1;
877 #endif
878             break;
879         case 'l':
880             strcpy(control_block.logfile, arg ? arg : "");
881             yaz_log_init(control_block.loglevel, me, control_block.logfile);
882             break;
883         case 'v':
884             control_block.loglevel = yaz_log_mask_str(arg);
885             yaz_log_init(control_block.loglevel, me, control_block.logfile);
886             break;
887         case 'a':
888             strcpy(control_block.apdufile, arg ? arg : "");
889             break;
890         case 'u':
891             strcpy(control_block.setuid, arg ? arg : "");
892             break;
893         case 'c':
894             strcpy(control_block.configname, arg ? arg : "");
895             break;
896         case 'C':
897             strcpy(control_block.cert_fname, arg ? arg : "");
898             break;
899         case 'd':
900             strcpy(control_block.daemon_name, arg ? arg : "");
901             break;
902         case 't':
903             if (!arg || !(r = atoi(arg)))
904             {
905                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
906                 return(1);
907             }
908             control_block.idle_timeout = r;
909             break;
910         case  'k':
911             if (!arg || !(r = atoi(arg)))
912             {
913                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
914                 return(1);
915             }
916             control_block.maxrecordsize = r * 1024;
917             break;
918         case 'i':
919             control_block.inetd = 1;
920             break;
921         case 'w':
922             if (chdir(arg))
923             {
924                 perror(arg);            
925                 return 1;
926             }
927             break;
928         case 'A':
929             max_sessions = atoi(arg);
930             break;
931         case 'p':
932             if (strlen(arg) >= sizeof(control_block.pid_fname))
933             {
934                 yaz_log(LOG_FATAL, "pid fname too long");
935                 exit(1);
936             }
937             strcpy(control_block.pid_fname, arg);
938             break;
939         case 'D':
940             control_block.background = 1;
941             break;
942         default:
943             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
944                     " -l <logfile> -u <user> -c <config> -t <minutes>"
945                     " -k <kilobytes> -d <daemon> -p <pidfile> -C certfile"
946                         " -ziDST1 -w <directory> <listener-addr>... ]\n", me);
947             return 1;
948         }
949     }
950     return 0;
951 }
952
953 #ifdef WIN32
954 typedef struct _Args
955 {
956     char **argv;
957     int argc;
958 } Args; 
959
960 static Args ArgDetails;
961
962 /* name of the executable */
963 #define SZAPPNAME            "server"
964
965 /* list of service dependencies - "dep1\0dep2\0\0" */
966 #define SZDEPENDENCIES       ""
967
968 int statserv_main(int argc, char **argv,
969                   bend_initresult *(*bend_init)(bend_initrequest *r),
970                   void (*bend_close)(void *handle))
971 {
972     statserv_options_block *cb = statserv_getcontrol();
973     
974     cb->bend_init = bend_init;
975     cb->bend_close = bend_close;
976
977     statserv_setcontrol(cb);
978
979     /* Lets setup the Arg structure */
980     ArgDetails.argc = argc;
981     ArgDetails.argv = argv;
982     
983     /* Now setup the service with the service controller */
984     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
985                  cb->service_name, /* internal service name */
986                  cb->service_display_name, /* displayed name */
987                  SZDEPENDENCIES);
988     return 0;
989 }
990
991 int StartAppService(void *pHandle, int argc, char **argv)
992 {
993     /* Initializes the App */
994     return 1;
995 }
996
997 void RunAppService(void *pHandle)
998 {
999     Args *pArgs = (Args *)pHandle;
1000     
1001     /* Starts the app running */
1002     statserv_start(pArgs->argc, pArgs->argv);
1003 }
1004
1005 void StopAppService(void *pHandle)
1006 {
1007     /* Stops the app */
1008     statserv_closedown();
1009     statserv_reset();
1010 }
1011 /* WIN32 */
1012 #else
1013 /* UNIX */
1014 int statserv_main(int argc, char **argv,
1015                   bend_initresult *(*bend_init)(bend_initrequest *r),
1016                   void (*bend_close)(void *handle))
1017 {
1018     int ret;
1019     statserv_options_block *cb = statserv_getcontrol();
1020     
1021     cb->bend_init = bend_init;
1022     cb->bend_close = bend_close;
1023
1024     statserv_setcontrol(cb);
1025     ret = statserv_start (argc, argv);
1026     statserv_closedown ();
1027     statserv_reset();
1028     return ret;
1029 }
1030 #endif