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