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