source: trunk/gsdl/packages/yaz/server/statserv.c@ 1343

Last change on this file since 1343 was 1343, checked in by johnmcp, 24 years ago

Added the YAZ toolkit source to the packages directory (for z39.50 stuff)

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