Separate malloc debug library. Removal of ASN_COMPILED-#ifdefs.
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (c) 1995-2001, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * NT threaded server code by
7  *   Chas Woodfield, Fretwell Downing Informatics.
8  *
9  * $Id: statserv.c,v 1.79 2001-11-13 23:00:43 adam Exp $
10  */
11
12 #include <stdio.h>
13 #include <string.h>
14 #ifdef WIN32
15 #include <process.h>
16 #include <winsock.h>
17 #include <direct.h>
18 #include "service.h"
19 #else
20 #include <unistd.h>
21 #include <pwd.h>
22 #endif
23
24 #if YAZ_POSIX_THREADS
25 #include <pthread.h>
26 #elif YAZ_GNU_THREADS
27 #include <pth.h>
28 #endif
29
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <errno.h>
33
34 #include <yaz/comstack.h>
35 #include <yaz/tcpip.h>
36 #include <yaz/options.h>
37 #ifdef USE_XTIMOSI
38 #include <yaz/xmosi.h>
39 #endif
40 #include <yaz/log.h>
41 #include "eventl.h"
42 #include "session.h"
43 #include <yaz/statserv.h>
44
45 static IOCHAN pListener = NULL;
46
47 static char *me = "statserver";
48 /*
49  * default behavior.
50  */
51 int check_options(int argc, char **argv);
52 statserv_options_block control_block = {
53     1,                          /* dynamic mode */
54     0,                          /* threaded mode */
55     0,                          /* one shot (single session) */
56     LOG_DEFAULT_LEVEL,          /* log level */
57     "",                         /* no PDUs */
58     "",                         /* diagnostic output to stderr */
59     "tcp:@:9999",               /* default listener port */
60     PROTO_Z3950,                /* default application protocol */
61     60,                         /* idle timeout (minutes) */
62     1024*1024,                  /* maximum PDU size (approx.) to allow */
63     "default-config",           /* configuration name to pass to backend */
64     "",                         /* set user id */
65     0,                          /* bend_start handler */
66     0,                          /* bend_stop handler */
67     check_options,              /* Default routine, for checking the run-time arguments */
68     check_ip_tcpd,
69     "",
70     0,                          /* default value for inet deamon */
71     0,                          /* handle (for service, etc) */
72     0,                          /* bend_init handle */
73     0                           /* bend_close handle */
74 #ifdef WIN32
75     ,"Z39.50 Server",           /* NT Service Name */
76     "Server",                   /* NT application Name */
77     "",                         /* NT Service Dependencies */
78     "Z39.50 Server"             /* NT Service Display Name */
79 #endif /* WIN32 */
80 };
81
82 /*
83  * handle incoming connect requests.
84  * The dynamic mode is a bit tricky mostly because we want to avoid
85  * doing all of the listening and accepting in the parent - it's
86  * safer that way.
87  */
88 #ifdef WIN32
89
90 typedef struct _ThreadList ThreadList;
91
92 struct _ThreadList
93 {
94     HANDLE hThread;
95     IOCHAN pIOChannel;
96     ThreadList *pNext;
97 };
98
99 static ThreadList *pFirstThread;
100 static CRITICAL_SECTION Thread_CritSect;
101 static BOOL bInitialized = FALSE;
102
103 static void ThreadList_Initialize()
104 {
105     /* Initialize the critical Sections */
106     InitializeCriticalSection(&Thread_CritSect);
107
108      /* Set the first thraed */
109     pFirstThread = NULL;
110
111     /* we have been initialized */
112     bInitialized = TRUE;
113 }
114
115 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
116 {
117     /* Only one thread can go through this section at a time */
118     EnterCriticalSection(&Thread_CritSect);
119
120     {
121         /* Lets create our new object */
122         ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
123         pNewThread->hThread = hThread;
124         pNewThread->pIOChannel = pIOChannel;
125         pNewThread->pNext = pFirstThread;
126         pFirstThread = pNewThread;
127
128         /* Lets let somebody else create a new object now */
129         LeaveCriticalSection(&Thread_CritSect);
130     }
131 }
132
133 void statserv_remove(IOCHAN pIOChannel)
134 {
135     /* Only one thread can go through this section at a time */
136     EnterCriticalSection(&Thread_CritSect);
137
138     {
139         ThreadList *pCurrentThread = pFirstThread;
140         ThreadList *pNextThread;
141         ThreadList *pPrevThread =NULL;
142
143         /* Step through alll the threads */
144         for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
145         {
146             /* We only need to compare on the IO Channel */
147             if (pCurrentThread->pIOChannel == pIOChannel)
148             {
149                 /* We have found the thread we want to delete */
150                 /* First of all reset the next pointers */
151                 if (pPrevThread == NULL)
152                     pFirstThread = pCurrentThread->pNext;
153                 else
154                     pPrevThread->pNext = pCurrentThread->pNext;
155
156                 /* All we need todo now is delete the memory */
157                 free(pCurrentThread);
158
159                 /* No need to look at any more threads */
160                 pNextThread = NULL;
161             }
162             else
163             {
164                 /* We need to look at another thread */
165                 pNextThread = pCurrentThread->pNext;
166                 pPrevThread = pCurrentThread;
167             }
168         }
169
170         /* Lets let somebody else remove an object now */
171         LeaveCriticalSection(&Thread_CritSect);
172     }
173 }
174
175 /* WIN32 statserv_closedown */
176 void statserv_closedown()
177 {
178     /* Shouldn't do anything if we are not initialized */
179     if (bInitialized)
180     {
181         int iHandles = 0;
182         HANDLE *pThreadHandles = NULL;
183
184         /* We need to stop threads adding and removing while we */
185         /* start the closedown process */
186         EnterCriticalSection(&Thread_CritSect);
187
188         {
189             /* We have exclusive access to the thread stuff now */
190             /* Y didn't i use a semaphore - Oh well never mind */
191             ThreadList *pCurrentThread = pFirstThread;
192
193             /* Before we do anything else, we need to shutdown the listener */
194             if (pListener != NULL)
195                 iochan_destroy(pListener);
196
197             for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
198             {
199                 /* Just destroy the IOCHAN, that should do the trick */
200                 iochan_destroy(pCurrentThread->pIOChannel);
201                 closesocket(pCurrentThread->pIOChannel->fd);
202
203                 /* Keep a running count of our handles */
204                 iHandles++;
205             }
206
207             if (iHandles > 0)
208             {
209                 HANDLE *pCurrentHandle ;
210
211                 /* Allocate the thread handle array */
212                 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
213                 pCurrentHandle = pThreadHandles; 
214
215                 for (pCurrentThread = pFirstThread;
216                      pCurrentThread != NULL;
217                      pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
218                 {
219                     /* Just the handle */
220                     *pCurrentHandle = pCurrentThread->hThread;
221                 }
222             }
223
224             /* We can now leave the critical section */
225             LeaveCriticalSection(&Thread_CritSect);
226         }
227
228         /* Now we can really do something */
229         if (iHandles > 0)
230         {
231             logf (LOG_LOG, "waiting for %d to die", iHandles);
232             /* This will now wait, until all the threads close */
233             WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
234
235             /* Free the memory we allocated for the handle array */
236             free(pThreadHandles);
237         }
238
239         if (control_block.bend_stop)
240             (*control_block.bend_stop)(&control_block);
241         /* No longer require the critical section, since all threads are dead */
242         DeleteCriticalSection(&Thread_CritSect);
243     }
244 }
245
246 void event_loop_thread (IOCHAN iochan)
247 {
248     event_loop (&iochan);
249 }
250
251 /* WIN32 listener */
252 static void listener(IOCHAN h, int event)   
253 {
254     COMSTACK line = (COMSTACK) iochan_getdata(h);
255     association *newas;
256     int res;
257     HANDLE newHandle;
258
259     if (event == EVENT_INPUT)
260     {
261         if ((res = cs_listen(line, 0, 0)) < 0)
262         {
263             yaz_log(LOG_FATAL, "cs_listen failed");
264             return;
265         }
266         else if (res == 1)
267             return;
268         yaz_log(LOG_DEBUG, "listen ok");
269         iochan_setevent(h, EVENT_OUTPUT);
270         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
271     }
272     else if (event == EVENT_OUTPUT)
273     {
274         COMSTACK new_line = cs_accept(line);
275         IOCHAN new_chan;
276         char *a = NULL;
277
278         if (!new_line)
279         {
280             yaz_log(LOG_FATAL, "Accept failed.");
281             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
282             return;
283         }
284         yaz_log(LOG_DEBUG, "Accept ok");
285
286         if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
287                                        EVENT_INPUT)))
288         {
289             yaz_log(LOG_FATAL, "Failed to create iochan");
290             iochan_destroy(h);
291             return;
292         }
293
294         yaz_log(LOG_DEBUG, "Creating association");
295         if (!(newas = create_association(new_chan, new_line)))
296         {
297             yaz_log(LOG_FATAL, "Failed to create new assoc.");
298             iochan_destroy(h);
299             return;
300         }
301         newas->cs_get_mask = EVENT_INPUT;
302         newas->cs_put_mask = 0;
303         newas->cs_accept_mask = 0;
304
305         yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
306         iochan_setdata(new_chan, newas);
307         iochan_settimeout(new_chan, control_block.idle_timeout * 60);
308
309         /* Now what we need todo is create a new thread with this iochan as
310            the parameter */
311         newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
312         if (newHandle == (HANDLE) -1)
313         {
314             
315             yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread.");
316             iochan_destroy(h);
317             return;
318         }
319         /* We successfully created the thread, so add it to the list */
320         statserv_add(newHandle, new_chan);
321
322         yaz_log(LOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
323         iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
324     }
325     else
326     {
327         yaz_log(LOG_FATAL, "Bad event on listener.");
328         iochan_destroy(h);
329         return;
330     }
331 }
332
333 #else /* ! WIN32 */
334
335 /* To save having an #ifdef in event_loop we need to define this empty function */
336 void statserv_remove(IOCHAN pIOChannel)
337 {
338 }
339
340 void statserv_closedown()
341 {
342     IOCHAN p;
343
344     if (control_block.bend_stop)
345         (*control_block.bend_stop)(&control_block);
346
347     for (p = pListener; p; p = p->next)
348         iochan_destroy(p);
349 }
350
351 void sigterm(int sig)
352 {
353     statserv_closedown();
354     exit (0);
355 }
356
357 static void *new_session (void *vp);
358 static int no_sessions = 0;
359
360 /* UNIX listener */
361 static void listener(IOCHAN h, int event)
362 {
363     COMSTACK line = (COMSTACK) iochan_getdata(h);
364     static int hand[2];
365     static int child = 0;
366     int res;
367
368     if (event == EVENT_INPUT)
369     {
370         if (control_block.dynamic && !child) 
371         {
372             int res;
373
374             ++no_sessions;
375             if (pipe(hand) < 0)
376             {
377                 yaz_log(LOG_FATAL|LOG_ERRNO, "pipe");
378                 iochan_destroy(h);
379                 return;
380             }
381             if ((res = fork()) < 0)
382             {
383                 yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
384                 iochan_destroy(h);
385                 return;
386             }
387             else if (res == 0) /* child */
388             {
389                 char nbuf[100];
390                 IOCHAN pp;
391
392                 close(hand[0]);
393                 child = 1;
394                 for (pp = pListener; pp; pp = iochan_getnext(pp))
395                 {
396                     if (pp != h)
397                     {
398                         COMSTACK l = (COMSTACK)iochan_getdata(pp);
399                         cs_close(l);
400                         iochan_destroy(pp);
401                     }
402                 }
403                 sprintf(nbuf, "%s(%d)", me, getpid());
404                 yaz_log_init(control_block.loglevel, nbuf, 0);
405             }
406             else /* parent */
407             {
408                 close(hand[1]);
409                 /* wait for child to take the call */
410                 for (;;)
411                 {
412                     char dummy[1];
413                     int res;
414                     
415                     if ((res = read(hand[0], dummy, 1)) < 0 && errno != EINTR)
416                     {
417                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
418                         return;
419                     }
420                     else if (res >= 0)
421                         break;
422                 }
423                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
424                 close(hand[0]);
425                 return;
426             }
427         }
428         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
429                                    control_block.daemon_name)) < 0)
430         {
431             yaz_log(LOG_WARN, "cs_listen failed");
432             return;
433         }
434         else if (res == 1)
435             return;
436         yaz_log(LOG_DEBUG, "listen ok");
437         iochan_setevent(h, EVENT_OUTPUT);
438         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
439     }
440     /* in dynamic mode, only the child ever comes down here */
441     else if (event == EVENT_OUTPUT)
442     {
443         COMSTACK new_line = cs_accept(line);
444
445         if (!new_line)
446         {
447             yaz_log(LOG_FATAL, "Accept failed.");
448             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
449             return;
450         }
451         yaz_log(LOG_DEBUG, "accept ok");
452         if (control_block.dynamic)
453         {
454             IOCHAN pp;
455             /* close our half of the listener socket */
456             for (pp = pListener; pp; pp = iochan_getnext(pp))
457             {
458                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
459                 cs_close(l);
460                 iochan_destroy(pp);
461             }
462             /* release dad */
463             yaz_log(LOG_DEBUG, "Releasing parent");
464             close(hand[1]);
465         }
466         else
467         {
468             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
469             ++no_sessions;
470         }
471 #if YAZ_POSIX_THREADS
472         if (control_block.threads)
473         {
474             pthread_t child_thread;
475             pthread_create (&child_thread, 0, new_session, new_line);
476             pthread_detach (child_thread);
477         }
478         else
479             new_session(new_line);
480 #elif YAZ_GNU_THREADS
481         if (control_block.threads)
482         {
483             pth_attr_t attr;
484             pth_t child_thread;
485
486             attr = pth_attr_new ();
487             pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
488             pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
489             pth_attr_set (attr, PTH_ATTR_NAME, "session");
490             yaz_log (LOG_LOG, "pth_spawn begin");
491             child_thread = pth_spawn (attr, new_session, new_line);
492             yaz_log (LOG_LOG, "pth_spawn finish");
493             pth_attr_destroy (attr);
494         }
495         else
496             new_session(new_line);
497 #else
498         new_session(new_line);
499 #endif
500     }
501     else
502     {
503         yaz_log(LOG_FATAL, "Bad event on listener.");
504         iochan_destroy(h);
505         return;
506     }
507 }
508
509 static void *new_session (void *vp)
510 {
511     char *a;
512     association *newas;
513     IOCHAN new_chan;
514     COMSTACK new_line = (COMSTACK) vp;
515
516     unsigned cs_get_mask, cs_accept_mask, mask =  
517         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
518         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
519
520     if (mask)    
521     {
522         cs_accept_mask = mask;  /* accept didn't complete */
523         cs_get_mask = 0;
524     }
525     else
526     {
527         cs_accept_mask = 0;     /* accept completed.  */
528         cs_get_mask = mask = EVENT_INPUT;
529     }
530
531     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
532     {
533         yaz_log(LOG_FATAL, "Failed to create iochan");
534         return 0;
535     }
536     if (!(newas = create_association(new_chan, new_line)))
537     {
538         yaz_log(LOG_FATAL, "Failed to create new assoc.");
539         return 0;
540     }
541     newas->cs_accept_mask = cs_accept_mask;
542     newas->cs_get_mask = cs_get_mask;
543
544     iochan_setdata(new_chan, newas);
545     iochan_settimeout(new_chan, control_block.idle_timeout * 60);
546     a = cs_addrstr(new_line);
547     yaz_log(LOG_LOG, "Starting session %d from %s",
548         no_sessions, a ? a : "[Unknown]");
549     if (control_block.threads)
550     {
551         event_loop(&new_chan);
552     }
553     else
554     {
555         new_chan->next = pListener;
556         pListener = new_chan;
557     }
558     return 0;
559 }
560
561 #endif /* WIN32 */
562
563 static void inetd_connection(int what)
564 {
565     COMSTACK line;
566     IOCHAN chan;
567     association *assoc;
568     char *addr;
569
570     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
571     {
572         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
573         {
574             if ((assoc = create_association(chan, line)))
575             {
576                 iochan_setdata(chan, assoc);
577                 iochan_settimeout(chan, control_block.idle_timeout * 60);
578                 addr = cs_addrstr(line);
579                 yaz_log(LOG_LOG, "Inetd association from %s",
580                         addr ? addr : "[UNKNOWN]");
581                 assoc->cs_get_mask = EVENT_INPUT;
582             }
583             else
584             {
585                 yaz_log(LOG_FATAL, "Failed to create association structure");
586             }
587             chan->next = pListener;
588             pListener = chan;
589         }
590         else
591         {
592             yaz_log(LOG_FATAL, "Failed to create iochan");
593         }
594     }
595     else
596     {
597         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
598     }
599 }
600
601 /*
602  * Set up a listening endpoint, and give it to the event-handler.
603  */
604 static void add_listener(char *where, int what)
605 {
606     COMSTACK l;
607     void *ap;
608     IOCHAN lst = NULL;
609     const char *mode;
610
611     if (control_block.dynamic)
612         mode = "dynamic";
613     else if (control_block.threads)
614         mode = "threaded";
615     else
616         mode = "static";
617
618     yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
619             what == PROTO_SR ? "SR" : "Z3950", where);
620
621     l = cs_create_host(where, 0, &ap);
622     if (!l)
623     {
624         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where);
625         return;
626     }
627     if (cs_bind(l, ap, CS_SERVER) < 0)
628     {
629         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
630         cs_close (l);
631         return;
632     }
633     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
634          EVENT_EXCEPT)))
635     {
636         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
637         cs_close (l);
638         return;
639     }
640     iochan_setdata(lst, l);
641
642     /* Ensure our listener chain is setup properly */
643     lst->next = pListener;
644     pListener = lst;
645 }
646
647 #ifndef WIN32
648 /* For windows we don't need to catch the signals */
649 static void catchchld(int num)
650 {
651     while (waitpid(-1, 0, WNOHANG) > 0)
652         ;
653     signal(SIGCHLD, catchchld);
654 }
655 #endif /* WIN32 */
656
657 statserv_options_block *statserv_getcontrol(void)
658 {
659     static statserv_options_block cb;
660
661     memcpy(&cb, &control_block, sizeof(cb));
662     return &cb;
663 }
664
665 void statserv_setcontrol(statserv_options_block *block)
666 {
667     memcpy(&control_block, block, sizeof(*block));
668 }
669
670 int statserv_start(int argc, char **argv)
671 {
672     int ret;
673
674 #ifdef WIN32
675     /* We need to initialize the thread list */
676     ThreadList_Initialize();
677 #endif /* WIN32 */
678     
679 #ifdef WIN32
680     if ((me = strrchr (argv[0], '\\')))
681         me++;
682     else
683         me = argv[0];
684 #else
685     me = argv[0];
686 #endif
687     if (control_block.options_func(argc, argv))
688         return(1);
689     
690     if (control_block.bend_start)
691         (*control_block.bend_start)(&control_block);
692 #ifdef WIN32
693     logf (LOG_LOG, "Starting server %s", me);
694 #else
695     if (control_block.inetd)
696         inetd_connection(control_block.default_proto);
697     else
698     {
699         logf (LOG_LOG, "Starting server %s pid=%d", me, getpid());
700 #if 0
701         sigset_t sigs_to_block;
702         
703         sigemptyset(&sigs_to_block);
704         sigaddset (&sigs_to_block, SIGTERM);
705         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
706         /* missing... */
707 #endif
708         if (control_block.dynamic)
709             signal(SIGCHLD, catchchld);
710     }
711     signal (SIGPIPE, SIG_IGN);
712     signal (SIGTERM, sigterm);
713     if (*control_block.setuid)
714     {
715         struct passwd *pw;
716         
717         if (!(pw = getpwnam(control_block.setuid)))
718         {
719             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
720             return(1);
721         }
722         if (setuid(pw->pw_uid) < 0)
723         {
724             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
725             exit(1);
726         }
727     }
728 #endif /* WIN32 */
729
730     if ((pListener == NULL) && *control_block.default_listen)
731         add_listener(control_block.default_listen,
732                      control_block.default_proto);
733         
734     if (pListener == NULL)
735         ret = 1;
736     else
737     {
738         yaz_log(LOG_LOG, "Entering event loop.");
739         ret = event_loop(&pListener);
740     }
741     return ret;
742 }
743
744 int check_options(int argc, char **argv)
745 {
746     int ret = 0, r;
747     char *arg;
748
749     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:", argv, argc, &arg)) != -2)
750     {
751         switch (ret)
752         {
753         case 0:
754             add_listener(arg, control_block.default_proto);
755             break;
756         case '1':        
757             control_block.one_shot = 1;
758             control_block.dynamic = 0;
759             break;
760         case 'z':
761             control_block.default_proto = PROTO_Z3950;
762             break;
763         case 's':
764             control_block.default_proto = PROTO_SR;
765             break;
766         case 'S':
767             control_block.dynamic = 0;
768             break;
769         case 'T':
770 #if YAZ_POSIX_THREADS
771             control_block.dynamic = 0;
772             control_block.threads = 1;
773 #elif YAZ_GNU_THREADS
774             control_block.dynamic = 0;
775             control_block.threads = 1;
776 #else
777             fprintf(stderr, "%s: Threaded mode not available.\n", me);
778             return 1;
779 #endif
780             break;
781         case 'l':
782             strcpy(control_block.logfile, arg ? arg : "");
783             yaz_log_init(control_block.loglevel, me, control_block.logfile);
784             break;
785         case 'v':
786             control_block.loglevel = yaz_log_mask_str(arg);
787             yaz_log_init(control_block.loglevel, me, control_block.logfile);
788             break;
789         case 'a':
790             strcpy(control_block.apdufile, arg ? arg : "");
791             break;
792         case 'u':
793             strcpy(control_block.setuid, arg ? arg : "");
794             break;
795         case 'c':
796             strcpy(control_block.configname, arg ? arg : "");
797             break;
798         case 'd':
799             strcpy(control_block.daemon_name, arg ? arg : "");
800             break;
801         case 't':
802             if (!arg || !(r = atoi(arg)))
803             {
804                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
805                 return(1);
806             }
807             control_block.idle_timeout = r;
808             break;
809         case  'k':
810             if (!arg || !(r = atoi(arg)))
811             {
812                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
813                 return(1);
814             }
815             control_block.maxrecordsize = r * 1024;
816             break;
817         case 'i':
818             control_block.inetd = 1;
819             break;
820         case 'w':
821             if (chdir(arg))
822             {
823                 perror(arg);            
824                 return 1;
825             }
826             break;
827         default:
828             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
829                     " -l <logfile> -u <user> -c <config> -t <minutes>"
830                     " -k <kilobytes> -d <daemon>"
831                         " -zsiST -w <directory> <listender-addr>... ]\n", me);
832             return 1;
833         }
834     }
835     return 0;
836 }
837
838 #ifdef WIN32
839 typedef struct _Args
840 {
841     char **argv;
842     int argc;
843 } Args; 
844
845 static Args ArgDetails;
846
847 /* name of the executable */
848 #define SZAPPNAME            "server"
849
850 /* list of service dependencies - "dep1\0dep2\0\0" */
851 #define SZDEPENDENCIES       ""
852
853 int statserv_main(int argc, char **argv,
854                   bend_initresult *(*bend_init)(bend_initrequest *r),
855                   void (*bend_close)(void *handle))
856 {
857     statserv_options_block *cb = statserv_getcontrol();
858     
859     cb->bend_init = bend_init;
860     cb->bend_close = bend_close;
861
862     statserv_setcontrol(cb);
863
864     /* Lets setup the Arg structure */
865     ArgDetails.argc = argc;
866     ArgDetails.argv = argv;
867     
868     /* Now setup the service with the service controller */
869     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
870                  cb->service_name, /* internal service name */
871                  cb->service_name, /* displayed name of the service */
872                  SZDEPENDENCIES);
873     return 0;
874 }
875
876 int StartAppService(void *pHandle, int argc, char **argv)
877 {
878     /* Initializes the App */
879     return 1;
880 }
881
882 void RunAppService(void *pHandle)
883 {
884     Args *pArgs = (Args *)pHandle;
885     
886     /* Starts the app running */
887     statserv_start(pArgs->argc, pArgs->argv);
888 }
889
890 void StopAppService(void *pHandle)
891 {
892     /* Stops the app */
893     statserv_closedown();
894 }
895 #else
896 int statserv_main(int argc, char **argv,
897                   bend_initresult *(*bend_init)(bend_initrequest *r),
898                   void (*bend_close)(void *handle))
899 {
900     int ret;
901     statserv_options_block *cb = statserv_getcontrol();
902     
903     cb->bend_init = bend_init;
904     cb->bend_close = bend_close;
905
906     statserv_setcontrol(cb);
907     ret = statserv_start (argc, argv);
908     statserv_closedown ();
909     return ret;
910 }
911 #endif