X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Fstatserv.c;h=6a685b89f2370558d428128939278324d6245051;hp=dd52b6a926f3fa2f5011d5c9715401822da27a17;hb=c519e4716646be3b24f7d4d3de99e06e423da865;hpb=c7753f2a498076aae60b9a4682041c573792d0cd diff --git a/src/statserv.c b/src/statserv.c index dd52b6a..6a685b8 100644 --- a/src/statserv.c +++ b/src/statserv.c @@ -1,26 +1,45 @@ /* - * Copyright (c) 1995-2004, Index Data + * Copyright (C) 1995-2005, Index Data ApS * 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.4 2004-01-17 01:20:13 adam Exp $ + * $Id: statserv.c,v 1.20 2005-02-01 14:46:47 adam Exp $ + */ + +/** + * \file statserv.c + * \brief Implements GFS logic */ #include #include +#include #ifdef WIN32 #include #include #include #include "service.h" -#else +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_WAIT_H +#include +#endif +#if HAVE_UNISTD_H #include +#endif +#if HAVE_PWD_H #include #endif +#if HAVE_XML2 +#include +#include +#endif + #if YAZ_POSIX_THREADS #include #elif YAZ_GNU_THREADS @@ -44,16 +63,30 @@ static IOCHAN pListener = NULL; -static char *me = "statserver"; +static struct gfs_server *gfs_server_list = 0; +static NMEM gfs_nmem = 0; + +static char *me = "statserver"; /* log prefix */ +static char *programname="statserver"; /* full program name */ +#if YAZ_POSIX_THREADS +static pthread_key_t current_control_tls; +static int init_control_tls = 0; +#else +static statserv_options_block *current_control_block = 0; +#endif + /* * default behavior. */ +#define STAT_DEFAULT_LOG_LEVEL "none,fatal,warn,log,server,session,request" +/* the 'none' clears yaz' own default settings, including [log] */ + 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 */ + YLOG_DEFAULT_LEVEL, /* log level */ "", /* no PDUs */ "", /* diagnostic output to stderr */ "tcp:@:9999", /* default listener port */ @@ -79,11 +112,314 @@ statserv_options_block control_block = { #endif /* WIN32 */ 0, /* SOAP handlers */ "", /* PID fname */ - 0 /* background daemon */ + 0, /* background daemon */ + "", /* SSL certificate filename */ + "", /* XML config filename */ }; static int max_sessions = 0; +static int logbits_set = 0; +static int log_session = 0; +static int log_server = 0; + +/** get_logbits sets global loglevel bits */ +static void get_logbits(int force) +{ /* needs to be called after parsing cmd-line args that can set loglevels!*/ + if (force || !logbits_set) + { + logbits_set = 1; + log_session = yaz_log_module_level("session"); + log_server = yaz_log_module_level("server"); + } +} + + +static int add_listener(char *where, int listen_id); + +#if HAVE_XML2 +static xmlDocPtr xml_config_doc = 0; +#endif + +#if HAVE_XML2 +static xmlNodePtr xml_config_get_root() +{ + xmlNodePtr ptr = 0; + if (xml_config_doc) + { + ptr = xmlDocGetRootElement(xml_config_doc); + if (!ptr || ptr->type != XML_ELEMENT_NODE || + strcmp((const char *) ptr->name, "yazgfs")) + { + yaz_log(YLOG_WARN, "Bad/missing root element for config %s", + control_block.xml_config); + return 0; + + } + } + return ptr; +} +#endif + +#if HAVE_XML2 +static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) +{ + unsigned char *cp; + xmlNodePtr p; + int len = 1; /* start with 1, because of trailing 0 */ + char *str; + int first = 1; /* whitespace lead flag .. */ + /* determine length */ + for (p = ptr; p; p = p->next) + { + if (p->type == XML_TEXT_NODE) + len += strlen(p->content); + } + /* now allocate for the string */ + str = nmem_malloc(n, len); + *str = '\0'; /* so we can use strcat */ + for (p = ptr; p; p = p->next) + { + if (p->type == XML_TEXT_NODE) + { + cp = p->content; + if (first) + { + while(*cp && isspace(*cp)) + cp++; + if (*cp) + first = 0; /* reset if we got non-whitespace out */ + } + strcat(str, cp); /* append */ + } + } + /* remove trailing whitespace */ + cp = strlen(str) + str; + while ((char*) cp != str && isspace(cp[-1])) + cp--; + *cp = '\0'; + /* return resulting string */ + return str; +} +#endif + +static struct gfs_server * gfs_server_new() +{ + struct gfs_server *n = nmem_malloc(gfs_nmem, sizeof(*n)); + memcpy(&n->cb, &control_block, sizeof(control_block)); + n->next = 0; + n->host = 0; + n->port = 0; + return n; +} + +int control_association(association *assoc, const char *host, int force_open) +{ + char vhost[128], *cp; + if (host) + { + strncpy(vhost, host, 127); + vhost[127] = '\0'; + cp = strchr(vhost, ':'); + if (cp) + *cp = '\0'; + host = vhost; + } + if (control_block.xml_config[0]) + { + struct gfs_server *gfs; + for (gfs = gfs_server_list; gfs; gfs = gfs->next) + { + int port_match = 0; + int host_match = 0; + if ( !gfs->host || (host && gfs->host && !strcmp(host, gfs->host))) + host_match = 1; + if (assoc->client_chan->port == gfs->port) + port_match= 1; + if (port_match && host_match) + { + if (force_open || + (assoc->last_control != &gfs->cb && assoc->backend)) + { + statserv_setcontrol(assoc->last_control); + if (assoc->backend && assoc->init) + (assoc->last_control->bend_close)(assoc->backend); + assoc->backend = 0; + xfree(assoc->init); + assoc->init = 0; + } + assoc->last_control = &gfs->cb; + statserv_setcontrol(&gfs->cb); + return 1; + } + } + statserv_setcontrol(0); + assoc->last_control = 0; + return 0; + } + else + { + statserv_setcontrol(&control_block); + assoc->last_control = &control_block; + return 1; + } +} + +static void xml_config_read() +{ + struct gfs_server **gfsp = &gfs_server_list; +#if HAVE_XML2 + xmlNodePtr ptr = xml_config_get_root(); + + if (!ptr) + return; + for (ptr = ptr->children; ptr; ptr = ptr->next) + { + if (ptr->type == XML_ELEMENT_NODE && + !strcmp((const char *) ptr->name, "server")) + { + xmlNodePtr ptr_children = ptr->children; + xmlNodePtr ptr; + + *gfsp = gfs_server_new(); + for (ptr = ptr_children; ptr; ptr = ptr->next) + { + if (ptr->type == XML_ELEMENT_NODE && + !strcmp((const char *) ptr->name, "host")) + { + (*gfsp)->host = nmem_dup_xml_content(gfs_nmem, + ptr->children); + } + if (ptr->type == XML_ELEMENT_NODE && + !strcmp((const char *) ptr->name, "port")) + { + (*gfsp)->port = atoi(nmem_dup_xml_content(gfs_nmem, + ptr->children)); + } + if (ptr->type == XML_ELEMENT_NODE && + !strcmp((const char *) ptr->name, "config")) + { + strcpy((*gfsp)->cb.configname, + nmem_dup_xml_content(gfs_nmem, ptr->children)); + } + } + gfsp = &(*gfsp)->next; + } + } +#endif + *gfsp = 0; +} + +static void xml_config_open() +{ + gfs_nmem = nmem_create(); +#if HAVE_XML2 + if (control_block.xml_config[0] == '\0') + return; + + if (!xml_config_doc) + { + xml_config_doc = xmlParseFile(control_block.xml_config); + if (!xml_config_doc) + { + yaz_log(YLOG_WARN, "Could not parse %s", control_block.xml_config); + return ; + } + } + xml_config_read(); + +#endif +} + +static void xml_config_close() +{ +#if HAVE_XML2 + if (xml_config_doc) + { + xmlFreeDoc(xml_config_doc); + xml_config_doc = 0; + } +#endif + gfs_server_list = 0; + nmem_destroy(gfs_nmem); +} + +static void xml_config_add_listeners() +{ +#define MAX_PORTS 200 + struct gfs_server *gfs = gfs_server_list; + int i, ports[MAX_PORTS]; + for (i = 0; inext) + { + int port = gfs->port; + if (port) + { + for (i = 0; inext) + { + yaz_log(YLOG_DEBUG, "xml_config_bend_start config=%s", + gfs->cb.configname); + statserv_setcontrol(&gfs->cb); + if (control_block.bend_start) + (control_block.bend_start)(&gfs->cb); + } + } + else + { + yaz_log(YLOG_DEBUG, "xml_config_bend_start default config"); + statserv_setcontrol(&control_block); + if (control_block.bend_start) + (*control_block.bend_start)(&control_block); + } + +} + +static void xml_config_bend_stop() +{ + if (control_block.xml_config[0]) + { + struct gfs_server *gfs = gfs_server_list; + for (; gfs; gfs = gfs->next) + { + yaz_log(YLOG_DEBUG, "xml_config_bend_stop config=%s", + gfs->cb.configname); + statserv_setcontrol(&gfs->cb); + if (control_block.bend_stop) + (control_block.bend_stop)(&gfs->cb); + } + } + else + { + yaz_log(YLOG_DEBUG, "xml_config_bend_stop default config"); + statserv_setcontrol(&control_block); + if (control_block.bend_stop) + (*control_block.bend_stop)(&control_block); + } +} + /* * handle incoming connect requests. * The dynamic mode is a bit tricky mostly because we want to avoid @@ -145,7 +481,7 @@ void statserv_remove(IOCHAN pIOChannel) ThreadList *pNextThread; ThreadList *pPrevThread =NULL; - /* Step through alll the threads */ + /* Step through all the threads */ for (; pCurrentThread != NULL; pCurrentThread = pNextThread) { /* We only need to compare on the IO Channel */ @@ -233,7 +569,7 @@ void statserv_closedown() /* Now we can really do something */ if (iHandles > 0) { - logf (LOG_LOG, "waiting for %d to die", iHandles); + yaz_log(log_server, "waiting for %d to die", iHandles); /* This will now wait, until all the threads close */ WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE); @@ -241,11 +577,11 @@ void statserv_closedown() free(pThreadHandles); } - if (control_block.bend_stop) - (*control_block.bend_stop)(&control_block); + xml_config_bend_stop(); /* No longer require the critical section, since all threads are dead */ DeleteCriticalSection(&Thread_CritSect); } + xml_config_close(); } void __cdecl event_loop_thread (IOCHAN iochan) @@ -265,12 +601,12 @@ static void listener(IOCHAN h, int event) { if ((res = cs_listen(line, 0, 0)) < 0) { - yaz_log(LOG_FATAL, "cs_listen failed"); + yaz_log(YLOG_FATAL, "cs_listen failed"); return; } else if (res == 1) return; - yaz_log(LOG_DEBUG, "listen ok"); + yaz_log(YLOG_DEBUG, "listen ok"); iochan_setevent(h, EVENT_OUTPUT); iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */ } @@ -282,24 +618,25 @@ static void listener(IOCHAN h, int event) if (!new_line) { - yaz_log(LOG_FATAL, "Accept failed."); + yaz_log(YLOG_FATAL, "Accept failed."); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); return; } - yaz_log(LOG_DEBUG, "Accept ok"); + yaz_log(YLOG_DEBUG, "Accept ok"); if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, EVENT_INPUT))) { - yaz_log(LOG_FATAL, "Failed to create iochan"); + yaz_log(YLOG_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(YLOG_DEBUG, "Creating association"); + if (!(newas = create_association(new_chan, new_line, + control_block.apdu_file))) { - yaz_log(LOG_FATAL, "Failed to create new assoc."); + yaz_log(YLOG_FATAL, "Failed to create new assoc."); iochan_destroy(h); return; } @@ -307,7 +644,7 @@ static void listener(IOCHAN h, int event) newas->cs_put_mask = 0; newas->cs_accept_mask = 0; - yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout); + yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout); iochan_setdata(new_chan, newas); iochan_settimeout(new_chan, 60); @@ -317,19 +654,19 @@ static void listener(IOCHAN h, int event) if (newHandle == (HANDLE) -1) { - yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread."); + yaz_log(YLOG_FATAL|YLOG_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); + yaz_log(YLOG_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."); + yaz_log(YLOG_FATAL, "Bad event on listener."); iochan_destroy(h); return; } @@ -359,12 +696,15 @@ void statserv_closedown() { IOCHAN p; - if (control_block.bend_stop) - (*control_block.bend_stop)(&control_block); + xml_config_bend_stop(); for (p = pListener; p; p = p->next) { iochan_destroy(p); } + xml_config_close(); +#if YAZ_POSIX_THREADS + pthread_key_delete(current_control_tls); +#endif } void sigterm(int sig) @@ -379,26 +719,38 @@ static int no_sessions = 0; 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) + COMSTACK new_line; + if ((res = cs_listen_check(line, 0, 0, control_block.check_ip, + control_block.daemon_name)) < 0) { - int res; + yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed"); + return; + } + else if (res == 1) + { + yaz_log(YLOG_WARN, "cs_listen incomplete"); + return; + } + new_line = cs_accept(line); + if (!new_line) + { + yaz_log(YLOG_FATAL, "Accept failed."); + iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ + return; + } - ++no_sessions; - if (pipe(hand) < 0) - { - yaz_log(LOG_FATAL|LOG_ERRNO, "pipe"); - iochan_destroy(h); - return; - } + yaz_log(log_session, "Connect from %s", cs_addrstr(new_line)); + + no_sessions++; + if (control_block.dynamic) + { if ((res = fork()) < 0) { - yaz_log(LOG_FATAL|LOG_ERRNO, "fork"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork"); iochan_destroy(h); return; } @@ -407,102 +759,32 @@ static void listener(IOCHAN h, int event) 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); - } + COMSTACK l = (COMSTACK)iochan_getdata(pp); + cs_close(l); + iochan_destroy(pp); } - sprintf(nbuf, "%s(%d)", me, getpid()); + sprintf(nbuf, "%s(%d)", me, no_sessions); yaz_log_init(control_block.loglevel, nbuf, 0); /* ensure that bend_stop is not called when each child exits - - only for the main process .. - */ + 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]); + cs_close(new_line); 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); - } - /* release dad */ - yaz_log(LOG_DEBUG, "Releasing parent"); - close(hand[1]); - } - else - { - iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ - ++no_sessions; - } -#if YAZ_POSIX_THREADS if (control_block.threads) { +#if YAZ_POSIX_THREADS pthread_t child_thread; pthread_create (&child_thread, 0, new_session, new_line); pthread_detach (child_thread); - } - else - new_session(new_line); #elif YAZ_GNU_THREADS - if (control_block.threads) - { pth_attr_t attr; pth_t child_thread; @@ -510,25 +792,25 @@ static void listener(IOCHAN h, int event) pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE); pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024); pth_attr_set (attr, PTH_ATTR_NAME, "session"); - yaz_log (LOG_LOG, "pth_spawn begin"); + yaz_log (YLOG_DEBUG, "pth_spawn begin"); child_thread = pth_spawn (attr, new_session, new_line); - yaz_log (LOG_LOG, "pth_spawn finish"); + yaz_log (YLOG_DEBUG, "pth_spawn finish"); pth_attr_destroy (attr); +#else + new_session(new_line); +#endif } else new_session(new_line); -#else - new_session(new_line); -#endif } else if (event == EVENT_TIMEOUT) { - yaz_log(LOG_LOG, "Shutting down listener."); + yaz_log(log_server, "Shutting down listener."); iochan_destroy(h); } else { - yaz_log(LOG_FATAL, "Bad event on listener."); + yaz_log(YLOG_FATAL, "Bad event on listener."); iochan_destroy(h); } } @@ -539,12 +821,13 @@ static void *new_session (void *vp) association *newas; IOCHAN new_chan; COMSTACK new_line = (COMSTACK) vp; + IOCHAN parent_chan = new_line->user; unsigned cs_get_mask, cs_accept_mask, mask = ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) | ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0); - if (mask) + if (mask) { cs_accept_mask = mask; /* accept didn't complete */ cs_get_mask = 0; @@ -555,14 +838,16 @@ static void *new_session (void *vp) cs_get_mask = mask = EVENT_INPUT; } - if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask))) + if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask, + parent_chan->port))) { - yaz_log(LOG_FATAL, "Failed to create iochan"); + yaz_log(YLOG_FATAL, "Failed to create iochan"); return 0; } - if (!(newas = create_association(new_chan, new_line))) + if (!(newas = create_association(new_chan, new_line, + control_block.apdufile))) { - yaz_log(LOG_FATAL, "Failed to create new assoc."); + yaz_log(YLOG_FATAL, "Failed to create new assoc."); return 0; } newas->cs_accept_mask = cs_accept_mask; @@ -575,9 +860,9 @@ static void *new_session (void *vp) #else a = 0; #endif - yaz_log(LOG_LOG, "Starting session %d from %s", - no_sessions, a ? a : "[Unknown]"); - if (max_sessions && no_sessions == max_sessions) + yaz_log(log_session, "Starting session %d from %s (pid=%ld)", + no_sessions, a ? a : "[Unknown]", (long) getpid()); + if (max_sessions && no_sessions >= max_sessions) control_block.one_shot = 1; if (control_block.threads) { @@ -603,39 +888,41 @@ static void inetd_connection(int what) if ((line = cs_createbysocket(0, tcpip_type, 0, what))) { - if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT))) + if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT, + 0))) { - if ((assoc = create_association(chan, line))) + if ((assoc = create_association(chan, line, + control_block.apdufile))) { iochan_setdata(chan, assoc); iochan_settimeout(chan, 60); addr = cs_addrstr(line); - yaz_log(LOG_LOG, "Inetd association from %s", + yaz_log(log_session, "Inetd association from %s", addr ? addr : "[UNKNOWN]"); assoc->cs_get_mask = EVENT_INPUT; } else { - yaz_log(LOG_FATAL, "Failed to create association structure"); + yaz_log(YLOG_FATAL, "Failed to create association structure"); } chan->next = pListener; pListener = chan; } else { - yaz_log(LOG_FATAL, "Failed to create iochan"); + yaz_log(YLOG_FATAL, "Failed to create iochan"); } } else { - yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0"); + yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0"); } } /* * Set up a listening endpoint, and give it to the event-handler. */ -static int add_listener(char *where, int what) +static int add_listener(char *where, int listen_id) { COMSTACK l; void *ap; @@ -649,31 +936,35 @@ static int add_listener(char *where, int what) else mode = "static"; - yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode, - what == PROTO_SR ? "SR" : "Z3950", where); + yaz_log(log_server, "Adding %s listener on %s id=%d", mode, where, + listen_id); l = cs_create_host(where, 2, &ap); if (!l) { - yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where); + yaz_log(YLOG_FATAL, "Failed to listen on %s", where); return -1; } + if (*control_block.cert_fname) + cs_set_ssl_certificate_file(l, control_block.cert_fname); + if (cs_bind(l, ap, CS_SERVER) < 0) { - yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where); cs_close (l); return -1; } if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT | - EVENT_EXCEPT))) + EVENT_EXCEPT, listen_id))) { - yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type"); cs_close (l); return -1; } - iochan_setdata(lst, l); + iochan_setdata(lst, l); /* user-defined data for listener is COMSTACK */ + l->user = lst; /* user-defined data for COMSTACK is listener chan */ - /* Ensure our listener chain is setup properly */ + /* Add listener to chain */ lst->next = pListener; pListener = lst; return 0; /* OK */ @@ -691,15 +982,24 @@ static void catchchld(int num) statserv_options_block *statserv_getcontrol(void) { - static statserv_options_block cb; - - memcpy(&cb, &control_block, sizeof(cb)); - return &cb; +#if YAZ_POSIX_THREADS + if (init_control_tls) + return pthread_getspecific(current_control_tls); + else + return &control_block; +#else + return current_control_block; +#endif } void statserv_setcontrol(statserv_options_block *block) { - memcpy(&control_block, block, sizeof(*block)); +#if YAZ_POSIX_THREADS + if (init_control_tls) + pthread_setspecific(current_control_tls, block); +#else + current_control_block = block; +#endif } static void statserv_reset(void) @@ -708,44 +1008,97 @@ static void statserv_reset(void) int statserv_start(int argc, char **argv) { - int ret = 0; - + char sep; #ifdef WIN32 /* We need to initialize the thread list */ ThreadList_Initialize(); /* WIN32 */ #endif - + + #ifdef WIN32 - if ((me = strrchr (argv[0], '\\'))) - me++; - else - me = argv[0]; + sep = '\\'; #else - me = argv[0]; + sep = '/'; #endif + if ((me = strrchr (argv[0], sep))) + me++; /* get the basename */ + else + me = argv[0]; + programname = argv[0]; + if (control_block.options_func(argc, argv)) - return(1); + return 1; + +#if YAZ_POSIX_THREADS + init_control_tls = 1; + pthread_key_create(¤t_control_tls, 0); +#endif - if (control_block.bend_start) - (*control_block.bend_start)(&control_block); + xml_config_open(); + + xml_config_bend_start(); + #ifdef WIN32 - yaz_log (LOG_LOG, "Starting server %s", me); + xml_config_add_listeners(); + + yaz_log (log_server, "Starting server %s", me); if (!pListener && *control_block.default_listen) - add_listener(control_block.default_listen, - control_block.default_proto); - - if (!pListener) - return 1; + add_listener(control_block.default_listen, 0); #else /* UNIX */ if (control_block.inetd) inetd_connection(control_block.default_proto); else { + static int hand[2]; + if (control_block.background) + { + /* create pipe so that parent waits until child has created + PID (or failed) */ + if (pipe(hand) < 0) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe"); + return 1; + } + switch (fork()) + { + case 0: + break; + case -1: + return 1; + default: + close(hand[1]); + while(1) + { + char dummy[1]; + int res = read(hand[0], dummy, 1); + if (res < 0 && yaz_errno() != EINTR) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake"); + break; + } + else if (res >= 0) + break; + } + close(hand[0]); + _exit(0); + } + /* child */ + close(hand[0]); + if (setsid() < 0) + return 1; + + close(0); + close(1); + close(2); + open("/dev/null", O_RDWR); + dup(0); dup(0); + } + xml_config_add_listeners(); + if (!pListener && *control_block.default_listen) - add_listener(control_block.default_listen, - control_block.default_proto); + add_listener(control_block.default_listen, 0); if (!pListener) return 1; @@ -755,15 +1108,20 @@ int statserv_start(int argc, char **argv) FILE *f = fopen(control_block.pid_fname, "w"); if (!f) { - yaz_log(LOG_FATAL|LOG_ERRNO, "Couldn't create %s", + yaz_log(YLOG_FATAL|YLOG_ERRNO, "Couldn't create %s", control_block.pid_fname); exit(0); } fprintf(f, "%ld", (long) getpid()); fclose(f); } + + if (control_block.background) + close(hand[1]); - yaz_log (LOG_LOG, "Starting server %s pid=%d", me, getpid()); + + yaz_log (log_server, "Starting server %s pid=%ld", programname, + (long) getpid()); #if 0 sigset_t sigs_to_block; @@ -783,50 +1141,27 @@ int statserv_start(int argc, char **argv) if (!(pw = getpwnam(control_block.setuid))) { - yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid); + yaz_log(YLOG_FATAL, "%s: Unknown user", control_block.setuid); return(1); } if (setuid(pw->pw_uid) < 0) { - yaz_log(LOG_FATAL|LOG_ERRNO, "setuid"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid"); exit(1); } } - if (!control_block.inetd && control_block.background) - { - switch (fork()) - { - case 0: - break; - case -1: - return 1; - default: - _exit(0); - } - - if (setsid() < 0) - return 1; - - close(0); - close(1); - close(2); - open("/dev/null",O_RDWR); - dup(0); dup(0); - } /* UNIX */ #endif - if ((pListener == NULL) && *control_block.default_listen) - add_listener(control_block.default_listen, - control_block.default_proto); - if (pListener == NULL) - ret = 1; - else - { - yaz_log(LOG_LOG, "Entering event loop."); - ret = event_loop(&pListener); - } - return ret; + return 1; + yaz_log(YLOG_DEBUG, "Entering event loop."); + return event_loop(&pListener); +} + +static void option_copy(char *dst, const char *src) +{ + strncpy(dst, src ? src : "", 127); + dst[127] = '\0'; } int check_options(int argc, char **argv) @@ -834,12 +1169,17 @@ int check_options(int argc, char **argv) int ret = 0, r; char *arg; - while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:D", argv, argc, &arg)) != -2) + /* set default log level */ + control_block.loglevel = yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL); + yaz_log_init_level(control_block.loglevel); + + while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:DC:f:", + argv, argc, &arg)) != -2) { switch (ret) { case 0: - if (add_listener(arg, control_block.default_proto)) + if (add_listener(arg, 0)) return 1; /* failed to create listener */ break; case '1': @@ -869,24 +1209,28 @@ int check_options(int argc, char **argv) #endif break; case 'l': - strcpy(control_block.logfile, arg ? arg : ""); + option_copy(control_block.logfile, arg); yaz_log_init(control_block.loglevel, me, control_block.logfile); break; case 'v': - control_block.loglevel = yaz_log_mask_str(arg); + control_block.loglevel = + yaz_log_mask_str_x(arg,control_block.loglevel); yaz_log_init(control_block.loglevel, me, control_block.logfile); break; case 'a': - strcpy(control_block.apdufile, arg ? arg : ""); + option_copy(control_block.apdufile, arg); break; case 'u': - strcpy(control_block.setuid, arg ? arg : ""); + option_copy(control_block.setuid, arg); break; case 'c': - strcpy(control_block.configname, arg ? arg : ""); + option_copy(control_block.configname, arg); + break; + case 'C': + option_copy(control_block.cert_fname, arg); break; case 'd': - strcpy(control_block.daemon_name, arg ? arg : ""); + option_copy(control_block.daemon_name, arg); break; case 't': if (!arg || !(r = atoi(arg))) @@ -918,12 +1262,15 @@ int check_options(int argc, char **argv) max_sessions = atoi(arg); break; case 'p': - if (strlen(arg) >= sizeof(control_block.pid_fname)) - { - yaz_log(LOG_FATAL, "pid fname too long"); - exit(1); - } - strcpy(control_block.pid_fname, arg); + option_copy(control_block.pid_fname, arg); + break; + case 'f': +#if HAVE_XML2 + option_copy(control_block.xml_config, arg); +#else + fprintf(stderr, "%s: Option -f unsupported since YAZ is compiled without Libxml2 support\n", me); + exit(1); +#endif break; case 'D': control_block.background = 1; @@ -931,11 +1278,12 @@ int check_options(int argc, char **argv) default: fprintf(stderr, "Usage: %s [ -a -v " " -l -u -c -t " - " -k -d -p " + " -k -d -p -C certfile" " -ziDST1 -w ... ]\n", me); return 1; } } + get_logbits(1); return 0; } @@ -958,12 +1306,8 @@ int statserv_main(int argc, char **argv, bend_initresult *(*bend_init)(bend_initrequest *r), void (*bend_close)(void *handle)) { - statserv_options_block *cb = statserv_getcontrol(); - - cb->bend_init = bend_init; - cb->bend_close = bend_close; - - statserv_setcontrol(cb); + control_block.bend_init = bend_init; + control_block.bend_close = bend_close; /* Lets setup the Arg structure */ ArgDetails.argc = argc; @@ -1005,12 +1349,10 @@ int statserv_main(int argc, char **argv, void (*bend_close)(void *handle)) { int ret; - statserv_options_block *cb = statserv_getcontrol(); - - cb->bend_init = bend_init; - cb->bend_close = bend_close; - statserv_setcontrol(cb); + control_block.bend_init = bend_init; + control_block.bend_close = bend_close; + ret = statserv_start (argc, argv); statserv_closedown (); statserv_reset();