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