From c519e4716646be3b24f7d4d3de99e06e423da865 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Tue, 1 Feb 2005 14:46:47 +0000 Subject: [PATCH] Added support for multi-homed YAZ frontend server. A backend config may be choosed based on host and/or port. For Z39.50 host is picked from OtherInfo in InitRequest (VAL_PROXY OID). For HTTP, host is picked from HTTP header. Configuration is XML based . Libxml2 is required. --- include/yaz/backend.h | 3 +- src/eventl.c | 5 +- src/eventl.h | 5 +- src/seshigh.c | 208 +++++++++++---------- src/session.h | 22 ++- src/statserv.c | 483 +++++++++++++++++++++++++++++++++++++++++-------- ztest/ztest.c | 20 +- 7 files changed, 556 insertions(+), 190 deletions(-) diff --git a/include/yaz/backend.h b/include/yaz/backend.h index 51a9324..c0f75da 100644 --- a/include/yaz/backend.h +++ b/include/yaz/backend.h @@ -23,7 +23,7 @@ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. * - * $Id: backend.h,v 1.28 2005-01-15 19:47:09 adam Exp $ + * $Id: backend.h,v 1.29 2005-02-01 14:46:47 adam Exp $ */ /** @@ -286,6 +286,7 @@ typedef struct statserv_options_block char pid_fname[128]; /* pid fname */ int background; /* auto daemon */ char cert_fname[128]; /* SSL certificate fname */ + char xml_config[128]; /* XML config filename */ } statserv_options_block; YAZ_EXPORT int statserv_main( diff --git a/src/eventl.c b/src/eventl.c index 1efe41f..649e6de 100644 --- a/src/eventl.c +++ b/src/eventl.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: eventl.c,v 1.6 2005-01-17 12:53:04 adam Exp $ + * $Id: eventl.c,v 1.7 2005-02-01 14:46:47 adam Exp $ */ /** @@ -55,7 +55,7 @@ static int log_level=0; static int log_level_initialized=0; -IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags) +IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int port) { IOCHAN new_iochan; @@ -74,6 +74,7 @@ IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags) new_iochan->force_event = 0; new_iochan->last_event = new_iochan->max_idle = 0; new_iochan->next = NULL; + new_iochan->port = port; return new_iochan; } diff --git a/src/eventl.h b/src/eventl.h index 84338a9..b3d663b 100644 --- a/src/eventl.h +++ b/src/eventl.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: eventl.h,v 1.3 2005-01-15 19:47:13 adam Exp $ + * $Id: eventl.h,v 1.4 2005-02-01 14:46:47 adam Exp $ */ /** @@ -39,6 +39,7 @@ int force_event; time_t max_idle; struct iochan *next; + int port; /* listening port (0 if none ) */ } *IOCHAN; #define iochan_destroy(i) (void)((i)->destroyed = 1) @@ -57,7 +58,7 @@ int force_event; #define iochan_getnext(i) ((i)->next) #define iochan_settimeout(i, t) ((i)->max_idle = (t), (i)->last_event = time(0)) -IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags); +IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int port); int event_loop(IOCHAN *iochans); void statserv_remove (IOCHAN pIOChannel); #endif diff --git a/src/seshigh.c b/src/seshigh.c index 64eeae1..70581d4 100644 --- a/src/seshigh.c +++ b/src/seshigh.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: seshigh.c,v 1.46 2005-01-19 09:18:08 adam Exp $ + * $Id: seshigh.c,v 1.47 2005-02-01 14:46:47 adam Exp $ */ /** * \file seshigh.c @@ -91,9 +91,6 @@ static Z_APDU *process_deleteRequest(association *assoc, request *reqb, int *fd); static Z_APDU *process_segmentRequest (association *assoc, request *reqb); -static FILE *apduf = 0; /* for use in static mode */ -static statserv_options_block *control_block = 0; - static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd); /* dynamic logging levels */ @@ -107,10 +104,10 @@ static void get_logbits() { /* needs to be called after parsing cmd-line args that can set loglevels!*/ if (!logbits_set) { - logbits_set=1; - log_session=yaz_log_module_level("session"); - log_request=yaz_log_module_level("request"); - log_requestdetail=yaz_log_module_level("requestdetail"); + logbits_set = 1; + log_session = yaz_log_module_level("session"); + log_request = yaz_log_module_level("request"); + log_requestdetail = yaz_log_module_level("requestdetail"); } } @@ -128,18 +125,18 @@ static void wr_diag(WRBUF w, int error, const char *addinfo) * link : communications channel. * Returns: 0 or a new association handle. */ -association *create_association(IOCHAN channel, COMSTACK link) +association *create_association(IOCHAN channel, COMSTACK link, + const char *apdufile) { association *anew; if (!logbits_set) get_logbits(); - if (!control_block) - control_block = statserv_getcontrol(); if (!(anew = (association *)xmalloc(sizeof(*anew)))) return 0; anew->init = 0; anew->version = 0; + anew->last_control = 0; anew->client_chan = channel; anew->client_link = link; anew->cs_get_mask = 0; @@ -148,44 +145,26 @@ association *create_association(IOCHAN channel, COMSTACK link) if (!(anew->decode = odr_createmem(ODR_DECODE)) || !(anew->encode = odr_createmem(ODR_ENCODE))) return 0; - if (*control_block->apdufile) + if (apdufile && *apdufile) { - char filename[256]; FILE *f; - strcpy(filename, control_block->apdufile); if (!(anew->print = odr_createmem(ODR_PRINT))) return 0; - if (*control_block->apdufile == '@') + if (*apdufile == '@') { odr_setprint(anew->print, yaz_log_file()); } - else if (*control_block->apdufile != '-') + else if (*apdufile != '-') { - strcpy(filename, control_block->apdufile); - if (!control_block->dynamic) - { - if (!apduf) - { - if (!(apduf = fopen(filename, "w"))) - { - yaz_log(YLOG_WARN|YLOG_ERRNO, "can't open apdu dump %s", filename); - return 0; - } - setvbuf(apduf, 0, _IONBF, 0); - } - f = apduf; - } - else - { - sprintf(filename + strlen(filename), ".%ld", (long)getpid()); - if (!(f = fopen(filename, "w"))) - { - yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename); - return 0; - } - setvbuf(f, 0, _IONBF, 0); - } + char filename[256]; + sprintf(filename, "%.200s.%ld", apdufile, (long)getpid()); + if (!(f = fopen(filename, "w"))) + { + yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename); + return 0; + } + setvbuf(f, 0, _IONBF, 0); odr_setprint(anew->print, f); } } @@ -226,7 +205,7 @@ void destroy_association(association *h) request_delq(&h->outgoing); xfree(h); xmalloc_trav("session closed"); - if (control_block && control_block->one_shot) + if (cb && cb->one_shot) { exit (0); } @@ -464,6 +443,7 @@ void ir_session(IOCHAN h, int event) static int process_z_request(association *assoc, request *req, char **msg); + static void assoc_init_reset(association *assoc) { xfree (assoc->init); @@ -493,37 +473,46 @@ static void assoc_init_reset(association *assoc) assoc->init->decode = assoc->decode; assoc->init->peer_name = odr_strdup (assoc->encode, cs_addrstr(assoc->client_link)); + + yaz_log(log_requestdetail, "peer %s", assoc->init->peer_name); + + } static int srw_bend_init(association *assoc, Z_SRW_diagnostic **d, int *num) { - const char *encoding = "UTF-8"; - Z_External *ce; - bend_initresult *binitres; statserv_options_block *cb = statserv_getcontrol(); - - assoc_init_reset(assoc); + if (!assoc->init) + { + const char *encoding = "UTF-8"; + Z_External *ce; + bend_initresult *binitres; - assoc->maximumRecordSize = 3000000; - assoc->preferredMessageSize = 3000000; + yaz_log(YLOG_LOG, "srw_bend_init config=%s", cb->configname); + assoc_init_reset(assoc); + + assoc->maximumRecordSize = 3000000; + assoc->preferredMessageSize = 3000000; #if 1 - ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1); - assoc->init->charneg_request = ce->u.charNeg3; + ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1); + assoc->init->charneg_request = ce->u.charNeg3; #endif - assoc->backend = 0; - if (!(binitres = (*cb->bend_init)(assoc->init))) - { - assoc->state = ASSOC_DEAD; - yaz_add_srw_diagnostic(assoc->encode, d, num, 3, 0); - return 0; - } - assoc->backend = binitres->handle; - if (binitres->errcode) - { - assoc->state = ASSOC_DEAD; - yaz_add_srw_diagnostic(assoc->encode, d, num, binitres->errcode, - binitres->errstring); - return 0; + assoc->backend = 0; + if (!(binitres = (*cb->bend_init)(assoc->init))) + { + assoc->state = ASSOC_DEAD; + yaz_add_srw_diagnostic(assoc->encode, d, num, 3, 0); + return 0; + } + assoc->backend = binitres->handle; + if (binitres->errcode) + { + assoc->state = ASSOC_DEAD; + yaz_add_srw_diagnostic(assoc->encode, d, num, binitres->errcode, + binitres->errstring); + return 0; + } + return 1; } return 1; } @@ -644,8 +633,7 @@ static void srw_bend_search(association *assoc, request *req, *http_code = 200; yaz_log(log_requestdetail, "Got SRW SearchRetrieveRequest"); - if (!assoc->init) - srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); + srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); if (srw_req->sort_type != Z_SRW_sort_type_none) yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics, &srw_res->num_diagnostics, 80, 0); @@ -839,8 +827,7 @@ static void srw_bend_explain(association *assoc, request *req, { yaz_log(log_requestdetail, "Got SRW ExplainRequest"); *http_code = 404; - if (!assoc->init) - srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); + srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); if (assoc->init && assoc->init->bend_explain) { bend_explain_rr rr; @@ -876,8 +863,7 @@ static void srw_bend_scan(association *assoc, request *req, yaz_log(log_requestdetail, "Got SRW ScanRequest"); *http_code = 200; - if (!assoc->init) - srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); + srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics); if (srw_res->num_diagnostics == 0 && assoc->init) { struct scan_entry *save_entries; @@ -1046,8 +1032,14 @@ static void process_http_request(association *assoc, request *req) char *stylesheet = 0; Z_SRW_diagnostic *diagnostic = 0; int num_diagnostic = 0; + const char *host = z_HTTP_header_lookup(hreq->headers, "Host"); - if (!strcmp(hreq->path, "/test")) + if (!control_association(assoc, host, 0)) + { + p = z_get_HTTP_Response(o, 404); + r = 1; + } + if (r == 2 && !strcmp(hreq->path, "/test")) { p = z_get_HTTP_Response(o, 200); hres = p->u.HTTP_Response; @@ -1236,8 +1228,6 @@ static int process_z_request(association *assoc, request *req, char **msg) switch (req->apdu_request->which) { case Z_APDU_initRequest: - iochan_settimeout(assoc->client_chan, - statserv_getcontrol()->idle_timeout * 60); res = process_initRequest(assoc, req); break; case Z_APDU_searchRequest: res = process_searchRequest(assoc, req, &fd); break; @@ -1317,7 +1307,7 @@ static int process_z_request(association *assoc, request *req, char **msg) yaz_log(YLOG_DEBUG, " establishing handler for result"); req->state = REQUEST_PENDING; - if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT))) + if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT, 0))) abort(); iochan_setdata(chan, assoc); retval = 0; @@ -1419,6 +1409,10 @@ static int process_z_response(association *assoc, request *req, Z_APDU *res) return process_gdu_response(assoc, req, gres); } +static char *get_vhost(Z_OtherInformation *otherInfo) +{ + return yaz_oi_get_string_oidval(&otherInfo, VAL_PROXY, 1, 0); +} /* * Handle init request. @@ -1429,22 +1423,28 @@ static int process_z_response(association *assoc, request *req, Z_APDU *res) */ static Z_APDU *process_initRequest(association *assoc, request *reqb) { - statserv_options_block *cb = statserv_getcontrol(); Z_InitRequest *req = reqb->apdu_request->u.initRequest; Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse); Z_InitResponse *resp = apdu->u.initResponse; bend_initresult *binitres; char *version; char options[140]; + statserv_options_block *cb = 0; /* by default no control for backend */ + if (control_association(assoc, get_vhost(req->otherInfo), 1)) + cb = statserv_getcontrol(); /* got control block for backend */ + yaz_log(log_requestdetail, "Got initRequest"); if (req->implementationId) - yaz_log(log_requestdetail, "Id: %s", req->implementationId); + yaz_log(log_requestdetail, "Id: %s", + req->implementationId); if (req->implementationName) - yaz_log(log_requestdetail, "Name: %s", req->implementationName); + yaz_log(log_requestdetail, "Name: %s", + req->implementationName); if (req->implementationVersion) - yaz_log(log_requestdetail, "Version: %s", req->implementationVersion); - + yaz_log(log_requestdetail, "Version: %s", + req->implementationVersion); + assoc_init_reset(assoc); assoc->init->auth = req->idAuthentication; @@ -1460,27 +1460,44 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb) } assoc->backend = 0; - if (!(binitres = (*cb->bend_init)(assoc->init))) + if (cb) { - yaz_log(YLOG_WARN, "Bad response from backend."); - return 0; + if (req->implementationVersion) + yaz_log(log_requestdetail, "Config: %s", + cb->configname); + + iochan_settimeout(assoc->client_chan, cb->idle_timeout * 60); + + /* we have a backend control block, so call that init function */ + if (!(binitres = (*cb->bend_init)(assoc->init))) + { + yaz_log(YLOG_WARN, "Bad response from backend."); + return 0; + } + assoc->backend = binitres->handle; + } + else + { + /* no backend. return error */ + binitres = odr_malloc(assoc->encode, sizeof(*binitres)); + binitres->errstring = 0; + binitres->errcode = 1; + iochan_settimeout(assoc->client_chan, 10); } - - assoc->backend = binitres->handle; if ((assoc->init->bend_sort)) - yaz_log (YLOG_DEBUG, "Sort handler installed"); + yaz_log (YLOG_DEBUG, "Sort handler installed"); if ((assoc->init->bend_search)) - yaz_log (YLOG_DEBUG, "Search handler installed"); + yaz_log (YLOG_DEBUG, "Search handler installed"); if ((assoc->init->bend_present)) - yaz_log (YLOG_DEBUG, "Present handler installed"); + yaz_log (YLOG_DEBUG, "Present handler installed"); if ((assoc->init->bend_esrequest)) - yaz_log (YLOG_DEBUG, "ESRequest handler installed"); + yaz_log (YLOG_DEBUG, "ESRequest handler installed"); if ((assoc->init->bend_delete)) - yaz_log (YLOG_DEBUG, "Delete handler installed"); + yaz_log (YLOG_DEBUG, "Delete handler installed"); if ((assoc->init->bend_scan)) - yaz_log (YLOG_DEBUG, "Scan handler installed"); + yaz_log (YLOG_DEBUG, "Scan handler installed"); if ((assoc->init->bend_segment)) - yaz_log (YLOG_DEBUG, "Segment handler installed"); + yaz_log (YLOG_DEBUG, "Segment handler installed"); resp->referenceId = req->referenceId; *options = '\0'; @@ -1567,8 +1584,9 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb) yaz_log(log_requestdetail, "Negotiated to v%d: %s", assoc->version, options); assoc->maximumRecordSize = *req->maximumRecordSize; - if (assoc->maximumRecordSize > control_block->maxrecordsize) - assoc->maximumRecordSize = control_block->maxrecordsize; + + if (cb && assoc->maximumRecordSize > cb->maxrecordsize) + assoc->maximumRecordSize = cb->maxrecordsize; assoc->preferredMessageSize = *req->preferredMessageSize; if (assoc->preferredMessageSize > assoc->maximumRecordSize) assoc->preferredMessageSize = assoc->maximumRecordSize; @@ -1584,7 +1602,7 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb) assoc->init->implementation_name, odr_prepend(assoc->encode, "GFS", resp->implementationName)); - version = odr_strdup(assoc->encode, "$Revision: 1.46 $"); + version = odr_strdup(assoc->encode, "$Revision: 1.47 $"); if (strlen(version) > 10) /* check for unexpanded CVS strings */ version[strlen(version)-2] = '\0'; resp->implementationVersion = odr_prepend(assoc->encode, @@ -2616,7 +2634,7 @@ static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd) resp->diagnostics = diagRecs->diagRecs; if (log_request) { - WRBUF wr=wrbuf_alloc(); + WRBUF wr = wrbuf_alloc(); wrbuf_diags(wr, resp->num_diagnostics, resp->diagnostics); yaz_log(log_request, "EsRequest %s", wrbuf_buf(wr) ); wrbuf_free(wr, 1); diff --git a/src/session.h b/src/session.h index 77919fb..69402ab 100644 --- a/src/session.h +++ b/src/session.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: session.h,v 1.4 2005-01-16 21:51:50 adam Exp $ + * $Id: session.h,v 1.5 2005-02-01 14:46:47 adam Exp $ */ /** * \file session.h @@ -15,12 +15,20 @@ #include #include #include +#include #include "eventl.h" +struct gfs_server { + statserv_options_block cb; + char *host; + int port; + struct gfs_server *next; +}; + typedef enum { - REQUEST_IDLE, /* the request is just sitting in the queue */ - REQUEST_PENDING /* operation pending (b'end processing or network I/O*/ - /* this list will have more elements when acc/res control is added */ + REQUEST_IDLE, /* the request is just sitting in the queue */ + REQUEST_PENDING /* operation pending (b'end processing or network I/O*/ + /* this list will have more elements when acc/res control is added */ } request_state; typedef struct request @@ -88,9 +96,11 @@ typedef struct association unsigned cs_accept_mask; struct bend_initrequest *init; + statserv_options_block *last_control; } association; -association *create_association(IOCHAN channel, COMSTACK link); +association *create_association(IOCHAN channel, COMSTACK link, + const char *apdufile); void destroy_association(association *h); void ir_session(IOCHAN h, int event); @@ -105,4 +115,6 @@ void request_release(request *r); int statserv_must_terminate(void); +int control_association(association *assoc, const char *host, int force); + #endif diff --git a/src/statserv.c b/src/statserv.c index 17f27b7..6a685b8 100644 --- a/src/statserv.c +++ b/src/statserv.c @@ -5,7 +5,7 @@ * NT threaded server code by * Chas Woodfield, Fretwell Downing Informatics. * - * $Id: statserv.c,v 1.19 2005-01-16 21:51:50 adam Exp $ + * $Id: statserv.c,v 1.20 2005-02-01 14:46:47 adam Exp $ */ /** @@ -15,6 +15,7 @@ #include #include +#include #ifdef WIN32 #include #include @@ -34,6 +35,11 @@ #include #endif +#if HAVE_XML2 +#include +#include +#endif + #if YAZ_POSIX_THREADS #include #elif YAZ_GNU_THREADS @@ -57,8 +63,18 @@ static IOCHAN pListener = NULL; +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. */ @@ -97,7 +113,8 @@ statserv_options_block control_block = { 0, /* SOAP handlers */ "", /* PID fname */ 0, /* background daemon */ - "" /* SSL certificate filename */ + "", /* SSL certificate filename */ + "", /* XML config filename */ }; static int max_sessions = 0; @@ -118,6 +135,291 @@ static void get_logbits(int force) } +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 @@ -179,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 */ @@ -267,7 +569,7 @@ void statserv_closedown() /* Now we can really do something */ if (iHandles > 0) { - logf (log_server, "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); @@ -275,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) @@ -331,7 +633,8 @@ static void listener(IOCHAN h, int event) } yaz_log(YLOG_DEBUG, "Creating association"); - if (!(newas = create_association(new_chan, new_line))) + if (!(newas = create_association(new_chan, new_line, + control_block.apdu_file))) { yaz_log(YLOG_FATAL, "Failed to create new assoc."); iochan_destroy(h); @@ -393,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) @@ -436,6 +742,9 @@ static void listener(IOCHAN h, int event) iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ return; } + + yaz_log(log_session, "Connect from %s", cs_addrstr(new_line)); + no_sessions++; if (control_block.dynamic) { @@ -468,6 +777,7 @@ static void listener(IOCHAN h, int event) return; } } + if (control_block.threads) { #if YAZ_POSIX_THREADS @@ -511,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; @@ -527,12 +838,14 @@ 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(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(YLOG_FATAL, "Failed to create new assoc."); return 0; @@ -575,9 +888,11 @@ 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); @@ -607,7 +922,7 @@ static void inetd_connection(int what) /* * 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; @@ -621,8 +936,8 @@ static int add_listener(char *where, int what) else mode = "static"; - yaz_log(log_server, "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) @@ -640,15 +955,16 @@ static int add_listener(char *where, int what) return -1; } if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT | - EVENT_EXCEPT))) + EVENT_EXCEPT, listen_id))) { 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 */ @@ -666,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) @@ -683,14 +1008,14 @@ 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 sep = '\\'; #else @@ -703,18 +1028,23 @@ int statserv_start(int argc, char **argv) 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 + 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) @@ -765,9 +1095,10 @@ int statserv_start(int argc, char **argv) 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; @@ -784,13 +1115,13 @@ int statserv_start(int argc, char **argv) fprintf(f, "%ld", (long) getpid()); fclose(f); } - + if (control_block.background) close(hand[1]); - yaz_log (log_server, "Starting server %s pid=%ld", programname, - (long) getpid()); - + + yaz_log (log_server, "Starting server %s pid=%ld", programname, + (long) getpid()); #if 0 sigset_t sigs_to_block; @@ -821,18 +1152,16 @@ int statserv_start(int argc, char **argv) } /* 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(YLOG_DEBUG, "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) @@ -844,13 +1173,13 @@ int check_options(int argc, char **argv) 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:", + 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': @@ -880,27 +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_x(arg,control_block.loglevel); + 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': - strcpy(control_block.cert_fname, arg ? arg : ""); + 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))) @@ -932,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(YLOG_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; @@ -973,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; @@ -1020,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(); diff --git a/ztest/ztest.c b/ztest/ztest.c index d061422..8307833 100644 --- a/ztest/ztest.c +++ b/ztest/ztest.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: ztest.c,v 1.71 2005-01-15 19:47:16 adam Exp $ + * $Id: ztest.c,v 1.72 2005-02-01 14:46:48 adam Exp $ */ /* @@ -24,13 +24,13 @@ Z_GenericRecord *dummy_grs_record (int num, ODR o); char *dummy_marc_record (int num, ODR odr); char *dummy_xml_record (int num, ODR odr); -int ztest_search (void *handle, bend_search_rr *rr); -int ztest_sort (void *handle, bend_sort_rr *rr); -int ztest_present (void *handle, bend_present_rr *rr); -int ztest_esrequest (void *handle, bend_esrequest_rr *rr); -int ztest_delete (void *handle, bend_delete_rr *rr); +int ztest_search(void *handle, bend_search_rr *rr); +int ztest_sort(void *handle, bend_sort_rr *rr); +int ztest_present(void *handle, bend_present_rr *rr); +int ztest_esrequest(void *handle, bend_esrequest_rr *rr); +int ztest_delete(void *handle, bend_delete_rr *rr); -int ztest_search (void *handle, bend_search_rr *rr) +int ztest_search(void *handle, bend_search_rr *rr) { if (rr->num_bases != 1) { @@ -677,11 +677,17 @@ bend_initresult *bend_init(bend_initrequest *q) q->bend_explain = ztest_explain; q->bend_srw_scan = ztest_scan; + yaz_log(YLOG_LOG, "ztest_init handle=%p control=%p", + counter, statserv_getcontrol()); + return r; } void bend_close(void *handle) { + yaz_log(YLOG_LOG, "ztest_close handle=%p control=%p", + handle, statserv_getcontrol()); + xfree (handle); /* release our user-defined handle */ return; } -- 1.7.10.4