X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Fstatserv.c;h=db503331219f7dce1f67d00eb5f9a370d9c2aceb;hp=a0eb0f5607216c2cadfdda5c32fb76b78ce4bd8b;hb=9afc8581574131c6d3ecea36c85740c07714318c;hpb=275991dfabd62de0128770c061c7f780716cc16e diff --git a/src/statserv.c b/src/statserv.c index a0eb0f5..db50333 100644 --- a/src/statserv.c +++ b/src/statserv.c @@ -1,11 +1,6 @@ -/* - * Copyright (C) 1995-2005, Index Data ApS +/* This file is part of the YAZ toolkit. + * Copyright (C) 1995-2013 Index Data * See the file LICENSE for details. - * - * NT threaded server code by - * Chas Woodfield, Fretwell Downing Informatics. - * - * $Id: statserv.c,v 1.35 2006-03-22 13:50:46 mike Exp $ */ /** @@ -13,16 +8,23 @@ * \brief Implements GFS logic */ +#if HAVE_CONFIG_H +#include +#endif + #include #include #include -#include + #ifdef WIN32 #include #include #include -#include "service.h" #endif + +#include +#include + #if HAVE_SYS_TYPES_H #include #endif @@ -36,7 +38,7 @@ #include #endif -#if HAVE_XML2 +#if YAZ_HAVE_XML2 #include #include #include @@ -44,8 +46,6 @@ #if YAZ_POSIX_THREADS #include -#elif YAZ_GNU_THREADS -#include #endif #include @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef USE_XTIMOSI #include #endif @@ -62,6 +63,8 @@ #include "eventl.h" #include "session.h" #include +#include +#include static IOCHAN pListener = NULL; @@ -85,8 +88,7 @@ static statserv_options_block *current_control_block = 0; /* * default behavior. */ -#define STAT_DEFAULT_LOG_LEVEL "none,fatal,warn,log,server,session,request" -/* the 'none' clears yaz' own default settings, including [log] */ +#define STAT_DEFAULT_LOG_LEVEL "server,session,request" int check_options(int argc, char **argv); statserv_options_block control_block = { @@ -95,10 +97,9 @@ statserv_options_block control_block = { 0, /* one shot (single session) */ "", /* 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 */ + 900, /* idle timeout (seconds) */ + 64*1024*1024, /* maximum PDU size (approx.) to allow */ "default-config", /* configuration name to pass to backend */ "", /* set user id */ 0, /* bend_start handler */ @@ -116,17 +117,18 @@ statserv_options_block control_block = { "", /* NT Service Dependencies */ "Z39.50 Server", /* NT Service Display Name */ #endif /* WIN32 */ - 0, /* SOAP handlers */ "", /* PID fname */ 0, /* background daemon */ "", /* SSL certificate filename */ - "" /* XML config filename */ + "", /* XML config filename */ + 1 /* keepalive */ }; static int max_sessions = 0; static int logbits_set = 0; -static int log_session = 0; +static int log_session = 0; /* one-line logs for session */ +static int log_sessiondetail = 0; /* more detailed stuff */ static int log_server = 0; /** get_logbits sets global loglevel bits */ @@ -136,6 +138,7 @@ static void get_logbits(int force) { logbits_set = 1; log_session = yaz_log_module_level("session"); + log_sessiondetail = yaz_log_module_level("sessiondetail"); log_server = yaz_log_module_level("server"); } } @@ -143,12 +146,12 @@ static void get_logbits(int force) static int add_listener(char *where, int listen_id); -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static xmlDocPtr xml_config_doc = 0; #endif -#if HAVE_XML2 -static xmlNodePtr xml_config_get_root() +#if YAZ_HAVE_XML2 +static xmlNodePtr xml_config_get_root(void) { xmlNodePtr ptr = 0; if (xml_config_doc) @@ -160,14 +163,13 @@ static xmlNodePtr xml_config_get_root() yaz_log(YLOG_WARN, "Bad/missing root element for config %s", control_block.xml_config); return 0; - } } return ptr; } #endif -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) { unsigned char *cp; @@ -182,7 +184,7 @@ static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) len += xmlStrlen(p->content); } /* now allocate for the string */ - str = nmem_malloc(n, len); + str = (unsigned char *) nmem_malloc(n, len); *str = '\0'; /* so we can use strcat */ for (p = ptr; p; p = p->next) { @@ -191,7 +193,7 @@ static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) cp = p->content; if (first) { - while(*cp && isspace(*cp)) + while(*cp && yaz_isspace(*cp)) cp++; if (*cp) first = 0; /* reset if we got non-whitespace out */ @@ -201,7 +203,7 @@ static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) } /* remove trailing whitespace */ cp = strlen((const char *)str) + str; - while (cp != str && isspace(cp[-1])) + while (cp != str && yaz_isspace(cp[-1])) cp--; *cp = '\0'; /* return resulting string */ @@ -209,25 +211,33 @@ static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr) } #endif -static struct gfs_server * gfs_server_new() +#if YAZ_HAVE_XML2 +static struct gfs_server * gfs_server_new(const char *id) { - struct gfs_server *n = nmem_malloc(gfs_nmem, sizeof(*n)); + struct gfs_server *n = (struct gfs_server *) + nmem_malloc(gfs_nmem, sizeof(*n)); memcpy(&n->cb, &control_block, sizeof(control_block)); n->next = 0; n->host = 0; n->listen_ref = 0; n->cql_transform = 0; + n->ccl_transform = 0; n->server_node_ptr = 0; n->directory = 0; n->docpath = 0; n->stylesheet = 0; + n->id = nmem_strdup_null(gfs_nmem, id); + n->retrieval = yaz_retrieval_create(); return n; } +#endif -static struct gfs_listen * gfs_listen_new(const char *id, +#if YAZ_HAVE_XML2 +static struct gfs_listen * gfs_listen_new(const char *id, const char *address) { - struct gfs_listen *n = nmem_malloc(gfs_nmem, sizeof(*n)); + struct gfs_listen *n = (struct gfs_listen *) + nmem_malloc(gfs_nmem, sizeof(*n)); if (id) n->id = nmem_strdup(gfs_nmem, id); else @@ -236,6 +246,7 @@ static struct gfs_listen * gfs_listen_new(const char *id, n->address = nmem_strdup(gfs_nmem, address); return n; } +#endif static void gfs_server_chdir(struct gfs_server *gfs) { @@ -264,10 +275,7 @@ int control_association(association *assoc, const char *host, int force_open) *cp = '\0'; host = vhost; } - assoc->cql_transform = 0; - assoc->server_node_ptr = 0; - assoc->docpath = 0; - assoc->stylesheet = 0; + assoc->server = 0; if (control_block.xml_config[0]) { struct gfs_server *gfs; @@ -277,9 +285,15 @@ int control_association(association *assoc, const char *host, int force_open) int host_match = 0; if ( !gfs->host || (host && gfs->host && !strcmp(host, gfs->host))) host_match = 1; - if (!gfs->listen_ref || - gfs->listen_ref == assoc->client_chan->chan_id) + if (!gfs->listen_ref) listen_match = 1; + else + { + int i; + for (i = 0; gfs->listen_ref[i] != -1; i++) + if (gfs->listen_ref[i] == assoc->client_chan->chan_id) + listen_match = 1; + } if (listen_match && host_match) { if (force_open || @@ -295,36 +309,40 @@ int control_association(association *assoc, const char *host, int force_open) xfree(assoc->init); assoc->init = 0; } - assoc->docpath = gfs->docpath; - assoc->stylesheet = gfs->stylesheet; - assoc->cql_transform = gfs->cql_transform; - assoc->server_node_ptr = gfs->server_node_ptr; + assoc->server = gfs; assoc->last_control = &gfs->cb; statserv_setcontrol(&gfs->cb); + gfs_server_chdir(gfs); - yaz_log(YLOG_DEBUG, "server select: %s", gfs->cb.configname); - return 1; + break; } } - statserv_setcontrol(0); - assoc->last_control = 0; - yaz_log(YLOG_DEBUG, "server select: no match"); - return 0; + if (!gfs) + { + statserv_setcontrol(0); + assoc->last_control = 0; + return 0; + } } else { statserv_setcontrol(&control_block); assoc->last_control = &control_block; - yaz_log(YLOG_DEBUG, "server select: config=%s", control_block.configname); - return 1; } + yaz_log(YLOG_DEBUG, "server select: config=%s", + assoc->last_control->configname); + + assoc->maximumRecordSize = assoc->last_control->maxrecordsize; + assoc->preferredMessageSize = assoc->last_control->maxrecordsize; + cs_set_max_recv_bytes(assoc->client_link, assoc->maximumRecordSize); + return 1; } -static void xml_config_read() +#if YAZ_HAVE_XML2 +static void xml_config_read(const char *base_path) { struct gfs_server **gfsp = &gfs_server_list; struct gfs_listen **gfslp = &gfs_listen_list; -#if HAVE_XML2 xmlNodePtr ptr = xml_config_get_root(); if (!ptr) @@ -360,9 +378,10 @@ static void xml_config_read() xmlNodePtr ptr; const char *listenref = 0; const char *id = 0; + struct gfs_server *gfs; for ( ; attr; attr = attr->next) - if (!xmlStrcmp(attr->name, BAD_CAST "listenref") + if (!xmlStrcmp(attr->name, BAD_CAST "listenref") && attr->children && attr->children->type == XML_TEXT_NODE) listenref = nmem_dup_xml_content(gfs_nmem, attr->children); else if (!xmlStrcmp(attr->name, BAD_CAST "id") @@ -372,21 +391,31 @@ static void xml_config_read() else yaz_log(YLOG_WARN, "Unknown attribute '%s' for server", attr->name); - *gfsp = gfs_server_new(); - (*gfsp)->server_node_ptr = ptr_server; + gfs = *gfsp = gfs_server_new(id); + gfs->server_node_ptr = ptr_server; if (listenref) { - int id_no; - struct gfs_listen *gl = gfs_listen_list; - for (id_no = 1; gl; gl = gl->next, id_no++) - if (gl->id && !strcmp(gl->id, listenref)) - { - (*gfsp)->listen_ref = id_no; - break; - } - if (!gl) - yaz_log(YLOG_WARN, "Non-existent listenref '%s' in server " - "config element", listenref); + char **refs; + int num, i; + nmem_strsplit(gfs_nmem, ",", listenref, &refs, &num); + gfs->listen_ref = (int*) nmem_malloc(gfs_nmem, + sizeof(int) * (num + 1)); + for (i = 0; i < num; i++) + { + int id_no; + struct gfs_listen *gl = gfs_listen_list; + gfs->listen_ref[i] = 0; + for (id_no = 1; gl; gl = gl->next, id_no++) + if (gl->id && !strcmp(gl->id, refs[i])) + { + gfs->listen_ref[i] = id_no; + break; + } + if (!gl) + yaz_log(YLOG_WARN, "Non-existent listenref '%s' " + "in server config element", refs[i]); + } + gfs->listen_ref[i] = -1; } for (ptr = ptr_server->children; ptr; ptr = ptr->next) { @@ -394,41 +423,89 @@ static void xml_config_read() continue; if (!strcmp((const char *) ptr->name, "host")) { - (*gfsp)->host = nmem_dup_xml_content(gfs_nmem, - ptr->children); + gfs->host = nmem_dup_xml_content(gfs_nmem, + ptr->children); } else if (!strcmp((const char *) ptr->name, "config")) { - strcpy((*gfsp)->cb.configname, + char fpath[1024]; + strcpy(gfs->cb.configname, nmem_dup_xml_content(gfs_nmem, ptr->children)); + + if (yaz_filepath_resolve(gfs->cb.configname, + base_path, 0, fpath)) + strcpy(gfs->cb.configname, fpath); } else if (!strcmp((const char *) ptr->name, "cql2rpn")) { - (*gfsp)->cql_transform = cql_transform_open_fname( - nmem_dup_xml_content(gfs_nmem, ptr->children) - ); + char fpath[1024]; + char *fname = nmem_dup_xml_content(gfs_nmem, ptr->children); + if (yaz_filepath_resolve(fname, base_path, 0, fpath)) + fname = fpath; + + gfs->cql_transform = cql_transform_open_fname(fname); + if (!gfs->cql_transform) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, + "open CQL transform file '%s'", fname); + exit(1); + } + } + else if (!strcmp((const char *) ptr->name, "ccl2rpn")) + { + char *fname, fpath[1024]; + FILE *f; + + fname = nmem_dup_xml_content(gfs_nmem, ptr->children); + if (yaz_filepath_resolve(fname, base_path, 0, fpath)) + fname = fpath; + + if ((f = fopen(fname, "r")) == 0) { + yaz_log(YLOG_FATAL, "can't open CCL file '%s'", fname); + exit(1); + } + gfs->ccl_transform = ccl_qual_mk(); + ccl_qual_file (gfs->ccl_transform, f); + fclose(f); } else if (!strcmp((const char *) ptr->name, "directory")) { - (*gfsp)->directory = + gfs->directory = nmem_dup_xml_content(gfs_nmem, ptr->children); } else if (!strcmp((const char *) ptr->name, "docpath")) { - (*gfsp)->docpath = + gfs->docpath = nmem_dup_xml_content(gfs_nmem, ptr->children); } + else if (!strcmp((const char *) ptr->name, "maximumrecordsize")) + { + gfs->cb.maxrecordsize = atoi( + nmem_dup_xml_content(gfs_nmem, ptr->children)); + } else if (!strcmp((const char *) ptr->name, "stylesheet")) { char *s = nmem_dup_xml_content(gfs_nmem, ptr->children); - (*gfsp)->stylesheet = + gfs->stylesheet = (char *) nmem_malloc(gfs_nmem, strlen(s) + 2); - sprintf((*gfsp)->stylesheet, "/%s", s); + sprintf(gfs->stylesheet, "/%s", s); } else if (!strcmp((const char *) ptr->name, "explain")) { ; /* being processed separately */ } + else if (!strcmp((const char *) ptr->name, "retrievalinfo")) + { + if (base_path) + yaz_retrieval_set_path(gfs->retrieval, base_path); + if (yaz_retrieval_configure(gfs->retrieval, ptr)) + { + yaz_log(YLOG_FATAL, "%s in config %s", + yaz_retrieval_get_error(gfs->retrieval), + control_block.xml_config); + exit(1); + } + } else { yaz_log(YLOG_FATAL, "Unknown element '%s' in config %s", @@ -439,12 +516,14 @@ static void xml_config_read() gfsp = &(*gfsp)->next; } } -#endif *gfsp = 0; } +#endif -static void xml_config_open() +static int xml_config_open(void) { + const char *last_p; + const char *fname = control_block.xml_config; if (!getcwd(gfs_root_dir, FILENAME_MAX)) { yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed"); @@ -457,19 +536,19 @@ static void xml_config_open() init_control_tls = 1; pthread_key_create(¤t_control_tls, 0); #endif - + gfs_nmem = nmem_create(); -#if HAVE_XML2 - if (control_block.xml_config[0] == '\0') - return; +#if YAZ_HAVE_XML2 + if (fname[0] == '\0') + return 0; if (!xml_config_doc) { - xml_config_doc = xmlParseFile(control_block.xml_config); + xml_config_doc = xmlParseFile(fname); if (!xml_config_doc) { - yaz_log(YLOG_FATAL, "Could not parse %s", control_block.xml_config); - exit(1); + yaz_log(YLOG_FATAL, "Could not parse %s", fname); + return -1; } else { @@ -477,18 +556,34 @@ static void xml_config_open() if (noSubstitutions == -1) { yaz_log(YLOG_WARN, "XInclude processing failed for config %s", - control_block.xml_config); - exit(1); + fname); + return -1; } } } - xml_config_read(); + last_p = strrchr(fname, +#ifdef WIN32 + '\\' +#else + '/' +#endif + ); + if (last_p) + { + WRBUF base_path = wrbuf_alloc(); + wrbuf_write(base_path, fname, last_p - fname); + xml_config_read(wrbuf_cstr(base_path)); + wrbuf_destroy(base_path); + } + else + xml_config_read(0); #endif + return 0; } -static void xml_config_close() +static void xml_config_close(void) { -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (xml_config_doc) { xmlFreeDoc(xml_config_doc); @@ -506,19 +601,21 @@ static void xml_config_close() #endif } -static void xml_config_add_listeners() +static int xml_config_add_listeners(void) { struct gfs_listen *gfs = gfs_listen_list; int id_no; + int ret = 0; for (id_no = 1; gfs; gfs = gfs->next, id_no++) { - if (gfs->address) - add_listener(gfs->address, id_no); + if (!ret && gfs->address) + ret = add_listener(gfs->address, id_no); } + return ret; } -static void xml_config_bend_start() +static void xml_config_bend_start(void) { if (control_block.xml_config[0]) { @@ -544,7 +641,7 @@ static void xml_config_bend_start() } } -static void xml_config_bend_stop() +static void xml_config_bend_stop(void) { if (control_block.xml_config[0]) { @@ -567,6 +664,8 @@ static void xml_config_bend_stop() } } +static void remove_listeners(void); + /* * handle incoming connect requests. * The dynamic mode is a bit tricky mostly because we want to avoid @@ -593,7 +692,7 @@ static void ThreadList_Initialize() /* Initialize the critical Sections */ InitializeCriticalSection(&Thread_CritSect); - /* Set the first thraed */ + /* Set the first thraed */ pFirstThread = NULL; /* we have been initialized */ @@ -661,7 +760,7 @@ void statserv_remove(IOCHAN pIOChannel) } /* WIN32 statserv_closedown */ -void statserv_closedown() +static void statserv_closedown() { /* Shouldn't do anything if we are not initialized */ if (bInitialized) @@ -698,7 +797,7 @@ void statserv_closedown() /* Allocate the thread handle array */ pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles); - pCurrentHandle = pThreadHandles; + pCurrentHandle = pThreadHandles; for (pCurrentThread = pFirstThread; pCurrentThread != NULL; @@ -731,13 +830,13 @@ void statserv_closedown() xml_config_close(); } -void __cdecl event_loop_thread (IOCHAN iochan) +void __cdecl event_loop_thread(IOCHAN iochan) { - event_loop (&iochan); + iochan_event_loop(&iochan); } /* WIN32 listener */ -static void listener(IOCHAN h, int event) +static void listener(IOCHAN h, int event) { COMSTACK line = (COMSTACK) iochan_getdata(h); IOCHAN parent_chan = line->user; @@ -747,27 +846,21 @@ static void listener(IOCHAN h, int event) if (event == EVENT_INPUT) { + COMSTACK new_line; + IOCHAN new_chan; + if ((res = cs_listen(line, 0, 0)) < 0) { - yaz_log(YLOG_FATAL, "cs_listen failed"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed"); return; } else if (res == 1) - return; + return; /* incomplete */ yaz_log(YLOG_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) + new_line = cs_accept(line); + if (!new_line) { yaz_log(YLOG_FATAL, "Accept failed."); - iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); return; } yaz_log(YLOG_DEBUG, "Accept ok"); @@ -801,7 +894,7 @@ static void listener(IOCHAN h, int event) newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan); if (newHandle == (HANDLE) -1) { - + yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread."); iochan_destroy(h); return; @@ -820,27 +913,16 @@ static void listener(IOCHAN h, int event) } } -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 + define this empty function */ -int statserv_must_terminate(void) -{ - return term_flag; -} - void statserv_remove(IOCHAN pIOChannel) { } -void statserv_closedown() +static void statserv_closedown(void) { IOCHAN p; @@ -852,12 +934,7 @@ void statserv_closedown() xml_config_close(); } -void sigterm(int sig) -{ - term_flag = 1; -} - -static void *new_session (void *vp); +static void *new_session(void *vp); static int no_sessions = 0; /* UNIX listener */ @@ -888,7 +965,10 @@ static void listener(IOCHAN h, int event) return; } - yaz_log(log_session, "Connect from %s", cs_addrstr(new_line)); + if (control_block.one_shot) + remove_listeners(); + + yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line)); no_sessions++; if (control_block.dynamic) @@ -927,20 +1007,8 @@ static void listener(IOCHAN h, int event) { #if YAZ_POSIX_THREADS pthread_t child_thread; - pthread_create (&child_thread, 0, new_session, new_line); - pthread_detach (child_thread); -#elif YAZ_GNU_THREADS - pth_attr_t attr; - pth_t child_thread; - - attr = pth_attr_new (); - 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 (YLOG_DEBUG, "pth_spawn begin"); - child_thread = pth_spawn (attr, new_session, new_line); - yaz_log (YLOG_DEBUG, "pth_spawn finish"); - pth_attr_destroy (attr); + pthread_create(&child_thread, 0, new_session, new_line); + pthread_detach(child_thread); #else new_session(new_line); #endif @@ -960,15 +1028,15 @@ static void listener(IOCHAN h, int event) } } -static void *new_session (void *vp) +static void *new_session(void *vp) { - char *a; + const char *a; association *newas; IOCHAN new_chan; COMSTACK new_line = (COMSTACK) vp; - IOCHAN parent_chan = new_line->user; + IOCHAN parent_chan = (IOCHAN) new_line->user; - unsigned cs_get_mask, cs_accept_mask, mask = + 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); @@ -1005,13 +1073,14 @@ static void *new_session (void *vp) #else a = 0; #endif - yaz_log(log_session, "Starting session %d from %s (pid=%ld)", + yaz_log_xml_errors(0, YLOG_WARN); + yaz_log(log_session, "Session - OK %d %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) { - event_loop(&new_chan); + iochan_event_loop(&new_chan); } else { @@ -1029,7 +1098,7 @@ static void inetd_connection(int what) COMSTACK line; IOCHAN chan; association *assoc; - char *addr; + const char *addr; if ((line = cs_createbysocket(0, tcpip_type, 0, what))) { @@ -1042,7 +1111,7 @@ static void inetd_connection(int what) iochan_setdata(chan, assoc); iochan_settimeout(chan, 60); addr = cs_addrstr(line); - yaz_log(log_session, "Inetd association from %s", + yaz_log(log_sessiondetail, "Inetd association from %s", addr ? addr : "[UNKNOWN]"); assoc->cs_get_mask = EVENT_INPUT; } @@ -1081,8 +1150,8 @@ static int add_listener(char *where, int listen_id) else mode = "static"; - yaz_log(log_server, "Adding %s listener on %s id=%d", mode, where, - listen_id); + yaz_log(log_server, "Adding %s listener on %s id=%d PID=%ld", mode, where, + listen_id, (long) getpid()); l = cs_create_host(where, 2, &ap); if (!l) @@ -1095,15 +1164,19 @@ static int add_listener(char *where, int listen_id) if (cs_bind(l, ap, CS_SERVER) < 0) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where); - cs_close (l); + if (cs_errno(l) == CSYSERR) + yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where); + else + yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where, + cs_strerror(l)); + cs_close(l); return -1; } if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT | - EVENT_EXCEPT, listen_id))) + EVENT_EXCEPT, listen_id))) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type"); - cs_close (l); + cs_close(l); return -1; } iochan_setdata(lst, l); /* user-defined data for listener is COMSTACK */ @@ -1115,6 +1188,13 @@ static int add_listener(char *where, int listen_id) return 0; /* OK */ } +static void remove_listeners(void) +{ + IOCHAN l = pListener; + for (; l; l = l->next) + iochan_destroy(l); +} + #ifndef WIN32 /* UNIX only (for windows we don't need to catch the signals) */ static void catchchld(int num) @@ -1134,7 +1214,8 @@ statserv_options_block *statserv_getcontrol(void) return &control_block; #elif YAZ_POSIX_THREADS if (init_control_tls) - return pthread_getspecific(current_control_tls); + return (statserv_options_block *) + pthread_getspecific(current_control_tls); else return &control_block; #else @@ -1146,7 +1227,11 @@ statserv_options_block *statserv_getcontrol(void) void statserv_setcontrol(statserv_options_block *block) { - chdir(gfs_root_dir); + if (gfs_root_dir[0]) + { + if (chdir(gfs_root_dir)) + yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir); + } #ifdef WIN32 if (init_control_tls) TlsSetValue(current_control_tls, block); @@ -1162,7 +1247,32 @@ static void statserv_reset(void) { } -int statserv_start(int argc, char **argv) +static void daemon_handler(void *data) +{ + IOCHAN *pListener = data; + iochan_event_loop(pListener); +} + +#ifndef WIN32 +static void normal_stop_handler(int num) +{ + yaz_log(log_server, "Received SIGTERM. PID=%ld", (long) getpid()); + exit(0); +} +#endif + +static void show_version(void) +{ + char vstr[20], sha1_str[41]; + + yaz_version(vstr, sha1_str); + printf("YAZ version: %s %s\n", YAZ_VERSION, YAZ_VERSION_SHA1); + if (strcmp(sha1_str, YAZ_VERSION_SHA1)) + printf("YAZ DLL/SO: %s %s\n", vstr, sha1_str); + exit(0); +} + +static int statserv_sc_main(yaz_sc_t s, int argc, char **argv) { char sep; #ifdef WIN32 @@ -1177,7 +1287,7 @@ int statserv_start(int argc, char **argv) #else sep = '/'; #endif - if ((me = strrchr (argv[0], sep))) + if ((me = strrchr(argv[0], sep))) me++; /* get the basename */ else me = argv[0]; @@ -1186,133 +1296,52 @@ int statserv_start(int argc, char **argv) if (control_block.options_func(argc, argv)) return 1; - xml_config_open(); - + if (xml_config_open()) + return 1; + xml_config_bend_start(); + if (control_block.inetd) + { #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, 0); + ; /* no inetd on Windows */ #else -/* UNIX */ - if (control_block.inetd) inetd_connection(control_block.default_proto); +#endif + } 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 (xml_config_add_listeners()) + return 1; - if (!pListener && *control_block.default_listen) - add_listener(control_block.default_listen, 0); - if (!pListener) - return 1; + add_listener("tcp:@:9999", 0); - if (*control_block.pid_fname) - { - FILE *f = fopen(control_block.pid_fname, "w"); - if (!f) - { - 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_server, "Starting server %s pid=%ld", programname, - (long) getpid()); -#if 0 - sigset_t sigs_to_block; - - sigemptyset(&sigs_to_block); - sigaddset (&sigs_to_block, SIGTERM); - pthread_sigmask (SIG_BLOCK, &sigs_to_block, 0); - /* missing... */ -#endif +#ifndef WIN32 if (control_block.dynamic) signal(SIGCHLD, catchchld); - } - signal (SIGPIPE, SIG_IGN); - signal (SIGTERM, sigterm); - if (*control_block.setuid) - { - struct passwd *pw; - - if (!(pw = getpwnam(control_block.setuid))) - { - yaz_log(YLOG_FATAL, "%s: Unknown user", control_block.setuid); - return(1); - } - if (setuid(pw->pw_uid) < 0) - { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid"); - exit(1); - } - } -/* UNIX */ #endif + } if (pListener == NULL) return 1; - yaz_log(YLOG_DEBUG, "Entering event loop."); - return event_loop(&pListener); + if (s) + yaz_sc_running(s); + +#ifndef WIN32 + signal(SIGTERM, normal_stop_handler); +#endif + yaz_daemon(programname, + (control_block.background ? YAZ_DAEMON_FORK : 0), + daemon_handler, &pListener, + *control_block.pid_fname ? control_block.pid_fname : 0, + *control_block.setuid ? control_block.setuid : 0); + return 0; } static void option_copy(char *dst, const char *src) { - strncpy(dst, src ? src : "", 127); - dst[127] = '\0'; + strncpy(dst, src ? src : "", BEND_NAME_MAX-1); + dst[BEND_NAME_MAX-1] = '\0'; } int check_options(int argc, char **argv) @@ -1320,19 +1349,11 @@ int check_options(int argc, char **argv) int ret = 0, r; char *arg; - if (getenv("YAZ_LOG") == 0) { - /* - * Set default log level. We want to avoid doing this if the - * user has already explicitly specified a preferred default - * log-level, hence the inelegant peek at the YAZ_LOG - * environment variable that will subsequently be interpreted - * by the YAZ logging module itself. - */ - yaz_log_init_level(yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL)); - } + yaz_log_init_level(yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL)); + + get_logbits(1); - get_logbits(1); - while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:DC:f:m:", + while ((ret = options("1a:iszSTl:v:u:c:w:t:k:Kd:A:p:DC:f:m:r:V", argv, argc, &arg)) != -2) { switch (ret) @@ -1341,7 +1362,7 @@ int check_options(int argc, char **argv) if (add_listener(arg, 0)) return 1; /* failed to create listener */ break; - case '1': + case '1': control_block.one_shot = 1; control_block.dynamic = 0; break; @@ -1349,8 +1370,8 @@ int check_options(int argc, char **argv) control_block.default_proto = PROTO_Z3950; break; case 's': - fprintf (stderr, "%s: SR protocol no longer supported\n", me); - exit (1); + fprintf(stderr, "%s: SR protocol no longer supported\n", me); + exit(1); break; case 'S': control_block.dynamic = 0; @@ -1359,9 +1380,6 @@ int check_options(int argc, char **argv) #if YAZ_POSIX_THREADS control_block.dynamic = 0; control_block.threads = 1; -#elif YAZ_GNU_THREADS - control_block.dynamic = 0; - control_block.threads = 1; #else fprintf(stderr, "%s: Threaded mode not available.\n", me); return 1; @@ -1378,12 +1396,10 @@ int check_options(int argc, char **argv) } yaz_log_time_format(arg); break; - case 'v': { - int default_level = yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL); - yaz_log_init_level(yaz_log_mask_str_x(arg, default_level)); - get_logbits(1); + case 'v': + yaz_log_init_level(yaz_log_mask_str(arg)); + get_logbits(1); break; - } case 'a': option_copy(control_block.apdufile, arg); break; @@ -1405,7 +1421,7 @@ int check_options(int argc, char **argv) fprintf(stderr, "%s: Specify positive timeout for -t.\n", me); return(1); } - control_block.idle_timeout = r; + control_block.idle_timeout = strchr(arg, 's') ? r : 60 * r; break; case 'k': if (!arg || !(r = atoi(arg))) @@ -1415,13 +1431,16 @@ int check_options(int argc, char **argv) } control_block.maxrecordsize = r * 1024; break; + case 'K': + control_block.keepalive = 0; + break; case 'i': control_block.inetd = 1; break; case 'w': if (chdir(arg)) { - perror(arg); + perror(arg); return 1; } break; @@ -1432,7 +1451,7 @@ int check_options(int argc, char **argv) option_copy(control_block.pid_fname, arg); break; case 'f': -#if HAVE_XML2 +#if YAZ_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); @@ -1442,93 +1461,62 @@ int check_options(int argc, char **argv) case 'D': control_block.background = 1; break; + case 'r': + if (!arg || !(r = atoi(arg))) + { + fprintf(stderr, "%s: Specify positive size for -r.\n", me); + return(1); + } + yaz_log_init_max_size(r * 1024); + break; + case 'V': + show_version(); + break; default: fprintf(stderr, "Usage: %s [ -a -v " " -l -u -c -t " " -k -d -p -C certfile" - " -ziDST1 -m -w ... ]\n", me); + " -zKiDSTV1 -m -w ... ]\n", me); return 1; } } return 0; } -#ifdef WIN32 -typedef struct _Args -{ - char **argv; - int argc; -} Args; - -static Args ArgDetails; - -/* name of the executable */ -#define SZAPPNAME "server" - -/* list of service dependencies - "dep1\0dep2\0\0" */ -#define SZDEPENDENCIES "" - -int statserv_main(int argc, char **argv, - bend_initresult *(*bend_init)(bend_initrequest *r), - void (*bend_close)(void *handle)) -{ - struct statserv_options_block *cb = &control_block; - cb->bend_init = bend_init; - cb->bend_close = bend_close; - - /* Lets setup the Arg structure */ - ArgDetails.argc = argc; - ArgDetails.argv = argv; - - /* Now setup the service with the service controller */ - SetupService(argc, argv, &ArgDetails, SZAPPNAME, - cb->service_name, /* internal service name */ - cb->service_display_name, /* displayed name */ - SZDEPENDENCIES); - return 0; -} - -int StartAppService(void *pHandle, int argc, char **argv) -{ - /* Initializes the App */ - return 1; -} - -void RunAppService(void *pHandle) -{ - Args *pArgs = (Args *)pHandle; - - /* Starts the app running */ - statserv_start(pArgs->argc, pArgs->argv); -} - -void StopAppService(void *pHandle) +void statserv_sc_stop(yaz_sc_t s) { - /* Stops the app */ statserv_closedown(); statserv_reset(); } -/* WIN32 */ -#else -/* UNIX */ + int statserv_main(int argc, char **argv, bend_initresult *(*bend_init)(bend_initrequest *r), void (*bend_close)(void *handle)) { int ret; + struct statserv_options_block *cb = &control_block; + + /* control block does not have service_name member on Unix */ + yaz_sc_t s = yaz_sc_create( +#ifdef WIN32 + cb->service_name, cb->service_display_name +#else + 0, 0 +#endif + ); - control_block.bend_init = bend_init; - control_block.bend_close = bend_close; + cb->bend_init = bend_init; + cb->bend_close = bend_close; - ret = statserv_start (argc, argv); - statserv_closedown (); - statserv_reset(); + ret = yaz_sc_program(s, argc, argv, statserv_sc_main, statserv_sc_stop); + yaz_sc_destroy(&s); return ret; } -#endif + /* * Local variables: * c-basic-offset: 4 + * c-file-style: "Stroustrup" * indent-tabs-mode: nil * End: * vim: shiftwidth=4 tabstop=8 expandtab