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