cdetails
t-server
tstthreads
+p2
-## $Id: Makefile.am,v 1.9 2005-06-21 21:54:05 adam Exp $
+## $Id: Makefile.am,v 1.10 2005-10-05 12:07:14 adam Exp $
AM_CXXFLAGS = $(YAZPPINC) -I$(srcdir)/../include $(XSLT_CFLAGS) $(USEMARCONINC)
bin_PROGRAMS = yazproxy
check_PROGRAMS = cdetails
-noinst_PROGRAMS = tstthreads t-server
+noinst_PROGRAMS = tstthreads t-server p2
TESTS=$(check_PROGRAMS)
cdetails_SOURCES=cdetails.cpp
tstthreads_SOURCES=tstthreads.cpp
t_server_SOURCES=t-server.cpp
+p2_SOURCES=p2_frontend.cpp p2_msg.cpp p2.cpp p2_frontend.h \
+ p2_config.cpp p2_config.h \
+ p2_backend.h p2_backend_dummy.cpp \
+ p2_modules.cpp p2_modules.h \
+ p2_xmlerror.cpp p2_xmlerror.h
LDADD=libyazproxy.la $(YAZPPLALIB) $(XSLT_LIBS) $(USEMARCONLALIB)
libyazproxy_la_LIBADD = $(XSLT_LIBS)
--- /dev/null
+/* $Id: p2.cpp,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <yaz/log.h>
+#include <yaz/diagbib1.h>
+#include <yaz/options.h>
+
+#include <yaz++/socket-manager.h>
+#include "p2_config.h"
+#include "p2_frontend.h"
+#include "p2_xmlerror.h"
+#include "p2_modules.h"
+
+using namespace yazpp_1;
+
+extern P2_ModuleEntry *p2_backend_dummy;
+
+/*
+ frontend result set map
+ resultset -> db,query
+
+ backend result set map
+ db,query -> resultset, target
+ resultset, target
+*/
+class P2_Frontend;
+
+P2_Config *P2_Server::lockConfig()
+{
+ pthread_mutex_lock(&m_mutex_config);
+ return m_config;
+}
+
+void P2_Server::unlockConfig()
+{
+ pthread_mutex_unlock(&m_mutex_config);
+}
+
+P2_Server::P2_Server(IPDU_Observable *the_PDU_Observable,
+ Msg_Thread *my_thread,
+ P2_Config *config,
+ P2_ModuleFactory *modules)
+ : Z_Assoc(the_PDU_Observable)
+{
+ m_my_thread = my_thread;
+ m_modules = modules;
+ m_config = config;
+
+ pthread_mutex_init(&m_mutex_config, 0);
+
+ yaz_log(YLOG_LOG, "Construct P2_Server=%p", this);
+}
+
+IPDU_Observer *P2_Server::sessionNotify(IPDU_Observable
+ *the_PDU_Observable, int fd)
+{
+ P2_Frontend *my = new P2_Frontend(the_PDU_Observable, m_my_thread, this);
+ yaz_log(YLOG_LOG, "New session %s", the_PDU_Observable->getpeername());
+ return my;
+}
+
+P2_Server::~P2_Server()
+{
+ yaz_log(YLOG_LOG, "Destroy P2_server=%p", this);
+ pthread_mutex_destroy(&m_mutex_config);
+}
+
+void P2_Server::recv_GDU(Z_GDU *apdu, int len)
+{
+}
+
+void P2_Server::failNotify()
+{
+}
+
+void P2_Server::timeoutNotify()
+{
+}
+
+void P2_Server::connectNotify()
+{
+}
+
+int main(int argc, char **argv)
+{
+ p2_xmlerror_setup();
+
+ P2_Config config;
+
+ if (!config.parse_options(argc, argv))
+ {
+ yaz_log(YLOG_FATAL, "Configuration incorrect. Exiting");
+ exit(1);
+ }
+
+ SocketManager mySocketManager;
+
+ PDU_Assoc *my_PDU_Assoc = 0;
+
+ Msg_Thread my_thread(&mySocketManager, config.m_no_threads);
+
+ my_PDU_Assoc = new PDU_Assoc(&mySocketManager);
+
+ P2_ModuleFactory modules;
+
+ modules.add(p2_backend_dummy);
+
+ std::list<P2_ConfigModule *>::const_iterator it;
+ for (it = config.m_modules.begin(); it != config.m_modules.end(); it++)
+ modules.add((*it)->m_fname.c_str());
+
+ P2_Server z(my_PDU_Assoc, &my_thread, &config, &modules);
+ z.server(config.m_listen_address.c_str());
+
+ while (mySocketManager.processEvent() > 0)
+ ;
+ return 0;
+}
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
--- /dev/null
+
+#ifndef P2_BACKEND_H
+#define P2_BACKEND_H
+
+#include <yaz++/z-query.h>
+
+class IP2_BackendSet {
+public:
+ virtual ~IP2_BackendSet();
+ virtual int get(int start, int number) = 0;
+};
+
+class IP2_Backend {
+ public:
+ virtual ~IP2_Backend();
+ virtual int search(yazpp_1::Yaz_Z_Query *q, IP2_BackendSet **rset, int *hits) = 0;
+};
+
+struct P2_ModuleInterface0 {
+ IP2_Backend *(*create)(const char *address);
+};
+
+struct P2_ModuleEntry {
+ int version;
+ const char *name;
+ const char *description;
+ void *interface_ptr;
+};
+
+
+#endif
--- /dev/null
+
+#include <yaz/log.h>
+#include "p2_backend.h"
+
+class P2_BackendSetDummy : public IP2_BackendSet {
+public:
+ P2_BackendSetDummy();
+ ~P2_BackendSetDummy();
+ int get(int start, int number);
+};
+
+class P2_BackendDummy : public IP2_Backend {
+public:
+ P2_BackendDummy(const char *address);
+ ~P2_BackendDummy();
+ int search(yazpp_1::Yaz_Z_Query *q, IP2_BackendSet **rset, int *hits);
+};
+
+P2_BackendDummy::P2_BackendDummy(const char *address)
+{
+ yaz_log(YLOG_LOG, "P2_backendDummy %p create", this);
+}
+
+P2_BackendDummy::~P2_BackendDummy()
+{
+ yaz_log(YLOG_LOG, "P2_backendDummy %p destroy", this);
+}
+
+int P2_BackendDummy::search(yazpp_1::Yaz_Z_Query *q, IP2_BackendSet **rset,
+ int *hits)
+{
+ yaz_log(YLOG_LOG, "P2_backendDummy %p search", this);
+
+ P2_BackendSetDummy *s = new P2_BackendSetDummy();
+
+ *rset = s;
+ *hits = 42;
+ return 0;
+}
+
+int P2_BackendSetDummy::get(int start, int number)
+{
+ yaz_log(YLOG_LOG, "P2_backendSetDummy %p get", this);
+ return 0;
+}
+
+P2_BackendSetDummy::P2_BackendSetDummy()
+{
+ yaz_log(YLOG_LOG, "P2_backendSetDummy %p create", this);
+
+}
+
+P2_BackendSetDummy::~P2_BackendSetDummy()
+{
+ yaz_log(YLOG_LOG, "P2_backendSetDummy %p destroy", this);
+}
+
+static IP2_Backend *dummy_create(const char *address)
+{
+ return new P2_BackendDummy(address);
+}
+
+P2_ModuleInterface0 int0 = {
+ dummy_create
+};
+
+P2_ModuleEntry p2_module_entry = {
+ 0,
+ "dummy",
+ "Dummy Backend",
+ (void *) &int0
+};
+
+P2_ModuleEntry *p2_backend_dummy = &p2_module_entry;
--- /dev/null
+/* $Id: p2_config.cpp,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <yaz/log.h>
+#include <yaz/options.h>
+#include <yaz/diagbib1.h>
+#include "p2_config.h"
+
+#if HAVE_XSLT
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xinclude.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+#endif
+
+#include <iostream>
+
+using namespace std;
+
+class P2_Config::Rep {
+
+public:
+ Rep();
+ ~Rep();
+public:
+#if HAVE_XSLT
+ xmlDocPtr m_docPtr;
+ xmlNodePtr m_proxyPtr;
+#endif
+};
+
+P2_Config::Rep::Rep()
+{
+#if HAVE_XSLT
+ m_docPtr = 0;
+ m_proxyPtr = 0;
+#endif
+}
+
+P2_Config::Rep::~Rep()
+{
+#if HAVE_XSLT
+ if (m_docPtr)
+ xmlFreeDoc(m_docPtr);
+#endif
+}
+
+P2_Config::P2_Config()
+{
+ m_max_clients = 500;
+ m_client_idletime = 600;
+ m_debug_mode = 0;
+ m_no_limit_files = 0;
+ m_no_threads = 20;
+ m_target_idletime = 600;
+
+ m_rep = new Rep();
+}
+
+bool P2_Config::parse_options(int argc, char **argv)
+{
+ char *arg;
+ int ret;
+ bool show_config = false;
+ while ((ret = options("o:a:t:v:c:u:i:m:l:T:p:n:h:XS",
+ argv, argc, &arg)) != -2)
+ {
+ switch (ret)
+ {
+ case 0:
+ if (m_listen_address.length())
+ {
+ yaz_log(YLOG_FATAL, "Multiple listener address given");
+ return false;
+ }
+ m_listen_address = arg;
+ break;
+ case 'a':
+ m_apdu_log = arg;
+ break;
+ case 'c':
+ if (m_xml_fname.length())
+ {
+ yaz_log(YLOG_FATAL, "Multiple -c options given");
+ return false;
+ }
+ if (!read_xml_config(arg))
+ {
+ return false;
+ }
+ m_xml_fname = arg;
+ break;
+ case 'i':
+ m_client_idletime = atoi(arg);
+ break;
+ case 'l':
+ m_log_file = arg;
+ break;
+ case 'm':
+ m_max_clients = atoi(arg);
+ break;
+ case 'n':
+ m_no_limit_files = atoi(arg);
+ break;
+ case 'h':
+ m_no_threads = atoi(arg);
+ break;
+ case 'o':
+ m_optimize_flags = arg;
+ break;
+ case 'p':
+ if (m_pid_fname.length())
+ {
+ yaz_log(YLOG_LOG, "Multiple -p options given");
+ return false;
+ }
+ m_pid_fname = arg;
+ break;
+ case 't':
+ if (m_default_target.length())
+ {
+ yaz_log(YLOG_LOG, "Multiple -t options given");
+ return false;
+ }
+ m_default_target = arg;
+ break;
+ case 'T':
+ m_target_idletime = atoi(arg);
+ break;
+ case 'u':
+ if (m_uid.length())
+ {
+ yaz_log(YLOG_FATAL, "-u specified more than once");
+ return false;
+ }
+ m_uid = arg;
+ break;
+ case 'v':
+ yaz_log_init_level(yaz_log_mask_str(arg));
+ break;
+ case 'X':
+ m_debug_mode = 1;
+ break;
+ case 'S':
+ show_config = true;
+ break;
+ default:
+ yaz_log(YLOG_FATAL, "Bad option %s", arg);
+ return false;
+ }
+ }
+ if (m_log_file.length())
+ yaz_log_init_file(m_log_file.c_str());
+ if (show_config)
+ print();
+ return true;
+}
+
+bool P2_Config::parse_xml_text(void *xml_ptr, bool &val)
+{
+ string v;
+ if (!parse_xml_text(xml_ptr, v))
+ return false;
+ if (v.length() == 1 && v[0] == '1')
+ val = true;
+ else
+ val = false;
+ return true;
+}
+
+bool P2_Config::parse_xml_text(void *xml_ptr, string &val)
+{
+ xmlNodePtr ptr = (xmlNodePtr) xml_ptr;
+ bool found = false;
+ string v;
+ for(ptr = ptr->children; ptr; ptr = ptr->next)
+ if (ptr->type == XML_TEXT_NODE)
+ {
+ xmlChar *t = ptr->content;
+ if (t)
+ {
+ v += (const char *) t;
+ found = true;
+ }
+ }
+ if (found)
+ val = v;
+ return found;
+}
+
+void P2_Config::parse_xml_element_target(void *xml_ptr,
+ P2_ConfigTarget *t)
+{
+ xmlNodePtr ptr = (xmlNodePtr) xml_ptr;
+
+ for (ptr = ptr->children; ptr; ptr = ptr->next)
+ {
+ if (ptr->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((const char *) ptr->name, "url"))
+ {
+ parse_xml_text(ptr, t->m_target_address);
+ }
+ else if (!strcmp((const char *) ptr->name, "database"))
+ {
+ parse_xml_text(ptr, t->m_target_database);
+ }
+ else
+ {
+ yaz_log(YLOG_WARN, "Unknown element '%s' inside target",
+ (const char *) ptr->name);
+ m_errors++;
+ }
+ }
+}
+
+void P2_Config::parse_xml_element_proxy(void *xml_ptr)
+{
+ xmlNodePtr ptr = (xmlNodePtr) xml_ptr;
+
+ for (ptr = ptr->children; ptr; ptr = ptr->next)
+ {
+ if (ptr->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((const char *) ptr->name, "target"))
+ {
+ P2_ConfigTarget *t = new P2_ConfigTarget();
+
+ struct _xmlAttr *attr;
+ for (attr = ptr->properties; attr; attr = attr->next)
+ if (!strcmp((const char *) attr->name, "name")
+ || !strcmp((const char *) attr->name, "host"))
+ {
+ parse_xml_text(attr, t->m_virt_address);
+ }
+ else if (!strcmp((const char *) attr->name, "database"))
+ {
+ parse_xml_text(attr, t->m_virt_database);
+ }
+ else if (!strcmp((const char *) attr->name, "default"))
+ {
+ parse_xml_text(attr, t->m_default);
+ }
+ else if (!strcmp((const char *) attr->name, "type"))
+ {
+ parse_xml_text(attr, t->m_type);
+ }
+ else
+ {
+ yaz_log(YLOG_WARN, "Unknown attribute '%s' for "
+ "element proxy",
+ (const char *) attr->name);
+ m_errors++;
+ }
+ parse_xml_element_target(ptr, t);
+ m_target_list.push_back(t);
+ }
+ else if (!strcmp((const char *) ptr->name, "max-clients"))
+ {
+ string v;
+ if (parse_xml_text(ptr, v))
+ m_max_clients = atoi(v.c_str());
+ }
+ else if (!strcmp((const char *) ptr->name, "module"))
+ {
+ P2_ConfigModule *t = new P2_ConfigModule();
+
+ string v;
+ if (parse_xml_text(ptr, v))
+ {
+ t->m_fname = v;
+ m_modules.push_back(t);
+ }
+ }
+ else
+ {
+ yaz_log(YLOG_WARN, "Unknown element '%s' inside proxy", ptr->name);
+ m_errors++;
+ }
+ }
+}
+
+void P2_Config::print()
+{
+ cout << "max_clients=" << m_max_clients << endl;
+ list<P2_ConfigTarget *>::const_iterator it;
+
+ for (it = m_target_list.begin(); it != m_target_list.end(); it++)
+ {
+ cout << "type=" << (*it)->m_type << " ";
+ cout << "v-address=" << (*it)->m_virt_address << " ";
+ cout << "v-db=" << (*it)->m_virt_database << " ";
+ cout << "t-address=" << (*it)->m_target_address << " ";
+ cout << "t-db=" << (*it)->m_target_database << " ";
+ cout << "default=" << (*it)->m_default << endl;
+ }
+}
+
+bool P2_Config::read_xml_config(const char *fname)
+{
+ xmlDocPtr ndoc = xmlParseFile(fname);
+
+ if (!ndoc)
+ {
+ yaz_log(YLOG_WARN, "Config file %s not found or parse error", fname);
+ return false;
+ }
+ int noSubstitutions = xmlXIncludeProcess(ndoc);
+ if (noSubstitutions == -1)
+ yaz_log(YLOG_WARN, "XInclude processing failed on config %s", fname);
+
+ xmlNodePtr proxyPtr = xmlDocGetRootElement(ndoc);
+ if (!proxyPtr || proxyPtr->type != XML_ELEMENT_NODE ||
+ strcmp((const char *) proxyPtr->name, "proxy"))
+ {
+ yaz_log(YLOG_WARN, "No proxy element in %s", fname);
+ xmlFreeDoc(ndoc);
+ return false;
+ }
+ m_rep->m_proxyPtr = proxyPtr;
+
+ // OK: release previous and make it the current one.
+ if (m_rep->m_docPtr)
+ xmlFreeDoc(m_rep->m_docPtr);
+ m_rep->m_docPtr = ndoc;
+
+ m_errors = 0;
+ parse_xml_element_proxy(proxyPtr);
+ if (m_errors && !m_debug_mode)
+ return false;
+ return true;
+}
+
+P2_Config::~P2_Config()
+{
+ delete m_rep;
+}
+
+P2_ConfigTarget::P2_ConfigTarget()
+{
+ m_default = false;
+}
+
+P2_ConfigTarget *P2_Config::find_target(string db)
+{
+ list<P2_ConfigTarget *>::const_iterator it;
+ for (it = m_target_list.begin(); it != m_target_list.end(); it++)
+ {
+ if ((*it)->m_virt_database == db)
+ return (*it);
+ }
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+/* $Id: p2_config.h,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#ifndef P2_CONFIG_INCLUDED
+#define P2_CONFIG_INCLUDED
+
+#include <string>
+#include <list>
+
+class P2_ConfigTarget {
+ public:
+ P2_ConfigTarget();
+ std::string m_virt_address;
+ std::string m_virt_database;
+ std::string m_target_address;
+ std::string m_target_database;
+ std::string m_type;
+ bool m_default;
+};
+
+class P2_ConfigModule {
+ public:
+ std::string m_fname;
+};
+
+class P2_Config {
+ class Rep;
+ public:
+ P2_Config::P2_Config();
+ P2_Config::~P2_Config();
+ bool P2_Config::parse_options(int argc, char **argv);
+ P2_ConfigTarget *find_target(std::string db);
+ void print();
+ private:
+ bool read_xml_config(const char *fname);
+ void parse_xml_element_proxy(void *xml_ptr);
+ void parse_xml_element_target(void *xml_ptr,
+ P2_ConfigTarget *t);
+ bool parse_xml_text(void *xml_ptr, std::string &val);
+ bool parse_xml_text(void *xml_ptr, bool &val);
+ public:
+ std::string m_apdu_log;
+ std::string m_default_target;
+ std::string m_listen_address;
+ std::string m_log_file;
+ std::string m_optimize_flags;
+ std::string m_pid_fname;
+ std::string m_uid;
+ std::string m_xml_fname;
+
+ int m_max_clients;
+ int m_client_idletime;
+ int m_debug_mode;
+ int m_no_limit_files;
+ int m_no_threads;
+ int m_target_idletime;
+
+ std::list<P2_ConfigTarget *> m_target_list;
+ std::list<P2_ConfigModule *> m_modules;
+ private:
+ Rep *m_rep;
+ int m_errors;
+};
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+/* $Id: p2_frontend.cpp,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <yaz/log.h>
+#include <yaz/diagbib1.h>
+#include "p2_frontend.h"
+
+using namespace yazpp_1;
+using namespace std;
+
+P2_Frontend::P2_Frontend(IPDU_Observable *the_PDU_Observable,
+ Msg_Thread *my_thread, P2_Server *server)
+ : Z_Assoc(the_PDU_Observable)
+{
+ m_my_thread = my_thread;
+ m_server = server;
+ m_no_requests = 0;
+ m_delete_flag = 0;
+ yaz_log(YLOG_LOG, "Construct P2_Frontend=%p", this);
+}
+
+
+IPDU_Observer *P2_Frontend::sessionNotify(IPDU_Observable
+ *the_PDU_Observable, int fd)
+{
+ return 0;
+}
+
+P2_Frontend::~P2_Frontend()
+{
+ yaz_log(YLOG_LOG, "Destroy P2_Frontend=%p", this);
+
+ list<P2_FrontResultSet *>::iterator it;
+
+ for (it = m_resultSets.begin(); it != m_resultSets.end(); it++)
+ {
+ delete *it;
+ *it = 0;
+ }
+}
+
+void P2_Frontend::recv_GDU(Z_GDU *z_pdu, int len)
+{
+ GDU *gdu = new GDU(z_pdu);
+
+ P2_Msg *m = new P2_Msg(gdu, this, m_server);
+ m_no_requests++;
+ m_my_thread->put(m);
+}
+
+void P2_Frontend::failNotify()
+{
+ m_delete_flag = 1;
+ if (m_no_requests == 0)
+ delete this;
+
+}
+
+void P2_Frontend::timeoutNotify()
+{
+ m_delete_flag = 1;
+ if (m_no_requests == 0)
+ delete this;
+}
+
+void P2_Frontend::connectNotify()
+{
+
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+/* $Id: p2_frontend.h,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#ifndef P2_FRONTEND_H
+#define P2_FRONTEND_H
+
+#include <list>
+#include <vector>
+#include <string>
+
+#include "msg-thread.h"
+#include <yaz++/z-assoc.h>
+#include <yaz++/pdu-assoc.h>
+#include <yaz++/gdu.h>
+#include <yaz++/z-query.h>
+
+class P2_Frontend;
+class P2_Server;
+class P2_Config;
+class P2_ConfigTarget;
+class P2_ModuleFactory;
+
+class IP2_BackendSet;
+
+class P2_BackendResultSet {
+ public:
+ P2_BackendResultSet();
+ ~P2_BackendResultSet();
+ yazpp_1::Yaz_Z_Query m_query;
+ std::list<std::string> m_db_list;
+ int m_hit_count;
+ IP2_BackendSet *m_int;
+ // record cache here
+};
+
+class IP2_Backend;
+
+class P2_Backend {
+ public:
+ P2_Backend(P2_ConfigTarget *cfg, IP2_Backend *backend_interface);
+ ~P2_Backend();
+ public:
+ std::list<P2_BackendResultSet *>m_resultSets;
+ P2_ConfigTarget *m_configTarget;
+ bool m_busy;
+ IP2_Backend *m_int;
+};
+
+class P2_Server : public yazpp_1::Z_Assoc {
+public:
+ ~P2_Server();
+ P2_Server(yazpp_1::IPDU_Observable *the_PDU_Observable,
+ Msg_Thread *m_my_thread,
+ P2_Config *config,
+ P2_ModuleFactory *modules);
+ P2_Config *lockConfig();
+ void unlockConfig();
+ std::list<P2_Backend *>m_backend_list;
+ P2_ModuleFactory *m_modules;
+private:
+ yazpp_1::IPDU_Observer* sessionNotify(
+ yazpp_1::IPDU_Observable *the_PDU_Observable,
+ int fd);
+ void recv_GDU(Z_GDU *apdu, int len);
+
+ void failNotify();
+ void timeoutNotify();
+ void connectNotify();
+private:
+ P2_Config *m_config;
+ Msg_Thread *m_my_thread;
+ pthread_mutex_t m_mutex_config;
+};
+
+class P2_FrontResultSet {
+public:
+ P2_FrontResultSet(const char *id);
+ ~P2_FrontResultSet();
+ void setQuery(Z_Query *z_query);
+ void setDatabases(char **db, int num);
+ std::string m_resultSetId;
+ std::vector<std::string> m_db_list;
+ yazpp_1::Yaz_Z_Query m_query;
+};
+
+class P2_Msg : public IMsg_Thread {
+public:
+ int m_close_flag;
+ yazpp_1::GDU *m_gdu;
+ yazpp_1::GDU *m_output;
+ P2_Frontend *m_front;
+ P2_Server *m_server;
+ IMsg_Thread *handle();
+ void result();
+ P2_Msg(yazpp_1::GDU *gdu, P2_Frontend *front, P2_Server *server);
+ virtual ~P2_Msg();
+ private:
+
+ Z_APDU *frontend_search_resultset(Z_APDU *z_gdu, ODR odr,
+ P2_FrontResultSet **rset);
+ Z_APDU *frontend_present_resultset(Z_APDU *z_gdu, ODR odr,
+ P2_FrontResultSet **rset);
+ Z_APDU *frontend_search_apdu(Z_APDU *z_gdu, ODR odr);
+ Z_APDU *frontend_present_apdu(Z_APDU *z_gdu, ODR odr);
+ P2_Backend *select_backend(std::string db,
+ yazpp_1::Yaz_Z_Query *query,
+ P2_BackendResultSet **bset);
+ P2_Backend *create_backend(std::string db);
+};
+
+class P2_Frontend : public yazpp_1::Z_Assoc {
+ public:
+ ~P2_Frontend();
+ P2_Frontend(yazpp_1::IPDU_Observable *the_PDU_Observable,
+ Msg_Thread *m_my_thread, P2_Server *server);
+ IPDU_Observer* sessionNotify(yazpp_1::IPDU_Observable *the_PDU_Observable,
+ int fd);
+
+ void recv_GDU(Z_GDU *apdu, int len);
+
+ void failNotify();
+ void timeoutNotify();
+ void connectNotify();
+
+ int m_no_requests;
+ int m_delete_flag;
+ std::list<P2_FrontResultSet *> m_resultSets;
+
+ private:
+ yazpp_1::GDUQueue m_in_queue;
+ Msg_Thread *m_my_thread;
+ P2_Server *m_server;
+ private:
+ bool P2_Frontend::search(Z_GDU *z_gdu);
+ bool P2_Frontend::handle_init(Z_GDU *z_gdu);
+};
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+
+#include <dlfcn.h>
+
+#include "p2_modules.h"
+
+class P2_ModuleDLEntry {
+public:
+ void *m_dl_handle;
+ P2_ModuleEntry *m_entry;
+ P2_ModuleDLEntry();
+ ~P2_ModuleDLEntry();
+};
+
+P2_ModuleDLEntry::P2_ModuleDLEntry()
+{
+ m_dl_handle = 0;
+ m_entry = 0;
+}
+
+P2_ModuleDLEntry::~P2_ModuleDLEntry()
+{
+ if (m_dl_handle)
+ dlclose(m_dl_handle);
+}
+
+P2_ModuleFactory::P2_ModuleFactory()
+{
+}
+
+P2_ModuleFactory::~P2_ModuleFactory()
+{
+}
+
+bool P2_ModuleFactory::add(P2_ModuleEntry *entry)
+{
+ P2_ModuleDLEntry *m = new P2_ModuleDLEntry();
+ m->m_entry = entry;
+ m_modules.push_back(m);
+ return true;
+}
+
+bool P2_ModuleFactory::add(const char *fname)
+{
+ void *dl_handle = dlopen(fname, RTLD_NOW|RTLD_GLOBAL);
+ if (!dl_handle)
+ return false;
+
+ P2_ModuleEntry *entry =
+ reinterpret_cast<P2_ModuleEntry *>
+ (dlsym(dl_handle, "p2_module_entry"));
+ if (!entry)
+ {
+ dlclose(dl_handle);
+ return false;
+ }
+ P2_ModuleDLEntry *m = new P2_ModuleDLEntry();
+ m->m_dl_handle = dl_handle;
+ m->m_entry = entry;
+ m_modules.push_back(m);
+ return true;
+}
+
+void *P2_ModuleFactory::get_interface(const char *name, int version)
+{
+ std::list<P2_ModuleDLEntry *>::const_iterator it;
+ for (it = m_modules.begin(); it != m_modules.end(); it++)
+ {
+ P2_ModuleDLEntry *ent = *it;
+ if (!strcmp(ent->m_entry->name, name) &&
+ ent->m_entry->version == version)
+ {
+ return ent->m_entry->interface_ptr;
+ }
+ }
+ return 0;
+}
+
--- /dev/null
+
+#ifndef P2_MODULES_H
+#define P2_MODULES_H
+
+#include "p2_backend.h"
+
+#include <list>
+
+class P2_ModuleDLEntry ;
+class P2_ModuleFactory {
+ public:
+ P2_ModuleFactory();
+ ~P2_ModuleFactory();
+ bool add(const char *fname);
+ bool add(P2_ModuleEntry *entry);
+ void *get_interface(const char *name, int version);
+ private:
+ std::list <P2_ModuleDLEntry *>m_modules;
+};
+
+#endif
--- /dev/null
+/* $Id: p2_msg.cpp,v 1.1 2005-10-05 12:07:14 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <yaz/log.h>
+#include <yaz/diagbib1.h>
+#include "p2_backend.h"
+#include "p2_frontend.h"
+#include "p2_config.h"
+#include "p2_modules.h"
+
+using namespace yazpp_1;
+using namespace std;
+
+IP2_BackendSet::~IP2_BackendSet()
+{
+}
+
+IP2_Backend::~IP2_Backend()
+{
+
+}
+
+P2_Backend::P2_Backend(P2_ConfigTarget *cfg, IP2_Backend *backend_int)
+{
+ m_configTarget = new P2_ConfigTarget;
+ *m_configTarget = *cfg;
+ m_busy = false;
+ m_int = backend_int;
+}
+
+P2_Backend::~P2_Backend()
+{
+ delete m_configTarget;
+}
+
+P2_BackendResultSet::P2_BackendResultSet()
+{
+ m_int = 0;
+}
+
+P2_BackendResultSet::~P2_BackendResultSet()
+{
+ delete m_int;
+}
+
+P2_Backend *P2_Msg::select_backend(string db,
+ Yaz_Z_Query *query,
+ P2_BackendResultSet **bset)
+{
+ P2_Config *cfg = m_server->lockConfig();
+
+ // see if some target has done this query before
+
+ *bset = 0;
+ P2_Backend *backend = 0;
+
+ list<P2_Backend *>::const_iterator it;
+ for (it = m_server->m_backend_list.begin();
+ it != m_server->m_backend_list.end(); it++)
+ {
+ if ((*it)->m_busy)
+ continue;
+
+ if (db != (*it)->m_configTarget->m_virt_database)
+ continue;
+ backend = *it;
+
+ if (query)
+ {
+ list<P2_BackendResultSet *>::const_iterator is;
+ for (is = (*it)->m_resultSets.begin();
+ is != (*it)->m_resultSets.end(); is++)
+ {
+ if (query->match(&(*is)->m_query))
+ {
+ *bset = *is;
+ break;
+ }
+ }
+ }
+ if (bset)
+ break;
+ }
+ if (!backend)
+ {
+ P2_ConfigTarget *target_cfg = cfg->find_target(db);
+
+ if (!target_cfg)
+ {
+ yaz_log(YLOG_WARN, "No backend for database %s",
+ db.c_str());
+ }
+ else
+ {
+ P2_ModuleInterface0 *int0 =
+ reinterpret_cast<P2_ModuleInterface0 *>
+ (m_server->m_modules->get_interface(target_cfg->m_type.c_str(),
+ 0));
+ IP2_Backend *bint = 0;
+
+ if (int0)
+ bint = int0->create(target_cfg->m_target_address.c_str());
+
+ if (bint)
+ backend = new P2_Backend(target_cfg, bint);
+
+ if (backend)
+ m_server->m_backend_list.push_back(backend);
+ }
+ }
+ if (backend)
+ backend->m_busy = true;
+ m_server->unlockConfig();
+ return backend;
+}
+
+void P2_FrontResultSet::setQuery(Z_Query *z_query)
+{
+ m_query.set_Z_Query(z_query);
+}
+
+void P2_FrontResultSet::setDatabases(char **db, int num)
+{
+ m_db_list.clear();
+
+ int i;
+ for (i = 0; i<num; i++)
+ m_db_list.push_back(db[i]);
+}
+
+P2_FrontResultSet::P2_FrontResultSet(const char *id)
+{
+ m_resultSetId = id;
+}
+
+
+P2_FrontResultSet::~P2_FrontResultSet()
+{
+}
+
+P2_Msg::P2_Msg(GDU *gdu, P2_Frontend *front, P2_Server *server)
+{
+ m_front = front;
+ m_server = server;
+ m_output = 0;
+ m_gdu = gdu;
+ m_close_flag = 0;
+}
+
+P2_Msg::~P2_Msg()
+{
+ delete m_output;
+ delete m_gdu;
+}
+
+Z_APDU *P2_Msg::frontend_search_resultset(Z_APDU *z_gdu, ODR odr,
+ P2_FrontResultSet **rset)
+{
+ Z_SearchRequest *req = z_gdu->u.searchRequest;
+ list<P2_FrontResultSet *>::iterator it;
+ P2_FrontResultSet *s = 0;
+
+ string id = req->resultSetName;
+ for (it = m_front->m_resultSets.begin(); it != m_front->m_resultSets.end(); it++)
+ {
+ if ((*it)->m_resultSetId == id)
+ {
+ s = *it;
+ break;
+ }
+ }
+ if (s)
+ {
+ // result set already exists
+ *rset = s;
+ if (req->replaceIndicator && *req->replaceIndicator)
+ { // replace indicator true
+ s->setQuery(req->query);
+ s->setDatabases(req->databaseNames, req->num_databaseNames);
+ return 0;
+ }
+ Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
+ Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
+ apdu->u.searchResponse->records = rec;
+ rec->which = Z_Records_NSD;
+ rec->u.nonSurrogateDiagnostic =
+ zget_DefaultDiagFormat(
+ odr, YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
+ req->resultSetName);
+
+ return apdu;
+ }
+ // does not exist
+ s = new P2_FrontResultSet(req->resultSetName);
+ s->setQuery(req->query);
+ s->setDatabases(req->databaseNames, req->num_databaseNames);
+ m_front->m_resultSets.push_back(s);
+ *rset = s;
+ return 0;
+}
+
+Z_APDU *P2_Msg::frontend_search_apdu(Z_APDU *request_apdu, ODR odr)
+{
+ P2_FrontResultSet *rset;
+ Z_APDU *response_apdu = frontend_search_resultset(request_apdu, odr,
+ &rset);
+ if (response_apdu)
+ return response_apdu;
+
+ // no immediate error (yet)
+ size_t i;
+ for (i = 0; i<rset->m_db_list.size(); i++)
+ {
+ string db = rset->m_db_list[i];
+ P2_BackendResultSet *bset;
+ P2_Backend *b = select_backend(db, &rset->m_query, &bset);
+ if (!b)
+ {
+ Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
+ Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
+ apdu->u.searchResponse->records = rec;
+ rec->which = Z_Records_NSD;
+ rec->u.nonSurrogateDiagnostic =
+ zget_DefaultDiagFormat(
+ odr, YAZ_BIB1_DATABASE_UNAVAILABLE, db.c_str());
+ return apdu;
+ }
+ if (!bset)
+ { // new set
+ bset = new P2_BackendResultSet();
+
+ bset->m_query.set_Z_Query(request_apdu->u.searchRequest->query);
+ bset->m_db_list.push_back(db);
+
+ b->m_int->search(&bset->m_query, &bset->m_int, &bset->m_hit_count);
+ b->m_resultSets.push_back(bset);
+ }
+ else
+ {
+ bset->m_int->get(1, 1);
+ }
+ response_apdu = zget_APDU(odr, Z_APDU_searchResponse);
+ *response_apdu->u.searchResponse->resultCount = bset->m_hit_count;
+ b->m_busy = false;
+ }
+ if (!response_apdu)
+ {
+ Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
+ Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
+ apdu->u.searchResponse->records = rec;
+ rec->which = Z_Records_NSD;
+ rec->u.nonSurrogateDiagnostic =
+ zget_DefaultDiagFormat(odr, YAZ_BIB1_UNSUPP_SEARCH, 0);
+ return apdu;
+ }
+ return response_apdu;
+}
+
+Z_APDU *P2_Msg::frontend_present_resultset(Z_APDU *z_gdu, ODR odr,
+ P2_FrontResultSet **rset)
+{
+ Z_PresentRequest *req = z_gdu->u.presentRequest;
+ list<P2_FrontResultSet *>::iterator it;
+ P2_FrontResultSet *s = 0;
+
+ string id = req->resultSetId;
+ for (it = m_front->m_resultSets.begin(); it != m_front->m_resultSets.end(); it++)
+ {
+ if ((*it)->m_resultSetId == id)
+ {
+ s = *it;
+ break;
+ }
+ }
+ *rset = s;
+ if (s)
+ return 0; // fine result set exists
+
+ Z_APDU *apdu = zget_APDU(odr, Z_APDU_presentResponse);
+
+ Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
+ apdu->u.presentResponse->records = rec;
+ rec->which = Z_Records_NSD;
+ rec->u.nonSurrogateDiagnostic =
+ zget_DefaultDiagFormat(
+ odr,
+ YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
+ req->resultSetId);
+ return apdu;
+}
+
+Z_APDU *P2_Msg::frontend_present_apdu(Z_APDU *request_apdu, ODR odr)
+{
+ P2_FrontResultSet *rset;
+ Z_APDU *response_apdu = frontend_present_resultset(request_apdu, odr,
+ &rset);
+ if (response_apdu)
+ return response_apdu;
+ return zget_APDU(odr, Z_APDU_presentResponse);
+}
+
+IMsg_Thread *P2_Msg::handle()
+{
+ ODR odr = odr_createmem(ODR_ENCODE);
+ yaz_log(YLOG_LOG, "P2_Msg:handle begin");
+ Z_GDU *request_gdu = m_gdu->get();
+
+ if (request_gdu->which == Z_GDU_Z3950)
+ {
+ Z_APDU *request_apdu = request_gdu->u.z3950;
+ Z_APDU *response_apdu = 0;
+ switch(request_apdu->which)
+ {
+ case Z_APDU_initRequest:
+ response_apdu = zget_APDU(odr, Z_APDU_initResponse);
+ ODR_MASK_SET(response_apdu->u.initResponse->options, Z_Options_triggerResourceCtrl);
+ ODR_MASK_SET(response_apdu->u.initResponse->options, Z_Options_search);
+ ODR_MASK_SET(response_apdu->u.initResponse->options, Z_Options_present);
+ ODR_MASK_SET(response_apdu->u.initResponse->options, Z_Options_namedResultSets);
+ break;
+ case Z_APDU_searchRequest:
+ response_apdu = frontend_search_apdu(request_apdu, odr);
+ break;
+ case Z_APDU_presentRequest:
+ response_apdu = frontend_present_apdu(request_apdu, odr);
+ break;
+ case Z_APDU_triggerResourceControlRequest:
+ break;
+ default:
+ response_apdu = zget_APDU(odr, Z_APDU_close);
+ m_close_flag = 1;
+ break;
+ }
+ if (response_apdu)
+ m_output = new GDU(response_apdu);
+ }
+ yaz_log(YLOG_LOG, "P2_Msg:handle end");
+ odr_destroy(odr);
+ return this;
+}
+
+void P2_Msg::result()
+{
+ m_front->m_no_requests--;
+ if (!m_front->m_delete_flag)
+ {
+ if (m_output)
+ {
+ int len;
+ m_front->send_GDU(m_output->get(), &len);
+ }
+ if (m_close_flag)
+ {
+ m_front->close();
+ m_front->m_delete_flag = 1;
+ }
+ }
+ if (m_front->m_delete_flag && m_front->m_no_requests == 0)
+ delete m_front;
+ delete this;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+/* $Id: p2_xmlerror.cpp,v 1.1 2005-10-05 12:07:15 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <yaz/log.h>
+
+#include "p2_xmlerror.h"
+
+#if HAVE_XSLT
+#include <libxml/parser.h>
+#include <libxslt/xsltutils.h>
+#endif
+
+#if HAVE_XSLT
+static void p2_xml_error_handler(void *ctx, const char *fmt, ...)
+{
+ char buf[1024];
+ size_t sz;
+
+ va_list ap;
+ va_start(ap, fmt);
+
+#ifdef WIN32
+ vsprintf(buf, fmt, ap);
+#else
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+#endif
+ sz = strlen(buf);
+ if (sz > 0 && buf[sz-1] == '\n')
+ buf[sz-1] = '\0';
+
+ yaz_log(YLOG_WARN, "%s: %s", (char*) ctx, buf);
+
+ va_end (ap);
+}
+#endif
+
+void p2_xmlerror_setup()
+{
+#if HAVE_XSLT
+ xmlSetGenericErrorFunc((void *) "XML", p2_xml_error_handler);
+ xsltSetGenericErrorFunc((void *) "XSLT", p2_xml_error_handler);
+#endif
+}
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
--- /dev/null
+/* $Id: p2_xmlerror.h,v 1.1 2005-10-05 12:07:15 adam Exp $
+ Copyright (c) 1998-2005, Index Data.
+
+This file is part of the yaz-proxy.
+
+YAZ proxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with YAZ proxy; see the file LICENSE. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#ifndef P2_XMLERROR_H
+#define P2_XMLERROR_H
+
+void p2_xmlerror_setup();
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
/*
- * Copyright (c) 1998-2005, Index Data.
+ * Copyright (c) 2005, Index Data.
* See the file LICENSE for details.
*
- * $Id: t-server.cpp,v 1.5 2005-09-26 09:24:14 adam Exp $
+ * $Id: t-server.cpp,v 1.6 2005-10-05 12:07:15 adam Exp $
*/
#include <stdlib.h>