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