Thread-safe handling of errno variable.
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (c) 1995-2002, 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.85 2002-09-25 12:37:07 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 __cdecl 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 &&
416                                      yaz_errno() != EINTR)
417                     {
418                         yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
419                         return;
420                     }
421                     else if (res >= 0)
422                         break;
423                 }
424                 yaz_log(LOG_DEBUG, "P: Child has taken the call");
425                 close(hand[0]);
426                 return;
427             }
428         }
429         if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
430                                    control_block.daemon_name)) < 0)
431         {
432             yaz_log(LOG_WARN, "cs_listen failed");
433             return;
434         }
435         else if (res == 1)
436             return;
437         yaz_log(LOG_DEBUG, "listen ok");
438         iochan_setevent(h, EVENT_OUTPUT);
439         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
440     }
441     /* in dynamic mode, only the child ever comes down here */
442     else if (event == EVENT_OUTPUT)
443     {
444         COMSTACK new_line = cs_accept(line);
445
446         if (!new_line)
447         {
448             yaz_log(LOG_FATAL, "Accept failed.");
449             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
450             return;
451         }
452         yaz_log(LOG_DEBUG, "accept ok");
453         if (control_block.dynamic)
454         {
455             IOCHAN pp;
456             /* close our half of the listener socket */
457             for (pp = pListener; pp; pp = iochan_getnext(pp))
458             {
459                 COMSTACK l = (COMSTACK)iochan_getdata(pp);
460                 cs_close(l);
461                 iochan_destroy(pp);
462             }
463             /* release dad */
464             yaz_log(LOG_DEBUG, "Releasing parent");
465             close(hand[1]);
466         }
467         else
468         {
469             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
470             ++no_sessions;
471         }
472 #if YAZ_POSIX_THREADS
473         if (control_block.threads)
474         {
475             pthread_t child_thread;
476             pthread_create (&child_thread, 0, new_session, new_line);
477             pthread_detach (child_thread);
478         }
479         else
480             new_session(new_line);
481 #elif YAZ_GNU_THREADS
482         if (control_block.threads)
483         {
484             pth_attr_t attr;
485             pth_t child_thread;
486
487             attr = pth_attr_new ();
488             pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
489             pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
490             pth_attr_set (attr, PTH_ATTR_NAME, "session");
491             yaz_log (LOG_LOG, "pth_spawn begin");
492             child_thread = pth_spawn (attr, new_session, new_line);
493             yaz_log (LOG_LOG, "pth_spawn finish");
494             pth_attr_destroy (attr);
495         }
496         else
497             new_session(new_line);
498 #else
499         new_session(new_line);
500 #endif
501     }
502     else
503     {
504         yaz_log(LOG_FATAL, "Bad event on listener.");
505         iochan_destroy(h);
506         return;
507     }
508 }
509
510 static void *new_session (void *vp)
511 {
512     char *a;
513     association *newas;
514     IOCHAN new_chan;
515     COMSTACK new_line = (COMSTACK) vp;
516
517     unsigned cs_get_mask, cs_accept_mask, mask =  
518         ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
519         ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
520
521     if (mask)    
522     {
523         cs_accept_mask = mask;  /* accept didn't complete */
524         cs_get_mask = 0;
525     }
526     else
527     {
528         cs_accept_mask = 0;     /* accept completed.  */
529         cs_get_mask = mask = EVENT_INPUT;
530     }
531
532     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
533     {
534         yaz_log(LOG_FATAL, "Failed to create iochan");
535         return 0;
536     }
537     if (!(newas = create_association(new_chan, new_line)))
538     {
539         yaz_log(LOG_FATAL, "Failed to create new assoc.");
540         return 0;
541     }
542     newas->cs_accept_mask = cs_accept_mask;
543     newas->cs_get_mask = cs_get_mask;
544
545     iochan_setdata(new_chan, newas);
546     iochan_settimeout(new_chan, control_block.idle_timeout * 60);
547     a = cs_addrstr(new_line);
548     yaz_log(LOG_LOG, "Starting session %d from %s",
549         no_sessions, a ? a : "[Unknown]");
550     if (control_block.threads)
551     {
552         event_loop(&new_chan);
553     }
554     else
555     {
556         new_chan->next = pListener;
557         pListener = new_chan;
558     }
559     return 0;
560 }
561
562 #endif /* WIN32 */
563
564 static void inetd_connection(int what)
565 {
566     COMSTACK line;
567     IOCHAN chan;
568     association *assoc;
569     char *addr;
570
571     if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
572     {
573         if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT)))
574         {
575             if ((assoc = create_association(chan, line)))
576             {
577                 iochan_setdata(chan, assoc);
578                 iochan_settimeout(chan, control_block.idle_timeout * 60);
579                 addr = cs_addrstr(line);
580                 yaz_log(LOG_LOG, "Inetd association from %s",
581                         addr ? addr : "[UNKNOWN]");
582                 assoc->cs_get_mask = EVENT_INPUT;
583             }
584             else
585             {
586                 yaz_log(LOG_FATAL, "Failed to create association structure");
587             }
588             chan->next = pListener;
589             pListener = chan;
590         }
591         else
592         {
593             yaz_log(LOG_FATAL, "Failed to create iochan");
594         }
595     }
596     else
597     {
598         yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
599     }
600 }
601
602 /*
603  * Set up a listening endpoint, and give it to the event-handler.
604  */
605 static int add_listener(char *where, int what)
606 {
607     COMSTACK l;
608     void *ap;
609     IOCHAN lst = NULL;
610     const char *mode;
611
612     if (control_block.dynamic)
613         mode = "dynamic";
614     else if (control_block.threads)
615         mode = "threaded";
616     else
617         mode = "static";
618
619     yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
620             what == PROTO_SR ? "SR" : "Z3950", where);
621
622     l = cs_create_host(where, 0, &ap);
623     if (!l)
624     {
625         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where);
626         return -1;
627     }
628     if (cs_bind(l, ap, CS_SERVER) < 0)
629     {
630         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
631         cs_close (l);
632         return -1;
633     }
634     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
635          EVENT_EXCEPT)))
636     {
637         yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
638         cs_close (l);
639         return -1;
640     }
641     iochan_setdata(lst, l);
642
643     /* Ensure our listener chain is setup properly */
644     lst->next = pListener;
645     pListener = lst;
646     return 0; /* OK */
647 }
648
649 #ifndef WIN32
650 /* For windows we don't need to catch the signals */
651 static void catchchld(int num)
652 {
653     while (waitpid(-1, 0, WNOHANG) > 0)
654         ;
655     signal(SIGCHLD, catchchld);
656 }
657 #endif /* WIN32 */
658
659 statserv_options_block *statserv_getcontrol(void)
660 {
661     static statserv_options_block cb;
662
663     memcpy(&cb, &control_block, sizeof(cb));
664     return &cb;
665 }
666
667 void statserv_setcontrol(statserv_options_block *block)
668 {
669     memcpy(&control_block, block, sizeof(*block));
670 }
671
672 int statserv_start(int argc, char **argv)
673 {
674     int ret;
675
676 #ifdef WIN32
677     /* We need to initialize the thread list */
678     ThreadList_Initialize();
679 #endif /* WIN32 */
680     
681 #ifdef WIN32
682     if ((me = strrchr (argv[0], '\\')))
683         me++;
684     else
685         me = argv[0];
686 #else
687     me = argv[0];
688 #endif
689     if (control_block.options_func(argc, argv))
690         return(1);
691     
692     if (control_block.bend_start)
693         (*control_block.bend_start)(&control_block);
694 #ifdef WIN32
695     logf (LOG_LOG, "Starting server %s", me);
696 #else
697     if (control_block.inetd)
698         inetd_connection(control_block.default_proto);
699     else
700     {
701         logf (LOG_LOG, "Starting server %s pid=%d", me, getpid());
702 #if 0
703         sigset_t sigs_to_block;
704         
705         sigemptyset(&sigs_to_block);
706         sigaddset (&sigs_to_block, SIGTERM);
707         pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0);
708         /* missing... */
709 #endif
710         if (control_block.dynamic)
711             signal(SIGCHLD, catchchld);
712     }
713     signal (SIGPIPE, SIG_IGN);
714     signal (SIGTERM, sigterm);
715     if (*control_block.setuid)
716     {
717         struct passwd *pw;
718         
719         if (!(pw = getpwnam(control_block.setuid)))
720         {
721             yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
722             return(1);
723         }
724         if (setuid(pw->pw_uid) < 0)
725         {
726             yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
727             exit(1);
728         }
729     }
730 #endif /* WIN32 */
731
732     if ((pListener == NULL) && *control_block.default_listen)
733         add_listener(control_block.default_listen,
734                      control_block.default_proto);
735         
736     if (pListener == NULL)
737         ret = 1;
738     else
739     {
740         yaz_log(LOG_LOG, "Entering event loop.");
741         ret = event_loop(&pListener);
742     }
743     return ret;
744 }
745
746 int check_options(int argc, char **argv)
747 {
748     int ret = 0, r;
749     char *arg;
750
751     while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:", argv, argc, &arg)) != -2)
752     {
753         switch (ret)
754         {
755         case 0:
756             if (add_listener(arg, control_block.default_proto))
757                 return 1;  /* failed to create listener */
758             break;
759         case '1':        
760             control_block.one_shot = 1;
761             control_block.dynamic = 0;
762             break;
763         case 'z':
764             control_block.default_proto = PROTO_Z3950;
765             break;
766         case 's':
767             fprintf (stderr, "%s: SR protocol no longer supported\n", me);
768             exit (1);
769             break;
770         case 'S':
771             control_block.dynamic = 0;
772             break;
773         case 'T':
774 #if YAZ_POSIX_THREADS
775             control_block.dynamic = 0;
776             control_block.threads = 1;
777 #elif YAZ_GNU_THREADS
778             control_block.dynamic = 0;
779             control_block.threads = 1;
780 #else
781             fprintf(stderr, "%s: Threaded mode not available.\n", me);
782             return 1;
783 #endif
784             break;
785         case 'l':
786             strcpy(control_block.logfile, arg ? arg : "");
787             yaz_log_init(control_block.loglevel, me, control_block.logfile);
788             break;
789         case 'v':
790             control_block.loglevel = yaz_log_mask_str(arg);
791             yaz_log_init(control_block.loglevel, me, control_block.logfile);
792             break;
793         case 'a':
794             strcpy(control_block.apdufile, arg ? arg : "");
795             break;
796         case 'u':
797             strcpy(control_block.setuid, arg ? arg : "");
798             break;
799         case 'c':
800             strcpy(control_block.configname, arg ? arg : "");
801             break;
802         case 'd':
803             strcpy(control_block.daemon_name, arg ? arg : "");
804             break;
805         case 't':
806             if (!arg || !(r = atoi(arg)))
807             {
808                 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
809                 return(1);
810             }
811             control_block.idle_timeout = r;
812             break;
813         case  'k':
814             if (!arg || !(r = atoi(arg)))
815             {
816                 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
817                 return(1);
818             }
819             control_block.maxrecordsize = r * 1024;
820             break;
821         case 'i':
822             control_block.inetd = 1;
823             break;
824         case 'w':
825             if (chdir(arg))
826             {
827                 perror(arg);            
828                 return 1;
829             }
830             break;
831         default:
832             fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
833                     " -l <logfile> -u <user> -c <config> -t <minutes>"
834                     " -k <kilobytes> -d <daemon>"
835                         " -ziST1 -w <directory> <listender-addr>... ]\n", me);
836             return 1;
837         }
838     }
839     return 0;
840 }
841
842 #ifdef WIN32
843 typedef struct _Args
844 {
845     char **argv;
846     int argc;
847 } Args; 
848
849 static Args ArgDetails;
850
851 /* name of the executable */
852 #define SZAPPNAME            "server"
853
854 /* list of service dependencies - "dep1\0dep2\0\0" */
855 #define SZDEPENDENCIES       ""
856
857 int statserv_main(int argc, char **argv,
858                   bend_initresult *(*bend_init)(bend_initrequest *r),
859                   void (*bend_close)(void *handle))
860 {
861     statserv_options_block *cb = statserv_getcontrol();
862     
863     cb->bend_init = bend_init;
864     cb->bend_close = bend_close;
865
866     statserv_setcontrol(cb);
867
868     /* Lets setup the Arg structure */
869     ArgDetails.argc = argc;
870     ArgDetails.argv = argv;
871     
872     /* Now setup the service with the service controller */
873     SetupService(argc, argv, &ArgDetails, SZAPPNAME,
874                  cb->service_name, /* internal service name */
875                  cb->service_display_name, /* displayed name */
876                  SZDEPENDENCIES);
877     return 0;
878 }
879
880 int StartAppService(void *pHandle, int argc, char **argv)
881 {
882     /* Initializes the App */
883     return 1;
884 }
885
886 void RunAppService(void *pHandle)
887 {
888     Args *pArgs = (Args *)pHandle;
889     
890     /* Starts the app running */
891     statserv_start(pArgs->argc, pArgs->argv);
892 }
893
894 void StopAppService(void *pHandle)
895 {
896     /* Stops the app */
897     statserv_closedown();
898 }
899 #else
900 int statserv_main(int argc, char **argv,
901                   bend_initresult *(*bend_init)(bend_initrequest *r),
902                   void (*bend_close)(void *handle))
903 {
904     int ret;
905     statserv_options_block *cb = statserv_getcontrol();
906     
907     cb->bend_init = bend_init;
908     cb->bend_close = bend_close;
909
910     statserv_setcontrol(cb);
911     ret = statserv_start (argc, argv);
912     statserv_closedown ();
913     return ret;
914 }
915 #endif