SSL comstack support. Separate POSIX thread support library.
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (c) 1995-2000, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * NT server based on threads by
7  *   Chas Woodfield, Fretwell Downing Informatics.
8  *
9  * $Log: statserv.c,v $
10  * Revision 1.67  2000-11-23 10:58:32  adam
11  * SSL comstack support. Separate POSIX thread support library.
12  *
13  * Revision 1.66  2000/10/06 12:00:28  adam
14  * Fixed Handle leak for WIN32.
15  *
16  * Revision 1.65  2000/09/04 08:58:15  adam
17  * Added prefix yaz_ for most logging utility functions.
18  *
19  * Revision 1.64  2000/04/05 07:39:55  adam
20  * Added shared library support (libtool).
21  *
22  * Revision 1.63  2000/03/20 19:06:25  adam
23  * Added Segment request for fronend server. Work on admin for client.
24  *
25  * Revision 1.62  2000/03/17 12:47:02  adam
26  * Minor changes to admin client.
27  *
28  * Revision 1.61  2000/03/15 12:59:49  adam
29  * Added handle member to statserv_control.
30  *
31  * Revision 1.60  2000/03/14 09:06:11  adam
32  * Added POSIX threads support for frontend server.
33  *
34  * Revision 1.59  1999/11/30 13:47:12  adam
35  * Improved installation. Moved header files to include/yaz.
36  *
37  * Revision 1.58  1999/08/27 09:40:32  adam
38  * Renamed logf function to yaz_log. Removed VC++ project files.
39  *
40  * Revision 1.57  1999/07/06 12:17:15  adam
41  * Added option -1 that runs server once (for profiling purposes).
42  *
43  * Revision 1.56  1999/06/10 11:45:30  adam
44  * Added bend_start, bend_stop handlers and removed pre_init.
45  * Handlers bend_start/bend_stop are called when service/daemon is
46  * started/stopped.
47  *
48  * Revision 1.55  1999/06/10 09:18:54  adam
49  * Modified so that pre_init is called when service/server is started.
50  *
51  * Revision 1.54  1999/04/16 14:45:55  adam
52  * Added interface for tcpd wrapper for access control.
53  *
54  * Revision 1.53  1999/02/02 13:57:39  adam
55  * Uses preprocessor define WIN32 instead of WINDOWS to build code
56  * for Microsoft WIN32.
57  *
58  * Revision 1.52  1998/08/21 14:13:34  adam
59  * Added GNU Configure script to build Makefiles.
60  *
61  * Revision 1.51  1998/07/07 15:51:03  adam
62  * Changed server so that it stops if bind fails - "address already in
63  * use" typically causes this.
64  *
65  * Revision 1.50  1998/06/22 11:32:39  adam
66  * Added 'conditional cs_listen' feature.
67  *
68  * Revision 1.49  1998/02/27 14:04:55  adam
69  * Fixed bug in statserv_remove.
70  *
71  * Revision 1.48  1998/02/11 11:53:36  adam
72  * Changed code so that it compiles as C++.
73  *
74  * Revision 1.47  1998/02/10 10:28:57  adam
75  * Added app_name, service_dependencies, service_display_name and
76  * options_func. options_func allows us to specify a different function
77  * to interogate the command line arguments. The other members allow us
78  * to pass the full service details accross to the service manager (CW).
79  *
80  *
81  * Revision 1.46  1998/01/30 15:24:57  adam
82  * Fixed bug in inetd code. The server listened on tcp:@:9999 even
83  * though it was started in inetd mode.
84  *
85  * Revision 1.45  1998/01/29 13:30:23  adam
86  * Better event handle system for NT/Unix.
87  *
88  * Revision 1.44  1997/11/07 13:31:52  adam
89  * Added NT Service name part of statserv_options_block. Moved NT
90  * service utility to server library.
91  *
92  * Revision 1.43  1997/10/31 12:20:09  adam
93  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
94  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
95  * Bug fix: missing fclose in data1_read_espec1.
96  *
97  * Revision 1.42  1997/10/27 14:03:02  adam
98  * Added new member to statserver_options_block, pre_init, which
99  * specifies a callback to be invoked after command line parsing and
100  * before the server listens for the first time.
101  *
102  * Revision 1.41  1997/09/29 07:19:32  adam
103  * Server library uses nmem_init/nmem_exit. The log prefix no longer
104  * includes leading path on NT.
105  *
106  * Revision 1.40  1997/09/17 12:10:41  adam
107  * YAZ version 1.4.
108  *
109  * Revision 1.39  1997/09/09 10:10:19  adam
110  * Another MSV5.0 port. Changed projects to include proper
111  * library/include paths.
112  * Server starts server in test-mode when no options are given.
113  *
114  * Revision 1.38  1997/09/04 14:19:14  adam
115  * Added credits.
116  *
117  * Revision 1.37  1997/09/01 08:53:01  adam
118  * New windows NT/95 port using MSV5.0. The test server 'ztest' was
119  * moved a separate directory. MSV5.0 project server.dsp created.
120  * As an option, the server can now operate as an NT service.
121  *
122  * Revision 1.36  1996/07/06 19:58:36  quinn
123  * System headerfiles gathered in yconfig
124  *
125  * Revision 1.35  1996/05/29  10:03:28  quinn
126  * Options work
127  *
128  * Revision 1.34  1996/02/21  13:12:07  quinn
129  * *** empty log message ***
130  *
131  * Revision 1.33  1996/02/10  12:23:49  quinn
132  * Enable inetd operations fro TCP/IP stack
133  *
134  * Revision 1.32  1996/01/19  15:41:52  quinn
135  * *** empty log message ***
136  *
137  * Revision 1.31  1995/11/17  11:09:39  adam
138  * Added new option '-c' to specify configuration name in control block.
139  *
140  * Revision 1.30  1995/11/01  13:54:59  quinn
141  * Minor adjustments
142  *
143  * Revision 1.29  1995/10/30  12:41:29  quinn
144  * Added hostname lookup for server.
145  *
146  * Revision 1.28  1995/09/29  17:12:30  quinn
147  * Smallish
148  *
149  * Revision 1.27  1995/09/27  15:03:02  quinn
150  * Modified function heads & prototypes.
151  *
152  * Revision 1.26  1995/08/29  14:44:51  quinn
153  * Reset timeouts.
154  *
155  * Revision 1.25  1995/08/29  11:18:02  quinn
156  * Added code to receive close
157  *
158  * Revision 1.24  1995/06/16  10:31:39  quinn
159  * Added session timeout.
160  *
161  * Revision 1.23  1995/06/15  12:30:48  quinn
162  * Setuid-facility.
163  *
164  * Revision 1.22  1995/06/15  07:45:17  quinn
165  * Moving to v3.
166  *
167  * Revision 1.21  1995/06/06  08:15:40  quinn
168  * Cosmetic.
169  *
170  * Revision 1.20  1995/05/29  08:12:09  quinn
171  * Moved oid to util
172  *
173  * Revision 1.19  1995/05/16  09:37:27  quinn
174  * Fixed bug
175  *
176  * Revision 1.18  1995/05/16  08:51:09  quinn
177  * License, documentation, and memory fixes
178  *
179  * Revision 1.17  1995/05/15  11:56:42  quinn
180  * Asynchronous facilities. Restructuring of seshigh code.
181  *
182  * Revision 1.16  1995/04/10  10:23:40  quinn
183  * Some work to add scan and other things.
184  *
185  * Revision 1.15  1995/03/31  10:16:51  quinn
186  * Fixed logging.
187  *
188  * Revision 1.14  1995/03/31  09:18:58  quinn
189  * Added logging.
190  *
191  * Revision 1.13  1995/03/30  16:08:39  quinn
192  * Little mods.
193  *
194  * Revision 1.12  1995/03/30  13:29:02  quinn
195  * Smallish
196  *
197  * Revision 1.11  1995/03/30  12:18:17  quinn
198  * Fixed bug.
199  *
200  * Revision 1.10  1995/03/29  15:40:16  quinn
201  * Ongoing work. Statserv is now dynamic by default
202  *
203  * Revision 1.9  1995/03/27  08:34:30  quinn
204  * Added dynamic server functionality.
205  * Released bindings to session.c (is now redundant)
206  *
207  * Revision 1.8  1995/03/20  09:46:26  quinn
208  * Added osi support.
209  *
210  * Revision 1.7  1995/03/16  13:29:04  quinn
211  * Partitioned server.
212  *
213  * Revision 1.6  1995/03/15  15:18:52  quinn
214  * Little changes to better support nonblocking I/O
215  * Added backend.h
216  *
217  * Revision 1.5  1995/03/15  08:37:45  quinn
218  * Now we're pretty much set for nonblocking I/O.
219  *
220  * Revision 1.4  1995/03/14  16:59:48  quinn
221  * Bug-fixes
222  *
223  * Revision 1.3  1995/03/14  11:30:15  quinn
224  * Works better now.
225  *
226  * Revision 1.2  1995/03/14  10:28:03  quinn
227  * More work on demo server.
228  *
229  * Revision 1.1  1995/03/10  18:22:45  quinn
230  * The rudiments of an asynchronous server.
231  *
232  */
233
234 #include <stdio.h>
235 #include <string.h>
236 #ifdef WIN32
237 #include <process.h>
238 #include <winsock.h>
239 #include <direct.h>
240 #include "service.h"
241 #else
242 #if HAVE_PTHREAD_H
243 #include <pthread.h>
244 #endif
245 #include <unistd.h>
246 #include <pwd.h>
247 #endif
248 #include <fcntl.h>
249 #include <signal.h>
250 #include <errno.h>
251
252 #include <yaz/comstack.h>
253 #include <yaz/tcpip.h>
254 #include <yaz/options.h>
255 #ifdef USE_XTIMOSI
256 #include <yaz/xmosi.h>
257 #endif
258 #include <yaz/log.h>
259 #include "eventl.h"
260 #include "session.h"
261 #include <yaz/statserv.h>
262
263 static IOCHAN pListener = NULL;
264
265 static char *me = "statserver";
266 /*
267  * default behavior.
268  */
269 int check_options(int argc, char **argv);
270 statserv_options_block control_block = {
271     1,                          /* dynamic mode */
272     0,                          /* threaded mode */
273     0,                          /* one shot (single session) */
274     LOG_DEFAULT_LEVEL,          /* log level */
275     "",                         /* no PDUs */
276     "",                         /* diagnostic output to stderr */
277     "tcp:@:9999",               /* default listener port */
278     PROTO_Z3950,                /* default application protocol */
279     60,                         /* idle timeout (minutes) */
280     1024*1024,                  /* maximum PDU size (approx.) to allow */
281     "default-config",           /* configuration name to pass to backend */
282     "",                         /* set user id */
283     0,                          /* bend_start handler */
284     0,                          /* bend_stop handler */
285     check_options,              /* Default routine, for checking the run-time arguments */
286     check_ip_tcpd,
287     "",
288     0,                          /* default value for inet deamon */
289     0,                          /* handle (for service, etc) */
290     0,                          /* bend_init handle */
291     0                           /* bend_close handle */
292 #ifdef WIN32
293     ,"Z39.50 Server",           /* NT Service Name */
294     "Server",                   /* NT application Name */
295     "",                         /* NT Service Dependencies */
296     "Z39.50 Server"             /* NT Service Display Name */
297 #endif /* WIN32 */
298 };
299
300 /*
301  * handle incoming connect requests.
302  * The dynamic mode is a bit tricky mostly because we want to avoid
303  * doing all of the listening and accepting in the parent - it's
304  * safer that way.
305  */
306 #ifdef WIN32
307
308 typedef struct _ThreadList ThreadList;
309
310 struct _ThreadList
311 {
312     HANDLE hThread;
313     IOCHAN pIOChannel;
314     ThreadList *pNext;
315 };
316
317 static ThreadList *pFirstThread;
318 static CRITICAL_SECTION Thread_CritSect;
319 static BOOL bInitialized = FALSE;
320
321 static void ThreadList_Initialize()
322 {
323     /* Initialize the critical Sections */
324     InitializeCriticalSection(&Thread_CritSect);
325
326      /* Set the first thraed */
327     pFirstThread = NULL;
328
329     /* we have been initialized */
330     bInitialized = TRUE;
331 }
332
333 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
334 {
335     /* Only one thread can go through this section at a time */
336     EnterCriticalSection(&Thread_CritSect);
337
338     {
339         /* Lets create our new object */
340         ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
341         pNewThread->hThread = hThread;
342         pNewThread->pIOChannel = pIOChannel;
343         pNewThread->pNext = pFirstThread;
344         pFirstThread = pNewThread;
345
346         /* Lets let somebody else create a new object now */
347         LeaveCriticalSection(&Thread_CritSect);
348     }
349 }
350
351 void statserv_remove(IOCHAN pIOChannel)
352 {
353     /* Only one thread can go through this section at a time */
354     EnterCriticalSection(&Thread_CritSect);
355
356     {
357         ThreadList *pCurrentThread = pFirstThread;
358         ThreadList *pNextThread;
359         ThreadList *pPrevThread =NULL;
360
361         /* Step through alll the threads */
362         for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
363         {
364             /* We only need to compare on the IO Channel */
365             if (pCurrentThread->pIOChannel == pIOChannel)
366             {
367                 /* We have found the thread we want to delete */
368                 /* First of all reset the next pointers */
369                 if (pPrevThread == NULL)
370                     pFirstThread = pCurrentThread->pNext;
371                 else
372                     pPrevThread->pNext = pCurrentThread->pNext;
373
374                 /* All we need todo now is delete the memory */
375                 free(pCurrentThread);
376
377                 /* No need to look at any more threads */
378                 pNextThread = NULL;
379             }
380             else
381             {
382                 /* We need to look at another thread */
383                 pNextThread = pCurrentThread->pNext;
384                 pPrevThread = pCurrentThread;
385             }
386         }
387
388         /* Lets let somebody else remove an object now */
389         LeaveCriticalSection(&Thread_CritSect);
390     }
391 }
392
393 /* WIN32 statserv_closedown */
394 void statserv_closedown()
395 {
396     /* Shouldn't do anything if we are not initialized */
397     if (bInitialized)
398     {
399         int iHandles = 0;
400         HANDLE *pThreadHandles = NULL;
401
402         /* We need to stop threads adding and removing while we */
403         /* start the closedown process */
404         EnterCriticalSection(&Thread_CritSect);
405
406         {
407             /* We have exclusive access to the thread stuff now */
408             /* Y didn't i use a semaphore - Oh well never mind */
409             ThreadList *pCurrentThread = pFirstThread;
410
411             /* Before we do anything else, we need to shutdown the listener */
412             if (pListener != NULL)
413                 iochan_destroy(pListener);
414
415             for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
416             {
417                 /* Just destroy the IOCHAN, that should do the trick */
418                 iochan_destroy(pCurrentThread->pIOChannel);
419
420                 /* Keep a running count of our handles */
421                 iHandles++;
422             }
423
424             if (iHandles > 0)
425             {
426                 HANDLE *pCurrentHandle ;
427
428                 /* Allocate the thread handle array */
429                 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
430                 pCurrentHandle = pThreadHandles; 
431
432                 for (pCurrentThread = pFirstThread;
433                      pCurrentThread != NULL;
434                      pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
435                 {
436                     /* Just the handle */
437                     *pCurrentHandle = pCurrentThread->hThread;
438                 }
439             }
440
441             /* We can now leave the critical section */
442             LeaveCriticalSection(&Thread_CritSect);
443         }
444
445         /* Now we can really do something */
446         if (iHandles > 0)
447         {
448             /* This will now wait, until all the threads close */
449             WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
450
451             /* Free the memory we allocated for the handle array */
452             free(pThreadHandles);
453         }
454
455         if (control_block.bend_stop)
456             (*control_block.bend_stop)(&control_block);
457         /* No longer require the critical section, since all threads are dead */
458         DeleteCriticalSection(&Thread_CritSect);
459     }
460 }
461
462 void event_loop_thread (IOCHAN iochan)
463 {
464     event_loop (&iochan);
465 }
466
467 /* WIN32 listener */
468 static void listener(IOCHAN h, int event)   
469 {
470     COMSTACK line = (COMSTACK) iochan_getdata(h);
471     association *newas;
472     int res;
473     HANDLE newHandle;
474
475     if (event == EVENT_INPUT)
476     {
477         if ((res = cs_listen(line, 0, 0)) < 0)
478         {
479             yaz_log(LOG_FATAL, "cs_listen failed");
480             return;
481         }
482         else if (res == 1)
483             return;
484         yaz_log(LOG_DEBUG, "listen ok");
485         iochan_setevent(h, EVENT_OUTPUT);
486         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
487     }
488     else if (event == EVENT_OUTPUT)
489     {
490         COMSTACK new_line = cs_accept(line);
491         IOCHAN new_chan;
492         char *a = NULL;
493
494         if (!new_line)
495         {
496             yaz_log(LOG_FATAL, "Accept failed.");
497             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
498             return;
499         }
500         yaz_log(LOG_DEBUG, "Accept ok");
501
502         if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
503                                        EVENT_INPUT)))
504         {
505             yaz_log(LOG_FATAL, "Failed to create iochan");
506             iochan_destroy(h);
507             return;
508         }
509
510         yaz_log(LOG_DEBUG, "Creating association");
511         if (!(newas = create_association(new_chan, new_line)))
512         {
513             yaz_log(LOG_FATAL, "Failed to create new assoc.");
514             iochan_destroy(h);
515             return;
516         }
517         newas->cs_get_mask = EVENT_INPUT;
518         newas->cs_put_mask = 0;
519         newas->cs_accept_mask = 0;
520
521         yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
522         iochan_setdata(new_chan, newas);
523         iochan_settimeout(new_chan, control_block.idle_timeout * 60);
524
525         /* Now what we need todo is create a new thread with this iochan as
526            the parameter */
527         newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
528         if (newHandle == (HANDLE) -1)
529         {
530             
531             yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread.");
532             iochan_destroy(h);
533             return;
534         }
535         /* We successfully created the thread, so add it to the list */
536         statserv_add(newHandle, new_chan);
537
538         yaz_log(LOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
539         iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
540     }
541     else
542     {
543         yaz_log(LOG_FATAL, "Bad event on listener.");
544         iochan_destroy(h);
545         return;
546     }
547 }
548
549 #else /* ! WIN32 */
550
551 /* To save having an #ifdef in event_loop we need to define this empty function */
552 void statserv_remove(IOCHAN pIOChannel)
553 {
554 }
555
556 void statserv_closedown()
557 {
558     IOCHAN p;
559
560     if (control_block.bend_stop)
561         (*control_block.bend_stop)(&control_block);
562
563     for (p = pListener; p; p = p->next)
564         iochan_destroy(p);
565 }
566
567 void sigterm(int sig)
568 {
569     statserv_closedown();
570     exit (0);
571 }
572
573 static void *new_session (void *vp);
574
575 /* UNIX listener */
576 static void listener(IOCHAN h, int event)
577 {
578     COMSTACK line = (COMSTACK) iochan_getdata(h);
579     static int hand[2];
580     static int child = 0;
581     int res;
582
583     if (event == EVENT_INPUT)
584     {
585         if (control_block.dynamic && !child) 
586         {
587             int res;
588
589             if (pipe(hand) < 0)
590             {
591                 yaz_log(LOG_FATAL|LOG_ERRNO, "pipe");
592                 iochan_destroy(h);
593                 return;
594             }
595             if ((res = fork()) < 0)
596             {
597                 yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
598                 iochan_destroy(h);
599                 return;
600             }
601             else if (res == 0) /* child */
602             {
603                 char nbuf[100];
604                 IOCHAN pp;
605
606                 close(hand[0]);
607                 child = 1;
608                 for (pp = pListener; pp; pp = iochan_getnext(pp))
609                 {
610                     if (pp != h)
611                     {
612                         COMSTACK l = (COMSTACK)iochan_getdata(pp);
613                         cs_close(l);
614                         iochan_destroy(pp);
615                     }
616                 }
617                 sprintf(nbuf, "%s(%d)", me, getpid());
618                 yaz_log_init(control_block.loglevel, nbuf, 0);
619             }
620             else /* parent */
621             {
622                 close(hand[1]);
623                 /* wait for child to take the call */
624                 for (;;)
625                 {
626                     char dummy[1];
627                     int res;
628                     
629                     if ((res = read(hand[0], dummy, 1)) < 0 && errno != EINTR)
630                     {
631                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
632                         return;
633                     }
634                     else if (res >= 0)
635                         break;
636                 }
637                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
638                 close(hand[0]);
639                 return;
640             }
641         }
642         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
643                                    control_block.daemon_name)) < 0)
644         {
645             yaz_log(LOG_WARN, "cs_listen failed");
646             return;
647         }
648         else if (res == 1)
649             return;
650         yaz_log(LOG_DEBUG, "listen ok");
651         iochan_setevent(h, EVENT_OUTPUT);
652         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
653     }
654     /* in dynamic mode, only the child ever comes down here */
655     else if (event == EVENT_OUTPUT)
656     {
657         COMSTACK new_line = cs_accept(line);
658
659         if (!new_line)
660         {
661             yaz_log(LOG_FATAL, "Accept failed.");
662             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
663             return;
664         }
665         yaz_log(LOG_DEBUG, "accept ok");
666         if (control_block.dynamic)
667         {
668             IOCHAN pp;
669             /* close our half of the listener socket */
670             for (pp = pListener; pp; pp = iochan_getnext(pp))
671             {
672                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
673                 cs_close(l);
674                 iochan_destroy(pp);
675             }
676             /* release dad */
677             yaz_log(LOG_DEBUG, "Releasing parent");
678             close(hand[1]);
679         }
680         else
681             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
682
683 #if HAVE_PTHREAD_H
684         if (control_block.threads)
685         {
686             pthread_t child_thread;
687             pthread_create (&child_thread, 0, new_session, new_line);
688             pthread_detach (child_thread);
689         }
690         else
691             new_session(new_line);
692 #else
693         new_session(new_line);
694 #endif
695     }
696     else
697     {
698         yaz_log(LOG_FATAL, "Bad event on listener.");
699         iochan_destroy(h);
700         return;
701     }
702 }
703
704 static void *new_session (void *vp)
705 {
706     char *a;
707     association *newas;
708     IOCHAN new_chan;
709     COMSTACK new_line = (COMSTACK) vp;
710
711     unsigned cs_get_mask, cs_accept_mask, mask =  
712         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
713         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
714
715     if (mask)    
716     {
717         yaz_log (LOG_LOG, "new_session , accept incomplete");
718         cs_accept_mask = mask;  /* accept didn't complete */
719         cs_get_mask = 0;
720     }
721     else
722     {
723         yaz_log (LOG_LOG, "new_session , accept complete");
724         cs_accept_mask = 0;     /* accept completed.  */
725         cs_get_mask = mask = EVENT_INPUT;
726     }
727
728     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
729     {
730         yaz_log(LOG_FATAL, "Failed to create iochan");
731         return 0;
732     }
733     if (!(newas = create_association(new_chan, new_line)))
734     {
735         yaz_log(LOG_FATAL, "Failed to create new assoc.");
736         return 0;
737     }
738     newas->cs_accept_mask = cs_accept_mask;
739     newas->cs_get_mask = cs_get_mask;
740
741     iochan_setdata(new_chan, newas);
742     iochan_settimeout(new_chan, control_block.idle_timeout * 60);
743     a = cs_addrstr(new_line);
744     yaz_log(LOG_LOG, "Accepted connection from %s", a ? a : "[Unknown]");
745     
746     if (control_block.threads)
747     {
748         event_loop(&new_chan);
749     }
750     else
751     {
752         new_chan->next = pListener;
753         pListener = new_chan;
754     }
755     return 0;
756 }
757
758 #endif /* WIN32 */
759
760 static void inetd_connection(int what)
761 {
762     COMSTACK line;
763     IOCHAN chan;
764     association *assoc;
765     char *addr;
766
767     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
768     {
769         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
770         {
771             if ((assoc = create_association(chan, line)))
772             {
773                 iochan_setdata(chan, assoc);
774                 iochan_settimeout(chan, control_block.idle_timeout * 60);
775                 addr = cs_addrstr(line);
776                 yaz_log(LOG_LOG, "Inetd association from %s", addr ? addr : "[UNKNOWN]");
777             }
778             else
779             {
780                 yaz_log(LOG_FATAL, "Failed to create association structure");
781             }
782             chan->next = pListener;
783             pListener = chan;
784         }
785         else
786         {
787             yaz_log(LOG_FATAL, "Failed to create iochan");
788         }
789     }
790     else
791     {
792         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
793     }
794 }
795
796 /*
797  * Set up a listening endpoint, and give it to the event-handler.
798  */
799 static void add_listener(char *where, int what)
800 {
801     COMSTACK l;
802     CS_TYPE type;
803     char mode[100], addr[100];
804     void *ap;
805     IOCHAN lst = NULL;
806
807     if (!where || sscanf(where, "%[^:]:%s", mode, addr) != 2)
808     {
809         yaz_log (LOG_WARN, "%s: Address format: ('tcp'|'ssl')':'<address>",
810                  me);
811         return;
812     }
813     if (!strcmp(mode, "tcp"))
814         type = tcpip_type;
815     else if (!strcmp(mode, "ssl"))
816     {
817 #if HAVE_OPENSSL_SSL_H
818         type = ssl_type;
819 #else
820         yaz_log (LOG_WARN, "SSL Transport not allowed by configuration.");
821         return;
822 #endif
823     }
824     else
825     {
826         yaz_log (LOG_WARN, "You must specify either 'ssl:' or 'tcp:'");
827         return;
828     }
829     yaz_log(LOG_LOG, "Adding %s %s listener on %s",
830             control_block.dynamic ? "dynamic" : 
831             (control_block.threads ? "threaded" : "static"),
832         what == PROTO_SR ? "SR" : "Z3950", where);
833     if (!(l = cs_create(type, 0, what)))
834     {
835         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create listener");
836         return;
837     }
838     ap = cs_straddr (l, addr);
839     if (!ap)
840     {
841         fprintf(stderr, "Address resolution failed.\n");
842         cs_close (l);
843         return;
844     }
845     if (cs_bind(l, ap, CS_SERVER) < 0)
846     {
847         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
848         cs_close (l);
849         return;
850     }
851     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
852          EVENT_EXCEPT)))
853     {
854         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
855         cs_close (l);
856         return;
857     }
858     iochan_setdata(lst, l);
859
860     /* Ensure our listener chain is setup properly */
861     lst->next = pListener;
862     pListener = lst;
863 }
864
865 #ifndef WIN32
866 /* For windows we don't need to catch the signals */
867 static void catchchld(int num)
868 {
869     while (waitpid(-1, 0, WNOHANG) > 0)
870         ;
871     signal(SIGCHLD, catchchld);
872 }
873 #endif /* WIN32 */
874
875 statserv_options_block *statserv_getcontrol(void)
876 {
877     static statserv_options_block cb;
878
879     memcpy(&cb, &control_block, sizeof(cb));
880     return &cb;
881 }
882
883 void statserv_setcontrol(statserv_options_block *block)
884 {
885     memcpy(&control_block, block, sizeof(*block));
886 }
887
888 int statserv_start(int argc, char **argv)
889 {
890     int ret;
891
892     nmem_init ();
893 #ifdef WIN32
894     /* We need to initialize the thread list */
895     ThreadList_Initialize();
896 #endif /* WIN32 */
897     
898 #ifdef WIN32
899     if ((me = strrchr (argv[0], '\\')))
900         me++;
901     else
902         me = argv[0];
903 #else
904     me = argv[0];
905 #endif
906     if (control_block.options_func(argc, argv))
907         return(1);
908     
909     if (control_block.bend_start)
910         (*control_block.bend_start)(&control_block);
911 #ifdef WIN32
912     logf (LOG_LOG, "Starting server %s", me);
913 #else
914     if (control_block.inetd)
915         inetd_connection(control_block.default_proto);
916     else
917     {
918         logf (LOG_LOG, "Starting server %s pid=%d", me, getpid());
919 #if 0
920         sigset_t sigs_to_block;
921         
922         sigemptyset(&sigs_to_block);
923         sigaddset (&sigs_to_block, SIGTERM);
924         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
925         /* missing... */
926 #endif
927         if (control_block.dynamic)
928             signal(SIGCHLD, catchchld);
929     }
930     signal (SIGPIPE, SIG_IGN);
931     signal (SIGTERM, sigterm);
932     if (*control_block.setuid)
933     {
934         struct passwd *pw;
935         
936         if (!(pw = getpwnam(control_block.setuid)))
937         {
938             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
939             return(1);
940         }
941         if (setuid(pw->pw_uid) < 0)
942         {
943             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
944             exit(1);
945         }
946     }
947 #endif /* WIN32 */
948
949     if ((pListener == NULL) && *control_block.default_listen)
950         add_listener(control_block.default_listen,
951                      control_block.default_proto);
952         
953     if (pListener == NULL)
954         ret = 1;
955     else
956     {
957         yaz_log(LOG_LOG, "Entering event loop.");
958         ret = event_loop(&pListener);
959     }
960     nmem_exit ();
961     return ret;
962 }
963
964 int check_options(int argc, char **argv)
965 {
966     int ret = 0, r;
967     char *arg;
968
969     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:", argv, argc, &arg)) != -2)
970     {
971         switch (ret)
972         {
973         case 0:
974             add_listener(arg, control_block.default_proto);
975             break;
976         case '1':        
977             control_block.one_shot = 1;
978             control_block.dynamic = 0;
979             break;
980         case 'z':
981             control_block.default_proto = PROTO_Z3950;
982             break;
983         case 's':
984             control_block.default_proto = PROTO_SR;
985             break;
986         case 'S':
987             control_block.dynamic = 0;
988             break;
989         case 'T':
990 #if HAVE_PTHREAD_H
991             control_block.dynamic = 0;
992             control_block.threads = 1;
993 #else
994             fprintf(stderr, "%s: Threaded mode not available.\n", me);
995             return 1;
996 #endif
997             break;
998         case 'l':
999             strcpy(control_block.logfile, arg ? arg : "");
1000             yaz_log_init(control_block.loglevel, me, control_block.logfile);
1001             break;
1002         case 'v':
1003             control_block.loglevel = yaz_log_mask_str(arg);
1004             yaz_log_init(control_block.loglevel, me, control_block.logfile);
1005             break;
1006         case 'a':
1007             strcpy(control_block.apdufile, arg ? arg : "");
1008             break;
1009         case 'u':
1010             strcpy(control_block.setuid, arg ? arg : "");
1011             break;
1012         case 'c':
1013             strcpy(control_block.configname, arg ? arg : "");
1014             break;
1015         case 'd':
1016             strcpy(control_block.daemon_name, arg ? arg : "");
1017             break;
1018         case 't':
1019             if (!arg || !(r = atoi(arg)))
1020             {
1021                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
1022                 return(1);
1023             }
1024             control_block.idle_timeout = r;
1025             break;
1026         case  'k':
1027             if (!arg || !(r = atoi(arg)))
1028             {
1029                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
1030                 return(1);
1031             }
1032             control_block.maxrecordsize = r * 1024;
1033             break;
1034         case 'i':
1035             control_block.inetd = 1;
1036             break;
1037         case 'w':
1038             if (chdir(arg))
1039             {
1040                 perror(arg);            
1041                 return 1;
1042             }
1043             break;
1044         default:
1045             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
1046                     " -l <logfile> -u <user> -c <config> -t <minutes>"
1047                     " -k <kilobytes> -d <daemon>"
1048                         " -zsiST -w <directory> <listender-addr>... ]\n", me);
1049             return 1;
1050         }
1051     }
1052     return 0;
1053 }
1054
1055 #ifdef WIN32
1056 typedef struct _Args
1057 {
1058     char **argv;
1059     int argc;
1060 } Args; 
1061
1062 static Args ArgDetails;
1063
1064 /* name of the executable */
1065 #define SZAPPNAME            "server"
1066
1067 /* list of service dependencies - "dep1\0dep2\0\0" */
1068 #define SZDEPENDENCIES       ""
1069
1070 int statserv_main(int argc, char **argv,
1071                   bend_initresult *(*bend_init)(bend_initrequest *r),
1072                   void (*bend_close)(void *handle))
1073 {
1074     statserv_options_block *cb = statserv_getcontrol();
1075     
1076     cb->bend_init = bend_init;
1077     cb->bend_close = bend_close;
1078
1079     statserv_setcontrol(cb);
1080
1081     /* Lets setup the Arg structure */
1082     ArgDetails.argc = argc;
1083     ArgDetails.argv = argv;
1084     
1085     /* Now setup the service with the service controller */
1086     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
1087                  cb->service_name, /* internal service name */
1088                  cb->service_name, /* displayed name of the service */
1089                  SZDEPENDENCIES);
1090     return 0;
1091 }
1092
1093 int StartAppService(void *pHandle, int argc, char **argv)
1094 {
1095     /* Initializes the App */
1096     return 1;
1097 }
1098
1099 void RunAppService(void *pHandle)
1100 {
1101     Args *pArgs = (Args *)pHandle;
1102     
1103     /* Starts the app running */
1104     statserv_start(pArgs->argc, pArgs->argv);
1105 }
1106
1107 void StopAppService(void *pHandle)
1108 {
1109     /* Stops the app */
1110     statserv_closedown();
1111 }
1112 #else
1113 int statserv_main(int argc, char **argv,
1114                   bend_initresult *(*bend_init)(bend_initrequest *r),
1115                   void (*bend_close)(void *handle))
1116 {
1117     int ret;
1118     statserv_options_block *cb = statserv_getcontrol();
1119     
1120     cb->bend_init = bend_init;
1121     cb->bend_close = bend_close;
1122
1123     statserv_setcontrol(cb);
1124     ret = statserv_start (argc, argv);
1125     statserv_closedown ();
1126     return ret;
1127 }
1128 #endif