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