📄 statserv.c
字号:
/* * Copyright (c) 1995-2003, Index Data * See the file LICENSE for details. * Sebastian Hammer, Adam Dickmeiss * * NT threaded server code by * Chas Woodfield, Fretwell Downing Informatics. * * $Id: statserv.c,v 1.98 2003/03/03 19:57:35 adam Exp $ */#include <stdio.h>#include <string.h>#ifdef WIN32#include <process.h>#include <winsock.h>#include <direct.h>#include "service.h"#else#include <unistd.h>#include <pwd.h>#endif#if YAZ_POSIX_THREADS#include <pthread.h>#elif YAZ_GNU_THREADS#include <pth.h>#endif#include <fcntl.h>#include <signal.h>#include <errno.h>#include <yaz/comstack.h>#include <yaz/tcpip.h>#include <yaz/options.h>#ifdef USE_XTIMOSI#include <yaz/xmosi.h>#endif#include <yaz/log.h>#include "eventl.h"#include "session.h"#include <yaz/statserv.h>static IOCHAN pListener = NULL;static char *me = "statserver";/* * default behavior. */int check_options(int argc, char **argv);statserv_options_block control_block = { 1, /* dynamic mode */ 0, /* threaded mode */ 0, /* one shot (single session) */ LOG_DEFAULT_LEVEL, /* log level */ "", /* no PDUs */ "", /* diagnostic output to stderr */ "tcp:@:9999", /* default listener port */ PROTO_Z3950, /* default application protocol */ 15, /* idle timeout (minutes) */ 1024*1024, /* maximum PDU size (approx.) to allow */ "default-config", /* configuration name to pass to backend */ "", /* set user id */ 0, /* bend_start handler */ 0, /* bend_stop handler */ check_options, /* Default routine, for checking the run-time arguments */ check_ip_tcpd, "", 0, /* default value for inet deamon */ 0, /* handle (for service, etc) */ 0, /* bend_init handle */ 0, /* bend_close handle */#ifdef WIN32 "Z39.50 Server", /* NT Service Name */ "Server", /* NT application Name */ "", /* NT Service Dependencies */ "Z39.50 Server", /* NT Service Display Name */#endif /* WIN32 */ 0 /* SOAP handlers */};static int max_sessions = 0;/* * handle incoming connect requests. * The dynamic mode is a bit tricky mostly because we want to avoid * doing all of the listening and accepting in the parent - it's * safer that way. */#ifdef WIN32typedef struct _ThreadList ThreadList;struct _ThreadList{ HANDLE hThread; IOCHAN pIOChannel; ThreadList *pNext;};static ThreadList *pFirstThread;static CRITICAL_SECTION Thread_CritSect;static BOOL bInitialized = FALSE;static void ThreadList_Initialize(){ /* Initialize the critical Sections */ InitializeCriticalSection(&Thread_CritSect); /* Set the first thraed */ pFirstThread = NULL; /* we have been initialized */ bInitialized = TRUE;}static void statserv_add(HANDLE hThread, IOCHAN pIOChannel){ /* Only one thread can go through this section at a time */ EnterCriticalSection(&Thread_CritSect); { /* Lets create our new object */ ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList)); pNewThread->hThread = hThread; pNewThread->pIOChannel = pIOChannel; pNewThread->pNext = pFirstThread; pFirstThread = pNewThread; /* Lets let somebody else create a new object now */ LeaveCriticalSection(&Thread_CritSect); }}void statserv_remove(IOCHAN pIOChannel){ /* Only one thread can go through this section at a time */ EnterCriticalSection(&Thread_CritSect); { ThreadList *pCurrentThread = pFirstThread; ThreadList *pNextThread; ThreadList *pPrevThread =NULL; /* Step through alll the threads */ for (; pCurrentThread != NULL; pCurrentThread = pNextThread) { /* We only need to compare on the IO Channel */ if (pCurrentThread->pIOChannel == pIOChannel) { /* We have found the thread we want to delete */ /* First of all reset the next pointers */ if (pPrevThread == NULL) pFirstThread = pCurrentThread->pNext; else pPrevThread->pNext = pCurrentThread->pNext; /* All we need todo now is delete the memory */ free(pCurrentThread); /* No need to look at any more threads */ pNextThread = NULL; } else { /* We need to look at another thread */ pNextThread = pCurrentThread->pNext; pPrevThread = pCurrentThread; } } /* Lets let somebody else remove an object now */ LeaveCriticalSection(&Thread_CritSect); }}/* WIN32 statserv_closedown */void statserv_closedown(){ /* Shouldn't do anything if we are not initialized */ if (bInitialized) { int iHandles = 0; HANDLE *pThreadHandles = NULL; /* We need to stop threads adding and removing while we */ /* start the closedown process */ EnterCriticalSection(&Thread_CritSect); { /* We have exclusive access to the thread stuff now */ /* Y didn't i use a semaphore - Oh well never mind */ ThreadList *pCurrentThread = pFirstThread; /* Before we do anything else, we need to shutdown the listener */ if (pListener != NULL) iochan_destroy(pListener); for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext) { /* Just destroy the IOCHAN, that should do the trick */ iochan_destroy(pCurrentThread->pIOChannel); closesocket(pCurrentThread->pIOChannel->fd); /* Keep a running count of our handles */ iHandles++; } if (iHandles > 0) { HANDLE *pCurrentHandle ; /* Allocate the thread handle array */ pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles); pCurrentHandle = pThreadHandles; for (pCurrentThread = pFirstThread; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext, pCurrentHandle++) { /* Just the handle */ *pCurrentHandle = pCurrentThread->hThread; } } /* We can now leave the critical section */ LeaveCriticalSection(&Thread_CritSect); } /* Now we can really do something */ if (iHandles > 0) { logf (LOG_LOG, "waiting for %d to die", iHandles); /* This will now wait, until all the threads close */ WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE); /* Free the memory we allocated for the handle array */ free(pThreadHandles); } if (control_block.bend_stop) (*control_block.bend_stop)(&control_block); /* No longer require the critical section, since all threads are dead */ DeleteCriticalSection(&Thread_CritSect); }}void __cdecl event_loop_thread (IOCHAN iochan){ event_loop (&iochan);}/* WIN32 listener */static void listener(IOCHAN h, int event) { COMSTACK line = (COMSTACK) iochan_getdata(h); association *newas; int res; HANDLE newHandle; if (event == EVENT_INPUT) { if ((res = cs_listen(line, 0, 0)) < 0) { yaz_log(LOG_FATAL, "cs_listen failed"); return; } else if (res == 1) return; yaz_log(LOG_DEBUG, "listen ok"); iochan_setevent(h, EVENT_OUTPUT); iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */ } else if (event == EVENT_OUTPUT) { COMSTACK new_line = cs_accept(line); IOCHAN new_chan; char *a = NULL; if (!new_line) { yaz_log(LOG_FATAL, "Accept failed."); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); return; } yaz_log(LOG_DEBUG, "Accept ok"); if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, EVENT_INPUT))) { yaz_log(LOG_FATAL, "Failed to create iochan"); iochan_destroy(h); return; } yaz_log(LOG_DEBUG, "Creating association"); if (!(newas = create_association(new_chan, new_line))) { yaz_log(LOG_FATAL, "Failed to create new assoc."); iochan_destroy(h); return; } newas->cs_get_mask = EVENT_INPUT; newas->cs_put_mask = 0; newas->cs_accept_mask = 0; yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout); iochan_setdata(new_chan, newas); iochan_settimeout(new_chan, 60); /* Now what we need todo is create a new thread with this iochan as the parameter */ newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan); if (newHandle == (HANDLE) -1) { yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread."); iochan_destroy(h); return; } /* We successfully created the thread, so add it to the list */ statserv_add(newHandle, new_chan); yaz_log(LOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ } else { yaz_log(LOG_FATAL, "Bad event on listener."); iochan_destroy(h); return; }}int statserv_must_terminate(void){ return 0;}#else /* ! WIN32 */static int term_flag = 0;/* To save having an #ifdef in event_loop we need to define this empty function */int statserv_must_terminate(void){ return term_flag;}void statserv_remove(IOCHAN pIOChannel){}void statserv_closedown(){ IOCHAN p; if (control_block.bend_stop) (*control_block.bend_stop)(&control_block); for (p = pListener; p; p = p->next) { iochan_destroy(p); }}void sigterm(int sig){ term_flag = 1;}static void *new_session (void *vp);static int no_sessions = 0;/* UNIX listener */static void listener(IOCHAN h, int event){ COMSTACK line = (COMSTACK) iochan_getdata(h); static int hand[2]; static int child = 0; int res; if (event == EVENT_INPUT) { if (control_block.dynamic && !child) { int res; ++no_sessions; if (pipe(hand) < 0) { yaz_log(LOG_FATAL|LOG_ERRNO, "pipe"); iochan_destroy(h); return; } if ((res = fork()) < 0) { yaz_log(LOG_FATAL|LOG_ERRNO, "fork"); iochan_destroy(h); return; } else if (res == 0) /* child */ { char nbuf[100]; IOCHAN pp; close(hand[0]); child = 1; for (pp = pListener; pp; pp = iochan_getnext(pp)) { if (pp != h) { COMSTACK l = (COMSTACK)iochan_getdata(pp); cs_close(l); iochan_destroy(pp); } } sprintf(nbuf, "%s(%d)", me, getpid()); yaz_log_init(control_block.loglevel, nbuf, 0); /* ensure that bend_stop is not called when each child exits - only for the main process .. */ control_block.bend_stop = 0; } else /* parent */ { close(hand[1]); /* wait for child to take the call */ for (;;) { char dummy[1]; int res; if ((res = read(hand[0], dummy, 1)) < 0 && yaz_errno() != EINTR) { yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read"); return; } else if (res >= 0) break; } yaz_log(LOG_DEBUG, "P: Child has taken the call"); close(hand[0]); return; } } if ((res = cs_listen_check(line, 0, 0, control_block.check_ip, control_block.daemon_name)) < 0) { yaz_log(LOG_WARN|LOG_ERRNO, "cs_listen failed"); return; } else if (res == 1) return; yaz_log(LOG_DEBUG, "listen ok"); iochan_setevent(h, EVENT_OUTPUT); iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */ } /* in dynamic mode, only the child ever comes down here */ else if (event == EVENT_OUTPUT) { COMSTACK new_line = cs_accept(line); if (!new_line) { yaz_log(LOG_FATAL, "Accept failed."); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ return; } yaz_log(LOG_DEBUG, "accept ok"); if (control_block.dynamic) { IOCHAN pp; /* close our half of the listener socket */ for (pp = pListener; pp; pp = iochan_getnext(pp)) { COMSTACK l = (COMSTACK)iochan_getdata(pp); cs_close(l); iochan_destroy(pp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -