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