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