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