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