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