Changed include/yaz/diagbib1.h and added include/yaz/diagsrw.h with
[yaz-moved-to-github.git] / src / statserv.c
index 6a685b8..dfba86b 100644 (file)
@@ -5,7 +5,7 @@
  * NT threaded server code by
  *   Chas Woodfield, Fretwell Downing Informatics.
  *
- * $Id: statserv.c,v 1.20 2005-02-01 14:46:47 adam Exp $
+ * $Id: statserv.c,v 1.29 2005-03-14 11:14:21 adam Exp $
  */
 
 /**
@@ -38,6 +38,7 @@
 #if HAVE_XML2
 #include <libxml/parser.h>
 #include <libxml/tree.h>
+#include <libxml/xinclude.h>
 #endif
 
 #if YAZ_POSIX_THREADS
 
 static IOCHAN pListener = NULL;
 
+static char gfs_root_dir[FILENAME_MAX+1];
 static struct gfs_server *gfs_server_list = 0;
+static struct gfs_listen *gfs_listen_list = 0;
 static NMEM gfs_nmem = 0;
 
 static char *me = "statserver"; /* log prefix */
 static char *programname="statserver"; /* full program name */
-#if YAZ_POSIX_THREADS
+#ifdef WIN32
+DWORD current_control_tls;
+static int init_control_tls = 0;
+#elif YAZ_POSIX_THREADS
 static pthread_key_t current_control_tls;
 static int init_control_tls = 0;
 #else
@@ -114,7 +120,7 @@ statserv_options_block control_block = {
     "",                         /* PID fname */
     0,                          /* background daemon */
     "",                         /* SSL certificate filename */
-    "",                         /* XML config filename */
+    ""                          /* XML config filename */
 };
 
 static int max_sessions = 0;
@@ -209,10 +215,41 @@ static struct gfs_server * gfs_server_new()
     memcpy(&n->cb, &control_block, sizeof(control_block));
     n->next = 0;
     n->host = 0;
-    n->port = 0;
+    n->listen_ref = 0;
+    n->cql_transform = 0;
+    n->server_node_ptr = 0;
+    n->directory = 0;
+    return n;
+}
+
+static struct gfs_listen * gfs_listen_new(const char *id, 
+                                         const char *address)
+{
+    struct gfs_listen *n = nmem_malloc(gfs_nmem, sizeof(*n));
+    if (id)
+       n->id = nmem_strdup(gfs_nmem, id);
+    else
+       n->id = 0;
+    n->next = 0;
+    n->address = nmem_strdup(gfs_nmem, address);
     return n;
 }
 
+static void gfs_server_chdir(struct gfs_server *gfs)
+{
+    if (gfs_root_dir[0])
+    {
+       if (chdir(gfs_root_dir))
+           yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
+    }
+    if (gfs->directory)
+    {
+       if (chdir(gfs->directory))
+           yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s",
+                   gfs->directory);
+    }
+}
+
 int control_association(association *assoc, const char *host, int force_open)
 {
     char vhost[128], *cp;
@@ -230,37 +267,51 @@ int control_association(association *assoc, const char *host, int force_open)
        struct gfs_server *gfs;
        for (gfs = gfs_server_list; gfs; gfs = gfs->next)
        {
-           int port_match = 0;
+           int listen_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 (!gfs->listen_ref ||
+               gfs->listen_ref == assoc->client_chan->chan_id)
+               listen_match = 1;
+           if (listen_match && host_match)
            {
                if (force_open ||
                    (assoc->last_control != &gfs->cb && assoc->backend))
                {
                    statserv_setcontrol(assoc->last_control);
                    if (assoc->backend && assoc->init)
+                   {
+                       gfs_server_chdir(gfs);
                        (assoc->last_control->bend_close)(assoc->backend);
+                   }
                    assoc->backend = 0;
                    xfree(assoc->init);
                    assoc->init = 0;
                }
+               assoc->cql_transform = gfs->cql_transform;
+               assoc->server_node_ptr = gfs->server_node_ptr;
                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;
            }
        }
        statserv_setcontrol(0);
        assoc->last_control = 0;
+       assoc->cql_transform = 0;
+       assoc->server_node_ptr = 0;
+       yaz_log(YLOG_DEBUG, "server select: no match");
        return 0;
     }
     else
     {
        statserv_setcontrol(&control_block);
        assoc->last_control = &control_block;
+       assoc->cql_transform = 0;
+       assoc->server_node_ptr = 0;
+       yaz_log(YLOG_DEBUG, "server select: config=%s", control_block.configname);
        return 1;
     }
 }
@@ -268,6 +319,7 @@ int control_association(association *assoc, const char *host, int force_open)
 static void xml_config_read()
 {
     struct gfs_server **gfsp = &gfs_server_list;
+    struct gfs_listen **gfslp = &gfs_listen_list;
 #if HAVE_XML2
     xmlNodePtr ptr = xml_config_get_root();
 
@@ -275,33 +327,87 @@ static void xml_config_read()
        return;
     for (ptr = ptr->children; ptr; ptr = ptr->next)
     {
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "server"))
+       struct _xmlAttr *attr;
+       if (ptr->type != XML_ELEMENT_NODE)
+           continue;
+       attr = ptr->properties;
+       if (!strcmp((const char *) ptr->name, "listen"))
+       {
+           /*
+             <listen id="listenerid">tcp:@:9999</listen>
+           */
+           const char *id = 0;
+           const char *address =
+               nmem_dup_xml_content(gfs_nmem, ptr->children);
+           for ( ; attr; attr = attr->next)
+               if (!strcmp(attr->name, "id")
+                   && attr->children && attr->children->type == XML_TEXT_NODE)
+                   id = nmem_dup_xml_content(gfs_nmem, attr->children);
+           if (address)
+           {
+               *gfslp = gfs_listen_new(id, address);
+               gfslp = &(*gfslp)->next;
+               *gfslp = 0; /* make listener list consistent for search */
+           }
+       }
+       else if (!strcmp((const char *) ptr->name, "server"))
        {
-           xmlNodePtr ptr_children = ptr->children;
+           xmlNodePtr ptr_server = ptr;
            xmlNodePtr ptr;
-           
+           const char *listenref = 0;
+           const char *id = 0;
+
+           for ( ; attr; attr = attr->next)
+               if (!strcmp(attr->name, "listenref") && attr->children &&
+                   attr->children->type == XML_TEXT_NODE)
+                   listenref = nmem_dup_xml_content(gfs_nmem, attr->children);
+               else if (!strcmp(attr->name, "id") && attr->children &&
+                        attr->children->type == XML_TEXT_NODE)
+                   id = nmem_dup_xml_content(gfs_nmem, attr->children);
+               else
+                   yaz_log(YLOG_WARN, "Unknown attribute '%s' for server",
+                           attr->name);
            *gfsp = gfs_server_new();
-           for (ptr = ptr_children; ptr; ptr = ptr->next)
+           (*gfsp)->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);
+           }
+           for (ptr = ptr_server->children; ptr; ptr = ptr->next)
            {
-               if (ptr->type == XML_ELEMENT_NODE &&
-                   !strcmp((const char *) ptr->name, "host"))
+               if (ptr->type != XML_ELEMENT_NODE)
+                   continue;
+               if (!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"))
+               else if (!strcmp((const char *) ptr->name, "config"))
                {
                    strcpy((*gfsp)->cb.configname,
                           nmem_dup_xml_content(gfs_nmem, ptr->children));
                }
+               else if (!strcmp((const char *) ptr->name, "cql2rpn"))
+               {
+                   (*gfsp)->cql_transform = cql_transform_open_fname(
+                       nmem_dup_xml_content(gfs_nmem, ptr->children)
+                       );
+               }
+               else if (!strcmp((const char *) ptr->name, "directory"))
+               {
+                   (*gfsp)->directory = 
+                       nmem_dup_xml_content(gfs_nmem, ptr->children);
+               }
            }
            gfsp = &(*gfsp)->next;
        }
@@ -312,6 +418,19 @@ static void xml_config_read()
 
 static void xml_config_open()
 {
+    if (!getcwd(gfs_root_dir, FILENAME_MAX))
+    {
+       yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed");
+       gfs_root_dir[0] = '\0';
+    }
+#ifdef WIN32
+    init_control_tls = 1;
+    current_control_tls = TlsAlloc();
+#elif YAZ_POSIX_THREADS
+    init_control_tls = 1;
+    pthread_key_create(&current_control_tls, 0);
+#endif
+    
     gfs_nmem = nmem_create();
 #if HAVE_XML2
     if (control_block.xml_config[0] == '\0')
@@ -322,12 +441,21 @@ static void xml_config_open()
        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 ;
+           yaz_log(YLOG_FATAL, "Could not parse %s", control_block.xml_config);
+           exit(1);
+       }
+       else
+       {
+           int noSubstitutions = xmlXIncludeProcess(xml_config_doc);
+           if (noSubstitutions == -1)
+           {
+               yaz_log(YLOG_WARN, "XInclude processing failed for config %s",
+                       control_block.xml_config);
+               exit(1);
+           }
        }
     }
     xml_config_read();
-
 #endif
 }
 
@@ -342,34 +470,24 @@ static void xml_config_close()
 #endif
     gfs_server_list = 0;
     nmem_destroy(gfs_nmem);
+#ifdef WIN32
+    if (init_control_tls)
+       TlsFree(current_control_tls);
+#elif YAZ_POSIX_THREADS
+    if (init_control_tls)
+        pthread_key_delete(current_control_tls);
+#endif
 }
 
 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; i<MAX_PORTS; i++)
-       ports[i] = 0;
+    struct gfs_listen *gfs = gfs_listen_list;
+    int id_no;
 
-    for (; gfs; gfs = gfs->next)
-    {
-       int port = gfs->port;
-       if (port)
-       {
-           for (i = 0; i<MAX_PORTS && ports[i] != port; i++)
-               if (ports[i] == 0)
-               {
-                   ports[i] = port;
-                   break;
-               }
-       }
-    }
-    for (i = 0; i<MAX_PORTS && ports[i]; i++)
+    for (id_no = 1; gfs; gfs = gfs->next, id_no++)
     {
-       char where[80];
-       sprintf(where, "@:%d", ports[i]);
-       add_listener(where, ports[i]);
+       if (gfs->address)
+           add_listener(gfs->address, id_no);
     }
 }
 
@@ -384,7 +502,10 @@ static void xml_config_bend_start()
                    gfs->cb.configname);
            statserv_setcontrol(&gfs->cb);
            if (control_block.bend_start)
+           {
+               gfs_server_chdir(gfs);
                (control_block.bend_start)(&gfs->cb);
+           }
        }
     }
     else
@@ -394,7 +515,6 @@ static void xml_config_bend_start()
        if (control_block.bend_start)
            (*control_block.bend_start)(&control_block);
     }
-
 }
 
 static void xml_config_bend_stop()
@@ -593,6 +713,7 @@ void __cdecl event_loop_thread (IOCHAN iochan)
 static void listener(IOCHAN h, int event)   
 {
     COMSTACK line = (COMSTACK) iochan_getdata(h);
+    IOCHAN parent_chan = line->user;
     association *newas;
     int res;
     HANDLE newHandle;
@@ -625,7 +746,7 @@ static void listener(IOCHAN h, int event)
        yaz_log(YLOG_DEBUG, "Accept ok");
 
        if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
-                                      EVENT_INPUT)))
+                                      EVENT_INPUT, parent_chan->chan_id)))
        {
            yaz_log(YLOG_FATAL, "Failed to create iochan");
             iochan_destroy(h);
@@ -634,7 +755,7 @@ static void listener(IOCHAN h, int event)
 
        yaz_log(YLOG_DEBUG, "Creating association");
        if (!(newas = create_association(new_chan, new_line,
-                                        control_block.apdu_file)))
+                                        control_block.apdufile)))
        {
            yaz_log(YLOG_FATAL, "Failed to create new assoc.");
             iochan_destroy(h);
@@ -702,9 +823,6 @@ void statserv_closedown()
         iochan_destroy(p);
     }
     xml_config_close();
-#if YAZ_POSIX_THREADS
-    pthread_key_delete(current_control_tls);
-#endif
 }
 
 void sigterm(int sig)
@@ -839,7 +957,7 @@ static void *new_session (void *vp)
     }
 
     if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask,
-                                  parent_chan->port)))
+                                  parent_chan->chan_id)))
     {
        yaz_log(YLOG_FATAL, "Failed to create iochan");
        return 0;
@@ -982,19 +1100,30 @@ static void catchchld(int num)
 
 statserv_options_block *statserv_getcontrol(void)
 {
-#if YAZ_POSIX_THREADS
+#ifdef WIN32
+    if (init_control_tls)
+       return (statserv_options_block *) TlsGetValue(current_control_tls);
+    else
+       return &control_block;
+#elif YAZ_POSIX_THREADS
     if (init_control_tls)
        return pthread_getspecific(current_control_tls);
     else
        return &control_block;
 #else
-    return current_control_block;
+    if (current_control_block)
+        return current_control_block;
+    return &control_block;
 #endif
 }
 
 void statserv_setcontrol(statserv_options_block *block)
 {
-#if YAZ_POSIX_THREADS
+    chdir(gfs_root_dir);
+#ifdef WIN32
+    if (init_control_tls)
+       TlsSetValue(current_control_tls, block);
+#elif YAZ_POSIX_THREADS
     if (init_control_tls)
        pthread_setspecific(current_control_tls, block);
 #else
@@ -1030,11 +1159,6 @@ int statserv_start(int argc, char **argv)
     if (control_block.options_func(argc, argv))
         return 1;
 
-#if YAZ_POSIX_THREADS
-    init_control_tls = 1;
-    pthread_key_create(&current_control_tls, 0);
-#endif
-    
     xml_config_open();
     
     xml_config_bend_start();
@@ -1306,8 +1430,9 @@ int statserv_main(int argc, char **argv,
                  bend_initresult *(*bend_init)(bend_initrequest *r),
                  void (*bend_close)(void *handle))
 {
-    control_block.bend_init = bend_init;
-    control_block.bend_close = bend_close;
+    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;