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